All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m12s
- Bumped APP_VERSION to 0.8.125 and updated the changelog to reflect recent changes. - Added new tests for the dashboard API to ensure proper HTTP 200 responses when inner lists are mocked. - Enhanced the ExerciseListBulkToolbar component with a data-testid for improved testing capabilities. - Refactored the TrainingPlanningPage by extracting utility functions to trainingPlanningPageHelpers for better code organization.
137 lines
5.7 KiB
Markdown
137 lines
5.7 KiB
Markdown
# Instructions
|
|
|
|
- Following Playwright test failed.
|
|
- Explain why, be concise, respect Playwright best practices.
|
|
- Provide a snippet of code with the fix, if possible.
|
|
|
|
# Test info
|
|
|
|
- Name: dev-smoke-test.spec.js >> 11. Übungsliste: Massenauswahl zeigt Bulk-Toolbar
|
|
- Location: tests\dev-smoke-test.spec.js:253:1
|
|
|
|
# Error details
|
|
|
|
```
|
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://127.0.0.1:3098/
|
|
Call log:
|
|
- navigating to "http://127.0.0.1:3098/", waiting until "load"
|
|
|
|
```
|
|
|
|
# Test source
|
|
|
|
```ts
|
|
1 | const { test, expect } = require('@playwright/test');
|
|
2 |
|
|
3 | const TEST_EMAIL = process.env.TEST_EMAIL || 'lars@stommer.com';
|
|
4 | const TEST_PASSWORD = process.env.TEST_PASSWORD || '12345678';
|
|
5 |
|
|
6 | /** Primärer Submit auf der Login-Seite (nicht den Tab "Login" vs. "Registrieren"). */
|
|
7 | async function submitLoginForm(page) {
|
|
8 | await page.getByRole('button', { name: 'Anmelden' }).click();
|
|
9 | }
|
|
10 |
|
|
11 | async function login(page) {
|
|
> 12 | await page.goto('/');
|
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://127.0.0.1:3098/
|
|
13 | await page.waitForLoadState('networkidle');
|
|
14 |
|
|
15 | // Warte bis Login-Seite geladen ist
|
|
16 | await page.waitForSelector('input[type="email"]', { timeout: 10000 });
|
|
17 |
|
|
18 | await page.fill('input[type="email"]', TEST_EMAIL);
|
|
19 | await page.fill('input[type="password"]', TEST_PASSWORD);
|
|
20 | await submitLoginForm(page);
|
|
21 | // Wait until auth is complete: URL leaves /login and Dashboard is rendered
|
|
22 | await page.waitForURL((url) => !url.toString().includes('/login'), { timeout: 15000 });
|
|
23 | await page.waitForLoadState('networkidle');
|
|
24 | }
|
|
25 |
|
|
26 | test('1. Login funktioniert', async ({ page }) => {
|
|
27 | await page.goto('/');
|
|
28 | await page.waitForSelector('input[type="email"]', { timeout: 10000 });
|
|
29 | await page.fill('input[type="email"]', TEST_EMAIL);
|
|
30 | await page.fill('input[type="password"]', TEST_PASSWORD);
|
|
31 | await submitLoginForm(page);
|
|
32 | await page.waitForLoadState('networkidle');
|
|
33 |
|
|
34 | // Nach Login soll der Tab "Login" (Moduswahl) verschwinden — nicht der Submit "Anmelden"
|
|
35 | const loginButton = page.locator('button:has-text("Login")');
|
|
36 | await expect(loginButton).toHaveCount(0, { timeout: 10000 });
|
|
37 |
|
|
38 | await page.screenshot({ path: 'screenshots/01-nach-login.png' });
|
|
39 | console.log('✓ Login erfolgreich');
|
|
40 | });
|
|
41 |
|
|
42 | test('2. Dashboard lädt ohne Fehler', async ({ page }) => {
|
|
43 | await login(page);
|
|
44 |
|
|
45 | // Warte bis Spinner verschwunden
|
|
46 | await expect(page.locator('.spinner')).toHaveCount(0, { timeout: 10000 });
|
|
47 |
|
|
48 | // Dashboard: h1 „Dashboard“ + Begrüßungstext (nicht mehr „Willkommen bei Shinkan“ als Überschrift)
|
|
49 | const main = page.locator('.app-main');
|
|
50 | await expect(main.getByRole('heading', { level: 1, name: 'Dashboard' })).toBeVisible({
|
|
51 | timeout: 5000,
|
|
52 | });
|
|
53 | await expect(main.getByText(/Shinkan unterstützt dich/i)).toBeVisible({ timeout: 5000 });
|
|
54 |
|
|
55 | await page.screenshot({ path: 'screenshots/02-dashboard.png' });
|
|
56 | console.log('✓ Dashboard OK');
|
|
57 | });
|
|
58 |
|
|
59 | test('3. Navigation zu Übungen', async ({ page }) => {
|
|
60 | await login(page);
|
|
61 |
|
|
62 | await expect(page.locator('.spinner')).toHaveCount(0, { timeout: 10000 });
|
|
63 |
|
|
64 | // Bei Viewport ≥1024px ist .bottom-nav versteckt — Mobile garantieren wie in playwright.config.js
|
|
65 | await page.setViewportSize({ width: 390, height: 844 });
|
|
66 |
|
|
67 | // Bottom-Nav: Navigation und URL gemeinsam abwarten (vermeidet race mit networkidle)
|
|
68 | const exercisesLink = page.locator('.bottom-nav').getByRole('link', { name: /Übungen/i });
|
|
69 | await Promise.all([
|
|
70 | page.waitForURL(
|
|
71 | (u) => {
|
|
72 | const path = u.pathname.replace(/\/$/, '') || '/'
|
|
73 | return path === '/exercises'
|
|
74 | },
|
|
75 | { timeout: 15000 },
|
|
76 | ),
|
|
77 | exercisesLink.click(),
|
|
78 | ]);
|
|
79 | await page.waitForLoadState('networkidle');
|
|
80 |
|
|
81 | // Wie Test 4 (Vereine): eine eindeutige h1 — nicht h1,h2-Kombi (Strict Mode + mehrere Treffer)
|
|
82 | const main = page.locator('.app-main');
|
|
83 | await expect(main.getByRole('heading', { level: 1, name: /Übungen/i })).toBeVisible({
|
|
84 | timeout: 10000,
|
|
85 | });
|
|
86 |
|
|
87 | await page.screenshot({ path: 'screenshots/03-uebungen.png' });
|
|
88 | console.log('✓ Übungen-Seite erreichbar');
|
|
89 | });
|
|
90 |
|
|
91 | test('4. Navigation zu Vereine', async ({ page }) => {
|
|
92 | await login(page);
|
|
93 | await page.setViewportSize({ width: 390, height: 844 });
|
|
94 |
|
|
95 | await page.locator('.bottom-nav a[href="/clubs"]').click();
|
|
96 | await page.waitForLoadState('networkidle');
|
|
97 |
|
|
98 | // ClubsPage: <h1>Vereinsverwaltung</h1> + Tab <h2>Vereine</h2> → ein kombinierter
|
|
99 | // Selektor löst 2 Treffer aus (Playwright strict mode). URL + primäre Überschrift reichen.
|
|
100 | await expect(page).toHaveURL(/\/clubs\/?$/, { timeout: 5000 });
|
|
101 | await expect(page.getByRole('heading', { level: 1, name: /Vereinsverwaltung/i })).toBeVisible({
|
|
102 | timeout: 5000,
|
|
103 | });
|
|
104 |
|
|
105 | await page.screenshot({ path: 'screenshots/04-vereine.png' });
|
|
106 | console.log('✓ Vereine-Seite erreichbar');
|
|
107 | });
|
|
108 |
|
|
109 | test('5. Desktop-Sidebar sichtbar (Desktop)', async ({ page }) => {
|
|
110 | // Desktop-Viewport
|
|
111 | await page.setViewportSize({ width: 1280, height: 800 });
|
|
112 |
|
|
``` |