Skip to content

Commit e65e0a8

Browse files
authored
Add e2e tets (#881)
* WIP * Add e2e tests * Prettier * Tests * CI two in parallel
1 parent ea8fff2 commit e65e0a8

30 files changed

+1713
-6
lines changed

.github/workflows/tests.yaml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,63 @@ jobs:
5858
uses: codecov/codecov-action@v4
5959
env:
6060
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
61+
62+
e2e-tests:
63+
name: E2E Tests
64+
runs-on: ubuntu-latest
65+
timeout-minutes: 30
66+
67+
concurrency:
68+
group: e2e-tests-${{ github.ref }}
69+
cancel-in-progress: true
70+
71+
steps:
72+
- name: Checkout Repo
73+
uses: actions/checkout@v4
74+
- name: Setup Node
75+
uses: actions/setup-node@v4
76+
with:
77+
node-version: "22.x"
78+
cache: "yarn"
79+
- name: Cache node modules
80+
uses: actions/cache@v4
81+
env:
82+
cache-name: cache-node-modules-v2
83+
with:
84+
path: |
85+
node_modules
86+
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('yarn.lock') }}
87+
restore-keys: |
88+
${{ runner.os }}-build-${{ env.cache-name }}-
89+
- name: Cache NextJS Build
90+
uses: actions/cache@v4
91+
env:
92+
cache-name: next-build-e2e
93+
with:
94+
path: |
95+
${{ github.workspace }}/.next/cache
96+
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('yarn.lock') }}-${{ hashFiles('components/**/*.tsx','components/**/*.ts', 'pages/**/*.tsx','pages/**/*.ts', 'src/**/*.tsx','src/**/*.ts' )}}
97+
restore-keys: |
98+
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('yarn.lock') }}
99+
- name: Install dependencies
100+
run: yarn --prefer-offline --frozen-lockfile install
101+
- name: Install Playwright Browsers
102+
run: yarn test:e2e:install
103+
- name: Build application
104+
run: yarn build:slim
105+
- name: Run Playwright tests
106+
run: yarn test:e2e
107+
- name: Upload Playwright Report
108+
uses: actions/upload-artifact@v4
109+
if: always()
110+
with:
111+
name: playwright-report
112+
path: playwright-report/
113+
retention-days: 30
114+
- name: Upload test results
115+
uses: actions/upload-artifact@v4
116+
if: failure()
117+
with:
118+
name: playwright-results
119+
path: test-results/
120+
retention-days: 7

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ unit-paths.txt
1212
# testing
1313
/coverage
1414

15+
# playwright
16+
/test-results/
17+
/playwright-report/
18+
/playwright/.cache/
19+
1520
# next.js
1621
/.next/
1722
/out/

e2e/README.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# End-to-End Tests
2+
3+
This directory contains end-to-end (e2e) tests for the COH3 Stats application using Playwright.
4+
5+
## Setup
6+
7+
### Install Playwright
8+
9+
First, install Playwright and its dependencies:
10+
11+
```bash
12+
yarn add -E -D @playwright/test@1.49.1
13+
```
14+
15+
Then install the Playwright browsers:
16+
17+
```bash
18+
npx playwright install
19+
```
20+
21+
Or install with dependencies (recommended for CI):
22+
23+
```bash
24+
npx playwright install --with-deps
25+
```
26+
27+
## Running Tests
28+
29+
### Run all tests
30+
31+
```bash
32+
yarn test:e2e
33+
```
34+
35+
### Run tests in UI mode (interactive)
36+
37+
```bash
38+
yarn test:e2e:ui
39+
```
40+
41+
### Run tests in headed mode (see browser)
42+
43+
```bash
44+
yarn test:e2e:headed
45+
```
46+
47+
### Run specific test file
48+
49+
```bash
50+
npx playwright test e2e/smoke/home.spec.ts
51+
```
52+
53+
### Run tests for specific browser
54+
55+
```bash
56+
npx playwright test --project=chromium
57+
npx playwright test --project=firefox
58+
npx playwright test --project=webkit
59+
```
60+
61+
## Test Structure
62+
63+
```
64+
e2e/
65+
├── helpers/
66+
│ └── test-utils.ts # Common test utilities and helper functions
67+
├── smoke/
68+
│ ├── home.spec.ts # Home page tests
69+
│ ├── leaderboards.spec.ts # Leaderboards tests
70+
│ ├── search.spec.ts # Search page tests
71+
│ ├── about.spec.ts # About page tests
72+
│ ├── news.spec.ts # News page tests
73+
│ ├── stats.spec.ts # Stats pages tests
74+
│ ├── explorer.spec.ts # Explorer pages tests
75+
│ ├── other-pages.spec.ts # Other static pages tests
76+
│ └── dynamic-routes.spec.ts # Dynamic route tests
77+
└── README.md # This file
78+
```
79+
80+
## Writing Tests
81+
82+
### Basic Test Structure
83+
84+
```typescript
85+
import { test, expect } from "@playwright/test";
86+
import { navigateAndWait, checkPageLoaded } from "../helpers/test-utils";
87+
88+
test.describe("Feature Name", () => {
89+
test("should do something", async ({ page }) => {
90+
await navigateAndWait(page, "/path");
91+
await checkPageLoaded(page);
92+
93+
// Your test assertions
94+
await expect(page.locator("selector")).toBeVisible();
95+
});
96+
});
97+
```
98+
99+
### Helper Functions
100+
101+
- `navigateAndWait(page, path)` - Navigate to a path and wait for page load
102+
- `checkPageLoaded(page)` - Verify page loaded without errors
103+
- `checkFooterPresent(page)` - Verify footer is present
104+
105+
## Configuration
106+
107+
The Playwright configuration is in `playwright.config.ts` at the root of the project.
108+
109+
Key settings:
110+
111+
- **Base URL**: `http://localhost:3000` (configurable via `PLAYWRIGHT_BASE_URL` env var)
112+
- **Test Directory**: `./e2e`
113+
- **Browsers**: Chromium, Firefox, WebKit, Mobile Chrome, Mobile Safari
114+
- **Retries**: 2 retries on CI, 0 locally
115+
- **Workers**: 1 worker on CI, parallel locally
116+
- **Web Server**: Automatically starts `yarn start` before tests
117+
118+
## CI/CD Integration
119+
120+
Tests are automatically run in GitHub Actions on:
121+
122+
- Pull requests to master
123+
- Pushes to master
124+
125+
See `.github/workflows/tests.yaml` for the CI configuration.
126+
127+
## Debugging
128+
129+
### View test report
130+
131+
After running tests, view the HTML report:
132+
133+
```bash
134+
npx playwright show-report
135+
```
136+
137+
### Debug specific test
138+
139+
```bash
140+
npx playwright test --debug e2e/smoke/home.spec.ts
141+
```
142+
143+
### View traces
144+
145+
Traces are automatically captured on first retry. View them with:
146+
147+
```bash
148+
npx playwright show-trace trace.zip
149+
```
150+
151+
## Best Practices
152+
153+
1. **Use data-testid sparingly** - Prefer semantic selectors (role, text, label)
154+
2. **Wait for elements** - Use Playwright's auto-waiting features
155+
3. **Keep tests independent** - Each test should be able to run in isolation
156+
4. **Use descriptive test names** - Make it clear what the test is checking
157+
5. **Group related tests** - Use `test.describe()` to organize tests
158+
6. **Handle dynamic content** - Use appropriate timeouts for API-dependent content
159+
160+
## Troubleshooting
161+
162+
### Tests timing out
163+
164+
- Increase timeout in test: `test.setTimeout(60000)`
165+
- Check if the dev server is running
166+
- Verify network connectivity
167+
168+
### Flaky tests
169+
170+
- Add explicit waits for dynamic content
171+
- Use `waitForLoadState('networkidle')`
172+
- Check for race conditions
173+
174+
### Browser not installed
175+
176+
Run: `npx playwright install`

e2e/helpers/test-utils.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { expect, Page } from "@playwright/test";
2+
3+
/**
4+
* Wait for the page to be fully loaded and hydrated
5+
*/
6+
export const waitForPageLoad = async (page: Page) => {
7+
// Wait for Next.js to be ready
8+
await page.waitForLoadState("networkidle");
9+
// Wait for React hydration
10+
await page.waitForFunction(() => document.readyState === "complete");
11+
};
12+
13+
/**
14+
* Check if the page has loaded successfully without errors
15+
*/
16+
export const checkPageLoaded = async (page: Page) => {
17+
// Check that we're not on an error page
18+
await expect(page.locator("text=Application error")).not.toBeVisible();
19+
// Check for 404 error in page title (more specific than just "text=404")
20+
await expect(page.locator("h1:has-text('404')")).not.toBeVisible();
21+
22+
// Check that the header is present (common across all pages)
23+
await expect(page.locator("header")).toBeVisible();
24+
};
25+
26+
/**
27+
* Check if the footer is present on the page
28+
*/
29+
export const checkFooterPresent = async (page: Page) => {
30+
await expect(page.locator("footer")).toBeVisible();
31+
};
32+
33+
/**
34+
* Navigate to a route and wait for it to load
35+
*/
36+
export const navigateAndWait = async (page: Page, path: string) => {
37+
await page.goto(path);
38+
await waitForPageLoad(page);
39+
};

0 commit comments

Comments
 (0)