import { test, expect } from './fixtures'; import { request as playwrightRequest } from '@playwright/test'; import { promisify } from 'util'; import { exec as cp_exec } from 'child_process'; const exec = promisify(cp_exec); test.describe('V. User & System Management', () => { test('5.1. A. User Profile - Update Personal Information', 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 expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); await page.getByTestId('profile-weight-input').fill('75'); await page.getByTestId('profile-height-input').fill('180'); await page.getByTestId('profile-birth-date').fill('1990-01-01'); await page.getByTestId('profile-gender').selectOption('FEMALE'); await page.getByRole('button', { name: 'Save Profile' }).click(); await expect(page.getByText('Profile saved successfully')).toBeVisible(); await page.reload(); if (!await page.getByRole('heading', { name: 'Profile' }).isVisible()) { await page.getByRole('button', { name: 'Profile', exact: true }).click(); } await expect(page.getByTestId('profile-weight-input')).toHaveValue('75'); await expect(page.getByTestId('profile-height-input')).toHaveValue('180'); await expect(page.getByTestId('profile-birth-date')).toHaveValue('1990-01-01'); await expect(page.getByTestId('profile-gender')).toHaveValue('FEMALE'); }); test('5.2. A. User Profile - Change Password', 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 expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); const newPassword = 'NewStrongPass!'; await page.getByRole('textbox', { name: 'New Password' }).fill(newPassword); await page.getByRole('button', { name: 'OK' }).click(); await expect(page.getByText('Password changed')).toBeVisible(); await page.getByRole('button', { name: 'Logout' }).click(); await page.getByRole('textbox', { name: 'Email' }).fill(user.email); await page.getByRole('textbox', { name: 'Password' }).fill(newPassword); await page.getByRole('button', { name: 'Login' }).click(); await expect(page.getByText('Free Workout')).toBeVisible(); }); test('5.3. A. User Profile - Change Password (Too Short)', 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 expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); await page.getByRole('textbox', { name: 'New Password' }).fill('123'); await page.getByRole('button', { name: 'OK' }).click(); await expect(page.getByText('Password too short')).toBeVisible(); }); test('5.4. A. User Profile - Dedicated Daily Weight Logging', 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 expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); await page.getByRole('button', { name: 'Weight Tracker' }).click(); const weight = '72.3'; await page.getByPlaceholder('Enter weight...').fill(weight); await page.getByRole('button', { name: 'Log', exact: true }).click(); await expect(page.getByText('Weight logged successfully')).toBeVisible(); await expect(page.getByText(`${weight} kg`)).toBeVisible(); await expect(page.getByRole('spinbutton').first()).toHaveValue(weight); }); test('5.5. A. User Profile - Language Preference Change', 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 expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); await page.getByRole('combobox').nth(1).selectOption(['ru']); await page.getByRole('button', { name: /Сохранить профиль|Save Profile/ }).click(); await expect(page.getByRole('heading', { name: 'Профиль', exact: true })).toBeVisible(); await expect(page.getByText(/Profile saved|Профиль успешно/)).toBeVisible(); await expect(page.getByRole('button', { name: 'Сохранить профиль' })).toBeVisible(); await page.reload(); if (await page.getByLabel('Email').isVisible()) { await page.getByLabel('Email').fill(user.email); await page.getByLabel('Password').fill(user.password || 'StrongNewPass123!'); await page.getByRole('button', { name: 'Login' }).click(); } await page.getByRole('button', { name: /^(Профиль|Profile)$/ }).click(); await expect(page.getByRole('heading', { name: 'Профиль', exact: true })).toBeVisible(); }); test('5.6. A. User Profile - Delete Own Account', 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 expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); await page.getByRole('button', { name: 'Delete' }).click(); await expect(page.getByText('Are you sure?')).toBeVisible(); await page.getByRole('button', { name: 'Delete', exact: true }).last().click(); await expect(page).toHaveURL(/\/login/); await expect(page.getByRole('button', { name: 'Login' })).toBeVisible(); await page.getByLabel('Email').fill(user.email); await page.getByLabel('Password').fill(user.password || 'StrongNewPass123!'); await page.getByRole('button', { name: 'Login' }).click(); await expect(page.getByText(/Invalid credentials|User not found/i)).toBeVisible(); }); test('5.7. B. Admin Panel - View User List', async ({ page, createAdminUser, request }) => { test.setTimeout(120000); const adminUser = await createAdminUser(); const createdEmails: string[] = []; const creationPromises = []; for (let i = 0; i < 25; i++) { const uniqueId = Math.random().toString(36).substring(7); const email = `list.user.${i}.${uniqueId}@example.com`; const password = 'StrongPassword123!'; createdEmails.push(email); creationPromises.push(request.post('/api/auth/register', { data: { email, password } })); } const responses = await Promise.all(creationPromises); for (const response of responses) { await expect(response).toBeOK(); } await page.goto('/'); await page.getByLabel('Email').fill(adminUser.email); await page.getByLabel('Password').fill(adminUser.password); await page.getByRole('button', { name: 'Login' }).click(); try { await expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongAdminNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); await page.getByRole('button', { name: /Users List|User List/i }).click(); await expect(page.getByText(/Users List/i)).toBeVisible(); for (const email of createdEmails) { await expect(page.getByText(email)).toBeVisible(); } }); test('5.8. B. Admin Panel - Create New User', async ({ page, createAdminUser }) => { const adminUser = await createAdminUser(); await page.goto('/'); await page.getByLabel('Email').fill(adminUser.email); await page.getByLabel('Password').fill(adminUser.password); await page.getByRole('button', { name: 'Login' }).click(); try { await expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongAdminNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); const uniqueId = Math.random().toString(36).substring(7); const newUserEmail = `new.user.${uniqueId}@example.com`; const newUserPassword = 'NewUserPass123!'; const createUserSection = page.locator('div').filter({ has: page.getByRole('heading', { name: 'Create User' }) }).last(); await createUserSection.getByLabel('Email').fill(newUserEmail); await createUserSection.getByLabel('Password').fill(newUserPassword); await page.getByRole('button', { name: /Create User|Create/i }).click(); await expect(page.getByText(/User created|successfully/i)).toBeVisible(); const userListButton = page.getByRole('button', { name: /Users List/i }); if (await userListButton.getAttribute('aria-expanded') !== 'true') { await userListButton.click(); } const listContainer = page.locator('div.space-y-4.mt-4'); await expect(listContainer).toBeVisible(); await expect(listContainer.getByText(newUserEmail)).toBeVisible(); }); test('5.9. B. Admin Panel - Block/Unblock User', async ({ page, createAdminUser, createUniqueUser }) => { const adminUser = await createAdminUser(); await page.goto('/'); await page.getByLabel('Email').fill(adminUser.email); await page.getByLabel('Password').fill(adminUser.password); await page.getByRole('button', { name: 'Login' }).click(); try { await expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongAdminNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } await expect(page.getByText('Free Workout')).toBeVisible(); const regularUser = await createUniqueUser(); await page.getByRole('button', { name: 'Profile', exact: true }).filter({ visible: true }).click(); const userListButton = page.getByRole('button', { name: /Users List/i }); const isExpanded = await userListButton.getAttribute('aria-expanded'); if (isExpanded !== 'true') { await userListButton.click(); } await expect(userListButton).toHaveAttribute('aria-expanded', 'true'); await Promise.all([ page.waitForResponse(resp => resp.url().includes('/auth/users')), page.getByTitle('Refresh List').click() ]); if (await userListButton.getAttribute('aria-expanded') !== 'true') { await userListButton.click(); } const listContainer = page.locator('div.space-y-4.mt-4'); await expect(listContainer).toBeVisible(); const userRow = listContainer.locator('.bg-surface-container-high').filter({ hasText: regularUser.email }).first(); await expect(userRow).toBeVisible(); const blockButton = userRow.getByRole('button', { name: 'Block', exact: true }); await expect(blockButton).toBeVisible(); await blockButton.click(); await expect(page.getByText('Block User?').or(page.getByText('Заблокировать?'))).toBeVisible(); await page.getByRole('button', { name: /Confirm|Подтвердить/i }).click(); await expect(userRow.getByText(/Blocked|Block/i)).toBeVisible(); const logoutButton = page.getByRole('button', { name: /Logout/i }); if (await logoutButton.isVisible()) { await logoutButton.click(); } else { await page.getByText(/Logout/i).click(); } await expect(page.getByRole('button', { name: 'Login' })).toBeVisible(); await page.getByLabel('Email').fill(regularUser.email); await page.getByLabel('Password').fill(regularUser.password); await page.getByRole('button', { name: 'Login' }).click(); await expect(page.getByText(/Account is blocked/i)).toBeVisible(); await page.reload(); await page.getByLabel('Email').fill(adminUser.email); await page.getByLabel('Password').fill('StrongAdminNewPass123!'); await page.getByRole('button', { name: 'Login' }).click(); await expect(page.getByText('Free Workout')).toBeVisible(); await page.waitForTimeout(1000); await page.getByRole('button', { name: 'Profile', exact: true }).filter({ visible: true }).click(); await userListButton.click(); await page.getByTitle('Refresh List').click(); const userRowAfter = listContainer.locator('.bg-surface-container-high').filter({ hasText: regularUser.email }).first(); await expect(userRowAfter).toBeVisible(); await userRowAfter.getByRole('button', { name: 'Unblock', exact: true }).click(); await expect(page.getByText('Unblock User?').or(page.getByText('Разблокировать?'))).toBeVisible(); await page.getByRole('button', { name: /Confirm|Подтвердить/i }).click(); await expect(userRowAfter.getByText(/Blocked/i)).not.toBeVisible(); await page.getByRole('button', { name: 'Logout' }).click(); await page.getByLabel('Email').fill(regularUser.email); await page.getByLabel('Password').fill(regularUser.password); await page.getByRole('button', { name: 'Login' }).click(); await expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongUserNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } await expect(page.getByText('Free Workout')).toBeVisible(); }); test('5.10. B. Admin Panel - Reset User Password', async ({ page, createAdminUser, createUniqueUser }) => { const adminUser = await createAdminUser(); await page.goto('/'); await page.getByLabel('Email').fill(adminUser.email); await page.getByLabel('Password').fill(adminUser.password); await page.getByRole('button', { name: 'Login' }).click(); try { await expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongAdminNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } const regularUser = await createUniqueUser(); const newPassword = 'NewStrongUserPass!'; await page.getByRole('button', { name: 'Profile', exact: true }).click(); const userListButton = page.getByRole('button', { name: /Users List/i }); const isExpanded = await userListButton.getAttribute('aria-expanded'); if (isExpanded !== 'true') { await userListButton.click(); } await expect(userListButton).toHaveAttribute('aria-expanded', 'true'); await Promise.all([ page.waitForResponse(resp => resp.url().includes('/auth/users')), page.getByTitle('Refresh List').click() ]); if (await userListButton.getAttribute('aria-expanded') !== 'true') { await userListButton.click(); } const listContainer = page.locator('div.space-y-4.mt-4'); await expect(listContainer).toBeVisible(); const userRow = listContainer.locator('.bg-surface-container-high').filter({ hasText: regularUser.email }).first(); await expect(userRow).toBeVisible(); await userRow.getByRole('textbox').fill(newPassword); page.on('dialog', async dialog => { await dialog.accept(); }); await userRow.getByRole('button', { name: /Reset Pass/i }).click(); await page.waitForTimeout(1000); await page.getByRole('button', { name: 'Logout' }).click(); await page.getByLabel('Email').fill(regularUser.email); await page.getByLabel('Password').fill(newPassword); await page.getByRole('button', { name: 'Login' }).click(); await expect(page.getByRole('heading', { name: /Change Password/i })).toBeVisible({ timeout: 10000 }); await page.getByLabel('New Password').fill('BrandNewUserPass1!'); await page.getByRole('button', { name: /Save|Change/i }).click(); await expect(page.getByText('Free Workout')).toBeVisible(); }); test('5.11. B. Admin Panel - Delete User', async ({ page, createAdminUser, createUniqueUser }) => { const adminUser = await createAdminUser(); await page.goto('/'); await page.getByLabel('Email').fill(adminUser.email); await page.getByLabel('Password').fill(adminUser.password); await page.getByRole('button', { name: 'Login' }).click(); try { await expect(page.getByRole('heading', { name: /Change Password/i }).or(page.getByText('Free Workout'))).toBeVisible({ timeout: 5000 }); if (await page.getByRole('heading', { name: /Change Password/i }).isVisible()) { await page.getByLabel('New Password').fill('StrongAdminNewPass123!'); await page.getByRole('button', { name: /Save|Change/i }).click(); } } catch (e) { } const userToDelete = await createUniqueUser(); await page.getByRole('button', { name: 'Profile', exact: true }).click(); const userListButton = page.getByRole('button', { name: /Users List/i }); const isExpanded = await userListButton.getAttribute('aria-expanded'); if (isExpanded !== 'true') { await userListButton.click(); } await expect(userListButton).toHaveAttribute('aria-expanded', 'true'); await Promise.all([ page.waitForResponse(resp => resp.url().includes('/auth/users')), page.getByTitle('Refresh List').click() ]); if (await userListButton.getAttribute('aria-expanded') !== 'true') { await userListButton.click(); } const listContainer = page.locator('div.space-y-4.mt-4'); await expect(listContainer).toBeVisible(); const userRow = listContainer.locator('.bg-surface-container-high').filter({ hasText: userToDelete.email }).first(); await expect(userRow).toBeVisible(); await userRow.getByRole('button', { name: /Delete/i }).click(); await expect(page.getByText('Delete User?').or(page.getByText('Удалить пользователя?'))).toBeVisible(); await page.getByRole('button', { name: /Confirm|Подтвердить/i }).click({ timeout: 5000 }); await expect(listContainer.getByText(userToDelete.email)).not.toBeVisible(); }); // Merged from default-exercises.spec.ts test.skip('5.12 Default Exercises Creation & Properties', async ({ createUniqueUser }) => { const user = await createUniqueUser(); const apiContext = await playwrightRequest.newContext({ baseURL: 'http://127.0.0.1:3201', extraHTTPHeaders: { 'Authorization': `Bearer ${user.token}` } }); const exercisesRes = await apiContext.get('/api/exercises'); await expect(exercisesRes).toBeOK(); const responseJson = await exercisesRes.json(); const exercises = responseJson.data; const expectedNames = ['Bench Press', 'Squat', 'Deadlift', 'Push-Ups', 'Pull-Ups', 'Running', 'Plank', 'Handstand', 'Sprint', 'Bulgarian Split-Squats']; for (const name of expectedNames) { const found = exercises.find((e: any) => e.name === name); expect(found, `Exercise ${name} should exist`).toBeDefined(); } const dumbbellCurl = exercises.find((e: any) => e.name === 'Dumbbell Curl'); expect(dumbbellCurl.isUnilateral).toBe(true); expect(dumbbellCurl.type).toBe('STRENGTH'); const handstand = exercises.find((e: any) => e.name === 'Handstand'); expect(handstand.type).toBe('BODYWEIGHT'); expect(handstand.bodyWeightPercentage).toBe(1.0); const pushUps = exercises.find((e: any) => e.name === 'Push-Ups'); expect(pushUps.bodyWeightPercentage).toBe(0.65); }); });