fix(steps): do not show unnecessary steps for internal waitForEventInfo (#10889)

This commit is contained in:
Dmitry Gozman 2021-12-13 13:32:53 -08:00 committed by GitHub
parent c27491cd4d
commit 9491f6652d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 32 additions and 23 deletions

View File

@ -200,7 +200,7 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
return this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this._channel, event);
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.AndroidDevice.Close)
waiter.rejectOnEvent(this, Events.AndroidDevice.Close, new Error('Device closed'));

View File

@ -268,7 +268,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
return this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this._channel, event);
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.BrowserContext.Close)
waiter.rejectOnEvent(this, Events.BrowserContext.Close, new Error('Context closed'));

View File

@ -104,7 +104,7 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
return this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this._channel, event);
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.ElectronApplication.Close)
waiter.rejectOnEvent(this, Events.ElectronApplication.Close, new Error('Electron application closed'));

View File

@ -93,7 +93,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
}
private _setupNavigationWaiter(options: { timeout?: number }): Waiter {
const waiter = new Waiter(this._page!._channel, '');
const waiter = new Waiter(this._page!, '');
if (this._page!.isClosed())
waiter.rejectImmediately(new Error('Navigation failed because page was closed!'));
waiter.rejectOnEvent(this._page!, Events.Page.Close, new Error('Navigation failed because page was closed!'));

View File

@ -474,7 +474,7 @@ export class WebSocket extends ChannelOwner<channels.WebSocketChannel> implement
return this._wrapApiCall(async () => {
const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this._channel, event);
const waiter = Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== Events.WebSocket.Error)
waiter.rejectOnEvent(this, Events.WebSocket.Error, new Error('Socket error'));

View File

@ -359,7 +359,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
};
const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for request ${trimmedUrl}` : undefined;
return this._waitForEvent(this._channel, Events.Page.Request, { predicate, timeout: options.timeout }, logLine);
return this._waitForEvent(Events.Page.Request, { predicate, timeout: options.timeout }, logLine);
}
async waitForResponse(urlOrPredicate: string | RegExp | ((r: Response) => boolean | Promise<boolean>), options: { timeout?: number } = {}): Promise<Response> {
@ -370,17 +370,17 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
};
const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for response ${trimmedUrl}` : undefined;
return this._waitForEvent(this._channel, Events.Page.Response, { predicate, timeout: options.timeout }, logLine);
return this._waitForEvent(Events.Page.Response, { predicate, timeout: options.timeout }, logLine);
}
async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> {
return this._waitForEvent(this._channel, event, optionsOrPredicate, `waiting for event "${event}"`);
return this._waitForEvent(event, optionsOrPredicate, `waiting for event "${event}"`);
}
private async _waitForEvent(channel: channels.EventTargetChannel, event: string, optionsOrPredicate: WaitForEventOptions, logLine?: string): Promise<any> {
private async _waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions, logLine?: string): Promise<any> {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(channel, event);
const waiter = Waiter.createForEvent(this, event);
if (logLine)
waiter.log(logLine);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);

View File

@ -19,28 +19,30 @@ import { rewriteErrorMessage } from '../utils/stackTrace';
import { TimeoutError } from '../utils/errors';
import { createGuid } from '../utils/utils';
import * as channels from '../protocol/channels';
import { ChannelOwner } from './channelOwner';
export class Waiter {
private _dispose: (() => void)[];
private _failures: Promise<any>[] = [];
private _immediateError?: Error;
// TODO: can/should we move these logs into wrapApiCall?
private _logs: string[] = [];
private _channel: channels.EventTargetChannel;
private _channelOwner: ChannelOwner<channels.EventTargetChannel>;
private _waitId: string;
private _error: string | undefined;
constructor(channel: channels.EventTargetChannel, event: string) {
constructor(channelOwner: ChannelOwner<channels.EventTargetChannel>, event: string) {
this._waitId = createGuid();
this._channel = channel;
this._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'before', event } }).catch(() => {});
this._channelOwner = channelOwner;
this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'before', event } }).catch(() => {});
this._dispose = [
() => this._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'after', error: this._error } }).catch(() => {})
() => this._channelOwner._wrapApiCall(async () => {
await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'after', error: this._error } });
}, true).catch(() => {}),
];
}
static createForEvent(channel: channels.EventTargetChannel, event: string) {
return new Waiter(channel, event);
static createForEvent(channelOwner: ChannelOwner<channels.EventTargetChannel>, event: string) {
return new Waiter(channelOwner, event);
}
async waitForEvent<T = void>(emitter: EventEmitter, event: string, predicate?: (arg: T) => boolean | Promise<boolean>): Promise<T> {
@ -89,7 +91,9 @@ export class Waiter {
log(s: string) {
this._logs.push(s);
this._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'log', message: s } }).catch(() => {});
this._channelOwner._wrapApiCall(async () => {
await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: 'log', message: s } }).catch(() => {});
}, true);
}
private _rejectOn(promise: Promise<any>, dispose?: () => void) {

View File

@ -252,7 +252,7 @@ test('should show trace title', async ({ runInlineTest, page, showReport }) => {
test('should show timed out steps', async ({ runInlineTest, page, showReport }) => {
const result = await runInlineTest({
'playwright.config.js': `
module.exports = { timeout: 500 };
module.exports = { timeout: 3000 };
`,
'a.test.js': `
const { test } = pwt;

View File

@ -259,7 +259,10 @@ test('should report api steps', async ({ runInlineTest }) => {
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<button></button>');
await Promise.all([
page.waitForNavigation(),
page.goto('data:text/html,<button></button>'),
]);
await page.click('button');
});
@ -290,8 +293,10 @@ test('should report api steps', async ({ runInlineTest }) => {
`%% begin {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`,
`%% end {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`,
`%% end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}`,
`%% begin {\"title\":\"page.setContent\",\"category\":\"pw:api\"}`,
`%% end {\"title\":\"page.setContent\",\"category\":\"pw:api\"}`,
`%% begin {\"title\":\"page.waitForNavigation\",\"category\":\"pw:api\"}`,
`%% begin {\"title\":\"page.goto(data:text/html,<button></button>)\",\"category\":\"pw:api\"}`,
`%% end {\"title\":\"page.waitForNavigation\",\"category\":\"pw:api\"}`,
`%% end {\"title\":\"page.goto(data:text/html,<button></button>)\",\"category\":\"pw:api\"}`,
`%% begin {\"title\":\"page.click(button)\",\"category\":\"pw:api\"}`,
`%% end {\"title\":\"page.click(button)\",\"category\":\"pw:api\"}`,
`%% begin {\"title\":\"After Hooks\",\"category\":\"hook\"}`,