@jant/core 0.3.36 → 0.3.38
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/url-FWFqPJPb.js +1 -0
- package/dist/client/client.css +1 -1
- package/dist/client/client.js +4012 -3276
- package/dist/index.js +10285 -5809
- package/package.json +11 -3
- package/src/__tests__/helpers/app.ts +9 -9
- package/src/__tests__/helpers/db.ts +91 -93
- package/src/app.tsx +157 -27
- 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/client/avatar-upload.ts +3 -2
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +645 -49
- package/src/client/components/__tests__/jant-compose-editor.test.ts +208 -16
- package/src/client/components/__tests__/jant-post-form.test.ts +19 -9
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-sidebar-types.ts +7 -9
- package/src/client/components/compose-types.ts +101 -4
- package/src/client/components/jant-collection-form.ts +43 -7
- package/src/client/components/jant-collection-sidebar.ts +88 -84
- package/src/client/components/jant-compose-dialog.ts +1655 -219
- package/src/client/components/jant-compose-editor.ts +732 -168
- package/src/client/components/jant-compose-fullscreen.ts +23 -78
- package/src/client/components/jant-media-lightbox.ts +2 -0
- package/src/client/components/jant-nav-manager.ts +24 -284
- package/src/client/components/jant-post-form.ts +89 -9
- package/src/client/components/jant-post-menu.ts +1019 -0
- package/src/client/components/jant-settings-avatar.ts +3 -3
- package/src/client/components/jant-settings-general.ts +38 -4
- package/src/client/components/jant-text-preview.ts +232 -0
- package/src/client/components/nav-manager-types.ts +4 -19
- package/src/client/components/post-form-template.ts +107 -12
- package/src/client/components/post-form-types.ts +11 -4
- package/src/client/compose-bridge.ts +410 -109
- package/src/client/image-processor.ts +26 -8
- package/src/client/media-metadata.ts +247 -0
- package/src/client/multipart-upload.ts +160 -0
- package/src/client/post-form-bridge.ts +52 -1
- package/src/client/settings-bridge.ts +0 -12
- package/src/client/thread-context.ts +140 -0
- package/src/client/tiptap/create-editor.ts +46 -0
- package/src/client/tiptap/extensions.ts +5 -0
- package/src/client/tiptap/image-node.ts +2 -8
- package/src/client/tiptap/paste-image.ts +2 -13
- package/src/client/tiptap/slash-commands.ts +173 -63
- package/src/client/toast.ts +101 -3
- package/src/client/types/sortablejs.d.ts +15 -0
- package/src/client/upload-with-metadata.ts +54 -0
- package/src/client/video-processor.ts +207 -0
- package/src/client.ts +5 -2
- 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 -145
- 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 +487 -1217
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +613 -996
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +624 -1007
- 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__/schemas.test.ts +181 -63
- 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__/view.test.ts +141 -66
- package/src/lib/blurhash-placeholder.ts +102 -0
- package/src/lib/constants.ts +3 -1
- package/src/lib/emoji-catalog.ts +885 -68
- package/src/lib/errors.ts +11 -8
- package/src/lib/feed.ts +78 -32
- 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 +12 -3
- package/src/lib/nanoid.ts +29 -0
- package/src/lib/navigation.ts +1 -1
- package/src/lib/render.tsx +20 -2
- package/src/lib/resolve-config.ts +6 -2
- package/src/lib/schemas.ts +224 -55
- 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 +66 -0
- package/src/lib/theme.ts +11 -8
- package/src/lib/timeline.ts +74 -34
- package/src/lib/tiptap-render.ts +5 -10
- package/src/lib/tiptap-to-markdown.ts +305 -0
- package/src/lib/upload.ts +190 -29
- package/src/lib/url.ts +31 -0
- package/src/lib/view.ts +204 -37
- 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/onboarding.ts +1 -1
- package/src/middleware/secure-headers.ts +40 -0
- package/src/preset.css +45 -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 +51 -42
- 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 +43 -39
- package/src/routes/api/search.ts +3 -4
- package/src/routes/api/upload-multipart.ts +245 -0
- package/src/routes/api/upload.ts +85 -19
- package/src/routes/auth/__tests__/setup.test.ts +20 -60
- package/src/routes/auth/setup.tsx +26 -33
- package/src/routes/auth/signin.tsx +3 -7
- package/src/routes/compose.tsx +10 -55
- package/src/routes/dash/__tests__/settings-avatar.test.ts +1 -1
- package/src/routes/dash/custom-urls.tsx +414 -0
- package/src/routes/dash/settings.tsx +304 -232
- package/src/routes/feed/__tests__/rss.test.ts +27 -28
- package/src/routes/feed/rss.ts +6 -4
- 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 +41 -22
- package/src/routes/pages/archive.tsx +175 -39
- package/src/routes/pages/collection.tsx +22 -10
- package/src/routes/pages/collections.tsx +3 -3
- package/src/routes/pages/featured.tsx +28 -4
- package/src/routes/pages/home.tsx +16 -15
- 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 +713 -234
- package/src/services/__tests__/search.test.ts +67 -10
- 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 +687 -154
- package/src/services/search.ts +160 -75
- package/src/styles/components.css +58 -7
- package/src/styles/tokens.css +84 -6
- package/src/styles/ui.css +2964 -457
- package/src/types/bindings.ts +4 -1
- package/src/types/config.ts +12 -4
- package/src/types/constants.ts +15 -3
- package/src/types/entities.ts +74 -35
- package/src/types/operations.ts +11 -24
- package/src/types/props.ts +51 -16
- package/src/types/views.ts +45 -22
- package/src/ui/color-themes.ts +133 -23
- package/src/ui/compose/ComposeDialog.tsx +239 -17
- 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 +3 -1
- package/src/ui/dash/appearance/AdvancedContent.tsx +22 -1
- package/src/ui/dash/appearance/ColorThemeContent.tsx +22 -2
- package/src/ui/dash/appearance/FontThemeContent.tsx +1 -1
- package/src/ui/dash/appearance/NavigationContent.tsx +5 -45
- package/src/ui/dash/index.ts +0 -3
- package/src/ui/dash/settings/AccountContent.tsx +3 -57
- 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 +8 -0
- package/src/ui/dash/settings/SessionsContent.tsx +159 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +45 -15
- 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 +105 -92
- package/src/ui/pages/ArchivePage.tsx +923 -98
- package/src/ui/pages/ComposePage.tsx +54 -0
- package/src/ui/pages/PostPage.tsx +30 -45
- package/src/ui/pages/SearchPage.tsx +181 -37
- package/src/ui/shared/AdminBreadcrumb.tsx +29 -0
- package/src/ui/shared/CollectionsSidebar.tsx +47 -37
- package/src/ui/shared/MediaGallery.tsx +445 -149
- 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/dist/client/assets/url-8Dj-5CLW.js +0 -1
- package/src/client/media-upload.ts +0 -161
- package/src/client/page-slug-bridge.ts +0 -42
- 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/0012_add_tiptap_columns.sql +0 -2
- package/src/db/migrations/0013_replace_featured_with_visibility.sql +0 -8
- package/src/db/migrations/meta/0003_snapshot.json +0 -821
- package/src/lib/__tests__/sqid.test.ts +0 -65
- 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/index.tsx +0 -109
- package/src/routes/dash/media.tsx +0 -135
- package/src/routes/dash/pages.tsx +0 -245
- package/src/routes/dash/posts.tsx +0 -338
- package/src/routes/dash/redirects.tsx +0 -263
- 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 -216
- package/src/services/path-registry.ts +0 -160
- package/src/services/redirect.ts +0 -97
- package/src/ui/dash/PageForm.tsx +0 -187
- package/src/ui/dash/PostList.tsx +0 -95
- package/src/ui/dash/media/MediaListContent.tsx +0 -206
- package/src/ui/dash/media/ViewMediaContent.tsx +0 -208
- package/src/ui/dash/pages/PagesContent.tsx +0 -75
- package/src/ui/dash/posts/PostForm.tsx +0 -260
- package/src/ui/layouts/DashLayout.tsx +0 -247
- package/src/ui/pages/SinglePage.tsx +0 -23
- package/src/ui/shared/ThreadView.tsx +0 -136
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
2
|
import { createTestDatabase } from "../../__tests__/helpers/db.js";
|
|
3
|
+
import { collections, sidebarItems } from "../../db/schema.js";
|
|
3
4
|
import { createCollectionService } from "../collection.js";
|
|
5
|
+
import { createPathService } from "../path.js";
|
|
4
6
|
import { createPostService } from "../post.js";
|
|
5
|
-
import { createPathRegistryService } from "../path-registry.js";
|
|
6
7
|
import type { Database } from "../../db/index.js";
|
|
7
8
|
|
|
8
9
|
describe("CollectionService", () => {
|
|
@@ -14,7 +15,7 @@ describe("CollectionService", () => {
|
|
|
14
15
|
const testDb = createTestDatabase();
|
|
15
16
|
db = testDb.db as unknown as Database;
|
|
16
17
|
collectionService = createCollectionService(db);
|
|
17
|
-
postService = createPostService(db,
|
|
18
|
+
postService = createPostService(db, { slugIdLength: 5 });
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
describe("create", () => {
|
|
@@ -24,7 +25,8 @@ describe("CollectionService", () => {
|
|
|
24
25
|
title: "My Collection",
|
|
25
26
|
});
|
|
26
27
|
|
|
27
|
-
expect(collection.id).toBe(
|
|
28
|
+
expect(typeof collection.id).toBe("string");
|
|
29
|
+
expect(collection.id.length).toBeGreaterThan(0);
|
|
28
30
|
expect(collection.slug).toBe("my-collection");
|
|
29
31
|
expect(collection.title).toBe("My Collection");
|
|
30
32
|
expect(collection.description).toBeNull();
|
|
@@ -39,7 +41,6 @@ describe("CollectionService", () => {
|
|
|
39
41
|
description: "Posts about technology",
|
|
40
42
|
icon: "laptop",
|
|
41
43
|
sortOrder: "oldest",
|
|
42
|
-
position: 5,
|
|
43
44
|
});
|
|
44
45
|
|
|
45
46
|
expect(collection.slug).toBe("tech");
|
|
@@ -47,7 +48,6 @@ describe("CollectionService", () => {
|
|
|
47
48
|
expect(collection.description).toBe("Posts about technology");
|
|
48
49
|
expect(collection.icon).toBe("laptop");
|
|
49
50
|
expect(collection.sortOrder).toBe("oldest");
|
|
50
|
-
expect(collection.position).toBe(5);
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
it("sets timestamps", async () => {
|
|
@@ -60,23 +60,41 @@ describe("CollectionService", () => {
|
|
|
60
60
|
expect(collection.updatedAt).toBeGreaterThan(0);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
it("auto-
|
|
64
|
-
const
|
|
65
|
-
slug: "
|
|
66
|
-
title: "
|
|
67
|
-
});
|
|
68
|
-
const second = await collectionService.create({
|
|
69
|
-
slug: "second",
|
|
70
|
-
title: "Second",
|
|
63
|
+
it("auto-creates a sidebar item", async () => {
|
|
64
|
+
const collection = await collectionService.create({
|
|
65
|
+
slug: "test",
|
|
66
|
+
title: "Test",
|
|
71
67
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
|
|
69
|
+
const sidebarItems = await collectionService.listSidebarItems();
|
|
70
|
+
expect(sidebarItems).toHaveLength(1);
|
|
71
|
+
expect(sidebarItems[0]?.type).toBe("collection");
|
|
72
|
+
expect(sidebarItems[0]?.collectionId).toBe(collection.id);
|
|
73
|
+
expect(typeof sidebarItems[0]?.position).toBe("string");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("rolls back the collection insert when slug persistence fails inside the batch", async () => {
|
|
77
|
+
await collectionService.create({
|
|
78
|
+
slug: "race-condition",
|
|
79
|
+
title: "Existing",
|
|
75
80
|
});
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
const paths = createPathService(db);
|
|
83
|
+
const raceyPaths = {
|
|
84
|
+
...paths,
|
|
85
|
+
isPathAvailable: async () => true,
|
|
86
|
+
};
|
|
87
|
+
const raceyCollectionService = createCollectionService(db, raceyPaths);
|
|
88
|
+
|
|
89
|
+
await expect(
|
|
90
|
+
raceyCollectionService.create({
|
|
91
|
+
slug: "race-condition",
|
|
92
|
+
title: "Race Condition",
|
|
93
|
+
}),
|
|
94
|
+
).rejects.toThrow('Slug "race-condition" is already in use');
|
|
95
|
+
|
|
96
|
+
const rows = await db.select({ id: collections.id }).from(collections);
|
|
97
|
+
expect(rows).toHaveLength(1);
|
|
80
98
|
});
|
|
81
99
|
});
|
|
82
100
|
|
|
@@ -94,7 +112,9 @@ describe("CollectionService", () => {
|
|
|
94
112
|
});
|
|
95
113
|
|
|
96
114
|
it("returns null for non-existent ID", async () => {
|
|
97
|
-
const found = await collectionService.getById(
|
|
115
|
+
const found = await collectionService.getById(
|
|
116
|
+
"00000000-0000-0000-0000-000000009999",
|
|
117
|
+
);
|
|
98
118
|
expect(found).toBeNull();
|
|
99
119
|
});
|
|
100
120
|
});
|
|
@@ -129,29 +149,6 @@ describe("CollectionService", () => {
|
|
|
129
149
|
const list = await collectionService.list();
|
|
130
150
|
expect(list).toHaveLength(3);
|
|
131
151
|
});
|
|
132
|
-
|
|
133
|
-
it("orders by position ASC, then createdAt DESC", async () => {
|
|
134
|
-
const a = await collectionService.create({
|
|
135
|
-
slug: "a",
|
|
136
|
-
title: "A",
|
|
137
|
-
position: 2,
|
|
138
|
-
});
|
|
139
|
-
const b = await collectionService.create({
|
|
140
|
-
slug: "b",
|
|
141
|
-
title: "B",
|
|
142
|
-
position: 0,
|
|
143
|
-
});
|
|
144
|
-
const c = await collectionService.create({
|
|
145
|
-
slug: "c",
|
|
146
|
-
title: "C",
|
|
147
|
-
position: 1,
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
const list = await collectionService.list();
|
|
151
|
-
expect(list[0]?.id).toBe(b.id);
|
|
152
|
-
expect(list[1]?.id).toBe(c.id);
|
|
153
|
-
expect(list[2]?.id).toBe(a.id);
|
|
154
|
-
});
|
|
155
152
|
});
|
|
156
153
|
|
|
157
154
|
describe("update", () => {
|
|
@@ -212,7 +209,7 @@ describe("CollectionService", () => {
|
|
|
212
209
|
expect(updated?.icon).toBeNull();
|
|
213
210
|
});
|
|
214
211
|
|
|
215
|
-
it("updates icon
|
|
212
|
+
it("updates icon and sortOrder", async () => {
|
|
216
213
|
const collection = await collectionService.create({
|
|
217
214
|
slug: "test",
|
|
218
215
|
title: "Test",
|
|
@@ -221,12 +218,10 @@ describe("CollectionService", () => {
|
|
|
221
218
|
const updated = await collectionService.update(collection.id, {
|
|
222
219
|
icon: "rocket",
|
|
223
220
|
sortOrder: "rating_desc",
|
|
224
|
-
position: 10,
|
|
225
221
|
});
|
|
226
222
|
|
|
227
223
|
expect(updated?.icon).toBe("rocket");
|
|
228
224
|
expect(updated?.sortOrder).toBe("rating_desc");
|
|
229
|
-
expect(updated?.position).toBe(10);
|
|
230
225
|
});
|
|
231
226
|
|
|
232
227
|
it("updates updatedAt timestamp", async () => {
|
|
@@ -243,7 +238,10 @@ describe("CollectionService", () => {
|
|
|
243
238
|
});
|
|
244
239
|
|
|
245
240
|
it("returns null for non-existent collection", async () => {
|
|
246
|
-
const result = await collectionService.update(
|
|
241
|
+
const result = await collectionService.update(
|
|
242
|
+
"00000000-0000-0000-0000-000000009999",
|
|
243
|
+
{ title: "X" },
|
|
244
|
+
);
|
|
247
245
|
expect(result).toBeNull();
|
|
248
246
|
});
|
|
249
247
|
});
|
|
@@ -269,7 +267,7 @@ describe("CollectionService", () => {
|
|
|
269
267
|
});
|
|
270
268
|
const post = await postService.create({
|
|
271
269
|
format: "note",
|
|
272
|
-
|
|
270
|
+
bodyMarkdown: "test post",
|
|
273
271
|
});
|
|
274
272
|
|
|
275
273
|
await collectionService.addPost(collection.id, post.id);
|
|
@@ -289,58 +287,213 @@ describe("CollectionService", () => {
|
|
|
289
287
|
expect(after).toHaveLength(0);
|
|
290
288
|
});
|
|
291
289
|
|
|
290
|
+
it("removes sidebar item when collection is deleted", async () => {
|
|
291
|
+
const collection = await collectionService.create({
|
|
292
|
+
slug: "test",
|
|
293
|
+
title: "Test",
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Verify sidebar item exists
|
|
297
|
+
const before = await collectionService.listSidebarItems();
|
|
298
|
+
expect(before).toHaveLength(1);
|
|
299
|
+
|
|
300
|
+
await collectionService.delete(collection.id);
|
|
301
|
+
|
|
302
|
+
// Sidebar item should be gone
|
|
303
|
+
const after = await collectionService.listSidebarItems();
|
|
304
|
+
expect(after).toHaveLength(0);
|
|
305
|
+
});
|
|
306
|
+
|
|
292
307
|
it("returns false for non-existent collection", async () => {
|
|
293
|
-
const result = await collectionService.delete(
|
|
308
|
+
const result = await collectionService.delete(
|
|
309
|
+
"00000000-0000-0000-0000-000000009999",
|
|
310
|
+
);
|
|
294
311
|
expect(result).toBe(false);
|
|
295
312
|
});
|
|
296
313
|
});
|
|
297
314
|
|
|
298
|
-
describe("
|
|
299
|
-
it("
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
315
|
+
describe("listSidebarItems", () => {
|
|
316
|
+
it("returns empty array when no items exist", async () => {
|
|
317
|
+
const items = await collectionService.listSidebarItems();
|
|
318
|
+
expect(items).toEqual([]);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("returns items ordered by position", async () => {
|
|
322
|
+
await collectionService.create({ slug: "first", title: "First" });
|
|
323
|
+
await collectionService.create({ slug: "second", title: "Second" });
|
|
324
|
+
|
|
325
|
+
const items = await collectionService.listSidebarItems();
|
|
326
|
+
expect(items).toHaveLength(2);
|
|
327
|
+
expect(items[0]?.type).toBe("collection");
|
|
328
|
+
expect(items[1]?.type).toBe("collection");
|
|
329
|
+
// First created should come first (string comparison for fractional indexing)
|
|
330
|
+
const pos0 = items[0]?.position ?? "";
|
|
331
|
+
const pos1 = items[1]?.position ?? "";
|
|
332
|
+
expect(pos0 < pos1).toBe(true);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("includes dividers", async () => {
|
|
336
|
+
await collectionService.create({ slug: "a", title: "A" });
|
|
337
|
+
await collectionService.createSidebarItem("divider");
|
|
338
|
+
await collectionService.create({ slug: "b", title: "B" });
|
|
303
339
|
|
|
304
|
-
|
|
305
|
-
|
|
340
|
+
const items = await collectionService.listSidebarItems();
|
|
341
|
+
expect(items).toHaveLength(3);
|
|
342
|
+
expect(items[0]?.type).toBe("collection");
|
|
343
|
+
expect(items[1]?.type).toBe("divider");
|
|
344
|
+
expect(items[2]?.type).toBe("collection");
|
|
345
|
+
});
|
|
346
|
+
});
|
|
306
347
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const
|
|
348
|
+
describe("createSidebarItem", () => {
|
|
349
|
+
it("creates a divider", async () => {
|
|
350
|
+
const item = await collectionService.createSidebarItem("divider");
|
|
310
351
|
|
|
311
|
-
expect(
|
|
312
|
-
expect(
|
|
313
|
-
expect(
|
|
352
|
+
expect(item.type).toBe("divider");
|
|
353
|
+
expect(item.collectionId).toBeNull();
|
|
354
|
+
expect(typeof item.position).toBe("string");
|
|
355
|
+
expect(item.createdAt).toBeGreaterThan(0);
|
|
314
356
|
});
|
|
315
357
|
|
|
316
|
-
it("
|
|
317
|
-
const
|
|
318
|
-
const
|
|
358
|
+
it("creates items with incrementing positions", async () => {
|
|
359
|
+
const first = await collectionService.createSidebarItem("divider");
|
|
360
|
+
const second = await collectionService.createSidebarItem("divider");
|
|
319
361
|
|
|
320
|
-
|
|
362
|
+
expect(first.position < second.position).toBe(true);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it("rejects adding the same collection twice", async () => {
|
|
366
|
+
const collection = await collectionService.create({
|
|
367
|
+
slug: "notes",
|
|
368
|
+
title: "Notes",
|
|
369
|
+
});
|
|
321
370
|
|
|
322
|
-
|
|
323
|
-
|
|
371
|
+
await expect(
|
|
372
|
+
collectionService.createSidebarItem("collection", collection.id),
|
|
373
|
+
).rejects.toThrow("Collection is already in the sidebar.");
|
|
324
374
|
});
|
|
325
375
|
|
|
326
|
-
it("
|
|
327
|
-
await collectionService.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
376
|
+
it("rejects duplicate sidebar positions at the database layer", async () => {
|
|
377
|
+
const item = await collectionService.createSidebarItem("divider");
|
|
378
|
+
|
|
379
|
+
await expect(
|
|
380
|
+
db.insert(sidebarItems).values({
|
|
381
|
+
id: "00000000-0000-7000-8000-000000000001",
|
|
382
|
+
type: "divider",
|
|
383
|
+
collectionId: null,
|
|
384
|
+
position: item.position,
|
|
385
|
+
createdAt: item.createdAt,
|
|
386
|
+
updatedAt: item.updatedAt,
|
|
387
|
+
}),
|
|
388
|
+
).rejects.toThrow();
|
|
331
389
|
});
|
|
390
|
+
});
|
|
332
391
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
const
|
|
392
|
+
describe("deleteSidebarItem", () => {
|
|
393
|
+
it("deletes a sidebar item", async () => {
|
|
394
|
+
const item = await collectionService.createSidebarItem("divider");
|
|
395
|
+
const result = await collectionService.deleteSidebarItem(item.id);
|
|
396
|
+
expect(result).toBe(true);
|
|
337
397
|
|
|
338
|
-
await collectionService.
|
|
398
|
+
const items = await collectionService.listSidebarItems();
|
|
399
|
+
expect(items).toHaveLength(0);
|
|
400
|
+
});
|
|
339
401
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
402
|
+
it("returns false for non-existent item", async () => {
|
|
403
|
+
const result = await collectionService.deleteSidebarItem(
|
|
404
|
+
"00000000-0000-0000-0000-000000009999",
|
|
405
|
+
);
|
|
406
|
+
expect(result).toBe(false);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
describe("moveSidebarItem", () => {
|
|
411
|
+
it("moves an item between two others", async () => {
|
|
412
|
+
const col1 = await collectionService.create({ slug: "a", title: "A" });
|
|
413
|
+
const col2 = await collectionService.create({ slug: "b", title: "B" });
|
|
414
|
+
const col3 = await collectionService.create({ slug: "c", title: "C" });
|
|
415
|
+
|
|
416
|
+
// Get sidebar items (A, B, C order)
|
|
417
|
+
const items = await collectionService.listSidebarItems();
|
|
418
|
+
expect(items).toHaveLength(3);
|
|
419
|
+
const itemA = items.find((i) => i.collectionId === col1.id);
|
|
420
|
+
const itemB = items.find((i) => i.collectionId === col2.id);
|
|
421
|
+
const itemC = items.find((i) => i.collectionId === col3.id);
|
|
422
|
+
expect(itemA).toBeDefined();
|
|
423
|
+
expect(itemB).toBeDefined();
|
|
424
|
+
expect(itemC).toBeDefined();
|
|
425
|
+
|
|
426
|
+
// Move C between A and B
|
|
427
|
+
const moved = await collectionService.moveSidebarItem(
|
|
428
|
+
itemC?.id ?? "",
|
|
429
|
+
itemA?.id ?? "",
|
|
430
|
+
itemB?.id ?? "",
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
expect(moved).not.toBeNull();
|
|
434
|
+
|
|
435
|
+
// Verify new order: A, C, B
|
|
436
|
+
const reordered = await collectionService.listSidebarItems();
|
|
437
|
+
expect(reordered[0]?.collectionId).toBe(col1.id);
|
|
438
|
+
expect(reordered[1]?.collectionId).toBe(col3.id);
|
|
439
|
+
expect(reordered[2]?.collectionId).toBe(col2.id);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it("moves an item to the beginning", async () => {
|
|
443
|
+
const col1 = await collectionService.create({ slug: "a", title: "A" });
|
|
444
|
+
const col2 = await collectionService.create({ slug: "b", title: "B" });
|
|
445
|
+
const col3 = await collectionService.create({ slug: "c", title: "C" });
|
|
446
|
+
|
|
447
|
+
const items = await collectionService.listSidebarItems();
|
|
448
|
+
const itemA = items.find((i) => i.collectionId === col1.id);
|
|
449
|
+
const itemC = items.find((i) => i.collectionId === col3.id);
|
|
450
|
+
expect(itemA).toBeDefined();
|
|
451
|
+
expect(itemC).toBeDefined();
|
|
452
|
+
|
|
453
|
+
// Move C to the beginning (before A, no after)
|
|
454
|
+
await collectionService.moveSidebarItem(
|
|
455
|
+
itemC?.id ?? "",
|
|
456
|
+
null,
|
|
457
|
+
itemA?.id ?? "",
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
const reordered = await collectionService.listSidebarItems();
|
|
461
|
+
expect(reordered[0]?.collectionId).toBe(col3.id);
|
|
462
|
+
expect(reordered[1]?.collectionId).toBe(col1.id);
|
|
463
|
+
expect(reordered[2]?.collectionId).toBe(col2.id);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it("moves an item to the end", async () => {
|
|
467
|
+
const col1 = await collectionService.create({ slug: "a", title: "A" });
|
|
468
|
+
const col2 = await collectionService.create({ slug: "b", title: "B" });
|
|
469
|
+
const col3 = await collectionService.create({ slug: "c", title: "C" });
|
|
470
|
+
|
|
471
|
+
const items = await collectionService.listSidebarItems();
|
|
472
|
+
const itemA = items.find((i) => i.collectionId === col1.id);
|
|
473
|
+
const itemC = items.find((i) => i.collectionId === col3.id);
|
|
474
|
+
expect(itemA).toBeDefined();
|
|
475
|
+
expect(itemC).toBeDefined();
|
|
476
|
+
|
|
477
|
+
// Move A to the end (after C, no before)
|
|
478
|
+
await collectionService.moveSidebarItem(
|
|
479
|
+
itemA?.id ?? "",
|
|
480
|
+
itemC?.id ?? "",
|
|
481
|
+
null,
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
const reordered = await collectionService.listSidebarItems();
|
|
485
|
+
expect(reordered[0]?.collectionId).toBe(col2.id);
|
|
486
|
+
expect(reordered[1]?.collectionId).toBe(col3.id);
|
|
487
|
+
expect(reordered[2]?.collectionId).toBe(col1.id);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("returns null for non-existent item", async () => {
|
|
491
|
+
const result = await collectionService.moveSidebarItem(
|
|
492
|
+
"00000000-0000-0000-0000-000000009999",
|
|
493
|
+
null,
|
|
494
|
+
null,
|
|
495
|
+
);
|
|
496
|
+
expect(result).toBeNull();
|
|
344
497
|
});
|
|
345
498
|
});
|
|
346
499
|
|
|
@@ -362,9 +515,18 @@ describe("CollectionService", () => {
|
|
|
362
515
|
title: "Col 2",
|
|
363
516
|
});
|
|
364
517
|
|
|
365
|
-
const p1 = await postService.create({
|
|
366
|
-
|
|
367
|
-
|
|
518
|
+
const p1 = await postService.create({
|
|
519
|
+
format: "note",
|
|
520
|
+
bodyMarkdown: "post 1",
|
|
521
|
+
});
|
|
522
|
+
const p2 = await postService.create({
|
|
523
|
+
format: "note",
|
|
524
|
+
bodyMarkdown: "post 2",
|
|
525
|
+
});
|
|
526
|
+
const p3 = await postService.create({
|
|
527
|
+
format: "note",
|
|
528
|
+
bodyMarkdown: "post 3",
|
|
529
|
+
});
|
|
368
530
|
|
|
369
531
|
await collectionService.addPost(col1.id, p1.id);
|
|
370
532
|
await collectionService.addPost(col1.id, p2.id);
|
|
@@ -383,9 +545,12 @@ describe("CollectionService", () => {
|
|
|
383
545
|
|
|
384
546
|
const p1 = await postService.create({
|
|
385
547
|
format: "note",
|
|
386
|
-
|
|
548
|
+
bodyMarkdown: "with collection",
|
|
549
|
+
});
|
|
550
|
+
await postService.create({
|
|
551
|
+
format: "note",
|
|
552
|
+
bodyMarkdown: "no collection",
|
|
387
553
|
});
|
|
388
|
-
await postService.create({ format: "note", body: "no collection" });
|
|
389
554
|
|
|
390
555
|
await collectionService.addPost(col.id, p1.id);
|
|
391
556
|
|
|
@@ -402,11 +567,11 @@ describe("CollectionService", () => {
|
|
|
402
567
|
|
|
403
568
|
const post = await postService.create({
|
|
404
569
|
format: "note",
|
|
405
|
-
|
|
570
|
+
bodyMarkdown: "will be deleted",
|
|
406
571
|
});
|
|
407
572
|
const post2 = await postService.create({
|
|
408
573
|
format: "note",
|
|
409
|
-
|
|
574
|
+
bodyMarkdown: "still alive",
|
|
410
575
|
});
|
|
411
576
|
|
|
412
577
|
await collectionService.addPost(col.id, post.id);
|
|
@@ -428,7 +593,7 @@ describe("CollectionService", () => {
|
|
|
428
593
|
});
|
|
429
594
|
const post = await postService.create({
|
|
430
595
|
format: "note",
|
|
431
|
-
|
|
596
|
+
bodyMarkdown: "test",
|
|
432
597
|
});
|
|
433
598
|
|
|
434
599
|
await collectionService.addPost(col.id, post.id);
|
|
@@ -447,7 +612,7 @@ describe("CollectionService", () => {
|
|
|
447
612
|
});
|
|
448
613
|
const post = await postService.create({
|
|
449
614
|
format: "note",
|
|
450
|
-
|
|
615
|
+
bodyMarkdown: "test",
|
|
451
616
|
});
|
|
452
617
|
|
|
453
618
|
await collectionService.addPost(col.id, post.id);
|
|
@@ -464,7 +629,7 @@ describe("CollectionService", () => {
|
|
|
464
629
|
});
|
|
465
630
|
const post = await postService.create({
|
|
466
631
|
format: "note",
|
|
467
|
-
|
|
632
|
+
bodyMarkdown: "test",
|
|
468
633
|
});
|
|
469
634
|
|
|
470
635
|
await collectionService.addPost(col.id, post.id);
|
|
@@ -479,37 +644,35 @@ describe("CollectionService", () => {
|
|
|
479
644
|
|
|
480
645
|
describe("getCollectionsByPostId", () => {
|
|
481
646
|
it("returns all collections a post belongs to", async () => {
|
|
482
|
-
|
|
647
|
+
await collectionService.create({
|
|
483
648
|
slug: "col1",
|
|
484
649
|
title: "Col 1",
|
|
485
|
-
position: 0,
|
|
486
650
|
});
|
|
487
|
-
|
|
651
|
+
await collectionService.create({
|
|
488
652
|
slug: "col2",
|
|
489
653
|
title: "Col 2",
|
|
490
|
-
position: 1,
|
|
491
654
|
});
|
|
492
655
|
|
|
656
|
+
const cols = await collectionService.list();
|
|
657
|
+
expect(cols).toHaveLength(2);
|
|
493
658
|
const post = await postService.create({
|
|
494
659
|
format: "note",
|
|
495
|
-
|
|
660
|
+
bodyMarkdown: "test",
|
|
496
661
|
});
|
|
497
662
|
|
|
498
|
-
await collectionService.addPost(
|
|
499
|
-
await collectionService.addPost(
|
|
663
|
+
await collectionService.addPost(cols[0]?.id ?? "", post.id);
|
|
664
|
+
await collectionService.addPost(cols[1]?.id ?? "", post.id);
|
|
500
665
|
|
|
501
666
|
const collections = await collectionService.getCollectionsByPostId(
|
|
502
667
|
post.id,
|
|
503
668
|
);
|
|
504
669
|
expect(collections).toHaveLength(2);
|
|
505
|
-
expect(collections[0]?.slug).toBe("col1");
|
|
506
|
-
expect(collections[1]?.slug).toBe("col2");
|
|
507
670
|
});
|
|
508
671
|
|
|
509
672
|
it("returns empty array for post with no collections", async () => {
|
|
510
673
|
const post = await postService.create({
|
|
511
674
|
format: "note",
|
|
512
|
-
|
|
675
|
+
bodyMarkdown: "test",
|
|
513
676
|
});
|
|
514
677
|
|
|
515
678
|
const collections = await collectionService.getCollectionsByPostId(
|
|
@@ -525,8 +688,14 @@ describe("CollectionService", () => {
|
|
|
525
688
|
slug: "test",
|
|
526
689
|
title: "Test",
|
|
527
690
|
});
|
|
528
|
-
const p1 = await postService.create({
|
|
529
|
-
|
|
691
|
+
const p1 = await postService.create({
|
|
692
|
+
format: "note",
|
|
693
|
+
bodyMarkdown: "one",
|
|
694
|
+
});
|
|
695
|
+
const p2 = await postService.create({
|
|
696
|
+
format: "note",
|
|
697
|
+
bodyMarkdown: "two",
|
|
698
|
+
});
|
|
530
699
|
|
|
531
700
|
await collectionService.addPost(col.id, p1.id);
|
|
532
701
|
await collectionService.addPost(col.id, p2.id);
|
|
@@ -538,127 +707,6 @@ describe("CollectionService", () => {
|
|
|
538
707
|
});
|
|
539
708
|
});
|
|
540
709
|
|
|
541
|
-
describe("createDivider", () => {
|
|
542
|
-
it("creates a divider with auto-assigned position", async () => {
|
|
543
|
-
const divider = await collectionService.createDivider();
|
|
544
|
-
|
|
545
|
-
expect(divider.id).toBe(1);
|
|
546
|
-
expect(divider.position).toBe(0);
|
|
547
|
-
expect(divider.createdAt).toBeGreaterThan(0);
|
|
548
|
-
expect(divider.updatedAt).toBeGreaterThan(0);
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
it("assigns position after existing collections", async () => {
|
|
552
|
-
await collectionService.create({ slug: "a", title: "A" }); // position 0
|
|
553
|
-
await collectionService.create({ slug: "b", title: "B" }); // position 1
|
|
554
|
-
|
|
555
|
-
const divider = await collectionService.createDivider();
|
|
556
|
-
expect(divider.position).toBe(2);
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
it("assigns position after existing dividers", async () => {
|
|
560
|
-
const d1 = await collectionService.createDivider(); // position 0
|
|
561
|
-
const d2 = await collectionService.createDivider(); // position 1
|
|
562
|
-
|
|
563
|
-
expect(d1.position).toBe(0);
|
|
564
|
-
expect(d2.position).toBe(1);
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
it("considers both collections and dividers for position", async () => {
|
|
568
|
-
await collectionService.create({ slug: "a", title: "A" }); // position 0
|
|
569
|
-
await collectionService.createDivider(); // position 1
|
|
570
|
-
await collectionService.create({ slug: "b", title: "B" }); // position 2
|
|
571
|
-
|
|
572
|
-
const divider = await collectionService.createDivider();
|
|
573
|
-
expect(divider.position).toBe(3);
|
|
574
|
-
});
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
describe("deleteDivider", () => {
|
|
578
|
-
it("deletes a divider by ID", async () => {
|
|
579
|
-
const divider = await collectionService.createDivider();
|
|
580
|
-
|
|
581
|
-
const result = await collectionService.deleteDivider(divider.id);
|
|
582
|
-
expect(result).toBe(true);
|
|
583
|
-
|
|
584
|
-
const list = await collectionService.listDividers();
|
|
585
|
-
expect(list).toHaveLength(0);
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
it("returns false for non-existent divider", async () => {
|
|
589
|
-
const result = await collectionService.deleteDivider(9999);
|
|
590
|
-
expect(result).toBe(false);
|
|
591
|
-
});
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
describe("listDividers", () => {
|
|
595
|
-
it("returns empty array when no dividers exist", async () => {
|
|
596
|
-
const list = await collectionService.listDividers();
|
|
597
|
-
expect(list).toEqual([]);
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
it("returns dividers ordered by position", async () => {
|
|
601
|
-
const d1 = await collectionService.createDivider();
|
|
602
|
-
const d2 = await collectionService.createDivider();
|
|
603
|
-
|
|
604
|
-
const list = await collectionService.listDividers();
|
|
605
|
-
expect(list).toHaveLength(2);
|
|
606
|
-
expect(list[0]?.id).toBe(d1.id);
|
|
607
|
-
expect(list[1]?.id).toBe(d2.id);
|
|
608
|
-
});
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
describe("reorderAll", () => {
|
|
612
|
-
it("handles mixed prefixed IDs correctly", async () => {
|
|
613
|
-
const a = await collectionService.create({ slug: "a", title: "A" });
|
|
614
|
-
const b = await collectionService.create({ slug: "b", title: "B" });
|
|
615
|
-
const d1 = await collectionService.createDivider();
|
|
616
|
-
|
|
617
|
-
// Reorder: divider first, then B, then A
|
|
618
|
-
await collectionService.reorderAll([
|
|
619
|
-
`d-${d1.id}`,
|
|
620
|
-
`c-${b.id}`,
|
|
621
|
-
`c-${a.id}`,
|
|
622
|
-
]);
|
|
623
|
-
|
|
624
|
-
const dividers = await collectionService.listDividers();
|
|
625
|
-
expect(dividers[0]?.position).toBe(0);
|
|
626
|
-
|
|
627
|
-
const colB = await collectionService.getById(b.id);
|
|
628
|
-
const colA = await collectionService.getById(a.id);
|
|
629
|
-
expect(colB?.position).toBe(1);
|
|
630
|
-
expect(colA?.position).toBe(2);
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
it("handles empty array", async () => {
|
|
634
|
-
await collectionService.reorderAll([]);
|
|
635
|
-
// Should not throw
|
|
636
|
-
const list = await collectionService.list();
|
|
637
|
-
expect(list).toEqual([]);
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
it("reflects new order in combined list", async () => {
|
|
641
|
-
const a = await collectionService.create({ slug: "a", title: "A" });
|
|
642
|
-
const d1 = await collectionService.createDivider();
|
|
643
|
-
const b = await collectionService.create({ slug: "b", title: "B" });
|
|
644
|
-
|
|
645
|
-
// Put divider between B and A
|
|
646
|
-
await collectionService.reorderAll([
|
|
647
|
-
`c-${b.id}`,
|
|
648
|
-
`d-${d1.id}`,
|
|
649
|
-
`c-${a.id}`,
|
|
650
|
-
]);
|
|
651
|
-
|
|
652
|
-
const cols = await collectionService.list();
|
|
653
|
-
const divs = await collectionService.listDividers();
|
|
654
|
-
|
|
655
|
-
// B at position 0, divider at 1, A at 2
|
|
656
|
-
expect(cols.find((c) => c.id === b.id)?.position).toBe(0);
|
|
657
|
-
expect(divs[0]?.position).toBe(1);
|
|
658
|
-
expect(cols.find((c) => c.id === a.id)?.position).toBe(2);
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
|
|
662
710
|
describe("syncPostCollections", () => {
|
|
663
711
|
it("replaces all collection memberships for a post", async () => {
|
|
664
712
|
const col1 = await collectionService.create({
|
|
@@ -676,7 +724,7 @@ describe("CollectionService", () => {
|
|
|
676
724
|
|
|
677
725
|
const post = await postService.create({
|
|
678
726
|
format: "note",
|
|
679
|
-
|
|
727
|
+
bodyMarkdown: "test",
|
|
680
728
|
});
|
|
681
729
|
|
|
682
730
|
// Initially in col1 and col2
|
|
@@ -703,7 +751,7 @@ describe("CollectionService", () => {
|
|
|
703
751
|
});
|
|
704
752
|
const post = await postService.create({
|
|
705
753
|
format: "note",
|
|
706
|
-
|
|
754
|
+
bodyMarkdown: "test",
|
|
707
755
|
});
|
|
708
756
|
|
|
709
757
|
await collectionService.addPost(col.id, post.id);
|