Skip to content

Commit 2724157

Browse files
authored
feat(waitUntil): allow waiting for navigation from clicks, etc (#1255)
1 parent 9c80c9e commit 2724157

15 files changed

+316
-163
lines changed

docs/api.md

+197-46
Large diffs are not rendered by default.

src/browserContext.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export interface BrowserContext {
4242
setDefaultTimeout(timeout: number): void;
4343
pages(): Promise<Page[]>;
4444
newPage(): Promise<Page>;
45-
cookies(...urls: string[]): Promise<network.NetworkCookie[]>;
45+
cookies(urls?: string | string[]): Promise<network.NetworkCookie[]>;
4646
setCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
4747
clearCookies(): Promise<void>;
4848
setPermissions(origin: string, permissions: string[]): Promise<void>;

src/chromium/crBrowser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export class CRBrowserContext extends BrowserContextBase {
274274
throw result;
275275
}
276276

277-
async cookies(...urls: string[]): Promise<network.NetworkCookie[]> {
277+
async cookies(urls?: string | string[]): Promise<network.NetworkCookie[]> {
278278
const { cookies } = await this._browser._client.send('Storage.getCookies', { browserContextId: this._browserContextId || undefined });
279279
return network.filterCookies(cookies.map(c => {
280280
const copy: any = { sameSite: 'None', ...c };

src/dom.ts

+37-25
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
241241
return point;
242242
}
243243

244-
async _performPointerAction(action: (point: types.Point) => Promise<void>, options?: PointerActionOptions & types.WaitForOptions): Promise<void> {
244+
async _performPointerAction(action: (point: types.Point) => Promise<void>, options?: PointerActionOptions & types.WaitForOptions & types.NavigateOptions): Promise<void> {
245245
const { waitFor = true } = (options || {});
246246
if (!helper.isBoolean(waitFor))
247247
throw new Error('waitFor option should be a boolean, got "' + (typeof waitFor) + '"');
@@ -260,28 +260,33 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
260260
await action(point);
261261
if (restoreModifiers)
262262
await this._page.keyboard._ensureModifiers(restoreModifiers);
263-
});
263+
}, options);
264264
}
265265

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

270-
click(options?: ClickOptions & types.WaitForOptions): Promise<void> {
270+
click(options?: ClickOptions & types.WaitForOptions & types.NavigateOptions): Promise<void> {
271271
return this._performPointerAction(point => this._page.mouse.click(point.x, point.y, options), options);
272272
}
273273

274-
dblclick(options?: MultiClickOptions & types.WaitForOptions): Promise<void> {
274+
dblclick(options?: MultiClickOptions & types.WaitForOptions & types.NavigateOptions): Promise<void> {
275275
return this._performPointerAction(point => this._page.mouse.dblclick(point.x, point.y, options), options);
276276
}
277277

278-
tripleclick(options?: MultiClickOptions & types.WaitForOptions): Promise<void> {
278+
tripleclick(options?: MultiClickOptions & types.WaitForOptions & types.NavigateOptions): Promise<void> {
279279
return this._performPointerAction(point => this._page.mouse.tripleclick(point.x, point.y, options), options);
280280
}
281281

282-
async select(...values: (string | ElementHandle | types.SelectOption)[]): Promise<string[]> {
283-
const options = values.map(value => typeof value === 'object' ? value : { value });
284-
for (const option of options) {
282+
async select(values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[], options?: types.NavigateOptions): Promise<string[]> {
283+
let vals: string[] | ElementHandle[] | types.SelectOption[];
284+
if (!Array.isArray(values))
285+
vals = [ values ] as (string[] | ElementHandle[] | types.SelectOption[]);
286+
else
287+
vals = values;
288+
const selectOptions = (vals as any).map((value: any) => typeof value === 'object' ? value : { value });
289+
for (const option of selectOptions) {
285290
if (option instanceof ElementHandle)
286291
continue;
287292
if (option.value !== undefined)
@@ -292,11 +297,11 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
292297
assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"');
293298
}
294299
return await this._page._frameManager.waitForNavigationsCreatedBy<string[]>(async () => {
295-
return this._evaluateInUtility((injected, node, ...optionsToSelect) => injected.selectOptions(node, optionsToSelect), ...options);
296-
});
300+
return this._evaluateInUtility((injected, node, ...optionsToSelect) => injected.selectOptions(node, optionsToSelect), ...selectOptions);
301+
}, options);
297302
}
298303

299-
async fill(value: string): Promise<void> {
304+
async fill(value: string, options?: types.NavigateOptions): Promise<void> {
300305
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
301306
await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
302307
const error = await this._evaluateInUtility((injected, node, value) => injected.fill(node, value), value);
@@ -306,28 +311,35 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
306311
await this._page.keyboard.sendCharacters(value);
307312
else
308313
await this._page.keyboard.press('Delete');
309-
});
314+
}, options);
310315
}
311316

312-
async setInputFiles(...files: (string | types.FilePayload)[]) {
317+
async setInputFiles(files: string | types.FilePayload | string[] | types.FilePayload[]) {
313318
const multiple = await this._evaluateInUtility((injected: Injected, node: Node) => {
314319
if (node.nodeType !== Node.ELEMENT_NODE || (node as Element).tagName !== 'INPUT')
315320
throw new Error('Node is not an HTMLInputElement');
316321
const input = node as HTMLInputElement;
317322
return input.multiple;
318323
});
319-
assert(multiple || files.length <= 1, 'Non-multiple file input can only accept single file!');
320-
const filePayloads = await Promise.all(files.map(async item => {
324+
let ff: string[] | types.FilePayload[];
325+
if (!Array.isArray(files))
326+
ff = [ files ] as string[] | types.FilePayload[];
327+
else
328+
ff = files;
329+
assert(multiple || ff.length <= 1, 'Non-multiple file input can only accept single file!');
330+
const filePayloads: types.FilePayload[] = [];
331+
for (const item of ff) {
321332
if (typeof item === 'string') {
322333
const file: types.FilePayload = {
323334
name: platform.basename(item),
324335
type: platform.getMimeType(item),
325336
data: await platform.readFileAsync(item, 'base64')
326337
};
327-
return file;
338+
filePayloads.push(file);
339+
} else {
340+
filePayloads.push(item);
328341
}
329-
return item;
330-
}));
342+
}
331343
await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
332344
await this._page._delegate.setInputFiles(this as any as ElementHandle<HTMLInputElement>, filePayloads);
333345
});
@@ -344,29 +356,29 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
344356
throw new Error(errorMessage);
345357
}
346358

347-
async type(text: string, options?: { delay?: number }) {
359+
async type(text: string, options?: { delay?: number } & types.NavigateOptions) {
348360
await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
349361
await this.focus();
350362
await this._page.keyboard.type(text, options);
351-
});
363+
}, options);
352364
}
353365

354-
async press(key: string, options?: { delay?: number, text?: string }) {
366+
async press(key: string, options?: { delay?: number, text?: string } & types.NavigateOptions) {
355367
await this._page._frameManager.waitForNavigationsCreatedBy(async () => {
356368
await this.focus();
357369
await this._page.keyboard.press(key, options);
358-
});
370+
}, options);
359371
}
360372

361-
async check(options?: types.WaitForOptions) {
373+
async check(options?: types.WaitForOptions & types.NavigateOptions) {
362374
await this._setChecked(true, options);
363375
}
364376

365-
async uncheck(options?: types.WaitForOptions) {
377+
async uncheck(options?: types.WaitForOptions & types.NavigateOptions) {
366378
await this._setChecked(false, options);
367379
}
368380

369-
private async _setChecked(state: boolean, options?: types.WaitForOptions) {
381+
private async _setChecked(state: boolean, options?: types.WaitForOptions & types.NavigateOptions) {
370382
if (await this._evaluateInUtility((injected, node) => injected.isCheckboxChecked(node)) === state)
371383
return;
372384
await this.click(options);

src/firefox/ffBrowser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ export class FFBrowserContext extends BrowserContextBase {
329329
throw result;
330330
}
331331

332-
async cookies(...urls: string[]): Promise<network.NetworkCookie[]> {
332+
async cookies(urls?: string | string[]): Promise<network.NetworkCookie[]> {
333333
const { cookies } = await this._browser._connection.send('Browser.getCookies', { browserContextId: this._browserContextId || undefined });
334334
return network.filterCookies(cookies.map(c => {
335335
const copy: any = { ... c };

src/firefox/ffPage.ts

-10
Original file line numberDiff line numberDiff line change
@@ -455,16 +455,6 @@ export class FFPage implements PageDelegate {
455455
}
456456
}
457457

458-
export function normalizeWaitUntil(waitUntil: frames.LifecycleEvent | frames.LifecycleEvent[]): frames.LifecycleEvent[] {
459-
if (!Array.isArray(waitUntil))
460-
waitUntil = [waitUntil];
461-
for (const condition of waitUntil) {
462-
if (condition !== 'load' && condition !== 'domcontentloaded')
463-
throw new Error('Unknown waitUntil condition: ' + condition);
464-
}
465-
return waitUntil;
466-
}
467-
468458
function toRemoteObject(handle: dom.ElementHandle): Protocol.Runtime.RemoteObject {
469459
return handle._remoteObject;
470460
}

0 commit comments

Comments
 (0)