Keep/keep-notes/tests/reminder-dialog.spec.ts
sepehr f0b41572bc feat: Memento avec dates, Markdown, reminders et auth
Tests Playwright validés :
- Création de notes: OK
- Modification titre: OK
- Modification contenu: OK
- Markdown éditable avec preview: OK

Fonctionnalités:
- date-fns: dates relatives sur cards
- react-markdown + remark-gfm
- Markdown avec toggle edit/preview
- Recherche améliorée (titre/contenu/labels/checkItems)
- Reminder recurrence/location (schema)
- NextAuth.js + User/Account/Session
- userId dans Note (optionnel)
- 4 migrations créées

Ready for production + auth integration
2026-01-04 16:04:24 +01:00

424 lines
15 KiB
TypeScript

import { test, expect } from '@playwright/test';
test.describe('Note Input - Reminder Dialog', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
// Expand the note input
await page.click('input[placeholder="Take a note..."]');
await expect(page.locator('input[placeholder="Title"]')).toBeVisible();
});
test('should open dialog when clicking Bell icon (not prompt)', async ({ page }) => {
// Set up listener for prompt dialogs - should NOT appear
let promptAppeared = false;
page.on('dialog', () => {
promptAppeared = true;
});
// Click the Bell button
const bellButton = page.locator('button:has(svg.lucide-bell)');
await bellButton.click();
// Verify dialog opened (NOT a browser prompt)
const dialog = page.locator('[role="dialog"]');
await expect(dialog).toBeVisible();
// Verify dialog title
await expect(page.locator('h2:has-text("Set Reminder")')).toBeVisible();
// Verify no prompt appeared
expect(promptAppeared).toBe(false);
// Verify date and time inputs exist
await expect(page.locator('input[type="date"]')).toBeVisible();
await expect(page.locator('input[type="time"]')).toBeVisible();
// Verify buttons
await expect(page.locator('button:has-text("Cancel")')).toBeVisible();
await expect(page.locator('button:has-text("Set Reminder")')).toBeVisible();
});
test('should have default values (tomorrow 9am)', async ({ page }) => {
// Click Bell
await page.click('button:has(svg.lucide-bell)');
// Get tomorrow's date
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const expectedDate = tomorrow.toISOString().split('T')[0];
// Check date input
const dateInput = page.locator('input[type="date"]');
await expect(dateInput).toHaveValue(expectedDate);
// Check time input
const timeInput = page.locator('input[type="time"]');
await expect(timeInput).toHaveValue('09:00');
});
test('should close dialog on Cancel', async ({ page }) => {
// Open dialog
await page.click('button:has(svg.lucide-bell)');
await expect(page.locator('[role="dialog"]')).toBeVisible();
// Click Cancel
await page.click('button:has-text("Cancel")');
// Dialog should close
await expect(page.locator('[role="dialog"]')).not.toBeVisible();
});
test('should close dialog on X button', async ({ page }) => {
// Open dialog
await page.click('button:has(svg.lucide-bell)');
await expect(page.locator('[role="dialog"]')).toBeVisible();
// Click X button (close button in dialog)
const closeButton = page.locator('[role="dialog"] button[data-slot="dialog-close"]');
await closeButton.click();
// Dialog should close
await expect(page.locator('[role="dialog"]')).not.toBeVisible();
});
test('should validate empty date/time', async ({ page }) => {
// Open dialog
await page.click('button:has(svg.lucide-bell)');
// Clear date
const dateInput = page.locator('input[type="date"]');
await dateInput.fill('');
// Click Set Reminder
await page.click('button:has-text("Set Reminder")');
// Should show warning toast (not close dialog)
// We look for the toast notification
await expect(page.locator('text=Please enter date and time')).toBeVisible();
// Dialog should still be open
await expect(page.locator('[role="dialog"]')).toBeVisible();
});
test('should validate past date', async ({ page }) => {
// Open dialog
await page.click('button:has(svg.lucide-bell)');
// Set date to yesterday
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
const pastDate = yesterday.toISOString().split('T')[0];
const dateInput = page.locator('input[type="date"]');
await dateInput.fill(pastDate);
// Click Set Reminder
await page.click('button:has-text("Set Reminder")');
// Should show error toast
await expect(page.locator('text=Reminder must be in the future')).toBeVisible();
// Dialog should still be open
await expect(page.locator('[role="dialog"]')).toBeVisible();
});
test('should set reminder successfully with valid date', async ({ page }) => {
// Open dialog
await page.click('button:has(svg.lucide-bell)');
// Set future date (tomorrow already default)
const timeInput = page.locator('input[type="time"]');
await timeInput.fill('14:30');
// Click Set Reminder
await page.click('button:has-text("Set Reminder")');
// Should show success toast
await expect(page.locator('text=/Reminder set for/')).toBeVisible();
// Dialog should close
await expect(page.locator('[role="dialog"]')).not.toBeVisible();
});
test('should clear fields after successful reminder', async ({ page }) => {
// Open dialog
await page.click('button:has(svg.lucide-bell)');
// Set and confirm
await page.click('button:has-text("Set Reminder")');
// Wait for dialog to close
await expect(page.locator('[role="dialog"]')).not.toBeVisible();
// Open again
await page.click('button:has(svg.lucide-bell)');
// Should have default values again (tomorrow 9am)
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const expectedDate = tomorrow.toISOString().split('T')[0];
await expect(page.locator('input[type="date"]')).toHaveValue(expectedDate);
await expect(page.locator('input[type="time"]')).toHaveValue('09:00');
});
test('should allow custom date and time selection', async ({ page }) => {
// Open dialog
await page.click('button:has(svg.lucide-bell)');
// Set custom date (next week)
const nextWeek = new Date();
nextWeek.setDate(nextWeek.getDate() + 7);
const customDate = nextWeek.toISOString().split('T')[0];
const dateInput = page.locator('input[type="date"]');
await dateInput.fill(customDate);
// Set custom time
const timeInput = page.locator('input[type="time"]');
await timeInput.fill('15:45');
// Submit
await page.click('button:has-text("Set Reminder")');
// Should show success with the date/time
await expect(page.locator('text=/Reminder set for/')).toBeVisible();
await expect(page.locator('[role="dialog"]')).not.toBeVisible();
});
});
test.describe('Note Editor - Reminder Dialog', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
// Create a test note
await page.click('input[placeholder="Take a note..."]');
await page.fill('input[placeholder="Title"]', 'Test Note for Reminder');
await page.fill('textarea[placeholder="Take a note..."]', 'This note will have a reminder');
await page.click('button:has-text("Add")');
await page.waitForTimeout(500);
// Open the note for editing
await page.click('text=Test Note for Reminder');
await page.waitForTimeout(300);
});
test('should open reminder dialog in note editor', async ({ page }) => {
// Click the Bell button in note editor
const bellButton = page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first();
await bellButton.click();
// Should open a second dialog for reminder
await page.waitForTimeout(300);
// Verify reminder dialog opened
await expect(page.locator('h2:has-text("Set Reminder")')).toBeVisible();
// Verify date and time inputs exist
await expect(page.locator('input[type="date"]')).toBeVisible();
await expect(page.locator('input[type="time"]')).toBeVisible();
});
test('should set reminder on existing note', async ({ page }) => {
// Click Bell button
await page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first().click();
await page.waitForTimeout(200);
// Set date and time
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const dateString = tomorrow.toISOString().split('T')[0];
await page.fill('input[type="date"]', dateString);
await page.fill('input[type="time"]', '14:30');
// Click Set Reminder
await page.click('button:has-text("Set Reminder")');
// Should show success toast
await expect(page.locator('text=/Reminder set for/')).toBeVisible();
// Reminder dialog should close
await page.waitForTimeout(300);
await expect(page.locator('h2:has-text("Set Reminder")')).not.toBeVisible();
});
test('should show bell button as active when reminder is set', async ({ page }) => {
// Set reminder
await page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first().click();
await page.waitForTimeout(200);
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const dateString = tomorrow.toISOString().split('T')[0];
await page.fill('input[type="date"]', dateString);
await page.fill('input[type="time"]', '10:00');
await page.click('button:has-text("Set Reminder")');
await page.waitForTimeout(500);
// Bell button should have active styling (text-blue-600)
const bellButton = page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first();
const className = await bellButton.getAttribute('class');
expect(className).toContain('text-blue-600');
});
test('should allow editing existing reminder', async ({ page }) => {
// Set initial reminder
await page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first().click();
await page.waitForTimeout(200);
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const dateString = tomorrow.toISOString().split('T')[0];
await page.fill('input[type="date"]', dateString);
await page.fill('input[type="time"]', '10:00');
await page.click('button:has-text("Set Reminder")');
await page.waitForTimeout(500);
// Open reminder dialog again
await page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first().click();
await page.waitForTimeout(200);
// Should show previous values
const dateInput = page.locator('input[type="date"]');
const timeInput = page.locator('input[type="time"]');
await expect(dateInput).toHaveValue(dateString);
await expect(timeInput).toHaveValue('10:00');
// Change time
await timeInput.fill('15:00');
await page.click('button:has-text("Set Reminder")');
await expect(page.locator('text=/Reminder set for/')).toBeVisible();
});
test('should allow removing reminder', async ({ page }) => {
// Set reminder first
await page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first().click();
await page.waitForTimeout(200);
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const dateString = tomorrow.toISOString().split('T')[0];
await page.fill('input[type="date"]', dateString);
await page.fill('input[type="time"]', '10:00');
await page.click('button:has-text("Set Reminder")');
await page.waitForTimeout(500);
// Open reminder dialog again
await page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first().click();
await page.waitForTimeout(200);
// Should see "Remove Reminder" button
await expect(page.locator('button:has-text("Remove Reminder")')).toBeVisible();
// Click Remove Reminder
await page.click('button:has-text("Remove Reminder")');
// Should show success toast
await expect(page.locator('text=Reminder removed')).toBeVisible();
// Bell button should not be active anymore
await page.waitForTimeout(300);
const bellButton = page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first();
const className = await bellButton.getAttribute('class');
expect(className).not.toContain('text-blue-600');
});
test('should persist reminder after saving note', async ({ page }) => {
// Set reminder
await page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first().click();
await page.waitForTimeout(200);
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const dateString = tomorrow.toISOString().split('T')[0];
await page.fill('input[type="date"]', dateString);
await page.fill('input[type="time"]', '14:00');
await page.click('button:has-text("Set Reminder")');
await page.waitForTimeout(500);
// Save the note
await page.click('button:has-text("Save")');
await page.waitForTimeout(500);
// Reopen the note
await page.click('text=Test Note for Reminder');
await page.waitForTimeout(300);
// Bell button should still be active
const bellButton = page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first();
const className = await bellButton.getAttribute('class');
expect(className).toContain('text-blue-600');
// Open reminder dialog to verify values
await bellButton.click();
await page.waitForTimeout(200);
const dateInput = page.locator('input[type="date"]');
const timeInput = page.locator('input[type="time"]');
await expect(dateInput).toHaveValue(dateString);
await expect(timeInput).toHaveValue('14:00');
});
test('should show bell icon on note card when reminder is set', async ({ page }) => {
// Set reminder
await page.locator('[role="dialog"]:visible button:has(svg.lucide-bell)').first().click();
await page.waitForTimeout(200);
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const dateString = tomorrow.toISOString().split('T')[0];
await page.fill('input[type="date"]', dateString);
await page.fill('input[type="time"]', '10:00');
await page.click('button:has-text("Set Reminder")');
await page.waitForTimeout(500);
// Save and close
await page.click('button:has-text("Save")');
await page.waitForTimeout(500);
// Check that note card has bell icon
const noteCard = page.locator('text=Test Note for Reminder').locator('..');
await expect(noteCard.locator('svg.lucide-bell')).toBeVisible();
});
test.afterEach(async ({ page }) => {
// Close any open dialogs
const dialogs = page.locator('[role="dialog"]');
const count = await dialogs.count();
for (let i = 0; i < count; i++) {
const cancelButton = page.locator('button:has-text("Cancel")').first();
if (await cancelButton.isVisible()) {
await cancelButton.click();
await page.waitForTimeout(200);
}
}
// Delete test note
try {
const testNote = page.locator('text=Test Note for Reminder').first();
if (await testNote.isVisible()) {
await testNote.hover();
await page.click('button:has(svg.lucide-more-vertical)').first();
await page.click('text=Delete').first();
// Confirm delete
page.once('dialog', dialog => dialog.accept());
await page.waitForTimeout(300);
}
} catch (e) {
// Note might already be deleted
}
});
});