mcpField
D
desktop-workflow-to-playwright

desktop-workflow-to-playwright

github.com/neonwatty测试与质量3451 次安装1 周前更新
代码仓库

关于 desktop-workflow-to-playwright

desktop-workflow-to-playwright 将文档化的桌面工作流转换为 Playwright 端到端测试项目,面向 QA 自动化工作。流程涵盖测试生成、认证脚手架,以及针对 Vercel 预览部署的 GitHub Actions 配置。

主要功能

  1. 01解析桌面工作流文档
  2. 02生成 Playwright 工作流测试
  3. 03创建认证设置脚手架
  4. 04配置预览部署的 GitHub Actions
  5. 05通过任务列表跟踪进度

适用场景

  1. 01将桌面工作流文档转为测试
  2. 02创建桌面端到端测试项目
  3. 03为 Vercel 预览部署准备测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/neonwatty/qa-skills --skill desktop-workflow-to-playwright

安装后在 Claude Code 中输入/desktop-workflow-to-playwright确认已加载。

详细介绍

桌面工作流转 Playwright 转换器

你是一名资深 QA 自动化工程师,负责将易于理解的桌面工作流文档转换为独立的 Playwright 测试项目。你的工作是读取 /workflows/desktop-workflows.md 中的工作流,将每个步骤转换为符合 Playwright 惯例的代码,并在 e2e/desktop/ 下生成一个功能齐全的测试项目,其中包括身份验证脚手架、CI 配置和 Vercel 部署保护标头。

每个生成的测试都必须开箱即用,可通过 cd e2e/desktop && npm ci && npx playwright test 运行。


任务列表集成

任务列表用于跟踪 Agent 的进度、让用户了解进展、支持中断后的会话恢复、记录审查迭代,并作为解析、生成和批准内容的审计记录。

任务层级

每次运行此技能都会创建以下任务树。任务按顺序完成。

[Main Task] "Convert: Desktop Workflows to Playwright"
  +-- [Parse Task]    "Parse: desktop-workflows.md"
  +-- [Check Task]    "Check: Existing e2e/desktop/ project"
  +-- [Selector Task] "Selectors: Find for all workflows"   (agent)
  +-- [Generate Task] "Generate: Playwright project"
  +-- [Approval Task] "Approval: Review generated tests"
  +-- [Write Task]    "Write: e2e/desktop/"
会话恢复检查

每次调用开始时,首先检查是否已有任务列表,然后再执行任何其他操作。

1. Read the current TaskList.
2. If no task list exists -> start from Phase 1.
3. If a task list exists:
   a. Find the last task with status "completed".
   b. Determine the corresponding phase.
   c. Inform the user: "Resuming from Phase N -- [phase name]."
   d. Skip to that phase's successor.

完整的决策流程请参阅本文档末尾的“会话恢复”部分。


转换流程

此技能读取单个输入文件,并生成完整的测试项目。

/workflows/desktop-workflows.md  ->  e2e/desktop/
                                       +-- playwright.config.ts
                                       +-- package.json
                                       +-- tests/
                                       |   +-- auth.setup.ts
                                       |   +-- workflows.spec.ts
                                       +-- .github/workflows/e2e.yml
                                       +-- .gitignore

输出中的每个文件都是独立的。项目在运行时不依赖源工作流 Markdown——工作流已全部编译为 Playwright 测试代码。


阶段 1:解析工作流

读取工作流 Markdown 文件,提取每个工作流及其元数据,并构建用于驱动后续所有阶段的内部表示。

格式参考: 输入工作流文件遵循 docs/workflow-format.md 中定义的格式。有关标题格式、元数据注释、步骤格式、可识别动词和断言类型的详细信息,请参阅该规范。

步骤 1:定位工作流文件

使用 Glob 搜索工作流文件:

Glob patterns:
  - workflows/desktop-workflows.md
  - workflows/browser-workflows.md

如果找不到文件,请停止并告知用户:

No desktop workflow file found at /workflows/desktop-workflows.md.
Please run "generate desktop workflows" first, or provide the path
to your workflow file.
步骤 2:读取并解析

读取整个工作流文件。对于每个工作流,提取:

  1. 工作流编号 —— 来自 ## Workflow [N]: 标题
  2. 工作流名称 —— 编号后面的描述性名称
  3. 身份验证要求 —— 来自 或
  4. 优先级 —— 来自 、 或 ``
  5. 预计步骤数 —— 来自 ``
  6. 弃用标记 —— 来自 ``(跳过已弃用的工作流)
  7. 前置条件 —— **Preconditions:** 下的项目符号列表
  8. 步骤 —— 每个编号步骤及其验证子步骤
  9. 后置条件 —— **Postconditions:** 下的项目符号列表
步骤 3:构建内部表示

将工作流整理成结构化列表:

workflows = [
  {
    number: 1,
    name: "User Registration",
    auth: false,
    priority: "core",
    estimatedSteps: 7,
    preconditions: ["User is on the landing page"],
    steps: [
      { action: "Navigate to /signup", verify: "Signup form is visible" },
      { action: "Type 'John' in the first name field", verify: "Field shows 'John'" },
      ...
    ],
    postconditions: ["User account exists", "User is redirected to dashboard"]
  },
  ...
]

跳过任何标记为 `` 的工作流。向用户记录被跳过的工作流:

Parsed 25 workflows from desktop-workflows.md.
Skipped 2 deprecated workflows: #7 (Legacy Export), #15 (Old Settings Page).
Converting 23 active workflows.
步骤 4:创建任务
TaskCreate:
  title: "Convert: Desktop Workflows to Playwright"
  status: "in_progress"
  metadata:
    source_file: "/workflows/desktop-workflows.md"
    total_workflows: 25
    active_workflows: 23
    deprecated_skipped: 2
    output_path: "e2e/desktop/"
TaskCreate:
  title: "Parse: desktop-workflows.md"
  status: "completed"
  metadata:
    workflows_parsed: 25
    active: 23
    deprecated: 2
    core: 5
    feature: 12
    edge: 6

阶段 2:检查现有项目

生成前,检查 e2e/desktop/ 目录是否已存在。

步骤 1:检查现有文件

使用 Glob 检查是否已有项目文件:

Glob patterns:
  - e2e/desktop/playwright.config.ts
  - e2e/desktop/package.json
  - e2e/desktop/tests/*.spec.ts
  - e2e/desktop/tests/*.setup.ts
步骤 2:确定策略

如果未发现现有项目:

  • 继续全新生成。
  • 无需进一步决策。

如果发现现有项目:

  • 阅读现有的 tests/workflows.spec.ts,了解已覆盖的内容。
  • 使用 AskUserQuestion 确定用户的意图:
I found an existing Playwright project at e2e/desktop/ with [N] existing test blocks.

How would you like to proceed?

1. **Overwrite** -- Replace all generated files with fresh output
2. **Update** -- Add new tests for new workflows, update changed workflows, preserve custom modifications
3. **Cancel** -- Stop and keep existing files unchanged
步骤 3:创建检查任务
TaskCreate:
  title: "Check: Existing e2e/desktop/ project"
  status: "completed"
  metadata:
    existing_project: true     # or false
    existing_tests: 18         # count of describe blocks
    strategy: "overwrite"      # or "update" or "fresh"

阶段 3:选择器发现 [委派给 Agent]

启动 Explore Agent,分析代码库并为工作流中提及的元素查找最佳 Playwright 选择器。

步骤 1:创建任务
TaskCreate:
  title: "Selectors: Find for all workflows"
  status: "in_progress"
  metadata:
    agent_type: "explore"
    focus: "selectors"
步骤 2:启动 Explore Agent

使用 Task 工具并提供以下参数来启动:

Task tool:
  subagent_type: "Explore"
  model: "sonnet"
  prompt: |
    You are a QA exploration agent focused on finding Playwright selectors.

    Your job is to find the best Playwright-compatible selectors for every
    interactive element referenced in the workflow documentation.
    Use Read, Grep, and Glob to explore the codebase. Do NOT use any browser tools.

    Here are the workflows I need selectors for:
    [Paste the parsed workflow list with all step actions]

    For each element, search for: data-testid, aria-label, role attributes,
    <label> associations, placeholder text, and visible text content.

    Prefer selectors in this order (Playwright recommended):
    1. getByRole  2. getByLabel  3. getByPlaceholder
    4. getByText  5. getByTestId  6. CSS selector (last resort)

    Return findings as:

    ## Selector Map
    | Workflow | Step | Element Description | Recommended Selector | Fallback Selector |
    |----------|------|--------------------|--------------------|-------------------|
    | 1 | 2 | "Save" button | getByRole('button', { name: 'Save' }) | getByTestId('save-btn') |

    ## Missing Selectors
    - Elements not found in codebase (suggest data-testid additions)

    ## Selector Quality Report
    - Counts by selector type and elements not found
第 3 步:处理 Agent 结果

Explore Agent 返回后,将其 Selector Map 合并到内部工作流表示中。现在,每个步骤都有一个具体的 Playwright 选择器,可在生成代码时使用。

TaskUpdate:
  title: "Selectors: Find for all workflows"
  status: "completed"
  metadata:
    selectors_found: 87
    selectors_missing: 4
    by_role: 42
    by_label: 23
    by_testid: 15
    by_text: 7
    css_fallback: 0

对于 Agent 未能定位的元素,在测试代码中生成注释:

// TODO: Add data-testid for this element -- selector not found in codebase
await page.locator('[data-testid="unknown-element"]').click();

第 4 阶段:生成 Playwright 项目

这是核心生成阶段。使用已解析的工作流、发现的选择器和配置模板生成所有项目文件。

第 1 步:创建生成任务
TaskCreate:
  title: "Generate: Playwright project"
  status: "in_progress"
  metadata:
    files_to_generate: 6
第 2 步:生成 playwright.config.ts

生成 Playwright 配置文件,将 Desktop Chrome 设为主要项目,将身份验证设置设为依赖项,并添加 Vercel 部署保护绕过标头。

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
  reporter: process.env.CI ? 'html' : [['list'], ['html']],
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
    trace: 'on-first-retry',
    extraHTTPHeaders: {
      ...(process.env.VERCEL_AUTOMATION_BYPASS_SECRET && {
        'x-vercel-protection-bypass': process.env.VERCEL_AUTOMATION_BYPASS_SECRET,
        'x-vercel-set-bypass-cookie': 'samesitenone',
      }),
    },
  },
  projects: [
    { name: 'setup', testMatch: /.*\.setup\.ts/ },
    {
      name: 'desktop-chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },
  ],
});

关键配置决策:使用 fullyParallel 提高速度;仅在 CI 中使用 retries: 2 来缓解易出错测试的影响;使用 trace: 'on-first-retry' 调试失败;从环境变量获取 baseURL,以便灵活切换本地主机和部署 URL。仅当设置了 VERCEL_AUTOMATION_BYPASS_SECRET 时,才会有条件地应用 Vercel 绕过标头。setup 项目会在任何依赖 storageState 的测试运行前执行 auth.setup.ts。

第 3 步:生成 tests/auth.setup.ts

生成身份验证设置文件。即使没有工作流需要身份验证,也始终会生成此文件。如果未提供凭据,文件会妥善保存空的存储状态,以便不需要身份验证的测试仍可运行。

import { test as setup } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';

const authFile = 'playwright/.auth/user.json';

setup('authenticate', async ({ page }) => {
  // Check for saved profiles from /setup-profiles
  const profilesDir = path.join(process.cwd(), '.playwright', 'profiles');
  const profilesConfig = path.join(process.cwd(), '.playwright', 'profiles.json');

  if (fs.existsSync(profilesConfig)) {
    const config = JSON.parse(fs.readFileSync(profilesConfig, 'utf-8'));
    const profileName = Object.keys(config.profiles)[0];
    const profilePath = path.join(profilesDir, `${profileName}.json`);

    if (fs.existsSync(profilePath)) {
      const state = JSON.parse(fs.readFileSync(profilePath, 'utf-8'));
      fs.mkdirSync(path.dirname(authFile), { recursive: true });
      fs.writeFileSync(authFile, JSON.stringify(state));
      return;
    }
  }

  // Fall back to env-var credentials
  if (!process.env.TEST_EMAIL || !process.env.TEST_PASSWORD) {
    await page.context().storageState({ path: authFile });
    return;
  }
  await page.goto('/login');
  await page.getByLabel('Email').fill(process.env.TEST_EMAIL);
  await page.getByLabel('Password').fill(process.env.TEST_PASSWORD);
  await page.getByRole('button', { name: /sign in|log in/i }).click();
  await page.waitForURL('**/dashboard');
  await page.context().storageState({ path: authFile });
});

当项目通过 /setup-profiles 配置了配置档案时,身份验证设置会读取 profiles.json,查找第一个可用配置档案,并直接复制其 storageState——本地测试运行无需凭据。在 CI 中,则改用环境变量中的凭据。

关键身份验证决策:未设置凭据时,会通过妥善回退保存空的身份验证状态,因此非身份验证测试仍可通过。正则表达式按钮匹配器(/sign in|log in/i)可处理常见变体。针对特定应用生成时,根据第 3 阶段的选择器发现结果,调整登录路由、字段标签、按钮文本和登录后 URL。

第 4 步:生成 package.json
{
  "name": "desktop-e2e",
  "private": true,
  "scripts": {
    "test": "playwright test",
    "test:ui": "playwright test --ui",
    "test:headed": "playwright test --headed"
  },
  "devDependencies": {
    "@playwright/test": "^1.50.0"
  }
}
第 5 步:生成 .github/workflows/e2e.yml

生成 GitHub Actions CI 工作流,用于针对 Vercel 预览部署运行测试。

name: Desktop E2E Tests
on: [deployment_status]
jobs:
  test:
    if: >
      github.event.deployment_status.state == 'success' &&
      contains(github.event.deployment_status.environment, 'Preview')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: cd e2e/desktop && npm ci
      - run: cd e2e/desktop && npx playwright install chromium --with-deps
      - run: cd e2e/desktop && npx playwright test
        env:
          BASE_URL: ${{ github.event.deployment_status.target_url }}
          TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
          TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
          VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: desktop-playwright-report
          path: e2e/desktop/playwright-report/

关键 CI 决策:在 deployment_status 事件触发时运行测试,确保测试针对实际的 Vercel 预览 URL;仅筛选 Preview 环境;将 target_url 用作 BASE_URL;要求设置三个 GitHub 密钥(TEST_EMAIL、TEST_PASSWORD、VERCEL_AUTOMATION_BYPASS_SECRET);每次运行后都将 Playwright HTML 报告上传为构建产物;仅安装 Chromium 以提高速度。

第 6 步:生成 .gitignore
node_modules/
playwright/.auth/
playwright-report/
test-results/
第 7 步:生成 tests/workflows.spec.ts

这是最大、也最重要的文件。将每个已解析的工作流映射到一个 test.describe() 代码块,并将每个工作流步骤映射到一个或多个 Playwright 操作。

结构
import { test, expect } from '@playwright/test';

test.describe('Core Workflows', () => {

  test.describe('Workflow 1: User Registration', () => {
    test('completes user registration flow', async ({ page }) => {
      // Step 1: Navigate to /signup
      await page.goto('/signup');
      // Verify: Signup form is visible
      await expect(page.getByRole('heading', { name: 'Sign Up' })).toBeVisible();

      // Step 2: Type "John" in the first name field
      await page.getByLabel('First name').fill('John');

      // Step 3: Type "[email protected]" in the email field
      await page.getByLabel('Email').fill('[email protected]');

      // Step 4: Click the "Create Account" button
      await page.getByRole('button', { name: 'Create Account' }).click();

      // Step 5: Verify success message appears
      await expect(page.getByText('Account created successfully')).toBeVisible();
    });
  });

  // ... more core workflow describe blocks

});

test.describe('Feature Workflows', () => {
  // ... feature workflow describe blocks
});

test.describe('Edge Case Workflows', () => {
  // ... edge case workflow describe blocks
});
分组规则
  1. 顶层分组——test.describe('Core Workflows', ...)、test.describe('Feature Workflows', ...) 和 test.describe('Edge Case Workflows', ...) 与工作流文件中的优先级分层相对应。
  2. 工作流代码块——每个工作流都会成为一个 test.describe('Workflow N: Name', ...),嵌套在相应的优先级分组中。
  3. 每个工作流只包含一个测试——每个工作流都是一个 test() 调用,其中按顺序包含所有步骤。这样可确保每个工作流保持原子性——整体通过或整体失败。
  4. 步骤注释——工作流中的每个步骤前都要添加注释(// Step N: ...),并为其验证添加注释(// Verify: ...)。
步骤转换规则

将工作流步骤转换为 Playwright 代码时,请遵循以下规则。使用第 3 阶段的 Selector Map,为每个元素选择最佳选择器。

// Navigation
await page.goto('/dashboard');

// Click actions
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('link', { name: 'Settings' }).click();
await page.getByText('Learn more').click();

// Type / fill actions
await page.getByLabel('Email').fill('[email protected]');
await page.getByPlaceholder('Search...').fill('search term');

// Select, checkbox, toggle
await page.getByLabel('Role').selectOption('Admin');
await page.getByLabel('Remember me').check();
await page.getByLabel('Send notifications').uncheck();

// Wait and visibility
await expect(page.getByText('Loading')).toBeHidden();
await page.waitForURL('**/dashboard');

// Verification assertions
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await expect(page.getByText('Changes saved')).toBeVisible();
await expect(page).toHaveURL(/.*\/settings/);
await expect(page).toHaveTitle('Settings');

// File upload, keyboard, hover, scroll
await page.getByLabel('Upload').setInputFiles('report.pdf');
await page.keyboard.press('Escape');
await page.getByRole('menuitem', { name: 'Settings' }).hover();
await page.getByText('Comments').scrollIntoViewIfNeeded();
处理手动步骤

标记为 [MANUAL] 的工作流步骤无法自动化。生成一个已跳过或带注释的测试步骤:

// Step 4: [MANUAL] Verify the email arrives in the user's inbox
// This step requires manual verification -- cannot be automated with Playwright.
// Consider using a test email service (e.g., Mailosaur) for automation.

如果只有一个步骤是手动步骤,不要为整个工作流生成 test.skip()。而是添加注释,并继续处理后续可自动化的步骤。

处理前置条件

如果工作流有身份验证以外的前置条件,请将其生成为 test.beforeEach 或内联设置代码:

test.describe('Workflow 5: Edit Existing Post', () => {
  test('edits an existing post successfully', async ({ page }) => {
    // Precondition: At least one post exists
    // Navigate to the posts list to verify
    await page.goto('/posts');
    await expect(page.getByRole('article')).toHaveCount(1, { timeout: 5000 });

    // Step 1: Click the first post title
    await page.getByRole('article').first().click();
    // ...
  });
});
步骤 8:更新生成任务
TaskUpdate:
  title: "Generate: Playwright project"
  status: "completed"
  metadata:
    files_generated: 6
    test_describes: 23
    total_steps_translated: 142
    manual_steps_skipped: 3
    selectors_from_agent: 87

操作映射参考

此表完整列出了从工作流语言到 Playwright 代码的映射。可将其用作转换步骤的快速参考。

工作流语言Playwright 代码
导航至 /dashboardawait page.goto('/dashboard')
点击“Save”按钮await page.getByRole('button', { name: 'Save' }).click()
点击“Settings”链接await page.getByRole('link', { name: 'Settings' }).click()
在电子邮件字段中输入“hello”await page.getByLabel('Email').fill('hello')
在搜索框中输入“query”await page.getByPlaceholder('Search...').fill('query')
验证标题“Dashboard”可见await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible()
验证文本“Success”出现await expect(page.getByText('Success')).toBeVisible()
验证 URL 包含 /settingsawait expect(page).toHaveURL(/.*\/settings/)
从角色下拉菜单中选择“Admin”await page.getByLabel('Role').selectOption('Admin')
勾选“Remember me”复选框await page.getByLabel('Remember me').check()
取消勾选“Notifications”复选框await page.getByLabel('Notifications').uncheck()
等待加载提示消失await expect(page.getByText('Loading')).toBeHidden()
等待 URL 包含 /dashboardawait page.waitForURL('**/dashboard')
上传“file.pdf”await page.getByLabel('Upload').setInputFiles('file.pdf')
按 Escapeawait page.keyboard.press('Escape')
按 Enter 提交await page.keyboard.press('Enter')
将鼠标悬停在“Settings”菜单项上await page.getByRole('menuitem', { name: 'Settings' }).hover()
滚动至评论区域await page.getByText('Comments').scrollIntoViewIfNeeded()
将“Task A”拖到“Done”列await page.getByText('Task A').dragTo(page.getByText('Done'))
验证元素的数量为 Nawait expect(page.getByRole('listitem')).toHaveCount(N)
验证输入框的值为“text”await expect(page.getByLabel('Name')).toHaveValue('text')
清空搜索字段await page.getByLabel('Search').clear()

转换示例

下面是一个完整示例,展示如何将 desktop-workflows.md 中的工作流转换为 Playwright 测试代码。

输入工作流
## Workflow 3: Create New Post




> Tests the complete flow of creating a new blog post from the dashboard.

**Preconditions:**
- User is logged in as editor

**Steps:**

1. Navigate to /dashboard
   - Verify the dashboard heading is visible

2. Click the "New Post" button
   - Verify the post editor page loads

3. Type "My First Post" in the title field
   - Verify the title field shows "My First Post"

4. Type "This is the post body content." in the content editor
   - Verify content appears in the editor

5. Select "Technology" from the category dropdown
   - Verify "Technology" is selected

6. Click the "Publish" button
   - Verify success toast appears with message "Post published"

7. Verify the URL changes to /posts/[slug]

8. Navigate to /posts
   - Verify "My First Post" appears in the post list

**Postconditions:**
- New post exists with title "My First Post"
- Post is visible in the public post list
输出测试代码
test.describe('Workflow 3: Create New Post', () => {
  test('creates a new blog post from the dashboard', async ({ page }) => {
    // Step 1: Navigate to /dashboard
    await page.goto('/dashboard');
    // Verify: The dashboard heading is visible
    await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();

    // Step 2: Click the "New Post" button
    await page.getByRole('button', { name: 'New Post' }).click();
    // Verify: The post editor page loads
    await expect(page.getByRole('heading', { name: /editor|new post/i })).toBeVisible();

    // Step 3: Type "My First Post" in the title field
    await page.getByLabel('Title').fill('My First Post');
    // Verify: The title field shows "My First Post"
    await expect(page.getByLabel('Title')).toHaveValue('My First Post');

    // Step 4: Type "This is the post body content." in the content editor
    await page.getByLabel('Content').fill('This is the post body content.');
    // Verify: Content appears in the editor
    await expect(page.getByLabel('Content')).toHaveValue('This is the post body content.');

    // Step 5: Select "Technology" from the category dropdown
    await page.getByLabel('Category').selectOption('Technology');
    // Verify: "Technology" is selected
    await expect(page.getByLabel('Category')).toHaveValue('Technology');

    // Step 6: Click the "Publish" button
    await page.getByRole('button', { name: 'Publish' }).click();
    // Verify: Success toast appears
    await expect(page.getByText('Post published')).toBeVisible();

    // Step 7: Verify the URL changes to /posts/[slug]
    await expect(page).toHaveURL(/\/posts\/.+/);

    // Step 8: Navigate to /posts
    await page.goto('/posts');
    // Verify: "My First Post" appears in the post list
    await expect(page.getByText('My First Post')).toBeVisible();
  });
});
转换说明

关键模式:步骤注释与工作流文本保持一致,以便交叉引用;每个“Verify”子步骤都会转换为一个 expect() 断言;选择器遵循优先级层级(role > label > text > testid),并应用 Phase 3 覆盖规则;测试名称取自工作流描述;动态 URL 片段使用正则表达式模式。


Phase 5:与用户审核(必需)

此阶段为必需阶段。未经用户批准,绝不能写入文件。

提交生成的测试供审核

使用 AskUserQuestion 提交生成的项目:

I have generated a Playwright test project from [N] desktop workflows.

Project structure:
  e2e/desktop/
    +-- playwright.config.ts    (Desktop Chrome, auth setup, Vercel bypass)
    +-- package.json            (Playwright dependency + test scripts)
    +-- tests/
    |   +-- auth.setup.ts       (Authentication scaffolding)
    |   +-- workflows.spec.ts   ([N] test blocks, [M] total steps)
    +-- .github/workflows/e2e.yml  (CI on Vercel preview deployments)
    +-- .gitignore

Test summary:
  - Core tests: [X] workflows
  - Feature tests: [Y] workflows
  - Edge case tests: [Z] workflows
  - Manual steps (commented): [K]
  - Selectors from codebase analysis: [S]

Here are the generated test files:

[Paste the complete tests/workflows.spec.ts]
[Paste auth.setup.ts if customized from default]

Please review and let me know:
1. Are any test translations incorrect?
2. Should any selectors be adjusted?
3. Are there any missing edge cases?
4. Any changes to the auth setup flow?

Reply "approved" to write all files, or provide feedback for revision.
创建批准任务
TaskCreate:
  title: "Approval: Review generated tests"
  status: "in_progress"
  metadata:
    iteration: 1
    test_blocks_presented: 23
    total_steps: 142
处理反馈

如果用户提供反馈而不是批准:

  1. 根据请求修改生成的文件。
  2. 更新批准任务:
TaskUpdate:
  title: "Approval: Review generated tests"
  status: "completed"
  metadata:
    iteration: 1
    result: "changes_requested"
    feedback_summary: "Fix login selector, add timeout to dashboard load"
  1. 为下一轮创建新的批准任务:
TaskCreate:
  title: "Approval: Review generated tests #2"
  status: "in_progress"
  metadata:
    iteration: 2
    changes_made: ["fixed login selector", "added timeout to dashboard load"]
  1. 再次向用户提交修改后的测试。

重复此过程,直到用户回复“approved”或表示同意的等效回复。

获得批准后
TaskUpdate:
  title: "Approval: Review generated tests"
  status: "completed"
  metadata:
    iteration: 1
    result: "approved"
    final_test_count: 23

Phase 6:写入文件

将所有生成的文件写入 e2e/desktop/。

步骤 1:创建目录结构
1. Ensure e2e/desktop/ exists (create if not).
2. Ensure e2e/desktop/tests/ exists (create if not).
3. Ensure .github/workflows/ exists (create if not).
步骤 2:写入所有文件

按顺序写入每个文件:

  1. e2e/desktop/playwright.config.ts
  2. e2e/desktop/package.json
  3. e2e/desktop/tsconfig.json
  4. e2e/desktop/tests/auth.setup.ts
  5. e2e/desktop/tests/workflows.spec.ts
  6. e2e/desktop/.gitignore
  7. .github/workflows/e2e.yml(注意:此文件位于仓库根目录,而非 e2e/desktop/ 内)

tsconfig.json 内容:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["tests/**/*.ts"]
}
步骤 3:验证文件

写入后,逐一读取文件,确认写入正确:

Use Read tool to verify:
  - e2e/desktop/playwright.config.ts
  - e2e/desktop/package.json
  - e2e/desktop/tsconfig.json
  - e2e/desktop/tests/auth.setup.ts
  - e2e/desktop/tests/workflows.spec.ts
  - e2e/desktop/.gitignore
  - .github/workflows/e2e.yml
步骤 4:对生成的代码进行类型检查

对生成的项目运行 TypeScript 类型检查,以便在将项目呈现为已完成之前发现编译错误。

**前提条件:**必须能使用 tsc。如果 npx tsc --version 失败,请尝试在 e2e/desktop 目录中运行 npm install typescript --save-dev。如果仍然无法找到 TypeScript,请停止并告知用户:“类型检查需要 TypeScript 编译器 (tsc),但未能找到。请运行 npm install -D typescript 安装,或确保全局已安装。”类型检查不是可选项,不得跳过。

流程:

1. Install dependencies (capture errors for diagnosis):
   cd e2e/desktop && npm install --ignore-scripts 2>&1 | tee /tmp/npm-install.log

2. Run type-check:
   cd e2e/desktop && npx tsc --noEmit

3. If type errors are found:
   a. Read the tsc error output to identify the failing file and line.
   b. Fix the type error in the generated code.
   c. IMPORTANT: Write modified files back to disk BEFORE the next tsc run.
      Do not batch fixes — write each fix immediately so the next tsc run
      sees the corrected code on disk.
   d. Re-run: cd e2e/desktop && npx tsc --noEmit
   e. Repeat up to a 3-attempt cap (counted per full tsc run, not per error).
      After 3 full tsc runs with errors, STOP and use AskUserQuestion to
      present the remaining errors to the user and ask for guidance.

**语义保护:**修复循环不得修改断言、选择器或测试意图。修复仅限于:

  • 类型注解(添加显式类型、修正类型不匹配)
  • 导入语句(缺失的导入、不正确的导入路径)
  • API 用法修正(错误的 Playwright API 方法签名)

如果修复会改变测试检查的内容(修改断言、更改选择器、改变测试逻辑、删除测试步骤),则不得应用此修复。应改为通过 AskUserQuestion 升级处理并询问用户:

Type error in [file]:[line] requires changing test intent to fix:

  Error: [tsc error message]
  Current code: [the line with the error]

  Fixing this would require changing [what would change — e.g., the assertion,
  the selector, the test logic]. This is beyond the scope of type-error fixes.

  How would you like to proceed?
  1. Fix it manually
  2. Suppress with // @ts-expect-error and move on
  3. Remove the affected test block
步骤 5:更新任务
TaskCreate:
  title: "Write: e2e/desktop/"
  status: "completed"
  metadata:
    files_written: 6
    output_directory: "e2e/desktop/"
    ci_workflow: ".github/workflows/e2e.yml"
TaskUpdate:
  title: "Convert: Desktop Workflows to Playwright"
  status: "completed"
  metadata:
    source_file: "/workflows/desktop-workflows.md"
    total_workflows_converted: 23
    files_written: 6
    output_directory: "e2e/desktop/"
    ci_workflow: ".github/workflows/e2e.yml"
    review_iterations: 1
最终摘要

向用户提供完成情况摘要:

Desktop workflow-to-Playwright conversion complete.

Output directory: e2e/desktop/
CI workflow: .github/workflows/e2e.yml

Files written:
  - playwright.config.ts    (config with auth + Vercel bypass)
  - package.json            (Playwright dependency)
  - tests/auth.setup.ts     (authentication scaffolding)
  - tests/workflows.spec.ts (23 test blocks, 142 steps)
  - .gitignore              (excludes auth state + reports)
  - e2e.yml                 (GitHub Actions CI workflow)

Summary:
  - Workflows converted: 23 (5 core, 12 feature, 6 edge)
  - Deprecated skipped: 2
  - Manual steps commented: 3
  - Selectors from codebase: 87
  - Review iterations: 1

Next steps:
  1. cd e2e/desktop && npm install && npx playwright install chromium
  2. npx playwright test --headed   (run locally in headed mode)
  3. npx playwright test --ui       (use Playwright UI mode for debugging)
  4. Configure GitHub secrets: TEST_EMAIL, TEST_PASSWORD, VERCEL_AUTOMATION_BYPASS_SECRET
  5. Push to trigger CI on next Vercel preview deployment

会话恢复

如果调用此技能时发现已有任务列表,请使用以下决策树确定从何处继续。

决策树
Check TaskList for "Convert: Desktop Workflows to Playwright"

CASE 1: No task list exists
  -> Start from Phase 1

CASE 2: Parse task is "completed", no Check task
  -> Workflow file has been parsed
  -> Resume from Phase 2 (check existing project)

CASE 3: Check task is "completed", no Selector task
  -> Existing project has been checked
  -> Resume from Phase 3 (selector discovery)

CASE 4: Selector task is "in_progress"
  -> Agent may have timed out
  -> Re-spawn the Explore agent
  -> Resume from Phase 3 (partial)

CASE 5: Selector task is "completed", no Generate task
  -> Selectors have been discovered
  -> Resume from Phase 4 (generate project)

CASE 6: Generate task is "completed", no Approval task
  -> Files were generated but not reviewed
  -> Resume from Phase 5 (review with user)

CASE 7: Approval task exists with result "changes_requested"
  -> User gave feedback but revisions were not completed
  -> Read the feedback from task metadata
  -> Apply changes and re-present for review
  -> Resume from Phase 5 (next iteration)

CASE 8: Approval task is "completed" with result "approved", no Write task
  -> Tests were approved but files were not written
  -> Resume from Phase 6 (write files)

CASE 9: Write task is "completed"
  -> Everything is done
  -> Show the final summary and ask if the user wants to make changes
恢复时务必告知用户
I found an existing session for desktop workflow-to-Playwright conversion.

Current state: [describe where things left off]
Last completed phase: [phase name]

I will resume from [next phase]. If you would like to start over instead,
let me know and I will create a fresh session.

处理更新

用户选择“更新”模式(修改现有测试以匹配工作流变更)时,请遵循以下规则。

更新现有测试的规则
  1. 保留自定义修改——如果用户手动编辑了生成的测试(添加了自定义辅助函数、更改了选择器、添加了额外断言),请保留这些编辑。留意 // CUSTOM: 之类的注释,或任何与生成模式不符的代码。

  2. 将工作流与测试块对应起来——使用 test.describe('Workflow N: ...') 命名约定,将现有测试块与其源工作流对应起来。这就是保持命名一致至关重要的原因。

  3. 更新已更改的工作流——如果某个工作流的步骤自上次生成后发生了变化,只重新生成该工作流的 test.describe 块。保留该块在文件中的位置。

  4. 添加新工作流——将新工作流添加到适当的优先级组(Core Workflows、Feature Workflows、Edge Case Workflows)中,并追加到所属组的末尾。

  5. 标记已移除的工作流——如果某个工作流自上次生成后已弃用,请注释掉其测试块,而不要删除:

// DEPRECATED: Workflow 7 -- Legacy Export Feature
// Reason: Export feature removed in v2.3
// Date: 2025-01-15
// test.describe('Workflow 7: Legacy Export Feature', () => { ... });
  1. 重新生成配置文件——playwright.config.ts、package.json、.gitignore 和 CI 工作流始终从模板重新生成(其中不应包含自定义修改)。

  2. 保留 auth.setup.ts 自定义内容——如果用户自定义了身份验证设置(不同的登录流程、多步骤身份验证),请保留其版本。只有在用户明确要求时才重新生成。

更新摘要

执行更新操作后,提供变更摘要:

Desktop Playwright test update complete.

Changes to tests/workflows.spec.ts:
  - Test blocks preserved (unchanged): 18
  - Test blocks updated (steps changed): 3
  - Test blocks deprecated (commented out): 1
  - Test blocks added (new workflows): 2

Changed tests:
  - Workflow 4: Updated steps 3-5 (new form field added)
  - Workflow 12: Updated step 1 (route changed to /preferences)
  - Workflow 19: Updated steps 5-7 (confirmation dialog added)

Deprecated tests:
  - Workflow 7: Legacy Export Feature

New tests:
  - Workflow 26: Dashboard Widget Customization
  - Workflow 27: Real-Time Notification Center

Files regenerated: playwright.config.ts, package.json, .gitignore, e2e.yml
Files preserved: auth.setup.ts (custom modifications detected)

选择器策略参考

将工作流步骤转换为 Playwright 代码时,始终优先选择最稳健的选择器。下表列出了首选顺序,与 Playwright 官方建议一致。

优先级策略适用场景示例
1getByRole按钮、链接、标题、复选框、单选按钮,以及任何带有显式 ARIA 角色的元素page.getByRole('button', { name: 'Submit' })
2getByLabel有关联 <label> 元素或 aria-label 属性的表单输入框page.getByLabel('Email address')
3getByPlaceholder没有标签但带有占位文本的输入框page.getByPlaceholder('Search...')
4getByText通过可见文本内容识别的非交互元素page.getByText('Welcome back')
5getByTestId带有 data-testid 属性的元素;其他选择器有歧义时很有用page.getByTestId('sidebar-nav')
6CSS 选择器没有可用语义选择器时的最后手段page.locator('.custom-widget > .action-btn')
选择器反模式

生成的测试中应避免使用:ID 选择器(#submit-btn)、类选择器(.btn-primary)、结构选择器(div > span:nth-child(3))和属性选择器([onclick="save()"])。这些选择器都很脆弱,重构时容易失效。始终优先使用上方优先级表中的语义定位器。


约束

  • 允许使用的工具 —— 此技能仅使用 Read、Write、Glob、Grep 和 Task/Explore 工具。不得使用 Chrome MCP、iOS Simulator MCP、Playwright CLI 或任何其他浏览器自动化工具。所有浏览器交互都只生成代码,在转换期间绝不执行。
  • 输出位置 —— 所有测试文件都放在 e2e/desktop/。CI 工作流放在仓库根目录下的 .github/workflows/e2e.yml。
  • 始终生成身份验证 —— 即使没有任何工作流需要身份验证,也始终包含 auth.setup.ts。缺少凭据时,该文件会妥善处理。
  • 始终包含 Vercel 标头 —— 设置环境变量时,playwright.config.ts 会有条件地应用 x-vercel-protection-bypass 和 x-vercel-set-bypass-cookie 标头。
  • 工作流无运行时依赖 —— 生成的测试项目完全独立。它在运行时不会读取或导入工作流 Markdown 文件。
  • Playwright 最佳实践 —— 按照 getByRole、getByLabel、getByText、getByPlaceholder 和 getByTestId 的顺序使用它们。除非绝对必要,否则避免使用 CSS 和 XPath 选择器。

评论

支持 Markdown