fix(selectOption): allow passing null to unselect all (#2405)

This commit is contained in:
Darío Kondratiuk 2020-06-10 13:37:29 -03:00 committed by GitHub
parent 24316ad261
commit e3f34f6ae2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 26 additions and 8 deletions

View file

@ -1632,7 +1632,7 @@ Page routes take precedence over browser context routes (set up with [browserCon
#### page.selectOption(selector, values[, options])
- `selector` <[string]> A selector to query page for. See [working with selectors](#working-with-selectors) for more details.
- `values` <[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `<select>` 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.
- `values` <null|[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `<select>` 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`.
- `index` <[number]> Matches by the index.
@ -2421,7 +2421,7 @@ Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported a
#### frame.selectOption(selector, values[, options])
- `selector` <[string]> A selector to query frame for. See [working with selectors](#working-with-selectors) for more details.
- `values` <[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `<select>` 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.
- `values` <null|[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `<select>` 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`.
- `index` <[number]> Matches by the index.
@ -2926,7 +2926,7 @@ Throws when ```elementHandle``` does not point to an element [connected](https:/
> **NOTE** If javascript is disabled, element is scrolled into view even when already completely visible.
#### elementHandle.selectOption(values[, options])
- `values` <[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `<select>` 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.
- `values` <null|[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>> Options to select. If the `<select>` 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`.
- `index` <[number]> Matches by the index.

View file

@ -344,18 +344,21 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._retryPointerAction(progress, point => this._page.mouse.dblclick(point.x, point.y, options), options);
}
async selectOption(values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[], options: types.NavigatingActionWaitOptions = {}): Promise<string[]> {
async selectOption(values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions = {}): Promise<string[]> {
return runAbortableTask(progress => this._selectOption(progress, values, options), this._page._logger, this._page._timeoutSettings.timeout(options));
}
async _selectOption(progress: Progress, values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[], options: types.NavigatingActionWaitOptions): Promise<string[]> {
async _selectOption(progress: Progress, values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions): Promise<string[]> {
let vals: string[] | ElementHandle[] | types.SelectOption[];
if (!Array.isArray(values))
if (values === null)
vals = [];
else if (!Array.isArray(values))
vals = [ values ] as (string[] | ElementHandle[] | types.SelectOption[]);
else
vals = values;
const selectOptions = (vals as any).map((value: any) => typeof value === 'object' ? value : { value });
for (const option of selectOptions) {
assert(option !== null, 'Value items must not be null');
if (option instanceof ElementHandle)
continue;
if (option.value !== undefined)

View file

@ -765,7 +765,7 @@ export class Frame {
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[], options: types.NavigatingActionWaitOptions = {}): Promise<string[]> {
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));
}

View file

@ -481,7 +481,7 @@ export class Page extends ExtendedEventEmitter {
return this.mainFrame().hover(selector, options);
}
async selectOption(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[], options?: types.NavigatingActionWaitOptions): Promise<string[]> {
async selectOption(selector: string, values: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | null, options?: types.NavigatingActionWaitOptions): Promise<string[]> {
return this.mainFrame().selectOption(selector, values, options);
}

View file

@ -953,6 +953,21 @@ describe('Page.selectOption', function() {
const result = await page.selectOption('select', []);
expect(result).toEqual([]);
});
it('should not allow null items',async({page, server}) => {
await page.goto(server.PREFIX + '/input/select.html');
await page.evaluate(() => makeMultiple());
let error = null
await page.selectOption('select', ['blue', null, 'black','magenta']).catch(e => error = e);
expect(error.message).toContain('Value items must not be null');
});
it('should unselect with null',async({page, server}) => {
await page.goto(server.PREFIX + '/input/select.html');
await page.evaluate(() => makeMultiple());
const result = await page.selectOption('select', ['blue', 'black','magenta']);
expect(result.reduce((accumulator,current) => ['blue', 'black', 'magenta'].includes(current) && accumulator, true)).toEqual(true);
await page.selectOption('select', null);
expect(await page.$eval('select', select => Array.from(select.options).every(option => !option.selected))).toEqual(true);
});
it('should deselect all options when passed no values for a multiple select',async({page, server}) => {
await page.goto(server.PREFIX + '/input/select.html');
await page.evaluate(() => makeMultiple());