qa-regression by skillcreatorai/ai-agent-skills
npx skills add https://github.com/skillcreatorai/ai-agent-skills --skill qa-regression使用 Playwright 构建和运行自动化回归测试。每个测试都是一个可复用的技能,可以组合成完整的测试套件。
npm init -y
npm install playwright @playwright/test
npx playwright install
在 tests/ 文件夹中创建测试:
tests/
├── auth/
│ ├── login.spec.ts
│ └── logout.spec.ts
├── dashboard/
│ └── load.spec.ts
├── users/
│ ├── create.spec.ts
│ └── delete.spec.ts
└── regression.spec.ts # 完整套件
// tests/auth/login.spec.ts
import { test, expect } from '@playwright/test';
test.describe('登录流程', () => {
test('应使用有效凭据登录', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="email"]', process.env.TEST_EMAIL!);
await page.fill('[data-testid="password"]', process.env.TEST_PASSWORD!);
await page.click('[data-testid="submit"]');
// 验证重定向到仪表板
await expect(page).toHaveURL(/dashboard/);
await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
});
test('应为无效凭据显示错误', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="email"]', 'wrong@example.com');
await page.fill('[data-testid="password"]', 'wrongpassword');
await page.click('[data-testid="submit"]');
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
});
});
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
// tests/dashboard/load.spec.ts
import { test, expect } from '@playwright/test';
import { login } from '../helpers/auth';
test.describe('仪表板', () => {
test.beforeEach(async ({ page }) => {
await login(page);
});
test('应在 3 秒内加载仪表板', async ({ page }) => {
const start = Date.now();
await page.goto('/dashboard');
await page.waitForSelector('[data-testid="dashboard-content"]');
const loadTime = Date.now() - start;
expect(loadTime).toBeLessThan(3000);
});
test('应显示所有小部件', async ({ page }) => {
await page.goto('/dashboard');
await expect(page.locator('[data-testid="stats-widget"]')).toBeVisible();
await expect(page.locator('[data-testid="chart-widget"]')).toBeVisible();
await expect(page.locator('[data-testid="activity-widget"]')).toBeVisible();
});
test('应在点击按钮时刷新数据', async ({ page }) => {
await page.goto('/dashboard');
const initialValue = await page.locator('[data-testid="last-updated"]').textContent();
await page.click('[data-testid="refresh-button"]');
await page.waitForTimeout(1000);
const newValue = await page.locator('[data-testid="last-updated"]').textContent();
expect(newValue).not.toBe(initialValue);
});
});
// tests/users/create.spec.ts
import { test, expect } from '@playwright/test';
import { login } from '../helpers/auth';
import { generateTestUser, deleteTestUser } from '../helpers/users';
test.describe('用户创建', () => {
let testUser: { email: string; name: string };
test.beforeEach(async ({ page }) => {
await login(page);
testUser = generateTestUser();
});
test.afterEach(async () => {
// 清理
await deleteTestUser(testUser.email);
});
test('应成功创建新用户', async ({ page }) => {
await page.goto('/users/new');
await page.fill('[data-testid="user-name"]', testUser.name);
await page.fill('[data-testid="user-email"]', testUser.email);
await page.selectOption('[data-testid="user-role"]', 'member');
await page.click('[data-testid="create-user-btn"]');
// 验证成功
await expect(page.locator('[data-testid="success-toast"]')).toBeVisible();
await expect(page).toHaveURL(/users/);
// 验证用户出现在列表中
await expect(page.locator(`text=${testUser.email}`)).toBeVisible();
});
test('应验证必填字段', async ({ page }) => {
await page.goto('/users/new');
await page.click('[data-testid="create-user-btn"]');
await expect(page.locator('[data-testid="name-error"]')).toBeVisible();
await expect(page.locator('[data-testid="email-error"]')).toBeVisible();
});
});
// tests/helpers/auth.ts
import { Page } from '@playwright/test';
export async function login(page: Page) {
await page.goto('/login');
await page.fill('[data-testid="email"]', process.env.TEST_EMAIL!);
await page.fill('[data-testid="password"]', process.env.TEST_PASSWORD!);
await page.click('[data-testid="submit"]');
await page.waitForURL(/dashboard/);
}
export async function logout(page: Page) {
await page.click('[data-testid="user-menu"]');
await page.click('[data-testid="logout"]');
await page.waitForURL(/login/);
}
// tests/helpers/users.ts
export function generateTestUser() {
const id = Date.now();
return {
name: `Test User ${id}`,
email: `test-${id}@example.com`,
};
}
export async function deleteTestUser(email: string) {
// 调用 API 清理测试用户
await fetch(`${process.env.API_URL}/admin/users`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.ADMIN_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
}
// tests/regression.spec.ts
import { test } from '@playwright/test';
// 导入所有测试套件
import './auth/login.spec';
import './auth/logout.spec';
import './dashboard/load.spec';
import './users/create.spec';
import './users/delete.spec';
test.describe('完整回归套件', () => {
// 测试按上面定义的顺序运行
});
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html'],
['json', { outputFile: 'test-results.json' }],
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
# 运行所有测试
npx playwright test
# 运行特定测试文件
npx playwright test tests/auth/login.spec.ts
# 在 UI 模式下运行测试
npx playwright test --ui
# 在 headed 模式下运行(可见浏览器)
npx playwright test --headed
# 生成报告
npx playwright show-report
# .github/workflows/regression.yml
name: 回归测试
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * *' # 每天上午 6 点
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: 安装依赖
run: npm ci
- name: 安装 Playwright
run: npx playwright install --with-deps
- name: 运行测试
run: npx playwright test
env:
BASE_URL: ${{ secrets.STAGING_URL }}
TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
waitForSelector 而不是 waitForTimeout| 任务 | 命令 |
|---|---|
| 运行全部 | npx playwright test |
| 运行单个文件 | npx playwright test login.spec.ts |
| 调试模式 | npx playwright test --debug |
| UI 模式 | npx playwright test --ui |
| 更新快照 | npx playwright test --update-snapshots |
每周安装次数
118
仓库
GitHub 星标数
957
首次出现
2026年1月20日
安全审计
已安装于
gemini-cli95
opencode95
codex90
claude-code81
github-copilot81
cursor77
Build and run automated regression tests using Playwright. Each test is a reusable skill that can be composed into full test suites.
npm init -y
npm install playwright @playwright/test
npx playwright install
Create tests in tests/ folder:
tests/
├── auth/
│ ├── login.spec.ts
│ └── logout.spec.ts
├── dashboard/
│ └── load.spec.ts
├── users/
│ ├── create.spec.ts
│ └── delete.spec.ts
└── regression.spec.ts # Full suite
// tests/auth/login.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Login Flow', () => {
test('should login with valid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="email"]', process.env.TEST_EMAIL!);
await page.fill('[data-testid="password"]', process.env.TEST_PASSWORD!);
await page.click('[data-testid="submit"]');
// Verify redirect to dashboard
await expect(page).toHaveURL(/dashboard/);
await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
});
test('should show error for invalid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="email"]', 'wrong@example.com');
await page.fill('[data-testid="password"]', 'wrongpassword');
await page.click('[data-testid="submit"]');
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
});
});
// tests/dashboard/load.spec.ts
import { test, expect } from '@playwright/test';
import { login } from '../helpers/auth';
test.describe('Dashboard', () => {
test.beforeEach(async ({ page }) => {
await login(page);
});
test('should load dashboard within 3 seconds', async ({ page }) => {
const start = Date.now();
await page.goto('/dashboard');
await page.waitForSelector('[data-testid="dashboard-content"]');
const loadTime = Date.now() - start;
expect(loadTime).toBeLessThan(3000);
});
test('should display all widgets', async ({ page }) => {
await page.goto('/dashboard');
await expect(page.locator('[data-testid="stats-widget"]')).toBeVisible();
await expect(page.locator('[data-testid="chart-widget"]')).toBeVisible();
await expect(page.locator('[data-testid="activity-widget"]')).toBeVisible();
});
test('should refresh data on button click', async ({ page }) => {
await page.goto('/dashboard');
const initialValue = await page.locator('[data-testid="last-updated"]').textContent();
await page.click('[data-testid="refresh-button"]');
await page.waitForTimeout(1000);
const newValue = await page.locator('[data-testid="last-updated"]').textContent();
expect(newValue).not.toBe(initialValue);
});
});
// tests/users/create.spec.ts
import { test, expect } from '@playwright/test';
import { login } from '../helpers/auth';
import { generateTestUser, deleteTestUser } from '../helpers/users';
test.describe('User Creation', () => {
let testUser: { email: string; name: string };
test.beforeEach(async ({ page }) => {
await login(page);
testUser = generateTestUser();
});
test.afterEach(async () => {
// Cleanup
await deleteTestUser(testUser.email);
});
test('should create new user successfully', async ({ page }) => {
await page.goto('/users/new');
await page.fill('[data-testid="user-name"]', testUser.name);
await page.fill('[data-testid="user-email"]', testUser.email);
await page.selectOption('[data-testid="user-role"]', 'member');
await page.click('[data-testid="create-user-btn"]');
// Verify success
await expect(page.locator('[data-testid="success-toast"]')).toBeVisible();
await expect(page).toHaveURL(/users/);
// Verify user appears in list
await expect(page.locator(`text=${testUser.email}`)).toBeVisible();
});
test('should validate required fields', async ({ page }) => {
await page.goto('/users/new');
await page.click('[data-testid="create-user-btn"]');
await expect(page.locator('[data-testid="name-error"]')).toBeVisible();
await expect(page.locator('[data-testid="email-error"]')).toBeVisible();
});
});
// tests/helpers/auth.ts
import { Page } from '@playwright/test';
export async function login(page: Page) {
await page.goto('/login');
await page.fill('[data-testid="email"]', process.env.TEST_EMAIL!);
await page.fill('[data-testid="password"]', process.env.TEST_PASSWORD!);
await page.click('[data-testid="submit"]');
await page.waitForURL(/dashboard/);
}
export async function logout(page: Page) {
await page.click('[data-testid="user-menu"]');
await page.click('[data-testid="logout"]');
await page.waitForURL(/login/);
}
// tests/helpers/users.ts
export function generateTestUser() {
const id = Date.now();
return {
name: `Test User ${id}`,
email: `test-${id}@example.com`,
};
}
export async function deleteTestUser(email: string) {
// API call to cleanup test user
await fetch(`${process.env.API_URL}/admin/users`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.ADMIN_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
}
// tests/regression.spec.ts
import { test } from '@playwright/test';
// Import all test suites
import './auth/login.spec';
import './auth/logout.spec';
import './dashboard/load.spec';
import './users/create.spec';
import './users/delete.spec';
test.describe('Full Regression Suite', () => {
// Tests run in order defined above
});
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html'],
['json', { outputFile: 'test-results.json' }],
],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
# Run all tests
npx playwright test
# Run specific test file
npx playwright test tests/auth/login.spec.ts
# Run tests with UI
npx playwright test --ui
# Run in headed mode (see browser)
npx playwright test --headed
# Generate report
npx playwright show-report
# .github/workflows/regression.yml
name: Regression Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * *' # Daily at 6 AM
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run tests
run: npx playwright test
env:
BASE_URL: ${{ secrets.STAGING_URL }}
TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
waitForSelector instead of waitForTimeout| Task | Command |
|---|---|
| Run all | npx playwright test |
| Run one file | npx playwright test login.spec.ts |
| Debug mode | npx playwright test --debug |
| UI mode | npx playwright test --ui |
| Update snapshots | npx playwright test --update-snapshots |
Weekly Installs
118
Repository
GitHub Stars
957
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
gemini-cli95
opencode95
codex90
claude-code81
github-copilot81
cursor77
Skills CLI 使用指南:AI Agent 技能包管理器安装与管理教程
40,000 周安装