关于 playwright-release-notes
playwright-release-notes 是帮助贡献者编写 Playwright 发布文档的技能。它指导编写 Node.js 发布说明及 Python、Java 和 .NET 译文,并将各语言章节转换为对应的 GitHub 发布说明 Markdown。
主要功能
- 01编写 Node.js 发布说明
- 02翻译为 Python、Java 和 .NET 版本
- 03适配各语言的 API 名称和示例
- 04转换为 GitHub 发布说明 Markdown
- 05检查文档链接
适用场景
- 01准备四种语言的发布文档
- 02调整各语言的 API 示例
- 03生成并验证发布公告 Markdown
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ playwright --skill playwright-release-notes安装后在 Claude Code 中输入/playwright-release-notes确认已加载。
详细介绍
发布说明
在此技能中,1.xx 表示发布版本(例如 1.64)。
docs/src/ 中的四个文件均包含一个 ## Version 1.xx 章节:
| 语言 | 说明 | 文档基础 URL | GitHub 发布所在仓库 |
|---|---|---|---|
| Node.js | release-notes-js.md | https://playwright.dev/docs/ | microsoft/playwright |
| Python | release-notes-python.md | https://playwright.dev/python/docs/ | microsoft/playwright-python |
| Java | release-notes-java.md | https://playwright.dev/java/docs/ | microsoft/playwright-java |
| .NET | release-notes-csharp.md | https://playwright.dev/dotnet/docs/ | microsoft/playwright-dotnet |
在同一个 PR 中编写 Node.js 说明和另外三种语言的译文,确保每种语言在开始发布时都有对应的说明。之后,各语言仓库只需添加各自的重要变更,并根据对应章节编写 GitHub 发布说明(第 3 步)。
1. Node.js 说明
在 docs/src/release-notes-js.md 中,在上一版本之前添加 ## Version 1.xx 章节,并参考之前的版本:
- 首先列出亮点,每项使用带有 emoji 的标题和简短示例。请队友协助撰写。
- 接着添加
### New APIs,并使用####分组,然后添加### Breaking Changes ⚠️、### Announcements和### Browser Versions。使用node utils/print_versions.js输出浏览器版本。 - 使用文档链接宏引用 API:
[`method: Page.frameLocator`]、[`property: Page.webmcp`]、[`event: Page.dialogClosed`]、[`option: Tracing.start.ariaSnapshots`]、[Locator]。各语言的文档会使用对应语言的名称显示这些内容。 - 使用相对路径链接其他文档页面:
[test locks](./test-parallel.md#test-locks)。
2. Python、Java 和 .NET 翻译
在 release-notes-python.md、release-notes-java.md 和 release-notes-csharp.md 中,分别在上一版本之前添加一个 ## Version 1.xx 部分。将其从 Node.js 部分翻译而来。对比这些文件的 Version 1.63 部分,查看过去的翻译遗漏或改动了哪些内容。
对于每种语言:
- 保留以下结构:带表情符号标题的亮点、带
####分组的### New APIs、### Breaking Changes ⚠️、### Announcements和### Browser Versions。 - 删除该语言不支持的内容:
- 测试运行程序:
@playwright/test、test.*、配置、fixture、项目、报告器和npx playwright test选项 - UI 模式、HTML 报告、组件测试和 VS Code 扩展
- 仅限 TypeScript 的功能(泛型、JSX、
tsconfig)和devices描述符 - 所有
langs不包含该语言(python、java、csharp)的 API。请检查docs/src/api/中的langs;多个方法共用的选项定义在docs/src/api/params.md中。
- 测试运行程序:
- 使用与 Node.js 不同的语言 API:
- 特定语言的选项有自己的
langs。例如,recordVideoFps的langs为csharp, java, python,而 Node.js 使用recordVideo.fps。 alias-<lang>用于重命名成员,例如LocatorAssertions.toBeVisible的alias-java: isVisible。- 同级仓库中的生成 API 可用于确认名称:
../playwright-python/playwright/sync_api/_generated.py、../playwright-java/playwright/src/main/java/com/microsoft/playwright/和../playwright-dotnet/src/Playwright/API/Generated/。
- 特定语言的选项有自己的
- 如果 Node.js 示例通过
playwright.config.ts展示库功能,则改为展示 API 调用。例如,1.64 的视频配置改成了调用Screencast.start和Screencast.showActions。 - 将 API 链接保留为文档链接宏;选项不同的语言应链接到该语言自己的选项(
[`option: Browser.newContext.recordVideoFps`])。纯文本中使用该语言的名称。 - 相对链接必须指向该语言中存在的页面:
*-js.md页面仅适用于 Node.js。npm run doc会报告各语言的失效链接。 - 不要添加仅由某种语言的移植版本引入的改动。语言仓库会将这些内容添加到各自的发布说明中。
按照各语言的风格改写代码示例。infra 工作流会检查 python、java 和 csharp 代码片段的语法。
Python
- 使用同步 API:
page.get_by_role("button").click()。使用蛇形命名法和 Python 值(True、False、None)。 - 选项改为关键字参数:
page.get_by_role("button", name="Save")。对象和数组改为字典和列表:size={"width": 1920, "height": 1080}。 - Aria 角色和其他字符串值保留为字符串:
"button"、"ai"。 - 事件:
page.on("dialogclosed", handler)。断言:expect(locator).to_have_text([...])。使用print(...)输出。 - 使用
alias-python名称:route.continue_()、locator.or_()、page.expect_request()。 - 命令:写
playwright install,不要写npx playwright install。
Java
| Node.js | Java |
|---|---|
await page.goto('...') | page.navigate("...");:无需 await,使用双引号、分号和类型化局部变量(Locator dialog = ...) |
page.getByRole('button', { name: 'Save' }) | page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Save")) |
选项对象 { path: 'video.webm', fps: 60 } | 选项构建器 new Screencast.StartOptions().setPath(Paths.get("video.webm")).setFps(60) |
对象字面量 { name: 'session-id', value: '42', url } | 值类 new Cookie("session-id", "42").setUrl(url) |
['a', 'b']、{ a: 2 } | Arrays.asList("a", "b")、Map.of("a", 2);断言参数使用 new String[] {"a", "b"} |
属性 page.webmcp、context.tracing | Getter page.webmcp()、context.tracing() |
事件 page.on('dialogclosed', handler) | page.onDialogClosed(handler) |
expect(locator).toHaveText(...) | assertThat(locator).hasText(...)。同理,toBeX → isX、toHaveX → hasX、toContainX → containsX、toMatchX → matchesX |
route.continue() 及其他带有 alias-java 的成员 | 别名,例如 route.resume() |
npx playwright install --no-remove | mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --no-remove" |
在正文中使用 "array"、undefined、'ai' | "list"、null、"ai" |
.NET
| Node.js | .NET |
|---|---|
await page.goto('...') | await page.GotoAsync("...");:成员使用 PascalCase,异步方法带 Async 后缀,局部变量使用 var,使用双引号和分号 |
page.getByRole('button', { name: 'Save' }) | page.GetByRole(AriaRole.Button, new() { Name = "Save" }) |
选项对象 { path: 'video.webm', fps: 60 } | 选项初始化器 new() { Path = "video.webm", Fps = 60 } |
嵌套对象 size: { width: 1920, height: 1080 } | Size = new() { Width = 1920, Height = 1080 } |
对象字面量 { name: 'session-id', value: '42', url } | new Cookie { Name = "session-id", Value = "42", Url = url } |
['a', 'b']、自由格式对象 { a: 2 } | new[] { "a", "b" }、匿名对象 new { a = 2 } |
字符串字面量联合值 'ai'、'button' | 枚举成员 AriaSnapshotMode.Ai、AriaRole.Button |
属性 page.webmcp、context.tracing | 属性 page.Webmcp、context.Tracing |
事件 page.on('dialogclosed', handler) | page.DialogClosed += (_, dialog) => { ... }; |
expect(locator).toHaveText(...) | await Expect(locator).ToHaveTextAsync(...); |
console.log(x) | Console.WriteLine(x); |
带有 alias-csharp 的成员 | 别名,例如 page.request → page.APIRequest |
联合类型选项,例如作为列表的 httpCredentials | 每种类型对应一个属性,例如 HttpCredentialsList |
npx playwright install --no-remove | pwsh bin/Debug/netX/playwright.ps1 install --no-remove |
正文中的选项名称(fontSize)、undefined | FontSize、null |
为全部四个文件提交一个 PR,标题为 docs: release notes for v1.xx。合并后,使用 playwright-cherry-pick skill 将其 cherry-pick 到 release-1.xx。
3. GitHub 发布说明 Markdown
每个仓库都会发布对应语言的 GitHub 发布说明,内容由 release-1.xx 上的 ## Version 1.xx 章节转换为 Markdown。请自行转换说明:
-
删除
## Version 1.xx标题。将其他所有标题提升一级(###变为##,####变为###)。 -
将每个链接宏替换为完整链接:使用顶部表格中的该语言文档基础 URL,加上下面的路径。链接文本使用该语言的名称:
宏 文档基础 URL 之后的路径 Node.js Python Java .NET [Locator]api/class-locatorLocatorLocatorLocatorLocator[`method: Page.frameLocator`]api/class-page#page-frame-locatorpage.frameLocator()page.frame_locator()page.frameLocator()Page.FrameLocator()[`method: Page.ariaSnapshot`]api/class-page#page-aria-snapshotpage.ariaSnapshot()page.aria_snapshot()page.ariaSnapshot()Page.AriaSnapshotAsync()[`method: Locator.first`]api/class-locator#locator-firstlocator.first()locator.firstlocator.first()Locator.First[`property: Page.webmcp`]api/class-page#page-webmcppage.webmcppage.webmcppage.webmcp()Page.Webmcp[`event: BrowserContext.dialogClosed`]api/class-browsercontext#browser-context-event-dialog-closedbrowserContext.on('dialogclosed')browser_context.on("dialogclosed")browserContext.onDialogClosed()BrowserContext.DialogClosed[`method: LocatorAssertions.toBeVisible`]api/class-locatorassertions#locator-assertions-to-be-visibleexpect(locator).toBeVisible()expect(locator).to_be_visible()assertThat(locator).isVisible()Expect(Locator).ToBeVisibleAsync()[`option: Page.content.includeShadow`]api/class-page#page-content-option-include-shadow`includeShadow``include_shadow``includeShadow``IncludeShadow`[Playwright MCP](./getting-started-mcp.md)getting-started-mcpPlaywright MCPPlaywright MCPPlaywright MCPPlaywright MCP -
图片
在每种语言中都会变为。
页面名称为 class- 加上小写类名。锚点由 kebab-case 格式的类名和成员名组成,因此 APIRequestContext.addCookies 会变为 api-request-context-add-cookies。选项会添加 -option-<name>,事件会添加 -event-<name>。锚点使用 docs/src/api/ 中的名称,即使某种语言重命名了成员也是如此,因此 Java 的 isVisible() 链接到 #locator-assertions-to-be-visible。
链接文本因语言而异:
- Node.js: 类名使用小驼峰格式(
page、apiRequestContext、testConfig)。方法带(),属性不带,事件变为on('<event in lower case>'),断言以expect(locator).、expect(page).或expect(response).开头。 - Python: 类名和成员名使用 snake_case(
browser_context、api_request_context)。方法带()。如果_generated.py使用@property声明成员,则该成员是属性,不带()。事件变为on("<event in lower case>"),断言以expect(locator).开头。 - Java: 类名使用小驼峰格式,成员名使用
alias-java中的名称。方法和属性带(),事件变为on<Event>(),断言以assertThat(locator).开头。 - .NET: 类名不带接口前缀
I(Page、APIRequestContext),成员名与生成的I<Class>.cs中的声明完全一致。异步方法带Async后缀和(),同步方法带(),属性和事件不带括号。断言以Expect(Locator).开头。
检查每个链接是否都能正常跳转。Node.js 文档滚动更新完成后,playwright.dev 就会记录该版本。它提供的是经过压缩的 HTML,因此锚点可能不带引号(id=page-webmcp>):
grep -oE 'https://playwright\.dev/[^) ]+' <notes-file> | sort -u | while read -r url; do
page=${url%%#*}
html=$(curl -sfL "$page") || { echo "MISSING PAGE $url"; continue; }
if [[ "$url" == *"#"* ]]; then
anchor=${url#*#}
grep -qE "id=\"?${anchor}[\" >]" <<<"$html" || echo "MISSING ANCHOR $url"
fi
done
然后扫描是否有未转换的内容,以及链接文本中是否有多余空格([Page. DialogClosed]、[`HttpCredentialsList `])。第一条命令适用于所有语言;然后运行对应语言的命令。它们不应输出任何内容:
# every language
grep -nE '\[`(method|property|event|option|param): |\]\(\./|\[[A-Z][A-Za-z]*\]([^(]|$)|\[[A-Za-z_]+\. |\[`[^`]+ `\]' <notes-file>
# Python: camelCase names in link text
grep -nE '\[[a-z_]+\.[a-z_]*[A-Z]|\[`[a-z]+[A-Z]' <notes-file>
# Java: JS-style events, assertions and properties
grep -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]*\]\(" <notes-file>
# .NET: lower-case classes or members, JS-style events
grep -nE "\.on\('|\bundefined\b|\[[a-z][A-Za-z]*\.|\[[A-Z][A-Za-z]*\.[a-z]|\[Expect\([a-z]" <notes-file>
如果转换结果表明问题出在注释本身,例如使用了该语言不支持的 API,或示例风格不正确,也请一并修正注释(第 2 步)。否则,问题仍会留在 playwright.dev 上。