tabwright 1.0.0 → 2.0.0

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 (568) hide show
  1. package/dist/agent-skills/tabwright/SKILL.md +3 -0
  2. package/dist/aria-snapshot.d.ts +0 -1
  3. package/dist/aria-snapshot.js +9 -1
  4. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts +0 -1
  5. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js +0 -1
  6. package/dist/bippy.js +5 -5
  7. package/dist/browser-config.d.ts +0 -1
  8. package/dist/browser-config.js +0 -1
  9. package/dist/browser-install.d.ts +0 -1
  10. package/dist/browser-install.js +0 -1
  11. package/dist/browser-launch.d.ts +0 -1
  12. package/dist/browser-launch.js +0 -1
  13. package/dist/capability-agent-skill.d.ts +0 -1
  14. package/dist/capability-agent-skill.js +0 -1
  15. package/dist/capability-auth-state.d.ts +0 -1
  16. package/dist/capability-auth-state.js +0 -1
  17. package/dist/capability-auth.d.ts +0 -1
  18. package/dist/capability-auth.js +0 -1
  19. package/dist/capability-options.d.ts +0 -1
  20. package/dist/capability-options.js +0 -1
  21. package/dist/capability-package.d.ts +0 -1
  22. package/dist/capability-package.js +47 -2
  23. package/dist/capability-registry.d.ts +0 -1
  24. package/dist/capability-registry.js +0 -1
  25. package/dist/capability-runner.d.ts +0 -1
  26. package/dist/capability-runner.js +0 -1
  27. package/dist/capability-studio.d.ts +0 -1
  28. package/dist/capability-studio.js +0 -1
  29. package/dist/cdp-log.d.ts +0 -1
  30. package/dist/cdp-log.js +0 -1
  31. package/dist/cdp-relay.d.ts +0 -1
  32. package/dist/cdp-relay.js +0 -1
  33. package/dist/cdp-session.d.ts +0 -1
  34. package/dist/cdp-session.js +0 -1
  35. package/dist/cdp-types.d.ts +0 -1
  36. package/dist/cdp-types.js +0 -1
  37. package/dist/chrome-discovery.d.ts +0 -1
  38. package/dist/chrome-discovery.js +0 -1
  39. package/dist/clean-html.d.ts +0 -1
  40. package/dist/clean-html.js +0 -1
  41. package/dist/cli.d.ts +0 -1
  42. package/dist/cli.js +10 -56
  43. package/dist/cloud-client.d.ts +0 -1
  44. package/dist/cloud-client.js +0 -1
  45. package/dist/create-logger.d.ts +0 -1
  46. package/dist/create-logger.js +0 -1
  47. package/dist/debugger-examples-types.d.ts +0 -1
  48. package/dist/debugger-examples-types.js +0 -1
  49. package/dist/debugger-examples.d.ts +0 -1
  50. package/dist/debugger-examples.js +0 -1
  51. package/dist/debugger.d.ts +0 -1
  52. package/dist/debugger.js +0 -1
  53. package/dist/diff-utils.d.ts +0 -1
  54. package/dist/diff-utils.js +0 -1
  55. package/dist/doctor.d.ts +0 -1
  56. package/dist/doctor.js +5 -7
  57. package/dist/editor-examples.d.ts +0 -1
  58. package/dist/editor-examples.js +0 -1
  59. package/dist/editor.d.ts +0 -1
  60. package/dist/editor.js +0 -1
  61. package/dist/env-compat.d.ts +0 -1
  62. package/dist/env-compat.js +0 -1
  63. package/dist/executor.d.ts +0 -1
  64. package/dist/executor.js +1 -2
  65. package/dist/extension/background.js +10 -6
  66. package/dist/extension/manifest.json +1 -1
  67. package/dist/extension/options.js +1 -1
  68. package/dist/extension/rrweb-recorder.js +1 -1
  69. package/dist/ghost-browser.d.ts +0 -1
  70. package/dist/ghost-browser.js +0 -1
  71. package/dist/ghost-cursor-controller.d.ts +0 -1
  72. package/dist/ghost-cursor-controller.js +0 -1
  73. package/dist/ghost-cursor.d.ts +0 -1
  74. package/dist/ghost-cursor.js +0 -1
  75. package/dist/htmlrewrite.d.ts +0 -1
  76. package/dist/htmlrewrite.js +0 -1
  77. package/dist/index.d.ts +0 -1
  78. package/dist/index.js +0 -1
  79. package/dist/kill-port.d.ts +0 -1
  80. package/dist/kill-port.js +0 -1
  81. package/dist/kitty-graphics.d.ts +0 -1
  82. package/dist/kitty-graphics.js +0 -1
  83. package/dist/mcp.d.ts +0 -1
  84. package/dist/mcp.js +0 -1
  85. package/dist/package-paths.d.ts +0 -1
  86. package/dist/package-paths.js +0 -1
  87. package/dist/page-markdown.d.ts +0 -1
  88. package/dist/page-markdown.js +0 -1
  89. package/dist/performance-examples.d.ts +0 -1
  90. package/dist/performance-examples.js +0 -1
  91. package/dist/playwright-import.d.ts +0 -1
  92. package/dist/playwright-import.js +0 -1
  93. package/dist/product-paths.d.ts +0 -1
  94. package/dist/product-paths.js +0 -1
  95. package/dist/protocol.d.ts +0 -1
  96. package/dist/protocol.js +0 -1
  97. package/dist/react-source.d.ts +0 -1
  98. package/dist/react-source.js +0 -1
  99. package/dist/readability.js +4 -4
  100. package/dist/relay-client.d.ts +0 -1
  101. package/dist/relay-client.js +0 -1
  102. package/dist/relay-state.d.ts +0 -1
  103. package/dist/relay-state.js +0 -1
  104. package/dist/replay-ai-index.d.ts +0 -1
  105. package/dist/replay-ai-index.js +0 -1
  106. package/dist/replay-eval.d.ts +0 -1
  107. package/dist/replay-eval.js +0 -1
  108. package/dist/replay-handoff.d.ts +0 -1
  109. package/dist/replay-handoff.js +0 -1
  110. package/dist/replay-workflow-compiler.d.ts +0 -1
  111. package/dist/replay-workflow-compiler.js +0 -1
  112. package/dist/rrweb-recording-relay.d.ts +0 -1
  113. package/dist/rrweb-recording-relay.js +0 -1
  114. package/dist/rrweb-recording.d.ts +0 -1
  115. package/dist/rrweb-recording.js +0 -1
  116. package/dist/scoped-fs.d.ts +0 -1
  117. package/dist/scoped-fs.js +0 -1
  118. package/dist/selector-generator.js +2 -2
  119. package/dist/start-relay-server.d.ts +0 -1
  120. package/dist/start-relay-server.js +0 -1
  121. package/dist/styles-examples.d.ts +0 -1
  122. package/dist/styles-examples.js +0 -1
  123. package/dist/styles.d.ts +0 -1
  124. package/dist/styles.js +0 -1
  125. package/dist/tabwright-agent-skill.d.ts +0 -1
  126. package/dist/tabwright-agent-skill.js +0 -1
  127. package/dist/test-declarations.d.ts +0 -1
  128. package/dist/test-declarations.js +0 -1
  129. package/dist/utils.d.ts +0 -1
  130. package/dist/utils.js +2 -2
  131. package/dist/wait-for-page-load.d.ts +0 -1
  132. package/dist/wait-for-page-load.js +0 -1
  133. package/dist/workflow-capability.d.ts +0 -1
  134. package/dist/workflow-capability.js +0 -1
  135. package/package.json +8 -4
  136. package/readme.md +43 -0
  137. package/src/skill.md +5 -2
  138. package/dist/aria-snapshot.d.ts.map +0 -1
  139. package/dist/aria-snapshot.js.map +0 -1
  140. package/dist/aria-snapshot.test.d.ts +0 -2
  141. package/dist/aria-snapshot.test.d.ts.map +0 -1
  142. package/dist/aria-snapshot.test.js +0 -270
  143. package/dist/aria-snapshot.test.js.map +0 -1
  144. package/dist/aria-snapshot.unit.test.d.ts +0 -2
  145. package/dist/aria-snapshot.unit.test.d.ts.map +0 -1
  146. package/dist/aria-snapshot.unit.test.js +0 -542
  147. package/dist/aria-snapshot.unit.test.js.map +0 -1
  148. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts.map +0 -1
  149. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js.map +0 -1
  150. package/dist/browser-config.d.ts.map +0 -1
  151. package/dist/browser-config.js.map +0 -1
  152. package/dist/browser-install.d.ts.map +0 -1
  153. package/dist/browser-install.js.map +0 -1
  154. package/dist/browser-launch.d.ts.map +0 -1
  155. package/dist/browser-launch.js.map +0 -1
  156. package/dist/builtin-capabilities.d.ts +0 -28
  157. package/dist/builtin-capabilities.d.ts.map +0 -1
  158. package/dist/builtin-capabilities.js +0 -2673
  159. package/dist/builtin-capabilities.js.map +0 -1
  160. package/dist/capability-agent-skill.d.ts.map +0 -1
  161. package/dist/capability-agent-skill.js.map +0 -1
  162. package/dist/capability-auth-state.d.ts.map +0 -1
  163. package/dist/capability-auth-state.js.map +0 -1
  164. package/dist/capability-auth.d.ts.map +0 -1
  165. package/dist/capability-auth.js.map +0 -1
  166. package/dist/capability-cli-confirmation.test.d.ts +0 -2
  167. package/dist/capability-cli-confirmation.test.d.ts.map +0 -1
  168. package/dist/capability-cli-confirmation.test.js +0 -79
  169. package/dist/capability-cli-confirmation.test.js.map +0 -1
  170. package/dist/capability-options.d.ts.map +0 -1
  171. package/dist/capability-options.js.map +0 -1
  172. package/dist/capability-options.test.d.ts +0 -2
  173. package/dist/capability-options.test.d.ts.map +0 -1
  174. package/dist/capability-options.test.js +0 -81
  175. package/dist/capability-options.test.js.map +0 -1
  176. package/dist/capability-package.d.ts.map +0 -1
  177. package/dist/capability-package.js.map +0 -1
  178. package/dist/capability-package.test.d.ts +0 -2
  179. package/dist/capability-package.test.d.ts.map +0 -1
  180. package/dist/capability-package.test.js +0 -179
  181. package/dist/capability-package.test.js.map +0 -1
  182. package/dist/capability-registry.d.ts.map +0 -1
  183. package/dist/capability-registry.js.map +0 -1
  184. package/dist/capability-registry.test.d.ts +0 -2
  185. package/dist/capability-registry.test.d.ts.map +0 -1
  186. package/dist/capability-registry.test.js +0 -1683
  187. package/dist/capability-registry.test.js.map +0 -1
  188. package/dist/capability-runner.d.ts.map +0 -1
  189. package/dist/capability-runner.js.map +0 -1
  190. package/dist/capability-search-relevance.test.d.ts +0 -2
  191. package/dist/capability-search-relevance.test.d.ts.map +0 -1
  192. package/dist/capability-search-relevance.test.js +0 -67
  193. package/dist/capability-search-relevance.test.js.map +0 -1
  194. package/dist/capability-studio.d.ts.map +0 -1
  195. package/dist/capability-studio.js.map +0 -1
  196. package/dist/capability-studio.test.d.ts +0 -2
  197. package/dist/capability-studio.test.d.ts.map +0 -1
  198. package/dist/capability-studio.test.js +0 -41
  199. package/dist/capability-studio.test.js.map +0 -1
  200. package/dist/cdp-log.d.ts.map +0 -1
  201. package/dist/cdp-log.js.map +0 -1
  202. package/dist/cdp-log.test.d.ts +0 -2
  203. package/dist/cdp-log.test.d.ts.map +0 -1
  204. package/dist/cdp-log.test.js +0 -109
  205. package/dist/cdp-log.test.js.map +0 -1
  206. package/dist/cdp-relay.d.ts.map +0 -1
  207. package/dist/cdp-relay.js.map +0 -1
  208. package/dist/cdp-session.d.ts.map +0 -1
  209. package/dist/cdp-session.js.map +0 -1
  210. package/dist/cdp-types.d.ts.map +0 -1
  211. package/dist/cdp-types.js.map +0 -1
  212. package/dist/channel-owner-inspect.test.d.ts +0 -2
  213. package/dist/channel-owner-inspect.test.d.ts.map +0 -1
  214. package/dist/channel-owner-inspect.test.js +0 -75
  215. package/dist/channel-owner-inspect.test.js.map +0 -1
  216. package/dist/chrome-discovery.d.ts.map +0 -1
  217. package/dist/chrome-discovery.js.map +0 -1
  218. package/dist/chrome-discovery.test.d.ts +0 -2
  219. package/dist/chrome-discovery.test.d.ts.map +0 -1
  220. package/dist/chrome-discovery.test.js +0 -41
  221. package/dist/chrome-discovery.test.js.map +0 -1
  222. package/dist/clean-html.d.ts.map +0 -1
  223. package/dist/clean-html.js.map +0 -1
  224. package/dist/cli-help.test.d.ts +0 -2
  225. package/dist/cli-help.test.d.ts.map +0 -1
  226. package/dist/cli-help.test.js +0 -98
  227. package/dist/cli-help.test.js.map +0 -1
  228. package/dist/cli.d.ts.map +0 -1
  229. package/dist/cli.js.map +0 -1
  230. package/dist/cloud-client.d.ts.map +0 -1
  231. package/dist/cloud-client.js.map +0 -1
  232. package/dist/create-logger.d.ts.map +0 -1
  233. package/dist/create-logger.js.map +0 -1
  234. package/dist/debugger-examples-types.d.ts.map +0 -1
  235. package/dist/debugger-examples-types.js.map +0 -1
  236. package/dist/debugger-examples.d.ts.map +0 -1
  237. package/dist/debugger-examples.js.map +0 -1
  238. package/dist/debugger.d.ts.map +0 -1
  239. package/dist/debugger.js.map +0 -1
  240. package/dist/diff-utils.d.ts.map +0 -1
  241. package/dist/diff-utils.js.map +0 -1
  242. package/dist/diff-utils.test.d.ts +0 -2
  243. package/dist/diff-utils.test.d.ts.map +0 -1
  244. package/dist/diff-utils.test.js +0 -137
  245. package/dist/diff-utils.test.js.map +0 -1
  246. package/dist/doctor.d.ts.map +0 -1
  247. package/dist/doctor.js.map +0 -1
  248. package/dist/doctor.test.d.ts +0 -2
  249. package/dist/doctor.test.d.ts.map +0 -1
  250. package/dist/doctor.test.js +0 -208
  251. package/dist/doctor.test.js.map +0 -1
  252. package/dist/editor-examples.d.ts.map +0 -1
  253. package/dist/editor-examples.js.map +0 -1
  254. package/dist/editor.d.ts.map +0 -1
  255. package/dist/editor.js.map +0 -1
  256. package/dist/env-compat.d.ts.map +0 -1
  257. package/dist/env-compat.js.map +0 -1
  258. package/dist/executor.d.ts.map +0 -1
  259. package/dist/executor.js.map +0 -1
  260. package/dist/executor.unit.test.d.ts +0 -2
  261. package/dist/executor.unit.test.d.ts.map +0 -1
  262. package/dist/executor.unit.test.js +0 -143
  263. package/dist/executor.unit.test.js.map +0 -1
  264. package/dist/extension-connection.test.d.ts +0 -2
  265. package/dist/extension-connection.test.d.ts.map +0 -1
  266. package/dist/extension-connection.test.js +0 -708
  267. package/dist/extension-connection.test.js.map +0 -1
  268. package/dist/extension-icon-warning.test.d.ts +0 -2
  269. package/dist/extension-icon-warning.test.d.ts.map +0 -1
  270. package/dist/extension-icon-warning.test.js +0 -33
  271. package/dist/extension-icon-warning.test.js.map +0 -1
  272. package/dist/ghost-browser.d.ts.map +0 -1
  273. package/dist/ghost-browser.js.map +0 -1
  274. package/dist/ghost-cursor-controller.d.ts.map +0 -1
  275. package/dist/ghost-cursor-controller.js.map +0 -1
  276. package/dist/ghost-cursor.d.ts.map +0 -1
  277. package/dist/ghost-cursor.js.map +0 -1
  278. package/dist/htmlrewrite.d.ts.map +0 -1
  279. package/dist/htmlrewrite.js.map +0 -1
  280. package/dist/htmlrewrite.test.d.ts +0 -2
  281. package/dist/htmlrewrite.test.d.ts.map +0 -1
  282. package/dist/htmlrewrite.test.js +0 -7975
  283. package/dist/htmlrewrite.test.js.map +0 -1
  284. package/dist/index.d.ts.map +0 -1
  285. package/dist/index.js.map +0 -1
  286. package/dist/kill-port.d.ts.map +0 -1
  287. package/dist/kill-port.js.map +0 -1
  288. package/dist/kitty-graphics.d.ts.map +0 -1
  289. package/dist/kitty-graphics.js.map +0 -1
  290. package/dist/kitty-graphics.test.d.ts +0 -2
  291. package/dist/kitty-graphics.test.d.ts.map +0 -1
  292. package/dist/kitty-graphics.test.js +0 -125
  293. package/dist/kitty-graphics.test.js.map +0 -1
  294. package/dist/locator-selector.test.d.ts +0 -2
  295. package/dist/locator-selector.test.d.ts.map +0 -1
  296. package/dist/locator-selector.test.js +0 -98
  297. package/dist/locator-selector.test.js.map +0 -1
  298. package/dist/mcp-client.d.ts +0 -20
  299. package/dist/mcp-client.d.ts.map +0 -1
  300. package/dist/mcp-client.js +0 -56
  301. package/dist/mcp-client.js.map +0 -1
  302. package/dist/mcp.d.ts.map +0 -1
  303. package/dist/mcp.js.map +0 -1
  304. package/dist/on-mouse-action.test.d.ts +0 -2
  305. package/dist/on-mouse-action.test.d.ts.map +0 -1
  306. package/dist/on-mouse-action.test.js +0 -180
  307. package/dist/on-mouse-action.test.js.map +0 -1
  308. package/dist/options-page.test.d.ts +0 -2
  309. package/dist/options-page.test.d.ts.map +0 -1
  310. package/dist/options-page.test.js +0 -773
  311. package/dist/options-page.test.js.map +0 -1
  312. package/dist/package-paths.d.ts.map +0 -1
  313. package/dist/package-paths.js.map +0 -1
  314. package/dist/page-markdown.d.ts.map +0 -1
  315. package/dist/page-markdown.js.map +0 -1
  316. package/dist/performance-examples.d.ts.map +0 -1
  317. package/dist/performance-examples.js.map +0 -1
  318. package/dist/playwright-import.d.ts.map +0 -1
  319. package/dist/playwright-import.js.map +0 -1
  320. package/dist/popup-relocation.test.d.ts +0 -7
  321. package/dist/popup-relocation.test.d.ts.map +0 -1
  322. package/dist/popup-relocation.test.js +0 -139
  323. package/dist/popup-relocation.test.js.map +0 -1
  324. package/dist/product-paths.d.ts.map +0 -1
  325. package/dist/product-paths.js.map +0 -1
  326. package/dist/product-paths.test.d.ts +0 -2
  327. package/dist/product-paths.test.d.ts.map +0 -1
  328. package/dist/product-paths.test.js +0 -39
  329. package/dist/product-paths.test.js.map +0 -1
  330. package/dist/protocol.d.ts.map +0 -1
  331. package/dist/protocol.js.map +0 -1
  332. package/dist/protocol.test.d.ts +0 -2
  333. package/dist/protocol.test.d.ts.map +0 -1
  334. package/dist/protocol.test.js +0 -31
  335. package/dist/protocol.test.js.map +0 -1
  336. package/dist/react-source.d.ts.map +0 -1
  337. package/dist/react-source.js.map +0 -1
  338. package/dist/relay-client.d.ts.map +0 -1
  339. package/dist/relay-client.js.map +0 -1
  340. package/dist/relay-client.test.d.ts +0 -2
  341. package/dist/relay-client.test.d.ts.map +0 -1
  342. package/dist/relay-client.test.js +0 -305
  343. package/dist/relay-client.test.js.map +0 -1
  344. package/dist/relay-core.test.d.ts +0 -2
  345. package/dist/relay-core.test.d.ts.map +0 -1
  346. package/dist/relay-core.test.js +0 -1427
  347. package/dist/relay-core.test.js.map +0 -1
  348. package/dist/relay-navigation.test.d.ts +0 -2
  349. package/dist/relay-navigation.test.d.ts.map +0 -1
  350. package/dist/relay-navigation.test.js +0 -661
  351. package/dist/relay-navigation.test.js.map +0 -1
  352. package/dist/relay-session.test.d.ts +0 -2
  353. package/dist/relay-session.test.d.ts.map +0 -1
  354. package/dist/relay-session.test.js +0 -1121
  355. package/dist/relay-session.test.js.map +0 -1
  356. package/dist/relay-state.d.ts.map +0 -1
  357. package/dist/relay-state.js.map +0 -1
  358. package/dist/relay-state.test.d.ts +0 -2
  359. package/dist/relay-state.test.d.ts.map +0 -1
  360. package/dist/relay-state.test.js +0 -594
  361. package/dist/relay-state.test.js.map +0 -1
  362. package/dist/replay-ai-index.d.ts.map +0 -1
  363. package/dist/replay-ai-index.js.map +0 -1
  364. package/dist/replay-ai-index.test.d.ts +0 -2
  365. package/dist/replay-ai-index.test.d.ts.map +0 -1
  366. package/dist/replay-ai-index.test.js +0 -255
  367. package/dist/replay-ai-index.test.js.map +0 -1
  368. package/dist/replay-cli.test.d.ts +0 -2
  369. package/dist/replay-cli.test.d.ts.map +0 -1
  370. package/dist/replay-cli.test.js +0 -275
  371. package/dist/replay-cli.test.js.map +0 -1
  372. package/dist/replay-eval.d.ts.map +0 -1
  373. package/dist/replay-eval.js.map +0 -1
  374. package/dist/replay-handoff.d.ts.map +0 -1
  375. package/dist/replay-handoff.js.map +0 -1
  376. package/dist/replay-handoff.test.d.ts +0 -2
  377. package/dist/replay-handoff.test.d.ts.map +0 -1
  378. package/dist/replay-handoff.test.js +0 -123
  379. package/dist/replay-handoff.test.js.map +0 -1
  380. package/dist/replay-workflow-compiler.d.ts.map +0 -1
  381. package/dist/replay-workflow-compiler.js.map +0 -1
  382. package/dist/replay-workflow-compiler.test.d.ts +0 -2
  383. package/dist/replay-workflow-compiler.test.d.ts.map +0 -1
  384. package/dist/replay-workflow-compiler.test.js +0 -223
  385. package/dist/replay-workflow-compiler.test.js.map +0 -1
  386. package/dist/rrweb-recording-relay.d.ts.map +0 -1
  387. package/dist/rrweb-recording-relay.js.map +0 -1
  388. package/dist/rrweb-recording-relay.test.d.ts +0 -2
  389. package/dist/rrweb-recording-relay.test.d.ts.map +0 -1
  390. package/dist/rrweb-recording-relay.test.js +0 -85
  391. package/dist/rrweb-recording-relay.test.js.map +0 -1
  392. package/dist/rrweb-recording.d.ts.map +0 -1
  393. package/dist/rrweb-recording.js.map +0 -1
  394. package/dist/scoped-fs.d.ts.map +0 -1
  395. package/dist/scoped-fs.js.map +0 -1
  396. package/dist/scoped-fs.test.d.ts +0 -5
  397. package/dist/scoped-fs.test.d.ts.map +0 -1
  398. package/dist/scoped-fs.test.js +0 -73
  399. package/dist/scoped-fs.test.js.map +0 -1
  400. package/dist/skill-stub.test.d.ts +0 -2
  401. package/dist/skill-stub.test.d.ts.map +0 -1
  402. package/dist/skill-stub.test.js +0 -22
  403. package/dist/skill-stub.test.js.map +0 -1
  404. package/dist/snapshot-tools.test.d.ts +0 -2
  405. package/dist/snapshot-tools.test.d.ts.map +0 -1
  406. package/dist/snapshot-tools.test.js +0 -856
  407. package/dist/snapshot-tools.test.js.map +0 -1
  408. package/dist/start-relay-server.d.ts.map +0 -1
  409. package/dist/start-relay-server.js.map +0 -1
  410. package/dist/styles-examples.d.ts.map +0 -1
  411. package/dist/styles-examples.js.map +0 -1
  412. package/dist/styles.d.ts.map +0 -1
  413. package/dist/styles.js.map +0 -1
  414. package/dist/tabwright-agent-skill.d.ts.map +0 -1
  415. package/dist/tabwright-agent-skill.js.map +0 -1
  416. package/dist/tabwright-agent-skill.test.d.ts +0 -2
  417. package/dist/tabwright-agent-skill.test.d.ts.map +0 -1
  418. package/dist/tabwright-agent-skill.test.js +0 -51
  419. package/dist/tabwright-agent-skill.test.js.map +0 -1
  420. package/dist/test-declarations.d.ts.map +0 -1
  421. package/dist/test-declarations.js.map +0 -1
  422. package/dist/test-utils.d.ts +0 -69
  423. package/dist/test-utils.d.ts.map +0 -1
  424. package/dist/test-utils.js +0 -448
  425. package/dist/test-utils.js.map +0 -1
  426. package/dist/test-utils.test.d.ts +0 -2
  427. package/dist/test-utils.test.d.ts.map +0 -1
  428. package/dist/test-utils.test.js +0 -55
  429. package/dist/test-utils.test.js.map +0 -1
  430. package/dist/utils.d.ts.map +0 -1
  431. package/dist/utils.js.map +0 -1
  432. package/dist/wait-for-page-load.d.ts.map +0 -1
  433. package/dist/wait-for-page-load.js.map +0 -1
  434. package/dist/workflow-capability.d.ts.map +0 -1
  435. package/dist/workflow-capability.js.map +0 -1
  436. package/dist/workflow-script-flow.test.d.ts +0 -2
  437. package/dist/workflow-script-flow.test.d.ts.map +0 -1
  438. package/dist/workflow-script-flow.test.js +0 -285
  439. package/dist/workflow-script-flow.test.js.map +0 -1
  440. package/src/__snapshots__/x.com.processed.html +0 -1003
  441. package/src/__snapshots__/x.com.processed.withStyles.html +0 -2720
  442. package/src/a11y-client.ts +0 -241
  443. package/src/agent-skills/conan-config/SKILL.md +0 -141
  444. package/src/agent-skills/conan-config/agents/openai.yaml +0 -4
  445. package/src/aria-snapshot.test.ts +0 -311
  446. package/src/aria-snapshot.ts +0 -1681
  447. package/src/aria-snapshot.unit.test.ts +0 -615
  448. package/src/aria-snapshots/github-interactive.txt +0 -193
  449. package/src/aria-snapshots/github-raw.txt +0 -309
  450. package/src/aria-snapshots/hackernews-interactive.txt +0 -477
  451. package/src/aria-snapshots/hackernews-raw.txt +0 -428
  452. package/src/aria-snapshots/prosemirror-interactive.txt +0 -66
  453. package/src/aria-snapshots/prosemirror-raw.txt +0 -145
  454. package/src/assets/aria-labels-hacker-news.png +0 -0
  455. package/src/assets/aria-labels-old-reddit.png +0 -0
  456. package/src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts +0 -5
  457. package/src/assets/cursors/screen-studio/pointer-macos-tahoe.svg +0 -18
  458. package/src/assets/x.com.html +0 -32946
  459. package/src/browser-config.ts +0 -179
  460. package/src/browser-install.ts +0 -284
  461. package/src/browser-launch.ts +0 -75
  462. package/src/builtin-capabilities.ts +0 -2765
  463. package/src/capability-agent-skill.ts +0 -305
  464. package/src/capability-auth-state.ts +0 -197
  465. package/src/capability-auth.ts +0 -249
  466. package/src/capability-cli-confirmation.test.ts +0 -90
  467. package/src/capability-options.test.ts +0 -89
  468. package/src/capability-options.ts +0 -57
  469. package/src/capability-package.test.ts +0 -204
  470. package/src/capability-package.ts +0 -555
  471. package/src/capability-registry.test.ts +0 -1905
  472. package/src/capability-registry.ts +0 -1234
  473. package/src/capability-runner.ts +0 -801
  474. package/src/capability-search-relevance.test.ts +0 -73
  475. package/src/capability-studio.test.ts +0 -45
  476. package/src/capability-studio.ts +0 -641
  477. package/src/cdp-log.test.ts +0 -131
  478. package/src/cdp-log.ts +0 -112
  479. package/src/cdp-relay.ts +0 -2944
  480. package/src/cdp-session.ts +0 -77
  481. package/src/cdp-types.ts +0 -158
  482. package/src/channel-owner-inspect.test.ts +0 -94
  483. package/src/chrome-discovery.test.ts +0 -50
  484. package/src/chrome-discovery.ts +0 -220
  485. package/src/clean-html.ts +0 -141
  486. package/src/cli-help.test.ts +0 -123
  487. package/src/cli.ts +0 -3181
  488. package/src/cloud-client.ts +0 -179
  489. package/src/create-logger.ts +0 -38
  490. package/src/debugger-examples-types.ts +0 -16
  491. package/src/debugger-examples.ts +0 -66
  492. package/src/debugger.ts +0 -708
  493. package/src/diff-utils.test.ts +0 -152
  494. package/src/diff-utils.ts +0 -70
  495. package/src/doctor.test.ts +0 -244
  496. package/src/doctor.ts +0 -321
  497. package/src/editor-examples.ts +0 -158
  498. package/src/editor.ts +0 -426
  499. package/src/env-compat.ts +0 -28
  500. package/src/executor.ts +0 -1824
  501. package/src/executor.unit.test.ts +0 -168
  502. package/src/extension-connection.test.ts +0 -855
  503. package/src/extension-icon-warning.test.ts +0 -45
  504. package/src/ghost-browser.ts +0 -149
  505. package/src/ghost-cursor-client.ts +0 -494
  506. package/src/ghost-cursor-controller.ts +0 -129
  507. package/src/ghost-cursor.ts +0 -123
  508. package/src/htmlrewrite.test.ts +0 -8008
  509. package/src/htmlrewrite.ts +0 -390
  510. package/src/index.ts +0 -53
  511. package/src/kill-port.ts +0 -219
  512. package/src/kitty-graphics.test.ts +0 -139
  513. package/src/kitty-graphics.ts +0 -79
  514. package/src/locator-selector.test.ts +0 -119
  515. package/src/mcp-client.ts +0 -78
  516. package/src/mcp.ts +0 -629
  517. package/src/on-mouse-action.test.ts +0 -226
  518. package/src/options-page.test.ts +0 -839
  519. package/src/package-paths.ts +0 -38
  520. package/src/page-markdown.ts +0 -240
  521. package/src/performance-examples.ts +0 -186
  522. package/src/playwright-import.ts +0 -59
  523. package/src/popup-relocation.test.ts +0 -163
  524. package/src/product-paths.test.ts +0 -47
  525. package/src/product-paths.ts +0 -28
  526. package/src/protocol.test.ts +0 -52
  527. package/src/protocol.ts +0 -351
  528. package/src/react-source.ts +0 -379
  529. package/src/relay-client.test.ts +0 -375
  530. package/src/relay-client.ts +0 -572
  531. package/src/relay-core.test.ts +0 -1603
  532. package/src/relay-navigation.test.ts +0 -790
  533. package/src/relay-session.test.ts +0 -1375
  534. package/src/relay-state.test.ts +0 -729
  535. package/src/relay-state.ts +0 -561
  536. package/src/replay-ai-index.test.ts +0 -278
  537. package/src/replay-ai-index.ts +0 -872
  538. package/src/replay-cli.test.ts +0 -325
  539. package/src/replay-eval.ts +0 -1039
  540. package/src/replay-handoff.test.ts +0 -163
  541. package/src/replay-handoff.ts +0 -109
  542. package/src/replay-workflow-compiler.test.ts +0 -250
  543. package/src/replay-workflow-compiler.ts +0 -700
  544. package/src/resource.md +0 -409
  545. package/src/rrweb-recording-relay.test.ts +0 -99
  546. package/src/rrweb-recording-relay.ts +0 -400
  547. package/src/rrweb-recording.ts +0 -322
  548. package/src/scoped-fs.test.ts +0 -80
  549. package/src/scoped-fs.ts +0 -420
  550. package/src/skill-stub.test.ts +0 -24
  551. package/src/snapshot-tools.test.ts +0 -1017
  552. package/src/snapshots/hacker-news-accessibility-full.md +0 -81
  553. package/src/snapshots/hacker-news-accessibility-interactive.md +0 -43
  554. package/src/snapshots/page-markdown-output.txt +0 -16
  555. package/src/snapshots/shadcn-ui-accessibility-full.md +0 -201
  556. package/src/snapshots/shadcn-ui-accessibility-interactive.md +0 -109
  557. package/src/start-relay-server.ts +0 -69
  558. package/src/styles-examples.ts +0 -84
  559. package/src/styles.ts +0 -343
  560. package/src/tabwright-agent-skill.test.ts +0 -61
  561. package/src/tabwright-agent-skill.ts +0 -105
  562. package/src/test-declarations.ts +0 -13
  563. package/src/test-utils.test.ts +0 -70
  564. package/src/test-utils.ts +0 -566
  565. package/src/utils.ts +0 -78
  566. package/src/wait-for-page-load.ts +0 -198
  567. package/src/workflow-capability.ts +0 -417
  568. package/src/workflow-script-flow.test.ts +0 -388
@@ -1,1375 +0,0 @@
1
- import { createMCPClient } from './mcp-client.js'
2
- import { describe, it, expect, beforeAll, afterAll } from 'vitest'
3
- import { chromium } from '@xmorse/playwright-core'
4
- import { getCdpUrl } from './utils.js'
5
- import { getCDPSessionForPage } from './cdp-session.js'
6
- import { Debugger } from './debugger.js'
7
- import { Editor } from './editor.js'
8
- import { PlaywrightExecutor } from './executor.js'
9
- import {
10
- setupTestContext,
11
- cleanupTestContext,
12
- getExtensionServiceWorker,
13
- createSseServer,
14
- safeCloseCDPBrowser,
15
- type TestContext,
16
- withTimeout,
17
- js,
18
- } from './test-utils.js'
19
- import './test-declarations.js'
20
-
21
- const TEST_PORT = 19993
22
-
23
- // --- CDP Session Tests ---
24
-
25
- describe('CDP Session Tests', () => {
26
- let testCtx: TestContext | null = null
27
-
28
- beforeAll(async () => {
29
- testCtx = await setupTestContext({ port: TEST_PORT, tempDirPrefix: 'pw-cdp-test-', toggleExtension: true })
30
-
31
- const serviceWorker = await getExtensionServiceWorker(testCtx.browserContext)
32
- await serviceWorker.evaluate(async () => {
33
- await globalThis.disconnectEverything()
34
- })
35
- await new Promise((r) => setTimeout(r, 100))
36
- }, 600000)
37
-
38
- afterAll(async () => {
39
- await cleanupTestContext(testCtx)
40
- testCtx = null
41
- })
42
-
43
- const getBrowserContext = () => {
44
- if (!testCtx?.browserContext) throw new Error('Browser not initialized')
45
- return testCtx.browserContext
46
- }
47
-
48
- it('should use Debugger class to set breakpoints and inspect variables', async () => {
49
- const browserContext = getBrowserContext()
50
- const serviceWorker = await getExtensionServiceWorker(browserContext)
51
-
52
- const page = await browserContext.newPage()
53
- const testUrl = 'https://example.com/?test=debugger-variables'
54
- await page.goto(testUrl)
55
- await page.bringToFront()
56
-
57
- await serviceWorker.evaluate(async () => {
58
- await globalThis.toggleExtensionForActiveTab()
59
- })
60
- await new Promise((r) => setTimeout(r, 100))
61
-
62
- const wsUrl = getCdpUrl({ port: TEST_PORT })
63
- const cdpSession = await getCDPSessionForPage({ page })
64
- const dbg = new Debugger({ cdp: cdpSession })
65
-
66
- await dbg.enable()
67
-
68
- expect(dbg.isPaused()).toBe(false)
69
-
70
- const pausedPromise = new Promise<void>((resolve) => {
71
- cdpSession.on('Debugger.paused', () => {
72
- resolve()
73
- })
74
- })
75
-
76
- const evalPromise = cdpSession.send('Runtime.evaluate', {
77
- expression: `(function testFunction() {
78
- const localVar = 'hello';
79
- const numberVar = 42;
80
- debugger;
81
- return localVar + numberVar;
82
- })()`,
83
- })
84
-
85
- await Promise.race([
86
- pausedPromise,
87
- new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Debugger.paused timeout')), 5000)),
88
- ])
89
-
90
- expect(dbg.isPaused()).toBe(true)
91
-
92
- const location = await dbg.getLocation()
93
- expect(location.callstack[0].functionName).toBe('testFunction')
94
- expect(location.sourceContext).toContain('debugger')
95
-
96
- const vars = await dbg.inspectLocalVariables()
97
- expect(vars).toMatchInlineSnapshot(`
98
- {
99
- "localVar": "hello",
100
- "numberVar": 42,
101
- }
102
- `)
103
-
104
- const evalResult = await dbg.evaluate({ expression: 'localVar + " world"' })
105
- expect(evalResult.value).toBe('hello world')
106
-
107
- await dbg.resume()
108
- await new Promise((r) => setTimeout(r, 100))
109
- expect(dbg.isPaused()).toBe(false)
110
- await evalPromise
111
-
112
- await cdpSession.detach()
113
- await page.close()
114
- }, 60000)
115
-
116
- it('should reuse cached CDP session and close on page close', async () => {
117
- const browserContext = getBrowserContext()
118
- const serviceWorker = await getExtensionServiceWorker(browserContext)
119
-
120
- const page = await browserContext.newPage()
121
- const testUrl = 'https://example.com/?test=debugger-step'
122
- await page.goto(testUrl)
123
- await page.bringToFront()
124
-
125
- await serviceWorker.evaluate(async () => {
126
- await globalThis.toggleExtensionForActiveTab()
127
- })
128
- await new Promise((r) => setTimeout(r, 100))
129
-
130
- const executor = new PlaywrightExecutor({
131
- cdpConfig: { port: TEST_PORT },
132
- logger: {
133
- log: () => {},
134
- error: () => {},
135
- },
136
- })
137
-
138
- const result = await executor.execute(js`
139
- const sessionA = await getCDPSession({ page })
140
- const sessionB = await getCDPSession({ page })
141
- await sessionA.send('Runtime.evaluate', { expression: '1 + 1', returnByValue: true })
142
- const evalResult = await sessionB.send('Runtime.evaluate', { expression: '2 + 2', returnByValue: true })
143
- return evalResult.result.value
144
- `)
145
-
146
- expect(result.isError).toBe(false)
147
- expect(result.text).toContain('[return value] 4')
148
-
149
- await page.close()
150
- }, 60000)
151
-
152
- it('should list scripts with Debugger class', async () => {
153
- const browserContext = getBrowserContext()
154
- const serviceWorker = await getExtensionServiceWorker(browserContext)
155
-
156
- // Use setContent with external script URLs so Debugger.listScripts returns them
157
- const page = await browserContext.newPage()
158
- await page.setContent(`
159
- <html>
160
- <head>
161
- <script src="data:text/javascript,function hello() { return 1; }"></script>
162
- <script src="data:text/javascript,function world() { return 2; }"></script>
163
- </head>
164
- <body><h1>Script test</h1></body>
165
- </html>
166
- `)
167
- await page.bringToFront()
168
-
169
- await serviceWorker.evaluate(async () => {
170
- await globalThis.toggleExtensionForActiveTab()
171
- })
172
- await new Promise((r) => setTimeout(r, 100))
173
-
174
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
175
- const cdpPage = browser
176
- .contexts()[0]
177
- .pages()
178
- .find((p) => p.url().startsWith('about:'))
179
- expect(cdpPage).toBeDefined()
180
-
181
- const wsUrl = getCdpUrl({ port: TEST_PORT })
182
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
183
- const dbg = new Debugger({ cdp: cdpSession })
184
-
185
- await dbg.enable()
186
-
187
- const scripts = await dbg.listScripts()
188
- expect(scripts.length).toBeGreaterThan(0)
189
- expect(scripts[0]).toHaveProperty('scriptId')
190
- expect(scripts[0]).toHaveProperty('url')
191
-
192
- await cdpSession.detach()
193
- await browser.close()
194
- await page.close()
195
- }, 60000)
196
-
197
- it('should manage breakpoints with Debugger class', async () => {
198
- const browserContext = getBrowserContext()
199
- const serviceWorker = await getExtensionServiceWorker(browserContext)
200
-
201
- const page = await browserContext.newPage()
202
- await page.setContent(`
203
- <html>
204
- <head>
205
- <script src="data:text/javascript,function testFunc() { return 42; }"></script>
206
- </head>
207
- <body></body>
208
- </html>
209
- `)
210
- await page.bringToFront()
211
-
212
- await serviceWorker.evaluate(async () => {
213
- await globalThis.toggleExtensionForActiveTab()
214
- })
215
- await new Promise((r) => setTimeout(r, 100))
216
-
217
- const wsUrl = getCdpUrl({ port: TEST_PORT })
218
- const cdpSession = await getCDPSessionForPage({ page })
219
- const dbg = new Debugger({ cdp: cdpSession })
220
-
221
- await dbg.enable()
222
-
223
- expect(dbg.listBreakpoints()).toHaveLength(0)
224
-
225
- const bpId = await dbg.setBreakpoint({ file: 'https://example.com/test.js', line: 1 })
226
- expect(typeof bpId).toBe('string')
227
- expect(dbg.listBreakpoints()).toHaveLength(1)
228
- expect(dbg.listBreakpoints()[0]).toMatchObject({
229
- id: bpId,
230
- file: 'https://example.com/test.js',
231
- line: 1,
232
- })
233
-
234
- await dbg.deleteBreakpoint({ breakpointId: bpId })
235
- expect(dbg.listBreakpoints()).toHaveLength(0)
236
-
237
- await cdpSession.detach()
238
- await page.close()
239
- }, 60000)
240
-
241
- it('should step through code with Debugger class', async () => {
242
- const browserContext = getBrowserContext()
243
- const serviceWorker = await getExtensionServiceWorker(browserContext)
244
-
245
- const page = await browserContext.newPage()
246
- await page.goto('https://example.com/')
247
- await page.bringToFront()
248
-
249
- await serviceWorker.evaluate(async () => {
250
- await globalThis.toggleExtensionForActiveTab()
251
- })
252
- await new Promise((r) => setTimeout(r, 100))
253
-
254
- const wsUrl = getCdpUrl({ port: TEST_PORT })
255
- const cdpSession = await getCDPSessionForPage({ page })
256
- const dbg = new Debugger({ cdp: cdpSession })
257
-
258
- await dbg.enable()
259
-
260
- const pausedPromise = new Promise<void>((resolve) => {
261
- cdpSession.on('Debugger.paused', () => resolve())
262
- })
263
-
264
- const evalPromise = cdpSession.send('Runtime.evaluate', {
265
- expression: `(function outer() {
266
- function inner() {
267
- const x = 1;
268
- debugger;
269
- const y = 2;
270
- return x + y;
271
- }
272
- const result = inner();
273
- return result;
274
- })()`,
275
- })
276
-
277
- await pausedPromise
278
- expect(dbg.isPaused()).toBe(true)
279
-
280
- const location1 = await dbg.getLocation()
281
- expect(location1.callstack.length).toBeGreaterThanOrEqual(2)
282
- expect(location1.callstack[0].functionName).toBe('inner')
283
- expect(location1.callstack[1].functionName).toBe('outer')
284
-
285
- const stepOverPromise = new Promise<void>((resolve) => {
286
- cdpSession.on('Debugger.paused', () => resolve())
287
- })
288
- await dbg.stepOver()
289
- await stepOverPromise
290
-
291
- const location2 = await dbg.getLocation()
292
- expect(location2.lineNumber).toBeGreaterThan(location1.lineNumber)
293
-
294
- const stepOutPromise = new Promise<void>((resolve) => {
295
- cdpSession.on('Debugger.paused', () => resolve())
296
- })
297
- await dbg.stepOut()
298
- await stepOutPromise
299
-
300
- const location3 = await dbg.getLocation()
301
- expect(location3.callstack[0].functionName).toBe('outer')
302
-
303
- await dbg.resume()
304
- await evalPromise
305
-
306
- await cdpSession.detach()
307
- await page.close()
308
- }, 60000)
309
-
310
- it('should profile JavaScript execution using CDP Profiler', async () => {
311
- const browserContext = getBrowserContext()
312
- const serviceWorker = await getExtensionServiceWorker(browserContext)
313
-
314
- const page = await browserContext.newPage()
315
- const testUrl = 'https://example.com/?test=debugger-profiler'
316
- await page.goto(testUrl)
317
- await page.bringToFront()
318
-
319
- await serviceWorker.evaluate(async () => {
320
- await globalThis.toggleExtensionForActiveTab()
321
- })
322
- await new Promise((r) => setTimeout(r, 100))
323
-
324
- const wsUrl = getCdpUrl({ port: TEST_PORT })
325
- const cdpSession = await getCDPSessionForPage({ page })
326
- await cdpSession.send('Profiler.enable')
327
- await cdpSession.send('Profiler.start')
328
-
329
- await cdpSession.send('Runtime.evaluate', {
330
- expression: `(() => {
331
- function fibonacci(n) {
332
- if (n <= 1) return n
333
- return fibonacci(n - 1) + fibonacci(n - 2)
334
- }
335
- for (let i = 0; i < 5; i++) {
336
- fibonacci(20)
337
- }
338
- for (let i = 0; i < 1000; i++) {
339
- document.querySelectorAll('*')
340
- }
341
- })()`,
342
- })
343
-
344
- const stopResult = await cdpSession.send('Profiler.stop')
345
- const profile = stopResult.profile
346
-
347
- const functionNames = profile.nodes
348
- .map((n) => n.callFrame.functionName)
349
- .filter((name) => name && name.length > 0)
350
- .slice(0, 10)
351
-
352
- expect(profile.nodes.length).toBeGreaterThan(0)
353
- expect(profile.endTime - profile.startTime).toBeGreaterThan(0)
354
- expect(functionNames.every((name) => typeof name === 'string')).toBe(true)
355
-
356
- await cdpSession.send('Profiler.disable')
357
- await cdpSession.detach()
358
- await page.close()
359
- }, 60000)
360
-
361
- it('should update Target.getTargets URL after page navigation', async () => {
362
- const browserContext = getBrowserContext()
363
- const serviceWorker = await getExtensionServiceWorker(browserContext)
364
-
365
- await serviceWorker.evaluate(async () => {
366
- await globalThis.disconnectEverything()
367
- })
368
-
369
- const page = await browserContext.newPage()
370
- await page.goto('https://example.com/')
371
- await page.bringToFront()
372
-
373
- await serviceWorker.evaluate(async () => {
374
- await globalThis.toggleExtensionForActiveTab()
375
- })
376
- await new Promise((r) => setTimeout(r, 100))
377
-
378
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
379
- const cdpPage = browser
380
- .contexts()[0]
381
- .pages()
382
- .find((p) => p.url().includes('example.com'))
383
- expect(cdpPage).toBeDefined()
384
-
385
- const wsUrl = getCdpUrl({ port: TEST_PORT })
386
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
387
-
388
- const initialTargets = await cdpSession.send('Target.getTargets')
389
- const initialPageTarget = initialTargets.targetInfos.find((t) => t.type === 'page' && t.url.includes('example.com'))
390
- expect(initialPageTarget?.url).toBe('https://example.com/')
391
-
392
- await cdpPage!.goto('https://example.org/', { waitUntil: 'domcontentloaded' })
393
- await new Promise((r) => setTimeout(r, 100))
394
-
395
- const afterNavTargets = await cdpSession.send('Target.getTargets')
396
- const allPageTargets = afterNavTargets.targetInfos.filter((t) => t.type === 'page')
397
-
398
- const aboutBlankTargets = allPageTargets.filter((t) => t.url === 'about:blank')
399
- expect(aboutBlankTargets).toHaveLength(0)
400
-
401
- const exampleComTargets = allPageTargets.filter((t) => t.url.includes('example.com'))
402
- expect(exampleComTargets).toHaveLength(0)
403
-
404
- const exampleOrgTargets = allPageTargets.filter((t) => t.url.includes('example.org'))
405
- expect(exampleOrgTargets).toHaveLength(1)
406
-
407
- await cdpSession.detach()
408
- await browser.close()
409
- await page.close()
410
- }, 60000)
411
-
412
- it('should return correct targets for multiple pages via Target.getTargets', async () => {
413
- const browserContext = getBrowserContext()
414
- const serviceWorker = await getExtensionServiceWorker(browserContext)
415
-
416
- await serviceWorker.evaluate(async () => {
417
- await globalThis.disconnectEverything()
418
- })
419
-
420
- const page1 = await browserContext.newPage()
421
- await page1.goto('https://example.com/')
422
- await page1.bringToFront()
423
- await serviceWorker.evaluate(async () => {
424
- await globalThis.toggleExtensionForActiveTab()
425
- })
426
-
427
- const page2 = await browserContext.newPage()
428
- await page2.goto('https://example.org/')
429
- await page2.bringToFront()
430
- await serviceWorker.evaluate(async () => {
431
- await globalThis.toggleExtensionForActiveTab()
432
- })
433
-
434
- await new Promise((r) => setTimeout(r, 100))
435
-
436
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
437
- const cdpPage = browser
438
- .contexts()[0]
439
- .pages()
440
- .find((p) => p.url().includes('example.com'))
441
- expect(cdpPage).toBeDefined()
442
-
443
- const wsUrl = getCdpUrl({ port: TEST_PORT })
444
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
445
-
446
- const { targetInfos } = await cdpSession.send('Target.getTargets')
447
- const allPageTargets = targetInfos.filter((t) => t.type === 'page')
448
-
449
- const aboutBlankTargets = allPageTargets.filter((t) => t.url === 'about:blank')
450
- expect(aboutBlankTargets).toHaveLength(0)
451
-
452
- const pageTargets = allPageTargets
453
- .map((t) => ({ type: t.type, url: t.url }))
454
- .sort((a, b) => a.url.localeCompare(b.url))
455
-
456
- expect(pageTargets).toMatchInlineSnapshot(`
457
- [
458
- {
459
- "type": "page",
460
- "url": "https://example.com/",
461
- },
462
- {
463
- "type": "page",
464
- "url": "https://example.org/",
465
- },
466
- ]
467
- `)
468
-
469
- await cdpSession.detach()
470
- await browser.close()
471
- await page1.close()
472
- await page2.close()
473
- }, 60000)
474
-
475
- it('should create CDP session for page after navigation', async () => {
476
- const browserContext = getBrowserContext()
477
- const serviceWorker = await getExtensionServiceWorker(browserContext)
478
-
479
- const page = await browserContext.newPage()
480
- await page.goto('https://example.com/')
481
- await page.bringToFront()
482
-
483
- await serviceWorker.evaluate(async () => {
484
- await globalThis.toggleExtensionForActiveTab()
485
- })
486
- await new Promise((r) => setTimeout(r, 100))
487
-
488
- await page.goto('https://example.org/', { waitUntil: 'domcontentloaded' })
489
- await new Promise((r) => setTimeout(r, 100))
490
-
491
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
492
- const cdpPage = browser
493
- .contexts()[0]
494
- .pages()
495
- .find((p) => p.url().includes('example.org'))
496
- expect(cdpPage).toBeDefined()
497
-
498
- const wsUrl = getCdpUrl({ port: TEST_PORT })
499
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
500
-
501
- const evalResult = await cdpSession.send('Runtime.evaluate', {
502
- expression: 'document.title',
503
- returnByValue: true,
504
- })
505
- expect(evalResult.result.value).toContain('Example Domain')
506
-
507
- await cdpSession.detach()
508
- await browser.close()
509
- await page.close()
510
- }, 60000)
511
-
512
- it('should maintain CDP session functionality after page URL change', async () => {
513
- const browserContext = getBrowserContext()
514
- const serviceWorker = await getExtensionServiceWorker(browserContext)
515
-
516
- const page = await browserContext.newPage()
517
- const initialUrl = 'https://example.com/'
518
- await page.goto(initialUrl)
519
- await page.bringToFront()
520
-
521
- await serviceWorker.evaluate(async () => {
522
- await globalThis.toggleExtensionForActiveTab()
523
- })
524
- await new Promise((r) => setTimeout(r, 100))
525
-
526
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
527
- const cdpPage = browser
528
- .contexts()[0]
529
- .pages()
530
- .find((p) => p.url().includes('example.com'))
531
- expect(cdpPage).toBeDefined()
532
-
533
- const wsUrl = getCdpUrl({ port: TEST_PORT })
534
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
535
-
536
- const initialEvalResult = await cdpSession.send('Runtime.evaluate', {
537
- expression: 'document.title',
538
- returnByValue: true,
539
- })
540
- expect(initialEvalResult.result.value).toBe('Example Domain')
541
-
542
- const newUrl = 'https://example.org/'
543
- await cdpPage!.goto(newUrl, { waitUntil: 'domcontentloaded' })
544
-
545
- expect(cdpPage!.url()).toBe(newUrl)
546
-
547
- const layoutMetrics = await cdpSession.send('Page.getLayoutMetrics')
548
- expect(layoutMetrics.cssVisualViewport).toBeDefined()
549
- expect(layoutMetrics.cssVisualViewport.clientWidth).toBeGreaterThan(0)
550
-
551
- const afterNavEvalResult = await cdpSession.send('Runtime.evaluate', {
552
- expression: 'document.title',
553
- returnByValue: true,
554
- })
555
- expect(afterNavEvalResult.result.value).toContain('Example Domain')
556
-
557
- const locationResult = await cdpSession.send('Runtime.evaluate', {
558
- expression: 'window.location.href',
559
- returnByValue: true,
560
- })
561
- expect(locationResult.result.value).toBe(newUrl)
562
-
563
- await cdpSession.detach()
564
- await browser.close()
565
- await page.close()
566
- }, 60000)
567
-
568
- it('should pause on all exceptions with setPauseOnExceptions', async () => {
569
- const browserContext = getBrowserContext()
570
- const serviceWorker = await getExtensionServiceWorker(browserContext)
571
-
572
- const page = await browserContext.newPage()
573
- await page.goto('https://example.com/')
574
- await page.bringToFront()
575
-
576
- await serviceWorker.evaluate(async () => {
577
- await globalThis.toggleExtensionForActiveTab()
578
- })
579
- await new Promise((r) => setTimeout(r, 100))
580
-
581
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
582
- const cdpPage = browser
583
- .contexts()[0]
584
- .pages()
585
- .find((p) => p.url().includes('example.com'))
586
- expect(cdpPage).toBeDefined()
587
-
588
- const wsUrl = getCdpUrl({ port: TEST_PORT })
589
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
590
- const dbg = new Debugger({ cdp: cdpSession })
591
-
592
- await dbg.enable()
593
- await dbg.setPauseOnExceptions({ state: 'all' })
594
-
595
- const pausedPromise = new Promise<void>((resolve) => {
596
- cdpSession.on('Debugger.paused', () => resolve())
597
- })
598
-
599
- const evalPromise = cdpSession.send('Runtime.evaluate', {
600
- expression: `(function() {
601
- try {
602
- throw new Error('Caught test error');
603
- } catch (e) {
604
- // caught but should still pause with state 'all'
605
- }
606
- })()`,
607
- })
608
-
609
- await Promise.race([
610
- pausedPromise,
611
- new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Debugger.paused timeout')), 5000)),
612
- ])
613
-
614
- expect(dbg.isPaused()).toBe(true)
615
-
616
- const location = await dbg.getLocation()
617
- expect(location.sourceContext).toContain('throw')
618
-
619
- await dbg.resume()
620
- await evalPromise
621
-
622
- await dbg.setPauseOnExceptions({ state: 'none' })
623
-
624
- await cdpSession.detach()
625
- await browser.close()
626
- await page.close()
627
- }, 60000)
628
-
629
- it('should inspect local and global variables with inline snapshots', async () => {
630
- const browserContext = getBrowserContext()
631
- const serviceWorker = await getExtensionServiceWorker(browserContext)
632
-
633
- const page = await browserContext.newPage()
634
- await page.setContent(`
635
- <html>
636
- <head>
637
- <script>
638
- const GLOBAL_CONFIG = 'production';
639
- function runTest() {
640
- const userName = 'Alice';
641
- const userAge = 25;
642
- const settings = { theme: 'dark', lang: 'en' };
643
- const scores = [10, 20, 30];
644
- debugger;
645
- return userName;
646
- }
647
- </script>
648
- </head>
649
- <body>
650
- <button onclick="runTest()">Run</button>
651
- </body>
652
- </html>
653
- `)
654
- await page.bringToFront()
655
-
656
- await serviceWorker.evaluate(async () => {
657
- await globalThis.toggleExtensionForActiveTab()
658
- })
659
- await new Promise((r) => setTimeout(r, 100))
660
-
661
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
662
- let cdpPage
663
- for (const p of browser.contexts()[0].pages()) {
664
- const html = await p.content()
665
- if (html.includes('runTest')) {
666
- cdpPage = p
667
- break
668
- }
669
- }
670
- expect(cdpPage).toBeDefined()
671
-
672
- const wsUrl = getCdpUrl({ port: TEST_PORT })
673
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
674
- const dbg = new Debugger({ cdp: cdpSession })
675
-
676
- await dbg.enable()
677
-
678
- const globalVars = await dbg.inspectGlobalVariables()
679
- expect(globalVars).toMatchInlineSnapshot(`
680
- [
681
- "GLOBAL_CONFIG",
682
- ]
683
- `)
684
-
685
- const pausedPromise = new Promise<void>((resolve) => {
686
- cdpSession.on('Debugger.paused', () => resolve())
687
- })
688
-
689
- // Don't await - we want it to pause at breakpoint
690
- const evalPromise = cdpPage!.evaluate('runTest()').catch(() => {
691
- // Ignore errors from evaluate when browser closes
692
- })
693
-
694
- await pausedPromise
695
- expect(dbg.isPaused()).toBe(true)
696
-
697
- const localVars = await dbg.inspectLocalVariables()
698
- expect(localVars).toMatchInlineSnapshot(`
699
- {
700
- "GLOBAL_CONFIG": "production",
701
- "scores": "[array]",
702
- "settings": "[object]",
703
- "userAge": 25,
704
- "userName": "Alice",
705
- }
706
- `)
707
-
708
- await dbg.resume()
709
- // Wait for evaluate to complete after resume
710
- await evalPromise
711
-
712
- await cdpSession.detach()
713
- await browser.close()
714
- await page.close()
715
- }, 60000)
716
-
717
- it('should click at correct coordinates on high-DPI simulation', async () => {
718
- const browserContext = getBrowserContext()
719
- const serviceWorker = await getExtensionServiceWorker(browserContext)
720
-
721
- const page = await browserContext.newPage()
722
- await page.goto('https://example.com/')
723
- await page.bringToFront()
724
-
725
- await serviceWorker.evaluate(async () => {
726
- await globalThis.toggleExtensionForActiveTab()
727
- })
728
- await new Promise((r) => setTimeout(r, 100))
729
-
730
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
731
- const cdpPage = browser
732
- .contexts()[0]
733
- .pages()
734
- .find((p) => p.url().includes('example.com'))
735
- expect(cdpPage).toBeDefined()
736
-
737
- const h1Bounds = await cdpPage!.locator('h1').boundingBox()
738
- expect(h1Bounds).toBeDefined()
739
- console.log('H1 bounding box:', h1Bounds)
740
-
741
- await cdpPage!.evaluate(() => {
742
- ;(window as any).clickedAt = null
743
- document.addEventListener('click', (e) => {
744
- ;(window as any).clickedAt = { x: e.clientX, y: e.clientY }
745
- })
746
- })
747
-
748
- await cdpPage!.locator('h1').click()
749
-
750
- const clickedAt = await cdpPage!.evaluate(() => (window as any).clickedAt)
751
- console.log('Clicked at:', clickedAt)
752
-
753
- expect(clickedAt).toBeDefined()
754
- expect(clickedAt.x).toBeGreaterThan(0)
755
- expect(clickedAt.y).toBeGreaterThan(0)
756
-
757
- await browser.close()
758
- await page.close()
759
- }, 60000)
760
-
761
- it('should use Editor class to list, read, and edit scripts', async () => {
762
- const browserContext = getBrowserContext()
763
- const serviceWorker = await getExtensionServiceWorker(browserContext)
764
-
765
- const page = await browserContext.newPage()
766
- await page.goto('https://example.com/')
767
- await page.bringToFront()
768
-
769
- await serviceWorker.evaluate(async () => {
770
- await globalThis.toggleExtensionForActiveTab()
771
- })
772
- await new Promise((r) => setTimeout(r, 100))
773
-
774
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
775
- const cdpPage = browser
776
- .contexts()[0]
777
- .pages()
778
- .find((p) => p.url().includes('example.com'))
779
- expect(cdpPage).toBeDefined()
780
-
781
- const wsUrl = getCdpUrl({ port: TEST_PORT })
782
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
783
- const editor = new Editor({ cdp: cdpSession })
784
-
785
- await editor.enable()
786
-
787
- await cdpPage!.addScriptTag({
788
- content: `
789
- function greetUser(name) {
790
- console.log('Hello, ' + name);
791
- return 'Hello, ' + name;
792
- }
793
- `,
794
- })
795
- await new Promise((r) => setTimeout(r, 100))
796
- const scripts = await editor.list()
797
- expect(scripts.length).toBeGreaterThan(0)
798
-
799
- const matches = await editor.grep({ regex: /greetUser/ })
800
- expect(matches.length).toBeGreaterThan(0)
801
-
802
- const match = matches[0]
803
- const { content, totalLines } = await editor.read({ url: match.url })
804
- expect(content).toContain('greetUser')
805
- expect(totalLines).toBeGreaterThan(0)
806
-
807
- await editor.edit({
808
- url: match.url,
809
- oldString: "console.log('Hello, ' + name);",
810
- newString: "console.log('Hello, ' + name); console.log('EDITOR_TEST_MARKER');",
811
- })
812
-
813
- const consoleLogs: string[] = []
814
- cdpPage!.on('console', (msg) => {
815
- consoleLogs.push(msg.text())
816
- })
817
-
818
- await cdpPage!.evaluate(() => {
819
- ;(window as any).greetUser('World')
820
- })
821
- await new Promise((r) => setTimeout(r, 100))
822
-
823
- expect(consoleLogs).toContain('Hello, World')
824
- expect(consoleLogs).toContain('EDITOR_TEST_MARKER')
825
-
826
- await cdpSession.detach()
827
- await browser.close()
828
- await page.close()
829
- }, 60000)
830
-
831
- it('editor can list, read, and edit CSS stylesheets', async () => {
832
- const browserContext = getBrowserContext()
833
- const serviceWorker = await getExtensionServiceWorker(browserContext)
834
-
835
- const page = await browserContext.newPage()
836
- await page.goto('https://example.com/')
837
- await page.bringToFront()
838
-
839
- await serviceWorker.evaluate(async () => {
840
- await globalThis.toggleExtensionForActiveTab()
841
- })
842
- await new Promise((r) => setTimeout(r, 100))
843
-
844
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
845
- const cdpPage = browser
846
- .contexts()[0]
847
- .pages()
848
- .find((p) => p.url().includes('example.com'))
849
- expect(cdpPage).toBeDefined()
850
-
851
- const wsUrl = getCdpUrl({ port: TEST_PORT })
852
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
853
- const editor = new Editor({ cdp: cdpSession })
854
-
855
- await editor.enable()
856
-
857
- await cdpPage!.addStyleTag({
858
- content: `
859
- .editor-test-element {
860
- color: rgb(255, 0, 0);
861
- background-color: rgb(0, 0, 255);
862
- }
863
- `,
864
- })
865
- await new Promise((r) => setTimeout(r, 100))
866
- const stylesheets = await editor.list({ pattern: /inline-css:/ })
867
- expect(stylesheets.length).toBeGreaterThan(0)
868
-
869
- const cssMatches = await editor.grep({ regex: /editor-test-element/, pattern: /inline-css:/ })
870
- expect(cssMatches.length).toBeGreaterThan(0)
871
-
872
- const cssMatch = cssMatches[0]
873
- const { content, totalLines } = await editor.read({ url: cssMatch.url })
874
- expect(content).toContain('editor-test-element')
875
- expect(content).toContain('rgb(255, 0, 0)')
876
- expect(totalLines).toBeGreaterThan(0)
877
-
878
- await cdpPage!.evaluate(() => {
879
- const el = document.createElement('div')
880
- el.className = 'editor-test-element'
881
- el.id = 'test-div'
882
- el.textContent = 'Test'
883
- document.body.appendChild(el)
884
- })
885
-
886
- const colorBefore = await cdpPage!.evaluate(() => {
887
- const el = document.getElementById('test-div')!
888
- return window.getComputedStyle(el).color
889
- })
890
- expect(colorBefore).toBe('rgb(255, 0, 0)')
891
-
892
- await editor.edit({
893
- url: cssMatch.url,
894
- oldString: 'color: rgb(255, 0, 0);',
895
- newString: 'color: rgb(0, 255, 0);',
896
- })
897
-
898
- const colorAfter = await cdpPage!.evaluate(() => {
899
- const el = document.getElementById('test-div')!
900
- return window.getComputedStyle(el).color
901
- })
902
- expect(colorAfter).toBe('rgb(0, 255, 0)')
903
-
904
- await cdpSession.detach()
905
- await browser.close()
906
- await page.close()
907
- }, 60000)
908
-
909
- it('should inject bippy and find React fiber with getReactSource', async () => {
910
- const browserContext = getBrowserContext()
911
- const serviceWorker = await getExtensionServiceWorker(browserContext)
912
-
913
- const page = await browserContext.newPage()
914
- await page.setContent(`
915
- <!DOCTYPE html>
916
- <html>
917
- <head>
918
- <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
919
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
920
- </head>
921
- <body>
922
- <div id="root"></div>
923
- <script>
924
- function SaveButton(props) {
925
- return React.createElement('button', { id: 'react-btn' }, props.label);
926
- }
927
- function Panel() {
928
- return React.createElement('section', null, React.createElement(SaveButton, {
929
- label: 'Click me',
930
- count: 3,
931
- config: { variant: 'primary' },
932
- onClick: () => {}
933
- }));
934
- }
935
- function App() {
936
- return React.createElement(Panel);
937
- }
938
- const root = ReactDOM.createRoot(document.getElementById('root'));
939
- root.render(React.createElement(App));
940
- </script>
941
- <button id="plain-btn">Plain</button>
942
- </body>
943
- </html>
944
- `)
945
- await page.bringToFront()
946
-
947
- await serviceWorker.evaluate(async () => {
948
- await globalThis.toggleExtensionForActiveTab()
949
- })
950
- await new Promise((r) => setTimeout(r, 500))
951
-
952
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
953
- const pages = browser.contexts()[0].pages()
954
- const cdpPage = pages.find((p) => p.url().startsWith('about:'))
955
- expect(cdpPage).toBeDefined()
956
-
957
- const btn = cdpPage!.locator('#react-btn')
958
- const btnCount = await btn.count()
959
- expect(btnCount).toBe(1)
960
-
961
- const hasBippyBefore = await cdpPage!.evaluate(() => !!globalThis.__bippy)
962
- expect(hasBippyBefore).toBe(false)
963
-
964
- const wsUrl = getCdpUrl({ port: TEST_PORT })
965
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
966
-
967
- const { getReactSource, getReactComponentInfo } = await import('./react-source.js')
968
- const source = await getReactSource({ locator: btn, cdp: cdpSession })
969
- const info = await getReactComponentInfo({ locator: btn, cdp: cdpSession })
970
- const plainInfo = await getReactComponentInfo({ locator: cdpPage!.locator('#plain-btn'), cdp: cdpSession })
971
-
972
- const hasBippyAfter = await cdpPage!.evaluate(() => !!globalThis.__bippy)
973
- expect(hasBippyAfter).toBe(true)
974
-
975
- const hasFiber = await btn.evaluate((el) => {
976
- const bippy = globalThis.__bippy
977
- if (!bippy) return false
978
- const fiber = bippy.getFiberFromHostInstance(el)
979
- return !!fiber
980
- })
981
- expect(hasFiber).toBe(true)
982
-
983
- const componentName = await btn.evaluate((el) => {
984
- const bippy = globalThis.__bippy
985
- if (!bippy) return null
986
- const fiber = bippy.getFiberFromHostInstance(el)
987
- let current = fiber
988
- while (current) {
989
- if (bippy.isCompositeFiber(current)) {
990
- return bippy.getDisplayName(current.type)
991
- }
992
- current = current.return ?? null
993
- }
994
- return null
995
- })
996
- expect(componentName).toBe('SaveButton')
997
- expect(plainInfo).toBe(null)
998
- expect(info?.componentName).toBe('SaveButton')
999
- expect(info?.hierarchy.map((item) => item.componentName)).toEqual(['SaveButton', 'Panel', 'App'])
1000
- expect(info?.props).toEqual({
1001
- label: 'Click me',
1002
- count: 3,
1003
- config: { variant: 'primary' },
1004
- onClick: '[function]',
1005
- })
1006
-
1007
- console.log('Component name from fiber:', componentName)
1008
- console.log('React component info:', info)
1009
- console.log('Source location (null for UMD React, works on local dev servers with JSX transform):', source)
1010
-
1011
- await browser.close()
1012
- await page.close()
1013
- }, 60000)
1014
- })
1015
-
1016
- // --- Service Worker Target Tests ---
1017
-
1018
- describe('Service Worker Target Tests', () => {
1019
- let testCtx: TestContext | null = null
1020
-
1021
- beforeAll(async () => {
1022
- testCtx = await setupTestContext({ port: TEST_PORT, tempDirPrefix: 'pw-sw-test-', toggleExtension: true })
1023
- }, 600000)
1024
-
1025
- afterAll(async () => {
1026
- await cleanupTestContext(testCtx)
1027
- testCtx = null
1028
- })
1029
-
1030
- const getBrowserContext = () => {
1031
- if (!testCtx?.browserContext) throw new Error('Browser not initialized')
1032
- return testCtx.browserContext
1033
- }
1034
-
1035
- it('should not expose service worker targets to Playwright (issue #14)', async () => {
1036
- const browserContext = getBrowserContext()
1037
- const serviceWorker = await getExtensionServiceWorker(browserContext)
1038
-
1039
- const page = await browserContext.newPage()
1040
- await page.goto('https://web.dev/', { waitUntil: 'load' })
1041
- await page.bringToFront()
1042
-
1043
- await serviceWorker.evaluate(async () => {
1044
- await globalThis.toggleExtensionForActiveTab()
1045
- })
1046
- await new Promise((r) => setTimeout(r, 500))
1047
-
1048
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
1049
- const context = browser.contexts()[0]
1050
-
1051
- const pages = context.pages()
1052
-
1053
- for (const p of pages) {
1054
- const url = p.url()
1055
- console.log('Page URL:', url)
1056
- expect(url).not.toMatch(/sw\.js$/i)
1057
- expect(url).not.toMatch(/service.?worker/i)
1058
- }
1059
-
1060
- const targetPage = pages.find((p) => p.url().includes('web.dev'))
1061
- expect(targetPage).toBeDefined()
1062
-
1063
- const title = await targetPage!.title()
1064
- expect(title).toBeTruthy()
1065
-
1066
- await safeCloseCDPBrowser(browser)
1067
- await page.close()
1068
- }, 60000)
1069
-
1070
- it('should allow reading response bodies after re-enabling Network buffering', async () => {
1071
- const browserContext = getBrowserContext()
1072
- const serviceWorker = await getExtensionServiceWorker(browserContext)
1073
-
1074
- const page = await browserContext.newPage()
1075
- await page.goto('https://example.com/')
1076
- await page.bringToFront()
1077
-
1078
- await serviceWorker.evaluate(async () => {
1079
- await globalThis.toggleExtensionForActiveTab()
1080
- })
1081
- await new Promise((r) => setTimeout(r, 100))
1082
-
1083
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
1084
- const cdpPage = browser
1085
- .contexts()[0]
1086
- .pages()
1087
- .find((p) => p.url().includes('example.com'))
1088
- expect(cdpPage).toBeDefined()
1089
-
1090
- const wsUrl = getCdpUrl({ port: TEST_PORT })
1091
- const cdpSession = await getCDPSessionForPage({ page: cdpPage! })
1092
-
1093
- await cdpSession.send('Network.disable')
1094
- await cdpSession.send('Network.enable', {
1095
- maxTotalBufferSize: 10000000,
1096
- maxResourceBufferSize: 5000000,
1097
- })
1098
-
1099
- const [response] = await Promise.all([
1100
- cdpPage!.waitForResponse((resp) => resp.url() === 'https://example.com/'),
1101
- cdpPage!.goto('https://example.com/'),
1102
- ])
1103
-
1104
- const body = await response.text()
1105
-
1106
- expect(body).toBeDefined()
1107
- expect(body).toContain('Example Domain')
1108
- expect(body).toContain('</html>')
1109
-
1110
- await cdpSession.detach()
1111
- await browser.close()
1112
- await page.close()
1113
- }, 60000)
1114
-
1115
- it('should stream SSE without waiting for response end', async () => {
1116
- const browserContext = getBrowserContext()
1117
- const serviceWorker = await withTimeout({
1118
- promise: getExtensionServiceWorker(browserContext),
1119
- timeoutMs: 5000,
1120
- errorMessage: 'getExtensionServiceWorker timed out',
1121
- })
1122
- const sseServer = await withTimeout({
1123
- promise: createSseServer(),
1124
- timeoutMs: 5000,
1125
- errorMessage: 'createSseServer timed out',
1126
- })
1127
- let page: Awaited<ReturnType<typeof browserContext.newPage>> | null = null
1128
- let browser: Awaited<ReturnType<typeof chromium.connectOverCDP>> | null = null
1129
-
1130
- try {
1131
- page = await withTimeout({
1132
- promise: browserContext.newPage(),
1133
- timeoutMs: 5000,
1134
- errorMessage: 'newPage timed out',
1135
- })
1136
- await withTimeout({
1137
- promise: page.goto(`${sseServer.baseUrl}/`),
1138
- timeoutMs: 5000,
1139
- errorMessage: 'page.goto timed out',
1140
- })
1141
- await page.bringToFront()
1142
-
1143
- await withTimeout({
1144
- promise: serviceWorker.evaluate(async () => {
1145
- await globalThis.toggleExtensionForActiveTab()
1146
- }),
1147
- timeoutMs: 5000,
1148
- errorMessage: 'toggleExtensionForActiveTab timed out',
1149
- })
1150
- await new Promise((resolve) => {
1151
- setTimeout(resolve, 100)
1152
- })
1153
-
1154
- browser = await withTimeout({
1155
- promise: chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT })),
1156
- timeoutMs: 5000,
1157
- errorMessage: 'connectOverCDP timed out',
1158
- })
1159
- const cdpPage = browser
1160
- .contexts()[0]
1161
- .pages()
1162
- .find((p) => {
1163
- return p.url().startsWith(sseServer.baseUrl)
1164
- })
1165
- expect(cdpPage).toBeDefined()
1166
-
1167
- await cdpPage!.evaluate(() => {
1168
- return window.startSse()
1169
- })
1170
- await withTimeout({
1171
- promise: cdpPage!.waitForFunction(
1172
- () => {
1173
- return window.__sseMessages.length > 0
1174
- },
1175
- { timeout: 5000 },
1176
- ),
1177
- timeoutMs: 7000,
1178
- errorMessage: 'SSE message not received in time',
1179
- })
1180
-
1181
- const firstMessage = await cdpPage!.evaluate(() => {
1182
- return window.__sseMessages[0]
1183
- })
1184
- expect(firstMessage).toBe('hello')
1185
-
1186
- const sseState = sseServer.getState()
1187
- expect(sseState.connected).toBe(true)
1188
- expect(sseState.finished).toBe(false)
1189
- expect(sseState.closed).toBe(false)
1190
- expect(sseState.writeCount).toBeGreaterThan(0)
1191
-
1192
- const readyState = await cdpPage!.evaluate(() => {
1193
- if (!window.__sseSource) {
1194
- return -1
1195
- }
1196
- return window.__sseSource.readyState
1197
- })
1198
- expect(readyState).toBe(1)
1199
-
1200
- await cdpPage!.evaluate(() => {
1201
- window.stopSse()
1202
- })
1203
- await new Promise((resolve) => {
1204
- setTimeout(resolve, 100)
1205
- })
1206
- } finally {
1207
- if (browser) {
1208
- await withTimeout({
1209
- promise: browser.close(),
1210
- timeoutMs: 5000,
1211
- errorMessage: 'browser.close timed out',
1212
- })
1213
- }
1214
- if (page) {
1215
- await withTimeout({
1216
- promise: page.close(),
1217
- timeoutMs: 5000,
1218
- errorMessage: 'page.close timed out',
1219
- })
1220
- }
1221
- await withTimeout({
1222
- promise: sseServer.close(),
1223
- timeoutMs: 5000,
1224
- errorMessage: 'sseServer.close timed out',
1225
- })
1226
- }
1227
- }, 60000)
1228
- })
1229
-
1230
- // --- Auto-enable Tests ---
1231
-
1232
- describe('Auto-enable Tests', () => {
1233
- let testCtx: TestContext | null = null
1234
- let client: Awaited<ReturnType<typeof createMCPClient>>['client']
1235
- let cleanup: (() => Promise<void>) | null = null
1236
-
1237
- beforeAll(async () => {
1238
- process.env.PLAYWRITER_AUTO_ENABLE = '1'
1239
- testCtx = await setupTestContext({ port: TEST_PORT, tempDirPrefix: 'pw-auto-test-' })
1240
-
1241
- const result = await createMCPClient({ port: TEST_PORT })
1242
- client = result.client
1243
- cleanup = result.cleanup
1244
-
1245
- // Disconnect all tabs to start with a clean state
1246
- const serviceWorker = await getExtensionServiceWorker(testCtx.browserContext)
1247
- await serviceWorker.evaluate(async () => {
1248
- await globalThis.disconnectEverything()
1249
- })
1250
- await new Promise((r) => setTimeout(r, 100))
1251
- }, 600000)
1252
-
1253
- afterAll(async () => {
1254
- delete process.env.PLAYWRITER_AUTO_ENABLE
1255
- await cleanupTestContext(testCtx, cleanup)
1256
- cleanup = null
1257
- testCtx = null
1258
- })
1259
-
1260
- const getBrowserContext = () => {
1261
- if (!testCtx?.browserContext) throw new Error('Browser not initialized')
1262
- return testCtx.browserContext
1263
- }
1264
-
1265
- it('should auto-create a tab when Playwright connects and no tabs exist', async () => {
1266
- const browserContext = getBrowserContext()
1267
- const serviceWorker = await getExtensionServiceWorker(browserContext)
1268
-
1269
- await serviceWorker.evaluate(async () => {
1270
- await globalThis.disconnectEverything()
1271
- })
1272
- await new Promise((r) => setTimeout(r, 100))
1273
-
1274
- const tabCountBefore = await serviceWorker.evaluate(() => {
1275
- const state = globalThis.getExtensionState()
1276
- return state.tabs.size
1277
- })
1278
- expect(tabCountBefore).toBe(0)
1279
-
1280
- const previousAutoEnable = process.env.PLAYWRITER_AUTO_ENABLE
1281
- delete process.env.PLAYWRITER_AUTO_ENABLE
1282
- const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT })).finally(() => {
1283
- if (previousAutoEnable === undefined) {
1284
- delete process.env.PLAYWRITER_AUTO_ENABLE
1285
- return
1286
- }
1287
- process.env.PLAYWRITER_AUTO_ENABLE = previousAutoEnable
1288
- })
1289
-
1290
- const pages = browser.contexts()[0].pages()
1291
- expect(pages.length).toBeGreaterThan(0)
1292
- expect(pages.length).toBe(1)
1293
-
1294
- const autoCreatedPage = pages[0]
1295
- expect(autoCreatedPage.url()).toBe('about:blank')
1296
-
1297
- const tabCountAfter = await serviceWorker.evaluate(() => {
1298
- const state = globalThis.getExtensionState()
1299
- return state.tabs.size
1300
- })
1301
- expect(tabCountAfter).toBe(1)
1302
-
1303
- await autoCreatedPage.setContent('<h1>Auto-created page</h1>')
1304
- const title = await autoCreatedPage.locator('h1').textContent()
1305
- expect(title).toBe('Auto-created page')
1306
-
1307
- await browser.close()
1308
- }, 60000)
1309
-
1310
- it('should auto-create a page when MCP executes with no connected pages', async () => {
1311
- const browserContext = getBrowserContext()
1312
- const serviceWorker = await getExtensionServiceWorker(browserContext)
1313
-
1314
- await serviceWorker.evaluate(async () => {
1315
- await globalThis.disconnectEverything()
1316
- })
1317
- await new Promise((r) => {
1318
- setTimeout(r, 100)
1319
- })
1320
-
1321
- const tabCountBefore = await serviceWorker.evaluate(() => {
1322
- const state = globalThis.getExtensionState()
1323
- return state.tabs.size
1324
- })
1325
- expect(tabCountBefore).toBe(0)
1326
-
1327
- const result = await client.callTool({
1328
- name: 'execute',
1329
- arguments: {
1330
- code: js`
1331
- return { pageCount: context.pages().length, url: page.url() };
1332
- `,
1333
- },
1334
- })
1335
-
1336
- expect((result as any).isError).toBeFalsy()
1337
- const text = (result as any).content[0].text
1338
- expect(text).toContain('pageCount')
1339
- expect(text).toContain('about:blank')
1340
-
1341
- const tabCountAfter = await serviceWorker.evaluate(() => {
1342
- const state = globalThis.getExtensionState()
1343
- return state.tabs.size
1344
- })
1345
- expect(tabCountAfter).toBe(1)
1346
-
1347
- await client.callTool({
1348
- name: 'execute',
1349
- arguments: {
1350
- code: js`
1351
- await page.close();
1352
- return { remaining: context.pages().length };
1353
- `,
1354
- },
1355
- })
1356
-
1357
- await new Promise((r) => {
1358
- setTimeout(r, 100)
1359
- })
1360
-
1361
- const afterCloseResult = await client.callTool({
1362
- name: 'execute',
1363
- arguments: {
1364
- code: js`
1365
- return { pageCount: context.pages().length, url: page.url() };
1366
- `,
1367
- },
1368
- })
1369
-
1370
- expect((afterCloseResult as any).isError).toBeFalsy()
1371
- const afterCloseText = (afterCloseResult as any).content[0].text
1372
- expect(afterCloseText).toContain('pageCount')
1373
- expect(afterCloseText).toContain('about:blank')
1374
- }, 60000)
1375
- })