首页
/ Pruebas en TypeScript/JavaScript

Pruebas en TypeScript/JavaScript

2026-09-08 11:13:03作者:裴麒琰

Este archivo extiende common/testing.md con contenido específico de TypeScript/JavaScript.

Testing E2E

Usar Playwright como framework de testing E2E para flujos de usuario críticos.

Soporte de Agentes

  • e2e-runner - Especialista en testing E2E con Playwright

其 YAML frontmatter 的 `paths` 决定了规则的自动触发范围(所有 TS/TSX/JS/JSX 文件):

```yaml
paths:
  - "**/*.ts"
  - "**/*.tsx"
  - "**/*.js"
  - "**/*.jsx"

rules/README.md 中「语言特定规则优先于通用规则」的约定(类似 CSS 特异性),本文件是对通用测试要求的唯一 TypeScript 覆盖点;它本身没有对通用层的覆盖率门槛或 TDD 做任何放宽,因此必须与 rules/common/testing.md 一起阅读生效。安装时应整目录复制(避免 /* 展开破坏 ../common/ 相对引用),参见 rules/README.md

二、底层红线:80% 覆盖率与三类测试全量必选

该语言规则没有降低通用层的强制要求。rules/common/testing.md 明确规定了每个 TypeScript/JavaScript 项目都必须同时具备:

测试类型 对象 典型落点
Unit Tests(单元测试) 单个函数、工具、组件 *.test.ts 与源码同目录
Integration Tests(集成测试) API 端点、数据库操作 tests/integration/
E2E Tests(端到端测试) 关键用户流程 tests/e2e/*.spec.ts

其中 E2E 层的框架选择,正是本语言规则唯一明确指定的事项:Playwright。这条规则同时出现在 rules/typescript/testing.md 及其西班牙语镜像 docs/es/rules/typescript/testing.md 中,说明它是跨翻译版本被一致执行的语言级决策。

三、强制 TDD:先写测试(RED → GREEN → IMPROVE)

通用规则要求所有 TypeScript 功能开发遵循 TDD 强制流程(rules/common/testing.md):

  1. 先写测试(RED)——测试应先失败;
  2. 运行测试,确认失败;
  3. 编写最小实现(GREEN)——让测试通过;
  4. 重构(IMPROVE);
  5. 校验覆盖率 ≥ 80%。

配套的 agent 是 tdd-guide(见 agents/tdd-guide.md),其职责是在新功能开发时主动(PROACTIVELY)强制执行「先写测试」。规则还特别强调排障纪律:优先调用 tdd-guide、检查测试隔离性、核对 mock 是否正确,并且在测试本身无误时修改实现而非修改测试

四、测试结构规范:AAA 模式与行为化命名

4.1 AAA(Arrange-Act-Assert)三段式

通用规则以 TypeScript 示例约定了测试的标准结构(rules/common/testing.md):

test('calculates similarity correctly', () => {
  // Arrange
  const vector1 = [1, 0, 0]
  const vector2 = [0, 1, 0]

  // Act
  const similarity = calculateCosineSimilarity(vector1, vector2)

  // Assert
  expect(similarity).toBe(0)
})

4.2 行为化命名

测试名必须描述被测行为而非实现细节:

test('returns empty array when no markets match query', () => {})
test('throws error when API key is missing', () => {})
test('falls back to substring search when Redis is unavailable', () => {})

这种命名的价值在 E2E 场景被进一步放大:行为化描述天然映射到用户旅程(user journey),便于 e2e-runner 在生成用例时直接翻译为「用户动作 + 断言」。

五、E2E 层深挖:Playwright 与专职 agent e2e-runner

语言规则在 E2E 上的落地不止是选型,还绑定了执行主体 e2e-runner(完整定义见 agents/e2e-runner.md),其核心职责包括:

  1. Test Journey Creation——为关键用户流程编写测试(优先 Agent Browser,回退 Playwright);
  2. Test Maintenance——随 UI 变更维护用例;
  3. Flaky Test Management——识别并隔离不稳定测试;
  4. Artifact Management——采集截图、视频、trace;
  5. CI/CD Integration——保证流水线内稳定运行;
  6. Test Reporting——产出 HTML 报告与 JUnit XML。

在 ECC 的命令体系中,这一能力通过 /e2e 命令暴露给用户(见 docs/es/commands/e2e.md),典型触发场景包括登录、交易、支付等高风险关键流程,以及在发布前验证前后端集成。

5.1 工具优先级:Agent Browser 优先,Playwright 兜底

e2e-runner 的默认偏好是 Agent Browser(语义化选择器、AI 优化、内置自动等待,底层构建于 Playwright):

npm install -g agent-browser && agent-browser install

agent-browser open https://example.com
agent-browser snapshot -i          # 获取带 [ref=e1] 引用的元素
agent-browser click @e1            # 按引用点击
agent-browser fill @e2 "text"      # 填充输入框
agent-browser wait visible @e5     # 等待元素可见
agent-browser screenshot result.png

当 Agent Browser 不可用时,回退到原生 Playwright:

npx playwright test                        # 运行全部 E2E 用例
npx playwright test tests/auth.spec.ts     # 只跑指定文件
npx playwright test --headed               # 有头模式查看浏览器
npx playwright test --debug                # 调试器逐行运行
npx playwright test --trace on             # 带 trace 运行
npx playwright show-report                 # 打开 HTML 报告

5.2 Playwright 配置基线

sdks 中提供了可直接落地的 playwright.config.ts,覆盖多浏览器项目、CI 差异化重试与多种报告器:

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

export default defineConfig({
  testDir: './tests/e2e',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [
    ['html', { outputFolder: 'playwright-report' }],
    ['junit', { outputFile: 'playwright-results.xml' }],
    ['json', { outputFile: 'playwright-results.json' }]
  ],
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    actionTimeout: 10000,
    navigationTimeout: 30000,
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
    { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
  ],
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
    timeout: 120000,
  },
})

关键参数解读:

  • trace: 'on-first-retry':首次失败自动重试并捕获 trace,是最省存储的失败调试策略;
  • screenshot: 'only-on-failure' + video: 'retain-on-failure':只在失败时保留证据,对应 e2e-runner 的制品管理职责;
  • retries / workers 在 CI 与本地差异化:CI 上收紧并发(workers: 1)换取稳定性;
  • 三个 reporter 并行输出:HTML 给人看,JUnit/JSON 喂给 CI 与脚本。

5.3 Page Object Model(POM)范式

e2e-runner 的用例生成与 skill 均强调 POM,将页面选择器与交互封装为类:

import { Page, Locator } from '@playwright/test'

export class ItemsPage {
  readonly page: Page
  readonly searchInput: Locator
  readonly itemCards: Locator
  readonly createButton: Locator

  constructor(page: Page) {
    this.page = page
    this.searchInput = page.locator('[data-testid="search-input"]')
    this.itemCards = page.locator('[data-testid="item-card"]')
    this.createButton = page.locator('[data-testid="create-btn"]')
  }

  async goto() {
    await this.page.goto('/items')
    await this.page.waitForLoadState('networkidle')
  }

  async search(query: string) {
    await this.searchInput.fill(query)
    await this.page.waitForResponse(resp => resp.url().includes('/api/search'))
    await this.page.waitForLoadState('networkidle')
  }

  async getItemCount() {
    return await this.itemCards.count()
  }
}

配套的测试骨架遵循 test.describe + beforeEach 组织用例,并在关键步骤放置 expect 断言(skills/e2e-testing/SKILL.md):

import { test, expect } from '@playwright/test'
import { ItemsPage } from '../../pages/ItemsPage'

test.describe('Item Search', () => {
  let itemsPage: ItemsPage

  test.beforeEach(async ({ page }) => {
    itemsPage = new ItemsPage(page)
    await itemsPage.goto()
  })

  test('should search by keyword', async ({ page }) => {
    await itemsPage.search('test')

    const count = await itemsPage.getItemCount()
    expect(count).toBeGreaterThan(0)

    await expect(itemsPage.itemCards.first()).toContainText(/test/i)
    await page.screenshot({ path: 'artifacts/search-results.png' })
  })

  test('should handle no results', async ({ page }) => {
    await itemsPage.search('xyznonexistent123')

    await expect(page.locator('[data-testid="no-results"]')).toBeVisible()
    expect(await itemsPage.getItemCount()).toBe(0)
  })
})

六、关键用户流程的端到端验证实战

docs/es/commands/e2e.md 给出了 e2e-runner 的完整工作样例:将「搜索市场 → 查看结果 → 点击进入 → 查看详情」的用户旅程翻译为多场景用例,并在每个关键步骤验证 API 响应、页面状态与截图取证。

6.1 场景设计三要素

场景类型 示例 断言重点
Happy path(主路径) 搜索关键词并进入详情页 标题匹配、URL 形态 /markets/[id]、图表渲染
边界情形(empty state) 搜索不存在的市场 no-results 可见、卡片数为 0
反向操作 清空搜索恢复全部 计数回到初始值

E2E 中等待的真实形态是等待条件而非固定时长,这正是 unstable 测试的主要来源之一:

// 错误:任意超时
await page.waitForTimeout(5000)

// 正确:等待具体网络条件
await page.waitForResponse(resp => resp.url().includes('/api/data'))

// 正确:使用自动等待的 locator
await page.locator('[data-testid="button"]').click()

6.2 高风险流程:金融/交易与 Web3

对于金融与支付类关键流程,skill 强调三个原则:绝不针对生产环境执行真实资金操作、通过 mock 注入链上/provider 行为、对交易确认使用带 { timeout } 的响应等待:

test('trade execution', async ({ page }) => {
  // Skip on production — real money
  test.skip(process.env.NODE_ENV === 'production', 'Skip on production')

  await page.goto('/markets/test-market')
  await page.locator('[data-testid="position-yes"]').click()
  await page.locator('[data-testid="trade-amount"]').fill('1.0')

  const preview = page.locator('[data-testid="trade-preview"]')
  await expect(preview).toContainText('1.0')

  await page.locator('[data-testid="confirm-trade"]').click()
  await page.waitForResponse(
    resp => resp.url().includes('/api/trade') && resp.status() === 200,
    { timeout: 30000 }
  )

  await expect(page.locator('[data-testid="trade-success"]')).toBeVisible()
})

七、不稳定测试(Flaky)治理:隔离、重跑与指标

e2e-runner 与 skill 共同定义了 flaky 治理闭环:

定位问题(本地重跑放大):

npx playwright test tests/search.spec.ts --repeat-each=10
npx playwright test tests/search.spec.ts --retries=3

隔离问题用例(quarantine):

test('flaky: complex search', async ({ page }) => {
  test.fixme(true, 'Flaky - Issue #123')
})

test('conditional skip', async ({ page }) => {
  test.skip(process.env.CI, 'Flaky in CI - Issue #123')
})

三类高频成因与修法(来自 skills/e2e-testing/SKILL.md):

成因 反模式 修法
竞态条件 假设元素就绪后裸 page.click() 用自动等待 locator page.locator(...).click()
网络时序 waitForTimeout(5000) waitForResponse(url 匹配)
动画时序 动画播放期间点击 waitFor({ state: 'visible' }) + networkidle

成功指标agents/e2e-runner.md):关键流程通过率 100%、整体通过率 > 95%、flaky 率 < 5%、单次测试时长 < 10 分钟、制品可访问。

八、CI/CD 集成与报告产物

规则要求 E2E 在合入主干(merge to main)前执行。Playwright 官方 GitHub Actions 流程可直接对接:

name: E2E Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
        env:
          BASE_URL: ${{ vars.STAGING_URL }}
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

产物规范:每次运行生成 HTML 报告与 JUnit XML(供 CI 消费);仅失败时保留截图、视频、trace 压缩包、网络与控制台日志。本地排查命令:

npx playwright show-report                        # HTML 报告
npx playwright show-trace artifacts/trace.zip     # 单步回放 trace
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391