feat: add snapshotDir to set base snapshot directory (#9260)
This commit is contained in:
parent
92c9e9a079
commit
a51ac39275
|
|
@ -15,6 +15,7 @@ These options define your test suite:
|
|||
- `metadata: any` - Any JSON-serializable metadata that will be put directly to the test report.
|
||||
- `name: string` - Project name, useful when defining multiple [test projects](#projects).
|
||||
- `outputDir: string` - Output directory for files created during the test run.
|
||||
- `snapshotDir: string` - Base output directory for snapshot files.
|
||||
- `repeatEach: number` - The number of times to repeat each test, useful for debugging flaky tests.
|
||||
- `retries: number` - The maximum number of retry attempts given to failed tests. If not specified, failing tests are not retried.
|
||||
- `testDir: string` - Directory that will be recursively scanned for test files.
|
||||
|
|
|
|||
|
|
@ -286,6 +286,15 @@ test('example test', async ({}, testInfo) => {
|
|||
});
|
||||
```
|
||||
|
||||
## property: TestConfig.snapshotDir
|
||||
- type: <[string]>
|
||||
|
||||
The base directory, relative to the config file, for snapshot files created with `toMatchSnapshot`. Defaults to [`property: TestConfig.testDir`].
|
||||
|
||||
The directory for each test can be accessed by [`property: TestInfo.snapshotDir`] and [`method: TestInfo.snapshotPath`].
|
||||
|
||||
This path will serve as the base directory for each test file snapshot directory. Setting `snapshotDir` to `'snapshots'`, the [`property: TestInfo.snapshotDir`] would resolve to `snapshots/a.spec.js-snapshots`.
|
||||
|
||||
## property: TestConfig.preserveOutput
|
||||
- type: <[PreserveOutput]<"always"|"never"|"failures-only">>
|
||||
|
||||
|
|
|
|||
|
|
@ -163,6 +163,11 @@ Test function as passed to `test(title, testFunction)`.
|
|||
|
||||
Line number where the currently running test is declared.
|
||||
|
||||
## property: TestInfo.snapshotDir
|
||||
- type: <[string]>
|
||||
|
||||
Absolute path to the snapshot output directory for this specific test. Each test suite gets its own directory so they cannot conflict.
|
||||
|
||||
## property: TestInfo.outputDir
|
||||
- type: <[string]>
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,15 @@ Any JSON-serializable metadata that will be put directly to the test report.
|
|||
|
||||
Project name is visible in the report and during test execution.
|
||||
|
||||
## property: TestProject.snapshotDir
|
||||
- type: <[string]>
|
||||
|
||||
The base directory, relative to the config file, for snapshot files created with `toMatchSnapshot`. Defaults to [`property: TestProject.testDir`].
|
||||
|
||||
The directory for each test can be accessed by [`property: TestInfo.snapshotDir`] and [`method: TestInfo.snapshotPath`].
|
||||
|
||||
This path will serve as the base directory for each test file snapshot directory. Setting `snapshotDir` to `'snapshots'`, the [`property: TestInfo.snapshotDir`] would resolve to `snapshots/a.spec.js-snapshots`.
|
||||
|
||||
## property: TestProject.outputDir
|
||||
- type: <[string]>
|
||||
|
||||
|
|
|
|||
|
|
@ -169,6 +169,9 @@ export class Loader {
|
|||
let outputDir = takeFirst(this._configOverrides.outputDir, projectConfig.outputDir, this._config.outputDir, path.resolve(process.cwd(), 'test-results'));
|
||||
if (!path.isAbsolute(outputDir))
|
||||
outputDir = path.resolve(configDir, outputDir);
|
||||
let snapshotDir = takeFirst(this._configOverrides.snapshotDir, projectConfig.snapshotDir, this._config.snapshotDir, testDir);
|
||||
if (!path.isAbsolute(snapshotDir))
|
||||
snapshotDir = path.resolve(configDir, snapshotDir);
|
||||
const fullProject: FullProject = {
|
||||
define: takeFirst(this._configOverrides.define, projectConfig.define, this._config.define, []),
|
||||
expect: takeFirst(this._configOverrides.expect, projectConfig.expect, this._config.expect, undefined),
|
||||
|
|
@ -178,6 +181,7 @@ export class Loader {
|
|||
metadata: takeFirst(this._configOverrides.metadata, projectConfig.metadata, this._config.metadata, undefined),
|
||||
name: takeFirst(this._configOverrides.name, projectConfig.name, this._config.name, ''),
|
||||
testDir,
|
||||
snapshotDir,
|
||||
testIgnore: takeFirst(this._configOverrides.testIgnore, projectConfig.testIgnore, this._config.testIgnore, []),
|
||||
testMatch: takeFirst(this._configOverrides.testMatch, projectConfig.testMatch, this._config.testMatch, '**/?(*.)@(spec|test).@(ts|js|mjs)'),
|
||||
timeout: takeFirst(this._configOverrides.timeout, projectConfig.timeout, this._config.timeout, 10000),
|
||||
|
|
|
|||
|
|
@ -241,6 +241,11 @@ export class WorkerRunner extends EventEmitter {
|
|||
return path.join(this._project.config.outputDir, testOutputDir);
|
||||
})();
|
||||
|
||||
const snapshotDir = (() => {
|
||||
const relativeTestFilePath = path.relative(this._project.config.testDir, test._requireFile);
|
||||
return path.join(this._project.config.snapshotDir, relativeTestFilePath + '-snapshots');
|
||||
})();
|
||||
|
||||
let testFinishedCallback = () => {};
|
||||
let lastStepId = 0;
|
||||
const testInfo: TestInfoImpl = {
|
||||
|
|
@ -266,13 +271,13 @@ export class WorkerRunner extends EventEmitter {
|
|||
timeout: this._project.config.timeout,
|
||||
snapshotSuffix: '',
|
||||
outputDir: baseOutputDir,
|
||||
snapshotDir,
|
||||
outputPath: (...pathSegments: string[]): string => {
|
||||
fs.mkdirSync(baseOutputDir, { recursive: true });
|
||||
const joinedPath = path.join(...pathSegments);
|
||||
const outputPath = getContainedPath(baseOutputDir, joinedPath);
|
||||
if (outputPath) return outputPath;
|
||||
throw new Error(`The outputPath is not allowed outside of the parent directory. Please fix the defined path.\n\n\toutputPath: ${joinedPath}`);
|
||||
|
||||
},
|
||||
snapshotPath: (...pathSegments: string[]): string => {
|
||||
let suffix = '';
|
||||
|
|
@ -280,11 +285,8 @@ export class WorkerRunner extends EventEmitter {
|
|||
suffix += '-' + this._projectNamePathSegment;
|
||||
if (testInfo.snapshotSuffix)
|
||||
suffix += '-' + testInfo.snapshotSuffix;
|
||||
|
||||
const baseSnapshotPath = test._requireFile + '-snapshots';
|
||||
const subPath = addSuffixToFilePath(path.join(...pathSegments), suffix);
|
||||
const snapshotPath = getContainedPath(baseSnapshotPath, subPath);
|
||||
|
||||
const snapshotPath = getContainedPath(snapshotDir, subPath);
|
||||
if (snapshotPath) return snapshotPath;
|
||||
throw new Error(`The snapshotPath is not allowed outside of the parent directory. Please fix the defined path.\n\n\tsnapshotPath: ${subPath}`);
|
||||
},
|
||||
|
|
|
|||
31
packages/playwright-test/types/test.d.ts
vendored
31
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -123,6 +123,19 @@ interface TestProject {
|
|||
* Project name is visible in the report and during test execution.
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* The base directory, relative to the config file, for snapshot files created with `toMatchSnapshot`. Defaults to
|
||||
* [testProject.testDir](https://playwright.dev/docs/api/class-testproject#test-project-test-dir).
|
||||
*
|
||||
* The directory for each test can be accessed by
|
||||
* [testInfo.snapshotDir](https://playwright.dev/docs/api/class-testinfo#test-info-snapshot-dir) and
|
||||
* [testInfo.snapshotPath(pathSegments)](https://playwright.dev/docs/api/class-testinfo#test-info-snapshot-path).
|
||||
*
|
||||
* This path will serve as the base directory for each test file snapshot directory. Setting `snapshotDir` to
|
||||
* `'snapshots'`, the [testInfo.snapshotDir](https://playwright.dev/docs/api/class-testinfo#test-info-snapshot-dir) would
|
||||
* resolve to `snapshots/a.spec.js-snapshots`.
|
||||
*/
|
||||
snapshotDir?: string;
|
||||
/**
|
||||
* The output directory for files created during test execution. Defaults to `test-results`.
|
||||
*
|
||||
|
|
@ -603,6 +616,19 @@ interface TestConfig {
|
|||
*/
|
||||
metadata?: any;
|
||||
name?: string;
|
||||
/**
|
||||
* The base directory, relative to the config file, for snapshot files created with `toMatchSnapshot`. Defaults to
|
||||
* [testConfig.testDir](https://playwright.dev/docs/api/class-testconfig#test-config-test-dir).
|
||||
*
|
||||
* The directory for each test can be accessed by
|
||||
* [testInfo.snapshotDir](https://playwright.dev/docs/api/class-testinfo#test-info-snapshot-dir) and
|
||||
* [testInfo.snapshotPath(pathSegments)](https://playwright.dev/docs/api/class-testinfo#test-info-snapshot-path).
|
||||
*
|
||||
* This path will serve as the base directory for each test file snapshot directory. Setting `snapshotDir` to
|
||||
* `'snapshots'`, the [testInfo.snapshotDir](https://playwright.dev/docs/api/class-testinfo#test-info-snapshot-dir) would
|
||||
* resolve to `snapshots/a.spec.js-snapshots`.
|
||||
*/
|
||||
snapshotDir?: string;
|
||||
/**
|
||||
* The output directory for files created during test execution. Defaults to `test-results`.
|
||||
*
|
||||
|
|
@ -1342,6 +1368,11 @@ export interface TestInfo {
|
|||
* [snapshots](https://playwright.dev/docs/test-snapshots).
|
||||
*/
|
||||
snapshotSuffix: string;
|
||||
/**
|
||||
* Absolute path to the snapshot output directory for this specific test. Each test suite gets its own directory so they
|
||||
* cannot conflict.
|
||||
*/
|
||||
snapshotDir: string;
|
||||
/**
|
||||
* Absolute path to the output directory for this specific test run. Each test run gets its own directory so they cannot
|
||||
* conflict.
|
||||
|
|
|
|||
|
|
@ -493,7 +493,96 @@ test('should join array of snapshot path segments without sanitizing ', async ({
|
|||
...files,
|
||||
'a.spec.js-snapshots/test/path/snapshot.txt': `Hello world`,
|
||||
'a.spec.js': `
|
||||
const { test } = require('./helper');;
|
||||
const { test } = require('./helper');
|
||||
test('is a test', ({}) => {
|
||||
expect('Hello world').toMatchSnapshot(['test', 'path', 'snapshot.txt']);
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should use snapshotDir as snapshot base directory', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
...files,
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
snapshotDir: 'snaps',
|
||||
};
|
||||
`,
|
||||
'snaps/a.spec.js-snapshots/snapshot.txt': `Hello world`,
|
||||
'a.spec.js': `
|
||||
const { test } = require('./helper');
|
||||
test('is a test', ({}) => {
|
||||
expect('Hello world').toMatchSnapshot('snapshot.txt');
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should use snapshotDir with path segments as snapshot directory', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
...files,
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
snapshotDir: 'snaps',
|
||||
};
|
||||
`,
|
||||
'snaps/tests/a.spec.js-snapshots/test/path/snapshot.txt': `Hello world`,
|
||||
'tests/a.spec.js': `
|
||||
const { test } = require('../helper');
|
||||
test('is a test', ({}) => {
|
||||
expect('Hello world').toMatchSnapshot(['test', 'path', 'snapshot.txt']);
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should use snapshotDir with nested test suite and path segments', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
...files,
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
snapshotDir: 'snaps',
|
||||
};
|
||||
`,
|
||||
'snaps/path/to/tests/a.spec.js-snapshots/path/to/snapshot.txt': `Hello world`,
|
||||
'path/to/tests/a.spec.js': `
|
||||
const { test } = require('../../../helper');
|
||||
test('is a test', ({}) => {
|
||||
expect('Hello world').toMatchSnapshot(['path', 'to', 'snapshot.txt']);
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should use project snapshotDir over base snapshotDir', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'helper.ts': `
|
||||
export const test = pwt.test.extend({
|
||||
auto: [ async ({}, run, testInfo) => {
|
||||
testInfo.snapshotSuffix = 'suffix';
|
||||
await run();
|
||||
}, { auto: true } ]
|
||||
});
|
||||
`,
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'foo',
|
||||
snapshotDir: 'project_snaps',
|
||||
},
|
||||
],
|
||||
snapshotDir: 'snaps',
|
||||
};
|
||||
`,
|
||||
'project_snaps/a.spec.js-snapshots/test/path/snapshot-foo-suffix.txt': `Hello world`,
|
||||
'a.spec.js': `
|
||||
const { test } = require('./helper');
|
||||
test('is a test', ({}) => {
|
||||
expect('Hello world').toMatchSnapshot(['test', 'path', 'snapshot.txt']);
|
||||
});
|
||||
|
|
|
|||
3
utils/generate_types/overrides-test.d.ts
vendored
3
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -51,6 +51,7 @@ interface TestProject {
|
|||
expect?: ExpectSettings;
|
||||
metadata?: any;
|
||||
name?: string;
|
||||
snapshotDir?: string;
|
||||
outputDir?: string;
|
||||
repeatEach?: number;
|
||||
retries?: number;
|
||||
|
|
@ -120,6 +121,7 @@ interface TestConfig {
|
|||
expect?: ExpectSettings;
|
||||
metadata?: any;
|
||||
name?: string;
|
||||
snapshotDir?: string;
|
||||
outputDir?: string;
|
||||
repeatEach?: number;
|
||||
retries?: number;
|
||||
|
|
@ -213,6 +215,7 @@ export interface TestInfo {
|
|||
stdout: (string | Buffer)[];
|
||||
stderr: (string | Buffer)[];
|
||||
snapshotSuffix: string;
|
||||
snapshotDir: string;
|
||||
outputDir: string;
|
||||
snapshotPath: (...pathSegments: string[]) => string;
|
||||
outputPath: (...pathSegments: string[]) => string;
|
||||
|
|
|
|||
Loading…
Reference in a new issue