- Treat All as an unfiltered Space collection while preserving exact Space filters. - Keep global resources outside remembered Space context and preserve memo navigation origins. - Surface Space placement consistently across memo, attachment, and visibility UI.
127 lines
4.6 KiB
TypeScript
127 lines
4.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
createMemoNavigationState,
|
|
isMemoCollectionOrigin,
|
|
isMemoDetailPath,
|
|
isMemoResourcePath,
|
|
resolveMemoDetailOrigin,
|
|
resolveMemoOrigin,
|
|
resolveMemoParentPage,
|
|
withMemoFilter,
|
|
} from "@/components/MemoView/navigation";
|
|
|
|
describe("memo view navigation", () => {
|
|
it("captures the complete collection origin", () => {
|
|
expect(
|
|
resolveMemoParentPage({
|
|
pathname: "/archived",
|
|
search: "?filter=tagSearch%3Awork",
|
|
memoName: "memos/123",
|
|
}),
|
|
).toBe("/archived?filter=tagSearch%3Awork");
|
|
});
|
|
|
|
it("keeps an explicit origin on a canonical memo route", () => {
|
|
expect(
|
|
resolveMemoParentPage({
|
|
explicitParentPage: "/explore?filter=contentSearch%3Aroadmap",
|
|
pathname: "/memos/123",
|
|
search: "",
|
|
memoName: "memos/123",
|
|
}),
|
|
).toBe("/explore?filter=contentSearch%3Aroadmap");
|
|
});
|
|
|
|
it("marks collection cards as preserving the remembered scope", () => {
|
|
expect(resolveMemoOrigin({ pathname: "/explore", search: "?filter=tagSearch%3Awork", memoName: "memos/123" })).toEqual({
|
|
parentPage: "/explore?filter=tagSearch%3Awork",
|
|
parentScope: "preserve",
|
|
});
|
|
});
|
|
|
|
it("marks Profile cards as global without losing their Profile origin", () => {
|
|
expect(resolveMemoOrigin({ pathname: "/u/alice", search: "?view=map", memoName: "memos/123" })).toEqual({
|
|
parentPage: "/u/alice?view=map",
|
|
parentScope: "all",
|
|
});
|
|
});
|
|
|
|
it("uses an All Home origin for direct and shared resource entries", () => {
|
|
expect(resolveMemoDetailOrigin(undefined)).toEqual({ parentPage: "/", parentScope: "all" });
|
|
expect(resolveMemoDetailOrigin({ unrelated: true })).toEqual({ parentPage: "/", parentScope: "all" });
|
|
});
|
|
|
|
it("uses an All Archived origin for a direct archived memo", () => {
|
|
expect(resolveMemoDetailOrigin(undefined, { memoArchived: true })).toEqual({ parentPage: "/archived", parentScope: "all" });
|
|
});
|
|
|
|
it("keeps an explicit origin ahead of the archived fallback", () => {
|
|
expect(resolveMemoDetailOrigin(createMemoNavigationState("/u/alice", "all"), { memoArchived: true })).toEqual({
|
|
parentPage: "/u/alice",
|
|
parentScope: "all",
|
|
});
|
|
});
|
|
|
|
it("round-trips an explicit origin policy through router state", () => {
|
|
const state = createMemoNavigationState("/", "preserve");
|
|
expect(resolveMemoDetailOrigin(state)).toEqual({ parentPage: "/", parentScope: "preserve" });
|
|
});
|
|
|
|
it("infers policy for legacy from-only router state", () => {
|
|
expect(resolveMemoDetailOrigin({ from: "/archived" })).toEqual({ parentPage: "/archived", parentScope: "preserve" });
|
|
expect(resolveMemoDetailOrigin({ from: "/u/alice" })).toEqual({ parentPage: "/u/alice", parentScope: "all" });
|
|
});
|
|
|
|
it.each([
|
|
"/",
|
|
"/explore?filter=tagSearch%3Awork",
|
|
"/archived",
|
|
"/attachments",
|
|
])("recognizes %s as a remembered collection origin", (page) => {
|
|
expect(isMemoCollectionOrigin(page)).toBe(true);
|
|
});
|
|
|
|
it.each([
|
|
"/memos/123",
|
|
"/memos/123/",
|
|
"/Memos/123",
|
|
"/memos/shares/token",
|
|
"/Memos/Shares/token/",
|
|
])("does not treat %s as its own origin", (pathname) => {
|
|
expect(isMemoDetailPath(pathname, "memos/123")).toBe(true);
|
|
expect(resolveMemoParentPage({ pathname, search: "", memoName: "memos/123" })).toBe("/");
|
|
});
|
|
|
|
it("does not confuse a different memo route with the current memo detail", () => {
|
|
const pathname = "/memos/1234";
|
|
expect(isMemoDetailPath(pathname, "memos/123")).toBe(false);
|
|
expect(resolveMemoParentPage({ pathname, search: "", memoName: "memos/123" })).toBe(pathname);
|
|
});
|
|
|
|
it("rejects extra path segments after a share token", () => {
|
|
expect(isMemoDetailPath("/memos/shares/token/extra", "memos/123")).toBe(false);
|
|
});
|
|
|
|
it.each([
|
|
"/memos/123",
|
|
"/Memos/123/",
|
|
"/memos/shares/token",
|
|
"/Memos/Shares/token/",
|
|
])("recognizes %s as a Memo resource route", (pathname) => {
|
|
expect(isMemoResourcePath(pathname)).toBe(true);
|
|
});
|
|
|
|
it.each(["/", "/memos", "/memos/123/extra", "/memos/shares/token/extra"])("rejects %s as a Memo resource route", (pathname) => {
|
|
expect(isMemoResourcePath(pathname)).toBe(false);
|
|
});
|
|
|
|
it("replaces the filter without dropping other origin query parameters", () => {
|
|
expect(withMemoFilter("/u/alice?view=memos&filter=old#section", "tagSearch:design")).toBe(
|
|
"/u/alice?view=memos&filter=tagSearch%3Adesign",
|
|
);
|
|
});
|
|
|
|
it("returns a Profile map origin to its memo list when applying a filter", () => {
|
|
expect(withMemoFilter("/u/alice?view=map", "tagSearch:design")).toBe("/u/alice?filter=tagSearch%3Adesign");
|
|
});
|
|
});
|