- carnet.md: enregistre la livraison CORS et la décision Git du 2026-08-01 - issue.md + index.json: mise à jour version/timestamp - plugins.test.tsx: tests d'intégration frontend liés - Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -7,8 +7,8 @@ import { describe, it, expect } from "vitest";
|
||||
import { render, screen, waitFor, fireEvent, within } from "@testing-library/react";
|
||||
|
||||
import { MockPluginGateway, MockSystemGateway } from "@/adapters/mock";
|
||||
import type { PluginInstallResult, PluginRuntimeContributionCatalog } from "@/domain";
|
||||
import type { Gateways } from "@/ports";
|
||||
import type { PluginInstallResult, PluginReview, PluginRuntimeContributionCatalog } from "@/domain";
|
||||
import type { Gateways, ReviewPluginPackageInput } from "@/ports";
|
||||
import { DIProvider } from "@/app/di";
|
||||
import { PluginRuntimeRegistry } from "@/plugins/runtime";
|
||||
import { PluginsPanel } from "./PluginsPanel";
|
||||
@ -77,6 +77,25 @@ class FailingRuntimeCatalogPluginGateway extends MockPluginGateway {
|
||||
}
|
||||
}
|
||||
|
||||
class BackendShapedReviewPluginGateway extends MockPluginGateway {
|
||||
async reviewPackage(input: ReviewPluginPackageInput): Promise<PluginReview> {
|
||||
const label = input.path.split("/").pop() ?? input.path;
|
||||
return {
|
||||
id: "dev.acme.gitgraph",
|
||||
displayName: label.replace(/\.(ideaplug|zip|vsix)$/i, ""),
|
||||
publisher: "Acme",
|
||||
version: "1.2.3",
|
||||
description: "Graph",
|
||||
sourceKind: input.sourceKind,
|
||||
sourceLabel: input.path,
|
||||
contentHash: "abc",
|
||||
trustLevel: "full",
|
||||
contributionSummary: { topLevelMenus: 1, menuItems: 2, layouts: 3, mcpServers: 4 },
|
||||
contributes: { menus: [], menuItems: [], layouts: [], mcpServers: [] },
|
||||
} as unknown as PluginReview;
|
||||
}
|
||||
}
|
||||
|
||||
describe("PluginsPanel", () => {
|
||||
it("shows an empty state with no plugins installed", async () => {
|
||||
renderPanel();
|
||||
@ -122,6 +141,30 @@ describe("PluginsPanel", () => {
|
||||
expect(screen.getByText("Activé")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("keeps install, uninstall and reinstall stable with backend-shaped reviews", async () => {
|
||||
renderPanel(new BackendShapedReviewPluginGateway());
|
||||
await screen.findByText("Aucun plugin installé.");
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Installer depuis une archive…" }));
|
||||
let dialog = await screen.findByRole("dialog");
|
||||
expect(within(dialog).getByText(/full-trust/)).toBeTruthy();
|
||||
fireEvent.click(within(dialog).getByRole("button", { name: "Installer" }));
|
||||
expect(await screen.findByText("mock-plugin")).toBeTruthy();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Désinstaller" }));
|
||||
dialog = await screen.findByRole("dialog");
|
||||
fireEvent.click(within(dialog).getByRole("button", { name: "Désinstaller" }));
|
||||
expect(await screen.findByText("Aucun plugin installé.")).toBeTruthy();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Installer depuis une archive…" }));
|
||||
dialog = await screen.findByRole("dialog");
|
||||
expect(within(dialog).getByRole("button", { name: "Installer" })).toBeTruthy();
|
||||
fireEvent.click(within(dialog).getByRole("button", { name: "Installer" }));
|
||||
|
||||
expect(await screen.findByText("mock-plugin")).toBeTruthy();
|
||||
expect(screen.queryByText("INTERFACE INTERROMPUE")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders an invalid plugin returned after install as isolated, without replacing the panel", async () => {
|
||||
renderPanel(new InvalidInstallPluginGateway());
|
||||
await screen.findByText("Aucun plugin installé.");
|
||||
|
||||
Reference in New Issue
Block a user