api-review
Produce the "Playwright 1.xx API Review" artifact for a range of commits - the public API additions and the breaking or behavior…
Query Playwright CI test results from the aggregated DuckDB database. Answers questions about flaky tests, failure rates, slow tests, and per-run/SHA/PR results without hunting through GitHub artifacts.
Claude Code
$ npx -y skills add microsoft/playwright --skill playwright-test-results --agent claude-code
Codex
$ npx skills add microsoft/playwright --skill playwright-test-results -a codex -y
Cursor
$ npx skills add microsoft/playwright --skill playwright-test-results -a cursor -y
Gemini CLI
$ npx skills add microsoft/playwright --skill playwright-test-results -a gemini-cli -y
GitHub Copilot
$ npx skills add microsoft/playwright --skill playwright-test-results -a github-copilot -y
OpenCode
$ npx skills add microsoft/playwright --skill playwright-test-results -a opencode -y
Windsurf
$ npx skills add microsoft/playwright --skill playwright-test-results -a windsurf -y
name: playwright-test-results description: Query Playwright CI test results from the aggregated DuckDB database. Answers questions about flaky tests, failure rates, slow tests, and per-run/SHA/PR results without hunting through GitHub artifacts. user_invocable: true
A single DuckDB file holds recent Playwright CI test results, so you can answer questions about failures, flakiness, and slow tests with plain SQL. It is refreshed every few hours.
Download the latest snapshot and top it up with the newest runs:
npm ci # first time only, from the repo root GITHUB_TOKEN=$(gh auth token) node utils/test-results-db/cli.ts update --lookback-days 3
`update` downloads the snapshot first when there is no local database, then ingests only the artifacts the snapshot lacks. Never delete the database to rebuild it: every artifact costs one GitHub API request, the budget is shared by every workflow in the repository, and a rebuild of a few days exhausts it. To only refresh the snapshot without ingesting, run `download`.
Query it through the bundled `@duckdb/node-api` binding — no separate DuckDB install needed, it ships in `node_modules` after `npm ci`:
node --input-type=module -e '
import { DuckDBInstance } from "@duckdb/node-api";
const conn = await (await DuckDBInstance.create("utils/test-results-db/test-results.duckdb")).connect();
console.table((await conn.runAndReadAll(process.argv[1])).getRowObjectsJson());
' "SELECT count(*) FROM test_results"Integer columns come back as strings (JSON-safe), so do ranking and filtering in SQL, not in JS.
Single table `test_results`, one row per test result (**one row per retry**). The columns are inferred from the parquet the reporter emits (`tests/config/parquetReporter.ts`), plus two trailing columns this CLI adds:
| Column | Meaning | | --- | --- | | `run_id`, `run_attempt` | GitHub Actions run identity | | `run_started_at` | when the run started | | `workflow_name` | e.g. `tests 1` / `tests 2` / `tests others` / `MCP` | | `event` | `push` / `pull_request` | | `head_sha`, `head_branch`, `pr_number` | what was tested | | `bot_name` | e.g. `chromium-ubuntu-22.04-node20`, `webkit-macos-15-large` — the CI bot. **OS and arch are encoded here**; there is no separate os column. | | `project_name` | CI project = browser + suite, e.g. `chromium-page`, `webkit-library`, `playwright-test` | | `test_title` | title path within the file, joined by ` › ` (`describe › test`) | | `file`, `line`, `column_number` | source location (file is relative to repo root) | | `expected_status` | `passed` / `skipped` / ... | | `status` | actual result: `passed` / `failed` / `timedOut` / `skipped` / `interrupted` | | `retry` | 0 = first attempt | | `result_started_at` | when this attempt started | | `duration_ms` | result duration | | `error_message` | all errors joined, ANSI-stripped (NULL when none) | | `tags` | **list** of strings, e.g. `['@slow', '@flaky']` (use list functions / `list_contains`) | | `annotations` | list of `{type, description}` structs, e.g. `[{'type': 'skip', 'description': 'flaky on CI'}]` (empty list when none) | | `artifact_id` | the GitHub artifact this row came from (dedupe key) | | `ingested_at` | debug only — when this row was imported |
Notes:
tuple. (Playwright's `test_id` hash is deliberately not stored; those three columns are its pre-image.)
**cross-run**: a test whose *final* verdict (after retries) flips between runs — green in some, red in others. A separate **within-run** flake is a test a retry rescued inside a single run (`failed`→`passed`).
Tests marked `test.fail()` record `status='failed'` *with* `expected_status='failed'` and would otherwise dominate any "most failing" list.
time, so it holds a recent window, not full history.
Group tests by `(project_name, file, test_title)` and (for failure/flakiness) scope to `expected_status = 'passed'` so intentional `test.fail()` tests don't skew the results.
**Flaky across runs** — the test's final verdict flips between runs (this is what makes a red CI run ambiguous). `least(failed_runs, passed_runs)` ranks genuinely bimodal tests above both always-broken and one-off failures:
WITH per_run AS (
SELECT project_name, file, test_title, run_id, run_attempt,
arg_max(status, retry) AS final_status,
any_value(expected_status) AS expected
FROM test_results
GROUP BY project_name, file, test_title, run_id, run_attempt)
SELECT project_name, test_title,
count(*) AS runs,
count(*) FILTER (WHERE final_status IN ('failed','timedOut')) AS failed_runs,
count(*) FILTER (WHERE final_status = 'passed') AS passed_runs,
round(100.0 * count(*) FILTER (WHERE final_status IN ('failed','timedOut'))
/ count(*), 1) AS fail_pct
FROM per_run
WHERE expected = 'passed'
GROUP BY project_name, test_title
HAVING failed_runs > 0 AND passed_runs > 0 AND runs >= 10
ORDER BY least(failed_runs, passed_runs) DESC, failed_runs DESC
LIMIT 20;**Filter by tag** (`tags` is a list, not a string):
SELECT project_name, test_title, count(*) AS runs FROM test_results WHERE list_contains(tags, '@slow') GROUP BY project_name, test_title ORDER BY runs DESC LIMIT 20;
For a compact result that drops straight into a GitHub comment, render each final run verdict as a linked square. Edit the four test identity fields, then run:
node --input-type=module <<'EOF'
import { DuckDBInstance } from "@duckdb/node-api";
const repository = "microsoft/playwright"Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
Produce the "Playwright 1.xx API Review" artifact for a range of commits - the public API additions and the breaking or behavior…
Roll the runtime dependencies that npm audit flags with a moderate or higher advisory to safe versions that are at least 8 days…
Cherry-pick a commit or PR from main into a release branch (release-<version>) and retitle it as cherry-pick(#<pr>): <original…
Explains how to develop Playwright - add APIs, MCP tools, CLI commands, and vendor dependencies.
DevOps workflows for Playwright - CI failure analysis, workflow debugging, and release operations.
Write the release notes for a Playwright release - the Node.js notes in docs/src/release-notes-js.md together with the Python,…
Write, refine, run, and QA promptfoo evaluation suites: promptfooconfig.yaml, prompts, providers, vars, tests, assertions,…
DeepEval evaluation workflow for AI agents and LLM applications. TRIGGER when the user wants to evaluate or improve an AI agent,…
Development workflows for the playwright-cli repository. Use when the user asks about rolling dependencies, releasing, or other…