);
render();
expect(screen.getByTestId("slot-a")).not.toBeNull();
expect(screen.getByTestId("slot-b")).not.toBeNull();
});
it("does not render a component registered for a different slot", () => {
pluginRegistry
.forPlugin("plugin-a")
.registerComponent("settings-nav", () =>
));
render();
expect(screen.getByTestId("slot-a").textContent).toBe("hello");
});
it("renders only the owning plugin's component when ownerPluginId is set", () => {
// "plugin-settings" is owner-scoped: the host isolates by owner, so a
// registered component never has to gate on the current plugin id itself.
const Card = ({ slotProps }: { slotProps?: unknown }) => (
card for {String((slotProps as { pluginId: string })?.pluginId)}
);
pluginRegistry.forPlugin("plugin-a").registerComponent(OWNER_SLOT, Card);
pluginRegistry.forPlugin("plugin-b").registerComponent(OWNER_SLOT, Card);
// Viewing plugin-b's page: only plugin-b's component renders, with its id.
render(
,
);
const cards = screen.getAllByTestId("settings-card");
expect(cards).toHaveLength(1);
expect(cards[0]?.textContent).toBe("card for plugin-b");
});
it("resets the error boundary when the owner changes so a healthy plugin's card is not hidden", () => {
// eslint-disable-next-line no-console -- expected error boundary log, assert + silence it
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
pluginRegistry.forPlugin("plugin-a").registerComponent(OWNER_SLOT, () => {
throw new Error("boom");
});
pluginRegistry
.forPlugin("plugin-b")
.registerComponent(OWNER_SLOT, () =>
B
);
// Same PluginSlot instance: plugin-a throws, then we "navigate" to plugin-b.
const { rerender } = render();
rerender();
// plugin-b is healthy and must render, not be hidden behind plugin-a's error.
expect(screen.getByTestId("slot-b")).not.toBeNull();
consoleError.mockRestore();
});
});
describe("PluginSlot registration identity", () => {
it("isolates a throwing slot component so a sibling still renders", () => {
// eslint-disable-next-line no-console -- expected error boundary log, assert + silence it
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
pluginRegistry.forPlugin("plugin-a").registerComponent(SLOT, () => {
throw new Error("boom");
});
pluginRegistry
.forPlugin("plugin-b")
.registerComponent(SLOT, () =>