216 Commits

Author SHA1 Message Date
Dmitry Gozman 7ae7c9507a devops: fix publish release job (#203) (#204)
We do not do `npm run build` anymore.
2021-01-22 12:05:05 -08:00
Dmitry Gozman 9a12ae4f41 chore: mark as 0.180.0 release (#202) 2021-01-22 11:02:01 -08:00
Dmitry Gozman 104a1931c9 devops: publish canary npm (#201) 2021-01-21 13:18:56 -08:00
Dmitry Gozman 281119ec63 chore: update canary_version to work with new setup (#200) 2021-01-21 13:02:45 -08:00
Dmitry Gozman a8f6c6af20 chore: redirect playwright-cli users to playwright (#198)
chore: redirect users of playwright-cli to playwright
2021-01-21 12:55:02 -08:00
Dmitry Gozman 225c4c6114 docs: add save/load storage to README (#194) 2021-01-05 12:56:18 -08:00
Yury Semikhatsky cbc13bda96 chore: roll to playwright@1.8.0-next.1608746109749 (#190) 2020-12-23 10:57:27 -08:00
Dmitry Gozman 149a88a221 feat(trace viewer): generate video tiles lazily (#185) 2020-12-22 10:27:30 -08:00
Yury Semikhatsky 250dc65a7a chore: avoid uncaught promise rejection when install fails (#184) 2020-12-21 12:29:35 -08:00
Dmitry Gozman a2d673e4dd chore: generate traceViewer/web into lib/ instead of out/ (#181) 2020-12-18 16:24:37 -08:00
Dmitry Gozman d444924fb1 feat(ui): switch from monaco to highlightjs (#180) 2020-12-18 16:03:22 -08:00
Dmitry Gozman f553931fa6 chore: split TraceViewer into files, small refactorings (#178) 2020-12-16 19:31:19 -08:00
Dmitry Gozman e464a439fd feat(ui): reduce the bundle size by loading monaco from node_modules (#177) 2020-12-16 18:08:15 -08:00
Dmitry Gozman 8f9a6fcf10 feat(ui): remove old non-react code (#176) 2020-12-16 11:05:50 -08:00
Dmitry Gozman 546b52c38a feat(ui): provide workers to monaco for performance improvement (#174) 2020-12-16 09:33:46 -08:00
Dmitry Gozman 199954e58e feat(ui): convert last bits to react (#171) 2020-12-16 07:02:06 -08:00
Dmitry Gozman 64a7ce370f chore: update canary version generation (#173) 2020-12-15 19:03:54 -08:00
Pavel Feldman 67df74655b chore: public canaries off the release branch 2020-12-15 12:52:40 -08:00
Dmitry Gozman 170b663228 chore: cut 0.180.0-next (#170) 2020-12-15 12:11:06 -08:00
Dmitry Gozman a7b9a6d094 chore: roll to playwright@1.7.0-next.1608058598043 (#169) 2020-12-15 11:08:35 -08:00
Dmitry Gozman 88f32b214d feat: save/load storage options (#166)
feat: save/load storage options

User can save the storage, and then load the storage in the next script.

```sh
npx playwright-cli --save-storage=storage.json open www.needs-login.com

npx playwright-cli --load-storage=storage.json codegen www.needs-login.com
```
2020-12-15 10:42:48 -08:00
Dmitry Gozman 5b636967fb feat(traceViewer): fix network and source tabs (#165)
Source now accounts for various stack trace formats.
Network now shows `page.goto` resources.
2020-12-15 06:38:33 -08:00
Dmitry Gozman 83d130a144 chore: roll to playwright@1.7.0-next.1607994464309 (#167) 2020-12-14 17:24:47 -08:00
Dmitry Gozman fee2f20111 feat(ui): convert timeline to react (#163) 2020-12-14 10:29:58 -08:00
Joel Einbinder 92c2cfd959 chore(lint): add eslint (#164) 2020-12-14 09:24:14 -08:00
Dmitry Gozman 0efc4efff3 feat(ui): extract react helpers (#162) 2020-12-12 17:51:38 -08:00
Dmitry Gozman 57dd4454a5 feat(ui): convert action list to react (#161) 2020-12-12 17:39:25 -08:00
Dmitry Gozman aff7b5bca4 feat(ui): convert FilmStripView to React (#159) 2020-12-12 13:02:00 -08:00
Dmitry Gozman 83ef538a5c feat(ui): add react, make a small component (#158)
This is a first step to convert to React.
2020-12-11 15:22:30 -08:00
dependabot[bot] 1e9558ebab chore(deps): bump ini from 1.3.5 to 1.3.7 (#157)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-10 12:35:36 -08:00
Pavel Feldman f8834324e0 chore: roll Playwright to 1.7.0-next.1607623793189 2020-12-10 12:28:09 -08:00
dependabot[bot] faae8d9d4f chore(deps): bump highlight.js from 10.3.2 to 10.4.1 (#152)
Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 10.3.2 to 10.4.1.
- [Release notes](https://github.com/highlightjs/highlight.js/releases)
- [Changelog](https://github.com/highlightjs/highlight.js/blob/master/CHANGES.md)
- [Commits](https://github.com/highlightjs/highlight.js/compare/10.3.2...10.4.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-05 07:55:36 -08:00
Pavel Feldman 0f6d8f0eea fix(checkbox): handle checkbox with dynamicly changing selector (#149) 2020-12-03 18:05:15 -08:00
Pavel Feldman 648bddea29 chore: roll Playwright to 1.7.0-next.1607022026758 (#151) 2020-12-03 15:38:27 -08:00
Pavel Feldman c8ba16a244 chore: force lo dpi recording on non-mac (#146) 2020-12-03 09:50:36 -08:00
Dmitry Gozman 6e50f097d5 feat(trace viewer): support multiple contexts (#148) 2020-12-02 15:52:56 -08:00
Dmitry Gozman 35ac80f50b fix(trace viewer): support non-chromium video, fit the snapshot frame (#144)
Also handle missing resources gracefully.
2020-11-30 21:04:39 -08:00
Kumar Aditya 1ffe0d4f6f fix: python code generator (#142) 2020-11-21 09:51:26 -08:00
Pavel Feldman bf60ecc02c chore: roll Playwright to 1.7.0-next.1605573954344 (#141) 2020-11-17 09:12:21 -08:00
Dmitry Gozman c0e1ab10e9 feat(trace): align video preview with actions timeline (#139) 2020-11-16 19:27:58 -08:00
Pavel Feldman d0ee952ee7 fix(codegen): do not fail open if no target specified (#140) 2020-11-16 19:25:15 -08:00
Pavel Feldman 9977aac088 chore: run ci for release branches 2020-11-13 15:10:42 -08:00
Pavel Feldman ea89933ed4 chore: roll Playwright to 1.7.0-next.1605305111464 (#135) 2020-11-13 14:12:46 -08:00
Pavel Feldman e8417a1b4f chore: bump version to 0.170.0-next 2020-11-12 21:21:11 -08:00
Pavel Feldman c150d22ada chore: roll to Playwright 1.6.1 (#134) 2020-11-12 21:16:17 -08:00
Pavel Feldman 0e0dc006c5 chore: roll to post-1.6.0 (#133) 2020-11-12 16:36:55 -08:00
Darío Kondratiuk c477580068 feat(codegen): introduce C# codegen (#131) 2020-11-10 16:22:50 -08:00
Yury Semikhatsky e3483a18d3 chore: roll to 1.6.0-next.1604370816246 (#127) 2020-11-02 19:24:06 -08:00
Pavel Feldman a69e52d4ad chore: roll to playwright@1.6.0-next.1604355180033 (#126) 2020-11-02 14:44:40 -08:00
Pavel 70bb9a4876 chore: bump Playwright to 1.6.0-next.1604018890865 2020-10-30 13:18:33 -07:00
Pavel 9f4f954c85 chore: roll to playwright@1.6.0-next.1604018890865 2020-10-29 17:51:29 -07:00
Pavel 42e3dcb31f chore: bump version to 0.160.0-next 2020-10-29 17:26:43 -07:00
Pavel d1b7273539 chore: bump version to 0.152.1 2020-10-29 17:22:51 -07:00
Pavel Feldman 44ac7dbc34 fix(tty): respect the terminal width when generating lines (#125) 2020-10-29 17:19:59 -07:00
Pavel Feldman 16fc9a98e3 chore: roll to 1.5.2 official (#123) 2020-10-27 23:11:56 -07:00
Yury Semikhatsky 3a3ecde1ac fix(installer): use executable directory only for pkg builds (#122) 2020-10-27 13:28:11 -07:00
Yury Semikhatsky d0cfa34dc4 fix(install): avoid accidental removal of downloaded browsers (#121) 2020-10-27 10:57:49 -07:00
Yury Semikhatsky 6d3dffce49 fix: force output fps=25 when generating video tiles (#119) 2020-10-27 09:18:02 -07:00
Pavel Feldman cd2ac3ae96 chore: roll to 1.5.1 official (#116) 2020-10-14 17:14:54 -07:00
Pavel b074ef4280 chore: roll Playwright to 1.5.0-next.1602697602857 2020-10-14 10:49:21 -07:00
Pavel Feldman e8d6f3b24c chore: roll to 1.5.0 official (#115) 2020-10-13 13:50:50 -07:00
Pavel Feldman 145e709133 chore: deploy canary cli to cdn (#114) 2020-10-13 09:26:09 -07:00
Pavel Feldman d8a6cd94e2 chore: close context before exit (#113) 2020-10-13 08:35:48 -07:00
Pavel Feldman 1d17a6c722 chore: bump playwright to 1.5 tag (2) 2020-10-12 20:29:57 -07:00
Pavel Feldman 42c3d86000 chore: bump playwright to 1.5 tag 2020-10-12 20:24:22 -07:00
Pavel Feldman fdfa3717a8 chore: roll Playwright and Folio to latest (#112) 2020-10-12 18:54:41 -07:00
Pavel Feldman 941195c5ed feat(python): generate sync dialect by default (#109) 2020-10-12 14:30:34 -07:00
Pavel Feldman f855785290 chore: remove stray log 2020-10-09 20:52:47 -07:00
Pavel Feldman 7e747a1e78 chore: move driver into the cli 2020-10-09 17:47:03 -07:00
Pavel Feldman 67a2b37802 chore: fix the environment credentials 2020-10-09 17:11:14 -07:00
Pavel Feldman f04dee5663 chore: fix upload paths 2020-10-09 16:58:20 -07:00
Pavel Feldman 0ddb714e23 chore: split 32 and 64 bit deployments (#108) 2020-10-09 16:26:46 -07:00
Pavel Feldman 49db82c3a1 feat(driver): bake driver into the cli (#107) 2020-10-09 15:26:49 -07:00
Arjun Attam ce038101ac chore: reduce footer separator length (#105) 2020-10-09 14:50:59 -07:00
Pavel Feldman 8647cd6a5e chore: restore NPM publish 2020-10-09 11:16:21 -07:00
Pavel Feldman e248abab2f fix(misc): misc bugfixes (#104) 2020-10-08 20:53:22 -07:00
Pavel Feldman 66d7522622 fix(codegen): generate Python if running Python version 2020-10-08 17:08:54 -07:00
Pavel Feldman 3afb2ae122 chore: merge publish scripts (7) 2020-10-08 16:12:55 -07:00
Pavel Feldman 9fad6f31e5 chore: merge publish scripts (6) 2020-10-08 15:50:16 -07:00
Pavel Feldman cf4a2cc6db chore: merge publish scripts (5) 2020-10-08 15:44:01 -07:00
Pavel Feldman d057bbd898 chore: merge publish scripts (4) 2020-10-08 15:35:24 -07:00
Pavel Feldman eb04a0a394 chore: merge publish scripts (3) 2020-10-08 15:20:24 -07:00
Pavel Feldman bae1d04010 chore: merge publish scripts (2) 2020-10-08 15:20:11 -07:00
Pavel Feldman 05c6ac8397 chore: merge publish scripts 2020-10-08 15:15:26 -07:00
Pavel Feldman 14f82e7414 feat(python): add python version (#98) 2020-10-08 15:08:41 -07:00
Dmitry Gozman ccc3f63690 feat(codegen): support python (#94)
`codegen --target=python` now emits async python code.
2020-10-08 10:28:20 -07:00
Dmitry Gozman 76ba600ccf feat(codegen): generate selectors in shadow dom (#92) 2020-10-05 09:54:59 -07:00
Sonja Leaf 04d682919e chore(pdf): fixed help text typo (#91) 2020-09-30 18:13:27 +02:00
Pavel Feldman 522fc31f5c chore: use ffmpeg bundled with Playwright (#89) 2020-09-28 16:15:48 -07:00
Pavel 2447f960e7 Revert "fix(codegen): escape slashes correctly in JavaScript (#75)"
This breaks xpath selectors.
2020-09-28 13:16:37 -07:00
Max Schmitt 37c99060f6 fix(codegen): select and input[type=date] (#87) 2020-09-25 22:34:32 +02:00
Max Schmitt 51c7b65df5 fix(highlight): hide initial hightlight elment (#86) 2020-09-25 18:54:49 +02:00
Max Schmitt e6a7a119ba fix(codegen): escape slashes correctly in JavaScript (#75) 2020-09-25 13:39:12 +02:00
Max Schmitt 64583b0e41 feat(pdf): detect file:// if file was passed (#84) 2020-09-25 13:38:48 +02:00
Max Schmitt 1ff73172d2 fix(overlay): use max z-index for highlight (#85) 2020-09-25 13:38:36 +02:00
Max Schmitt 66c693bfbf devops: fix canary deployment (#83) 2020-09-24 23:35:50 +02:00
Max Schmitt 305971b762 devops: release canary releases on NPM (#82) 2020-09-24 23:28:51 +02:00
Pavel 211aa79981 chore(trace): show actions on the timeline grid 2020-09-21 11:25:42 -07:00
Pavel Feldman 528e5e28a9 chore(trace): add film strip 2020-09-20 14:50:38 -07:00
Pavel Feldman 7e001eb631 feat(tiles): add video tile generator 2020-09-18 22:30:52 -07:00
Dmitry Gozman c50bc006d7 feat: show videos at the top (#72)
- roll to 1.4.0-next.1600475934986;
- use upstream traceTypes;
- use `__playwright_target__` element in the snapshot.
2020-09-18 18:45:22 -07:00
Dmitry Gozman 237bc64094 feat(trace-viewer): assorted fixes (#70) 2020-09-15 11:07:11 -07:00
Pavel 121b479229 chore(trace): keyboard navigation in network tab 2020-09-14 16:40:26 -07:00
Pavel f2b799e232 chore(trace): wire snapshot router to thumbnail generator 2020-09-14 15:46:18 -07:00
Pavel 38f92f75bb chore(trace): remove webpack-dev-server dep 2020-09-14 15:10:25 -07:00
Pavel 844a32e2d2 chore(trace): restore custom elements 2020-09-14 15:06:51 -07:00
Pavel c8bfbf64cb chore(trace): extract network tab 2020-09-14 10:44:45 -07:00
Pavel Feldman 5b074faeaf chore(trace): add workbench class 2020-09-14 10:21:00 -07:00
Pavel Feldman 23db262712 chore(trace): update color scheme 2020-09-14 10:01:30 -07:00
Pavel 08780bdb8e chore(trace): highlight target in the snapshot 2020-09-13 23:24:14 -07:00
Pavel eeafd5fdab chore(trace): add missing model definitions 2020-09-13 22:19:26 -07:00
Pavel fa54df8705 chore(trace): add thumbnails 2020-09-13 18:45:36 -07:00
Pavel d35052d5d8 chore(trace): add screenshot tab 2020-09-13 16:40:35 -07:00
Pavel 3bba9e768a chore(trace): add network resources 2020-09-13 16:07:15 -07:00
Pavel Feldman 9d1899fc14 chore(trace): use monaco to render source (#67) 2020-09-13 11:39:05 -07:00
Max Schmitt e04e8364f1 fix(codegen): HTML/root element was clicked (#66) 2020-09-13 16:54:31 +02:00
Pavel Feldman fca5f748b9 chore(trace): added source tab 2020-09-12 23:30:56 -07:00
Pavel Feldman 9bf05b89e0 chore: experimental trace viewer (#65) 2020-09-12 22:30:17 -07:00
Max Schmitt f7963b6e38 feat: refactoring to provide cross-lang support (#64) 2020-09-12 19:03:26 -07:00
Pavel Feldman bc0dffeb58 feat(browser.close): generate browser.close in the end (#62) 2020-09-11 19:31:46 -07:00
Max Schmitt 5b0baa0fe5 feat(codegen): save output to file if specified (#59) 2020-09-11 14:43:44 -07:00
Pavel Feldman efff74ad51 chore: roll test-runner to 0.3.7 2020-09-10 21:28:15 -07:00
Pavel Feldman d5f54db786 chore: roll the official to ~1.4.0 2020-09-10 15:32:07 -07:00
Pavel Feldman 50112f2489 chore: roll the official to 1.4.0 2020-09-10 15:30:04 -07:00
Pavel Feldman b56effcc38 chore: roll to 1.4.0-next.1599776227719, the 1.4 release 2020-09-10 15:23:13 -07:00
Pavel 6f442ce36c chore: bump version to 0.5.0 2020-09-09 17:55:55 -07:00
Pavel bc16472032 fix(clip): soften the clip keyboard filtering 2020-09-09 17:51:08 -07:00
Max Schmitt f50b043e23 fix(codegen): print ampersand correctly (#58) 2020-09-09 17:33:15 -07:00
Arjun Attam d409d659fd docs: fix npm badge 2020-09-09 17:31:42 -07:00
Max Schmitt 9a2ef83e98 fix(codegen): shortcuts for ctr + a/c/v and at sign (#53) 2020-09-10 00:27:45 +02:00
Pavel Feldman bd9098520e fix(test): fix Ctrl/Meta modifier in the popup test 2020-09-09 14:34:23 -07:00
Pavel Feldman a7ab322867 fix(mouse): emit mouse events in order (#57) 2020-09-09 14:12:21 -07:00
Pavel Feldman e8047b4537 fix(clash): don't clash signals from different pages (#56) 2020-09-09 13:49:33 -07:00
Max Schmitt 1bec2d3407 fix(cli): remove isMobile on Firefox (#55) 2020-09-09 13:07:27 -07:00
Max Schmitt 47748ac6da enh(codegen): spread devices inside context (#44) 2020-09-09 17:14:57 +02:00
Max Schmitt 928ed88ca6 fix(codegen): textarea handling (#51) 2020-09-09 16:59:28 +02:00
Pavel 85e57228a6 chore: roll test-runner to 0.3.4 2020-09-08 18:01:54 -07:00
Pavel a4e83525f9 chore: bump version to 0.4.7 2020-09-08 17:15:13 -07:00
Pavel f6e12059e6 fix(press): do not dispatch keyup twice 2020-09-08 17:08:08 -07:00
Arjun Attam fa627130bc docs: update readme 2020-09-08 16:29:12 -07:00
Arjun Attam 8f44f58cd2 docs: update readme (#42) 2020-09-08 16:09:22 -07:00
Pavel a12be9f139 chore: bump version to 0.4.6 2020-09-08 14:15:56 -07:00
Pavel cc2453ca61 fix(newpage): make sure new page is navigated 2020-09-08 14:14:59 -07:00
Pavel e4f8ca945d chore: bump version to 0.4.5 2020-09-08 13:30:40 -07:00
Pavel b38c120d45 fix(key): record navigation key presses 2020-09-08 13:29:30 -07:00
Pavel 70e0163d40 chore: roll test-runner to 0.3.3 2020-09-08 11:02:23 -07:00
Pavel Feldman 2d2e804fa3 chore: bump version to 0.4.4 2020-09-07 18:27:43 -07:00
Pavel Feldman b3e6ab8f71 chore: roll playwright to 1599519120622 (2) 2020-09-07 16:49:02 -07:00
Pavel Feldman 2a7746ebb2 chore: roll playwright to 1599519120622 2020-09-07 16:46:52 -07:00
Pavel Feldman f449ecf9a3 chore: bump version to 0.4.3 2020-09-07 11:04:17 -07:00
Pavel Feldman e68ebc769a infra: use nmp ci not i 2020-09-07 10:55:04 -07:00
Pavel Feldman a4238dafa6 chore: bump version to 0.4.2 2020-09-07 10:30:53 -07:00
Pavel Feldman 7a75b45aa9 infra: add registry url to publish action 2020-09-07 10:29:50 -07:00
Pavel Feldman 1bf8492925 chore: bump version to 0.4.1 2020-09-07 10:15:50 -07:00
Pavel Feldman c8b97974ec infra: add publish.yml 2020-09-07 10:12:43 -07:00
Max Schmitt aa7d0f8771 chore: fix build due case sensitive import (#39) 2020-09-07 10:05:31 -07:00
Pavel Feldman 7e1fbf5980 chore: bump version to 0.4.0 2020-09-06 23:21:16 -07:00
Pavel Feldman d6a8703a34 chore: migrate to the sync engine query 2020-09-06 23:15:47 -07:00
Pavel Feldman b274301b10 test: make tests reliable via emitting generated code after action 2020-09-06 21:39:50 -07:00
Max Schmitt 0e6b779da5 feat(codegen): added dialog support (#36) 2020-09-06 11:20:02 +02:00
Pavel Feldman 0e446c24ea test: roll test-runner to 0.3.2 2020-09-05 15:04:18 -07:00
Max Schmitt 9eb0e09067 feat: download support (#35) 2020-09-05 23:16:29 +02:00
Pavel Feldman 54dc65fa7f chore: update package-lock 2020-09-05 11:36:11 -07:00
Max Schmitt 5fe3a60ea1 feat(codegen): allow file uploads (#33) 2020-09-05 20:33:38 +02:00
Pavel Feldman 439c7235f2 test: mark test as failing due to upstream issue 2020-09-05 08:58:42 -07:00
Max Schmitt 69e847d4b5 test(codegen): add test for using Tab (#30) 2020-09-05 13:53:09 +02:00
Dmitry Gozman 0427d7b5bb fix(codegen): do not record multiple actions at once (#31)
We have a bug where "commit" action does not respect the
"is performing action" flag and therefore may reset it.
This leads to action reentrancy and multiple clicks being
performed/recorded per single user click.
2020-09-04 19:44:18 -07:00
Pavel Feldman fe2da88604 fix(input): do not use input value in selectors 2020-09-05 00:13:39 -08:00
Pavel Feldman 11a02e287c fix(typo): inverse condition fixed 2020-09-04 23:56:09 -08:00
Pavel Feldman fa17f7e5ea fix(focus): treat keyboard and mouse differently (#32) 2020-09-04 16:50:40 -07:00
Pavel Feldman b3d926d425 chore: bump to 0.3.2 2020-09-04 19:44:06 -08:00
Pavel Feldman 0adabe0a95 feat(ui): some ui love 2020-09-04 11:02:42 -07:00
Pavel 941e0046a3 chore: bump version to 0.3.1 2020-09-03 17:57:22 -07:00
Pavel f35db74640 chore: roll @playwright/test-runner to 0.2.11 2020-09-03 17:56:17 -07:00
Pavel b646c13fe8 chore: roll playwright, derive browser type from device 2020-09-03 17:51:20 -07:00
Max Schmitt 4368180516 fix(codegen): input attribute prioritisation (#27) 2020-09-03 16:33:40 -07:00
Pavel Feldman 5993a4ae2a fix(page): generate newPage for new tabs (#25) 2020-09-03 14:28:13 -07:00
Max Schmitt abd805114a fix(codegen): invalid syntax for Promise.all (#23) 2020-09-03 14:25:00 -07:00
Pavel 05ce2068b8 chore: bump version to 0.3.0 2020-09-03 14:19:23 -07:00
Dmitry Gozman ff102b87de fix: ensure we do not match selector preview (#21)
Also fixes the NPE when _hoveredSelector is undefined.
2020-09-03 14:19:14 -07:00
Pavel 2a164338d3 chore: remove jest config 2020-09-03 13:21:10 -07:00
Pavel 602d4bb9a8 chore: bump version to 0.2.0 2020-09-03 13:16:35 -07:00
Pavel Feldman a98b2d69eb docs: update README.md 2020-09-03 12:39:40 -07:00
Pavel 10eeedc527 feat(fullPage): implement full page screenshot, improve README 2020-09-03 12:38:44 -07:00
Pavel 8728fe1230 chore: roll test-runner to 0.2.10 2020-09-03 10:56:36 -07:00
Max Schmitt 10aa24adf3 fix(pdf): print error when != cr browser is used (#18) 2020-09-03 00:26:50 +02:00
Max Schmitt 14edce8d9a devops: fixed local build (#5) 2020-09-03 00:22:39 +02:00
Dmitry Gozman d92b934a11 fix: default to dpr=2 on mac, avoid close exception (#19) 2020-09-02 14:40:09 -07:00
Pavel 8698ae19c5 feat(screenshot, pdf): add support for capturing screenshots (2) 2020-09-01 12:47:24 -07:00
Pavel 27ea019980 feat(screenshot, pdf): add support for capturing screenshots 2020-09-01 12:46:45 -07:00
Pavel Feldman db85f56a86 docs: update README.md 2020-09-01 12:14:29 -07:00
Pavel fc407de3d7 infra: add ci 2020-09-01 12:12:31 -07:00
Pavel 8267c4aed9 license: Playwright family is licensed under Apache 2 2020-09-01 12:02:51 -07:00
Pavel ea48d31fef test: make tests work in matrix 2020-09-01 11:00:29 -07:00
Pavel 308b583cbb feat(codegen): inline lauch / context options into the generated code 2020-09-01 10:48:11 -07:00
Pavel 8538203e74 fix: use x- tag names to avoid div poisoning 2020-09-01 10:28:35 -07:00
Pavel ee431a3c92 feat(cli): add a bunch of emulation options 2020-09-01 10:11:36 -07:00
Pavel 4df84b20c7 test: use a simple pom 2020-08-31 16:52:47 -07:00
Pavel 481af71f71 test: add basic selector tests 2020-08-31 16:37:38 -07:00
Pavel 453b93cb5a chore: roll test-runner to 0.2.2 2020-08-31 15:35:28 -07:00
Pavel eb12021b5d test: migrate to test-runner 2020-08-31 15:07:50 -07:00
Pavel 214a7cab22 fix(recorder): fix xpath logic 2020-08-31 11:06:42 -07:00
Pavel Feldman 659dc7ab23 fix(recorder): various recorder fixes 2020-08-30 23:14:00 -07:00
Pavel Feldman a87ef72372 chore: extract selector builder 2020-08-30 20:06:00 -07:00
Pavel Feldman be74b393eb feat(recorder): highlight candidate nodes 2020-08-29 22:30:42 -07:00
Pavel Feldman b9a3cdee8a chore: roll playwright to 1.3 2020-08-28 22:33:43 -07:00
Dmitry Gozman 44980ef3d5 feat(recorder): recorder implementation using public apis (#4) 2020-07-16 09:04:11 -07:00
Andrey Lushnikov 7b129aa780 fix: default launched browsers to null viewport (#1)
Otherwise fonts look blurry on retina mac.
2020-07-07 15:26:34 -07:00
Andrey Lushnikov 3ac367e595 chore: add typescript to devdeps 2020-07-07 14:19:31 -07:00
Andrey Lushnikov beeee56b2c chore: set proper upstream version 2020-07-07 14:06:20 -07:00
Andrey Lushnikov 64580c80cc chore: name binary as playwright-cli 2020-06-19 18:06:37 -07:00
Andrey Lushnikov 0f79f41fe2 chore: staging commit
This is a migration of https://github.com/microsoft/playwright/pull/2586
into a separate repository.
2020-06-19 17:56:38 -07:00
Microsoft Open Source c69827bc29 Initial README.md commit 2020-06-19 11:49:43 -07:00
Microsoft Open Source b277c58ecf Initial SECURITY.md commit 2020-06-19 11:49:42 -07:00
Microsoft Open Source 56a2e8b5de Initial LICENSE commit 2020-06-19 11:49:41 -07:00
Microsoft Open Source d655d9ad20 Initial CODE_OF_CONDUCT.md commit 2020-06-19 11:49:40 -07:00
34 changed files with 349 additions and 3704 deletions
-141
View File
@@ -1,141 +0,0 @@
# Publishes @playwright/cli via ESRP. Manual trigger only, regular publishing
# is done from GitHub Actions, see .github/workflows/publish.yml.
# Depending on the selected ref, a manual run publishes:
# - @next (alpha with current timestamp) from main
# - @latest from v* release tags
trigger: none
pr: none
resources:
repositories:
- repository: 1esPipelines
type: git
name: 1ESPipelineTemplates/1ESPipelineTemplates
ref: refs/tags/release
extends:
template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines
parameters:
pool:
name: DevDivPlaywrightAzurePipelinesUbuntu2204
os: linux
sdl:
sourceAnalysisPool:
# SDL tools require windows, see https://aka.ms/AAo6v8e
name: DevDivPlaywrightAzurePipelinesWindows2022
os: windows
stages:
- stage: Stage
jobs:
- job: Build
displayName: "Build npm package"
templateContext:
outputs:
- output: pipelineArtifact
path: $(Build.ArtifactStagingDirectory)/esrp-build
artifact: esrp-build
steps:
- checkout: self
displayName: "Checkout code"
- task: Bash@3
displayName: "Check the branch is main or a v* tag"
inputs:
targetType: "inline"
script: |
if [[ "$BUILD_SOURCE_BRANCH" != "refs/heads/main" && "$BUILD_SOURCE_BRANCH" != refs/tags/v* ]]; then
echo "Can only publish from main or v* tags."
echo "Unexpected branch: $BUILD_SOURCE_BRANCH"
exit 1
fi
env:
BUILD_SOURCE_BRANCH: $(Build.SourceBranch)
- task: UseNode@1
inputs:
version: '24.x'
displayName: "Install Node.js"
- task: Bash@3
displayName: "setup .npmrc"
inputs:
targetType: "inline"
script: |
echo "registry=https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/DevDiv_PublicPackages/npm/registry/" >> .npmrc
- task: npmAuthenticate@0
displayName: "authenticate the private npm registry"
inputs:
workingFile: .npmrc
- script: npm ci
displayName: "npm ci"
- task: Bash@3
name: setVersion
displayName: "Set version and dist-tag"
inputs:
targetType: "inline"
script: |
set -e
BASE_VERSION=$(node -p "require('./package.json').version.split('-')[0]")
if [[ "$BUILD_SOURCE_BRANCH" == refs/tags/v* ]]; then
# Release version is already checked in, only publish what the tag points at.
NPM_DIST_TAG="latest"
if [[ "$BUILD_SOURCE_BRANCH" != "refs/tags/v$BASE_VERSION" ]]; then
echo "ERROR: version '$BASE_VERSION' does not match tag '$BUILD_SOURCE_BRANCH'"
exit 1
fi
else
NPM_DIST_TAG="next"
npm version "${BASE_VERSION}-alpha-$(date +%s)000" --no-git-tag-version
fi
echo "Publishing version $(node -p "require('./package.json').version") with dist-tag $NPM_DIST_TAG"
echo "##vso[task.setvariable variable=npmDistTag;isOutput=true]$NPM_DIST_TAG"
env:
BUILD_SOURCE_BRANCH: $(Build.SourceBranch)
- task: Bash@3
displayName: "Pack the package"
inputs:
targetType: "inline"
script: |
set -e
mkdir -p "$(Build.ArtifactStagingDirectory)/esrp-build"
npm pack --pack-destination="$(Build.ArtifactStagingDirectory)/esrp-build"
ls -la "$(Build.ArtifactStagingDirectory)/esrp-build"
- job: Publish
displayName: "ESRP Release to npm"
dependsOn: Build
variables:
npmDistTag: $[ dependencies.Build.outputs['setVersion.npmDistTag'] ]
templateContext:
type: releaseJob
isProduction: true
inputs:
- input: pipelineArtifact
artifactName: esrp-build
targetPath: $(Build.ArtifactStagingDirectory)/esrp-build
steps:
- checkout: none
- task: EsrpRelease@11
inputs:
connectedservicename: 'Playwright-ESRP-PME'
usemanagedidentity: true
keyvaultname: 'playwright-esrp-pme'
signcertname: 'ESRP-Release-Sign'
clientid: '13434a40-7de4-4c23-81a3-d843dc81c2c5'
intent: 'PackageDistribution'
contenttype: 'npm'
# npm dist-tag to publish with.
productstate: '$(npmDistTag)'
folderlocation: '$(Build.ArtifactStagingDirectory)/esrp-build'
waitforreleasecompletion: true
owners: 'yurys@microsoft.com'
approvers: 'yurys@microsoft.com'
serviceendpointurl: 'https://api.esrp.microsoft.com'
mainpublisher: 'Playwright'
domaintenantid: '975f013f-7f24-47e8-a7d3-abc4752bf346'
displayName: 'ESRP Release to npm'
-9
View File
@@ -1,9 +0,0 @@
---
name: dev
description: Development workflows for the playwright-cli repository. Use when the user asks about rolling dependencies, releasing, or other repo maintenance tasks.
---
# Development skills
* **Rolling Playwright dependency** [roll.md](roll.md)
* **Preparing Release** [release.md](release.md)
-77
View File
@@ -1,77 +0,0 @@
# How to prepare a release
A release is a `chore: mark v<next-patch>` commit whose PR body is the release notes. Example: https://github.com/microsoft/playwright-cli/pull/367.
## Steps
1. **Bump the patch version** in `package.json` (e.g. `0.1.7``0.1.8`), then `npm install` to sync `package-lock.json`. This is the entry point — everything else (branch name, PR title, release notes filename) keys off the new version.
2. **Find the baseline.** The previous release is the last `chore: mark v...` commit on `main`. Read the Playwright version pinned at that commit — that's the baseline for the diff.
```bash
git log --oneline | grep "mark v" | head -1
git show <sha>:package.json | grep '"playwright"'
```
3. **Figure out the playwright commit window.** Convert the baseline's alpha timestamp to a UTC date, and use the new alpha's date as the upper bound. Alphas are either `1.X.0-alpha-<ms-epoch>` or `1.X.0-alpha-<YYYY-MM-DD>`.
```bash
date -u -d @<seconds> '+%Y-%m-%d %H:%M:%S UTC' # for ms-epoch, divide by 1000 first
```
4. **List Playwright commits in the window.** Run from `~/code/playwright` (a local Playwright checkout). `--after` / `--before` work on any ref regardless of what `origin/main` currently points at; `--since` / `--until` can silently return empty if the branch is behind.
```bash
cd ~/code/playwright && git log --after='<baseline-date>' --before='<new-date>' --pretty=format:'%h %ci %s'
```
5. **Filter to CLI-relevant commits.** Keep anything touching the CLI surface or its runtime; drop internal/unrelated churn.
- **Keep:** `src/tools/cli-client/**`, `src/tools/cli-daemon/**`, `src/tools/mcp/**`, `remote/playwrightConnection`, CDP-attach paths, tracing/video APIs the CLI exposes, and anything with a `fix(cli)` / `feat(cli)` / `fix(mcp)` / `feat(mcp)` prefix.
- **Drop:** test-runner rolls, firefox/chromium/webkit version bumps, docs-only, test infra, unrelated refactors.
- Use `git show --stat <sha>` to sanity-check whether a commit's files touch the CLI.
6. **Pull issue context for each kept PR.** The PR's linked issue often has better user-facing wording than the PR/commit title.
```bash
gh pr view <pr> --repo microsoft/playwright --json title,body,closingIssuesReferences
gh issue view <issue> --repo microsoft/playwright-cli --json title,body,state
```
7. **Write the release notes** to `RELEASE_NOTES_v<version>.md`. Use this exact shape — **no top-level `#` header**, the PR title is the heading:
```markdown
## Highlights
- **<issue wording, not commit wording>** ([#<issue>](https://github.com/microsoft/playwright-cli/issues/<issue>)) — one sentence on the user-facing effect. ([microsoft/playwright#<pr>](https://github.com/microsoft/playwright/pull/<pr>))
## Fixes
- `<commit subject>` — what changed and why it matters. ([#<pr>](https://github.com/microsoft/playwright/pull/<pr>))
## Upgrading
```bash
npm install -g @playwright/cli@<version>
```
```
Wording rules:
- **Highlights lead with the user-reported problem from the linked issue**, not the commit subject. Drop internal terms (`cdpPort`, `tombstones`) from highlight bullets.
- Only list things that change user-visible behavior. Skip internal cleanups unless they have a user-facing effect.
- Reference both the playwright-cli issue (if any) and the microsoft/playwright PR.
8. **Commit, push, open PR.** The PR body is the contents of the release notes file (no `#` header, no filename).
```bash
git checkout -b mark-v<version>
git add package.json package-lock.json
git commit -m "chore: mark v<version>"
git push -u origin mark-v<version>
gh pr create --repo microsoft/playwright-cli \
--head pavelfeldman:mark-v<version> \
--base main \
--title "chore: mark v<version>" \
--body "$(cat RELEASE_NOTES_v<version>.md)"
```
## Pitfalls
- **Don't use `--since` / `--until`** when diffing Playwright — if `origin/main` in the local checkout is behind, they return empty. `--after` / `--before` against the local ref work.
- **Don't include a `# playwright-cli vX.Y.Z` header** in the PR body — GitHub already renders the PR title.
- **Don't paraphrase the commit subject as the highlight.** A user who filed an issue described the pain; reuse their framing.
- **Don't include test-runner / browser-version-roll commits** in release notes — they're noise for CLI users.
-47
View File
@@ -1,47 +0,0 @@
# How to roll Playwright dependency
## Steps
1. **Obtain latest Playwright version**
`npm info playwright@next version`
2. **Update Playwright packages** in `package.json`:
- Update `playwright` and `playwright-core` (dependencies) and `@playwright/test` (devDependency) to the target version.
- Run `npm install` to update `package-lock.json`.
- Verify with `npm ls playwright-core` that the root `playwright-core` actually rolled — it is pinned directly in `package.json` and is what `playwright-cli.js` runs; forgetting it leaves the CLI on the old version while tests pass against the new `@playwright/test`.
3. **Run the update script** to sync skills and README:
```bash
node scripts/update.js
```
This script:
- Runs `node playwright-cli.js install --skills` to regenerate skills from the new Playwright version.
- Copies the generated skills from `.claude/skills/playwright-cli/` into `skills/playwright-cli/`.
- Cleans up the generated `.claude/skills/` directory.
3. **Update README.md** with relevant changes from the updated skill at `skills/playwright-cli/SKILL.md`. Compare the skill file with the README and update any sections that are out of date (commands, flags, default behaviors, examples).
4. **Verify** the CLI works:
```bash
node playwright-cli.js --help
```
5. **Test** the CLI:
```bash
npm run test
```
5. **Create a branch and commit**:
- Branch name: `roll_<version>` (e.g. `roll_214`)
- Commit message: `chore: roll Playwright to <version>`
- do not add Co-Authored-By
## Key files
| File | Role |
|---|---|
| `package.json` | Playwright version pins (`playwright`, `@playwright/test`) |
| `playwright-cli.js` | CLI entry point — requires Playwright's program module |
| `scripts/update.js` | Automation script for syncing skills and README after version bump |
| `skills/playwright-cli/SKILL.md` | Skill definition installed from Playwright (source of truth for commands) |
| `README.md` | User-facing docs — must reflect current skill commands and behavior |
-11
View File
@@ -1,11 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
groups:
github-actions:
patterns: ["*"]
schedule:
interval: "weekly"
cooldown:
default-days: 7
+16 -12
View File
@@ -2,25 +2,29 @@ name: CI
on:
push:
branches: [ main ]
branches:
- master
- release-*
pull_request:
branches: [ main ]
branches:
- master
- release-*
jobs:
test:
build:
name: Build
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-15, windows-latest]
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Use Node.js 20
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
node-version: 12.x
- name: install
run: npm ci
- name: Run tests
run: npm run test
+14 -14
View File
@@ -1,21 +1,21 @@
name: Publish
name: "publish"
on:
workflow_dispatch:
release:
types: [published]
jobs:
publish-npm:
if: github.event_name == 'release'
publish-npm-release:
name: "publish to NPM"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # Required for OIDC npm publishing
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm publish
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
registry-url: 'https://registry.npmjs.org'
- uses: microsoft/playwright-github-action@v1
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+28
View File
@@ -0,0 +1,28 @@
name: "devrelease:npm"
on:
push:
branches:
- master
- release-*
jobs:
publish-canary-npm:
name: "publish to NPM"
runs-on: ubuntu-latest
if: github.repository == 'microsoft/playwright-cli'
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 10
registry-url: 'https://registry.npmjs.org'
- uses: microsoft/playwright-github-action@v1
- run: npm ci
- run: |
git config user.email "devops@playwright.dev"
git config user.name "playwright-devops"
npm version "$(node utils/canary_version.js)"
- run: npm publish --tag next
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+4 -9
View File
@@ -1,9 +1,4 @@
node_modules/
.vscode/
/test-results/
/.playwright-cli/
# Ignore self-skill which is a build artifact
.claude/skills/playwright-cli/
.npmrc
# Playwright CLI output (may contain credentials)
.playwright-cli/
/node_modules/
*.swp
.vscode
.trace
+1 -5
View File
@@ -1,6 +1,2 @@
**/*
!README.md
!LICENSE
!playwright-cli.js
!skillCheck.js
!skills/**
!/index.js
-30
View File
@@ -1,30 +0,0 @@
## Commit Convention
Semantic commit messages: `label(scope): description`
Labels: `fix`, `feat`, `chore`, `docs`, `test`, `devops`
```bash
git checkout -b fix-39562
# ... make changes ...
git add <changed-files>
git commit -m "$(cat <<'EOF'
fix(proxy): handle SOCKS proxy authentication
Fixes: https://github.com/microsoft/playwright/issues/39562
EOF
)"
git push origin fix-39562
gh pr create --repo microsoft/playwright --head username:fix-39562 \
--title "fix(proxy): handle SOCKS proxy authentication" \
--body "$(cat <<'EOF'
## Summary
- <describe the change very! briefly>
Fixes https://github.com/microsoft/playwright/issues/39562
EOF
)"
```
Never add Co-Authored-By agents in commit message.
Branch naming for issue fixes: `fix-<issue-number>`
+9
View File
@@ -0,0 +1,9 @@
# Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
-45
View File
@@ -1,45 +0,0 @@
# Contributing
[Playwright CLI sources](https://github.com/microsoft/playwright/tree/main/packages/playwright/src/mcp/terminal) are located in the [Playwright monorepo](https://github.com/microsoft/playwright).
### Clone
```bash
git clone https://github.com/microsoft/playwright
cd playwright
npm i
```
### Build
```bash
npm run watch
```
### Run
```bash
npm run playwright-cli open example.com -- --headed
```
### Test
```bash
npm run test-playwright-cli
```
## Contributor License Agreement
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
### Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+202 -201
View File
@@ -1,201 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) Microsoft Corporation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Portions Copyright (c) Microsoft Corporation.
Portions Copyright 2017 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+4 -576
View File
@@ -1,579 +1,7 @@
# playwright-cli
# 🎭 Playwright CLI [![npm version](https://img.shields.io/npm/v/playwright-cli.svg?style=flat)](https://www.npmjs.com/package/playwright-cli) [![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://join.slack.com/t/playwright/shared_invite/enQtOTEyMTUxMzgxMjIwLThjMDUxZmIyNTRiMTJjNjIyMzdmZDA3MTQxZWUwZTFjZjQwNGYxZGM5MzRmNzZlMWI5ZWUyOTkzMjE5Njg1NDg)
Playwright CLI with SKILLS
Playwright [Command Line Interface](https://playwright.dev/docs/next/cli) has moved directly to Playwright. Use `npx playwright` instead.
### Playwright CLI vs Playwright MCP
This package provides CLI interface into Playwright. If you are using **coding agents**, that is the best fit.
- **CLI**: Modern **coding agents** increasingly favor CLIbased workflows exposed as SKILLs over MCP because CLI invocations are more token-efficient: they avoid loading large tool schemas and verbose accessibility trees into the model context, allowing agents to act through concise, purpose-built commands. This makes CLI + SKILLs better suited for high-throughput coding agents that must balance browser automation with large codebases, tests, and reasoning within limited context windows.
- **MCP**: MCP remains relevant for specialized agentic loops that benefit from persistent state, rich introspection, and iterative reasoning over page structure, such as exploratory automation, self-healing tests, or long-running autonomous workflows where maintaining continuous browser context outweighs token cost concerns. Learn more about [Playwright MCP](https://github.com/microsoft/playwright-mcp).
### Key Features
- **Token-efficient**. Does not force page data into LLM.
### Requirements
- Node.js 18 or newer
- Claude Code, GitHub Copilot, or any other coding agent.
## Getting Started
## Installation
```bash
npm install -g @playwright/cli@latest
playwright-cli --help
```sh
$ npx playwright --help
```
### Installing skills
Claude Code, GitHub Copilot and others will use the locally installed skills.
```bash
playwright-cli install --skills
```
### Skills-less operation
Point your agent at the CLI and let it cook. It'll read the skill off `playwright-cli --help` on its own:
```
Test the "add todo" flow on https://demo.playwright.dev/todomvc using playwright-cli.
Check playwright-cli --help for available commands.
```
## Demo
```
> Use playwright skills to test https://demo.playwright.dev/todomvc/.
Take screenshots for all successful and failing scenarios.
```
Your agent will be running commands, but it does not mean you can't play with it manually:
```
playwright-cli open https://demo.playwright.dev/todomvc/ --headed
playwright-cli type "Buy groceries"
playwright-cli press Enter
playwright-cli type "Water flowers"
playwright-cli press Enter
playwright-cli check e21
playwright-cli check e35
playwright-cli screenshot
```
## Headed operation
Playwright CLI is headless by default. If you'd like to see the browser, pass `--headed` to `open`:
```bash
playwright-cli open https://playwright.dev --headed
```
## Sessions
Playwright CLI keeps the browser profile in memory by default. Your cookies and storage state
are preserved between CLI calls within the session, but lost when the browser closes. Use
`--persistent` to save the profile to disk for persistence across browser restarts.
You can use different instances of the browser for different projects with sessions. Pass `-s=` to
the invocation to talk to a specific browser.
```bash
playwright-cli open https://playwright.dev
playwright-cli -s=example open https://example.com --persistent
playwright-cli list
```
You can run your coding agent with the `PLAYWRIGHT_CLI_SESSION` environment variable:
```bash
PLAYWRIGHT_CLI_SESSION=todo-app claude .
```
Or instruct it to prepend `-s=` to the calls.
Manage your sessions as follows:
```bash
playwright-cli list # list all sessions
playwright-cli close-all # close all browsers
playwright-cli kill-all # forcefully kill all browser processes
```
## Monitoring
Use `playwright-cli show` to open a visual dashboard that lets you see and control all running
browser sessions. This is useful when your coding agents are running browser automation in the
background and you want to observe their progress or step in to help.
```bash
playwright-cli show
```
<img width="1107" height="729" alt="Image" src="https://github.com/user-attachments/assets/99df739d-106a-4520-b004-bb315db41da7" />
The dashboard opens a window with two views:
- **Session grid** — shows all active sessions grouped by workspace, each with a live screencast
preview, session name, current URL, and page title. Click any session to zoom in.
- **Session detail** — shows a live view of the selected session with a tab bar, navigation
controls (back, forward, reload, address bar), and full remote control. Click into the viewport
to take over mouse and keyboard input; press Escape to release.
From the grid you can also close running sessions or delete data for inactive ones.
## Commands
### Core
```bash
playwright-cli open [url] # open browser, optionally navigate to url
playwright-cli goto <url> # navigate to a url
playwright-cli close # close the page
playwright-cli type <text> # type text into editable element
playwright-cli click <ref> [button] # perform click on a web page
playwright-cli dblclick <ref> [button] # perform double click on a web page
playwright-cli fill <ref> <text> # fill text into editable element
playwright-cli fill <ref> <text> --submit # fill and press Enter
playwright-cli drag <startRef> <endRef> # perform drag and drop between two elements
playwright-cli drop <ref> --path=<file> # drop files onto an element (from outside the page)
playwright-cli drop <ref> --data="k=v" # drop data onto an element
playwright-cli hover <ref> # hover over element on page
playwright-cli select <ref> <val> # select an option in a dropdown
playwright-cli upload <file> # upload one or multiple files
playwright-cli check <ref> # check a checkbox or radio button
playwright-cli uncheck <ref> # uncheck a checkbox or radio button
playwright-cli snapshot # capture page snapshot to obtain element ref
playwright-cli snapshot --filename=f # save snapshot to specific file
playwright-cli snapshot <ref> # snapshot a specific element
playwright-cli snapshot --depth=N # limit snapshot depth for efficiency
playwright-cli find <text> # search the snapshot for text, returns matching nodes
playwright-cli find --regex <pattern> # search the snapshot with a regexp
playwright-cli eval <func> [ref] # evaluate javascript expression on page or element
playwright-cli dialog-accept [prompt] # accept a dialog
playwright-cli dialog-dismiss # dismiss a dialog
playwright-cli resize <w> <h> # resize the browser window
```
### Navigation
```bash
playwright-cli go-back # go back to the previous page
playwright-cli go-forward # go forward to the next page
playwright-cli reload # reload the current page
```
### Keyboard
```bash
playwright-cli press <key> # press a key on the keyboard, `a`, `arrowleft`
playwright-cli keydown <key> # press a key down on the keyboard
playwright-cli keyup <key> # press a key up on the keyboard
```
### Mouse
```bash
playwright-cli mousemove <x> <y> # move mouse to a given position
playwright-cli mousedown [button] # press mouse down
playwright-cli mouseup [button] # press mouse up
playwright-cli mousewheel <dx> <dy> # scroll mouse wheel
```
### Save as
```bash
playwright-cli screenshot [ref] # screenshot of the current page or element
playwright-cli screenshot --filename=f # save screenshot with specific filename
playwright-cli screenshot --hires # capture at full device pixel ratio
playwright-cli pdf # save page as pdf
playwright-cli pdf --filename=page.pdf # save pdf with specific filename
```
### Tabs
```bash
playwright-cli tab-list # list all tabs
playwright-cli tab-new [url] # create a new tab
playwright-cli tab-close [index] # close a browser tab
playwright-cli tab-select <index> # select a browser tab
```
### Storage
```bash
playwright-cli state-save [filename] # save storage state
playwright-cli state-load <filename> # load storage state
# Cookies
playwright-cli cookie-list [--domain] # list cookies
playwright-cli cookie-get <name> # get a cookie
playwright-cli cookie-set <name> <val> # set a cookie
playwright-cli cookie-delete <name> # delete a cookie
playwright-cli cookie-clear # clear all cookies
# LocalStorage
playwright-cli localstorage-list # list localStorage entries
playwright-cli localstorage-get <key> # get localStorage value
playwright-cli localstorage-set <k> <v> # set localStorage value
playwright-cli localstorage-delete <k> # delete localStorage entry
playwright-cli localstorage-clear # clear all localStorage
# SessionStorage
playwright-cli sessionstorage-list # list sessionStorage entries
playwright-cli sessionstorage-get <k> # get sessionStorage value
playwright-cli sessionstorage-set <k> <v> # set sessionStorage value
playwright-cli sessionstorage-delete <k> # delete sessionStorage entry
playwright-cli sessionstorage-clear # clear all sessionStorage
```
### Network
```bash
playwright-cli route <pattern> [opts] # mock network requests
playwright-cli route-list # list active routes
playwright-cli unroute [pattern] # remove route(s)
```
### DevTools
```bash
playwright-cli console [min-level] # list console messages
playwright-cli requests # list all network requests since loading the page
playwright-cli request <index> # show details for a specific request
playwright-cli run-code <code> # run playwright code snippet
playwright-cli run-code --filename=f # run playwright code from a file
playwright-cli tracing-start # start trace recording
playwright-cli tracing-stop # stop trace recording
playwright-cli recording-start # record user actions in the browser
playwright-cli recording-stop # stop recording, print actions as Playwright code
playwright-cli video-start [filename] # start video recording
playwright-cli video-chapter <title> # add a chapter marker to the video
playwright-cli video-show-actions # annotate each action with a callout in the video
playwright-cli video-hide-actions # stop annotating actions in the video
playwright-cli video-stop # stop video recording
playwright-cli show # open the visual dashboard
playwright-cli show --annotate # launch dashboard for UI review / design feedback
playwright-cli generate-locator <ref> # generate a playwright locator for an element
playwright-cli highlight <ref> # show a persistent highlight overlay
playwright-cli highlight <ref> --style= # highlight with a custom CSS style
playwright-cli highlight <ref> --hide # hide highlight on a specific element
playwright-cli highlight --hide # hide all page highlights
```
### Open parameters
```bash
playwright-cli open --browser=chrome # use specific browser
playwright-cli open --mobile # emulate a generic mobile device
playwright-cli open --device="iPhone 15" # emulate a specific device
playwright-cli attach --extension=chrome # connect via Playwright Extension
playwright-cli attach --cdp=chrome # attach to running Chrome/Edge by channel
playwright-cli attach --cdp=<url> # attach via CDP endpoint
playwright-cli detach # detach an attached session, leaves the external browser running
playwright-cli open --persistent # use persistent profile
playwright-cli open --profile=<path> # use custom profile directory
playwright-cli open --config=file.json # use config file
playwright-cli close # close the browser
playwright-cli delete-data # delete user data for default session
```
### Snapshots
After each command, playwright-cli provides a snapshot of the current browser state.
```bash
> playwright-cli goto https://example.com
### Page
- Page URL: https://example.com/
- Page Title: Example Domain
### Snapshot
[Snapshot](.playwright-cli/page-2026-02-14T19-22-42-679Z.yml)
```
You can also take a snapshot on demand using `playwright-cli snapshot` command. All the options below can be combined as needed.
```bash
# default - save to a file with timestamp-based name
playwright-cli snapshot
# save to file, use when snapshot is a part of the workflow result
playwright-cli snapshot --filename=after-click.yaml
# snapshot an element instead of the whole page
playwright-cli snapshot "#main"
# limit snapshot depth for efficiency, take a partial snapshot afterwards
playwright-cli snapshot --depth=4
playwright-cli snapshot e34
# include each element's bounding box as [box=x,y,width,height]
playwright-cli snapshot --boxes
# search a large snapshot instead of capturing it all — returns matching nodes
# with 3 lines of context around each match (like grep -C)
playwright-cli find "Add to cart"
playwright-cli find --regex "\\$[0-9]+\\.[0-9]{2}"
# wrap the regexp in slashes to add flags, e.g. /i for case-insensitive
playwright-cli find --regex "/sign (in|up)/i"
```
### Targeting elements
By default, use refs from the snapshot to interact with page elements.
```bash
# get snapshot with refs
playwright-cli snapshot
# interact using a ref
playwright-cli click e15
```
You can also use css selectors or Playwright locators.
```bash
# css selector
playwright-cli click "#main > button.submit"
# role locator
playwright-cli click "getByRole('button', { name: 'Submit' })"
# test id
playwright-cli click "getByTestId('submit-button')"
```
### Sessions
```bash
playwright-cli -s=name <cmd> # run command in named session
playwright-cli -s=name close # stop a named browser
playwright-cli -s=name delete-data # delete user data for named browser
playwright-cli list # list all sessions
playwright-cli close-all # close all browsers
playwright-cli kill-all # forcefully kill all browser processes
```
### Local installation
If global `playwright-cli` command is not available, try a local version via `npx playwright cli`:
```bash
npx --no-install playwright --version
```
When local version is available, use `npx playwright cli` in all commands. Otherwise, install `playwright-cli` as a global command:
```bash
npm install -g @playwright/cli@latest
```
## Configuration file
The Playwright CLI can be configured using a JSON configuration file. You can specify the configuration file using the `--config` command line option:
```bash
playwright-cli --config path/to/config.json open example.com
```
Playwright CLI will load config from `.playwright/cli.config.json` by default so that you did not need to specify it every time.
<details>
<summary>Configuration file schema</summary>
```typescript
{
/**
* The browser to use.
*/
browser?: {
/**
* The type of browser to use.
*/
browserName?: 'chromium' | 'firefox' | 'webkit';
/**
* Keep the browser profile in memory, do not save it to disk.
*/
isolated?: boolean;
/**
* Path to a user data directory for browser profile persistence.
* Temporary directory is created by default.
*/
userDataDir?: string;
/**
* Launch options passed to
* @see https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context
*
* This is useful for settings options like `channel`, `headless`, `executablePath`, etc.
*/
launchOptions?: playwright.LaunchOptions;
/**
* Context options for the browser context.
*
* This is useful for settings options like `viewport`.
*/
contextOptions?: playwright.BrowserContextOptions;
/**
* Chrome DevTools Protocol endpoint to connect to an existing browser instance in case of Chromium family browsers.
*/
cdpEndpoint?: string;
/**
* CDP headers to send with the connect request.
*/
cdpHeaders?: Record<string, string>;
/**
* Timeout in milliseconds for connecting to CDP endpoint. Defaults to 30000 (30 seconds). Pass 0 to disable timeout.
*/
cdpTimeout?: number;
/**
* Remote endpoint to connect to an existing Playwright server.
*/
remoteEndpoint?: string;
/**
* Paths to TypeScript files to add as initialization scripts for Playwright page.
*/
initPage?: string[];
/**
* Paths to JavaScript files to add as initialization scripts.
* The scripts will be evaluated in every page before any of the page's scripts.
*/
initScript?: string[];
},
/**
* If specified, saves the Playwright video of the session into the output directory.
*/
saveVideo?: {
width: number;
height: number;
};
/**
* The directory to save output files.
*/
outputDir?: string;
/**
* Whether to save snapshots, console messages, network logs and other session logs to a file or to the standard output. Defaults to "stdout".
*/
outputMode?: 'file' | 'stdout';
console?: {
/**
* The level of console messages to return. Each level includes the messages of more severe levels. Defaults to "info".
*/
level?: 'error' | 'warning' | 'info' | 'debug';
},
network?: {
/**
* List of origins to allow the browser to request. Default is to allow all. Origins matching both `allowedOrigins` and `blockedOrigins` will be blocked.
*/
allowedOrigins?: string[];
/**
* List of origins to block the browser to request. Origins matching both `allowedOrigins` and `blockedOrigins` will be blocked.
*/
blockedOrigins?: string[];
};
/**
* Specify the attribute to use for test ids, defaults to "data-testid".
*/
testIdAttribute?: string;
timeouts?: {
/*
* Configures default action timeout: https://playwright.dev/docs/api/class-page#page-set-default-timeout. Defaults to 5000ms.
*/
action?: number;
/*
* Configures default navigation timeout: https://playwright.dev/docs/api/class-page#page-set-default-navigation-timeout. Defaults to 60000ms.
*/
navigation?: number;
};
/**
* Whether to allow file uploads from anywhere on the file system.
* By default (false), file uploads are restricted to paths within the MCP roots only.
*/
allowUnrestrictedFileAccess?: boolean;
/**
* Specify the language to use for code generation.
*/
codegen?: 'typescript' | 'none';
}
```
</details>
<details>
<summary>Configuration via env</summary>
| Environment |
|-------------|
| `PLAYWRIGHT_MCP_ALLOWED_HOSTS` comma-separated list of hosts this server is allowed to serve from. Defaults to the host the server is bound to. Pass '*' to disable the host check. |
| `PLAYWRIGHT_MCP_ALLOWED_ORIGINS` semicolon-separated list of TRUSTED origins to allow the browser to request. Default is to allow all. Important: *does not* serve as a security boundary and *does not* affect redirects. |
| `PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS` allow access to files outside of the workspace roots. Also allows unrestricted access to file:// URLs. By default access to file system is restricted to workspace root directories (or cwd if no roots are configured) only, and navigation to file:// URLs is blocked. |
| `PLAYWRIGHT_MCP_BLOCKED_ORIGINS` semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed. Important: *does not* serve as a security boundary and *does not* affect redirects. |
| `PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS` block service workers |
| `PLAYWRIGHT_MCP_BROWSER` browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge. |
| `PLAYWRIGHT_MCP_CAPS` comma-separated list of additional capabilities to enable, possible values: vision, pdf. |
| `PLAYWRIGHT_MCP_CDP_ENDPOINT` CDP endpoint to connect to. |
| `PLAYWRIGHT_MCP_CDP_HEADERS` CDP headers to send with the connect request, multiple can be specified. |
| `PLAYWRIGHT_MCP_CDP_TIMEOUT` timeout for the CDP connection. |
| `PLAYWRIGHT_MCP_CONFIG` path to the configuration file. |
| `PLAYWRIGHT_MCP_CONSOLE_LEVEL` level of console messages to return: "error", "warning", "info", "debug". Each level includes the messages of more severe levels. |
| `PLAYWRIGHT_MCP_DEVICE` device to emulate, for example: "iPhone 15" |
| `PLAYWRIGHT_MCP_EXECUTABLE_PATH` path to the browser executable. |
| `PLAYWRIGHT_MCP_EXTENSION` Connect to a running browser instance (Edge/Chrome only). Requires the "Playwright MCP Bridge" browser extension to be installed. |
| `PLAYWRIGHT_MCP_GRANT_PERMISSIONS` List of permissions to grant to the browser context, for example "geolocation", "clipboard-read", "clipboard-write". |
| `PLAYWRIGHT_MCP_HEADLESS` whether to run browser in headless mode, headless by default. |
| `PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS` ignore https errors |
| `PLAYWRIGHT_MCP_INIT_PAGE` path to TypeScript file to evaluate on Playwright page object |
| `PLAYWRIGHT_MCP_INIT_SCRIPT` path to JavaScript file to add as an initialization script. The script will be evaluated in every page before any of the page's scripts. Can be specified multiple times. |
| `PLAYWRIGHT_MCP_ISOLATED` keep the browser profile in memory, do not save it to disk. |
| `PLAYWRIGHT_MCP_SANDBOX` whether to enable the browser sandbox. |
| `PLAYWRIGHT_MCP_OUTPUT_DIR` path to the directory for output files. |
| `PLAYWRIGHT_MCP_PROXY_BYPASS` comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com" |
| `PLAYWRIGHT_MCP_PROXY_SERVER` specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080" |
| `PLAYWRIGHT_MCP_SAVE_TRACE` Whether to save the Playwright Trace of the session into the output directory. |
| `PLAYWRIGHT_MCP_SAVE_VIDEO` Whether to save the video of the session into the output directory. For example "--save-video=800x600" |
| `PLAYWRIGHT_MCP_SECRETS_FILE` path to a file containing secrets in the dotenv format |
| `PLAYWRIGHT_MCP_STORAGE_STATE` path to the storage state file for isolated sessions. |
| `PLAYWRIGHT_MCP_TEST_ID_ATTRIBUTE` specify the attribute to use for test ids, defaults to "data-testid" |
| `PLAYWRIGHT_MCP_TIMEOUT_ACTION` specify action timeout in milliseconds, defaults to 5000ms |
| `PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION` specify navigation timeout in milliseconds, defaults to 60000ms |
| `PLAYWRIGHT_MCP_USER_AGENT` specify user agent string |
| `PLAYWRIGHT_MCP_USER_DATA_DIR` path to the user data directory. If not specified, a temporary directory will be created. |
| `PLAYWRIGHT_MCP_VIEWPORT_SIZE` specify browser viewport size in pixels, for example "1280x720" |
</details>
## Specific tasks
The installed skill includes detailed reference guides for common tasks:
* **Running and Debugging Playwright tests** — run, debug and manage Playwright test suites
* **Request mocking** — intercept and mock network requests
* **Running Playwright code** — execute arbitrary Playwright scripts
* **Browser session management** — manage multiple browser sessions
* **Storage state (cookies, localStorage)** — persist and restore browser state
* **Test generation (plan / generate / heal)** — generate Playwright tests from a spec or interactions
* **Tracing** — record and inspect execution traces
* **Video recording** — capture browser session videos
* **Inspecting element attributes** — get element id, class, or any attribute not visible in the snapshot
+34 -7
View File
@@ -1,14 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V1.0.0 BLOCK -->
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which
includes all source code repositories in our GitHub organizations.
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
For security reporting information, locations, contact information, and policies,
please review the latest guidance for Microsoft repositories at
[https://aka.ms/SECURITY.md](https://aka.ms/SECURITY.md).
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
<!-- END MICROSOFT SECURITY.MD BLOCK -->
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->
+4 -9
View File
@@ -1,3 +1,5 @@
#!/usr/bin/env node
/**
* Copyright (c) Microsoft Corporation.
*
@@ -14,12 +16,5 @@
* limitations under the License.
*/
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
workers: process.env.CI ? 2 : undefined,
reporter: 'list',
});
console.error(`playwright-cli has moved to playwright. Use npx playwright instead.`);
process.exit(1);
+3 -85
View File
@@ -1,87 +1,5 @@
{
"name": "@playwright/cli",
"version": "0.1.19",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@playwright/cli",
"version": "0.1.19",
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.63.0-alpha-2026-08-31",
"playwright-core": "1.63.0-alpha-2026-08-31"
},
"bin": {
"playwright-cli": "playwright-cli.js"
},
"devDependencies": {
"@playwright/test": "1.63.0-alpha-2026-08-31",
"@types/node": "^25.2.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@playwright/test": {
"version": "1.63.0-alpha-2026-08-31",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0-alpha-2026-08-31.tgz",
"integrity": "sha512-sNAYSkzbTC67x/v4ZekJ5ZzBFhSaHGc5t8+/3lqZI9LSvtYWVC+L2bHDbfFIhVXBjtl+1v+rg9Fdx+Rx3w2E8g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.63.0-alpha-2026-08-31"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/@types/node": {
"version": "25.2.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz",
"integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/playwright": {
"version": "1.63.0-alpha-2026-08-31",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0-alpha-2026-08-31.tgz",
"integrity": "sha512-3XAsuznfu8jBVJ4QxdGvBkt0+b8ZFwuwJYyOfiIw5ZjUOrNLNRhKxzLzLuydou3gJ9c6eMwVqgzdiOwhy54Kzw==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.63.0-alpha-2026-08-31"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/playwright-core": {
"version": "1.63.0-alpha-2026-08-31",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0-alpha-2026-08-31.tgz",
"integrity": "sha512-1ek0Lyr12h6jcs/WTcNoVtzZkQp7D/90PsMuBW/Rm6h3AsWAbzpqj0geMv8+8Tzzr9CSUYvg9kznrVZINQMXXw==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
}
}
"name": "playwright-cli",
"version": "0.180.0-next",
"lockfileVersion": 1
}
+8 -22
View File
@@ -1,31 +1,17 @@
{
"name": "@playwright/cli",
"version": "0.1.19",
"name": "playwright-cli",
"version": "0.180.0",
"description": "Playwright CLI",
"repository": {
"type": "git",
"url": "git+https://github.com/microsoft/playwright-cli.git"
},
"repository": "github:Microsoft/playwright-cli",
"homepage": "https://playwright.dev",
"engines": {
"node": ">=18"
"scripts": {},
"bin": {
"playwright-cli": "./index.js"
},
"author": {
"name": "Microsoft Corporation"
},
"license": "Apache-2.0",
"scripts": {
"test": "playwright test"
},
"devDependencies": {
"@playwright/test": "1.63.0-alpha-2026-08-31",
"@types/node": "^25.2.1"
},
"dependencies": {
"playwright": "1.63.0-alpha-2026-08-31",
"playwright-core": "1.63.0-alpha-2026-08-31"
},
"bin": {
"playwright-cli": "playwright-cli.js"
}
"dependencies": {},
"devDependencies": {}
}
-115
View File
@@ -1,115 +0,0 @@
#!/usr/bin/env node
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @ts-check
const fs = require('fs');
const path = require('path');
const { program } = require('playwright-core/lib/tools/cli-client/program');
const coreBundle = require('playwright-core/lib/coreBundle');
const { tools, registry } = coreBundle;
const { checkInstalledSkills, frame } = require('./skillCheck');
const packageJson = require('./package.json');
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
main();
async function main() {
await checkForUpdates().catch(() => {});
program({ embedderVersion: packageJson.version });
}
async function checkForUpdates() {
if (process.env.NO_UPDATE_NOTIFIER || process.env.CI)
return;
const cache = readCache();
const stale = !cache || (Date.now() - cache.lastCheck) > ONE_DAY_MS;
if (!stale)
return;
writeCache({ lastCheck: Date.now() });
const command = process.argv.slice(2).find(arg => !arg.startsWith('-'));
if (command !== 'install')
checkInstalledSkills();
const latest = await fetchLatestVersion();
if (latest && tools.compareSemver(latest, packageJson.version) > 0)
printNotice(packageJson.version, latest);
}
async function fetchLatestVersion() {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 1500);
try {
const res = await fetch(`https://registry.npmjs.org/${packageJson.name}/latest`, { signal: controller.signal });
if (!res.ok)
return undefined;
const json = await res.json();
return typeof json.version === 'string' ? json.version : undefined;
} finally {
clearTimeout(timeout);
}
} catch {
return undefined;
}
}
/**
*
* @param {string} current
* @param {string} latest
*/
function printNotice(current, latest) {
process.stderr.write('\n' + frame([
`Update available for ${packageJson.name}: ${current}${latest}`,
`Run \`npm install -g ${packageJson.name}@latest\` (global) or`,
`\`npm install --save-dev ${packageJson.name}@latest\` (local) to update.`,
]) + '\n');
}
function cacheFile() {
const dir = process.env.PLAYWRIGHT_CLI_INSTALLATION_FOR_TEST || registry.defaultRegistryDirectory();
return path.join(dir, 'cli-update-check.json');
}
function readCache() {
const file = cacheFile();
try {
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
if (typeof data.lastCheck === 'number')
return data;
} catch {
}
return undefined;
}
/**
* @param {*} data
*/
function writeCache(data) {
const file = cacheFile();
try {
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, JSON.stringify(data));
} catch {
}
}
-42
View File
@@ -1,42 +0,0 @@
const { execSync } = require('child_process');
const fs = require('fs/promises');
const path = require('path');
const rootDir = path.resolve(__dirname, '..');
const skillsDir = path.join(rootDir, 'skills');
function run(command, options = {}) {
console.log(`Running: ${command}`);
execSync(command, { stdio: 'inherit', cwd: rootDir, ...options });
}
async function main() {
// 2. Run playwright-cli install-skills
console.log('\n=== Running playwright-cli install --skills ===\n');
run('node playwright-cli.js install --skills');
// 3. Move generated skills into the existing skills folder
console.log('\n=== Updating skills folder ===\n');
const generatedSkillsDir = path.join(rootDir, '.claude', 'skills', 'playwright-cli');
const targetSkillsDir = path.join(skillsDir, 'playwright-cli');
try {
await fs.access(generatedSkillsDir);
// Remove existing skills and copy new ones
await fs.rm(targetSkillsDir, { recursive: true, force: true });
await fs.cp(generatedSkillsDir, targetSkillsDir, { recursive: true });
console.log(`Copied skills from ${generatedSkillsDir} to ${targetSkillsDir}`);
// Clean up generated skills directory
await fs.rm(generatedSkillsDir, { recursive: true });
console.log('Cleaned up generated skills directory');
} catch {
console.warn('Warning: Generated skills directory not found at', generatedSkillsDir);
}
console.log('\n=== Update complete! ===\n');
}
main().catch(err => {
console.error('Error:', err);
process.exit(1);
});
-79
View File
@@ -1,79 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @ts-check
const fs = require('fs');
const path = require('path');
function bundledSkillFile() {
const corePath = require.resolve('playwright-core/package.json');
return path.join(path.dirname(corePath), 'lib', 'tools', 'skills', 'playwright-cli', 'SKILL.md');
}
function installedSkillTargets() {
const cwd = process.cwd();
return [
{ dir: path.join(cwd, '.claude', 'skills', 'playwright-cli'), command: 'playwright-cli install --skills' },
{ dir: path.join(cwd, '.agents', 'skills', 'playwright-cli'), command: 'playwright-cli install --skills=agents' },
];
}
/**
* @param {string} file
* @returns
*/
function readSkill(file) {
// Normalize line endings, they could be affected by git or editor settings.
return fs.existsSync(file) ? fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n') : null;
}
/**
* @param {string[]} lines
* @returns {string}
*/
function frame(lines) {
const width = Math.max(...lines.map(line => line.length));
const top = '╔' + '═'.repeat(width + 2) + '╗';
const bottom = '╚' + '═'.repeat(width + 2) + '╝';
const body = lines.map(line => `${line.padEnd(width)}`);
return [top, ...body, bottom].join('\n') + '\n';
}
function checkInstalledSkills() {
try {
const bundled = readSkill(bundledSkillFile());
if (!bundled)
return;
for (const target of installedSkillTargets()) {
const installed = readSkill(path.join(target.dir, 'SKILL.md'));
if (installed === null)
continue;
if (installed !== bundled) {
process.stderr.write(frame([
`The playwright-cli skill at '${path.relative(process.cwd(), target.dir)}'`,
`does not match the tool version.`,
``,
`Run \`${target.command}\``,
`to install the up-to-date skill.`,
]));
}
}
} catch {
}
}
module.exports = { checkInstalledSkills, frame };
-425
View File
@@ -1,425 +0,0 @@
---
name: playwright-cli
description: Automate browser interactions, test web pages and work with Playwright tests.
allowed-tools: Bash(playwright-cli:*) Bash(npx:*) Bash(npm:*)
---
# Browser Automation with playwright-cli
## Quick start
```bash
# open new browser
playwright-cli open
# navigate to a page
playwright-cli goto https://playwright.dev
# interact with the page using refs from the snapshot
playwright-cli click e15
playwright-cli type "page.click"
playwright-cli press Enter
# take a screenshot (rarely used, as snapshot is more common)
playwright-cli screenshot
# close the browser
playwright-cli close
```
## Commands
### Core
```bash
playwright-cli open
# open and navigate right away
playwright-cli open https://example.com/
playwright-cli goto https://playwright.dev
playwright-cli type "search query"
playwright-cli click e3
playwright-cli dblclick e7
# --submit presses Enter after filling the element
playwright-cli fill e5 "user@example.com" --submit
playwright-cli drag e2 e8
# drop files or data onto an element (from outside the page)
playwright-cli drop e4 --path=./image.png
playwright-cli drop e4 --data="text/plain=hello world"
playwright-cli hover e4
playwright-cli select e9 "option-value"
playwright-cli upload ./document.pdf
playwright-cli check e12
playwright-cli uncheck e12
playwright-cli snapshot
# search the snapshot for text or a regexp, returns matching nodes with surrounding context
playwright-cli find "Sign in"
playwright-cli find --regex "Sign (in|up)"
# wrap the regexp in slashes to add flags, e.g. /i for case-insensitive
playwright-cli find --regex "/sign (in|up)/i"
playwright-cli eval "document.title"
playwright-cli eval "el => el.textContent" e5
# get element id, class, or any attribute not visible in the snapshot
playwright-cli eval "el => el.id" e5
playwright-cli eval "el => el.getAttribute('data-testid')" e5
playwright-cli dialog-accept
playwright-cli dialog-accept "confirmation text"
playwright-cli dialog-dismiss
playwright-cli resize 1920 1080
playwright-cli close
```
### Navigation
```bash
playwright-cli go-back
playwright-cli go-forward
playwright-cli reload
```
### Keyboard
```bash
playwright-cli press Enter
playwright-cli press ArrowDown
playwright-cli keydown Shift
playwright-cli keyup Shift
```
### Mouse
```bash
playwright-cli mousemove 150 300
playwright-cli mousedown
playwright-cli mousedown right
playwright-cli mouseup
playwright-cli mouseup right
playwright-cli mousewheel 0 100
```
### Save as
```bash
playwright-cli screenshot
playwright-cli screenshot e5
playwright-cli screenshot --filename=page.png
playwright-cli screenshot --hires
playwright-cli pdf --filename=page.pdf
```
### Tabs
```bash
playwright-cli tab-list
playwright-cli tab-new
playwright-cli tab-new https://example.com/page
playwright-cli tab-close
playwright-cli tab-close 2
playwright-cli tab-select 0
```
### Storage
```bash
playwright-cli state-save
playwright-cli state-save auth.json
playwright-cli state-load auth.json
# Cookies
playwright-cli cookie-list
playwright-cli cookie-list --domain=example.com
playwright-cli cookie-get session_id
playwright-cli cookie-set session_id abc123
playwright-cli cookie-set session_id abc123 --domain=example.com --httpOnly --secure
playwright-cli cookie-delete session_id
playwright-cli cookie-clear
# LocalStorage
playwright-cli localstorage-list
playwright-cli localstorage-get theme
playwright-cli localstorage-set theme dark
playwright-cli localstorage-delete theme
playwright-cli localstorage-clear
# SessionStorage
playwright-cli sessionstorage-list
playwright-cli sessionstorage-get step
playwright-cli sessionstorage-set step 3
playwright-cli sessionstorage-delete step
playwright-cli sessionstorage-clear
```
### Network
```bash
playwright-cli route "**/*.jpg" --status=404
playwright-cli route "https://api.example.com/**" --body='{"mock": true}'
playwright-cli route-list
playwright-cli unroute "**/*.jpg"
playwright-cli unroute
```
### DevTools
```bash
playwright-cli console
playwright-cli console warning
playwright-cli requests
playwright-cli request 5
playwright-cli run-code "async page => await page.context().grantPermissions(['geolocation'])"
playwright-cli run-code --filename=script.js
playwright-cli tracing-start
playwright-cli tracing-stop
# record user actions in the browser, print them as Playwright code on stop
playwright-cli recording-start
playwright-cli recording-stop
playwright-cli video-start video.webm
playwright-cli video-chapter "Chapter Title" --description="Details" --duration=2000
playwright-cli video-stop
# annotate each subsequent action (click, type, ...) with a callout naming the action and highlighting the target
playwright-cli video-show-actions --duration=600 --position=top-right
playwright-cli video-hide-actions
# launch the dashboard for UI review / design feedback — user annotates the page, you receive the annotated screenshot, snapshot, and notes
playwright-cli show --annotate
# generate a Playwright locator for an element from its ref or selector
playwright-cli generate-locator e5 --raw
# show a persistent highlight overlay for an element, optionally with a custom style
playwright-cli highlight e5
playwright-cli highlight e5 --style="outline: 3px dashed red"
# hide a single element highlight, or all page highlights when no target is given
playwright-cli highlight e5 --hide
playwright-cli highlight --hide
```
## Raw output
The global `--raw` option strips page status, generated code, and snapshot sections from the output, returning only the result value. Use it to pipe command output into other tools. Commands that don't produce output return nothing.
```bash
playwright-cli --raw eval "JSON.stringify(performance.timing)" | jq '.loadEventEnd - .navigationStart'
playwright-cli --raw eval "JSON.stringify([...document.querySelectorAll('a')].map(a => a.href))" > links.json
playwright-cli --raw snapshot > before.yml
playwright-cli click e5
playwright-cli --raw snapshot > after.yml
diff before.yml after.yml
TOKEN=$(playwright-cli --raw cookie-get session_id)
playwright-cli --raw localstorage-get theme
```
For structured output wrapping every reply as JSON, pass --json
```bash
playwright-cli list --json
```
## Open parameters
```bash
# Use specific browser when creating session
playwright-cli open --browser=chrome
playwright-cli open --browser=firefox
playwright-cli open --browser=webkit
playwright-cli open --browser=msedge
# Emulate a generic mobile device (Pixel 10 for Chromium, iPhone 17 for WebKit).
# Prefer this when a mobile layout is acceptable: mobile pages are usually
# lighter, so snapshots are smaller and cheaper.
playwright-cli open --mobile
playwright-cli open --device="iPhone 15"
# Use persistent profile (by default profile is in-memory)
playwright-cli open --persistent
# Use persistent profile with custom directory
playwright-cli open --profile=/path/to/profile
# Connect to browser via Playwright Extension
playwright-cli attach --extension=chrome
# Connect to a running Chrome or Edge by channel name
playwright-cli attach --cdp=chrome
playwright-cli attach --cdp=msedge
# Connect to a running browser via CDP endpoint
playwright-cli attach --cdp=http://localhost:9222
# Start with config file
playwright-cli open --config=my-config.json
# Close the browser
playwright-cli close
# Detach from an attached browser (leaves the external browser running)
playwright-cli -s=msedge detach
# Delete user data for the default session
playwright-cli delete-data
```
## URLs with `&` on Windows
On Windows, `cmd.exe` and PowerShell treat `&` as a command separator, so URLs with multiple query parameters get truncated before `playwright-cli` runs. Escape `&` with `^&` in `cmd.exe`, or use `--%` in PowerShell:
```batch
playwright-cli goto "https://example.com/?a=1^&b=2"
```
```powershell
playwright-cli --% goto "https://example.com/?a=1&b=2"
```
## Snapshots
After each command, playwright-cli provides a snapshot of the current browser state.
```bash
> playwright-cli goto https://example.com
### Page
- Page URL: https://example.com/
- Page Title: Example Domain
### Snapshot
[Snapshot](.playwright-cli/page-2026-02-14T19-22-42-679Z.yml)
```
You can also take a snapshot on demand using `playwright-cli snapshot` command. All the options below can be combined as needed.
```bash
# default - save to a file with timestamp-based name
playwright-cli snapshot
# save to file, use when snapshot is a part of the workflow result
playwright-cli snapshot --filename=after-click.yaml
# snapshot an element instead of the whole page
playwright-cli snapshot "#main"
# limit snapshot depth for efficiency, take a partial snapshot afterwards
playwright-cli snapshot --depth=4
playwright-cli snapshot e34
# include each element's bounding box as [box=x,y,width,height]
playwright-cli snapshot --boxes
# search a large snapshot instead of capturing it all — returns matching nodes
# with 3 lines of context around each match (like grep -C)
playwright-cli find "Add to cart"
playwright-cli find --regex "\\$[0-9]+\\.[0-9]{2}"
```
## Targeting elements
By default, use refs from the snapshot to interact with page elements.
```bash
# get snapshot with refs
playwright-cli snapshot
# interact using a ref
playwright-cli click e15
```
You can also use css selectors or Playwright locators.
```bash
# css selector
playwright-cli click "#main > button.submit"
# role locator
playwright-cli click "getByRole('button', { name: 'Submit' })"
# test id
playwright-cli click "getByTestId('submit-button')"
```
## Browser Sessions
```bash
# create new browser session named "mysession" with persistent profile
playwright-cli -s=mysession open example.com --persistent
# same with manually specified profile directory (use when requested explicitly)
playwright-cli -s=mysession open example.com --profile=/path/to/profile
playwright-cli -s=mysession click e6
playwright-cli -s=mysession close # stop a named browser
playwright-cli -s=mysession delete-data # delete user data for persistent session
playwright-cli list
# Close all browsers
playwright-cli close-all
# Forcefully kill all browser processes
playwright-cli kill-all
```
## Installation
If global `playwright-cli` command is not available, try a local version via `npx playwright cli`:
```bash
npx --no-install playwright --version
```
When local version is available, use `npx playwright cli` in all commands. Otherwise, install `playwright-cli` as a global command:
```bash
npm install -g @playwright/cli@latest
```
## Example: Form submission
```bash
playwright-cli open https://example.com/form
playwright-cli snapshot
playwright-cli fill e1 "user@example.com"
playwright-cli fill e2 "password123"
playwright-cli click e3
playwright-cli snapshot
playwright-cli close
```
## Example: Multi-tab workflow
```bash
playwright-cli open https://example.com
playwright-cli tab-new https://example.com/other
playwright-cli tab-list
playwright-cli tab-select 0
playwright-cli snapshot
playwright-cli close
```
## Example: Debugging with DevTools
```bash
playwright-cli open https://example.com
playwright-cli click e4
playwright-cli fill e7 "test"
playwright-cli console
playwright-cli requests
playwright-cli close
```
```bash
playwright-cli open https://example.com
playwright-cli tracing-start
playwright-cli click e4
playwright-cli fill e7 "test"
playwright-cli tracing-stop
playwright-cli close
```
## Example: Interactive session
Ask the user for UI review or design feedback. The user draws boxes on the live page and types comments; you receive the annotated screenshot, the snapshot of the marked region, and the user's notes. Use this whenever the user asks for "UI review", "design feedback", or to "ask the user what they think / want / mean":
```bash
playwright-cli open https://example.com
playwright-cli show --annotate
```
## Specific tasks
* **Running and Debugging Playwright tests** [references/playwright-tests.md](references/playwright-tests.md)
* **Request mocking** [references/request-mocking.md](references/request-mocking.md)
* **Running Playwright code** [references/running-code.md](references/running-code.md)
* **Browser session management** [references/session-management.md](references/session-management.md)
* **Storage state (cookies, localStorage)** [references/storage-state.md](references/storage-state.md)
* **Test generation (plan / generate / heal)** [references/test-generation.md](references/test-generation.md)
* **Tracing** [references/tracing.md](references/tracing.md)
* **Video recording** [references/video-recording.md](references/video-recording.md)
* **Inspecting element attributes** [references/element-attributes.md](references/element-attributes.md)
@@ -1,23 +0,0 @@
# Inspecting Element Attributes
When the snapshot doesn't show an element's `id`, `class`, `data-*` attributes, or other DOM properties, use `eval` to inspect them.
## Examples
```bash
playwright-cli snapshot
# snapshot shows a button as e7 but doesn't reveal its id or data attributes
# get the element's id
playwright-cli eval "el => el.id" e7
# get all CSS classes
playwright-cli eval "el => el.className" e7
# get a specific attribute
playwright-cli eval "el => el.getAttribute('data-testid')" e7
playwright-cli eval "el => el.getAttribute('aria-label')" e7
# get a computed style property
playwright-cli eval "el => getComputedStyle(el).display" e7
```
@@ -1,39 +0,0 @@
# Running Playwright Tests
To run Playwright tests, use the `npx playwright test` command, or a package manager script. To avoid opening the interactive html report, use `PLAYWRIGHT_HTML_OPEN=never` environment variable.
```bash
# Run all tests
PLAYWRIGHT_HTML_OPEN=never npx playwright test
# Run all tests through a custom npm script
PLAYWRIGHT_HTML_OPEN=never npm run special-test-command
```
# Debugging Playwright Tests
To debug a failing Playwright test, run it with `--debug=cli` option. This command will pause the test at the start and print the debugging instructions.
**IMPORTANT**: run the command in the background and check the output until "Debugging Instructions" is printed. Make sure to stop the command after you have finished.
Once instructions containing a session name are printed, use `playwright-cli` to attach the session and explore the page.
```bash
# Run the test
PLAYWRIGHT_HTML_OPEN=never npx playwright test --debug=cli
# ...
# ... debugging instructions for "tw-abcdef" session ...
# ...
# Attach to the test
playwright-cli attach tw-abcdef
```
Keep the test running in the background while you explore and look for a fix.
The test is paused at the start, so you should step over or pause at a particular location
where the problem is most likely to be.
Every action you perform with `playwright-cli` generates corresponding Playwright TypeScript code.
This code appears in the output and can be copied directly into the test. Most of the time, a specific locator or an expectation should be updated, but it could also be a bug in the app. Use your judgement.
After fixing the test, stop the background test run. Rerun to check that test passes.
@@ -1,87 +0,0 @@
# Request Mocking
Intercept, mock, modify, and block network requests.
## CLI Route Commands
```bash
# Mock with custom status
playwright-cli route "**/*.jpg" --status=404
# Mock with JSON body
playwright-cli route "**/api/users" --body='[{"id":1,"name":"Alice"}]' --content-type=application/json
# Mock with custom headers
playwright-cli route "**/api/data" --body='{"ok":true}' --header="X-Custom: value"
# Remove headers from requests
playwright-cli route "**/*" --remove-header=cookie,authorization
# List active routes
playwright-cli route-list
# Remove a route or all routes
playwright-cli unroute "**/*.jpg"
playwright-cli unroute
```
## URL Patterns
```
**/api/users - Exact path match
**/api/*/details - Wildcard in path
**/*.{png,jpg,jpeg} - Match file extensions
**/search?q=* - Match query parameters
```
## Advanced Mocking with run-code
For conditional responses, request body inspection, response modification, or delays:
### Conditional Response Based on Request
```bash
playwright-cli run-code "async page => {
await page.route('**/api/login', route => {
const body = route.request().postDataJSON();
if (body.username === 'admin') {
route.fulfill({ body: JSON.stringify({ token: 'mock-token' }) });
} else {
route.fulfill({ status: 401, body: JSON.stringify({ error: 'Invalid' }) });
}
});
}"
```
### Modify Real Response
```bash
playwright-cli run-code "async page => {
await page.route('**/api/user', async route => {
const response = await route.fetch();
const json = await response.json();
json.isPremium = true;
await route.fulfill({ response, json });
});
}"
```
### Simulate Network Failures
```bash
playwright-cli run-code "async page => {
await page.route('**/api/offline', route => route.abort('internetdisconnected'));
}"
# Options: connectionrefused, timedout, connectionreset, internetdisconnected
```
### Delayed Response
```bash
playwright-cli run-code "async page => {
await page.route('**/api/slow', async route => {
await new Promise(r => setTimeout(r, 3000));
route.fulfill({ body: JSON.stringify({ data: 'loaded' }) });
});
}"
```
@@ -1,241 +0,0 @@
# Running Custom Playwright Code
Use `run-code` to execute arbitrary Playwright code for advanced scenarios not covered by CLI commands.
## Syntax
```bash
playwright-cli run-code "async page => {
// Your Playwright code here
// Access page.context() for browser context operations
}"
```
You can also load the function from a file:
```bash
playwright-cli run-code --filename=./my-script.js
```
The code must be a single function expression, it is wrapped in `(...)` and evaluated.
import/export/require syntax is not supported.
## Geolocation
```bash
# Grant geolocation permission and set location
playwright-cli run-code "async page => {
await page.context().grantPermissions(['geolocation']);
await page.context().setGeolocation({ latitude: 37.7749, longitude: -122.4194 });
}"
# Set location to London
playwright-cli run-code "async page => {
await page.context().grantPermissions(['geolocation']);
await page.context().setGeolocation({ latitude: 51.5074, longitude: -0.1278 });
}"
# Clear geolocation override
playwright-cli run-code "async page => {
await page.context().clearPermissions();
}"
```
## Permissions
```bash
# Grant multiple permissions
playwright-cli run-code "async page => {
await page.context().grantPermissions([
'geolocation',
'notifications',
'camera',
'microphone'
]);
}"
# Grant permissions for specific origin
playwright-cli run-code "async page => {
await page.context().grantPermissions(['clipboard-read'], {
origin: 'https://example.com'
});
}"
```
## Media Emulation
```bash
# Emulate dark color scheme
playwright-cli run-code "async page => {
await page.emulateMedia({ colorScheme: 'dark' });
}"
# Emulate light color scheme
playwright-cli run-code "async page => {
await page.emulateMedia({ colorScheme: 'light' });
}"
# Emulate reduced motion
playwright-cli run-code "async page => {
await page.emulateMedia({ reducedMotion: 'reduce' });
}"
# Emulate print media
playwright-cli run-code "async page => {
await page.emulateMedia({ media: 'print' });
}"
```
## Wait Strategies
```bash
# Wait for network idle
playwright-cli run-code "async page => {
await page.waitForLoadState('networkidle');
}"
# Wait for specific element
playwright-cli run-code "async page => {
await page.locator('.loading').waitFor({ state: 'hidden' });
}"
# Wait for function to return true
playwright-cli run-code "async page => {
await page.waitForFunction(() => window.appReady === true);
}"
# Wait with timeout
playwright-cli run-code "async page => {
await page.locator('.result').waitFor({ timeout: 10000 });
}"
```
## Frames and Iframes
```bash
# Work with iframe
playwright-cli run-code "async page => {
const frame = page.locator('iframe#my-iframe').contentFrame();
await frame.locator('button').click();
}"
# Get all frames
playwright-cli run-code "async page => {
const frames = page.frames();
return frames.map(f => f.url());
}"
```
## File Downloads
```bash
# Handle file download
playwright-cli run-code "async page => {
const downloadPromise = page.waitForEvent('download');
await page.getByRole('link', { name: 'Download' }).click();
const download = await downloadPromise;
await download.saveAs('./downloaded-file.pdf');
return download.suggestedFilename();
}"
```
## Clipboard
```bash
# Read clipboard (requires permission)
playwright-cli run-code "async page => {
await page.context().grantPermissions(['clipboard-read']);
return await page.evaluate(() => navigator.clipboard.readText());
}"
# Write to clipboard
playwright-cli run-code "async page => {
await page.evaluate(text => navigator.clipboard.writeText(text), 'Hello clipboard!');
}"
```
## Page Information
```bash
# Get page title
playwright-cli run-code "async page => {
return await page.title();
}"
# Get current URL
playwright-cli run-code "async page => {
return page.url();
}"
# Get page content
playwright-cli run-code "async page => {
return await page.content();
}"
# Get viewport size
playwright-cli run-code "async page => {
return page.viewportSize();
}"
```
## JavaScript Execution
```bash
# Execute JavaScript and return result
playwright-cli run-code "async page => {
return await page.evaluate(() => {
return {
userAgent: navigator.userAgent,
language: navigator.language,
cookiesEnabled: navigator.cookieEnabled
};
});
}"
# Pass arguments to evaluate
playwright-cli run-code "async page => {
const multiplier = 5;
return await page.evaluate(m => document.querySelectorAll('li').length * m, multiplier);
}"
```
## Error Handling
```bash
# Try-catch in run-code
playwright-cli run-code "async page => {
try {
await page.getByRole('button', { name: 'Submit' }).click({ timeout: 1000 });
return 'clicked';
} catch (e) {
return 'element not found';
}
}"
```
## Complex Workflows
```bash
# Login and save state
playwright-cli run-code "async page => {
await page.goto('https://example.com/login');
await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
await page.getByRole('textbox', { name: 'Password' }).fill('secret');
await page.getByRole('button', { name: 'Sign in' }).click();
await page.waitForURL('**/dashboard');
await page.context().storageState({ path: 'auth.json' });
return 'Login successful';
}"
# Scrape data from multiple pages
playwright-cli run-code "async page => {
const results = [];
for (let i = 1; i <= 3; i++) {
await page.goto(\`https://example.com/page/\${i}\`);
const items = await page.locator('.item').allTextContents();
results.push(...items);
}
return results;
}"
```
@@ -1,225 +0,0 @@
# Browser Session Management
Run multiple isolated browser sessions concurrently with state persistence.
## Named Browser Sessions
Use `-s` flag to isolate browser contexts:
```bash
# Browser 1: Authentication flow
playwright-cli -s=auth open https://app.example.com/login
# Browser 2: Public browsing (separate cookies, storage)
playwright-cli -s=public open https://example.com
# Commands are isolated by browser session
playwright-cli -s=auth fill e1 "user@example.com"
playwright-cli -s=public snapshot
```
## Browser Session Isolation Properties
Each browser session has independent:
- Cookies
- LocalStorage / SessionStorage
- IndexedDB
- Cache
- Browsing history
- Open tabs
## Browser Session Commands
```bash
# List all browser sessions
playwright-cli list
# Stop a browser session (close the browser)
playwright-cli close # stop the default browser
playwright-cli -s=mysession close # stop a named browser
# Stop all browser sessions
playwright-cli close-all
# Forcefully kill all daemon processes (for stale/zombie processes)
playwright-cli kill-all
# Delete browser session user data (profile directory)
playwright-cli delete-data # delete default browser data
playwright-cli -s=mysession delete-data # delete named browser data
```
## Environment Variable
Set a default browser session name via environment variable:
```bash
export PLAYWRIGHT_CLI_SESSION="mysession"
playwright-cli open example.com # Uses "mysession" automatically
```
## Common Patterns
### Concurrent Scraping
```bash
#!/bin/bash
# Scrape multiple sites concurrently
# Start all browsers
playwright-cli -s=site1 open https://site1.com &
playwright-cli -s=site2 open https://site2.com &
playwright-cli -s=site3 open https://site3.com &
wait
# Take snapshots from each
playwright-cli -s=site1 snapshot
playwright-cli -s=site2 snapshot
playwright-cli -s=site3 snapshot
# Cleanup
playwright-cli close-all
```
### A/B Testing Sessions
```bash
# Test different user experiences
playwright-cli -s=variant-a open "https://app.com?variant=a"
playwright-cli -s=variant-b open "https://app.com?variant=b"
# Compare
playwright-cli -s=variant-a screenshot
playwright-cli -s=variant-b screenshot
```
### Persistent Profile
By default, browser profile is kept in memory only. Use `--persistent` flag on `open` to persist the browser profile to disk:
```bash
# Use persistent profile (auto-generated location)
playwright-cli open https://example.com --persistent
# Use persistent profile with custom directory
playwright-cli open https://example.com --profile=/path/to/profile
```
## Attaching to a Running Browser
Use `attach` to connect to a browser that is already running, instead of launching a new one.
### Attach by channel name
Connect to a running Chrome or Edge instance by its channel name. The browser must have remote debugging enabled — navigate to `chrome://inspect/#remote-debugging` in the target browser and check "Allow remote debugging for this browser instance".
```bash
# Attach to Chrome
playwright-cli attach --cdp=chrome
# Attach to Chrome Canary
playwright-cli attach --cdp=chrome-canary
# Attach to Microsoft Edge
playwright-cli attach --cdp=msedge
# Attach to Edge Dev
playwright-cli attach --cdp=msedge-dev
```
Supported channels: `chrome`, `chrome-beta`, `chrome-dev`, `chrome-canary`, `msedge`, `msedge-beta`, `msedge-dev`, `msedge-canary`.
When `--session` is not provided, the session is named after the channel (e.g. `--cdp=msedge` creates a session called `msedge`), so parallel attaches to Chrome and Edge don't collide on `default`. Pass `--session=<name>` to override.
### Attach via CDP endpoint
Connect to a browser that exposes a Chrome DevTools Protocol endpoint:
```bash
playwright-cli attach --cdp=http://localhost:9222
```
### Attach via browser extension
Connect to a browser with the Playwright extension installed:
```bash
playwright-cli attach --extension
```
### Detach
Tear down an attached session without affecting the external browser:
```bash
# Detach the default attached session
playwright-cli detach
# Detach a specific attached session
playwright-cli -s=msedge detach
```
`detach` only works on sessions created via `attach`. For sessions created via `open`, use `close`.
## Default Browser Session
When `-s` is omitted, commands use the default browser session:
```bash
# These use the same default browser session
playwright-cli open https://example.com
playwright-cli snapshot
playwright-cli close # Stops default browser
```
## Browser Session Configuration
Configure a browser session with specific settings when opening:
```bash
# Open with config file
playwright-cli open https://example.com --config=.playwright/my-cli.json
# Open with specific browser
playwright-cli open https://example.com --browser=firefox
# Open in headed mode
playwright-cli open https://example.com --headed
# Open with persistent profile
playwright-cli open https://example.com --persistent
```
## Best Practices
### 1. Name Browser Sessions Semantically
```bash
# GOOD: Clear purpose
playwright-cli -s=github-auth open https://github.com
playwright-cli -s=docs-scrape open https://docs.example.com
# AVOID: Generic names
playwright-cli -s=s1 open https://github.com
```
### 2. Always Clean Up
```bash
# Stop browsers when done
playwright-cli -s=auth close
playwright-cli -s=scrape close
# Or stop all at once
playwright-cli close-all
# If browsers become unresponsive or zombie processes remain
playwright-cli kill-all
```
### 3. Delete Stale Browser Data
```bash
# Remove old browser data to free disk space
playwright-cli -s=oldsession delete-data
```
@@ -1,275 +0,0 @@
# Storage Management
Manage cookies, localStorage, sessionStorage, and browser storage state.
## Storage State
Save and restore complete browser state including cookies and storage.
### Save Storage State
```bash
# Save to auto-generated filename (storage-state-{timestamp}.json)
playwright-cli state-save
# Save to specific filename
playwright-cli state-save my-auth-state.json
```
### Restore Storage State
```bash
# Load storage state from file
playwright-cli state-load my-auth-state.json
# Reload page to apply cookies
playwright-cli open https://example.com
```
### Storage State File Format
The saved file contains:
```json
{
"cookies": [
{
"name": "session_id",
"value": "abc123",
"domain": "example.com",
"path": "/",
"expires": 1893456000,
"httpOnly": true,
"secure": true,
"sameSite": "Lax"
}
],
"origins": [
{
"origin": "https://example.com",
"localStorage": [
{ "name": "theme", "value": "dark" },
{ "name": "user_id", "value": "12345" }
]
}
]
}
```
## Cookies
### List All Cookies
```bash
playwright-cli cookie-list
```
### Filter Cookies by Domain
```bash
playwright-cli cookie-list --domain=example.com
```
### Filter Cookies by Path
```bash
playwright-cli cookie-list --path=/api
```
### Get Specific Cookie
```bash
playwright-cli cookie-get session_id
```
### Set a Cookie
```bash
# Basic cookie
playwright-cli cookie-set session abc123
# Cookie with options
playwright-cli cookie-set session abc123 --domain=example.com --path=/ --httpOnly --secure --sameSite=Lax
# Cookie with expiration (Unix timestamp)
playwright-cli cookie-set remember_me token123 --expires=1893456000
```
### Delete a Cookie
```bash
playwright-cli cookie-delete session_id
```
### Clear All Cookies
```bash
playwright-cli cookie-clear
```
### Advanced: Multiple Cookies or Custom Options
For complex scenarios like adding multiple cookies at once, use `run-code`:
```bash
playwright-cli run-code "async page => {
await page.context().addCookies([
{ name: 'session_id', value: 'sess_abc123', domain: 'example.com', path: '/', httpOnly: true },
{ name: 'preferences', value: JSON.stringify({ theme: 'dark' }), domain: 'example.com', path: '/' }
]);
}"
```
## Local Storage
### List All localStorage Items
```bash
playwright-cli localstorage-list
```
### Get Single Value
```bash
playwright-cli localstorage-get token
```
### Set Value
```bash
playwright-cli localstorage-set theme dark
```
### Set JSON Value
```bash
playwright-cli localstorage-set user_settings '{"theme":"dark","language":"en"}'
```
### Delete Single Item
```bash
playwright-cli localstorage-delete token
```
### Clear All localStorage
```bash
playwright-cli localstorage-clear
```
### Advanced: Multiple Operations
For complex scenarios like setting multiple values at once, use `run-code`:
```bash
playwright-cli run-code "async page => {
await page.evaluate(() => {
localStorage.setItem('token', 'jwt_abc123');
localStorage.setItem('user_id', '12345');
localStorage.setItem('expires_at', Date.now() + 3600000);
});
}"
```
## Session Storage
### List All sessionStorage Items
```bash
playwright-cli sessionstorage-list
```
### Get Single Value
```bash
playwright-cli sessionstorage-get form_data
```
### Set Value
```bash
playwright-cli sessionstorage-set step 3
```
### Delete Single Item
```bash
playwright-cli sessionstorage-delete step
```
### Clear sessionStorage
```bash
playwright-cli sessionstorage-clear
```
## IndexedDB
### List Databases
```bash
playwright-cli run-code "async page => {
return await page.evaluate(async () => {
const databases = await indexedDB.databases();
return databases;
});
}"
```
### Delete Database
```bash
playwright-cli run-code "async page => {
await page.evaluate(() => {
indexedDB.deleteDatabase('myDatabase');
});
}"
```
## Common Patterns
### Authentication State Reuse
```bash
# Step 1: Login and save state
playwright-cli open https://app.example.com/login
playwright-cli snapshot
playwright-cli fill e1 "user@example.com"
playwright-cli fill e2 "password123"
playwright-cli click e3
# Save the authenticated state
playwright-cli state-save auth.json
# Step 2: Later, restore state and skip login
playwright-cli state-load auth.json
playwright-cli open https://app.example.com/dashboard
# Already logged in!
```
### Save and Restore Roundtrip
```bash
# Set up authentication state
playwright-cli open https://example.com
playwright-cli eval "() => { document.cookie = 'session=abc123'; localStorage.setItem('user', 'john'); }"
# Save state to file
playwright-cli state-save my-session.json
# ... later, in a new session ...
# Restore state
playwright-cli state-load my-session.json
playwright-cli open https://example.com
# Cookies and localStorage are restored!
```
## Security Notes
- Never commit storage state files containing auth tokens
- Add `*.auth-state.json` to `.gitignore`
- Delete state files after automation completes
- Use environment variables for sensitive data
- By default, sessions run in-memory mode which is safer for sensitive operations
@@ -1,433 +0,0 @@
# Test generation (plan → generate → heal)
End-to-end workflow for authoring and maintaining Playwright tests with `playwright-cli`. Every `playwright-cli` action emits the equivalent Playwright TypeScript, and that generated code is the raw material for every test. The sections below can be used independently:
- **How generation works** — the core mechanic everything else relies on: actions become TypeScript, plus how to add assertions.
- **Plan** — explore the app, produce a spec file describing what to test.
- **Generate** — turn a spec into Playwright test files. Update the spec if it's vague or stale.
- **Heal** — diagnose failing tests, fix the code, reconcile the spec with reality.
Plan / generate / heal lean on the same mechanic: run `npx playwright test --debug=cli` in the background, then `playwright-cli attach tw-XXXX` to drive the paused page interactively. See [playwright-tests.md](playwright-tests.md) for the debug/attach mechanics.
---
## 0. How generation works
Every action you perform with `playwright-cli` generates corresponding Playwright TypeScript code. This code appears in the output and can be copied directly into your test files.
```bash
# Start a session
playwright-cli open https://example.com/login
# Take a snapshot to see elements
playwright-cli snapshot
# Output shows: e1 [textbox "Email"], e2 [textbox "Password"], e3 [button "Sign In"]
# Fill form fields - generates code automatically
playwright-cli fill e1 "user@example.com"
# Ran Playwright code:
# await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
playwright-cli fill e2 "password123"
# Ran Playwright code:
# await page.getByRole('textbox', { name: 'Password' }).fill('password123');
playwright-cli click e3
# Ran Playwright code:
# await page.getByRole('button', { name: 'Sign In' }).click();
```
### Building a test file
Collect the generated code into a Playwright test:
```typescript
import { test, expect } from '@playwright/test';
test('login flow', async ({ page }) => {
// Generated code from playwright-cli session:
await page.goto('https://example.com/login');
await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
await page.getByRole('textbox', { name: 'Password' }).fill('password123');
await page.getByRole('button', { name: 'Sign In' }).click();
// Add assertions
await expect(page).toHaveURL(/.*dashboard/);
});
```
### Use semantic locators
The generated code uses role-based locators when possible, which are more resilient:
```typescript
// Generated (good - semantic)
await page.getByRole('button', { name: 'Submit' }).click();
// Avoid (fragile - CSS selectors)
await page.locator('#submit-btn').click();
```
### Explore before recording
Take snapshots to understand the page structure before recording actions:
```bash
playwright-cli open https://example.com
playwright-cli snapshot
# Review the element structure
playwright-cli click e5
```
### Add assertions manually
Generated code captures actions but not assertions. Add expectations in your test using one of the recommended matchers:
- `toBeVisible()` — element is rendered and visible
- `toHaveText(text)` — element text content matches
- `toHaveValue(value) / toBeEmpty()` — input/select value matches
- `toBeChecked() / toBeUnchecked()` — checkbox state matches
- `toMatchAriaSnapshot(snapshot)` — page (or locator) matches a partial accessibility snapshot
Use `playwright-cli generate-locator <target>` to produce the locator expression for the assertion, and the snapshot/eval commands to capture the expected value.
When asserting text content, make sure that generated locator does not contain text from the element itself. `getByTestId()` or `getByLabel()` usually work well with asserting text. When locator is text-based, prefer `toBeVisible()` instead.
Snapshot to be matched does not have to contain all the information - only capture what's necessary for the assertion. You can use regular expressions for unstable values.
```bash
# Get a stable locator for an element ref to use in the assertion
playwright-cli --raw generate-locator e5
# getByRole('button', { name: 'Submit' })
# Capture expected text content for toHaveText
playwright-cli --raw eval "el => el.textContent" e5
# Capture expected input value for toHaveValue/toBeEmpty
playwright-cli --raw eval "el => el.value" e5
# Capture expected aria snapshot for toMatchAriaSnapshot/toBeChecked
# (whole page, or use a ref to scope to a region)
playwright-cli --raw snapshot
playwright-cli --raw snapshot e5
```
```typescript
// Generated action
await page.getByRole('button', { name: 'Submit' }).click();
// Manual assertions using the outputs above:
await expect(page.getByRole('alert', { name: 'Success' })).toBeVisible();
await expect(page.getByTestId('main-header')).toHaveText('Welcome, user');
await expect(page.getByRole('textbox', { name: 'Email' })).toHaveValue('user@example.com');
await expect(page.getByRole('checkbox', { name: 'Enable notifications' })).toBeChecked();
// toMatchAriaSnapshot on the whole page, finds a matching region
await expect(page).toMatchAriaSnapshot(`
- heading "Welcome, user"
- link /\\d+ new messages?/
- button "Sign out"
`);
// toMatchAriaSnapshot scoped to a region
await expect(page.getByRole('navigation')).toMatchAriaSnapshot(`
- link "Home"
- link /\\d+ new messages?/
- link "Profile"
`);
```
---
## 1. Planning
Goal: produce a spec file (e.g. `specs/<feature>.plan.md`) that enumerates the scenarios to test. **Always** write the spec to a file.
### 1.1 Prerequisite: workspace
Check the workspace has Playwright installed before anything else:
```bash
# Either of these confirms a workspace:
test -f playwright.config.ts || test -f playwright.config.js
npx --no-install playwright --version
```
If there is no Playwright install, bootstrap one and let the user pick the defaults:
```bash
npm init playwright@latest
```
### 1.2 Prerequisite: seed test
A **seed test** is a minimal test that lands the page in the state every scenario starts from: navigation to the app, any required login, feature flags, etc. Scenarios assume a fresh start *after* the seed. `--debug=cli` pauses *inside* this test, so the seed is where every planning and generation session begins.
Minimum viable seed:
```ts
// tests/seed.spec.ts
import { test } from '@playwright/test';
test('seed', async ({ page }) => {
await page.goto('https://example.com/');
});
```
Preferred — push navigation into a fixture so scenario tests reuse it:
```ts
// tests/fixtures.ts
import { test as baseTest } from '@playwright/test';
export { expect } from '@playwright/test';
export const test = baseTest.extend({
page: async ({ page }, use) => {
await page.goto('https://example.com/');
await use(page);
},
});
```
```ts
// tests/seed.spec.ts
import { test } from './fixtures';
test('seed', async ({ page }) => {
// Fixture already navigates. This empty body tells agents where to start.
});
```
If no seed exists, create one that at least navigates to the app.
### 1.3 Explore the app
Launch the app via the seed in the background and attach:
```bash
PLAYWRIGHT_HTML_OPEN=never npx playwright test tests/seed.spec.ts --debug=cli
# wait for "Debugging Instructions" and the session name tw-XXXX
playwright-cli attach tw-XXXX
```
Resume so the seed runs, then probe the app:
```bash
playwright-cli resume # resume so that seed test runs fully
playwright-cli snapshot # inventory of interactive elements
playwright-cli click e5 # follow a flow
playwright-cli eval "location.href" # read URL / state
playwright-cli show --annotate # ask the user to point at something
```
Map out:
- Interactive surfaces (forms, buttons, lists, filters, modals).
- Primary user journeys end-to-end.
- Edge cases: empty states, validation errors, very long input, boundary values.
- Persistence: reload, local/session storage, URL fragments.
- Navigation: which controls change the URL, back/forward behaviour.
**Important**: Do not just open the app url with playwright-cli, always go through the test to capture any custom setup done there.
**Important**: Stop the background test when done exploring.
### 1.4 Write the spec file
Save under `specs/<feature>.plan.md`. Use this structure:
```markdown
# <Feature> Test Plan
## Application Overview
<One paragraph describing what the feature does and why it matters.>
## Test Scenarios
### 1. <Group Name>
**Seed:** `tests/seed.spec.ts`
#### 1.1. <kebab-case-scenario-name>
**File:** `tests/<group>/<kebab-case-scenario-name>.spec.ts`
**Steps:**
1. <Concrete user step>
- expect: <observable outcome>
- expect: <another observable outcome>
2. <Next step>
- expect: <outcome>
#### 1.2. <next-scenario>
...
### 2. <Next Group>
**Seed:** `tests/seed.spec.ts`
...
```
Guidelines:
- Each scenario is independent and starts from the seed's fresh state — never chain scenarios.
- Scenario names are kebab-case and match the test file name (`should-add-single-todo``should-add-single-todo.spec.ts`).
- Cover happy path, edge cases, validation, negative flows, persistence.
- Write steps at the user level ("Type 'Buy milk' into the input"), not the API level ("call `fill`").
- Put observable outcomes in `- expect:` bullets; each becomes an assertion during generation.
---
## 2. Generate
Goal: take a spec file and produce Playwright test files. Optionally update the spec if it has drifted.
### 2.1 Inputs
- **Spec file**, e.g. `specs/basic-operations.plan.md`.
- **Target**: either a single scenario (e.g. `1.2`), a whole group (`1`), or all.
- **Seed file**, read from the `**Seed:**` line of the scenario's group.
### 2.2 Generate one scenario
For each target scenario, in sequence (never in parallel — scenarios share the seed session):
```bash
PLAYWRIGHT_HTML_OPEN=never npx playwright test <seed-file> --debug=cli # background
playwright-cli attach tw-XXXX
# resume
```
**Do not** just open the app url with playwright-cli, always go through the test to capture any custom setup done there.
Walk the scenario's `Steps:` one by one with `playwright-cli`, treating the spec as the plan and the live app as the source of truth. If a step is vague ("click the button" — which button?), references an element that no longer exists, or contradicts the app's actual behaviour, use your judgement: update the spec to match what the app really does, then keep going. Editing the spec mid-generation is expected.
Every action prints the equivalent Playwright TypeScript (see [How generation works](#0-how-generation-works)):
```bash
playwright-cli snapshot # find refs
playwright-cli fill e3 "John Doe" # -> page.getByRole('textbox', {...}).fill(...)
playwright-cli press Enter
playwright-cli click e7
```
For each `- expect:` bullet, add an explicit assertion. See [How generation works](#0-how-generation-works) for details.
Collect the generated code and write the test file at the path given in the spec:
```ts
// spec: specs/basic-operations.plan.md
// seed: tests/seed.spec.ts
import { test, expect } from './fixtures'; // or '@playwright/test' if no fixtures file
test.describe('Signing in and out', () => {
test('should sign in', async ({ page }) => {
// 1. Navigate to the application
// (handled by the seed fixture)
// 2. Type 'John Doe' into the username field
await page.getByRole('textbox', { name: 'username' }).fill('John Doe');
// 3. Type password
await page.getByRole('textbox', { name: 'password' }).fill('TestPassword');
// 4. Press Enter to submit
await page.getByRole('textbox', { name: 'password' }).press('Enter');
await expect(page.getByRole('heading')).toContainText('Welcome, John Doe!');
});
});
```
Rules:
- **One test per file.** File path, describe name, and test name come verbatim from the spec (minus the ordinal).
- Prefix each numbered step with a `// N. <step text>` comment before its actions.
- Use the describe group name verbatim from the spec (no `1.` ordinal).
- Import from `./fixtures` if the project has one; otherwise `@playwright/test`.
- **Important**: close the CLI session and stop the background test before moving to the next scenario.
### 2.3 Generate multiple scenarios
Loop 2.2 over the targeted scenarios one at a time, restarting the seed between each so every test starts from a clean page. This is safe to parallelise due to unique generated session names - just make sure each test run is stopped.
### 2.4 Run generated tests
After generation, run the new tests once:
```bash
PLAYWRIGHT_HTML_OPEN=never npx playwright test tests/<group>/<scenario>.spec.ts
```
Any failure goes to Section 3.
---
## 3. Heal
Goal: fix failing tests, and update the spec if the app's intended behaviour changed.
### 3.1 Find failing tests
```bash
PLAYWRIGHT_HTML_OPEN=never npx playwright test
```
Record the list of failing `<file>:<line>` entries and process them one at a time. Do not attempt parallel fixes — shared state and the single CLI session make that fragile.
### 3.2 Debug one failure
Run the single failing test in debug mode in the background, then attach:
```bash
PLAYWRIGHT_HTML_OPEN=never npx playwright test tests/<group>/<scenario>.spec.ts:<line> --debug=cli
# wait for "Debugging Instructions" and the tw-XXXX session name
playwright-cli attach tw-XXXX
```
The test is paused at the start. Step forward or run to until just before the failing action or assertion, then diagnose:
```bash
playwright-cli snapshot # did the element change / move / rename?
playwright-cli console # app-side errors?
playwright-cli requests # failed request? wrong payload?
playwright-cli show --annotate # ask the user to point somewhere
```
Common causes: selector drift, new wrapper element, label/ARIA rename, timing (transition, async load), assertion text updated in the app, test data leaking between runs.
Rehearse the corrected interaction with `playwright-cli` — the generated code in the output is what you paste back into the test.
### 3.3 Apply the fix
Edit the test file: update the locator, assertion, step order, or inputs to match the corrected behaviour. Stop the background debug run. Rerun the single test to confirm green.
Never skip hooks or add sleeps as a fix. Never use `networkidle`.
### 3.4 Reconcile with the spec
Open the spec referenced by the `// spec:` header in the test file and locate the scenario that matches the test.
- **Fix was purely technical** (locator drift, better assertion shape) and the spec's user-level behaviour still matches the app → leave the spec alone.
- **Fix changed user-visible steps, inputs, order, or expected outcomes** that the spec describes → update the spec to match reality. Keep the scenario id and file path stable; only the step / expect lines change.
- **Unclear whether the app change is intentional** (spec is stale) **or a regression** (test was right, app is wrong) → **stop and ask the user**. Provide:
- the scenario id (e.g. `2.3`),
- the spec lines that no longer match,
- the observed app behaviour (quote a snapshot excerpt or a concrete outcome).
Only after the user answers, either update the spec (intentional change) or file/flag the test as covering a bug (regression).
### 3.5 Iteration and giving up
- Fix failures one at a time; rerun after each.
- If after thorough investigation you are confident the test is correct but the app is wrong *and* the user has confirmed it's a bug: mark the test `test.fixme(...)` with a comment pointing at the user's decision or issue link. Never silently skip.
---
## Cross-references
| For... | See |
|---|---|
| `--debug=cli` / attach mechanics | [playwright-tests.md](playwright-tests.md) |
| Mocking requests during exploration/generation | [request-mocking.md](request-mocking.md) |
| Managing the CLI browser session | [session-management.md](session-management.md) |
-139
View File
@@ -1,139 +0,0 @@
# Tracing
Capture detailed execution traces for debugging and analysis. Traces include DOM snapshots, screenshots, network activity, and console logs.
## Basic Usage
```bash
# Start trace recording
playwright-cli tracing-start
# Perform actions
playwright-cli open https://example.com
playwright-cli click e1
playwright-cli fill e2 "test"
# Stop trace recording
playwright-cli tracing-stop
```
## Trace Output Files
When you start tracing, Playwright creates a `.playwright-cli/traces/` directory with several files:
### `trace-{timestamp}.trace`
**Action log** - The main trace file containing:
- Every action performed (clicks, fills, navigations)
- DOM snapshots before and after each action
- Screenshots at each step
- Timing information
- Console messages
- Source locations
### `trace-{timestamp}.network`
**Network log** - Complete network activity:
- All HTTP requests and responses
- Request headers and bodies
- Response headers and bodies
- Timing (DNS, connect, TLS, TTFB, download)
- Resource sizes
- Failed requests and errors
### `resources/`
**Resources directory** - Cached resources:
- Images, fonts, stylesheets, scripts
- Response bodies for replay
- Assets needed to reconstruct page state
## What Traces Capture
| Category | Details |
|----------|---------|
| **Actions** | Clicks, fills, hovers, keyboard input, navigations |
| **DOM** | Full DOM snapshot before/after each action |
| **Screenshots** | Visual state at each step |
| **Network** | All requests, responses, headers, bodies, timing |
| **Console** | All console.log, warn, error messages |
| **Timing** | Precise timing for each operation |
## Use Cases
### Debugging Failed Actions
```bash
playwright-cli tracing-start
playwright-cli open https://app.example.com
# This click fails - why?
playwright-cli click e5
playwright-cli tracing-stop
# Open trace to see DOM state when click was attempted
```
### Analyzing Performance
```bash
playwright-cli tracing-start
playwright-cli open https://slow-site.com
playwright-cli tracing-stop
# View network waterfall to identify slow resources
```
### Capturing Evidence
```bash
# Record a complete user flow for documentation
playwright-cli tracing-start
playwright-cli open https://app.example.com/checkout
playwright-cli fill e1 "4111111111111111"
playwright-cli fill e2 "12/25"
playwright-cli fill e3 "123"
playwright-cli click e4
playwright-cli tracing-stop
# Trace shows exact sequence of events
```
## Trace vs Video vs Screenshot
| Feature | Trace | Video | Screenshot |
|---------|-------|-------|------------|
| **Format** | .trace file | .webm video | .png/.jpeg image |
| **DOM inspection** | Yes | No | No |
| **Network details** | Yes | No | No |
| **Step-by-step replay** | Yes | Continuous | Single frame |
| **File size** | Medium | Large | Small |
| **Best for** | Debugging | Demos | Quick capture |
## Best Practices
### 1. Start Tracing Before the Problem
```bash
# Trace the entire flow, not just the failing step
playwright-cli tracing-start
playwright-cli open https://example.com
# ... all steps leading to the issue ...
playwright-cli tracing-stop
```
### 2. Clean Up Old Traces
Traces can consume significant disk space:
```bash
# Remove traces older than 7 days
find .playwright-cli/traces -mtime +7 -delete
```
## Limitations
- Traces add overhead to automation
- Large traces can consume significant disk space
- Some dynamic content may not replay perfectly
@@ -1,143 +0,0 @@
# Video Recording
Capture browser automation sessions as video for debugging, documentation, or verification. Produces WebM (VP8/VP9 codec).
## Basic Recording
```bash
# Open browser first
playwright-cli open
# Start recording
playwright-cli video-start demo.webm
# Add a chapter marker for section transitions
playwright-cli video-chapter "Getting Started" --description="Opening the homepage" --duration=2000
# Navigate and perform actions
playwright-cli goto https://example.com
playwright-cli snapshot
playwright-cli click e1
# Add another chapter
playwright-cli video-chapter "Filling Form" --description="Entering test data" --duration=2000
playwright-cli fill e2 "test input"
# Stop and save
playwright-cli video-stop
```
## Best Practices
### 1. Use Descriptive Filenames
```bash
# Include context in filename
playwright-cli video-start recordings/login-flow-2024-01-15.webm
playwright-cli video-start recordings/checkout-test-run-42.webm
```
### 2. Record entire hero scripts.
When recording a video for the user or as a proof of work, it is best to create a code snippet and execute it with run-code.
It allows inserting appropriate pauses between the actions and annotating the video. There are new Playwright APIs for that.
1) Perform scenario using CLI and take note of all locators and actions. You'll need those locators to request their bounding boxes for highlight.
2) Create a file with the intended script for video (below). Use pressSequentially w/ delay for nice typing, make reasonable pauses.
3) Use playwright-cli run-code --filename your-script.js
**Important**: Overlays are `pointer-events: none` — they do not interfere with page interactions. You can safely keep sticky overlays visible while clicking, filling, or performing any actions on the page.
```js
async page => {
await page.screencast.start({ path: 'video.webm', size: { width: 1280, height: 800 } });
await page.goto('https://demo.playwright.dev/todomvc');
// Show a chapter card — blurs the page and shows a dialog.
// Blocks until duration expires, then auto-removes.
// Use this for simple use cases, but always feel free to hand-craft your own beautiful
// overlay via await page.screencast.showOverlay().
await page.screencast.showChapter('Adding Todo Items', {
description: 'We will add several items to the todo list.',
duration: 2000,
});
// Perform action
await page.getByRole('textbox', { name: 'What needs to be done?' }).pressSequentially('Walk the dog', { delay: 60 });
await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter');
await page.waitForTimeout(1000);
// Show next chapter
await page.screencast.showChapter('Verifying Results', {
description: 'Checking the item appeared in the list.',
duration: 2000,
});
// Add a sticky annotation that stays while you perform actions.
// Overlays are pointer-events: none, so they won't block clicks.
const annotation = await page.screencast.showOverlay(`
<div style="position: absolute; top: 8px; right: 8px;
padding: 6px 12px; background: rgba(0,0,0,0.7);
border-radius: 8px; font-size: 13px; color: white;">
✓ Item added successfully
</div>
`);
// Perform more actions while the annotation is visible
await page.getByRole('textbox', { name: 'What needs to be done?' }).pressSequentially('Buy groceries', { delay: 60 });
await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter');
await page.waitForTimeout(1500);
// Remove the annotation when done
await annotation.dispose();
// You can also highlight relevant locators and provide contextual annotations.
const bounds = await page.getByText('Walk the dog').boundingBox();
await page.screencast.showOverlay(`
<div style="position: absolute;
top: ${bounds.y}px;
left: ${bounds.x}px;
width: ${bounds.width}px;
height: ${bounds.height}px;
border: 1px solid red;">
</div>
<div style="position: absolute;
top: ${bounds.y + bounds.height + 5}px;
left: ${bounds.x + bounds.width / 2}px;
transform: translateX(-50%);
padding: 6px;
background: #808080;
border-radius: 10px;
font-size: 14px;
color: white;">Check it out, it is right above this text
</div>
`, { duration: 2000 });
await page.screencast.stop();
}
```
Embrace creativity, overlays are powerful.
### Overlay API Summary
| Method | Use Case |
|--------|----------|
| `page.screencast.showChapter(title, { description?, duration?, styleSheet? })` | Full-screen chapter card with blurred backdrop — ideal for section transitions |
| `page.screencast.showOverlay(html, { duration? })` | Custom HTML overlay — use for callouts, labels, highlights |
| `disposable.dispose()` | Remove a sticky overlay added without duration |
| `page.screencast.hideOverlays()` / `page.screencast.showOverlays()` | Temporarily hide/show all overlays |
## Tracing vs Video
| Feature | Video | Tracing |
|---------|-------|---------|
| Output | WebM file | Trace file (viewable in Trace Viewer) |
| Shows | Visual recording | DOM snapshots, network, console, actions |
| Use case | Demos, documentation | Debugging, analysis |
| Size | Larger | Smaller |
## Limitations
- Recording adds slight overhead to automation
- Large recordings can consume significant disk space
-138
View File
@@ -1,138 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'fs';
import path from 'path';
import { spawn } from 'child_process';
import { test, expect } from '@playwright/test';
type CliResult = {
output: string;
error: string;
exitCode: number | null;
};
async function runCli(args: string[], env: Record<string, string> = {}): Promise<CliResult> {
const cliPath = path.join(__dirname, '../playwright-cli.js');
return new Promise<CliResult>((resolve, reject) => {
let stdout = '';
let stderr = '';
const childProcess = spawn(process.execPath, [cliPath, ...args], {
env: {
...process.env,
PLAYWRIGHT_CLI_INSTALLATION_FOR_TEST: test.info().outputPath(),
...env,
},
cwd: test.info().outputPath(),
});
childProcess.stdout?.on('data', (data) => {
stdout += data.toString();
});
childProcess.stderr?.on('data', (data) => {
stderr += data.toString();
});
childProcess.on('close', (code) => {
resolve({
output: stdout.trim(),
error: stderr.trim(),
exitCode: code,
});
});
childProcess.on('error', reject);
});
}
test('open data URL', async ({}) => {
expect(await runCli(['open', 'data:text/html,hello', '--persistent'])).toEqual(expect.objectContaining({
output: expect.stringContaining('hello'),
exitCode: 0,
}));
expect(await runCli(['delete-data'])).toEqual(expect.objectContaining({
output: expect.stringContaining('Deleted user data for'),
exitCode: 0,
}));
});
test('warns when installed skill is out of date', async ({}) => {
expect(await runCli(['install', '--skills'], { NO_UPDATE_NOTIFIER: '1' })).toEqual(expect.objectContaining({
exitCode: 0,
}));
const skillFile = path.join(test.info().outputPath(), '.claude', 'skills', 'playwright-cli', 'SKILL.md');
fs.appendFileSync(skillFile, 'x');
const env = { CI: '', NO_UPDATE_NOTIFIER: '' };
expect(await runCli(['--help'], env)).toEqual(expect.objectContaining({
error: expect.stringContaining('does not match the tool version'),
}));
expect(await runCli(['--help'], env)).toEqual(expect.objectContaining({
error: expect.not.stringContaining('does not match the tool version'),
}));
});
test('does not warn when installed skill only differs in line endings', async ({}) => {
expect(await runCli(['install', '--skills'], { NO_UPDATE_NOTIFIER: '1' })).toEqual(expect.objectContaining({
exitCode: 0,
}));
const skillFile = path.join(test.info().outputPath(), '.claude', 'skills', 'playwright-cli', 'SKILL.md');
fs.writeFileSync(skillFile, fs.readFileSync(skillFile, 'utf8').replace(/\n/g, '\r\n'));
expect(await runCli(['--help'], { CI: '', NO_UPDATE_NOTIFIER: '' })).toEqual(expect.objectContaining({
error: expect.not.stringContaining('does not match the tool version'),
}));
});
test('caches the update check in the default registry directory', async ({}) => {
// Redirect the home/cache directories so the real user cache is untouched, and
// leave PLAYWRIGHT_CLI_INSTALLATION_FOR_TEST empty so the default path is used.
const home = test.info().outputPath('home');
fs.mkdirSync(home, { recursive: true });
const env = {
CI: '',
NO_UPDATE_NOTIFIER: '',
PLAYWRIGHT_CLI_INSTALLATION_FOR_TEST: '',
HOME: home,
USERPROFILE: home,
XDG_CACHE_HOME: path.join(home, '.cache'),
LOCALAPPDATA: path.join(home, 'AppData', 'Local'),
};
expect(await runCli(['--version'], env)).toEqual(expect.objectContaining({ exitCode: 0 }));
const found: string[] = [];
const walk = (dir: string) => {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory())
walk(full);
else if (entry.name === 'cli-update-check.json')
found.push(full);
}
};
walk(home);
expect(found).toHaveLength(1);
expect(JSON.parse(fs.readFileSync(found[0], 'utf8')).lastCheck).toEqual(expect.any(Number));
});
+22
View File
@@ -0,0 +1,22 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { execSync } = require('child_process');
const timestamp = execSync('git show -s --format=%ct HEAD', {
stdio: ['ignore', 'pipe', 'ignore']
}).toString('utf8').trim() + '000';
const packageJSON = require('../package.json');
console.log(packageJSON.version + '-' + timestamp);