From e04910501b3bcd90f7acf14089dbba7e2ca42635 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Fri, 22 Nov 2019 11:54:49 -0800 Subject: [PATCH] feat(select): use ElementHandle instead of 'id=foo' when choosing options (#54) --- docs/api.md | 9 +++------ src/chromium/DOMWorld.ts | 2 +- src/chromium/Frame.ts | 6 ++++-- src/chromium/JSHandle.ts | 15 +++++++-------- src/chromium/Page.ts | 2 +- test/page.spec.js | 12 ++---------- 6 files changed, 18 insertions(+), 28 deletions(-) diff --git a/docs/api.md b/docs/api.md index 81d49201a9..c0349ae105 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1593,10 +1593,9 @@ Page is guaranteed to have a main frame which persists during navigations. #### page.select(selector, ...values) - `selector` <[string]> A [selector] to query page for. -- `...values` <...[string]|[Object]> Options to select. If the `` has the `multiple` attribute, all matching options are selected, otherwise only the first option matching one of the passed options is selected. String values are equivalent to `{value:'string'}`. Option is considered matching if all specified properties match. - `value` <[string]> Matches by `option.value`. - `label` <[string]> Matches by `option.label`. - - `id` <[string]> Matches by `option.id`. - `index` <[number]> Matches by the index. - returns: <[Promise]<[Array]<[string]>>> An array of option values that have been successfully selected. @@ -2813,10 +2812,9 @@ If the name is empty, returns the id attribute instead. #### frame.select(selector, ...values) - `selector` <[string]> A [selector] to query frame for. -- `...values` <...[string]|[Object]> Options to select. If the `` has the `multiple` attribute, all matching options are selected, otherwise only the first option matching one of the passed options is selected. String values are equivalent to `{value:'string'}`. Option is considered matching if all specified properties match. - `value` <[string]> Matches by `option.value`. - `label` <[string]> Matches by `option.label`. - - `id` <[string]> Matches by `option.id`. - `index` <[number]> Matches by the index. - returns: <[Promise]<[Array]<[string]>>> An array of option values that have been successfully selected. @@ -3539,10 +3537,9 @@ This method scrolls element into view if needed, and then uses [page.screenshot] If the element is detached from DOM, the method throws an error. #### elementHandle.select(...values) -- `...values` <...[string]|[Object]> Options to select. If the `` has the `multiple` attribute, all matching options are selected, otherwise only the first option matching one of the passed options is selected. String values are equivalent to `{value:'string'}`. Option is considered matching if all specified properties match. - `value` <[string]> Matches by `option.value`. - `label` <[string]> Matches by `option.label`. - - `id` <[string]> Matches by `option.id`. - `index` <[number]> Matches by the index. - returns: <[Promise]<[Array]<[string]>>> An array of option values that have been successfully selected. diff --git a/src/chromium/DOMWorld.ts b/src/chromium/DOMWorld.ts index 6ac039c4a0..da5e038239 100644 --- a/src/chromium/DOMWorld.ts +++ b/src/chromium/DOMWorld.ts @@ -322,7 +322,7 @@ export class DOMWorld { await handle.dispose(); } - async select(selector: string, ...values: (string | SelectOption)[]): Promise { + async select(selector: string, ...values: (string | ElementHandle | SelectOption)[]): Promise { const handle = await this.$(selector); assert(handle, 'No node found for selector: ' + selector); const result = await handle.select(...values); diff --git a/src/chromium/Frame.ts b/src/chromium/Frame.ts index 07511f656d..a10639cc2a 100644 --- a/src/chromium/Frame.ts +++ b/src/chromium/Frame.ts @@ -164,8 +164,10 @@ export class Frame { return this._secondaryWorld.hover(selector, options); } - select(selector: string, ...values: (string | SelectOption)[]): Promise{ - return this._secondaryWorld.select(selector, ...values); + async select(selector: string, ...values: (string | ElementHandle | SelectOption)[]): Promise{ + const secondaryExecutionContext = await this._secondaryWorld.executionContext(); + const adoptedValues = values.map(async value => value instanceof ElementHandle ? secondaryExecutionContext._adoptElementHandle(value) : value); + return this._secondaryWorld.select(selector, ...(await Promise.all(adoptedValues))); } async type(selector: string, text: string, options: { delay: (number | undefined); } | undefined) { diff --git a/src/chromium/JSHandle.ts b/src/chromium/JSHandle.ts index 630ab4f5ac..bd2045d3b8 100644 --- a/src/chromium/JSHandle.ts +++ b/src/chromium/JSHandle.ts @@ -49,7 +49,6 @@ export type MultiClickOptions = PointerActionOptions & { export type SelectOption = { value?: string; - id?: string; label?: string; index?: number; }; @@ -324,19 +323,19 @@ export class ElementHandle extends JSHandle { return this._performPointerAction(point => this._page.mouse.tripleclick(point.x, point.y, options), options); } - async select(...values: (string | SelectOption)[]): Promise { + async select(...values: (string | ElementHandle | SelectOption)[]): Promise { const options = values.map(value => typeof value === 'object' ? value : { value }); for (const option of options) { + if (option instanceof ElementHandle) + continue; if (option.value !== undefined) assert(helper.isString(option.value), 'Values must be strings. Found value "' + option.value + '" of type "' + (typeof option.value) + '"'); if (option.label !== undefined) assert(helper.isString(option.label), 'Labels must be strings. Found label "' + option.label + '" of type "' + (typeof option.label) + '"'); - if (option.id !== undefined) - assert(helper.isString(option.id), 'Ids must be strings. Found id "' + option.id + '" of type "' + (typeof option.id) + '"'); if (option.index !== undefined) assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"'); } - return this.evaluate((element: HTMLSelectElement, optionsToSelect: SelectOption[]) => { + return this.evaluate((element: HTMLSelectElement, ...optionsToSelect: (Node | SelectOption)[]) => { if (element.nodeName.toLowerCase() !== 'select') throw new Error('Element is not a