Skip to content

Commit 42e625d

Browse files
committed
fix(highlight): highlight Top Layer elements
1 parent be1af15 commit 42e625d

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

packages/playwright-core/src/server/injected/consoleApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class Locator {
6161
self.nth = (index: number): Locator => self.locator(`nth=${index}`);
6262
self.and = (locator: Locator): Locator => new Locator(injectedScript, selectorBase + ` >> internal:and=` + JSON.stringify(locator[selectorSymbol]));
6363
self.or = (locator: Locator): Locator => new Locator(injectedScript, selectorBase + ` >> internal:or=` + JSON.stringify(locator[selectorSymbol]));
64+
self.highlight = () => injectedScript.highlight(injectedScript.parseSelector(selectorBase));
6465
}
6566
}
6667

packages/playwright-core/src/server/injected/highlight.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export type HighlightOptions = {
4040
};
4141

4242
export class Highlight {
43+
private _dialogElement: HTMLDialogElement;
44+
private _dialogStyles: HTMLStyleElement;
4345
private _glassPaneElement: HTMLElement;
4446
private _glassPaneShadow: ShadowRoot;
4547
private _highlightEntries: HighlightEntry[] = [];
@@ -54,6 +56,19 @@ export class Highlight {
5456
this._injectedScript = injectedScript;
5557
const document = injectedScript.document;
5658
this._isUnderTest = injectedScript.isUnderTest;
59+
this._dialogStyles = document.createElement('style');
60+
this._dialogStyles.textContent = `
61+
dialog#pw-dialog::backdrop {
62+
background-color: transparent;
63+
}
64+
`;
65+
this._dialogElement = document.createElement('dialog');
66+
this._dialogElement.id = 'pw-dialog';
67+
this._dialogElement.style.padding = '0';
68+
this._dialogElement.style.margin = '0';
69+
this._dialogElement.style.border = 'none';
70+
this._dialogElement.style.maxWidth = 'inherit';
71+
this._dialogElement.style.maxHeight = 'inherit';
5772
this._glassPaneElement = document.createElement('x-pw-glass');
5873
this._glassPaneElement.style.position = 'fixed';
5974
this._glassPaneElement.style.top = '0';
@@ -64,6 +79,7 @@ export class Highlight {
6479
this._glassPaneElement.style.pointerEvents = 'none';
6580
this._glassPaneElement.style.display = 'flex';
6681
this._glassPaneElement.style.backgroundColor = 'transparent';
82+
this._dialogElement.appendChild(this._glassPaneElement);
6783
for (const eventName of ['click', 'auxclick', 'dragstart', 'input', 'keydown', 'keyup', 'pointerdown', 'pointerup', 'mousedown', 'mouseup', 'mouseleave', 'focus', 'scroll']) {
6884
this._glassPaneElement.addEventListener(eventName, e => {
6985
e.stopPropagation();
@@ -82,7 +98,9 @@ export class Highlight {
8298
}
8399

84100
install() {
85-
this._injectedScript.document.documentElement.appendChild(this._glassPaneElement);
101+
this._injectedScript.document.documentElement.appendChild(this._dialogStyles);
102+
this._injectedScript.document.documentElement.appendChild(this._dialogElement);
103+
this._dialogElement.showModal();
86104
}
87105

88106
setLanguage(language: Language) {
@@ -99,7 +117,9 @@ export class Highlight {
99117
uninstall() {
100118
if (this._rafRequest)
101119
cancelAnimationFrame(this._rafRequest);
102-
this._glassPaneElement.remove();
120+
this._dialogElement.close();
121+
this._dialogElement.remove();
122+
this._dialogStyles.remove();
103123
}
104124

105125
showActionPoint(x: number, y: number) {
@@ -127,7 +147,7 @@ export class Highlight {
127147
}
128148

129149
maskElements(elements: Element[], color: string) {
130-
this._innerUpdateHighlight(elements, { color: color });
150+
this._innerUpdateHighlight(elements, { color });
131151
}
132152

133153
private _innerUpdateHighlight(elements: Element[], options: HighlightOptions) {

tests/page/page-screenshot.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,33 @@ it.describe('page screenshot', () => {
482482
})).toMatchSnapshot('should-mask-inside-iframe.png');
483483
});
484484

485+
it('should mask inside <dialog />', async ({ page, server }) => {
486+
await page.setViewportSize({ width: 500, height: 500 });
487+
await page.goto(server.PREFIX + '/grid.html');
488+
page.on('console', msg => console.log(msg.text()));
489+
await page.evaluate(() => {
490+
const elements = document.body.innerHTML;
491+
document.body.innerHTML = '';
492+
// Move all the elements of the body (grid elements) into a <dialog /> which lives on the Top-Layer.
493+
const dialog = document.createElement('dialog');
494+
dialog.style.padding = '0';
495+
dialog.style.margin = '0';
496+
dialog.style.border = 'none';
497+
dialog.style.maxWidth = 'inherit';
498+
dialog.style.maxHeight = 'inherit';
499+
dialog.style.outline = 'none';
500+
document.body.appendChild(dialog);
501+
dialog.innerHTML = elements;
502+
dialog.showModal();
503+
});
504+
expect(await page.screenshot({
505+
mask: [
506+
page.locator('div').nth(5),
507+
page.frameLocator('#frame1').locator('div').nth(12),
508+
],
509+
})).toMatchSnapshot('should-mask-inside-iframe.png');
510+
});
511+
485512
it('should mask in parallel', async ({ page, server }) => {
486513
await page.setViewportSize({ width: 500, height: 500 });
487514
await attachFrame(page, 'frame1', server.PREFIX + '/grid.html');

0 commit comments

Comments
 (0)