fix: parse empty options in <select> (#8489)

This commit is contained in:
jrandolf 2022-06-09 13:27:34 +02:00 committed by GitHub
parent f64ec2051b
commit b30f3f44cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 30 deletions

View File

@ -263,8 +263,8 @@ export class JSHandle<HandleObjectType = unknown> {
*/ */
asElement(): ElementHandle | null { asElement(): ElementHandle | null {
/* This always returns null, but subclasses can override this and return an /* This always returns null, but subclasses can override this and return an
ElementHandle. ElementHandle.
*/ */
return null; return null;
} }
@ -766,7 +766,7 @@ export class ElementHandle<
* one is taken into account. * one is taken into account.
*/ */
async select(...values: string[]): Promise<string[]> { async select(...values: string[]): Promise<string[]> {
for (const value of values) for (const value of values) {
assert( assert(
helper.isString(value), helper.isString(value),
'Values must be strings. Found value "' + 'Values must be strings. Found value "' +
@ -775,26 +775,38 @@ export class ElementHandle<
typeof value + typeof value +
'"' '"'
); );
}
return this.evaluate<(element: Element, values: string[]) => string[]>( return this.evaluate((element: Element, vals: string[]): string[] => {
(element, values) => { const values = new Set(vals);
if (!(element instanceof HTMLSelectElement)) if (!(element instanceof HTMLSelectElement)) {
throw new Error('Element is not a <select> element.'); throw new Error('Element is not a <select> element.');
}
const options = Array.from(element.options); const selectedValues = new Set<string>();
element.value = ''; if (!element.multiple) {
for (const option of options) { for (const option of element.options) {
option.selected = values.includes(option.value); option.selected = false;
if (option.selected && !element.multiple) break;
} }
element.dispatchEvent(new Event('input', { bubbles: true })); for (const option of element.options) {
element.dispatchEvent(new Event('change', { bubbles: true })); if (values.has(option.value)) {
return options option.selected = true;
.filter((option) => option.selected) selectedValues.add(option.value);
.map((option) => option.value); break;
}, }
values }
); } else {
for (const option of element.options) {
option.selected = values.has(option.value);
if (option.selected) {
selectedValues.add(option.value);
}
}
}
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
return [...selectedValues.values()];
}, values);
} }
/** /**
@ -845,9 +857,9 @@ export class ElementHandle<
const { backendNodeId } = node; const { backendNodeId } = node;
/* The zero-length array is a special case, it seems that /* The zero-length array is a special case, it seems that
DOM.setFileInputFiles does not actually update the files in that case, DOM.setFileInputFiles does not actually update the files in that case,
so the solution is to eval the element value to a new FileList directly. so the solution is to eval the element value to a new FileList directly.
*/ */
if (files.length === 0) { if (files.length === 0) {
await (this as ElementHandle<HTMLInputElement>).evaluate((element) => { await (this as ElementHandle<HTMLInputElement>).evaluate((element) => {
element.files = new DataTransfer().files; element.files = new DataTransfer().files;
@ -1300,8 +1312,8 @@ export interface Point {
function computeQuadArea(quad: Point[]): number { function computeQuadArea(quad: Point[]): number {
/* Compute sum of all directed areas of adjacent triangles /* Compute sum of all directed areas of adjacent triangles
https://en.wikipedia.org/wiki/Polygon#Simple_polygons https://en.wikipedia.org/wiki/Polygon#Simple_polygons
*/ */
let area = 0; let area = 0;
for (let i = 0; i < quad.length; ++i) { for (let i = 0; i < quad.length; ++i) {
const p1 = quad[i]!; const p1 = quad[i]!;

View File

@ -5,6 +5,7 @@
</head> </head>
<body> <body>
<select> <select>
<option value="">Empty</option>
<option value="black">Black</option> <option value="black">Black</option>
<option value="blue">Blue</option> <option value="blue">Blue</option>
<option value="brown">Brown</option> <option value="brown">Brown</option>

View File

@ -1887,12 +1887,13 @@ describe('Page', function () {
await page.select('select', 'blue', 'black', 'magenta'); await page.select('select', 'blue', 'black', 'magenta');
await page.select('select'); await page.select('select');
expect( expect(
await page.$eval('select', (select: HTMLSelectElement) => await page.$eval(
Array.from(select.options).every( 'select',
(option: HTMLOptionElement) => !option.selected (select: HTMLSelectElement) =>
) Array.from(select.options).filter((option) => option.selected)[0]
.value
) )
).toEqual(true); ).toEqual('');
}); });
it('should throw if passed in non-strings', async () => { it('should throw if passed in non-strings', async () => {
const { page } = getTestState(); const { page } = getTestState();