playwright/tests/playwright-test/to-have-screenshot.spec.ts

1157 lines
44 KiB
TypeScript
Raw Normal View History

/**
* Copyright Microsoft Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as fs from 'fs';
2022-04-19 06:20:49 +03:00
import { PNG } from 'playwright-core/lib/utilsBundle';
import * as path from 'path';
import { pathToFileURL } from 'url';
2023-02-08 02:11:44 +03:00
import { test, expect, createImage, paintBlackPixels } from './playwright-test-fixtures';
import { comparePNGs } from '../config/comparator';
test.describe.configure({ mode: 'parallel' });
const IMG_WIDTH = 1280;
const IMG_HEIGHT = 720;
const whiteImage = createImage(IMG_WIDTH, IMG_HEIGHT, 255, 255, 255);
const redImage = createImage(IMG_WIDTH, IMG_HEIGHT, 255, 0, 0);
const greenImage = createImage(IMG_WIDTH, IMG_HEIGHT, 0, 255, 0);
const blueImage = createImage(IMG_WIDTH, IMG_HEIGHT, 0, 0, 255);
test('should fail to screenshot a page with infinite animation', async ({ runInlineTest }, testInfo) => {
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
...playwrightConfig({
expect: {
toHaveScreenshot: {
animations: 'allow',
},
},
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.goto('${infiniteAnimationURL}');
await expect(page).toHaveScreenshot({ timeout: 2000 });
});
`
});
expect(result.exitCode).toBe(1);
2023-02-08 02:11:44 +03:00
expect(result.output).toContain(`Timeout 2000ms exceeded`);
expect(result.output).toContain(`expect.toHaveScreenshot with timeout 2000ms`);
expect(result.output).toContain(`generating new stable screenshot expectation`);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-actual.png'))).toBe(true);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-expected.png'))).toBe(false);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-previous.png'))).toBe(true);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-diff.png'))).toBe(true);
expect(fs.existsSync(testInfo.outputPath('a.spec.js-snapshots', 'is-a-test-1.png'))).toBe(false);
});
test('should disable animations by default', async ({ runInlineTest }, testInfo) => {
const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html'));
const result = await runInlineTest({
...playwrightConfig({}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.goto('${cssTransitionURL}');
await expect(page).toHaveScreenshot({ timeout: 2000 });
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
});
test.describe('expect config animations option', () => {
test('disabled', async ({ runInlineTest }, testInfo) => {
const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html'));
const result = await runInlineTest({
...playwrightConfig({
expect: { toHaveScreenshot: { animations: 'disabled' } },
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.goto('${cssTransitionURL}');
await expect(page).toHaveScreenshot({ timeout: 2000 });
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
});
test('allow', async ({ runInlineTest }, testInfo) => {
const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html'));
const result = await runInlineTest({
...playwrightConfig({
expect: { toHaveScreenshot: { animations: 'allow' } },
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.goto('${cssTransitionURL}');
await expect(page).toHaveScreenshot({ timeout: 2000 });
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(1);
expect(result.output).toContain('is-a-test-1-diff.png');
});
});
test('should fail with proper error when unsupported argument is given', async ({ runInlineTest }, testInfo) => {
const cssTransitionURL = pathToFileURL(path.join(__dirname, '../assets/css-transition.html'));
const result = await runInlineTest({
...playwrightConfig({}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.goto('${cssTransitionURL}');
await expect(page).toHaveScreenshot({
clip: {
x: 0,
y: 0,
width: 0,
height: 0,
},
timeout: 2000,
});
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(1);
2023-02-08 02:11:44 +03:00
expect(result.output).toContain(`Expected options.clip.width not to be 0`);
});
test('should have scale:css by default', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ browser }) => {
const context = await browser.newContext({
viewport: { width: ${IMG_WIDTH}, height: ${IMG_HEIGHT} },
deviceScaleFactor: 2,
});
const page = await context.newPage();
await expect(page).toHaveScreenshot('snapshot.png');
await context.close();
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
const snapshotOutputPath = testInfo.outputPath('__screenshots__', 'a.spec.js', 'snapshot.png');
expect(comparePNGs(fs.readFileSync(snapshotOutputPath), whiteImage)).toBe(null);
});
test('should ignore non-documented options in toHaveScreenshot config', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
expect: {
toHaveScreenshot: {
clip: { x: 0, y: 0, width: 10, height: 10 },
},
},
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
const snapshotOutputPath = testInfo.outputPath('__screenshots__', 'a.spec.js', 'snapshot.png');
expect(comparePNGs(fs.readFileSync(snapshotOutputPath), whiteImage)).toBe(null);
});
test('should report toHaveScreenshot step with expectation name in title', async ({ runInlineTest }) => {
const result = await runInlineTest({
'reporter.ts': `
class Reporter {
onStepEnd(test, result, step) {
console.log('%% end ' + step.title);
}
}
module.exports = Reporter;
`,
...playwrightConfig({ reporter: './reporter' }),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
// Named expectation.
await expect(page).toHaveScreenshot('foo.png', { timeout: 2000 });
// Anonymous expectation.
await expect(page).toHaveScreenshot({ timeout: 2000 });
});
`
}, { 'reporter': '', 'workers': 1, 'update-snapshots': true });
expect(result.exitCode).toBe(0);
2023-02-08 02:11:44 +03:00
expect(result.outputLines).toEqual([
`end browserContext.newPage`,
`end Before Hooks`,
`end expect.toHaveScreenshot(foo.png)`,
`end expect.toHaveScreenshot(is-a-test-1.png)`,
`end browserContext.close`,
`end After Hooks`,
]);
});
test('should not fail when racing with navigation', async ({ runInlineTest }, testInfo) => {
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
...playwrightConfig({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': createImage(10, 10, 255, 0, 0),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await Promise.all([
page.goto('${infiniteAnimationURL}'),
expect(page).toHaveScreenshot({
name: 'snapshot.png',
animations: "disabled",
clip: { x: 0, y: 0, width: 10, height: 10 },
}),
]);
});
`
});
expect(result.exitCode).toBe(0);
});
test('should successfully screenshot a page with infinite animation with disableAnimation: true', async ({ runInlineTest }, testInfo) => {
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.goto('${infiniteAnimationURL}');
await expect(page).toHaveScreenshot({
animations: "disabled",
});
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
expect(fs.existsSync(testInfo.outputPath('__screenshots__', 'a.spec.js', 'is-a-test-1.png'))).toBe(true);
});
test('should support clip option for page', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': createImage(50, 50, 255, 255, 255),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot({
name: 'snapshot.png',
clip: { x: 0, y: 0, width: 50, height: 50, },
});
});
`
});
expect(result.exitCode).toBe(0);
});
test('should support omitBackground option for locator', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.evaluate(() => {
document.body.style.setProperty('width', '100px');
document.body.style.setProperty('height', '100px');
});
await expect(page.locator('body')).toHaveScreenshot({
name: 'snapshot.png',
omitBackground: true,
});
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
const snapshotPath = testInfo.outputPath('__screenshots__', 'a.spec.js', 'snapshot.png');
expect(fs.existsSync(snapshotPath)).toBe(true);
const png = PNG.sync.read(fs.readFileSync(snapshotPath));
expect.soft(png.width, 'image width must be 100').toBe(100);
expect.soft(png.height, 'image height must be 100').toBe(100);
expect.soft(png.data[0], 'image R must be 0').toBe(0);
expect.soft(png.data[1], 'image G must be 0').toBe(0);
expect.soft(png.data[2], 'image B must be 0').toBe(0);
expect.soft(png.data[3], 'image A must be 0').toBe(0);
});
test('should fail to screenshot an element with infinite animation', async ({ runInlineTest }, testInfo) => {
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
...playwrightConfig({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
projects: [{
expect: {
toHaveScreenshot: {
animations: 'allow',
},
},
}],
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.goto('${infiniteAnimationURL}');
await expect(page.locator('body')).toHaveScreenshot({ timeout: 2000 });
});
`
});
expect(result.exitCode).toBe(1);
2023-02-08 02:11:44 +03:00
expect(result.output).toContain(`Timeout 2000ms exceeded`);
expect(result.output).toContain(`expect.toHaveScreenshot with timeout 2000ms`);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-previous.png'))).toBe(true);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-actual.png'))).toBe(true);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-expected.png'))).toBe(false);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-diff.png'))).toBe(true);
expect(fs.existsSync(testInfo.outputPath('__screenshots__', 'a.spec.js', 'is-a-test-1.png'))).toBe(false);
});
test('should fail to screenshot an element that keeps moving', async ({ runInlineTest }, testInfo) => {
const infiniteAnimationURL = pathToFileURL(path.join(__dirname, '../assets/rotate-z.html'));
const result = await runInlineTest({
...playwrightConfig({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
expect: {
toHaveScreenshot: {
animations: 'allow',
},
},
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await page.goto('${infiniteAnimationURL}');
await expect(page.locator('div')).toHaveScreenshot({ timeout: 2000 });
});
`
});
expect(result.exitCode).toBe(1);
2023-02-08 02:11:44 +03:00
expect(result.output).toContain(`Timeout 2000ms exceeded`);
expect(result.output).toContain(`element is not stable - waiting`);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-actual.png'))).toBe(false);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-expected.png'))).toBe(false);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-diff.png'))).toBe(false);
expect(fs.existsSync(testInfo.outputPath('__screenshots__', 'a.spec.js', 'is-a-test-1.png'))).toBe(false);
});
test('should generate default name', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot();
});
`
});
expect(result.exitCode).toBe(1);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-actual.png'))).toBe(true);
expect(fs.existsSync(testInfo.outputPath('__screenshots__', 'a.spec.js', 'is-a-test-1.png'))).toBe(true);
});
test('should compile with different option combinations', async ({ runTSC }) => {
const result = await runTSC({
'playwright.config.ts': `
import { defineConfig } from '@playwright/test';
export default defineConfig({
expect: {
timeout: 10000,
toHaveScreenshot: {
threshold: 0.2,
maxDiffPixels: 10,
maxDiffPixelRatio: 0.2,
animations: "allow",
caret: "hide",
scale: "css",
},
},
});
`,
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot();
await expect(page).toHaveScreenshot('img.png');
await expect(page).toHaveScreenshot('img.png', { threshold: 0.2, caret: 'initial' });
await expect(page.locator('body')).toHaveScreenshot({ threshold: 0.2 });
await expect(page).toHaveScreenshot({ maxDiffPixelRatio: 0.2 });
await expect(page).toHaveScreenshot({
threshold: 0.2,
maxDiffPixels: 10,
maxDiffPixelRatio: 0.2,
animations: "disabled",
omitBackground: true,
caret: "initial",
scale: "device",
timeout: 1000,
});
});
`
});
expect(result.exitCode).toBe(0);
});
test('should fail when screenshot is different size', async ({ runInlineTest }) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': createImage(22, 33),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
});
expect(result.exitCode).toBe(1);
2023-02-08 02:11:44 +03:00
expect(result.output).toContain(`verifying given screenshot expectation`);
expect(result.output).toContain(`captured a stable screenshot`);
expect(result.output).toContain('Expected an image 22px by 33px, received 1280px by 720px.');
});
test('should fail when given non-png snapshot name', async ({ runInlineTest }) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.jpeg');
});
`
});
expect(result.exitCode).toBe(1);
2023-02-08 02:11:44 +03:00
expect(result.output).toContain(`Screenshot name "snapshot.jpeg" must have '.png' extension`);
});
test('should fail when given buffer', async ({ runInlineTest }) => {
const result = await runInlineTest({
...playwrightConfig({}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(Buffer.from([1])).toHaveScreenshot();
});
`
});
expect(result.exitCode).toBe(1);
2023-02-08 02:11:44 +03:00
expect(result.output).toContain(`toHaveScreenshot can be only used with Page or Locator objects`);
});
test('should fail when screenshot is different pixels', async ({ runInlineTest }) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': paintBlackPixels(whiteImage, 12345),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
});
expect(result.exitCode).toBe(1);
expect(result.output).toContain('Screenshot comparison failed');
expect(result.output).toContain('12345 pixels');
expect(result.output).toContain('Call log');
expect(result.output).toContain('ratio 0.02');
expect(result.output).toContain('Expected:');
expect(result.output).toContain('Received:');
});
test('doesn\'t create comparison artifacts in an output folder for passed negated snapshot matcher', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': blueImage,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).not.toHaveScreenshot('snapshot.png');
});
`
});
expect(result.exitCode).toBe(0);
2023-02-08 02:11:44 +03:00
const outputText = result.output;
const expectedSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-expected.png');
const actualSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-actual.png');
expect(outputText).not.toContain(`Expected: ${expectedSnapshotArtifactPath}`);
expect(outputText).not.toContain(`Received: ${actualSnapshotArtifactPath}`);
expect(fs.existsSync(expectedSnapshotArtifactPath)).toBe(false);
expect(fs.existsSync(actualSnapshotArtifactPath)).toBe(false);
});
test('should fail on same snapshots with negate matcher', async ({ runInlineTest }) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': whiteImage,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).not.toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
});
expect(result.exitCode).toBe(1);
expect(result.output).toContain('Screenshot comparison failed:');
expect(result.output).toContain('Expected result should be different from the actual one.');
});
test('should not fail if --ignore-snapshots is passed', async ({ runInlineTest }) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': redImage,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
}, { 'ignore-snapshots': true });
expect(result.exitCode).toBe(0);
});
test('should write missing expectations locally twice and continue', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png');
await expect(page).toHaveScreenshot('snapshot2.png');
console.log('Here we are!');
});
`
});
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
const snapshot1OutputPath = testInfo.outputPath('__screenshots__', 'a.spec.js', 'snapshot.png');
expect(result.output).toContain(`Error: A snapshot doesn't exist at ${snapshot1OutputPath}, writing actual`);
expect(comparePNGs(fs.readFileSync(snapshot1OutputPath), whiteImage)).toBe(null);
const snapshot2OutputPath = testInfo.outputPath('__screenshots__', 'a.spec.js', 'snapshot2.png');
expect(result.output).toContain(`Error: A snapshot doesn't exist at ${snapshot2OutputPath}, writing actual`);
expect(comparePNGs(fs.readFileSync(snapshot2OutputPath), whiteImage)).toBe(null);
expect(result.output).toContain('Here we are!');
2023-02-08 02:11:44 +03:00
const stackLines = result.output.split('\n').filter(line => line.includes(' at ')).filter(line => !line.includes(testInfo.outputPath()));
expect(result.output).toContain('a.spec.js:5');
expect(stackLines.length).toBe(0);
});
test('shouldn\'t write missing expectations locally for negated matcher', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).not.toHaveScreenshot('snapshot.png');
});
`
});
expect(result.exitCode).toBe(1);
const snapshotOutputPath = testInfo.outputPath('__screenshots__/a.spec.js/snapshot.png');
expect(result.output).toContain(`A snapshot doesn't exist at ${snapshotOutputPath}, matchers using ".not" won\'t write them automatically.`);
expect(fs.existsSync(snapshotOutputPath)).toBe(false);
});
test('should update snapshot with the update-snapshots flag', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': blueImage,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
const snapshotOutputPath = testInfo.outputPath('__screenshots__/a.spec.js/snapshot.png');
expect(result.output).toContain(`${snapshotOutputPath} is re-generated, writing actual.`);
expect(comparePNGs(fs.readFileSync(snapshotOutputPath), whiteImage)).toBe(null);
});
test('shouldn\'t update snapshot with the update-snapshots flag for negated matcher', async ({ runInlineTest }, testInfo) => {
const EXPECTED_SNAPSHOT = blueImage;
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).not.toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
const snapshotOutputPath = testInfo.outputPath('__screenshots__/a.spec.js/snapshot.png');
expect(fs.readFileSync(snapshotOutputPath).equals(EXPECTED_SNAPSHOT)).toBe(true);
});
test('should silently write missing expectations locally with the update-snapshots flag', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
const snapshotOutputPath = testInfo.outputPath('__screenshots__/a.spec.js/snapshot.png');
expect(result.output).toContain(`A snapshot doesn't exist at ${snapshotOutputPath}, writing actual`);
const data = fs.readFileSync(snapshotOutputPath);
expect(comparePNGs(data, whiteImage)).toBe(null);
});
test('should not write missing expectations locally with the update-snapshots flag for negated matcher', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).not.toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(1);
const snapshotOutputPath = testInfo.outputPath('__screenshots__/a.spec.js/snapshot.png');
expect(result.output).toContain(`A snapshot doesn't exist at ${snapshotOutputPath}, matchers using ".not" won\'t write them automatically.`);
expect(fs.existsSync(snapshotOutputPath)).toBe(false);
});
test('should match multiple snapshots', async ({ runInlineTest }) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/red.png': redImage,
'__screenshots__/a.spec.js/green.png': greenImage,
'__screenshots__/a.spec.js/blue.png': blueImage,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await Promise.all([
page.evaluate(() => document.documentElement.style.setProperty('background', '#f00')),
expect(page).toHaveScreenshot('red.png'),
]);
await Promise.all([
page.evaluate(() => document.documentElement.style.setProperty('background', '#0f0')),
expect(page).toHaveScreenshot('green.png'),
]);
await Promise.all([
page.evaluate(() => document.documentElement.style.setProperty('background', '#00f')),
expect(page).toHaveScreenshot('blue.png'),
]);
});
`
});
expect(result.exitCode).toBe(0);
});
test('should use provided name', async ({ runInlineTest }) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/provided.png': whiteImage,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('provided.png');
});
`
});
expect(result.exitCode).toBe(0);
});
test('should use provided name via options', async ({ runInlineTest }) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/provided.png': whiteImage,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot({ name: 'provided.png' });
});
`
});
expect(result.exitCode).toBe(0);
});
test('should respect maxDiffPixels option', async ({ runInlineTest }) => {
const BAD_PIXELS = 120;
const EXPECTED_SNAPSHOT = paintBlackPixels(whiteImage, BAD_PIXELS);
expect((await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
})).exitCode, 'make sure default comparison fails').toBe(1);
expect((await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixels: ${BAD_PIXELS}
});
});
`
})).exitCode, 'make sure maxDiffPixels option is respected').toBe(0);
expect((await runInlineTest({
...playwrightConfig({
expect: {
toHaveScreenshot: {
maxDiffPixels: BAD_PIXELS
}
},
projects: [
{
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
},
],
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png');
});
`
})).exitCode, 'make sure maxDiffPixels option in project config is respected').toBe(0);
});
test('should not update screenshot that matches with maxDiffPixels option when -u is passed', async ({ runInlineTest }, testInfo) => {
const BAD_PIXELS = 120;
const EXPECTED_SNAPSHOT = paintBlackPixels(whiteImage, BAD_PIXELS);
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', { maxDiffPixels: ${BAD_PIXELS} });
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
expect(result.output).not.toContain(`is re-generated, writing actual`);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-actual.png'))).toBe(false);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-expected.png'))).toBe(false);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-previous.png'))).toBe(false);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-is-a-test', 'is-a-test-1-diff.png'))).toBe(false);
const data = fs.readFileSync(testInfo.outputPath('__screenshots__/a.spec.js/snapshot.png'));
expect(comparePNGs(data, EXPECTED_SNAPSHOT)).toBe(null);
});
test('should satisfy both maxDiffPixelRatio and maxDiffPixels', async ({ runInlineTest }) => {
const BAD_RATIO = 0.25;
const BAD_COUNT = Math.floor(IMG_WIDTH * IMG_HEIGHT * BAD_RATIO);
const EXPECTED_SNAPSHOT = paintBlackPixels(whiteImage, BAD_COUNT);
expect((await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
})).exitCode, 'make sure default comparison fails').toBe(1);
expect((await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixels: ${Math.floor(BAD_COUNT / 2)},
maxDiffPixelRatio: ${BAD_RATIO},
timeout: 2000,
});
});
`
})).exitCode, 'make sure it fails when maxDiffPixels < actualBadPixels < maxDiffPixelRatio').toBe(1);
expect((await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixels: ${BAD_COUNT},
maxDiffPixelRatio: ${BAD_RATIO / 2},
timeout: 2000,
});
});
`
})).exitCode, 'make sure it fails when maxDiffPixelRatio < actualBadPixels < maxDiffPixels').toBe(1);
expect((await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixels: ${BAD_COUNT},
maxDiffPixelRatio: ${BAD_RATIO},
});
});
`
})).exitCode, 'make sure it passes when actualBadPixels < maxDiffPixelRatio && actualBadPixels < maxDiffPixels').toBe(0);
});
test('should respect maxDiffPixelRatio option', async ({ runInlineTest }) => {
const BAD_RATIO = 0.25;
const BAD_PIXELS = IMG_WIDTH * IMG_HEIGHT * BAD_RATIO;
const EXPECTED_SNAPSHOT = paintBlackPixels(whiteImage, BAD_PIXELS);
expect((await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
})).exitCode, 'make sure default comparison fails').toBe(1);
expect((await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', {
maxDiffPixelRatio: ${BAD_RATIO}
});
});
`
})).exitCode, 'make sure maxDiffPixelRatio option is respected').toBe(0);
expect((await runInlineTest({
...playwrightConfig({
expect: {
toHaveScreenshot: {
maxDiffPixelRatio: BAD_RATIO,
},
},
projects: [{
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}],
}),
'__screenshots__/a.spec.js/snapshot.png': EXPECTED_SNAPSHOT,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png');
});
`
})).exitCode, 'make sure maxDiffPixels option in project config is respected').toBe(0);
});
test('should throw for invalid maxDiffPixels values', async ({ runInlineTest }) => {
expect((await runInlineTest({
...playwrightConfig({}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot({
maxDiffPixels: -1,
});
});
`
})).exitCode).toBe(1);
});
test('should throw for invalid maxDiffPixelRatio values', async ({ runInlineTest }) => {
expect((await runInlineTest({
...playwrightConfig({}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot({
maxDiffPixelRatio: 12,
});
});
`
})).exitCode).toBe(1);
});
test('should attach expected/actual/diff when sizes are different', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': createImage(2, 2),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test.afterEach(async ({}, testInfo) => {
console.log('## ' + JSON.stringify(testInfo.attachments));
});
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png', { timeout: 2000 });
});
`
});
expect(result.exitCode).toBe(1);
2023-02-08 02:11:44 +03:00
const outputText = result.output;
expect(outputText).toContain('Expected an image 2px by 2px, received 1280px by 720px.');
expect(outputText).toContain('4 pixels (ratio 0.01 of all image pixels) are different.');
const attachments = outputText.split('\n').filter(l => l.startsWith('## ')).map(l => l.substring(3)).map(l => JSON.parse(l))[0];
for (const attachment of attachments)
attachment.path = attachment.path.replace(/\\/g, '/').replace(/.*test-results\//, '');
expect(attachments).toEqual([
{
name: 'snapshot-expected.png',
contentType: 'image/png',
path: 'a-is-a-test/snapshot-expected.png'
},
{
name: 'snapshot-actual.png',
contentType: 'image/png',
path: 'a-is-a-test/snapshot-actual.png'
},
{
name: 'snapshot-diff.png',
contentType: 'image/png',
path: 'a-is-a-test/snapshot-diff.png'
},
]);
});
test('should fail with missing expectations and retries', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({
retries: 1,
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png');
});
`
});
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
const snapshotOutputPath = testInfo.outputPath('__screenshots__/a.spec.js/snapshot.png');
expect(result.output).toContain(`A snapshot doesn't exist at ${snapshotOutputPath}, writing actual`);
const data = fs.readFileSync(snapshotOutputPath);
expect(comparePNGs(data, whiteImage)).toBe(null);
});
test('should update expectations with retries', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
...playwrightConfig({
retries: 1,
feat(playwright-test): introduce snapshotPathTemplate configuration (#18568) This configuration option allows to set a string with template values for precise control over snapshot path location. An example of `snapshotPathTemplate` usage: ```ts // playwright.config.ts // Notice the `testDir` configuration! export default { testDir: './tests', snapshotPathTemplate: './__screenshots__/{platform}/{projectName}/{testFilePath}/{arg}{ext}', } ``` Currently supported "magic tokens" inside the `snapshotPathTemplate` are: - `{testDir}` - project's `testDir` - `{snapshotDir}` - project's `snapshotDir` - `{platform}` - `process.platform` - `{projectName}` - Project's sanitized name - `{testFileDir}` - Directories in relative path from `testDir` to test file path (e.g. `page/` in the example below) - `{testFileName}` - Test file name (with extension) (e.g. `page-click.spec.ts` in the example below) - `{testFilePath}` - Relative path from `testDir` to test file path (e.g. `page/page-click.spec.ts` in the example below) - `{ext}` - snapshot extension (with dots) - `{arg}` - joined snapshot name parts, without extension (e.g. `foo/bar/baz` in the example below) - `{snapshotSuffix}` - `testInfo.snapshotSuffix` value. Consider the following file structure: ``` playwright.config.ts tests/ └── page/ └── page-click.spec.ts ``` The following `page-click.spec.ts`: ```ts // page-click.spec.ts import { test, expect } from '@playwright/test'; test('should work', async ({ page }) => { await expect(page).toHaveScreenshot(['foo', 'bar', 'baz.png']); }); ``` Fixes #7792
2022-11-10 02:29:07 +03:00
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('is a test', async ({ page }) => {
await expect(page).toHaveScreenshot('snapshot.png');
});
`
}, { 'update-snapshots': true });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
const snapshotOutputPath = testInfo.outputPath('__screenshots__/a.spec.js/snapshot.png');
expect(result.output).toContain(`A snapshot doesn't exist at ${snapshotOutputPath}, writing actual`);
const data = fs.readFileSync(snapshotOutputPath);
expect(comparePNGs(data, whiteImage)).toBe(null);
});
feat: implement a new image comparison function (#19166) This patch implements a new image comparison function, codenamed "ssim-cie94". The goal of the new comparison function is to cancel out browser non-determenistic rendering. To use the new comparison function: ```ts await expect(page).toHaveScreenshot({ comparator: 'ssim-cie94', }); ``` As of Nov 30, 2022, we identified the following sources of non-determenistic rendering for Chromium: - Anti-aliasing for certain shapes might be different due to the way skia rasterizes certain shapes. - Color blending might be different on `x86` and `aarch64` architectures. The new function employs a few heuristics to fight these differences. Consider two non-equal image pixels `(r1, g1, b1)` and `(r2, g2, b2)`: 1. If the [CIE94] metric is less then 1.0, then we consider these pixels **EQUAL**. (The value `1.0` is the [just-noticeable difference] for [CIE94].). Otherwise, proceed to next step. 1. If all the 8 neighbors of the first pixel match its color, or if the 8 neighbors of the second pixel match its color, then these pixels are **DIFFERENT**. (In case of anti-aliasing, some of the direct neighbors have to be blended up or down.) Otherwise, proceed to next step. 1. If SSIM in some locality around the different pixels is more than 0.99, then consider this pixels to be **EQUAL**. Otherwise, mark them as **DIFFERENT**. (Local SSIM for anti-aliased pixels turns out to be very close to 1.0). [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94 [just-noticeable difference]: https://en.wikipedia.org/wiki/Just-noticeable_difference
2022-12-03 02:22:05 +03:00
test('should respect comparator name', async ({ runInlineTest }) => {
const expected = fs.readFileSync(path.join(__dirname, '../image_tools/fixtures/should-match/tiny-antialiasing-sample/tiny-expected.png'));
const actualURL = pathToFileURL(path.join(__dirname, '../image_tools/fixtures/should-match/tiny-antialiasing-sample/tiny-actual.png'));
const result = await runInlineTest({
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
}),
'__screenshots__/a.spec.js/snapshot.png': expected,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('should pass', async ({ page }) => {
feat: implement a new image comparison function (#19166) This patch implements a new image comparison function, codenamed "ssim-cie94". The goal of the new comparison function is to cancel out browser non-determenistic rendering. To use the new comparison function: ```ts await expect(page).toHaveScreenshot({ comparator: 'ssim-cie94', }); ``` As of Nov 30, 2022, we identified the following sources of non-determenistic rendering for Chromium: - Anti-aliasing for certain shapes might be different due to the way skia rasterizes certain shapes. - Color blending might be different on `x86` and `aarch64` architectures. The new function employs a few heuristics to fight these differences. Consider two non-equal image pixels `(r1, g1, b1)` and `(r2, g2, b2)`: 1. If the [CIE94] metric is less then 1.0, then we consider these pixels **EQUAL**. (The value `1.0` is the [just-noticeable difference] for [CIE94].). Otherwise, proceed to next step. 1. If all the 8 neighbors of the first pixel match its color, or if the 8 neighbors of the second pixel match its color, then these pixels are **DIFFERENT**. (In case of anti-aliasing, some of the direct neighbors have to be blended up or down.) Otherwise, proceed to next step. 1. If SSIM in some locality around the different pixels is more than 0.99, then consider this pixels to be **EQUAL**. Otherwise, mark them as **DIFFERENT**. (Local SSIM for anti-aliased pixels turns out to be very close to 1.0). [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94 [just-noticeable difference]: https://en.wikipedia.org/wiki/Just-noticeable_difference
2022-12-03 02:22:05 +03:00
await page.goto('${actualURL}');
await expect(page.locator('img')).toHaveScreenshot('snapshot.png', {
threshold: 0,
_comparator: 'ssim-cie94',
feat: implement a new image comparison function (#19166) This patch implements a new image comparison function, codenamed "ssim-cie94". The goal of the new comparison function is to cancel out browser non-determenistic rendering. To use the new comparison function: ```ts await expect(page).toHaveScreenshot({ comparator: 'ssim-cie94', }); ``` As of Nov 30, 2022, we identified the following sources of non-determenistic rendering for Chromium: - Anti-aliasing for certain shapes might be different due to the way skia rasterizes certain shapes. - Color blending might be different on `x86` and `aarch64` architectures. The new function employs a few heuristics to fight these differences. Consider two non-equal image pixels `(r1, g1, b1)` and `(r2, g2, b2)`: 1. If the [CIE94] metric is less then 1.0, then we consider these pixels **EQUAL**. (The value `1.0` is the [just-noticeable difference] for [CIE94].). Otherwise, proceed to next step. 1. If all the 8 neighbors of the first pixel match its color, or if the 8 neighbors of the second pixel match its color, then these pixels are **DIFFERENT**. (In case of anti-aliasing, some of the direct neighbors have to be blended up or down.) Otherwise, proceed to next step. 1. If SSIM in some locality around the different pixels is more than 0.99, then consider this pixels to be **EQUAL**. Otherwise, mark them as **DIFFERENT**. (Local SSIM for anti-aliased pixels turns out to be very close to 1.0). [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94 [just-noticeable difference]: https://en.wikipedia.org/wiki/Just-noticeable_difference
2022-12-03 02:22:05 +03:00
});
});
test('should fail', async ({ page }) => {
feat: implement a new image comparison function (#19166) This patch implements a new image comparison function, codenamed "ssim-cie94". The goal of the new comparison function is to cancel out browser non-determenistic rendering. To use the new comparison function: ```ts await expect(page).toHaveScreenshot({ comparator: 'ssim-cie94', }); ``` As of Nov 30, 2022, we identified the following sources of non-determenistic rendering for Chromium: - Anti-aliasing for certain shapes might be different due to the way skia rasterizes certain shapes. - Color blending might be different on `x86` and `aarch64` architectures. The new function employs a few heuristics to fight these differences. Consider two non-equal image pixels `(r1, g1, b1)` and `(r2, g2, b2)`: 1. If the [CIE94] metric is less then 1.0, then we consider these pixels **EQUAL**. (The value `1.0` is the [just-noticeable difference] for [CIE94].). Otherwise, proceed to next step. 1. If all the 8 neighbors of the first pixel match its color, or if the 8 neighbors of the second pixel match its color, then these pixels are **DIFFERENT**. (In case of anti-aliasing, some of the direct neighbors have to be blended up or down.) Otherwise, proceed to next step. 1. If SSIM in some locality around the different pixels is more than 0.99, then consider this pixels to be **EQUAL**. Otherwise, mark them as **DIFFERENT**. (Local SSIM for anti-aliased pixels turns out to be very close to 1.0). [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94 [just-noticeable difference]: https://en.wikipedia.org/wiki/Just-noticeable_difference
2022-12-03 02:22:05 +03:00
await page.goto('${actualURL}');
await expect(page.locator('img')).toHaveScreenshot('snapshot.png', {
threshold: 0,
_comparator: 'pixelmatch',
feat: implement a new image comparison function (#19166) This patch implements a new image comparison function, codenamed "ssim-cie94". The goal of the new comparison function is to cancel out browser non-determenistic rendering. To use the new comparison function: ```ts await expect(page).toHaveScreenshot({ comparator: 'ssim-cie94', }); ``` As of Nov 30, 2022, we identified the following sources of non-determenistic rendering for Chromium: - Anti-aliasing for certain shapes might be different due to the way skia rasterizes certain shapes. - Color blending might be different on `x86` and `aarch64` architectures. The new function employs a few heuristics to fight these differences. Consider two non-equal image pixels `(r1, g1, b1)` and `(r2, g2, b2)`: 1. If the [CIE94] metric is less then 1.0, then we consider these pixels **EQUAL**. (The value `1.0` is the [just-noticeable difference] for [CIE94].). Otherwise, proceed to next step. 1. If all the 8 neighbors of the first pixel match its color, or if the 8 neighbors of the second pixel match its color, then these pixels are **DIFFERENT**. (In case of anti-aliasing, some of the direct neighbors have to be blended up or down.) Otherwise, proceed to next step. 1. If SSIM in some locality around the different pixels is more than 0.99, then consider this pixels to be **EQUAL**. Otherwise, mark them as **DIFFERENT**. (Local SSIM for anti-aliased pixels turns out to be very close to 1.0). [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94 [just-noticeable difference]: https://en.wikipedia.org/wiki/Just-noticeable_difference
2022-12-03 02:22:05 +03:00
});
});
`
});
expect(result.exitCode).toBe(1);
expect(result.report.suites[0].specs[0].title).toBe('should pass');
expect(result.report.suites[0].specs[0].ok).toBe(true);
expect(result.report.suites[0].specs[1].title).toBe('should fail');
expect(result.report.suites[0].specs[1].ok).toBe(false);
});
test('should respect comparator in config', async ({ runInlineTest }) => {
const expected = fs.readFileSync(path.join(__dirname, '../image_tools/fixtures/should-match/tiny-antialiasing-sample/tiny-expected.png'));
const actualURL = pathToFileURL(path.join(__dirname, '../image_tools/fixtures/should-match/tiny-antialiasing-sample/tiny-actual.png'));
const result = await runInlineTest({
...playwrightConfig({
snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}',
projects: [
{
name: 'should-pass',
expect: {
toHaveScreenshot: {
_comparator: 'ssim-cie94',
feat: implement a new image comparison function (#19166) This patch implements a new image comparison function, codenamed "ssim-cie94". The goal of the new comparison function is to cancel out browser non-determenistic rendering. To use the new comparison function: ```ts await expect(page).toHaveScreenshot({ comparator: 'ssim-cie94', }); ``` As of Nov 30, 2022, we identified the following sources of non-determenistic rendering for Chromium: - Anti-aliasing for certain shapes might be different due to the way skia rasterizes certain shapes. - Color blending might be different on `x86` and `aarch64` architectures. The new function employs a few heuristics to fight these differences. Consider two non-equal image pixels `(r1, g1, b1)` and `(r2, g2, b2)`: 1. If the [CIE94] metric is less then 1.0, then we consider these pixels **EQUAL**. (The value `1.0` is the [just-noticeable difference] for [CIE94].). Otherwise, proceed to next step. 1. If all the 8 neighbors of the first pixel match its color, or if the 8 neighbors of the second pixel match its color, then these pixels are **DIFFERENT**. (In case of anti-aliasing, some of the direct neighbors have to be blended up or down.) Otherwise, proceed to next step. 1. If SSIM in some locality around the different pixels is more than 0.99, then consider this pixels to be **EQUAL**. Otherwise, mark them as **DIFFERENT**. (Local SSIM for anti-aliased pixels turns out to be very close to 1.0). [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94 [just-noticeable difference]: https://en.wikipedia.org/wiki/Just-noticeable_difference
2022-12-03 02:22:05 +03:00
}
},
},
{
name: 'should-fail',
expect: {
toHaveScreenshot: {
_comparator: 'pixelmatch',
feat: implement a new image comparison function (#19166) This patch implements a new image comparison function, codenamed "ssim-cie94". The goal of the new comparison function is to cancel out browser non-determenistic rendering. To use the new comparison function: ```ts await expect(page).toHaveScreenshot({ comparator: 'ssim-cie94', }); ``` As of Nov 30, 2022, we identified the following sources of non-determenistic rendering for Chromium: - Anti-aliasing for certain shapes might be different due to the way skia rasterizes certain shapes. - Color blending might be different on `x86` and `aarch64` architectures. The new function employs a few heuristics to fight these differences. Consider two non-equal image pixels `(r1, g1, b1)` and `(r2, g2, b2)`: 1. If the [CIE94] metric is less then 1.0, then we consider these pixels **EQUAL**. (The value `1.0` is the [just-noticeable difference] for [CIE94].). Otherwise, proceed to next step. 1. If all the 8 neighbors of the first pixel match its color, or if the 8 neighbors of the second pixel match its color, then these pixels are **DIFFERENT**. (In case of anti-aliasing, some of the direct neighbors have to be blended up or down.) Otherwise, proceed to next step. 1. If SSIM in some locality around the different pixels is more than 0.99, then consider this pixels to be **EQUAL**. Otherwise, mark them as **DIFFERENT**. (Local SSIM for anti-aliased pixels turns out to be very close to 1.0). [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94 [just-noticeable difference]: https://en.wikipedia.org/wiki/Just-noticeable_difference
2022-12-03 02:22:05 +03:00
}
},
},
],
}),
'__screenshots__/a.spec.js/snapshot.png': expected,
'a.spec.js': `
const { test, expect } = require('@playwright/test');
test('test', async ({ page }) => {
feat: implement a new image comparison function (#19166) This patch implements a new image comparison function, codenamed "ssim-cie94". The goal of the new comparison function is to cancel out browser non-determenistic rendering. To use the new comparison function: ```ts await expect(page).toHaveScreenshot({ comparator: 'ssim-cie94', }); ``` As of Nov 30, 2022, we identified the following sources of non-determenistic rendering for Chromium: - Anti-aliasing for certain shapes might be different due to the way skia rasterizes certain shapes. - Color blending might be different on `x86` and `aarch64` architectures. The new function employs a few heuristics to fight these differences. Consider two non-equal image pixels `(r1, g1, b1)` and `(r2, g2, b2)`: 1. If the [CIE94] metric is less then 1.0, then we consider these pixels **EQUAL**. (The value `1.0` is the [just-noticeable difference] for [CIE94].). Otherwise, proceed to next step. 1. If all the 8 neighbors of the first pixel match its color, or if the 8 neighbors of the second pixel match its color, then these pixels are **DIFFERENT**. (In case of anti-aliasing, some of the direct neighbors have to be blended up or down.) Otherwise, proceed to next step. 1. If SSIM in some locality around the different pixels is more than 0.99, then consider this pixels to be **EQUAL**. Otherwise, mark them as **DIFFERENT**. (Local SSIM for anti-aliased pixels turns out to be very close to 1.0). [CIE94]: https://en.wikipedia.org/wiki/Color_difference#CIE94 [just-noticeable difference]: https://en.wikipedia.org/wiki/Just-noticeable_difference
2022-12-03 02:22:05 +03:00
await page.goto('${actualURL}');
await expect(page.locator('img')).toHaveScreenshot('snapshot.png', { threshold: 0, });
});
`
});
expect(result.exitCode).toBe(1);
expect(result.report.suites[0].specs[0].tests[0].projectName).toBe('should-pass');
expect(result.report.suites[0].specs[0].tests[0].status).toBe('expected');
expect(result.report.suites[0].specs[0].tests[1].projectName).toBe('should-fail');
expect(result.report.suites[0].specs[0].tests[1].status).toBe('unexpected');
});
function playwrightConfig(obj: any) {
return {
'playwright.config.js': `
module.exports = ${JSON.stringify(obj, null, 2)}
`,
};
}