test: add E2E database seed and Playwright global setup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
10
e2e/global-setup.ts
Normal file
10
e2e/global-setup.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { seedTestDatabase } from "./seed";
|
||||||
|
|
||||||
|
export default async function globalSetup() {
|
||||||
|
await seedTestDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow direct invocation: bun run e2e/global-setup.ts
|
||||||
|
if (import.meta.main) {
|
||||||
|
await globalSetup();
|
||||||
|
}
|
||||||
220
e2e/seed.ts
Normal file
220
e2e/seed.ts
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import { unlink } from "node:fs/promises";
|
||||||
|
import { drizzle } from "drizzle-orm/bun-sqlite";
|
||||||
|
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
||||||
|
import * as schema from "../src/db/schema";
|
||||||
|
|
||||||
|
const DB_PATH = "./e2e/test.db";
|
||||||
|
|
||||||
|
export async function seedTestDatabase() {
|
||||||
|
// Remove old test DB if it exists
|
||||||
|
try {
|
||||||
|
await unlink(DB_PATH);
|
||||||
|
} catch {
|
||||||
|
// File doesn't exist, that's fine
|
||||||
|
}
|
||||||
|
|
||||||
|
const sqlite = new Database(DB_PATH);
|
||||||
|
sqlite.run("PRAGMA journal_mode = WAL");
|
||||||
|
sqlite.run("PRAGMA foreign_keys = ON");
|
||||||
|
|
||||||
|
const db = drizzle(sqlite, { schema });
|
||||||
|
migrate(db, { migrationsFolder: "./drizzle" });
|
||||||
|
|
||||||
|
// ── Categories ──
|
||||||
|
const [uncategorized] = db
|
||||||
|
.insert(schema.categories)
|
||||||
|
.values({ name: "Uncategorized", icon: "package" })
|
||||||
|
.returning()
|
||||||
|
.all();
|
||||||
|
|
||||||
|
const [shelter] = db
|
||||||
|
.insert(schema.categories)
|
||||||
|
.values({ name: "Shelter", icon: "tent" })
|
||||||
|
.returning()
|
||||||
|
.all();
|
||||||
|
|
||||||
|
const [sleep] = db
|
||||||
|
.insert(schema.categories)
|
||||||
|
.values({ name: "Sleep System", icon: "moon" })
|
||||||
|
.returning()
|
||||||
|
.all();
|
||||||
|
|
||||||
|
const [cook] = db
|
||||||
|
.insert(schema.categories)
|
||||||
|
.values({ name: "Cook Kit", icon: "flame" })
|
||||||
|
.returning()
|
||||||
|
.all();
|
||||||
|
|
||||||
|
// ── Items ──
|
||||||
|
const tent = db
|
||||||
|
.insert(schema.items)
|
||||||
|
.values({
|
||||||
|
name: "Zpacks Duplex",
|
||||||
|
weightGrams: 539,
|
||||||
|
priceCents: 67900,
|
||||||
|
categoryId: shelter.id,
|
||||||
|
notes: "DCF shelter, 2-person",
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
db.insert(schema.items)
|
||||||
|
.values({
|
||||||
|
name: "Borah Gear Tarp",
|
||||||
|
weightGrams: 156,
|
||||||
|
priceCents: 11000,
|
||||||
|
categoryId: shelter.id,
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
|
||||||
|
const quilt = db
|
||||||
|
.insert(schema.items)
|
||||||
|
.values({
|
||||||
|
name: "Enlightened Equipment Enigma 20",
|
||||||
|
weightGrams: 595,
|
||||||
|
priceCents: 34000,
|
||||||
|
categoryId: sleep.id,
|
||||||
|
notes: "20F quilt",
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
const pad = db
|
||||||
|
.insert(schema.items)
|
||||||
|
.values({
|
||||||
|
name: "Therm-a-Rest NeoAir XLite",
|
||||||
|
weightGrams: 354,
|
||||||
|
priceCents: 20999,
|
||||||
|
categoryId: sleep.id,
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
const stove = db
|
||||||
|
.insert(schema.items)
|
||||||
|
.values({
|
||||||
|
name: "BRS-3000T Stove",
|
||||||
|
weightGrams: 25,
|
||||||
|
priceCents: 2000,
|
||||||
|
categoryId: cook.id,
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
db.insert(schema.items)
|
||||||
|
.values({
|
||||||
|
name: "Toaks 750ml Pot",
|
||||||
|
weightGrams: 103,
|
||||||
|
priceCents: 3000,
|
||||||
|
categoryId: cook.id,
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
|
||||||
|
// ── Active Thread with 3 Candidates ──
|
||||||
|
const activeThread = db
|
||||||
|
.insert(schema.threads)
|
||||||
|
.values({
|
||||||
|
name: "New Backpack",
|
||||||
|
status: "active",
|
||||||
|
categoryId: uncategorized.id,
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
db.insert(schema.threadCandidates)
|
||||||
|
.values({
|
||||||
|
threadId: activeThread.id,
|
||||||
|
name: "ULA Circuit",
|
||||||
|
weightGrams: 1077,
|
||||||
|
priceCents: 27500,
|
||||||
|
categoryId: uncategorized.id,
|
||||||
|
pros: "Great hip belt\nLarge capacity",
|
||||||
|
cons: "Heavier than competitors",
|
||||||
|
sortOrder: 1000,
|
||||||
|
status: "researching",
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
|
||||||
|
db.insert(schema.threadCandidates)
|
||||||
|
.values({
|
||||||
|
threadId: activeThread.id,
|
||||||
|
name: "Gossamer Gear Mariposa",
|
||||||
|
weightGrams: 737,
|
||||||
|
priceCents: 28500,
|
||||||
|
categoryId: uncategorized.id,
|
||||||
|
pros: "Very lightweight\nGood ventilation",
|
||||||
|
cons: "Smaller hip belt pockets",
|
||||||
|
sortOrder: 2000,
|
||||||
|
status: "researching",
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
|
||||||
|
db.insert(schema.threadCandidates)
|
||||||
|
.values({
|
||||||
|
threadId: activeThread.id,
|
||||||
|
name: "Granite Gear Crown2 38",
|
||||||
|
weightGrams: 850,
|
||||||
|
priceCents: 18000,
|
||||||
|
categoryId: uncategorized.id,
|
||||||
|
sortOrder: 3000,
|
||||||
|
status: "ordered",
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
|
||||||
|
// ── Resolved Thread ──
|
||||||
|
const resolvedThread = db
|
||||||
|
.insert(schema.threads)
|
||||||
|
.values({
|
||||||
|
name: "Camp Stove",
|
||||||
|
status: "resolved",
|
||||||
|
categoryId: cook.id,
|
||||||
|
resolvedCandidateId: 1,
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
db.insert(schema.threadCandidates)
|
||||||
|
.values({
|
||||||
|
threadId: resolvedThread.id,
|
||||||
|
name: "BRS-3000T",
|
||||||
|
weightGrams: 25,
|
||||||
|
priceCents: 2000,
|
||||||
|
categoryId: cook.id,
|
||||||
|
sortOrder: 1000,
|
||||||
|
status: "arrived",
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
|
||||||
|
// ── Setup with Items ──
|
||||||
|
const setup = db
|
||||||
|
.insert(schema.setups)
|
||||||
|
.values({ name: "Weekend Overnighter" })
|
||||||
|
.returning()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
db.insert(schema.setupItems)
|
||||||
|
.values([
|
||||||
|
{ setupId: setup.id, itemId: tent.id, classification: "base" },
|
||||||
|
{ setupId: setup.id, itemId: quilt.id, classification: "base" },
|
||||||
|
{ setupId: setup.id, itemId: pad.id, classification: "base" },
|
||||||
|
{ setupId: setup.id, itemId: stove.id, classification: "consumable" },
|
||||||
|
])
|
||||||
|
.run();
|
||||||
|
|
||||||
|
// ── User ──
|
||||||
|
const passwordHash = await Bun.password.hash("password123");
|
||||||
|
db.insert(schema.users).values({ username: "admin", passwordHash }).run();
|
||||||
|
|
||||||
|
// ── Settings ──
|
||||||
|
db.insert(schema.settings)
|
||||||
|
.values([
|
||||||
|
{ key: "weightUnit", value: "g" },
|
||||||
|
{ key: "currency", value: "USD" },
|
||||||
|
{ key: "onboardingComplete", value: "true" },
|
||||||
|
])
|
||||||
|
.run();
|
||||||
|
|
||||||
|
sqlite.close();
|
||||||
|
console.log("E2E test database seeded at", DB_PATH);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user