Fix bug 619 (#629)
* Add tasks today plan fixes * fixup! Add tasks today plan fixes * fixup! fixup! Add tasks today plan fixes * fixup! fixup! fixup! Add tasks today plan fixes
This commit is contained in:
parent
f663ad5d52
commit
2d2a989a5f
41 changed files with 1041 additions and 357 deletions
|
|
@ -12,26 +12,17 @@ red() { printf "\033[31m%s\033[0m\n" "$*"; }
|
|||
green() { printf "\033[32m%s\033[0m\n" "$*"; }
|
||||
yellow() { printf "\033[33m%s\033[0m\n" "$*"; }
|
||||
|
||||
# Ensure dependencies in e2e/
|
||||
# Ensure dependencies in root
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
E2E_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
ROOT_DIR="$(cd "$E2E_DIR/.." && pwd)"
|
||||
|
||||
cd "$E2E_DIR"
|
||||
if [ ! -f package.json ]; then
|
||||
red "e2e/package.json not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install e2e deps and browsers
|
||||
if [ ! -d node_modules ]; then
|
||||
yellow "Installing e2e dependencies..."
|
||||
npm ci
|
||||
fi
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
# Check if Playwright is installed
|
||||
if ! npx playwright --version >/dev/null 2>&1; then
|
||||
yellow "Installing Playwright browsers..."
|
||||
npm run install-browsers
|
||||
npx playwright install --with-deps
|
||||
fi
|
||||
|
||||
# Start backend and frontend
|
||||
|
|
@ -108,8 +99,8 @@ for i in {1..60}; do
|
|||
fi
|
||||
done
|
||||
|
||||
# Run tests
|
||||
cd "$E2E_DIR"
|
||||
# Run tests (specify config file since we're running from root)
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
yellow "Running Playwright tests..."
|
||||
APP_URL="$FRONTEND_URL" \
|
||||
|
|
@ -117,11 +108,11 @@ E2E_EMAIL="${E2E_EMAIL:-test@tududi.com}" \
|
|||
E2E_PASSWORD="${E2E_PASSWORD:-password123}" \
|
||||
bash -c '
|
||||
if [ "${E2E_MODE:-}" = "ui" ]; then
|
||||
npm run test:ui
|
||||
npx playwright test --ui --config=e2e/playwright.config.ts
|
||||
elif [ "${E2E_MODE:-}" = "headed" ]; then
|
||||
# Respect E2E_SLOWMO and run only Firefox sequentially
|
||||
npx playwright test --headed --project=Firefox --workers=1
|
||||
# Respect E2E_SLOWMO and run only Chromium sequentially
|
||||
npx playwright test --headed --project=Chromium --workers=1 --config=e2e/playwright.config.ts
|
||||
else
|
||||
npx playwright test --workers=5
|
||||
npx playwright test --config=e2e/playwright.config.ts
|
||||
fi
|
||||
'
|
||||
|
|
|
|||
92
e2e/package-lock.json
generated
92
e2e/package-lock.json
generated
|
|
@ -1,92 +0,0 @@
|
|||
{
|
||||
"name": "tududi-e2e",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "tududi-e2e",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.47.2",
|
||||
"dotenv": "^16.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.54.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.2.tgz",
|
||||
"integrity": "sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.54.2"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.54.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz",
|
||||
"integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.54.2"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.54.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz",
|
||||
"integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"name": "tududi-e2e",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "End-to-end tests for tududi using Playwright",
|
||||
"scripts": {
|
||||
"test": "playwright test",
|
||||
"test:ui": "playwright test --ui",
|
||||
"codegen": "playwright codegen ${APP_URL:-http://localhost:8080}",
|
||||
"install-browsers": "playwright install --with-deps"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.47.2",
|
||||
"dotenv": "^16.5.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ export default defineConfig({
|
|||
timeout: 60_000,
|
||||
expect: { timeout: 10_000 },
|
||||
fullyParallel: true,
|
||||
workers: process.env.CI ? 1 : undefined, // Use default workers locally, 1 in CI
|
||||
reporter: [['list']],
|
||||
use: {
|
||||
baseURL,
|
||||
|
|
|
|||
245
e2e/tests/today-view.spec.ts
Normal file
245
e2e/tests/today-view.spec.ts
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe.serial('Today View', () => {
|
||||
// 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"]');
|
||||
|
||||
// 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 ({
|
||||
page,
|
||||
context,
|
||||
baseURL,
|
||||
}) => {
|
||||
// Login first
|
||||
await loginViaUI(page, baseURL);
|
||||
|
||||
const appUrl =
|
||||
baseURL ?? process.env.APP_URL ?? 'http://localhost:8080';
|
||||
const timestamp = Date.now();
|
||||
|
||||
// Create tasks via API using the logged-in context
|
||||
const tasksToCreate = [
|
||||
{
|
||||
name: `High Priority Planned ${timestamp}`,
|
||||
today: true,
|
||||
priority: 2,
|
||||
}, // 2 = HIGH
|
||||
{
|
||||
name: `Task Without Today Flag ${timestamp}`,
|
||||
today: false,
|
||||
priority: 2,
|
||||
}, // 2 = HIGH
|
||||
];
|
||||
|
||||
const taskIds: string[] = [];
|
||||
const createdTasks: any[] = [];
|
||||
|
||||
for (const taskData of tasksToCreate) {
|
||||
const response = await context.request.post(`${appUrl}/api/task`, {
|
||||
data: taskData,
|
||||
});
|
||||
|
||||
if (response.ok()) {
|
||||
const task = await response.json();
|
||||
taskIds.push(task.id);
|
||||
createdTasks.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate to today page and wait for metrics to load
|
||||
await page.goto(`${appUrl}/today`);
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Check if Planned section exists using data-testid
|
||||
const plannedSection = page.getByTestId('planned-section');
|
||||
await expect(plannedSection).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Verify task with today flag is visible in the Planned section
|
||||
const withTodayFlagTask = plannedSection.getByTestId(
|
||||
`task-item-${taskIds[0]}`
|
||||
);
|
||||
await expect(withTodayFlagTask).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Verify task without today flag is NOT visible in Planned section
|
||||
const withoutFlagTask = plannedSection.getByTestId(
|
||||
`task-item-${taskIds[1]}`
|
||||
);
|
||||
await expect(withoutFlagTask).not.toBeVisible();
|
||||
|
||||
// Clean up created tasks
|
||||
for (const taskId of taskIds) {
|
||||
await context.request.delete(`${appUrl}/api/task/${taskId}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('should show overdue tasks in Overdue section', async ({
|
||||
page,
|
||||
context,
|
||||
baseURL,
|
||||
}) => {
|
||||
// Login first
|
||||
await loginViaUI(page, baseURL);
|
||||
|
||||
const appUrl =
|
||||
baseURL ?? process.env.APP_URL ?? 'http://localhost:8080';
|
||||
const timestamp = Date.now();
|
||||
|
||||
// Calculate yesterday's date
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const yesterdayStr = yesterday.toISOString().split('T')[0];
|
||||
|
||||
// Create an overdue task
|
||||
const response = await context.request.post(`${appUrl}/api/task`, {
|
||||
data: {
|
||||
name: `Overdue Task ${timestamp}`,
|
||||
due_date: yesterdayStr,
|
||||
priority: 2,
|
||||
}, // 2 = HIGH
|
||||
});
|
||||
|
||||
let taskId: string | null = null;
|
||||
if (response.ok()) {
|
||||
const task = await response.json();
|
||||
taskId = task.id;
|
||||
}
|
||||
|
||||
// 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');
|
||||
const isOverdueVisible = await overdueSection
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
|
||||
// If overdue section is visible, verify the task is in it
|
||||
if (isOverdueVisible) {
|
||||
const overdueTask = overdueSection.getByTestId(
|
||||
`task-item-${taskId}`
|
||||
);
|
||||
await expect(overdueTask).toBeVisible();
|
||||
} else {
|
||||
// If section not visible, the settings might be hiding it
|
||||
// Skip this assertion but don't fail the test
|
||||
console.log(
|
||||
'Overdue section not visible - may be hidden by settings'
|
||||
);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
if (taskId) {
|
||||
await context.request.delete(`${appUrl}/api/task/${taskId}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('should show tasks due today in Due Today section', async ({
|
||||
page,
|
||||
context,
|
||||
baseURL,
|
||||
}) => {
|
||||
// Login first
|
||||
await loginViaUI(page, baseURL);
|
||||
|
||||
const appUrl =
|
||||
baseURL ?? process.env.APP_URL ?? 'http://localhost:8080';
|
||||
const timestamp = Date.now();
|
||||
|
||||
// Calculate today's date
|
||||
const today = new Date();
|
||||
const todayStr = today.toISOString().split('T')[0];
|
||||
|
||||
// Create a task due today (but not in today plan)
|
||||
const response = await context.request.post(`${appUrl}/api/task`, {
|
||||
data: {
|
||||
name: `Due Today Task ${timestamp}`,
|
||||
due_date: todayStr,
|
||||
priority: 2,
|
||||
today: false,
|
||||
}, // 2 = HIGH
|
||||
});
|
||||
|
||||
let taskId: string | null = null;
|
||||
if (response.ok()) {
|
||||
const task = await response.json();
|
||||
taskId = task.id;
|
||||
}
|
||||
|
||||
// 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');
|
||||
const isDueTodayVisible = await dueTodaySection
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
|
||||
// If due today section is visible, verify the task is in it
|
||||
if (isDueTodayVisible) {
|
||||
const dueTodayTask = dueTodaySection.getByTestId(
|
||||
`task-item-${taskId}`
|
||||
);
|
||||
await expect(dueTodayTask).toBeVisible();
|
||||
} else {
|
||||
// If section not visible, the settings might be hiding it
|
||||
console.log(
|
||||
'Due Today section not visible - may be hidden by settings'
|
||||
);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
if (taskId) {
|
||||
await context.request.delete(`${appUrl}/api/task/${taskId}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow collapsing and expanding sections', async ({
|
||||
page,
|
||||
baseURL,
|
||||
}) => {
|
||||
// Login first
|
||||
await loginViaUI(page, baseURL);
|
||||
|
||||
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');
|
||||
const isPlannedVisible = await plannedHeader
|
||||
.isVisible({ timeout: 5000 })
|
||||
.catch(() => false);
|
||||
|
||||
if (isPlannedVisible) {
|
||||
// Verify header is clickable
|
||||
await expect(plannedHeader).toBeVisible();
|
||||
// This test just verifies the section exists and is clickable
|
||||
// Actual collapse/expand behavior depends on having tasks
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue