Skip to content

Commit f7fb274

Browse files
pavelfeldmanSkn0tt
authored andcommitted
cherry-pick(#36203): fix: capture snapshot for ai during navigation (#36203)
1 parent b1734fd commit f7fb274

File tree

2 files changed

+31
-8
lines changed

2 files changed

+31
-8
lines changed

packages/playwright-core/src/server/page.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ export class Page extends SdkObject {
812812

813813
async snapshotForAI(metadata: CallMetadata): Promise<string> {
814814
this.lastSnapshotFrameIds = [];
815-
const snapshot = await snapshotFrameForAI(this.mainFrame(), 0, this.lastSnapshotFrameIds);
815+
const snapshot = await snapshotFrameForAI(metadata, this.mainFrame(), 0, this.lastSnapshotFrameIds);
816816
return snapshot.join('\n');
817817
}
818818
}
@@ -989,12 +989,24 @@ class FrameThrottler {
989989
}
990990
}
991991

992-
async function snapshotFrameForAI(frame: frames.Frame, frameOrdinal: number, frameIds: string[]): Promise<string[]> {
993-
const context = await frame._utilityContext();
994-
const injectedScript = await context.injectedScript();
995-
const snapshot = await injectedScript.evaluate((injected, refPrefix) => {
996-
return injected.ariaSnapshot(injected.document.body, { forAI: true, refPrefix });
997-
}, frameOrdinal ? 'f' + frameOrdinal : '');
992+
async function snapshotFrameForAI(metadata: CallMetadata, frame: frames.Frame, frameOrdinal: number, frameIds: string[]): Promise<string[]> {
993+
// Only await the topmost navigations, inner frames will be empty when racing.
994+
const controller = new ProgressController(metadata, frame);
995+
const snapshot = await controller.run(progress => {
996+
return frame.retryWithProgressAndTimeouts(progress, [1000, 2000, 4000, 8000], async continuePolling => {
997+
try {
998+
const context = await frame._utilityContext();
999+
const injectedScript = await context.injectedScript();
1000+
return await injectedScript.evaluate((injected, refPrefix) => {
1001+
return injected.ariaSnapshot(injected.document.body, { forAI: true, refPrefix });
1002+
}, frameOrdinal ? 'f' + frameOrdinal : '');
1003+
} catch (e) {
1004+
if (js.isJavaScriptErrorInEvaluate(e))
1005+
throw e;
1006+
return continuePolling;
1007+
}
1008+
});
1009+
});
9981010

9991011
const lines = snapshot.split('\n');
10001012
const result = [];
@@ -1017,7 +1029,7 @@ async function snapshotFrameForAI(frame: frames.Frame, frameOrdinal: number, fra
10171029
const frameOrdinal = frameIds.length + 1;
10181030
frameIds.push(child.frame._id);
10191031
try {
1020-
const childSnapshot = await snapshotFrameForAI(child.frame, frameOrdinal, frameIds);
1032+
const childSnapshot = await snapshotFrameForAI(metadata, child.frame, frameOrdinal, frameIds);
10211033
result.push(line + ':', ...childSnapshot.map(l => leadingSpace + ' ' + l));
10221034
} catch {
10231035
result.push(line);

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,14 @@ it('should gracefully fallback when child frame cant be captured', async ({ page
229229
- iframe [ref=e3]
230230
`);
231231
});
232+
233+
it('should auto-wait for navigation', async ({ page, server }) => {
234+
await page.goto(server.PREFIX + '/frames/frame.html');
235+
const [, snapshot] = await Promise.all([
236+
page.evaluate(() => window.location.reload()),
237+
snapshotForAI(page)
238+
]);
239+
expect(snapshot).toContainYaml(`
240+
- generic [ref=e2]: Hi, I'm frame
241+
`);
242+
});

0 commit comments

Comments
 (0)