diff --git a/.gitignore b/.gitignore index 9773157..1009968 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -# Logs logs *.log npm-debug.log* @@ -15,7 +14,6 @@ server/prisma/dev.db test-results/ playwright-report/ -# Editor directories and files .vscode/* !.vscode/extensions.json .idea diff --git a/playwright.config.ts b/playwright.config.ts index 3aa4bac..6e8d368 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -5,14 +5,17 @@ export default defineConfig({ fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, + workers: 1, reporter: 'html', use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry', + launchOptions: { + headless: false + } }, webServer: { - command: 'npm run dev:full', + command: 'rm -f server/prisma/prisma/dev.db && npm run migrate:deploy --prefix server && npm run dev:full & wait-on http-get://localhost:3000', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, diff --git a/server/package.json b/server/package.json index 5176134..b12e0f3 100644 --- a/server/package.json +++ b/server/package.json @@ -6,7 +6,9 @@ "scripts": { "start": "node dist/index.js", "dev": "ts-node-dev -r dotenv/config --respawn --transpile-only src/index.ts", - "build": "tsc" + "build": "tsc", + "migrate:deploy": "npx prisma migrate deploy" + }, "dependencies": { "@google/generative-ai": "^0.24.1", diff --git a/server/prisma/dev.db b/server/prisma/dev.db index 89d7553..34bc2f7 100644 Binary files a/server/prisma/dev.db and b/server/prisma/dev.db differ diff --git a/tests/active-session-finish-session.spec.ts b/tests/active-session-finish-session.spec.ts index 4b7f0b1..315047a 100644 --- a/tests/active-session-finish-session.spec.ts +++ b/tests/active-session-finish-session.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('C. Active Session - Finish Session', async ({ page }) => { // 1. Start a 'Free Workout' session. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); await page.getByRole('spinbutton').fill('83'); @@ -17,8 +17,8 @@ test.describe('III. Workout Tracking', () => { // 2. Log at least one set. await page.getByRole('textbox', { name: '0' }).fill('Bench Press'); await page.getByRole('button', { name: 'Bench Press' }).click(); - await page.getByPlaceholder('0').nth(1).fill('80'); - await page.getByPlaceholder('0').nth(2).fill('5'); + await page.getByRole('spinbutton').nth(1).fill('80'); + await page.getByRole('spinbutton').nth(2).fill('5'); await page.getByRole('button', { name: 'Log Set' }).click(); // 3. Click 'Finish' button. diff --git a/tests/active-session-log-bodyweight-set.spec.ts b/tests/active-session-log-bodyweight-set.spec.ts index 7684047..f2e3087 100644 --- a/tests/active-session-log-bodyweight-set.spec.ts +++ b/tests/active-session-log-bodyweight-set.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('C. Active Session - Log Bodyweight Set', async ({ page }) => { // 1. Start a 'Free Workout' session. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); await page.getByRole('spinbutton').fill('83'); diff --git a/tests/active-session-log-cardio-set.spec.ts b/tests/active-session-log-cardio-set.spec.ts index 7ba5490..065162e 100644 --- a/tests/active-session-log-cardio-set.spec.ts +++ b/tests/active-session-log-cardio-set.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('C. Active Session - Log Cardio Set', async ({ page }) => { // 1. Start a 'Free Workout' session. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); await page.getByRole('spinbutton').fill('83'); diff --git a/tests/active-session-log-strength-set.spec.ts b/tests/active-session-log-strength-set.spec.ts index 7a8996e..227679a 100644 --- a/tests/active-session-log-strength-set.spec.ts +++ b/tests/active-session-log-strength-set.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('C. Active Session - Log Strength Set', async ({ page }) => { // 1. Start a 'Free Workout' session (ensure body weight is set). await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); await page.getByRole('spinbutton').fill('83'); diff --git a/tests/active-session-plan-progression-and-jump-to-step.spec.ts b/tests/active-session-plan-progression-and-jump-to-step.spec.ts index 6c6e5e3..e32de51 100644 --- a/tests/active-session-plan-progression-and-jump-to-step.spec.ts +++ b/tests/active-session-plan-progression-and-jump-to-step.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('C. Active Session - Plan Progression and Jump to Step', async ({ page }) => { // 1. Start a session from a workout plan with multiple steps. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // Create a plan with multiple exercises diff --git a/tests/active-session-quit-session-without-saving.spec.ts b/tests/active-session-quit-session-without-saving.spec.ts index 99d9f54..0d7a07f 100644 --- a/tests/active-session-quit-session-without-saving.spec.ts +++ b/tests/active-session-quit-session-without-saving.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('C. Active Session - Quit Session Without Saving', async ({ page }) => { // 1. Start a 'Free Workout' session. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); await page.getByRole('spinbutton').fill('83'); diff --git a/tests/adaptive-gui-fluid-layout-responsiveness.spec.ts b/tests/adaptive-gui-fluid-layout-responsiveness.spec.ts index bfb6d6e..5bec2b5 100644 --- a/tests/adaptive-gui-fluid-layout-responsiveness.spec.ts +++ b/tests/adaptive-gui-fluid-layout-responsiveness.spec.ts @@ -7,7 +7,7 @@ test.describe('User Interface & Experience', () => { test('Adaptive GUI - Fluid Layout Responsiveness', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://192.168.50.234:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); + await page.getByRole('textbox', { name: '0' }).first().fill('admin@gymflow.ai'); await page.locator('input[type="password"]').fill('admin'); await page.getByRole('button', { name: 'Login' }).click(); diff --git a/tests/adaptive-gui-responsive-charts-in-stats.spec.ts b/tests/adaptive-gui-responsive-charts-in-stats.spec.ts index 88f681e..c64b859 100644 --- a/tests/adaptive-gui-responsive-charts-in-stats.spec.ts +++ b/tests/adaptive-gui-responsive-charts-in-stats.spec.ts @@ -7,7 +7,7 @@ test.describe('User Interface & Experience', () => { test('Adaptive GUI - Responsive Charts in Stats', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://192.168.50.234:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); + await page.getByRole('textbox', { name: '0' }).first().fill('admin@gymflow.ai'); await page.locator('input[type="password"]').fill('admin'); await page.getByRole('button', { name: 'Login' }).click(); diff --git a/tests/exercise-library-create-custom-exercise-bodyweight-with-percentage.spec.ts b/tests/exercise-library-create-custom-exercise-bodyweight-with-percentage.spec.ts index 9a86eb0..4211c5e 100644 --- a/tests/exercise-library-create-custom-exercise-bodyweight-with-percentage.spec.ts +++ b/tests/exercise-library-create-custom-exercise-bodyweight-with-percentage.spec.ts @@ -7,8 +7,8 @@ test.describe('II. Workout Management', () => { test('B. Exercise Library - Create Custom Exercise (Bodyweight with %)', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // 2. Navigate to the 'Profile' section. diff --git a/tests/exercise-library-create-custom-exercise-strength.spec.ts b/tests/exercise-library-create-custom-exercise-strength.spec.ts index 31ac180..f706dad 100644 --- a/tests/exercise-library-create-custom-exercise-strength.spec.ts +++ b/tests/exercise-library-create-custom-exercise-strength.spec.ts @@ -7,8 +7,8 @@ test.describe('II. Workout Management', () => { test('B. Exercise Library - Create Custom Exercise (Strength)', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // 2. Navigate to the 'Profile' section. diff --git a/tests/idle-state-body-weight-defaults-from-profile.spec.ts b/tests/idle-state-body-weight-defaults-from-profile.spec.ts index e98ef9f..99decc3 100644 --- a/tests/idle-state-body-weight-defaults-from-profile.spec.ts +++ b/tests/idle-state-body-weight-defaults-from-profile.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('B. Idle State - Body Weight Defaults from Profile', async ({ page }) => { // 1. Log in as a regular user with a weight set in their profile. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // Ensure the weight is set in the profile before navigating to tracker diff --git a/tests/idle-state-start-free-workout.spec.ts b/tests/idle-state-start-free-workout.spec.ts index e1889f5..f69ff61 100644 --- a/tests/idle-state-start-free-workout.spec.ts +++ b/tests/idle-state-start-free-workout.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('B. Idle State - Start Free Workout', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // 2. Ensure the tracker is in the idle state. (This is the default view after login) diff --git a/tests/idle-state-start-quick-log.spec.ts b/tests/idle-state-start-quick-log.spec.ts index a2d9419..88d884b 100644 --- a/tests/idle-state-start-quick-log.spec.ts +++ b/tests/idle-state-start-quick-log.spec.ts @@ -7,8 +7,8 @@ test.describe('III. Workout Tracking', () => { test('B. Idle State - Start Quick Log', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // 2. Ensure the tracker is in the idle state. (This is the default view after login) diff --git a/tests/login-first-time-password-change-short.spec.ts b/tests/login-first-time-password-change-short.spec.ts index 8751ced..b6a6c8e 100644 --- a/tests/login-first-time-password-change-short.spec.ts +++ b/tests/login-first-time-password-change-short.spec.ts @@ -9,8 +9,8 @@ test.describe('I. Core & Authentication', () => { await page.goto('http://localhost:3000/'); // Log in as admin to create a new user for testing first-time login - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // Navigate to profile and create a new user with a short password diff --git a/tests/login-invalid-credentials.spec.ts b/tests/login-invalid-credentials.spec.ts index 4f8a223..2081149 100644 --- a/tests/login-invalid-credentials.spec.ts +++ b/tests/login-invalid-credentials.spec.ts @@ -9,7 +9,7 @@ test.describe('I. Core & Authentication', () => { await page.goto('http://localhost:3000/'); // 2. Enter an invalid email or password. - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('invalid@gymflow.ai'); + await page.locator('input[type="email"]').fill('invalid@gymflow.com'); await page.locator('input[type="password"]').fill('wrongpassword'); // 3. Click the 'Login' button. diff --git a/tests/login-successful-authentication.spec.ts b/tests/login-successful-authentication.spec.ts index 52fdbaf..59fa9f5 100644 --- a/tests/login-successful-authentication.spec.ts +++ b/tests/login-successful-authentication.spec.ts @@ -9,15 +9,16 @@ test.describe('I. Core & Authentication', () => { await page.goto('http://localhost:3000/'); // 2. Enter a valid email in the email field. - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); // 3. Enter a valid password in the password field. - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="password"]').fill('admin123'); // 4. Click the 'Login' button. await page.getByRole('button', { name: 'Login' }).click(); // Wait for navigation to complete + await page.waitForURL('http://localhost:3000/tracker'); await page.waitForLoadState('networkidle'); // Expected Results: diff --git a/tests/session-history-delete-sporadic-set.spec.ts b/tests/session-history-delete-sporadic-set.spec.ts index 1a55f19..afaeac4 100644 --- a/tests/session-history-delete-sporadic-set.spec.ts +++ b/tests/session-history-delete-sporadic-set.spec.ts @@ -7,8 +7,8 @@ test.describe('IV. Data & Progress', () => { test('A. Session History - Delete Sporadic Set', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // 2. Log at least one sporadic set. diff --git a/tests/user-profile-change-password-too-short.spec.ts b/tests/user-profile-change-password-too-short.spec.ts index 365a03d..1813cb5 100644 --- a/tests/user-profile-change-password-too-short.spec.ts +++ b/tests/user-profile-change-password-too-short.spec.ts @@ -7,8 +7,8 @@ test.describe('V. User & System Management', () => { test('A. User Profile - Change Password (Too Short)', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // 2. Navigate to the 'Profile' section. @@ -27,7 +27,7 @@ test.describe('V. User & System Management', () => { // (Implicitly tested by the fact that the error message is displayed and no success message) // Reset password to original for subsequent tests - await page.getByRole('textbox', { name: 'New Password' }).fill('admin1234'); + await page.getByRole('textbox', { name: 'New Password' }).fill('admin123'); await page.getByRole('button', { name: 'OK' }).click(); await expect(page.getByText('Password changed')).toBeVisible(); }); diff --git a/tests/user-profile-change-password.spec.ts b/tests/user-profile-change-password.spec.ts index 676b490..68711c1 100644 --- a/tests/user-profile-change-password.spec.ts +++ b/tests/user-profile-change-password.spec.ts @@ -7,8 +7,8 @@ test.describe('V. User & System Management', () => { test('A. User Profile - Change Password', async ({ page }) => { // 1. Log in as a regular user. await page.goto('http://localhost:3000/'); - await page.getByRole('textbox', { name: 'user@gymflow.ai' }).fill('admin@gymflow.ai'); - await page.locator('input[type="password"]').fill('admin1234'); + await page.locator('input[type="email"]').fill('admin@gymflow.ai'); + await page.locator('input[type="password"]').fill('admin123'); await page.getByRole('button', { name: 'Login' }).click(); // 2. Navigate to the 'Profile' section. @@ -34,7 +34,7 @@ test.describe('V. User & System Management', () => { // Change password back to original for subsequent tests await page.getByRole('button', { name: 'Profile' }).click(); - await page.getByRole('textbox', { name: 'New Password' }).fill('admin1234'); + await page.getByRole('textbox', { name: 'New Password' }).fill('admin123'); await page.getByRole('button', { name: 'OK' }).click(); await expect(page.getByText('Password changed')).toBeVisible(); });