chore: remove apiName plumbing and some unused methods from server side (#3481)

We append apiName where needed on the client instead.
This commit is contained in:
Dmitry Gozman 2020-08-14 18:25:32 -07:00 committed by GitHub
parent 244c2f37b6
commit bc23324878
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 67 additions and 269 deletions

View file

@ -86,7 +86,7 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate;
const progressController = new ProgressController(this._apiLogger, this._timeoutSettings.timeout(options), 'browserContext.waitForEvent');
const progressController = new ProgressController(this._apiLogger, this._timeoutSettings.timeout(options));
if (event !== Events.BrowserContext.Close)
this._closePromise.then(error => progressController.abort(error));
return progressController.run(progress => helper.waitForEvent(progress, this, event, options.predicate).promise);
@ -191,9 +191,9 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
if (!this.pages().length)
await this.waitForEvent('page');
const pages = this.pages();
await pages[0].waitForLoadState();
if (pages.length !== 1 || pages[0].url() !== 'about:blank')
throw new Error(`Arguments can not specify page to be opened (first url is ${pages[0].url()})`);
await pages[0].mainFrame().waitForLoadState();
if (pages.length !== 1 || pages[0].mainFrame().url() !== 'about:blank')
throw new Error(`Arguments can not specify page to be opened (first url is ${pages[0].mainFrame().url()})`);
if (this._options.isMobile || this._options.locale) {
// Workaround for:
// - chromium fails to change isMobile for existing page;

View file

@ -223,7 +223,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
async scrollIntoViewIfNeeded(options: types.TimeoutOptions = {}) {
return this._page._runAbortableTask(
progress => this._waitAndScrollIntoViewIfNeeded(progress),
this._page._timeoutSettings.timeout(options), 'elementHandle.scrollIntoViewIfNeeded');
this._page._timeoutSettings.timeout(options));
}
private async _waitForVisible(progress: Progress): Promise<'error:notconnected' | 'done'> {
@ -376,7 +376,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._hover(progress, options);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.hover');
}, this._page._timeoutSettings.timeout(options));
}
_hover(progress: Progress, options: types.PointerActionOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -387,7 +387,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._click(progress, options);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.click');
}, this._page._timeoutSettings.timeout(options));
}
_click(progress: Progress, options: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -398,7 +398,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._dblclick(progress, options);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.dblclick');
}, this._page._timeoutSettings.timeout(options));
}
_dblclick(progress: Progress, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -409,7 +409,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._selectOption(progress, values, options);
return throwRetargetableDOMError(result);
}, this._page._timeoutSettings.timeout(options), 'elementHandle.selectOption');
}, this._page._timeoutSettings.timeout(options));
}
async _selectOption(progress: Progress, values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions): Promise<string[] | 'error:notconnected'> {
@ -444,7 +444,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._fill(progress, value, options);
assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.fill');
}, this._page._timeoutSettings.timeout(options));
}
async _fill(progress: Progress, value: string, options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -483,14 +483,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
const pollHandler = new InjectedScriptPollHandler(progress, poll);
const result = throwFatalDOMError(await pollHandler.finish());
assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.selectText');
}, this._page._timeoutSettings.timeout(options));
}
async setInputFiles(files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions = {}) {
return this._page._runAbortableTask(async progress => {
const result = await this._setInputFiles(progress, files, options);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.setInputFiles');
}, this._page._timeoutSettings.timeout(options));
}
async _setInputFiles(progress: Progress, files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -517,7 +517,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._focus(progress);
return assertDone(throwRetargetableDOMError(result));
}, 0, 'elementHandle.focus');
}, 0);
}
async _focus(progress: Progress, resetSelectionIfNotFocused?: boolean): Promise<'error:notconnected' | 'done'> {
@ -530,7 +530,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._type(progress, text, options);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.type');
}, this._page._timeoutSettings.timeout(options));
}
async _type(progress: Progress, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -549,7 +549,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._press(progress, key, options);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.press');
}, this._page._timeoutSettings.timeout(options));
}
async _press(progress: Progress, key: string, options: { delay?: number } & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -568,14 +568,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._page._runAbortableTask(async progress => {
const result = await this._setChecked(progress, true, options);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.check');
}, this._page._timeoutSettings.timeout(options));
}
async uncheck(options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
return this._page._runAbortableTask(async progress => {
const result = await this._setChecked(progress, false, options);
return assertDone(throwRetargetableDOMError(result));
}, this._page._timeoutSettings.timeout(options), 'elementHandle.uncheck');
}, this._page._timeoutSettings.timeout(options));
}
async _setChecked(progress: Progress, state: boolean, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
@ -596,7 +596,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
async screenshot(options: types.ElementScreenshotOptions = {}): Promise<Buffer> {
return this._page._runAbortableTask(
progress => this._page._screenshotter.screenshotElement(progress, this, options),
this._page._timeoutSettings.timeout(options), 'elementHandle.screenshot');
this._page._timeoutSettings.timeout(options));
}
async $(selector: string): Promise<ElementHandle | null> {
@ -655,7 +655,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}
const handle = result.asElement() as ElementHandle<Element>;
return handle._adoptTo(await this._context.frame._mainContext());
}, this._page._timeoutSettings.timeout(options), 'elementHandle.waitForSelector');
}, this._page._timeoutSettings.timeout(options));
}
async _adoptTo(context: FrameExecutionContext): Promise<ElementHandle<T>> {

View file

@ -378,11 +378,6 @@ export class Frame {
return this._page;
}
private _apiName(method: string) {
const subject = this._page._callingPageAPI ? 'page' : 'frame';
return `${subject}.${method}`;
}
_onLifecycleEvent(event: types.LifecycleEvent) {
if (this._firedLifecycleEvents.has(event))
return;
@ -434,7 +429,7 @@ export class Frame {
}
async goto(url: string, options: types.GotoOptions = {}): Promise<network.Response | null> {
return runNavigationTask(this, options, this._apiName('goto'), async progress => {
return runNavigationTask(this, options, async progress => {
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
progress.logger.info(`navigating to "${url}", waiting until "${waitUntil}"`);
const headers = (this._page._state.extraHTTPHeaders || {});
@ -478,7 +473,7 @@ export class Frame {
}
async waitForNavigation(options: types.WaitForNavigationOptions = {}): Promise<network.Response | null> {
return runNavigationTask(this, options, this._apiName('waitForNavigation'), async progress => {
return runNavigationTask(this, options, async progress => {
const toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : '';
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
progress.logger.info(`waiting for navigation${toUrl} until "${waitUntil}"`);
@ -502,7 +497,7 @@ export class Frame {
}
async waitForLoadState(state: types.LifecycleEvent = 'load', options: types.TimeoutOptions = {}): Promise<void> {
return runNavigationTask(this, options, this._apiName('waitForLoadState'), progress => this._waitForLoadState(progress, state));
return runNavigationTask(this, options, progress => this._waitForLoadState(progress, state));
}
async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise<void> {
@ -578,7 +573,7 @@ export class Frame {
}
const handle = result.asElement() as dom.ElementHandle<Element>;
return handle._adoptTo(await this._mainContext());
}, this._page._timeoutSettings.timeout(options), this._apiName('waitForSelector'));
}, this._page._timeoutSettings.timeout(options));
}
async dispatchEvent(selector: string, type: string, eventInit?: Object, options: types.TimeoutOptions = {}): Promise<void> {
@ -588,7 +583,7 @@ export class Frame {
progress.logger.info(`Dispatching "${type}" event on selector "${selector}"...`);
// Note: we always dispatch events in the main world.
await this._scheduleRerunnableTask(progress, 'main', task);
}, this._page._timeoutSettings.timeout(options), this._apiName('dispatchEvent'));
}, this._page._timeoutSettings.timeout(options));
}
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
@ -638,7 +633,7 @@ export class Frame {
}
async setContent(html: string, options: types.NavigateOptions = {}): Promise<void> {
return runNavigationTask(this, options, this._apiName('setContent'), async progress => {
return runNavigationTask(this, options, async progress => {
const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil;
progress.logger.info(`setting frame content, waiting until "${waitUntil}"`);
const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
@ -823,8 +818,7 @@ export class Frame {
private async _retryWithSelectorIfNotConnected<R>(
selector: string, options: types.TimeoutOptions,
action: (progress: Progress, handle: dom.ElementHandle<Element>) => Promise<R | 'error:notconnected'>,
apiName: string): Promise<R> {
action: (progress: Progress, handle: dom.ElementHandle<Element>) => Promise<R | 'error:notconnected'>): Promise<R> {
const info = selectors._parseSelector(selector);
return this._page._runAbortableTask(async progress => {
while (progress.isRunning()) {
@ -846,23 +840,23 @@ export class Frame {
return result;
}
return undefined as any;
}, this._page._timeoutSettings.timeout(options), apiName);
}, this._page._timeoutSettings.timeout(options));
}
async click(selector: string, options: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._click(progress, options), this._apiName('click'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._click(progress, options));
}
async dblclick(selector: string, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._dblclick(progress, options), this._apiName('dblclick'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._dblclick(progress, options));
}
async fill(selector: string, value: string, options: types.NavigatingActionWaitOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._fill(progress, value, options), this._apiName('fill'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._fill(progress, value, options));
}
async focus(selector: string, options: types.TimeoutOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._focus(progress), this._apiName('focus'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._focus(progress));
}
async textContent(selector: string, options: types.TimeoutOptions = {}): Promise<string | null> {
@ -871,7 +865,7 @@ export class Frame {
return this._page._runAbortableTask(async progress => {
progress.logger.info(` retrieving textContent from "${selector}"`);
return this._scheduleRerunnableTask(progress, info.world, task);
}, this._page._timeoutSettings.timeout(options), this._apiName('textContent'));
}, this._page._timeoutSettings.timeout(options));
}
async innerText(selector: string, options: types.TimeoutOptions = {}): Promise<string> {
@ -881,7 +875,7 @@ export class Frame {
progress.logger.info(` retrieving innerText from "${selector}"`);
const result = dom.throwFatalDOMError(await this._scheduleRerunnableTask(progress, info.world, task));
return result.innerText;
}, this._page._timeoutSettings.timeout(options), this._apiName('innerText'));
}, this._page._timeoutSettings.timeout(options));
}
async innerHTML(selector: string, options: types.TimeoutOptions = {}): Promise<string> {
@ -890,7 +884,7 @@ export class Frame {
return this._page._runAbortableTask(async progress => {
progress.logger.info(` retrieving innerHTML from "${selector}"`);
return this._scheduleRerunnableTask(progress, info.world, task);
}, this._page._timeoutSettings.timeout(options), this._apiName('innerHTML'));
}, this._page._timeoutSettings.timeout(options));
}
async getAttribute(selector: string, name: string, options: types.TimeoutOptions = {}): Promise<string | null> {
@ -899,35 +893,35 @@ export class Frame {
return this._page._runAbortableTask(async progress => {
progress.logger.info(` retrieving attribute "${name}" from "${selector}"`);
return this._scheduleRerunnableTask(progress, info.world, task);
}, this._page._timeoutSettings.timeout(options), this._apiName('getAttribute'));
}, this._page._timeoutSettings.timeout(options));
}
async hover(selector: string, options: types.PointerActionOptions & types.PointerActionWaitOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._hover(progress, options), this._apiName('hover'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._hover(progress, options));
}
async selectOption(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions = {}): Promise<string[]> {
return this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._selectOption(progress, values, options), this._apiName('selectOption'));
return this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._selectOption(progress, values, options));
}
async setInputFiles(selector: string, files: string | types.FilePayload | string[] | types.FilePayload[], options: types.NavigatingActionWaitOptions = {}): Promise<void> {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setInputFiles(progress, files, options), this._apiName('setInputFiles'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setInputFiles(progress, files, options));
}
async type(selector: string, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._type(progress, text, options), this._apiName('type'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._type(progress, text, options));
}
async press(selector: string, key: string, options: { delay?: number } & types.NavigatingActionWaitOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._press(progress, key, options), this._apiName('press'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._press(progress, key, options));
}
async check(selector: string, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setChecked(progress, true, options), this._apiName('check'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setChecked(progress, true, options));
}
async uncheck(selector: string, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setChecked(progress, false, options), this._apiName('uncheck'));
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._setChecked(progress, false, options));
}
async waitForTimeout(timeout: number) {
@ -957,7 +951,7 @@ export class Frame {
}, { predicateBody, polling, arg });
return this._page._runAbortableTask(
progress => this._scheduleRerunnableHandleTask(progress, 'main', task),
this._page._timeoutSettings.timeout(options), this._apiName('waitForFunction'));
this._page._timeoutSettings.timeout(options));
}
async title(): Promise<string> {
@ -1134,9 +1128,9 @@ class SignalBarrier {
}
}
async function runNavigationTask<T>(frame: Frame, options: types.TimeoutOptions, apiName: string, task: (progress: Progress) => Promise<T>): Promise<T> {
async function runNavigationTask<T>(frame: Frame, options: types.TimeoutOptions, task: (progress: Progress) => Promise<T>): Promise<T> {
const page = frame._page;
const controller = new ProgressController(page._logger, page._timeoutSettings.navigationTimeout(options), apiName);
const controller = new ProgressController(page._logger, page._timeoutSettings.navigationTimeout(options));
page._disconnectedPromise.then(() => controller.abort(new Error('Navigation failed because page was closed!')));
page._crashedPromise.then(() => controller.abort(new Error('Navigation failed because page crashed!')));
frame._detachedPromise.then(() => controller.abort(new Error('Navigating frame was detached!')));

View file

@ -117,7 +117,6 @@ export class Page extends EventEmitter {
readonly coverage: any;
_routes: { url: types.URLMatch, handler: network.RouteHandler }[] = [];
_ownedContext: BrowserContext | undefined;
_callingPageAPI = false;
constructor(delegate: PageDelegate, browserContext: BrowserContextBase) {
super();
@ -168,10 +167,10 @@ export class Page extends EventEmitter {
this._disconnectedCallback(new Error('Page closed'));
}
async _runAbortableTask<T>(task: (progress: Progress) => Promise<T>, timeout: number, apiName: string): Promise<T> {
async _runAbortableTask<T>(task: (progress: Progress) => Promise<T>, timeout: number): Promise<T> {
return runAbortableTask(async progress => {
return task(progress);
}, this._logger, timeout, apiName);
}, this._logger, timeout);
}
async _onFileChooserOpened(handle: dom.ElementHandle) {
@ -219,63 +218,6 @@ export class Page extends EventEmitter {
this._timeoutSettings.setDefaultTimeout(timeout);
}
async $(selector: string): Promise<dom.ElementHandle<Element> | null> {
return this._attributeToPage(() => this.mainFrame().$(selector));
}
async waitForSelector(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element> | null> {
return this._attributeToPage(() => this.mainFrame().waitForSelector(selector, options));
}
async dispatchEvent(selector: string, type: string, eventInit?: Object, options?: types.TimeoutOptions): Promise<void> {
return this._attributeToPage(() => this.mainFrame().dispatchEvent(selector, type, eventInit, options));
}
async evaluateHandle<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: js.Func1<void, R>, arg?: any): Promise<js.SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>> {
assertMaxArguments(arguments.length, 2);
return this._attributeToPage(() => this.mainFrame().evaluateHandle(pageFunction, arg));
}
async _evaluateExpressionHandle(expression: string, isFunction: boolean, arg: any): Promise<any> {
return this._attributeToPage(() => this.mainFrame()._evaluateExpressionHandle(expression, isFunction, arg));
}
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
async $eval<R>(selector: string, pageFunction: js.FuncOn<Element, void, R>, arg?: any): Promise<R>;
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
assertMaxArguments(arguments.length, 3);
return this._attributeToPage(() => this.mainFrame().$eval(selector, pageFunction, arg));
}
async _$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
return this._attributeToPage(() => this.mainFrame()._$evalExpression(selector, expression, isFunction, arg));
}
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R>;
async $$eval<R>(selector: string, pageFunction: js.FuncOn<Element[], void, R>, arg?: any): Promise<R>;
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
assertMaxArguments(arguments.length, 3);
return this._attributeToPage(() => this.mainFrame().$$eval(selector, pageFunction, arg));
}
async _$$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
return this._attributeToPage(() => this.mainFrame()._$$evalExpression(selector, expression, isFunction, arg));
}
async $$(selector: string): Promise<dom.ElementHandle<Element>[]> {
return this._attributeToPage(() => this.mainFrame().$$(selector));
}
async addScriptTag(options: { url?: string; path?: string; content?: string; type?: string; }): Promise<dom.ElementHandle> {
return this._attributeToPage(() => this.mainFrame().addScriptTag(options));
}
async addStyleTag(options: { url?: string; path?: string; content?: string; }): Promise<dom.ElementHandle> {
return this._attributeToPage(() => this.mainFrame().addStyleTag(options));
}
async exposeFunction(name: string, playwrightFunction: Function) {
await this.exposeBinding(name, (options, ...args: any) => playwrightFunction(...args));
}
@ -310,36 +252,12 @@ export class Page extends EventEmitter {
this.emit(Events.Page.Console, message);
}
url(): string {
return this._attributeToPage(() => this.mainFrame().url());
}
async content(): Promise<string> {
return this._attributeToPage(() => this.mainFrame().content());
}
async setContent(html: string, options?: types.NavigateOptions): Promise<void> {
return this._attributeToPage(() => this.mainFrame().setContent(html, options));
}
async goto(url: string, options?: types.GotoOptions): Promise<network.Response | null> {
return this._attributeToPage(() => this.mainFrame().goto(url, options));
}
async reload(options?: types.NavigateOptions): Promise<network.Response | null> {
const waitPromise = this.waitForNavigation(options);
const waitPromise = this.mainFrame().waitForNavigation(options);
await this._delegate.reload();
return waitPromise;
}
async waitForLoadState(state?: types.LifecycleEvent, options?: types.TimeoutOptions): Promise<void> {
return this._attributeToPage(() => this.mainFrame().waitForLoadState(state, options));
}
async waitForNavigation(options?: types.WaitForNavigationOptions): Promise<network.Response | null> {
return this._attributeToPage(() => this.mainFrame().waitForNavigation(options));
}
async waitForRequest(urlOrPredicate: string | RegExp | ((r: network.Request) => boolean), options: types.TimeoutOptions = {}): Promise<network.Request> {
const predicate = (request: network.Request) => {
if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
@ -360,7 +278,7 @@ export class Page extends EventEmitter {
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate;
const progressController = new ProgressController(this._logger, this._timeoutSettings.timeout(options), 'page.waitForEvent');
const progressController = new ProgressController(this._logger, this._timeoutSettings.timeout(options));
this._disconnectedPromise.then(error => progressController.abort(error));
if (event !== Events.Page.Crash)
this._crashedPromise.then(error => progressController.abort(error));
@ -368,7 +286,7 @@ export class Page extends EventEmitter {
}
async goBack(options?: types.NavigateOptions): Promise<network.Response | null> {
const waitPromise = this.waitForNavigation(options);
const waitPromise = this.mainFrame().waitForNavigation(options);
const result = await this._delegate.goBack();
if (!result) {
waitPromise.catch(() => {});
@ -378,7 +296,7 @@ export class Page extends EventEmitter {
}
async goForward(options?: types.NavigateOptions): Promise<network.Response | null> {
const waitPromise = this.waitForNavigation(options);
const waitPromise = this.mainFrame().waitForNavigation(options);
const result = await this._delegate.goForward();
if (!result) {
waitPromise.catch(() => {});
@ -412,17 +330,6 @@ export class Page extends EventEmitter {
await this._delegate.bringToFront();
}
async evaluate<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: js.Func1<void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R> {
assertMaxArguments(arguments.length, 2);
return this._attributeToPage(() => this.mainFrame().evaluate(pageFunction, arg));
}
async _evaluateExpression(expression: string, isFunction: boolean, arg: any): Promise<any> {
return this._attributeToPage(() => this.mainFrame()._evaluateExpression(expression, isFunction, arg));
}
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
const source = await helper.evaluationScript(script, arg);
await this._addInitScriptExpression(source);
@ -482,11 +389,7 @@ export class Page extends EventEmitter {
async screenshot(options: types.ScreenshotOptions = {}): Promise<Buffer> {
return this._runAbortableTask(
progress => this._screenshotter.screenshotPage(progress, options),
this._timeoutSettings.timeout(options), 'page.screenshot');
}
async title(): Promise<string> {
return this._attributeToPage(() => this.mainFrame().title());
this._timeoutSettings.timeout(options));
}
async close(options?: { runBeforeUnload?: boolean }) {
@ -513,89 +416,10 @@ export class Page extends EventEmitter {
return this._closedState === 'closed';
}
private _attributeToPage<T>(func: () => T): T {
try {
this._callingPageAPI = true;
return func();
} finally {
this._callingPageAPI = false;
}
}
async click(selector: string, options?: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this._attributeToPage(() => this.mainFrame().click(selector, options));
}
async dblclick(selector: string, options?: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this._attributeToPage(() => this.mainFrame().dblclick(selector, options));
}
async fill(selector: string, value: string, options?: types.NavigatingActionWaitOptions) {
return this._attributeToPage(() => this.mainFrame().fill(selector, value, options));
}
async focus(selector: string, options?: types.TimeoutOptions) {
return this._attributeToPage(() => this.mainFrame().focus(selector, options));
}
async textContent(selector: string, options?: types.TimeoutOptions): Promise<null|string> {
return this._attributeToPage(() => this.mainFrame().textContent(selector, options));
}
async innerText(selector: string, options?: types.TimeoutOptions): Promise<string> {
return this._attributeToPage(() => this.mainFrame().innerText(selector, options));
}
async innerHTML(selector: string, options?: types.TimeoutOptions): Promise<string> {
return this._attributeToPage(() => this.mainFrame().innerHTML(selector, options));
}
async getAttribute(selector: string, name: string, options?: types.TimeoutOptions): Promise<string | null> {
return this._attributeToPage(() => this.mainFrame().getAttribute(selector, name, options));
}
async hover(selector: string, options?: types.PointerActionOptions & types.PointerActionWaitOptions) {
return this._attributeToPage(() => this.mainFrame().hover(selector, options));
}
async selectOption(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | null, options?: types.NavigatingActionWaitOptions): Promise<string[]> {
return this._attributeToPage(() => this.mainFrame().selectOption(selector, values, options));
}
async setInputFiles(selector: string, files: string | types.FilePayload | string[] | types.FilePayload[], options?: types.NavigatingActionWaitOptions): Promise<void> {
return this._attributeToPage(() => this.mainFrame().setInputFiles(selector, files, options));
}
async type(selector: string, text: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
return this._attributeToPage(() => this.mainFrame().type(selector, text, options));
}
async press(selector: string, key: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
return this._attributeToPage(() => this.mainFrame().press(selector, key, options));
}
async check(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this._attributeToPage(() => this.mainFrame().check(selector, options));
}
async uncheck(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this._attributeToPage(() => this.mainFrame().uncheck(selector, options));
}
async waitForTimeout(timeout: number) {
await this.mainFrame().waitForTimeout(timeout);
}
async waitForFunction<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>>;
async waitForFunction<R>(pageFunction: js.Func1<void, R>, arg?: any, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>>;
async waitForFunction<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>> {
return this._attributeToPage(() => this.mainFrame().waitForFunction(pageFunction, arg, options));
}
async _waitForFunctionExpression<R>(expression: string, isFunction: boolean, arg: any, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
return this._attributeToPage(() => this.mainFrame()._waitForFunctionExpression(expression, isFunction, arg, options));
}
workers(): Worker[] {
return [...this._workers.values()];
}

View file

@ -28,16 +28,11 @@ export interface Progress {
throwIfAborted(): void;
}
export async function runAbortableTask<T>(task: (progress: Progress) => Promise<T>, logger: Logger, timeout: number, apiName: string): Promise<T> {
const controller = new ProgressController(logger, timeout, apiName);
export async function runAbortableTask<T>(task: (progress: Progress) => Promise<T>, logger: Logger, timeout: number): Promise<T> {
const controller = new ProgressController(logger, timeout);
return controller.run(task);
}
let useApiName = true;
export function setUseApiName(value: boolean) {
useApiName = value;
}
export class ProgressController {
// Promise and callback that forcefully abort the progress.
// This promise always rejects.
@ -54,12 +49,10 @@ export class ProgressController {
private _logger: Logger;
private _state: 'before' | 'running' | 'aborted' | 'finished' = 'before';
private _apiName: string;
private _deadline: number;
private _timeout: number;
constructor(logger: Logger, timeout: number, apiName: string) {
this._apiName = apiName;
constructor(logger: Logger, timeout: number) {
this._logger = logger;
this._timeout = timeout;
@ -74,7 +67,7 @@ export class ProgressController {
assert(this._state === 'before');
this._state = 'running';
const loggerScope = this._logger.createScope(useApiName ? this._apiName : undefined, true);
const loggerScope = this._logger.createScope(undefined, true);
const progress: Progress = {
aborted: this._abortedPromise,
@ -105,7 +98,6 @@ export class ProgressController {
} catch (e) {
this._aborted();
rewriteErrorMessage(e,
(useApiName ? `${this._apiName}: ` : '') +
e.message +
formatLogRecording(loggerScope.recording()) +
kLoggingNote);

View file

@ -18,14 +18,11 @@ import { DispatcherConnection } from './server/dispatcher';
import type { Playwright as PlaywrightImpl } from '../server/playwright';
import type { Playwright as PlaywrightAPI } from './client/playwright';
import { PlaywrightDispatcher } from './server/playwrightDispatcher';
import { setUseApiName } from '../progress';
import { Connection } from './client/connection';
import { isUnderTest } from '../helper';
import { BrowserServerLauncherImpl } from './browserServerImpl';
export function setupInProcess(playwright: PlaywrightImpl): PlaywrightAPI {
setUseApiName(false);
const clientConnection = new Connection();
const dispatcherConnection = new DispatcherConnection();

View file

@ -19,11 +19,8 @@ import { DispatcherConnection } from './server/dispatcher';
import { Playwright } from '../server/playwright';
import { PlaywrightDispatcher } from './server/playwrightDispatcher';
import { Electron } from '../server/electron';
import { setUseApiName } from '../progress';
import { gracefullyCloseAll } from '../server/processLauncher';
setUseApiName(false);
const dispatcherConnection = new DispatcherConnection();
const transport = new Transport(process.stdout, process.stdin);
transport.onclose = async () => {

View file

@ -149,10 +149,6 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
this._page.removeListener(Events.Page.FileChooser, this._onFileChooser);
}
async title() {
return await this._page.title();
}
async keyboardDown(params: { key: string }): Promise<void> {
await this._page.keyboard.down(params.key);
}

View file

@ -85,8 +85,7 @@ export abstract class BrowserTypeBase implements BrowserType {
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
options = validateLaunchOptions(options);
const loggers = new Loggers(options.logger);
const label = 'browserType.launch';
const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, undefined), loggers.browser, TimeoutSettings.timeout(options), label).catch(e => { throw this._rewriteStartupError(e, label); });
const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, undefined), loggers.browser, TimeoutSettings.timeout(options)).catch(e => { throw this._rewriteStartupError(e); });
return browser;
}
@ -95,8 +94,7 @@ export abstract class BrowserTypeBase implements BrowserType {
options = validateLaunchOptions(options);
const persistent = validateBrowserContextOptions(options);
const loggers = new Loggers(options.logger);
const label = 'browserType.launchPersistentContext';
const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, persistent, userDataDir), loggers.browser, TimeoutSettings.timeout(options), label).catch(e => { throw this._rewriteStartupError(e, label); });
const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, loggers, persistent, userDataDir), loggers.browser, TimeoutSettings.timeout(options)).catch(e => { throw this._rewriteStartupError(e); });
return browser._defaultContext!;
}
@ -224,7 +222,7 @@ export abstract class BrowserTypeBase implements BrowserType {
abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BrowserBase>;
abstract _amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env;
abstract _amendArguments(browserArguments: string[]): string[];
abstract _rewriteStartupError(error: Error, prefix: string): Error;
abstract _rewriteStartupError(error: Error): Error;
abstract _attemptToGracefullyCloseBrowser(transport: ConnectionTransport): void;
}

View file

@ -61,13 +61,13 @@ export class Chromium extends BrowserTypeBase {
return CRBrowser.connect(transport, options, devtools);
}
_rewriteStartupError(error: Error, prefix: string): Error {
_rewriteStartupError(error: Error): Error {
// These error messages are taken from Chromium source code as of July, 2020:
// https://github.com/chromium/chromium/blob/70565f67e79f79e17663ad1337dc6e63ee207ce9/content/browser/zygote_host/zygote_host_impl_linux.cc
if (!error.message.includes('crbug.com/357670') && !error.message.includes('No usable sandbox!') && !error.message.includes('crbug.com/638180'))
return error;
return rewriteErrorMessage(error, [
`${prefix}: Chromium sandboxing failed!`,
`Chromium sandboxing failed!`,
`================================`,
`To workaround sandboxing issues, do either of the following:`,
` - (preferred): Configure environment to support sandboxing: https://github.com/microsoft/playwright/blob/master/docs/troubleshooting.md`,

View file

@ -94,7 +94,7 @@ export class ElectronApplication extends EventEmitter {
this._windows.delete(page);
});
this._windows.add(page);
await page.waitForLoadState('domcontentloaded').catch(e => {}); // can happen after detach
await page.mainFrame().waitForLoadState('domcontentloaded').catch(e => {}); // can happen after detach
this.emit(ElectronEvents.ElectronApplication.Window, page);
}
@ -136,7 +136,7 @@ export class ElectronApplication extends EventEmitter {
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
const options = typeof optionsOrPredicate === 'function' ? { predicate: optionsOrPredicate } : optionsOrPredicate;
const progressController = new ProgressController(this._apiLogger, this._timeoutSettings.timeout(options), 'electron.waitForEvent');
const progressController = new ProgressController(this._apiLogger, this._timeoutSettings.timeout(options));
if (event !== ElectronEvents.ElectronApplication.Close)
this._browserContext._closePromise.then(error => progressController.abort(error));
return progressController.run(progress => helper.waitForEvent(progress, this, event, options.predicate).promise);
@ -212,6 +212,6 @@ export class Electron {
app = new ElectronApplication(loggers, browser, nodeConnection);
await app._init();
return app;
}, loggers.browser, TimeoutSettings.timeout(options), 'electron.launch');
}, loggers.browser, TimeoutSettings.timeout(options));
}
}

View file

@ -36,7 +36,7 @@ export class Firefox extends BrowserTypeBase {
return FFBrowser.connect(transport, options);
}
_rewriteStartupError(error: Error, prefix: string): Error {
_rewriteStartupError(error: Error): Error {
return error;
}

View file

@ -42,7 +42,7 @@ export class WebKit extends BrowserTypeBase {
return browserArguments;
}
_rewriteStartupError(error: Error, prefix: string): Error {
_rewriteStartupError(error: Error): Error {
return error;
}

View file

@ -22,7 +22,7 @@ const CRASH_FAIL = (FFOX && WIN) || WIRE;
// Firefox Win: it just doesn't crash sometimes.
function crash(pageImpl) {
if (CHROMIUM)
pageImpl.goto('chrome://crash').catch(e => {});
pageImpl.mainFrame().goto('chrome://crash').catch(e => {});
else if (WEBKIT)
pageImpl._delegate._session.send('Page.crash', {}).catch(e => {});
else if (FFOX)