Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: 🎭 Playwright E2E & Regression Tests

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: 🧪 Run Playwright E2E Tests
timeout-minutes: 15
runs-on: ubuntu-latest

steps:
- name: ⬇️ Checkout Repository
uses: actions/checkout@v4

- name: 🟢 Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: "package.json"
cache: npm
cache-dependency-path: "package-lock.json"

- name: 📦 Install Dependencies
run: npm ci

- name: 🎭 Install Playwright Browsers (Chromium)
run: npx playwright install --with-deps chromium

- name: 🔎 Type Check & Lint
run: |
npm run type-check
npm run lint

- name: 🛠️ Build Static Site Export
run: npm run build

- name: 🚀 Run Playwright End-to-End Tests
run: npm run test:e2e

- name: 📊 Upload Playwright Test Report Artifact
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 14
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

# testing
/coverage
/test-results/
/playwright-report/

# next.js
/.next/
Expand Down
102 changes: 102 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# 🎭 End-to-End & Regression Testing with Playwright

This repository maintains an automated end-to-end (E2E) and regression test suite powered by [Playwright](https://playwright.dev/). The test suite guards user-facing flows and critical business logic against unintended regressions.

---

## 🏗️ Architecture & Design Principles

1. **Production-Faithful Static Export Testing**
Next.js static site generation (`next build` / `npm run build`) generates static HTML/JS output in the `out/` directory. Tests run against this production export served locally via `npx serve out -l 3000 -L` to ensure tests evaluate exact client-side runtime behavior without development-mode artifacts.

2. **Deterministic Offline Testing (Zero Rate Limits)**
The website client-side queries the GitHub API for releases (`https://api.github.com/**`). In the custom test fixture (`tests/fixtures/base-test.ts`), all GitHub API requests are routed and answered with local snapshots (`src/data/raw-releases.json`). This ensures:
- Zero dependency on live internet access during test runs.
- Zero risk of hitting GitHub's unauthenticated IP rate limit (60 requests/hour).
- High speed and 100% deterministic test outcomes.

3. **No Flaky Arbitrary Timeouts**
Tests use Playwright web assertions (`expect(...).toBeVisible()`, `expect(...).toHaveClass(...)`) and locator auto-waiting. Explicit calls to `page.waitForTimeout()` are avoided.

4. **Cross-Platform Device Coverage**
Configured for both Desktop Chrome (`1280x720`) and Mobile Chrome (`Pixel 5` viewport) to validate responsive layouts, desktop navigation, and mobile hamburger drawer interactions.

5. **Visual Regression Policy**
Pixel-by-pixel screenshot comparisons (`toMatchSnapshot`) are intentionally **deferred** to prevent brittle test failures caused by font rendering variations across operating systems (e.g., Linux CI runners vs. Windows/macOS local machines). Instead, tests focus on structural DOM, role-based semantics, ARIA states, and CSS class assertions.

---

## 🚀 Running Tests Locally

### Prerequisites

Ensure project dependencies and Playwright Chromium binaries are installed:

```bash
npm install
npx playwright install chromium
```

Before running tests, ensure the static production export is built:

```bash
npm run build
```

### Test Commands

| Command | Purpose |
| :--- | :--- |
| `npm run test:e2e` | Runs all end-to-end tests headlessly across Desktop and Mobile projects. |
| `npm run test:e2e:ui` | Launches Playwright's interactive UI mode with time-travel debugging and locator inspection. |
| `npm run test:e2e:report` | Opens the generated HTML test results report in your browser. |

You can also run specific test files or projects directly:

```bash
# Run only smoke tests
npx playwright test tests/smoke/

# Run only on Desktop Chrome
npx playwright test --project=chromium

# Run a specific test in debug mode
npx playwright test tests/smoke/homepage.spec.ts --debug
```

---

## 📁 Test Suite Structure

```text
tests/
├── fixtures/
│ └── base-test.ts # Custom Playwright fixture intercepting GitHub API requests & checking console errors
├── functional/
│ ├── changelog-search.spec.ts # Changelog search filter, debouncing, & clear interactions
│ ├── release-filters.spec.ts # Downloads channel tabs (Stable/Beta/Alpha) & release search
│ └── theme.spec.ts # Dark/light theme toggle, html class mutation, & localStorage persistence
└── smoke/
├── changelog.spec.ts # Changelog timeline, v1/v2 deprecation badges, yanked notices, & detail pages
├── community.spec.ts # Community page, contributor cards, "A Guy" Community Manager card integrity
├── downloads.spec.ts # Downloads page, recommended build card, split ABIs, & deprecated/yanked releases
├── footer.spec.ts # Footer design credit (@harsha-vardhan-burra) & navigation links
├── homepage.spec.ts # Homepage hero, tagline, primary CTAs, & section landmarks
└── mobile.spec.ts # Mobile viewport hamburger button, drawer menu, and mobile navigation
```

---

## 🤖 Continuous Integration (CI)

The GitHub Actions workflow (`.github/workflows/playwright.yml`) executes automatically on:
- Every push to `main`
- Every pull request targeting `main`
- Manual workflow dispatch

The CI pipeline runs:
1. `npm run type-check` (TypeScript type integrity)
2. `npm run lint` (ESLint code standards)
3. `npm run build` (Static export generation)
4. `npm run test:e2e` (Playwright test suite execution)
5. Test report upload on failure for artifact inspection
2 changes: 2 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const nextConfig = {
{ protocol: 'https', hostname: 'raw.githubusercontent.com' },
],
},

productionBrowserSourceMaps: true,
};

export default nextConfig;
46 changes: 46 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
"lint": "next lint",
"preview": "npm run build && serve out",
"format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
"type-check": "tsc --noEmit"
"type-check": "tsc --noEmit",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:report": "playwright show-report"
},
"dependencies": {
"@tanstack/react-query": "^5.50.0",
Expand All @@ -26,6 +29,7 @@
"tailwind-merge": "^2.4.0"
},
"devDependencies": {
"@playwright/test": "^1.63.0",
"@types/node": "^20.14.0",
"@types/react": "^18.3.31",
"@types/react-dom": "^18.3.7",
Expand All @@ -38,5 +42,12 @@
"serve": "^14.2.6",
"tailwindcss": "^3.4.0",
"typescript": "^5.5.0"
}
},
"browserslist": [
"last 2 Chrome versions",
"last 2 Firefox versions",
"last 2 Safari versions",
"last 2 Edge versions",
"not dead"
]
}
43 changes: 43 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { defineConfig, devices } from "@playwright/test";

/**
* Playwright E2E testing configuration for Passcodes website.
* Tests the locally built static export for deterministic, network-independent execution.
*/
export default defineConfig({
testDir: "./tests",
timeout: 30 * 1000,
expect: {
timeout: 5000,
},
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
reporter: [
["html", { open: "never" }],
["list"],
],
use: {
baseURL: "http://localhost:3000",
trace: "retain-on-failure",
screenshot: "only-on-failure",
video: "retain-on-failure",
},
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
{
name: "mobile-chrome",
use: { ...devices["Pixel 5"] },
},
],
webServer: {
command: "npx serve out -l 3000 -L",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
},
});
Binary file modified public/komi-store-badge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/komi-store-badge.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/logo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions scripts/sync-releases.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,14 @@ async function main() {
const category = editorial.category || inferCategory(parsedSections);
const isMajor = editorial.isMajor ?? (version.endsWith(".0.0") || releaseType === "Stable");

// Deprecation rule: v1.x and v2.x releases are deprecated (superseded by newer database architecture in later releases).
const majorMatch = version.match(/^v?(\d+)\./i);
const majorVersion = majorMatch ? parseInt(majorMatch[1], 10) : undefined;
const isDeprecated = majorVersion === 1 || majorVersion === 2;
const deprecatedReason = isDeprecated
? "Superseded by the newer database architecture introduced in later releases."
: undefined;

const entry = {
slug,
version,
Expand All @@ -450,6 +458,8 @@ async function main() {
isMajor,
isYanked: isYanked || false,
yankedReason: editorial.yankedReason || undefined,
isDeprecated,
deprecatedReason,
assetsCount: assetsCount > 0 ? assetsCount : undefined,
downloadCount: downloadCount > 0 ? downloadCount : undefined,
};
Expand Down
Loading
Loading