mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
## Summary - Changed all modal "Done" dismiss/close button labels to "Close" across 48 frontend component files - Updated instructional text in `AutoEnrollMdmModal` that referenced the "Done" button to say "Close" instead - Updated 7 test files to assert "Close" instead of "Done" for modal button names ## Excluded (intentionally not changed) - `LiveResultsHeading.tsx` — "Done" button is a page-level navigation action, not a modal dismiss - `AddAbmModal.tsx` — Instructional text referencing Apple Business Manager's "Done" button - `Calendars.tsx` — Instructional text referencing Google Calendar's "Done" button - `ModalFooter.stories.tsx` — Storybook demo example Built for [Mel](https://fleetdm.slack.com/archives/D0AKX7DJFCN/p1773674157011109?thread_ts=1773673149.649299&cid=D0AKX7DJFCN) by [Kilo for Slack](https://kilo.ai/features/slack-integration) --------- Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> Co-authored-by: melpike <mel@fleetdm.com> Co-authored-by: melpike <79950145+melpike@users.noreply.github.com>
386 lines
13 KiB
TypeScript
386 lines
13 KiB
TypeScript
import React from "react";
|
|
import { render, screen } from "@testing-library/react";
|
|
import { renderWithSetup, createCustomRenderer } from "test/test-utils";
|
|
import { createMockHostSoftware } from "__mocks__/hostMock";
|
|
import { createMockSoftwareInstallResult } from "__mocks__/softwareMock";
|
|
import {
|
|
getDefaultSoftwareInstallHandler,
|
|
getSoftwareInstallHandlerNoOutputs,
|
|
getSoftwareInstallHandlerOnlyInstallOutput,
|
|
getSoftwareInstallHandlerWithPreInstall,
|
|
getSoftwareInstallHandlerOnlyPreInstallOutput,
|
|
} from "test/handlers/software-handlers";
|
|
import mockServer from "test/mock-server";
|
|
import { noop } from "lodash";
|
|
|
|
import SoftwareInstallDetailsModal, {
|
|
StatusMessage,
|
|
ModalButtons,
|
|
} from "./SoftwareInstallDetailsModal";
|
|
|
|
describe("SoftwareInstallDetailsModal", () => {
|
|
describe("StatusMessage component", () => {
|
|
it("renders basic 'is installed' message when not installed by fleet (no installResult provided)", () => {
|
|
render(<StatusMessage softwareName="CoolApp" isMyDevicePage={false} />);
|
|
expect(screen.getByText(/CoolApp/)).toBeInTheDocument();
|
|
expect(screen.getByText(/is installed/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("on software library page/pending activity, renders pending install message with host and package name", () => {
|
|
render(
|
|
<StatusMessage
|
|
softwareName="CoolApp"
|
|
installResult={createMockSoftwareInstallResult({
|
|
status: "pending_install",
|
|
})}
|
|
isMyDevicePage={false}
|
|
/>
|
|
);
|
|
|
|
expect(screen.queryByTestId("pending-outline-icon")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText(/is installing or will install/)
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText(/\(com\.cool\.app\)/)).toBeInTheDocument();
|
|
expect(screen.getByText(/Test Host/)).toBeInTheDocument();
|
|
expect(screen.getByText(/when it comes online/)).toBeInTheDocument();
|
|
expect(screen.queryByText(/\d+.*ago/)).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("on device user page, renders failed install with retry option with contact link", () => {
|
|
render(
|
|
<StatusMessage
|
|
softwareName="CoolApp"
|
|
installResult={createMockSoftwareInstallResult({
|
|
status: "failed_install",
|
|
})}
|
|
isMyDevicePage
|
|
contactUrl="http://support"
|
|
/>
|
|
);
|
|
|
|
expect(screen.queryByTestId("error-icon")).toBeInTheDocument();
|
|
expect(screen.getByText(/failed to install/)).toBeInTheDocument();
|
|
expect(screen.getByText(/CoolApp/)).toBeInTheDocument();
|
|
// Host name should not be rendered for device user page
|
|
expect(screen.queryByText(/Test Host/)).not.toBeInTheDocument();
|
|
expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument();
|
|
expect(screen.getByText(/You can retry/)).toBeInTheDocument();
|
|
expect(
|
|
screen.getByRole("link", { name: /contact your IT admin/ })
|
|
).toHaveAttribute("href", "http://support");
|
|
});
|
|
|
|
it("on device user page, renders failed install with retry option without contact link", () => {
|
|
render(
|
|
<StatusMessage
|
|
softwareName="CoolApp"
|
|
installResult={createMockSoftwareInstallResult({
|
|
status: "failed_install",
|
|
})}
|
|
isMyDevicePage
|
|
/>
|
|
);
|
|
|
|
expect(screen.queryByTestId("error-icon")).toBeInTheDocument();
|
|
expect(screen.getByText(/failed to install/)).toBeInTheDocument();
|
|
expect(screen.getByText(/CoolApp/)).toBeInTheDocument();
|
|
// Host name should not be rendered for device user page
|
|
expect(screen.queryByText(/Test Host/)).not.toBeInTheDocument();
|
|
expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument();
|
|
expect(screen.getByText(/You can retry/)).toBeInTheDocument();
|
|
// Don't show link of not provided
|
|
expect(
|
|
screen.queryByRole("link", { name: /contact your IT admin/ })
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("treats failed_install as installed when host still reports installed versions", () => {
|
|
render(
|
|
<StatusMessage
|
|
softwareName="CoolApp"
|
|
installResult={createMockSoftwareInstallResult({
|
|
status: "failed_install",
|
|
})}
|
|
isMyDevicePage={false}
|
|
canOverrideFailureWithInstalled
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText(/CoolApp/)).toBeInTheDocument();
|
|
expect(screen.getByText(/is installed\./i)).toBeInTheDocument();
|
|
expect(screen.getByTestId("success-icon")).toBeInTheDocument();
|
|
expect(screen.queryByText(/failed to install/i)).not.toBeInTheDocument();
|
|
expect(screen.queryByTestId("failed-icon")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("on host details page, renders failed install without retry", () => {
|
|
render(
|
|
<StatusMessage
|
|
softwareName="CoolApp"
|
|
installResult={createMockSoftwareInstallResult({
|
|
status: "failed_install",
|
|
})}
|
|
isMyDevicePage={false}
|
|
contactUrl="http://support"
|
|
/>
|
|
);
|
|
|
|
expect(screen.queryByTestId("error-icon")).toBeInTheDocument();
|
|
expect(screen.getByText(/failed to install/)).toBeInTheDocument();
|
|
expect(screen.getByText(/Test Host/)).toBeInTheDocument();
|
|
expect(screen.queryByText(/You can retry/)).not.toBeInTheDocument();
|
|
expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("on host details page/install activity, renders installed message with timestamp", () => {
|
|
render(
|
|
<StatusMessage
|
|
softwareName="CoolApp"
|
|
installResult={createMockSoftwareInstallResult({
|
|
status: "installed",
|
|
})}
|
|
isMyDevicePage={false}
|
|
/>
|
|
);
|
|
|
|
expect(screen.queryByTestId("success-icon")).toBeInTheDocument();
|
|
expect(screen.getByText(/Fleet installed/)).toBeInTheDocument();
|
|
expect(screen.getByText(/CoolApp/)).toBeInTheDocument();
|
|
expect(screen.getByText(/Test Host/)).toBeInTheDocument();
|
|
expect(screen.getByText(/\(com\.cool\.app\)/)).toBeInTheDocument();
|
|
expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("ModalButtons component", () => {
|
|
it("on device user page, shows Retry/Cancel for failed install and triggers handlers", async () => {
|
|
const onCancel = jest.fn();
|
|
const onRetry = jest.fn();
|
|
|
|
const { user } = renderWithSetup(
|
|
<ModalButtons
|
|
deviceAuthToken="token123"
|
|
status="failed_install"
|
|
hostSoftwareId={99}
|
|
onCancel={onCancel}
|
|
onRetry={onRetry}
|
|
/>
|
|
);
|
|
expect(screen.getByRole("button", { name: "Retry" })).toBeInTheDocument();
|
|
expect(
|
|
screen.getByRole("button", { name: "Cancel" })
|
|
).toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole("button", { name: "Retry" }));
|
|
expect(onRetry).toHaveBeenCalledWith(99);
|
|
expect(onCancel).toHaveBeenCalled();
|
|
|
|
await user.click(screen.getByRole("button", { name: "Cancel" }));
|
|
expect(onCancel).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("shows Close button for pending install", () => {
|
|
const onCancel = jest.fn();
|
|
render(<ModalButtons status="pending_install" onCancel={onCancel} />);
|
|
expect(screen.getByRole("button", { name: "Close" })).toBeInTheDocument();
|
|
expect(
|
|
screen.queryByRole("button", { name: "Retry" })
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("on device user page, shows Close button for installed software", () => {
|
|
const onCancel = jest.fn();
|
|
render(
|
|
<ModalButtons
|
|
deviceAuthToken="token123"
|
|
status="installed"
|
|
onCancel={onCancel}
|
|
/>
|
|
);
|
|
expect(screen.getByRole("button", { name: "Close" })).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
const baseDetails = {
|
|
install_uuid: "uuid-123",
|
|
host_display_name: "Test Host",
|
|
};
|
|
|
|
const baseHostSoftware = createMockHostSoftware({
|
|
id: 99,
|
|
name: "CoolApp",
|
|
installed_versions: [],
|
|
});
|
|
|
|
describe("Install Details Section", () => {
|
|
afterEach(() => {
|
|
mockServer.resetHandlers();
|
|
});
|
|
|
|
it("does not show install details outputs until Details is clicked", async () => {
|
|
mockServer.use(getDefaultSoftwareInstallHandler);
|
|
const renderWithServer = createCustomRenderer({ withBackendMock: true });
|
|
|
|
renderWithServer(
|
|
<SoftwareInstallDetailsModal
|
|
details={baseDetails}
|
|
hostSoftware={baseHostSoftware}
|
|
onCancel={noop}
|
|
/>
|
|
);
|
|
|
|
// waiting for the button to render
|
|
const detailsButton = await screen.findByRole("button", {
|
|
name: /Details/i,
|
|
});
|
|
|
|
expect(detailsButton).toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText("Pre-install query output:")
|
|
).not.toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText("Install script output:")
|
|
).not.toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText(/Post-install script output:/i)
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows pre-install, install, and post-install outputs after clicking Details", async () => {
|
|
mockServer.use(getSoftwareInstallHandlerWithPreInstall);
|
|
const renderWithServer = createCustomRenderer({ withBackendMock: true });
|
|
const { user } = renderWithServer(
|
|
<SoftwareInstallDetailsModal
|
|
details={baseDetails}
|
|
hostSoftware={baseHostSoftware}
|
|
onCancel={noop}
|
|
/>
|
|
);
|
|
|
|
const detailsBtn = await screen.findByRole("button", {
|
|
name: /Details/i,
|
|
});
|
|
await user.click(detailsBtn);
|
|
|
|
// Pre-install output
|
|
expect(
|
|
await screen.getByText("Pre-install query output:")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Pre-install check passed")).toBeInTheDocument();
|
|
|
|
// Install output
|
|
expect(screen.getByText("Install script output:")).toBeInTheDocument();
|
|
expect(screen.getByText("Install script ran")).toBeInTheDocument();
|
|
|
|
// Post-install output
|
|
expect(
|
|
screen.getByText("Post-install script output:")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Post-install success")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders only pre-install output if that's the only script output present", async () => {
|
|
mockServer.use(getSoftwareInstallHandlerOnlyPreInstallOutput);
|
|
const renderWithServer = createCustomRenderer({ withBackendMock: true });
|
|
const { user } = renderWithServer(
|
|
<SoftwareInstallDetailsModal
|
|
details={baseDetails}
|
|
hostSoftware={baseHostSoftware}
|
|
onCancel={noop}
|
|
/>
|
|
);
|
|
|
|
const detailsBtn = await screen.findByRole("button", {
|
|
name: /Details/i,
|
|
});
|
|
await user.click(detailsBtn);
|
|
|
|
expect(
|
|
await screen.getByText("Pre-install query output:")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText(/pre-install only/i)).toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText("Install script output:")
|
|
).not.toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText(/Post-install script output:/i)
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows install and post-install outputs after clicking Details (no pre-install)", async () => {
|
|
mockServer.use(getDefaultSoftwareInstallHandler);
|
|
const renderWithServer = createCustomRenderer({ withBackendMock: true });
|
|
const { user } = renderWithServer(
|
|
<SoftwareInstallDetailsModal
|
|
details={baseDetails}
|
|
hostSoftware={baseHostSoftware}
|
|
onCancel={noop}
|
|
/>
|
|
);
|
|
|
|
const detailsBtn = await screen.findByRole("button", {
|
|
name: /Details/i,
|
|
});
|
|
await user.click(detailsBtn);
|
|
|
|
expect(
|
|
await screen.getByText("Install script output:")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Install script ran")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText(/Post-install script output:/i)
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Post-install success")).toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText("Pre-install query output:")
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows only the install output if post-install and pre-install output is empty", async () => {
|
|
mockServer.use(getSoftwareInstallHandlerOnlyInstallOutput);
|
|
const renderWithServer = createCustomRenderer({ withBackendMock: true });
|
|
const { user } = renderWithServer(
|
|
<SoftwareInstallDetailsModal
|
|
details={baseDetails}
|
|
hostSoftware={baseHostSoftware}
|
|
onCancel={noop}
|
|
/>
|
|
);
|
|
|
|
const detailsBtn = await screen.findByRole("button", {
|
|
name: /Details/i,
|
|
});
|
|
await user.click(detailsBtn);
|
|
|
|
expect(
|
|
await screen.getByText("Install script output:")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText(/install only/i)).toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText("Pre-install query output:")
|
|
).not.toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText(/Post-install script output:/i)
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("does not render details button if all script outputs are empty", async () => {
|
|
mockServer.use(getSoftwareInstallHandlerNoOutputs);
|
|
const renderWithServer = createCustomRenderer({ withBackendMock: true });
|
|
renderWithServer(
|
|
<SoftwareInstallDetailsModal
|
|
details={baseDetails}
|
|
hostSoftware={baseHostSoftware}
|
|
onCancel={noop}
|
|
/>
|
|
);
|
|
|
|
expect(
|
|
screen.queryByRole("button", {
|
|
name: /Details/i,
|
|
})
|
|
).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|