chore: store scroll positions in trace viewer (#26938)

This commit is contained in:
Pavel Feldman 2023-09-07 17:14:39 -07:00 committed by GitHub
parent aa3f4f208e
commit 167c35ca66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 48 additions and 23 deletions

View File

@ -62,7 +62,7 @@ export const ActionList: React.FC<ActionListProps> = ({
return <div className='vbox'>
{selectedTime && <div className='action-list-show-all' onClick={() => setSelectedTime(undefined)}><span className='codicon codicon-triangle-left'></span>Show all</div>}
<ActionTreeView
dataTestId='action-list'
name='actions'
rootItem={rootItem}
treeState={treeState}
setTreeState={setTreeState}

View File

@ -119,6 +119,7 @@ export const ConsoleTab: React.FunctionComponent<{
return <div className='console-tab'>
<ConsoleListView
name='console'
items={consoleModel.entries}
isError={entry => entry.isError}
isWarning={entry => entry.isWarning}

View File

@ -27,7 +27,7 @@ export const LogTab: React.FunctionComponent<{
if (!action?.log.length)
return <PlaceholderPanel text='No log entries' />;
return <LogList
dataTestId='log-list'
name='log'
items={action?.log || []}
render={logLine => logLine}
/>;

View File

@ -69,7 +69,7 @@ export const NetworkTab: React.FunctionComponent<{
{!resource && <div className='vbox'>
<NetworkHeader sorting={sorting} toggleSorting={toggleSorting} />
<NetworkListView
dataTestId='network-request-list'
name='network'
items={networkModel.resources}
render={entry => <NetworkResource boundaries={boundaries} resource={entry}></NetworkResource>}
onSelected={setResource}

View File

@ -29,7 +29,7 @@ export const StackTraceView: React.FunctionComponent<{
}> = ({ action, setSelectedFrame, selectedFrame }) => {
const frames = action?.stack || [];
return <StackFrameListView
dataTestId='stack-trace'
name='stack-trace'
items={frames}
selectedItem={frames[selectedFrame]}
render={frame => {

View File

@ -467,6 +467,7 @@ const TestList: React.FC<{
};
return <TestTreeView
name='tests'
treeState={treeState}
setTreeState={setTreeState}
rootItem={rootItem}

View File

@ -18,6 +18,7 @@ import * as React from 'react';
import './listView.css';
export type ListViewProps<T> = {
name: string,
items: T[],
id?: (item: T, index: number) => string,
render: (item: T, index: number) => React.ReactNode,
@ -36,7 +37,10 @@ export type ListViewProps<T> = {
dataTestId?: string,
};
const scrollPositions = new Map<string, number>();
export function ListView<T>({
name,
items = [],
id,
render,
@ -61,7 +65,23 @@ export function ListView<T>({
onHighlighted?.(highlightedItem);
}, [onHighlighted, highlightedItem]);
return <div className='list-view vbox' role='list' data-testid={dataTestId}>
React.useEffect(() => {
const listElem = itemListRef.current;
if (!listElem)
return;
const saveScrollPosition = () => {
scrollPositions.set(name, listElem.scrollTop);
};
listElem.addEventListener('scroll', saveScrollPosition, { passive: true });
return () => listElem.removeEventListener('scroll', saveScrollPosition);
}, [name]);
React.useEffect(() => {
if (itemListRef.current)
itemListRef.current.scrollTop = scrollPositions.get(name) || 0;
}, [name]);
return <div className='list-view vbox' role='list' data-testid={dataTestId || (name + '-list')}>
<div
className='list-view-content'
tabIndex={0}

View File

@ -28,6 +28,7 @@ export type TreeState = {
};
export type TreeViewProps<T> = {
name: string,
rootItem: T,
render: (item: T) => React.ReactNode,
icon?: (item: T) => string | undefined,
@ -47,6 +48,7 @@ export type TreeViewProps<T> = {
const TreeListView = ListView<TreeItem>;
export function TreeView<T extends TreeItem>({
name,
rootItem,
render,
icon,
@ -96,9 +98,10 @@ export function TreeView<T extends TreeItem>({
}, [treeItems, isVisible]);
return <TreeListView
name={name}
items={visibleItems}
id={item => item.id}
dataTestId={dataTestId}
dataTestId={dataTestId || (name + '-tree')}
render={item => {
const rendered = render(item as T);
return <>

View File

@ -52,8 +52,8 @@ class TraceViewerPage {
this.consoleLines = page.locator('.console-line');
this.consoleLineMessages = page.locator('.console-line-message');
this.consoleStacks = page.locator('.console-stack');
this.stackFrames = page.getByTestId('stack-trace').locator('.list-view-entry');
this.networkRequests = page.getByTestId('network-request-list').locator('.list-view-entry');
this.stackFrames = page.getByTestId('stack-trace-list').locator('.list-view-entry');
this.networkRequests = page.getByTestId('network-list').locator('.list-view-entry');
this.snapshotContainer = page.locator('.snapshot-container iframe.snapshot-visible[name=snapshot]');
}

View File

@ -674,7 +674,7 @@ test('should show action source', async ({ showTraceViewer }) => {
await page.click('text=Source');
await expect(page.locator('.source-line-running')).toContainText('await page.getByText(\'Click\').click()');
await expect(page.getByTestId('stack-trace').locator('.list-view-entry.selected')).toHaveText(/doClick.*trace-viewer\.spec\.ts:[\d]+/);
await expect(page.getByTestId('stack-trace-list').locator('.list-view-entry.selected')).toHaveText(/doClick.*trace-viewer\.spec\.ts:[\d]+/);
});
test('should follow redirects', async ({ page, runAndTrace, server, asset }) => {

View File

@ -668,7 +668,7 @@ test('generate html with attachment urls', async ({ runInlineTest, mergeReports,
// Check that trace loads.
await page.locator('div').filter({ hasText: /^a\.test\.js:13$/ }).getByRole('link', { name: 'View trace' }).click();
await expect(page).toHaveTitle('Playwright Trace Viewer');
await expect(page.getByTestId('action-list').locator('div').filter({ hasText: /^expect\.toBe$/ })).toBeVisible();
await expect(page.getByTestId('actions-tree').locator('div').filter({ hasText: /^expect\.toBe$/ })).toBeVisible();
});
test('resource names should not clash between runs', async ({ runInlineTest, showReport, mergeReports, page }) => {

View File

@ -454,10 +454,10 @@ for (const useIntermediateMergeReport of [false] as const) {
]);
await expect(page.locator('.source-line-running')).toContainText('page.evaluate');
await expect(page.getByTestId('stack-trace')).toContainText([
await expect(page.getByTestId('stack-trace-list')).toContainText([
/a.test.js:[\d]+/,
]);
await expect(page.getByTestId('stack-trace').locator('.list-view-entry.selected')).toContainText('a.test.js');
await expect(page.getByTestId('stack-trace-list').locator('.list-view-entry.selected')).toContainText('a.test.js');
});
test('should show trace title', async ({ runInlineTest, page, showReport }) => {

View File

@ -47,7 +47,7 @@ test('should update trace live', async ({ runUITest, server }) => {
await page.getByText('live test').dblclick();
// It should halt on loading one.html.
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'
@ -134,7 +134,7 @@ test('should preserve action list selection upon live trace update', async ({ ru
await page.getByText('live test').dblclick();
// It should wait on the latch.
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'
@ -145,7 +145,7 @@ test('should preserve action list selection upon live trace update', async ({ ru
]);
// Manually select page.goto.
await page.getByTestId('action-list').getByText('page.goto').click();
await page.getByTestId('actions-tree').getByText('page.goto').click();
// Generate more actions and check that we are still on the page.goto action.
latch.open();
@ -195,7 +195,7 @@ test('should update tracing network live', async ({ runUITest, server }) => {
await page.getByText('live test').dblclick();
// It should wait on the latch.
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'
@ -207,7 +207,7 @@ test('should update tracing network live', async ({ runUITest, server }) => {
// Once page.setContent is visible, we can be sure that page.goto has all required
// resources in the trace. Switch to it and check that everything renders.
await page.getByTestId('action-list').getByText('page.goto').click();
await page.getByTestId('actions-tree').getByText('page.goto').click();
await expect(
page.frameLocator('iframe.snapshot-visible[name=snapshot]').locator('body'),
@ -235,7 +235,7 @@ test('should show trace w/ multiple contexts', async ({ runUITest, server, creat
await page.getByText('live test').dblclick();
// It should wait on the latch.
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'
@ -280,7 +280,7 @@ test('should show live trace for serial', async ({ runUITest, server, createLatc
await page.getByText('two', { exact: true }).click();
await page.getByTitle('Run all').click();
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'

View File

@ -34,7 +34,7 @@ test('should merge trace events', async ({ runUITest, server }) => {
await page.getByText('trace test').dblclick();
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'
@ -63,7 +63,7 @@ test('should merge web assertion events', async ({ runUITest }, testInfo) => {
await page.getByText('trace test').dblclick();
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'
@ -90,7 +90,7 @@ test('should merge screenshot assertions', async ({ runUITest }, testInfo) => {
await page.getByText('trace test').dblclick();
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'
@ -137,7 +137,7 @@ test('should show snapshots for sync assertions', async ({ runUITest, server })
await page.getByText('trace test').dblclick();
const listItem = page.getByTestId('action-list').getByRole('listitem');
const listItem = page.getByTestId('actions-tree').getByRole('listitem');
await expect(
listItem,
'action list'