chore: two-line trace view (3) (#36058)
This commit is contained in:
parent
3a8592910f
commit
a15e94aa3f
|
@ -16,7 +16,7 @@
|
|||
|
||||
import { EventEmitter } from './eventEmitter';
|
||||
import { ValidationError, maybeFindValidator } from '../protocol/validator';
|
||||
import { methodMetainfo } from '../protocol/debug';
|
||||
import { methodMetainfo } from '../utils/isomorphic/protocolMetainfo';
|
||||
import { captureLibraryStackTrace } from './clientStackTrace';
|
||||
import { stringifyStackFrames } from '../utils/isomorphic/stackTrace';
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import { EventEmitter } from 'events';
|
|||
|
||||
import { debugMode, isUnderTest, monotonicTime } from '../utils';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { methodMetainfo } from '../protocol/debug';
|
||||
import { methodMetainfo } from '../utils/isomorphic/protocolMetainfo';
|
||||
|
||||
import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
|
||||
|
||||
|
|
|
@ -18,13 +18,12 @@ import { EventEmitter } from 'events';
|
|||
|
||||
import { eventsHelper } from '../utils/eventsHelper';
|
||||
import { ValidationError, createMetadataValidator, findValidator } from '../../protocol/validator';
|
||||
import { LongStandingScope, assert, formatProtocolParam, monotonicTime, rewriteErrorMessage } from '../../utils';
|
||||
import { LongStandingScope, assert, monotonicTime, rewriteErrorMessage } from '../../utils';
|
||||
import { isUnderTest } from '../utils/debug';
|
||||
import { TargetClosedError, isTargetClosedError, serializeError } from '../errors';
|
||||
import { SdkObject } from '../instrumentation';
|
||||
import { isProtocolError } from '../protocolError';
|
||||
import { compressCallLog } from '../callLog';
|
||||
import { methodMetainfo } from '../../protocol/debug';
|
||||
|
||||
import type { CallMetadata } from '../instrumentation';
|
||||
import type { PlaywrightDispatcher } from './playwrightDispatcher';
|
||||
|
@ -309,7 +308,7 @@ export class DispatcherConnection {
|
|||
const callMetadata: CallMetadata = {
|
||||
id: `call@${id}`,
|
||||
location: validMetadata.location,
|
||||
title: renderTitle(dispatcher._type, method, params, validMetadata.title),
|
||||
title: validMetadata.title,
|
||||
internal: validMetadata.internal,
|
||||
stepId: validMetadata.stepId,
|
||||
objectId: sdkObject?.guid,
|
||||
|
@ -389,10 +388,3 @@ function closeReason(sdkObject: SdkObject): string | undefined {
|
|||
sdkObject.attribution.context?._closeReason ||
|
||||
sdkObject.attribution.browser?._closeReason;
|
||||
}
|
||||
|
||||
function renderTitle(type: string, method: string, params: Record<string, string> | undefined, title?: string) {
|
||||
const titleFormat = title ?? methodMetainfo.get(type + '.' + method)?.title ?? method;
|
||||
return titleFormat.replace(/\{([^}]+)\}/g, (_, p1) => {
|
||||
return formatProtocolParam(params, p1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import * as network from './network';
|
|||
import { Page } from './page';
|
||||
import { ProgressController } from './progress';
|
||||
import * as types from './types';
|
||||
import { LongStandingScope, asLocator, assert, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime } from '../utils';
|
||||
import { LongStandingScope, asLocator, assert, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime, renderTitleForCall } from '../utils';
|
||||
import { isSessionClosedError } from './protocolError';
|
||||
import { debugLogger } from './utils/debugLogger';
|
||||
import { eventsHelper } from './utils/eventsHelper';
|
||||
|
@ -1432,7 +1432,7 @@ export class Frame extends SdkObject {
|
|||
|
||||
// Step 1: perform locator handlers checkpoint with a specified timeout.
|
||||
await (new ProgressController(metadata, this)).run(async progress => {
|
||||
progress.log(`${metadata.title}${timeout ? ` with timeout ${timeout}ms` : ''}`);
|
||||
progress.log(`${renderTitleForCall(metadata)}${timeout ? ` with timeout ${timeout}ms` : ''}`);
|
||||
progress.log(`waiting for ${this._asLocator(selector)}`);
|
||||
await this._page.performActionPreChecks(progress);
|
||||
}, timeout);
|
||||
|
|
|
@ -27,7 +27,7 @@ import { SdkObject } from './instrumentation';
|
|||
import * as js from './javascript';
|
||||
import { ProgressController } from './progress';
|
||||
import { Screenshotter, validateScreenshotOptions } from './screenshotter';
|
||||
import { LongStandingScope, assert, trimStringWithEllipsis } from '../utils';
|
||||
import { LongStandingScope, assert, renderTitleForCall, trimStringWithEllipsis } from '../utils';
|
||||
import { asLocator } from '../utils';
|
||||
import { getComparator } from './utils/comparators';
|
||||
import { debugLogger } from './utils/debugLogger';
|
||||
|
@ -624,7 +624,7 @@ export class Page extends SdkObject {
|
|||
let actual: Buffer | undefined;
|
||||
let previous: Buffer | undefined;
|
||||
const pollIntervals = [0, 100, 250, 500];
|
||||
progress.log(`${metadata.title}${callTimeout ? ` with timeout ${callTimeout}ms` : ''}`);
|
||||
progress.log(`${renderTitleForCall(metadata)}${callTimeout ? ` with timeout ${callTimeout}ms` : ''}`);
|
||||
if (options.expected)
|
||||
progress.log(` verifying given screenshot expectation`);
|
||||
else
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { renderTitleForCall } from '../../utils/isomorphic/protocolFormatter';
|
||||
|
||||
import type { Frame } from '../frames';
|
||||
import type { CallMetadata } from '../instrumentation';
|
||||
import type { Page } from '../page';
|
||||
|
@ -25,7 +27,7 @@ export function buildFullSelector(framePath: string[], selector: string) {
|
|||
}
|
||||
|
||||
export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog {
|
||||
const title = metadata.title;
|
||||
const title = renderTitleForCall(metadata);
|
||||
if (metadata.error)
|
||||
status = 'error';
|
||||
const params = {
|
||||
|
|
|
@ -19,7 +19,7 @@ import os from 'os';
|
|||
import path from 'path';
|
||||
|
||||
import { Snapshotter } from './snapshotter';
|
||||
import { methodMetainfo } from '../../../protocol/debug';
|
||||
import { methodMetainfo } from '../../../utils/isomorphic/protocolMetainfo';
|
||||
import { assert } from '../../../utils/isomorphic/assert';
|
||||
import { monotonicTime } from '../../../utils/isomorphic/time';
|
||||
import { eventsHelper } from '../../utils/eventsHelper';
|
||||
|
|
|
@ -21,6 +21,8 @@ export * from './utils/isomorphic/locatorGenerators';
|
|||
export * from './utils/isomorphic/manualPromise';
|
||||
export * from './utils/isomorphic/mimeType';
|
||||
export * from './utils/isomorphic/multimap';
|
||||
export * from './utils/isomorphic/protocolFormatter';
|
||||
export * from './utils/isomorphic/protocolMetainfo';
|
||||
export * from './utils/isomorphic/rtti';
|
||||
export * from './utils/isomorphic/semaphore';
|
||||
export * from './utils/isomorphic/stackTrace';
|
||||
|
@ -52,7 +54,4 @@ export * from './server/utils/wsServer';
|
|||
export * from './server/utils/zipFile';
|
||||
export * from './server/utils/zones';
|
||||
|
||||
export * from './protocol/debug';
|
||||
export * from './protocol/formatter';
|
||||
|
||||
export { colors } from './utilsBundle';
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { methodMetainfo } from './protocolMetainfo';
|
||||
|
||||
export function formatProtocolParam(params: Record<string, string> | undefined, name: string): string {
|
||||
if (!params)
|
||||
return '';
|
||||
|
@ -29,8 +31,10 @@ export function formatProtocolParam(params: Record<string, string> | undefined,
|
|||
return params[name];
|
||||
}
|
||||
}
|
||||
if (name === 'timeNumber')
|
||||
if (name === 'timeNumber') {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
return new Date(params[name]).toString();
|
||||
}
|
||||
return deepParam(params, name);
|
||||
}
|
||||
|
||||
|
@ -46,3 +50,10 @@ function deepParam(params: Record<string, any>, name: string): string {
|
|||
return '';
|
||||
return String(current);
|
||||
}
|
||||
|
||||
export function renderTitleForCall(metadata: { title?: string, type: string, method: string, params: Record<string, string> | undefined }) {
|
||||
const titleFormat = metadata.title ?? methodMetainfo.get(metadata.type + '.' + metadata.method)?.title ?? metadata.method;
|
||||
return titleFormat.replace(/\{([^}]+)\}/g, (_, p1) => {
|
||||
return formatProtocolParam(metadata.params, p1);
|
||||
});
|
||||
}
|
|
@ -18,7 +18,7 @@ import fs from 'fs';
|
|||
import path from 'path';
|
||||
|
||||
import * as playwrightLibrary from 'playwright-core';
|
||||
import { setBoxedStackPrefixes, createGuid, currentZone, debugMode, jsonStringifyForceASCII, methodMetainfo, asLocatorDescription, formatProtocolParam } from 'playwright-core/lib/utils';
|
||||
import { setBoxedStackPrefixes, createGuid, currentZone, debugMode, jsonStringifyForceASCII, asLocatorDescription, renderTitleForCall } from 'playwright-core/lib/utils';
|
||||
|
||||
import { currentTestInfo } from './common/globals';
|
||||
import { rootTestType } from './common/testType';
|
||||
|
@ -758,8 +758,7 @@ class ArtifactsRecorder {
|
|||
}
|
||||
|
||||
function renderTitle(type: string, method: string, params: Record<string, string> | undefined, title?: string) {
|
||||
const titleFormat = title ?? methodMetainfo.get(type + '.' + method)?.title ?? method;
|
||||
const prefix = titleFormat.replace(/\{([^}]+)\}/g, (_, p1) => formatProtocolParam(params, p1));
|
||||
const prefix = renderTitleForCall({ title, type, method, params });
|
||||
let selector;
|
||||
if (params?.['selector'])
|
||||
selector = asLocatorDescription('javascript', params.selector);
|
||||
|
|
|
@ -26,6 +26,8 @@ import type { ActionTraceEventInContext, ActionTreeItem } from './modelUtil';
|
|||
import type { Boundaries } from './geometry';
|
||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||
import { testStatusIcon } from './testUtils';
|
||||
import { methodMetainfo } from '@isomorphic/protocolMetainfo';
|
||||
import { formatProtocolParam } from '@isomorphic/protocolFormatter';
|
||||
|
||||
export interface ActionListProps {
|
||||
actions: ActionTraceEventInContext[],
|
||||
|
@ -128,10 +130,10 @@ export const renderAction = (
|
|||
time = 'Timed out';
|
||||
else if (!isLive)
|
||||
time = '-';
|
||||
const renderedTitle = highlightQuotedText(action.title || action.method);
|
||||
const { elements, title } = renderTitleForCall(action);
|
||||
return <div className='action-title vbox'>
|
||||
<div className='hbox'>
|
||||
<span className='action-title-method' title={action.title || action.method}>{renderedTitle}</span>
|
||||
<span className='action-title-method' title={title}>{elements}</span>
|
||||
{(showDuration || showBadges || showAttachments || isSkipped) && <div className='spacer'></div>}
|
||||
{showAttachments && <ToolbarButton icon='attach' title='Open Attachment' onClick={() => revealAttachment(action.attachments![0])} />}
|
||||
{showDuration && !isSkipped && <div className='action-duration'>{time || <span className='codicon codicon-loading'></span>}</div>}
|
||||
|
@ -145,19 +147,33 @@ export const renderAction = (
|
|||
</div>;
|
||||
};
|
||||
|
||||
function highlightQuotedText(text: string): React.ReactNode[] {
|
||||
const result: React.ReactNode[] = [];
|
||||
export function renderTitleForCall(action: ActionTraceEvent): { elements: React.ReactNode[], title: string } {
|
||||
const titleFormat = action.title ?? methodMetainfo.get(action.class + '.' + action.method)?.title ?? action.method;
|
||||
|
||||
const elements: React.ReactNode[] = [];
|
||||
const title: string[] = [];
|
||||
let currentIndex = 0;
|
||||
const regex = /("[^"]*")/g;
|
||||
const regex = /\{([^}]+)\}/g;
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
while ((match = regex.exec(titleFormat)) !== null) {
|
||||
const [fullMatch, quotedText] = match;
|
||||
result.push(text.slice(currentIndex, match.index));
|
||||
result.push(<span className='action-title-param'>{quotedText}</span>);
|
||||
const chunk = titleFormat.slice(currentIndex, match.index);
|
||||
|
||||
elements.push(chunk);
|
||||
title.push(chunk);
|
||||
|
||||
const param = formatProtocolParam(action.params, quotedText);
|
||||
elements.push(<span className='action-title-param'>{param}</span>);
|
||||
title.push(param);
|
||||
currentIndex = match.index + fullMatch.length;
|
||||
}
|
||||
if (currentIndex < text.length)
|
||||
result.push(text.slice(currentIndex));
|
||||
return result;
|
||||
|
||||
if (currentIndex < titleFormat.length) {
|
||||
const chunk = titleFormat.slice(currentIndex);
|
||||
elements.push(chunk);
|
||||
title.push(chunk);
|
||||
}
|
||||
|
||||
return { elements, title: title.join('') };
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import type { ActionTreeItem } from '../../packages/trace-viewer/src/ui/modelUti
|
|||
import { buildActionTree, MultiTraceModel } from '../../packages/trace-viewer/src/ui/modelUtil';
|
||||
import type { ActionTraceEvent, ConsoleMessageTraceEvent, EventTraceEvent, TraceEvent } from '@trace/trace';
|
||||
import style from 'ansi-styles';
|
||||
import { renderTitleForCall } from '../../packages/playwright-core/lib/utils/isomorphic/protocolFormatter';
|
||||
|
||||
export async function attachFrame(page: Page, frameId: string, url: string): Promise<Frame> {
|
||||
const handle = await page.evaluateHandle(async ({ frameId, url }) => {
|
||||
|
@ -151,7 +152,7 @@ export async function parseTraceRaw(file: string): Promise<{ events: any[], reso
|
|||
return {
|
||||
events,
|
||||
resources,
|
||||
actions: actionObjects.map(a => a.title ?? a.class.toLowerCase() + '.' + a.method),
|
||||
actions: actionObjects.map(a => renderTitleForCall({ ...a, type: a.class })),
|
||||
actionObjects,
|
||||
stacks,
|
||||
};
|
||||
|
@ -165,13 +166,14 @@ export async function parseTrace(file: string): Promise<{ resources: Map<string,
|
|||
const { rootItem } = buildActionTree(model.actions);
|
||||
const actionTree: string[] = [];
|
||||
const visit = (actionItem: ActionTreeItem, indent: string) => {
|
||||
actionTree.push(`${indent}${actionItem.action?.title || actionItem.id}`);
|
||||
const title = renderTitleForCall({ ...actionItem.action, type: actionItem.action.class });
|
||||
actionTree.push(`${indent}${title || actionItem.id}`);
|
||||
for (const child of actionItem.children)
|
||||
visit(child, indent + ' ');
|
||||
};
|
||||
rootItem.children.forEach(a => visit(a, ''));
|
||||
return {
|
||||
titles: model.actions.map(a => a.title ?? a.class.toLowerCase() + '.' + a.method),
|
||||
titles: model.actions.map(a => renderTitleForCall({ ...a, type: a.class })),
|
||||
resources: backend.entries,
|
||||
actions: model.actions,
|
||||
events: model.events,
|
||||
|
|
|
@ -271,7 +271,7 @@ test('should render console', async ({ showTraceViewer, browserName }) => {
|
|||
await expect.soft(traceViewer.consoleLines.filter({ hasText: 'Cheers!' }).locator('.codicon')).toHaveClass('codicon codicon-browser status-none');
|
||||
await expect(traceViewer.consoleStacks.first()).toContainText('Error: Unhandled exception');
|
||||
|
||||
await traceViewer.selectAction('EVALUATE');
|
||||
await traceViewer.selectAction('Evaluate');
|
||||
|
||||
const listViews = traceViewer.page.locator('.console-tab').locator('.list-view-entry');
|
||||
await expect(listViews.nth(0)).toHaveClass('list-view-entry');
|
||||
|
@ -284,17 +284,17 @@ test('should render console', async ({ showTraceViewer, browserName }) => {
|
|||
|
||||
test('should open console errors on click', async ({ showTraceViewer, browserName }) => {
|
||||
const traceViewer = await showTraceViewer([traceFile]);
|
||||
expect(await traceViewer.actionIconsText('EVALUATE')).toEqual(['2', '1']);
|
||||
expect(await traceViewer.actionIconsText('Evaluate')).toEqual(['2', '1']);
|
||||
expect(await traceViewer.page.isHidden('.console-tab')).toBeTruthy();
|
||||
await (await traceViewer.actionIcons('EVALUATE')).click();
|
||||
await (await traceViewer.actionIcons('Evaluate')).click();
|
||||
expect(await traceViewer.page.waitForSelector('.console-tab')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should show params and return value', async ({ showTraceViewer }) => {
|
||||
const traceViewer = await showTraceViewer([traceFile]);
|
||||
await traceViewer.selectAction('EVALUATE');
|
||||
await traceViewer.selectAction('Evaluate');
|
||||
await expect(traceViewer.callLines).toHaveText([
|
||||
/Evaluate/,
|
||||
'',
|
||||
/start:[\d\.]+m?s/,
|
||||
/duration:[\d]+ms/,
|
||||
/expression:"\({↵ a↵ }\) => {↵ console\.log\(\'Info\'\);↵ console\.warn\(\'Warning\'\);↵ console/,
|
||||
|
@ -318,9 +318,9 @@ test('should show params and return value', async ({ showTraceViewer }) => {
|
|||
|
||||
test('should show null as a param', async ({ showTraceViewer, browserName }) => {
|
||||
const traceViewer = await showTraceViewer([traceFile]);
|
||||
await traceViewer.selectAction('EVALUATE', 1);
|
||||
await traceViewer.selectAction('Evaluate', 1);
|
||||
await expect(traceViewer.callLines).toHaveText([
|
||||
/Evaluate/,
|
||||
'',
|
||||
/start:[\d\.]+m?s/,
|
||||
/duration:[\d]+ms/,
|
||||
'expression:"() => 1 + 1"',
|
||||
|
@ -354,7 +354,7 @@ test('should have correct stack trace', async ({ showTraceViewer }) => {
|
|||
|
||||
test('should have network requests', async ({ showTraceViewer }) => {
|
||||
const traceViewer = await showTraceViewer([traceFile]);
|
||||
await traceViewer.selectAction('NAVIGATE');
|
||||
await traceViewer.selectAction('Navigate');
|
||||
await traceViewer.showNetworkTab();
|
||||
await expect(traceViewer.networkRequests).toContainText([/frame.htmlGET200text\/html/]);
|
||||
await expect(traceViewer.networkRequests).toContainText([/style.cssGET200text\/css/]);
|
||||
|
@ -369,7 +369,7 @@ test('should filter network requests by resource type', async ({ page, runAndTra
|
|||
await page.goto(`${server.PREFIX}/network-tab/network.html`);
|
||||
await page.evaluate(() => (window as any).donePromise);
|
||||
});
|
||||
await traceViewer.selectAction('NAVIGATE');
|
||||
await traceViewer.selectAction('Navigate');
|
||||
await traceViewer.showNetworkTab();
|
||||
|
||||
await traceViewer.page.getByText('JS', { exact: true }).click();
|
||||
|
@ -402,7 +402,7 @@ test('should show font preview', async ({ page, runAndTrace, server }) => {
|
|||
await page.goto(`${server.PREFIX}/network-tab/network.html`);
|
||||
await page.evaluate(() => (window as any).donePromise);
|
||||
});
|
||||
await traceViewer.selectAction('NAVIGATE');
|
||||
await traceViewer.selectAction('Navigate');
|
||||
await traceViewer.showNetworkTab();
|
||||
|
||||
await traceViewer.page.getByText('Font', { exact: true }).click();
|
||||
|
@ -417,7 +417,7 @@ test('should filter network requests by url', async ({ page, runAndTrace, server
|
|||
await page.goto(`${server.PREFIX}/network-tab/network.html`);
|
||||
await page.evaluate(() => (window as any).donePromise);
|
||||
});
|
||||
await traceViewer.selectAction('NAVIGATE');
|
||||
await traceViewer.selectAction('Navigate');
|
||||
await traceViewer.showNetworkTab();
|
||||
|
||||
await traceViewer.page.getByPlaceholder('Filter network').fill('script.');
|
||||
|
@ -446,7 +446,7 @@ test('should have network request overrides', async ({ page, server, runAndTrace
|
|||
await page.route('**/style.css', route => route.abort());
|
||||
await page.goto(server.PREFIX + '/frames/frame.html');
|
||||
});
|
||||
await traceViewer.selectAction('NAVIGATE');
|
||||
await traceViewer.selectAction('Navigate');
|
||||
await traceViewer.showNetworkTab();
|
||||
await expect(traceViewer.networkRequests).toContainText([/frame.htmlGET200text\/html/]);
|
||||
await expect(traceViewer.networkRequests).toContainText([/style.cssGETx-unknown.*aborted/]);
|
||||
|
@ -458,7 +458,7 @@ test('should have network request overrides 2', async ({ page, server, runAndTra
|
|||
await page.route('**/script.js', route => route.continue());
|
||||
await page.goto(server.PREFIX + '/frames/frame.html');
|
||||
});
|
||||
await traceViewer.selectAction('NAVIGATE');
|
||||
await traceViewer.selectAction('Navigate');
|
||||
await traceViewer.showNetworkTab();
|
||||
await expect.soft(traceViewer.networkRequests).toContainText([/frame.htmlGET200text\/html.*/]);
|
||||
await expect.soft(traceViewer.networkRequests).toContainText([/script.jsGET200application\/javascript.*continued/]);
|
||||
|
@ -1506,7 +1506,7 @@ test('should show correct request start time', {
|
|||
return fetch('/api').then(r => r.text());
|
||||
});
|
||||
});
|
||||
await traceViewer.selectAction('EVALUATE');
|
||||
await traceViewer.selectAction('Evaluate');
|
||||
await traceViewer.showNetworkTab();
|
||||
await expect(traceViewer.networkRequests).toContainText([/apiGET200text/]);
|
||||
const line = traceViewer.networkRequests.getByText(/apiGET200text/);
|
||||
|
@ -1553,7 +1553,7 @@ test('should show baseURL in metadata pane', {
|
|||
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31847' },
|
||||
}, async ({ showTraceViewer }) => {
|
||||
const traceViewer = await showTraceViewer([traceFile]);
|
||||
await traceViewer.selectAction('EVALUATE');
|
||||
await traceViewer.selectAction('Evaluate');
|
||||
await traceViewer.showMetadataTab();
|
||||
await expect(traceViewer.metadataTab).toContainText('baseURL:https://example.com');
|
||||
});
|
||||
|
@ -1595,22 +1595,22 @@ test('should not leak recorders', {
|
|||
|
||||
await expect(traceViewer.snapshotContainer.contentFrame().locator('body')).toContainText(`Hi, I'm frame`);
|
||||
|
||||
const frame1 = await forceRecorder('NAVIGATE');
|
||||
const frame1 = await forceRecorder('Navigate');
|
||||
await expect(frame1.locator('body')).toContainText('Hello world');
|
||||
|
||||
const frame2 = await forceRecorder('EVALUATE');
|
||||
const frame2 = await forceRecorder('Evaluate');
|
||||
await expect(frame2.locator('button')).toBeVisible();
|
||||
|
||||
await traceViewer.page.requestGC();
|
||||
await expect.poll(() => aliveCount()).toBeLessThanOrEqual(2); // two snapshot iframes
|
||||
|
||||
const frame3 = await forceRecorder('SET VIEWPORT SIZE');
|
||||
const frame3 = await forceRecorder('Set viewport size');
|
||||
await expect(frame3.locator('body')).toContainText(`Hi, I'm frame`);
|
||||
|
||||
const frame4 = await forceRecorder('NAVIGATE');
|
||||
const frame4 = await forceRecorder('Navigate');
|
||||
await expect(frame4.locator('body')).toContainText('Hello world');
|
||||
|
||||
const frame5 = await forceRecorder('EVALUATE');
|
||||
const frame5 = await forceRecorder('Evaluate');
|
||||
await expect(frame5.locator('button')).toBeVisible();
|
||||
|
||||
await traceViewer.page.requestGC();
|
||||
|
@ -1751,14 +1751,14 @@ test('should show a modal dialog', async ({ runAndTrace, page, platform, browser
|
|||
|
||||
test('should open settings dialog', async ({ showTraceViewer }) => {
|
||||
const traceViewer = await showTraceViewer([traceFile]);
|
||||
await traceViewer.selectAction('NAVIGATE');
|
||||
await traceViewer.selectAction('Navigate');
|
||||
await traceViewer.showSettings();
|
||||
await expect(traceViewer.settingsDialog).toBeVisible();
|
||||
});
|
||||
|
||||
test('should toggle theme color', async ({ showTraceViewer, page }) => {
|
||||
const traceViewer = await showTraceViewer([traceFile]);
|
||||
await traceViewer.selectAction('NAVIGATE');
|
||||
await traceViewer.selectAction('Navigate');
|
||||
await traceViewer.showSettings();
|
||||
|
||||
await expect(traceViewer.darkModeSetting).toBeChecked({ checked: false });
|
||||
|
@ -1781,7 +1781,7 @@ test('should toggle canvas rendering', async ({ runAndTrace, page }) => {
|
|||
let snapshotRequestPromise = traceViewer.page.waitForRequest(request => request.url().includes('/snapshot/'));
|
||||
|
||||
// Click on the action with a canvas snapshot
|
||||
await traceViewer.selectAction('NAVIGATE', 0);
|
||||
await traceViewer.selectAction('Navigate', 0);
|
||||
|
||||
let snapshotRequest = await snapshotRequestPromise;
|
||||
|
||||
|
@ -1794,12 +1794,12 @@ test('should toggle canvas rendering', async ({ runAndTrace, page }) => {
|
|||
await expect(traceViewer.displayCanvasContentSetting).toBeChecked({ checked: true });
|
||||
|
||||
// Deselect canvas
|
||||
await traceViewer.selectAction('NAVIGATE', 1);
|
||||
await traceViewer.selectAction('Navigate', 1);
|
||||
|
||||
snapshotRequestPromise = traceViewer.page.waitForRequest(request => request.url().includes('/snapshot/'));
|
||||
|
||||
// Select canvas again
|
||||
await traceViewer.selectAction('NAVIGATE', 0);
|
||||
await traceViewer.selectAction('Navigate', 0);
|
||||
|
||||
snapshotRequest = await snapshotRequestPromise;
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ test('should not include buffers in the trace', async ({ context, page, server }
|
|||
await page.screenshot();
|
||||
await context.tracing.stop({ path: testInfo.outputPath('trace.zip') });
|
||||
const { actionObjects } = await parseTraceRaw(testInfo.outputPath('trace.zip'));
|
||||
const screenshotEvent = actionObjects.find(a => a.title === 'Screenshot');
|
||||
const screenshotEvent = actionObjects.find(a => a.method === 'screenshot');
|
||||
expect(screenshotEvent.beforeSnapshot).toBeTruthy();
|
||||
expect(screenshotEvent.afterSnapshot).toBeTruthy();
|
||||
expect(screenshotEvent.result).toEqual({
|
||||
|
@ -160,13 +160,12 @@ test('should exclude internal pages', async ({ browserName, context, page, serve
|
|||
expect(pageIds.size).toBe(1);
|
||||
});
|
||||
|
||||
test('should include context API requests', async ({ browserName, context, page, server }, testInfo) => {
|
||||
test('should include context API requests', async ({ context, page, server }, testInfo) => {
|
||||
await context.tracing.start({ snapshots: true });
|
||||
await page.request.post(server.PREFIX + '/simple.json', { data: { foo: 'bar' } });
|
||||
await context.tracing.stop({ path: testInfo.outputPath('trace.zip') });
|
||||
const { events } = await parseTraceRaw(testInfo.outputPath('trace.zip'));
|
||||
const postEvent = events.find(e => e.title === 'Fetch "/simple.json"');
|
||||
expect(postEvent).toBeTruthy();
|
||||
const { events, actions } = await parseTraceRaw(testInfo.outputPath('trace.zip'));
|
||||
expect(actions).toContain('Fetch "/simple.json"');
|
||||
const harEntry = events.find(e => e.type === 'resource-snapshot');
|
||||
expect(harEntry).toBeTruthy();
|
||||
expect(harEntry.snapshot.request.url).toBe(server.PREFIX + '/simple.json');
|
||||
|
@ -482,9 +481,8 @@ test('should include interrupted actions', async ({ context, page, server }, tes
|
|||
await context.tracing.stop({ path: testInfo.outputPath('trace.zip') });
|
||||
await context.close();
|
||||
|
||||
const { events } = await parseTraceRaw(testInfo.outputPath('trace.zip'));
|
||||
const clickEvent = events.find(e => e.title === 'Click');
|
||||
expect(clickEvent).toBeTruthy();
|
||||
const { actions } = await parseTraceRaw(testInfo.outputPath('trace.zip'));
|
||||
expect(actions).toContain('Click');
|
||||
});
|
||||
|
||||
test('should throw when starting with different options', async ({ context }) => {
|
||||
|
@ -741,7 +739,6 @@ test('should not flush console events', async ({ context, page, mode }, testInfo
|
|||
await expect(async () => {
|
||||
const traceName = fs.readdirSync(dir).find(name => name.endsWith(testId + '.trace'));
|
||||
content = await fs.promises.readFile(path.join(dir, traceName), 'utf8');
|
||||
expect(content).toContain('Evaluate');
|
||||
expect(content).toContain('31415926');
|
||||
}).toPass();
|
||||
expect(content).not.toContain('hello 0');
|
||||
|
@ -823,17 +820,14 @@ test('should not emit after w/o before', async ({ browserType, mode }, testInfo)
|
|||
{
|
||||
type: 'before',
|
||||
callId: expect.any(Number),
|
||||
title: 'Evaluate'
|
||||
},
|
||||
{
|
||||
type: 'before',
|
||||
callId: expect.any(Number),
|
||||
title: 'Wait for event "console"'
|
||||
},
|
||||
{
|
||||
type: 'after',
|
||||
callId: expect.any(Number),
|
||||
title: undefined,
|
||||
},
|
||||
]);
|
||||
call1 = sanitized[0].callId;
|
||||
|
@ -849,12 +843,10 @@ test('should not emit after w/o before', async ({ browserType, mode }, testInfo)
|
|||
{
|
||||
type: 'before',
|
||||
callId: expect.any(Number),
|
||||
title: 'Evaluate'
|
||||
},
|
||||
{
|
||||
type: 'after',
|
||||
callId: expect.any(Number),
|
||||
title: undefined
|
||||
}
|
||||
]);
|
||||
call2before = sanitized[0].callId;
|
||||
|
|
|
@ -1346,7 +1346,7 @@ test('should record trace snapshot for more obscure commands', async ({ runInlin
|
|||
const snapshots = trace.traceModel.storage();
|
||||
const snapshotFrameOrPageId = snapshots.snapshotsForTest()[0];
|
||||
|
||||
const countAction = trace.actions.find(a => a.title === 'Query count');
|
||||
const countAction = trace.actions.find(a => a.method === 'queryCount');
|
||||
expect(countAction.beforeSnapshot).toBeTruthy();
|
||||
expect(countAction.afterSnapshot).toBeTruthy();
|
||||
expect(snapshots.snapshotByName(snapshotFrameOrPageId, countAction.beforeSnapshot)).toBeTruthy();
|
||||
|
|
|
@ -157,7 +157,7 @@ export type { Validator, ValidatorContext } from './validatorPrimitives';
|
|||
export { ValidationError, findValidator, maybeFindValidator, createMetadataValidator } from './validatorPrimitives';
|
||||
`];
|
||||
|
||||
const debug_ts = [
|
||||
const metainfo_ts = [
|
||||
`/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
|
@ -328,7 +328,7 @@ for (const [name, item] of Object.entries(protocol)) {
|
|||
}
|
||||
}
|
||||
|
||||
debug_ts.push(`export const methodMetainfo = new Map<string, { internal?: boolean, title?: string, slowMo?: boolean, snapshot?: boolean, pausesBeforeInput?: boolean }>([
|
||||
metainfo_ts.push(`export const methodMetainfo = new Map<string, { internal?: boolean, title?: string, slowMo?: boolean, snapshot?: boolean, pausesBeforeInput?: boolean }>([
|
||||
${methodMetainfo.join(`,\n `)}
|
||||
]);`);
|
||||
|
||||
|
@ -348,6 +348,6 @@ function writeFile(filePath, content) {
|
|||
}
|
||||
|
||||
writeFile(path.join(__dirname, '..', 'packages', 'protocol', 'src', 'channels.d.ts'), channels_ts.join('\n') + '\n');
|
||||
writeFile(path.join(__dirname, '..', 'packages', 'playwright-core', 'src', 'protocol', 'debug.ts'), debug_ts.join('\n') + '\n');
|
||||
writeFile(path.join(__dirname, '..', 'packages', 'playwright-core', 'src', 'utils', 'isomorphic', 'protocolMetainfo.ts'), metainfo_ts.join('\n') + '\n');
|
||||
writeFile(path.join(__dirname, '..', 'packages', 'playwright-core', 'src', 'protocol', 'validator.ts'), validator_ts.join('\n') + '\n');
|
||||
process.exit(hasChanges ? 1 : 0);
|
||||
|
|
Loading…
Reference in New Issue