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
424 lines
15 KiB
TypeScript
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
|
|
}
|
|
});
|
|
});
|