feat: add snapshotDir to set base snapshot directory (#9260)

This commit is contained in:
Nick Partridge 2021-11-02 10:02:49 -05:00 committed by GitHub
parent 92c9e9a079
commit a51ac39275
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 161 additions and 8 deletions

View file

@ -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.

View file

@ -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">>

View file

@ -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]>

View file

@ -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]>

View file

@ -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),

View file

@ -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}`);
},

View file

@ -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.

View file

@ -488,12 +488,101 @@ test('should write missing expectations with sanitized snapshot name', async ({
expect(data.toString()).toBe('Hello world');
});
test('should join array of snapshot path segments without sanitizing ', async ({ runInlineTest }) => {
test('should join array of snapshot path segments without sanitizing', async ({ runInlineTest }) => {
const result = await runInlineTest({
...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']);
});
@ -689,4 +778,4 @@ test('should allow comparing text with text without file extension', async ({ ru
`
});
expect(result.exitCode).toBe(0);
});
});

View file

@ -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;