mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
344 lines
9.5 KiB
TypeScript
344 lines
9.5 KiB
TypeScript
import React from "react";
|
|
|
|
import { screen, waitFor } from "@testing-library/react";
|
|
import { createCustomRenderer, createMockRouter } from "test/test-utils";
|
|
import { AxiosError } from "axios";
|
|
|
|
import hostAPI from "services/entities/hosts";
|
|
import paths from "router/paths";
|
|
import MDMStatusModal from "./MDMStatusModal";
|
|
|
|
jest.mock("services/entities/hosts");
|
|
|
|
const mockRouter = createMockRouter();
|
|
|
|
const mockDepAssignmentResponse = {
|
|
id: 32,
|
|
dep_device: {
|
|
asset_tag: "",
|
|
color: "MIDNIGHT",
|
|
description: "IPHONE 13 MIDNIGHT 128GB-USA",
|
|
device_assigned_by: "fleetie@example.com",
|
|
device_assigned_date: "2026-01-29T21:17:25Z",
|
|
device_family: "iPhone",
|
|
os: "iOS",
|
|
profile_status: "assigned",
|
|
profile_assign_time: "2026-01-29T21:17:25Z",
|
|
profile_push_time: "2026-01-03T00:00:00Z",
|
|
profile_uuid: "762C4D36550103CCC53AA212A8D31CDD",
|
|
mdm_migration_deadline: null,
|
|
serial_number: "ABC1FND0ZX",
|
|
},
|
|
host_dep_assignment: {
|
|
assign_profile_response: "SUCCESS",
|
|
profile_uuid: "762C4D36550103CCC53AA212A8D31CDD",
|
|
response_updated_at: "2025-12-04 01:35:27",
|
|
added_at: "2025-12-04 01:35:27",
|
|
deleted_at: null,
|
|
abm_token_id: 1,
|
|
mdm_migration_deadline: "2025-12-05 00:00:00.000000",
|
|
mdm_migration_completed: "2025-12-05 00:00:00.000000",
|
|
},
|
|
};
|
|
|
|
describe("MDMStatusModal - component", () => {
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {},
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.resetAllMocks();
|
|
});
|
|
|
|
it("renders MDM status row with enrollment status text", () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue(
|
|
mockDepAssignmentResponse
|
|
);
|
|
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText(/On \(manual\)/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("does not render profile assignment section when not premium or not macOS", () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue(
|
|
mockDepAssignmentResponse
|
|
);
|
|
|
|
// not premium
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier={false}
|
|
isAppleDevice
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
expect(screen.queryByText("Profile assignment")).not.toBeInTheDocument();
|
|
|
|
// not macOS
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier
|
|
isAppleDevice={false}
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
expect(screen.queryByText("Profile assignment")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("renders profile assignment section when premium apple device host", async () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue(
|
|
mockDepAssignmentResponse
|
|
);
|
|
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier
|
|
isAppleDevice
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(await screen.findByText("Profile assignment")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText(/Details about automatic enrollment profile from Apple/i)
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Profile assigned")).toBeInTheDocument();
|
|
expect(screen.getByText("Profile pushed")).toBeInTheDocument();
|
|
expect(screen.getByText("Profile status")).toBeInTheDocument();
|
|
// profile_status "assigned" renders "Assigned"
|
|
expect(screen.getByText("Assigned")).toBeInTheDocument();
|
|
});
|
|
|
|
it("does not render profile assignment section when non-Apple host", async () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue(
|
|
mockDepAssignmentResponse
|
|
);
|
|
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(
|
|
await screen.queryByText("Profile assignment")
|
|
).not.toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText(
|
|
/Details about automatic enrollment profile from Apple/i
|
|
)
|
|
).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Profile assigned")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Profile pushed")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Profile status")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Assigned")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows spinner while DEP assignment is loading", () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockReturnValue(
|
|
new Promise(() => {
|
|
// never resolve
|
|
})
|
|
);
|
|
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier
|
|
isAppleDevice
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId("spinner")).toBeVisible();
|
|
});
|
|
|
|
it("shows DataError if DEP assignment fails", async () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockRejectedValue(
|
|
new AxiosError("network error")
|
|
);
|
|
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier
|
|
isAppleDevice
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
expect(
|
|
await screen.findByText(
|
|
"We can't retrieve data from Apple right now. Please try again later."
|
|
)
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("adds profile assignment error row when depProfileError is true and API returns THROTTLED", async () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue({
|
|
...mockDepAssignmentResponse,
|
|
host_dep_assignment: {
|
|
...mockDepAssignmentResponse.host_dep_assignment,
|
|
assign_profile_response: "THROTTLED",
|
|
},
|
|
});
|
|
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier
|
|
isAppleDevice
|
|
depProfileError
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
// Ensure the async list has loaded
|
|
await screen.findByText("Profile assigned");
|
|
|
|
expect(
|
|
await screen.findByText("Profile assignment error")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Throttled")).toBeInTheDocument();
|
|
});
|
|
|
|
it("navigates to hosts with dep_assign_profile_response filter when profile error row is clicked", async () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue({
|
|
...mockDepAssignmentResponse,
|
|
host_dep_assignment: {
|
|
...mockDepAssignmentResponse.host_dep_assignment,
|
|
assign_profile_response: "FAILED",
|
|
},
|
|
});
|
|
const router = createMockRouter();
|
|
|
|
const { user } = render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={router}
|
|
isPremiumTier
|
|
isAppleDevice
|
|
depProfileError
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
// Wait for list to be hydrated
|
|
await screen.findByText("Profile assigned");
|
|
|
|
const profileErrorRow = await screen.findByText("Profile assignment error");
|
|
await user.click(profileErrorRow);
|
|
|
|
await waitFor(() => {
|
|
expect(router.push).toHaveBeenCalled();
|
|
const firstCall = (router.push as jest.Mock).mock.calls[0][0];
|
|
expect(firstCall).toContain(paths.MANAGE_HOSTS);
|
|
// Router navigation still uses uppercase responseParam
|
|
expect(firstCall).toContain("dep_assign_profile_response=FAILED");
|
|
});
|
|
});
|
|
|
|
it("navigates to filtered hosts when MDM status row is clicked", async () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue(
|
|
mockDepAssignmentResponse
|
|
);
|
|
const router = createMockRouter();
|
|
|
|
const { user } = render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={router}
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
const mdmStatus = screen.getByText(/On \(manual\)/i);
|
|
await user.click(mdmStatus);
|
|
|
|
await waitFor(() => {
|
|
expect(router.push).toHaveBeenCalled();
|
|
const firstCall = (router.push as jest.Mock).mock.calls[0][0];
|
|
expect(firstCall).toContain(paths.MANAGE_HOSTS);
|
|
expect(firstCall).toContain("mdm_enrollment_status=");
|
|
});
|
|
});
|
|
|
|
it("renders 'Never' for zero-value profile timestamps", async () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue({
|
|
...mockDepAssignmentResponse,
|
|
dep_device: {
|
|
...mockDepAssignmentResponse.dep_device,
|
|
profile_assign_time: "0001-01-01T00:00:00Z",
|
|
profile_push_time: "0001-01-01T00:00:00Z",
|
|
},
|
|
});
|
|
|
|
render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier
|
|
isAppleDevice
|
|
onExit={jest.fn()}
|
|
/>
|
|
);
|
|
|
|
await screen.findByText("Profile assigned");
|
|
|
|
const neverTexts = screen.getAllByText("Never");
|
|
// Both profile_assign_time and profile_push_time should show "Never"
|
|
expect(neverTexts.length).toBeGreaterThanOrEqual(2);
|
|
});
|
|
|
|
it("calls onExit when Close is clicked", async () => {
|
|
(hostAPI.getDepAssignment as jest.Mock).mockResolvedValue(
|
|
mockDepAssignmentResponse
|
|
);
|
|
const onExit = jest.fn();
|
|
|
|
const { user } = render(
|
|
<MDMStatusModal
|
|
hostId={3}
|
|
enrollmentStatus="On (manual)"
|
|
router={mockRouter}
|
|
isPremiumTier
|
|
isAppleDevice
|
|
onExit={onExit}
|
|
/>
|
|
);
|
|
|
|
await user.click(screen.getByRole("button", { name: "Close" }));
|
|
expect(onExit).toHaveBeenCalled();
|
|
});
|
|
});
|