Rest timer polishing. Rest timer sets done
This commit is contained in:
@@ -1,150 +1,212 @@
|
||||
import { test, expect } from './fixtures';
|
||||
|
||||
test.describe('Rest Timer Feature', () => {
|
||||
|
||||
// Helper to handle first login if needed (copied from core-auth)
|
||||
async function handleFirstLogin(page: any) {
|
||||
try {
|
||||
const heading = page.getByRole('heading', { name: /Change Password/i });
|
||||
await expect(heading).toBeVisible({ timeout: 5000 });
|
||||
await page.getByLabel('New Password').fill('StrongNewPass123!');
|
||||
await page.getByRole('button', { name: /Save|Change/i }).click();
|
||||
await expect(page.getByText('Free Workout')).toBeVisible();
|
||||
} catch (e) {
|
||||
if (await page.getByText('Free Workout').isVisible()) return;
|
||||
// If login failed or other error
|
||||
const error = page.locator('.text-error');
|
||||
if (await error.isVisible()) throw new Error(`Login failed: ${await error.textContent()}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for logging in
|
||||
const loginUser = async (page: any, email: string, pass: string) => {
|
||||
await page.goto('/login');
|
||||
await page.getByLabel('Email').fill(email);
|
||||
await page.getByLabel('Password').fill(pass);
|
||||
await page.getByRole('button', { name: "Login" }).click();
|
||||
await handleFirstLogin(page);
|
||||
await page.waitForURL('/tracker');
|
||||
};
|
||||
|
||||
test('TC-RT-01: Default timer value and manual adjustment in Free Session', async ({ page, createUniqueUser }) => {
|
||||
// Register/Create user via API
|
||||
test.describe('Rest Timer', () => {
|
||||
test('should allow setting a rest time in a free session', async ({ page, createUniqueUser }) => {
|
||||
const user = await createUniqueUser();
|
||||
await loginUser(page, user.email, user.password);
|
||||
await page.goto('/');
|
||||
await page.getByLabel('Email').fill(user.email);
|
||||
await page.getByLabel('Password').fill(user.password);
|
||||
await page.getByRole('button', { name: 'Login' }).click();
|
||||
|
||||
// 1. Start a free session
|
||||
await page.getByRole('button', { name: "Start Empty Session" }).click();
|
||||
try {
|
||||
await page.getByRole('heading', { name: 'Change Password' }).waitFor();
|
||||
await page.getByLabel('New Password').fill('StrongNewPassword123!');
|
||||
await page.getByRole('button', { name: 'Save & Login' }).click();
|
||||
} catch (e) {
|
||||
// Ignore if the change password screen is not visible
|
||||
}
|
||||
await expect(page.getByText('Free Workout')).toBeVisible();
|
||||
|
||||
// 2. Check default timer value (should be 120s / 2:00)
|
||||
// Click the "Free Workout" button.
|
||||
await page.getByRole('button', { name: 'Free Workout' }).click();
|
||||
|
||||
// The FAB timer should be visible (IDLE state: icon only)
|
||||
const fab = page.locator('.fixed.bottom-24.right-6');
|
||||
await expect(fab).toBeVisible();
|
||||
await fab.click(); // Expand
|
||||
|
||||
const timeDisplay = fab.getByText('2:00');
|
||||
await expect(timeDisplay).toBeVisible();
|
||||
// Click on the rest timer FAB to expand it and reveal the time value.
|
||||
await fab.click();
|
||||
|
||||
// 3. Adjust time to 90s
|
||||
await fab.getByLabel('Edit').click(); // Using aria-label added in component
|
||||
// Wait for expansion and Edit button visibility
|
||||
const editBtn = fab.locator('button[aria-label="Edit"]');
|
||||
await expect(editBtn).toBeVisible();
|
||||
await editBtn.click();
|
||||
|
||||
// Decrease 3 times (120 -> 120-15 = 105s)
|
||||
const minusBtn = fab.locator('button').filter({ has: page.locator('svg.lucide-minus') });
|
||||
await minusBtn.click();
|
||||
await minusBtn.click();
|
||||
await minusBtn.click();
|
||||
// Change the rest timer value to 90 seconds.
|
||||
await page.getByRole('textbox').nth(1).fill('90');
|
||||
|
||||
// Save
|
||||
const saveBtn = fab.locator('button').filter({ has: page.locator('svg.lucide-check') });
|
||||
// Confirm the new rest timer value.
|
||||
const saveBtn = fab.locator('button[aria-label="Save"]');
|
||||
await expect(saveBtn).toBeVisible();
|
||||
await saveBtn.click();
|
||||
|
||||
// Verify display is 1:45 and visible immediately (menu stays expanded)
|
||||
await expect(fab.getByText('1:45')).toBeVisible();
|
||||
// The timer should now be 90 seconds.
|
||||
await expect(page.locator('div').filter({ hasText: /1:30|90/ }).first()).toBeVisible();
|
||||
});
|
||||
|
||||
// 4. Persistence check: Quit and reload
|
||||
await page.getByLabel('Options').click();
|
||||
await page.getByText('Quit without saving').click();
|
||||
test('should validate manual input in the rest timer', async ({ page, createUniqueUser }) => {
|
||||
const user = await createUniqueUser();
|
||||
await page.goto('/');
|
||||
await page.getByLabel('Email').fill(user.email);
|
||||
await page.getByLabel('Password').fill(user.password);
|
||||
await page.getByRole('button', { name: 'Login' }).click();
|
||||
|
||||
try {
|
||||
await page.getByRole('heading', { name: 'Change Password' }).waitFor();
|
||||
await page.getByLabel('New Password').fill('StrongNewPassword123!');
|
||||
await page.getByRole('button', { name: 'Save & Login' }).click();
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
await expect(page.getByText('Free Workout')).toBeVisible();
|
||||
|
||||
// Start a Free Workout
|
||||
await page.getByRole('button', { name: 'Free Workout' }).click();
|
||||
|
||||
// Expand the Rest Timer FAB (Click first!)
|
||||
const fab = page.locator('.fixed.bottom-24.right-6');
|
||||
await fab.click();
|
||||
|
||||
// Click 'Edit'
|
||||
const editBtn = fab.locator('button[aria-label="Edit"]');
|
||||
await expect(editBtn).toBeVisible();
|
||||
await editBtn.click();
|
||||
|
||||
// Type '90' -> Verify '1:30' (if auto-format implemented) or manual '1:30'.
|
||||
const timerInput = page.getByRole('textbox').nth(1);
|
||||
await timerInput.fill('90');
|
||||
await expect(timerInput).toHaveValue('90');
|
||||
|
||||
// Attempt to type non-digits -> Verify they are ignored.
|
||||
await timerInput.fill('90abc');
|
||||
await expect(timerInput).toHaveValue('90');
|
||||
|
||||
// Attempt to type '10:99' (invalid seconds) -> Verify it corrects to '10:59'.
|
||||
await timerInput.fill('10:99');
|
||||
await expect(timerInput).toHaveValue('10:59');
|
||||
|
||||
// Save
|
||||
const saveBtn = fab.locator('button[aria-label="Save"]');
|
||||
await saveBtn.click();
|
||||
|
||||
// Verify updated value in FAB (might need to wait or check visibility)
|
||||
// After save, it usually stays expanded per code "setIsExpanded(true)"
|
||||
});
|
||||
|
||||
test('should persist the rest timer value across sessions', async ({ page, createUniqueUser }) => {
|
||||
const user = await createUniqueUser();
|
||||
await page.goto('/');
|
||||
await page.getByLabel('Email').fill(user.email);
|
||||
await page.getByLabel('Password').fill(user.password);
|
||||
await page.getByRole('button', { name: 'Login' }).click();
|
||||
|
||||
try {
|
||||
await page.getByRole('heading', { name: 'Change Password' }).waitFor();
|
||||
await page.getByLabel('New Password').fill('StrongNewPassword123!');
|
||||
await page.getByRole('button', { name: 'Save & Login' }).click();
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
await expect(page.getByText('Free Workout')).toBeVisible();
|
||||
|
||||
// Start a Free Workout
|
||||
await page.getByRole('button', { name: 'Free Workout' }).click();
|
||||
|
||||
// Click FAB to expand
|
||||
const fab = page.locator('.fixed.bottom-24.right-6');
|
||||
await fab.click();
|
||||
|
||||
// Edit timer to '0:45'.
|
||||
const editBtn = fab.locator('button[aria-label="Edit"]');
|
||||
await editBtn.click();
|
||||
|
||||
const timerInput = page.getByRole('textbox').nth(1);
|
||||
await timerInput.fill('45');
|
||||
|
||||
const saveBtn = fab.locator('button[aria-label="Save"]');
|
||||
await saveBtn.click();
|
||||
|
||||
// Quit session
|
||||
await page.getByRole('button', { name: 'Finish' }).click();
|
||||
await page.getByRole('button', { name: 'Confirm' }).click();
|
||||
|
||||
await page.reload(); // Reload page to ensure persistence from server
|
||||
await page.getByRole('button', { name: "Start Empty Session" }).click();
|
||||
await fab.click(); // Expand
|
||||
await expect(fab.getByText('1:45')).toBeVisible();
|
||||
// Start Quick Log
|
||||
await page.getByRole('button', { name: 'Quick Log' }).click();
|
||||
|
||||
// Verify timer default is now '0:45'.
|
||||
const quickFab = page.locator('.fixed.bottom-24.right-6');
|
||||
await quickFab.click();
|
||||
|
||||
await expect(page.locator('div').filter({ hasText: /0:45/ }).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('TC-RT-02: Timer functionality (Start/Pause/Reset)', async ({ page, createUniqueUser }) => {
|
||||
test('should integrate the rest timer with plans', async ({ page, createUniqueUser }) => {
|
||||
const user = await createUniqueUser();
|
||||
await loginUser(page, user.email, user.password);
|
||||
await page.goto('/');
|
||||
await page.getByLabel('Email').fill(user.email);
|
||||
await page.getByLabel('Password').fill(user.password);
|
||||
await page.getByRole('button', { name: 'Login' }).click();
|
||||
|
||||
await page.getByRole('button', { name: "Start Empty Session" }).click();
|
||||
try {
|
||||
await page.getByRole('heading', { name: 'Change Password' }).waitFor();
|
||||
await page.getByLabel('New Password').fill('StrongNewPassword123!');
|
||||
await page.getByRole('button', { name: 'Save & Login' }).click();
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
await expect(page.getByText('Free Workout')).toBeVisible();
|
||||
|
||||
// Navigate to the "Plans" page.
|
||||
await page.getByRole('button', { name: 'Plans' }).click();
|
||||
|
||||
// Create a new plan.
|
||||
await page.getByRole('button', { name: 'Create Plan' }).click();
|
||||
|
||||
// Name the plan "Timer Test Plan".
|
||||
await page.getByRole('textbox', { name: 'Name' }).fill('Timer Test Plan');
|
||||
|
||||
// Add the first exercise.
|
||||
await page.getByRole('button', { name: 'Add Exercise' }).click();
|
||||
await page.getByRole('button', { name: 'New Exercise' }).click();
|
||||
await page.locator('[id="_r_4_"]').fill('Bench Press');
|
||||
await page.getByRole('button', { name: 'Free Weights & Machines' }).click();
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByPlaceholder('Rest (s)').fill('30');
|
||||
|
||||
// Add the second exercise.
|
||||
await page.getByRole('button', { name: 'Add Exercise' }).click();
|
||||
await page.getByRole('button', { name: 'New Exercise' }).click();
|
||||
await page.locator('[id="_r_5_"]').fill('Squat');
|
||||
await page.getByRole('button', { name: 'Free Weights & Machines' }).click();
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByPlaceholder('Rest (s)').nth(1).fill('60');
|
||||
|
||||
// Save the plan.
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
// Start the plan.
|
||||
await page.getByRole('button', { name: 'Start' }).click();
|
||||
|
||||
// Timer Update: Click FAB
|
||||
const fab = page.locator('.fixed.bottom-24.right-6');
|
||||
await fab.click(); // Expand
|
||||
await fab.click();
|
||||
|
||||
// Start
|
||||
const startBtn = fab.getByLabel('Start');
|
||||
// Verify Timer shows '0:30'. Start it.
|
||||
await expect(page.locator('div').filter({ hasText: /0:30/ }).first()).toBeVisible();
|
||||
const startBtn = fab.locator('button[aria-label="Start"]');
|
||||
await startBtn.click();
|
||||
|
||||
// Check if running
|
||||
await expect(fab).toHaveText(/1:59|2:00/);
|
||||
await expect(fab.getByText('1:5')).toBeVisible({ timeout: 4000 }); // Wait for it to tick down
|
||||
|
||||
// Pause
|
||||
const pauseBtn = fab.getByLabel('Pause');
|
||||
await pauseBtn.click();
|
||||
const pausedText = await fab.innerText();
|
||||
await page.waitForTimeout(2000);
|
||||
const pausedTextAfter = await fab.innerText();
|
||||
expect(pausedText).toBe(pausedTextAfter);
|
||||
|
||||
// Reset
|
||||
const resetBtn = fab.getByLabel('Reset');
|
||||
await resetBtn.click();
|
||||
await expect(fab.getByText('2:00')).toBeVisible();
|
||||
});
|
||||
|
||||
test('TC-RT-03: Plan Integration - Rest Time per Step', async ({ page, createUniqueUser }) => {
|
||||
const user = await createUniqueUser();
|
||||
await loginUser(page, user.email, user.password);
|
||||
|
||||
// 1. Create Exercise (Inline helper)
|
||||
await page.goto('/exercises');
|
||||
await page.getByRole('button', { name: 'Create New Exercise' }).click();
|
||||
await page.getByPlaceholder('Exercise Name').fill('Rest Bench Press');
|
||||
// Select Muscle Group (Mock selection or just save if defaults work? Validation requires muscle/type)
|
||||
// Assuming defaults or simple selection
|
||||
await page.getByText('Target Muscle').click();
|
||||
await page.getByText('Chest').click();
|
||||
await page.getByText('Exercise Type').click();
|
||||
await page.getByText('Strength').click();
|
||||
await page.getByRole('button', { name: 'Save Exercise' }).click();
|
||||
|
||||
// 2. Create Plan with Rest Time
|
||||
await page.goto('/plans');
|
||||
await page.getByRole('button', { name: 'Create Plan' }).click();
|
||||
await page.getByPlaceholder('Plan Name').fill('Rest Test Plan');
|
||||
await page.getByRole('button', { name: 'Add Exercise' }).click();
|
||||
await page.getByText('Rest Bench Press').click();
|
||||
|
||||
// Set Rest Time to 60s
|
||||
await page.getByPlaceholder('Rest (s)').fill('60');
|
||||
|
||||
await page.getByRole('button', { name: 'Save Plan' }).click();
|
||||
|
||||
// 3. Start Plan
|
||||
await page.goto('/tracker');
|
||||
// Ensure plans list is refreshed
|
||||
await page.reload();
|
||||
await page.getByText('Rest Test Plan').click();
|
||||
await page.getByRole('button', { name: 'Start Workout' }).click();
|
||||
|
||||
// 4. Log Set
|
||||
// Needs input for Weight/Reps if Weighted? Default is unweighted.
|
||||
await page.getByPlaceholder('Weight (kg)').fill('50');
|
||||
await page.getByPlaceholder('Reps').fill('10');
|
||||
// Log Set for Step A while timer is running.
|
||||
await page.getByRole('button', { name: 'Log Set' }).click();
|
||||
|
||||
// 5. Verify Timer Auto-Start with 60s
|
||||
const fab = page.locator('.fixed.bottom-24.right-6');
|
||||
// It should be running and showing ~1:00 or 0:59
|
||||
await expect(fab).toHaveText(/1:00|0:59/);
|
||||
// Verify Timer continues running (does not reset).
|
||||
await expect(page.locator('div').filter({ hasText: /0:2[0-9]/ }).first()).toBeVisible();
|
||||
|
||||
// Reset Timer manually (or wait for finish).
|
||||
const resetBtn = fab.locator('button[aria-label="Reset"]');
|
||||
await resetBtn.click();
|
||||
|
||||
// Verify Timer now shows '1:00' (for Step B).
|
||||
await expect(page.locator('div').filter({ hasText: /1:00/ }).first()).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user