microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
67.13k stars 3.69k forks source link

[Feature] `locator.screenshot()` add option for expanding space captured around locator #16928

Open fredboyle opened 2 years ago

fredboyle commented 2 years ago

When creating visual regression tests(VRT) for a variety of elements it would be very helpful if the locator.screenshot() method had an option to add extra space around the locator.

This is particularly handy for testing :focus-visible CSS states that are actually outside the bounding box of the element. It would also be helpful for hover and active states that may go outside the element, likely other scenarios as well.

Example

const button = page.locator('button');
await button.focus();

expect(await button.screenshot({ expandAreaBy: '20px' })).toMatchSnapshot(
                'button-focused.png',
            );

// Alternative option syntax
expect(await button.screenshot({ expandAreaBy: { value: 20, unit: 'px' } })).toMatchSnapshot(
                'button-focused.png',
            );
p01 commented 2 years ago

It would be great if the value can be negative as well, to tighten a bit a screenshot. Then the property should be renamed to something like margin.

The reason I am asking for this is that we have seen visual tests for locator.screenshot(...) fail due to fractional width and height of the element pointed by the locator and non deterministic things rendered behind/underneath the locator, which leads to "noisy" things on one edge of the screenshots

xdubx commented 1 year ago

Have the same problem. And my shadowdom elements get custom margin and the toHaveScreenshot remove it. An wrapper with a div round don't work too.

fredboyle commented 1 year ago

Checking in on this to see if there's any hope of one day having it. New project and this need still exists.

k-ruben commented 2 months ago

I build the functionality myself. The Code adds additional height and width and positions the element in the middle. additionalWidth = 40 -> top and bottom have both increased by 20px

const additionalHeight = 20;
const additionalWidth = 40;
const boundingBox = await component.boundingBox();

if (!boundingBox) {
    return;
}

const documentWidth = await page.evaluate(() => document.body.clientWidth);
const documentHeight = await page.evaluate(() => document.body.clientHeight);

const scrollX = await page.evaluate(() => window.scrollX);
const scrollY = await page.evaluate(() => window.scrollY);

const x = Math.max(scrollX + boundingBox.x - additionalWidth / 2, 0);
const y = Math.max(scrollY + boundingBox.y - additionalHeight / 2, 0);
const width = Math.min(boundingBox.width + additionalWidth, documentWidth - x);
const height = Math.min(boundingBox.height + additionalHeight, documentHeight - y);

await page.screenshot({
    path: `${name}-component-${componentName}.png`,
    clip: { x, y, width, height },
    fullPage: true,
});