diff --git a/backend/routes/tasks/queries/query-builders.js b/backend/routes/tasks/queries/query-builders.js index 5f3d9d3..d1a83ae 100644 --- a/backend/routes/tasks/queries/query-builders.js +++ b/backend/routes/tasks/queries/query-builders.js @@ -116,6 +116,7 @@ async function filterTasksByParams( }; whereClause[Op.or] = [ { + // Non-recurring tasks that are marked for today [Op.and]: [ { [Op.or]: [ @@ -124,16 +125,20 @@ async function filterTasksByParams( ], }, { recurring_parent_id: null }, + { today: true }, ], }, { + // Recurring parent tasks that are marked for today [Op.and]: [ { recurrence_type: { [Op.ne]: 'none' } }, { recurrence_type: { [Op.ne]: null } }, { recurring_parent_id: null }, + { today: true }, ], }, { + // Recurring task instances that are due today (regardless of today flag) [Op.and]: [ { recurring_parent_id: { [Op.ne]: null } }, { diff --git a/backend/tests/integration/project-sharing.test.js b/backend/tests/integration/project-sharing.test.js index 947e342..71f4838 100644 --- a/backend/tests/integration/project-sharing.test.js +++ b/backend/tests/integration/project-sharing.test.js @@ -152,19 +152,19 @@ describe('Project Sharing Integration Tests', () => { }); const taskDueToday = taskDueTodayResponse.body; - // Fetch today's tasks as shared user - const response = await sharedUserAgent.get('/api/tasks?type=today'); + // Fetch today's tasks as shared user with include_lists to get tasks_due_today + const response = await sharedUserAgent.get( + '/api/tasks?type=today&include_lists=true' + ); expect(response.status).toBe(200); expect(response.body.tasks).toBeDefined(); - // Check if the task appears in the main tasks or metrics - const allTasks = [ - ...response.body.tasks, - ...(response.body.metrics?.tasks_due_today || []), - ]; - - const foundTask = allTasks.find((t) => t.id === taskDueToday.id); + // Task should appear in tasks_due_today since it has a due date but today=false + expect(response.body.tasks_due_today).toBeDefined(); + const foundTask = response.body.tasks_due_today.find( + (t) => t.id === taskDueToday.id + ); expect(foundTask).toBeDefined(); expect(foundTask.name).toBe('Task due today in shared project'); }); diff --git a/backend/tests/integration/tasks.test.js b/backend/tests/integration/tasks.test.js index 5dd523e..5b317b9 100644 --- a/backend/tests/integration/tasks.test.js +++ b/backend/tests/integration/tasks.test.js @@ -99,13 +99,13 @@ describe('Tasks Routes', () => { expect(response.body.tasks.map((t) => t.id)).toContain(task2.id); }); - it('should filter today tasks (returns all user tasks)', async () => { + it('should filter today tasks (returns only tasks with today=true)', async () => { const response = await agent.get('/api/tasks?type=today'); expect(response.status).toBe(200); expect(response.body.tasks).toBeDefined(); - expect(response.body.tasks.length).toBe(2); - // Both tasks should be returned as "today" doesn't filter by the today field + expect(response.body.tasks.length).toBe(1); + expect(response.body.tasks[0].id).toBe(task1.id); }); it('should require authentication', async () => { diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 39f25d5..d1ab4e6 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -8,15 +8,15 @@ const slowMoMs = Number(process.env.E2E_SLOWMO || '0') || 0; export default defineConfig({ testDir: './tests', - timeout: 60_000, - expect: { timeout: 10_000 }, + timeout: 30_000, // Reduced from 60s to 30s + expect: { timeout: 5_000 }, // Reduced from 10s to 5s fullyParallel: true, - workers: process.env.CI ? 1 : undefined, // Use default workers locally, 1 in CI + workers: process.env.CI ? 1 : 4, // Use 4 workers locally for speed reporter: [['list']], use: { baseURL, trace: 'on-first-retry', - video: 'retain-on-failure', + video: 'off', // Disable video for speed (was 'retain-on-failure') screenshot: 'only-on-failure', viewport: { width: 1280, height: 800 }, launchOptions: { slowMo: slowMoMs }, diff --git a/e2e/tests/registration.spec.ts b/e2e/tests/registration.spec.ts index 28f49a6..ac6c603 100644 --- a/e2e/tests/registration.spec.ts +++ b/e2e/tests/registration.spec.ts @@ -1,7 +1,7 @@ import { test, expect } from '@playwright/test'; -test.describe.serial('User Registration', () => { -test.describe.serial('User Registration - Enabled', () => { +test.describe.serial('Registration', () => { +test.describe.serial('Enabled', () => { test.beforeAll(async ({ request, baseURL }) => { const appUrl = baseURL ?? process.env.APP_URL ?? 'http://localhost:8080'; @@ -37,7 +37,7 @@ test.describe.serial('User Registration - Enabled', () => { await page.goto(appUrl + '/register'); }); - test('should display registration form', async ({ page }) => { + test('Shows form', async ({ page }) => { await expect(page.getByTestId('register-heading')).toBeVisible(); await expect(page.getByTestId('register-email')).toBeVisible(); await expect(page.getByTestId('register-password')).toBeVisible(); @@ -45,7 +45,7 @@ test.describe.serial('User Registration - Enabled', () => { await expect(page.getByTestId('register-submit')).toBeVisible(); }); - test('should show error when passwords do not match', async ({ page }) => { + test('Password mismatch error', async ({ page }) => { const timestamp = Date.now(); const email = `test${timestamp}@example.com`; @@ -58,7 +58,7 @@ test.describe.serial('User Registration - Enabled', () => { await expect(page.getByTestId('register-error')).toContainText(/passwords do not match/i); }); - test('should prevent submission when password is too short (HTML5 validation)', async ({ page }) => { + test('Password too short', async ({ page }) => { const timestamp = Date.now(); const email = `test${timestamp}@example.com`; @@ -75,7 +75,7 @@ test.describe.serial('User Registration - Enabled', () => { expect(validationMessage).toBeTruthy(); }); - test('should successfully register a new user or show email error', async ({ page }) => { + test('Register successfully', async ({ page }) => { const timestamp = Date.now(); const email = `test${timestamp}@example.com`; const password = 'password123'; @@ -107,13 +107,13 @@ test.describe.serial('User Registration - Enabled', () => { } }); - test('should navigate to login page from link', async ({ page }) => { + test('Link to login', async ({ page }) => { await page.getByTestId('register-login-link').click(); await expect(page).toHaveURL(/\/login$/); }); - test('should show error for duplicate email registration', async ({ page }) => { + test('Duplicate email error', async ({ page }) => { const email = process.env.E2E_EMAIL || 'test@tududi.com'; const password = 'password123'; @@ -127,7 +127,7 @@ test.describe.serial('User Registration - Enabled', () => { }); }); -test.describe.serial('User Registration - Disabled', () => { +test.describe.serial('Disabled', () => { test.beforeAll(async ({ request, baseURL }) => { const appUrl = baseURL ?? process.env.APP_URL ?? 'http://localhost:8080'; @@ -163,7 +163,7 @@ test.describe.serial('User Registration - Disabled', () => { await page.goto(appUrl + '/register'); }); - test('should show registration disabled error', async ({ page }) => { + test('Shows disabled error', async ({ page }) => { const timestamp = Date.now(); const email = `test${timestamp}@example.com`; const password = 'password123'; diff --git a/e2e/tests/today-view.spec.ts b/e2e/tests/today-view.spec.ts index 7bfed5e..70b2e84 100644 --- a/e2e/tests/today-view.spec.ts +++ b/e2e/tests/today-view.spec.ts @@ -1,27 +1,25 @@ import { test, expect } from '@playwright/test'; -test.describe.serial('Today View', () => { +test.describe('Today', () => { // Helper function to login via UI async function loginViaUI(page, baseURL) { const appUrl = baseURL ?? process.env.APP_URL ?? 'http://localhost:8080'; await page.goto(`${appUrl}/login`); - await page.fill( - 'input[type="email"]', - process.env.E2E_EMAIL || 'test@tududi.com' - ); - await page.fill( - 'input[type="password"]', - process.env.E2E_PASSWORD || 'password123' - ); - await page.click('button[type="submit"]'); + await page + .getByTestId('login-email') + .fill(process.env.E2E_EMAIL || 'test@tududi.com'); + await page + .getByTestId('login-password') + .fill(process.env.E2E_PASSWORD || 'password123'); + await page.getByTestId('login-submit').click(); // Wait for redirect to dashboard or today page await page.waitForURL(/\/(dashboard|today)/, { timeout: 10000 }); } - test('should only show tasks with today flag in Planned section', async ({ + test('Planned: only today=true tasks', async ({ page, context, baseURL, @@ -62,11 +60,10 @@ test.describe.serial('Today View', () => { } } - // Navigate to today page and wait for metrics to load + // Navigate to today page await page.goto(`${appUrl}/today`); - await page.waitForLoadState('networkidle'); - // Check if Planned section exists using data-testid + // Wait for Planned section to appear (indicates page loaded) const plannedSection = page.getByTestId('planned-section'); await expect(plannedSection).toBeVisible({ timeout: 10000 }); @@ -88,7 +85,7 @@ test.describe.serial('Today View', () => { } }); - test('should show overdue tasks in Overdue section', async ({ + test('Overdue: shows overdue tasks', async ({ page, context, baseURL, @@ -122,10 +119,6 @@ test.describe.serial('Today View', () => { // Navigate to today page await page.goto(`${appUrl}/today`); - await page.waitForLoadState('networkidle'); - - // Wait a bit for React to render the sections - await page.waitForTimeout(2000); // Check if Overdue section exists using data-testid const overdueSection = page.getByTestId('overdue-section'); @@ -153,7 +146,7 @@ test.describe.serial('Today View', () => { } }); - test('should show tasks due today in Due Today section', async ({ + test('Due Today: shows tasks', async ({ page, context, baseURL, @@ -187,10 +180,6 @@ test.describe.serial('Today View', () => { // Navigate to today page await page.goto(`${appUrl}/today`); - await page.waitForLoadState('networkidle'); - - // Wait a bit for React to render the sections - await page.waitForTimeout(2000); // Check if Due Today section exists using data-testid const dueTodaySection = page.getByTestId('due-today-section'); @@ -217,7 +206,7 @@ test.describe.serial('Today View', () => { } }); - test('should allow collapsing and expanding sections', async ({ + test('Collapse/expand sections', async ({ page, baseURL, }) => { @@ -227,7 +216,6 @@ test.describe.serial('Today View', () => { const appUrl = baseURL ?? process.env.APP_URL ?? 'http://localhost:8080'; await page.goto(`${appUrl}/today`); - await page.waitForLoadState('networkidle'); // Test Planned section collapse/expand if it exists using data-testid const plannedHeader = page.getByTestId('planned-section-header'); diff --git a/frontend/components/Login.tsx b/frontend/components/Login.tsx index 63e1537..0d4c7ca 100644 --- a/frontend/components/Login.tsx +++ b/frontend/components/Login.tsx @@ -183,6 +183,7 @@ const Login: React.FC = () => { setEmail(e.target.value) } className="w-full px-4 py-2 border dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" + data-testid="login-email" required /> @@ -202,12 +203,14 @@ const Login: React.FC = () => { setPassword(e.target.value) } className="w-full px-4 py-2 border dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" + data-testid="login-password" required /> diff --git a/public/locales/el/translation.json b/public/locales/el/translation.json index 287d42a..1227ccb 100644 --- a/public/locales/el/translation.json +++ b/public/locales/el/translation.json @@ -355,7 +355,7 @@ "unknownProject": "Άγνωστο έργο", "tasks": "εργασίες", "showingItems": "Εμφάνιση {{current}} από {{total}} στοιχεία", - "overdue": "Υπερβολικό", + "overdue": "Εκπρόθεσμο", "planned": "Προγραμματισμένο", "open": "Άνοιγμα", "completed": "Ολοκληρώθηκε"