/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import assert from 'assert';
import expect from 'expect';
import {TimeoutError} from 'puppeteer';
import type {ElementHandle} from 'puppeteer-core/internal/api/ElementHandle.js';
import {
getTestState,
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import utils from './utils.js';
describe('AriaQueryHandler', () => {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('parseAriaSelector', () => {
beforeEach(async () => {
const {page} = getTestState();
await page.setContent(
''
);
});
it('should find button', async () => {
const {page} = getTestState();
const expectFound = async (button: ElementHandle | null) => {
assert(button);
const id = await button.evaluate((button: Element) => {
return button.id;
});
expect(id).toBe('btn');
};
let button = await page.$(
'aria/Submit button and some spaces[role="button"]'
);
await expectFound(button);
button = await page.$(
"aria/Submit button and some spaces[role='button']"
);
await expectFound(button);
button = await page.$(
'aria/ Submit button and some spaces[role="button"]'
);
await expectFound(button);
button = await page.$(
'aria/Submit button and some spaces [role="button"]'
);
await expectFound(button);
button = await page.$(
'aria/Submit button and some spaces [ role = "button" ] '
);
await expectFound(button);
button = await page.$(
'aria/[role="button"]Submit button and some spaces'
);
await expectFound(button);
button = await page.$(
'aria/Submit button [role="button"]and some spaces'
);
await expectFound(button);
button = await page.$(
'aria/[name=" Submit button and some spaces"][role="button"]'
);
await expectFound(button);
button = await page.$(
"aria/[name=' Submit button and some spaces'][role='button']"
);
await expectFound(button);
button = await page.$(
'aria/ignored[name="Submit button and some spaces"][role="button"]'
);
await expectFound(button);
await expect(page.$('aria/smth[smth="true"]')).rejects.toThrow(
'Unknown aria attribute "smth" in selector'
);
});
});
describe('queryOne', () => {
it('should find button by role', async () => {
const {page} = getTestState();
await page.setContent(
'
'
);
const button = (await page.$(
'aria/[role="button"]'
)) as ElementHandle;
const id = await button!.evaluate(button => {
return button.id;
});
expect(id).toBe('btn');
});
it('should find button by name and role', async () => {
const {page} = getTestState();
await page.setContent(
''
);
const button = (await page.$(
'aria/Submit[role="button"]'
)) as ElementHandle;
const id = await button!.evaluate(button => {
return button.id;
});
expect(id).toBe('btn');
});
it('should find first matching element', async () => {
const {page} = getTestState();
await page.setContent(
`
`
);
const div = (await page.$(
'aria/menu div'
)) as ElementHandle;
const id = await div!.evaluate(div => {
return div.id;
});
expect(id).toBe('mnu1');
});
it('should find by name', async () => {
const {page} = getTestState();
await page.setContent(
`
menu div
menu div
`
);
const menu = (await page.$(
'aria/menu-label1'
)) as ElementHandle;
const id = await menu!.evaluate(div => {
return div.id;
});
expect(id).toBe('mnu1');
});
it('should find by name', async () => {
const {page} = getTestState();
await page.setContent(
`
menu div
menu div
`
);
const menu = (await page.$(
'aria/menu-label2'
)) as ElementHandle;
const id = await menu!.evaluate(div => {
return div.id;
});
expect(id).toBe('mnu2');
});
});
describe('queryAll', () => {
it('should find menu by name', async () => {
const {page} = getTestState();
await page.setContent(
`
`
);
const divs = (await page.$$('aria/menu div')) as Array<
ElementHandle
>;
const ids = await Promise.all(
divs.map(n => {
return n.evaluate(div => {
return div.id;
});
})
);
expect(ids.join(', ')).toBe('mnu1, mnu2');
});
});
describe('queryAllArray', () => {
it('$$eval should handle many elements', async function () {
this.timeout(25_000);
const {page} = getTestState();
await page.setContent('');
await page.evaluate(
`
for (var i = 0; i <= 10000; i++) {
const button = document.createElement('button');
button.textContent = i;
document.body.appendChild(button);
}
`
);
const sum = await page.$$eval('aria/[role="button"]', buttons => {
return buttons.reduce((acc, button) => {
return acc + Number(button.textContent);
}, 0);
});
expect(sum).toBe(50005000);
});
});
describe('waitForSelector (aria)', function () {
const addElement = (tag: string) => {
return document.body.appendChild(document.createElement(tag));
};
it('should immediately resolve promise if node exists', async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
});
it('should work for ElementHandle.waitForSelector', async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
return (document.body.innerHTML = ``);
});
const element = (await page.$('div'))!;
await element!.waitForSelector('aria/test');
});
it('should persist query handler bindings across reloads', async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
await page.reload();
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
});
it('should persist query handler bindings across navigations', async () => {
const {page, server} = getTestState();
// Reset page but make sure that execution context ids start with 1.
await page.goto('data:text/html,');
await page.goto(server.EMPTY_PAGE);
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
// Reset page but again make sure that execution context ids start with 1.
await page.goto('data:text/html,');
await page.goto(server.EMPTY_PAGE);
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
});
it('should work independently of `exposeFunction`', async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.exposeFunction('ariaQuerySelector', (a: number, b: number) => {
return a + b;
});
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
const result = await page.evaluate('globalThis.ariaQuerySelector(2,8)');
expect(result).toBe(10);
});
it('should work with removed MutationObserver', async () => {
const {page} = getTestState();
await page.evaluate(() => {
// @ts-expect-error This is the point of the test.
return delete window.MutationObserver;
});
const [handle] = await Promise.all([
page.waitForSelector('aria/anything'),
page.setContent(`