chore: use aria snapshots in some ui mode tests (#33212)
This commit is contained in:
parent
0351fd9401
commit
2a3d67195d
|
@ -208,7 +208,7 @@ function matchesText(text: string | undefined, template: RegExp | string | undef
|
|||
export function matchesAriaTree(rootElement: Element, template: AriaTemplateNode): { matches: boolean, received: string } {
|
||||
const root = generateAriaTree(rootElement);
|
||||
const matches = matchesNodeDeep(root, template);
|
||||
return { matches, received: renderAriaTree(root) };
|
||||
return { matches, received: renderAriaTree(root, { noText: true }) };
|
||||
}
|
||||
|
||||
function matchesNode(node: AriaNode | string, template: AriaTemplateNode | RegExp | string, depth: number): boolean {
|
||||
|
@ -276,10 +276,11 @@ 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') {
|
||||
if (!options?.noText)
|
||||
lines.push(indent + '- text: ' + escapeYamlString(ariaNode));
|
||||
return;
|
||||
}
|
||||
|
@ -301,10 +302,12 @@ export function renderAriaTree(ariaNode: AriaNode): string {
|
|||
line += ` [pressed=mixed]`;
|
||||
if (ariaNode.pressed === true)
|
||||
line += ` [pressed]`;
|
||||
if (ariaNode.selected === true)
|
||||
line += ` [selected]`;
|
||||
|
||||
const stringValue = !ariaNode.children.length || (ariaNode.children?.length === 1 && typeof ariaNode.children[0] === 'string');
|
||||
if (stringValue) {
|
||||
if (ariaNode.children.length)
|
||||
if (!options?.noText && ariaNode.children.length)
|
||||
line += ': ' + escapeYamlString(ariaNode.children?.[0] as string);
|
||||
lines.push(line);
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export function findRepeatedSubsequences(s: string[]): { sequence: string[]; count: number }[] {
|
||||
const n = s.length;
|
||||
const result = [];
|
||||
let i = 0;
|
||||
|
||||
const arraysEqual = (a1: string[], a2: string[]) => {
|
||||
if (a1.length !== a2.length) return false;
|
||||
for (let j = 0; j < a1.length; j++) {
|
||||
if (a1[j] !== a2[j]) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
while (i < n) {
|
||||
let maxRepeatCount = 1;
|
||||
let maxRepeatSubstr = [s[i]]; // Initialize with the element at index i
|
||||
let maxRepeatLength = 1;
|
||||
|
||||
// Try substrings of length from 1 to the remaining length of the array
|
||||
for (let p = 1; p <= n - i; p++) {
|
||||
const substr = s.slice(i, i + p); // Extract substring as array
|
||||
let k = 1;
|
||||
|
||||
// Count how many times the substring repeats consecutively
|
||||
while (
|
||||
i + p * k <= n &&
|
||||
arraysEqual(s.slice(i + p * (k - 1), i + p * k), substr)
|
||||
) {
|
||||
k += 1;
|
||||
}
|
||||
k -= 1; // Adjust k since it increments one extra time in the loop
|
||||
|
||||
// Update the maximal repeating substring if necessary
|
||||
if (k > 1 && (k * p) > (maxRepeatCount * maxRepeatLength)) {
|
||||
maxRepeatCount = k;
|
||||
maxRepeatSubstr = substr;
|
||||
maxRepeatLength = p;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the substring and its count
|
||||
result.push({ sequence: maxRepeatSubstr, count: maxRepeatCount });
|
||||
i += maxRepeatLength * maxRepeatCount; // Move index forward
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -19,6 +19,7 @@ import { parseStackTraceLine } from '../utilsBundle';
|
|||
import { isUnderTest } from './';
|
||||
import type { StackFrame } from '@protocol/channels';
|
||||
import { colors } from '../utilsBundle';
|
||||
import { findRepeatedSubsequences } from './sequence';
|
||||
|
||||
export function rewriteErrorMessage<E extends Error>(e: E, newMessage: string): E {
|
||||
const lines: string[] = (e.stack?.split('\n') || []).filter(l => l.startsWith(' at '));
|
||||
|
@ -132,9 +133,26 @@ export function splitErrorMessage(message: string): { name: string, message: str
|
|||
export function formatCallLog(log: string[] | undefined): string {
|
||||
if (!log || !log.some(l => !!l))
|
||||
return '';
|
||||
|
||||
const lines: string[] = [];
|
||||
|
||||
for (const block of findRepeatedSubsequences(log)) {
|
||||
for (let i = 0; i < block.sequence.length; i++) {
|
||||
const line = block.sequence[i];
|
||||
const leadingWhitespace = line.match(/^\s*/);
|
||||
const whitespacePrefix = ' ' + leadingWhitespace?.[0] || '';
|
||||
const countPrefix = `${block.count} × `;
|
||||
if (block.count > 1 && i === 0)
|
||||
lines.push(whitespacePrefix + countPrefix + line.trim());
|
||||
else if (block.count > 1)
|
||||
lines.push(whitespacePrefix + ' '.repeat(countPrefix.length - 2) + '- ' + line.trim());
|
||||
else
|
||||
lines.push(whitespacePrefix + '- ' + line.trim());
|
||||
}
|
||||
}
|
||||
return `
|
||||
Call log:
|
||||
${colors.dim('- ' + (log || []).join('\n - '))}
|
||||
${colors.dim(lines.join('\n'))}
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
|
@ -159,12 +159,15 @@ export const TestListView: React.FC<{
|
|||
rootItem={testTree.rootItem}
|
||||
dataTestId='test-tree'
|
||||
render={treeItem => {
|
||||
return <div className='hbox ui-mode-tree-item'>
|
||||
<div className='ui-mode-tree-item-title'>
|
||||
const prefixId = treeItem.id.replace(/[^\w\d-_]/g, '-');
|
||||
const labelId = prefixId + '-label';
|
||||
const timeId = prefixId + '-time';
|
||||
return <div className='hbox ui-mode-tree-item' aria-labelledby={`${labelId} ${timeId}`}>
|
||||
<div id={labelId} className='ui-mode-tree-item-title'>
|
||||
<span>{treeItem.title}</span>
|
||||
{treeItem.kind === 'case' ? treeItem.tags.map(tag => <TagView key={tag} tag={tag.slice(1)} onClick={e => handleTagClick(e, tag)} />) : null}
|
||||
</div>
|
||||
{!!treeItem.duration && treeItem.status !== 'skipped' && <div className='ui-mode-tree-item-time'>{msToString(treeItem.duration)}</div>}
|
||||
{!!treeItem.duration && treeItem.status !== 'skipped' && <div id={timeId} className='ui-mode-tree-item-time'>{msToString(treeItem.duration)}</div>}
|
||||
<Toolbar noMinHeight={true} noShadow={true}>
|
||||
<ToolbarButton icon='play' title='Run' onClick={() => runTreeItem(treeItem)} disabled={!!runningState && !runningState.completed}></ToolbarButton>
|
||||
<ToolbarButton icon='go-to-file' title='Show source' onClick={onRevealSource} style={(treeItem.kind === 'group' && treeItem.subKind === 'folder') ? { visibility: 'hidden' } : {}}></ToolbarButton>
|
||||
|
|
|
@ -249,8 +249,9 @@ export function TreeItemHeader<T extends TreeItem>({
|
|||
const rendered = render(item);
|
||||
const children = expanded && item.children.length ? item.children as T[] : [];
|
||||
const titled = title?.(item);
|
||||
const iconed = icon?.(item) || 'codicon-blank';
|
||||
|
||||
return <div ref={itemRef} role='treeitem' aria-selected={item === selectedItem} aria-expanded={expanded} aria-label={titled} title={titled} className='vbox' style={{ flex: 'none' }}>
|
||||
return <div ref={itemRef} role='treeitem' aria-selected={item === selectedItem} aria-expanded={expanded} title={titled} className='vbox' style={{ flex: 'none' }}>
|
||||
<div
|
||||
onDoubleClick={() => onAccepted?.(item)}
|
||||
className={clsx(
|
||||
|
@ -277,10 +278,10 @@ export function TreeItemHeader<T extends TreeItem>({
|
|||
toggleExpanded(item);
|
||||
}}
|
||||
/>
|
||||
{icon && <div className={'codicon ' + (icon(item) || 'codicon-blank')} style={{ minWidth: 16, marginRight: 4 }} aria-hidden='true'></div>}
|
||||
{icon && <div className={'codicon ' + iconed} style={{ minWidth: 16, marginRight: 4 }} aria-label={'[' + iconed.replace('codicon', 'icon') + ']'}></div>}
|
||||
{typeof rendered === 'string' ? <div style={{ textOverflow: 'ellipsis', overflow: 'hidden' }}>{rendered}</div> : rendered}
|
||||
</div>
|
||||
{!!children.length && <div aria-label='group'>
|
||||
{!!children.length && <div role='group'>
|
||||
{children.map(child => {
|
||||
const itemData = treeItems.get(child);
|
||||
return itemData && <TreeItemHeader
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { test as it, expect } from '@playwright/test';
|
||||
import { findRepeatedSubsequences } from '../../packages/playwright-core/lib/utils/sequence';
|
||||
|
||||
it('should return an empty array when the input is empty', () => {
|
||||
const input = [];
|
||||
const expectedOutput = [];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle a single-element array', () => {
|
||||
const input = ['a'];
|
||||
const expectedOutput = [{ sequence: ['a'], count: 1 }];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle an array with no repeats', () => {
|
||||
const input = ['a', 'b', 'c'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a'], count: 1 },
|
||||
{ sequence: ['b'], count: 1 },
|
||||
{ sequence: ['c'], count: 1 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle contiguous repeats of single elements', () => {
|
||||
const input = ['a', 'a', 'a', 'b', 'b', 'c'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a'], count: 3 },
|
||||
{ sequence: ['b'], count: 2 },
|
||||
{ sequence: ['c'], count: 1 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should detect longer repeating substrings', () => {
|
||||
const input = ['a', 'b', 'a', 'b', 'a', 'b'];
|
||||
const expectedOutput = [{ sequence: ['a', 'b'], count: 3 }];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle multiple repeating substrings', () => {
|
||||
const input = ['a', 'a', 'b', 'b', 'a', 'a', 'b', 'b'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a', 'a', 'b', 'b'], count: 2 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle complex cases with overlapping repeats', () => {
|
||||
const input = ['a', 'a', 'a', 'a'];
|
||||
const expectedOutput = [{ sequence: ['a'], count: 4 }];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle complex acceptance cases with multiple possible repeats', () => {
|
||||
const input = ['a', 'a', 'b', 'b', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a', 'a', 'b', 'b'], count: 2 },
|
||||
{ sequence: ['c'], count: 4 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle non-repeating sequences correctly', () => {
|
||||
const input = ['a', 'b', 'c', 'd', 'e'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a'], count: 1 },
|
||||
{ sequence: ['b'], count: 1 },
|
||||
{ sequence: ['c'], count: 1 },
|
||||
{ sequence: ['d'], count: 1 },
|
||||
{ sequence: ['e'], count: 1 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle a case where the entire array is a repeating sequence', () => {
|
||||
const input = ['x', 'y', 'x', 'y', 'x', 'y'];
|
||||
const expectedOutput = [{ sequence: ['x', 'y'], count: 3 }];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should correctly identify the maximal repeating substring', () => {
|
||||
const input = ['a', 'b', 'a', 'b', 'a', 'b', 'c', 'c', 'c', 'c'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a', 'b'], count: 3 },
|
||||
{ sequence: ['c'], count: 4 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle repeats with varying lengths', () => {
|
||||
const input = ['a', 'a', 'b', 'b', 'b', 'b', 'a', 'a'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a'], count: 2 },
|
||||
{ sequence: ['b'], count: 4 },
|
||||
{ sequence: ['a'], count: 2 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should correctly handle a repeat count of one (k adjustment to zero)', () => {
|
||||
const input = ['a', 'b', 'a', 'b', 'c'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a', 'b'], count: 2 },
|
||||
{ sequence: ['c'], count: 1 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should correctly handle repeats at the end of the array', () => {
|
||||
const input = ['x', 'y', 'x', 'y', 'x', 'y', 'z'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['x', 'y'], count: 3 },
|
||||
{ sequence: ['z'], count: 1 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should not overcount repeats when the last potential repeat is incomplete', () => {
|
||||
const input = ['m', 'n', 'm', 'n', 'm'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['m', 'n'], count: 2 },
|
||||
{ sequence: ['m'], count: 1 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
it('should handle single repeats correctly when the substring length is greater than one', () => {
|
||||
const input = ['a', 'b', 'c', 'a', 'b', 'd'];
|
||||
const expectedOutput = [
|
||||
{ sequence: ['a'], count: 1 },
|
||||
{ sequence: ['b'], count: 1 },
|
||||
{ sequence: ['c'], count: 1 },
|
||||
{ sequence: ['a'], count: 1 },
|
||||
{ sequence: ['b'], count: 1 },
|
||||
{ sequence: ['d'], count: 1 },
|
||||
];
|
||||
expect(findRepeatedSubsequences(input)).toEqual(expectedOutput);
|
||||
});
|
|
@ -5,15 +5,15 @@
|
|||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.49.0-alpha-2024-10-17"
|
||||
"@playwright/test": "1.49.0-alpha-2024-10-20"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.49.0-alpha-2024-10-17",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-17.tgz",
|
||||
"integrity": "sha512-HLZY3sM6xt9Wi8K09zPwjJQtcUBZNBcNSIVoMZhtJM3+TikCKx4SiJ3P8vbSlk7Tm3s2oqlS+wA181IxhbTGBA==",
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"playwright": "1.49.0-alpha-2024-10-17"
|
||||
"playwright": "1.49.0-alpha-2024-10-20"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
@ -36,11 +36,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.49.0-alpha-2024-10-17",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-17.tgz",
|
||||
"integrity": "sha512-IgcLunnpocVS/AEq2lcftVOu0DGQzFm1Qt25SCJsrVvKVe83ElKXZYskPz7yA0HeuOVxQyN69EDWI09ph7lfoQ==",
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.49.0-alpha-2024-10-17"
|
||||
"playwright-core": "1.49.0-alpha-2024-10-20"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
@ -53,9 +53,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.49.0-alpha-2024-10-17",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-17.tgz",
|
||||
"integrity": "sha512-XLTKmPBm2ZIOXBckXtiimSOIjQsYy8MqEP9CsHSgytsP0E+j/44v1BuwHOOMaG8sfjcuZLZ1QdFidnl07A9wSg==",
|
||||
"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==",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
|
@ -66,11 +66,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@playwright/test": {
|
||||
"version": "1.49.0-alpha-2024-10-17",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-alpha-2024-10-17.tgz",
|
||||
"integrity": "sha512-HLZY3sM6xt9Wi8K09zPwjJQtcUBZNBcNSIVoMZhtJM3+TikCKx4SiJ3P8vbSlk7Tm3s2oqlS+wA181IxhbTGBA==",
|
||||
"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==",
|
||||
"requires": {
|
||||
"playwright": "1.49.0-alpha-2024-10-17"
|
||||
"playwright": "1.49.0-alpha-2024-10-20"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
|
@ -80,18 +80,18 @@
|
|||
"optional": true
|
||||
},
|
||||
"playwright": {
|
||||
"version": "1.49.0-alpha-2024-10-17",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-alpha-2024-10-17.tgz",
|
||||
"integrity": "sha512-IgcLunnpocVS/AEq2lcftVOu0DGQzFm1Qt25SCJsrVvKVe83ElKXZYskPz7yA0HeuOVxQyN69EDWI09ph7lfoQ==",
|
||||
"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==",
|
||||
"requires": {
|
||||
"fsevents": "2.3.2",
|
||||
"playwright-core": "1.49.0-alpha-2024-10-17"
|
||||
"playwright-core": "1.49.0-alpha-2024-10-20"
|
||||
}
|
||||
},
|
||||
"playwright-core": {
|
||||
"version": "1.49.0-alpha-2024-10-17",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-alpha-2024-10-17.tgz",
|
||||
"integrity": "sha512-XLTKmPBm2ZIOXBckXtiimSOIjQsYy8MqEP9CsHSgytsP0E+j/44v1BuwHOOMaG8sfjcuZLZ1QdFidnl07A9wSg=="
|
||||
"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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.49.0-alpha-2024-10-17"
|
||||
"@playwright/test": "1.49.0-alpha-2024-10-20"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,22 +61,25 @@ test('should run visible', async ({ runUITest }) => {
|
|||
⊘ skipped
|
||||
`);
|
||||
|
||||
// await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
// - tree:
|
||||
// - treeitem "a.test.ts" [expanded]:
|
||||
// - treeitem "passes"
|
||||
// - treeitem "fails" [selected]:
|
||||
// - button "Run"
|
||||
// - button "Show source"
|
||||
// - button "Watch"
|
||||
// - treeitem "suite"
|
||||
// - treeitem "b.test.ts" [expanded]:
|
||||
// - treeitem "passes"
|
||||
// - treeitem "fails"
|
||||
// - treeitem "c.test.ts" [expanded]:
|
||||
// - treeitem "passes"
|
||||
// - treeitem "skipped"
|
||||
// `);
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-error] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- treeitem "[icon-error] suite"
|
||||
- treeitem "[icon-error] b.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/}
|
||||
- treeitem "[icon-check] c.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem "[icon-circle-slash] skipped"
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('4/8 passed (50%)');
|
||||
});
|
||||
|
@ -117,6 +120,17 @@ test('should run on hover', async ({ runUITest }) => {
|
|||
✅ passes <=
|
||||
◯ fails
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- treeitem "[icon-circle-outline] fails"
|
||||
`);
|
||||
});
|
||||
|
||||
test('should run on double click', async ({ runUITest }) => {
|
||||
|
@ -135,6 +149,17 @@ test('should run on double click', async ({ runUITest }) => {
|
|||
✅ passes <=
|
||||
◯ fails
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- treeitem "[icon-circle-outline] fails"
|
||||
`);
|
||||
});
|
||||
|
||||
test('should run on Enter', async ({ runUITest }) => {
|
||||
|
@ -154,6 +179,17 @@ test('should run on Enter', async ({ runUITest }) => {
|
|||
◯ passes
|
||||
❌ fails <=
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-error] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem "[icon-circle-outline] passes"
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
`);
|
||||
});
|
||||
|
||||
test('should run by project', async ({ runUITest }) => {
|
||||
|
@ -185,6 +221,26 @@ test('should run by project', async ({ runUITest }) => {
|
|||
⊘ skipped
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-error] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- treeitem "[icon-error] suite"
|
||||
- treeitem "[icon-error] b.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/}
|
||||
- treeitem "[icon-check] c.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem "[icon-circle-slash] skipped"
|
||||
`);
|
||||
|
||||
await page.getByText('Status:').click();
|
||||
await page.getByLabel('bar').setChecked(true);
|
||||
|
||||
|
@ -203,6 +259,29 @@ test('should run by project', async ({ runUITest }) => {
|
|||
► ◯ skipped
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-error] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-circle-outline\] passes/}
|
||||
- treeitem ${/\[icon-error\] fails/}:
|
||||
- group:
|
||||
- treeitem ${/\[icon-error\] foo/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- treeitem "[icon-circle-outline] bar"
|
||||
- treeitem "[icon-error] suite"
|
||||
- treeitem "[icon-error] b.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-circle-outline\] passes/}
|
||||
- treeitem ${/\[icon-error\] fails/}
|
||||
- treeitem "[icon-circle-outline] c.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-circle-outline\] passes/}
|
||||
- treeitem ${/\[icon-circle-outline\] skipped/}
|
||||
`);
|
||||
|
||||
await page.getByText('Status:').click();
|
||||
|
||||
await page.getByTestId('test-tree').getByText('passes').first().click();
|
||||
|
@ -216,6 +295,20 @@ test('should run by project', async ({ runUITest }) => {
|
|||
► ❌ fails
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-error] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-circle-outline\] passes \d+ms/} [expanded] [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] foo \d+ms/}
|
||||
- treeitem ${/\[icon-circle-outline\] bar/}
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/}
|
||||
`);
|
||||
|
||||
await expect(page.getByText('Projects: foo bar')).toBeVisible();
|
||||
|
||||
await page.getByTitle('Run all').click();
|
||||
|
@ -235,6 +328,32 @@ test('should run by project', async ({ runUITest }) => {
|
|||
► ✅ passes
|
||||
► ⊘ skipped
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-error] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/} [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] foo \d+ms/}
|
||||
- treeitem ${/\[icon-check\] bar \d+ms/}
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/} [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-error\] foo \d+ms/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- treeitem ${/\[icon-error\] bar \d+ms/}
|
||||
- treeitem ${/\[icon-error\] suite/}
|
||||
- treeitem "[icon-error] b.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes/}
|
||||
- treeitem ${/\[icon-error\] fails/}
|
||||
- treeitem "[icon-check] c.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes/}
|
||||
- treeitem ${/\[icon-circle-slash\] skipped/}
|
||||
`);
|
||||
});
|
||||
|
||||
test('should stop', async ({ runUITest }) => {
|
||||
|
@ -261,6 +380,16 @@ test('should stop', 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 \d+ms/}
|
||||
- treeitem ${/\[icon-loading\] test 2/}
|
||||
- treeitem ${/\[icon-clock\] test 3/}
|
||||
`);
|
||||
|
||||
await expect(page.getByTitle('Run all')).toBeDisabled();
|
||||
await expect(page.getByTitle('Stop')).toBeEnabled();
|
||||
|
||||
|
@ -273,6 +402,16 @@ test('should stop', 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-slash] test 0"
|
||||
- treeitem ${/\[icon-check\] test 1 \d+ms/}
|
||||
- treeitem ${/\[icon-circle-outline\] test 2/}
|
||||
- treeitem ${/\[icon-circle-outline\] test 3/}
|
||||
`);
|
||||
});
|
||||
|
||||
test('should run folder', async ({ runUITest }) => {
|
||||
|
@ -301,6 +440,17 @@ test('should run folder', async ({ runUITest }) => {
|
|||
▼ ◯ in-a.test.ts
|
||||
◯ passes
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-check] folder-b" [expanded] [selected]:
|
||||
- group:
|
||||
- treeitem "[icon-check] folder-c"
|
||||
- treeitem "[icon-check] in-b.test.ts"
|
||||
- treeitem "[icon-circle-outline] in-a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem "[icon-circle-outline] passes"
|
||||
`);
|
||||
});
|
||||
|
||||
test('should show time', async ({ runUITest }) => {
|
||||
|
@ -324,6 +474,26 @@ test('should show time', async ({ runUITest }) => {
|
|||
⊘ skipped
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-error] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- treeitem "[icon-error] suite"
|
||||
- treeitem "[icon-error] b.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem ${/\[icon-error\] fails \d+ms/}
|
||||
- treeitem "[icon-check] c.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] passes \d+ms/}
|
||||
- treeitem "[icon-circle-slash] skipped"
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('4/8 passed (50%)');
|
||||
});
|
||||
|
||||
|
@ -348,6 +518,13 @@ test('should show test.fail as passing', async ({ runUITest }) => {
|
|||
✅ should fail XXms
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-check] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] should fail \d+ms/}
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||
});
|
||||
|
||||
|
@ -377,6 +554,13 @@ test('should ignore repeatEach', async ({ runUITest }) => {
|
|||
✅ should pass
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-check] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] should pass \d+ms/}
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||
});
|
||||
|
||||
|
@ -404,6 +588,14 @@ test('should remove output folder before test run', async ({ runUITest }) => {
|
|||
▼ ✅ a.test.ts
|
||||
✅ should pass
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-check] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] should pass \d+ms/}
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||
|
||||
await page.getByTitle('Run all').click();
|
||||
|
@ -411,6 +603,14 @@ test('should remove output folder before test run', async ({ runUITest }) => {
|
|||
▼ ✅ a.test.ts
|
||||
✅ should pass
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-check] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] should pass \d+ms/}
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||
});
|
||||
|
||||
|
@ -451,6 +651,18 @@ test('should show proper total when using deps', async ({ runUITest }) => {
|
|||
✅ run @setup <=
|
||||
◯ run @chromium
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-circle-outline] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] run @setup setup \d+ms/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
- treeitem "[icon-circle-outline] run @chromium chromium"
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||
|
||||
await page.getByTitle('run @chromium').dblclick();
|
||||
|
@ -459,6 +671,18 @@ test('should show proper total when using deps', async ({ runUITest }) => {
|
|||
✅ run @setup
|
||||
✅ run @chromium <=
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-check] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] run @setup setup \d+ms/}
|
||||
- treeitem ${/\[icon-check\] run @chromium chromium \d+ms/} [selected]:
|
||||
- button "Run"
|
||||
- button "Show source"
|
||||
- button "Watch"
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('2/2 passed (100%)');
|
||||
});
|
||||
|
||||
|
@ -518,6 +742,13 @@ test('should respect --tsconfig option', {
|
|||
✅ test
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-check] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] test \d+ms/}
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
|
||||
});
|
||||
|
||||
|
@ -539,4 +770,11 @@ test('should respect --ignore-snapshots option', {
|
|||
▼ ✅ a.test.ts
|
||||
✅ snapshot
|
||||
`);
|
||||
|
||||
await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(`
|
||||
- tree:
|
||||
- treeitem "[icon-check] a.test.ts" [expanded]:
|
||||
- group:
|
||||
- treeitem ${/\[icon-check\] snapshot \d+ms/}
|
||||
`);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue