@browserbasehq/orca 3.1.0-patch.4 → 3.2.0-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/dist/cjs/lib/inference.js +1 -4
  2. package/dist/cjs/lib/inference.js.map +1 -1
  3. package/dist/cjs/lib/v3/agent/AgentProvider.js +0 -1
  4. package/dist/cjs/lib/v3/agent/AgentProvider.js.map +1 -1
  5. package/dist/cjs/lib/v3/agent/tools/act.d.ts +1 -1
  6. package/dist/cjs/lib/v3/agent/tools/act.js +20 -4
  7. package/dist/cjs/lib/v3/agent/tools/act.js.map +1 -1
  8. package/dist/cjs/lib/v3/agent/tools/ariaTree.d.ts +8 -1
  9. package/dist/cjs/lib/v3/agent/tools/ariaTree.js +60 -22
  10. package/dist/cjs/lib/v3/agent/tools/ariaTree.js.map +1 -1
  11. package/dist/cjs/lib/v3/agent/tools/click.js +23 -31
  12. package/dist/cjs/lib/v3/agent/tools/click.js.map +1 -1
  13. package/dist/cjs/lib/v3/agent/tools/dragAndDrop.js +22 -30
  14. package/dist/cjs/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
  15. package/dist/cjs/lib/v3/agent/tools/extract.d.ts +2 -2
  16. package/dist/cjs/lib/v3/agent/tools/extract.js +16 -3
  17. package/dist/cjs/lib/v3/agent/tools/extract.js.map +1 -1
  18. package/dist/cjs/lib/v3/agent/tools/fillFormVision.js +30 -30
  19. package/dist/cjs/lib/v3/agent/tools/fillFormVision.js.map +1 -1
  20. package/dist/cjs/lib/v3/agent/tools/fillform.d.ts +7 -1
  21. package/dist/cjs/lib/v3/agent/tools/fillform.js +60 -37
  22. package/dist/cjs/lib/v3/agent/tools/fillform.js.map +1 -1
  23. package/dist/cjs/lib/v3/agent/tools/index.d.ts +5 -0
  24. package/dist/cjs/lib/v3/agent/tools/index.js +5 -5
  25. package/dist/cjs/lib/v3/agent/tools/index.js.map +1 -1
  26. package/dist/cjs/lib/v3/agent/tools/screenshot.d.ts +8 -0
  27. package/dist/cjs/lib/v3/agent/tools/screenshot.js +32 -15
  28. package/dist/cjs/lib/v3/agent/tools/screenshot.js.map +1 -1
  29. package/dist/cjs/lib/v3/agent/tools/scroll.js +12 -0
  30. package/dist/cjs/lib/v3/agent/tools/scroll.js.map +1 -1
  31. package/dist/cjs/lib/v3/agent/tools/type.js +23 -31
  32. package/dist/cjs/lib/v3/agent/tools/type.js.map +1 -1
  33. package/dist/cjs/lib/v3/agent/tools/wait.js +6 -0
  34. package/dist/cjs/lib/v3/agent/tools/wait.js.map +1 -1
  35. package/dist/cjs/lib/v3/agent/utils/handleDoneToolCall.js +4 -0
  36. package/dist/cjs/lib/v3/agent/utils/handleDoneToolCall.js.map +1 -1
  37. package/dist/cjs/lib/v3/api.d.ts +2 -2
  38. package/dist/cjs/lib/v3/api.js +1 -1
  39. package/dist/cjs/lib/v3/api.js.map +1 -1
  40. package/dist/cjs/lib/v3/cache/ActCache.d.ts +0 -1
  41. package/dist/cjs/lib/v3/cache/ActCache.js +2 -18
  42. package/dist/cjs/lib/v3/cache/ActCache.js.map +1 -1
  43. package/dist/cjs/lib/v3/handlers/actHandler.js +1 -2
  44. package/dist/cjs/lib/v3/handlers/actHandler.js.map +1 -1
  45. package/dist/cjs/lib/v3/handlers/extractHandler.js +2 -2
  46. package/dist/cjs/lib/v3/handlers/extractHandler.js.map +1 -1
  47. package/dist/cjs/lib/v3/handlers/observeHandler.js +1 -2
  48. package/dist/cjs/lib/v3/handlers/observeHandler.js.map +1 -1
  49. package/dist/cjs/lib/v3/handlers/v3AgentHandler.js +11 -16
  50. package/dist/cjs/lib/v3/handlers/v3AgentHandler.js.map +1 -1
  51. package/dist/cjs/lib/v3/index.d.ts +2 -1
  52. package/dist/cjs/lib/v3/launch/browserbase.d.ts +1 -1
  53. package/dist/cjs/lib/v3/launch/browserbase.js +4 -9
  54. package/dist/cjs/lib/v3/launch/browserbase.js.map +1 -1
  55. package/dist/cjs/lib/v3/llm/LLMProvider.js +0 -5
  56. package/dist/cjs/lib/v3/llm/LLMProvider.js.map +1 -1
  57. package/dist/cjs/lib/v3/runtimePaths.js +2 -1
  58. package/dist/cjs/lib/v3/runtimePaths.js.map +1 -1
  59. package/dist/cjs/lib/v3/shutdown/supervisor.js +2 -2
  60. package/dist/cjs/lib/v3/shutdown/supervisor.js.map +1 -1
  61. package/dist/cjs/lib/v3/timeoutConfig.d.ts +1 -1
  62. package/dist/cjs/lib/v3/timeoutConfig.js +5 -0
  63. package/dist/cjs/lib/v3/timeoutConfig.js.map +1 -1
  64. package/dist/cjs/lib/v3/types/private/shutdown.d.ts +1 -1
  65. package/dist/cjs/lib/v3/types/private/shutdown.js.map +1 -1
  66. package/dist/cjs/lib/v3/types/public/agent.d.ts +12 -1
  67. package/dist/cjs/lib/v3/types/public/agent.js +0 -1
  68. package/dist/cjs/lib/v3/types/public/agent.js.map +1 -1
  69. package/dist/cjs/lib/v3/types/public/api.d.ts +3 -0
  70. package/dist/cjs/lib/v3/types/public/api.js +1 -0
  71. package/dist/cjs/lib/v3/types/public/api.js.map +1 -1
  72. package/dist/cjs/lib/v3/types/public/model.d.ts +4 -2
  73. package/dist/cjs/lib/v3/types/public/model.js.map +1 -1
  74. package/dist/cjs/lib/v3/types/public/options.d.ts +1 -0
  75. package/dist/cjs/lib/v3/understudy/cdp.d.ts +5 -1
  76. package/dist/cjs/lib/v3/understudy/cdp.js +54 -7
  77. package/dist/cjs/lib/v3/understudy/cdp.js.map +1 -1
  78. package/dist/cjs/lib/v3/understudy/context.d.ts +1 -0
  79. package/dist/cjs/lib/v3/understudy/context.js +142 -60
  80. package/dist/cjs/lib/v3/understudy/context.js.map +1 -1
  81. package/dist/cjs/lib/v3/understudy/frame.js +23 -6
  82. package/dist/cjs/lib/v3/understudy/frame.js.map +1 -1
  83. package/dist/cjs/lib/v3/understudy/page.d.ts +13 -0
  84. package/dist/cjs/lib/v3/understudy/page.js +56 -3
  85. package/dist/cjs/lib/v3/understudy/page.js.map +1 -1
  86. package/dist/cjs/lib/v3/understudy/screenshotUtils.d.ts +0 -1
  87. package/dist/cjs/lib/v3/understudy/screenshotUtils.js +0 -18
  88. package/dist/cjs/lib/v3/understudy/screenshotUtils.js.map +1 -1
  89. package/dist/cjs/lib/v3/v3.js +38 -24
  90. package/dist/cjs/lib/v3/v3.js.map +1 -1
  91. package/dist/cjs/tests/integration/cdp-session-detached.spec.js +1 -1
  92. package/dist/cjs/tests/integration/cdp-session-detached.spec.js.map +1 -1
  93. package/dist/cjs/tests/integration/context-addInitScript.spec.js +104 -11
  94. package/dist/cjs/tests/integration/context-addInitScript.spec.js.map +1 -1
  95. package/dist/cjs/tests/integration/iframe-ctx-addInitScript-race.spec.d.ts +1 -0
  96. package/dist/cjs/tests/integration/iframe-ctx-addInitScript-race.spec.js +219 -0
  97. package/dist/cjs/tests/integration/iframe-ctx-addInitScript-race.spec.js.map +1 -0
  98. package/dist/cjs/tests/integration/page-extra-http-headers.spec.d.ts +1 -0
  99. package/dist/cjs/tests/integration/page-extra-http-headers.spec.js +85 -0
  100. package/dist/cjs/tests/integration/page-extra-http-headers.spec.js.map +1 -0
  101. package/dist/cjs/tests/integration/page-screenshot.spec.js +1 -1
  102. package/dist/cjs/tests/integration/page-screenshot.spec.js.map +1 -1
  103. package/dist/cjs/tests/integration/timeouts.spec.js +168 -0
  104. package/dist/cjs/tests/integration/timeouts.spec.js.map +1 -1
  105. package/dist/cjs/tests/unit/model-deprecation.test.js +5 -8
  106. package/dist/cjs/tests/unit/model-deprecation.test.js.map +1 -1
  107. package/dist/cjs/tests/unit/page-extra-http-headers.test.d.ts +1 -0
  108. package/dist/cjs/tests/unit/page-extra-http-headers.test.js +92 -0
  109. package/dist/cjs/tests/unit/page-extra-http-headers.test.js.map +1 -0
  110. package/dist/cjs/tests/unit/public-api/llm-and-agents.test.js +13 -1
  111. package/dist/cjs/tests/unit/public-api/llm-and-agents.test.js.map +1 -1
  112. package/dist/cjs/tests/unit/public-api/public-types.test.js.map +1 -1
  113. package/dist/esm/lib/inference.js +1 -4
  114. package/dist/esm/lib/inference.js.map +1 -1
  115. package/dist/esm/lib/v3/agent/AgentProvider.js +0 -1
  116. package/dist/esm/lib/v3/agent/AgentProvider.js.map +1 -1
  117. package/dist/esm/lib/v3/agent/tools/act.d.ts +1 -1
  118. package/dist/esm/lib/v3/agent/tools/act.js +20 -4
  119. package/dist/esm/lib/v3/agent/tools/act.js.map +1 -1
  120. package/dist/esm/lib/v3/agent/tools/ariaTree.d.ts +8 -1
  121. package/dist/esm/lib/v3/agent/tools/ariaTree.js +60 -22
  122. package/dist/esm/lib/v3/agent/tools/ariaTree.js.map +1 -1
  123. package/dist/esm/lib/v3/agent/tools/click.js +23 -31
  124. package/dist/esm/lib/v3/agent/tools/click.js.map +1 -1
  125. package/dist/esm/lib/v3/agent/tools/dragAndDrop.js +22 -30
  126. package/dist/esm/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
  127. package/dist/esm/lib/v3/agent/tools/extract.d.ts +2 -2
  128. package/dist/esm/lib/v3/agent/tools/extract.js +16 -3
  129. package/dist/esm/lib/v3/agent/tools/extract.js.map +1 -1
  130. package/dist/esm/lib/v3/agent/tools/fillFormVision.js +30 -30
  131. package/dist/esm/lib/v3/agent/tools/fillFormVision.js.map +1 -1
  132. package/dist/esm/lib/v3/agent/tools/fillform.d.ts +7 -1
  133. package/dist/esm/lib/v3/agent/tools/fillform.js +60 -37
  134. package/dist/esm/lib/v3/agent/tools/fillform.js.map +1 -1
  135. package/dist/esm/lib/v3/agent/tools/index.d.ts +5 -0
  136. package/dist/esm/lib/v3/agent/tools/index.js +5 -5
  137. package/dist/esm/lib/v3/agent/tools/index.js.map +1 -1
  138. package/dist/esm/lib/v3/agent/tools/screenshot.d.ts +8 -0
  139. package/dist/esm/lib/v3/agent/tools/screenshot.js +32 -15
  140. package/dist/esm/lib/v3/agent/tools/screenshot.js.map +1 -1
  141. package/dist/esm/lib/v3/agent/tools/scroll.js +12 -0
  142. package/dist/esm/lib/v3/agent/tools/scroll.js.map +1 -1
  143. package/dist/esm/lib/v3/agent/tools/type.js +23 -31
  144. package/dist/esm/lib/v3/agent/tools/type.js.map +1 -1
  145. package/dist/esm/lib/v3/agent/tools/wait.js +6 -0
  146. package/dist/esm/lib/v3/agent/tools/wait.js.map +1 -1
  147. package/dist/esm/lib/v3/agent/utils/handleDoneToolCall.js +4 -0
  148. package/dist/esm/lib/v3/agent/utils/handleDoneToolCall.js.map +1 -1
  149. package/dist/esm/lib/v3/api.d.ts +2 -2
  150. package/dist/esm/lib/v3/api.js +1 -1
  151. package/dist/esm/lib/v3/api.js.map +1 -1
  152. package/dist/esm/lib/v3/cache/ActCache.d.ts +0 -1
  153. package/dist/esm/lib/v3/cache/ActCache.js +2 -18
  154. package/dist/esm/lib/v3/cache/ActCache.js.map +1 -1
  155. package/dist/esm/lib/v3/handlers/actHandler.js +1 -2
  156. package/dist/esm/lib/v3/handlers/actHandler.js.map +1 -1
  157. package/dist/esm/lib/v3/handlers/extractHandler.js +2 -2
  158. package/dist/esm/lib/v3/handlers/extractHandler.js.map +1 -1
  159. package/dist/esm/lib/v3/handlers/observeHandler.js +1 -2
  160. package/dist/esm/lib/v3/handlers/observeHandler.js.map +1 -1
  161. package/dist/esm/lib/v3/handlers/v3AgentHandler.js +11 -16
  162. package/dist/esm/lib/v3/handlers/v3AgentHandler.js.map +1 -1
  163. package/dist/esm/lib/v3/index.d.ts +2 -1
  164. package/dist/esm/lib/v3/launch/browserbase.d.ts +1 -1
  165. package/dist/esm/lib/v3/launch/browserbase.js +4 -9
  166. package/dist/esm/lib/v3/launch/browserbase.js.map +1 -1
  167. package/dist/esm/lib/v3/llm/LLMProvider.js +0 -5
  168. package/dist/esm/lib/v3/llm/LLMProvider.js.map +1 -1
  169. package/dist/esm/lib/v3/runtimePaths.js +2 -1
  170. package/dist/esm/lib/v3/runtimePaths.js.map +1 -1
  171. package/dist/esm/lib/v3/shutdown/supervisor.js +2 -2
  172. package/dist/esm/lib/v3/shutdown/supervisor.js.map +1 -1
  173. package/dist/esm/lib/v3/timeoutConfig.d.ts +1 -1
  174. package/dist/esm/lib/v3/timeoutConfig.js +5 -0
  175. package/dist/esm/lib/v3/timeoutConfig.js.map +1 -1
  176. package/dist/esm/lib/v3/types/private/shutdown.d.ts +1 -1
  177. package/dist/esm/lib/v3/types/private/shutdown.js.map +1 -1
  178. package/dist/esm/lib/v3/types/public/agent.d.ts +12 -1
  179. package/dist/esm/lib/v3/types/public/agent.js +0 -1
  180. package/dist/esm/lib/v3/types/public/agent.js.map +1 -1
  181. package/dist/esm/lib/v3/types/public/api.d.ts +3 -0
  182. package/dist/esm/lib/v3/types/public/api.js +1 -0
  183. package/dist/esm/lib/v3/types/public/api.js.map +1 -1
  184. package/dist/esm/lib/v3/types/public/model.d.ts +4 -2
  185. package/dist/esm/lib/v3/types/public/model.js.map +1 -1
  186. package/dist/esm/lib/v3/types/public/options.d.ts +1 -0
  187. package/dist/esm/lib/v3/understudy/cdp.d.ts +5 -1
  188. package/dist/esm/lib/v3/understudy/cdp.js +55 -8
  189. package/dist/esm/lib/v3/understudy/cdp.js.map +1 -1
  190. package/dist/esm/lib/v3/understudy/context.d.ts +1 -0
  191. package/dist/esm/lib/v3/understudy/context.js +142 -60
  192. package/dist/esm/lib/v3/understudy/context.js.map +1 -1
  193. package/dist/esm/lib/v3/understudy/frame.js +23 -6
  194. package/dist/esm/lib/v3/understudy/frame.js.map +1 -1
  195. package/dist/esm/lib/v3/understudy/page.d.ts +13 -0
  196. package/dist/esm/lib/v3/understudy/page.js +58 -5
  197. package/dist/esm/lib/v3/understudy/page.js.map +1 -1
  198. package/dist/esm/lib/v3/understudy/screenshotUtils.d.ts +0 -1
  199. package/dist/esm/lib/v3/understudy/screenshotUtils.js +0 -17
  200. package/dist/esm/lib/v3/understudy/screenshotUtils.js.map +1 -1
  201. package/dist/esm/lib/v3/v3.js +38 -24
  202. package/dist/esm/lib/v3/v3.js.map +1 -1
  203. package/dist/esm/tests/integration/cdp-session-detached.spec.js +1 -1
  204. package/dist/esm/tests/integration/cdp-session-detached.spec.js.map +1 -1
  205. package/dist/esm/tests/integration/context-addInitScript.spec.js +104 -11
  206. package/dist/esm/tests/integration/context-addInitScript.spec.js.map +1 -1
  207. package/dist/esm/tests/integration/iframe-ctx-addInitScript-race.spec.d.ts +1 -0
  208. package/dist/esm/tests/integration/iframe-ctx-addInitScript-race.spec.js +217 -0
  209. package/dist/esm/tests/integration/iframe-ctx-addInitScript-race.spec.js.map +1 -0
  210. package/dist/esm/tests/integration/page-extra-http-headers.spec.d.ts +1 -0
  211. package/dist/esm/tests/integration/page-extra-http-headers.spec.js +83 -0
  212. package/dist/esm/tests/integration/page-extra-http-headers.spec.js.map +1 -0
  213. package/dist/esm/tests/integration/page-screenshot.spec.js +1 -1
  214. package/dist/esm/tests/integration/page-screenshot.spec.js.map +1 -1
  215. package/dist/esm/tests/integration/timeouts.spec.js +168 -0
  216. package/dist/esm/tests/integration/timeouts.spec.js.map +1 -1
  217. package/dist/esm/tests/unit/model-deprecation.test.js +5 -8
  218. package/dist/esm/tests/unit/model-deprecation.test.js.map +1 -1
  219. package/dist/esm/tests/unit/page-extra-http-headers.test.d.ts +1 -0
  220. package/dist/esm/tests/unit/page-extra-http-headers.test.js +90 -0
  221. package/dist/esm/tests/unit/page-extra-http-headers.test.js.map +1 -0
  222. package/dist/esm/tests/unit/public-api/llm-and-agents.test.js +13 -1
  223. package/dist/esm/tests/unit/public-api/llm-and-agents.test.js.map +1 -1
  224. package/dist/esm/tests/unit/public-api/public-types.test.js.map +1 -1
  225. package/package.json +4 -2
@@ -1 +1 @@
1
- {"version":3,"file":"cdp-session-detached.spec.js","sourceRoot":"","sources":["../../../../tests/integration/cdp-session-detached.spec.ts"],"names":[],"mappings":";;AAAA,2CAAgD;AAChD,2CAA4D;AAC5D,8CAAwC;AACxC,iDAA8C;AAE9C,WAAI,CAAC,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAChD,IAAI,EAAM,CAAC;IAEX,WAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE;QACzB,EAAE,GAAG,IAAI,UAAE,CAAC,2BAAY,CAAC,CAAC;QAC1B,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,WAAI,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE;QACxB,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAI,EAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,SAAS,GAAc,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,CAAC,MAAe,EAAE,EAAE;YACtC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;QAE9C,IAAI,SAAS,GAEF,IAAI,CAAC;QAEhB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,qBAAkB,CAAC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;YACrE,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YAEpC,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,MAAM,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YAElE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE;gBACjD,UAAU,EAAE,qDAAqD;gBACjE,YAAY,EAAE,IAAI;gBAClB,aAAa,EAAE,IAAI;aACpB,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YAErB,MAAM,IAAA,aAAM,EAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;YAE9D,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC5C,IAAA,aAAM,EAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;YAC/C,MAAM,SAAS,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { test, expect } from \"@playwright/test\";\nimport { chromium as playwrightChromium } from \"playwright\";\nimport { V3 } from \"../../lib/v3/v3.js\";\nimport { v3TestConfig } from \"./v3.config.js\";\n\ntest.describe(\"CDP session detach handling\", () => {\n let v3: V3;\n\n test.beforeEach(async () => {\n v3 = new V3(v3TestConfig);\n await v3.init();\n });\n\n test.afterEach(async () => {\n await v3?.close?.().catch(() => {});\n });\n\n test(\"rejects inflight CDP calls when a target is closed\", async () => {\n const unhandled: unknown[] = [];\n const onUnhandled = (reason: unknown) => {\n unhandled.push(reason);\n };\n\n process.on(\"unhandledRejection\", onUnhandled);\n\n let pwBrowser: Awaited<\n ReturnType<typeof playwrightChromium.connectOverCDP>\n > | null = null;\n\n try {\n pwBrowser = await playwrightChromium.connectOverCDP(v3.connectURL());\n const pwContext = pwBrowser.contexts()[0];\n const pwPage = pwContext.pages()[0];\n\n const v3Page = v3.context.pages()[0];\n await v3Page.goto(\"data:text/html,<html><body>cdp</body></html>\");\n\n const pending = v3Page.sendCDP(\"Runtime.evaluate\", {\n expression: \"new Promise(r => setTimeout(() => r('done'), 5000))\",\n awaitPromise: true,\n returnByValue: true,\n });\n\n await pwPage.close();\n\n await expect(pending).rejects.toThrow(/CDP session detached/);\n\n await new Promise((r) => setTimeout(r, 50));\n expect(unhandled).toHaveLength(0);\n } finally {\n process.off(\"unhandledRejection\", onUnhandled);\n await pwBrowser?.close().catch(() => {});\n }\n });\n});\n"]}
1
+ {"version":3,"file":"cdp-session-detached.spec.js","sourceRoot":"","sources":["../../../../tests/integration/cdp-session-detached.spec.ts"],"names":[],"mappings":";;AAAA,2CAAgD;AAChD,2CAA4D;AAC5D,8CAAwC;AACxC,iDAA8C;AAE9C,WAAI,CAAC,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAChD,IAAI,EAAM,CAAC;IAEX,WAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE;QACzB,EAAE,GAAG,IAAI,UAAE,CAAC,2BAAY,CAAC,CAAC;QAC1B,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,WAAI,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE;QACxB,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAI,EAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,SAAS,GAAc,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,CAAC,MAAe,EAAE,EAAE;YACtC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;QAE9C,IAAI,SAAS,GAEF,IAAI,CAAC;QAEhB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,qBAAkB,CAAC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;YACrE,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YAEpC,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,MAAM,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YAElE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE;gBACjD,UAAU,EAAE,qDAAqD;gBACjE,YAAY,EAAE,IAAI;gBAClB,aAAa,EAAE,IAAI;aACpB,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YAErB,MAAM,IAAA,aAAM,EAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CACnC,qDAAqD,CACtD,CAAC;YAEF,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC5C,IAAA,aAAM,EAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;YAC/C,MAAM,SAAS,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { test, expect } from \"@playwright/test\";\nimport { chromium as playwrightChromium } from \"playwright\";\nimport { V3 } from \"../../lib/v3/v3.js\";\nimport { v3TestConfig } from \"./v3.config.js\";\n\ntest.describe(\"CDP session detach handling\", () => {\n let v3: V3;\n\n test.beforeEach(async () => {\n v3 = new V3(v3TestConfig);\n await v3.init();\n });\n\n test.afterEach(async () => {\n await v3?.close?.().catch(() => {});\n });\n\n test(\"rejects inflight CDP calls when a target is closed\", async () => {\n const unhandled: unknown[] = [];\n const onUnhandled = (reason: unknown) => {\n unhandled.push(reason);\n };\n\n process.on(\"unhandledRejection\", onUnhandled);\n\n let pwBrowser: Awaited<\n ReturnType<typeof playwrightChromium.connectOverCDP>\n > | null = null;\n\n try {\n pwBrowser = await playwrightChromium.connectOverCDP(v3.connectURL());\n const pwContext = pwBrowser.contexts()[0];\n const pwPage = pwContext.pages()[0];\n\n const v3Page = v3.context.pages()[0];\n await v3Page.goto(\"data:text/html,<html><body>cdp</body></html>\");\n\n const pending = v3Page.sendCDP(\"Runtime.evaluate\", {\n expression: \"new Promise(r => setTimeout(() => r('done'), 5000))\",\n awaitPromise: true,\n returnByValue: true,\n });\n\n await pwPage.close();\n\n await expect(pending).rejects.toThrow(\n /No Page found for target closed before CDP response/,\n );\n\n await new Promise((r) => setTimeout(r, 50));\n expect(unhandled).toHaveLength(0);\n } finally {\n process.off(\"unhandledRejection\", onUnhandled);\n await pwBrowser?.close().catch(() => {});\n }\n });\n});\n"]}
@@ -3,7 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const test_1 = require("@playwright/test");
4
4
  const v3_js_1 = require("../../lib/v3/v3.js");
5
5
  const v3_config_js_1 = require("./v3.config.js");
6
+ const POPUP_TIMEOUT_MS = 20_000;
6
7
  const toDataUrl = (html) => `data:text/html,${encodeURIComponent(html)}`;
8
+ const waitForPopupPage = async (ctx, knownTargetIds, timeoutMs = POPUP_TIMEOUT_MS) => {
9
+ const deadline = Date.now() + timeoutMs;
10
+ while (Date.now() < deadline) {
11
+ const popup = ctx
12
+ .pages()
13
+ .find((page) => !knownTargetIds.has(page.targetId()));
14
+ if (popup)
15
+ return popup;
16
+ try {
17
+ const active = await ctx.awaitActivePage(500);
18
+ if (!knownTargetIds.has(active.targetId()))
19
+ return active;
20
+ }
21
+ catch {
22
+ // keep polling
23
+ }
24
+ await new Promise((resolve) => setTimeout(resolve, 50));
25
+ }
26
+ throw new Error("Popup page was not created");
27
+ };
7
28
  test_1.test.describe("context.addInitScript", () => {
8
29
  let v3;
9
30
  let ctx;
@@ -145,7 +166,7 @@ test_1.test.describe("context.addInitScript", () => {
145
166
  setPayload();
146
167
  }
147
168
  }, payload);
148
- const popupUrl = toDataUrl("<html><body>popup</body></html>");
169
+ const popupUrl = "https://example.com/";
149
170
  const openerHtml = "<!DOCTYPE html>" +
150
171
  "<html><body>" +
151
172
  '<a id="open" target="_blank" href="' +
@@ -154,23 +175,95 @@ test_1.test.describe("context.addInitScript", () => {
154
175
  "</body></html>";
155
176
  const opener = await ctx.awaitActivePage();
156
177
  await opener.goto(toDataUrl(openerHtml), { waitUntil: "load" });
178
+ const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));
157
179
  await opener.locator("#open").click();
158
- const openerId = opener.targetId();
159
- const deadline = Date.now() + 2000;
160
- let popup = ctx.pages().find((p) => p.targetId() !== openerId);
161
- while (!popup && Date.now() < deadline) {
162
- await opener.waitForTimeout(25);
163
- popup = ctx.pages().find((p) => p.targetId() !== openerId);
164
- }
165
- if (!popup) {
166
- throw new Error("Popup page was not created");
167
- }
180
+ const popup = await waitForPopupPage(ctx, knownTargetIds);
168
181
  await popup.waitForLoadState("load");
169
182
  const observed = await popup.evaluate(() => {
170
183
  const raw = document.documentElement.dataset.initPayload;
171
184
  return raw ? JSON.parse(raw) : undefined;
172
185
  });
173
186
  (0, test_1.expect)(observed).toEqual(payload);
187
+ await popup.reload({ waitUntil: "load" });
188
+ const observedAfterReload = await popup.evaluate(() => {
189
+ const raw = document.documentElement.dataset.initPayload;
190
+ return raw ? JSON.parse(raw) : undefined;
191
+ });
192
+ (0, test_1.expect)(observedAfterReload).toEqual(payload);
193
+ });
194
+ (0, test_1.test)("applies script to in-process popup", async () => {
195
+ await ctx.addInitScript(() => {
196
+ window.__injected = 123;
197
+ });
198
+ const opener = await ctx.awaitActivePage();
199
+ const openerHtml = "<!DOCTYPE html>" +
200
+ "<html><body>" +
201
+ '<a id="open" target="_blank" href="about:blank">open</a>' +
202
+ "</body></html>";
203
+ await opener.goto(toDataUrl(openerHtml), { waitUntil: "load" });
204
+ const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));
205
+ await opener.locator("#open").click();
206
+ const popup = await waitForPopupPage(ctx, knownTargetIds);
207
+ await popup.waitForLoadState("load");
208
+ const injected = await popup.evaluate(() => {
209
+ return window.__injected;
210
+ });
211
+ (0, test_1.expect)(injected).toBe(123);
212
+ });
213
+ (0, test_1.test)("applies script to cross-process popup and survives reload", async () => {
214
+ await ctx.addInitScript(() => {
215
+ window.__injected = 123;
216
+ });
217
+ const opener = await ctx.awaitActivePage();
218
+ const openerHtml = "<!DOCTYPE html>" +
219
+ "<html><body>" +
220
+ '<a id="open" target="_blank" href="https://example.com/">open</a>' +
221
+ "</body></html>";
222
+ await opener.goto(toDataUrl(openerHtml), {
223
+ waitUntil: "load",
224
+ });
225
+ const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));
226
+ await opener.locator("#open").click();
227
+ const popup = await waitForPopupPage(ctx, knownTargetIds);
228
+ await popup.waitForLoadState("load");
229
+ const injected = await popup.evaluate(() => {
230
+ return window.__injected;
231
+ });
232
+ (0, test_1.expect)(injected).toBe(123);
233
+ await popup.reload({ waitUntil: "load" });
234
+ const injectedAfterReload = await popup.evaluate(() => {
235
+ return window.__injected;
236
+ });
237
+ (0, test_1.expect)(injectedAfterReload).toBe(123);
238
+ });
239
+ (0, test_1.test)("applies script to cross-process popup opened via window.open and survives reload", async () => {
240
+ await ctx.addInitScript(() => {
241
+ window.__injected = 789;
242
+ });
243
+ const opener = await ctx.awaitActivePage();
244
+ await opener.goto("about:blank", { waitUntil: "load" });
245
+ await opener.mainFrame().evaluate(() => {
246
+ const button = document.createElement("button");
247
+ button.id = "open-via-window-open";
248
+ button.textContent = "open popup";
249
+ button.addEventListener("click", () => {
250
+ window.open("https://example.com/", "_blank");
251
+ });
252
+ document.body.appendChild(button);
253
+ });
254
+ const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));
255
+ await opener.locator("#open-via-window-open").click();
256
+ const popup = await waitForPopupPage(ctx, knownTargetIds);
257
+ await popup.waitForLoadState("load");
258
+ const injected = await popup.evaluate(() => {
259
+ return window.__injected;
260
+ });
261
+ (0, test_1.expect)(injected).toBe(789);
262
+ await popup.reload({ waitUntil: "load" });
263
+ const injectedAfterReload = await popup.evaluate(() => {
264
+ return window.__injected;
265
+ });
266
+ (0, test_1.expect)(injectedAfterReload).toBe(789);
174
267
  });
175
268
  (0, test_1.test)("context.addInitScript installs a function callable from page.evaluate", async () => {
176
269
  const page = await ctx.awaitActivePage();
@@ -1 +1 @@
1
- {"version":3,"file":"context-addInitScript.spec.js","sourceRoot":"","sources":["../../../../tests/integration/context-addInitScript.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"@playwright/test\";\nimport { V3 } from \"../../lib/v3/v3.js\";\nimport { v3TestConfig } from \"./v3.config.js\";\nimport { V3Context } from \"../../lib/v3/understudy/context.js\";\n\nconst toDataUrl = (html: string): string =>\n `data:text/html,${encodeURIComponent(html)}`;\n\ntest.describe(\"context.addInitScript\", () => {\n let v3: V3;\n let ctx: V3Context;\n\n test.beforeEach(async () => {\n v3 = new V3(v3TestConfig);\n await v3.init();\n ctx = v3.context;\n });\n\n test.afterEach(async () => {\n await v3?.close?.().catch(() => {});\n });\n\n test(\"runs before inline document scripts on navigation\", async () => {\n const page = await ctx.awaitActivePage();\n\n await ctx.addInitScript(() => {\n (window as unknown as { __fromContextInit?: string }).__fromContextInit =\n \"injected-value\";\n });\n\n const html = `<!DOCTYPE html>\n <html>\n <body>\n <script>\n var value = (window && window.__fromContextInit) || 'missing';\n document.body.dataset.initWitness = value;\n </script>\n </body>\n </html>`;\n\n await page.goto(toDataUrl(html), { waitUntil: \"load\" });\n\n const observed = await page.evaluate(() => {\n return document.body.dataset.initWitness;\n });\n expect(observed).toBe(\"injected-value\");\n });\n\n test(\"re-applies the script on every navigation for the same page\", async () => {\n const page = await ctx.awaitActivePage();\n\n await ctx.addInitScript(`\n (function () {\n function markVisit() {\n var root = document.documentElement;\n if (!root) return;\n var current = Number(window.name || \"0\");\n var next = current + 1;\n window.name = String(next);\n root.dataset.visitCount = String(next);\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", markVisit, {\n once: true,\n });\n } else {\n markVisit();\n }\n })();\n `);\n\n await page.goto(toDataUrl(\"<html><body>first</body></html>\"), {\n waitUntil: \"load\",\n });\n const first = await page.evaluate(() => {\n return Number(document.documentElement.dataset.visitCount ?? \"0\");\n });\n expect(first).toBe(1);\n\n await page.goto(toDataUrl(\"<html><body>second</body></html>\"), {\n waitUntil: \"load\",\n });\n const second = await page.evaluate(() => {\n return Number(document.documentElement.dataset.visitCount ?? \"0\");\n });\n expect(second).toBe(2);\n });\n\n test(\"applies script (with args) to newly created pages\", async () => {\n const payload = { greeting: \"hi\", nested: { count: 2 } };\n\n const initPayload = ((arg) => {\n function setPayload() {\n const root = document.documentElement;\n if (!root) return;\n root.dataset.initPayload = JSON.stringify(arg);\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", setPayload, {\n once: true,\n });\n } else {\n setPayload();\n }\n }) as (arg: typeof payload) => void;\n await ctx.addInitScript(initPayload, payload);\n\n const newPage = await ctx.newPage();\n await newPage.goto(toDataUrl(\"<html><body>child</body></html>\"), {\n waitUntil: \"load\",\n });\n\n const observed = await newPage.evaluate(() => {\n const raw = document.documentElement.dataset.initPayload;\n return raw ? JSON.parse(raw) : undefined;\n });\n expect(observed).toEqual(payload);\n });\n\n test(\"applies script to newPage(url) on initial document\", async () => {\n const payload = { marker: \"newPageUrl\" };\n\n await ctx.addInitScript((arg) => {\n function setPayload(): void {\n const root = document.documentElement;\n if (!root) return;\n root.dataset.initPayload = JSON.stringify(arg);\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", setPayload, {\n once: true,\n });\n } else {\n setPayload();\n }\n }, payload);\n\n const newPage = await ctx.newPage(\n toDataUrl(\"<html><body>new page</body></html>\"),\n );\n await newPage.waitForLoadState(\"load\");\n\n const observed = await newPage.evaluate(() => {\n const raw = document.documentElement.dataset.initPayload;\n return raw ? JSON.parse(raw) : undefined;\n });\n expect(observed).toEqual(payload);\n });\n\n test(\"applies script to pages opened via link clicks\", async () => {\n const payload = { marker: \"linkClick\" };\n\n await ctx.addInitScript((arg) => {\n function setPayload(): void {\n const root = document.documentElement;\n if (!root) return;\n root.dataset.initPayload = JSON.stringify(arg);\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", setPayload, {\n once: true,\n });\n } else {\n setPayload();\n }\n }, payload);\n\n const popupUrl = toDataUrl(\"<html><body>popup</body></html>\");\n const openerHtml =\n \"<!DOCTYPE html>\" +\n \"<html><body>\" +\n '<a id=\"open\" target=\"_blank\" href=\"' +\n popupUrl +\n '\">open</a>' +\n \"</body></html>\";\n\n const opener = await ctx.awaitActivePage();\n await opener.goto(toDataUrl(openerHtml), { waitUntil: \"load\" });\n await opener.locator(\"#open\").click();\n\n const openerId = opener.targetId();\n const deadline = Date.now() + 2000;\n let popup = ctx.pages().find((p) => p.targetId() !== openerId);\n while (!popup && Date.now() < deadline) {\n await opener.waitForTimeout(25);\n popup = ctx.pages().find((p) => p.targetId() !== openerId);\n }\n if (!popup) {\n throw new Error(\"Popup page was not created\");\n }\n\n await popup.waitForLoadState(\"load\");\n\n const observed = await popup.evaluate(() => {\n const raw = document.documentElement.dataset.initPayload;\n return raw ? JSON.parse(raw) : undefined;\n });\n expect(observed).toEqual(payload);\n });\n\n test(\"context.addInitScript installs a function callable from page.evaluate\", async () => {\n const page = await ctx.awaitActivePage();\n\n await ctx.addInitScript(() => {\n // installed before any navigation\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n window.sayHelloFromStagehand = () => \"hello from stagehand\";\n });\n\n await page.goto(\"https://example.com\", { waitUntil: \"domcontentloaded\" });\n\n const result = await page.evaluate(() => {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n return window.sayHelloFromStagehand();\n });\n\n expect(result).toBe(\"hello from stagehand\");\n });\n});\n"]}
1
+ {"version":3,"file":"context-addInitScript.spec.js","sourceRoot":"","sources":["../../../../tests/integration/context-addInitScript.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"@playwright/test\";\nimport { V3 } from \"../../lib/v3/v3.js\";\nimport { v3TestConfig } from \"./v3.config.js\";\nimport { V3Context } from \"../../lib/v3/understudy/context.js\";\nimport type { Page as V3Page } from \"../../lib/v3/understudy/page.js\";\n\nconst POPUP_TIMEOUT_MS = 20_000;\n\nconst toDataUrl = (html: string): string =>\n `data:text/html,${encodeURIComponent(html)}`;\n\nconst waitForPopupPage = async (\n ctx: V3Context,\n knownTargetIds: Set<string>,\n timeoutMs = POPUP_TIMEOUT_MS,\n): Promise<V3Page> => {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const popup = ctx\n .pages()\n .find((page) => !knownTargetIds.has(page.targetId()));\n if (popup) return popup;\n try {\n const active = await ctx.awaitActivePage(500);\n if (!knownTargetIds.has(active.targetId())) return active;\n } catch {\n // keep polling\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n throw new Error(\"Popup page was not created\");\n};\n\ntest.describe(\"context.addInitScript\", () => {\n let v3: V3;\n let ctx: V3Context;\n\n test.beforeEach(async () => {\n v3 = new V3(v3TestConfig);\n await v3.init();\n ctx = v3.context;\n });\n\n test.afterEach(async () => {\n await v3?.close?.().catch(() => {});\n });\n\n test(\"runs before inline document scripts on navigation\", async () => {\n const page = await ctx.awaitActivePage();\n\n await ctx.addInitScript(() => {\n (window as unknown as { __fromContextInit?: string }).__fromContextInit =\n \"injected-value\";\n });\n\n const html = `<!DOCTYPE html>\n <html>\n <body>\n <script>\n var value = (window && window.__fromContextInit) || 'missing';\n document.body.dataset.initWitness = value;\n </script>\n </body>\n </html>`;\n\n await page.goto(toDataUrl(html), { waitUntil: \"load\" });\n\n const observed = await page.evaluate(() => {\n return document.body.dataset.initWitness;\n });\n expect(observed).toBe(\"injected-value\");\n });\n\n test(\"re-applies the script on every navigation for the same page\", async () => {\n const page = await ctx.awaitActivePage();\n\n await ctx.addInitScript(`\n (function () {\n function markVisit() {\n var root = document.documentElement;\n if (!root) return;\n var current = Number(window.name || \"0\");\n var next = current + 1;\n window.name = String(next);\n root.dataset.visitCount = String(next);\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", markVisit, {\n once: true,\n });\n } else {\n markVisit();\n }\n })();\n `);\n\n await page.goto(toDataUrl(\"<html><body>first</body></html>\"), {\n waitUntil: \"load\",\n });\n const first = await page.evaluate(() => {\n return Number(document.documentElement.dataset.visitCount ?? \"0\");\n });\n expect(first).toBe(1);\n\n await page.goto(toDataUrl(\"<html><body>second</body></html>\"), {\n waitUntil: \"load\",\n });\n const second = await page.evaluate(() => {\n return Number(document.documentElement.dataset.visitCount ?? \"0\");\n });\n expect(second).toBe(2);\n });\n\n test(\"applies script (with args) to newly created pages\", async () => {\n const payload = { greeting: \"hi\", nested: { count: 2 } };\n\n const initPayload = ((arg) => {\n function setPayload() {\n const root = document.documentElement;\n if (!root) return;\n root.dataset.initPayload = JSON.stringify(arg);\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", setPayload, {\n once: true,\n });\n } else {\n setPayload();\n }\n }) as (arg: typeof payload) => void;\n await ctx.addInitScript(initPayload, payload);\n\n const newPage = await ctx.newPage();\n await newPage.goto(toDataUrl(\"<html><body>child</body></html>\"), {\n waitUntil: \"load\",\n });\n\n const observed = await newPage.evaluate(() => {\n const raw = document.documentElement.dataset.initPayload;\n return raw ? JSON.parse(raw) : undefined;\n });\n expect(observed).toEqual(payload);\n });\n\n test(\"applies script to newPage(url) on initial document\", async () => {\n const payload = { marker: \"newPageUrl\" };\n\n await ctx.addInitScript((arg) => {\n function setPayload(): void {\n const root = document.documentElement;\n if (!root) return;\n root.dataset.initPayload = JSON.stringify(arg);\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", setPayload, {\n once: true,\n });\n } else {\n setPayload();\n }\n }, payload);\n\n const newPage = await ctx.newPage(\n toDataUrl(\"<html><body>new page</body></html>\"),\n );\n await newPage.waitForLoadState(\"load\");\n\n const observed = await newPage.evaluate(() => {\n const raw = document.documentElement.dataset.initPayload;\n return raw ? JSON.parse(raw) : undefined;\n });\n expect(observed).toEqual(payload);\n });\n\n test(\"applies script to pages opened via link clicks\", async () => {\n const payload = { marker: \"linkClick\" };\n\n await ctx.addInitScript((arg) => {\n function setPayload(): void {\n const root = document.documentElement;\n if (!root) return;\n root.dataset.initPayload = JSON.stringify(arg);\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", setPayload, {\n once: true,\n });\n } else {\n setPayload();\n }\n }, payload);\n\n const popupUrl = \"https://example.com/\";\n const openerHtml =\n \"<!DOCTYPE html>\" +\n \"<html><body>\" +\n '<a id=\"open\" target=\"_blank\" href=\"' +\n popupUrl +\n '\">open</a>' +\n \"</body></html>\";\n\n const opener = await ctx.awaitActivePage();\n await opener.goto(toDataUrl(openerHtml), { waitUntil: \"load\" });\n const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));\n await opener.locator(\"#open\").click();\n\n const popup = await waitForPopupPage(ctx, knownTargetIds);\n\n await popup.waitForLoadState(\"load\");\n\n const observed = await popup.evaluate(() => {\n const raw = document.documentElement.dataset.initPayload;\n return raw ? JSON.parse(raw) : undefined;\n });\n expect(observed).toEqual(payload);\n\n await popup.reload({ waitUntil: \"load\" });\n const observedAfterReload = await popup.evaluate(() => {\n const raw = document.documentElement.dataset.initPayload;\n return raw ? JSON.parse(raw) : undefined;\n });\n expect(observedAfterReload).toEqual(payload);\n });\n\n test(\"applies script to in-process popup\", async () => {\n await ctx.addInitScript(() => {\n (window as unknown as { __injected?: number }).__injected = 123;\n });\n\n const opener = await ctx.awaitActivePage();\n const openerHtml =\n \"<!DOCTYPE html>\" +\n \"<html><body>\" +\n '<a id=\"open\" target=\"_blank\" href=\"about:blank\">open</a>' +\n \"</body></html>\";\n await opener.goto(toDataUrl(openerHtml), { waitUntil: \"load\" });\n const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));\n await opener.locator(\"#open\").click();\n\n const popup = await waitForPopupPage(ctx, knownTargetIds);\n await popup.waitForLoadState(\"load\");\n const injected = await popup.evaluate(() => {\n return (window as unknown as { __injected?: number }).__injected;\n });\n expect(injected).toBe(123);\n });\n\n test(\"applies script to cross-process popup and survives reload\", async () => {\n await ctx.addInitScript(() => {\n (window as unknown as { __injected?: number }).__injected = 123;\n });\n\n const opener = await ctx.awaitActivePage();\n const openerHtml =\n \"<!DOCTYPE html>\" +\n \"<html><body>\" +\n '<a id=\"open\" target=\"_blank\" href=\"https://example.com/\">open</a>' +\n \"</body></html>\";\n await opener.goto(toDataUrl(openerHtml), {\n waitUntil: \"load\",\n });\n const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));\n await opener.locator(\"#open\").click();\n\n const popup = await waitForPopupPage(ctx, knownTargetIds);\n await popup.waitForLoadState(\"load\");\n\n const injected = await popup.evaluate(() => {\n return (window as unknown as { __injected?: number }).__injected;\n });\n expect(injected).toBe(123);\n\n await popup.reload({ waitUntil: \"load\" });\n const injectedAfterReload = await popup.evaluate(() => {\n return (window as unknown as { __injected?: number }).__injected;\n });\n expect(injectedAfterReload).toBe(123);\n });\n\n test(\"applies script to cross-process popup opened via window.open and survives reload\", async () => {\n await ctx.addInitScript(() => {\n (window as unknown as { __injected?: number }).__injected = 789;\n });\n\n const opener = await ctx.awaitActivePage();\n await opener.goto(\"about:blank\", { waitUntil: \"load\" });\n await opener.mainFrame().evaluate(() => {\n const button = document.createElement(\"button\");\n button.id = \"open-via-window-open\";\n button.textContent = \"open popup\";\n button.addEventListener(\"click\", () => {\n window.open(\"https://example.com/\", \"_blank\");\n });\n document.body.appendChild(button);\n });\n\n const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));\n await opener.locator(\"#open-via-window-open\").click();\n\n const popup = await waitForPopupPage(ctx, knownTargetIds);\n await popup.waitForLoadState(\"load\");\n\n const injected = await popup.evaluate(() => {\n return (window as unknown as { __injected?: number }).__injected;\n });\n expect(injected).toBe(789);\n\n await popup.reload({ waitUntil: \"load\" });\n const injectedAfterReload = await popup.evaluate(() => {\n return (window as unknown as { __injected?: number }).__injected;\n });\n expect(injectedAfterReload).toBe(789);\n });\n\n test(\"context.addInitScript installs a function callable from page.evaluate\", async () => {\n const page = await ctx.awaitActivePage();\n\n await ctx.addInitScript(() => {\n // installed before any navigation\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n window.sayHelloFromStagehand = () => \"hello from stagehand\";\n });\n\n await page.goto(\"https://example.com\", { waitUntil: \"domcontentloaded\" });\n\n const result = await page.evaluate(() => {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n return window.sayHelloFromStagehand();\n });\n\n expect(result).toBe(\"hello from stagehand\");\n });\n});\n"]}
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ const v3_js_1 = require("../../lib/v3/v3.js");
5
+ const v3_config_js_1 = require("./v3.config.js");
6
+ const DEFAULT_INIT_SCRIPT_DELAY_MS = 250;
7
+ const INIT_SCRIPT_DELAY_MS = (() => {
8
+ const rawValue = process.env.IFRAME_INIT_SCRIPT_SEND_DELAY_MS;
9
+ if (rawValue === undefined)
10
+ return DEFAULT_INIT_SCRIPT_DELAY_MS;
11
+ const parsed = Number(rawValue);
12
+ if (!Number.isFinite(parsed) || parsed <= 0)
13
+ return DEFAULT_INIT_SCRIPT_DELAY_MS;
14
+ return parsed;
15
+ })();
16
+ const POPUP_TIMEOUT_MS = 20_000;
17
+ const RACE_INIT_SCRIPT_SENTINEL = "__stagehand_init_script_race_sentinel__";
18
+ const INIT_SCRIPT_MARKER_KEY = "__stagehand_init_script_loaded__";
19
+ const POPUP_URL = "https://example.com/";
20
+ const POPUP_IFRAME_URL = "https://example.org/";
21
+ const INIT_SCRIPT_SOURCE = `
22
+ (() => {
23
+ /* ${RACE_INIT_SCRIPT_SENTINEL} */
24
+ window["${INIT_SCRIPT_MARKER_KEY}"] = true;
25
+ })();
26
+ `;
27
+ async function closeAllPages(ctx) {
28
+ const pages = ctx.pages();
29
+ await Promise.allSettled(pages.map((page) => page.close()));
30
+ }
31
+ async function waitForPopupPage(ctx, knownTargetIds, timeoutMs = POPUP_TIMEOUT_MS) {
32
+ const deadline = Date.now() + timeoutMs;
33
+ while (Date.now() < deadline) {
34
+ const popup = ctx
35
+ .pages()
36
+ .find((candidate) => !knownTargetIds.has(candidate.targetId()));
37
+ if (popup)
38
+ return popup;
39
+ try {
40
+ const active = await ctx.awaitActivePage(500);
41
+ if (!knownTargetIds.has(active.targetId()))
42
+ return active;
43
+ }
44
+ catch {
45
+ // keep polling
46
+ }
47
+ await new Promise((resolve) => setTimeout(resolve, 50));
48
+ }
49
+ throw new Error("Timed out waiting for popup page");
50
+ }
51
+ async function waitForChildFrame(page, expectedUrl, timeoutMs = POPUP_TIMEOUT_MS) {
52
+ const mainFrameId = page.mainFrame().frameId;
53
+ const deadline = Date.now() + timeoutMs;
54
+ while (Date.now() < deadline) {
55
+ for (const frame of page.frames()) {
56
+ if (frame.frameId === mainFrameId)
57
+ continue;
58
+ try {
59
+ const href = await frame.evaluate(() => window.location.href);
60
+ if (href === expectedUrl)
61
+ return frame;
62
+ }
63
+ catch {
64
+ // frame context may not be ready yet
65
+ }
66
+ }
67
+ await new Promise((resolve) => setTimeout(resolve, 50));
68
+ }
69
+ throw new Error("Timed out waiting for child frame");
70
+ }
71
+ async function prepareTargetBlankPopupOpener(opener) {
72
+ await opener.goto("about:blank", { waitUntil: "domcontentloaded" });
73
+ await opener.mainFrame().evaluate((popupUrl) => {
74
+ const link = document.createElement("a");
75
+ link.id = "open-popup";
76
+ link.target = "_blank";
77
+ link.href = popupUrl;
78
+ link.textContent = "open popup";
79
+ document.body.appendChild(link);
80
+ }, POPUP_URL);
81
+ }
82
+ async function prepareWindowOpenPopupOpener(opener) {
83
+ await opener.goto("about:blank", { waitUntil: "domcontentloaded" });
84
+ await opener.mainFrame().evaluate((popupUrl) => {
85
+ const button = document.createElement("button");
86
+ button.id = "open-popup";
87
+ button.textContent = "open popup";
88
+ button.addEventListener("click", () => {
89
+ window.open(popupUrl, "_blank");
90
+ });
91
+ document.body.appendChild(button);
92
+ }, POPUP_URL);
93
+ }
94
+ const POPUP_TRIGGER_CASES = [
95
+ {
96
+ name: 'target="_blank" link click',
97
+ prepare: prepareTargetBlankPopupOpener,
98
+ },
99
+ {
100
+ name: "window.open from click handler",
101
+ prepare: prepareWindowOpenPopupOpener,
102
+ },
103
+ ];
104
+ test_1.test.describe("repro: popup iframe addInitScript race under delayed CDP send", () => {
105
+ test_1.test.describe.configure({ mode: "serial" });
106
+ let restoreSend;
107
+ let v3;
108
+ let ctx;
109
+ let sequence = 0;
110
+ let records = [];
111
+ test_1.test.beforeAll(async () => {
112
+ v3 = new v3_js_1.V3(v3_config_js_1.v3TestConfig);
113
+ await v3.init();
114
+ ctx = v3.context;
115
+ const conn = ctx.conn;
116
+ if (!conn || typeof conn._sendViaSession !== "function") {
117
+ throw new Error("Unable to access CDP connection for race repro patch");
118
+ }
119
+ const originalSendViaSession = conn._sendViaSession.bind(conn);
120
+ conn._sendViaSession = function patchedSendViaSession(sessionId, method, params) {
121
+ const source = typeof params?.source === "string"
122
+ ? params.source
123
+ : "";
124
+ const isRaceInitScript = method === "Page.addScriptToEvaluateOnNewDocument" &&
125
+ source.includes(RACE_INIT_SCRIPT_SENTINEL);
126
+ const sendNow = () => {
127
+ records.push({
128
+ sequence: ++sequence,
129
+ sessionId,
130
+ method,
131
+ isRaceInitScript,
132
+ });
133
+ return originalSendViaSession(sessionId, method, params);
134
+ };
135
+ if (isRaceInitScript && INIT_SCRIPT_DELAY_MS > 0) {
136
+ return new Promise((resolve, reject) => {
137
+ setTimeout(() => {
138
+ sendNow().then(resolve, reject);
139
+ }, INIT_SCRIPT_DELAY_MS);
140
+ });
141
+ }
142
+ return sendNow();
143
+ };
144
+ restoreSend = () => {
145
+ conn._sendViaSession = originalSendViaSession;
146
+ };
147
+ await ctx.addInitScript(INIT_SCRIPT_SOURCE);
148
+ });
149
+ test_1.test.afterAll(async () => {
150
+ restoreSend?.();
151
+ await v3?.close?.().catch(() => { });
152
+ });
153
+ test_1.test.beforeEach(async () => {
154
+ records = [];
155
+ sequence = 0;
156
+ if (!ctx)
157
+ return;
158
+ await closeAllPages(ctx);
159
+ });
160
+ test_1.test.afterEach(async () => {
161
+ if (!ctx)
162
+ return;
163
+ await closeAllPages(ctx);
164
+ });
165
+ for (const popupCase of POPUP_TRIGGER_CASES) {
166
+ (0, test_1.test)(`should send addScript before resume for popup targets via ${popupCase.name}`, async () => {
167
+ if (!ctx)
168
+ throw new Error("Context not initialized");
169
+ const opener = await ctx.newPage();
170
+ await popupCase.prepare(opener);
171
+ const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));
172
+ const knownSessionIds = new Set(records.map((record) => record.sessionId));
173
+ await opener.locator("#open-popup").click();
174
+ const popup = await waitForPopupPage(ctx, knownTargetIds);
175
+ await popup.waitForLoadState("load", POPUP_TIMEOUT_MS);
176
+ await popup.mainFrame().evaluate((iframeUrl) => {
177
+ const iframe = document.createElement("iframe");
178
+ iframe.id = "race-child-iframe";
179
+ iframe.src = iframeUrl;
180
+ document.body.appendChild(iframe);
181
+ }, POPUP_IFRAME_URL);
182
+ const iframe = await waitForChildFrame(popup, POPUP_IFRAME_URL, POPUP_TIMEOUT_MS);
183
+ const popupInitScriptMarker = await popup.mainFrame().evaluate((key) => {
184
+ return Boolean(Reflect.get(window, key));
185
+ }, INIT_SCRIPT_MARKER_KEY);
186
+ const iframeInitScriptMarker = await iframe.evaluate((key) => {
187
+ return Boolean(Reflect.get(window, key));
188
+ }, INIT_SCRIPT_MARKER_KEY);
189
+ const perSession = new Map();
190
+ for (const record of records) {
191
+ if (knownSessionIds.has(record.sessionId))
192
+ continue;
193
+ const entry = perSession.get(record.sessionId) ?? {};
194
+ if (record.isRaceInitScript &&
195
+ entry.raceInitScriptSequence === undefined) {
196
+ entry.raceInitScriptSequence = record.sequence;
197
+ }
198
+ if (record.method === "Runtime.runIfWaitingForDebugger" &&
199
+ entry.resumeSequence === undefined) {
200
+ entry.resumeSequence = record.sequence;
201
+ }
202
+ perSession.set(record.sessionId, entry);
203
+ }
204
+ const comparableSessions = [...perSession.entries()]
205
+ .map(([sessionId, entry]) => ({ sessionId, ...entry }))
206
+ .filter((entry) => entry.raceInitScriptSequence !== undefined &&
207
+ entry.resumeSequence !== undefined);
208
+ (0, test_1.expect)(comparableSessions.length).toBeGreaterThan(0);
209
+ const orderingViolations = comparableSessions.filter((entry) => {
210
+ return (entry.raceInitScriptSequence >
211
+ entry.resumeSequence);
212
+ });
213
+ (0, test_1.expect)(orderingViolations, `Expected addScript before resume for ${popupCase.name}. initScriptDelayMs=${INIT_SCRIPT_DELAY_MS}; comparableSessions=${JSON.stringify(comparableSessions)}`).toEqual([]);
214
+ (0, test_1.expect)(popupInitScriptMarker).toBe(true);
215
+ (0, test_1.expect)(iframeInitScriptMarker).toBe(true);
216
+ });
217
+ }
218
+ });
219
+ //# sourceMappingURL=iframe-ctx-addInitScript-race.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iframe-ctx-addInitScript-race.spec.js","sourceRoot":"","sources":["../../../../tests/integration/iframe-ctx-addInitScript-race.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"@playwright/test\";\nimport { V3 } from \"../../lib/v3/v3.js\";\nimport { v3TestConfig } from \"./v3.config.js\";\nimport type { V3Context } from \"../../lib/v3/understudy/context.js\";\nimport type { Page } from \"../../lib/v3/understudy/page.js\";\n\nconst DEFAULT_INIT_SCRIPT_DELAY_MS = 250;\nconst INIT_SCRIPT_DELAY_MS = (() => {\n const rawValue = process.env.IFRAME_INIT_SCRIPT_SEND_DELAY_MS;\n if (rawValue === undefined) return DEFAULT_INIT_SCRIPT_DELAY_MS;\n const parsed = Number(rawValue);\n if (!Number.isFinite(parsed) || parsed <= 0)\n return DEFAULT_INIT_SCRIPT_DELAY_MS;\n return parsed;\n})();\n\nconst POPUP_TIMEOUT_MS = 20_000;\nconst RACE_INIT_SCRIPT_SENTINEL = \"__stagehand_init_script_race_sentinel__\";\nconst INIT_SCRIPT_MARKER_KEY = \"__stagehand_init_script_loaded__\";\nconst POPUP_URL = \"https://example.com/\";\nconst POPUP_IFRAME_URL = \"https://example.org/\";\n\nconst INIT_SCRIPT_SOURCE = `\n(() => {\n /* ${RACE_INIT_SCRIPT_SENTINEL} */\n window[\"${INIT_SCRIPT_MARKER_KEY}\"] = true;\n})();\n`;\n\ntype PatchedConn = {\n _sendViaSession: (\n sessionId: string,\n method: string,\n params?: object,\n ) => Promise<unknown>;\n};\n\ntype SessionCommandRecord = {\n sequence: number;\n sessionId: string;\n method: string;\n isRaceInitScript: boolean;\n};\n\ntype PopupTriggerCase = {\n name: string;\n prepare: (opener: Page) => Promise<void>;\n};\n\nasync function closeAllPages(ctx: V3Context): Promise<void> {\n const pages = ctx.pages();\n await Promise.allSettled(pages.map((page) => page.close()));\n}\n\nasync function waitForPopupPage(\n ctx: V3Context,\n knownTargetIds: Set<string>,\n timeoutMs = POPUP_TIMEOUT_MS,\n): Promise<Page> {\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const popup = ctx\n .pages()\n .find((candidate) => !knownTargetIds.has(candidate.targetId()));\n if (popup) return popup;\n try {\n const active = await ctx.awaitActivePage(500);\n if (!knownTargetIds.has(active.targetId())) return active;\n } catch {\n // keep polling\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n throw new Error(\"Timed out waiting for popup page\");\n}\n\nasync function waitForChildFrame(\n page: Page,\n expectedUrl: string,\n timeoutMs = POPUP_TIMEOUT_MS,\n): Promise<ReturnType<Page[\"frames\"]>[number]> {\n const mainFrameId = page.mainFrame().frameId;\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n for (const frame of page.frames()) {\n if (frame.frameId === mainFrameId) continue;\n try {\n const href = await frame.evaluate(() => window.location.href);\n if (href === expectedUrl) return frame;\n } catch {\n // frame context may not be ready yet\n }\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n throw new Error(\"Timed out waiting for child frame\");\n}\n\nasync function prepareTargetBlankPopupOpener(opener: Page): Promise<void> {\n await opener.goto(\"about:blank\", { waitUntil: \"domcontentloaded\" });\n await opener.mainFrame().evaluate((popupUrl) => {\n const link = document.createElement(\"a\");\n link.id = \"open-popup\";\n link.target = \"_blank\";\n link.href = popupUrl;\n link.textContent = \"open popup\";\n document.body.appendChild(link);\n }, POPUP_URL);\n}\n\nasync function prepareWindowOpenPopupOpener(opener: Page): Promise<void> {\n await opener.goto(\"about:blank\", { waitUntil: \"domcontentloaded\" });\n await opener.mainFrame().evaluate((popupUrl) => {\n const button = document.createElement(\"button\");\n button.id = \"open-popup\";\n button.textContent = \"open popup\";\n button.addEventListener(\"click\", () => {\n window.open(popupUrl, \"_blank\");\n });\n document.body.appendChild(button);\n }, POPUP_URL);\n}\n\nconst POPUP_TRIGGER_CASES: PopupTriggerCase[] = [\n {\n name: 'target=\"_blank\" link click',\n prepare: prepareTargetBlankPopupOpener,\n },\n {\n name: \"window.open from click handler\",\n prepare: prepareWindowOpenPopupOpener,\n },\n];\n\ntest.describe(\"repro: popup iframe addInitScript race under delayed CDP send\", () => {\n test.describe.configure({ mode: \"serial\" });\n\n let restoreSend: (() => void) | undefined;\n let v3: V3 | undefined;\n let ctx: V3Context | undefined;\n let sequence = 0;\n let records: SessionCommandRecord[] = [];\n\n test.beforeAll(async () => {\n v3 = new V3(v3TestConfig);\n await v3.init();\n ctx = v3.context;\n\n const conn = (ctx as unknown as { conn?: PatchedConn }).conn;\n if (!conn || typeof conn._sendViaSession !== \"function\") {\n throw new Error(\"Unable to access CDP connection for race repro patch\");\n }\n\n const originalSendViaSession = conn._sendViaSession.bind(conn);\n conn._sendViaSession = function patchedSendViaSession(\n sessionId: string,\n method: string,\n params?: object,\n ) {\n const source =\n typeof (params as { source?: unknown } | undefined)?.source === \"string\"\n ? (params as { source: string }).source\n : \"\";\n const isRaceInitScript =\n method === \"Page.addScriptToEvaluateOnNewDocument\" &&\n source.includes(RACE_INIT_SCRIPT_SENTINEL);\n\n const sendNow = () => {\n records.push({\n sequence: ++sequence,\n sessionId,\n method,\n isRaceInitScript,\n });\n return originalSendViaSession(sessionId, method, params);\n };\n\n if (isRaceInitScript && INIT_SCRIPT_DELAY_MS > 0) {\n return new Promise((resolve, reject) => {\n setTimeout(() => {\n sendNow().then(resolve, reject);\n }, INIT_SCRIPT_DELAY_MS);\n });\n }\n\n return sendNow();\n };\n\n restoreSend = () => {\n conn._sendViaSession = originalSendViaSession;\n };\n\n await ctx.addInitScript(INIT_SCRIPT_SOURCE);\n });\n\n test.afterAll(async () => {\n restoreSend?.();\n await v3?.close?.().catch(() => {});\n });\n\n test.beforeEach(async () => {\n records = [];\n sequence = 0;\n if (!ctx) return;\n await closeAllPages(ctx);\n });\n\n test.afterEach(async () => {\n if (!ctx) return;\n await closeAllPages(ctx);\n });\n\n for (const popupCase of POPUP_TRIGGER_CASES) {\n test(`should send addScript before resume for popup targets via ${popupCase.name}`, async () => {\n if (!ctx) throw new Error(\"Context not initialized\");\n\n const opener = await ctx.newPage();\n await popupCase.prepare(opener);\n\n const knownTargetIds = new Set(ctx.pages().map((p) => p.targetId()));\n const knownSessionIds = new Set(\n records.map((record) => record.sessionId),\n );\n\n await opener.locator(\"#open-popup\").click();\n\n const popup = await waitForPopupPage(ctx, knownTargetIds);\n await popup.waitForLoadState(\"load\", POPUP_TIMEOUT_MS);\n await popup.mainFrame().evaluate((iframeUrl) => {\n const iframe = document.createElement(\"iframe\");\n iframe.id = \"race-child-iframe\";\n iframe.src = iframeUrl;\n document.body.appendChild(iframe);\n }, POPUP_IFRAME_URL);\n const iframe = await waitForChildFrame(\n popup,\n POPUP_IFRAME_URL,\n POPUP_TIMEOUT_MS,\n );\n\n const popupInitScriptMarker = await popup.mainFrame().evaluate((key) => {\n return Boolean(Reflect.get(window, key));\n }, INIT_SCRIPT_MARKER_KEY);\n const iframeInitScriptMarker = await iframe.evaluate((key) => {\n return Boolean(Reflect.get(window, key));\n }, INIT_SCRIPT_MARKER_KEY);\n\n const perSession = new Map<\n string,\n {\n raceInitScriptSequence?: number;\n resumeSequence?: number;\n }\n >();\n\n for (const record of records) {\n if (knownSessionIds.has(record.sessionId)) continue;\n const entry = perSession.get(record.sessionId) ?? {};\n if (\n record.isRaceInitScript &&\n entry.raceInitScriptSequence === undefined\n ) {\n entry.raceInitScriptSequence = record.sequence;\n }\n if (\n record.method === \"Runtime.runIfWaitingForDebugger\" &&\n entry.resumeSequence === undefined\n ) {\n entry.resumeSequence = record.sequence;\n }\n perSession.set(record.sessionId, entry);\n }\n\n const comparableSessions = [...perSession.entries()]\n .map(([sessionId, entry]) => ({ sessionId, ...entry }))\n .filter(\n (entry) =>\n entry.raceInitScriptSequence !== undefined &&\n entry.resumeSequence !== undefined,\n );\n expect(comparableSessions.length).toBeGreaterThan(0);\n\n const orderingViolations = comparableSessions.filter((entry) => {\n return (\n (entry.raceInitScriptSequence as number) >\n (entry.resumeSequence as number)\n );\n });\n\n expect(\n orderingViolations,\n `Expected addScript before resume for ${popupCase.name}. initScriptDelayMs=${INIT_SCRIPT_DELAY_MS}; comparableSessions=${JSON.stringify(comparableSessions)}`,\n ).toEqual([]);\n expect(popupInitScriptMarker).toBe(true);\n expect(iframeInitScriptMarker).toBe(true);\n });\n }\n});\n"]}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ const v3_js_1 = require("../../lib/v3/v3.js");
5
+ const v3_config_js_1 = require("./v3.config.js");
6
+ const testUtils_js_1 = require("./testUtils.js");
7
+ const TEST_URL = "https://browserbase.github.io/stagehand-eval-sites/sites/example/";
8
+ test_1.test.describe("page.setExtraHTTPHeaders", () => {
9
+ let v3;
10
+ test_1.test.beforeEach(async () => {
11
+ v3 = new v3_js_1.V3(v3_config_js_1.v3TestConfig);
12
+ await v3.init();
13
+ });
14
+ test_1.test.afterEach(async () => {
15
+ await (0, testUtils_js_1.closeV3)(v3);
16
+ });
17
+ (0, test_1.test)("applies headers to navigation requests", async () => {
18
+ const ctx = v3.context;
19
+ const page = await ctx.awaitActivePage();
20
+ await page.setExtraHTTPHeaders({ "x-page-header": "from-page" });
21
+ const internal = page;
22
+ await internal.mainSession.send("Network.enable");
23
+ const requestPromise = new Promise((resolve, reject) => {
24
+ const timeout = setTimeout(() => {
25
+ internal.mainSession.off("Network.requestWillBeSent", handler);
26
+ reject(new Error("Timed out waiting for request"));
27
+ }, 5000);
28
+ const handler = (evt) => {
29
+ if (evt.type !== "Document")
30
+ return;
31
+ const url = String(evt.request?.url ?? "");
32
+ if (!url.startsWith(TEST_URL))
33
+ return;
34
+ clearTimeout(timeout);
35
+ internal.mainSession.off("Network.requestWillBeSent", handler);
36
+ resolve(evt);
37
+ };
38
+ internal.mainSession.on("Network.requestWillBeSent", handler);
39
+ });
40
+ await page.goto(TEST_URL, { waitUntil: "domcontentloaded" });
41
+ const request = await requestPromise;
42
+ const headers = Object.fromEntries(Object.entries(request.request.headers ?? {}).map(([key, value]) => [
43
+ key.toLowerCase(),
44
+ String(value),
45
+ ]));
46
+ (0, test_1.expect)(headers["x-page-header"]).toBe("from-page");
47
+ });
48
+ (0, test_1.test)("updated headers replace previous ones", async () => {
49
+ const ctx = v3.context;
50
+ const page = await ctx.awaitActivePage();
51
+ const internal = page;
52
+ await internal.mainSession.send("Network.enable");
53
+ // Set initial headers and navigate
54
+ await page.setExtraHTTPHeaders({ "x-first": "yes" });
55
+ await page.goto(TEST_URL, { waitUntil: "domcontentloaded" });
56
+ // Update headers
57
+ await page.setExtraHTTPHeaders({ "x-second": "yes" });
58
+ const requestPromise = new Promise((resolve, reject) => {
59
+ const timeout = setTimeout(() => {
60
+ internal.mainSession.off("Network.requestWillBeSent", handler);
61
+ reject(new Error("Timed out waiting for request"));
62
+ }, 5000);
63
+ const handler = (evt) => {
64
+ if (evt.type !== "Document")
65
+ return;
66
+ const url = String(evt.request?.url ?? "");
67
+ if (!url.startsWith(TEST_URL))
68
+ return;
69
+ clearTimeout(timeout);
70
+ internal.mainSession.off("Network.requestWillBeSent", handler);
71
+ resolve(evt);
72
+ };
73
+ internal.mainSession.on("Network.requestWillBeSent", handler);
74
+ });
75
+ await page.goto(TEST_URL, { waitUntil: "domcontentloaded" });
76
+ const request = await requestPromise;
77
+ const headers = Object.fromEntries(Object.entries(request.request.headers ?? {}).map(([key, value]) => [
78
+ key.toLowerCase(),
79
+ String(value),
80
+ ]));
81
+ (0, test_1.expect)(headers["x-second"]).toBe("yes");
82
+ (0, test_1.expect)(headers["x-first"]).toBeUndefined();
83
+ });
84
+ });
85
+ //# sourceMappingURL=page-extra-http-headers.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-extra-http-headers.spec.js","sourceRoot":"","sources":["../../../../tests/integration/page-extra-http-headers.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"@playwright/test\";\nimport type { Protocol } from \"devtools-protocol\";\nimport { V3 } from \"../../lib/v3/v3.js\";\nimport { v3TestConfig } from \"./v3.config.js\";\nimport { closeV3 } from \"./testUtils.js\";\n\nconst TEST_URL =\n \"https://browserbase.github.io/stagehand-eval-sites/sites/example/\";\n\ntest.describe(\"page.setExtraHTTPHeaders\", () => {\n let v3: V3;\n\n test.beforeEach(async () => {\n v3 = new V3(v3TestConfig);\n await v3.init();\n });\n\n test.afterEach(async () => {\n await closeV3(v3);\n });\n\n test(\"applies headers to navigation requests\", async () => {\n const ctx = v3.context;\n const page = await ctx.awaitActivePage();\n\n await page.setExtraHTTPHeaders({ \"x-page-header\": \"from-page\" });\n\n const internal = page as unknown as {\n mainSession: {\n send: (method: string, params?: unknown) => Promise<unknown>;\n on: (event: string, handler: (params: unknown) => void) => void;\n off: (event: string, handler: (params: unknown) => void) => void;\n };\n };\n\n await internal.mainSession.send(\"Network.enable\");\n\n const requestPromise = new Promise<Protocol.Network.RequestWillBeSentEvent>(\n (resolve, reject) => {\n const timeout = setTimeout(() => {\n internal.mainSession.off(\"Network.requestWillBeSent\", handler);\n reject(new Error(\"Timed out waiting for request\"));\n }, 5000);\n\n const handler = (evt: Protocol.Network.RequestWillBeSentEvent) => {\n if (evt.type !== \"Document\") return;\n const url = String(evt.request?.url ?? \"\");\n if (!url.startsWith(TEST_URL)) return;\n clearTimeout(timeout);\n internal.mainSession.off(\"Network.requestWillBeSent\", handler);\n resolve(evt);\n };\n\n internal.mainSession.on(\"Network.requestWillBeSent\", handler);\n },\n );\n\n await page.goto(TEST_URL, { waitUntil: \"domcontentloaded\" });\n\n const request = await requestPromise;\n const headers = Object.fromEntries(\n Object.entries(request.request.headers ?? {}).map(([key, value]) => [\n key.toLowerCase(),\n String(value),\n ]),\n );\n\n expect(headers[\"x-page-header\"]).toBe(\"from-page\");\n });\n\n test(\"updated headers replace previous ones\", async () => {\n const ctx = v3.context;\n const page = await ctx.awaitActivePage();\n\n const internal = page as unknown as {\n mainSession: {\n send: (method: string, params?: unknown) => Promise<unknown>;\n on: (event: string, handler: (params: unknown) => void) => void;\n off: (event: string, handler: (params: unknown) => void) => void;\n };\n };\n\n await internal.mainSession.send(\"Network.enable\");\n\n // Set initial headers and navigate\n await page.setExtraHTTPHeaders({ \"x-first\": \"yes\" });\n await page.goto(TEST_URL, { waitUntil: \"domcontentloaded\" });\n\n // Update headers\n await page.setExtraHTTPHeaders({ \"x-second\": \"yes\" });\n\n const requestPromise = new Promise<Protocol.Network.RequestWillBeSentEvent>(\n (resolve, reject) => {\n const timeout = setTimeout(() => {\n internal.mainSession.off(\"Network.requestWillBeSent\", handler);\n reject(new Error(\"Timed out waiting for request\"));\n }, 5000);\n\n const handler = (evt: Protocol.Network.RequestWillBeSentEvent) => {\n if (evt.type !== \"Document\") return;\n const url = String(evt.request?.url ?? \"\");\n if (!url.startsWith(TEST_URL)) return;\n clearTimeout(timeout);\n internal.mainSession.off(\"Network.requestWillBeSent\", handler);\n resolve(evt);\n };\n\n internal.mainSession.on(\"Network.requestWillBeSent\", handler);\n },\n );\n\n await page.goto(TEST_URL, { waitUntil: \"domcontentloaded\" });\n\n const request = await requestPromise;\n const headers = Object.fromEntries(\n Object.entries(request.request.headers ?? {}).map(([key, value]) => [\n key.toLowerCase(),\n String(value),\n ]),\n );\n\n expect(headers[\"x-second\"]).toBe(\"yes\");\n expect(headers[\"x-first\"]).toBeUndefined();\n });\n});\n"]}
@@ -81,7 +81,7 @@ test_1.test.describe("Page.screenshot options", () => {
81
81
  return Buffer.from("late");
82
82
  };
83
83
  try {
84
- await (0, test_1.expect)(page.screenshot({ timeout: 10 })).rejects.toThrow(/timeout/);
84
+ await (0, test_1.expect)(page.screenshot({ timeout: 10 })).rejects.toThrow(/timed out|timeout/i);
85
85
  }
86
86
  finally {
87
87
  mainFrame.screenshot = originalScreenshot;