Fix an issue with tasks appearing in today without a flag (#640)

This commit is contained in:
Chris 2025-12-03 16:48:47 +02:00 committed by GitHub
parent 08be7f8eda
commit 270a80f71b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 49 additions and 53 deletions

View file

@ -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 } },
{

View file

@ -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');
});

View file

@ -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 () => {

View file

@ -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 },

View file

@ -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';

View file

@ -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');

View file

@ -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
/>
</div>
@ -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
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition-colors"
data-testid="login-submit"
>
{t('auth.login', 'Login')}
</button>

View file

@ -355,7 +355,7 @@
"unknownProject": "Άγνωστο έργο",
"tasks": "εργασίες",
"showingItems": "Εμφάνιση {{current}} από {{total}} στοιχεία",
"overdue": "Υπερβολικό",
"overdue": "Εκπρόθεσμο",
"planned": "Προγραμματισμένο",
"open": "Άνοιγμα",
"completed": "Ολοκληρώθηκε"