Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
387 lines
9.6 KiB
TypeScript
387 lines
9.6 KiB
TypeScript
import type { Kysely } from "kysely";
|
|
import { sql } from "kysely";
|
|
import { ulid } from "ulidx";
|
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
|
|
import type { Database } from "../../../src/database/types.js";
|
|
import { getMenuWithDb } from "../../../src/menus/index.js";
|
|
import { SchemaRegistry } from "../../../src/schema/registry.js";
|
|
import { applySeed } from "../../../src/seed/apply.js";
|
|
import type { SeedFile } from "../../../src/seed/types.js";
|
|
import { setupTestDatabase, teardownTestDatabase } from "../../utils/test-db.js";
|
|
|
|
describe("urlPattern", () => {
|
|
let db: Kysely<Database>;
|
|
let registry: SchemaRegistry;
|
|
|
|
beforeEach(async () => {
|
|
db = await setupTestDatabase();
|
|
registry = new SchemaRegistry(db);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await teardownTestDatabase(db);
|
|
});
|
|
|
|
describe("schema registry", () => {
|
|
it("should store urlPattern on create", async () => {
|
|
const collection = await registry.createCollection({
|
|
slug: "pages",
|
|
label: "Pages",
|
|
urlPattern: "/{slug}",
|
|
});
|
|
|
|
expect(collection.urlPattern).toBe("/{slug}");
|
|
});
|
|
|
|
it("should default urlPattern to undefined when not provided", async () => {
|
|
const collection = await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
});
|
|
|
|
expect(collection.urlPattern).toBeUndefined();
|
|
});
|
|
|
|
it("should persist urlPattern in the database", async () => {
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
});
|
|
|
|
const row = await db
|
|
.selectFrom("_emdash_collections")
|
|
.select("url_pattern")
|
|
.where("slug", "=", "posts")
|
|
.executeTakeFirst();
|
|
|
|
expect(row?.url_pattern).toBe("/blog/{slug}");
|
|
});
|
|
|
|
it("should return urlPattern from getCollection", async () => {
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
});
|
|
|
|
const collection = await registry.getCollection("posts");
|
|
expect(collection?.urlPattern).toBe("/blog/{slug}");
|
|
});
|
|
|
|
it("should update urlPattern", async () => {
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
});
|
|
|
|
const updated = await registry.updateCollection("posts", {
|
|
urlPattern: "/articles/{slug}",
|
|
});
|
|
|
|
expect(updated.urlPattern).toBe("/articles/{slug}");
|
|
});
|
|
|
|
it("should clear urlPattern when set to undefined", async () => {
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
});
|
|
|
|
// Setting to undefined in the update should clear it
|
|
const updated = await registry.updateCollection("posts", {
|
|
urlPattern: undefined,
|
|
});
|
|
|
|
// urlPattern was not in the update input, so it should keep the old value
|
|
expect(updated.urlPattern).toBe("/blog/{slug}");
|
|
});
|
|
|
|
it("should clear urlPattern when explicitly set to null-ish", async () => {
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
});
|
|
|
|
// Explicitly passing null (via the update interface) should clear it
|
|
const updated = await registry.updateCollection("posts", {
|
|
urlPattern: "" as any, // empty string to clear
|
|
});
|
|
|
|
// Empty string is falsy but still a defined value
|
|
expect(updated.urlPattern).toBe("");
|
|
});
|
|
|
|
it("should include urlPattern in listCollections", async () => {
|
|
await registry.createCollection({
|
|
slug: "pages",
|
|
label: "Pages",
|
|
urlPattern: "/{slug}",
|
|
});
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
});
|
|
|
|
const collections = await registry.listCollections();
|
|
const patterns = collections.map((c) => ({ slug: c.slug, urlPattern: c.urlPattern }));
|
|
|
|
expect(patterns).toEqual([
|
|
{ slug: "pages", urlPattern: "/{slug}" },
|
|
{ slug: "posts", urlPattern: "/blog/{slug}" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("menu URL resolution", () => {
|
|
it("should use urlPattern for content URL resolution", async () => {
|
|
// Create a pages collection with urlPattern
|
|
await registry.createCollection({
|
|
slug: "pages",
|
|
label: "Pages",
|
|
urlPattern: "/{slug}",
|
|
});
|
|
await registry.createField("pages", {
|
|
slug: "title",
|
|
label: "Title",
|
|
type: "string",
|
|
});
|
|
|
|
// Insert a page
|
|
const pageId = ulid();
|
|
await sql`
|
|
INSERT INTO ec_pages (id, slug, status) VALUES (${pageId}, ${"about"}, ${"published"})
|
|
`.execute(db);
|
|
|
|
// Create a menu with a page reference
|
|
const menuId = ulid();
|
|
await db
|
|
.insertInto("_emdash_menus")
|
|
.values({ id: menuId, name: "primary", label: "Primary" })
|
|
.execute();
|
|
|
|
await db
|
|
.insertInto("_emdash_menu_items")
|
|
.values({
|
|
id: ulid(),
|
|
menu_id: menuId,
|
|
sort_order: 0,
|
|
type: "page",
|
|
reference_collection: "pages",
|
|
reference_id: pageId,
|
|
label: "About",
|
|
})
|
|
.execute();
|
|
|
|
const menu = await getMenuWithDb("primary", db);
|
|
expect(menu).not.toBeNull();
|
|
expect(menu!.items).toHaveLength(1);
|
|
expect(menu!.items[0].url).toBe("/about");
|
|
});
|
|
|
|
it("should fall back to /{collection}/{slug} when no urlPattern", async () => {
|
|
// Create a posts collection without urlPattern
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
});
|
|
await registry.createField("posts", {
|
|
slug: "title",
|
|
label: "Title",
|
|
type: "string",
|
|
});
|
|
|
|
// Insert a post
|
|
const postId = ulid();
|
|
await sql`
|
|
INSERT INTO ec_posts (id, slug, status) VALUES (${postId}, ${"hello"}, ${"published"})
|
|
`.execute(db);
|
|
|
|
// Create a menu with a post reference
|
|
const menuId = ulid();
|
|
await db
|
|
.insertInto("_emdash_menus")
|
|
.values({ id: menuId, name: "primary", label: "Primary" })
|
|
.execute();
|
|
|
|
await db
|
|
.insertInto("_emdash_menu_items")
|
|
.values({
|
|
id: ulid(),
|
|
menu_id: menuId,
|
|
sort_order: 0,
|
|
type: "post",
|
|
reference_collection: "posts",
|
|
reference_id: postId,
|
|
label: "Hello",
|
|
})
|
|
.execute();
|
|
|
|
const menu = await getMenuWithDb("primary", db);
|
|
expect(menu!.items[0].url).toBe("/posts/hello");
|
|
});
|
|
|
|
it("should interpolate {slug} in urlPattern", async () => {
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
});
|
|
await registry.createField("posts", {
|
|
slug: "title",
|
|
label: "Title",
|
|
type: "string",
|
|
});
|
|
|
|
const postId = ulid();
|
|
await sql`
|
|
INSERT INTO ec_posts (id, slug, status) VALUES (${postId}, ${"my-post"}, ${"published"})
|
|
`.execute(db);
|
|
|
|
const menuId = ulid();
|
|
await db
|
|
.insertInto("_emdash_menus")
|
|
.values({ id: menuId, name: "primary", label: "Primary" })
|
|
.execute();
|
|
|
|
await db
|
|
.insertInto("_emdash_menu_items")
|
|
.values({
|
|
id: ulid(),
|
|
menu_id: menuId,
|
|
sort_order: 0,
|
|
type: "post",
|
|
reference_collection: "posts",
|
|
reference_id: postId,
|
|
label: "My Post",
|
|
})
|
|
.execute();
|
|
|
|
const menu = await getMenuWithDb("primary", db);
|
|
expect(menu!.items[0].url).toBe("/blog/my-post");
|
|
});
|
|
|
|
it("should handle multiple collections with different patterns", async () => {
|
|
// Pages: /{slug}
|
|
await registry.createCollection({
|
|
slug: "pages",
|
|
label: "Pages",
|
|
urlPattern: "/{slug}",
|
|
});
|
|
await registry.createField("pages", {
|
|
slug: "title",
|
|
label: "Title",
|
|
type: "string",
|
|
});
|
|
|
|
// Posts: /blog/{slug}
|
|
await registry.createCollection({
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
});
|
|
await registry.createField("posts", {
|
|
slug: "title",
|
|
label: "Title",
|
|
type: "string",
|
|
});
|
|
|
|
const pageId = ulid();
|
|
const postId = ulid();
|
|
await sql`INSERT INTO ec_pages (id, slug, status) VALUES (${pageId}, ${"about"}, ${"published"})`.execute(
|
|
db,
|
|
);
|
|
await sql`INSERT INTO ec_posts (id, slug, status) VALUES (${postId}, ${"hello"}, ${"published"})`.execute(
|
|
db,
|
|
);
|
|
|
|
const menuId = ulid();
|
|
await db
|
|
.insertInto("_emdash_menus")
|
|
.values({ id: menuId, name: "nav", label: "Nav" })
|
|
.execute();
|
|
|
|
await db
|
|
.insertInto("_emdash_menu_items")
|
|
.values([
|
|
{
|
|
id: ulid(),
|
|
menu_id: menuId,
|
|
sort_order: 0,
|
|
type: "page",
|
|
reference_collection: "pages",
|
|
reference_id: pageId,
|
|
label: "About",
|
|
},
|
|
{
|
|
id: ulid(),
|
|
menu_id: menuId,
|
|
sort_order: 1,
|
|
type: "post",
|
|
reference_collection: "posts",
|
|
reference_id: postId,
|
|
label: "Hello",
|
|
},
|
|
])
|
|
.execute();
|
|
|
|
const menu = await getMenuWithDb("nav", db);
|
|
expect(menu!.items[0].url).toBe("/about");
|
|
expect(menu!.items[1].url).toBe("/blog/hello");
|
|
});
|
|
});
|
|
|
|
describe("seed", () => {
|
|
it("should persist urlPattern from seed", async () => {
|
|
const seed: SeedFile = {
|
|
version: "1",
|
|
collections: [
|
|
{
|
|
slug: "pages",
|
|
label: "Pages",
|
|
urlPattern: "/{slug}",
|
|
fields: [{ slug: "title", label: "Title", type: "string" }],
|
|
},
|
|
{
|
|
slug: "posts",
|
|
label: "Posts",
|
|
urlPattern: "/blog/{slug}",
|
|
fields: [{ slug: "title", label: "Title", type: "string" }],
|
|
},
|
|
],
|
|
};
|
|
|
|
await applySeed(db, seed);
|
|
|
|
const pages = await registry.getCollection("pages");
|
|
const posts = await registry.getCollection("posts");
|
|
|
|
expect(pages?.urlPattern).toBe("/{slug}");
|
|
expect(posts?.urlPattern).toBe("/blog/{slug}");
|
|
});
|
|
|
|
it("should handle seed without urlPattern", async () => {
|
|
const seed: SeedFile = {
|
|
version: "1",
|
|
collections: [
|
|
{
|
|
slug: "posts",
|
|
label: "Posts",
|
|
fields: [{ slug: "title", label: "Title", type: "string" }],
|
|
},
|
|
],
|
|
};
|
|
|
|
await applySeed(db, seed);
|
|
|
|
const posts = await registry.getCollection("posts");
|
|
expect(posts?.urlPattern).toBeUndefined();
|
|
});
|
|
});
|
|
});
|