207 lines
7.8 KiB
TypeScript
207 lines
7.8 KiB
TypeScript
/**
|
|
* 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 type { Fixtures, FrameLocator, Locator, Page, Browser, BrowserContext } from '@playwright/test';
|
|
import { step } from './baseTest';
|
|
import path from 'path';
|
|
import { CommonFixtures, TestChildProcess } from './commonFixtures';
|
|
|
|
type BaseTestFixtures = CommonFixtures & {
|
|
context: BrowserContext;
|
|
};
|
|
|
|
type BaseWorkerFixtures = {
|
|
headless: boolean;
|
|
browser: Browser;
|
|
browserName: 'chromium' | 'firefox' | 'webkit';
|
|
playwright: typeof import('@playwright/test');
|
|
};
|
|
|
|
export type TraceViewerFixtures = {
|
|
showTraceViewer: (trace: string | undefined, options?: {host?: string, port?: number, stdin?: boolean}) => Promise<TraceViewerPage>;
|
|
runAndTrace: (body: () => Promise<void>, optsOverrides?: Parameters<BrowserContext['tracing']['start']>[0]) => Promise<TraceViewerPage>;
|
|
};
|
|
|
|
class TraceViewerPage {
|
|
actionTitles: Locator;
|
|
actionsTree: Locator;
|
|
callLines: Locator;
|
|
consoleLines: Locator;
|
|
logLines: Locator;
|
|
errorMessages: Locator;
|
|
consoleLineMessages: Locator;
|
|
consoleStacks: Locator;
|
|
networkRequests: Locator;
|
|
metadataTab: Locator;
|
|
snapshotContainer: Locator;
|
|
sourceCodeTab: Locator;
|
|
networkTab: Locator;
|
|
|
|
settingsDialog: Locator;
|
|
themeSetting: Locator;
|
|
displayCanvasContentSetting: Locator;
|
|
|
|
constructor(public page: Page, public process: TestChildProcess) {
|
|
this.actionTitles = page.locator('.action-title');
|
|
this.actionsTree = page.getByTestId('actions-tree');
|
|
this.callLines = page.locator('.call-tab .call-line');
|
|
this.logLines = page.getByRole('listbox', { name: 'Log entries' }).getByRole('option');
|
|
this.consoleLines = page.getByRole('tabpanel', { name: 'Console' }).getByRole('option');
|
|
this.consoleLineMessages = page.locator('.console-line-message');
|
|
this.errorMessages = page.locator('.error-message');
|
|
this.consoleStacks = page.locator('.console-stack');
|
|
this.networkRequests = page.getByRole('listbox', { name: 'Network requests' }).getByRole('option');
|
|
this.snapshotContainer = page.locator('.snapshot-container iframe.snapshot-visible[name=snapshot]');
|
|
this.metadataTab = page.getByRole('tabpanel', { name: 'Metadata' });
|
|
this.sourceCodeTab = page.getByRole('tabpanel', { name: 'Source' });
|
|
this.networkTab = page.getByRole('tabpanel', { name: 'Network' });
|
|
|
|
this.settingsDialog = page.getByTestId('settings-toolbar-dialog');
|
|
this.themeSetting = this.settingsDialog.getByRole('combobox', { name: 'Theme' });
|
|
this.displayCanvasContentSetting = page.locator('.setting').getByText('Display canvas content');
|
|
}
|
|
|
|
@step
|
|
async showAllActions() {
|
|
await this.page.getByRole('button', { name: 'Filter actions' }).click();
|
|
await this.page.locator('.setting').getByText('Network routes').click();
|
|
await this.page.locator('.setting').getByText('Getters').click();
|
|
await this.page.locator('.setting').getByText('Configuration').click();
|
|
await this.page.getByRole('button', { name: 'Filter actions' }).click();
|
|
}
|
|
|
|
stackFrames(options: { selected?: boolean } = {}) {
|
|
return this.page.getByRole('listbox', { name: 'Stack trace' }).getByRole('option', options);
|
|
}
|
|
|
|
actionIconsText(action: string) {
|
|
const entry = this.actionsTree.getByRole('treeitem', { name: action });
|
|
return entry.locator('.action-icon-value').filter({ visible: true });
|
|
}
|
|
|
|
actionIcons(action: string) {
|
|
return this.actionsTree.getByRole('treeitem', { name: action }).locator('.action-icons').filter({ visible: true });
|
|
}
|
|
|
|
@step
|
|
async expandAction(title: string) {
|
|
await this.actionsTree.getByRole('treeitem', { name: title }).locator('.codicon-chevron-right').click();
|
|
}
|
|
|
|
@step
|
|
async selectAction(title: string, ordinal: number = 0) {
|
|
await this.actionsTree.getByTitle(title).nth(ordinal).click();
|
|
}
|
|
|
|
@step
|
|
async hoverAction(title: string, ordinal: number = 0) {
|
|
await this.actionsTree.getByRole('treeitem', { name: title }).nth(ordinal).hover();
|
|
}
|
|
|
|
@step
|
|
async selectSnapshot(name: string) {
|
|
await this.page.getByRole('tab', { name }).click();
|
|
}
|
|
|
|
async showErrorsTab() {
|
|
await this.page.getByRole('tab', { name: 'Errors' }).click();
|
|
}
|
|
|
|
async showConsoleTab() {
|
|
await this.page.getByRole('tab', { name: 'Console' }).click();
|
|
}
|
|
|
|
async showSourceTab() {
|
|
await this.page.getByRole('tab', { name: 'Source' }).click();
|
|
}
|
|
|
|
async showNetworkTab() {
|
|
await this.page.getByRole('tab', { name: 'Network' }).click();
|
|
}
|
|
|
|
async showMetadataTab() {
|
|
await this.page.getByRole('tab', { name: 'Metadata' }).click();
|
|
}
|
|
|
|
async showSettings() {
|
|
await this.page.getByRole('button', { name: 'Settings' }).click();
|
|
}
|
|
|
|
@step
|
|
async snapshotFrame(actionName: string, ordinal: number = 0, hasSubframe: boolean = false): Promise<FrameLocator> {
|
|
await this.selectAction(actionName, ordinal);
|
|
while (this.page.frames().length < (hasSubframe ? 4 : 3))
|
|
await this.page.waitForEvent('frameattached');
|
|
return this.page.frameLocator('iframe.snapshot-visible[name=snapshot]');
|
|
}
|
|
}
|
|
|
|
export const traceViewerFixtures: Fixtures<TraceViewerFixtures, {}, BaseTestFixtures, BaseWorkerFixtures> = {
|
|
showTraceViewer: async ({ playwright, childProcess, browserName }, use, testInfo) => {
|
|
const browsers: Browser[] = [];
|
|
const tracings: any[] = [];
|
|
await use(async (trace: string | undefined, { host, port, stdin } = {}) => {
|
|
const command = [
|
|
'node',
|
|
path.join(__dirname, '../../packages/playwright-core/cli.js'),
|
|
'show-trace',
|
|
'--port', '' + (port ?? '0'),
|
|
];
|
|
if (host)
|
|
command.push('--host', host);
|
|
if (stdin)
|
|
command.push('--stdin');
|
|
if (trace)
|
|
command.push(trace);
|
|
const cp = childProcess({ command });
|
|
await cp.waitForOutput('Listening on');
|
|
const browser = await playwright.chromium.launch({
|
|
...(browserName === 'chromium' ? {} : { channel: 'chromium' }),
|
|
executablePath: process.env.CRPATH, // without this, setting FFPATH makes us launch Firefox with Chromium args
|
|
});
|
|
browsers.push(browser);
|
|
const page = await browser.newPage();
|
|
if (process.env.PWTEST_DEBUG_TRACE_VIEWER) {
|
|
const tracing = page.context().tracing;
|
|
await tracing.start({ snapshots: true, screenshots: true });
|
|
tracings.push(tracing);
|
|
}
|
|
const url = cp.output.match(/Listening on (http:\/\/[^\s]+)/)![1];
|
|
await page.goto(url);
|
|
return new TraceViewerPage(page, cp);
|
|
});
|
|
for (const [index, tracing] of tracings.entries()) {
|
|
const path = testInfo.outputPath(`viewer-trace-${index}.zip`);
|
|
await tracing.stop({ path });
|
|
await testInfo.attach(`viewer-trace-${index}.zip`, { path, contentType: 'application/zip' });
|
|
}
|
|
for (const browser of browsers)
|
|
await browser.close();
|
|
},
|
|
|
|
runAndTrace: async ({ context, showTraceViewer }, use, testInfo) => {
|
|
await use(async (body: () => Promise<void>, optsOverrides = {}) => {
|
|
const traceFile = testInfo.outputPath('trace.zip');
|
|
await context.tracing.start({ snapshots: true, screenshots: true, sources: true, ...optsOverrides });
|
|
await body();
|
|
await context.tracing.stop({ path: traceFile });
|
|
if (process.env.PWTEST_DEBUG_TRACE_VIEWER)
|
|
await testInfo.attach('recorded-trace.zip', { path: traceFile, contentType: 'application/zip' });
|
|
return showTraceViewer(traceFile);
|
|
});
|
|
},
|
|
};
|