The selected Space used to live in sessionStorage and ride along invisibly with every collection route. It now lives in the URL: collection pages exist both globally and beneath /spaces/:spaceUid, and the Space context derives its scope from the current location. - Add a Space route branch (Home, Explore, Calendar, Attachments) guarded by a SpaceRoute that shows loading, unavailable, and retry states. - Resolve collection routes through one matchPath-based helper built from the route constants; build Space-scoped paths with buildCollectionPath. - Fetch the route Space directly, seeded from the switcher list so pages are not held behind a second round-trip. - Turn the Space switcher into plain links that preserve the current collection view and query. - Drop the remembered-Space machinery: clearSelectedSpace/selectMemos, the compose-placement policy, routeSupportsCollectionScope, and the quick-find switchToAll flag are all implied by the URL now. - Return from memo details to the full origin URL, including its Space. - Add space.unavailable and space.back-to-memos strings; align the Azerbaijani catalog with English.
137 lines
5.6 KiB
TypeScript
137 lines
5.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { buildQuickFindFilters, resolveQuickFindSubmission } from "@/components/AppSidebar/QuickFindDialog";
|
|
import { type MemoFilter, parseFilterQuery, replaceFiltersByFactor } from "@/contexts/MemoFilterContext";
|
|
|
|
describe("Quick Find", () => {
|
|
const scopedFilters: MemoFilter[] = [
|
|
{ factor: "tagSearch", value: "work" },
|
|
{ factor: "displayTime", value: "2026-08-03" },
|
|
{ factor: "contentSearch", value: "old" },
|
|
];
|
|
|
|
it.each([
|
|
"content.contains('urgent')",
|
|
'tags.exists(t, t.startsWith("work/"))',
|
|
'content.matches("(?i)urgent")',
|
|
"space == null",
|
|
'content.contains("50%, café & C++")\n || pinned',
|
|
])("retains CEL verbatim through a navigation URL: %s", (expression) => {
|
|
const submission = resolveQuickFindSubmission("/attachments", expression, scopedFilters, "cel");
|
|
const decoded = parseFilterQuery(new URL(submission.destination!, "https://memos.test").searchParams.get("filter"));
|
|
expect(decoded).toEqual([
|
|
...scopedFilters.filter((filter) => filter.factor !== "contentSearch"),
|
|
{ factor: "celSearch", value: expression },
|
|
]);
|
|
});
|
|
|
|
it("replaces both kinds of search without dropping facets", () => {
|
|
const filters: MemoFilter[] = [...scopedFilters, { factor: "celSearch", value: "pinned" }];
|
|
expect(buildQuickFindFilters("has_link", filters, true, "cel")).toEqual([
|
|
...scopedFilters.slice(0, 2),
|
|
{ factor: "celSearch", value: "has_link" },
|
|
]);
|
|
expect(buildQuickFindFilters("new new", filters, true, "text")).toEqual([
|
|
...scopedFilters.slice(0, 2),
|
|
{ factor: "contentSearch", value: "new" },
|
|
]);
|
|
expect(buildQuickFindFilters(" \n ", filters, true, "cel")).toEqual(scopedFilters.slice(0, 2));
|
|
expect(buildQuickFindFilters(" ", filters, true, "text")).toEqual(scopedFilters.slice(0, 2));
|
|
});
|
|
|
|
it("does not recognize expressions in Text mode", () => {
|
|
expect(buildQuickFindFilters('content.contains("urgent")', [], false, "text")).toEqual([
|
|
{ factor: "contentSearch", value: 'content.contains("urgent")' },
|
|
]);
|
|
});
|
|
|
|
it("replaces search terms while preserving date and tag facets in a collection", () => {
|
|
expect(buildQuickFindFilters("project plan project", scopedFilters, true, "text")).toEqual([
|
|
{ factor: "tagSearch", value: "work" },
|
|
{ factor: "displayTime", value: "2026-08-03" },
|
|
{ factor: "contentSearch", value: "project" },
|
|
{ factor: "contentSearch", value: "plan" },
|
|
]);
|
|
});
|
|
|
|
it("starts a clean All search outside collection routes", () => {
|
|
expect(buildQuickFindFilters("project", scopedFilters, false, "text")).toEqual([{ factor: "contentSearch", value: "project" }]);
|
|
});
|
|
|
|
it.each(["/", "/explore"])("keeps scoped filters and stays on %s", (pathname) => {
|
|
expect(resolveQuickFindSubmission(pathname, "project", scopedFilters, "text")).toEqual({
|
|
filters: [
|
|
{ factor: "tagSearch", value: "work" },
|
|
{ factor: "displayTime", value: "2026-08-03" },
|
|
{ factor: "contentSearch", value: "project" },
|
|
],
|
|
destination: undefined,
|
|
});
|
|
});
|
|
|
|
it("searches Archived as a user collection", () => {
|
|
expect(resolveQuickFindSubmission("/archived", "project", scopedFilters, "text")).toEqual({
|
|
filters: [
|
|
{ factor: "tagSearch", value: "work" },
|
|
{ factor: "displayTime", value: "2026-08-03" },
|
|
{ factor: "contentSearch", value: "project" },
|
|
],
|
|
destination: undefined,
|
|
});
|
|
});
|
|
|
|
it("keeps the collection filters when searching from Attachments", () => {
|
|
expect(resolveQuickFindSubmission("/attachments", "project", scopedFilters, "text")).toEqual({
|
|
filters: [
|
|
{ factor: "tagSearch", value: "work" },
|
|
{ factor: "displayTime", value: "2026-08-03" },
|
|
{ factor: "contentSearch", value: "project" },
|
|
],
|
|
destination: "/?filter=tagSearch%3Awork%2CdisplayTime%3A2026-08-03%2CcontentSearch%3Aproject",
|
|
});
|
|
});
|
|
|
|
it("keeps the collection on a normalized Attachments route", () => {
|
|
expect(resolveQuickFindSubmission("/Attachments/", "project", scopedFilters, "text")).toEqual({
|
|
filters: [
|
|
{ factor: "tagSearch", value: "work" },
|
|
{ factor: "displayTime", value: "2026-08-03" },
|
|
{ factor: "contentSearch", value: "project" },
|
|
],
|
|
destination: "/?filter=tagSearch%3Awork%2CdisplayTime%3A2026-08-03%2CcontentSearch%3Aproject",
|
|
});
|
|
});
|
|
|
|
it("keeps Profile search on the Profile and returns its map tab to the memo list", () => {
|
|
expect(resolveQuickFindSubmission("/u/steven", "project", scopedFilters, "text")).toEqual({
|
|
filters: [
|
|
{ factor: "tagSearch", value: "work" },
|
|
{ factor: "displayTime", value: "2026-08-03" },
|
|
{ factor: "contentSearch", value: "project" },
|
|
],
|
|
destination: "/u/steven?filter=tagSearch%3Awork%2CdisplayTime%3A2026-08-03%2CcontentSearch%3Aproject",
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
"/inbox",
|
|
"/setting",
|
|
"/views",
|
|
"/about",
|
|
"/memos/abc",
|
|
"/memos/shares/token",
|
|
"/404",
|
|
])("starts a clean All search from %s", (pathname) => {
|
|
expect(resolveQuickFindSubmission(pathname, "project", scopedFilters, "text")).toEqual({
|
|
filters: [{ factor: "contentSearch", value: "project" }],
|
|
destination: "/?filter=contentSearch%3Aproject",
|
|
});
|
|
});
|
|
|
|
it("replaces only the selected date facet", () => {
|
|
expect(replaceFiltersByFactor(scopedFilters, "displayTime", [{ factor: "displayTime", value: "2026-08-02" }])).toEqual([
|
|
{ factor: "tagSearch", value: "work" },
|
|
{ factor: "contentSearch", value: "old" },
|
|
{ factor: "displayTime", value: "2026-08-02" },
|
|
]);
|
|
});
|
|
});
|