[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69B77VHWT9K6D6MSF3MC93S":3,"skill-same:01M69B77VHWT9K6D6MSF3MC93S":57},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":28,"linkOnly":29,"markdownContent":30,"markdownBody":31,"frontMatter":32,"refName":33,"commitSha":34,"skillDirectory":35,"forksCount":36,"repositoryHtmlUrl":37,"repositoryHomepageUrl":38,"ownerHtmlUrl":39,"ownerBlogUrl":40,"ownerDescription":41,"updatedAt":42,"install":43,"resources":56},"01M69B77VHWT9K6D6MSF3MC93S","playwright-roll",null,"Roll Playwright .NET to a new version","microsoft\u002Fplaywright-dotnet","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M69B72XE0TTTF0T5HQBQ5X9F",3028,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright-dotnet\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplaywright-roll\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27],"playwright","playwright roll","new","version","2026-11-10 10:12:30",false,"---\nname: playwright-roll\ndescription: Roll Playwright .NET to a new version\n---\n\nHelp the user roll to a new version of Playwright.\n..\u002F..\u002F..\u002FROLLING.md contains general instructions and scripts.\nSee also [CLAUDE.md](..\u002F..\u002F..\u002FCLAUDE.md) for build, test, and architecture details.\n\n`DriverVersion` in `src\u002FCommon\u002FVersion.props` holds one `microsoft\u002Fplaywright` commit. `.upstream\u002F` (gitignored) is a full clone of `microsoft\u002Fplaywright` at that commit. The driver is built from it, the API and the transport channels are generated from it, and the hand-written client ports its JS client. A roll moves `DriverVersion` from the old commit to the new one, and ports the diff between the two. Do not commit, branch or edit in `.upstream\u002F`.\n\nRoll to the ref that the user names: a branch (`main`, `release-1.66`), a tag (`v1.66.0`) or a commit. Without one, roll to `main`.\n\nAlways start with running the roll script, to see the state of things. It writes the commit to `DriverVersion`, checks it out in `.upstream\u002F`, builds the driver from it and regenerates the API. It needs git and Node.js with npm.\n\n```bash\n.\u002Fbuild.sh --roll \u003Cref>\nold=$(git show HEAD:src\u002FCommon\u002FVersion.props | sed -n 's|.*\u003CDriverVersion>\\(.*\\)\u003C\u002FDriverVersion>.*|\\1|p')\nnew=$(git -C .upstream rev-parse HEAD)\n```\n\nThe driver reports the version of `packages\u002Fplaywright-core\u002Fpackage.json` at the new commit: `1.66.0` on a release tag, `1.67.0-next` on `main`. The script prints it.\n\nAfterwards, walk through the upstream changes that affect the .NET client and port the relevant ones.\n\n## Determining what to port\n\nList the upstream commits between `$old` and `$new` that touched a client-relevant path. The paths cover everything that can change the public .NET surface or the wire protocol:\n\n- `docs\u002Fsrc\u002Fapi\u002F` — the source of truth for `api.json`. Method\u002Foption additions, removals, and `langs:` filter changes flow from here.\n- `packages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002F` — the JS client implementation that the .NET client mirrors.\n- `packages\u002Fisomorphic\u002F` — selector engines, locator generation\u002Fparsing, and aria-snapshot logic shared between client and server. Changes here can affect client-side helpers like `getByRoleSelector`.\n- `packages\u002Fplaywright\u002Fsrc\u002Fmatchers\u002Fmatchers.ts` — assertion-method definitions. Changes here usually correspond to new options on `LocatorAssertions` \u002F `PageAssertions`.\n- `packages\u002Fprotocol\u002Fspec\u002F**.yml` — the wire protocol schema. Method\u002Fevent additions, parameter renames, and result-shape changes affect what the .NET implementation classes need to send\u002Freceive.\n\n```bash\n.\u002Fbuild.sh --changes            # since DriverVersion at HEAD, oldest first\n.\u002Fbuild.sh --changes \u003Cfrom>     # or since another commit or tag, e.g. v1.65.0\n```\n\nWalk that list top-to-bottom. For each commit:\n1. Read the commit (`git -C .upstream show \u003Csha>`) to see what client\u002Fprotocol\u002Fdocs changed.\n2. If it's JS-internal (bundling, dispatcher conventions, electron, mcp, dashboard, trace-viewer, test-runner) — skip.\n3. If it touches `docs\u002Fsrc\u002Fapi\u002F` or types, check `langs:` annotations — features marked with languages other than \"csharp\" do not apply.\n4. If it adds\u002Fchanges a public API method or option that applies to .NET, port it. The 'build.sh --roll' already regenerated the types\u002Foptions, so we usually only need to update the implementation.\n5. Watch for follow-up reverts — a \"feat: X\" commit might be undone by a later \"Revert X\". Check whether the change still exists in `.upstream\u002F` before porting.\n6. Maintain a running notes file (e.g. `\u002Ftmp\u002Froll-notes.md`) listing each upstream PR as ported \u002F skipped \u002F verified-already-supported, with a one-line reason. This file becomes the body of the eventual PR.\n7. Do not forget to port tests that cover .NET-relevant changes. At least one test per every API method\u002Foption to excercise the new code paths. Some server-side implementation tests are not needed in this port.\n\n## Mimicking the JavaScript implementation\n\nThe .NET client is a port of the JS client in `.upstream\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002F`. When implementing a new or changed method, always read the corresponding JS file first and mirror its logic:\n\n```\n.upstream\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002FbrowserContext.ts\n.upstream\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002Fpage.ts\n...\n```\n\n**Read the final state in `.upstream\u002F`, not just the introducing PR diff.** When a feature evolves across multiple PRs (e.g. a method was introduced in one PR, then some options were added in follow-ups, then some logic was changed), the original PR's diff is a misleading reference — the final shape lives in `.upstream\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002Ffoo.ts`, which is at the new commit. Always read that file before porting non-trivial logic. `git -C .upstream diff \"$old\" \"$new\" -- \u003Cpath>` shows everything that changed in one file.\n\nKey translation rules:\n\n**Protocol calls** — `await this._channel.methodName(params)` → `SendMessageToServerAsync(\"methodName\", paramsDictionary)`\n\n**Extracting a returned channel object from a result** — JS uses `SomeClass.from(result.foo)` which resolves the JS-side object for a channel reference. In .NET, extract it from the connection: `.GetObject\u003CFoo>(\"foo\", _connection)`\n\n**Watch for channel migrations.** A method that lived on `BrowserContext` may move to `Tracing` (and vice-versa) without notice — the protocol spec is the source of truth. If a `SendMessageToServerAsync(\"foo\", ...)` call suddenly fails, grep `packages\u002Fprotocol\u002Fspec\u002F*.yml` to find which channel actually owns `foo` now.\n\n**Wire format changes can be subtle.** A method's result shape can change without renaming anything. These are easy to miss when walking client.ts because the JS code just adapts; the regression only surfaces when an existing test's deserialization breaks. Always diff `packages\u002Fprotocol\u002Fspec\u002F*.yml` for each commit on the walk list, not just the client code. `git -C .upstream diff \"$old\" \"$new\" -- packages\u002Fprotocol\u002Fspec` shows the whole change of the protocol.\n\n**Update tests similarly to upstream.** When upstream removes or modifies a test, apply similar changes.\n\n## Renaming generated types\n\nWhen the API generator produces an unhelpful name for a return type (e.g. `Bind` instead of `BrowserBindResult`), you can control it by adding a struct alias in the upstream docs.\n\nIn the docs markdown file (e.g. `docs\u002Fsrc\u002Fapi\u002Fclass-browser.md`), add `alias` (applies to all languages) or `alias-csharp` (overrides for .NET only) to the `\u003C[Object]>` type:\n\n```diff\n-- returns: \u003C[Object]>\n+- returns: \u003C[Object]>\n+  - alias-csharp: BrowserBindResult\n```\n\nUse `alias-csharp` (in addition to bare `alias`) when upstream already declared a different `alias` for other languages and you only want to override .NET's name — typically to preserve a name we've already shipped and don't want to break.\n\nThe generated files come from `.upstream\u002F` at `DriverVersion`, and CI fails when they differ, so the docs change must land in `microsoft\u002Fplaywright` first. Make it in another checkout of `microsoft\u002Fplaywright` (not in `.upstream\u002F`), and try it with `PW_SRC_DIR=\u003Cthat checkout> .\u002Fbuild.sh --generate`. Once the upstream PR has merged, roll to a commit that has it, then update any hand-written implementation code to use the new type name.\n\n## Running the full test suite\n\nAfter porting, run the chromium suite end-to-end before opening the PR — many regressions only surface in older tests that depended on now-changed behavior. Tests take ~8–10 minutes:\n\n```bash\nBROWSER=chromium dotnet test .\u002Fsrc\u002FPlaywright.Tests\u002FPlaywright.Tests.csproj \\\n  -c Debug -f net8.0 --logger:\"console;verbosity=detailed\" > \u002Ftmp\u002Ftest-results.txt 2>&1\ngrep \"^  Failed \" \u002Ftmp\u002Ftest-results.txt        # list failures\ntail -5 \u002Ftmp\u002Ftest-results.txt                  # summary\n```\n\nRun as a background task — don't block the conversation on it. For each failure: check whether it's a flake (port collision, browser timing) by running the single test in isolation, then investigate whether your roll introduced it or it's pre-existing.\n\n## Tips & Tricks\n- `.upstream\u002F` is the upstream checkout, at the new commit. Only read from it.\n- Use the \"gh\" cli to interact with GitHub.\n- When running git commands against the upstream repo, always use `git -C .upstream \u003Csubcommand>` instead of `cd .upstream && git \u003Csubcommand>`.\n- `.upstream\u002F is not at \u003Ccommit>`: run `.\u002Fbuild.sh --build-driver`. It checks out `DriverVersion` and builds the driver again when the commit changed.\n- `.upstream has local changes`: see them with `git -C .upstream status`, and discard them.\n- `.\u002Fbuild.sh --generate` regenerates the API and the transport channels without moving `DriverVersion`.\n","Help the user roll to a new version of Playwright.\n..\u002F..\u002F..\u002FROLLING.md contains general instructions and scripts.\nSee also [CLAUDE.md](..\u002F..\u002F..\u002FCLAUDE.md) for build, test, and architecture details.\n\n`DriverVersion` in `src\u002FCommon\u002FVersion.props` holds one `microsoft\u002Fplaywright` commit. `.upstream\u002F` (gitignored) is a full clone of `microsoft\u002Fplaywright` at that commit. The driver is built from it, the API and the transport channels are generated from it, and the hand-written client ports its JS client. A roll moves `DriverVersion` from the old commit to the new one, and ports the diff between the two. Do not commit, branch or edit in `.upstream\u002F`.\n\nRoll to the ref that the user names: a branch (`main`, `release-1.66`), a tag (`v1.66.0`) or a commit. Without one, roll to `main`.\n\nAlways start with running the roll script, to see the state of things. It writes the commit to `DriverVersion`, checks it out in `.upstream\u002F`, builds the driver from it and regenerates the API. It needs git and Node.js with npm.\n\n```bash\n.\u002Fbuild.sh --roll \u003Cref>\nold=$(git show HEAD:src\u002FCommon\u002FVersion.props | sed -n 's|.*\u003CDriverVersion>\\(.*\\)\u003C\u002FDriverVersion>.*|\\1|p')\nnew=$(git -C .upstream rev-parse HEAD)\n```\n\nThe driver reports the version of `packages\u002Fplaywright-core\u002Fpackage.json` at the new commit: `1.66.0` on a release tag, `1.67.0-next` on `main`. The script prints it.\n\nAfterwards, walk through the upstream changes that affect the .NET client and port the relevant ones.\n\n## Determining what to port\n\nList the upstream commits between `$old` and `$new` that touched a client-relevant path. The paths cover everything that can change the public .NET surface or the wire protocol:\n\n- `docs\u002Fsrc\u002Fapi\u002F` — the source of truth for `api.json`. Method\u002Foption additions, removals, and `langs:` filter changes flow from here.\n- `packages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002F` — the JS client implementation that the .NET client mirrors.\n- `packages\u002Fisomorphic\u002F` — selector engines, locator generation\u002Fparsing, and aria-snapshot logic shared between client and server. Changes here can affect client-side helpers like `getByRoleSelector`.\n- `packages\u002Fplaywright\u002Fsrc\u002Fmatchers\u002Fmatchers.ts` — assertion-method definitions. Changes here usually correspond to new options on `LocatorAssertions` \u002F `PageAssertions`.\n- `packages\u002Fprotocol\u002Fspec\u002F**.yml` — the wire protocol schema. Method\u002Fevent additions, parameter renames, and result-shape changes affect what the .NET implementation classes need to send\u002Freceive.\n\n```bash\n.\u002Fbuild.sh --changes            # since DriverVersion at HEAD, oldest first\n.\u002Fbuild.sh --changes \u003Cfrom>     # or since another commit or tag, e.g. v1.65.0\n```\n\nWalk that list top-to-bottom. For each commit:\n1. Read the commit (`git -C .upstream show \u003Csha>`) to see what client\u002Fprotocol\u002Fdocs changed.\n2. If it's JS-internal (bundling, dispatcher conventions, electron, mcp, dashboard, trace-viewer, test-runner) — skip.\n3. If it touches `docs\u002Fsrc\u002Fapi\u002F` or types, check `langs:` annotations — features marked with languages other than \"csharp\" do not apply.\n4. If it adds\u002Fchanges a public API method or option that applies to .NET, port it. The 'build.sh --roll' already regenerated the types\u002Foptions, so we usually only need to update the implementation.\n5. Watch for follow-up reverts — a \"feat: X\" commit might be undone by a later \"Revert X\". Check whether the change still exists in `.upstream\u002F` before porting.\n6. Maintain a running notes file (e.g. `\u002Ftmp\u002Froll-notes.md`) listing each upstream PR as ported \u002F skipped \u002F verified-already-supported, with a one-line reason. This file becomes the body of the eventual PR.\n7. Do not forget to port tests that cover .NET-relevant changes. At least one test per every API method\u002Foption to excercise the new code paths. Some server-side implementation tests are not needed in this port.\n\n## Mimicking the JavaScript implementation\n\nThe .NET client is a port of the JS client in `.upstream\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002F`. When implementing a new or changed method, always read the corresponding JS file first and mirror its logic:\n\n```\n.upstream\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002FbrowserContext.ts\n.upstream\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002Fpage.ts\n...\n```\n\n**Read the final state in `.upstream\u002F`, not just the introducing PR diff.** When a feature evolves across multiple PRs (e.g. a method was introduced in one PR, then some options were added in follow-ups, then some logic was changed), the original PR's diff is a misleading reference — the final shape lives in `.upstream\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Fclient\u002Ffoo.ts`, which is at the new commit. Always read that file before porting non-trivial logic. `git -C .upstream diff \"$old\" \"$new\" -- \u003Cpath>` shows everything that changed in one file.\n\nKey translation rules:\n\n**Protocol calls** — `await this._channel.methodName(params)` → `SendMessageToServerAsync(\"methodName\", paramsDictionary)`\n\n**Extracting a returned channel object from a result** — JS uses `SomeClass.from(result.foo)` which resolves the JS-side object for a channel reference. In .NET, extract it from the connection: `.GetObject\u003CFoo>(\"foo\", _connection)`\n\n**Watch for channel migrations.** A method that lived on `BrowserContext` may move to `Tracing` (and vice-versa) without notice — the protocol spec is the source of truth. If a `SendMessageToServerAsync(\"foo\", ...)` call suddenly fails, grep `packages\u002Fprotocol\u002Fspec\u002F*.yml` to find which channel actually owns `foo` now.\n\n**Wire format changes can be subtle.** A method's result shape can change without renaming anything. These are easy to miss when walking client.ts because the JS code just adapts; the regression only surfaces when an existing test's deserialization breaks. Always diff `packages\u002Fprotocol\u002Fspec\u002F*.yml` for each commit on the walk list, not just the client code. `git -C .upstream diff \"$old\" \"$new\" -- packages\u002Fprotocol\u002Fspec` shows the whole change of the protocol.\n\n**Update tests similarly to upstream.** When upstream removes or modifies a test, apply similar changes.\n\n## Renaming generated types\n\nWhen the API generator produces an unhelpful name for a return type (e.g. `Bind` instead of `BrowserBindResult`), you can control it by adding a struct alias in the upstream docs.\n\nIn the docs markdown file (e.g. `docs\u002Fsrc\u002Fapi\u002Fclass-browser.md`), add `alias` (applies to all languages) or `alias-csharp` (overrides for .NET only) to the `\u003C[Object]>` type:\n\n```diff\n-- returns: \u003C[Object]>\n+- returns: \u003C[Object]>\n+  - alias-csharp: BrowserBindResult\n```\n\nUse `alias-csharp` (in addition to bare `alias`) when upstream already declared a different `alias` for other languages and you only want to override .NET's name — typically to preserve a name we've already shipped and don't want to break.\n\nThe generated files come from `.upstream\u002F` at `DriverVersion`, and CI fails when they differ, so the docs change must land in `microsoft\u002Fplaywright` first. Make it in another checkout of `microsoft\u002Fplaywright` (not in `.upstream\u002F`), and try it with `PW_SRC_DIR=\u003Cthat checkout> .\u002Fbuild.sh --generate`. Once the upstream PR has merged, roll to a commit that has it, then update any hand-written implementation code to use the new type name.\n\n## Running the full test suite\n\nAfter porting, run the chromium suite end-to-end before opening the PR — many regressions only surface in older tests that depended on now-changed behavior. Tests take ~8–10 minutes:\n\n```bash\nBROWSER=chromium dotnet test .\u002Fsrc\u002FPlaywright.Tests\u002FPlaywright.Tests.csproj \\\n  -c Debug -f net8.0 --logger:\"console;verbosity=detailed\" > \u002Ftmp\u002Ftest-results.txt 2>&1\ngrep \"^  Failed \" \u002Ftmp\u002Ftest-results.txt        # list failures\ntail -5 \u002Ftmp\u002Ftest-results.txt                  # summary\n```\n\nRun as a background task — don't block the conversation on it. For each failure: check whether it's a flake (port collision, browser timing) by running the single test in isolation, then investigate whether your roll introduced it or it's pre-existing.\n\n## Tips & Tricks\n- `.upstream\u002F` is the upstream checkout, at the new commit. Only read from it.\n- Use the \"gh\" cli to interact with GitHub.\n- When running git commands against the upstream repo, always use `git -C .upstream \u003Csubcommand>` instead of `cd .upstream && git \u003Csubcommand>`.\n- `.upstream\u002F is not at \u003Ccommit>`: run `.\u002Fbuild.sh --build-driver`. It checks out `DriverVersion` and builds the driver again when the commit changed.\n- `.upstream has local changes`: see them with `git -C .upstream status`, and discard them.\n- `.\u002Fbuild.sh --generate` regenerates the API and the transport channels without moving `DriverVersion`.\n",{"name":5,"description":7},"main","e074a35174db19d8b64c847bec0e155292377fce",".claude\u002Fskills\u002Fplaywright-roll",307,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright-dotnet","https:\u002F\u002Fplaywright.dev\u002Fdotnet\u002F","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-14 00:43:19",{"source":8,"skillName":5,"commands":44,"prompt":54,"zipAvailable":55},[45,48,51],{"tool":46,"command":47},"npx","npx skills add microsoft\u002Fplaywright-dotnet --skill playwright-roll",{"tool":49,"command":50},"bunx","bunx skills add microsoft\u002Fplaywright-dotnet --skill playwright-roll",{"tool":52,"command":53},"pnpm","pnpm dlx skills add microsoft\u002Fplaywright-dotnet --skill playwright-roll","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright-dotnet\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplaywright-roll\u002FSKILL.md",true,[],[58],{"skillNo":59,"name":60,"nameZh":6,"description":61,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":62,"categories":63,"keywords":65,"publishedAt":28},"01M69B75AX6QEMD3DQTT7M2BFE","playwright-dotnet-release","Prepare a Playwright .NET release after the rolling PR has merged — mark the version, cut the release branch, add .NET-specific changes to the release notes, draft the GitHub release, and tick the .NET boxes in the internal checklist.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright-dotnet\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplaywright-dotnet-release\u002FSKILL.md",[64],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[66,67,24,68,69,70,71,72,73,74,75,76,77,27,78,79,80,81,82,83],"playwright dotnet release","dotnet","release","github release","boxes","add","tick","checklist","cut","changes","mark","draft","branch","prepare","github","pr","release notes","notes"]