Skip to content

Commit 19e9d31

Browse files
committed
snapshot tests, cursor fix
1 parent 6848ab8 commit 19e9d31

File tree

4 files changed

+155
-9
lines changed

4 files changed

+155
-9
lines changed

packages/injected/src/ariaSnapshot.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,5 +698,5 @@ function textContributesInfo(node: AriaNode, text: string): boolean {
698698
}
699699

700700
function hasPointerCursor(ariaNode: AriaNode): boolean {
701-
return ariaNode.box.style?.cursor === 'pointer';
701+
return ariaNode.box.cursor === 'pointer';
702702
}

packages/injected/src/domUtils.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,29 @@ export type Box = {
112112
visible: boolean;
113113
inline: boolean;
114114
rect?: DOMRect;
115-
style?: CSSStyleDeclaration;
115+
cursor?: CSSStyleDeclaration['cursor'];
116116
};
117117

118118
export function computeBox(element: Element): Box {
119119
// Note: this logic should be similar to waitForDisplayedAtStablePosition() to avoid surprises.
120120
const style = getElementComputedStyle(element);
121121
if (!style)
122122
return { visible: true, inline: false };
123+
const cursor = style.cursor;
123124
if (style.display === 'contents') {
124125
// display:contents is not rendered itself, but its child nodes are.
125126
for (let child = element.firstChild; child; child = child.nextSibling) {
126127
if (child.nodeType === 1 /* Node.ELEMENT_NODE */ && isElementVisible(child as Element))
127-
return { visible: true, inline: false, style };
128+
return { visible: true, inline: false, cursor };
128129
if (child.nodeType === 3 /* Node.TEXT_NODE */ && isVisibleTextNode(child as Text))
129-
return { visible: true, inline: true, style };
130+
return { visible: true, inline: true, cursor };
130131
}
131-
return { visible: false, inline: false, style };
132+
return { visible: false, inline: false, cursor };
132133
}
133134
if (!isElementStyleVisibilityVisible(element, style))
134-
return { style, visible: false, inline: false };
135+
return { cursor, visible: false, inline: false };
135136
const rect = element.getBoundingClientRect();
136-
return { rect, style, visible: rect.width > 0 && rect.height > 0, inline: style.display === 'inline' };
137+
return { rect, cursor, visible: rect.width > 0 && rect.height > 0, inline: style.display === 'inline' };
137138
}
138139

139140
export function isElementVisible(element: Element): boolean {

packages/playwright/src/mcp/browser/tab.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ export class Tab extends EventEmitter<TabEventsInterface> {
6262
this.context = context;
6363
this.page = page as Page;
6464
this._onPageClose = onPageClose;
65-
page.on('console', console.error);
6665
page.on('console', event => this._handleConsoleMessage(messageToConsoleMessage(event)));
6766
page.on('pageerror', error => this._handleConsoleMessage(pageErrorToConsoleMessage(error)));
6867
page.on('request', request => this._requests.add(request));

tests/page/page-aria-snapshot-ai.spec.ts

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { asLocator } from 'playwright-core/lib/utils';
1919

2020
import { test as it, expect, unshift } from './pageTest';
2121

22-
function snapshotForAI(page: any, options?: { timeout?: number }): Promise<string> {
22+
function snapshotForAI(page: any, options?: { timeout?: number, mode?: 'full' | 'incremental', track?: string }): Promise<string> {
2323
return page._snapshotForAI(options);
2424
}
2525

@@ -448,3 +448,149 @@ it('should not remove generic nodes with title', async ({ page }) => {
448448
- generic "Element title" [ref=e2]
449449
`);
450450
});
451+
452+
it('should create incremental snapshots on multiple tracks', async ({ page }) => {
453+
await page.setContent(`<ul><li><button>a button</button></li><li><span>a span</span></li><li id=hidden-li style="display:none">some text</li></ul>`);
454+
455+
expect(await snapshotForAI(page, { track: 'first', mode: 'incremental' })).toContainYaml(`
456+
- list [ref=e2]:
457+
- listitem [ref=e3]:
458+
- button "a button" [ref=e4]
459+
- listitem [ref=e5]: a span
460+
`);
461+
expect(await snapshotForAI(page, { track: 'second', mode: 'incremental' })).toContainYaml(`
462+
- list [ref=e2]:
463+
- listitem [ref=e3]:
464+
- button "a button" [ref=e4]
465+
- listitem [ref=e5]: a span
466+
`);
467+
expect(await snapshotForAI(page, { track: 'first', mode: 'incremental' })).toContainYaml(`
468+
- ref=e2 [unchanged]
469+
`);
470+
471+
await page.evaluate(() => {
472+
document.querySelector('span').textContent = 'changed span';
473+
document.getElementById('hidden-li').style.display = 'inline';
474+
});
475+
expect(await snapshotForAI(page, { track: 'first', mode: 'incremental' })).toContainYaml(`
476+
- list [ref=e2]:
477+
- ref=e3 [unchanged]
478+
- listitem [ref=e5]: changed span
479+
- listitem [ref=e6]: some text
480+
`);
481+
482+
await page.evaluate(() => {
483+
document.querySelector('span').textContent = 'a span';
484+
document.getElementById('hidden-li').style.display = 'none';
485+
});
486+
expect(await snapshotForAI(page, { track: 'first', mode: 'incremental' })).toContainYaml(`
487+
- list [ref=e2]:
488+
- ref=e3 [unchanged]
489+
- listitem [ref=e5]: a span
490+
`);
491+
expect(await snapshotForAI(page, { track: 'second', mode: 'incremental' })).toContainYaml(`
492+
- ref=e2 [unchanged]
493+
`);
494+
495+
expect(await snapshotForAI(page, { track: 'second', mode: 'full' })).toContainYaml(`
496+
- list [ref=e2]:
497+
- listitem [ref=e3]:
498+
- button "a button" [ref=e4]
499+
- listitem [ref=e5]: a span
500+
`);
501+
});
502+
503+
it('should create incremental snapshot for attribute change', async ({ page }) => {
504+
await page.setContent(`<button>a button</button>`);
505+
await page.evaluate(() => document.querySelector('button').focus());
506+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
507+
- button "a button" [active] [ref=e2]
508+
`);
509+
510+
await page.evaluate(() => document.querySelector('button').blur());
511+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
512+
- button "a button" [ref=e2]
513+
`);
514+
});
515+
516+
it('should create incremental snapshot for child removal', async ({ page }) => {
517+
await page.setContent(`<li><button>a button</button><span>some text</span></li>`);
518+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
519+
- listitem [ref=e2]:
520+
- button "a button" [ref=e3]
521+
- text: some text
522+
`);
523+
524+
await page.evaluate(() => document.querySelector('span').remove());
525+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
526+
- listitem [ref=e2]:
527+
- ref=e3 [unchanged]
528+
`);
529+
});
530+
531+
it('should create incremental snapshot for child addition', async ({ page }) => {
532+
await page.setContent(`<li><button>a button</button><span style="display:none">some text</span></li>`);
533+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
534+
- listitem [ref=e2]:
535+
- button "a button" [ref=e3]
536+
`);
537+
538+
await page.evaluate(() => document.querySelector('span').style.display = 'inline');
539+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
540+
- listitem [ref=e2]:
541+
- ref=e3 [unchanged]
542+
- text: some text
543+
`);
544+
});
545+
546+
it('should create incremental snapshot for prop change', async ({ page }) => {
547+
await page.setContent(`<a href="about:blank">a link</a>`);
548+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
549+
- link "a link" [ref=e2] [cursor=pointer]:
550+
- /url: about:blank
551+
`);
552+
553+
await page.evaluate(() => document.querySelector('a').setAttribute('href', 'https://playwright.dev'));
554+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
555+
- link "a link" [ref=e2] [cursor=pointer]:
556+
- /url: https://playwright.dev
557+
`);
558+
});
559+
560+
it('should create incremental snapshot for cursor change', async ({ page }) => {
561+
await page.setContent(`<a href="about:blank">a link</a>`);
562+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
563+
- link "a link" [ref=e2] [cursor=pointer]:
564+
- /url: about:blank
565+
`);
566+
567+
await page.evaluate(() => document.querySelector('a').style.cursor = 'default');
568+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
569+
- link "a link" [ref=e2]:
570+
- /url: about:blank
571+
`);
572+
});
573+
574+
it('should create incremental snapshot for name change', async ({ page }) => {
575+
await page.setContent(`<button><span>a button</span></button>`);
576+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
577+
- button "a button" [ref=e2]
578+
`);
579+
580+
await page.evaluate(() => document.querySelector('span').textContent = 'new button');
581+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
582+
- button "new button" [ref=e3]
583+
`);
584+
});
585+
586+
it('should create incremental snapshot for text change', async ({ page }) => {
587+
await page.setContent(`<li><span>an item</span></li>`);
588+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
589+
- listitem [ref=e2]: an item
590+
`);
591+
592+
await page.evaluate(() => document.querySelector('span').textContent = 'new text');
593+
expect(await snapshotForAI(page, { mode: 'incremental' })).toContainYaml(`
594+
- listitem [ref=e2]: new text
595+
`);
596+
});

0 commit comments

Comments
 (0)