[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69995VG17SFSMYC2F5VQTTC":3,"skill-same:01M69995VG17SFSMYC2F5VQTTC":74},{"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":45,"linkOnly":46,"markdownContent":47,"markdownBody":48,"frontMatter":49,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":53,"repositoryHtmlUrl":54,"repositoryHomepageUrl":55,"ownerHtmlUrl":56,"ownerBlogUrl":57,"ownerDescription":58,"updatedAt":59,"install":60,"resources":73},"01M69995VG17SFSMYC2F5VQTTC","playwright-release-notes",null,"Write the release notes for a Playwright release - the Node.js notes in docs\u002Fsrc\u002Frelease-notes-js.md together with the Python, Java and .NET translations, and the GitHub markdown for each language's release. Use when asked to write, translate or publish release notes.","microsoft\u002Fplaywright","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M6998NK29WMCQG4W7K65QXNR",99801,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplaywright-release-notes\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"playwright release notes","playwright","release notes","notes","release","playwright release","node","write","src","markdown","with","publish","java","when","use","for","release-notes","docs","python","translate","github","2026-11-10 10:12:30",false,"---\nname: playwright-release-notes\ndescription: Write the release notes for a Playwright release - the Node.js notes in docs\u002Fsrc\u002Frelease-notes-js.md together with the Python, Java and .NET translations, and the GitHub markdown for each language's release. Use when asked to write, translate or publish release notes.\n---\n\n# Release notes\n\nIn this skill, `1.xx` is the release (for example `1.64`).\n\nEvery release has a `## Version 1.xx` section in four files in `docs\u002Fsrc\u002F`:\n\n| Language | Notes | Docs base URL | GitHub release in |\n|---|---|---|---|\n| Node.js | `release-notes-js.md` | `https:\u002F\u002Fplaywright.dev\u002Fdocs\u002F` | `microsoft\u002Fplaywright` |\n| Python | `release-notes-python.md` | `https:\u002F\u002Fplaywright.dev\u002Fpython\u002Fdocs\u002F` | `microsoft\u002Fplaywright-python` |\n| Java | `release-notes-java.md` | `https:\u002F\u002Fplaywright.dev\u002Fjava\u002Fdocs\u002F` | `microsoft\u002Fplaywright-java` |\n| .NET | `release-notes-csharp.md` | `https:\u002F\u002Fplaywright.dev\u002Fdotnet\u002Fdocs\u002F` | `microsoft\u002Fplaywright-dotnet` |\n\nWrite the Node.js notes and the three translations in the same PR, so every language has its notes when it starts its release. The language repos then add only their own significant changes, and each one writes its GitHub release from its section (step 3).\n\n## 1. Node.js notes\n\nIn `docs\u002Fsrc\u002Frelease-notes-js.md`, add a `## Version 1.xx` section above the previous version, and use previous versions as a guide:\n\n- Highlights come first, each with an emoji heading and a short sample. Ask teammates for help with them.\n- Then `### New APIs` with `####` groups, `### Breaking Changes ⚠️`, `### Announcements` and `### Browser Versions`. Print the browser versions with `node utils\u002Fprint_versions.js`.\n- Refer to APIs with docs link macros: `` [`method: Page.frameLocator`] ``, `` [`property: Page.webmcp`] ``, `` [`event: Page.dialogClosed`] ``, `` [`option: Tracing.start.ariaSnapshots`] ``, `[Locator]`. Each language's docs show them with that language's names.\n- Link other docs pages with relative paths: `[test locks](.\u002Ftest-parallel.md#test-locks)`.\n\n## 2. Python, Java and .NET translations\n\nIn each of `release-notes-python.md`, `release-notes-java.md` and `release-notes-csharp.md`, add a `## Version 1.xx` section above the previous version. Translate it from the Node.js section. Compare the `Version 1.63` sections of the files to see what past translations dropped and changed.\n\nFor every language:\n\n- Keep the structure: highlights with emoji headings, `### New APIs` with `####` groups, `### Breaking Changes ⚠️`, `### Announcements` and `### Browser Versions`.\n- Remove what the language does not have:\n  - the test runner: `@playwright\u002Ftest`, `test.*`, config, fixtures, projects, reporters and `npx playwright test` options\n  - UI mode, the HTML report, component testing and the VS Code extension\n  - TypeScript-only features (generics, JSX, `tsconfig`) and `devices` descriptors\n  - every API whose `langs` excludes the language (`python`, `java`, `csharp`). Check `langs` in `docs\u002Fsrc\u002Fapi\u002F`; options shared between methods are defined in `docs\u002Fsrc\u002Fapi\u002Fparams.md`.\n- Use the language's API where it differs from Node.js:\n  - A language-specific option has its own `langs`. For example, `recordVideoFps` is `langs: csharp, java, python`, while Node.js has `recordVideo.fps`.\n  - `alias-\u003Clang>` renames a member, e.g. `alias-java: isVisible` for `LocatorAssertions.toBeVisible`.\n  - The generated APIs in sibling checkouts confirm the names: `..\u002Fplaywright-python\u002Fplaywright\u002Fsync_api\u002F_generated.py`, `..\u002Fplaywright-java\u002Fplaywright\u002Fsrc\u002Fmain\u002Fjava\u002Fcom\u002Fmicrosoft\u002Fplaywright\u002F` and `..\u002Fplaywright-dotnet\u002Fsrc\u002FPlaywright\u002FAPI\u002FGenerated\u002F`.\n- When a Node.js sample shows a library feature through `playwright.config.ts`, show the API call instead. For example, the 1.64 video config became calls to `Screencast.start` and `Screencast.showActions`.\n- Keep API links as docs link macros, and link the language's own option where it differs (`` [`option: Browser.newContext.recordVideoFps`] ``). In plain text, use the language's names.\n- A relative link must point to a page that exists in the language: `*-js.md` pages are Node.js only. `npm run doc` reports broken links for each language.\n- Do not add changes that only a language port made. The language repo adds those to its notes.\n\nRewrite code samples in each language's style. The `infra` workflow checks the syntax of `python`, `java` and `csharp` snippets.\n\n### Python\n\n- Use the sync API: `page.get_by_role(\"button\").click()`. Use snake_case names and Python values (`True`, `False`, `None`).\n- Options become keyword arguments: `page.get_by_role(\"button\", name=\"Save\")`. Objects and arrays become dicts and lists: `size={\"width\": 1920, \"height\": 1080}`.\n- Aria roles and other string values stay strings: `\"button\"`, `\"ai\"`.\n- Events: `page.on(\"dialogclosed\", handler)`. Assertions: `expect(locator).to_have_text([...])`. Print with `print(...)`.\n- Use the `alias-python` names: `route.continue_()`, `locator.or_()`, `page.expect_request()`.\n- Commands: write `playwright install`, not `npx playwright install`.\n\n### Java\n\n| Node.js | Java |\n|---|---|\n| `await page.goto('...')` | `page.navigate(\"...\");`: no `await`, double quotes, semicolons, typed locals (`Locator dialog = ...`) |\n| `page.getByRole('button', { name: 'Save' })` | `page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName(\"Save\"))` |\n| options object `{ path: 'video.webm', fps: 60 }` | options builder `new Screencast.StartOptions().setPath(Paths.get(\"video.webm\")).setFps(60)` |\n| object literal `{ name: 'session-id', value: '42', url }` | value class `new Cookie(\"session-id\", \"42\").setUrl(url)` |\n| `['a', 'b']`, `{ a: 2 }` | `Arrays.asList(\"a\", \"b\")`, `Map.of(\"a\", 2)`; `new String[] {\"a\", \"b\"}` for assertion arguments |\n| property `page.webmcp`, `context.tracing` | getter `page.webmcp()`, `context.tracing()` |\n| event `page.on('dialogclosed', handler)` | `page.onDialogClosed(handler)` |\n| `expect(locator).toHaveText(...)` | `assertThat(locator).hasText(...)`. Likewise `toBeX` → `isX`, `toHaveX` → `hasX`, `toContainX` → `containsX`, `toMatchX` → `matchesX` |\n| `route.continue()` and other members with `alias-java` | the alias, e.g. `route.resume()` |\n| `npx playwright install --no-remove` | `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args=\"install --no-remove\"` |\n| \"array\", `undefined`, `'ai'` in prose | \"list\", `null`, `\"ai\"` |\n\n### .NET\n\n| Node.js | .NET |\n|---|---|\n| `await page.goto('...')` | `await page.GotoAsync(\"...\");`: PascalCase members, `Async` suffix on async methods, `var` locals, double quotes, semicolons |\n| `page.getByRole('button', { name: 'Save' })` | `page.GetByRole(AriaRole.Button, new() { Name = \"Save\" })` |\n| options object `{ path: 'video.webm', fps: 60 }` | options initializer `new() { Path = \"video.webm\", Fps = 60 }` |\n| nested object `size: { width: 1920, height: 1080 }` | `Size = new() { Width = 1920, Height = 1080 }` |\n| object literal `{ name: 'session-id', value: '42', url }` | `new Cookie { Name = \"session-id\", Value = \"42\", Url = url }` |\n| `['a', 'b']`, free-form object `{ a: 2 }` | `new[] { \"a\", \"b\" }`, anonymous object `new { a = 2 }` |\n| string-literal union value `'ai'`, `'button'` | enum member `AriaSnapshotMode.Ai`, `AriaRole.Button` |\n| property `page.webmcp`, `context.tracing` | property `page.Webmcp`, `context.Tracing` |\n| event `page.on('dialogclosed', handler)` | `page.DialogClosed += (_, dialog) => { ... };` |\n| `expect(locator).toHaveText(...)` | `await Expect(locator).ToHaveTextAsync(...);` |\n| `console.log(x)` | `Console.WriteLine(x);` |\n| members with `alias-csharp` | the alias, e.g. `page.request` → `page.APIRequest` |\n| union-typed option, e.g. `httpCredentials` as a list | one property per type, e.g. `HttpCredentialsList` |\n| `npx playwright install --no-remove` | `pwsh bin\u002FDebug\u002FnetX\u002Fplaywright.ps1 install --no-remove` |\n| option names in prose (`fontSize`), `undefined` | `FontSize`, `null` |\n\nOpen one PR for all four files, titled `docs: release notes for v1.xx`. After it merges, cherry-pick it to `release-1.xx` with the `playwright-cherry-pick` skill.\n\n## 3. GitHub release markdown\n\nEach repo publishes the GitHub release for its language, with markdown converted from its `## Version 1.xx` section on `release-1.xx`. Convert the notes yourself:\n\n- Leave out the `## Version 1.xx` heading. Move every other heading up one level (`###` becomes `##`, `####` becomes `###`).\n- Replace each link macro with a full link: the language's docs base URL from the table at the top, plus the path below. Use the language's name as the link text:\n\n  | Macro | Path after the docs base URL | Node.js | Python | Java | .NET |\n  |---|---|---|---|---|---|\n  | `[Locator]` | `api\u002Fclass-locator` | `Locator` | `Locator` | `Locator` | `Locator` |\n  | `` [`method: Page.frameLocator`] `` | `api\u002Fclass-page#page-frame-locator` | `page.frameLocator()` | `page.frame_locator()` | `page.frameLocator()` | `Page.FrameLocator()` |\n  | `` [`method: Page.ariaSnapshot`] `` | `api\u002Fclass-page#page-aria-snapshot` | `page.ariaSnapshot()` | `page.aria_snapshot()` | `page.ariaSnapshot()` | `Page.AriaSnapshotAsync()` |\n  | `` [`method: Locator.first`] `` | `api\u002Fclass-locator#locator-first` | `locator.first()` | `locator.first` | `locator.first()` | `Locator.First` |\n  | `` [`property: Page.webmcp`] `` | `api\u002Fclass-page#page-webmcp` | `page.webmcp` | `page.webmcp` | `page.webmcp()` | `Page.Webmcp` |\n  | `` [`event: BrowserContext.dialogClosed`] `` | `api\u002Fclass-browsercontext#browser-context-event-dialog-closed` | `browserContext.on('dialogclosed')` | `browser_context.on(\"dialogclosed\")` | `browserContext.onDialogClosed()` | `BrowserContext.DialogClosed` |\n  | `` [`method: LocatorAssertions.toBeVisible`] `` | `api\u002Fclass-locatorassertions#locator-assertions-to-be-visible` | `expect(locator).toBeVisible()` | `expect(locator).to_be_visible()` | `assertThat(locator).isVisible()` | `Expect(Locator).ToBeVisibleAsync()` |\n  | `` [`option: Page.content.includeShadow`] `` | `api\u002Fclass-page#page-content-option-include-shadow` | `` `includeShadow` `` | `` `include_shadow` `` | `` `includeShadow` `` | `` `IncludeShadow` `` |\n\n- An image `![alt](.\u002Fimages\u002Ffoo.png)` becomes `![alt](https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002Fdocs\u002Fsrc\u002Fimages\u002Ffoo.png?raw=true)` in every language.\n\nThe page is `class-` and the class name in lower case. The anchor is the class and member names in kebab-case, so `APIRequestContext.addCookies` becomes `api-request-context-add-cookies`. Options add `-option-\u003Cname>`, and events add `-event-\u003Cname>`. Anchors use the names from `docs\u002Fsrc\u002Fapi\u002F` even where a language renames the member, so Java's `isVisible()` links to `#locator-assertions-to-be-visible`.\n\nThe link text follows the language:\n\n- **Node.js:** the class in lower camel case (`page`, `apiRequestContext`, `testConfig`). Methods get `()`, properties none, events become `on('\u003Cevent in lower case>')`, and assertions start with `expect(locator).`, `expect(page).` or `expect(response).`.\n- **Python:** the class and member in snake_case (`browser_context`, `api_request_context`). Methods get `()`. A member is a property, without `()`, if `_generated.py` declares it with `@property`. Events become `on(\"\u003Cevent in lower case>\")`, and assertions start with `expect(locator).`.\n- **Java:** the class in lower camel case and the `alias-java` member name. Methods and properties get `()`, events become `on\u003CEvent>()`, and assertions start with `assertThat(locator).`.\n- **.NET:** the class without the interface `I` (`Page`, `APIRequestContext`) and the member exactly as the generated `I\u003CClass>.cs` declares it. Async methods take the `Async` suffix and `()`, sync methods get `()`, and properties and events get no parentheses. Assertions start with `Expect(Locator).`.\n\nCheck that every link resolves. playwright.dev already documents the release once the Node.js docs roll has landed. It serves minified HTML, so an anchor may appear without quotes (`id=page-webmcp>`):\n\n```bash\ngrep -oE 'https:\u002F\u002Fplaywright\\.dev\u002F[^) ]+' \u003Cnotes-file> | sort -u | while read -r url; do\n  page=${url%%#*}\n  html=$(curl -sfL \"$page\") || { echo \"MISSING PAGE   $url\"; continue; }\n  if [[ \"$url\" == *\"#\"* ]]; then\n    anchor=${url#*#}\n    grep -qE \"id=\\\"?${anchor}[\\\" >]\" \u003C\u003C\u003C\"$html\" || echo \"MISSING ANCHOR $url\"\n  fi\ndone\n```\n\nThen scan for anything that wasn't converted, and for stray spaces inside link text (`[Page. DialogClosed]`, `` [`HttpCredentialsList `] ``). The first command applies to every language; then run the one for the language. They should print nothing:\n\n```bash\n# every language\ngrep -nE '\\[`(method|property|event|option|param): |\\]\\(\\.\u002F|\\[[A-Z][A-Za-z]*\\]([^(]|$)|\\[[A-Za-z_]+\\. |\\[`[^`]+ `\\]' \u003Cnotes-file>\n# Python: camelCase names in link text\ngrep -nE '\\[[a-z_]+\\.[a-z_]*[A-Z]|\\[`[a-z]+[A-Z]' \u003Cnotes-file>\n# Java: JS-style events, assertions and properties\ngrep -nE \"\\.on\\('|\\b(toBe|toHave|toContain|toMatch)[A-Z]|\\bawait\\b|\\barray\\b|\\bundefined\\b|\\[[a-z][A-Za-z]*\\.[a-z][A-Za-z]*\\]\\(\" \u003Cnotes-file>\n# .NET: lower-case classes or members, JS-style events\ngrep -nE \"\\.on\\('|\\bundefined\\b|\\[[a-z][A-Za-z]*\\.|\\[[A-Z][A-Za-z]*\\.[a-z]|\\[Expect\\([a-z]\" \u003Cnotes-file>\n```\n\nIf the conversion shows a problem in the notes themselves, such as an API the language does not have or a sample in the wrong style, fix the notes too (step 2). Otherwise the problem stays on playwright.dev.\n","# Release notes\n\nIn this skill, `1.xx` is the release (for example `1.64`).\n\nEvery release has a `## Version 1.xx` section in four files in `docs\u002Fsrc\u002F`:\n\n| Language | Notes | Docs base URL | GitHub release in |\n|---|---|---|---|\n| Node.js | `release-notes-js.md` | `https:\u002F\u002Fplaywright.dev\u002Fdocs\u002F` | `microsoft\u002Fplaywright` |\n| Python | `release-notes-python.md` | `https:\u002F\u002Fplaywright.dev\u002Fpython\u002Fdocs\u002F` | `microsoft\u002Fplaywright-python` |\n| Java | `release-notes-java.md` | `https:\u002F\u002Fplaywright.dev\u002Fjava\u002Fdocs\u002F` | `microsoft\u002Fplaywright-java` |\n| .NET | `release-notes-csharp.md` | `https:\u002F\u002Fplaywright.dev\u002Fdotnet\u002Fdocs\u002F` | `microsoft\u002Fplaywright-dotnet` |\n\nWrite the Node.js notes and the three translations in the same PR, so every language has its notes when it starts its release. The language repos then add only their own significant changes, and each one writes its GitHub release from its section (step 3).\n\n## 1. Node.js notes\n\nIn `docs\u002Fsrc\u002Frelease-notes-js.md`, add a `## Version 1.xx` section above the previous version, and use previous versions as a guide:\n\n- Highlights come first, each with an emoji heading and a short sample. Ask teammates for help with them.\n- Then `### New APIs` with `####` groups, `### Breaking Changes ⚠️`, `### Announcements` and `### Browser Versions`. Print the browser versions with `node utils\u002Fprint_versions.js`.\n- Refer to APIs with docs link macros: `` [`method: Page.frameLocator`] ``, `` [`property: Page.webmcp`] ``, `` [`event: Page.dialogClosed`] ``, `` [`option: Tracing.start.ariaSnapshots`] ``, `[Locator]`. Each language's docs show them with that language's names.\n- Link other docs pages with relative paths: `[test locks](.\u002Ftest-parallel.md#test-locks)`.\n\n## 2. Python, Java and .NET translations\n\nIn each of `release-notes-python.md`, `release-notes-java.md` and `release-notes-csharp.md`, add a `## Version 1.xx` section above the previous version. Translate it from the Node.js section. Compare the `Version 1.63` sections of the files to see what past translations dropped and changed.\n\nFor every language:\n\n- Keep the structure: highlights with emoji headings, `### New APIs` with `####` groups, `### Breaking Changes ⚠️`, `### Announcements` and `### Browser Versions`.\n- Remove what the language does not have:\n  - the test runner: `@playwright\u002Ftest`, `test.*`, config, fixtures, projects, reporters and `npx playwright test` options\n  - UI mode, the HTML report, component testing and the VS Code extension\n  - TypeScript-only features (generics, JSX, `tsconfig`) and `devices` descriptors\n  - every API whose `langs` excludes the language (`python`, `java`, `csharp`). Check `langs` in `docs\u002Fsrc\u002Fapi\u002F`; options shared between methods are defined in `docs\u002Fsrc\u002Fapi\u002Fparams.md`.\n- Use the language's API where it differs from Node.js:\n  - A language-specific option has its own `langs`. For example, `recordVideoFps` is `langs: csharp, java, python`, while Node.js has `recordVideo.fps`.\n  - `alias-\u003Clang>` renames a member, e.g. `alias-java: isVisible` for `LocatorAssertions.toBeVisible`.\n  - The generated APIs in sibling checkouts confirm the names: `..\u002Fplaywright-python\u002Fplaywright\u002Fsync_api\u002F_generated.py`, `..\u002Fplaywright-java\u002Fplaywright\u002Fsrc\u002Fmain\u002Fjava\u002Fcom\u002Fmicrosoft\u002Fplaywright\u002F` and `..\u002Fplaywright-dotnet\u002Fsrc\u002FPlaywright\u002FAPI\u002FGenerated\u002F`.\n- When a Node.js sample shows a library feature through `playwright.config.ts`, show the API call instead. For example, the 1.64 video config became calls to `Screencast.start` and `Screencast.showActions`.\n- Keep API links as docs link macros, and link the language's own option where it differs (`` [`option: Browser.newContext.recordVideoFps`] ``). In plain text, use the language's names.\n- A relative link must point to a page that exists in the language: `*-js.md` pages are Node.js only. `npm run doc` reports broken links for each language.\n- Do not add changes that only a language port made. The language repo adds those to its notes.\n\nRewrite code samples in each language's style. The `infra` workflow checks the syntax of `python`, `java` and `csharp` snippets.\n\n### Python\n\n- Use the sync API: `page.get_by_role(\"button\").click()`. Use snake_case names and Python values (`True`, `False`, `None`).\n- Options become keyword arguments: `page.get_by_role(\"button\", name=\"Save\")`. Objects and arrays become dicts and lists: `size={\"width\": 1920, \"height\": 1080}`.\n- Aria roles and other string values stay strings: `\"button\"`, `\"ai\"`.\n- Events: `page.on(\"dialogclosed\", handler)`. Assertions: `expect(locator).to_have_text([...])`. Print with `print(...)`.\n- Use the `alias-python` names: `route.continue_()`, `locator.or_()`, `page.expect_request()`.\n- Commands: write `playwright install`, not `npx playwright install`.\n\n### Java\n\n| Node.js | Java |\n|---|---|\n| `await page.goto('...')` | `page.navigate(\"...\");`: no `await`, double quotes, semicolons, typed locals (`Locator dialog = ...`) |\n| `page.getByRole('button', { name: 'Save' })` | `page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName(\"Save\"))` |\n| options object `{ path: 'video.webm', fps: 60 }` | options builder `new Screencast.StartOptions().setPath(Paths.get(\"video.webm\")).setFps(60)` |\n| object literal `{ name: 'session-id', value: '42', url }` | value class `new Cookie(\"session-id\", \"42\").setUrl(url)` |\n| `['a', 'b']`, `{ a: 2 }` | `Arrays.asList(\"a\", \"b\")`, `Map.of(\"a\", 2)`; `new String[] {\"a\", \"b\"}` for assertion arguments |\n| property `page.webmcp`, `context.tracing` | getter `page.webmcp()`, `context.tracing()` |\n| event `page.on('dialogclosed', handler)` | `page.onDialogClosed(handler)` |\n| `expect(locator).toHaveText(...)` | `assertThat(locator).hasText(...)`. Likewise `toBeX` → `isX`, `toHaveX` → `hasX`, `toContainX` → `containsX`, `toMatchX` → `matchesX` |\n| `route.continue()` and other members with `alias-java` | the alias, e.g. `route.resume()` |\n| `npx playwright install --no-remove` | `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args=\"install --no-remove\"` |\n| \"array\", `undefined`, `'ai'` in prose | \"list\", `null`, `\"ai\"` |\n\n### .NET\n\n| Node.js | .NET |\n|---|---|\n| `await page.goto('...')` | `await page.GotoAsync(\"...\");`: PascalCase members, `Async` suffix on async methods, `var` locals, double quotes, semicolons |\n| `page.getByRole('button', { name: 'Save' })` | `page.GetByRole(AriaRole.Button, new() { Name = \"Save\" })` |\n| options object `{ path: 'video.webm', fps: 60 }` | options initializer `new() { Path = \"video.webm\", Fps = 60 }` |\n| nested object `size: { width: 1920, height: 1080 }` | `Size = new() { Width = 1920, Height = 1080 }` |\n| object literal `{ name: 'session-id', value: '42', url }` | `new Cookie { Name = \"session-id\", Value = \"42\", Url = url }` |\n| `['a', 'b']`, free-form object `{ a: 2 }` | `new[] { \"a\", \"b\" }`, anonymous object `new { a = 2 }` |\n| string-literal union value `'ai'`, `'button'` | enum member `AriaSnapshotMode.Ai`, `AriaRole.Button` |\n| property `page.webmcp`, `context.tracing` | property `page.Webmcp`, `context.Tracing` |\n| event `page.on('dialogclosed', handler)` | `page.DialogClosed += (_, dialog) => { ... };` |\n| `expect(locator).toHaveText(...)` | `await Expect(locator).ToHaveTextAsync(...);` |\n| `console.log(x)` | `Console.WriteLine(x);` |\n| members with `alias-csharp` | the alias, e.g. `page.request` → `page.APIRequest` |\n| union-typed option, e.g. `httpCredentials` as a list | one property per type, e.g. `HttpCredentialsList` |\n| `npx playwright install --no-remove` | `pwsh bin\u002FDebug\u002FnetX\u002Fplaywright.ps1 install --no-remove` |\n| option names in prose (`fontSize`), `undefined` | `FontSize`, `null` |\n\nOpen one PR for all four files, titled `docs: release notes for v1.xx`. After it merges, cherry-pick it to `release-1.xx` with the `playwright-cherry-pick` skill.\n\n## 3. GitHub release markdown\n\nEach repo publishes the GitHub release for its language, with markdown converted from its `## Version 1.xx` section on `release-1.xx`. Convert the notes yourself:\n\n- Leave out the `## Version 1.xx` heading. Move every other heading up one level (`###` becomes `##`, `####` becomes `###`).\n- Replace each link macro with a full link: the language's docs base URL from the table at the top, plus the path below. Use the language's name as the link text:\n\n  | Macro | Path after the docs base URL | Node.js | Python | Java | .NET |\n  |---|---|---|---|---|---|\n  | `[Locator]` | `api\u002Fclass-locator` | `Locator` | `Locator` | `Locator` | `Locator` |\n  | `` [`method: Page.frameLocator`] `` | `api\u002Fclass-page#page-frame-locator` | `page.frameLocator()` | `page.frame_locator()` | `page.frameLocator()` | `Page.FrameLocator()` |\n  | `` [`method: Page.ariaSnapshot`] `` | `api\u002Fclass-page#page-aria-snapshot` | `page.ariaSnapshot()` | `page.aria_snapshot()` | `page.ariaSnapshot()` | `Page.AriaSnapshotAsync()` |\n  | `` [`method: Locator.first`] `` | `api\u002Fclass-locator#locator-first` | `locator.first()` | `locator.first` | `locator.first()` | `Locator.First` |\n  | `` [`property: Page.webmcp`] `` | `api\u002Fclass-page#page-webmcp` | `page.webmcp` | `page.webmcp` | `page.webmcp()` | `Page.Webmcp` |\n  | `` [`event: BrowserContext.dialogClosed`] `` | `api\u002Fclass-browsercontext#browser-context-event-dialog-closed` | `browserContext.on('dialogclosed')` | `browser_context.on(\"dialogclosed\")` | `browserContext.onDialogClosed()` | `BrowserContext.DialogClosed` |\n  | `` [`method: LocatorAssertions.toBeVisible`] `` | `api\u002Fclass-locatorassertions#locator-assertions-to-be-visible` | `expect(locator).toBeVisible()` | `expect(locator).to_be_visible()` | `assertThat(locator).isVisible()` | `Expect(Locator).ToBeVisibleAsync()` |\n  | `` [`option: Page.content.includeShadow`] `` | `api\u002Fclass-page#page-content-option-include-shadow` | `` `includeShadow` `` | `` `include_shadow` `` | `` `includeShadow` `` | `` `IncludeShadow` `` |\n\n- An image `![alt](.\u002Fimages\u002Ffoo.png)` becomes `![alt](https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002Fdocs\u002Fsrc\u002Fimages\u002Ffoo.png?raw=true)` in every language.\n\nThe page is `class-` and the class name in lower case. The anchor is the class and member names in kebab-case, so `APIRequestContext.addCookies` becomes `api-request-context-add-cookies`. Options add `-option-\u003Cname>`, and events add `-event-\u003Cname>`. Anchors use the names from `docs\u002Fsrc\u002Fapi\u002F` even where a language renames the member, so Java's `isVisible()` links to `#locator-assertions-to-be-visible`.\n\nThe link text follows the language:\n\n- **Node.js:** the class in lower camel case (`page`, `apiRequestContext`, `testConfig`). Methods get `()`, properties none, events become `on('\u003Cevent in lower case>')`, and assertions start with `expect(locator).`, `expect(page).` or `expect(response).`.\n- **Python:** the class and member in snake_case (`browser_context`, `api_request_context`). Methods get `()`. A member is a property, without `()`, if `_generated.py` declares it with `@property`. Events become `on(\"\u003Cevent in lower case>\")`, and assertions start with `expect(locator).`.\n- **Java:** the class in lower camel case and the `alias-java` member name. Methods and properties get `()`, events become `on\u003CEvent>()`, and assertions start with `assertThat(locator).`.\n- **.NET:** the class without the interface `I` (`Page`, `APIRequestContext`) and the member exactly as the generated `I\u003CClass>.cs` declares it. Async methods take the `Async` suffix and `()`, sync methods get `()`, and properties and events get no parentheses. Assertions start with `Expect(Locator).`.\n\nCheck that every link resolves. playwright.dev already documents the release once the Node.js docs roll has landed. It serves minified HTML, so an anchor may appear without quotes (`id=page-webmcp>`):\n\n```bash\ngrep -oE 'https:\u002F\u002Fplaywright\\.dev\u002F[^) ]+' \u003Cnotes-file> | sort -u | while read -r url; do\n  page=${url%%#*}\n  html=$(curl -sfL \"$page\") || { echo \"MISSING PAGE   $url\"; continue; }\n  if [[ \"$url\" == *\"#\"* ]]; then\n    anchor=${url#*#}\n    grep -qE \"id=\\\"?${anchor}[\\\" >]\" \u003C\u003C\u003C\"$html\" || echo \"MISSING ANCHOR $url\"\n  fi\ndone\n```\n\nThen scan for anything that wasn't converted, and for stray spaces inside link text (`[Page. DialogClosed]`, `` [`HttpCredentialsList `] ``). The first command applies to every language; then run the one for the language. They should print nothing:\n\n```bash\n# every language\ngrep -nE '\\[`(method|property|event|option|param): |\\]\\(\\.\u002F|\\[[A-Z][A-Za-z]*\\]([^(]|$)|\\[[A-Za-z_]+\\. |\\[`[^`]+ `\\]' \u003Cnotes-file>\n# Python: camelCase names in link text\ngrep -nE '\\[[a-z_]+\\.[a-z_]*[A-Z]|\\[`[a-z]+[A-Z]' \u003Cnotes-file>\n# Java: JS-style events, assertions and properties\ngrep -nE \"\\.on\\('|\\b(toBe|toHave|toContain|toMatch)[A-Z]|\\bawait\\b|\\barray\\b|\\bundefined\\b|\\[[a-z][A-Za-z]*\\.[a-z][A-Za-z]*\\]\\(\" \u003Cnotes-file>\n# .NET: lower-case classes or members, JS-style events\ngrep -nE \"\\.on\\('|\\bundefined\\b|\\[[a-z][A-Za-z]*\\.|\\[[A-Z][A-Za-z]*\\.[a-z]|\\[Expect\\([a-z]\" \u003Cnotes-file>\n```\n\nIf the conversion shows a problem in the notes themselves, such as an API the language does not have or a sample in the wrong style, fix the notes too (step 2). Otherwise the problem stays on playwright.dev.\n",{"name":5,"description":7},"main","b6110e409d2087143d5722fa47b4197f71654734",".claude\u002Fskills\u002Fplaywright-release-notes",6837,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright","https:\u002F\u002Fplaywright.dev","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-11 06:37:31",{"source":8,"skillName":5,"commands":61,"prompt":71,"zipAvailable":72},[62,65,68],{"tool":63,"command":64},"npx","npx skills add microsoft\u002Fplaywright --skill playwright-release-notes",{"tool":66,"command":67},"bunx","bunx skills add microsoft\u002Fplaywright --skill playwright-release-notes",{"tool":69,"command":70},"pnpm","pnpm dlx skills add microsoft\u002Fplaywright --skill playwright-release-notes","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplaywright-release-notes\u002FSKILL.md",true,[],[75,99,127,145,169,185],{"skillNo":76,"name":77,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":82,"publishedAt":45},"01M6999MNG7F462VPA3A3DQT1D","playwright-trace","Inspect Playwright trace files from the command line — list actions, view requests, console, errors, snapshots and screenshots.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Ftools\u002Fskills\u002Fplaywright-trace\u002FSKILL.md",[81],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[25,83,84,85,86,87,88,89,90,91,92,93,94,95,63,96,97,98],"playwright trace","trace","command","line","command line","console","list","actions","snapshots","inspect","view","screenshots","errors","files","Bash","requests",{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":109,"publishedAt":45},"01M6999HEFRKHCK11RJYDBHG5N","playwright-component-testing","Set up component testing with Playwright using a story gallery — scaffold stories and a gallery dev page driven by the built-in mount fixture, no dedicated component-testing runtime. Use when asked to test React or Vue components in isolation with Playwright, or to migrate off @playwright\u002Fexperimental-ct-react \u002F -vue.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Ftools\u002Fskills\u002Fplaywright-component-testing\u002FSKILL.md",[105,106],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":107,"categoryName":108,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[25,110,111,112,113,114,37,115,116,117,118,34,119,38,120,121,122,123,124,125,126],"playwright component testing","testing","component","components","migrate","up","fixture","SET","test","stories","react","dev","page","runtime","isolation","vue","gallery",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":134,"publishedAt":45},"01M6999DVV27DR1Q2WMH329ST3","playwright-cli","Automate browser interactions, test web pages and work with Playwright tests.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002Fpackages\u002Fplaywright-core\u002Fsrc\u002Ftools\u002Fskills\u002Fplaywright-cli\u002FSKILL.md",[133],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[135,136,25,137,34,63,138,139,140,141,142,97,129,143,118,144],"playwright cli","cli","pages","tests","install","test web","work","browser","web","automate",{"skillNo":146,"name":147,"nameZh":6,"description":148,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":149,"categories":150,"keywords":155,"publishedAt":45},"01M69998DRR0DMSH3N60ZCWVKB","playwright-test-results","Query Playwright CI test results from the aggregated DuckDB database. Answers questions about flaky tests, failure rates, slow tests, and per-run\u002FSHA\u002FPR results without hunting through GitHub artifacts.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplaywright-test-results\u002FSKILL.md",[151,152],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":153,"categoryName":154,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",[156,118,25,157,44,158,159,160,161,162,163,164,165,138,166,167,168],"results","playwright test results","questions","duckdb","about","run","hunting","artifacts","query","database","true","ci","pr",{"skillNo":170,"name":171,"nameZh":6,"description":172,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":173,"categories":174,"keywords":176,"publishedAt":45},"01M699938PWX7P2KS04997VVZD","playwright-devops","DevOps workflows for Playwright - CI failure analysis, workflow debugging, and release operations.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplaywright-devops\u002FSKILL.md",[175],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[177,178,25,28,179,180,181,182,166,167,183,39,184],"devops","playwright devops","operations","analysis","workflows","debugging","failure analysis","workflow",{"skillNo":186,"name":187,"nameZh":6,"description":188,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":189,"categories":190,"keywords":195,"publishedAt":45},"01M6999036ZZB26N62YQTAAFCG","playwright-dev","Explains how to develop Playwright - add APIs, MCP tools, CLI commands, and vendor dependencies.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fplaywright\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplaywright-dev\u002FSKILL.md",[191,192],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":193,"categoryName":194,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[196,25,121,197,198,199,200,201,202,203,204,205,206,136],"playwright dev","add","Tools","mcp tools","commands","dependencies","vendor","how","develop","how to","MCP"]