@@ -2,12 +2,53 @@ import { test, expect } from "@playwright/test";
22import fs from "fs/promises" ;
33import path from "path" ;
44import { V3 } from "../../lib/v3/v3.js" ;
5- import { v3TestConfig } from "./v3.config.js" ;
5+ import { getV3TestConfig } from "./v3.config.js" ;
66import type {
77 AgentReplayActStep ,
88 AgentReplayFillFormStep ,
99 CachedAgentEntry ,
1010} from "../../lib/v3/types/private/cache.js" ;
11+ import {
12+ createScriptedAisdkTestLlmClient ,
13+ doneToolResponse ,
14+ findElementRefForText ,
15+ toolCallResponse ,
16+ } from "./testUtils.js" ;
17+
18+ function encodeHtml ( html : string ) : string {
19+ return `data:text/html,${ encodeURIComponent ( html ) } ` ;
20+ }
21+
22+ function createSelfHealLlmClient ( ) {
23+ return createScriptedAisdkTestLlmClient ( {
24+ jsonResponses : {
25+ act : [
26+ ( options ) => ( {
27+ action : {
28+ target : findElementRefForText ( options , "Launch self-heal" ) ,
29+ description : "launch self-heal button" ,
30+ method : "click" ,
31+ button : null ,
32+ } ,
33+ twoStep : false ,
34+ } ) ,
35+ ( options ) => ( {
36+ action : {
37+ target : findElementRefForText ( options , "Launch self-heal" ) ,
38+ description : "launch self-heal button" ,
39+ method : "click" ,
40+ button : null ,
41+ } ,
42+ twoStep : false ,
43+ } ) ,
44+ ] ,
45+ } ,
46+ generateResponses : [
47+ toolCallResponse ( "act" , { action : "click the button" } ) ,
48+ doneToolResponse ( "Clicked the button successfully." , true ) ,
49+ ] ,
50+ } ) ;
51+ }
1152
1253test . describe ( "Agent cache self-heal (e2e)" , ( ) => {
1354 let v3 : V3 ;
@@ -18,7 +59,10 @@ test.describe("Agent cache self-heal (e2e)", () => {
1859 await fs . mkdir ( testInfo . outputDir , { recursive : true } ) ;
1960 cacheDir = await fs . mkdtemp ( path . join ( testInfo . outputDir , "agent-cache-" ) ) ;
2061 v3 = new V3 ( {
21- ...v3TestConfig ,
62+ ...getV3TestConfig ( {
63+ experimental : true ,
64+ llmClient : createSelfHealLlmClient ( ) ,
65+ } ) ,
2266 cacheDir,
2367 selfHeal : true ,
2468 } ) ;
@@ -30,19 +74,32 @@ test.describe("Agent cache self-heal (e2e)", () => {
3074 } ) ;
3175
3276 test ( "replays heal corrupted selectors" , async ( ) => {
33- test . setTimeout ( 120_000 ) ;
77+ test . setTimeout ( 60_000 ) ;
3478
35- const agent = v3 . agent ( {
36- model : "anthropic/claude-haiku-4-5-20251001" ,
37- } ) ;
79+ const agent = v3 . agent ( ) ;
3880 const page = v3 . context . pages ( ) [ 0 ] ;
39- const url =
40- "https://browserbase.github.io/stagehand-eval-sites/sites/shadow-dom/" ;
81+ const url = encodeHtml ( `
82+ <!doctype html>
83+ <html>
84+ <body>
85+ <button
86+ id="launch"
87+ onclick="document.getElementById('status').textContent = 'clicked';"
88+ >
89+ Launch self-heal
90+ </button>
91+ <div id="status">idle</div>
92+ </body>
93+ </html>
94+ ` ) ;
4195 const instruction = "click the button" ;
4296
43- await page . goto ( url , { waitUntil : "networkidle " } ) ;
97+ await page . goto ( url , { waitUntil : "load " } ) ;
4498 const firstResult = await agent . execute ( { instruction, maxSteps : 20 } ) ;
4599 expect ( firstResult . success ) . toBe ( true ) ;
100+ await expect
101+ . poll ( async ( ) => page . evaluate ( ( ) => document . body . textContent ?? "" ) )
102+ . toContain ( "clicked" ) ;
46103
47104 const cachePath = await locateAgentCacheFile ( cacheDir ) ;
48105 const originalEntry = await readCacheEntry ( cachePath ) ;
@@ -62,9 +119,13 @@ test.describe("Agent cache self-heal (e2e)", () => {
62119 ) ;
63120
64121 // Second run should replay from cache, self-heal, and update the file.
65- await page . goto ( url , { waitUntil : "networkidle " } ) ;
122+ await page . goto ( url , { waitUntil : "load " } ) ;
66123 const replayResult = await agent . execute ( { instruction, maxSteps : 20 } ) ;
67124 expect ( replayResult . success ) . toBe ( true ) ;
125+ expect ( replayResult . metadata ?. cacheHit ) . toBe ( true ) ;
126+ await expect
127+ . poll ( async ( ) => page . evaluate ( ( ) => document . body . textContent ?? "" ) )
128+ . toContain ( "clicked" ) ;
68129
69130 const healedEntry = await readCacheEntry ( cachePath ) ;
70131 const healedActionStep = findFirstActionStep ( healedEntry ) ;
0 commit comments