159 lines
5.8 KiB
TypeScript
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();
|
|
|
|
});
|