chore: implement tree w/o list (#33169)

This commit is contained in:
Pavel Feldman 2024-10-18 13:50:43 -07:00 committed by GitHub
parent 6ea17a5d82
commit 2e8e7a66cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 221 additions and 142 deletions

View File

@ -49,8 +49,8 @@ export async function toMatchAriaSnapshot(
const messagePrefix = matcherHint(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined); const messagePrefix = matcherHint(this, receiver, matcherName, 'locator', undefined, matcherOptions, timedOut ? timeout : undefined);
const notFound = received === kNoElementsFoundError; const notFound = received === kNoElementsFoundError;
const escapedExpected = escapePrivateUsePoints(expected); const escapedExpected = unshift(escapePrivateUsePoints(expected));
const escapedReceived = escapePrivateUsePoints(received); const escapedReceived = unshift(escapePrivateUsePoints(received));
const message = () => { const message = () => {
if (pass) { if (pass) {
if (notFound) if (notFound)
@ -79,3 +79,17 @@ export async function toMatchAriaSnapshot(
function escapePrivateUsePoints(str: string) { function escapePrivateUsePoints(str: string) {
return str.replace(/[\uE000-\uF8FF]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`); return str.replace(/[\uE000-\uF8FF]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`);
} }
function unshift(snapshot: string): string {
const lines = snapshot.split('\n');
let whitespacePrefixLength = 100;
for (const line of lines) {
if (!line.trim())
continue;
const match = line.match(/^(\s*)/);
if (match && match[1].length < whitespacePrefixLength)
whitespacePrefixLength = match[1].length;
break;
}
return lines.filter(t => t.trim()).map(line => line.substring(whitespacePrefixLength)).join('\n');
}

View File

@ -59,6 +59,30 @@ export const ActionList: React.FC<ActionListProps> = ({
return { selectedItem }; return { selectedItem };
}, [itemMap, selectedAction]); }, [itemMap, selectedAction]);
const isError = React.useCallback((item: ActionTreeItem) => {
return !!item.action?.error?.message;
}, []);
const onAccepted = React.useCallback((item: ActionTreeItem) => {
return setSelectedTime({ minimum: item.action!.startTime, maximum: item.action!.endTime });
}, [setSelectedTime]);
const render = React.useCallback((item: ActionTreeItem) => {
return renderAction(item.action!, { sdkLanguage, revealConsole, isLive, showDuration: true, showBadges: true });
}, [isLive, revealConsole, sdkLanguage]);
const isVisible = React.useCallback((item: ActionTreeItem) => {
return !selectedTime || !item.action || (item.action!.startTime <= selectedTime.maximum && item.action!.endTime >= selectedTime.minimum);
}, [selectedTime]);
const onSelectedAction = React.useCallback((item: ActionTreeItem) => {
onSelected?.(item.action!);
}, [onSelected]);
const onHighlightedAction = React.useCallback((item: ActionTreeItem | undefined) => {
onHighlighted?.(item?.action);
}, [onHighlighted]);
return <div className='vbox'> return <div className='vbox'>
{selectedTime && <div className='action-list-show-all' onClick={() => setSelectedTime(undefined)}><span className='codicon codicon-triangle-left'></span>Show all</div>} {selectedTime && <div className='action-list-show-all' onClick={() => setSelectedTime(undefined)}><span className='codicon codicon-triangle-left'></span>Show all</div>}
<ActionTreeView <ActionTreeView
@ -67,12 +91,12 @@ export const ActionList: React.FC<ActionListProps> = ({
treeState={treeState} treeState={treeState}
setTreeState={setTreeState} setTreeState={setTreeState}
selectedItem={selectedItem} selectedItem={selectedItem}
onSelected={item => onSelected?.(item.action!)} onSelected={onSelectedAction}
onHighlighted={item => onHighlighted?.(item?.action)} onHighlighted={onHighlightedAction}
onAccepted={item => setSelectedTime({ minimum: item.action!.startTime, maximum: item.action!.endTime })} onAccepted={onAccepted}
isError={item => !!item.action?.error?.message} isError={isError}
isVisible={item => !selectedTime || (item.action!.startTime <= selectedTime.maximum && item.action!.endTime >= selectedTime.minimum)} isVisible={isVisible}
render={item => renderAction(item.action!, { sdkLanguage, revealConsole, isLive, showDuration: true, showBadges: true })} render={render}
/> />
</div>; </div>;
}; };

View File

@ -161,7 +161,7 @@ export const TestListView: React.FC<{
render={treeItem => { render={treeItem => {
return <div className='hbox ui-mode-tree-item'> return <div className='hbox ui-mode-tree-item'>
<div className='ui-mode-tree-item-title'> <div className='ui-mode-tree-item-title'>
<span title={treeItem.title}>{treeItem.title}</span> <span>{treeItem.title}</span>
{treeItem.kind === 'case' ? treeItem.tags.map(tag => <TagView key={tag} tag={tag.slice(1)} onClick={e => handleTagClick(e, tag)} />) : null} {treeItem.kind === 'case' ? treeItem.tags.map(tag => <TagView key={tag} tag={tag.slice(1)} onClick={e => handleTagClick(e, tag)} />) : null}
</div> </div>
{!!treeItem.duration && treeItem.status !== 'skipped' && <div className='ui-mode-tree-item-time'>{msToString(treeItem.duration)}</div>} {!!treeItem.duration && treeItem.status !== 'skipped' && <div className='ui-mode-tree-item-time'>{msToString(treeItem.duration)}</div>}
@ -179,6 +179,7 @@ export const TestListView: React.FC<{
</div>; </div>;
}} }}
icon={treeItem => testStatusIcon(treeItem.status)} icon={treeItem => testStatusIcon(treeItem.status)}
title={treeItem => treeItem.title}
selectedItem={selectedTreeItem} selectedItem={selectedTreeItem}
onAccepted={runTreeItem} onAccepted={runTreeItem}
onSelected={treeItem => { onSelected={treeItem => {

View File

@ -52,7 +52,7 @@ export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps>
disabled={!!disabled} disabled={!!disabled}
style={style} style={style}
data-testid={testId} data-testid={testId}
aria-label={ariaLabel} aria-label={ariaLabel || title}
> >
{icon && <span className={`codicon codicon-${icon}`} style={children ? { marginRight: 5 } : {}}></span>} {icon && <span className={`codicon codicon-${icon}`} style={children ? { marginRight: 5 } : {}}></span>}
{children} {children}

View File

@ -32,6 +32,7 @@ export type TreeViewProps<T> = {
name: string, name: string,
rootItem: T, rootItem: T,
render: (item: T) => React.ReactNode, render: (item: T) => React.ReactNode,
title?: (item: T) => string,
icon?: (item: T) => string | undefined, icon?: (item: T) => string | undefined,
isError?: (item: T) => boolean, isError?: (item: T) => boolean,
isVisible?: (item: T) => boolean, isVisible?: (item: T) => boolean,
@ -52,6 +53,7 @@ export function TreeView<T extends TreeItem>({
name, name,
rootItem, rootItem,
render, render,
title,
icon, icon,
isError, isError,
isVisible, isVisible,
@ -66,40 +68,12 @@ export function TreeView<T extends TreeItem>({
autoExpandDepth, autoExpandDepth,
}: TreeViewProps<T>) { }: TreeViewProps<T>) {
const treeItems = React.useMemo(() => { const treeItems = React.useMemo(() => {
return flattenTree<T>(rootItem, selectedItem, treeState.expandedItems, autoExpandDepth || 0); return indexTree<T>(rootItem, selectedItem, treeState.expandedItems, autoExpandDepth || 0, isVisible);
}, [rootItem, selectedItem, treeState, autoExpandDepth]); }, [rootItem, selectedItem, treeState, autoExpandDepth, isVisible]);
// Filter visible items.
const visibleItems = React.useMemo(() => {
if (!isVisible)
return [...treeItems.keys()];
const cachedVisible = new Map<TreeItem, boolean>();
const visit = (item: TreeItem): boolean => {
const cachedResult = cachedVisible.get(item);
if (cachedResult !== undefined)
return cachedResult;
let hasVisibleChildren = item.children.some(child => visit(child));
for (const child of item.children) {
const result = visit(child);
hasVisibleChildren = hasVisibleChildren || result;
}
const result = isVisible(item as T) || hasVisibleChildren;
cachedVisible.set(item, result);
return result;
};
for (const item of treeItems.keys())
visit(item);
const result: T[] = [];
for (const item of treeItems.keys()) {
if (isVisible(item))
result.push(item);
}
return result;
}, [treeItems, isVisible]);
const itemListRef = React.useRef<HTMLDivElement>(null); const itemListRef = React.useRef<HTMLDivElement>(null);
const [highlightedItem, setHighlightedItem] = React.useState<any>(); const [highlightedItem, setHighlightedItem] = React.useState<any>();
const [isKeyboardNavigation, setIsKeyboardNavigation] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
onHighlighted?.(highlightedItem); onHighlighted?.(highlightedItem);
@ -171,45 +145,55 @@ export function TreeView<T extends TreeItem>({
return; return;
} }
const index = selectedItem ? visibleItems.indexOf(selectedItem) : -1; let newSelectedItem: T | undefined = selectedItem;
let newIndex = index;
if (event.key === 'ArrowDown') { if (event.key === 'ArrowDown') {
if (index === -1) if (selectedItem) {
newIndex = 0; const itemData = treeItems.get(selectedItem)!;
else newSelectedItem = itemData.next as T;
newIndex = Math.min(index + 1, visibleItems.length - 1); } else if (treeItems.size) {
const itemList = [...treeItems.keys()];
newSelectedItem = itemList[0];
}
} }
if (event.key === 'ArrowUp') { if (event.key === 'ArrowUp') {
if (index === -1) if (selectedItem) {
newIndex = visibleItems.length - 1; const itemData = treeItems.get(selectedItem)!;
else newSelectedItem = itemData.prev as T;
newIndex = Math.max(index - 1, 0); } else if (treeItems.size) {
const itemList = [...treeItems.keys()];
newSelectedItem = itemList[itemList.length - 1];
}
} }
const element = itemListRef.current?.children.item(newIndex); // scrollIntoViewIfNeeded(element || undefined);
scrollIntoViewIfNeeded(element || undefined);
onHighlighted?.(undefined); onHighlighted?.(undefined);
onSelected?.(visibleItems[newIndex]); if (newSelectedItem) {
setIsKeyboardNavigation(true);
onSelected?.(newSelectedItem);
}
setHighlightedItem(undefined); setHighlightedItem(undefined);
}} }}
ref={itemListRef} ref={itemListRef}
> >
{noItemsMessage && visibleItems.length === 0 && <div className='tree-view-empty'>{noItemsMessage}</div>} {noItemsMessage && treeItems.size === 0 && <div className='tree-view-empty'>{noItemsMessage}</div>}
{visibleItems.map(item => { {rootItem.children.map(child => {
return <div key={item.id} role='treeitem' aria-selected={item === selectedItem}> const itemData = treeItems.get(child as T);
<TreeItemHeader return itemData && <TreeItemHeader
item={item} key={child.id}
itemData={treeItems.get(item)!} item={child}
selectedItem={selectedItem} treeItems={treeItems}
onSelected={onSelected} selectedItem={selectedItem}
onAccepted={onAccepted} onSelected={onSelected}
isError={isError} onAccepted={onAccepted}
toggleExpanded={toggleExpanded} isError={isError}
highlightedItem={highlightedItem} toggleExpanded={toggleExpanded}
setHighlightedItem={setHighlightedItem} highlightedItem={highlightedItem}
render={render} setHighlightedItem={setHighlightedItem}
icon={icon} /> render={render}
</div>; icon={icon}
title={title}
isKeyboardNavigation={isKeyboardNavigation}
setIsKeyboardNavigation={setIsKeyboardNavigation} />;
})} })}
</div> </div>
</div>; </div>;
@ -217,7 +201,7 @@ export function TreeView<T extends TreeItem>({
type TreeItemHeaderProps<T> = { type TreeItemHeaderProps<T> = {
item: T, item: T,
itemData: TreeItemData, treeItems: Map<T, TreeItemData>,
selectedItem: T | undefined, selectedItem: T | undefined,
onSelected?: (item: T) => void, onSelected?: (item: T) => void,
toggleExpanded: (item: T) => void, toggleExpanded: (item: T) => void,
@ -226,12 +210,15 @@ type TreeItemHeaderProps<T> = {
onAccepted?: (item: T) => void, onAccepted?: (item: T) => void,
setHighlightedItem: (item: T | undefined) => void, setHighlightedItem: (item: T | undefined) => void,
render: (item: T) => React.ReactNode, render: (item: T) => React.ReactNode,
title?: (item: T) => string,
icon?: (item: T) => string | undefined, icon?: (item: T) => string | undefined,
isKeyboardNavigation: boolean,
setIsKeyboardNavigation: (value: boolean) => void,
}; };
export function TreeItemHeader<T extends TreeItem>({ export function TreeItemHeader<T extends TreeItem>({
item, item,
itemData, treeItems,
selectedItem, selectedItem,
onSelected, onSelected,
highlightedItem, highlightedItem,
@ -240,68 +227,122 @@ export function TreeItemHeader<T extends TreeItem>({
onAccepted, onAccepted,
toggleExpanded, toggleExpanded,
render, render,
icon }: TreeItemHeaderProps<T>) { title,
icon,
isKeyboardNavigation,
setIsKeyboardNavigation }: TreeItemHeaderProps<T>) {
const itemRef = React.useRef(null);
React.useEffect(() => {
if (selectedItem === item && isKeyboardNavigation && itemRef.current) {
scrollIntoViewIfNeeded(itemRef.current);
setIsKeyboardNavigation(false);
}
}, [item, selectedItem, isKeyboardNavigation, setIsKeyboardNavigation]);
const itemData = treeItems.get(item)!;
const indentation = itemData.depth; const indentation = itemData.depth;
const expanded = itemData.expanded; const expanded = itemData.expanded;
let expandIcon = 'codicon-blank'; let expandIcon = 'codicon-blank';
if (typeof expanded === 'boolean') if (typeof expanded === 'boolean')
expandIcon = expanded ? 'codicon-chevron-down' : 'codicon-chevron-right'; expandIcon = expanded ? 'codicon-chevron-down' : 'codicon-chevron-right';
const rendered = render(item); const rendered = render(item);
const children = expanded && item.children.length ? item.children as T[] : [];
const titled = title?.(item);
return <div return <div ref={itemRef} role='treeitem' aria-selected={item === selectedItem} aria-expanded={expanded} aria-label={titled} title={titled} className='vbox' style={{ flex: 'none' }}>
onDoubleClick={() => onAccepted?.(item)}
className={clsx(
'tree-view-entry',
selectedItem === item && 'selected',
highlightedItem === item && 'highlighted',
isError?.(item) && 'error')}
onClick={() => onSelected?.(item)}
onMouseEnter={() => setHighlightedItem(item)}
onMouseLeave={() => setHighlightedItem(undefined)}
>
{indentation ? new Array(indentation).fill(0).map((_, i) => <div key={'indent-' + i} className='tree-view-indent'></div>) : undefined}
<div <div
className={'codicon ' + expandIcon} onDoubleClick={() => onAccepted?.(item)}
style={{ minWidth: 16, marginRight: 4 }} className={clsx(
onDoubleClick={e => { 'tree-view-entry',
e.preventDefault(); selectedItem === item && 'selected',
e.stopPropagation(); highlightedItem === item && 'highlighted',
}} isError?.(item) && 'error')}
onClick={e => { onClick={() => onSelected?.(item)}
e.stopPropagation(); onMouseEnter={() => setHighlightedItem(item)}
e.preventDefault(); onMouseLeave={() => setHighlightedItem(undefined)}
toggleExpanded(item); >
}} {indentation ? new Array(indentation).fill(0).map((_, i) => <div key={'indent-' + i} className='tree-view-indent'></div>) : undefined}
/> <div
{icon && <div className={'codicon ' + (icon(item) || 'codicon-blank')} style={{ minWidth: 16, marginRight: 4 }}></div>} aria-hidden='true'
{typeof rendered === 'string' ? <div style={{ textOverflow: 'ellipsis', overflow: 'hidden' }}>{rendered}</div> : rendered} className={'codicon ' + expandIcon}
style={{ minWidth: 16, marginRight: 4 }}
onDoubleClick={e => {
e.preventDefault();
e.stopPropagation();
}}
onClick={e => {
e.stopPropagation();
e.preventDefault();
toggleExpanded(item);
}}
/>
{icon && <div className={'codicon ' + (icon(item) || 'codicon-blank')} style={{ minWidth: 16, marginRight: 4 }} aria-hidden='true'></div>}
{typeof rendered === 'string' ? <div style={{ textOverflow: 'ellipsis', overflow: 'hidden' }}>{rendered}</div> : rendered}
</div>
{!!children.length && <div aria-label='group'>
{children.map(child => {
const itemData = treeItems.get(child);
return itemData && <TreeItemHeader
key={child.id}
item={child}
treeItems={treeItems}
selectedItem={selectedItem}
onSelected={onSelected}
onAccepted={onAccepted}
isError={isError}
toggleExpanded={toggleExpanded}
highlightedItem={highlightedItem}
setHighlightedItem={setHighlightedItem}
render={render}
title={title}
icon={icon}
isKeyboardNavigation={isKeyboardNavigation}
setIsKeyboardNavigation={setIsKeyboardNavigation} />;
})}
</div>}
</div>; </div>;
} }
type TreeItemData = { type TreeItemData = {
depth: number, depth: number;
expanded: boolean | undefined, expanded: boolean | undefined;
parent: TreeItem | null, parent: TreeItem | null;
next: TreeItem | null;
prev: TreeItem | null;
}; };
function flattenTree<T extends TreeItem>( function indexTree<T extends TreeItem>(
rootItem: T, rootItem: T,
selectedItem: T | undefined, selectedItem: T | undefined,
expandedItems: Map<string, boolean | undefined>, expandedItems: Map<string, boolean | undefined>,
autoExpandDepth: number): Map<T, TreeItemData> { autoExpandDepth: number,
isVisible?: (item: T) => boolean): Map<T, TreeItemData> {
const result = new Map<T, TreeItemData>(); const result = new Map<T, TreeItemData>();
const temporaryExpanded = new Set<string>(); const temporaryExpanded = new Set<string>();
for (let item: TreeItem | undefined = selectedItem?.parent; item; item = item.parent) for (let item: TreeItem | undefined = selectedItem?.parent; item; item = item.parent)
temporaryExpanded.add(item.id); temporaryExpanded.add(item.id);
let lastItem: T | null = null;
const appendChildren = (parent: T, depth: number) => { const appendChildren = (parent: T, depth: number) => {
if (isVisible && !isVisible(parent))
return;
for (const item of parent.children as T[]) { for (const item of parent.children as T[]) {
const expandState = temporaryExpanded.has(item.id) || expandedItems.get(item.id); const expandState = temporaryExpanded.has(item.id) || expandedItems.get(item.id);
const autoExpandMatches = autoExpandDepth > depth && result.size < 25 && expandState !== false; const autoExpandMatches = autoExpandDepth > depth && result.size < 25 && expandState !== false;
const expanded = item.children.length ? expandState ?? autoExpandMatches : undefined; const expanded = item.children.length ? expandState ?? autoExpandMatches : undefined;
result.set(item, { depth, expanded, parent: rootItem === parent ? null : parent }); const itemData: TreeItemData = {
depth,
expanded,
parent: rootItem === parent ? null : parent,
next: null,
prev: lastItem,
};
if (lastItem)
result.get(lastItem)!.next = item;
lastItem = item;
result.set(item, itemData);
if (expanded) if (expanded)
appendChildren(item, depth + 1); appendChildren(item, depth + 1);
} }

View File

@ -181,14 +181,12 @@ test('expected formatter', async ({ page }) => {
expect(stripAnsi(error.message)).toContain(` expect(stripAnsi(error.message)).toContain(`
Locator: locator('body') Locator: locator('body')
- Expected - 4 - Expected - 2
+ Received string + 3 + Received string + 3
- - - heading "todos"
+ - banner: + - banner:
- - heading "todos"
+ - heading "todos" + - heading "todos"
- - textbox "Wrong text" - - textbox "Wrong text"
-
+ - textbox "What needs to be done?"`); + - textbox "What needs to be done?"`);
}); });

View File

@ -68,14 +68,15 @@ export function dumpTestTree(page: Page, options: { time?: boolean } = {}): () =
const result: string[] = []; const result: string[] = [];
const treeItems = treeElement.querySelectorAll('[role=treeitem]'); const treeItems = treeElement.querySelectorAll('[role=treeitem]');
for (const treeItem of treeItems) { for (const treeItem of treeItems) {
const iconElements = treeItem.querySelectorAll('.codicon'); const treeItemHeader = treeItem.querySelector('.tree-view-entry');
const iconElements = treeItemHeader.querySelectorAll('.codicon');
const treeIcon = iconName(iconElements[0]); const treeIcon = iconName(iconElements[0]);
const statusIcon = iconName(iconElements[1]); const statusIcon = iconName(iconElements[1]);
const indent = treeItem.querySelectorAll('.tree-view-indent').length; const indent = treeItemHeader.querySelectorAll('.tree-view-indent').length;
const watch = treeItem.querySelector('.toolbar-button.eye.toggled') ? ' 👁' : ''; const watch = treeItemHeader.querySelector('.toolbar-button.eye.toggled') ? ' 👁' : '';
const selected = treeItem.getAttribute('aria-selected') === 'true' ? ' <=' : ''; const selected = treeItem.getAttribute('aria-selected') === 'true' ? ' <=' : '';
const title = treeItem.querySelector('.ui-mode-tree-item-title').childNodes[0].textContent; const title = treeItemHeader.querySelector('.ui-mode-tree-item-title').childNodes[0].textContent;
const timeElement = options.time ? treeItem.querySelector('.ui-mode-tree-item-time') : undefined; const timeElement = options.time ? treeItemHeader.querySelector('.ui-mode-tree-item-time') : undefined;
const time = timeElement ? ' ' + timeElement.textContent.replace(/[.\d]+m?s/, 'XXms') : ''; const time = timeElement ? ' ' + timeElement.textContent.replace(/[.\d]+m?s/, 'XXms') : '';
result.push(' ' + ' '.repeat(indent) + treeIcon + ' ' + statusIcon + ' ' + title + time + watch + selected); result.push(' ' + ' '.repeat(indent) + treeIcon + ' ' + statusIcon + ' ' + title + time + watch + selected);
} }

View File

@ -33,7 +33,7 @@ test('should display annotations', async ({ runUITest }) => {
}); });
await page.getByTitle('Run all').click(); await page.getByTitle('Run all').click();
await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)');
await page.getByRole('treeitem').filter({ hasText: 'suite' }).locator('.codicon-chevron-right').click(); await page.getByRole('treeitem', { name: 'suite' }).locator('.codicon-chevron-right').click();
await page.getByText('annotation test').click(); await page.getByText('annotation test').click();
await page.getByText('Annotations', { exact: true }).click(); await page.getByText('Annotations', { exact: true }).click();

View File

@ -93,7 +93,7 @@ test('should run on hover', async ({ runUITest }) => {
}); });
await page.getByText('passes').hover(); await page.getByText('passes').hover();
await page.getByRole('treeitem').filter({ hasText: 'passes' }).getByTitle('Run').click(); await page.getByRole('treeitem', { name: 'passes' }).getByRole('button', { name: 'Run' }).click();
await expect.poll(dumpTestTree(page)).toBe(` await expect.poll(dumpTestTree(page)).toBe(`
a.test.ts a.test.ts
@ -275,7 +275,7 @@ test('should run folder', async ({ runUITest }) => {
}); });
await page.getByText('folder-b').hover(); await page.getByText('folder-b').hover();
await page.getByRole('treeitem').filter({ hasText: 'folder-b' }).getByTitle('Run').click(); await page.getByRole('treeitem', { name: 'folder-b' }).getByRole('button', { name: 'Run' }).click();
await expect.poll(dumpTestTree(page)).toContain(` await expect.poll(dumpTestTree(page)).toContain(`
folder-b <= folder-b <=
@ -421,8 +421,8 @@ test('should show proper total when using deps', async ({ runUITest }) => {
await page.getByText('Status:').click(); await page.getByText('Status:').click();
await page.getByLabel('setup').setChecked(true); await page.getByRole('checkbox', { name: 'setup' }).setChecked(true);
await page.getByLabel('chromium').setChecked(true); await page.getByRole('checkbox', { name: 'chromium' }).setChecked(true);
await expect.poll(dumpTestTree(page)).toContain(` await expect.poll(dumpTestTree(page)).toContain(`
a.test.ts a.test.ts

View File

@ -140,9 +140,9 @@ const testsWithSetup = {
test('should run setup and teardown projects (1)', async ({ runUITest }) => { test('should run setup and teardown projects (1)', async ({ runUITest }) => {
const { page } = await runUITest(testsWithSetup); const { page } = await runUITest(testsWithSetup);
await page.getByText('Status:').click(); await page.getByText('Status:').click();
await page.getByLabel('setup').setChecked(false); await page.getByRole('checkbox', { name: 'setup' }).setChecked(false);
await page.getByLabel('teardown').setChecked(false); await page.getByRole('checkbox', { name: 'teardown' }).setChecked(false);
await page.getByLabel('test').setChecked(false); await page.getByRole('checkbox', { name: 'test' }).setChecked(false);
await page.getByTitle('Run all').click(); await page.getByTitle('Run all').click();
@ -164,9 +164,9 @@ test('should run setup and teardown projects (1)', async ({ runUITest }) => {
test('should run setup and teardown projects (2)', async ({ runUITest }) => { test('should run setup and teardown projects (2)', async ({ runUITest }) => {
const { page } = await runUITest(testsWithSetup); const { page } = await runUITest(testsWithSetup);
await page.getByText('Status:').click(); await page.getByText('Status:').click();
await page.getByLabel('setup').setChecked(false); await page.getByRole('checkbox', { name: 'setup' }).setChecked(false);
await page.getByLabel('teardown').setChecked(true); await page.getByRole('checkbox', { name: 'teardown' }).setChecked(true);
await page.getByLabel('test').setChecked(true); await page.getByRole('checkbox', { name: 'test' }).setChecked(true);
await page.getByTitle('Run all').click(); await page.getByTitle('Run all').click();
@ -186,9 +186,9 @@ test('should run setup and teardown projects (2)', async ({ runUITest }) => {
test('should run setup and teardown projects (3)', async ({ runUITest }) => { test('should run setup and teardown projects (3)', async ({ runUITest }) => {
const { page } = await runUITest(testsWithSetup); const { page } = await runUITest(testsWithSetup);
await page.getByText('Status:').click(); await page.getByText('Status:').click();
await page.getByLabel('setup').setChecked(false); await page.getByRole('checkbox', { name: 'setup' }).setChecked(false);
await page.getByLabel('teardown').setChecked(false); await page.getByRole('checkbox', { name: 'teardown' }).setChecked(false);
await page.getByLabel('test').setChecked(true); await page.getByRole('checkbox', { name: 'test' }).setChecked(true);
await page.getByTitle('Run all').click(); await page.getByTitle('Run all').click();
@ -206,12 +206,12 @@ test('should run setup and teardown projects (3)', async ({ runUITest }) => {
test('should run part of the setup only', async ({ runUITest }) => { test('should run part of the setup only', async ({ runUITest }) => {
const { page } = await runUITest(testsWithSetup); const { page } = await runUITest(testsWithSetup);
await page.getByText('Status:').click(); await page.getByText('Status:').click();
await page.getByLabel('setup').setChecked(true); await page.getByRole('checkbox', { name: 'setup' }).setChecked(true);
await page.getByLabel('teardown').setChecked(true); await page.getByRole('checkbox', { name: 'teardown' }).setChecked(true);
await page.getByLabel('test').setChecked(true); await page.getByRole('checkbox', { name: 'test' }).setChecked(true);
await page.getByText('setup.ts').hover(); await page.getByText('setup.ts').hover();
await page.getByRole('treeitem').filter({ hasText: 'setup.ts' }).getByTitle('Run').click(); await page.getByRole('treeitem', { name: 'setup.ts' }).getByRole('button', { name: 'Run' }).click();
await expect.poll(dumpTestTree(page)).toBe(` await expect.poll(dumpTestTree(page)).toBe(`
setup.ts <= setup.ts <=

View File

@ -215,7 +215,7 @@ test('should update test locations', async ({ runUITest, writeFiles }) => {
const messages: any[] = []; const messages: any[] = [];
await page.exposeBinding('__logForTest', (source, arg) => messages.push(arg)); await page.exposeBinding('__logForTest', (source, arg) => messages.push(arg));
const passesItemLocator = page.getByRole('treeitem').filter({ hasText: 'passes' }); const passesItemLocator = page.getByRole('treeitem', { name: 'passes' });
await passesItemLocator.hover(); await passesItemLocator.hover();
await passesItemLocator.getByTitle('Show source').click(); await passesItemLocator.getByTitle('Show source').click();
await page.getByTitle('Open in VS Code').click(); await page.getByTitle('Open in VS Code').click();

View File

@ -28,14 +28,14 @@ test('should watch files', async ({ runUITest, writeFiles }) => {
}); });
await page.getByText('fails').click(); await page.getByText('fails').click();
await page.getByRole('treeitem').filter({ hasText: 'fails' }).getByTitle('Watch').click(); await page.getByRole('treeitem', { name: 'fails' }).getByRole('button', { name: 'Watch' }).click();
await expect.poll(dumpTestTree(page)).toBe(` await expect.poll(dumpTestTree(page)).toBe(`
a.test.ts a.test.ts
passes passes
fails 👁 <= fails 👁 <=
`); `);
await page.getByRole('treeitem').filter({ hasText: 'fails' }).getByTitle('Run').click(); await page.getByRole('treeitem', { name: 'fails' }).getByRole('button', { name: 'Run' }).click();
await expect.poll(dumpTestTree(page)).toBe(` await expect.poll(dumpTestTree(page)).toBe(`
a.test.ts a.test.ts
@ -75,7 +75,7 @@ test('should watch e2e deps', async ({ runUITest, writeFiles }) => {
}); });
await page.getByText('answer').click(); await page.getByText('answer').click();
await page.getByRole('treeitem').filter({ hasText: 'answer' }).getByTitle('Watch').click(); await page.getByRole('treeitem', { name: 'answer' }).getByRole('button', { name: 'Watch' }).click();
await expect.poll(dumpTestTree(page)).toBe(` await expect.poll(dumpTestTree(page)).toBe(`
a.test.ts a.test.ts
answer 👁 <= answer 👁 <=
@ -102,13 +102,13 @@ test('should batch watch updates', async ({ runUITest, writeFiles }) => {
}); });
await page.getByText('a.test.ts').click(); await page.getByText('a.test.ts').click();
await page.getByRole('treeitem').filter({ hasText: 'a.test.ts' }).getByTitle('Watch').click(); await page.getByRole('treeitem', { name: 'a.test.ts' }).getByRole('button', { name: 'Watch' }).click();
await page.getByText('b.test.ts').click(); await page.getByText('b.test.ts').click();
await page.getByRole('treeitem').filter({ hasText: 'b.test.ts' }).getByTitle('Watch').click(); await page.getByRole('treeitem', { name: 'b.test.ts' }).getByRole('button', { name: 'Watch' }).click();
await page.getByText('c.test.ts').click(); await page.getByText('c.test.ts').click();
await page.getByRole('treeitem').filter({ hasText: 'c.test.ts' }).getByTitle('Watch').click(); await page.getByRole('treeitem', { name: 'c.test.ts' }).getByRole('button', { name: 'Watch' }).click();
await page.getByText('d.test.ts').click(); await page.getByText('d.test.ts').click();
await page.getByRole('treeitem').filter({ hasText: 'd.test.ts' }).getByTitle('Watch').click(); await page.getByRole('treeitem', { name: 'd.test.ts' }).getByRole('button', { name: 'Watch' }).click();
await expect.poll(dumpTestTree(page)).toBe(` await expect.poll(dumpTestTree(page)).toBe(`
a.test.ts 👁 a.test.ts 👁
@ -229,7 +229,7 @@ test('should run added test in watched file', async ({ runUITest, writeFiles })
}); });
await page.getByText('a.test.ts').click(); await page.getByText('a.test.ts').click();
await page.getByRole('treeitem').filter({ hasText: 'a.test.ts' }).getByTitle('Watch').click(); await page.getByRole('treeitem', { name: 'a.test.ts' }).getByRole('button', { name: 'Watch' }).click();
await expect.poll(dumpTestTree(page)).toBe(` await expect.poll(dumpTestTree(page)).toBe(`
a.test.ts 👁 <= a.test.ts 👁 <=