Add Playwright E2E testing with recorded workflows
- Add @playwright/test as dev dependency - Create playwright.config.ts with Chrome-only testing config - Add npm scripts: test:record, test:e2e, test:replay - Create 13 test recordings covering: - 2-player and 3-player games - 8-ball, 9-ball, and 10-ball game types - Various race-to values (1, 3, 5, 7, 9) and "endlos" mode - Both wechselbreak (alternating) and winnerbreak rules - Fix Infinity handling in gameService.ts and NewGameScreen.tsx - Parse "endlos" and "Infinity" strings as Infinity number - Properly serialize Infinity as string in form data - Increase GameDetail score font size from 20vh to 40vh - Update README.md with testing documentation: - Quick start guide for recording and running tests - Move E2E testing from "Future Improvements" (now implemented) - Add comprehensive tests/recordings/README.md documentation Purpose: Establishes browser automation testing infrastructure with real workflow recordings, enabling regression testing and interaction documentation for all game configuration combinations.
This commit is contained in:
37
README.md
37
README.md
@@ -77,8 +77,42 @@ npm run dev
|
|||||||
npm run dev # Start development server
|
npm run dev # Start development server
|
||||||
npm run build # Build for production
|
npm run build # Build for production
|
||||||
npm run preview # Preview production build
|
npm run preview # Preview production build
|
||||||
|
npm run test:record # Record browser interactions with Playwright
|
||||||
|
npm run test:e2e # Run all recorded browser automation scripts
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🧪 Testing
|
||||||
|
|
||||||
|
The project uses **Playwright** for browser automation and recording. This allows you to record interactions once and replay them anytime, making it easy to test repetitive workflows.
|
||||||
|
|
||||||
|
### Quick Start
|
||||||
|
|
||||||
|
**Recording interactions:**
|
||||||
|
```bash
|
||||||
|
# Terminal 1: Start dev server
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Terminal 2: Start recording
|
||||||
|
npm run test:record
|
||||||
|
```
|
||||||
|
|
||||||
|
**Running recordings:**
|
||||||
|
```bash
|
||||||
|
npm run test:e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- **Record interactions**: Use Playwright codegen to capture clicks, form fills, and navigation
|
||||||
|
- **Replay scripts**: Run recorded scripts automatically
|
||||||
|
- **Duplicate & modify**: Copy any script and modify it (e.g., change the last step from clicking 'z' to clicking 'a')
|
||||||
|
- **Full scripting power**: Edit generated TypeScript files directly for custom automation
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
For detailed instructions on recording, modifying, and running scripts, see:
|
||||||
|
- **[tests/recordings/README.md](tests/recordings/README.md)** - Complete workflow documentation
|
||||||
|
|
||||||
## 📁 Project Structure
|
## 📁 Project Structure
|
||||||
|
|
||||||
### **Core Components**
|
### **Core Components**
|
||||||
@@ -199,8 +233,7 @@ The application includes PWA features:
|
|||||||
|
|
||||||
## 📈 Future Improvements
|
## 📈 Future Improvements
|
||||||
|
|
||||||
- Unit and integration testing with Vitest
|
- Unit and integration testing (if needed)
|
||||||
- E2E testing with Playwright
|
|
||||||
- Internationalization (i18n) support
|
- Internationalization (i18n) support
|
||||||
- Advanced game statistics and analytics
|
- Advanced game statistics and analytics
|
||||||
- Real-time multiplayer support
|
- Real-time multiplayer support
|
||||||
|
|||||||
73
package-lock.json
generated
73
package-lock.json
generated
@@ -13,7 +13,9 @@
|
|||||||
"preact": "^10.26.8"
|
"preact": "^10.26.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.3"
|
"@playwright/test": "^1.56.1",
|
||||||
|
"@types/node": "^24.0.3",
|
||||||
|
"playwright": "^1.56.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
@@ -146,6 +148,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz",
|
||||||
"integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==",
|
"integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.2.0",
|
"@ampproject/remapping": "^2.2.0",
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
@@ -1266,6 +1269,22 @@
|
|||||||
"integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
|
"integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@playwright/test": {
|
||||||
|
"version": "1.56.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz",
|
||||||
|
"integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "1.56.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@preact/preset-vite": {
|
"node_modules/@preact/preset-vite": {
|
||||||
"version": "2.10.1",
|
"version": "2.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.1.tgz",
|
||||||
@@ -2204,6 +2223,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001718",
|
"caniuse-lite": "^1.0.30001718",
|
||||||
"electron-to-chromium": "^1.5.160",
|
"electron-to-chromium": "^1.5.160",
|
||||||
@@ -4409,6 +4429,53 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.56.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz",
|
||||||
|
"integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.56.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.56.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz",
|
||||||
|
"integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright/node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.4",
|
"version": "8.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz",
|
||||||
@@ -4442,6 +4509,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz",
|
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz",
|
||||||
"integrity": "sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==",
|
"integrity": "sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/preact"
|
"url": "https://opencollective.com/preact"
|
||||||
@@ -4754,6 +4822,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz",
|
||||||
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
|
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.7"
|
"@types/estree": "1.0.7"
|
||||||
},
|
},
|
||||||
@@ -5447,6 +5516,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
@@ -5683,6 +5753,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.51.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.51.tgz",
|
||||||
"integrity": "sha512-TQSnBldh+XSGL+opiSIq0575wvDPqu09AqWe1F7JhUMKY+M91/aGlK4MhpVNO7MgYfHcVCB1ffwAUTJzllKJqg==",
|
"integrity": "sha512-TQSnBldh+XSGL+opiSIq0575wvDPqu09AqWe1F7JhUMKY+M91/aGlK4MhpVNO7MgYfHcVCB1ffwAUTJzllKJqg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
"dev": "astro dev --host",
|
"dev": "astro dev --host",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro"
|
"astro": "astro",
|
||||||
|
"test:record": "playwright codegen http://localhost:3000",
|
||||||
|
"test:e2e": "playwright test",
|
||||||
|
"test:replay": "playwright test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/preact": "^4.1.0",
|
"@astrojs/preact": "^4.1.0",
|
||||||
@@ -14,6 +17,8 @@
|
|||||||
"preact": "^10.26.8"
|
"preact": "^10.26.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.3"
|
"@playwright/test": "^1.56.1",
|
||||||
|
"@types/node": "^24.0.3",
|
||||||
|
"playwright": "^1.56.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
85
playwright-report/index.html
Normal file
85
playwright-report/index.html
Normal file
File diff suppressed because one or more lines are too long
64
playwright.config.ts
Normal file
64
playwright.config.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Playwright configuration for BSC Score browser automation
|
||||||
|
* Configured to work with local dev server on port 3000
|
||||||
|
*/
|
||||||
|
export default defineConfig({
|
||||||
|
// Test directory - where your recorded scripts live
|
||||||
|
testDir: './tests/recordings',
|
||||||
|
|
||||||
|
// Match all .ts files in the recordings directory (not just .test.ts or .spec.ts)
|
||||||
|
testMatch: '**/*.ts',
|
||||||
|
|
||||||
|
// Maximum time one test can run for
|
||||||
|
timeout: 30 * 1000,
|
||||||
|
|
||||||
|
// Test execution settings
|
||||||
|
fullyParallel: false, // Run tests sequentially to avoid conflicts
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
workers: 1, // Run one at a time for recordings
|
||||||
|
|
||||||
|
// Reporter configuration
|
||||||
|
reporter: 'html',
|
||||||
|
|
||||||
|
// Shared settings for all tests
|
||||||
|
use: {
|
||||||
|
// Base URL for tests
|
||||||
|
baseURL: 'http://localhost:3000',
|
||||||
|
|
||||||
|
// Browser context options
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
screenshot: 'only-on-failure',
|
||||||
|
|
||||||
|
// Viewport size
|
||||||
|
viewport: { width: 1280, height: 720 },
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure projects for different browsers
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
// Uncomment to add more browsers
|
||||||
|
// {
|
||||||
|
// name: 'firefox',
|
||||||
|
// use: { ...devices['Desktop Firefox'] },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'webkit',
|
||||||
|
// use: { ...devices['Desktop Safari'] },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
|
||||||
|
// Web server configuration - starts dev server automatically if needed
|
||||||
|
// webServer: {
|
||||||
|
// command: 'npm run dev',
|
||||||
|
// url: 'http://localhost:3000',
|
||||||
|
// reuseExistingServer: !process.env.CI,
|
||||||
|
// timeout: 120 * 1000,
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
color: #222;
|
color: #222;
|
||||||
}
|
}
|
||||||
.score {
|
.score {
|
||||||
font-size: 20vh;
|
font-size: 40vh;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
margin: 20px 0 30px 0;
|
margin: 20px 0 30px 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|||||||
@@ -63,10 +63,12 @@ export default function NewGameScreen({
|
|||||||
onCreateGame(finalData as any);
|
onCreateGame(finalData as any);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRaceToNext = (raceTo: string) => {
|
const handleRaceToNext = (raceTo: string | number) => {
|
||||||
const finalData = { ...data, raceTo };
|
// Convert to string, handling Infinity case explicitly
|
||||||
|
const raceToStr = raceTo === Infinity ? 'Infinity' : String(raceTo);
|
||||||
|
const finalData = { ...data, raceTo: raceToStr };
|
||||||
// After race to, go to break rule selection
|
// After race to, go to break rule selection
|
||||||
onDataChange({ raceTo });
|
onDataChange({ raceTo: raceToStr });
|
||||||
onStepChange('breakRule');
|
onStepChange('breakRule');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -141,9 +141,15 @@ export class GameService {
|
|||||||
throw new Error('Game type is required');
|
throw new Error('Game type is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
const raceTo = parseInt(gameData.raceTo, 10);
|
// Handle "endlos" (Infinity) case - raceTo is stored as string but can be "Infinity"
|
||||||
if (isNaN(raceTo) || raceTo <= 0) {
|
let raceTo: number;
|
||||||
throw new Error('Invalid race to value');
|
if (gameData.raceTo === 'Infinity' || gameData.raceTo === 'endlos' || String(gameData.raceTo).toLowerCase() === 'infinity') {
|
||||||
|
raceTo = Infinity;
|
||||||
|
} else {
|
||||||
|
raceTo = parseInt(gameData.raceTo, 10);
|
||||||
|
if (isNaN(raceTo) || raceTo <= 0) {
|
||||||
|
throw new Error('Invalid race to value');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseGame = {
|
const baseGame = {
|
||||||
|
|||||||
4
test-results/.last-run.json
Normal file
4
test-results/.last-run.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"status": "passed",
|
||||||
|
"failedTests": []
|
||||||
|
}
|
||||||
26
tests/recordings/2-10-ball-endlos-wechsel.ts
Normal file
26
tests/recordings/2-10-ball-endlos-wechsel.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Kim');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Leo');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||||
|
await page.getByRole('button', { name: '10-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Endlos' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Wechselbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Leo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Leo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Kim' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Leo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Kim' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Leo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Kim' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Spiel beenden' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
24
tests/recordings/2-10-ball-race3-wechsel.ts
Normal file
24
tests/recordings/2-10-ball-race3-wechsel.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Charlie');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Diana');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||||
|
await page.getByRole('button', { name: '10-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '3' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Wechselbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Diana' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Diana' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Charlie' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Diana' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Diana' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
29
tests/recordings/2-8-ball-race5-wechsel.ts
Normal file
29
tests/recordings/2-8-ball-race5-wechsel.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Wendy');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Xavier');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||||
|
await page.getByRole('button', { name: '8-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '5' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Wechselbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Xavier' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Xavier' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Wendy' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Xavier' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Wendy' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Xavier' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Wendy' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Xavier' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Wendy' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Xavier' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
31
tests/recordings/2-8-ball-race9-winner.ts
Normal file
31
tests/recordings/2-8-ball-race9-winner.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Mia');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Noah');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||||
|
await page.getByRole('button', { name: '8-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '9' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Winnerbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Noah' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Noah' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Mia' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
26
tests/recordings/2-8-endlos-winner.ts
Normal file
26
tests/recordings/2-8-endlos-winner.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Foo');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Bar');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||||
|
await page.getByRole('button', { name: '8-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Endlos' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Winnerbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Bar' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Bar' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Spiel beenden' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
});
|
||||||
21
tests/recordings/2-9-ball-race1-wechsel.ts
Normal file
21
tests/recordings/2-9-ball-race1-wechsel.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Rita');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Sam');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||||
|
await page.getByRole('button', { name: '9-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '1' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Wechselbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Sam' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Sam' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
26
tests/recordings/2-9-ball-race5-winner.ts
Normal file
26
tests/recordings/2-9-ball-race5-winner.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Alice');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Bob');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||||
|
await page.getByRole('button', { name: '9-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '5' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Winnerbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Alice' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Alice' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Bob' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Alice' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Alice' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Alice' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Alice' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
29
tests/recordings/3-10-ball-race5-winner.ts
Normal file
29
tests/recordings/3-10-ball-race5-winner.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Oscar');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Paula');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Quinn');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: '10-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '5' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Winnerbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Quinn' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Quinn' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Oscar' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Paula' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Quinn' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Quinn' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Quinn' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Quinn' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
29
tests/recordings/3-8-ball-endlos-winner.ts
Normal file
29
tests/recordings/3-8-ball-endlos-winner.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Eva');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Frank');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Grace');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: '8-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Endlos' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Winnerbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Eva' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Eva' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Frank' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Grace' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Eva' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Frank' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Grace' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Eva' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Spiel beenden' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
28
tests/recordings/3-8-ball-race3-wechsel.ts
Normal file
28
tests/recordings/3-8-ball-race3-wechsel.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Tom');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Uma');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Victor');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: '8-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '3' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Wechselbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Tom' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zweites Break: Uma' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Tom' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Uma' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Victor' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Tom' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Tom' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
33
tests/recordings/3-9-ball-race7-wechsel.ts
Normal file
33
tests/recordings/3-9-ball-race7-wechsel.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Henry');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Iris');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Jack');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: '9-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '7' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Wechselbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Iris' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zweites Break: Jack' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Iris' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Henry' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Jack' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Iris' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Henry' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Iris' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Iris' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Iris' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Iris' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Iris' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
147
tests/recordings/README.md
Normal file
147
tests/recordings/README.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# Playwright Recordings
|
||||||
|
|
||||||
|
This directory contains recorded browser interaction scripts for BSC Score. Use Playwright's codegen to record interactions once, then replay them anytime.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Recording Interactions
|
||||||
|
|
||||||
|
1. **Start the dev server** (in a separate terminal):
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Start Playwright codegen**:
|
||||||
|
```bash
|
||||||
|
npm run test:record
|
||||||
|
```
|
||||||
|
|
||||||
|
This opens:
|
||||||
|
- A browser window (use the app normally)
|
||||||
|
- Playwright Inspector (shows generated code in real-time)
|
||||||
|
|
||||||
|
3. **Interact with the app**:
|
||||||
|
- Click buttons, fill forms, navigate
|
||||||
|
- All actions are automatically captured
|
||||||
|
- Code appears in the Playwright Inspector window
|
||||||
|
|
||||||
|
4. **Save your recording**:
|
||||||
|
- Copy the generated code from the Inspector
|
||||||
|
- Create a new `.ts` file in this directory
|
||||||
|
- Paste the code and save (e.g., `create-game-basic.ts`)
|
||||||
|
|
||||||
|
### Running Recordings
|
||||||
|
|
||||||
|
Run all recordings:
|
||||||
|
```bash
|
||||||
|
npm run test:e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
Run a specific recording:
|
||||||
|
```bash
|
||||||
|
npx playwright test tests/recordings/create-game-basic.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with UI mode (helpful for debugging):
|
||||||
|
```bash
|
||||||
|
npx playwright test --ui
|
||||||
|
```
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
Use descriptive names that indicate the flow:
|
||||||
|
- `create-game-basic.ts` - Basic game creation flow
|
||||||
|
- `create-game-3players.ts` - Game creation with 3 players
|
||||||
|
- `score-update.ts` - Score update flow
|
||||||
|
- `undo-action.ts` - Undo functionality
|
||||||
|
|
||||||
|
## Duplicating & Modifying Scripts
|
||||||
|
|
||||||
|
This is the key feature! Copy any script to create a variant:
|
||||||
|
|
||||||
|
1. **Copy a script**:
|
||||||
|
```bash
|
||||||
|
cp create-game-basic.ts create-game-variant.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Modify the copy**:
|
||||||
|
- Change player names
|
||||||
|
- Modify game type
|
||||||
|
- Change the last step (e.g., click 'a' instead of 'z')
|
||||||
|
- Add or remove steps
|
||||||
|
|
||||||
|
3. **Run the variant**:
|
||||||
|
```bash
|
||||||
|
npx playwright test tests/recordings/create-game-variant.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Modification
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Original: create-game-basic.ts
|
||||||
|
await page.click('button:has-text("Option Z")');
|
||||||
|
|
||||||
|
// Modified: create-game-variant.ts
|
||||||
|
await page.click('button:has-text("Option A")'); // changed from Z to A
|
||||||
|
```
|
||||||
|
|
||||||
|
## Script Structure
|
||||||
|
|
||||||
|
All recordings follow this structure:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('description of what this script does', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000');
|
||||||
|
|
||||||
|
// Your recorded interactions here
|
||||||
|
await page.click('text=Neues Spiel');
|
||||||
|
await page.fill('input[name="player1"]', 'Alice');
|
||||||
|
// ... more steps
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- **Use the dev server**: Make sure `npm run dev` is running on port 3000 before recording or running scripts
|
||||||
|
- **Descriptive test names**: The test name helps identify what the script does
|
||||||
|
- **Comments**: Add comments in scripts to explain non-obvious steps
|
||||||
|
- **Wait for navigation**: If something doesn't work, you might need to add `await page.waitForNavigation()` or `await page.waitForSelector()`
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Fill a form field
|
||||||
|
```typescript
|
||||||
|
await page.fill('input[name="fieldName"]', 'value');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Click a button
|
||||||
|
```typescript
|
||||||
|
await page.click('button:has-text("Button Text")');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wait for element
|
||||||
|
```typescript
|
||||||
|
await page.waitForSelector('text=Expected Text');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check element is visible
|
||||||
|
```typescript
|
||||||
|
await expect(page.locator('text=Some Text')).toBeVisible();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Script fails with "element not found"**:
|
||||||
|
- Add wait statements: `await page.waitForSelector('selector')`
|
||||||
|
- Check if the selector changed (use Playwright Inspector to find new selectors)
|
||||||
|
|
||||||
|
**Browser doesn't open during recording**:
|
||||||
|
- Make sure dev server is running on port 3000
|
||||||
|
- Check if port 3000 is available
|
||||||
|
|
||||||
|
**Test runs but doesn't do anything**:
|
||||||
|
- Check that baseURL in `playwright.config.ts` is correct
|
||||||
|
- Verify the app is accessible at `http://localhost:3000`
|
||||||
|
|
||||||
26
tests/recordings/example-flow.ts
Normal file
26
tests/recordings/example-flow.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:3000/');
|
||||||
|
await page.getByRole('button', { name: 'Neues Spiel starten' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Foo');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Name Spieler' }).fill('Bar');
|
||||||
|
await page.getByRole('button', { name: 'Weiter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Überspringen' }).click();
|
||||||
|
await page.getByRole('button', { name: '8-Ball' }).click();
|
||||||
|
await page.getByRole('button', { name: '6' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Break-Regel wählen: Wechselbreak' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zuerst: Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Bar' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Aktueller Punktestand für Foo' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Bestätigen' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Zurück zur Liste' }).click();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user