- Rename directory keep-notes -> memento-note with all code references - Prisma: SQLite -> PostgreSQL (both app and MCP server schemas) - Sync MCP schema with main app (add missing fields, relations, indexes) - Delete 17 SQLite migrations (clean slate for PostgreSQL) - Remove SQLite dependencies (@libsql/client, better-sqlite3, etc.) - Fix MCP server: hardcoded Windows DB paths -> DATABASE_URL env var - Fix MCP server: .dockerignore excluded index-sse.js (SSE mode broken) - MCP Dockerfile: node:20 -> node:22 - Docker Compose: add postgres service, remove SQLite volume - Generate favicon.ico, icon-192.png, icon-512.png, apple-icon.png - Update layout.tsx icons and manifest.json for PNG icons - Update all .env files for PostgreSQL - Rewrite README.md with updated sections - Remove mcp-server/node_modules and prisma/client-generated from git tracking Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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);
|
|
}
|
|
});
|
|
});
|