chore: recommend double ext for file based snapshot matcher
This commit is contained in:
parent
a9bbf4b56d
commit
b8b68cdad1
|
|
@ -2263,13 +2263,13 @@ assertThat(page.locator("body")).matchesAriaSnapshot("""
|
|||
|
||||
Asserts that the target element matches the given [accessibility snapshot](../aria-snapshots.md).
|
||||
|
||||
Snapshot is stored in a separate `.yml` file in a location configured by `expect.toMatchAriaSnapshot.pathTemplate` and/or `snapshotPathTemplate` properties in the configuration file.
|
||||
Snapshot is stored in a separate `.snapshot.yml` file in a location configured by `expect.toMatchAriaSnapshot.pathTemplate` and/or `snapshotPathTemplate` properties in the configuration file.
|
||||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot();
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'body.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'body.snapshot.yml' });
|
||||
```
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#2.name
|
||||
|
|
|
|||
|
|
@ -339,10 +339,10 @@ npx playwright test --update-snapshots --update-source-mode=3way
|
|||
|
||||
#### Snapshots as separate files
|
||||
|
||||
To store your snapshots in a separate file, use the `toMatchAriaSnapshot` method with the `name` option, specifying a `.yml` file extension.
|
||||
To store your snapshots in a separate file, use the `toMatchAriaSnapshot` method with the `name` option, specifying a `.snapshot.yml` file extension.
|
||||
|
||||
```js
|
||||
await expect(page.getByRole('main')).toMatchAriaSnapshot({ name: 'main-snapshot.yml' });
|
||||
await expect(page.getByRole('main')).toMatchAriaSnapshot({ name: 'main-snapshot.snapshot.yml' });
|
||||
```
|
||||
|
||||
By default, snapshots from a test file `example.spec.ts` are placed in the `example.spec.ts-snapshots` directory. As snapshots should be the same across browsers, only one snapshot is saved even if testing with multiple browsers. Should you wish, you can customize the [snapshot path template](./api/class-testconfig#test-config-snapshot-path-template) using the following configuration:
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export async function toMatchAriaSnapshot(
|
|||
(testInfo as any)[snapshotNamesSymbol] = snapshotNames;
|
||||
}
|
||||
const fullTitleWithoutSpec = [...testInfo.titlePath.slice(1), ++snapshotNames.anonymousSnapshotIndex].join(' ');
|
||||
expectedPath = testInfo._resolveSnapshotPath(pathTemplate, defaultTemplate, [sanitizeForFilePath(trimLongString(fullTitleWithoutSpec)) + '.yml']);
|
||||
expectedPath = testInfo._resolveSnapshotPath(pathTemplate, defaultTemplate, [sanitizeForFilePath(trimLongString(fullTitleWithoutSpec)) + '.snapshot.yml']);
|
||||
}
|
||||
expected = await fs.promises.readFile(expectedPath, 'utf8').catch(() => '');
|
||||
timeout = expectedParam?.timeout ?? this.timeout;
|
||||
|
|
|
|||
|
|
@ -200,14 +200,37 @@ export function trimLongString(s: string, length = 100) {
|
|||
return s.substring(0, start) + middle + s.slice(-end);
|
||||
}
|
||||
|
||||
function findNthFromEnd(string: string, searchString: string, n: number) {
|
||||
let i = string.length;
|
||||
while (n--) {
|
||||
i = string.lastIndexOf(searchString, i - 1);
|
||||
if (i === -1)
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
function multiExtname(filePath: string, maximum = 2): string {
|
||||
const basename = path.basename(filePath);
|
||||
const startOfExtension = findNthFromEnd(basename, '.', maximum);
|
||||
return basename.substring(startOfExtension);
|
||||
}
|
||||
|
||||
export function parsePathMultiExt(filePath: string, maximum = 2) {
|
||||
const startOfExtension = findNthFromEnd(filePath, '.', maximum);
|
||||
const result = path.parse(filePath.substring(0, startOfExtension) + '.ext');
|
||||
result.ext = filePath.substring(startOfExtension);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function addSuffixToFilePath(filePath: string, suffix: string): string {
|
||||
const ext = path.extname(filePath);
|
||||
const ext = multiExtname(filePath);
|
||||
const base = filePath.substring(0, filePath.length - ext.length);
|
||||
return base + suffix + ext;
|
||||
}
|
||||
|
||||
export function sanitizeFilePathBeforeExtension(filePath: string): string {
|
||||
const ext = path.extname(filePath);
|
||||
const ext = multiExtname(filePath);
|
||||
const base = filePath.substring(0, filePath.length - ext.length);
|
||||
return sanitizeForFilePath(base) + ext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import path from 'path';
|
|||
import { captureRawStack, monotonicTime, sanitizeForFilePath, stringifyStackFrames, currentZone } from 'playwright-core/lib/utils';
|
||||
|
||||
import { TimeoutManager, TimeoutManagerError, kMaxDeadline } from './timeoutManager';
|
||||
import { debugTest, filteredStackTrace, formatLocation, getContainedPath, normalizeAndSaveAttachment, trimLongString, windowsFilesystemFriendlyLength } from '../util';
|
||||
import { debugTest, filteredStackTrace, formatLocation, getContainedPath, normalizeAndSaveAttachment, trimLongString, windowsFilesystemFriendlyLength, parsePathMultiExt } from '../util';
|
||||
import { TestTracing } from './testTracing';
|
||||
import { testInfoError } from './util';
|
||||
import { FloatingPromiseScope } from './floatingPromiseScope';
|
||||
|
|
@ -465,7 +465,7 @@ export class TestInfoImpl implements TestInfo {
|
|||
|
||||
_resolveSnapshotPath(template: string | undefined, defaultTemplate: string, pathSegments: string[]) {
|
||||
const subPath = path.join(...pathSegments);
|
||||
const parsedSubPath = path.parse(subPath);
|
||||
const parsedSubPath = parsePathMultiExt(subPath);
|
||||
const relativeTestFilePath = path.relative(this.project.testDir, this._requireFile);
|
||||
const parsedRelativeTestFilePath = path.parse(relativeTestFilePath);
|
||||
const projectNamePathSegment = sanitizeForFilePath(this.project.name);
|
||||
|
|
|
|||
6
packages/playwright/types/test.d.ts
vendored
6
packages/playwright/types/test.d.ts
vendored
|
|
@ -8815,14 +8815,14 @@ interface LocatorAssertions {
|
|||
/**
|
||||
* Asserts that the target element matches the given [accessibility snapshot](https://playwright.dev/docs/aria-snapshots).
|
||||
*
|
||||
* Snapshot is stored in a separate `.yml` file in a location configured by `expect.toMatchAriaSnapshot.pathTemplate`
|
||||
* and/or `snapshotPathTemplate` properties in the configuration file.
|
||||
* Snapshot is stored in a separate `.snapshot.yml` file in a location configured by
|
||||
* `expect.toMatchAriaSnapshot.pathTemplate` and/or `snapshotPathTemplate` properties in the configuration file.
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```js
|
||||
* await expect(page.locator('body')).toMatchAriaSnapshot();
|
||||
* await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'body.yml' });
|
||||
* await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'body.snapshot.yml' });
|
||||
* ```
|
||||
*
|
||||
* @param options
|
||||
|
|
|
|||
|
|
@ -18,18 +18,20 @@ import { test, expect } from './playwright-test-fixtures';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
// TODO: this is where to change some tests
|
||||
|
||||
test.describe.configure({ mode: 'parallel' });
|
||||
|
||||
test('should match snapshot with name', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'a.spec.ts-snapshots/test.yml': `
|
||||
'a.spec.ts-snapshots/test.snapshot.yml': `
|
||||
- heading "hello world"
|
||||
`,
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
await page.setContent(\`<h1>hello world</h1>\`);
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test.snapshot.yml' });
|
||||
});
|
||||
`
|
||||
});
|
||||
|
|
@ -43,66 +45,66 @@ test('should generate multiple missing', async ({ runInlineTest }, testInfo) =>
|
|||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
await page.setContent(\`<h1>hello world</h1>\`);
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test-1.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test-1.snapshot.yml' });
|
||||
await page.setContent(\`<h1>hello world 2</h1>\`);
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test-2.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test-2.snapshot.yml' });
|
||||
});
|
||||
`
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.output).toContain(`A snapshot doesn't exist at a.spec.ts-snapshots${path.sep}test-1.yml, writing actual`);
|
||||
expect(result.output).toContain(`A snapshot doesn't exist at a.spec.ts-snapshots${path.sep}test-2.yml, writing actual`);
|
||||
const snapshot1 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-1.yml'), 'utf8');
|
||||
expect(result.output).toContain(`A snapshot doesn't exist at a.spec.ts-snapshots${path.sep}test-1.snapshot.yml, writing actual`);
|
||||
expect(result.output).toContain(`A snapshot doesn't exist at a.spec.ts-snapshots${path.sep}test-2.snapshot.yml, writing actual`);
|
||||
const snapshot1 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-1.snapshot.yml'), 'utf8');
|
||||
expect(snapshot1).toBe('- heading "hello world" [level=1]');
|
||||
const snapshot2 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-2.yml'), 'utf8');
|
||||
const snapshot2 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-2.snapshot.yml'), 'utf8');
|
||||
expect(snapshot2).toBe('- heading "hello world 2" [level=1]');
|
||||
});
|
||||
|
||||
test('should rebaseline all', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'a.spec.ts-snapshots/test-1.yml': `
|
||||
'a.spec.ts-snapshots/test-1.snapshot.yml': `
|
||||
- heading "foo"
|
||||
`,
|
||||
'a.spec.ts-snapshots/test-2.yml': `
|
||||
'a.spec.ts-snapshots/test-2.snapshot.yml': `
|
||||
- heading "bar"
|
||||
`,
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
await page.setContent(\`<h1>hello world</h1>\`);
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test-1.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test-1.snapshot.yml' });
|
||||
await page.setContent(\`<h1>hello world 2</h1>\`);
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test-2.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test-2.snapshot.yml' });
|
||||
});
|
||||
`
|
||||
}, { 'update-snapshots': 'all' });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.output).toContain(`A snapshot is generated at a.spec.ts-snapshots${path.sep}test-1.yml`);
|
||||
expect(result.output).toContain(`A snapshot is generated at a.spec.ts-snapshots${path.sep}test-2.yml`);
|
||||
const snapshot1 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-1.yml'), 'utf8');
|
||||
expect(result.output).toContain(`A snapshot is generated at a.spec.ts-snapshots${path.sep}test-1.snapshot.yml`);
|
||||
expect(result.output).toContain(`A snapshot is generated at a.spec.ts-snapshots${path.sep}test-2.snapshot.yml`);
|
||||
const snapshot1 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-1.snapshot.yml'), 'utf8');
|
||||
expect(snapshot1).toBe('- heading "hello world" [level=1]');
|
||||
const snapshot2 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-2.yml'), 'utf8');
|
||||
const snapshot2 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-2.snapshot.yml'), 'utf8');
|
||||
expect(snapshot2).toBe('- heading "hello world 2" [level=1]');
|
||||
});
|
||||
|
||||
test('should not rebaseline matching', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'a.spec.ts-snapshots/test.yml': `
|
||||
'a.spec.ts-snapshots/test.snapshot.yml': `
|
||||
- heading "hello world"
|
||||
`,
|
||||
'a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
await page.setContent(\`<h1>hello world</h1>\`);
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test.snapshot.yml' });
|
||||
});
|
||||
`
|
||||
}, { 'update-snapshots': 'changed' });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
const snapshot1 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test.yml'), 'utf8');
|
||||
const snapshot1 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test.snapshot.yml'), 'utf8');
|
||||
expect(snapshot1.trim()).toBe('- heading "hello world"');
|
||||
});
|
||||
|
||||
|
|
@ -120,11 +122,11 @@ test('should generate snapshot name', async ({ runInlineTest }, testInfo) => {
|
|||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.output).toContain(`A snapshot doesn't exist at a.spec.ts-snapshots${path.sep}test-name-1.yml, writing actual`);
|
||||
expect(result.output).toContain(`A snapshot doesn't exist at a.spec.ts-snapshots${path.sep}test-name-2.yml, writing actual`);
|
||||
const snapshot1 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-name-1.yml'), 'utf8');
|
||||
expect(result.output).toContain(`A snapshot doesn't exist at a.spec.ts-snapshots${path.sep}test-name-1.snapshot.yml, writing actual`);
|
||||
expect(result.output).toContain(`A snapshot doesn't exist at a.spec.ts-snapshots${path.sep}test-name-2.snapshot.yml, writing actual`);
|
||||
const snapshot1 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-name-1.snapshot.yml'), 'utf8');
|
||||
expect(snapshot1).toBe('- heading "hello world" [level=1]');
|
||||
const snapshot2 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-name-2.yml'), 'utf8');
|
||||
const snapshot2 = await fs.promises.readFile(testInfo.outputPath('a.spec.ts-snapshots/test-name-2.snapshot.yml'), 'utf8');
|
||||
expect(snapshot2).toBe('- heading "hello world 2" [level=1]');
|
||||
});
|
||||
|
||||
|
|
@ -143,13 +145,13 @@ for (const updateSnapshots of ['all', 'changed', 'missing', 'none']) {
|
|||
await expect(page.locator('body')).toMatchAriaSnapshot({ timeout: 1 });
|
||||
});
|
||||
`,
|
||||
'a.spec.ts-snapshots/test-1.yml': '- heading "Old content" [level=1]',
|
||||
'a.spec.ts-snapshots/test-1.snapshot.yml': '- heading "Old content" [level=1]',
|
||||
});
|
||||
|
||||
const rebase = updateSnapshots === 'all' || updateSnapshots === 'changed';
|
||||
expect(result.exitCode).toBe(rebase ? 0 : 1);
|
||||
if (rebase) {
|
||||
const snapshotOutputPath = testInfo.outputPath('a.spec.ts-snapshots/test-1.yml');
|
||||
const snapshotOutputPath = testInfo.outputPath('a.spec.ts-snapshots/test-1.snapshot.yml');
|
||||
expect(result.output).toContain(`A snapshot is generated at`);
|
||||
const data = fs.readFileSync(snapshotOutputPath);
|
||||
expect(data.toString()).toBe('- heading "New content" [level=1]');
|
||||
|
|
@ -169,7 +171,7 @@ test('should respect timeout', async ({ runInlineTest }, testInfo) => {
|
|||
await expect(page.locator('body')).toMatchAriaSnapshot({ timeout: 1 });
|
||||
});
|
||||
`,
|
||||
'a.spec.ts-snapshots/test-1.yml': '- heading "new world" [level=1]',
|
||||
'a.spec.ts-snapshots/test-1.snapshot.yml': '- heading "new world" [level=1]',
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
|
|
@ -183,14 +185,14 @@ test('should respect config.snapshotPathTemplate', async ({ runInlineTest }, tes
|
|||
snapshotPathTemplate: 'my-snapshots/{testFilePath}/{arg}{ext}',
|
||||
};
|
||||
`,
|
||||
'my-snapshots/dir/a.spec.ts/test.yml': `
|
||||
'my-snapshots/dir/a.spec.ts/test.snapshot.yml': `
|
||||
- heading "hello world"
|
||||
`,
|
||||
'dir/a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
await page.setContent(\`<h1>hello world</h1>\`);
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test.snapshot.yml' });
|
||||
});
|
||||
`
|
||||
});
|
||||
|
|
@ -210,17 +212,17 @@ test('should respect config.expect.toMatchAriaSnapshot.pathTemplate', async ({ r
|
|||
},
|
||||
};
|
||||
`,
|
||||
'my-snapshots/dir/a.spec.ts/test.yml': `
|
||||
'my-snapshots/dir/a.spec.ts/test.snapshot.yml': `
|
||||
- heading "wrong one"
|
||||
`,
|
||||
'actual-snapshots/dir/a.spec.ts/test.yml': `
|
||||
'actual-snapshots/dir/a.spec.ts/test.snapshot.yml': `
|
||||
- heading "hello world"
|
||||
`,
|
||||
'dir/a.spec.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('test', async ({ page }) => {
|
||||
await page.setContent(\`<h1>hello world</h1>\`);
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test.yml' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'test.snapshot.yml' });
|
||||
});
|
||||
`
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue