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
This commit is contained in:
36
package-lock.json
generated
36
package-lock.json
generated
@@ -32,6 +32,7 @@
|
|||||||
"@vitest/browser-playwright": "^4.0.18",
|
"@vitest/browser-playwright": "^4.0.18",
|
||||||
"@vitest/coverage-v8": "^4.0.18",
|
"@vitest/coverage-v8": "^4.0.18",
|
||||||
"drizzle-kit": "^0.31.8",
|
"drizzle-kit": "^0.31.8",
|
||||||
|
"drizzle-seed": "^0.3.1",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-svelte": "^3.14.0",
|
"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": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.5",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||||
@@ -5573,6 +5592,23 @@
|
|||||||
"node": ">=6"
|
"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": {
|
"node_modules/rc": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"@vitest/browser-playwright": "^4.0.18",
|
"@vitest/browser-playwright": "^4.0.18",
|
||||||
"@vitest/coverage-v8": "^4.0.18",
|
"@vitest/coverage-v8": "^4.0.18",
|
||||||
"drizzle-kit": "^0.31.8",
|
"drizzle-kit": "^0.31.8",
|
||||||
|
"drizzle-seed": "^0.3.1",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-svelte": "^3.14.0",
|
"eslint-plugin-svelte": "^3.14.0",
|
||||||
|
|||||||
174
tests/e2e/fixtures/db.ts
Normal file
174
tests/e2e/fixtures/db.ts
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
});
|
||||||
7
tests/e2e/index.ts
Normal file
7
tests/e2e/index.ts
Normal file
@@ -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';
|
||||||
Reference in New Issue
Block a user