@jant/core 0.3.35 → 0.3.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/commands/export.js +1 -1
- package/bin/commands/import-site.js +529 -0
- package/bin/commands/reset-password.js +3 -2
- package/dist/client/assets/heic-to-DIRPI3VF.js +1 -0
- package/dist/client/assets/module-RjUF93sV.js +716 -0
- package/dist/client/assets/native-48B9X9Wg.js +1 -0
- package/dist/client/assets/url-FWFqPJPb.js +1 -0
- package/dist/client/client.css +1 -1
- package/dist/client/client.js +4564 -3013
- package/dist/index.js +12885 -8161
- package/package.json +23 -6
- package/src/__tests__/helpers/app.ts +10 -10
- package/src/__tests__/helpers/db.ts +91 -87
- package/src/app.tsx +157 -31
- package/src/auth.ts +20 -2
- package/src/client/archive-nav.js +187 -0
- package/src/client/audio-player.ts +478 -0
- package/src/client/audio-processor.ts +84 -0
- package/src/{lib → client}/avatar-upload.ts +4 -3
- package/src/{lib → client}/collection-form-bridge.ts +2 -2
- package/src/{ui → client}/components/__tests__/jant-collection-form.test.ts +26 -9
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1140 -0
- package/src/client/components/__tests__/jant-compose-editor.test.ts +504 -0
- package/src/{ui → client}/components/__tests__/jant-post-form.test.ts +37 -17
- package/src/{ui → client}/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/{ui → client}/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-sidebar-types.ts +43 -0
- package/src/{ui → client}/components/collection-types.ts +3 -4
- package/src/client/components/compose-types.ts +174 -0
- package/src/client/components/jant-collection-form.ts +667 -0
- package/src/client/components/jant-collection-sidebar.ts +805 -0
- package/src/client/components/jant-compose-dialog.ts +2161 -0
- package/src/client/components/jant-compose-editor.ts +1813 -0
- package/src/client/components/jant-compose-fullscreen.ts +283 -0
- package/src/client/components/jant-media-lightbox.ts +259 -0
- package/src/{ui → client}/components/jant-nav-manager.ts +97 -298
- package/src/{ui → client}/components/jant-post-form.ts +141 -12
- package/src/client/components/jant-post-menu.ts +1019 -0
- package/src/{ui → client}/components/jant-settings-avatar.ts +3 -3
- package/src/{ui → client}/components/jant-settings-general.ts +38 -4
- package/src/client/components/jant-text-preview.ts +232 -0
- package/src/{ui → client}/components/nav-manager-types.ts +6 -18
- package/src/{ui → client}/components/post-form-template.ts +137 -38
- package/src/{ui → client}/components/post-form-types.ts +15 -4
- package/src/client/compose-bridge.ts +583 -0
- package/src/{lib → client}/image-processor.ts +26 -8
- package/src/client/lazy-slugify.ts +51 -0
- package/src/client/media-metadata.ts +247 -0
- package/src/client/multipart-upload.ts +160 -0
- package/src/{lib → client}/nav-manager-bridge.ts +1 -1
- package/src/{lib → client}/post-form-bridge.ts +53 -2
- package/src/{lib → client}/settings-bridge.ts +3 -15
- package/src/client/thread-context.ts +140 -0
- package/src/client/tiptap/bubble-menu.ts +205 -0
- package/src/client/tiptap/create-editor.ts +86 -0
- package/src/client/tiptap/exitable-marks.ts +73 -0
- package/src/client/tiptap/extensions.ts +65 -0
- package/src/client/tiptap/image-node.ts +482 -0
- package/src/client/tiptap/link-toolbar.ts +371 -0
- package/src/client/tiptap/more-break.ts +50 -0
- package/src/client/tiptap/paste-image.ts +129 -0
- package/src/client/tiptap/slash-commands.ts +438 -0
- package/src/{lib → client}/toast.ts +101 -3
- package/src/client/types/sortablejs.d.ts +44 -0
- package/src/client/upload-with-metadata.ts +54 -0
- package/src/client/video-processor.ts +207 -0
- package/src/client.ts +27 -17
- package/src/db/__tests__/migrations.test.ts +118 -0
- package/src/db/index.ts +52 -0
- package/src/db/migrations/0000_baseline.sql +269 -0
- package/src/db/migrations/0001_fts_setup.sql +31 -0
- package/src/db/migrations/meta/0000_snapshot.json +703 -119
- package/src/db/migrations/meta/0001_snapshot.json +1337 -0
- package/src/db/migrations/meta/_journal.json +4 -39
- package/src/db/schema.ts +409 -140
- package/src/i18n/__tests__/detect.test.ts +115 -0
- package/src/i18n/context.tsx +2 -2
- package/src/i18n/detect.ts +85 -1
- package/src/i18n/i18n.ts +1 -1
- package/src/i18n/index.ts +2 -1
- package/src/i18n/locales/en.po +783 -1087
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +867 -812
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +878 -823
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/i18n/middleware.ts +6 -0
- package/src/index.ts +5 -7
- package/src/lib/__tests__/blurhash-placeholder.test.ts +75 -0
- package/src/lib/__tests__/constants.test.ts +0 -1
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +358 -0
- package/src/lib/__tests__/nanoid.test.ts +26 -0
- package/src/lib/__tests__/resolve-config.test.ts +2 -2
- package/src/lib/__tests__/schemas.test.ts +186 -65
- package/src/lib/__tests__/slug.test.ts +126 -0
- package/src/lib/__tests__/sse.test.ts +6 -6
- package/src/lib/__tests__/summary.test.ts +264 -0
- package/src/lib/__tests__/theme.test.ts +1 -1
- package/src/lib/__tests__/timeline.test.ts +33 -30
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +346 -0
- package/src/lib/__tests__/url.test.ts +2 -2
- package/src/lib/__tests__/view.test.ts +140 -65
- package/src/lib/blurhash-placeholder.ts +102 -0
- package/src/lib/constants.ts +3 -1
- package/src/lib/emoji-catalog.ts +963 -0
- package/src/lib/errors.ts +11 -8
- package/src/lib/feed.ts +77 -31
- package/src/lib/html.ts +2 -1
- package/src/lib/icon-catalog.ts +5033 -1
- package/src/lib/icons.ts +3 -2
- package/src/lib/index.ts +0 -1
- package/src/lib/markdown-to-tiptap.ts +286 -0
- package/src/lib/media-helpers.ts +22 -12
- package/src/lib/nanoid.ts +29 -0
- package/src/lib/navigation.ts +1 -1
- package/src/lib/render.tsx +24 -5
- package/src/lib/resolve-config.ts +13 -2
- package/src/lib/schemas.ts +226 -58
- package/src/lib/search-snippet.ts +34 -0
- package/src/lib/slug.ts +96 -0
- package/src/lib/sse.ts +6 -6
- package/src/lib/storage.ts +115 -7
- package/src/lib/summary.ts +158 -0
- package/src/lib/theme.ts +11 -8
- package/src/lib/timeline.ts +76 -34
- package/src/lib/tiptap-render.ts +191 -0
- package/src/lib/tiptap-to-markdown.ts +305 -0
- package/src/lib/upload.ts +263 -14
- package/src/lib/url.ts +37 -22
- package/src/lib/view.ts +236 -55
- package/src/middleware/__tests__/auth.test.ts +191 -11
- package/src/middleware/__tests__/onboarding.test.ts +12 -10
- package/src/middleware/auth.ts +63 -9
- package/src/middleware/error-handler.ts +3 -3
- package/src/middleware/onboarding.ts +1 -1
- package/src/middleware/secure-headers.ts +40 -0
- package/src/preset.css +83 -2
- package/src/routes/__tests__/compose.test.ts +17 -24
- package/src/routes/api/__tests__/collections.test.ts +109 -61
- package/src/routes/api/__tests__/nav-items.test.ts +46 -29
- package/src/routes/api/__tests__/posts.test.ts +132 -68
- package/src/routes/api/__tests__/search.test.ts +15 -2
- package/src/routes/api/__tests__/upload-multipart.test.ts +534 -0
- package/src/routes/api/collections.ts +57 -31
- package/src/routes/api/custom-urls.ts +80 -0
- package/src/routes/api/export.ts +31 -0
- package/src/routes/api/nav-items.ts +23 -19
- package/src/routes/api/posts.ts +81 -62
- package/src/routes/api/search.ts +3 -4
- package/src/routes/api/upload-multipart.ts +245 -0
- package/src/routes/api/upload.ts +92 -24
- package/src/routes/auth/__tests__/setup.test.ts +20 -60
- package/src/routes/auth/reset.tsx +5 -4
- package/src/routes/auth/setup.tsx +39 -31
- package/src/routes/auth/signin.tsx +13 -14
- package/src/routes/compose.tsx +27 -63
- package/src/routes/dash/__tests__/settings-avatar.test.ts +44 -9
- package/src/routes/dash/custom-urls.tsx +414 -0
- package/src/routes/dash/settings.tsx +475 -99
- package/src/routes/feed/__tests__/rss.test.ts +22 -23
- package/src/routes/feed/rss.ts +6 -2
- package/src/routes/feed/sitemap.ts +2 -12
- package/src/routes/pages/__tests__/collections.test.ts +5 -6
- package/src/routes/pages/__tests__/featured.test.ts +36 -18
- package/src/routes/pages/archive.tsx +177 -37
- package/src/routes/pages/collection.tsx +43 -14
- package/src/routes/pages/collections.tsx +11 -2
- package/src/routes/pages/featured.tsx +27 -3
- package/src/routes/pages/home.tsx +15 -14
- package/src/routes/pages/latest.tsx +1 -11
- package/src/routes/pages/new.tsx +39 -0
- package/src/routes/pages/page.tsx +95 -49
- package/src/routes/pages/search.tsx +1 -1
- package/src/services/__tests__/api-token.test.ts +135 -0
- package/src/services/__tests__/collection.test.ts +275 -227
- package/src/services/__tests__/custom-url.test.ts +213 -0
- package/src/services/__tests__/media.test.ts +162 -22
- package/src/services/__tests__/navigation.test.ts +109 -68
- package/src/services/__tests__/post-timeline.test.ts +205 -32
- package/src/services/__tests__/post.test.ts +800 -230
- package/src/services/__tests__/search.test.ts +67 -10
- package/src/services/__tests__/settings.test.ts +3 -3
- package/src/services/api-token.ts +166 -0
- package/src/services/auth.ts +17 -2
- package/src/services/collection.ts +397 -131
- package/src/services/custom-url.ts +188 -0
- package/src/services/export.ts +802 -0
- package/src/services/index.ts +26 -19
- package/src/services/media.ts +100 -22
- package/src/services/navigation.ts +158 -47
- package/src/services/path.ts +339 -0
- package/src/services/post.ts +764 -172
- package/src/services/search.ts +161 -74
- package/src/services/settings.ts +6 -2
- package/src/styles/components.css +293 -62
- package/src/styles/tokens.css +93 -5
- package/src/styles/ui.css +4349 -766
- package/src/types/bindings.ts +8 -0
- package/src/types/config.ts +34 -4
- package/src/types/constants.ts +17 -2
- package/src/types/entities.ts +83 -37
- package/src/types/operations.ts +20 -27
- package/src/types/props.ts +52 -17
- package/src/types/views.ts +48 -24
- package/src/ui/color-themes.ts +133 -23
- package/src/ui/compose/ComposeDialog.tsx +255 -16
- package/src/ui/compose/ComposePrompt.tsx +1 -1
- package/src/ui/dash/CrudPageHeader.tsx +1 -1
- package/src/ui/dash/ListItemRow.tsx +1 -1
- package/src/ui/dash/StatusBadge.tsx +12 -2
- package/src/ui/dash/appearance/AdvancedContent.tsx +71 -59
- package/src/ui/dash/appearance/ColorThemeContent.tsx +48 -33
- package/src/ui/dash/appearance/FontThemeContent.tsx +65 -73
- package/src/ui/dash/appearance/NavigationContent.tsx +106 -135
- package/src/ui/dash/index.ts +0 -3
- package/src/ui/dash/settings/AccountContent.tsx +87 -146
- package/src/ui/dash/settings/AccountMenuContent.tsx +147 -0
- package/src/ui/dash/settings/ApiTokensContent.tsx +232 -0
- package/src/ui/dash/settings/AvatarContent.tsx +78 -0
- package/src/ui/dash/settings/GeneralContent.tsx +3 -62
- package/src/ui/dash/settings/SessionsContent.tsx +159 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +266 -0
- package/src/ui/feed/LinkCard.tsx +89 -40
- package/src/ui/feed/NoteCard.tsx +39 -25
- package/src/ui/feed/PostStatusBadges.tsx +67 -0
- package/src/ui/feed/QuoteCard.tsx +38 -23
- package/src/ui/feed/ThreadPreview.tsx +90 -26
- package/src/ui/feed/TimelineFeed.tsx +3 -2
- package/src/ui/feed/TimelineItem.tsx +15 -6
- package/src/ui/feed/__tests__/thread-preview.test.ts +107 -0
- package/src/ui/feed/thread-preview-state.ts +61 -0
- package/src/ui/font-themes.ts +2 -2
- package/src/ui/layouts/BaseLayout.tsx +2 -2
- package/src/ui/layouts/SiteLayout.tsx +116 -103
- package/src/ui/pages/ArchivePage.tsx +923 -95
- package/src/ui/pages/CollectionPage.tsx +6 -35
- package/src/ui/pages/CollectionsPage.tsx +2 -1
- package/src/ui/pages/ComposePage.tsx +54 -0
- package/src/ui/pages/FeaturedPage.tsx +2 -1
- package/src/ui/pages/HomePage.tsx +1 -1
- package/src/ui/pages/PostPage.tsx +30 -45
- package/src/ui/pages/SearchPage.tsx +182 -38
- package/src/ui/shared/AdminBreadcrumb.tsx +29 -0
- package/src/ui/shared/CollectionsSidebar.tsx +239 -4
- package/src/ui/shared/MediaGallery.tsx +475 -41
- package/src/ui/shared/PostFooter.tsx +204 -0
- package/src/ui/shared/StarRating.tsx +27 -0
- package/src/ui/shared/__tests__/format-chars.test.ts +35 -0
- package/src/ui/shared/index.ts +0 -1
- package/src/db/migrations/0000_square_wallflower.sql +0 -118
- package/src/db/migrations/0001_add_search_fts.sql +0 -34
- package/src/db/migrations/0002_add_media_attachments.sql +0 -3
- package/src/db/migrations/0003_add_navigation_links.sql +0 -8
- package/src/db/migrations/0004_add_storage_provider.sql +0 -3
- package/src/db/migrations/0005_v2_schema_migration.sql +0 -268
- package/src/db/migrations/0006_rename_slug_to_path.sql +0 -5
- package/src/db/migrations/0007_post_collections_m2m.sql +0 -94
- package/src/db/migrations/0008_add_collection_dividers.sql +0 -8
- package/src/db/migrations/0009_drop_collection_show_divider.sql +0 -2
- package/src/db/migrations/0010_add_performance_indexes.sql +0 -16
- package/src/db/migrations/0011_add_path_registry.sql +0 -23
- package/src/db/migrations/meta/0003_snapshot.json +0 -821
- package/src/lib/__tests__/sqid.test.ts +0 -65
- package/src/lib/collections-reorder.ts +0 -28
- package/src/lib/compose-bridge.ts +0 -280
- package/src/lib/media-upload.ts +0 -148
- package/src/lib/sqid.ts +0 -79
- package/src/routes/api/__tests__/pages.test.ts +0 -218
- package/src/routes/api/pages.ts +0 -73
- package/src/routes/dash/__tests__/pages.test.ts +0 -226
- package/src/routes/dash/appearance.tsx +0 -240
- package/src/routes/dash/collections.tsx +0 -211
- package/src/routes/dash/index.tsx +0 -103
- package/src/routes/dash/media.tsx +0 -132
- package/src/routes/dash/pages.tsx +0 -239
- package/src/routes/dash/posts.tsx +0 -334
- package/src/routes/dash/redirects.tsx +0 -257
- package/src/routes/pages/post.tsx +0 -59
- package/src/services/__tests__/page.test.ts +0 -298
- package/src/services/__tests__/path-registry.test.ts +0 -165
- package/src/services/__tests__/redirect.test.ts +0 -159
- package/src/services/page.ts +0 -203
- package/src/services/path-registry.ts +0 -160
- package/src/services/redirect.ts +0 -97
- package/src/types/sortablejs.d.ts +0 -29
- package/src/ui/components/__tests__/jant-compose-dialog.test.ts +0 -512
- package/src/ui/components/__tests__/jant-compose-editor.test.ts +0 -272
- package/src/ui/components/compose-types.ts +0 -75
- package/src/ui/components/jant-collection-form.ts +0 -512
- package/src/ui/components/jant-compose-dialog.ts +0 -495
- package/src/ui/components/jant-compose-editor.ts +0 -814
- package/src/ui/dash/PageForm.tsx +0 -185
- package/src/ui/dash/PostList.tsx +0 -95
- package/src/ui/dash/appearance/AppearanceNav.tsx +0 -60
- package/src/ui/dash/collections/CollectionForm.tsx +0 -166
- package/src/ui/dash/collections/CollectionsListContent.tsx +0 -146
- package/src/ui/dash/collections/IconPickerGrid.tsx +0 -50
- package/src/ui/dash/collections/ViewCollectionContent.tsx +0 -103
- package/src/ui/dash/media/MediaListContent.tsx +0 -201
- package/src/ui/dash/media/ViewMediaContent.tsx +0 -208
- package/src/ui/dash/pages/PagesContent.tsx +0 -74
- package/src/ui/dash/posts/PostForm.tsx +0 -248
- package/src/ui/dash/settings/SettingsNav.tsx +0 -52
- package/src/ui/layouts/DashLayout.tsx +0 -165
- package/src/ui/pages/SinglePage.tsx +0 -23
- package/src/ui/shared/ThreadView.tsx +0 -136
- /package/src/{ui → client}/components/settings-types.ts +0 -0
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
2
|
import { createTestDatabase } from "../../__tests__/helpers/db.js";
|
|
3
3
|
import { createNavItemService } from "../navigation.js";
|
|
4
|
-
import { createPageService } from "../page.js";
|
|
5
|
-
import { createPathRegistryService } from "../path-registry.js";
|
|
6
4
|
import type { Database } from "../../db/index.js";
|
|
7
5
|
|
|
8
6
|
describe("NavItemService", () => {
|
|
9
7
|
let db: Database;
|
|
10
8
|
let navItemService: ReturnType<typeof createNavItemService>;
|
|
11
|
-
let pageService: ReturnType<typeof createPageService>;
|
|
12
9
|
|
|
13
10
|
beforeEach(() => {
|
|
14
11
|
const testDb = createTestDatabase();
|
|
15
12
|
db = testDb.db as unknown as Database;
|
|
16
13
|
navItemService = createNavItemService(db);
|
|
17
|
-
pageService = createPageService(db, createPathRegistryService(db));
|
|
18
14
|
});
|
|
19
15
|
|
|
20
16
|
describe("create", () => {
|
|
@@ -28,29 +24,13 @@ describe("NavItemService", () => {
|
|
|
28
24
|
expect(item.type).toBe("link");
|
|
29
25
|
expect(item.label).toBe("Home");
|
|
30
26
|
expect(item.url).toBe("/");
|
|
31
|
-
expect(item.
|
|
32
|
-
expect(item.
|
|
33
|
-
expect(item.id).
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("creates a page-type nav item with pageId", async () => {
|
|
37
|
-
const page = await pageService.create({ slug: "about", title: "About" });
|
|
38
|
-
|
|
39
|
-
const item = await navItemService.create({
|
|
40
|
-
type: "page",
|
|
41
|
-
label: "About",
|
|
42
|
-
url: "/about",
|
|
43
|
-
pageId: page.id,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
expect(item.type).toBe("page");
|
|
47
|
-
expect(item.label).toBe("About");
|
|
48
|
-
expect(item.url).toBe("/about");
|
|
49
|
-
expect(item.pageId).toBe(page.id);
|
|
27
|
+
expect(typeof item.position).toBe("string");
|
|
28
|
+
expect(typeof item.id).toBe("string");
|
|
29
|
+
expect(item.id.length).toBeGreaterThan(0);
|
|
50
30
|
});
|
|
51
31
|
|
|
52
32
|
it("auto-increments position for subsequent items", async () => {
|
|
53
|
-
await navItemService.create({
|
|
33
|
+
const first = await navItemService.create({
|
|
54
34
|
type: "link",
|
|
55
35
|
label: "Home",
|
|
56
36
|
url: "/",
|
|
@@ -61,7 +41,7 @@ describe("NavItemService", () => {
|
|
|
61
41
|
url: "/archive",
|
|
62
42
|
});
|
|
63
43
|
|
|
64
|
-
expect(second.position).toBe(
|
|
44
|
+
expect(second.position > first.position).toBe(true);
|
|
65
45
|
});
|
|
66
46
|
|
|
67
47
|
it("uses provided position when specified", async () => {
|
|
@@ -69,10 +49,28 @@ describe("NavItemService", () => {
|
|
|
69
49
|
type: "link",
|
|
70
50
|
label: "Home",
|
|
71
51
|
url: "/",
|
|
72
|
-
position:
|
|
52
|
+
position: "z99",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(item.position).toBe("z99");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("rejects duplicate provided positions", async () => {
|
|
59
|
+
await navItemService.create({
|
|
60
|
+
type: "link",
|
|
61
|
+
label: "Home",
|
|
62
|
+
url: "/",
|
|
63
|
+
position: "m0",
|
|
73
64
|
});
|
|
74
65
|
|
|
75
|
-
expect(
|
|
66
|
+
await expect(
|
|
67
|
+
navItemService.create({
|
|
68
|
+
type: "link",
|
|
69
|
+
label: "Archive",
|
|
70
|
+
url: "/archive",
|
|
71
|
+
position: "m0",
|
|
72
|
+
}),
|
|
73
|
+
).rejects.toThrow();
|
|
76
74
|
});
|
|
77
75
|
|
|
78
76
|
it("sets createdAt and updatedAt timestamps", async () => {
|
|
@@ -103,7 +101,9 @@ describe("NavItemService", () => {
|
|
|
103
101
|
});
|
|
104
102
|
|
|
105
103
|
it("returns null for non-existent ID", async () => {
|
|
106
|
-
const found = await navItemService.getById(
|
|
104
|
+
const found = await navItemService.getById(
|
|
105
|
+
"00000000-0000-0000-0000-000000009999",
|
|
106
|
+
);
|
|
107
107
|
expect(found).toBeNull();
|
|
108
108
|
});
|
|
109
109
|
});
|
|
@@ -119,19 +119,19 @@ describe("NavItemService", () => {
|
|
|
119
119
|
type: "link",
|
|
120
120
|
label: "C",
|
|
121
121
|
url: "/c",
|
|
122
|
-
position:
|
|
122
|
+
position: "c0",
|
|
123
123
|
});
|
|
124
124
|
await navItemService.create({
|
|
125
125
|
type: "link",
|
|
126
126
|
label: "A",
|
|
127
127
|
url: "/a",
|
|
128
|
-
position:
|
|
128
|
+
position: "a0",
|
|
129
129
|
});
|
|
130
130
|
await navItemService.create({
|
|
131
|
-
type: "
|
|
131
|
+
type: "link",
|
|
132
132
|
label: "B",
|
|
133
133
|
url: "/b",
|
|
134
|
-
position:
|
|
134
|
+
position: "b0",
|
|
135
135
|
});
|
|
136
136
|
|
|
137
137
|
const items = await navItemService.list();
|
|
@@ -142,25 +142,21 @@ describe("NavItemService", () => {
|
|
|
142
142
|
});
|
|
143
143
|
|
|
144
144
|
it("returns items with correct types", async () => {
|
|
145
|
-
const page = await pageService.create({ slug: "about", title: "About" });
|
|
146
|
-
|
|
147
145
|
await navItemService.create({
|
|
148
146
|
type: "link",
|
|
149
147
|
label: "External",
|
|
150
148
|
url: "https://example.com",
|
|
151
149
|
});
|
|
152
150
|
await navItemService.create({
|
|
153
|
-
type: "
|
|
154
|
-
label: "
|
|
155
|
-
url: "/
|
|
156
|
-
pageId: page.id,
|
|
151
|
+
type: "system",
|
|
152
|
+
label: "Settings",
|
|
153
|
+
url: "/settings",
|
|
157
154
|
});
|
|
158
155
|
|
|
159
156
|
const items = await navItemService.list();
|
|
160
157
|
expect(items).toHaveLength(2);
|
|
161
158
|
expect(items[0]?.type).toBe("link");
|
|
162
|
-
expect(items[1]?.type).toBe("
|
|
163
|
-
expect(items[1]?.pageId).toBe(page.id);
|
|
159
|
+
expect(items[1]?.type).toBe("system");
|
|
164
160
|
});
|
|
165
161
|
});
|
|
166
162
|
|
|
@@ -196,24 +192,6 @@ describe("NavItemService", () => {
|
|
|
196
192
|
expect(updated?.label).toBe("Blog");
|
|
197
193
|
});
|
|
198
194
|
|
|
199
|
-
it("updates a nav item's type", async () => {
|
|
200
|
-
const page = await pageService.create({ slug: "about", title: "About" });
|
|
201
|
-
|
|
202
|
-
const created = await navItemService.create({
|
|
203
|
-
type: "link",
|
|
204
|
-
label: "About",
|
|
205
|
-
url: "/about",
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const updated = await navItemService.update(created.id, {
|
|
209
|
-
type: "page",
|
|
210
|
-
pageId: page.id,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
expect(updated?.type).toBe("page");
|
|
214
|
-
expect(updated?.pageId).toBe(page.id);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
195
|
it("updates updatedAt timestamp", async () => {
|
|
218
196
|
const created = await navItemService.create({
|
|
219
197
|
type: "link",
|
|
@@ -229,7 +207,10 @@ describe("NavItemService", () => {
|
|
|
229
207
|
});
|
|
230
208
|
|
|
231
209
|
it("returns null for non-existent ID", async () => {
|
|
232
|
-
const result = await navItemService.update(
|
|
210
|
+
const result = await navItemService.update(
|
|
211
|
+
"00000000-0000-0000-0000-000000009999",
|
|
212
|
+
{ label: "Nope" },
|
|
213
|
+
);
|
|
233
214
|
expect(result).toBeNull();
|
|
234
215
|
});
|
|
235
216
|
});
|
|
@@ -250,13 +231,15 @@ describe("NavItemService", () => {
|
|
|
250
231
|
});
|
|
251
232
|
|
|
252
233
|
it("returns false for non-existent ID", async () => {
|
|
253
|
-
const result = await navItemService.delete(
|
|
234
|
+
const result = await navItemService.delete(
|
|
235
|
+
"00000000-0000-0000-0000-000000009999",
|
|
236
|
+
);
|
|
254
237
|
expect(result).toBe(false);
|
|
255
238
|
});
|
|
256
239
|
});
|
|
257
240
|
|
|
258
|
-
describe("
|
|
259
|
-
it("
|
|
241
|
+
describe("move", () => {
|
|
242
|
+
it("moves an item between two others", async () => {
|
|
260
243
|
const a = await navItemService.create({
|
|
261
244
|
type: "link",
|
|
262
245
|
label: "A",
|
|
@@ -273,16 +256,74 @@ describe("NavItemService", () => {
|
|
|
273
256
|
url: "/c",
|
|
274
257
|
});
|
|
275
258
|
|
|
276
|
-
//
|
|
277
|
-
await navItemService.
|
|
259
|
+
// Move C between A and B
|
|
260
|
+
await navItemService.move(c.id, a.id, b.id);
|
|
261
|
+
|
|
262
|
+
const items = await navItemService.list();
|
|
263
|
+
expect(items[0]?.label).toBe("A");
|
|
264
|
+
expect(items[1]?.label).toBe("C");
|
|
265
|
+
expect(items[2]?.label).toBe("B");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("moves an item to the beginning", async () => {
|
|
269
|
+
const a = await navItemService.create({
|
|
270
|
+
type: "link",
|
|
271
|
+
label: "A",
|
|
272
|
+
url: "/a",
|
|
273
|
+
});
|
|
274
|
+
await navItemService.create({
|
|
275
|
+
type: "link",
|
|
276
|
+
label: "B",
|
|
277
|
+
url: "/b",
|
|
278
|
+
});
|
|
279
|
+
const c = await navItemService.create({
|
|
280
|
+
type: "link",
|
|
281
|
+
label: "C",
|
|
282
|
+
url: "/c",
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Move C before A
|
|
286
|
+
await navItemService.move(c.id, null, a.id);
|
|
278
287
|
|
|
279
288
|
const items = await navItemService.list();
|
|
280
289
|
expect(items[0]?.label).toBe("C");
|
|
281
|
-
expect(items[
|
|
282
|
-
expect(items[
|
|
283
|
-
|
|
290
|
+
expect(items[1]?.label).toBe("A");
|
|
291
|
+
expect(items[2]?.label).toBe("B");
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("moves an item to the end", async () => {
|
|
295
|
+
const a = await navItemService.create({
|
|
296
|
+
type: "link",
|
|
297
|
+
label: "A",
|
|
298
|
+
url: "/a",
|
|
299
|
+
});
|
|
300
|
+
await navItemService.create({
|
|
301
|
+
type: "link",
|
|
302
|
+
label: "B",
|
|
303
|
+
url: "/b",
|
|
304
|
+
});
|
|
305
|
+
const c = await navItemService.create({
|
|
306
|
+
type: "link",
|
|
307
|
+
label: "C",
|
|
308
|
+
url: "/c",
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Move A after C
|
|
312
|
+
await navItemService.move(a.id, c.id, null);
|
|
313
|
+
|
|
314
|
+
const items = await navItemService.list();
|
|
315
|
+
expect(items[0]?.label).toBe("B");
|
|
316
|
+
expect(items[1]?.label).toBe("C");
|
|
284
317
|
expect(items[2]?.label).toBe("A");
|
|
285
|
-
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("returns null for non-existent item", async () => {
|
|
321
|
+
const result = await navItemService.move(
|
|
322
|
+
"00000000-0000-0000-0000-000000009999",
|
|
323
|
+
null,
|
|
324
|
+
null,
|
|
325
|
+
);
|
|
326
|
+
expect(result).toBeNull();
|
|
286
327
|
});
|
|
287
328
|
});
|
|
288
329
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
2
|
import { createTestDatabase } from "../../__tests__/helpers/db.js";
|
|
3
3
|
import { createPostService } from "../post.js";
|
|
4
|
-
import { createPathRegistryService } from "../path-registry.js";
|
|
5
4
|
import type { Database } from "../../db/index.js";
|
|
6
5
|
|
|
7
6
|
describe("PostService - Timeline features", () => {
|
|
@@ -11,20 +10,20 @@ describe("PostService - Timeline features", () => {
|
|
|
11
10
|
beforeEach(() => {
|
|
12
11
|
const testDb = createTestDatabase();
|
|
13
12
|
db = testDb.db as unknown as Database;
|
|
14
|
-
postService = createPostService(db,
|
|
13
|
+
postService = createPostService(db, { slugIdLength: 5 });
|
|
15
14
|
});
|
|
16
15
|
|
|
17
16
|
describe("format filter", () => {
|
|
18
17
|
it("filters by format", async () => {
|
|
19
|
-
await postService.create({ format: "note",
|
|
18
|
+
await postService.create({ format: "note", bodyMarkdown: "a note" });
|
|
20
19
|
await postService.create({
|
|
21
20
|
format: "link",
|
|
22
|
-
|
|
21
|
+
bodyMarkdown: "a link",
|
|
23
22
|
url: "https://example.com",
|
|
24
23
|
});
|
|
25
24
|
await postService.create({
|
|
26
25
|
format: "quote",
|
|
27
|
-
|
|
26
|
+
bodyMarkdown: "a quote",
|
|
28
27
|
quoteText: "something wise",
|
|
29
28
|
});
|
|
30
29
|
|
|
@@ -36,17 +35,17 @@ describe("PostService - Timeline features", () => {
|
|
|
36
35
|
it("combines format and status filters", async () => {
|
|
37
36
|
await postService.create({
|
|
38
37
|
format: "note",
|
|
39
|
-
|
|
38
|
+
bodyMarkdown: "published note",
|
|
40
39
|
status: "published",
|
|
41
40
|
});
|
|
42
41
|
await postService.create({
|
|
43
42
|
format: "note",
|
|
44
|
-
|
|
43
|
+
bodyMarkdown: "draft note",
|
|
45
44
|
status: "draft",
|
|
46
45
|
});
|
|
47
46
|
await postService.create({
|
|
48
47
|
format: "link",
|
|
49
|
-
|
|
48
|
+
bodyMarkdown: "published link",
|
|
50
49
|
status: "published",
|
|
51
50
|
url: "https://example.com",
|
|
52
51
|
});
|
|
@@ -70,16 +69,16 @@ describe("PostService - Timeline features", () => {
|
|
|
70
69
|
it("returns preview replies for a thread root", async () => {
|
|
71
70
|
const root = await postService.create({
|
|
72
71
|
format: "note",
|
|
73
|
-
|
|
72
|
+
bodyMarkdown: "root",
|
|
74
73
|
});
|
|
75
74
|
await postService.create({
|
|
76
75
|
format: "note",
|
|
77
|
-
|
|
76
|
+
bodyMarkdown: "reply 1",
|
|
78
77
|
replyToId: root.id,
|
|
79
78
|
});
|
|
80
79
|
await postService.create({
|
|
81
80
|
format: "note",
|
|
82
|
-
|
|
81
|
+
bodyMarkdown: "reply 2",
|
|
83
82
|
replyToId: root.id,
|
|
84
83
|
});
|
|
85
84
|
|
|
@@ -87,19 +86,19 @@ describe("PostService - Timeline features", () => {
|
|
|
87
86
|
const replies = previews.get(root.id);
|
|
88
87
|
expect(replies).toBeDefined();
|
|
89
88
|
expect(replies).toHaveLength(2);
|
|
90
|
-
expect(replies?.[0]?.
|
|
91
|
-
expect(replies?.[1]?.
|
|
89
|
+
expect(replies?.[0]?.bodyText).toBe("reply 1");
|
|
90
|
+
expect(replies?.[1]?.bodyText).toBe("reply 2");
|
|
92
91
|
});
|
|
93
92
|
|
|
94
93
|
it("limits preview replies to previewCount", async () => {
|
|
95
94
|
const root = await postService.create({
|
|
96
95
|
format: "note",
|
|
97
|
-
|
|
96
|
+
bodyMarkdown: "root",
|
|
98
97
|
});
|
|
99
98
|
for (let i = 0; i < 5; i++) {
|
|
100
99
|
await postService.create({
|
|
101
100
|
format: "note",
|
|
102
|
-
|
|
101
|
+
bodyMarkdown: `reply ${i}`,
|
|
103
102
|
replyToId: root.id,
|
|
104
103
|
});
|
|
105
104
|
}
|
|
@@ -107,19 +106,19 @@ describe("PostService - Timeline features", () => {
|
|
|
107
106
|
const previews = await postService.getThreadPreviews([root.id], 2);
|
|
108
107
|
const replies = previews.get(root.id);
|
|
109
108
|
expect(replies).toHaveLength(2);
|
|
110
|
-
expect(replies?.[0]?.
|
|
111
|
-
expect(replies?.[1]?.
|
|
109
|
+
expect(replies?.[0]?.bodyText).toBe("reply 0");
|
|
110
|
+
expect(replies?.[1]?.bodyText).toBe("reply 1");
|
|
112
111
|
});
|
|
113
112
|
|
|
114
113
|
it("defaults to 3 preview replies", async () => {
|
|
115
114
|
const root = await postService.create({
|
|
116
115
|
format: "note",
|
|
117
|
-
|
|
116
|
+
bodyMarkdown: "root",
|
|
118
117
|
});
|
|
119
118
|
for (let i = 0; i < 5; i++) {
|
|
120
119
|
await postService.create({
|
|
121
120
|
format: "note",
|
|
122
|
-
|
|
121
|
+
bodyMarkdown: `reply ${i}`,
|
|
123
122
|
replyToId: root.id,
|
|
124
123
|
});
|
|
125
124
|
}
|
|
@@ -132,20 +131,20 @@ describe("PostService - Timeline features", () => {
|
|
|
132
131
|
it("handles multiple thread roots", async () => {
|
|
133
132
|
const root1 = await postService.create({
|
|
134
133
|
format: "note",
|
|
135
|
-
|
|
134
|
+
bodyMarkdown: "root 1",
|
|
136
135
|
});
|
|
137
136
|
const root2 = await postService.create({
|
|
138
137
|
format: "note",
|
|
139
|
-
|
|
138
|
+
bodyMarkdown: "root 2",
|
|
140
139
|
});
|
|
141
140
|
await postService.create({
|
|
142
141
|
format: "note",
|
|
143
|
-
|
|
142
|
+
bodyMarkdown: "reply to root 1",
|
|
144
143
|
replyToId: root1.id,
|
|
145
144
|
});
|
|
146
145
|
await postService.create({
|
|
147
146
|
format: "note",
|
|
148
|
-
|
|
147
|
+
bodyMarkdown: "reply to root 2",
|
|
149
148
|
replyToId: root2.id,
|
|
150
149
|
});
|
|
151
150
|
|
|
@@ -161,16 +160,16 @@ describe("PostService - Timeline features", () => {
|
|
|
161
160
|
it("excludes deleted replies", async () => {
|
|
162
161
|
const root = await postService.create({
|
|
163
162
|
format: "note",
|
|
164
|
-
|
|
163
|
+
bodyMarkdown: "root",
|
|
165
164
|
});
|
|
166
165
|
const reply1 = await postService.create({
|
|
167
166
|
format: "note",
|
|
168
|
-
|
|
167
|
+
bodyMarkdown: "reply 1",
|
|
169
168
|
replyToId: root.id,
|
|
170
169
|
});
|
|
171
170
|
await postService.create({
|
|
172
171
|
format: "note",
|
|
173
|
-
|
|
172
|
+
bodyMarkdown: "reply 2",
|
|
174
173
|
replyToId: root.id,
|
|
175
174
|
});
|
|
176
175
|
|
|
@@ -179,13 +178,36 @@ describe("PostService - Timeline features", () => {
|
|
|
179
178
|
const previews = await postService.getThreadPreviews([root.id]);
|
|
180
179
|
const replies = previews.get(root.id);
|
|
181
180
|
expect(replies).toHaveLength(1);
|
|
182
|
-
expect(replies?.[0]?.
|
|
181
|
+
expect(replies?.[0]?.bodyText).toBe("reply 2");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("excludes draft replies", async () => {
|
|
185
|
+
const root = await postService.create({
|
|
186
|
+
format: "note",
|
|
187
|
+
bodyMarkdown: "root",
|
|
188
|
+
});
|
|
189
|
+
await postService.create({
|
|
190
|
+
format: "note",
|
|
191
|
+
bodyMarkdown: "published reply",
|
|
192
|
+
replyToId: root.id,
|
|
193
|
+
});
|
|
194
|
+
await postService.create({
|
|
195
|
+
format: "note",
|
|
196
|
+
bodyMarkdown: "draft reply",
|
|
197
|
+
replyToId: root.id,
|
|
198
|
+
status: "draft",
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const previews = await postService.getThreadPreviews([root.id]);
|
|
202
|
+
const replies = previews.get(root.id);
|
|
203
|
+
expect(replies).toHaveLength(1);
|
|
204
|
+
expect(replies?.[0]?.bodyText).toBe("published reply");
|
|
183
205
|
});
|
|
184
206
|
|
|
185
207
|
it("returns empty for roots with no replies", async () => {
|
|
186
208
|
const root = await postService.create({
|
|
187
209
|
format: "note",
|
|
188
|
-
|
|
210
|
+
bodyMarkdown: "root with no replies",
|
|
189
211
|
});
|
|
190
212
|
|
|
191
213
|
const previews = await postService.getThreadPreviews([root.id]);
|
|
@@ -193,22 +215,173 @@ describe("PostService - Timeline features", () => {
|
|
|
193
215
|
});
|
|
194
216
|
});
|
|
195
217
|
|
|
218
|
+
describe("getThreadTimelineContext", () => {
|
|
219
|
+
it("returns empty map for empty input", async () => {
|
|
220
|
+
const result = await postService.getThreadTimelineContext([]);
|
|
221
|
+
expect(result.size).toBe(0);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("returns latestReply with no parentReply for a 2-post thread", async () => {
|
|
225
|
+
const root = await postService.create({
|
|
226
|
+
format: "note",
|
|
227
|
+
bodyMarkdown: "root",
|
|
228
|
+
});
|
|
229
|
+
const reply = await postService.create({
|
|
230
|
+
format: "note",
|
|
231
|
+
bodyMarkdown: "only reply",
|
|
232
|
+
replyToId: root.id,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const result = await postService.getThreadTimelineContext([root.id]);
|
|
236
|
+
const ctx = result.get(root.id);
|
|
237
|
+
expect(ctx).toBeDefined();
|
|
238
|
+
expect(ctx?.latestReply.id).toBe(reply.id);
|
|
239
|
+
expect(ctx?.parentReply).toBeNull();
|
|
240
|
+
expect(ctx?.totalReplyCount).toBe(1);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("returns latestReply + parentReply for a 3-post thread", async () => {
|
|
244
|
+
const root = await postService.create({
|
|
245
|
+
format: "note",
|
|
246
|
+
bodyMarkdown: "root",
|
|
247
|
+
});
|
|
248
|
+
const reply1 = await postService.create({
|
|
249
|
+
format: "note",
|
|
250
|
+
bodyMarkdown: "reply 1",
|
|
251
|
+
replyToId: root.id,
|
|
252
|
+
});
|
|
253
|
+
const reply2 = await postService.create({
|
|
254
|
+
format: "note",
|
|
255
|
+
bodyMarkdown: "reply 2",
|
|
256
|
+
replyToId: reply1.id,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const result = await postService.getThreadTimelineContext([root.id]);
|
|
260
|
+
const ctx = result.get(root.id);
|
|
261
|
+
expect(ctx).toBeDefined();
|
|
262
|
+
expect(ctx?.latestReply.id).toBe(reply2.id);
|
|
263
|
+
expect(ctx?.parentReply?.id).toBe(reply1.id);
|
|
264
|
+
expect(ctx?.totalReplyCount).toBe(2);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("returns correct totalReplyCount for 4+ post thread", async () => {
|
|
268
|
+
const root = await postService.create({
|
|
269
|
+
format: "note",
|
|
270
|
+
bodyMarkdown: "root",
|
|
271
|
+
});
|
|
272
|
+
let prev = root;
|
|
273
|
+
for (let i = 0; i < 5; i++) {
|
|
274
|
+
prev = await postService.create({
|
|
275
|
+
format: "note",
|
|
276
|
+
bodyMarkdown: `reply ${i}`,
|
|
277
|
+
replyToId: prev.id,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const result = await postService.getThreadTimelineContext([root.id]);
|
|
282
|
+
const ctx = result.get(root.id);
|
|
283
|
+
expect(ctx).toBeDefined();
|
|
284
|
+
expect(ctx?.latestReply.bodyText).toBe("reply 4");
|
|
285
|
+
expect(ctx?.parentReply?.bodyText).toBe("reply 3");
|
|
286
|
+
expect(ctx?.totalReplyCount).toBe(5);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("excludes deleted replies", async () => {
|
|
290
|
+
const root = await postService.create({
|
|
291
|
+
format: "note",
|
|
292
|
+
bodyMarkdown: "root",
|
|
293
|
+
});
|
|
294
|
+
const reply1 = await postService.create({
|
|
295
|
+
format: "note",
|
|
296
|
+
bodyMarkdown: "reply 1",
|
|
297
|
+
replyToId: root.id,
|
|
298
|
+
});
|
|
299
|
+
const reply2 = await postService.create({
|
|
300
|
+
format: "note",
|
|
301
|
+
bodyMarkdown: "reply 2",
|
|
302
|
+
replyToId: reply1.id,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Delete the latest reply — reply1 should become the latest
|
|
306
|
+
await postService.delete(reply2.id);
|
|
307
|
+
|
|
308
|
+
const result = await postService.getThreadTimelineContext([root.id]);
|
|
309
|
+
const ctx = result.get(root.id);
|
|
310
|
+
expect(ctx).toBeDefined();
|
|
311
|
+
expect(ctx?.latestReply.id).toBe(reply1.id);
|
|
312
|
+
expect(ctx?.totalReplyCount).toBe(1);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("excludes draft replies from thread context", async () => {
|
|
316
|
+
const root = await postService.create({
|
|
317
|
+
format: "note",
|
|
318
|
+
bodyMarkdown: "root",
|
|
319
|
+
});
|
|
320
|
+
const publishedReply = await postService.create({
|
|
321
|
+
format: "note",
|
|
322
|
+
bodyMarkdown: "published reply",
|
|
323
|
+
replyToId: root.id,
|
|
324
|
+
});
|
|
325
|
+
await postService.create({
|
|
326
|
+
format: "note",
|
|
327
|
+
bodyMarkdown: "draft reply",
|
|
328
|
+
replyToId: root.id,
|
|
329
|
+
status: "draft",
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const result = await postService.getThreadTimelineContext([root.id]);
|
|
333
|
+
const ctx = result.get(root.id);
|
|
334
|
+
expect(ctx).toBeDefined();
|
|
335
|
+
expect(ctx?.latestReply.id).toBe(publishedReply.id);
|
|
336
|
+
expect(ctx?.totalReplyCount).toBe(1);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("handles multiple roots in batch", async () => {
|
|
340
|
+
const root1 = await postService.create({
|
|
341
|
+
format: "note",
|
|
342
|
+
bodyMarkdown: "root 1",
|
|
343
|
+
});
|
|
344
|
+
const root2 = await postService.create({
|
|
345
|
+
format: "note",
|
|
346
|
+
bodyMarkdown: "root 2",
|
|
347
|
+
});
|
|
348
|
+
const r1Reply = await postService.create({
|
|
349
|
+
format: "note",
|
|
350
|
+
bodyMarkdown: "reply to root 1",
|
|
351
|
+
replyToId: root1.id,
|
|
352
|
+
});
|
|
353
|
+
const r2Reply = await postService.create({
|
|
354
|
+
format: "note",
|
|
355
|
+
bodyMarkdown: "reply to root 2",
|
|
356
|
+
replyToId: root2.id,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const result = await postService.getThreadTimelineContext([
|
|
360
|
+
root1.id,
|
|
361
|
+
root2.id,
|
|
362
|
+
]);
|
|
363
|
+
expect(result.size).toBe(2);
|
|
364
|
+
expect(result.get(root1.id)?.latestReply.id).toBe(r1Reply.id);
|
|
365
|
+
expect(result.get(root2.id)?.latestReply.id).toBe(r2Reply.id);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
196
369
|
describe("timeline assembly", () => {
|
|
197
370
|
it("fetches published non-reply posts for the timeline", async () => {
|
|
198
371
|
const root = await postService.create({
|
|
199
372
|
format: "note",
|
|
200
|
-
|
|
373
|
+
bodyMarkdown: "a published note",
|
|
201
374
|
status: "published",
|
|
202
375
|
});
|
|
203
376
|
await postService.create({
|
|
204
377
|
format: "note",
|
|
205
|
-
|
|
378
|
+
bodyMarkdown: "a reply",
|
|
206
379
|
status: "published",
|
|
207
380
|
replyToId: root.id,
|
|
208
381
|
});
|
|
209
382
|
await postService.create({
|
|
210
383
|
format: "note",
|
|
211
|
-
|
|
384
|
+
bodyMarkdown: "a draft",
|
|
212
385
|
status: "draft",
|
|
213
386
|
});
|
|
214
387
|
|
|
@@ -219,7 +392,7 @@ describe("PostService - Timeline features", () => {
|
|
|
219
392
|
});
|
|
220
393
|
|
|
221
394
|
expect(posts).toHaveLength(1);
|
|
222
|
-
expect(posts[0]?.
|
|
395
|
+
expect(posts[0]?.bodyText).toBe("a published note");
|
|
223
396
|
});
|
|
224
397
|
});
|
|
225
398
|
});
|