How to: Interact with a slider using Playwright

2.4k views Asked by At

What I expected I am attempting to interact with a slider ui element on my angular based webpage. The code shown below is not interacting with the elements in question anymore. It was at one point..

More Info

Reading on the "white-space" issue mentioned, this is called "view-trailing". I am not 100% sure this relates to the issue.

What I have tried

I am having issues interacting with a slider on a webpage built with angular. Through my current solution

 async slideToAgree() {
    const { page } = this

    const {
      width: sw,
      height: sh,
      x: sx,
      y: sy
    } = await (await page.$('.slider-controller')).boundingBox();

    const { width: tw, x: tx } = await (
      await page.$('.slider-container')
    ).boundingBox();

    await page.mouse.move(sx + sw / 2, sy + sh / 2);
    await page.mouse.down();
    await page.mouse.move(tx + tw, sy + sh / 2, { steps: 10 });

  }
}

What actually happened / result

Playwright is clicking on the top right of the screen, outside of the webpage in this strange "extra white space".. making me think the bounding box for the element is broken / not attached to the DOM?

Can someone please provide an updated implementation of "Click and drag" or "interacting with a slider"? Nothing I have ran into so far has worked.

3

There are 3 answers

1
candre On BEST ANSWER

Maybe it will solve your problem using the width of the slider instead of the relative to viewport position of the box? I don't know how your slider looks like, so I tested against a generic slider written in Angular and it seems to drag the slider as intended:

import {test} from '@playwright/test'

test('Move slider', async ({ page, context }) => {

    await page.goto('https://material.angular.io/components/slider/examples')
    const sliderTrack = await page.locator('.mdc-slider__track').first()
    const sliderOffsetWidth = await sliderTrack.evaluate(el => {
        return el.getBoundingClientRect().width
    })

    // Using the hover method to place the mouse cursor then moving it to the right
    await sliderTrack.hover({ force: true, position: { x: 0, y: 0 } })
    await page.mouse.down()
    await sliderTrack.hover({ force: true, position: { x: sliderOffsetWidth, y: 0 } })
    await page.mouse.up()

})
0
Mendi45 On

In addition to the great answer of @candre if you need to click on the slider use the .click() method with the same parameters such as the hover():

await sliderTrack.click({ force: true, position: { x: (sliderOffsetWidth / 3), y: 0 } });
0
Roman86 On

There are a few problems to consider:

  1. while moving a thumb you should respect the slider step size in pixels (my code deals with it by gradually increasing the shift until value is changed)
  2. slider might be in position 3 while you're looking for 2 (must scan entire values range)
import { type Locator, type Page, test } from '@playwright/test';

test('Any slider move', async ({ page }) => {
  await page.goto('https://material.angular.io/components/slider/examples');

  const slider = page.locator('.mat-mdc-slider').first();
  const sliderThumb = slider.locator('.mdc-slider__thumb').first();
  const value = slider.locator('.mdc-slider__input').first();

  await slideTo(
    page,
    sliderThumb,
    async () => {
      const strV = await value.getAttribute('aria-valuetext');
      if (strV == null) {
        throw new Error("Can't read the slider value");
      }
      return parseInt(strV, 10);
    },
    (v) => v === 10,
  );
});

async function slideTo(
  page: Page,
  sliderThumb: Locator,
  value: () => Promise<number>,
  done: (v: number) => boolean,
) {
  await sliderThumb.waitFor({
    timeout: 500,
  });

  let thumbMoveBy = 2;
  let stepSizeOk = false;

  const hoverTimeout = 1000;

  await sliderThumb.hover({ force: true, timeout: hoverTimeout });
  await page.mouse.down();
  await sliderThumb.hover({ force: true, position: { x: -1000, y: 0 }, timeout: hoverTimeout });
  await page.mouse.up();

  let lastValue = await value();

  while (!done(lastValue)) {
    await sliderThumb.hover({ force: true, timeout: hoverTimeout });
    await page.mouse.down();
    await sliderThumb.hover({
      force: true,
      position: { x: thumbMoveBy, y: 0 },
      timeout: hoverTimeout,
    });
    await page.mouse.up();
    await page.waitForTimeout(10);
    const newValue = await value();
    if (newValue === lastValue) {
      if (stepSizeOk) {
        throw new Error(`Slider stop at ${lastValue}, value was not found`);
      }
      thumbMoveBy += 2;
    } else {
      lastValue = newValue;
      stepSizeOk = true;
    }
  }
}