fix: toBeFocused should match shadow elements (#16362)

Fixes #16268.
This commit is contained in:
Ross Wollman 2022-08-08 15:34:58 -07:00 committed by GitHub
parent 6552788a6e
commit 36b92d8847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 3 deletions

View File

@ -668,14 +668,19 @@ export class InjectedScript {
return 'done';
}
private _activelyFocused(node: Node): { activeElement: Element | null, isFocused: boolean } {
const activeElement = (node.getRootNode() as (Document | ShadowRoot)).activeElement;
const isFocused = activeElement === node && !!node.ownerDocument && node.ownerDocument.hasFocus();
return { activeElement, isFocused };
}
focusNode(node: Node, resetSelectionIfNotFocused?: boolean): 'error:notconnected' | 'done' {
if (!node.isConnected)
return 'error:notconnected';
if (node.nodeType !== Node.ELEMENT_NODE)
throw this.createStacklessError('Node is not an element');
const activeElement = (node.getRootNode() as (Document | ShadowRoot)).activeElement;
const wasFocused = activeElement === node && node.ownerDocument && node.ownerDocument.hasFocus();
const { activeElement, isFocused: wasFocused } = this._activelyFocused(node);
if ((node as HTMLElement).isContentEditable && !wasFocused && activeElement && (activeElement as HTMLElement | SVGElement).blur) {
// Workaround the Firefox bug where focusing the element does not switch current
// contenteditable to the new element. However, blurring the previous one helps.
@ -1029,7 +1034,7 @@ export class InjectedScript {
} else if (expression === 'to.be.enabled') {
elementState = progress.injectedScript.elementState(element, 'enabled');
} else if (expression === 'to.be.focused') {
elementState = document.activeElement === element;
elementState = this._activelyFocused(element).isFocused;
} else if (expression === 'to.be.hidden') {
elementState = progress.injectedScript.elementState(element, 'hidden');
} else if (expression === 'to.be.visible') {

View File

@ -352,6 +352,35 @@ test('should support toBeFocused', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0);
});
test('should support toBeFocused with shadow elements', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('focused', async ({ page }) => {
await page.setContent(\`
<div id="app">
</div>
<script>
const root = document.querySelector('div');
const shadowRoot = root.attachShadow({ mode: 'open' });
const input = document.createElement('input');
input.id = "my-input"
shadowRoot.appendChild(input);
</script>
\`);
await page.locator("input").focus();
expect(await page.evaluate(() => document.activeElement.shadowRoot.activeElement.id)).toBe("my-input");
await expect(page.locator("#app")).toBeFocused();
await expect(page.locator("input")).toBeFocused();
});
`,
}, { workers: 1 });
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});
test('should print unknown engine error', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `