100 lines
3.7 KiB
TypeScript
100 lines
3.7 KiB
TypeScript
import { beforeEach, describe, expect, it } from "vitest";
|
|
import type { PostInput } from "@/lib/services/posts";
|
|
import {
|
|
createPost,
|
|
getPublishedPostBySlug,
|
|
listPublishedPosts,
|
|
setPostStatus,
|
|
} from "@/lib/services/posts";
|
|
import { resetDb } from "../helpers/db";
|
|
|
|
const input = (overrides: Partial<PostInput> = {}): PostInput => ({
|
|
title: "A Post",
|
|
slug: "",
|
|
body: "Some body text for the post.",
|
|
authorName: "Tester",
|
|
featuredImageUrl: null,
|
|
featuredImageAlt: null,
|
|
status: "draft",
|
|
tagIds: [],
|
|
newTagNames: [],
|
|
...overrides,
|
|
});
|
|
|
|
beforeEach(resetDb);
|
|
|
|
describe("public post queries", () => {
|
|
it("excludes draft posts from listings and slug lookups", async () => {
|
|
await createPost(input({ title: "Published one", status: "published" }));
|
|
const draft = await createPost(input({ title: "Hidden draft", status: "draft" }));
|
|
|
|
const page = await listPublishedPosts({ page: 1, perPage: 10 });
|
|
expect(page.total).toBe(1);
|
|
expect(page.items.map((p) => p.title)).toEqual(["Published one"]);
|
|
|
|
expect(await getPublishedPostBySlug(draft.slug)).toBeNull();
|
|
});
|
|
|
|
it("orders posts in reverse chronological order of publication", async () => {
|
|
await createPost(input({ title: "Oldest", status: "published" }));
|
|
await createPost(input({ title: "Middle", status: "published" }));
|
|
await createPost(input({ title: "Newest", status: "published" }));
|
|
|
|
const page = await listPublishedPosts({ page: 1, perPage: 10 });
|
|
expect(page.items.map((p) => p.title)).toEqual(["Newest", "Middle", "Oldest"]);
|
|
});
|
|
|
|
it("paginates with correct totals", async () => {
|
|
for (let i = 1; i <= 7; i++) {
|
|
await createPost(input({ title: `Post ${i}`, status: "published" }));
|
|
}
|
|
|
|
const first = await listPublishedPosts({ page: 1, perPage: 5 });
|
|
expect(first.items).toHaveLength(5);
|
|
expect(first.total).toBe(7);
|
|
expect(first.pageCount).toBe(2);
|
|
|
|
const second = await listPublishedPosts({ page: 2, perPage: 5 });
|
|
expect(second.items).toHaveLength(2);
|
|
expect(second.items.map((p) => p.title)).toEqual(["Post 2", "Post 1"]);
|
|
});
|
|
});
|
|
|
|
describe("creating and publishing a post", () => {
|
|
it("keeps a new draft private, then makes it public on publish", async () => {
|
|
const draft = await createPost(input({ title: "Launch notes" }));
|
|
expect(draft.publishedAt).toBeNull();
|
|
expect(await getPublishedPostBySlug("launch-notes")).toBeNull();
|
|
|
|
const published = await setPostStatus(draft.id, "published");
|
|
expect(published?.status).toBe("published");
|
|
expect(published?.publishedAt).toBeInstanceOf(Date);
|
|
|
|
const publicPost = await getPublishedPostBySlug("launch-notes");
|
|
expect(publicPost?.title).toBe("Launch notes");
|
|
|
|
const listing = await listPublishedPosts({ page: 1, perPage: 10 });
|
|
expect(listing.items.map((p) => p.slug)).toContain("launch-notes");
|
|
});
|
|
|
|
it("stamps publishedAt once and keeps it across unpublish/republish", async () => {
|
|
const post = await createPost(input({ status: "published" }));
|
|
const firstDate = post.publishedAt;
|
|
expect(firstDate).toBeInstanceOf(Date);
|
|
|
|
const unpublished = await setPostStatus(post.id, "draft");
|
|
expect(unpublished?.publishedAt?.getTime()).toBe(firstDate?.getTime());
|
|
|
|
const republished = await setPostStatus(post.id, "published");
|
|
expect(republished?.publishedAt?.getTime()).toBe(firstDate?.getTime());
|
|
});
|
|
|
|
it("attaches existing and newly created tags", async () => {
|
|
const post = await createPost(
|
|
input({ status: "published", newTagNames: ["Fresh Tag", "Another"] }),
|
|
);
|
|
const publicPost = await getPublishedPostBySlug(post.slug);
|
|
expect(publicPost?.tags.map((t) => t.slug).sort()).toEqual(["another", "fresh-tag"]);
|
|
});
|
|
});
|