Skip to content

Commit 49bd2b2

Browse files
committed
Add e2e tests
1 parent 293fe7c commit 49bd2b2

12 files changed

Lines changed: 1165 additions & 47 deletions

File tree

.github/workflows/pythonpackage.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ jobs:
4545
run: |
4646
npm run build
4747
48+
- name: Install Playwright browsers
49+
run: |
50+
npx playwright install --with-deps chromium
51+
52+
- name: Run e2e tests
53+
run: |
54+
npx playwright test
55+
4856
- name: Deploy to GitHub Pages
4957
if: github.ref == 'refs/heads/master'
5058
uses: crazy-max/ghaction-github-pages@v2

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
.pnp.js
77

88
# testing
9+
test-results
910
/coverage
1011

1112
# production

e2e/app.spec.ts

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { test, expect } from '@playwright/test'
2+
3+
test.describe('MMR-Ranges E2E Tests', () => {
4+
test.beforeEach(async ({ page }) => {
5+
await page.goto('/')
6+
})
7+
8+
test.describe('App Load Tests', () => {
9+
test('header is visible on desktop', async ({ page }) => {
10+
await expect(page.getByTestId('header')).toBeVisible()
11+
await expect(page.getByTestId('header-title')).toContainText('MMR Ranges')
12+
})
13+
14+
test('header is visible on mobile', async ({ page }) => {
15+
await expect(page.getByTestId('header')).toBeVisible()
16+
await expect(page.getByTestId('header-title')).toContainText('MMR Ranges')
17+
})
18+
19+
test('no console errors on desktop', async ({ page }) => {
20+
const errors: string[] = []
21+
page.on('console', (msg) => {
22+
if (msg.type() === 'error') {
23+
errors.push(msg.text())
24+
}
25+
})
26+
await page.goto('/')
27+
await page.waitForLoadState('networkidle')
28+
expect(errors).toHaveLength(0)
29+
})
30+
31+
test('no console errors on mobile', async ({ page }) => {
32+
const errors: string[] = []
33+
page.on('console', (msg) => {
34+
if (msg.type() === 'error') {
35+
errors.push(msg.text())
36+
}
37+
})
38+
await page.goto('/')
39+
await page.waitForLoadState('networkidle')
40+
expect(errors).toHaveLength(0)
41+
})
42+
})
43+
44+
test.describe('Default Tab Tests', () => {
45+
test('1v1 tab is selected by default on desktop', async ({ page }) => {
46+
const tab1v1 = page.getByTestId('tab-1v1')
47+
await expect(tab1v1).toHaveClass(/bg-blue-500/)
48+
// Use first() since there are 5 table elements (one per league)
49+
await expect(page.getByTestId('table').first()).toBeVisible()
50+
})
51+
52+
test('1v1 tab is selected by default on mobile', async ({ page }) => {
53+
const tab1v1 = page.getByTestId('tab-1v1')
54+
await expect(tab1v1).toHaveClass(/bg-blue-500/)
55+
await expect(page.getByTestId('table').first()).toBeVisible()
56+
})
57+
})
58+
59+
test.describe('MMR Tab Navigation', () => {
60+
test('clicking 2v2 tab shows 2v2 table', async ({ page }) => {
61+
await page.getByTestId('tab-2v2').click()
62+
await expect(page.getByTestId('tab-2v2')).toHaveClass(/bg-blue-500/)
63+
await expect(page.getByTestId('tab-1v1')).not.toHaveClass(/bg-blue-500/)
64+
})
65+
66+
test('clicking 3v3 tab shows 3v3 table', async ({ page }) => {
67+
await page.getByTestId('tab-3v3').click()
68+
await expect(page.getByTestId('tab-3v3')).toHaveClass(/bg-blue-500/)
69+
})
70+
71+
test('clicking 4v4 tab shows 4v4 table', async ({ page }) => {
72+
await page.getByTestId('tab-4v4').click()
73+
await expect(page.getByTestId('tab-4v4')).toHaveClass(/bg-blue-500/)
74+
})
75+
76+
test('clicking Archon tab shows Archon table', async ({ page }) => {
77+
await page.getByTestId('tab-archon').click()
78+
await expect(page.getByTestId('tab-archon')).toHaveClass(/bg-blue-500/)
79+
})
80+
81+
test('clicking 1v1 tab shows 1v1 table', async ({ page }) => {
82+
await page.getByTestId('tab-2v2').click()
83+
await page.getByTestId('tab-1v1').click()
84+
await expect(page.getByTestId('tab-1v1')).toHaveClass(/bg-blue-500/)
85+
})
86+
})
87+
88+
test.describe('Statistics Tab Tests', () => {
89+
test('clicking Average games shows region selector', async ({ page }) => {
90+
await page.getByTestId('tab-avg-games').click()
91+
// Use nth(0) since avg_games statistics is the first Statistics component
92+
await expect(page.getByTestId('statistics').nth(0)).toBeVisible()
93+
// Use getByText to find the Americas button within the visible statistics
94+
await expect(page.getByTestId('statistics').nth(0).getByText('Americas')).toBeVisible()
95+
})
96+
97+
test('clicking Total games shows region selector', async ({ page }) => {
98+
await page.getByTestId('tab-total-games').click()
99+
// Use nth(1) since total_games statistics is the second Statistics component
100+
await expect(page.getByTestId('statistics').nth(1)).toBeVisible()
101+
await expect(page.getByTestId('statistics').nth(1).getByText('Europe')).toBeVisible()
102+
})
103+
104+
test('clicking Average winrate shows region selector', async ({ page }) => {
105+
await page.getByTestId('tab-avg-winrate').click()
106+
// Use nth(2) since avg_winrate statistics is the third Statistics component
107+
await expect(page.getByTestId('statistics').nth(2)).toBeVisible()
108+
await expect(page.getByTestId('statistics').nth(2).getByText('Korea')).toBeVisible()
109+
})
110+
})
111+
112+
test.describe('Region Selector Tests', () => {
113+
test.beforeEach(async ({ page }) => {
114+
await page.getByTestId('tab-avg-games').click()
115+
// Wait for visibility before proceeding
116+
await expect(page.getByTestId('statistics').nth(0)).toBeVisible()
117+
})
118+
119+
test('Americas region is selected by default', async ({ page }) => {
120+
await expect(page.getByTestId('statistics').nth(0).getByText('Americas')).toHaveClass(/bg-blue-500/)
121+
await expect(page.getByTestId('table-us').first()).toBeVisible()
122+
})
123+
124+
test('clicking Europe switches to Europe table', async ({ page }) => {
125+
await page.getByTestId('statistics').nth(0).getByText('Europe').click()
126+
await expect(page.getByTestId('statistics').nth(0).getByText('Europe')).toHaveClass(/bg-blue-500/)
127+
await expect(page.getByTestId('table-eu').first()).toBeVisible()
128+
})
129+
130+
test('clicking Korea switches to Korea table', async ({ page }) => {
131+
await page.getByTestId('statistics').nth(0).getByText('Korea').click()
132+
await expect(page.getByTestId('statistics').nth(0).getByText('Korea')).toHaveClass(/bg-blue-500/)
133+
await expect(page.getByTestId('table-kr').first()).toBeVisible()
134+
})
135+
})
136+
137+
test.describe('Tooltip Tests', () => {
138+
test('tooltips appear on hover for Average games', async ({ page }) => {
139+
await page.getByTestId('tab-avg-games').hover()
140+
await expect(page.locator('[data-tip="(total wins + total losses) / player accounts"]')).toBeVisible()
141+
})
142+
143+
test('tooltips appear on hover for Total games', async ({ page }) => {
144+
await page.getByTestId('tab-total-games').hover()
145+
await expect(page.locator('[data-tip="total wins + total losses"]')).toBeVisible()
146+
})
147+
})
148+
149+
test.describe('Footer Tests', () => {
150+
test('footer is visible', async ({ page }) => {
151+
await expect(page.getByTestId('footer')).toBeVisible()
152+
await expect(page.getByTestId('footer-link')).toContainText('Source code')
153+
})
154+
})
155+
})

e2e/playwright.config.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { defineConfig, devices } from '@playwright/test'
2+
3+
export default defineConfig({
4+
testDir: './e2e',
5+
fullyParallel: true,
6+
forbidOnly: !!process.env.CI,
7+
retries: process.env.CI ? 2 : 0,
8+
workers: process.env.CI ? 1 : undefined,
9+
reporter: 'line',
10+
use: {
11+
baseURL: 'http://localhost:3000',
12+
trace: 'on-first-retry',
13+
},
14+
projects: [
15+
{
16+
name: 'chromium',
17+
use: { ...devices['Desktop Chrome'] },
18+
},
19+
{
20+
name: 'Mobile Safari',
21+
use: { ...devices['iPhone 13'] },
22+
},
23+
],
24+
webServer: {
25+
command: 'NODE_OPTIONS=--openssl-legacy-provider npx serve -s build -p 3000',
26+
port: 3000,
27+
timeout: 180 * 1000,
28+
reuseExistingServer: !process.env.CI,
29+
},
30+
})

0 commit comments

Comments
 (0)