tabwright 1.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 (599) hide show
  1. package/LICENSE +21 -0
  2. package/bin.js +4 -0
  3. package/dist/a11y-client.js +204 -0
  4. package/dist/agent-skills/tabwright/SKILL.md +119 -0
  5. package/dist/aria-snapshot.d.ts +262 -0
  6. package/dist/aria-snapshot.d.ts.map +1 -0
  7. package/dist/aria-snapshot.js +1256 -0
  8. package/dist/aria-snapshot.js.map +1 -0
  9. package/dist/aria-snapshot.test.d.ts +2 -0
  10. package/dist/aria-snapshot.test.d.ts.map +1 -0
  11. package/dist/aria-snapshot.test.js +270 -0
  12. package/dist/aria-snapshot.test.js.map +1 -0
  13. package/dist/aria-snapshot.unit.test.d.ts +2 -0
  14. package/dist/aria-snapshot.unit.test.d.ts.map +1 -0
  15. package/dist/aria-snapshot.unit.test.js +542 -0
  16. package/dist/aria-snapshot.unit.test.js.map +1 -0
  17. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts +5 -0
  18. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts.map +1 -0
  19. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js +5 -0
  20. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js.map +1 -0
  21. package/dist/bippy.js +894 -0
  22. package/dist/browser-config.d.ts +13 -0
  23. package/dist/browser-config.d.ts.map +1 -0
  24. package/dist/browser-config.js +126 -0
  25. package/dist/browser-config.js.map +1 -0
  26. package/dist/browser-install.d.ts +16 -0
  27. package/dist/browser-install.d.ts.map +1 -0
  28. package/dist/browser-install.js +238 -0
  29. package/dist/browser-install.js.map +1 -0
  30. package/dist/browser-launch.d.ts +17 -0
  31. package/dist/browser-launch.d.ts.map +1 -0
  32. package/dist/browser-launch.js +44 -0
  33. package/dist/browser-launch.js.map +1 -0
  34. package/dist/builtin-capabilities.d.ts +28 -0
  35. package/dist/builtin-capabilities.d.ts.map +1 -0
  36. package/dist/builtin-capabilities.js +2673 -0
  37. package/dist/builtin-capabilities.js.map +1 -0
  38. package/dist/capability-agent-skill.d.ts +60 -0
  39. package/dist/capability-agent-skill.d.ts.map +1 -0
  40. package/dist/capability-agent-skill.js +225 -0
  41. package/dist/capability-agent-skill.js.map +1 -0
  42. package/dist/capability-auth-state.d.ts +34 -0
  43. package/dist/capability-auth-state.d.ts.map +1 -0
  44. package/dist/capability-auth-state.js +157 -0
  45. package/dist/capability-auth-state.js.map +1 -0
  46. package/dist/capability-auth.d.ts +36 -0
  47. package/dist/capability-auth.d.ts.map +1 -0
  48. package/dist/capability-auth.js +198 -0
  49. package/dist/capability-auth.js.map +1 -0
  50. package/dist/capability-cli-confirmation.test.d.ts +2 -0
  51. package/dist/capability-cli-confirmation.test.d.ts.map +1 -0
  52. package/dist/capability-cli-confirmation.test.js +79 -0
  53. package/dist/capability-cli-confirmation.test.js.map +1 -0
  54. package/dist/capability-options.d.ts +20 -0
  55. package/dist/capability-options.d.ts.map +1 -0
  56. package/dist/capability-options.js +31 -0
  57. package/dist/capability-options.js.map +1 -0
  58. package/dist/capability-options.test.d.ts +2 -0
  59. package/dist/capability-options.test.d.ts.map +1 -0
  60. package/dist/capability-options.test.js +81 -0
  61. package/dist/capability-options.test.js.map +1 -0
  62. package/dist/capability-package.d.ts +27 -0
  63. package/dist/capability-package.d.ts.map +1 -0
  64. package/dist/capability-package.js +444 -0
  65. package/dist/capability-package.js.map +1 -0
  66. package/dist/capability-package.test.d.ts +2 -0
  67. package/dist/capability-package.test.d.ts.map +1 -0
  68. package/dist/capability-package.test.js +179 -0
  69. package/dist/capability-package.test.js.map +1 -0
  70. package/dist/capability-registry.d.ts +262 -0
  71. package/dist/capability-registry.d.ts.map +1 -0
  72. package/dist/capability-registry.js +931 -0
  73. package/dist/capability-registry.js.map +1 -0
  74. package/dist/capability-registry.test.d.ts +2 -0
  75. package/dist/capability-registry.test.d.ts.map +1 -0
  76. package/dist/capability-registry.test.js +1683 -0
  77. package/dist/capability-registry.test.js.map +1 -0
  78. package/dist/capability-runner.d.ts +99 -0
  79. package/dist/capability-runner.d.ts.map +1 -0
  80. package/dist/capability-runner.js +608 -0
  81. package/dist/capability-runner.js.map +1 -0
  82. package/dist/capability-search-relevance.test.d.ts +2 -0
  83. package/dist/capability-search-relevance.test.d.ts.map +1 -0
  84. package/dist/capability-search-relevance.test.js +67 -0
  85. package/dist/capability-search-relevance.test.js.map +1 -0
  86. package/dist/capability-studio.d.ts +11 -0
  87. package/dist/capability-studio.d.ts.map +1 -0
  88. package/dist/capability-studio.js +593 -0
  89. package/dist/capability-studio.js.map +1 -0
  90. package/dist/capability-studio.test.d.ts +2 -0
  91. package/dist/capability-studio.test.d.ts.map +1 -0
  92. package/dist/capability-studio.test.js +41 -0
  93. package/dist/capability-studio.test.js.map +1 -0
  94. package/dist/cdp-log.d.ts +19 -0
  95. package/dist/cdp-log.d.ts.map +1 -0
  96. package/dist/cdp-log.js +83 -0
  97. package/dist/cdp-log.js.map +1 -0
  98. package/dist/cdp-log.test.d.ts +2 -0
  99. package/dist/cdp-log.test.d.ts.map +1 -0
  100. package/dist/cdp-log.test.js +109 -0
  101. package/dist/cdp-log.test.js.map +1 -0
  102. package/dist/cdp-relay.d.ts +19 -0
  103. package/dist/cdp-relay.d.ts.map +1 -0
  104. package/dist/cdp-relay.js +2375 -0
  105. package/dist/cdp-relay.js.map +1 -0
  106. package/dist/cdp-session.d.ts +37 -0
  107. package/dist/cdp-session.d.ts.map +1 -0
  108. package/dist/cdp-session.js +36 -0
  109. package/dist/cdp-session.js.map +1 -0
  110. package/dist/cdp-types.d.ts +65 -0
  111. package/dist/cdp-types.d.ts.map +1 -0
  112. package/dist/cdp-types.js +91 -0
  113. package/dist/cdp-types.js.map +1 -0
  114. package/dist/channel-owner-inspect.test.d.ts +2 -0
  115. package/dist/channel-owner-inspect.test.d.ts.map +1 -0
  116. package/dist/channel-owner-inspect.test.js +75 -0
  117. package/dist/channel-owner-inspect.test.js.map +1 -0
  118. package/dist/chrome-discovery.d.ts +65 -0
  119. package/dist/chrome-discovery.d.ts.map +1 -0
  120. package/dist/chrome-discovery.js +173 -0
  121. package/dist/chrome-discovery.js.map +1 -0
  122. package/dist/chrome-discovery.test.d.ts +2 -0
  123. package/dist/chrome-discovery.test.d.ts.map +1 -0
  124. package/dist/chrome-discovery.test.js +41 -0
  125. package/dist/chrome-discovery.test.js.map +1 -0
  126. package/dist/clean-html.d.ts +11 -0
  127. package/dist/clean-html.d.ts.map +1 -0
  128. package/dist/clean-html.js +106 -0
  129. package/dist/clean-html.js.map +1 -0
  130. package/dist/cli-help.test.d.ts +2 -0
  131. package/dist/cli-help.test.d.ts.map +1 -0
  132. package/dist/cli-help.test.js +98 -0
  133. package/dist/cli-help.test.js.map +1 -0
  134. package/dist/cli.d.ts +3 -0
  135. package/dist/cli.d.ts.map +1 -0
  136. package/dist/cli.js +2709 -0
  137. package/dist/cli.js.map +1 -0
  138. package/dist/cloud-client.d.ts +56 -0
  139. package/dist/cloud-client.d.ts.map +1 -0
  140. package/dist/cloud-client.js +127 -0
  141. package/dist/cloud-client.js.map +1 -0
  142. package/dist/create-logger.d.ts +9 -0
  143. package/dist/create-logger.d.ts.map +1 -0
  144. package/dist/create-logger.js +27 -0
  145. package/dist/create-logger.js.map +1 -0
  146. package/dist/debugger-api.md +458 -0
  147. package/dist/debugger-examples-types.d.ts +24 -0
  148. package/dist/debugger-examples-types.d.ts.map +1 -0
  149. package/dist/debugger-examples-types.js +2 -0
  150. package/dist/debugger-examples-types.js.map +1 -0
  151. package/dist/debugger-examples.d.ts +6 -0
  152. package/dist/debugger-examples.d.ts.map +1 -0
  153. package/dist/debugger-examples.js +53 -0
  154. package/dist/debugger-examples.js.map +1 -0
  155. package/dist/debugger.d.ts +381 -0
  156. package/dist/debugger.d.ts.map +1 -0
  157. package/dist/debugger.js +630 -0
  158. package/dist/debugger.js.map +1 -0
  159. package/dist/diff-utils.d.ts +20 -0
  160. package/dist/diff-utils.d.ts.map +1 -0
  161. package/dist/diff-utils.js +50 -0
  162. package/dist/diff-utils.js.map +1 -0
  163. package/dist/diff-utils.test.d.ts +2 -0
  164. package/dist/diff-utils.test.d.ts.map +1 -0
  165. package/dist/diff-utils.test.js +137 -0
  166. package/dist/diff-utils.test.js.map +1 -0
  167. package/dist/doctor.d.ts +44 -0
  168. package/dist/doctor.d.ts.map +1 -0
  169. package/dist/doctor.js +246 -0
  170. package/dist/doctor.js.map +1 -0
  171. package/dist/doctor.test.d.ts +2 -0
  172. package/dist/doctor.test.d.ts.map +1 -0
  173. package/dist/doctor.test.js +208 -0
  174. package/dist/doctor.test.js.map +1 -0
  175. package/dist/editor-api.md +374 -0
  176. package/dist/editor-examples.d.ts +11 -0
  177. package/dist/editor-examples.d.ts.map +1 -0
  178. package/dist/editor-examples.js +124 -0
  179. package/dist/editor-examples.js.map +1 -0
  180. package/dist/editor.d.ts +203 -0
  181. package/dist/editor.d.ts.map +1 -0
  182. package/dist/editor.js +363 -0
  183. package/dist/editor.js.map +1 -0
  184. package/dist/env-compat.d.ts +2 -0
  185. package/dist/env-compat.d.ts.map +1 -0
  186. package/dist/env-compat.js +27 -0
  187. package/dist/env-compat.js.map +1 -0
  188. package/dist/executor.d.ts +229 -0
  189. package/dist/executor.d.ts.map +1 -0
  190. package/dist/executor.js +1465 -0
  191. package/dist/executor.js.map +1 -0
  192. package/dist/executor.unit.test.d.ts +2 -0
  193. package/dist/executor.unit.test.d.ts.map +1 -0
  194. package/dist/executor.unit.test.js +143 -0
  195. package/dist/executor.unit.test.js.map +1 -0
  196. package/dist/extension/_locales/en/messages.json +278 -0
  197. package/dist/extension/_locales/zh_CN/messages.json +256 -0
  198. package/dist/extension/assets/options-CYH96Lcx.css +1349 -0
  199. package/dist/extension/background.js +3994 -0
  200. package/dist/extension/icons/SolarCursorSquareBold.png +0 -0
  201. package/dist/extension/icons/icon-black-128.png +0 -0
  202. package/dist/extension/icons/icon-black-16.png +0 -0
  203. package/dist/extension/icons/icon-black-32.png +0 -0
  204. package/dist/extension/icons/icon-black-48.png +0 -0
  205. package/dist/extension/icons/icon-gray-128.png +0 -0
  206. package/dist/extension/icons/icon-gray-16.png +0 -0
  207. package/dist/extension/icons/icon-gray-32.png +0 -0
  208. package/dist/extension/icons/icon-gray-48.png +0 -0
  209. package/dist/extension/icons/icon-green-128.png +0 -0
  210. package/dist/extension/icons/icon-green-16.png +0 -0
  211. package/dist/extension/icons/icon-green-32.png +0 -0
  212. package/dist/extension/icons/icon-green-48.png +0 -0
  213. package/dist/extension/icons/tabwright-icon-black.png +0 -0
  214. package/dist/extension/icons/tabwright-icon-gray-disabled.png +0 -0
  215. package/dist/extension/icons/tabwright-icon-gray-disabled.svg +1 -0
  216. package/dist/extension/manifest.json +54 -0
  217. package/dist/extension/options.js +16145 -0
  218. package/dist/extension/rrweb-recorder.js +12952 -0
  219. package/dist/extension/src/options.html +165 -0
  220. package/dist/extension/src/prism-bash.min.js +1 -0
  221. package/dist/extension/src/prism.min.js +1 -0
  222. package/dist/extension/src/welcome.html +396 -0
  223. package/dist/extension-connection.test.d.ts +2 -0
  224. package/dist/extension-connection.test.d.ts.map +1 -0
  225. package/dist/extension-connection.test.js +708 -0
  226. package/dist/extension-connection.test.js.map +1 -0
  227. package/dist/extension-icon-warning.test.d.ts +2 -0
  228. package/dist/extension-icon-warning.test.d.ts.map +1 -0
  229. package/dist/extension-icon-warning.test.js +33 -0
  230. package/dist/extension-icon-warning.test.js.map +1 -0
  231. package/dist/ghost-browser.d.ts +62 -0
  232. package/dist/ghost-browser.d.ts.map +1 -0
  233. package/dist/ghost-browser.js +107 -0
  234. package/dist/ghost-browser.js.map +1 -0
  235. package/dist/ghost-cursor-client.js +374 -0
  236. package/dist/ghost-cursor-controller.d.ts +46 -0
  237. package/dist/ghost-cursor-controller.d.ts.map +1 -0
  238. package/dist/ghost-cursor-controller.js +98 -0
  239. package/dist/ghost-cursor-controller.js.map +1 -0
  240. package/dist/ghost-cursor.d.ts +27 -0
  241. package/dist/ghost-cursor.d.ts.map +1 -0
  242. package/dist/ghost-cursor.js +79 -0
  243. package/dist/ghost-cursor.js.map +1 -0
  244. package/dist/htmlrewrite.d.ts +8 -0
  245. package/dist/htmlrewrite.d.ts.map +1 -0
  246. package/dist/htmlrewrite.js +339 -0
  247. package/dist/htmlrewrite.js.map +1 -0
  248. package/dist/htmlrewrite.test.d.ts +2 -0
  249. package/dist/htmlrewrite.test.d.ts.map +1 -0
  250. package/dist/htmlrewrite.test.js +7975 -0
  251. package/dist/htmlrewrite.test.js.map +1 -0
  252. package/dist/index.d.ts +20 -0
  253. package/dist/index.d.ts.map +1 -0
  254. package/dist/index.js +11 -0
  255. package/dist/index.js.map +1 -0
  256. package/dist/kill-port.d.ts +29 -0
  257. package/dist/kill-port.d.ts.map +1 -0
  258. package/dist/kill-port.js +190 -0
  259. package/dist/kill-port.js.map +1 -0
  260. package/dist/kitty-graphics.d.ts +22 -0
  261. package/dist/kitty-graphics.d.ts.map +1 -0
  262. package/dist/kitty-graphics.js +71 -0
  263. package/dist/kitty-graphics.js.map +1 -0
  264. package/dist/kitty-graphics.test.d.ts +2 -0
  265. package/dist/kitty-graphics.test.d.ts.map +1 -0
  266. package/dist/kitty-graphics.test.js +125 -0
  267. package/dist/kitty-graphics.test.js.map +1 -0
  268. package/dist/locator-selector.test.d.ts +2 -0
  269. package/dist/locator-selector.test.d.ts.map +1 -0
  270. package/dist/locator-selector.test.js +98 -0
  271. package/dist/locator-selector.test.js.map +1 -0
  272. package/dist/mcp-client.d.ts +20 -0
  273. package/dist/mcp-client.d.ts.map +1 -0
  274. package/dist/mcp-client.js +56 -0
  275. package/dist/mcp-client.js.map +1 -0
  276. package/dist/mcp.d.ts +6 -0
  277. package/dist/mcp.d.ts.map +1 -0
  278. package/dist/mcp.js +511 -0
  279. package/dist/mcp.js.map +1 -0
  280. package/dist/on-mouse-action.test.d.ts +2 -0
  281. package/dist/on-mouse-action.test.d.ts.map +1 -0
  282. package/dist/on-mouse-action.test.js +180 -0
  283. package/dist/on-mouse-action.test.js.map +1 -0
  284. package/dist/options-page.test.d.ts +2 -0
  285. package/dist/options-page.test.d.ts.map +1 -0
  286. package/dist/options-page.test.js +773 -0
  287. package/dist/options-page.test.js.map +1 -0
  288. package/dist/package-paths.d.ts +4 -0
  289. package/dist/package-paths.d.ts.map +1 -0
  290. package/dist/package-paths.js +30 -0
  291. package/dist/package-paths.js.map +1 -0
  292. package/dist/page-markdown.d.ts +40 -0
  293. package/dist/page-markdown.d.ts.map +1 -0
  294. package/dist/page-markdown.js +181 -0
  295. package/dist/page-markdown.js.map +1 -0
  296. package/dist/performance-examples.d.ts +5 -0
  297. package/dist/performance-examples.d.ts.map +1 -0
  298. package/dist/performance-examples.js +112 -0
  299. package/dist/performance-examples.js.map +1 -0
  300. package/dist/performance-profiling.md +417 -0
  301. package/dist/playwright-import.d.ts +19 -0
  302. package/dist/playwright-import.d.ts.map +1 -0
  303. package/dist/playwright-import.js +40 -0
  304. package/dist/playwright-import.js.map +1 -0
  305. package/dist/popup-relocation.test.d.ts +7 -0
  306. package/dist/popup-relocation.test.d.ts.map +1 -0
  307. package/dist/popup-relocation.test.js +139 -0
  308. package/dist/popup-relocation.test.js.map +1 -0
  309. package/dist/product-paths.d.ts +9 -0
  310. package/dist/product-paths.d.ts.map +1 -0
  311. package/dist/product-paths.js +24 -0
  312. package/dist/product-paths.js.map +1 -0
  313. package/dist/product-paths.test.d.ts +2 -0
  314. package/dist/product-paths.test.d.ts.map +1 -0
  315. package/dist/product-paths.test.js +39 -0
  316. package/dist/product-paths.test.js.map +1 -0
  317. package/dist/prompt.md +1004 -0
  318. package/dist/protocol.d.ts +248 -0
  319. package/dist/protocol.d.ts.map +1 -0
  320. package/dist/protocol.js +59 -0
  321. package/dist/protocol.js.map +1 -0
  322. package/dist/protocol.test.d.ts +2 -0
  323. package/dist/protocol.test.d.ts.map +1 -0
  324. package/dist/protocol.test.js +31 -0
  325. package/dist/protocol.test.js.map +1 -0
  326. package/dist/react-source.d.ts +57 -0
  327. package/dist/react-source.d.ts.map +1 -0
  328. package/dist/react-source.js +254 -0
  329. package/dist/react-source.js.map +1 -0
  330. package/dist/readability.js +1674 -0
  331. package/dist/relay-client.d.ts +104 -0
  332. package/dist/relay-client.d.ts.map +1 -0
  333. package/dist/relay-client.js +426 -0
  334. package/dist/relay-client.js.map +1 -0
  335. package/dist/relay-client.test.d.ts +2 -0
  336. package/dist/relay-client.test.d.ts.map +1 -0
  337. package/dist/relay-client.test.js +305 -0
  338. package/dist/relay-client.test.js.map +1 -0
  339. package/dist/relay-core.test.d.ts +2 -0
  340. package/dist/relay-core.test.d.ts.map +1 -0
  341. package/dist/relay-core.test.js +1427 -0
  342. package/dist/relay-core.test.js.map +1 -0
  343. package/dist/relay-navigation.test.d.ts +2 -0
  344. package/dist/relay-navigation.test.d.ts.map +1 -0
  345. package/dist/relay-navigation.test.js +661 -0
  346. package/dist/relay-navigation.test.js.map +1 -0
  347. package/dist/relay-session.test.d.ts +2 -0
  348. package/dist/relay-session.test.d.ts.map +1 -0
  349. package/dist/relay-session.test.js +1121 -0
  350. package/dist/relay-session.test.js.map +1 -0
  351. package/dist/relay-state.d.ts +178 -0
  352. package/dist/relay-state.d.ts.map +1 -0
  353. package/dist/relay-state.js +346 -0
  354. package/dist/relay-state.js.map +1 -0
  355. package/dist/relay-state.test.d.ts +2 -0
  356. package/dist/relay-state.test.d.ts.map +1 -0
  357. package/dist/relay-state.test.js +594 -0
  358. package/dist/relay-state.test.js.map +1 -0
  359. package/dist/replay-ai-index.d.ts +99 -0
  360. package/dist/replay-ai-index.d.ts.map +1 -0
  361. package/dist/replay-ai-index.js +677 -0
  362. package/dist/replay-ai-index.js.map +1 -0
  363. package/dist/replay-ai-index.test.d.ts +2 -0
  364. package/dist/replay-ai-index.test.d.ts.map +1 -0
  365. package/dist/replay-ai-index.test.js +255 -0
  366. package/dist/replay-ai-index.test.js.map +1 -0
  367. package/dist/replay-cli.test.d.ts +2 -0
  368. package/dist/replay-cli.test.d.ts.map +1 -0
  369. package/dist/replay-cli.test.js +275 -0
  370. package/dist/replay-cli.test.js.map +1 -0
  371. package/dist/replay-eval.d.ts +44 -0
  372. package/dist/replay-eval.d.ts.map +1 -0
  373. package/dist/replay-eval.js +902 -0
  374. package/dist/replay-eval.js.map +1 -0
  375. package/dist/replay-handoff.d.ts +29 -0
  376. package/dist/replay-handoff.d.ts.map +1 -0
  377. package/dist/replay-handoff.js +83 -0
  378. package/dist/replay-handoff.js.map +1 -0
  379. package/dist/replay-handoff.test.d.ts +2 -0
  380. package/dist/replay-handoff.test.d.ts.map +1 -0
  381. package/dist/replay-handoff.test.js +123 -0
  382. package/dist/replay-handoff.test.js.map +1 -0
  383. package/dist/replay-workflow-compiler.d.ts +44 -0
  384. package/dist/replay-workflow-compiler.d.ts.map +1 -0
  385. package/dist/replay-workflow-compiler.js +601 -0
  386. package/dist/replay-workflow-compiler.js.map +1 -0
  387. package/dist/replay-workflow-compiler.test.d.ts +2 -0
  388. package/dist/replay-workflow-compiler.test.d.ts.map +1 -0
  389. package/dist/replay-workflow-compiler.test.js +223 -0
  390. package/dist/replay-workflow-compiler.test.js.map +1 -0
  391. package/dist/rrweb-recording-relay.d.ts +56 -0
  392. package/dist/rrweb-recording-relay.d.ts.map +1 -0
  393. package/dist/rrweb-recording-relay.js +299 -0
  394. package/dist/rrweb-recording-relay.js.map +1 -0
  395. package/dist/rrweb-recording-relay.test.d.ts +2 -0
  396. package/dist/rrweb-recording-relay.test.d.ts.map +1 -0
  397. package/dist/rrweb-recording-relay.test.js +85 -0
  398. package/dist/rrweb-recording-relay.test.js.map +1 -0
  399. package/dist/rrweb-recording.d.ts +104 -0
  400. package/dist/rrweb-recording.d.ts.map +1 -0
  401. package/dist/rrweb-recording.js +180 -0
  402. package/dist/rrweb-recording.js.map +1 -0
  403. package/dist/scoped-fs.d.ts +95 -0
  404. package/dist/scoped-fs.d.ts.map +1 -0
  405. package/dist/scoped-fs.js +358 -0
  406. package/dist/scoped-fs.js.map +1 -0
  407. package/dist/scoped-fs.test.d.ts +5 -0
  408. package/dist/scoped-fs.test.d.ts.map +1 -0
  409. package/dist/scoped-fs.test.js +73 -0
  410. package/dist/scoped-fs.test.js.map +1 -0
  411. package/dist/selector-generator.js +7378 -0
  412. package/dist/skill-stub.test.d.ts +2 -0
  413. package/dist/skill-stub.test.d.ts.map +1 -0
  414. package/dist/skill-stub.test.js +22 -0
  415. package/dist/skill-stub.test.js.map +1 -0
  416. package/dist/snapshot-tools.test.d.ts +2 -0
  417. package/dist/snapshot-tools.test.d.ts.map +1 -0
  418. package/dist/snapshot-tools.test.js +856 -0
  419. package/dist/snapshot-tools.test.js.map +1 -0
  420. package/dist/start-relay-server.d.ts +6 -0
  421. package/dist/start-relay-server.d.ts.map +1 -0
  422. package/dist/start-relay-server.js +57 -0
  423. package/dist/start-relay-server.js.map +1 -0
  424. package/dist/styles-api.md +124 -0
  425. package/dist/styles-examples.d.ts +8 -0
  426. package/dist/styles-examples.d.ts.map +1 -0
  427. package/dist/styles-examples.js +64 -0
  428. package/dist/styles-examples.js.map +1 -0
  429. package/dist/styles.d.ts +27 -0
  430. package/dist/styles.d.ts.map +1 -0
  431. package/dist/styles.js +231 -0
  432. package/dist/styles.js.map +1 -0
  433. package/dist/tabwright-agent-skill.d.ts +30 -0
  434. package/dist/tabwright-agent-skill.d.ts.map +1 -0
  435. package/dist/tabwright-agent-skill.js +74 -0
  436. package/dist/tabwright-agent-skill.js.map +1 -0
  437. package/dist/tabwright-agent-skill.test.d.ts +2 -0
  438. package/dist/tabwright-agent-skill.test.d.ts.map +1 -0
  439. package/dist/tabwright-agent-skill.test.js +51 -0
  440. package/dist/tabwright-agent-skill.test.js.map +1 -0
  441. package/dist/test-declarations.d.ts +13 -0
  442. package/dist/test-declarations.d.ts.map +1 -0
  443. package/dist/test-declarations.js +2 -0
  444. package/dist/test-declarations.js.map +1 -0
  445. package/dist/test-utils.d.ts +69 -0
  446. package/dist/test-utils.d.ts.map +1 -0
  447. package/dist/test-utils.js +448 -0
  448. package/dist/test-utils.js.map +1 -0
  449. package/dist/test-utils.test.d.ts +2 -0
  450. package/dist/test-utils.test.d.ts.map +1 -0
  451. package/dist/test-utils.test.js +55 -0
  452. package/dist/test-utils.test.js.map +1 -0
  453. package/dist/utils.d.ts +26 -0
  454. package/dist/utils.d.ts.map +1 -0
  455. package/dist/utils.js +60 -0
  456. package/dist/utils.js.map +1 -0
  457. package/dist/wait-for-page-load.d.ts +16 -0
  458. package/dist/wait-for-page-load.d.ts.map +1 -0
  459. package/dist/wait-for-page-load.js +146 -0
  460. package/dist/wait-for-page-load.js.map +1 -0
  461. package/dist/workflow-capability.d.ts +72 -0
  462. package/dist/workflow-capability.d.ts.map +1 -0
  463. package/dist/workflow-capability.js +316 -0
  464. package/dist/workflow-capability.js.map +1 -0
  465. package/dist/workflow-script-flow.test.d.ts +2 -0
  466. package/dist/workflow-script-flow.test.d.ts.map +1 -0
  467. package/dist/workflow-script-flow.test.js +285 -0
  468. package/dist/workflow-script-flow.test.js.map +1 -0
  469. package/package.json +80 -0
  470. package/src/__snapshots__/x.com.processed.html +1003 -0
  471. package/src/__snapshots__/x.com.processed.withStyles.html +2720 -0
  472. package/src/a11y-client.ts +241 -0
  473. package/src/agent-skills/conan-config/SKILL.md +141 -0
  474. package/src/agent-skills/conan-config/agents/openai.yaml +4 -0
  475. package/src/aria-snapshot.test.ts +311 -0
  476. package/src/aria-snapshot.ts +1681 -0
  477. package/src/aria-snapshot.unit.test.ts +615 -0
  478. package/src/aria-snapshots/github-interactive.txt +193 -0
  479. package/src/aria-snapshots/github-raw.txt +309 -0
  480. package/src/aria-snapshots/hackernews-interactive.txt +477 -0
  481. package/src/aria-snapshots/hackernews-raw.txt +428 -0
  482. package/src/aria-snapshots/prosemirror-interactive.txt +66 -0
  483. package/src/aria-snapshots/prosemirror-raw.txt +145 -0
  484. package/src/assets/aria-labels-hacker-news.png +0 -0
  485. package/src/assets/aria-labels-old-reddit.png +0 -0
  486. package/src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts +5 -0
  487. package/src/assets/cursors/screen-studio/pointer-macos-tahoe.svg +18 -0
  488. package/src/assets/x.com.html +32946 -0
  489. package/src/browser-config.ts +179 -0
  490. package/src/browser-install.ts +284 -0
  491. package/src/browser-launch.ts +75 -0
  492. package/src/builtin-capabilities.ts +2765 -0
  493. package/src/capability-agent-skill.ts +305 -0
  494. package/src/capability-auth-state.ts +197 -0
  495. package/src/capability-auth.ts +249 -0
  496. package/src/capability-cli-confirmation.test.ts +90 -0
  497. package/src/capability-options.test.ts +89 -0
  498. package/src/capability-options.ts +57 -0
  499. package/src/capability-package.test.ts +204 -0
  500. package/src/capability-package.ts +555 -0
  501. package/src/capability-registry.test.ts +1905 -0
  502. package/src/capability-registry.ts +1234 -0
  503. package/src/capability-runner.ts +801 -0
  504. package/src/capability-search-relevance.test.ts +73 -0
  505. package/src/capability-studio.test.ts +45 -0
  506. package/src/capability-studio.ts +641 -0
  507. package/src/cdp-log.test.ts +131 -0
  508. package/src/cdp-log.ts +112 -0
  509. package/src/cdp-relay.ts +2944 -0
  510. package/src/cdp-session.ts +77 -0
  511. package/src/cdp-types.ts +158 -0
  512. package/src/channel-owner-inspect.test.ts +94 -0
  513. package/src/chrome-discovery.test.ts +50 -0
  514. package/src/chrome-discovery.ts +220 -0
  515. package/src/clean-html.ts +141 -0
  516. package/src/cli-help.test.ts +123 -0
  517. package/src/cli.ts +3181 -0
  518. package/src/cloud-client.ts +179 -0
  519. package/src/create-logger.ts +38 -0
  520. package/src/debugger-examples-types.ts +16 -0
  521. package/src/debugger-examples.ts +66 -0
  522. package/src/debugger.ts +708 -0
  523. package/src/diff-utils.test.ts +152 -0
  524. package/src/diff-utils.ts +70 -0
  525. package/src/doctor.test.ts +244 -0
  526. package/src/doctor.ts +321 -0
  527. package/src/editor-examples.ts +158 -0
  528. package/src/editor.ts +426 -0
  529. package/src/env-compat.ts +28 -0
  530. package/src/executor.ts +1824 -0
  531. package/src/executor.unit.test.ts +168 -0
  532. package/src/extension-connection.test.ts +855 -0
  533. package/src/extension-icon-warning.test.ts +45 -0
  534. package/src/ghost-browser.ts +149 -0
  535. package/src/ghost-cursor-client.ts +494 -0
  536. package/src/ghost-cursor-controller.ts +129 -0
  537. package/src/ghost-cursor.ts +123 -0
  538. package/src/htmlrewrite.test.ts +8008 -0
  539. package/src/htmlrewrite.ts +390 -0
  540. package/src/index.ts +53 -0
  541. package/src/kill-port.ts +219 -0
  542. package/src/kitty-graphics.test.ts +139 -0
  543. package/src/kitty-graphics.ts +79 -0
  544. package/src/locator-selector.test.ts +119 -0
  545. package/src/mcp-client.ts +78 -0
  546. package/src/mcp.ts +629 -0
  547. package/src/on-mouse-action.test.ts +226 -0
  548. package/src/options-page.test.ts +839 -0
  549. package/src/package-paths.ts +38 -0
  550. package/src/page-markdown.ts +240 -0
  551. package/src/performance-examples.ts +186 -0
  552. package/src/playwright-import.ts +59 -0
  553. package/src/popup-relocation.test.ts +163 -0
  554. package/src/product-paths.test.ts +47 -0
  555. package/src/product-paths.ts +28 -0
  556. package/src/protocol.test.ts +52 -0
  557. package/src/protocol.ts +351 -0
  558. package/src/react-source.ts +379 -0
  559. package/src/relay-client.test.ts +375 -0
  560. package/src/relay-client.ts +572 -0
  561. package/src/relay-core.test.ts +1603 -0
  562. package/src/relay-navigation.test.ts +790 -0
  563. package/src/relay-session.test.ts +1375 -0
  564. package/src/relay-state.test.ts +729 -0
  565. package/src/relay-state.ts +561 -0
  566. package/src/replay-ai-index.test.ts +278 -0
  567. package/src/replay-ai-index.ts +872 -0
  568. package/src/replay-cli.test.ts +325 -0
  569. package/src/replay-eval.ts +1039 -0
  570. package/src/replay-handoff.test.ts +163 -0
  571. package/src/replay-handoff.ts +109 -0
  572. package/src/replay-workflow-compiler.test.ts +250 -0
  573. package/src/replay-workflow-compiler.ts +700 -0
  574. package/src/resource.md +409 -0
  575. package/src/rrweb-recording-relay.test.ts +99 -0
  576. package/src/rrweb-recording-relay.ts +400 -0
  577. package/src/rrweb-recording.ts +322 -0
  578. package/src/scoped-fs.test.ts +80 -0
  579. package/src/scoped-fs.ts +420 -0
  580. package/src/skill-stub.test.ts +24 -0
  581. package/src/skill.md +1413 -0
  582. package/src/snapshot-tools.test.ts +1017 -0
  583. package/src/snapshots/hacker-news-accessibility-full.md +81 -0
  584. package/src/snapshots/hacker-news-accessibility-interactive.md +43 -0
  585. package/src/snapshots/page-markdown-output.txt +16 -0
  586. package/src/snapshots/shadcn-ui-accessibility-full.md +201 -0
  587. package/src/snapshots/shadcn-ui-accessibility-interactive.md +109 -0
  588. package/src/start-relay-server.ts +69 -0
  589. package/src/styles-examples.ts +84 -0
  590. package/src/styles.ts +343 -0
  591. package/src/tabwright-agent-skill.test.ts +61 -0
  592. package/src/tabwright-agent-skill.ts +105 -0
  593. package/src/test-declarations.ts +13 -0
  594. package/src/test-utils.test.ts +70 -0
  595. package/src/test-utils.ts +566 -0
  596. package/src/utils.ts +78 -0
  597. package/src/wait-for-page-load.ts +198 -0
  598. package/src/workflow-capability.ts +417 -0
  599. package/src/workflow-script-flow.test.ts +388 -0
@@ -0,0 +1,856 @@
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 path from 'node:path';
5
+ import fs from 'node:fs';
6
+ import os from 'node:os';
7
+ import { imageSize } from 'image-size';
8
+ import { getCdpUrl } from './utils.js';
9
+ import { getCDPSessionForPage } from './cdp-session.js';
10
+ import { setupTestContext, cleanupTestContext, getExtensionServiceWorker, js } from './test-utils.js';
11
+ import './test-declarations.js';
12
+ const TEST_PORT = 19991;
13
+ describe('Snapshot & Screenshot Tests', () => {
14
+ let client;
15
+ let cleanup = null;
16
+ let testCtx = null;
17
+ beforeAll(async () => {
18
+ testCtx = await setupTestContext({ port: TEST_PORT, tempDirPrefix: 'pw-snap-test-', toggleExtension: true });
19
+ const result = await createMCPClient({ port: TEST_PORT });
20
+ client = result.client;
21
+ cleanup = result.cleanup;
22
+ }, 600000);
23
+ afterAll(async () => {
24
+ await cleanupTestContext(testCtx, cleanup);
25
+ cleanup = null;
26
+ testCtx = null;
27
+ });
28
+ const getBrowserContext = () => {
29
+ if (!testCtx?.browserContext)
30
+ throw new Error('Browser not initialized');
31
+ return testCtx.browserContext;
32
+ };
33
+ it('should capture screenshot correctly', async () => {
34
+ const browserContext = getBrowserContext();
35
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
36
+ const page = await browserContext.newPage();
37
+ await page.goto('https://example.com/');
38
+ await page.bringToFront();
39
+ await serviceWorker.evaluate(async () => {
40
+ await globalThis.toggleExtensionForActiveTab();
41
+ });
42
+ await new Promise((r) => setTimeout(r, 100));
43
+ const capturedCommands = [];
44
+ const commandHandler = ({ command }) => {
45
+ if (command.method === 'Page.captureScreenshot') {
46
+ capturedCommands.push(command);
47
+ }
48
+ };
49
+ testCtx.relayServer.on('cdp:command', commandHandler);
50
+ const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }));
51
+ const cdpPage = browser
52
+ .contexts()[0]
53
+ .pages()
54
+ .find((p) => p.url().includes('example.com'));
55
+ expect(cdpPage).toBeDefined();
56
+ const viewportSize = cdpPage.viewportSize();
57
+ console.log('Viewport size:', viewportSize);
58
+ const viewportScreenshot = await cdpPage.screenshot();
59
+ expect(viewportScreenshot).toBeDefined();
60
+ const viewportDimensions = imageSize(viewportScreenshot);
61
+ console.log('Viewport screenshot dimensions:', viewportDimensions);
62
+ expect(viewportDimensions.width).toBeGreaterThan(0);
63
+ expect(viewportDimensions.height).toBeGreaterThan(0);
64
+ if (viewportSize) {
65
+ expect(viewportDimensions.width).toBe(viewportSize.width);
66
+ expect(viewportDimensions.height).toBe(viewportSize.height);
67
+ }
68
+ const fullPageScreenshot = await cdpPage.screenshot({ fullPage: true });
69
+ expect(fullPageScreenshot).toBeDefined();
70
+ const fullPageDimensions = imageSize(fullPageScreenshot);
71
+ console.log('Full page screenshot dimensions:', fullPageDimensions);
72
+ expect(fullPageDimensions.width).toBeGreaterThan(0);
73
+ expect(fullPageDimensions.height).toBeGreaterThan(0);
74
+ expect(fullPageDimensions.width).toBeGreaterThanOrEqual(viewportDimensions.width);
75
+ testCtx.relayServer.off('cdp:command', commandHandler);
76
+ expect(capturedCommands.length).toBe(2);
77
+ expect(capturedCommands.map((c) => ({
78
+ method: c.method,
79
+ params: c.params,
80
+ }))).toMatchInlineSnapshot(`
81
+ [
82
+ {
83
+ "method": "Page.captureScreenshot",
84
+ "params": {
85
+ "captureBeyondViewport": false,
86
+ "clip": {
87
+ "height": 720,
88
+ "scale": 1,
89
+ "width": 1280,
90
+ "x": 0,
91
+ "y": 0,
92
+ },
93
+ "format": "png",
94
+ },
95
+ },
96
+ {
97
+ "method": "Page.captureScreenshot",
98
+ "params": {
99
+ "captureBeyondViewport": false,
100
+ "clip": {
101
+ "height": 581,
102
+ "scale": 1,
103
+ "width": 1280,
104
+ "x": 0,
105
+ "y": 0,
106
+ },
107
+ "format": "png",
108
+ },
109
+ },
110
+ ]
111
+ `);
112
+ const screenshotPath = path.join(os.tmpdir(), 'tabwright-test-screenshot.png');
113
+ fs.writeFileSync(screenshotPath, viewportScreenshot);
114
+ console.log('Screenshot saved to:', screenshotPath);
115
+ await browser.close();
116
+ await page.close();
117
+ }, 60000);
118
+ it('should match window.innerWidth/Height without clip param', async () => {
119
+ // Proves that in connectOverCDP mode, Playwright already queries
120
+ // window.innerWidth/innerHeight for viewport screenshots, so passing
121
+ // a manual clip is redundant.
122
+ const browserContext = getBrowserContext();
123
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
124
+ const page = await browserContext.newPage();
125
+ await page.goto('https://example.com/');
126
+ await page.bringToFront();
127
+ await serviceWorker.evaluate(async () => {
128
+ await globalThis.toggleExtensionForActiveTab();
129
+ });
130
+ await new Promise((r) => setTimeout(r, 100));
131
+ const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }));
132
+ const cdpPage = browser
133
+ .contexts()[0]
134
+ .pages()
135
+ .find((p) => p.url().includes('example.com'));
136
+ expect(cdpPage).toBeDefined();
137
+ // Get actual browser viewport via JS
138
+ const actualViewport = await cdpPage.evaluate(() => ({
139
+ width: window.innerWidth,
140
+ height: window.innerHeight,
141
+ }));
142
+ console.log('Actual viewport (window.inner*):', actualViewport);
143
+ // Plain screenshot with scale:'css', NO clip
144
+ const screenshot = await cdpPage.screenshot({ scale: 'css' });
145
+ const dimensions = imageSize(screenshot);
146
+ console.log('Screenshot dimensions (no clip):', dimensions);
147
+ expect(dimensions.width).toBe(actualViewport.width);
148
+ expect(dimensions.height).toBe(actualViewport.height);
149
+ await browser.close();
150
+ await page.close();
151
+ }, 60000);
152
+ it('should capture element screenshot with correct coordinates', async () => {
153
+ const browserContext = getBrowserContext();
154
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
155
+ const target = { x: 200, y: 150, width: 300, height: 100 };
156
+ const scrolledTarget = { x: 100, y: 1500, width: 200, height: 80 };
157
+ const page = await browserContext.newPage();
158
+ await page.setContent(`
159
+ <html>
160
+ <head>
161
+ <style>
162
+ body { margin: 0; padding: 0; height: 2000px; }
163
+ #target {
164
+ position: absolute;
165
+ top: ${target.y}px;
166
+ left: ${target.x}px;
167
+ width: ${target.width}px;
168
+ height: ${target.height}px;
169
+ background: red;
170
+ }
171
+ #scrolled-target {
172
+ position: absolute;
173
+ top: ${scrolledTarget.y}px;
174
+ left: ${scrolledTarget.x}px;
175
+ width: ${scrolledTarget.width}px;
176
+ height: ${scrolledTarget.height}px;
177
+ background: blue;
178
+ }
179
+ </style>
180
+ </head>
181
+ <body>
182
+ <div id="target">Target Element</div>
183
+ <div id="scrolled-target">Scrolled Target</div>
184
+ </body>
185
+ </html>
186
+ `);
187
+ await page.bringToFront();
188
+ await serviceWorker.evaluate(async () => {
189
+ await globalThis.toggleExtensionForActiveTab();
190
+ });
191
+ await new Promise((r) => setTimeout(r, 400));
192
+ const capturedCommands = [];
193
+ const commandHandler = ({ command }) => {
194
+ if (command.method === 'Page.captureScreenshot') {
195
+ capturedCommands.push(command);
196
+ }
197
+ };
198
+ testCtx.relayServer.on('cdp:command', commandHandler);
199
+ const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }));
200
+ let cdpPage;
201
+ for (const p of browser.contexts()[0].pages()) {
202
+ const html = await p.content();
203
+ if (html.includes('scrolled-target')) {
204
+ cdpPage = p;
205
+ break;
206
+ }
207
+ }
208
+ expect(cdpPage).toBeDefined();
209
+ await cdpPage.locator('#target').screenshot();
210
+ await cdpPage.locator('#scrolled-target').screenshot();
211
+ testCtx.relayServer.off('cdp:command', commandHandler);
212
+ expect(capturedCommands.length).toBe(2);
213
+ const targetCmd = capturedCommands[0];
214
+ expect(targetCmd.method).toBe('Page.captureScreenshot');
215
+ const targetClip = targetCmd.params.clip;
216
+ expect(targetClip.x).toBe(target.x);
217
+ expect(targetClip.y).toBe(target.y);
218
+ expect(targetClip.width).toBe(target.width);
219
+ expect(targetClip.height).toBe(target.height);
220
+ const scrolledCmd = capturedCommands[1];
221
+ expect(scrolledCmd.method).toBe('Page.captureScreenshot');
222
+ const scrolledClip = scrolledCmd.params.clip;
223
+ expect(scrolledClip.x).toBe(scrolledTarget.x);
224
+ expect(scrolledClip.y).toBe(scrolledTarget.y);
225
+ expect(scrolledClip.width).toBe(scrolledTarget.width);
226
+ expect(scrolledClip.height).toBe(scrolledTarget.height);
227
+ await browser.close();
228
+ await page.close();
229
+ }, 60000);
230
+ it('should get locator string for element using getLocatorStringForElement', async () => {
231
+ const browserContext = getBrowserContext();
232
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
233
+ const page = await browserContext.newPage();
234
+ await page.setContent(`
235
+ <html>
236
+ <body>
237
+ <button id="test-btn">Click Me</button>
238
+ <input type="text" placeholder="Enter name" />
239
+ </body>
240
+ </html>
241
+ `);
242
+ await page.bringToFront();
243
+ await serviceWorker.evaluate(async () => {
244
+ await globalThis.toggleExtensionForActiveTab();
245
+ });
246
+ await new Promise((r) => setTimeout(r, 400));
247
+ const result = await client.callTool({
248
+ name: 'execute',
249
+ arguments: {
250
+ code: js `
251
+ let testPage;
252
+ for (const p of context.pages()) {
253
+ const html = await p.content();
254
+ if (html.includes('test-btn')) { testPage = p; break; }
255
+ }
256
+ if (!testPage) throw new Error('Test page not found');
257
+ const btn = testPage.locator('#test-btn');
258
+ const locatorString = await getLocatorStringForElement(btn);
259
+ console.log('Locator string:', locatorString);
260
+ const locatorFromString = eval('testPage.' + locatorString);
261
+ const count = await locatorFromString.count();
262
+ console.log('Locator count:', count);
263
+ const text = await locatorFromString.textContent();
264
+ console.log('Locator text:', text);
265
+ `,
266
+ timeout: 30000,
267
+ },
268
+ });
269
+ expect(result.isError).toBeFalsy();
270
+ const text = result.content[0]?.text || '';
271
+ expect(text).toContain('Locator string:');
272
+ expect(text).toContain("getByRole('button', { name: 'Click Me' })");
273
+ expect(text).toContain('Locator count:');
274
+ expect(text).toContain('Locator text:');
275
+ expect(text).toContain('Click Me');
276
+ await page.close();
277
+ }, 60000);
278
+ it('should get styles for element using getStylesForLocator', async () => {
279
+ const browserContext = getBrowserContext();
280
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
281
+ const page = await browserContext.newPage();
282
+ await page.setContent(`
283
+ <html>
284
+ <head>
285
+ <style>
286
+ body { font-family: Arial, sans-serif; color: #333; }
287
+ .container { padding: 20px; margin: 10px; }
288
+ #main-btn { background-color: blue; color: white; border-radius: 4px; }
289
+ .btn { padding: 8px 16px; }
290
+ </style>
291
+ </head>
292
+ <body>
293
+ <div class="container">
294
+ <button id="main-btn" class="btn" style="font-weight: bold;">Click Me</button>
295
+ </div>
296
+ </body>
297
+ </html>
298
+ `);
299
+ await page.bringToFront();
300
+ await serviceWorker.evaluate(async () => {
301
+ await globalThis.toggleExtensionForActiveTab();
302
+ });
303
+ await new Promise((r) => setTimeout(r, 400));
304
+ const stylesResult = await client.callTool({
305
+ name: 'execute',
306
+ arguments: {
307
+ code: js `
308
+ let testPage;
309
+ for (const p of context.pages()) {
310
+ const html = await p.content();
311
+ if (html.includes('main-btn')) { testPage = p; break; }
312
+ }
313
+ if (!testPage) throw new Error('Test page not found');
314
+ const btn = testPage.locator('#main-btn');
315
+ const styles = await getStylesForLocator({ locator: btn });
316
+ return styles;
317
+ `,
318
+ timeout: 30000,
319
+ },
320
+ });
321
+ expect(stylesResult.isError).toBeFalsy();
322
+ const stylesText = stylesResult.content[0]?.text || '';
323
+ expect(stylesText).toMatchInlineSnapshot(`
324
+ "[return value] {
325
+ element: 'button#main-btn.btn',
326
+ inlineStyle: { 'font-weight': 'bold' },
327
+ rules: [
328
+ {
329
+ selector: '.btn',
330
+ source: null,
331
+ origin: 'regular',
332
+ declarations: {
333
+ padding: '8px 16px',
334
+ 'padding-top': '8px',
335
+ 'padding-right': '16px',
336
+ 'padding-bottom': '8px',
337
+ 'padding-left': '16px'
338
+ },
339
+ inheritedFrom: null
340
+ },
341
+ {
342
+ selector: '#main-btn',
343
+ source: null,
344
+ origin: 'regular',
345
+ declarations: {
346
+ 'background-color': 'blue',
347
+ color: 'white',
348
+ 'border-radius': '4px',
349
+ 'border-top-left-radius': '4px',
350
+ 'border-top-right-radius': '4px',
351
+ 'border-bottom-right-radius': '4px',
352
+ 'border-bottom-left-radius': '4px'
353
+ },
354
+ inheritedFrom: null
355
+ },
356
+ {
357
+ selector: '.container',
358
+ source: null,
359
+ origin: 'regular',
360
+ declarations: {
361
+ padding: '20px',
362
+ margin: '10px',
363
+ 'padding-top': '20px',
364
+ 'padding-right': '20px',
365
+ 'padding-bottom': '20px',
366
+ 'padding-left': '20px',
367
+ 'margin-top': '10px',
368
+ 'margin-right': '10px',
369
+ 'margin-bottom': '10px',
370
+ 'margin-left': '10px'
371
+ },
372
+ inheritedFrom: 'ancestor[1]'
373
+ },
374
+ {
375
+ selector: 'body',
376
+ source: null,
377
+ origin: 'regular',
378
+ declarations: { 'font-family': 'Arial, sans-serif', color: 'rgb(51, 51, 51)' },
379
+ inheritedFrom: 'ancestor[2]'
380
+ }
381
+ ]
382
+ }"
383
+ `);
384
+ const formattedResult = await client.callTool({
385
+ name: 'execute',
386
+ arguments: {
387
+ code: js `
388
+ let testPage;
389
+ for (const p of context.pages()) {
390
+ const html = await p.content();
391
+ if (html.includes('main-btn')) { testPage = p; break; }
392
+ }
393
+ if (!testPage) throw new Error('Test page not found');
394
+ const btn = testPage.locator('#main-btn');
395
+ const styles = await getStylesForLocator({ locator: btn });
396
+ return formatStylesAsText(styles);
397
+ `,
398
+ timeout: 30000,
399
+ },
400
+ });
401
+ expect(formattedResult.isError).toBeFalsy();
402
+ const formattedText = formattedResult.content[0]?.text || '';
403
+ expect(formattedText).toMatchInlineSnapshot(`
404
+ "[return value] Element: button#main-btn.btn
405
+
406
+ Inline styles:
407
+ font-weight: bold
408
+
409
+ Matched rules:
410
+ .btn {
411
+ padding: 8px 16px;
412
+ padding-top: 8px;
413
+ padding-right: 16px;
414
+ padding-bottom: 8px;
415
+ padding-left: 16px;
416
+ }
417
+ #main-btn {
418
+ background-color: blue;
419
+ color: white;
420
+ border-radius: 4px;
421
+ border-top-left-radius: 4px;
422
+ border-top-right-radius: 4px;
423
+ border-bottom-right-radius: 4px;
424
+ border-bottom-left-radius: 4px;
425
+ }
426
+
427
+ Inherited from ancestor[1]:
428
+ .container {
429
+ padding: 20px;
430
+ margin: 10px;
431
+ padding-top: 20px;
432
+ padding-right: 20px;
433
+ padding-bottom: 20px;
434
+ padding-left: 20px;
435
+ margin-top: 10px;
436
+ margin-right: 10px;
437
+ margin-bottom: 10px;
438
+ margin-left: 10px;
439
+ }
440
+
441
+ Inherited from ancestor[2]:
442
+ body {
443
+ font-family: Arial, sans-serif;
444
+ color: rgb(51, 51, 51);
445
+ }"
446
+ `);
447
+ await page.close();
448
+ }, 60000);
449
+ it('should return correct layout metrics via CDP', async () => {
450
+ const browserContext = getBrowserContext();
451
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
452
+ const page = await browserContext.newPage();
453
+ await page.goto('https://example.com/');
454
+ await page.bringToFront();
455
+ await serviceWorker.evaluate(async () => {
456
+ await globalThis.toggleExtensionForActiveTab();
457
+ });
458
+ await new Promise((r) => setTimeout(r, 100));
459
+ const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }));
460
+ const cdpPage = browser
461
+ .contexts()[0]
462
+ .pages()
463
+ .find((p) => p.url().includes('example.com'));
464
+ expect(cdpPage).toBeDefined();
465
+ const cdpSession = await getCDPSessionForPage({ page: cdpPage });
466
+ const layoutMetrics = await cdpSession.send('Page.getLayoutMetrics');
467
+ const normalized = {
468
+ cssLayoutViewport: layoutMetrics.cssLayoutViewport,
469
+ cssVisualViewport: layoutMetrics.cssVisualViewport,
470
+ layoutViewport: layoutMetrics.layoutViewport,
471
+ visualViewport: layoutMetrics.visualViewport,
472
+ devicePixelRatio: layoutMetrics.cssVisualViewport.clientWidth > 0
473
+ ? layoutMetrics.visualViewport.clientWidth / layoutMetrics.cssVisualViewport.clientWidth
474
+ : 1,
475
+ };
476
+ expect(normalized).toMatchInlineSnapshot(`
477
+ {
478
+ "cssLayoutViewport": {
479
+ "clientHeight": 720,
480
+ "clientWidth": 1280,
481
+ "pageX": 0,
482
+ "pageY": 0,
483
+ },
484
+ "cssVisualViewport": {
485
+ "clientHeight": 720,
486
+ "clientWidth": 1280,
487
+ "offsetX": 0,
488
+ "offsetY": 0,
489
+ "pageX": 0,
490
+ "pageY": 0,
491
+ "scale": 1,
492
+ "zoom": 1,
493
+ },
494
+ "devicePixelRatio": 1,
495
+ "layoutViewport": {
496
+ "clientHeight": 720,
497
+ "clientWidth": 1280,
498
+ "pageX": 0,
499
+ "pageY": 0,
500
+ },
501
+ "visualViewport": {
502
+ "clientHeight": 720,
503
+ "clientWidth": 1280,
504
+ "offsetX": 0,
505
+ "offsetY": 0,
506
+ "pageX": 0,
507
+ "pageY": 0,
508
+ "scale": 1,
509
+ "zoom": 1,
510
+ },
511
+ }
512
+ `);
513
+ const windowDpr = await cdpPage.evaluate(() => globalThis.devicePixelRatio);
514
+ console.log('window.devicePixelRatio:', windowDpr);
515
+ expect(windowDpr).toBe(1);
516
+ await cdpSession.detach();
517
+ await browser.close();
518
+ await page.close();
519
+ }, 60000);
520
+ it('should support getExistingCDPSession through the relay (reusing Playwright WS)', async () => {
521
+ const browserContext = getBrowserContext();
522
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
523
+ const page = await browserContext.newPage();
524
+ await page.goto('https://example.com/');
525
+ await page.bringToFront();
526
+ await serviceWorker.evaluate(async () => {
527
+ await globalThis.toggleExtensionForActiveTab();
528
+ });
529
+ await new Promise((r) => setTimeout(r, 100));
530
+ const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }));
531
+ const cdpPage = browser
532
+ .contexts()[0]
533
+ .pages()
534
+ .find((p) => p.url().includes('example.com'));
535
+ expect(cdpPage).toBeDefined();
536
+ // Use the new getCDPSessionForPage which reuses Playwright's internal WS
537
+ const cdpClient = await getCDPSessionForPage({ page: cdpPage });
538
+ // Should be able to send CDP commands just like the regular getCDPSessionForPage
539
+ const layoutMetrics = await cdpClient.send('Page.getLayoutMetrics');
540
+ expect(layoutMetrics).toBeDefined();
541
+ const metrics = layoutMetrics;
542
+ expect(metrics.cssVisualViewport).toBeDefined();
543
+ expect(metrics.cssVisualViewport.clientWidth).toBeGreaterThan(0);
544
+ // Test DOM access
545
+ const document = await cdpClient.send('DOM.getDocument');
546
+ expect(document).toBeDefined();
547
+ await cdpClient.detach();
548
+ await browser.close();
549
+ await page.close();
550
+ }, 60000);
551
+ it('should get aria ref for locator using getAriaSnapshot', async () => {
552
+ const browserContext = getBrowserContext();
553
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
554
+ const page = await browserContext.newPage();
555
+ // Use data-testid for stable refs, regular id for the button
556
+ await page.setContent(`
557
+ <html>
558
+ <body>
559
+ <button data-testid="submit-btn">Submit Form</button>
560
+ <a href="/about" data-testid="about-link">About Us</a>
561
+ <input type="text" placeholder="Enter your name" data-testid="name-input" />
562
+ </body>
563
+ </html>
564
+ `);
565
+ await page.bringToFront();
566
+ await serviceWorker.evaluate(async () => {
567
+ await globalThis.toggleExtensionForActiveTab();
568
+ });
569
+ await new Promise((r) => setTimeout(r, 400));
570
+ const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }));
571
+ let cdpPage;
572
+ for (const p of browser.contexts()[0].pages()) {
573
+ const html = await p.content();
574
+ if (html.includes('submit-btn')) {
575
+ cdpPage = p;
576
+ break;
577
+ }
578
+ }
579
+ expect(cdpPage).toBeDefined();
580
+ const { getAriaSnapshot } = await import('./aria-snapshot.js');
581
+ const ariaResult = await getAriaSnapshot({
582
+ page: cdpPage,
583
+ });
584
+ expect(ariaResult.snapshot).toBeDefined();
585
+ expect(ariaResult.snapshot.length).toBeGreaterThan(0);
586
+ expect(ariaResult.snapshot).toContain('Submit Form');
587
+ // Snapshot lines include Playwright locators for interactive elements
588
+ expect(ariaResult.snapshot).toContain('[data-testid="submit-btn"]');
589
+ expect(ariaResult.snapshot).toContain('[data-testid="about-link"]');
590
+ expect(ariaResult.snapshot).toContain('[data-testid="name-input"]');
591
+ const flattenNodes = (nodes) => {
592
+ return nodes.flatMap((node) => {
593
+ return [node, ...flattenNodes(node.children)];
594
+ });
595
+ };
596
+ const allNodes = flattenNodes(ariaResult.tree);
597
+ const findByLocator = (locator) => {
598
+ return allNodes.find((node) => node.locator === locator);
599
+ };
600
+ const submitNode = findByLocator('[data-testid="submit-btn"]');
601
+ const aboutNode = findByLocator('[data-testid="about-link"]');
602
+ const nameNode = findByLocator('[data-testid="name-input"]');
603
+ expect(submitNode).toBeDefined();
604
+ expect(aboutNode).toBeDefined();
605
+ expect(nameNode).toBeDefined();
606
+ const submitLocator = cdpPage.locator(submitNode.locator);
607
+ const aboutLocator = cdpPage.locator(aboutNode.locator);
608
+ const nameLocator = cdpPage.locator(nameNode.locator);
609
+ expect(await submitLocator.count()).toBe(1);
610
+ expect(await aboutLocator.count()).toBe(1);
611
+ expect(await nameLocator.count()).toBe(1);
612
+ expect(await submitLocator.textContent()).toBe('Submit Form');
613
+ expect(await aboutLocator.textContent()).toBe('About Us');
614
+ expect(await nameLocator.getAttribute('placeholder')).toBe('Enter your name');
615
+ expect(ariaResult.refToElement.size).toBeGreaterThan(0);
616
+ console.log('RefToElement map size:', ariaResult.refToElement.size);
617
+ console.log('RefToElement entries:', [...ariaResult.refToElement.entries()]);
618
+ // Verify refs are stable test IDs
619
+ expect(ariaResult.refToElement.has('submit-btn')).toBe(true);
620
+ expect(ariaResult.refToElement.has('about-link')).toBe(true);
621
+ expect(ariaResult.refToElement.has('name-input')).toBe(true);
622
+ // Use getSelectorForRef to get CSS selector for a ref
623
+ const btnSelector = ariaResult.getSelectorForRef('submit-btn');
624
+ expect(btnSelector).toBeDefined();
625
+ console.log('Button selector:', btnSelector);
626
+ // Verify the selector works
627
+ const btnViaSelector = cdpPage.locator(btnSelector);
628
+ const btnTextViaRef = await btnViaSelector.textContent();
629
+ console.log('Button text via selector:', btnTextViaRef);
630
+ expect(btnTextViaRef).toBe('Submit Form');
631
+ // Test role and name
632
+ const btnInfo = ariaResult.refToElement.get('submit-btn');
633
+ expect(btnInfo?.role).toBe('button');
634
+ expect(btnInfo?.name).toBe('Submit Form');
635
+ const linkInfo = ariaResult.refToElement.get('about-link');
636
+ expect(linkInfo?.role).toBe('link');
637
+ expect(linkInfo?.name).toBe('About Us');
638
+ const inputInfo = ariaResult.refToElement.get('name-input');
639
+ expect(inputInfo?.role).toBe('textbox');
640
+ await browser.close();
641
+ await page.close();
642
+ }, 60000);
643
+ it('should show aria ref labels on real pages and save screenshots', async () => {
644
+ const browserContext = getBrowserContext();
645
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
646
+ const { showAriaRefLabels, hideAriaRefLabels } = await import('./aria-snapshot.js');
647
+ // Create assets folder for screenshots
648
+ const assetsDir = path.join(path.dirname(new URL(import.meta.url).pathname), 'assets');
649
+ if (!fs.existsSync(assetsDir)) {
650
+ fs.mkdirSync(assetsDir, { recursive: true });
651
+ }
652
+ const testPages = [
653
+ { name: 'old-reddit', url: 'https://old.reddit.com/' },
654
+ { name: 'hacker-news', url: 'https://news.ycombinator.com/' },
655
+ ];
656
+ const loadPageWithRetries = async ({ name, url }) => {
657
+ const page = await browserContext.newPage();
658
+ page.setDefaultNavigationTimeout(60000);
659
+ const attempts = 2;
660
+ let lastError = null;
661
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
662
+ try {
663
+ console.log(`[labels] opening ${name}: ${url} (attempt ${attempt}/${attempts})`);
664
+ await page.goto(url, { waitUntil: 'load' });
665
+ console.log(`[labels] loaded ${name}: ${page.url()}`);
666
+ return { name, url, page };
667
+ }
668
+ catch (error) {
669
+ lastError = error;
670
+ }
671
+ }
672
+ await page.close();
673
+ throw new Error(`Failed to load ${name} after ${attempts} attempts`, {
674
+ cause: lastError instanceof Error ? lastError : undefined,
675
+ });
676
+ };
677
+ const pages = await Promise.all(testPages.map((testPage) => {
678
+ return loadPageWithRetries(testPage);
679
+ }));
680
+ for (const { page } of pages) {
681
+ await page.bringToFront();
682
+ await serviceWorker.evaluate(async () => {
683
+ await globalThis.toggleExtensionForActiveTab();
684
+ });
685
+ }
686
+ const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }));
687
+ const withTimeout = async (label, task, timeoutMs) => {
688
+ let timeoutId = null;
689
+ const timeoutPromise = new Promise((_, reject) => {
690
+ timeoutId = setTimeout(() => {
691
+ reject(new Error(`Timed out after ${timeoutMs}ms: ${label}`));
692
+ }, timeoutMs);
693
+ });
694
+ try {
695
+ return await Promise.race([task(), timeoutPromise]);
696
+ }
697
+ finally {
698
+ if (timeoutId) {
699
+ clearTimeout(timeoutId);
700
+ }
701
+ }
702
+ };
703
+ const wsUrl = getCdpUrl({ port: TEST_PORT });
704
+ for (const { name, url, page } of pages) {
705
+ console.log(`[labels] start ${name}`);
706
+ const cdpPage = browser
707
+ .contexts()[0]
708
+ .pages()
709
+ .find((p) => p.url().includes(new URL(url).hostname));
710
+ if (!cdpPage) {
711
+ throw new Error(`Could not find CDP page for ${name}`);
712
+ }
713
+ console.log(`[labels] show labels ${name}`);
714
+ const { labelCount } = await withTimeout(`showAriaRefLabels(${name})`, async () => {
715
+ return await showAriaRefLabels({ page: cdpPage });
716
+ }, 60000);
717
+ console.log(`${name}: ${labelCount} labels shown`);
718
+ expect(labelCount).toBeGreaterThan(0);
719
+ console.log(`[labels] screenshot ${name}`);
720
+ const screenshot = await withTimeout(`screenshot(${name})`, async () => {
721
+ return await cdpPage.screenshot({ type: 'png', fullPage: false });
722
+ }, 30000);
723
+ const screenshotPath = path.join(assetsDir, `aria-labels-${name}.png`);
724
+ fs.writeFileSync(screenshotPath, screenshot);
725
+ console.log(`Screenshot saved: ${screenshotPath}`);
726
+ console.log(`[labels] count dom labels ${name}`);
727
+ const labelElements = await withTimeout(`countLabels(${name})`, async () => {
728
+ return await cdpPage.evaluate(() => document.querySelectorAll('.__pw_label__').length);
729
+ }, 10000);
730
+ expect(labelElements).toBe(labelCount);
731
+ console.log(`[labels] hide labels ${name}`);
732
+ await withTimeout(`hideAriaRefLabels(${name})`, async () => {
733
+ await hideAriaRefLabels({ page: cdpPage });
734
+ }, 10000);
735
+ const labelsAfterHide = await withTimeout(`verifyHide(${name})`, async () => {
736
+ return await cdpPage.evaluate(() => document.getElementById('__tabwright_labels__'));
737
+ }, 10000);
738
+ expect(labelsAfterHide).toBeNull();
739
+ console.log(`[labels] close page ${name}`);
740
+ await page.close();
741
+ }
742
+ await browser.close();
743
+ console.log(`Screenshots saved to: ${assetsDir}`);
744
+ }, 180000);
745
+ it('should take screenshot with accessibility labels via MCP execute tool', async () => {
746
+ const browserContext = getBrowserContext();
747
+ const serviceWorker = await getExtensionServiceWorker(browserContext);
748
+ const page = await browserContext.newPage();
749
+ await page.setContent(`
750
+ <html>
751
+ <head>
752
+ <style>
753
+ body {
754
+ margin: 0;
755
+ background: #e8f4f8;
756
+ position: relative;
757
+ min-height: 100vh;
758
+ }
759
+ .controls {
760
+ padding: 20px;
761
+ position: relative;
762
+ z-index: 10;
763
+ }
764
+ .grid-marker {
765
+ position: absolute;
766
+ background: rgba(255, 100, 100, 0.3);
767
+ border: 1px solid #ff6464;
768
+ font-size: 10px;
769
+ color: #333;
770
+ display: flex;
771
+ align-items: center;
772
+ justify-content: center;
773
+ }
774
+ .h-marker {
775
+ left: 0;
776
+ width: 100%;
777
+ height: 20px;
778
+ }
779
+ .v-marker {
780
+ top: 0;
781
+ height: 100%;
782
+ width: 20px;
783
+ }
784
+ </style>
785
+ </head>
786
+ <body>
787
+ <div class="controls">
788
+ <button id="submit-btn">Submit Form</button>
789
+ <a href="/about">About Us</a>
790
+ <input type="text" placeholder="Enter your name" />
791
+ </div>
792
+ <!-- Horizontal markers every 200px -->
793
+ <div class="grid-marker h-marker" style="top: 200px;">200px</div>
794
+ <div class="grid-marker h-marker" style="top: 400px;">400px</div>
795
+ <div class="grid-marker h-marker" style="top: 600px;">600px</div>
796
+ <!-- Vertical markers every 200px -->
797
+ <div class="grid-marker v-marker" style="left: 200px;">200</div>
798
+ <div class="grid-marker v-marker" style="left: 400px;">400</div>
799
+ <div class="grid-marker v-marker" style="left: 600px;">600</div>
800
+ <div class="grid-marker v-marker" style="left: 800px;">800</div>
801
+ <div class="grid-marker v-marker" style="left: 1000px;">1000</div>
802
+ <div class="grid-marker v-marker" style="left: 1200px;">1200</div>
803
+ </body>
804
+ </html>
805
+ `);
806
+ await page.bringToFront();
807
+ await serviceWorker.evaluate(async () => {
808
+ await globalThis.toggleExtensionForActiveTab();
809
+ });
810
+ await new Promise((r) => setTimeout(r, 400));
811
+ const result = await client.callTool({
812
+ name: 'execute',
813
+ arguments: {
814
+ code: js `
815
+ let testPage;
816
+ for (const p of context.pages()) {
817
+ const html = await p.content();
818
+ if (html.includes('submit-btn')) { testPage = p; break; }
819
+ }
820
+ if (!testPage) throw new Error('Test page not found');
821
+ await screenshotWithAccessibilityLabels({ page: testPage });
822
+ `,
823
+ timeout: 15000,
824
+ },
825
+ });
826
+ expect(result.isError).toBeFalsy();
827
+ const content = result.content;
828
+ expect(content.length).toBe(2);
829
+ const textContent = content.find((c) => c.type === 'text');
830
+ expect(textContent).toBeDefined();
831
+ expect(textContent.text).toContain('Screenshot saved to:');
832
+ expect(textContent.text).toContain('image included below');
833
+ expect(textContent.text).toContain('Accessibility snapshot:');
834
+ expect(textContent.text).toContain('Submit Form');
835
+ const imageContent = content.find((c) => c.type === 'image');
836
+ expect(imageContent).toBeDefined();
837
+ expect(imageContent.mimeType).toBe('image/png');
838
+ expect(imageContent.data).toBeDefined();
839
+ expect(imageContent.data.length).toBeGreaterThan(100);
840
+ const buffer = Buffer.from(imageContent.data, 'base64');
841
+ const dimensions = imageSize(buffer);
842
+ const viewport = await page.evaluate(() => ({
843
+ innerWidth: window.innerWidth,
844
+ innerHeight: window.innerHeight,
845
+ outerWidth: window.outerWidth,
846
+ outerHeight: window.outerHeight,
847
+ }));
848
+ console.log('Screenshot dimensions:', dimensions.width, 'x', dimensions.height);
849
+ console.log('Window viewport:', viewport);
850
+ expect(dimensions.type).toBe('png');
851
+ expect(dimensions.width).toBeGreaterThan(0);
852
+ expect(dimensions.height).toBeGreaterThan(0);
853
+ await page.close();
854
+ }, 60000);
855
+ });
856
+ //# sourceMappingURL=snapshot-tools.test.js.map