chore: make console stream live in ui mode (#26562)

This commit is contained in:
Pavel Feldman 2023-08-21 10:59:37 -07:00 committed by GitHub
parent 32a309ccb8
commit f83d81956d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 50 additions and 15 deletions

View file

@ -33,10 +33,15 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
} }
async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean } = {}) { async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean, _live?: boolean } = {}) {
this._includeSources = !!options.sources; this._includeSources = !!options.sources;
const traceName = await this._wrapApiCall(async () => { const traceName = await this._wrapApiCall(async () => {
await this._channel.tracingStart(options); await this._channel.tracingStart({
name: options.name,
snapshots: options.snapshots,
screenshots: options.screenshots,
live: options._live,
});
const response = await this._channel.tracingStartChunk({ name: options.name, title: options.title }); const response = await this._channel.tracingStartChunk({ name: options.name, title: options.title });
return response.traceName; return response.traceName;
}); });

View file

@ -2130,7 +2130,7 @@ scheme.TracingTracingStartParams = tObject({
name: tOptional(tString), name: tOptional(tString),
snapshots: tOptional(tBoolean), snapshots: tOptional(tBoolean),
screenshots: tOptional(tBoolean), screenshots: tOptional(tBoolean),
sources: tOptional(tBoolean), live: tOptional(tBoolean),
}); });
scheme.TracingTracingStartResult = tOptional(tObject({})); scheme.TracingTracingStartResult = tOptional(tObject({}));
scheme.TracingTracingStartChunkParams = tObject({ scheme.TracingTracingStartChunkParams = tObject({

View file

@ -49,6 +49,7 @@ export type TracerOptions = {
name?: string; name?: string;
snapshots?: boolean; snapshots?: boolean;
screenshots?: boolean; screenshots?: boolean;
live?: boolean;
}; };
type RecordingState = { type RecordingState = {
@ -455,8 +456,8 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
private _appendTraceEvent(event: trace.TraceEvent) { private _appendTraceEvent(event: trace.TraceEvent) {
const visited = visitTraceEvent(event, this._state!.traceSha1s); const visited = visitTraceEvent(event, this._state!.traceSha1s);
// Do not flush events, they are too noisy. // Do not flush (console) events, they are too noisy, unless we are in ui mode (live).
const flush = event.type !== 'event' && event.type !== 'object'; const flush = this._state!.options.live || (event.type !== 'event' && event.type !== 'object');
this._fs.appendFile(this._state!.traceFile, JSON.stringify(visited) + '\n', flush); this._fs.appendFile(this._state!.traceFile, JSON.stringify(visited) + '\n', flush);
} }

View file

@ -526,7 +526,7 @@ class ArtifactsRecorder {
private _traceMode: TraceMode; private _traceMode: TraceMode;
private _captureTrace = false; private _captureTrace = false;
private _screenshotOptions: { mode: ScreenshotMode } & Pick<playwrightLibrary.PageScreenshotOptions, 'fullPage' | 'omitBackground'> | undefined; private _screenshotOptions: { mode: ScreenshotMode } & Pick<playwrightLibrary.PageScreenshotOptions, 'fullPage' | 'omitBackground'> | undefined;
private _traceOptions: { screenshots: boolean, snapshots: boolean, sources: boolean, attachments: boolean, mode?: TraceMode }; private _traceOptions: { screenshots: boolean, snapshots: boolean, sources: boolean, attachments: boolean, _live: boolean, mode?: TraceMode };
private _temporaryTraceFiles: string[] = []; private _temporaryTraceFiles: string[] = [];
private _temporaryScreenshots: string[] = []; private _temporaryScreenshots: string[] = [];
private _reusedContexts = new Set<BrowserContext>(); private _reusedContexts = new Set<BrowserContext>();
@ -541,7 +541,7 @@ class ArtifactsRecorder {
this._screenshotMode = normalizeScreenshotMode(screenshot); this._screenshotMode = normalizeScreenshotMode(screenshot);
this._screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot; this._screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot;
this._traceMode = normalizeTraceMode(trace); this._traceMode = normalizeTraceMode(trace);
const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true }; const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true, _live: false };
this._traceOptions = typeof trace === 'string' ? defaultTraceOptions : { ...defaultTraceOptions, ...trace, mode: undefined }; this._traceOptions = typeof trace === 'string' ? defaultTraceOptions : { ...defaultTraceOptions, ...trace, mode: undefined };
this._screenshottedSymbol = Symbol('screenshotted'); this._screenshottedSymbol = Symbol('screenshotted');
this._startedCollectingArtifacts = Symbol('startedCollectingArtifacts'); this._startedCollectingArtifacts = Symbol('startedCollectingArtifacts');

View file

@ -52,7 +52,7 @@ class UIMode {
p.project.repeatEach = 1; p.project.repeatEach = 1;
} }
config.configCLIOverrides.use = config.configCLIOverrides.use || {}; config.configCLIOverrides.use = config.configCLIOverrides.use || {};
config.configCLIOverrides.use.trace = { mode: 'on', sources: false }; config.configCLIOverrides.use.trace = { mode: 'on', sources: false, _live: true };
this._originalStdoutWrite = process.stdout.write; this._originalStdoutWrite = process.stdout.write;
this._originalStderrWrite = process.stderr.write; this._originalStderrWrite = process.stderr.write;

View file

@ -3803,13 +3803,13 @@ export type TracingTracingStartParams = {
name?: string, name?: string,
snapshots?: boolean, snapshots?: boolean,
screenshots?: boolean, screenshots?: boolean,
sources?: boolean, live?: boolean,
}; };
export type TracingTracingStartOptions = { export type TracingTracingStartOptions = {
name?: string, name?: string,
snapshots?: boolean, snapshots?: boolean,
screenshots?: boolean, screenshots?: boolean,
sources?: boolean, live?: boolean,
}; };
export type TracingTracingStartResult = void; export type TracingTracingStartResult = void;
export type TracingTracingStartChunkParams = { export type TracingTracingStartChunkParams = {

View file

@ -3014,7 +3014,7 @@ Tracing:
name: string? name: string?
snapshots: boolean? snapshots: boolean?
screenshots: boolean? screenshots: boolean?
sources: boolean? live: boolean?
tracingStartChunk: tracingStartChunk:
parameters: parameters:

View file

@ -200,10 +200,14 @@ function format(args: { preview: string, value: any }[]): JSX.Element[] {
function parseCSSStyle(cssFormat: string): Record<string, string | number> { function parseCSSStyle(cssFormat: string): Record<string, string | number> {
try { try {
const styleObject: Record<string, string | number> = {}; const styleObject: Record<string, string | number> = {};
const cssText = cssFormat.replace(/;$/, '').replace(/: /g, ':').replace(/; /g, ';'); const cssProperties = cssFormat.split(';');
const cssProperties = cssText.split(';'); for (const token of cssProperties) {
for (const property of cssProperties) { const property = token.trim();
const [key, value] = property.split(':'); if (!property)
continue;
let [key, value] = property.split(':');
key = key.trim();
value = value.trim();
if (!supportProperty(key)) if (!supportProperty(key))
continue; continue;
// cssProperties are background-color, JSDom ones are backgroundColor // cssProperties are background-color, JSDom ones are backgroundColor

View file

@ -151,3 +151,28 @@ test('should format console messages in page', async ({ runUITest }, testInfo) =
await expect(link).toHaveCSS('color', 'rgb(0, 0, 255)'); await expect(link).toHaveCSS('color', 'rgb(0, 0, 255)');
await expect(link).toHaveCSS('text-decoration', 'none solid rgb(0, 0, 255)'); await expect(link).toHaveCSS('text-decoration', 'none solid rgb(0, 0, 255)');
}); });
test('should stream console messages live', async ({ runUITest }, testInfo) => {
const { page } = await runUITest({
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('print', async ({ page }) => {
await page.setContent('<button>Click me</button>');
const button = page.getByRole('button', { name: 'Click me' });
await button.evaluate(node => node.addEventListener('click', () => {
setTimeout(() => { console.log('I was clicked'); }, 1000);
}));
await button.click();
await page.locator('#not-there').waitFor();
});
`,
});
await page.getByTitle('Run all').click();
await page.getByText('Console').click();
await page.getByText('print').click();
await expect(page.locator('.console-tab .console-line-message')).toHaveText([
'I was clicked',
]);
await page.getByTitle('Stop').click();
});