import { test, expect } from '@playwright/test'; test.describe('Note Grid - Drag and Drop', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); // Create multiple notes for testing drag and drop for (let i = 1; i <= 4; i++) { await page.click('input[placeholder="Take a note..."]'); await page.fill('input[placeholder="Title"]', `Note ${i}`); await page.fill('textarea[placeholder="Take a note..."]', `Content ${i}`); await page.click('button:has-text("Add")'); await page.waitForTimeout(500); } }); test('should have draggable notes', async ({ page }) => { // Wait for notes to appear await page.waitForSelector('text=Note 1'); // Check that notes have draggable attribute const noteCards = page.locator('[draggable="true"]'); const count = await noteCards.count(); expect(count).toBeGreaterThanOrEqual(4); }); test('should show cursor-move on note cards', async ({ page }) => { await page.waitForSelector('text=Note 1'); // Check CSS class for cursor-move const firstNote = page.locator('[draggable="true"]').first(); const className = await firstNote.getAttribute('class'); expect(className).toContain('cursor-move'); }); test('should change opacity when dragging', async ({ page }) => { await page.waitForSelector('text=Note 1'); const firstNote = page.locator('[draggable="true"]').first(); // Start drag const box = await firstNote.boundingBox(); if (box) { await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); await page.mouse.down(); // Check if opacity changed (isDragging class) await page.waitForTimeout(100); const className = await firstNote.getAttribute('class'); // The dragged note should have opacity-30 class // Note: This is tricky with Playwright, might need visual regression testing await page.mouse.up(); } }); test('should reorder notes when dropped on another note', async ({ page }) => { await page.waitForSelector('text=Note 1'); // Get initial order const notes = page.locator('[draggable="true"]'); const firstNoteText = await notes.first().textContent(); const secondNoteText = await notes.nth(1).textContent(); expect(firstNoteText).toContain('Note'); expect(secondNoteText).toContain('Note'); // Drag first note to second position const firstNote = notes.first(); const secondNote = notes.nth(1); const firstBox = await firstNote.boundingBox(); const secondBox = await secondNote.boundingBox(); if (firstBox && secondBox) { await page.mouse.move(firstBox.x + firstBox.width / 2, firstBox.y + firstBox.height / 2); await page.mouse.down(); await page.mouse.move(secondBox.x + secondBox.width / 2, secondBox.y + secondBox.height / 2); await page.mouse.up(); // Wait for reorder to complete await page.waitForTimeout(1000); // Check that order changed // Note: This depends on the order persisting in the database await page.reload(); await page.waitForSelector('text=Note'); // Verify the order changed (implementation dependent) } }); test('should work with pinned and unpinned notes separately', async ({ page }) => { await page.waitForSelector('text=Note 1'); // Pin first note const firstNote = page.locator('text=Note 1').first(); await firstNote.hover(); await page.click('button[title*="Pin"]:visible').first(); await page.waitForTimeout(500); // Check that "Pinned" section appears await expect(page.locator('text=Pinned')).toBeVisible(); // Verify note is in pinned section const pinnedSection = page.locator('h2:has-text("Pinned")').locator('..').locator('..'); await expect(pinnedSection.locator('text=Note 1')).toBeVisible(); }); test('should not mix pinned and unpinned notes when dragging', async ({ page }) => { await page.waitForSelector('text=Note 1'); // Pin first note const firstNote = page.locator('text=Note 1').first(); await firstNote.hover(); await page.click('button[title*="Pin"]:visible').first(); await page.waitForTimeout(500); // Should have both Pinned and Others sections await expect(page.locator('text=Pinned')).toBeVisible(); await expect(page.locator('text=Others')).toBeVisible(); // Count notes in each section const pinnedNotes = page.locator('h2:has-text("Pinned") ~ div [draggable="true"]'); const unpinnedNotes = page.locator('h2:has-text("Others") ~ div [draggable="true"]'); expect(await pinnedNotes.count()).toBeGreaterThanOrEqual(1); expect(await unpinnedNotes.count()).toBeGreaterThanOrEqual(3); }); test('should persist note order after page reload', async ({ page }) => { await page.waitForSelector('text=Note 1'); // Get initial order const notes = page.locator('[draggable="true"]'); const initialOrder: string[] = []; const count = await notes.count(); for (let i = 0; i < Math.min(count, 4); i++) { const text = await notes.nth(i).textContent(); if (text) initialOrder.push(text); } // Reload page await page.reload(); await page.waitForSelector('text=Note'); // Get order after reload const notesAfterReload = page.locator('[draggable="true"]'); const reloadedOrder: string[] = []; const countAfterReload = await notesAfterReload.count(); for (let i = 0; i < Math.min(countAfterReload, 4); i++) { const text = await notesAfterReload.nth(i).textContent(); if (text) reloadedOrder.push(text); } // Order should be the same expect(reloadedOrder.length).toBe(initialOrder.length); }); test.afterEach(async ({ page }) => { // Clean up created notes const notes = page.locator('[draggable="true"]'); const count = await notes.count(); for (let i = 0; i < count; i++) { const note = notes.first(); await note.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); } }); });