import * as React from "react"; import { fireEvent } from "@testing-library/react"; import { DiscoveryServices } from "../../../src/components/DiscoveryServices"; import renderWithContext from "../testUtils/renderWithContext"; import { ConfigurationSettings, DiscoveryServicesData, } from "../../../src/interfaces"; import { defaultFeatureFlags } from "../../../src/utils/featureFlags"; // NB: This adds tests to the already existing tests in: // - `src/components/__tests__/DiscoveryServices-test.tsx`. // // Those tests should eventually be migrated here and // adapted to the Jest/React Testing Library paradigm. describe("DiscoveryServices - registered library disclosure", () => { // ── Shared fixtures ─────────────────────────────────────────────────────── const allLibraries = [ { short_name: "gamma", name: "Gamma Library", uuid: "uuid-gamma" }, { short_name: "alpha", name: "Alpha Library", uuid: "uuid-alpha" }, { short_name: "beta", name: "Beta Library", uuid: "uuid-beta" }, { short_name: "delta", name: "Delta Library" }, // no uuid ]; const sysAdminConfig: Partial = { csrfToken: "", featureFlags: defaultFeatureFlags, roles: [{ role: "system" }], }; const renderServices = (data: Partial) => renderWithContext( , sysAdminConfig ); // ── Toggle visibility ───────────────────────────────────────────────────── it("shows no toggle when libraryRegistrations data has not yet loaded", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], // libraryRegistrations omitted → undefined }); expect(container.querySelector(".association-toggle")).toBeNull(); expect(container.querySelector(".library-count")).toBeNull(); }); it("shows a disabled toggle when libraryRegistrations is loaded but the service has no entry in it", () => { // libraryRegistrations is present (so getAssociatedEntries returns [] not // undefined), but service id=99 has no corresponding record in the array. // Result: disabled toggle + "no registered libraries", not "no toggle". const { container } = renderServices({ discovery_services: [{ id: 99, protocol: "p", name: "Service X" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, ], }); const toggle = container.querySelector( ".association-toggle" ); expect(toggle).not.toBeNull(); expect(toggle.disabled).toBe(true); expect(container.querySelector(".library-count").textContent).toBe( " (no registered libraries)" ); }); it("shows a disabled toggle and 'no registered libraries' when none are registered", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "warning", } as any, { short_name: "beta", status: "failure", } as any, ], }, ], }); const toggle = container.querySelector( ".association-toggle" ); expect(toggle).not.toBeNull(); expect(toggle.disabled).toBe(true); expect(container.querySelector(".library-count").textContent).toBe( " (no registered libraries)" ); }); it("shows '1 registered library' when exactly one library is registered", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, ], }); const toggle = container.querySelector( ".association-toggle" ); expect(toggle.disabled).toBe(false); expect(container.querySelector(".library-count").textContent).toBe( " (1 registered library)" ); }); it("shows 'N registered libraries' when multiple are registered", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, { short_name: "beta", status: "success", stage: "testing" } as any, { short_name: "gamma", status: "warning" } as any, ], }, ], }); expect(container.querySelector(".library-count").textContent).toBe( " (2 registered libraries)" ); }); // ── Content filtering ───────────────────────────────────────────────────── it("shows only registered (status=success) libraries in the expanded list", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, { short_name: "beta", status: "warning", stage: "testing" } as any, { short_name: "gamma", status: "failure" } as any, ], }, ], }); fireEvent.click(container.querySelector(".association-toggle")); const items = container.querySelectorAll(".associated-items li"); expect(items).toHaveLength(1); expect(items[0].textContent).toContain("Alpha Library"); }); // ── Stage suffix ────────────────────────────────────────────────────────── it("shows the registration stage in the suffix", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, ], }); fireEvent.click(container.querySelector(".association-toggle")); const item = container.querySelector(".associated-items li"); expect(item.textContent).toBe("Alpha Library - registered - production"); }); it("shows '- registered' without a stage when stage is absent", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [{ short_name: "alpha", status: "success" } as any], }, ], }); fireEvent.click(container.querySelector(".association-toggle")); const item = container.querySelector(".associated-items li"); expect(item.textContent).toBe("Alpha Library - registered"); }); // ── Sorting ─────────────────────────────────────────────────────────────── it("sorts registered libraries alphabetically by display name", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "gamma", status: "success", stage: "production", } as any, { short_name: "alpha", status: "success", stage: "testing" } as any, { short_name: "beta", status: "success", stage: "production", } as any, ], }, ], }); fireEvent.click(container.querySelector(".association-toggle")); const items = container.querySelectorAll(".associated-items li"); expect(items[0].textContent).toContain("Alpha Library"); expect(items[1].textContent).toContain("Beta Library"); expect(items[2].textContent).toContain("Gamma Library"); }); // ── Links ───────────────────────────────────────────────────────────────── it("links the library name to its config page when a uuid is available", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, ], }); fireEvent.click(container.querySelector(".association-toggle")); const link = container.querySelector( ".associated-items a" ); expect(link).not.toBeNull(); expect(link.textContent).toBe("Alpha Library"); expect(link.href).toContain("/admin/web/config/libraries/edit/uuid-alpha"); // The suffix should not be inside the link. expect(link.nextSibling.textContent).toBe(" - registered - production"); }); it("renders the library name as plain text when no uuid is available", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "delta", status: "success", stage: "testing" } as any, ], }, ], }); fireEvent.click(container.querySelector(".association-toggle")); expect(container.querySelector(".associated-items a")).toBeNull(); expect(container.querySelector(".associated-items li").textContent).toBe( "Delta Library - registered - testing" ); }); // ── Per-service isolation ───────────────────────────────────────────────── it("shows each service's own registered libraries independently", () => { const { container } = renderServices({ discovery_services: [ { id: 1, protocol: "p", name: "Service A" } as any, { id: 2, protocol: "p", name: "Service B" } as any, ], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, { id: 2, libraries: [ { short_name: "beta", status: "success", stage: "testing" } as any, { short_name: "gamma", status: "success", stage: "production", } as any, ], }, ], }); expect(container.querySelector(".library-count").textContent).toBe( " (1 registered library)" ); const toggles = container.querySelectorAll( ".association-toggle" ); expect(toggles).toHaveLength(2); expect( toggles[1].closest("li").querySelector(".library-count").textContent ).toBe(" (2 registered libraries)"); }); // ── Expand all / Collapse all buttons ──────────────────────────────────── it("Expand all expands all services that have registered libraries", () => { const { container } = renderServices({ discovery_services: [ { id: 1, protocol: "p", name: "Service A" } as any, { id: 2, protocol: "p", name: "Service B" } as any, { id: 3, protocol: "p", name: "Service C" } as any, ], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, { id: 2, libraries: [], // no registrations → disabled toggle }, { id: 3, libraries: [ { short_name: "beta", status: "success", stage: "testing" } as any, ], }, ], }); fireEvent.click(container.querySelector(".expand-all")); // Services 1 and 3 have registered libraries; service 2 has none. expect(container.querySelectorAll(".associated-items")).toHaveLength(2); }); it("shows no expand/collapse controls when libraryRegistrations has not yet loaded", () => { const { container } = renderServices({ discovery_services: [{ id: 1, protocol: "p", name: "Service A" } as any], // libraryRegistrations omitted → getAssociatedEntries returns undefined // for every item, so expandableItems() is empty and no controls render. }); expect(container.querySelector(".expand-collapse-controls")).toBeNull(); }); it("shows no expand/collapse controls when no services have successfully registered libraries", () => { const { container } = renderServices({ discovery_services: [ { id: 1, protocol: "p", name: "Service A" } as any, { id: 2, protocol: "p", name: "Service B" } as any, ], libraryRegistrations: [ { id: 1, libraries: [{ short_name: "alpha", status: "warning" } as any], }, { id: 2, libraries: [], }, ], }); expect(container.querySelector(".expand-collapse-controls")).toBeNull(); }); it("Collapse all collapses all expanded services", () => { const { container } = renderServices({ discovery_services: [ { id: 1, protocol: "p", name: "Service A" } as any, { id: 2, protocol: "p", name: "Service B" } as any, ], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, { id: 2, libraries: [ { short_name: "beta", status: "success", stage: "testing" } as any, ], }, ], }); fireEvent.click(container.querySelector(".expand-all")); expect(container.querySelectorAll(".associated-items")).toHaveLength(2); fireEvent.click(container.querySelector(".collapse-all")); expect(container.querySelectorAll(".associated-items")).toHaveLength(0); expect( container.querySelector(".collapse-all").disabled ).toBe(true); }); // ── Alt+click toggle-all ────────────────────────────────────────────────── it("alt+click expands all services that have registered libraries", () => { const { container } = renderServices({ discovery_services: [ { id: 1, protocol: "p", name: "Service A" } as any, { id: 2, protocol: "p", name: "Service B" } as any, { id: 3, protocol: "p", name: "Service C" } as any, ], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, { id: 2, libraries: [], // no registrations → disabled toggle }, { id: 3, libraries: [ { short_name: "beta", status: "success", stage: "testing" } as any, ], }, ], }); const toggles = container.querySelectorAll(".association-toggle"); fireEvent.click(toggles[0], { altKey: true }); // Services 1 and 3 have registered libraries; service 2 has none. expect(container.querySelectorAll(".associated-items")).toHaveLength(2); }); it("alt+click collapses all when all services are already expanded", () => { const { container } = renderServices({ discovery_services: [ { id: 1, protocol: "p", name: "Service A" } as any, { id: 2, protocol: "p", name: "Service B" } as any, ], libraryRegistrations: [ { id: 1, libraries: [ { short_name: "alpha", status: "success", stage: "production", } as any, ], }, { id: 2, libraries: [ { short_name: "beta", status: "success", stage: "testing" } as any, ], }, ], }); const toggles = container.querySelectorAll(".association-toggle"); // Expand all first. fireEvent.click(toggles[0], { altKey: true }); expect(container.querySelectorAll(".associated-items")).toHaveLength(2); // Alt+click again should collapse all. fireEvent.click(toggles[0], { altKey: true }); expect(container.querySelectorAll(".associated-items")).toHaveLength(0); }); });