test(e2e): add Playwright with browser tests for login, logo, dashboard
This commit is contained in:
+7
-4
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"lastRun": "2026-03-01T04:00:00+01:00",
|
||||
"status": "completed",
|
||||
"lastRun": "2026-03-01T08:44:00+01:00",
|
||||
"status": "in_progress",
|
||||
"phase": "04-workout-modification",
|
||||
"activeTask": "04-02-backend-api",
|
||||
"activeTask": "04-03-frontend-workout-edit",
|
||||
"tasksCompleted": ["01-input-ux", "02-flexible-sets", "03-design-polish", "04-01-schema-migration", "04-02-backend-api"],
|
||||
"nextTask": "04-03-frontend-workout-edit",
|
||||
"notes": "Backend API complete for custom workouts. Added 6 new endpoints + updated 3 log endpoints with source_type support. Next: Frontend edit UI."
|
||||
"recoveryFrom": "2026-03-01T06:42:00+01:00",
|
||||
"agentSession": "mild-reef",
|
||||
"agentType": "claude-code",
|
||||
"notes": "Frontend agent spawned for 04-03. Working on: Edit Workout button, Exercise picker modal, swap/add exercise flows, fork confirmation dialog. Session: mild-reef"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# Gravl PM - Active Task Queue
|
||||
|
||||
## Current: 04-03 Frontend - Workout Edit Mode
|
||||
**Status:** IN PROGRESS (recovery from interruption)
|
||||
**Agent:** Frontend (Claude Code)
|
||||
**Directory:** /workspace/gravl/frontend
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 1. Add "Edit Workout" Button
|
||||
- Add edit button/icon on WorkoutSelectPage for program workouts
|
||||
- Only show for workouts that are part of a program
|
||||
- Button triggers edit mode/modal
|
||||
|
||||
#### 2. Create ExercisePicker Modal/Component
|
||||
- Modal for selecting exercises from the database
|
||||
- Search/filter functionality
|
||||
- Exercise list with categories
|
||||
- Select exercise with click/tap
|
||||
- Reuse existing exercise data from current workout flow
|
||||
|
||||
#### 3. Implement Swap Exercise Flow
|
||||
- On exercise row in edit mode, show swap button
|
||||
- Open ExercisePicker modal
|
||||
- Replace selected exercise in workout structure
|
||||
- Maintain set/rep info where applicable
|
||||
|
||||
#### 4. Implement Add Exercise Flow
|
||||
- "Add Exercise" button at bottom of workout
|
||||
- Open ExercisePicker modal
|
||||
- Append new exercise to workout with default sets/reps
|
||||
- Allow configuring sets/reps for new exercise
|
||||
|
||||
#### 5. Fork Confirmation Dialog
|
||||
- When user first modifies a program workout
|
||||
- Explain: "This creates your personal version of this workout"
|
||||
- Options: "Cancel", "Create My Version"
|
||||
- Show only once per workout (set flag)
|
||||
|
||||
#### 6. Save Custom Workout
|
||||
- POST to /api/custom-workouts on first modification (creates fork)
|
||||
- PUT to /api/custom-workouts/:id on subsequent changes
|
||||
- Update local state to use custom_workout_id
|
||||
- Mark workout as "custom" in UI
|
||||
|
||||
### API Endpoints Available (from 04-02)
|
||||
- POST /api/custom-workouts - Create custom workout from program
|
||||
- PUT /api/custom-workouts/:id - Update exercises
|
||||
- GET /api/custom-workouts/:id - Fetch with exercises
|
||||
- GET /api/custom-workouts - List user's custom workouts
|
||||
|
||||
### Database Schema (from 04-01)
|
||||
- custom_workouts table with user_id, name, original_program_day_id
|
||||
- custom_workout_exercises table with exercise_id, set_order, sets, reps
|
||||
|
||||
### Success Criteria
|
||||
- [ ] "Edit Workout" button visible on program workouts
|
||||
- [ ] Exercise picker modal opens and shows exercises
|
||||
- [ ] Can swap an exercise (replaces in workout)
|
||||
- [ ] Can add new exercise (appends to workout)
|
||||
- [ ] Fork confirmation shown on first edit
|
||||
- [ ] Custom workout saves to backend
|
||||
- [ ] Subsequent sessions use custom workout
|
||||
|
||||
### Next After This
|
||||
- 04-04: Visual distinction (custom vs program badges)
|
||||
- 04-05: Reset to original program option
|
||||
|
||||
Generated
+64
@@ -13,6 +13,7 @@
|
||||
"react-router-dom": "^6.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
@@ -742,6 +743,22 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
|
||||
"integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.58.2"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
|
||||
@@ -1481,6 +1498,53 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.58.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
|
||||
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.58.2"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.58.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
|
||||
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
|
||||
"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": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"react-router-dom": "^6.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
testDir: "./tests",
|
||||
use: {
|
||||
baseURL: process.env.STAGING_URL || "https://gravl.homelab.local",
|
||||
headless: true,
|
||||
screenshot: "only-on-failure",
|
||||
},
|
||||
projects: [{
|
||||
name: "chromium",
|
||||
use: { browserName: "chromium" }
|
||||
}]
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
test("login page loads", async ({ page }) => {
|
||||
await page.goto("/login");
|
||||
await expect(page.locator("form")).toBeVisible();
|
||||
});
|
||||
|
||||
test("logo exists", async ({ page }) => {
|
||||
await page.goto("/login");
|
||||
const logo = await page.locator("svg, img[class*=logo], .logo").first();
|
||||
await expect(logo).toBeVisible();
|
||||
});
|
||||
|
||||
test("dashboard loads", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page).toHaveTitle(/Gravl/);
|
||||
});
|
||||
Reference in New Issue
Block a user