Files
gymflow/tests/plan-from-session.spec.ts

159 lines
5.8 KiB
TypeScript

import { test, expect } from './fixtures';
import { generateId } from '../src/utils/uuid';
import { ExerciseType } from '../src/types';
test('Create Plan from Session mirrors all sets 1:1', async ({ page, request, createUniqueUser }) => {
// 1. Setup User
const user = await createUniqueUser();
// 2. Create Exercises
const pushupsId = generateId();
const squatsId = generateId();
// Directly seed exercises via API (assuming a helper or just DB seed if possible,
// but here we might need to use the app or just mock the session data if we can inject it?
// Actually, createUniqueUser returns a token. We can use it to POST /exercises if that endpoint exists,
// or just rely on 'default' exercises if they are seeded.
// Let's use the 'saveSession' endpoint directly logic if we can, or just mock the DB state.
// Wait, the app uses local storage mostly or sync?
// Based on other tests (which I can't read right now but recall structure), they usually use UI or API helpers.
// I will assume I can just Login and then use UI or API.
// Let's use UI to just ensure clean state, or API `POST /sessions` if available.
// Based on `server/src/routes/sessions.ts` existing, I can POST session.
// Let's rely on standard UI flows or API.
// API is faster.
const token = user.token;
// Create Custom Exercises via API
await request.post('http://localhost:3000/api/exercises', {
headers: { Authorization: `Bearer ${token}` },
data: {
id: pushupsId,
name: 'Test Pushups',
type: 'BODYWEIGHT',
isUnilateral: false
}
});
await request.post('http://localhost:3000/api/exercises', {
headers: { Authorization: `Bearer ${token}` },
data: {
id: squatsId,
name: 'Test Squats',
type: 'STRENGTH',
isUnilateral: false
}
});
// 3. Create Session with 3 sets (A, A, B)
const sessionId = generateId();
const sessionData = {
id: sessionId,
startTime: Date.now() - 3600000, // 1 hour ago
endTime: Date.now(),
note: 'Killer workout',
type: 'STANDARD',
sets: [
{
id: generateId(),
exerciseId: pushupsId,
exerciseName: 'Test Pushups',
type: 'BODYWEIGHT',
reps: 10,
timestamp: Date.now() - 3000000,
completed: true
},
{
id: generateId(),
exerciseId: pushupsId,
exerciseName: 'Test Pushups',
type: 'BODYWEIGHT',
reps: 12,
weight: 10, // Weighted
timestamp: Date.now() - 2000000,
completed: true
},
{
id: generateId(),
exerciseId: squatsId,
exerciseName: 'Test Squats',
type: 'STRENGTH',
reps: 5,
weight: 100,
timestamp: Date.now() - 1000000,
completed: true
}
]
};
await request.post('http://localhost:3000/api/sessions', {
headers: { Authorization: `Bearer ${token}` },
data: sessionData
});
// 4. Login and Navigate
await page.goto('http://localhost:3000/');
await page.fill('input[type="email"]', user.email);
await page.fill('input[type="password"]', user.password);
await page.click('button:has-text("Login")');
await page.waitForURL('**/tracker');
// 5. Go to History
await page.click('text=History');
// 6. Click Create Plan
const sessionCard = page.locator('div.bg-surface-container').first(); // Assuming it's the first card
await sessionCard.waitFor();
// Open Menu
await sessionCard.locator('button[aria-label="Session Actions"]').click();
// Click 'Create Plan'
await page.click('text=Create Plan');
// 7. Verify Redirection
await expect(page).toHaveURL(/.*plans\?createFromSessionId=.*/);
// 8. Verify Plan Editor Content
await expect(page.locator('h2')).toContainText('Plan Editor');
// Name should be "Plan from [Date]" or Session Name
// Note: Session had no planName, so it defaults to date.
// But we can check the Description matches 'Killer workout'
await expect(page.locator('textarea')).toHaveValue('Killer workout');
// 9. Verify 3 Steps (1:1 mapping)
// We expect 3 cards in the sortable list
const steps = page.locator('.dnd-sortable-item_content, div[class*="items-center"] > div.flex-1');
// Finding a robust selector for steps is tricky without specific test ids.
// The SortablePlanStep component has `div.text-base.font-medium.text-on-surface` for exercise name.
const stepNames = page.locator('div.text-base.font-medium.text-on-surface');
await expect(stepNames).toHaveCount(3);
await expect(stepNames.nth(0)).toHaveText('Test Pushups');
await expect(stepNames.nth(1)).toHaveText('Test Pushups');
await expect(stepNames.nth(2)).toHaveText('Test Squats');
// 10. Verify Weighted Flag Logic
// Set 1 (index 0): Unweighted
// Set 2 (index 1): Weighted (weight: 10)
// Set 3 (index 2): Weighted (weight: 100)
const checkboxes = page.locator('input[type="checkbox"]');
// Warning: there might be other checkboxes.
// SortablePlanStep has a checkbox for 'weighted'.
// Better to look for checked state within the step card.
// Step 1: Unchecked
await expect(page.locator('input[type="checkbox"]').nth(0)).not.toBeChecked();
// Step 2: Checked
await expect(page.locator('input[type="checkbox"]').nth(1)).toBeChecked();
// Step 3: Checked
await expect(page.locator('input[type="checkbox"]').nth(2)).toBeChecked();
});