Skip to content

Commit

Permalink
chore: introduce update-source-method (#33738)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman authored Nov 23, 2024
1 parent 66d9f3a commit 971b5da
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 12 deletions.
6 changes: 6 additions & 0 deletions docs/src/test-api/class-fullconfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ See [`property: TestConfig.shard`].

See [`property: TestConfig.updateSnapshots`].

## property: FullConfig.updateSourceMethod
* since: v1.50
- type: <[UpdateSourceMethod]<"overwrite"|"3way"|"patch">>

See [`property: TestConfig.updateSourceMethod`].

## property: FullConfig.version
* since: v1.20
- type: <[string]>
Expand Down
9 changes: 9 additions & 0 deletions docs/src/test-api/class-testconfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
* since: v1.10
- type: ?<[TestOptions]>
Expand Down
1 change: 1 addition & 0 deletions packages/playwright/src/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export class FullConfigInternal {
projects: [],
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, 'missing'),
updateSourceMethod: takeFirst(configCLIOverrides.updateSourceMethod, userConfig.updateSourceMethod, 'patch'),
version: require('../../package.json').version,
workers: 0,
webServer: null,
Expand Down
1 change: 1 addition & 0 deletions packages/playwright/src/common/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type ConfigCLIOverrides = {
tsconfig?: string;
ignoreSnapshots?: boolean;
updateSnapshots?: 'all'|'changed'|'missing'|'none';
updateSourceMethod?: 'overwrite'|'patch'|'3way';
workers?: number | string;
projects?: { name: string, use?: any }[],
use?: any;
Expand Down
1 change: 1 addition & 0 deletions packages/playwright/src/isomorphic/teleReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ export const baseFullConfig: reporterTypes.FullConfig = {
quiet: false,
shard: null,
updateSnapshots: 'missing',
updateSourceMethod: 'patch',
version: '',
workers: 0,
webServer: null,
Expand Down
2 changes: 2 additions & 0 deletions packages/playwright/src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ function overridesFromOptions(options: { [key: string]: any }): ConfigCLIOverrid
tsconfig: options.tsconfig ? path.resolve(process.cwd(), options.tsconfig) : undefined,
ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : undefined,
updateSnapshots,
updateSourceMethod: options.updateSourceMethod || 'patch',
workers: options.workers,
};

Expand Down Expand Up @@ -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-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'`],
['--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%)`],
['-x', `Stop after the first failure`],
];
Expand Down
29 changes: 17 additions & 12 deletions packages/playwright/src/runner/rebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export async function applySuggestedRebaselines(config: FullConfigInternal, repo
const files: string[] = [];
const gitCache = new Map<string, string | null>();

const patchFile = path.join(project.project.outputDir, 'rebaselines.patch');

for (const fileName of [...suggestedRebaselines.keys()].sort()) {
const source = await fs.promises.readFile(fileName, 'utf8');
const lines = source.split('\n');
Expand Down Expand Up @@ -97,24 +99,27 @@ export async function applySuggestedRebaselines(config: FullConfigInternal, repo
for (const range of ranges)
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);
} else if (process.env.PWTEST_UPDATE_SNAPSHOTS === 'manual') {
} else if (config.config.updateSourceMethod === '3way') {
await fs.promises.writeFile(fileName, applyPatchWithConflictMarkers(source, result));
} else {
const gitFolder = findGitRoot(path.dirname(fileName), gitCache);
const relativeName = path.relative(gitFolder || process.cwd(), fileName);
files.push(relativeName);
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');
reporter.onStdErr(`\nNew baselines created for:\n\n${fileList}\n\n ` + colors.cyan('git apply ' + path.relative(process.cwd(), patchFile)) + '\n');
const relativeToGit = path.relative(gitFolder || process.cwd(), fileName);
patches.push(createPatch(relativeToGit, source, result));
}
}

const fileList = files.map(file => ' ' + colors.dim(file)).join('\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');
}
}

function createPatch(fileName: string, before: string, after: string) {
Expand Down
14 changes: 14 additions & 0 deletions packages/playwright/types/test.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,14 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
*/
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
* logical CPU cores, e.g. `'50%'.`
Expand Down Expand Up @@ -1837,6 +1845,12 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
*/
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.
*/
Expand Down
79 changes: 79 additions & 0 deletions tests/playwright-test/update-aria-snapshot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,82 @@ test.describe('update-snapshots all', () => {
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
`);
});
});

0 comments on commit 971b5da

Please sign in to comment.