chore: do not use Array.from in injected script (#6876)

This method is most often overridden by some bad polyfill that
does not work correctly and breaks `$eval` and `$$eval` methods.

As a best-effort fix, use a `[...iterable]` throughout the code.
This commit is contained in:
Dmitry Gozman 2021-06-03 15:10:02 -07:00 committed by GitHub
parent f2cc439d8b
commit d4482f3ad3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 15 additions and 6 deletions

View file

@ -124,7 +124,7 @@ export class InjectedScript {
}
set = newSet;
}
let result = Array.from(set) as Element[];
let result = [...set] as Element[];
if (partsToCheckOne.length) {
const partial = { parts: partsToCheckOne };
result = result.filter(e => !!this._querySelectorRecursively(e, partial, 0));
@ -448,7 +448,7 @@ export class InjectedScript {
if (element.nodeName.toLowerCase() !== 'select')
return 'error:notselect';
const select = element as HTMLSelectElement;
const options = Array.from(select.options);
const options = [...select.options];
const selectedOptions = [];
let remainingOptionsToSelect = optionsToSelect.slice();
for (let index = 0; index < options.length; index++) {

View file

@ -68,9 +68,9 @@ export class SelectorEvaluatorImpl implements SelectorEvaluator {
this._engines.set('near', createPositionEngine('near', boxNear));
this._engines.set('nth-match', nthMatchEngine);
const allNames = Array.from(this._engines.keys());
const allNames = [...this._engines.keys()];
allNames.sort();
const parserNames = Array.from(customCSSNames).slice();
const parserNames = [...customCSSNames];
parserNames.sort();
if (allNames.join('|') !== parserNames.join('|'))
throw new Error(`Please keep customCSSNames in sync with evaluator engines: ${allNames.join('|')} vs ${parserNames.join('|')}`);

View file

@ -245,7 +245,7 @@ function cssFallback(injectedScript: InjectedScript, targetElement: Element): Se
const parent = element.parentNode as (Element | ShadowRoot);
// Combine class names until unique.
const classes = Array.from(element.classList);
const classes = [...element.classList];
for (let i = 0; i < classes.length; ++i) {
const token = '.' + classes.slice(0, i + 1).join('.');
const selector = uniqueCSSSelector(token);
@ -261,7 +261,7 @@ function cssFallback(injectedScript: InjectedScript, targetElement: Element): Se
// Ordinal is the weakest signal.
if (parent) {
const siblings = Array.from(parent.children);
const siblings = [...parent.children];
const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);
const token = sameTagSiblings.indexOf(element) === 0 ? nodeName : `${nodeName}:nth-child(${1 + siblings.indexOf(element)})`;
const selector = uniqueCSSSelector(token);

View file

@ -74,3 +74,12 @@ it('should return complex values', async ({page, server}) => {
const texts = await page.$$eval('css=div', divs => divs.map(div => div.textContent));
expect(texts).toEqual(['hello', 'beautiful', 'world!']);
});
it('should work with bogus Array.from', async ({page, server}) => {
await page.setContent('<div>hello</div><div>beautiful</div><div>world!</div>');
await page.evaluate(() => {
Array.from = () => [];
});
const divsCount = await page.$$eval('css=div', divs => divs.length);
expect(divsCount).toBe(3);
});