function options
This commit is contained in:
parent
d7020cba63
commit
4d0db5018c
|
|
@ -1751,7 +1751,7 @@ await Expect(Page.GetByTitle("Issues count")).toHaveText("25 issues");
|
||||||
```
|
```
|
||||||
|
|
||||||
## test-config-snapshot-path-template
|
## test-config-snapshot-path-template
|
||||||
- `type` ?<[string]>
|
- `type` ?<[string]|[SnapshotPathResolver]>
|
||||||
* langs: js
|
* langs: js
|
||||||
|
|
||||||
This option configures a template controlling location of snapshots generated by [`method: PageAssertions.toHaveScreenshot#1`] and [`method: SnapshotAssertions.toMatchSnapshot#1`].
|
This option configures a template controlling location of snapshots generated by [`method: PageAssertions.toHaveScreenshot#1`] and [`method: SnapshotAssertions.toMatchSnapshot#1`].
|
||||||
|
|
@ -1764,6 +1764,12 @@ import { defineConfig } from '@playwright/test';
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './tests',
|
testDir: './tests',
|
||||||
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
// Using a function for runtime control
|
||||||
|
snapshotPathTemplate: ({ testDir, testFilePath, arg, ext }) => `${testDir}/__screenshots__/${testFilePath}/${arg}${ext}`
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { getPackageJsonPath, mergeObjects } from '../util';
|
||||||
import type { Matcher } from '../util';
|
import type { Matcher } from '../util';
|
||||||
import type { ConfigCLIOverrides } from './ipc';
|
import type { ConfigCLIOverrides } from './ipc';
|
||||||
import type { FullConfig, FullProject } from '../../types/testReporter';
|
import type { FullConfig, FullProject } from '../../types/testReporter';
|
||||||
|
import type { SnapshotPathResolver } from '../worker/testInfo';
|
||||||
|
|
||||||
export type ConfigLocation = {
|
export type ConfigLocation = {
|
||||||
resolvedConfigFile?: string;
|
resolvedConfigFile?: string;
|
||||||
|
|
@ -154,7 +155,7 @@ export class FullProjectInternal {
|
||||||
readonly fullyParallel: boolean;
|
readonly fullyParallel: boolean;
|
||||||
readonly expect: Project['expect'];
|
readonly expect: Project['expect'];
|
||||||
readonly respectGitIgnore: boolean;
|
readonly respectGitIgnore: boolean;
|
||||||
readonly snapshotPathTemplate: string;
|
readonly snapshotPathTemplate: string | SnapshotPathResolver;
|
||||||
readonly ignoreSnapshots: boolean;
|
readonly ignoreSnapshots: boolean;
|
||||||
id = '';
|
id = '';
|
||||||
deps: FullProjectInternal[] = [];
|
deps: FullProjectInternal[] = [];
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,22 @@ export type TestStage = {
|
||||||
step?: TestStepInternal;
|
step?: TestStepInternal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SnapshotPathArgs = {
|
||||||
|
arg: string;
|
||||||
|
ext: string;
|
||||||
|
platform: NodeJS.Platform;
|
||||||
|
projectName?: string;
|
||||||
|
snapshotDir: string;
|
||||||
|
snapshotSuffix?: string;
|
||||||
|
testDir: string;
|
||||||
|
testFileDir: string;
|
||||||
|
testFileName: string;
|
||||||
|
testFilePath: string;
|
||||||
|
testName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SnapshotPathResolver = (snapshotPathArgs: SnapshotPathArgs, testInfo: TestInfo) => string;
|
||||||
|
|
||||||
export class TestInfoImpl implements TestInfo {
|
export class TestInfoImpl implements TestInfo {
|
||||||
private _onStepBegin: (payload: StepBeginPayload) => void;
|
private _onStepBegin: (payload: StepBeginPayload) => void;
|
||||||
private _onStepEnd: (payload: StepEndPayload) => void;
|
private _onStepEnd: (payload: StepEndPayload) => void;
|
||||||
|
|
@ -441,20 +457,35 @@ export class TestInfoImpl implements TestInfo {
|
||||||
const parsedSubPath = path.parse(subPath);
|
const parsedSubPath = path.parse(subPath);
|
||||||
const relativeTestFilePath = path.relative(this.project.testDir, this._requireFile);
|
const relativeTestFilePath = path.relative(this.project.testDir, this._requireFile);
|
||||||
const parsedRelativeTestFilePath = path.parse(relativeTestFilePath);
|
const parsedRelativeTestFilePath = path.parse(relativeTestFilePath);
|
||||||
const projectNamePathSegment = sanitizeForFilePath(this.project.name);
|
const options: SnapshotPathArgs = {
|
||||||
|
arg: path.join(parsedSubPath.dir, parsedSubPath.name),
|
||||||
|
ext: parsedSubPath.ext,
|
||||||
|
platform: process.platform,
|
||||||
|
projectName: sanitizeForFilePath(this.project.name),
|
||||||
|
snapshotDir: this.project.snapshotDir,
|
||||||
|
snapshotSuffix: this.snapshotSuffix,
|
||||||
|
testDir: this.project.testDir,
|
||||||
|
testFileDir: parsedRelativeTestFilePath.dir,
|
||||||
|
testFileName: parsedRelativeTestFilePath.base,
|
||||||
|
testFilePath: relativeTestFilePath,
|
||||||
|
testName: this._fsSanitizedTestName(),
|
||||||
|
};
|
||||||
|
|
||||||
const snapshotPath = (this._projectInternal.snapshotPathTemplate || '')
|
const snapshotPath: string =
|
||||||
.replace(/\{(.)?testDir\}/g, '$1' + this.project.testDir)
|
typeof this._projectInternal.snapshotPathTemplate === 'function' ?
|
||||||
.replace(/\{(.)?snapshotDir\}/g, '$1' + this.project.snapshotDir)
|
this._projectInternal.snapshotPathTemplate(options, this) :
|
||||||
.replace(/\{(.)?snapshotSuffix\}/g, this.snapshotSuffix ? '$1' + this.snapshotSuffix : '')
|
(this._projectInternal.snapshotPathTemplate || '')
|
||||||
.replace(/\{(.)?testFileDir\}/g, '$1' + parsedRelativeTestFilePath.dir)
|
.replace(/\{(.)?testDir\}/g, '$1' + options.testDir)
|
||||||
.replace(/\{(.)?platform\}/g, '$1' + process.platform)
|
.replace(/\{(.)?snapshotDir\}/g, '$1' + options.snapshotDir)
|
||||||
.replace(/\{(.)?projectName\}/g, projectNamePathSegment ? '$1' + projectNamePathSegment : '')
|
.replace(/\{(.)?snapshotSuffix\}/g, options.snapshotSuffix ? '$1' + options.snapshotSuffix : '')
|
||||||
.replace(/\{(.)?testName\}/g, '$1' + this._fsSanitizedTestName())
|
.replace(/\{(.)?testFileDir\}/g, '$1' + options.testFileDir)
|
||||||
.replace(/\{(.)?testFileName\}/g, '$1' + parsedRelativeTestFilePath.base)
|
.replace(/\{(.)?platform\}/g, '$1' + options.platform)
|
||||||
.replace(/\{(.)?testFilePath\}/g, '$1' + relativeTestFilePath)
|
.replace(/\{(.)?projectName\}/g, options.projectName ? '$1' + options.projectName : '')
|
||||||
.replace(/\{(.)?arg\}/g, '$1' + path.join(parsedSubPath.dir, parsedSubPath.name))
|
.replace(/\{(.)?testName\}/g, '$1' + options.testName)
|
||||||
.replace(/\{(.)?ext\}/g, parsedSubPath.ext ? '$1' + parsedSubPath.ext : '');
|
.replace(/\{(.)?testFileName\}/g, '$1' + options.testFileName)
|
||||||
|
.replace(/\{(.)?testFilePath\}/g, '$1' + options.testFilePath)
|
||||||
|
.replace(/\{(.)?arg\}/g, '$1' + options.arg)
|
||||||
|
.replace(/\{(.)?ext\}/g, options.ext ? '$1' + options.ext : '');
|
||||||
|
|
||||||
return path.normalize(path.resolve(this._configInternal.configDir, snapshotPath));
|
return path.normalize(path.resolve(this._configInternal.configDir, snapshotPath));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
packages/playwright/types/test.d.ts
vendored
16
packages/playwright/types/test.d.ts
vendored
|
|
@ -417,6 +417,12 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> {
|
||||||
* export default defineConfig({
|
* export default defineConfig({
|
||||||
* testDir: './tests',
|
* testDir: './tests',
|
||||||
* snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
* snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
||||||
|
* projects: [
|
||||||
|
* {
|
||||||
|
* // Using a function for runtime control
|
||||||
|
* snapshotPathTemplate: ({ testDir, testFilePath, arg, ext }) => `${testDir}/__screenshots__/${testFilePath}/${arg}${ext}`
|
||||||
|
* },
|
||||||
|
* ],
|
||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
|
@ -498,7 +504,7 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> {
|
||||||
* 1. Since `snapshotPathTemplate` resolves to relative path, it will be resolved relative to `configDir`.
|
* 1. Since `snapshotPathTemplate` resolves to relative path, it will be resolved relative to `configDir`.
|
||||||
* 1. Forward slashes `"/"` can be used as path separators on any platform.
|
* 1. Forward slashes `"/"` can be used as path separators on any platform.
|
||||||
*/
|
*/
|
||||||
snapshotPathTemplate?: string;
|
snapshotPathTemplate?: string|SnapshotPathResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of a project that needs to run after this and all dependent projects have finished. Teardown is useful to
|
* Name of a project that needs to run after this and all dependent projects have finished. Teardown is useful to
|
||||||
|
|
@ -1479,6 +1485,12 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||||
* export default defineConfig({
|
* export default defineConfig({
|
||||||
* testDir: './tests',
|
* testDir: './tests',
|
||||||
* snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
* snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
||||||
|
* projects: [
|
||||||
|
* {
|
||||||
|
* // Using a function for runtime control
|
||||||
|
* snapshotPathTemplate: ({ testDir, testFilePath, arg, ext }) => `${testDir}/__screenshots__/${testFilePath}/${arg}${ext}`
|
||||||
|
* },
|
||||||
|
* ],
|
||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
|
@ -1560,7 +1572,7 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||||
* 1. Since `snapshotPathTemplate` resolves to relative path, it will be resolved relative to `configDir`.
|
* 1. Since `snapshotPathTemplate` resolves to relative path, it will be resolved relative to `configDir`.
|
||||||
* 1. Forward slashes `"/"` can be used as path separators on any platform.
|
* 1. Forward slashes `"/"` can be used as path separators on any platform.
|
||||||
*/
|
*/
|
||||||
snapshotPathTemplate?: string;
|
snapshotPathTemplate?: string|SnapshotPathResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directory that will be recursively scanned for test files. Defaults to the directory of the configuration file.
|
* Directory that will be recursively scanned for test files. Defaults to the directory of the configuration file.
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { test, expect } from './playwright-test-fixtures';
|
import { test, expect } from './playwright-test-fixtures';
|
||||||
|
|
||||||
async function getSnapshotPaths(runInlineTest, testInfo, playwrightConfig, pathArgs) {
|
|
||||||
const SEPARATOR = '==== 8< ---- ';
|
const SEPARATOR = '==== 8< ---- ';
|
||||||
|
async function getSnapshotPaths(runInlineTest, testInfo, playwrightConfig, pathArgs) {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'playwright.config.js': `
|
'playwright.config.js': `
|
||||||
module.exports = ${JSON.stringify(playwrightConfig, null, 2)}
|
module.exports = ${JSON.stringify(playwrightConfig, null, 2)}
|
||||||
|
|
@ -106,6 +106,53 @@ test('tokens should expand property', async ({ runInlineTest }, testInfo) => {
|
||||||
expect.soft(snapshotPath['testName']).toBe('suite-test-should-work');
|
expect.soft(snapshotPath['testName']).toBe('suite-test-should-work');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('supports function arg', async ({ runInlineTest }, testInfo) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.js': `
|
||||||
|
module.exports = {
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'proj',
|
||||||
|
snapshotPathTemplate: (args) => {
|
||||||
|
console.log(JSON.stringify(args));
|
||||||
|
return 'path/to/snapshot';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('is a test', async ({ }, testInfo) => {
|
||||||
|
console.log(${JSON.stringify(SEPARATOR)})
|
||||||
|
const snapshotPath = testInfo.snapshotPath('foo', 'bar.png');
|
||||||
|
console.log(${JSON.stringify(SEPARATOR)});
|
||||||
|
console.log(JSON.stringify({ snapshotPath }));
|
||||||
|
console.log(${JSON.stringify(SEPARATOR)});
|
||||||
|
});
|
||||||
|
`
|
||||||
|
});
|
||||||
|
const output = result.output.split(SEPARATOR).slice(1, -1).map(json => JSON.parse(json));
|
||||||
|
expect.soft(output).toEqual([
|
||||||
|
{
|
||||||
|
arg: 'foo/bar',
|
||||||
|
ext: '.png',
|
||||||
|
platform: process.platform,
|
||||||
|
projectName: 'proj',
|
||||||
|
snapshotDir: testInfo.outputPath(test.name),
|
||||||
|
snapshotSuffix: process.platform,
|
||||||
|
testDir: testInfo.outputDir,
|
||||||
|
testFileDir: '',
|
||||||
|
testFileName: 'a.spec.ts',
|
||||||
|
testFilePath: 'a.spec.ts',
|
||||||
|
testName: 'is-a-test',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshotPath: testInfo.outputPath('path/to/snapshot')
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
test('args array should work', async ({ runInlineTest }, testInfo) => {
|
test('args array should work', async ({ runInlineTest }, testInfo) => {
|
||||||
const snapshotPath = await getSnapshotPaths(runInlineTest, testInfo, {
|
const snapshotPath = await getSnapshotPaths(runInlineTest, testInfo, {
|
||||||
projects: [{
|
projects: [{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue