Files
IdeA/frontend/src/adapters/window.test.ts
Blomios 45f819e1e8 fix(frontend,application): lot utilisateur #315 — détachement Paramètres projet, couleurs projet fiables, restauration conversation custom CLI, images post-aller-retour projet, retrait menus Projects/Project Context (QA verte)
- détachement: le panneau Paramètres du projet devient détachable (mapping historique panel=context → projectSettings dans parseViewWindowParams, DETACHABLE/ViewPanelBody alignés, ProjectsView ne court-circuitant plus le refus); Retry d'échec de détachement préservé
- couleurs projet: projectTheme publie idea:project-theme-changed localement + BroadcastChannel inter-webviews (fenêtres OS détachées), garde de révision par projet dans ProjectsView/ViewWindow contre les lectures settings en course — plus de flash/retour au bleu par défaut
- conversation custom CLI: transcript_restore_expected ne considère plus l'absence de scrollback volatile comme une restauration échouée quand la session rouvre en SessionPlan::None (continuation vide valide); CustomAgentChatView n'affiche plus le faux « conversation liée n'a pas pu être restaurée »
- images: conservation des images de conversation après changement de projet puis retour
- menus Panneaux: retrait des entrées Projects et Project Context (panelOrder WebWorkspace, création/switch via tab-bar +, contexte projet fusionné dans Paramètres du projet)
- QA verte: tests frontend ciblés window/ViewWindow/ProjectSettingsPanel/ProjectsView.detach/projects/CustomAgentChatView/WebDesktopWorkspace + cargo test -p application lifecycle transcript_restore_expected; fichiers .ideai/* (tickets 308-315, index/counter, agents/test, chat-drafts, system-permissions, project-settings.json, attachments/) et scripts/ hors périmètre préservés en worktree; aucun push
2026-09-11 11:27:09 +02:00

122 lines
3.7 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
const invoke = vi.fn();
const listen = vi.fn();
vi.mock("@tauri-apps/api/core", () => ({
invoke: (...args: unknown[]) => invoke(...args),
}));
vi.mock("@tauri-apps/api/event", () => ({
listen: (...args: unknown[]) => listen(...args),
}));
import { TauriWindowGateway } from "./window";
describe("TauriWindowGateway (#23)", () => {
beforeEach(() => {
invoke.mockReset().mockResolvedValue({});
listen.mockReset();
});
it("open/close relay panel-only payloads to the backend commands (#47)", async () => {
const gw = new TauriWindowGateway();
await gw.openViewWindow("tickets");
expect(invoke).toHaveBeenCalledWith("open_view_window", {
panel: "tickets",
});
await gw.closeViewWindow("git");
expect(invoke).toHaveBeenCalledWith("close_view_window", {
panel: "git",
});
});
it("maps project settings to the backend's historical context window id", async () => {
const gw = new TauriWindowGateway();
await gw.openViewWindow("projectSettings");
expect(invoke).toHaveBeenLastCalledWith("open_view_window", {
panel: "context",
});
await gw.closeViewWindow("projectSettings");
expect(invoke).toHaveBeenLastCalledWith("close_view_window", {
panel: "context",
});
});
it("opens plugin layout windows through the backend command", async () => {
invoke.mockResolvedValueOnce({
label: "view-plugin-layout",
url: "index.html?pluginLayout=1",
alreadyOpen: false,
providerPluginDisplayName: "Plugin",
layoutLabel: "Dashboard",
surface: {
pluginId: "dev.acme.plugin",
layoutType: "dev.acme.plugin.dashboard",
state: { tab: "overview" },
},
});
const gw = new TauriWindowGateway();
await expect(
gw.openPluginLayoutWindow({
pluginId: "dev.acme.plugin",
layoutType: "dev.acme.plugin.dashboard",
state: { tab: "overview" },
}),
).resolves.toMatchObject({
label: "view-plugin-layout",
surface: { layoutType: "dev.acme.plugin.dashboard" },
});
expect(invoke).toHaveBeenCalledWith("open_plugin_layout_window", {
input: {
pluginId: "dev.acme.plugin",
layoutType: "dev.acme.plugin.dashboard",
state: { tab: "overview" },
},
});
});
it("onViewWindowClosed listens on the lifecycle channel and forwards only closes", async () => {
// Capture the raw Tauri listener so we can drive lifecycle events at will.
let raw: ((e: { payload: unknown }) => void) | undefined;
const unlisten = vi.fn();
listen.mockImplementation((_name: string, cb: typeof raw) => {
raw = cb;
return Promise.resolve(unlisten);
});
const gw = new TauriWindowGateway();
const handler = vi.fn();
const off = await gw.onViewWindowClosed(handler);
expect(listen).toHaveBeenCalledWith(
"view-window://lifecycle",
expect.any(Function),
);
// opened / focused are dropped…
raw?.({
payload: { kind: "opened", panel: "git", label: "view-git" },
});
raw?.({
payload: { kind: "focused", panel: "git", label: "view-git" },
});
expect(handler).not.toHaveBeenCalled();
// …only closed reaches the port handler, narrowed to { panel } (#47).
raw?.({
payload: { kind: "closed", panel: "git", label: "view-git" },
});
expect(handler).toHaveBeenCalledTimes(1);
expect(handler).toHaveBeenCalledWith({ panel: "git" });
raw?.({
payload: { kind: "closed", panel: "context", label: "view-context" },
});
expect(handler).toHaveBeenLastCalledWith({ panel: "projectSettings" });
off();
expect(unlisten).toHaveBeenCalled();
});
});