Ctrl K
ComunidadGuías…

lib/app-tab-restoration.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// @vitest-environment jsdom
import { act, createElement } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useBoardViews } from "./use-board-views";
import { useAppTabLocalState } from "./app-tab-local-state";
import { useAppTabChange } from "./use-app-tab-change";
import { DEFAULT_CONFIG } from "./view-filter";
import type { View } from "./types";

const context = vi.hoisted(() => ({ activeId: "a" as string | null, values: new Map<string, unknown>(), views: [] as View[] }));
vi.mock("./app-tabs-context", () => ({ useOptionalAppTabNavigation: () => ({
  activeId: context.activeId,
  session: { getLocalState: (key: string) => context.values.get(key), setLocalState: (key: string, value: unknown) => context.values.set(key, value) },
}) }));
vi.mock("./use-views-query", () => ({ useViewsQuery: () => ({ views: context.views, loading: false }) }));
vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key }));
vi.mock("sonner", async (importOriginal) => ({
  ...(await importOriginal<Record<string, unknown>>()),
  toast: { success: vi.fn(), error: vi.fn() },
}));

let root: Root;
let element: HTMLDivElement;
beforeEach(() => {
  Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true });
  context.activeId = "a"; context.values.clear(); localStorage.clear();
  context.views = ["view-a", "view-b"].map((id, position) => ({
    id, position, ...DEFAULT_CONFIG, name: id, kind: "custom", project_id: null,
    user_id: null, created_at: "", updated_at: "",
  }));
  element = document.createElement("div"); document.body.append(element); root = createRoot(element);
});
afterEach(async () => { await act(() => root.unmount()); element.remove(); });

describe("application tab page restoration", () => {
  it("opens an explicit remote destination before a different remembered view", async () => {
    context.values.set("a:minddy:view:global", { id: "view-a", config: { ...DEFAULT_CONFIG, sort: "priority" } });
    let board!: ReturnType<typeof useBoardViews>;
    const consume = vi.fn();
    function Board() {
      board = useBoardViews({ kind: "global" }, { viewParam: "view-b", onViewParamConsumed: consume });
      return null;
    }
    await act(() => root.render(createElement(Board)));
    expect(board.activeViewId).toBe("view-b");
    expect(board.config).toEqual(DEFAULT_CONFIG);
    expect(consume).toHaveBeenCalledOnce();
  });
  it("falls back to an available view when the remembered view was deleted", async () => {
    context.values.set("a:minddy:view:global", { id: "deleted", config: DEFAULT_CONFIG });
    let selected: string | null = null;
    function Board() {
      selected = useBoardViews({ kind: "global" }, { viewParam: "deleted", onViewParamConsumed: vi.fn() }).activeViewId;
      return null;
    }
    await act(() => root.render(createElement(Board)));
    expect(selected).toBe("view-a");
  });
  it("closes transient panels on activation, but preserves the initial notification panel", async () => {
    context.activeId = null;
    const close = vi.fn();
    function Panel() { useAppTabChange(close); return null; }
    const render = () => act(() => root.render(createElement(Panel)));
    await render(); context.activeId = "a"; await render();
    expect(close).not.toHaveBeenCalled();
    context.activeId = "b"; await render(); expect(close).toHaveBeenCalledOnce();
    await render(); expect(close).toHaveBeenCalledOnce();
  });
  it("keeps independent working filters through same-route activation and delayed URL consumption", async () => {
    let board!: ReturnType<typeof useBoardViews>;
    const consume = vi.fn();
    function Board({ param }: { param: string | null }) {
      board = useBoardViews({ kind: "global" }, { viewParam: param, onViewParamConsumed: consume });
      return null;
    }
    const render = (param: string | null) => act(() => root.render(createElement(Board, { param })));
    await render("view-a"); await render(null);
    await act(() => board.setConfig({ ...DEFAULT_CONFIG, sort: "priority" }));
    context.activeId = "b";
    await render(null); await render("view-b"); await render(null);
    expect(board.activeViewId).toBe("view-b");
    await act(() => board.setConfig({ ...DEFAULT_CONFIG, sort: "created" }));
    context.activeId = "a";
    await render(null); await render("view-a"); await render(null);
    expect(board.activeViewId).toBe("view-a");
    expect(board.config.sort).toBe("priority");
    expect(board.dirty).toBe(true);
    context.activeId = "b";
    await render(null); await render("view-b"); await render(null);
    expect(board.config.sort).toBe("created");
  });

  it("restores the selected cycle independently and supports functional updates", async () => {
    let state!: ReturnType<typeof useAppTabLocalState<string | null>>;
    function Selection() { state = useAppTabLocalState<string | null>("cycle", null); return null; }
    const render = () => act(() => root.render(createElement(Selection)));
    await render(); await act(() => state[1]("past-cycle"));
    context.activeId = "b"; await render(); expect(state[0]).toBeNull();
    await act(() => state[1]((previous) => previous ?? "future-cycle"));
    context.activeId = "a"; await render(); expect(state[0]).toBe("past-cycle");
    context.activeId = "b"; await render(); expect(state[0]).toBe("future-cycle");
  });
  it("hands a consumed startup selection to the first tab after the account list loads", async () => {
    context.activeId = null;
    let state!: ReturnType<typeof useAppTabLocalState<boolean>>;
    function Selection() { state = useAppTabLocalState("cycle-mode", false); return null; }
    await act(() => root.render(createElement(Selection)));
    await act(() => state[1](true));
    context.activeId = "a";
    await act(() => root.render(createElement(Selection)));
    expect(state[0]).toBe(true);
    context.activeId = "b";
    await act(() => root.render(createElement(Selection)));
    expect(state[0]).toBe(false);
  });
});