/** * Database seeding fixture for E2E tests * * Uses direct SQL for cleanup and drizzle for typed inserts. * Each test gets a known starting state that can be asserted against. * * Note: drizzle-seed is installed but we use manual cleanup for better control * and to avoid type compatibility issues with reset(). */ import { test as base } from '@playwright/test'; import Database from 'better-sqlite3'; import { drizzle } from 'drizzle-orm/better-sqlite3'; import * as schema from '../../../src/lib/server/db/schema'; // Test database path - same as application for E2E tests const DATA_DIR = process.env.DATA_DIR || './data'; const DB_PATH = `${DATA_DIR}/taskplaner.db`; // Known test data with predictable IDs for assertions export const testData = { entries: [ { id: 'test-entry-001', title: null, content: 'Buy groceries for the week', type: 'task' as const, status: 'open' as const, pinned: false, dueDate: '2026-02-10', createdAt: '2026-02-01T10:00:00.000Z', updatedAt: '2026-02-01T10:00:00.000Z' }, { id: 'test-entry-002', title: null, content: 'Completed task from yesterday', type: 'task' as const, status: 'done' as const, pinned: false, dueDate: null, createdAt: '2026-02-02T09:00:00.000Z', updatedAt: '2026-02-02T15:00:00.000Z' }, { id: 'test-entry-003', title: null, content: 'Important pinned thought about project architecture', type: 'thought' as const, status: null, pinned: true, dueDate: null, createdAt: '2026-02-01T08:00:00.000Z', updatedAt: '2026-02-01T08:00:00.000Z' }, { id: 'test-entry-004', title: null, content: 'Meeting notes with stakeholders', type: 'thought' as const, status: null, pinned: false, dueDate: null, createdAt: '2026-02-03T14:00:00.000Z', updatedAt: '2026-02-03T14:00:00.000Z' }, { id: 'test-entry-005', title: null, content: 'Review pull request for feature branch', type: 'task' as const, status: 'open' as const, pinned: false, dueDate: '2026-02-05', createdAt: '2026-02-03T11:00:00.000Z', updatedAt: '2026-02-03T11:00:00.000Z' } ], tags: [ { id: 'test-tag-001', name: 'work', createdAt: '2026-02-01T00:00:00.000Z' }, { id: 'test-tag-002', name: 'personal', createdAt: '2026-02-01T00:00:00.000Z' }, { id: 'test-tag-003', name: 'urgent', createdAt: '2026-02-01T00:00:00.000Z' } ], entryTags: [ { entryId: 'test-entry-001', tagId: 'test-tag-002' }, // groceries -> personal { entryId: 'test-entry-003', tagId: 'test-tag-001' }, // architecture -> work { entryId: 'test-entry-004', tagId: 'test-tag-001' }, // meeting notes -> work { entryId: 'test-entry-005', tagId: 'test-tag-001' }, // PR review -> work { entryId: 'test-entry-005', tagId: 'test-tag-003' } // PR review -> urgent ] }; /** * Clear all data from the database (respecting foreign key order) */ function clearDatabase(sqlite: Database.Database) { // Delete in order that respects foreign key constraints sqlite.exec('DELETE FROM entry_tags'); sqlite.exec('DELETE FROM images'); sqlite.exec('DELETE FROM tags'); sqlite.exec('DELETE FROM entries'); } /** * Seed the database with known test data */ async function seedDatabase() { const sqlite = new Database(DB_PATH); sqlite.pragma('journal_mode = WAL'); const db = drizzle(sqlite, { schema }); // Clear existing data clearDatabase(sqlite); // Insert test entries for (const entry of testData.entries) { db.insert(schema.entries).values(entry).run(); } // Insert test tags for (const tag of testData.tags) { db.insert(schema.tags).values(tag).run(); } // Insert entry-tag relationships for (const entryTag of testData.entryTags) { db.insert(schema.entryTags).values(entryTag).run(); } sqlite.close(); } /** * Clean up test data after tests */ async function cleanupDatabase() { const sqlite = new Database(DB_PATH); sqlite.pragma('journal_mode = WAL'); // Clear all test data clearDatabase(sqlite); sqlite.close(); } // Export fixture type for TypeScript export type SeededDbFixture = { testData: typeof testData; }; // Extend Playwright test with seeded database fixture export const test = base.extend<{ seededDb: SeededDbFixture }>({ seededDb: async ({}, use) => { // Setup: seed database before test await seedDatabase(); // Provide test data for assertions await use({ testData }); // Teardown: clean up after test await cleanupDatabase(); } });