chore: update missing snapshots by default (#33311)

This commit is contained in:
Pavel Feldman 2024-10-25 16:13:38 -07:00 committed by GitHub
parent c66af9c525
commit 74e5e5560f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 463 additions and 66 deletions

View File

@ -107,7 +107,7 @@ export function generateAriaTree(rootElement: Element): AriaNode {
function toAriaNode(element: Element): AriaNode | null {
const role = roleUtils.getAriaRole(element);
if (!role)
if (!role || role === 'presentation' || role === 'none')
return null;
const name = roleUtils.getElementAccessibleName(element, false) || '';
@ -168,7 +168,7 @@ function normalizeStringChildren(rootA11yNode: AriaNode) {
visit(rootA11yNode);
}
const normalizeWhitespaceWithin = (text: string) => text.replace(/[\s\t\r\n]+/g, ' ');
const normalizeWhitespaceWithin = (text: string) => text.replace(/[\u200b\s\t\r\n]+/g, ' ');
function matchesText(text: string | undefined, template: RegExp | string | undefined) {
if (!template)
@ -251,11 +251,12 @@ function matchesNodeDeep(root: AriaNode, template: AriaTemplateNode): boolean {
return !!results.length;
}
export function renderAriaTree(ariaNode: AriaNode): string {
export function renderAriaTree(ariaNode: AriaNode, options?: { noText?: boolean }): string {
const lines: string[] = [];
const visit = (ariaNode: AriaNode | string, indent: string) => {
if (typeof ariaNode === 'string') {
lines.push(indent + '- text: ' + quoteYamlString(ariaNode));
if (!options?.noText)
lines.push(indent + '- text: ' + quoteYamlString(ariaNode));
return;
}
let line = `${indent}- ${ariaNode.role}`;
@ -282,7 +283,8 @@ export function renderAriaTree(ariaNode: AriaNode): string {
if (!ariaNode.children.length) {
lines.push(line);
} else if (ariaNode.children.length === 1 && typeof ariaNode.children[0] === 'string') {
line += ': ' + quoteYamlString(ariaNode.children[0]);
if (!options?.noText)
line += ': ' + quoteYamlString(ariaNode.children[0]);
lines.push(line);
} else {
lines.push(line + ':');

View File

@ -33,7 +33,7 @@ export function matcherHint(state: ExpectMatcherState, locator: Locator | undefi
export type MatcherResult<E, A> = {
name: string;
expected: E;
expected?: E;
message: () => string;
pass: boolean;
actual?: A;

View File

@ -34,10 +34,10 @@ export async function toMatchAriaSnapshot(
const testInfo = currentTestInfo();
if (!testInfo)
throw new Error(`toMatchSnapshot() must be called during the test`);
throw new Error(`toMatchAriaSnapshot() must be called during the test`);
if (testInfo._projectInternal.ignoreSnapshots)
return { pass: !this.isNot, message: () => '', name: 'toMatchSnapshot', expected };
return { pass: !this.isNot, message: () => '', name: 'toMatchAriaSnapshot', expected };
const updateSnapshots = testInfo.config.updateSnapshots;
@ -54,13 +54,27 @@ export async function toMatchAriaSnapshot(
].join('\n\n'));
}
const generateMissingBaseline = updateSnapshots === 'missing' && !expected;
const generateNewBaseline = updateSnapshots === 'all' || generateMissingBaseline;
if (generateMissingBaseline) {
if (this.isNot) {
const message = `Matchers using ".not" can't generate new baselines`;
return { pass: this.isNot, message: () => message, name: 'toMatchAriaSnapshot' };
} else {
// When generating new baseline, run entire pipeline against impossible match.
expected = `- none "Generating new baseline"`;
}
}
const timeout = options.timeout ?? this.timeout;
expected = unshift(expected);
const { matches: pass, received, log, timedOut } = await receiver._expect('to.match.aria', { expectedValue: expected, isNot: this.isNot, timeout });
const messagePrefix = matcherHint(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined);
const notFound = received === kNoElementsFoundError;
const escapedExpected = unshift(escapePrivateUsePoints(expected));
const escapedReceived = unshift(escapePrivateUsePoints(received));
const escapedExpected = escapePrivateUsePoints(expected);
const escapedReceived = escapePrivateUsePoints(received);
const message = () => {
if (pass) {
if (notFound)
@ -75,10 +89,10 @@ export async function toMatchAriaSnapshot(
}
};
let suggestedRebaseline: string | undefined;
if (!this.isNot && pass === this.isNot) {
if (updateSnapshots === 'all' || (updateSnapshots === 'missing' && !expected.trim()))
suggestedRebaseline = `toMatchAriaSnapshot(\`\n${unshift(received, '${indent} ')}\n\${indent}\`)`;
if (!this.isNot && pass === this.isNot && generateNewBaseline) {
// Only rebaseline failed snapshots.
const suggestedRebaseline = `toMatchAriaSnapshot(\`\n${indent(received, '${indent} ')}\n\${indent}\`)`;
return { pass: this.isNot, message: () => '', name: 'toMatchAriaSnapshot', suggestedRebaseline };
}
return {
@ -88,7 +102,6 @@ export async function toMatchAriaSnapshot(
pass,
actual: received,
log,
suggestedRebaseline,
timeout: timedOut ? timeout : undefined,
};
}
@ -97,7 +110,7 @@ function escapePrivateUsePoints(str: string) {
return str.replace(/[\uE000-\uF8FF]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`);
}
function unshift(snapshot: string, indent: string = ''): string {
function unshift(snapshot: string): string {
const lines = snapshot.split('\n');
let whitespacePrefixLength = 100;
for (const line of lines) {
@ -108,5 +121,9 @@ function unshift(snapshot: string, indent: string = ''): string {
whitespacePrefixLength = match[1].length;
break;
}
return lines.filter(t => t.trim()).map(line => indent + line.substring(whitespacePrefixLength)).join('\n');
return lines.filter(t => t.trim()).map(line => line.substring(whitespacePrefixLength)).join('\n');
}
function indent(snapshot: string, indent: string): string {
return snapshot.split('\n').map(line => indent + line).join('\n');
}

View File

@ -111,7 +111,7 @@ export const SourceTab: React.FunctionComponent<{
<CopyToClipboard description='Copy filename' value={shortFileName}/>
{location && <ToolbarButton icon='link-external' title='Open in VS Code' onClick={openExternally}></ToolbarButton>}
</Toolbar> }
<CodeMirrorWrapper text={source.content || ''} language='javascript' highlight={highlight} revealLine={targetLine} readOnly={true} lineNumbers={true} />
<CodeMirrorWrapper text={source.content || ''} language='javascript' highlight={highlight} revealLine={targetLine} readOnly={true} lineNumbers={true} dataTestId='source-code-mirror'/>
</div>}
sidebar={<StackTraceView stack={stack} selectedFrame={selectedFrame} setSelectedFrame={setSelectedFrame} />}
/>;

View File

@ -58,9 +58,9 @@ export const FiltersView: React.FC<{
<span className='filter-label'>Projects:</span> {projectsLine}
</div>
{expanded && <div className='hbox' style={{ marginLeft: 14, maxHeight: 200, overflowY: 'auto' }}>
<div className='filter-list'>
<div className='filter-list' role='list' data-testid='status-filters'>
{[...statusFilters.entries()].map(([status, value]) => {
return <div className='filter-entry' key={status}>
return <div className='filter-entry' key={status} role='listitem'>
<label>
<input type='checkbox' checked={value} onClick={() => {
const copy = new Map(statusFilters);
@ -72,9 +72,9 @@ export const FiltersView: React.FC<{
</div>;
})}
</div>
<div className='filter-list'>
<div className='filter-list' role='list' data-testid='project-filters'>
{[...projectFilters.entries()].map(([projectName, value]) => {
return <div className='filter-entry' key={projectName}>
return <div className='filter-entry' key={projectName} role='listitem'>
<label>
<input type='checkbox' checked={value} onClick={() => {
const copy = new Map(projectFilters);

View File

@ -44,6 +44,7 @@ export interface SourceProps {
focusOnChange?: boolean;
wrapLines?: boolean;
onChange?: (text: string) => void;
dataTestId?: string;
}
export const CodeMirrorWrapper: React.FC<SourceProps> = ({
@ -59,6 +60,7 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
focusOnChange,
wrapLines,
onChange,
dataTestId,
}) => {
const [measure, codemirrorElement] = useMeasure<HTMLDivElement>();
const [modulePromise] = React.useState<Promise<CodeMirror>>(import('./codeMirrorModule').then(m => m.default));
@ -170,7 +172,7 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
};
}, [codemirror, text, highlight, revealLine, focusOnChange, onChange]);
return <div className='cm-wrapper' ref={codemirrorElement} onClick={onCodeMirrorClick}></div>;
return <div data-testid={dataTestId} className='cm-wrapper' ref={codemirrorElement} onClick={onCodeMirrorClick}></div>;
};
function onCodeMirrorClick(event: React.MouseEvent) {

View File

@ -399,3 +399,17 @@ it('check aria-hidden text', async ({ page, server }) => {
- paragraph: "hello"
`);
});
it('should ignore presentation and none roles', async ({ page, server }) => {
await page.goto(server.EMPTY_PAGE);
await page.setContent(`
<ul>
<li role='presentation'>hello</li>
<li role='none'>world</li>
</ul>
`);
await checkAndMatchSnapshot(page.locator('body'), `
- list: "hello world"
`);
});

View File

@ -5,15 +5,15 @@
"packages": {
"": {
"dependencies": {
"@playwright/test": "1.49.0-alpha-2024-10-20"
"@playwright/test": "1.49.0-alpha-2024-10-25"
}
},
"node_modules/@playwright/test": {
"version": "1.49.0-alpha-2024-10-20",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-20.tgz",
"integrity": "sha512-lSagJ8KSD636T/TNfSJRh+vuBBssCL5xJgYmsvsF37cDMATTdVf2OVozVK91V9MAL7CxP4F5sQFVq/8rqu23WA==",
"version": "1.49.0-alpha-2024-10-25",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-25.tgz",
"integrity": "sha512-ds5PrtfECd21uiyVhKFOns0B2QdbQrxTYZSJQYCKcjomH3i6Fu0ZgPMe8Fh/6Nnx1vYUv9P6XWsHdkSHF2IZVA==",
"dependencies": {
"playwright": "1.49.0-alpha-2024-10-20"
"playwright": "1.49.0-alpha-2024-10-25"
},
"bin": {
"playwright": "cli.js"
@ -36,11 +36,11 @@
}
},
"node_modules/playwright": {
"version": "1.49.0-alpha-2024-10-20",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-20.tgz",
"integrity": "sha512-lkZXCaLoVKaa3eVu8qJJiLym6SkjXD+ilE4XZJx3AIE0o4vqMEYVB8tjLzAcl4UZx8wVcCps/WcCvTWhOSIXRA==",
"version": "1.49.0-alpha-2024-10-25",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-25.tgz",
"integrity": "sha512-D8hV9H9MARk2ZyOO0/ivoRYXqBcsXnKsDzTLpiF5HIiweGIlxdiDcRQhelNAdfnxa01oAy+Qosu7Dsoy/RYWBw==",
"dependencies": {
"playwright-core": "1.49.0-alpha-2024-10-20"
"playwright-core": "1.49.0-alpha-2024-10-25"
},
"bin": {
"playwright": "cli.js"
@ -53,9 +53,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.49.0-alpha-2024-10-20",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-20.tgz",
"integrity": "sha512-TeQNA7vsGVrHaArr+giPyiWPAV27+wIcuMLrAJXzUB0leVA9bkXbNQ5lA5+G4OhqlmYAbMOpJMtN+TREDv4nXA==",
"version": "1.49.0-alpha-2024-10-25",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-25.tgz",
"integrity": "sha512-zHESnOKxzkEwUpi391rHnnBR0D+dkn6Oto5L9SqrBgOIhYUm7fnIywXk8FWq/uRDtlTH1RaGh0H1aAoPvVuW1w==",
"bin": {
"playwright-core": "cli.js"
},
@ -66,11 +66,11 @@
},
"dependencies": {
"@playwright/test": {
"version": "1.49.0-alpha-2024-10-20",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-20.tgz",
"integrity": "sha512-lSagJ8KSD636T/TNfSJRh+vuBBssCL5xJgYmsvsF37cDMATTdVf2OVozVK91V9MAL7CxP4F5sQFVq/8rqu23WA==",
"version": "1.49.0-alpha-2024-10-25",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-25.tgz",
"integrity": "sha512-ds5PrtfECd21uiyVhKFOns0B2QdbQrxTYZSJQYCKcjomH3i6Fu0ZgPMe8Fh/6Nnx1vYUv9P6XWsHdkSHF2IZVA==",
"requires": {
"playwright": "1.49.0-alpha-2024-10-20"
"playwright": "1.49.0-alpha-2024-10-25"
}
},
"fsevents": {
@ -80,18 +80,18 @@
"optional": true
},
"playwright": {
"version": "1.49.0-alpha-2024-10-20",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-20.tgz",
"integrity": "sha512-lkZXCaLoVKaa3eVu8qJJiLym6SkjXD+ilE4XZJx3AIE0o4vqMEYVB8tjLzAcl4UZx8wVcCps/WcCvTWhOSIXRA==",
"version": "1.49.0-alpha-2024-10-25",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-25.tgz",
"integrity": "sha512-D8hV9H9MARk2ZyOO0/ivoRYXqBcsXnKsDzTLpiF5HIiweGIlxdiDcRQhelNAdfnxa01oAy+Qosu7Dsoy/RYWBw==",
"requires": {
"fsevents": "2.3.2",
"playwright-core": "1.49.0-alpha-2024-10-20"
"playwright-core": "1.49.0-alpha-2024-10-25"
}
},
"playwright-core": {
"version": "1.49.0-alpha-2024-10-20",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-20.tgz",
"integrity": "sha512-TeQNA7vsGVrHaArr+giPyiWPAV27+wIcuMLrAJXzUB0leVA9bkXbNQ5lA5+G4OhqlmYAbMOpJMtN+TREDv4nXA=="
"version": "1.49.0-alpha-2024-10-25",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-25.tgz",
"integrity": "sha512-zHESnOKxzkEwUpi391rHnnBR0D+dkn6Oto5L9SqrBgOIhYUm7fnIywXk8FWq/uRDtlTH1RaGh0H1aAoPvVuW1w=="
}
}
}

View File

@ -1,6 +1,6 @@
{
"private": true,
"dependencies": {
"@playwright/test": "1.49.0-alpha-2024-10-20"
"@playwright/test": "1.49.0-alpha-2024-10-25"
}
}

View File

@ -70,7 +70,7 @@ test('should run visible', async ({ runUITest }) => {
- button "Run"
- button "Show source"
- button "Watch"
- treeitem "[icon-error] suite"
- treeitem "[icon-error] suite" [expanded=false]
- treeitem "[icon-error] b.test.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] passes/}
@ -230,7 +230,7 @@ test('should run by project', async ({ runUITest }) => {
- button "Run"
- button "Show source"
- button "Watch"
- treeitem "[icon-error] suite"
- treeitem "[icon-error] suite" [expanded=false]
- treeitem "[icon-error] b.test.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] passes/}
@ -263,23 +263,20 @@ test('should run by project', async ({ runUITest }) => {
- tree:
- treeitem "[icon-error] a.test.ts" [expanded]:
- group:
- treeitem ${/\[icon-circle-outline\] passes/}
- treeitem ${/\[icon-circle-outline\] passes/} [expanded=false]
- treeitem ${/\[icon-error\] fails/}:
- group:
- treeitem ${/\[icon-error\] foo/} [selected]:
- button "Run"
- button "Show source"
- button "Watch"
- treeitem ${/\[icon-error\] foo/} [selected]
- treeitem "[icon-circle-outline] bar"
- treeitem "[icon-error] suite"
- treeitem "[icon-error] suite" [expanded=false]
- treeitem "[icon-error] b.test.ts" [expanded]:
- group:
- treeitem ${/\[icon-circle-outline\] passes/}
- treeitem ${/\[icon-error\] fails/}
- treeitem ${/\[icon-circle-outline\] passes/} [expanded=false]
- treeitem ${/\[icon-error\] fails/} [expanded=false]
- treeitem "[icon-circle-outline] c.test.ts" [expanded]:
- group:
- treeitem ${/\[icon-circle-outline\] passes/}
- treeitem ${/\[icon-circle-outline\] skipped/}
- treeitem ${/\[icon-circle-outline\] passes/} [expanded=false]
- treeitem ${/\[icon-circle-outline\] skipped/} [expanded=false]
`);
await page.getByText('Status:').click();
@ -347,12 +344,12 @@ test('should run by project', async ({ runUITest }) => {
- treeitem ${/\[icon-error\] suite/}
- treeitem "[icon-error] b.test.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] passes/}
- treeitem ${/\[icon-error\] fails/}
- treeitem ${/\[icon-check\] passes/} [expanded=false]
- treeitem ${/\[icon-error\] fails/} [expanded=false]
- treeitem "[icon-check] c.test.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] passes/}
- treeitem ${/\[icon-circle-slash\] skipped/}
- treeitem ${/\[icon-check\] passes/} [expanded=false]
- treeitem ${/\[icon-circle-slash\] skipped/} [expanded=false]
`);
});
@ -445,8 +442,8 @@ test('should run folder', async ({ runUITest }) => {
- tree:
- treeitem "[icon-check] folder-b" [expanded] [selected]:
- group:
- treeitem "[icon-check] folder-c"
- treeitem "[icon-check] in-b.test.ts"
- treeitem "[icon-check] folder-c" [expanded=false]
- treeitem "[icon-check] in-b.test.ts" [expanded=false]
- treeitem "[icon-circle-outline] in-a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
@ -483,7 +480,7 @@ test('should show time', async ({ runUITest }) => {
- button "Run"
- button "Show source"
- button "Watch"
- treeitem "[icon-error] suite"
- treeitem "[icon-error] suite" [expanded=false]
- treeitem "[icon-error] b.test.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] passes \d+m?s/}

View File

@ -144,6 +144,16 @@ test('should run setup and teardown projects (1)', async ({ runUITest }) => {
await page.getByRole('checkbox', { name: 'teardown' }).setChecked(false);
await page.getByRole('checkbox', { name: 'test' }).setChecked(false);
await expect(page.getByTestId('project-filters')).toMatchAriaSnapshot(`
- list:
- listitem:
- checkbox "teardown"
- listitem:
- checkbox "setup"
- listitem:
- checkbox "test"
`);
await page.getByTitle('Run all').click();
await expect.poll(dumpTestTree(page)).toBe(`
@ -155,10 +165,27 @@ test('should run setup and teardown projects (1)', async ({ runUITest }) => {
test
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-check] setup.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] setup/}
- treeitem "[icon-check] teardown.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] teardown/}
- treeitem "[icon-check] test.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] test/}
`);
await page.getByTitle('Toggle output').click();
await expect(page.getByTestId('output')).toContainText(`from-setup`);
await expect(page.getByTestId('output')).toContainText(`from-test`);
await expect(page.getByTestId('output')).toContainText(`from-teardown`);
await expect(page.getByTestId('output')).toMatchAriaSnapshot(`
- textbox "Terminal input"
`);
});
test('should run setup and teardown projects (2)', async ({ runUITest }) => {
@ -168,6 +195,16 @@ test('should run setup and teardown projects (2)', async ({ runUITest }) => {
await page.getByRole('checkbox', { name: 'teardown' }).setChecked(true);
await page.getByRole('checkbox', { name: 'test' }).setChecked(true);
await expect(page.getByTestId('project-filters')).toMatchAriaSnapshot(`
- list:
- listitem:
- checkbox "teardown" [checked]
- listitem:
- checkbox "setup"
- listitem:
- checkbox "test" [checked]
`);
await page.getByTitle('Run all').click();
await expect.poll(dumpTestTree(page)).toBe(`
@ -177,10 +214,24 @@ test('should run setup and teardown projects (2)', async ({ runUITest }) => {
test
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-check] teardown.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] teardown/}
- treeitem "[icon-check] test.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] test/}
`);
await page.getByTitle('Toggle output').click();
await expect(page.getByTestId('output')).toContainText(`from-test`);
await expect(page.getByTestId('output')).toContainText(`from-teardown`);
await expect(page.getByTestId('output')).not.toContainText(`from-setup`);
await expect(page.getByTestId('output')).toMatchAriaSnapshot(`
- textbox "Terminal input"
`);
});
test('should run setup and teardown projects (3)', async ({ runUITest }) => {
@ -190,6 +241,16 @@ test('should run setup and teardown projects (3)', async ({ runUITest }) => {
await page.getByRole('checkbox', { name: 'teardown' }).setChecked(false);
await page.getByRole('checkbox', { name: 'test' }).setChecked(true);
await expect(page.getByTestId('project-filters')).toMatchAriaSnapshot(`
- list:
- listitem:
- checkbox "teardown"
- listitem:
- checkbox "setup"
- listitem:
- checkbox "test" [checked]
`);
await page.getByTitle('Run all').click();
await expect.poll(dumpTestTree(page)).toBe(`
@ -197,10 +258,21 @@ test('should run setup and teardown projects (3)', async ({ runUITest }) => {
test
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-check] test.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] test/}
`);
await page.getByTitle('Toggle output').click();
await expect(page.getByTestId('output')).toContainText(`from-test`);
await expect(page.getByTestId('output')).not.toContainText(`from-setup`);
await expect(page.getByTestId('output')).not.toContainText(`from-teardown`);
await expect(page.getByTestId('output')).toMatchAriaSnapshot(`
- textbox "Terminal input"
`);
});
test('should run part of the setup only', async ({ runUITest }) => {
@ -210,6 +282,16 @@ test('should run part of the setup only', async ({ runUITest }) => {
await page.getByRole('checkbox', { name: 'teardown' }).setChecked(true);
await page.getByRole('checkbox', { name: 'test' }).setChecked(true);
await expect(page.getByTestId('project-filters')).toMatchAriaSnapshot(`
- list:
- listitem:
- checkbox "teardown" [checked]
- listitem:
- checkbox "setup" [checked]
- listitem:
- checkbox "test" [checked]
`);
await page.getByText('setup.ts').hover();
await page.getByRole('treeitem', { name: 'setup.ts' }).getByRole('button', { name: 'Run' }).click();
@ -221,6 +303,22 @@ test('should run part of the setup only', async ({ runUITest }) => {
test.ts
test
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-check] setup.ts" [expanded] [selected]:
- button "Run"
- button "Show source"
- button "Watch"
- group:
- treeitem ${/\[icon-check\] setup/}
- treeitem "[icon-check] teardown.ts" [expanded]:
- group:
- treeitem ${/\[icon-check\] teardown/}
- treeitem "[icon-circle-outline] test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] test"
`);
});
for (const useWeb of [true, false]) {

View File

@ -48,6 +48,16 @@ test('should run tests', async ({ runUITest }) => {
test 2
test 3
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] test 0"
- treeitem "[icon-circle-outline] test 1"
- treeitem "[icon-circle-outline] test 2"
- treeitem ${/\[icon-check\] test 3/}
`);
});
test('should stop tests', async ({ runUITest }) => {
@ -66,6 +76,16 @@ test('should stop tests', async ({ runUITest }) => {
🕦 test 3
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-loading] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-slash] test 0"
- treeitem ${/\[icon-check\] test 1/}
- treeitem ${/\[icon-loading\] test 2/}
- treeitem ${/\[icon-clock\] test 3/}
`);
await expect(page.getByTitle('Run all')).toBeDisabled();
await expect(page.getByTitle('Stop')).toBeEnabled();

View File

@ -40,6 +40,17 @@ test('should show selected test in sources', async ({ runUITest }) => {
third
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] first"
- treeitem "[icon-circle-outline] second"
- treeitem "[icon-circle-outline] b.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] third"
`);
await page.getByTestId('test-tree').getByText('first').click();
await expect(
page.getByTestId('source-code').locator('.source-tab-file-name')
@ -48,6 +59,13 @@ test('should show selected test in sources', async ({ runUITest }) => {
page.locator('.CodeMirror .source-line-running'),
).toHaveText(`3 test('first', () => {});`);
await expect(page.getByTestId('source-code-mirror')).toMatchAriaSnapshot(`
- text: |
import { test } from '@playwright/test';
test('first', () => {});
test('second', () => {});
`);
await page.getByTestId('test-tree').getByText('second').click();
await expect(
page.getByTestId('source-code').locator('.source-tab-file-name')
@ -85,6 +103,14 @@ test('should show top-level errors in file', async ({ runUITest }) => {
third
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts"
- treeitem "[icon-circle-outline] b.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] third"
`);
await page.getByTestId('test-tree').getByText('a.test.ts').click();
await expect(
page.getByTestId('source-code').locator('.source-tab-file-name')
@ -113,6 +139,11 @@ test('should show syntax errors in file', async ({ runUITest }) => {
a.test.ts
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts"
`);
await page.getByTestId('test-tree').getByText('a.test.ts').click();
await expect(
page.getByTestId('source-code').locator('.source-tab-file-name')
@ -155,4 +186,12 @@ test('should load error (dupe tests) indicator on sources', async ({ runUITest }
'                              ',
/Error: duplicate test title "first", first declared in a.test.ts:3/
]);
await expect(page.getByTestId('source-code-mirror')).toMatchAriaSnapshot(`
- text: |
import { test } from '@playwright/test';
test('first', () => {});
test('first', () => {});
Error: duplicate test title "first", first declared in a.test.ts:3
`);
});

View File

@ -46,6 +46,19 @@ test('should list tests', async ({ runUITest }) => {
passes
fails
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
- treeitem "[icon-circle-outline] suite" [expanded=false]
- treeitem "[icon-circle-outline] b.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
`);
});
test('should list all tests from projects with clashing names', async ({ runUITest }) => {
@ -100,6 +113,22 @@ test('should list all tests from projects with clashing names', async ({ runUITe
one
two
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] bar" [expanded]:
- group:
- treeitem "[icon-circle-outline] b.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] three"
- treeitem "[icon-circle-outline] four"
- treeitem "[icon-circle-outline] foo" [expanded]:
- group:
- treeitem "[icon-circle-outline] a.test.ts" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] one"
- treeitem "[icon-circle-outline] two"
`);
});
test('should traverse up/down', async ({ runUITest }) => {
@ -111,6 +140,14 @@ test('should traverse up/down', async ({ runUITest }) => {
fails
suite
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
- treeitem "[icon-circle-outline] suite" [expanded=false]
`);
await page.keyboard.press('ArrowDown');
await expect.poll(dumpTestTree(page)).toContain(`
@ -119,6 +156,15 @@ test('should traverse up/down', async ({ runUITest }) => {
fails
suite
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes" [selected]
- treeitem "[icon-circle-outline] fails"
- treeitem "[icon-circle-outline] suite" [expanded=false]
`);
await page.keyboard.press('ArrowDown');
await expect.poll(dumpTestTree(page)).toContain(`
a.test.ts
@ -126,6 +172,14 @@ test('should traverse up/down', async ({ runUITest }) => {
fails <=
suite
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails" [selected]
- treeitem "[icon-circle-outline] suite" [expanded=false]
`);
await page.keyboard.press('ArrowUp');
await expect.poll(dumpTestTree(page)).toContain(`
@ -134,6 +188,14 @@ test('should traverse up/down', async ({ runUITest }) => {
fails
suite
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes" [selected]
- treeitem "[icon-circle-outline] fails"
- treeitem "[icon-circle-outline] suite" [expanded=false]
`);
});
test('should expand / collapse groups', async ({ runUITest }) => {
@ -149,6 +211,17 @@ test('should expand / collapse groups', async ({ runUITest }) => {
inner passes
inner fails
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
- treeitem "[icon-circle-outline] suite" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] inner passes"
- treeitem "[icon-circle-outline] inner fails"
`);
await page.keyboard.press('ArrowLeft');
await expect.poll(dumpTestTree(page)).toContain(`
@ -157,6 +230,14 @@ test('should expand / collapse groups', async ({ runUITest }) => {
fails
suite <=
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
- treeitem "[icon-circle-outline] suite" [selected] [expanded=false]
`);
await page.getByTestId('test-tree').getByText('passes').first().click();
await page.keyboard.press('ArrowLeft');
@ -165,11 +246,22 @@ test('should expand / collapse groups', async ({ runUITest }) => {
passes
fails
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
`);
await page.keyboard.press('ArrowLeft');
await expect.poll(dumpTestTree(page)).toContain(`
a.test.ts <=
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [selected] [expanded=false]
`);
});
test('should merge folder trees', async ({ runUITest }) => {
@ -195,6 +287,16 @@ test('should merge folder trees', async ({ runUITest }) => {
in-a.test.ts
passes
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] b" [expanded]:
- group:
- treeitem "[icon-circle-outline] c" [expanded=false]
- treeitem "[icon-circle-outline] in-b.test.ts" [expanded=false]
- treeitem "[icon-circle-outline] in-a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
`);
});
test('should list parametrized tests', async ({ runUITest }) => {
@ -224,6 +326,18 @@ test('should list parametrized tests', async ({ runUITest }) => {
test DE
test LT
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] cookies" [expanded]:
- group:
- treeitem "[icon-circle-outline] <anonymous>" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] test FR"
- treeitem "[icon-circle-outline] test DE"
- treeitem "[icon-circle-outline] test LT"
`);
});
test('should update parametrized tests', async ({ runUITest, writeFiles }) => {
@ -253,6 +367,18 @@ test('should update parametrized tests', async ({ runUITest, writeFiles }) => {
test DE
test LT
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] cookies" [expanded]:
- group:
- treeitem "[icon-circle-outline] <anonymous>" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] test FR"
- treeitem "[icon-circle-outline] test DE"
- treeitem "[icon-circle-outline] test LT"
`);
await writeFiles({
'a.test.ts': `
@ -275,6 +401,17 @@ test('should update parametrized tests', async ({ runUITest, writeFiles }) => {
test FR
test LT
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] cookies" [expanded]:
- group:
- treeitem "[icon-circle-outline] <anonymous>" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] test FR"
- treeitem "[icon-circle-outline] test LT"
`);
});
test('should collapse all', async ({ runUITest }) => {
@ -290,11 +427,26 @@ test('should collapse all', async ({ runUITest }) => {
inner passes
inner fails
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
- treeitem "[icon-circle-outline] suite" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] inner passes"
- treeitem "[icon-circle-outline] inner fails"
`);
await page.getByTitle('Collapse all').click();
await expect.poll(dumpTestTree(page)).toContain(`
a.test.ts
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded=false]
`);
});
test('should expand all', {
@ -321,6 +473,21 @@ test('should expand all', {
passes
fails
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
- treeitem "[icon-circle-outline] suite" [expanded]:
- group:
- treeitem "[icon-circle-outline] inner passes"
- treeitem "[icon-circle-outline] inner fails"
- treeitem "[icon-circle-outline] b.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] passes"
- treeitem "[icon-circle-outline] fails"
`);
});
test('should resolve title conflicts', async ({ runUITest }) => {
@ -349,4 +516,14 @@ test('should resolve title conflicts', async ({ runUITest }) => {
bar
bar 2
`);
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
- tree:
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
- group:
- treeitem "[icon-circle-outline] foo"
- treeitem "[icon-circle-outline] foo" [expanded] [selected]:
- group:
- treeitem "[icon-circle-outline] bar"
- treeitem "[icon-circle-outline] bar 2"
`);
});

View File

@ -17,6 +17,8 @@
import * as fs from 'fs';
import { test, expect } from './playwright-test-fixtures';
test.describe.configure({ mode: 'parallel' });
test('should update snapshot with the update-snapshots flag', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
'a.spec.ts': `
@ -46,3 +48,32 @@ test('should update snapshot with the update-snapshots flag', async ({ runInline
`);
});
test('should update missing snapshots', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
'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(\`\`);
});
`
});
expect(result.exitCode).toBe(0);
const patchPath = testInfo.outputPath('test-results/rebaselines.patch');
const data = fs.readFileSync(patchPath, 'utf-8');
expect(data).toBe(`--- a/a.spec.ts
+++ b/a.spec.ts
@@ -2,6 +2,8 @@
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.setContent(\`<h1>hello</h1>\`);
- await expect(page.locator('body')).toMatchAriaSnapshot(\`\`);
+ await expect(page.locator('body')).toMatchAriaSnapshot(\`
+ - heading "hello" [level=1]
+ \`);
});
`);
});