From 283a9214ad6012c7eb1f8defa0ed2de3f2c92fca Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 3 Feb 2026 23:35:23 +0100 Subject: [PATCH] feat(09-03): create database seeding fixture for E2E tests - Add test fixture with seededDb for predictable test data - Include 5 entries: tasks and thoughts with various states - Include 3 tags with entry-tag relationships - Export extended test with fixtures from tests/e2e/index.ts - Install drizzle-seed dependency --- package-lock.json | 36 ++++++++ package.json | 1 + tests/e2e/fixtures/db.ts | 174 +++++++++++++++++++++++++++++++++++++++ tests/e2e/index.ts | 7 ++ 4 files changed, 218 insertions(+) create mode 100644 tests/e2e/fixtures/db.ts create mode 100644 tests/e2e/index.ts diff --git a/package-lock.json b/package-lock.json index 913429a..1c2c850 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "@vitest/browser-playwright": "^4.0.18", "@vitest/coverage-v8": "^4.0.18", "drizzle-kit": "^0.31.8", + "drizzle-seed": "^0.3.1", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.14.0", @@ -3873,6 +3874,24 @@ } } }, + "node_modules/drizzle-seed": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/drizzle-seed/-/drizzle-seed-0.3.1.tgz", + "integrity": "sha512-F/0lgvfOAsqlYoHM/QAGut4xXIOXoE5VoAdv2FIl7DpGYVXlAzKuJO+IphkKUFK3Dz+rFlOsQLnMNrvoQ0cx7g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "peerDependencies": { + "drizzle-orm": ">=0.36.4" + }, + "peerDependenciesMeta": { + "drizzle-orm": { + "optional": true + } + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -5573,6 +5592,23 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", diff --git a/package.json b/package.json index 1157931..6b17934 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@vitest/browser-playwright": "^4.0.18", "@vitest/coverage-v8": "^4.0.18", "drizzle-kit": "^0.31.8", + "drizzle-seed": "^0.3.1", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.14.0", diff --git a/tests/e2e/fixtures/db.ts b/tests/e2e/fixtures/db.ts new file mode 100644 index 0000000..e11ad2e --- /dev/null +++ b/tests/e2e/fixtures/db.ts @@ -0,0 +1,174 @@ +/** + * 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(); + } +}); diff --git a/tests/e2e/index.ts b/tests/e2e/index.ts new file mode 100644 index 0000000..54ff418 --- /dev/null +++ b/tests/e2e/index.ts @@ -0,0 +1,7 @@ +/** + * E2E test exports with database fixtures + * + * Import { test, expect } from this file to get tests with seeded database. + */ +export { test, testData } from './fixtures/db'; +export { expect } from '@playwright/test';