Skip to content

Commit 4593426

Browse files
yury-sclaude
andcommitted
test: add AriaSnapshotMode.AI tests; fix serialization and page-level snapshot
- Register AriaSnapshotMode in Gson with ToLowerCaseSerializer so AI mode is serialized as "ai" (protocol expects lowercase) - Fix PageImpl.ariaSnapshot() to call the frame-level ariaSnapshot protocol message directly (no selector), matching the JS client behavior; using locator(":root") produced "document [ref=e1]" as root in AI mode instead of the expected body-level snapshot - Add TestPageAriaSnapshotAI with 12 tests covering: ref generation, aria-ref locator usage, iframe listing, locator-inside-iframe, generic node collapsing, cursor pointer hints, active element tracking, inline generic collapsing, title preservation, depth limit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ffb4d44 commit 4593426

File tree

3 files changed

+198
-1
lines changed

3 files changed

+198
-1
lines changed

playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,12 @@ private void onLocatorHandlerTriggered(int uid) {
638638

639639
@Override
640640
public String ariaSnapshot(AriaSnapshotOptions options) {
641-
return mainFrame.locator(":root").ariaSnapshot(convertType(options, Locator.AriaSnapshotOptions.class));
641+
if (options == null) {
642+
options = new AriaSnapshotOptions();
643+
}
644+
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
645+
JsonObject result = mainFrame.sendMessage("ariaSnapshot", params, mainFrame.timeout(options.timeout)).getAsJsonObject();
646+
return result.get("snapshot").getAsString();
642647
}
643648

644649
@Override

playwright/src/main/java/com/microsoft/playwright/impl/Serialization.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class Serialization {
5959
.registerTypeAdapter(ServiceWorkerPolicy.class, new ToLowerCaseAndDashSerializer<ServiceWorkerPolicy>())
6060
.registerTypeAdapter(MouseButton.class, new ToLowerCaseSerializer<MouseButton>())
6161
.registerTypeAdapter(ConsoleMessagesFilter.class, new ConsoleMessagesFilterSerializer())
62+
.registerTypeAdapter(AriaSnapshotMode.class, new ToLowerCaseSerializer<AriaSnapshotMode>())
6263
.registerTypeAdapter(LoadState.class, new ToLowerCaseSerializer<LoadState>())
6364
.registerTypeAdapter(WaitUntilState.class, new ToLowerCaseSerializer<WaitUntilState>())
6465
.registerTypeAdapter(WaitForSelectorState.class, new ToLowerCaseSerializer<WaitForSelectorState>())
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package com.microsoft.playwright;
2+
3+
import com.microsoft.playwright.junit.FixtureTest;
4+
import com.microsoft.playwright.junit.UsePlaywright;
5+
import com.microsoft.playwright.options.AriaSnapshotMode;
6+
import org.junit.jupiter.api.Test;
7+
8+
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
9+
import static org.junit.jupiter.api.Assertions.*;
10+
11+
@FixtureTest
12+
@UsePlaywright
13+
public class TestPageAriaSnapshotAI {
14+
private static String aiSnapshot(Page page) {
15+
return page.ariaSnapshot(new Page.AriaSnapshotOptions().setMode(AriaSnapshotMode.AI));
16+
}
17+
18+
@Test
19+
void shouldGenerateRefs(Page page) {
20+
page.setContent("<button>One</button><button>Two</button><button>Three</button>");
21+
22+
String snapshot1 = aiSnapshot(page);
23+
assertTrue(snapshot1.contains("button \"One\" [ref=e2]"), snapshot1);
24+
assertTrue(snapshot1.contains("button \"Two\" [ref=e3]"), snapshot1);
25+
assertTrue(snapshot1.contains("button \"Three\" [ref=e4]"), snapshot1);
26+
assertThat(page.locator("aria-ref=e2")).hasText("One");
27+
assertThat(page.locator("aria-ref=e3")).hasText("Two");
28+
assertThat(page.locator("aria-ref=e4")).hasText("Three");
29+
30+
page.locator("aria-ref=e3").evaluate("e => e.textContent = 'Not Two'");
31+
32+
String snapshot2 = aiSnapshot(page);
33+
assertTrue(snapshot2.contains("button \"One\" [ref=e2]"), snapshot2);
34+
assertTrue(snapshot2.contains("button \"Not Two\" [ref=e5]"), snapshot2);
35+
assertTrue(snapshot2.contains("button \"Three\" [ref=e4]"), snapshot2);
36+
}
37+
38+
@Test
39+
void shouldListIframes(Page page) {
40+
page.setContent(
41+
"<h1>Hello</h1>" +
42+
"<iframe name=\"foo\" src=\"data:text/html,<h1>World</h1>\">");
43+
44+
String snapshot = aiSnapshot(page);
45+
assertTrue(snapshot.contains("- iframe"), snapshot);
46+
47+
String frameSnapshot = page.frameLocator("iframe").locator("body").ariaSnapshot();
48+
assertEquals("- heading \"World\" [level=1]", frameSnapshot);
49+
}
50+
51+
@Test
52+
void shouldSnapshotLocatorInsideIframe(Page page) {
53+
page.setContent(
54+
"<h1>Main Page</h1>" +
55+
"<iframe srcdoc=\"<ul><li>Item 1</li><li>Item 2</li></ul>\"></iframe>");
56+
57+
Locator list = page.frames().get(1).locator("ul");
58+
String snapshot = list.ariaSnapshot(new Locator.AriaSnapshotOptions().setMode(AriaSnapshotMode.AI));
59+
assertTrue(snapshot.contains("list [ref=f1e1]"), snapshot);
60+
assertTrue(snapshot.contains("listitem [ref=f1e2]: Item 1"), snapshot);
61+
assertTrue(snapshot.contains("listitem [ref=f1e3]: Item 2"), snapshot);
62+
}
63+
64+
@Test
65+
void shouldCollapseGenericNodes(Page page) {
66+
page.setContent("<div><div><div><button>Button</button></div></div></div>");
67+
String snapshot = aiSnapshot(page);
68+
assertTrue(snapshot.contains("button \"Button\" [ref=e5]"), snapshot);
69+
}
70+
71+
@Test
72+
void shouldIncludeCursorPointerHint(Page page) {
73+
page.setContent("<button style=\"cursor: pointer\">Button</button>");
74+
String snapshot = aiSnapshot(page);
75+
assertTrue(snapshot.contains("button \"Button\" [ref=e2] [cursor=pointer]"), snapshot);
76+
}
77+
78+
@Test
79+
void shouldNotNestCursorPointerHints(Page page) {
80+
page.setContent(
81+
"<a style=\"cursor: pointer\" href=\"about:blank\">" +
82+
"Link with a button <button style=\"cursor: pointer\">Button</button>" +
83+
"</a>");
84+
String snapshot = aiSnapshot(page);
85+
assertTrue(snapshot.contains("link \"Link with a button Button\" [ref=e2] [cursor=pointer]"), snapshot);
86+
// The button inside a cursor-pointer link should not get a redundant [cursor=pointer]
87+
assertTrue(snapshot.contains("button \"Button\" [ref=e3]"), snapshot);
88+
assertFalse(snapshot.contains("button \"Button\" [ref=e3] [cursor=pointer]"), snapshot);
89+
}
90+
91+
@Test
92+
void shouldShowVisibleChildrenOfHiddenElements(Page page) {
93+
page.setContent(
94+
"<div style=\"visibility: hidden\">" +
95+
" <div style=\"visibility: visible\"><button>Visible</button></div>" +
96+
" <div style=\"visibility: hidden\"><button style=\"visibility: visible\">Visible</button></div>" +
97+
" <div>" +
98+
" <div style=\"visibility: visible\"><button style=\"visibility: hidden\">Hidden</button></div>" +
99+
" <button>Hidden</button>" +
100+
" </div>" +
101+
"</div>");
102+
String snapshot = aiSnapshot(page);
103+
assertEquals(
104+
"- generic [active] [ref=e1]:\n" +
105+
" - button \"Visible\" [ref=e3]\n" +
106+
" - button \"Visible\" [ref=e4]",
107+
snapshot);
108+
}
109+
110+
@Test
111+
void shouldIncludeActiveElementInformation(Page page) {
112+
page.setContent(
113+
"<button id=\"btn1\">Button 1</button>" +
114+
"<button id=\"btn2\" autofocus>Button 2</button>" +
115+
"<div>Not focusable</div>");
116+
page.waitForFunction("document.activeElement?.id === 'btn2'");
117+
118+
String snapshot = aiSnapshot(page);
119+
assertTrue(snapshot.contains("button \"Button 2\" [active] [ref=e3]"), snapshot);
120+
assertFalse(snapshot.contains("button \"Button 1\" [active]"), snapshot);
121+
}
122+
123+
@Test
124+
void shouldUpdateActiveElementOnFocus(Page page) {
125+
page.setContent(
126+
"<input id=\"input1\" placeholder=\"First input\">" +
127+
"<input id=\"input2\" placeholder=\"Second input\">");
128+
129+
String initialSnapshot = aiSnapshot(page);
130+
assertTrue(initialSnapshot.contains("textbox \"First input\" [ref=e2]"), initialSnapshot);
131+
assertTrue(initialSnapshot.contains("textbox \"Second input\" [ref=e3]"), initialSnapshot);
132+
assertFalse(initialSnapshot.contains("textbox \"First input\" [active]"), initialSnapshot);
133+
assertFalse(initialSnapshot.contains("textbox \"Second input\" [active]"), initialSnapshot);
134+
135+
page.locator("#input2").focus();
136+
137+
String afterFocusSnapshot = aiSnapshot(page);
138+
assertTrue(afterFocusSnapshot.contains("textbox \"Second input\" [active] [ref=e3]"), afterFocusSnapshot);
139+
assertFalse(afterFocusSnapshot.contains("textbox \"First input\" [active]"), afterFocusSnapshot);
140+
}
141+
142+
@Test
143+
void shouldCollapseInlineGenericNodes(Page page) {
144+
page.setContent(
145+
"<ul>" +
146+
"<li><b>3</b> <abbr>bds</abbr></li>" +
147+
"<li><b>2</b> <abbr>ba</abbr></li>" +
148+
"<li><b>1,200</b> <abbr>sqft</abbr></li>" +
149+
"</ul>");
150+
String snapshot = aiSnapshot(page);
151+
assertTrue(snapshot.contains("listitem [ref=e3]: 3 bds"), snapshot);
152+
assertTrue(snapshot.contains("listitem [ref=e4]: 2 ba"), snapshot);
153+
assertTrue(snapshot.contains("listitem [ref=e5]: 1,200 sqft"), snapshot);
154+
}
155+
156+
@Test
157+
void shouldNotRemoveGenericNodesWithTitle(Page page) {
158+
page.setContent("<div title=\"Element title\">Element content</div>");
159+
String snapshot = aiSnapshot(page);
160+
assertTrue(snapshot.contains("generic \"Element title\" [ref=e2]"), snapshot);
161+
}
162+
163+
@Test
164+
void shouldLimitDepth(Page page) {
165+
page.setContent(
166+
"<ul>" +
167+
"<li>item1</li>" +
168+
"<a href=\"about:blank\" style=\"cursor:pointer\">link</a>" +
169+
"<li><ul id=\"target\"><li>item2</li><li><ul><li>item3</li></ul></li></ul></li>" +
170+
"</ul>");
171+
172+
String snapshot1 = page.ariaSnapshot(new Page.AriaSnapshotOptions().setMode(AriaSnapshotMode.AI).setDepth(1));
173+
assertTrue(snapshot1.contains("listitem [ref=e3]: item1"), snapshot1);
174+
assertFalse(snapshot1.contains("item2"), snapshot1);
175+
assertFalse(snapshot1.contains("item3"), snapshot1);
176+
177+
String snapshot2 = page.ariaSnapshot(new Page.AriaSnapshotOptions().setMode(AriaSnapshotMode.AI).setDepth(3));
178+
assertTrue(snapshot2.contains("item1"), snapshot2);
179+
assertTrue(snapshot2.contains("item2"), snapshot2);
180+
assertFalse(snapshot2.contains("item3"), snapshot2);
181+
182+
String snapshot3 = page.ariaSnapshot(new Page.AriaSnapshotOptions().setMode(AriaSnapshotMode.AI).setDepth(100));
183+
assertTrue(snapshot3.contains("item1"), snapshot3);
184+
assertTrue(snapshot3.contains("item2"), snapshot3);
185+
assertTrue(snapshot3.contains("item3"), snapshot3);
186+
187+
String snapshot4 = page.locator("#target").ariaSnapshot(new Locator.AriaSnapshotOptions().setMode(AriaSnapshotMode.AI).setDepth(1));
188+
assertTrue(snapshot4.contains("listitem [ref=e7]: item2"), snapshot4);
189+
assertFalse(snapshot4.contains("item3"), snapshot4);
190+
}
191+
}

0 commit comments

Comments
 (0)