mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: store scroll positions in trace viewer (#26938)
This commit is contained in:
parent
aa3f4f208e
commit
167c35ca66
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
/>;
|
||||
|
@ -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}
|
||||
|
@ -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 => {
|
||||
|
@ -467,6 +467,7 @@ const TestList: React.FC<{
|
||||
};
|
||||
|
||||
return <TestTreeView
|
||||
name='tests'
|
||||
treeState={treeState}
|
||||
setTreeState={setTreeState}
|
||||
rootItem={rootItem}
|
||||
|
@ -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}
|
||||
|
@ -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 <>
|
||||
|
@ -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]');
|
||||
}
|
||||
|
||||
|
@ -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 }) => {
|
||||
|
@ -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 }) => {
|
||||
|
@ -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 }) => {
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
Loading…
x
Reference in New Issue
Block a user