From e5f56fc9fac4c202cfe2070133515bf43a046e34 Mon Sep 17 00:00:00 2001 From: RachelElysia <71795832+RachelElysia@users.noreply.github.com> Date: Fri, 2 May 2025 10:41:06 -0400 Subject: [PATCH] Fleet UI: VPP apps with self service shows correct install status (#28739) --- changes/28379-vpp-app-install-status | 1 + .../InstallStatusCell.tests.tsx | 160 ++++++++++++++++++ .../InstallStatusCell/InstallStatusCell.tsx | 6 +- 3 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 changes/28379-vpp-app-install-status create mode 100644 frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tests.tsx diff --git a/changes/28379-vpp-app-install-status b/changes/28379-vpp-app-install-status new file mode 100644 index 0000000000..8ccad0f8a6 --- /dev/null +++ b/changes/28379-vpp-app-install-status @@ -0,0 +1 @@ +Fleet UI: Install Status correctly displays available for self-service for VPP apps diff --git a/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tests.tsx b/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tests.tsx new file mode 100644 index 0000000000..daa311abdb --- /dev/null +++ b/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tests.tsx @@ -0,0 +1,160 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { renderWithSetup } from "test/test-utils"; +import { + createMockHostAppStoreApp, + createMockHostSoftwarePackage, +} from "__mocks__/hostMock"; + +import InstallStatusCell from "./InstallStatusCell"; + +// Mock lodash uniqueId to always return the same id for stable tests +jest.mock("lodash", () => ({ + ...jest.requireActual("lodash"), + uniqueId: jest.fn(() => "test-tooltip-id"), +})); + +const testSoftwarePackage = createMockHostSoftwarePackage(); + +describe("InstallStatusCell - component", () => { + it("renders 'Installed' status with tooltip", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getByText("Installed")).toBeInTheDocument(); + + await user.hover(screen.getByText("Installed")); + expect(screen.getByText(/Software is installed/i)).toBeInTheDocument(); + }); + + it("renders 'Installing (pending)' status with tooltip", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getByText("Installing (pending)")).toBeInTheDocument(); + + await user.hover(screen.getByText("Installing (pending)")); + expect( + screen.getByText(/Fleet is installing or will install/i) + ).toBeInTheDocument(); + }); + + it("renders 'Uninstalling (pending)' status with tooltip", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getByText("Uninstalling (pending)")).toBeInTheDocument(); + + await user.hover(screen.getByText("Uninstalling (pending)")); + expect( + screen.getByText(/Fleet is uninstalling or will uninstall/i) + ).toBeInTheDocument(); + }); + + it("renders 'Install (failed)' status with tooltip", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getByText("Install (failed)")).toBeInTheDocument(); + + await user.hover(screen.getByText("Install (failed)")); + expect( + screen.getByText(/The host failed to install software/i) + ).toBeInTheDocument(); + }); + + it("renders 'Uninstall (failed)' status with tooltip", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getByText("Uninstall (failed)")).toBeInTheDocument(); + + await user.hover(screen.getByText("Uninstall (failed)")); + expect( + screen.getByText(/The host failed to uninstall software/i) + ).toBeInTheDocument(); + }); + + it("renders 'Available for install' for package", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getByText("Available for install")).toBeInTheDocument(); + + await user.hover(screen.getByText("Available for install")); + expect(screen.getByText(/can be installed/i)).toBeInTheDocument(); + }); + + it("renders 'Available for install' for App Store app", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getByText("Available for install")).toBeInTheDocument(); + + await user.hover(screen.getByText("Available for install")); + expect(screen.getByText(/can be installed/i)).toBeInTheDocument(); + }); + + it("renders 'Self-service' for package with self_service true", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getAllByText("Self-service").length).toBeGreaterThan(0); + + await user.hover(screen.getAllByText("Self-service")[0]); + expect(screen.getByText(/can be installed/i)).toBeInTheDocument(); + }); + + it("renders 'Self-service' for App Store app with self_service true", async () => { + const { user } = renderWithSetup( + + ); + + expect(screen.getAllByText("Self-service").length).toBeGreaterThan(0); + + await user.hover(screen.getAllByText("Self-service")[0]); + expect(screen.getByText(/Software can be installed/i)).toBeInTheDocument(); + }); + + it("renders placeholder for missing status and packages", () => { + render(); + + expect(screen.getByText("---")).toBeInTheDocument(); + }); +}); diff --git a/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx b/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx index f27df2fd38..b6c74df851 100644 --- a/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx +++ b/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx @@ -123,7 +123,8 @@ export const INSTALL_STATUS_DISPLAY_OPTIONS: Record< }, }; -type IInstallStatusCellProps = IHostSoftware; +type IInstallStatusCellProps = Pick & + Partial>; const InstallStatusCell = ({ status, @@ -138,8 +139,7 @@ const InstallStatusCell = ({ if (status !== null) { displayStatus = status; - } else if (software_package?.self_service) { - // currently only software packages can be self-service + } else if (software_package?.self_service || app_store_app?.self_service) { displayStatus = "selfService"; } else if (hasPackage || hasAppStoreApp) { displayStatus = "avaiableForInstall";