Update UI to allow device users to reinstall self-service software (#20472)

This commit is contained in:
Sarah Gillespie 2024-07-15 13:56:10 -05:00 committed by GitHub
parent e4044c5d63
commit ac368bdcf4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 157 additions and 25 deletions

View file

@ -0,0 +1 @@
- Updated UI to allow device users to reinstall self-service software.

View file

@ -6,7 +6,23 @@ import mockServer from "test/mock-server";
import { customDeviceSoftwareHandler } from "test/handlers/device-handler";
import { createMockDeviceSoftware } from "__mocks__/deviceUserMock";
import SelfService from "./SelfService";
import SelfService, { ISoftwareSelfServiceProps } from "./SelfService";
const TEST_PROPS: ISoftwareSelfServiceProps = {
contactUrl: "http://example.com",
deviceToken: "123-456",
isSoftwareEnabled: true,
pathname: "/test",
queryParams: {
page: 1,
query: "",
order_key: "name",
order_direction: "asc",
per_page: 10,
vulnerable: true,
},
router: createMockRouter(),
};
describe("SelfService", () => {
it("should render the self service items correctly", async () => {
@ -23,23 +39,7 @@ describe("SelfService", () => {
const render = createCustomRenderer({ withBackendMock: true });
render(
<SelfService
contactUrl={"http://example.com"}
deviceToken={"123-456"}
isSoftwareEnabled
pathname={"/test"}
queryParams={{
page: 1,
query: "",
order_key: "name",
order_direction: "asc",
per_page: 10,
vulnerable: true,
}}
router={createMockRouter()}
/>
);
render(<SelfService {...TEST_PROPS} />);
// waiting for the device software data to render
await screen.findByText("test1");
@ -55,6 +55,31 @@ describe("SelfService", () => {
it("should render the contact link text if contact url is provided", () => {
mockServer.use(customDeviceSoftwareHandler());
const render = createCustomRenderer({ withBackendMock: true });
render(<SelfService {...TEST_PROPS} router={createMockRouter()} />);
expect(screen.getByText("reach out to IT")).toBeInTheDocument();
expect(screen.getByText("reach out to IT").getAttribute("href")).toBe(
"http://example.com"
);
});
it("renders 'Reinstall' action button with 'Installed' status", async () => {
mockServer.use(
customDeviceSoftwareHandler({
software: [
createMockDeviceSoftware({
name: "test-software",
status: "installed",
last_install: {
install_uuid: "test-uuid",
installed_at: "2021-08-18T15:11:35Z",
},
}),
],
})
);
const render = createCustomRenderer({ withBackendMock: true });
const expectedUrl = "http://example.com";
@ -77,9 +102,96 @@ describe("SelfService", () => {
/>
);
expect(screen.getByText("reach out to IT")).toBeInTheDocument();
expect(screen.getByText("reach out to IT").getAttribute("href")).toBe(
expectedUrl
// waiting for the device software data to render
await screen.findByText("test-software");
expect(
screen.getByTestId("self-service-item__status--test")
).toHaveTextContent("Installed");
expect(
screen.getByTestId("self-service-item__item-action-button--test")
).toHaveTextContent("Reinstall");
});
it("renders 'Retry' action button with 'Failed' status", async () => {
mockServer.use(
customDeviceSoftwareHandler({
software: [
createMockDeviceSoftware({
name: "test-software",
status: "failed",
}),
],
})
);
const render = createCustomRenderer({ withBackendMock: true });
render(<SelfService {...TEST_PROPS} />);
// waiting for the device software data to render
await screen.findByText("test-software");
expect(
screen.getByTestId("self-service-item__status--test")
).toHaveTextContent("Failed");
expect(
screen.getByTestId("self-service-item__item-action-button--test")
).toHaveTextContent("Retry");
});
it("renders 'Install' action button with no status", async () => {
mockServer.use(
customDeviceSoftwareHandler({
software: [
createMockDeviceSoftware({
name: "test-software",
status: null,
}),
],
})
);
const render = createCustomRenderer({ withBackendMock: true });
render(<SelfService {...TEST_PROPS} />);
// waiting for the device software data to render
await screen.findByText("test-software");
expect(
screen.queryByTestId("self-service-item__status--test")
).not.toBeInTheDocument();
expect(
screen.getByTestId("self-service-item__item-action-button--test")
).toHaveTextContent("Install");
});
it("renders no action button with 'Install in progress...' status", async () => {
mockServer.use(
customDeviceSoftwareHandler({
software: [
createMockDeviceSoftware({
name: "test-software",
status: "pending",
}),
],
})
);
const render = createCustomRenderer({ withBackendMock: true });
render(<SelfService {...TEST_PROPS} />);
// waiting for the device software data to render
await screen.findByText("test-software");
expect(
screen.getByTestId("self-service-item__status--test")
).toHaveTextContent("Install in progress...");
expect(
screen.queryByTestId("self-service-item__item-action-button--test")
).not.toBeInTheDocument();
});
});

View file

@ -32,7 +32,7 @@ const DEFAULT_SELF_SERVICE_QUERY_PARAMS = {
self_service: true,
} as const;
interface ISoftwareSelfServiceProps {
export interface ISoftwareSelfServiceProps {
contactUrl: string;
deviceToken: string;
isSoftwareEnabled?: boolean;

View file

@ -94,7 +94,9 @@ const InstallerStatus = ({
data-for={`install-tooltip__${id}`}
>
<Icon name={displayConfig.iconName} />
<span>{displayConfig.displayText}</span>
<span data-testid={`${baseClass}__status--test`}>
{displayConfig.displayText}
</span>
</div>
<ReactTooltip
className={`${baseClass}__status-tooltip`}
@ -119,6 +121,20 @@ interface IInstallerStatusActionProps {
onInstall: () => void;
}
const getInstallButtonText = (status: SoftwareInstallStatus | null) => {
switch (status) {
case null:
return "Install";
case "failed":
return "Retry";
case "installed":
return "Reinstall";
default:
// we don't show a button for pending installs
return "";
}
};
const InstallerStatusAction = ({
deviceToken,
software: { id, status, last_install },
@ -134,6 +150,7 @@ const InstallerStatusAction = ({
// displayStatus allows us to display the localStatus (if any) or the status from the list
// software reponse
const displayStatus = localStatus || status;
const installButtonText = getInstallButtonText(displayStatus);
// if the localStatus is "failed", we don't our tooltip to include the old installed_at date so we
// set this to null, which tells the tooltip to omit the parenthetical date
@ -172,7 +189,7 @@ const InstallerStatusAction = ({
/>
</div>
<div className={`${baseClass}__item-action`}>
{(displayStatus === "failed" || displayStatus === null) && (
{!!installButtonText && (
<Button
variant="text-icon"
type="button"
@ -181,7 +198,9 @@ const InstallerStatusAction = ({
}`}
onClick={onClick}
>
{displayStatus === "failed" ? "Retry" : "Install"}
<span data-testid={`${baseClass}__item-action-button--test`}>
{installButtonText}
</span>
</Button>
)}
</div>