{"id":48569908,"url":"https://github.com/practicajs/test-eyes","last_synced_at":"2026-04-08T14:08:13.655Z","repository":{"id":345280551,"uuid":"1184086282","full_name":"practicajs/test-eyes","owner":"practicajs","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-02T17:41:44.000Z","size":49057,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T03:39:24.190Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/practicajs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-17T08:35:37.000Z","updated_at":"2026-04-02T17:41:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/practicajs/test-eyes","commit_stats":null,"previous_names":["practicajs/test-eyes"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/practicajs/test-eyes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/practicajs%2Ftest-eyes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/practicajs%2Ftest-eyes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/practicajs%2Ftest-eyes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/practicajs%2Ftest-eyes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/practicajs","download_url":"https://codeload.github.com/practicajs/test-eyes/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/practicajs%2Ftest-eyes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31558453,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-08T14:08:13.027Z","updated_at":"2026-04-08T14:08:13.648Z","avatar_url":"https://github.com/practicajs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Test Eyes\n\n[![npm version](https://img.shields.io/npm/v/@practica/test-eyes.svg)](https://www.npmjs.com/package/@practica/test-eyes)\n[![npm version](https://img.shields.io/npm/v/@practica/test-eyes-junit.svg)](https://www.npmjs.com/package/@practica/test-eyes-junit)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n\nTest analytics dashboard that collects test results from CI, aggregates statistics over time, and displays them on GitHub Pages. Track flaky tests, slow tests, and test performance trends across your repositories.\n\n## Features\n\n- **Flaky Test Detection** - Automatically identifies tests that fail and then pass on retry\n- **Performance Tracking** - P95 and average duration metrics for every test\n- **Multiple Views** - Slowest tests, fastest tests, flaky tests, and flaky by category\n- **Real-time Search** - Filter tests instantly by name\n- **GitHub Pages Dashboard** - Automatically deployed, no infrastructure needed\n- **Multiple Input Formats** - Playwright reporter and JUnit XML collector\n\n## Quick Start\n\n### Using with Playwright\n\n1. Install the Playwright reporter:\n\n```bash\nnpm install @practica/test-eyes\n```\n\n2. Add it to your Playwright config:\n\n```typescript\n// playwright.config.ts\nimport { defineConfig } from '@playwright/test';\n\nexport default defineConfig({\n  reporter: [\n    ['list'],\n    ['@practica/test-eyes', {\n      dataBranch: 'gh-data',\n      deployBranch: 'gh-pages',\n      deploy: true\n    }]\n  ]\n});\n```\n\n3. Run your tests in CI - data is automatically collected and dashboard deployed.\n\n### Using with JUnit XML\n\n1. Install the JUnit collector:\n\n```bash\nnpm install @practica/test-eyes-junit\n```\n\n2. Run after your tests:\n\n```bash\nnpx test-eyes-junit ./test-results.xml --deploy\n```\n\n### Using the GitHub Action\n\n```yaml\n# .github/workflows/test.yml\nname: Tests\n\non: [push, pull_request]\n\npermissions:\n  contents: write\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Run tests\n        run: npm test -- --reporter=junit --outputFile=test-results.xml\n\n      - name: Collect and deploy\n        uses: your-org/test-eyes@main\n        with:\n          input-path: test-results.xml\n          data-branch: gh-data\n          deploy-branch: gh-pages\n```\n\n## Packages\n\n| Package | Description | npm |\n|---------|-------------|-----|\n| [`@practica/test-eyes`](./collectors/playwright-reporter) | Playwright reporter - collects test data directly from Playwright | [![npm](https://img.shields.io/npm/v/@practica/test-eyes.svg)](https://www.npmjs.com/package/@practica/test-eyes) |\n| [`@practica/test-eyes-junit`](./collectors/junit) | JUnit collector - parses JUnit XML files from any test runner | [![npm](https://img.shields.io/npm/v/@practica/test-eyes-junit.svg)](https://www.npmjs.com/package/@practica/test-eyes-junit) |\n| `apps/frontend` | React dashboard (Vite + Tailwind + TanStack Table) | - |\n| `apps/test-processing` | Core aggregation and deployment logic | - |\n| `libraries/design-system` | Shared UI components | - |\n\n## Playwright Reporter Options\n\nConfigure the reporter in your `playwright.config.ts`:\n\n```typescript\n['@practica/test-eyes', {\n  dataBranch: 'gh-data',\n  deployBranch: 'gh-pages',\n  deploy: true,\n  prNumber: 123\n}]\n```\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `dataBranch` | `string` | `'gh-data'` | Branch to store test data |\n| `deployBranch` | `string` | `'gh-pages'` | Branch to deploy dashboard |\n| `deploy` | `boolean` | `true` | Whether to deploy dashboard after collecting data |\n| `frontendDistPath` | `string` | auto-detect | Path to frontend dist directory |\n| `prNumber` | `number` | from env | PR number (auto-detected from `GITHUB_PR_NUMBER` or `PR_NUMBER`) |\n\nThe reporter only runs in CI environments (when `CI` or `GITHUB_ACTIONS` is set).\n\n## JUnit Collector CLI Options\n\n```bash\ntest-eyes-junit \u003cjunit-path\u003e [options]\n```\n\n| Option | Default | Description |\n|--------|---------|-------------|\n| `--data-branch \u003cbranch\u003e` | `gh-data` | Branch to store test data |\n| `--commit-sha \u003csha\u003e` | `GITHUB_SHA` or git rev-parse | Git commit SHA |\n| `--pr-number \u003cnumber\u003e` | `GITHUB_PR_NUMBER` or `0` | PR number |\n| `--deploy` | - | Deploy dashboard after collection |\n| `--deploy-branch \u003cbranch\u003e` | `gh-pages` | Branch to deploy to |\n\n### Examples\n\n```bash\n# Basic usage\ntest-eyes-junit ./test-results.xml\n\n# With custom branches\ntest-eyes-junit ./test-results.xml --data-branch test-data --deploy\n\n# In GitHub Actions\ntest-eyes-junit ./test-results.xml --commit-sha $GITHUB_SHA --pr-number $PR_NUMBER --deploy\n```\n\n## GitHub Action Inputs\n\n| Input | Required | Default | Description |\n|-------|----------|---------|-------------|\n| `input-path` | Yes* | - | Path to test results file |\n| `junit-path` | Yes* | - | Alias for `input-path` (backward compatibility) |\n| `input-type` | No | `junit` | Type of input file (`junit`, `playwright`) |\n| `data-branch` | No | `gh-data` | Branch to store test data |\n| `deploy-branch` | No | `gh-pages` | Branch to deploy dashboard |\n\n*Either `input-path` or `junit-path` is required.\n\n## How It Works\n\n### Data Flow\n\n```\nTests Run in CI\n      |\n      v\n+------------------+\n|  Collector       |  (Playwright Reporter or JUnit CLI)\n|  - Parse results |\n|  - Build RunData |\n+------------------+\n      |\n      v\n+------------------+\n|  test-processing |  (Core domain layer)\n|  - Aggregate     |\n|  - Calculate p95 |\n|  - Track flaky   |\n+------------------+\n      |\n      v\n+------------------+\n|  gh-data branch  |  (Raw test data JSON files)\n+------------------+\n      |\n      v\n+------------------+\n|  gh-pages branch |  (Static dashboard)\n+------------------+\n```\n\n### Branch Structure\n\n- **`gh-data`** - Stores raw test run data as JSON files. Each test run creates a new file with the commit SHA and timestamp.\n- **`gh-pages`** - Hosts the static dashboard. Contains the React app and aggregated `main-test-data.json`.\n\n### Test Data Schema\n\nEach test run is stored as:\n\n```typescript\ninterface RunData {\n  runId: string;        // Unique identifier\n  prNumber: number;     // PR number (0 for main branch)\n  commitSha: string;    // Git commit SHA\n  createdAt: string;    // ISO timestamp\n  tests: TestResult[];  // Array of test results\n}\n\ninterface TestResult {\n  name: string;              // Full test name\n  durationMs: number;        // Test duration in milliseconds\n  status: 'passed' | 'failed' | 'skipped';\n  wasFlaky?: boolean;        // True if failed then passed on retry\n  retries?: number;          // Number of retry attempts\n}\n```\n\nAggregated statistics:\n\n```typescript\ninterface TestStats {\n  totalRuns: number;      // Total times this test ran\n  passCount: number;      // Successful runs\n  failCount: number;      // Failed runs\n  flakyCount: number;     // Times marked as flaky\n  avgDurationMs: number;  // Average duration\n  p95DurationMs: number;  // 95th percentile duration\n}\n```\n\n## Dashboard Features\n\nThe dashboard provides four main views:\n\n| View | Description |\n|------|-------------|\n| **Slowest Tests** | Tests sorted by P95 duration (highest first) |\n| **Fastest Tests** | Tests sorted by P95 duration (lowest first) |\n| **Flaky Tests** | Tests with flaky count \u003e 0, sorted by flaky count |\n| **Flaky by Category** | Flaky tests grouped by test file/category |\n\nAll views support real-time search filtering.\n\n## Local Development\n\n```bash\n# Install dependencies\npnpm install\n\n# Start dev servers\npnpm dev\n\n# Build all packages\npnpm build\n\n# Run tests\npnpm test\n\n# Type check\npnpm typecheck\n```\n\n### Project Structure\n\n```\ntest-eyes/\n├── apps/\n│   ├── frontend/           # React dashboard (Vite + Tailwind)\n│   ├── test-processing/    # Core aggregation logic\n│   └── example-app/        # Sample app for testing\n├── collectors/\n│   ├── playwright-reporter/  # @practica/test-eyes\n│   └── junit/                # @practica/test-eyes-junit\n├── libraries/\n│   └── design-system/      # Shared UI components\n└── action.yml              # GitHub Action definition\n```\n\n## CLI Commands\n\n### test-processing CLI\n\n```bash\n# Aggregate test data files\npnpm --filter test-processing cli aggregate ./data\n\n# Deploy dashboard\npnpm --filter test-processing cli deploy --dist-dir ./dist --data-dir ./data\n```\n\n#### Deploy Options\n\n| Option | Short | Default | Description |\n|--------|-------|---------|-------------|\n| `--dist-dir` | `-d` | - | Frontend dist directory (required) |\n| `--data-dir` | - | `data` | Data directory |\n| `--commit-sha` | `-c` | `GITHUB_SHA` | Commit SHA |\n| `--target-branch` | `-t` | `gh-pages` | Target branch |\n\n## GitHub Actions Workflows\n\nThe repository includes several workflow examples:\n\n### collect-test-data.yml\nRuns tests and collects data on PRs:\n- Runs tests with JUnit output\n- Parses results and saves to `gh-data` branch\n\n### deploy-frontend.yml\nDeploys dashboard on push to main:\n- Builds frontend\n- Copies data from `gh-data` branch\n- Deploys to GitHub Pages\n\n### aggregate-and-deploy.yml\nAggregates data and deploys:\n- Fetches all test data files\n- Runs aggregation to compute statistics\n- Deploys updated dashboard\n\n## Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| `CI` | Set to `true` in CI environments |\n| `GITHUB_ACTIONS` | Set automatically by GitHub Actions |\n| `GITHUB_SHA` | Current commit SHA |\n| `GITHUB_PR_NUMBER` | PR number (if applicable) |\n| `PR_NUMBER` | Alternative PR number variable |\n| `GITHUB_REPOSITORY` | Repository in `owner/repo` format |\n\n## Requirements\n\n- Node.js 22+\n- pnpm 9+ (for development)\n- Playwright 1.40+ (for Playwright reporter)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpracticajs%2Ftest-eyes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpracticajs%2Ftest-eyes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpracticajs%2Ftest-eyes/lists"}