From 4a580d713621f99abb8baabc9b847ce039d44842 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Sat, 4 Oct 2025 13:40:24 +0100 Subject: feat: Revamp import experience (#2001) * WIP: import v2 * remove new session button * don't redirect after import * store and lint to root list * models + tests * redesign the progress * simplify the import session for ow * drop status from session schema * split the import session page * i18n * fix test * remove pagination * fix some colors in darkmode * one last fix * add privacy filter * privacy check * fix interactivity of import progress * fix test --- packages/trpc/routers/importSessions.test.ts | 204 +++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 packages/trpc/routers/importSessions.test.ts (limited to 'packages/trpc/routers/importSessions.test.ts') diff --git a/packages/trpc/routers/importSessions.test.ts b/packages/trpc/routers/importSessions.test.ts new file mode 100644 index 00000000..b28d1421 --- /dev/null +++ b/packages/trpc/routers/importSessions.test.ts @@ -0,0 +1,204 @@ +import { beforeEach, describe, expect, test } from "vitest"; +import { z } from "zod"; + +import { + BookmarkTypes, + zNewBookmarkRequestSchema, +} from "@karakeep/shared/types/bookmarks"; +import { + zCreateImportSessionRequestSchema, + zDeleteImportSessionRequestSchema, + zGetImportSessionStatsRequestSchema, +} from "@karakeep/shared/types/importSessions"; +import { zNewBookmarkListSchema } from "@karakeep/shared/types/lists"; + +import type { APICallerType, CustomTestContext } from "../testUtils"; +import { defaultBeforeEach } from "../testUtils"; + +beforeEach(defaultBeforeEach(true)); + +describe("ImportSessions Routes", () => { + async function createTestBookmark(api: APICallerType, sessionId: string) { + const newBookmarkInput: z.infer = { + type: BookmarkTypes.TEXT, + text: "Test bookmark text", + importSessionId: sessionId, + }; + const createdBookmark = + await api.bookmarks.createBookmark(newBookmarkInput); + return createdBookmark.id; + } + + async function createTestList(api: APICallerType) { + const newListInput: z.infer = { + name: "Test Import List", + description: "A test list for imports", + icon: "📋", + type: "manual", + }; + const createdList = await api.lists.create(newListInput); + return createdList.id; + } + + test("create import session", async ({ apiCallers }) => { + const api = apiCallers[0].importSessions; + const listId = await createTestList(apiCallers[0]); + + const newSessionInput: z.infer = { + name: "Test Import Session", + rootListId: listId, + }; + + const createdSession = await api.createImportSession(newSessionInput); + + expect(createdSession).toMatchObject({ + id: expect.any(String), + }); + + // Verify session appears in list + const sessions = await api.listImportSessions({}); + const sessionFromList = sessions.sessions.find( + (s) => s.id === createdSession.id, + ); + expect(sessionFromList).toBeDefined(); + expect(sessionFromList?.name).toEqual(newSessionInput.name); + expect(sessionFromList?.rootListId).toEqual(listId); + }); + + test("create import session without rootListId", async ({ + apiCallers, + }) => { + const api = apiCallers[0].importSessions; + + const newSessionInput: z.infer = { + name: "Test Import Session", + }; + + const createdSession = await api.createImportSession(newSessionInput); + + expect(createdSession).toMatchObject({ + id: expect.any(String), + }); + + // Verify session appears in list + const sessions = await api.listImportSessions({}); + const sessionFromList = sessions.sessions.find( + (s) => s.id === createdSession.id, + ); + expect(sessionFromList?.rootListId).toBeNull(); + }); + + test("get import session stats", async ({ + apiCallers, + }) => { + const api = apiCallers[0]; + + const session = await api.importSessions.createImportSession({ + name: "Test Import Session", + }); + await createTestBookmark(api, session.id); + await createTestBookmark(api, session.id); + + const statsInput: z.infer = { + importSessionId: session.id, + }; + + const stats = await api.importSessions.getImportSessionStats(statsInput); + + expect(stats).toMatchObject({ + id: session.id, + name: "Test Import Session", + status: "in_progress", + totalBookmarks: 2, + pendingBookmarks: 2, + completedBookmarks: 0, + failedBookmarks: 0, + processingBookmarks: 0, + }); + }); + + test("list import sessions returns all sessions", async ({ + apiCallers, + }) => { + const api = apiCallers[0].importSessions; + + const sessionNames = ["Session 1", "Session 2", "Session 3"]; + for (const name of sessionNames) { + await api.createImportSession({ name }); + } + + const result = await api.listImportSessions({}); + + expect(result.sessions).toHaveLength(3); + expect(result.sessions.map((session) => session.name)).toEqual( + sessionNames, + ); + expect( + result.sessions.every((session) => session.totalBookmarks === 0), + ).toBe(true); + }); + + test("delete import session", async ({ apiCallers }) => { + const api = apiCallers[0].importSessions; + + const session = await api.createImportSession({ + name: "Session to Delete", + }); + + const deleteInput: z.infer = { + importSessionId: session.id, + }; + + const result = await api.deleteImportSession(deleteInput); + expect(result.success).toBe(true); + + // Verify session no longer exists + await expect( + api.getImportSessionStats({ + importSessionId: session.id, + }), + ).rejects.toThrow("Import session not found"); + }); + + test("cannot access other user's session", async ({ + apiCallers, + }) => { + const api1 = apiCallers[0].importSessions; + const api2 = apiCallers[1].importSessions; + + // User 1 creates a session + const session = await api1.createImportSession({ + name: "User 1 Session", + }); + + // User 2 tries to access it + await expect( + api2.getImportSessionStats({ + importSessionId: session.id, + }), + ).rejects.toThrow("Import session not found"); + + await expect( + api2.deleteImportSession({ + importSessionId: session.id, + }), + ).rejects.toThrow("Import session not found"); + }); + + test("cannot attach other user's bookmark", async ({ + apiCallers, + }) => { + const api1 = apiCallers[0]; + const api2 = apiCallers[1]; + + // User 1 creates session and bookmark + const session = await api1.importSessions.createImportSession({ + name: "User 1 Session", + }); + + // User 1 tries to attach User 2's bookmark + await expect( + createTestBookmark(api2, session.id), // User 2's bookmark + ).rejects.toThrow("Import session not found"); + }); +}); -- cgit v1.2.3-70-g09d2