Fix tests and add changelog
This commit is contained in:
@@ -1,42 +1,109 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { test, expect, request } from '@playwright/test';
|
||||
|
||||
test.describe('Note Grid - Drag and Drop', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
test.beforeAll(async ({ request }) => {
|
||||
console.log('[CLEANUP] beforeAll: Cleaning up any existing test notes...');
|
||||
|
||||
// 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);
|
||||
// Clean up any existing test notes from previous runs
|
||||
try {
|
||||
const response = await request.get('http://localhost:3000/api/notes');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && data.data) {
|
||||
const testNotes = data.data.filter((note: any) =>
|
||||
note.title?.startsWith('test-') &&
|
||||
note.content?.startsWith('Content ')
|
||||
);
|
||||
|
||||
console.log(`[CLEANUP] beforeAll: Found ${testNotes.length} test notes to delete`);
|
||||
|
||||
for (const note of testNotes) {
|
||||
try {
|
||||
await request.delete(`http://localhost:3000/api/notes?id=${note.id}`);
|
||||
console.log(`[CLEANUP] beforeAll: Deleted note ${note.id}`);
|
||||
} catch (error) {
|
||||
console.log(`[CLEANUP] beforeAll: Failed to delete note ${note.id}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('[CLEANUP] beforeAll: Error fetching notes for cleanup', error);
|
||||
}
|
||||
});
|
||||
|
||||
test('should have draggable notes', async ({ page }) => {
|
||||
// Wait for notes to appear
|
||||
await page.waitForSelector('text=Note 1');
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Generate unique timestamp for this test run to avoid conflicts
|
||||
const timestamp = Date.now();
|
||||
const testId = `test-${timestamp}`;
|
||||
|
||||
// Check that notes have draggable attribute
|
||||
const noteCards = page.locator('[draggable="true"]');
|
||||
await page.goto('/');
|
||||
|
||||
// Wait for page to fully load
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Log initial note count before creating new notes
|
||||
const initialNotes = page.locator('[data-draggable="true"]');
|
||||
const initialCount = await initialNotes.count();
|
||||
console.log(`[DEBUG] [${testId}] Initial note count: ${initialCount}`);
|
||||
|
||||
// Create multiple notes for testing drag and drop with unique identifiers
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
const noteTitle = `${testId}-Note ${i}`;
|
||||
const noteContent = `${testId}-Content ${i}`;
|
||||
|
||||
console.log(`[DEBUG] [${testId}] Creating ${noteTitle}`);
|
||||
await page.click('input[placeholder="Take a note..."]');
|
||||
await page.fill('input[placeholder="Title"]', noteTitle);
|
||||
await page.fill('textarea[placeholder="Take a note..."]', noteContent);
|
||||
await page.click('button:has-text("Add")');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Log note count after each creation
|
||||
const currentCount = await page.locator('[data-draggable="true"]').count();
|
||||
console.log(`[DEBUG] [${testId}] Note count after creating ${noteTitle}: ${currentCount}`);
|
||||
}
|
||||
|
||||
// Log final note count
|
||||
const finalCount = await page.locator('[data-draggable="true"]').count();
|
||||
console.log(`[DEBUG] [${testId}] Final note count after all creations: ${finalCount}`);
|
||||
});
|
||||
|
||||
test('should have draggable notes', async ({ page }) => {
|
||||
console.log('[DEBUG] Test: should have draggable notes');
|
||||
|
||||
// Wait for notes to appear (use a more flexible selector that matches pattern)
|
||||
await page.waitForSelector('[data-draggable="true"]');
|
||||
|
||||
// Check that notes have data-draggable attribute (dnd-kit uses this)
|
||||
const noteCards = page.locator('[data-draggable="true"]');
|
||||
const count = await noteCards.count();
|
||||
console.log(`[DEBUG] Found ${count} notes with data-draggable="true"`);
|
||||
|
||||
// Log first few notes details
|
||||
for (let i = 0; i < Math.min(count, 3); i++) {
|
||||
const note = noteCards.nth(i);
|
||||
const text = await note.textContent();
|
||||
const draggableAttr = await note.getAttribute('data-draggable');
|
||||
console.log(`[DEBUG] Note ${i}: "${text?.substring(0, 50)}", data-draggable="${draggableAttr}"`);
|
||||
}
|
||||
|
||||
expect(count).toBeGreaterThanOrEqual(4);
|
||||
});
|
||||
|
||||
test('should show cursor-move on note cards', async ({ page }) => {
|
||||
await page.waitForSelector('text=Note 1');
|
||||
await page.waitForSelector('[data-draggable="true"]');
|
||||
|
||||
// Check CSS class for cursor-move
|
||||
const firstNote = page.locator('[draggable="true"]').first();
|
||||
const className = await firstNote.getAttribute('class');
|
||||
// Check CSS class for cursor-move on the note card inside
|
||||
const firstNote = page.locator('[data-draggable="true"]').first();
|
||||
const noteCard = firstNote.locator('.note-card-main');
|
||||
const className = await noteCard.getAttribute('class');
|
||||
expect(className).toContain('cursor-move');
|
||||
});
|
||||
|
||||
test('should change opacity when dragging', async ({ page }) => {
|
||||
await page.waitForSelector('text=Note 1');
|
||||
await page.waitForSelector('[data-draggable="true"]');
|
||||
|
||||
const firstNote = page.locator('[draggable="true"]').first();
|
||||
const firstNote = page.locator('[data-draggable="true"]').first();
|
||||
|
||||
// Start drag
|
||||
const box = await firstNote.boundingBox();
|
||||
@@ -44,98 +111,109 @@ test.describe('Note Grid - Drag and Drop', () => {
|
||||
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);
|
||||
// Move to trigger drag
|
||||
await page.mouse.move(box.x + box.width / 2 + 50, box.y + box.height / 2 + 50);
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
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
|
||||
// Check if opacity changed (style should have opacity: 0.5)
|
||||
const style = await firstNote.getAttribute('style');
|
||||
expect(style).toContain('opacity');
|
||||
|
||||
await page.mouse.up();
|
||||
}
|
||||
});
|
||||
|
||||
test('should reorder notes when dropped on another note', async ({ page }) => {
|
||||
await page.waitForSelector('text=Note 1');
|
||||
console.log('[DEBUG] Test: should reorder notes when dropped on another note');
|
||||
|
||||
// Wait for notes to be fully loaded and drag-and-drop initialized
|
||||
await page.waitForSelector('[data-draggable="true"]', { state: 'attached' });
|
||||
await page.waitForTimeout(500); // Extra wait for dnd-kit to initialize
|
||||
|
||||
// Get initial order
|
||||
const notes = page.locator('[draggable="true"]');
|
||||
const notes = page.locator('[data-draggable="true"]');
|
||||
const firstNoteText = await notes.first().textContent();
|
||||
const secondNoteText = await notes.nth(1).textContent();
|
||||
|
||||
expect(firstNoteText).toContain('Note');
|
||||
expect(secondNoteText).toContain('Note');
|
||||
console.log(`[DEBUG] Initial first note: ${firstNoteText?.substring(0, 30)}`);
|
||||
console.log(`[DEBUG] Initial second note: ${secondNoteText?.substring(0, 30)}`);
|
||||
|
||||
// Drag first note to second position
|
||||
expect(firstNoteText).toMatch(/Note \d+/);
|
||||
expect(secondNoteText).toMatch(/Note \d+/);
|
||||
|
||||
// Use dragTo for more reliable drag and drop
|
||||
const firstNote = notes.first();
|
||||
const secondNote = notes.nth(1);
|
||||
|
||||
const firstBox = await firstNote.boundingBox();
|
||||
const secondBox = await secondNote.boundingBox();
|
||||
console.log('[DEBUG] Starting drag operation...');
|
||||
await firstNote.dragTo(secondNote);
|
||||
console.log('[DEBUG] Drag operation completed');
|
||||
|
||||
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)
|
||||
}
|
||||
// Wait for reorder to complete and database to update
|
||||
await page.waitForTimeout(2000); // Increased wait time for async operations
|
||||
|
||||
// Check that order changed
|
||||
// Note: This depends on order persisting in the database
|
||||
console.log('[DEBUG] Reloading page to verify persistence...');
|
||||
await page.reload();
|
||||
await page.waitForSelector('[data-draggable="true"]', { state: 'attached' });
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Verify that order changed (implementation dependent)
|
||||
console.log('[DEBUG] Page reloaded, checking order...');
|
||||
});
|
||||
|
||||
test('should work with pinned and unpinned notes separately', async ({ page }) => {
|
||||
await page.waitForSelector('text=Note 1');
|
||||
await page.waitForSelector('[data-draggable="true"]');
|
||||
|
||||
// Pin first note
|
||||
const firstNote = page.locator('text=Note 1').first();
|
||||
await firstNote.hover();
|
||||
await page.click('button[title*="Pin"]:visible').first();
|
||||
// Pin first note by hovering and clicking pin button
|
||||
const firstNoteCard = page.locator('[data-draggable="true"]').first();
|
||||
await firstNoteCard.hover();
|
||||
|
||||
// Click the pin button
|
||||
const pinButton = firstNoteCard.locator('button[title="Pin"]');
|
||||
await pinButton.click();
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Check that "Pinned" section appears
|
||||
await expect(page.locator('text=Pinned')).toBeVisible();
|
||||
await expect(page.locator('h2:has-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();
|
||||
const pinnedSection = page.locator('h2:has-text("Pinned")').locator('..').locator('div[data-draggable="true"]');
|
||||
expect(await pinnedSection.count()).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
test('should not mix pinned and unpinned notes when dragging', async ({ page }) => {
|
||||
await page.waitForSelector('text=Note 1');
|
||||
await page.waitForSelector('[data-draggable="true"]');
|
||||
|
||||
// Pin first note
|
||||
const firstNote = page.locator('text=Note 1').first();
|
||||
await firstNote.hover();
|
||||
await page.click('button[title*="Pin"]:visible').first();
|
||||
// Pin first note by hovering and clicking pin button
|
||||
const firstNoteCard = page.locator('[data-draggable="true"]').first();
|
||||
await firstNoteCard.hover();
|
||||
|
||||
// Click the pin button
|
||||
const pinButton = firstNoteCard.locator('button[title="Pin"]');
|
||||
await pinButton.click();
|
||||
|
||||
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();
|
||||
await expect(page.locator('h2:has-text("Pinned")')).toBeVisible();
|
||||
await expect(page.locator('h2:has-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"]');
|
||||
// Count notes in each section using data-draggable attribute
|
||||
const pinnedNotes = page.locator('h2:has-text("Pinned") ~ div [data-draggable="true"]');
|
||||
const unpinnedNotes = page.locator('h2:has-text("Others") ~ div [data-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');
|
||||
await page.waitForSelector('[data-draggable="true"]');
|
||||
|
||||
// Get initial order
|
||||
const notes = page.locator('[draggable="true"]');
|
||||
const notes = page.locator('[data-draggable="true"]');
|
||||
const initialOrder: string[] = [];
|
||||
const count = await notes.count();
|
||||
|
||||
@@ -146,10 +224,10 @@ test.describe('Note Grid - Drag and Drop', () => {
|
||||
|
||||
// Reload page
|
||||
await page.reload();
|
||||
await page.waitForSelector('text=Note');
|
||||
await page.waitForSelector('[data-draggable="true"]');
|
||||
|
||||
// Get order after reload
|
||||
const notesAfterReload = page.locator('[draggable="true"]');
|
||||
const notesAfterReload = page.locator('[data-draggable="true"]');
|
||||
const reloadedOrder: string[] = [];
|
||||
const countAfterReload = await notesAfterReload.count();
|
||||
|
||||
@@ -162,20 +240,63 @@ test.describe('Note Grid - Drag and Drop', () => {
|
||||
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();
|
||||
test.afterEach(async ({ page, request }) => {
|
||||
console.log('[CLEANUP] Starting cleanup...');
|
||||
|
||||
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();
|
||||
// Clean up created notes via API - more reliable than UI interaction
|
||||
try {
|
||||
const response = await request.get('http://localhost:3000/api/notes');
|
||||
const data = await response.json();
|
||||
|
||||
// Confirm delete
|
||||
page.once('dialog', dialog => dialog.accept());
|
||||
await page.waitForTimeout(300);
|
||||
if (data.success && data.data) {
|
||||
const testNotes = data.data.filter((note: any) =>
|
||||
note.title?.startsWith('test-') &&
|
||||
note.content?.startsWith('Content ')
|
||||
);
|
||||
|
||||
console.log(`[CLEANUP] Found ${testNotes.length} test notes to delete via API`);
|
||||
|
||||
for (const note of testNotes) {
|
||||
try {
|
||||
await request.delete(`http://localhost:3000/api/notes?id=${note.id}`);
|
||||
console.log(`[CLEANUP] Deleted note ${note.id}`);
|
||||
} catch (error) {
|
||||
console.log(`[CLEANUP] Failed to delete note ${note.id}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('[CLEANUP] Error fetching notes for cleanup', error);
|
||||
}
|
||||
});
|
||||
|
||||
test.afterAll(async ({ request }) => {
|
||||
console.log('[CLEANUP] afterAll: Final cleanup of any remaining test notes...');
|
||||
|
||||
// Final cleanup to ensure no test notes remain
|
||||
try {
|
||||
const response = await request.get('http://localhost:3000/api/notes');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && data.data) {
|
||||
const testNotes = data.data.filter((note: any) =>
|
||||
note.title?.startsWith('test-') &&
|
||||
note.content?.startsWith('Content ')
|
||||
);
|
||||
|
||||
console.log(`[CLEANUP] afterAll: Found ${testNotes.length} remaining test notes to delete`);
|
||||
|
||||
for (const note of testNotes) {
|
||||
try {
|
||||
await request.delete(`http://localhost:3000/api/notes?id=${note.id}`);
|
||||
console.log(`[CLEANUP] afterAll: Deleted note ${note.id}`);
|
||||
} catch (error) {
|
||||
console.log(`[CLEANUP] afterAll: Failed to delete note ${note.id}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('[CLEANUP] afterAll: Error fetching notes for final cleanup', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user