mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 11:20:37 +09:00
154 lines
3.9 KiB
TypeScript
154 lines
3.9 KiB
TypeScript
|
|
// @vitest-environment jsdom
|
||
|
|
|
||
|
|
import { act } from "react";
|
||
|
|
import type { ReactNode } from "react";
|
||
|
|
import { createRoot } from "react-dom/client";
|
||
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||
|
|
import { Sidebar } from "./Sidebar";
|
||
|
|
|
||
|
|
const mockHeartbeatsApi = vi.hoisted(() => ({
|
||
|
|
liveRunsForCompany: vi.fn(),
|
||
|
|
}));
|
||
|
|
|
||
|
|
const mockInstanceSettingsApi = vi.hoisted(() => ({
|
||
|
|
getExperimental: vi.fn(),
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("@/lib/router", () => ({
|
||
|
|
NavLink: ({ to, children, className, ...props }: {
|
||
|
|
to: string;
|
||
|
|
children: ReactNode;
|
||
|
|
className?: string | ((state: { isActive: boolean }) => string);
|
||
|
|
}) => (
|
||
|
|
<a
|
||
|
|
href={to}
|
||
|
|
className={typeof className === "function" ? className({ isActive: false }) : className}
|
||
|
|
{...props}
|
||
|
|
>
|
||
|
|
{children}
|
||
|
|
</a>
|
||
|
|
),
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("../context/DialogContext", () => ({
|
||
|
|
useDialog: () => ({
|
||
|
|
openNewIssue: vi.fn(),
|
||
|
|
}),
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("../context/CompanyContext", () => ({
|
||
|
|
useCompany: () => ({
|
||
|
|
selectedCompanyId: "company-1",
|
||
|
|
selectedCompany: { id: "company-1", issuePrefix: "PAP", name: "Paperclip" },
|
||
|
|
}),
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("../context/SidebarContext", () => ({
|
||
|
|
useSidebar: () => ({
|
||
|
|
isMobile: false,
|
||
|
|
setSidebarOpen: vi.fn(),
|
||
|
|
}),
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("../api/heartbeats", () => ({
|
||
|
|
heartbeatsApi: mockHeartbeatsApi,
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("../api/instanceSettings", () => ({
|
||
|
|
instanceSettingsApi: mockInstanceSettingsApi,
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("../hooks/useInboxBadge", () => ({
|
||
|
|
useInboxBadge: () => ({ inbox: 0, failedRuns: 0 }),
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("@/plugins/slots", () => ({
|
||
|
|
PluginSlotOutlet: () => null,
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("./SidebarCompanyMenu", () => ({
|
||
|
|
SidebarCompanyMenu: () => <div>Company menu</div>,
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("./SidebarProjects", () => ({
|
||
|
|
SidebarProjects: () => null,
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock("./SidebarAgents", () => ({
|
||
|
|
SidebarAgents: () => null,
|
||
|
|
}));
|
||
|
|
|
||
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
|
|
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||
|
|
|
||
|
|
async function flushReact() {
|
||
|
|
await act(async () => {
|
||
|
|
await Promise.resolve();
|
||
|
|
await new Promise((resolve) => window.setTimeout(resolve, 0));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
describe("Sidebar", () => {
|
||
|
|
let container: HTMLDivElement;
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
container = document.createElement("div");
|
||
|
|
document.body.appendChild(container);
|
||
|
|
mockHeartbeatsApi.liveRunsForCompany.mockResolvedValue([]);
|
||
|
|
});
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
container.remove();
|
||
|
|
document.body.innerHTML = "";
|
||
|
|
vi.clearAllMocks();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("does not flash the Workspaces link while experimental settings are loading", async () => {
|
||
|
|
mockInstanceSettingsApi.getExperimental.mockImplementation(() => new Promise(() => {}));
|
||
|
|
const root = createRoot(container);
|
||
|
|
const queryClient = new QueryClient({
|
||
|
|
defaultOptions: { queries: { retry: false } },
|
||
|
|
});
|
||
|
|
|
||
|
|
await act(async () => {
|
||
|
|
root.render(
|
||
|
|
<QueryClientProvider client={queryClient}>
|
||
|
|
<Sidebar />
|
||
|
|
</QueryClientProvider>,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
await flushReact();
|
||
|
|
|
||
|
|
expect(container.textContent).not.toContain("Workspaces");
|
||
|
|
|
||
|
|
await act(async () => {
|
||
|
|
root.unmount();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it("shows the Workspaces link when isolated workspaces are enabled", async () => {
|
||
|
|
mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableIsolatedWorkspaces: true });
|
||
|
|
const root = createRoot(container);
|
||
|
|
const queryClient = new QueryClient({
|
||
|
|
defaultOptions: { queries: { retry: false } },
|
||
|
|
});
|
||
|
|
|
||
|
|
await act(async () => {
|
||
|
|
root.render(
|
||
|
|
<QueryClientProvider client={queryClient}>
|
||
|
|
<Sidebar />
|
||
|
|
</QueryClientProvider>,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
await flushReact();
|
||
|
|
|
||
|
|
const link = [...container.querySelectorAll("a")].find((anchor) => anchor.textContent === "Workspaces");
|
||
|
|
expect(link?.getAttribute("href")).toBe("/workspaces");
|
||
|
|
|
||
|
|
await act(async () => {
|
||
|
|
root.unmount();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|