Skip to content

Commit

Permalink
fix: parse empty options in <select> (#8489)
Browse files Browse the repository at this point in the history
  • Loading branch information
jrandolf committed Jun 9, 2022
1 parent f64ec20 commit b30f3f4
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 30 deletions.
62 changes: 37 additions & 25 deletions src/common/JSHandle.ts
Expand Up @@ -263,8 +263,8 @@ export class JSHandle<HandleObjectType = unknown> {
*/
asElement(): ElementHandle | null {
/* This always returns null, but subclasses can override this and return an
ElementHandle.
*/
ElementHandle.
*/
return null;
}

Expand Down Expand Up @@ -766,7 +766,7 @@ export class ElementHandle<
* one is taken into account.
*/
async select(...values: string[]): Promise<string[]> {
for (const value of values)
for (const value of values) {
assert(
helper.isString(value),
'Values must be strings. Found value "' +
Expand All @@ -775,26 +775,38 @@ export class ElementHandle<
typeof value +
'"'
);
}

return this.evaluate<(element: Element, values: string[]) => string[]>(
(element, values) => {
if (!(element instanceof HTMLSelectElement))
throw new Error('Element is not a <select> element.');
return this.evaluate((element: Element, vals: string[]): string[] => {
const values = new Set(vals);
if (!(element instanceof HTMLSelectElement)) {
throw new Error('Element is not a <select> element.');
}

const options = Array.from(element.options);
element.value = '';
for (const option of options) {
option.selected = values.includes(option.value);
if (option.selected && !element.multiple) break;
const selectedValues = new Set<string>();
if (!element.multiple) {
for (const option of element.options) {
option.selected = false;
}
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
return options
.filter((option) => option.selected)
.map((option) => option.value);
},
values
);
for (const option of element.options) {
if (values.has(option.value)) {
option.selected = true;
selectedValues.add(option.value);
break;
}
}
} 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);
}

/**
Expand Down Expand Up @@ -845,9 +857,9 @@ export class ElementHandle<
const { backendNodeId } = node;

/* The zero-length array is a special case, it seems that
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.
*/
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.
*/
if (files.length === 0) {
await (this as ElementHandle<HTMLInputElement>).evaluate((element) => {
element.files = new DataTransfer().files;
Expand Down Expand Up @@ -1300,8 +1312,8 @@ export interface Point {

function computeQuadArea(quad: Point[]): number {
/* 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;
for (let i = 0; i < quad.length; ++i) {
const p1 = quad[i]!;
Expand Down
1 change: 1 addition & 0 deletions test/assets/input/select.html
Expand Up @@ -5,6 +5,7 @@
</head>
<body>
<select>
<option value="">Empty</option>
<option value="black">Black</option>
<option value="blue">Blue</option>
<option value="brown">Brown</option>
Expand Down
11 changes: 6 additions & 5 deletions test/page.spec.ts
Expand Up @@ -1887,12 +1887,13 @@ describe('Page', function () {
await page.select('select', 'blue', 'black', 'magenta');
await page.select('select');
expect(
await page.$eval('select', (select: HTMLSelectElement) =>
Array.from(select.options).every(
(option: HTMLOptionElement) => !option.selected
)
await page.$eval(
'select',
(select: HTMLSelectElement) =>
Array.from(select.options).filter((option) => option.selected)[0]
.value
)
).toEqual(true);
).toEqual('');
});
it('should throw if passed in non-strings', async () => {
const { page } = getTestState();
Expand Down

0 comments on commit b30f3f4

Please sign in to comment.