fix(toHaveScreenshot): attach "expected" when writing a missing expectation (#31745)

Previously, only the "actual" attachment was created, pointing to the
file in `test-results`. Now, the "expected" attachment pointing to the
file in `__screenshots__` is also created. This will help any reporters
that would like to know the "expected" path, for example to do a manual
accept/decline of the baseline.

Fixes #30693.
This commit is contained in:
Dmitry Gozman 2024-07-18 02:42:44 -07:00 committed by GitHub
parent 6491e5b415
commit 056997c41f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 5 deletions

View File

@ -222,11 +222,11 @@ class SnapshotHelper {
handleMissing(actual: Buffer | string): ImageMatcherResult { handleMissing(actual: Buffer | string): ImageMatcherResult {
const isWriteMissingMode = this.updateSnapshots === 'all' || this.updateSnapshots === 'missing'; const isWriteMissingMode = this.updateSnapshots === 'all' || this.updateSnapshots === 'missing';
if (isWriteMissingMode) { if (isWriteMissingMode)
writeFileSync(this.expectedPath, actual); writeFileSync(this.expectedPath, actual);
writeFileSync(this.actualPath, actual); this.testInfo.attachments.push({ name: addSuffixToFilePath(this.attachmentBaseName, '-expected'), contentType: this.mimeType, path: this.expectedPath });
this.testInfo.attachments.push({ name: addSuffixToFilePath(this.attachmentBaseName, '-actual'), contentType: this.mimeType, path: this.actualPath }); writeFileSync(this.actualPath, actual);
} this.testInfo.attachments.push({ name: addSuffixToFilePath(this.attachmentBaseName, '-actual'), contentType: this.mimeType, path: this.actualPath });
const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', writing actual.' : '.'}`; const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', writing actual.' : '.'}`;
if (this.updateSnapshots === 'all') { if (this.updateSnapshots === 'all') {
/* eslint-disable no-console */ /* eslint-disable no-console */

View File

@ -263,8 +263,10 @@ test('should report toHaveScreenshot step with expectation name in title', async
`end browserContext.newPage`, `end browserContext.newPage`,
`end fixture: page`, `end fixture: page`,
`end Before Hooks`, `end Before Hooks`,
`end attach "foo-expected.png"`,
`end attach "foo-actual.png"`, `end attach "foo-actual.png"`,
`end expect.toHaveScreenshot(foo.png)`, `end expect.toHaveScreenshot(foo.png)`,
`end attach "is-a-test-1-expected.png"`,
`end attach "is-a-test-1-actual.png"`, `end attach "is-a-test-1-actual.png"`,
`end expect.toHaveScreenshot(is-a-test-1.png)`, `end expect.toHaveScreenshot(is-a-test-1.png)`,
`end fixture: page`, `end fixture: page`,
@ -654,13 +656,23 @@ test('should write missing expectations locally twice and attach them', async ({
const attachments = result.outputLines.map(l => JSON.parse(l))[0]; const attachments = result.outputLines.map(l => JSON.parse(l))[0];
for (const attachment of attachments) for (const attachment of attachments)
attachment.path = attachment.path.replace(/\\/g, '/').replace(/.*test-results\//, ''); attachment.path = attachment.path.replace(/\\/g, '/').replace(/.*test-results\//, '').replace(/.*__screenshots__/, '__screenshots__');
expect(attachments).toEqual([ expect(attachments).toEqual([
{
name: 'snapshot-expected.png',
contentType: 'image/png',
path: '__screenshots__/a.spec.js/snapshot.png'
},
{ {
name: 'snapshot-actual.png', name: 'snapshot-actual.png',
contentType: 'image/png', contentType: 'image/png',
path: 'a-is-a-test/snapshot-actual.png' path: 'a-is-a-test/snapshot-actual.png'
}, },
{
name: 'snapshot2-expected.png',
contentType: 'image/png',
path: '__screenshots__/a.spec.js/snapshot2.png'
},
{ {
name: 'snapshot2-actual.png', name: 'snapshot2-actual.png',
contentType: 'image/png', contentType: 'image/png',
@ -1348,16 +1360,31 @@ test('should trim+sanitize attachment names and paths', async ({ runInlineTest }
attachment.name = attachment.name.replace(/\\/g, '/'); attachment.name = attachment.name.replace(/\\/g, '/');
} }
expect(attachments).toEqual([ expect(attachments).toEqual([
{
name: 'long-long-long-long-long-l-852e1-long-long-long-long-title-1-expected.png',
contentType: 'image/png',
path: '__screenshots__/a.spec.js/long-long-long-long-long-long-long-long-long-l-852e1-long-long-long-long-long-long-long-long-title-1.png',
},
{ {
name: 'long-long-long-long-long-l-852e1-long-long-long-long-title-1-actual.png', name: 'long-long-long-long-long-l-852e1-long-long-long-long-title-1-actual.png',
contentType: 'image/png', contentType: 'image/png',
path: 'test-results/a-long-long-long-long-long-abd51-g-long-long-long-long-title/long-long-long-long-long-l-852e1-long-long-long-long-title-1-actual.png', path: 'test-results/a-long-long-long-long-long-abd51-g-long-long-long-long-title/long-long-long-long-long-l-852e1-long-long-long-long-title-1-actual.png',
}, },
{
name: 'long-long-long-long-long-l-6bf1e-ong-long-long-long-name-expected.png',
contentType: 'image/png',
path: '__screenshots__/a.spec.js/long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-long-name.png',
},
{ {
name: 'long-long-long-long-long-l-6bf1e-ong-long-long-long-name-actual.png', name: 'long-long-long-long-long-l-6bf1e-ong-long-long-long-name-actual.png',
contentType: 'image/png', contentType: 'image/png',
path: 'test-results/a-long-long-long-long-long-abd51-g-long-long-long-long-title/long-long-long-long-long-l-6bf1e-ong-long-long-long-name-actual.png', path: 'test-results/a-long-long-long-long-long-abd51-g-long-long-long-long-title/long-long-long-long-long-l-6bf1e-ong-long-long-long-name-actual.png',
}, },
{
name: 'dir/long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long name-expected.png',
contentType: 'image/png',
path: '__screenshots__/a.spec.js/dir/long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long name.png',
},
{ {
name: 'dir/long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long name-actual.png', name: 'dir/long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long name-actual.png',
contentType: 'image/png', contentType: 'image/png',

View File

@ -94,6 +94,7 @@ test('should merge screenshot assertions', async ({ runUITest }, testInfo) => {
/Before Hooks[\d.]+m?s/, /Before Hooks[\d.]+m?s/,
/page.setContent[\d.]+m?s/, /page.setContent[\d.]+m?s/,
/expect.toHaveScreenshot[\d.]+m?s/, /expect.toHaveScreenshot[\d.]+m?s/,
/attach "trace-test-1-expected.png/,
/attach "trace-test-1-actual.png/, /attach "trace-test-1-actual.png/,
/After Hooks[\d.]+m?s/, /After Hooks[\d.]+m?s/,
/Worker Cleanup[\d.]+m?s/, /Worker Cleanup[\d.]+m?s/,