Emdash source with visual editor image upload fix
Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
This commit is contained in:
289
packages/core/tests/unit/cli/seed-commands.test.ts
Normal file
289
packages/core/tests/unit/cli/seed-commands.test.ts
Normal file
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* Tests for CLI seed commands
|
||||
*/
|
||||
|
||||
import { mkdtemp, rm, writeFile, mkdir, readFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
|
||||
import { createDatabase } from "../../../src/database/connection.js";
|
||||
import { runMigrations } from "../../../src/database/migrations/runner.js";
|
||||
import { applySeed } from "../../../src/seed/apply.js";
|
||||
import type { SeedFile } from "../../../src/seed/types.js";
|
||||
import { validateSeed } from "../../../src/seed/validate.js";
|
||||
|
||||
describe("CLI Seed Commands", () => {
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), "emdash-cli-test-"));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await rm(tempDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe("seed file resolution", () => {
|
||||
it("should resolve .emdash/seed.json by convention", async () => {
|
||||
// Create convention seed file
|
||||
const emdashDir = join(tempDir, ".emdash");
|
||||
await mkdir(emdashDir);
|
||||
const seedPath = join(emdashDir, "seed.json");
|
||||
|
||||
const seed: SeedFile = {
|
||||
version: "1",
|
||||
settings: { title: "Convention Seed" },
|
||||
};
|
||||
await writeFile(seedPath, JSON.stringify(seed));
|
||||
|
||||
// Read it back
|
||||
const content = await readFile(seedPath, "utf-8");
|
||||
const parsed = JSON.parse(content);
|
||||
expect(parsed.settings.title).toBe("Convention Seed");
|
||||
});
|
||||
|
||||
it("should resolve seed from package.json emdash.seed", async () => {
|
||||
// Create seed file in custom location
|
||||
const customDir = join(tempDir, "custom");
|
||||
await mkdir(customDir);
|
||||
const seedPath = join(customDir, "my-seed.json");
|
||||
|
||||
const seed: SeedFile = {
|
||||
version: "1",
|
||||
settings: { title: "Package.json Seed" },
|
||||
};
|
||||
await writeFile(seedPath, JSON.stringify(seed));
|
||||
|
||||
// Create package.json referencing it
|
||||
const pkg = {
|
||||
name: "test-project",
|
||||
emdash: {
|
||||
seed: "custom/my-seed.json",
|
||||
},
|
||||
};
|
||||
await writeFile(join(tempDir, "package.json"), JSON.stringify(pkg));
|
||||
|
||||
// Verify the referenced path works
|
||||
const content = await readFile(seedPath, "utf-8");
|
||||
const parsed = JSON.parse(content);
|
||||
expect(parsed.settings.title).toBe("Package.json Seed");
|
||||
});
|
||||
});
|
||||
|
||||
describe("seed validation", () => {
|
||||
it("should validate a valid seed file", () => {
|
||||
const seed: SeedFile = {
|
||||
version: "1",
|
||||
settings: { title: "Test Site" },
|
||||
collections: [
|
||||
{
|
||||
slug: "posts",
|
||||
label: "Posts",
|
||||
fields: [{ slug: "title", label: "Title", type: "string", required: true }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = validateSeed(seed);
|
||||
expect(result.valid).toBe(true);
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should reject invalid seed version", () => {
|
||||
const seed = {
|
||||
version: "999",
|
||||
settings: {},
|
||||
};
|
||||
|
||||
const result = validateSeed(seed);
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors.some((e) => e.includes("version"))).toBe(true);
|
||||
});
|
||||
|
||||
it("should reject seed with invalid collection", () => {
|
||||
const seed: SeedFile = {
|
||||
version: "1",
|
||||
collections: [
|
||||
{
|
||||
slug: "", // Invalid: empty slug
|
||||
label: "Posts",
|
||||
fields: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = validateSeed(seed);
|
||||
expect(result.valid).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("seed application", () => {
|
||||
it("should apply settings from seed", async () => {
|
||||
const dbPath = join(tempDir, "test.db");
|
||||
const db = createDatabase({ url: `file:${dbPath}` });
|
||||
|
||||
try {
|
||||
await runMigrations(db);
|
||||
|
||||
const seed: SeedFile = {
|
||||
version: "1",
|
||||
settings: {
|
||||
title: "My Test Site",
|
||||
tagline: "A test site for testing",
|
||||
},
|
||||
};
|
||||
|
||||
const result = await applySeed(db, seed, {});
|
||||
|
||||
expect(result.settings.applied).toBe(2);
|
||||
} finally {
|
||||
await db.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
it("should apply collections from seed", async () => {
|
||||
const dbPath = join(tempDir, "test.db");
|
||||
const db = createDatabase({ url: `file:${dbPath}` });
|
||||
|
||||
try {
|
||||
await runMigrations(db);
|
||||
|
||||
const seed: SeedFile = {
|
||||
version: "1",
|
||||
collections: [
|
||||
{
|
||||
slug: "articles",
|
||||
label: "Articles",
|
||||
labelSingular: "Article",
|
||||
fields: [
|
||||
{
|
||||
slug: "title",
|
||||
label: "Title",
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
{ slug: "body", label: "Body", type: "portableText" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await applySeed(db, seed, {});
|
||||
|
||||
expect(result.collections.created).toBe(1);
|
||||
expect(result.fields.created).toBe(2);
|
||||
} finally {
|
||||
await db.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
it("should be idempotent (skip existing)", async () => {
|
||||
const dbPath = join(tempDir, "test.db");
|
||||
const db = createDatabase({ url: `file:${dbPath}` });
|
||||
|
||||
try {
|
||||
await runMigrations(db);
|
||||
|
||||
const seed: SeedFile = {
|
||||
version: "1",
|
||||
collections: [
|
||||
{
|
||||
slug: "pages",
|
||||
label: "Pages",
|
||||
fields: [{ slug: "title", label: "Title", type: "string" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// First apply
|
||||
const result1 = await applySeed(db, seed, {});
|
||||
expect(result1.collections.created).toBe(1);
|
||||
expect(result1.collections.skipped).toBe(0);
|
||||
|
||||
// Second apply - should skip
|
||||
const result2 = await applySeed(db, seed, {});
|
||||
expect(result2.collections.created).toBe(0);
|
||||
expect(result2.collections.skipped).toBe(1);
|
||||
} finally {
|
||||
await db.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("export-seed output", () => {
|
||||
it("should produce valid seed from exported data", async () => {
|
||||
const dbPath = join(tempDir, "test.db");
|
||||
const db = createDatabase({ url: `file:${dbPath}` });
|
||||
|
||||
try {
|
||||
await runMigrations(db);
|
||||
|
||||
// Apply a seed first
|
||||
const inputSeed: SeedFile = {
|
||||
version: "1",
|
||||
settings: { title: "Export Test" },
|
||||
collections: [
|
||||
{
|
||||
slug: "docs",
|
||||
label: "Documentation",
|
||||
fields: [
|
||||
{ slug: "title", label: "Title", type: "string" },
|
||||
{ slug: "content", label: "Content", type: "portableText" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await applySeed(db, inputSeed, {});
|
||||
|
||||
// Now export (simulating what export-seed does)
|
||||
// For this test, we just verify the input seed validates
|
||||
const validation = validateSeed(inputSeed);
|
||||
expect(validation.valid).toBe(true);
|
||||
} finally {
|
||||
await db.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("content export with $media", () => {
|
||||
it("should handle content without media gracefully", async () => {
|
||||
const dbPath = join(tempDir, "test.db");
|
||||
const db = createDatabase({ url: `file:${dbPath}` });
|
||||
|
||||
try {
|
||||
await runMigrations(db);
|
||||
|
||||
const seed: SeedFile = {
|
||||
version: "1",
|
||||
collections: [
|
||||
{
|
||||
slug: "posts",
|
||||
label: "Posts",
|
||||
fields: [{ slug: "title", label: "Title", type: "string" }],
|
||||
},
|
||||
],
|
||||
content: {
|
||||
posts: [
|
||||
{
|
||||
id: "post-1",
|
||||
slug: "hello-world",
|
||||
status: "published",
|
||||
data: { title: "Hello World" },
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = await applySeed(db, seed, { includeContent: true });
|
||||
|
||||
expect(result.collections.created).toBe(1);
|
||||
expect(result.content.created).toBe(1);
|
||||
} finally {
|
||||
await db.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user