memos/web/tests/memo-action-menu.test.tsx
johnnyjoygh af008b2153 feat(web): scope Spaces by URL path instead of a remembered selection
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.
2026-09-07 09:29:50 +08:00

50 lines
1.9 KiB
TypeScript

import { create } from "@bufbuild/protobuf";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import MemoActionMenu from "@/components/MemoActionMenu";
import { State } from "@/types/proto/api/v1/common_pb";
import { MemoSchema } from "@/types/proto/api/v1/memo_service_pb";
const handlers = vi.hoisted(() => ({
handleTogglePinMemoBtnClick: vi.fn(),
handleEditMemoClick: vi.fn(),
handleToggleMemoStatusClick: vi.fn(),
handleCopyLink: vi.fn(),
handleCopyContent: vi.fn(),
handleCheckAllTaskListItemsClick: vi.fn(),
handleUncheckAllTaskListItemsClick: vi.fn(),
handleDeleteMemoClick: vi.fn(),
confirmDeleteMemo: vi.fn(),
}));
vi.mock("@/components/ConfirmDialog", () => ({
default: () => null,
}));
vi.mock("@/components/MemoActionMenu/hooks", () => ({
useMemoActionHandlers: () => handlers,
}));
vi.mock("@/utils/i18n", () => ({
useTranslate: () => (key: string) => key,
}));
describe("MemoActionMenu", () => {
it("is a quiet compact control that takes the accent fill while open", async () => {
const memo = create(MemoSchema, { name: "memos/1", state: State.NORMAL, pinned: false });
render(<MemoActionMenu memo={memo} />);
const trigger = screen.getByRole("button", { name: "common.more" });
expect(trigger).toHaveClass("size-6", "rounded-md", "text-muted-foreground/70", "hover:bg-muted/60", "data-popup-open:bg-accent");
expect(trigger.className).not.toMatch(/ring-/);
expect(trigger).toHaveAttribute("aria-haspopup", "menu");
expect(trigger).toHaveAttribute("aria-expanded", "false");
expect(trigger.querySelector(".lucide-ellipsis-vertical")).toHaveClass("size-4");
fireEvent.click(trigger);
await waitFor(() => expect(trigger).toHaveAttribute("aria-expanded", "true"));
expect(trigger).toHaveAttribute("data-popup-open");
expect(await screen.findByRole("menuitem", { name: "common.pin" })).toBeInTheDocument();
});
});