From 5ee939c2c0d729d40f0acfee2170dd6c0199d82d Mon Sep 17 00:00:00 2001 From: Sarah Gillespie <73313222+gillespi314@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:31:43 -0600 Subject: [PATCH] Fix UI bug with host software install/uninstall actions (#24510) --- changes/24487-host-software-actions | 2 ++ .../HostSoftwareTableConfig.tests.tsx | 29 +++++++++++++++++-- .../Software/HostSoftwareTableConfig.tsx | 14 ++++++++- 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 changes/24487-host-software-actions diff --git a/changes/24487-host-software-actions b/changes/24487-host-software-actions new file mode 100644 index 0000000000..f98664612a --- /dev/null +++ b/changes/24487-host-software-actions @@ -0,0 +1,2 @@ +- Fixed UI bug where "Actions" dropdown on host software page included "Install" and "Uninstall" + options for software that is not able to be installed via Fleet. diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tests.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tests.tsx index eb7547205e..5968abdfaf 100644 --- a/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tests.tsx +++ b/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tests.tsx @@ -1,3 +1,5 @@ +import { createMockHostSoftwarePackage } from "__mocks__/hostMock"; + import { generateActions, DEFAULT_ACTION_OPTIONS, @@ -17,20 +19,38 @@ describe("generateActions", () => { hostMDMEnrolled: false, }; - it("returns default actions when user has write permission and scripts are enabled", () => { + const defaultPackage = createMockHostSoftwarePackage(); + + it("returns only view details when software does not have software package or app store app", () => { const actions = generateActions(defaultProps); + expect(actions).toEqual([DEFAULT_ACTION_OPTIONS[0]]); + }); + + it("returns default actions for software package when user has write permission and scripts are enabled", () => { + const actions = generateActions({ + ...defaultProps, + software_package: defaultPackage, + }); expect(actions).toEqual(DEFAULT_ACTION_OPTIONS); }); it("removes install and uninstall actions when user has no write permission", () => { - const props = { ...defaultProps, userHasSWWritePermission: false }; + const props = { + ...defaultProps, + software_package: defaultPackage, + userHasSWWritePermission: false, + }; const actions = generateActions(props); expect(actions.find((a) => a.value === "install")).toBeUndefined(); expect(actions.find((a) => a.value === "uninstall")).toBeUndefined(); }); it("disables install and uninstall actions when host scripts are disabled", () => { - const props = { ...defaultProps, hostScriptsEnabled: false }; + const props = { + ...defaultProps, + software_package: defaultPackage, + hostScriptsEnabled: false, + }; const actions = generateActions(props); expect(actions.find((a) => a.value === "install")?.disabled).toBe(true); expect(actions.find((a) => a.value === "uninstall")?.disabled).toBe(true); @@ -41,6 +61,7 @@ describe("generateActions", () => { ...defaultProps, softwareIdActionPending: 1, softwareId: 1, + software_package: defaultPackage, }; const actions = generateActions(props); expect(actions.find((a) => a.value === "install")?.disabled).toBe(true); @@ -50,6 +71,7 @@ describe("generateActions", () => { it("disables install and uninstall actions when pending install status", () => { const props: generateActionsProps = { ...defaultProps, + software_package: defaultPackage, status: "pending_install", }; const actions = generateActions(props); @@ -60,6 +82,7 @@ describe("generateActions", () => { it("disables install and uninstall actions when pending uninstall status", () => { const props: generateActionsProps = { ...defaultProps, + software_package: defaultPackage, status: "pending_uninstall", }; const actions = generateActions(props); diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tsx index 50d7a56767..ebe9d09272 100644 --- a/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tsx +++ b/frontend/pages/hosts/details/cards/Software/HostSoftwareTableConfig.tsx @@ -70,6 +70,7 @@ export const generateActions = ({ softwareIdActionPending, softwareId, status, + software_package, app_store_app, hostMDMEnrolled, }: generateActionsProps) => { @@ -77,6 +78,11 @@ export const generateActions = ({ // the options. const actions = cloneDeep(DEFAULT_ACTION_OPTIONS); + // we want to hide the install/uninstall actions if (1) this item doesn't have a + // software_package or app_store_app or (2) the user doens't have write permission + const hideActions = + (!app_store_app && !software_package) || !userHasSWWritePermission; + const indexInstallAction = actions.findIndex((a) => a.value === "install"); if (indexInstallAction === -1) { // this should never happen unless the default actions change, but if it does we'll throw an @@ -92,7 +98,13 @@ export const generateActions = ({ throw new Error("Uninstall action not found in default actions"); } - if (!userHasSWWritePermission) { + if (indexInstallAction > indexUninstallAction) { + // subsquent code depends on relative index order; this shouldn't change, but if it does we'll throw an + // error to fail loudly so that we know to update this function + throw new Error("Order of install/uninstall actions changed"); + } + + if (hideActions) { // Reverse order to not change index of subsequent array element before removal actions.splice(indexUninstallAction, 1); actions.splice(indexInstallAction, 1);