Skip to content

Commit

Permalink
api(waitFor): click(waitFor) -> click(force) (#1275)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman authored Mar 7, 2020
1 parent 578880c commit 3c35d7b
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 133 deletions.
118 changes: 90 additions & 28 deletions docs/api.md

Large diffs are not rendered by default.

32 changes: 15 additions & 17 deletions src/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return point;
}

async _performPointerAction(action: (point: types.Point) => Promise<void>, options?: PointerActionOptions & types.ActionWaitOptions): Promise<void> {
const { waitFor = true } = (options || {});
if (!helper.isBoolean(waitFor))
throw new Error('waitFor option should be a boolean, got "' + (typeof waitFor) + '"');
if (waitFor)
async _performPointerAction(action: (point: types.Point) => Promise<void>, options?: PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<void> {
const { force = false } = (options || {});
if (!force)
await this._waitForDisplayedAtStablePosition(options);
const offset = options ? options.offset : undefined;
await this._scrollRectIntoViewIfNeeded(offset ? { x: offset.x, y: offset.y, width: 0, height: 0 } : undefined);
const point = offset ? await this._offsetPoint(offset) : await this._clickablePoint();
if (waitFor)
if (!force)
await this._waitForHitTargetAt(point, options);

await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
Expand All @@ -263,23 +261,23 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}, options);
}

hover(options?: PointerActionOptions & types.ActionWaitOptionsNoNavigation): Promise<void> {
hover(options?: PointerActionOptions & types.PointerActionWaitOptions): Promise<void> {
return this._performPointerAction(point => this._page.mouse.move(point.x, point.y), options);
}

click(options?: ClickOptions & types.ActionWaitOptions): Promise<void> {
click(options?: ClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<void> {
return this._performPointerAction(point => this._page.mouse.click(point.x, point.y, options), options);
}

dblclick(options?: MultiClickOptions & types.ActionWaitOptions): Promise<void> {
dblclick(options?: MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<void> {
return this._performPointerAction(point => this._page.mouse.dblclick(point.x, point.y, options), options);
}

tripleclick(options?: MultiClickOptions & types.ActionWaitOptions): Promise<void> {
tripleclick(options?: MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<void> {
return this._performPointerAction(point => this._page.mouse.tripleclick(point.x, point.y, options), options);
}

async select(values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[], options?: types.ActionWaitOptionsNoWaitFor): Promise<string[]> {
async select(values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[], options?: types.NavigatingActionWaitOptions): Promise<string[]> {
let vals: string[] | ElementHandle[] | types.SelectOption[];
if (!Array.isArray(values))
vals = [ values ] as (string[] | ElementHandle[] | types.SelectOption[]);
Expand All @@ -301,7 +299,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}, options);
}

async fill(value: string, options?: types.ActionWaitOptionsNoWaitFor): Promise<void> {
async fill(value: string, options?: types.NavigatingActionWaitOptions): Promise<void> {
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
const error = await this._evaluateInUtility((injected, node, value) => injected.fill(node, value), value);
Expand Down Expand Up @@ -356,29 +354,29 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
throw new Error(errorMessage);
}

async type(text: string, options?: { delay?: number } & types.ActionWaitOptionsNoWaitFor) {
async type(text: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
await this.focus();
await this._page.keyboard.type(text, options);
}, options);
}

async press(key: string, options?: { delay?: number, text?: string } & types.ActionWaitOptionsNoWaitFor) {
async press(key: string, options?: { delay?: number, text?: string } & types.NavigatingActionWaitOptions) {
await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
await this.focus();
await this._page.keyboard.press(key, options);
}, options);
}

async check(options?: types.ActionWaitOptions) {
async check(options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
await this._setChecked(true, options);
}

async uncheck(options?: types.ActionWaitOptions) {
async uncheck(options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
await this._setChecked(false, options);
}

private async _setChecked(state: boolean, options?: types.ActionWaitOptions) {
private async _setChecked(state: boolean, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
if (await this._evaluateInUtility((injected, node) => injected.isCheckboxChecked(node)) === state)
return;
await this.click(options);
Expand Down
91 changes: 38 additions & 53 deletions src/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class FrameManager {
}
}

async waitForNavigationsCreatedBy<T>(action: () => Promise<T>, options?: types.ActionWaitOptionsNoWaitFor): Promise<T> {
async waitForNavigationsCreatedBy<T>(action: () => Promise<T>, options?: types.NavigatingActionWaitOptions): Promise<T> {
if (options && options.waitUntil === 'nowait')
return action();
const barrier = new PendingNavigationBarrier(options);
Expand Down Expand Up @@ -583,10 +583,20 @@ export class Frame {
async waitForSelector(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element> | null> {
if (options && (options as any).visibility)
throw new Error('options.visibility is not supported, did you mean options.waitFor?');
const handle = await this._waitForSelectorInUtilityContext(selector, options);
const { timeout = this._page._timeoutSettings.timeout(), waitFor = 'attached' } = (options || {});
if (!['attached', 'detached', 'visible', 'hidden'].includes(waitFor))
throw new Error(`Unsupported waitFor option "${waitFor}"`);

const task = dom.waitForSelectorTask(selector, waitFor, timeout);
const result = await this._scheduleRerunnableTask(task, 'utility', timeout, `selector "${selectorToString(selector, waitFor)}"`);
if (!result.asElement()) {
result.dispose();
return null;
}
const handle = result.asElement() as dom.ElementHandle<Element>;
const mainContext = await this._mainContext();
if (handle && handle._context !== mainContext) {
const adopted = this._page._delegate.adoptElementHandle(handle, mainContext);
const adopted = await this._page._delegate.adoptElementHandle(handle, mainContext);
handle.dispose();
return adopted;
}
Expand Down Expand Up @@ -801,69 +811,69 @@ export class Frame {
return result!;
}

async click(selector: string, options?: dom.ClickOptions & types.ActionWaitOptions) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async click(selector: string, options?: dom.ClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.click(options);
handle.dispose();
}

async dblclick(selector: string, options?: dom.MultiClickOptions & types.ActionWaitOptions) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async dblclick(selector: string, options?: dom.MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.dblclick(options);
handle.dispose();
}

async tripleclick(selector: string, options?: dom.MultiClickOptions & types.ActionWaitOptions) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async tripleclick(selector: string, options?: dom.MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.tripleclick(options);
handle.dispose();
}

async fill(selector: string, value: string, options?: types.ActionWaitOptions) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async fill(selector: string, value: string, options?: types.NavigatingActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.fill(value, options);
handle.dispose();
}

async focus(selector: string, options?: types.ActionWaitOptionsNoNavigation) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async focus(selector: string, options?: types.TimeoutOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.focus();
handle.dispose();
}

async hover(selector: string, options?: dom.PointerActionOptions & types.ActionWaitOptionsNoNavigation) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async hover(selector: string, options?: dom.PointerActionOptions & types.PointerActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.hover(options);
handle.dispose();
}

async select(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[], options?: types.ActionWaitOptions): Promise<string[]> {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async select(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[], options?: types.NavigatingActionWaitOptions): Promise<string[]> {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
const result = await handle.select(values, options);
handle.dispose();
return result;
}

async type(selector: string, text: string, options?: { delay?: number } & types.ActionWaitOptions) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async type(selector: string, text: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.type(text, options);
handle.dispose();
}

async press(selector: string, key: string, options?: { delay?: number, text?: string } & types.ActionWaitOptions) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async press(selector: string, key: string, options?: { delay?: number, text?: string } & types.NavigatingActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.press(key, options);
handle.dispose();
}

async check(selector: string, options?: types.ActionWaitOptions) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async check(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.check(options);
handle.dispose();
}

async uncheck(selector: string, options?: types.ActionWaitOptions) {
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
async uncheck(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
const handle = await this._waitForSelectorInUtilityContext(selector, options);
await handle.uncheck(options);
handle.dispose();
}
Expand All @@ -878,35 +888,10 @@ export class Frame {
return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
}

private async _optionallyWaitForSelectorInUtilityContext(selector: string, options: types.ActionWaitOptions | undefined): Promise<dom.ElementHandle<Element>> {
const { timeout = this._page._timeoutSettings.timeout(), waitFor = true } = (options || {});
if (!helper.isBoolean(waitFor))
throw new Error('waitFor option should be a boolean, got "' + (typeof waitFor) + '"');
let handle: dom.ElementHandle<Element>;
if (waitFor) {
const maybeHandle = await this._waitForSelectorInUtilityContext(selector, { timeout, waitFor: 'attached' });
if (!maybeHandle)
throw new Error('No node found for selector: ' + selectorToString(selector, 'attached'));
handle = maybeHandle;
} else {
const context = await this._context('utility');
const maybeHandle = await context._$(selector);
assert(maybeHandle, 'No node found for selector: ' + selector);
handle = maybeHandle;
}
return handle;
}

private async _waitForSelectorInUtilityContext(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element> | null> {
private async _waitForSelectorInUtilityContext(selector: string, options?: types.WaitForElementOptions): Promise<dom.ElementHandle<Element>> {
const { timeout = this._page._timeoutSettings.timeout(), waitFor = 'attached' } = (options || {});
if (!['attached', 'detached', 'visible', 'hidden'].includes(waitFor))
throw new Error(`Unsupported waitFor option "${waitFor}"`);
const task = dom.waitForSelectorTask(selector, waitFor, timeout);
const result = await this._scheduleRerunnableTask(task, 'utility', timeout, `selector "${selectorToString(selector, waitFor)}"`);
if (!result.asElement()) {
result.dispose();
return null;
}
return result.asElement() as dom.ElementHandle<Element>;
}

Expand Down Expand Up @@ -1099,12 +1084,12 @@ function selectorToString(selector: string, waitFor: 'attached' | 'detached' | '

class PendingNavigationBarrier {
private _frameIds = new Map<string, number>();
private _options: types.ActionWaitOptionsNoWaitFor | undefined;
private _options: types.NavigatingActionWaitOptions | undefined;
private _protectCount = 0;
private _promise: Promise<void>;
private _promiseCallback = () => {};

constructor(options: types.ActionWaitOptionsNoWaitFor | undefined) {
constructor(options: types.NavigatingActionWaitOptions | undefined) {
this._options = options;
this._promise = new Promise(f => this._promiseCallback = f);
this.retain();
Expand Down
22 changes: 11 additions & 11 deletions src/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,47 +433,47 @@ export class Page extends platform.EventEmitter {
return this._closed;
}

async click(selector: string, options?: dom.ClickOptions & types.ActionWaitOptions) {
async click(selector: string, options?: dom.ClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this.mainFrame().click(selector, options);
}

async dblclick(selector: string, options?: dom.MultiClickOptions & types.ActionWaitOptions) {
async dblclick(selector: string, options?: dom.MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this.mainFrame().dblclick(selector, options);
}

async tripleclick(selector: string, options?: dom.MultiClickOptions & types.ActionWaitOptions) {
async tripleclick(selector: string, options?: dom.MultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this.mainFrame().tripleclick(selector, options);
}

async fill(selector: string, value: string, options?: types.ActionWaitOptions) {
async fill(selector: string, value: string, options?: types.NavigatingActionWaitOptions) {
return this.mainFrame().fill(selector, value, options);
}

async focus(selector: string, options?: types.ActionWaitOptionsNoNavigation) {
async focus(selector: string, options?: types.TimeoutOptions) {
return this.mainFrame().focus(selector, options);
}

async hover(selector: string, options?: dom.PointerActionOptions & types.ActionWaitOptionsNoNavigation) {
async hover(selector: string, options?: dom.PointerActionOptions & types.PointerActionWaitOptions) {
return this.mainFrame().hover(selector, options);
}

async select(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[], options?: types.ActionWaitOptions): Promise<string[]> {
async select(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[], options?: types.NavigatingActionWaitOptions): Promise<string[]> {
return this.mainFrame().select(selector, values, options);
}

async type(selector: string, text: string, options?: { delay?: number } & types.ActionWaitOptions) {
async type(selector: string, text: string, options?: { delay?: number } & types.NavigatingActionWaitOptions) {
return this.mainFrame().type(selector, text, options);
}

async press(selector: string, key: string, options?: { delay?: number, text?: string } & types.ActionWaitOptions) {
async press(selector: string, key: string, options?: { delay?: number, text?: string } & types.NavigatingActionWaitOptions) {
return this.mainFrame().press(selector, key, options);
}

async check(selector: string, options?: types.ActionWaitOptions) {
async check(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this.mainFrame().check(selector, options);
}

async uncheck(selector: string, options?: types.ActionWaitOptions) {
async uncheck(selector: string, options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
return this.mainFrame().uncheck(selector, options);
}

Expand Down
8 changes: 3 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,14 @@ export type NavigateOptions = TimeoutOptions & {
waitUntil?: LifecycleEvent,
};

export type ActionWaitOptionsNoWaitFor = TimeoutOptions & {
export type NavigatingActionWaitOptions = TimeoutOptions & {
waitUntil?: LifecycleEvent | 'nowait',
};

export type ActionWaitOptionsNoNavigation = TimeoutOptions & {
waitFor?: boolean,
export type PointerActionWaitOptions = TimeoutOptions & {
force?: boolean,
};

export type ActionWaitOptions = ActionWaitOptionsNoWaitFor & ActionWaitOptionsNoNavigation;

export type WaitForNavigationOptions = TimeoutOptions & {
waitUntil?: LifecycleEvent,
url?: URLMatch
Expand Down
Loading

0 comments on commit 3c35d7b

Please sign in to comment.