## Bug Fixes ### Note Card Actions - Fix broken size change functionality (missing state declaration) - Implement React 19 useOptimistic for instant UI feedback - Add startTransition for non-blocking updates - Ensure smooth animations without page refresh - All note actions now work: pin, archive, color, size, checklist ### Markdown LaTeX Rendering - Add remark-math and rehype-katex plugins - Support inline equations with dollar sign syntax - Support block equations with double dollar sign syntax - Import KaTeX CSS for proper styling - Equations now render correctly instead of showing raw LaTeX ## Technical Details - Replace undefined currentNote references with optimistic state - Add optimistic updates before server actions for instant feedback - Use router.refresh() in transitions for smart cache invalidation - Install remark-math, rehype-katex, and katex packages ## Testing - Build passes successfully with no TypeScript errors - Dev server hot-reloads changes correctly
133 lines
4.8 KiB
TypeScript
133 lines
4.8 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Collaboration Feature', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Login before each test
|
|
await page.goto('http://localhost:3000/login');
|
|
await page.fill('input[name="email"]', 'test@example.com');
|
|
await page.fill('input[name="password"]', 'password123');
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL('http://localhost:3000/');
|
|
});
|
|
|
|
test('COLLAB-2: Add collaborator to existing note', async ({ page, context }) => {
|
|
// Start from the main page
|
|
await page.goto('http://localhost:3000/');
|
|
|
|
// Find a note card (should have at least one)
|
|
const noteCard = page.locator('[data-testid="note-card"]').first();
|
|
await expect(noteCard).toBeVisible();
|
|
|
|
// Open collaborator dialog from the note menu
|
|
await noteCard.hover();
|
|
await page.click('[aria-label="More options"]');
|
|
|
|
// Click "Share with collaborators"
|
|
await page.click('text=Share with collaborators');
|
|
|
|
// Verify dialog opens
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog).toBeVisible();
|
|
await expect(dialog.locator('text=Share with collaborators')).toBeVisible();
|
|
|
|
// Try to add a collaborator (note: user may not exist)
|
|
await page.fill('input[type="email"]', 'collaborator@example.com');
|
|
await page.click('button:has-text("Invite")');
|
|
|
|
// Wait for response (either success or error toast)
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Verify either:
|
|
// 1. Success: User added to list
|
|
// 2. Error: User not found message
|
|
const collaboratorInList = page.locator('text=collaborator@example.com');
|
|
const errorMessage = page.locator('.toast:has-text("User not found")');
|
|
|
|
const collaboratorAdded = await collaboratorInList.count() > 0;
|
|
const errorShown = await errorMessage.count() > 0;
|
|
|
|
expect(collaboratorAdded || errorShown).toBeTruthy();
|
|
});
|
|
|
|
test('COLLAB-2: Remove collaborator from note', async ({ page }) => {
|
|
// This test assumes a note with collaborators exists
|
|
await page.goto('http://localhost:3000/');
|
|
|
|
// Find a note card
|
|
const noteCard = page.locator('[data-testid="note-card"]').first();
|
|
await noteCard.hover();
|
|
await page.click('[aria-label="More options"]');
|
|
await page.click('text=Share with collaborators');
|
|
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// Check if there are any collaborators listed
|
|
const collaboratorItems = dialog.locator('[data-testid="collaborator-item"]');
|
|
const count = await collaboratorItems.count();
|
|
|
|
if (count > 0) {
|
|
// Get initial count
|
|
const initialCount = count;
|
|
|
|
// Remove first collaborator
|
|
await collaboratorItems.first().hover();
|
|
await collaboratorItems.first().locator('button[aria-label="Remove"]').click();
|
|
|
|
// Wait for update
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Verify count decreased
|
|
const newCount = await dialog.locator('[data-testid="collaborator-item"]').count();
|
|
expect(newCount).toBeLessThan(initialCount);
|
|
} else {
|
|
// Skip test if no collaborators exist
|
|
test.skip(true, 'No collaborators to remove');
|
|
}
|
|
});
|
|
|
|
test('COLLAB-2: View collaborators list', async ({ page }) => {
|
|
await page.goto('http://localhost:3000/');
|
|
|
|
const noteCard = page.locator('[data-testid="note-card"]').first();
|
|
await noteCard.hover();
|
|
await page.click('[aria-label="More options"]');
|
|
await page.click('text=Share with collaborators');
|
|
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// Verify "People with access" section exists
|
|
await expect(dialog.locator('text=People with access')).toBeVisible();
|
|
|
|
// Check for owner badge
|
|
const ownerBadge = dialog.locator('text=Owner');
|
|
await expect(ownerBadge).toBeVisible();
|
|
});
|
|
|
|
test('COLLAB-2: Non-owner cannot remove collaborators', async ({ page }) => {
|
|
// This test requires setup with a shared note where current user is not owner
|
|
// For now, we'll test the UI shows correct state
|
|
|
|
await page.goto('http://localhost:3000/');
|
|
|
|
const noteCard = page.locator('[data-testid="note-card"]').first();
|
|
await noteCard.hover();
|
|
await page.click('[aria-label="More options"]');
|
|
await page.click('text=Share with collaborators');
|
|
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// Verify "Only the owner can manage collaborators" message appears if not owner
|
|
const description = dialog.locator('text=You have access to this note');
|
|
const isNotOwner = await description.count() > 0;
|
|
|
|
if (isNotOwner) {
|
|
// Verify no remove buttons are visible
|
|
const removeButtons = dialog.locator('button[aria-label="Remove"]');
|
|
expect(await removeButtons.count()).toBe(0);
|
|
}
|
|
});
|
|
});
|