chore: introduce update-source-method (#33738)
This commit is contained in:
parent
66d9f3acbe
commit
971b5da741
|
|
@ -118,6 +118,12 @@ See [`property: TestConfig.shard`].
|
||||||
|
|
||||||
See [`property: TestConfig.updateSnapshots`].
|
See [`property: TestConfig.updateSnapshots`].
|
||||||
|
|
||||||
|
## property: FullConfig.updateSourceMethod
|
||||||
|
* since: v1.50
|
||||||
|
- type: <[UpdateSourceMethod]<"overwrite"|"3way"|"patch">>
|
||||||
|
|
||||||
|
See [`property: TestConfig.updateSourceMethod`].
|
||||||
|
|
||||||
## property: FullConfig.version
|
## property: FullConfig.version
|
||||||
* since: v1.20
|
* since: v1.20
|
||||||
- type: <[string]>
|
- type: <[string]>
|
||||||
|
|
|
||||||
|
|
@ -590,6 +590,15 @@ export default defineConfig({
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## property: TestConfig.updateSourceMethod
|
||||||
|
* since: v1.50
|
||||||
|
- type: ?<[UpdateSourceMethod]<"overwrite"|"3way"|"patch">>
|
||||||
|
|
||||||
|
Defines how to update the source code snapshots.
|
||||||
|
* `'overwrite'` - Overwrite the source code snapshot with the actual result.
|
||||||
|
* `'3way'` - Use a three-way merge to update the source code snapshot.
|
||||||
|
* `'patch'` - Use a patch to update the source code snapshot. This is the default.
|
||||||
|
|
||||||
## property: TestConfig.use
|
## property: TestConfig.use
|
||||||
* since: v1.10
|
* since: v1.10
|
||||||
- type: ?<[TestOptions]>
|
- type: ?<[TestOptions]>
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ export class FullConfigInternal {
|
||||||
projects: [],
|
projects: [],
|
||||||
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
|
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
|
||||||
updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, 'missing'),
|
updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, 'missing'),
|
||||||
|
updateSourceMethod: takeFirst(configCLIOverrides.updateSourceMethod, userConfig.updateSourceMethod, 'patch'),
|
||||||
version: require('../../package.json').version,
|
version: require('../../package.json').version,
|
||||||
workers: 0,
|
workers: 0,
|
||||||
webServer: null,
|
webServer: null,
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ export type ConfigCLIOverrides = {
|
||||||
tsconfig?: string;
|
tsconfig?: string;
|
||||||
ignoreSnapshots?: boolean;
|
ignoreSnapshots?: boolean;
|
||||||
updateSnapshots?: 'all'|'changed'|'missing'|'none';
|
updateSnapshots?: 'all'|'changed'|'missing'|'none';
|
||||||
|
updateSourceMethod?: 'overwrite'|'patch'|'3way';
|
||||||
workers?: number | string;
|
workers?: number | string;
|
||||||
projects?: { name: string, use?: any }[],
|
projects?: { name: string, use?: any }[],
|
||||||
use?: any;
|
use?: any;
|
||||||
|
|
|
||||||
|
|
@ -594,6 +594,7 @@ export const baseFullConfig: reporterTypes.FullConfig = {
|
||||||
quiet: false,
|
quiet: false,
|
||||||
shard: null,
|
shard: null,
|
||||||
updateSnapshots: 'missing',
|
updateSnapshots: 'missing',
|
||||||
|
updateSourceMethod: 'patch',
|
||||||
version: '',
|
version: '',
|
||||||
workers: 0,
|
workers: 0,
|
||||||
webServer: null,
|
webServer: null,
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,7 @@ function overridesFromOptions(options: { [key: string]: any }): ConfigCLIOverrid
|
||||||
tsconfig: options.tsconfig ? path.resolve(process.cwd(), options.tsconfig) : undefined,
|
tsconfig: options.tsconfig ? path.resolve(process.cwd(), options.tsconfig) : undefined,
|
||||||
ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : undefined,
|
ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : undefined,
|
||||||
updateSnapshots,
|
updateSnapshots,
|
||||||
|
updateSourceMethod: options.updateSourceMethod || 'patch',
|
||||||
workers: options.workers,
|
workers: options.workers,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -385,6 +386,7 @@ const testOptions: [string, string][] = [
|
||||||
['--ui-host <host>', 'Host to serve UI on; specifying this option opens UI in a browser tab'],
|
['--ui-host <host>', 'Host to serve UI on; specifying this option opens UI in a browser tab'],
|
||||||
['--ui-port <port>', 'Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab'],
|
['--ui-port <port>', 'Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab'],
|
||||||
['-u, --update-snapshots [mode]', `Update snapshots with actual results. Possible values are 'all', 'changed', 'missing' and 'none'. Not passing defaults to 'missing', passing without value defaults to 'changed'`],
|
['-u, --update-snapshots [mode]', `Update snapshots with actual results. Possible values are 'all', 'changed', 'missing' and 'none'. Not passing defaults to 'missing', passing without value defaults to 'changed'`],
|
||||||
|
['--update-source-method <method>', `Chooses the way source is updated. Possible values are 'overwrite', '3way' and 'patch'. Defaults to 'patch'`],
|
||||||
['-j, --workers <workers>', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`],
|
['-j, --workers <workers>', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`],
|
||||||
['-x', `Stop after the first failure`],
|
['-x', `Stop after the first failure`],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ export async function applySuggestedRebaselines(config: FullConfigInternal, repo
|
||||||
const files: string[] = [];
|
const files: string[] = [];
|
||||||
const gitCache = new Map<string, string | null>();
|
const gitCache = new Map<string, string | null>();
|
||||||
|
|
||||||
|
const patchFile = path.join(project.project.outputDir, 'rebaselines.patch');
|
||||||
|
|
||||||
for (const fileName of [...suggestedRebaselines.keys()].sort()) {
|
for (const fileName of [...suggestedRebaselines.keys()].sort()) {
|
||||||
const source = await fs.promises.readFile(fileName, 'utf8');
|
const source = await fs.promises.readFile(fileName, 'utf8');
|
||||||
const lines = source.split('\n');
|
const lines = source.split('\n');
|
||||||
|
|
@ -97,23 +99,26 @@ export async function applySuggestedRebaselines(config: FullConfigInternal, repo
|
||||||
for (const range of ranges)
|
for (const range of ranges)
|
||||||
result = result.substring(0, range.start) + range.newText + result.substring(range.end);
|
result = result.substring(0, range.start) + range.newText + result.substring(range.end);
|
||||||
|
|
||||||
if (process.env.PWTEST_UPDATE_SNAPSHOTS === 'overwrite') {
|
const relativeName = path.relative(process.cwd(), fileName);
|
||||||
|
files.push(relativeName);
|
||||||
|
|
||||||
|
if (config.config.updateSourceMethod === 'overwrite') {
|
||||||
await fs.promises.writeFile(fileName, result);
|
await fs.promises.writeFile(fileName, result);
|
||||||
} else if (process.env.PWTEST_UPDATE_SNAPSHOTS === 'manual') {
|
} else if (config.config.updateSourceMethod === '3way') {
|
||||||
await fs.promises.writeFile(fileName, applyPatchWithConflictMarkers(source, result));
|
await fs.promises.writeFile(fileName, applyPatchWithConflictMarkers(source, result));
|
||||||
} else {
|
} else {
|
||||||
const gitFolder = findGitRoot(path.dirname(fileName), gitCache);
|
const gitFolder = findGitRoot(path.dirname(fileName), gitCache);
|
||||||
const relativeName = path.relative(gitFolder || process.cwd(), fileName);
|
const relativeToGit = path.relative(gitFolder || process.cwd(), fileName);
|
||||||
files.push(relativeName);
|
patches.push(createPatch(relativeToGit, source, result));
|
||||||
patches.push(createPatch(relativeName, source, result));
|
}
|
||||||
|
}
|
||||||
const patchFile = path.join(project.project.outputDir, 'rebaselines.patch');
|
|
||||||
await fs.promises.mkdir(path.dirname(patchFile), { recursive: true });
|
|
||||||
await fs.promises.writeFile(patchFile, patches.join('\n'));
|
|
||||||
|
|
||||||
const fileList = files.map(file => ' ' + colors.dim(file)).join('\n');
|
const fileList = files.map(file => ' ' + colors.dim(file)).join('\n');
|
||||||
reporter.onStdErr(`\nNew baselines created for:\n\n${fileList}\n\n ` + colors.cyan('git apply ' + path.relative(process.cwd(), patchFile)) + '\n');
|
reporter.onStdErr(`\nNew baselines created for:\n\n${fileList}\n`);
|
||||||
}
|
if (config.config.updateSourceMethod === 'patch') {
|
||||||
|
await fs.promises.mkdir(path.dirname(patchFile), { recursive: true });
|
||||||
|
await fs.promises.writeFile(patchFile, patches.join('\n'));
|
||||||
|
reporter.onStdErr(`\n ` + colors.cyan('git apply ' + path.relative(process.cwd(), patchFile)) + '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
14
packages/playwright/types/test.d.ts
vendored
14
packages/playwright/types/test.d.ts
vendored
|
|
@ -1688,6 +1688,14 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||||
*/
|
*/
|
||||||
updateSnapshots?: "all"|"changed"|"missing"|"none";
|
updateSnapshots?: "all"|"changed"|"missing"|"none";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines how to update the source code snapshots.
|
||||||
|
* - `'overwrite'` - Overwrite the source code snapshot with the actual result.
|
||||||
|
* - `'3way'` - Use a three-way merge to update the source code snapshot.
|
||||||
|
* - `'patch'` - Use a patch to update the source code snapshot. This is the default.
|
||||||
|
*/
|
||||||
|
updateSourceMethod?: "overwrite"|"3way"|"patch";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of concurrent worker processes to use for parallelizing tests. Can also be set as percentage of
|
* The maximum number of concurrent worker processes to use for parallelizing tests. Can also be set as percentage of
|
||||||
* logical CPU cores, e.g. `'50%'.`
|
* logical CPU cores, e.g. `'50%'.`
|
||||||
|
|
@ -1837,6 +1845,12 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||||
*/
|
*/
|
||||||
updateSnapshots: "all"|"changed"|"missing"|"none";
|
updateSnapshots: "all"|"changed"|"missing"|"none";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See
|
||||||
|
* [testConfig.updateSourceMethod](https://playwright.dev/docs/api/class-testconfig#test-config-update-source-method).
|
||||||
|
*/
|
||||||
|
updateSourceMethod: "overwrite"|"3way"|"patch";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Playwright version.
|
* Playwright version.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -490,3 +490,82 @@ test.describe('update-snapshots all', () => {
|
||||||
expect(result2.exitCode).toBe(0);
|
expect(result2.exitCode).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('update-source-method', () => {
|
||||||
|
test('should overwrite source', async ({ runInlineTest }, testInfo) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'.git/marker': '',
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.setContent(\`<h1>hello</h1>\`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(\`
|
||||||
|
- heading "world"
|
||||||
|
\`);
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { 'update-snapshots': 'all', 'update-source-method': 'overwrite' });
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
const patchPath = testInfo.outputPath('test-results/rebaselines.patch');
|
||||||
|
expect(fs.existsSync(patchPath)).toBeFalsy();
|
||||||
|
|
||||||
|
const data = fs.readFileSync(testInfo.outputPath('a.spec.ts'), 'utf-8');
|
||||||
|
expect(data).toBe(`
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.setContent(\`<h1>hello</h1>\`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(\`
|
||||||
|
- heading "hello" [level=1]
|
||||||
|
\`);
|
||||||
|
});
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(stripAnsi(result.output).replace(/\\/g, '/')).toContain(`New baselines created for:
|
||||||
|
|
||||||
|
a.spec.ts
|
||||||
|
`);
|
||||||
|
|
||||||
|
const result2 = await runInlineTest({});
|
||||||
|
expect(result2.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should 3way source', async ({ runInlineTest }, testInfo) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'.git/marker': '',
|
||||||
|
'a.spec.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.setContent(\`<h1>hello</h1>\`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(\`
|
||||||
|
- heading "world"
|
||||||
|
\`);
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { 'update-snapshots': 'all', 'update-source-method': '3way' });
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
const patchPath = testInfo.outputPath('test-results/rebaselines.patch');
|
||||||
|
expect(fs.existsSync(patchPath)).toBeFalsy();
|
||||||
|
|
||||||
|
const data = fs.readFileSync(testInfo.outputPath('a.spec.ts'), 'utf-8');
|
||||||
|
expect(data).toBe(`
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.setContent(\`<h1>hello</h1>\`);
|
||||||
|
await expect(page.locator('body')).toMatchAriaSnapshot(\`
|
||||||
|
\<<<<<<< HEAD
|
||||||
|
- heading "world"
|
||||||
|
=======
|
||||||
|
- heading "hello" [level=1]
|
||||||
|
>>>>>>> SNAPSHOT
|
||||||
|
\`);
|
||||||
|
});
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(stripAnsi(result.output).replace(/\\/g, '/')).toContain(`New baselines created for:
|
||||||
|
|
||||||
|
a.spec.ts
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue