fix(selectOption): allow passing null to unselect all (#2405)
This commit is contained in:
parent
24316ad261
commit
e3f34f6ae2
|
|
@ -1632,7 +1632,7 @@ Page routes take precedence over browser context routes (set up with [browserCon
|
||||||
|
|
||||||
#### page.selectOption(selector, values[, options])
|
#### page.selectOption(selector, values[, options])
|
||||||
- `selector` <[string]> A selector to query page for. See [working with selectors](#working-with-selectors) for more details.
|
- `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`.
|
- `value` <[string]> Matches by `option.value`.
|
||||||
- `label` <[string]> Matches by `option.label`.
|
- `label` <[string]> Matches by `option.label`.
|
||||||
- `index` <[number]> Matches by the index.
|
- `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])
|
#### frame.selectOption(selector, values[, options])
|
||||||
- `selector` <[string]> A selector to query frame for. See [working with selectors](#working-with-selectors) for more details.
|
- `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`.
|
- `value` <[string]> Matches by `option.value`.
|
||||||
- `label` <[string]> Matches by `option.label`.
|
- `label` <[string]> Matches by `option.label`.
|
||||||
- `index` <[number]> Matches by the index.
|
- `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.
|
> **NOTE** If javascript is disabled, element is scrolled into view even when already completely visible.
|
||||||
|
|
||||||
#### elementHandle.selectOption(values[, options])
|
#### 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`.
|
- `value` <[string]> Matches by `option.value`.
|
||||||
- `label` <[string]> Matches by `option.label`.
|
- `label` <[string]> Matches by `option.label`.
|
||||||
- `index` <[number]> Matches by the index.
|
- `index` <[number]> Matches by the index.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
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));
|
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[];
|
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[]);
|
vals = [ values ] as (string[] | ElementHandle[] | types.SelectOption[]);
|
||||||
else
|
else
|
||||||
vals = values;
|
vals = values;
|
||||||
const selectOptions = (vals as any).map((value: any) => typeof value === 'object' ? value : { value });
|
const selectOptions = (vals as any).map((value: any) => typeof value === 'object' ? value : { value });
|
||||||
for (const option of selectOptions) {
|
for (const option of selectOptions) {
|
||||||
|
assert(option !== null, 'Value items must not be null');
|
||||||
if (option instanceof ElementHandle)
|
if (option instanceof ElementHandle)
|
||||||
continue;
|
continue;
|
||||||
if (option.value !== undefined)
|
if (option.value !== undefined)
|
||||||
|
|
|
||||||
|
|
@ -765,7 +765,7 @@ export class Frame {
|
||||||
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._hover(progress, options));
|
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));
|
return this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._selectOption(progress, values, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -481,7 +481,7 @@ export class Page extends ExtendedEventEmitter {
|
||||||
return this.mainFrame().hover(selector, options);
|
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);
|
return this.mainFrame().selectOption(selector, values, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -953,6 +953,21 @@ describe('Page.selectOption', function() {
|
||||||
const result = await page.selectOption('select', []);
|
const result = await page.selectOption('select', []);
|
||||||
expect(result).toEqual([]);
|
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}) => {
|
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.goto(server.PREFIX + '/input/select.html');
|
||||||
await page.evaluate(() => makeMultiple());
|
await page.evaluate(() => makeMultiple());
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue