Add tests for new "auto update" front-end (#37877)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** For #35459 # Details This PR adds front-end tests for: * `<SoftwareSummaryCard>` * Smoke-test of basic functionality (showing the software title and type, showing an icon) * Action dropdown options for various kinds of software * `<EditAutoUpdateConfigModal>` * Options for auto-updates (enable button, maintenance window validation) * Targets ("All hosts" and "Custom" values and validation) * Form submission (ensuring the API receives the expected payload for various permutations of the form) The `<TargetLabelSelector>` component has its own tests so we don't go through it thoroughly here, just integration tests with the new component. Conversely the `<SoftwareDetailsSummary>` component _doesn't_ have its own tests, and could use some, but in this instance we're just concerned with how it integrates with the software summary card (that is, how the passed-in software title affects the Actions dropdown). ## Testing - [X] Added/updated automated tests --------- Co-authored-by: Gabriel Hernandez <ghernandez345@gmail.com> Co-authored-by: Nico <32375741+nulmete@users.noreply.github.com>
This commit is contained in:
co-authored by
Gabriel Hernandez
Nico
parent
df36372a44
commit
0603346065
@@ -42,7 +42,7 @@ const CustomOption = (props: CustomOptionProps) => {
|
||||
const { data, ...rest } = props;
|
||||
|
||||
const optionContent = (
|
||||
<div className={`${baseClass}__option`}>
|
||||
<div className={`${baseClass}__option`} data-testid="dropdown-option">
|
||||
{data.label}
|
||||
{data.helpText && (
|
||||
<span className={`${baseClass}__help-text`}>{data.helpText}</span>
|
||||
|
||||
+586
@@ -0,0 +1,586 @@
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
createMockSoftwareTitleDetails,
|
||||
createMockAppStoreApp,
|
||||
} from "__mocks__/softwareMock";
|
||||
|
||||
import { act, screen, waitFor } from "@testing-library/react";
|
||||
import { http, HttpResponse } from "msw";
|
||||
import mockServer from "test/mock-server";
|
||||
import { createCustomRenderer } from "test/test-utils";
|
||||
import { ILabelSummary } from "interfaces/label";
|
||||
|
||||
import createMockUser from "__mocks__/userMock";
|
||||
|
||||
import EditAutoUpdateConfigModal, {
|
||||
ISoftwareAutoUpdateConfigFormData,
|
||||
} from "./EditAutoUpdateConfigModal";
|
||||
|
||||
const baseUrl = (path: string) => {
|
||||
return `/api/latest/fleet${path}`;
|
||||
};
|
||||
|
||||
const mockLabels: ILabelSummary[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Fun",
|
||||
description: "Computers that like to have a good time",
|
||||
label_type: "regular",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Fresh",
|
||||
description: "Laptops with dirty mouths",
|
||||
label_type: "regular",
|
||||
},
|
||||
];
|
||||
|
||||
const labelSummariesHandler = http.get(baseUrl("/labels/summary"), () => {
|
||||
return HttpResponse.json({
|
||||
labels: mockLabels,
|
||||
});
|
||||
});
|
||||
|
||||
describe("Edit Auto Update Config Modal", () => {
|
||||
beforeEach(() => {
|
||||
mockServer.use(labelSummariesHandler);
|
||||
});
|
||||
|
||||
const render = createCustomRenderer({
|
||||
withBackendMock: true,
|
||||
context: {
|
||||
app: {
|
||||
currentUser: createMockUser(),
|
||||
isGlobalObserver: false,
|
||||
isGlobalAdmin: true,
|
||||
isGlobalMaintainer: false,
|
||||
isOnGlobalTeam: true,
|
||||
isPremiumTier: true,
|
||||
isSandboxMode: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
describe("Auto updates options", () => {
|
||||
it("Does not show maintenance window options when 'Enable auto updates' is not configured", async () => {
|
||||
render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
// Verify that "Enable auto updates" checkbox is not checked.
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
expect(enableAutoUpdatesCheckbox).not.toBeChecked();
|
||||
// Verify that the maintenance window fields are not shown.
|
||||
expect(
|
||||
screen.queryByLabelText("Earliest start time")
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByLabelText("Latest start time")
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("Shows maintenance window options when 'Enable auto updates' is configured", async () => {
|
||||
render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails({
|
||||
auto_update_enabled: true,
|
||||
auto_update_start_time: "02:00",
|
||||
auto_update_end_time: "04:00",
|
||||
})}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
// Verify that "Enable auto updates" checkbox is checked.
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
expect(enableAutoUpdatesCheckbox).toBeChecked();
|
||||
// Verify that the maintenance window fields are shown correctly.
|
||||
const startTimeField = screen.getByLabelText("Earliest start time");
|
||||
const endTimeField = screen.getByLabelText("Latest start time");
|
||||
expect(startTimeField).toBeInTheDocument();
|
||||
expect(startTimeField).toHaveValue("02:00");
|
||||
expect(endTimeField).toBeInTheDocument();
|
||||
expect(endTimeField).toHaveValue("04:00");
|
||||
});
|
||||
|
||||
it("Shows maintenance window options when 'Enable auto updates' is checked", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
expect(enableAutoUpdatesCheckbox).not.toBeChecked();
|
||||
// Click the checkbox to enable auto updates.
|
||||
await user.click(enableAutoUpdatesCheckbox);
|
||||
await waitFor(() => {
|
||||
expect(enableAutoUpdatesCheckbox).toBeChecked();
|
||||
// Verify that the maintenance window fields are shown (but empty).
|
||||
const startTimeField = screen.getByLabelText("Earliest start time");
|
||||
const endTimeField = screen.getByLabelText("Latest start time");
|
||||
expect(startTimeField).toBeInTheDocument();
|
||||
expect(endTimeField).toBeInTheDocument();
|
||||
expect(startTimeField).toHaveValue("");
|
||||
expect(endTimeField).toHaveValue("");
|
||||
});
|
||||
});
|
||||
|
||||
it("Hides maintenance window options when 'Enable auto updates' is unchecked", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails({
|
||||
auto_update_enabled: true,
|
||||
auto_update_start_time: "02:00",
|
||||
auto_update_end_time: "04:00",
|
||||
})}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
expect(enableAutoUpdatesCheckbox).toBeChecked();
|
||||
// Click the checkbox to disable auto updates.
|
||||
await user.click(enableAutoUpdatesCheckbox);
|
||||
await waitFor(() => {
|
||||
expect(enableAutoUpdatesCheckbox).not.toBeChecked();
|
||||
// Verify that the maintenance window fields are not shown.
|
||||
const startTimeField = screen.queryByText("Earliest start time");
|
||||
const endTimeField = screen.queryByText("Latest start time");
|
||||
expect(startTimeField).not.toBeInTheDocument();
|
||||
expect(endTimeField).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Maintenance window validation", () => {
|
||||
it("Requires start time to be HH:MM format", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
expect(enableAutoUpdatesCheckbox).not.toBeChecked();
|
||||
// Click the checkbox to enable auto updates.
|
||||
await user.click(enableAutoUpdatesCheckbox);
|
||||
await waitFor(() => {
|
||||
expect(enableAutoUpdatesCheckbox).toBeChecked();
|
||||
});
|
||||
const startTimeField = screen.getByLabelText("Earliest start time");
|
||||
let endTimeField = screen.getByLabelText("Latest start time");
|
||||
expect(startTimeField).toBeInTheDocument();
|
||||
expect(endTimeField).toBeInTheDocument();
|
||||
// Enter invalid start time.
|
||||
await user.type(startTimeField, "19:99");
|
||||
// Move focus to trigger validation.
|
||||
await user.click(endTimeField);
|
||||
await user.type(endTimeField, "12:00");
|
||||
// Verify that validation message is shown
|
||||
const errorField = screen.getByLabelText(
|
||||
"Use HH:MM format (24-hour clock)"
|
||||
);
|
||||
expect(errorField).toBeInTheDocument();
|
||||
expect(errorField).toHaveValue("19:99");
|
||||
// Veryfy that end time is still present with valid label.
|
||||
endTimeField = screen.getByLabelText("Latest start time");
|
||||
expect(endTimeField).toBeInTheDocument();
|
||||
expect(endTimeField).toHaveValue("12:00");
|
||||
|
||||
const saveButton = screen.getByRole("button", { name: "Save" });
|
||||
expect(saveButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("Requires end time to be HH:MM format", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
expect(enableAutoUpdatesCheckbox).not.toBeChecked();
|
||||
await user.click(enableAutoUpdatesCheckbox);
|
||||
await waitFor(() => {
|
||||
expect(enableAutoUpdatesCheckbox).toBeChecked();
|
||||
});
|
||||
let startTimeField = screen.getByLabelText("Earliest start time");
|
||||
const endTimeField = screen.getByLabelText("Latest start time");
|
||||
expect(startTimeField).toBeInTheDocument();
|
||||
expect(endTimeField).toBeInTheDocument();
|
||||
// Enter invalid end time.
|
||||
await user.type(endTimeField, "19:99");
|
||||
// Move focus to trigger validation
|
||||
await user.click(startTimeField);
|
||||
await user.type(startTimeField, "12:00");
|
||||
// Verify that validation message is shown.
|
||||
const errorField = screen.getByLabelText(
|
||||
"Use HH:MM format (24-hour clock)"
|
||||
);
|
||||
expect(errorField).toBeInTheDocument();
|
||||
expect(errorField).toHaveValue("19:99");
|
||||
// Veryfy that start time is still present with valid label.
|
||||
startTimeField = screen.getByLabelText("Earliest start time");
|
||||
expect(startTimeField).toBeInTheDocument();
|
||||
expect(startTimeField).toHaveValue("12:00");
|
||||
|
||||
const saveButton = screen.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
expect(saveButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("Requires both start and end times to be set", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
expect(enableAutoUpdatesCheckbox).not.toBeChecked();
|
||||
await user.click(enableAutoUpdatesCheckbox);
|
||||
await waitFor(() => {
|
||||
expect(enableAutoUpdatesCheckbox).toBeChecked();
|
||||
});
|
||||
const startTimeField = screen.getByLabelText("Earliest start time");
|
||||
const endTimeField = screen.getByLabelText("Latest start time");
|
||||
const saveButton = screen.getByRole("button", { name: "Save" });
|
||||
|
||||
expect(startTimeField).toBeInTheDocument();
|
||||
expect(endTimeField).toBeInTheDocument();
|
||||
// Enter only start time.
|
||||
await user.type(startTimeField, "10:00");
|
||||
// Click Save button to trigger validation.
|
||||
await user.click(saveButton);
|
||||
// Verify that validation message is shown for end time.
|
||||
expect(
|
||||
screen.getByLabelText("Latest start time is required")
|
||||
).toBeInTheDocument();
|
||||
// Now enter only end time.
|
||||
await user.clear(startTimeField);
|
||||
await user.type(endTimeField, "12:00");
|
||||
// Click Save button to trigger validation.
|
||||
await user.click(saveButton);
|
||||
// Verify that validation message is shown for start time
|
||||
// but the end-time validation message is cleared.
|
||||
expect(
|
||||
screen.getByLabelText("Earliest start time is required")
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("Latest start time is required")
|
||||
).not.toBeInTheDocument();
|
||||
expect(saveButton).toBeDisabled();
|
||||
|
||||
// Clear both
|
||||
await user.clear(startTimeField);
|
||||
await user.clear(endTimeField);
|
||||
// Click Save button to trigger validation.
|
||||
await user.click(saveButton);
|
||||
// Verify that validation message is shown for both times.
|
||||
expect(
|
||||
screen.getByLabelText("Earliest start time is required")
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByLabelText("Latest start time is required")
|
||||
).toBeInTheDocument();
|
||||
expect(saveButton).toBeDisabled();
|
||||
// Fill both with valid values.
|
||||
await user.type(startTimeField, "10:00");
|
||||
await user.type(endTimeField, "12:30");
|
||||
await user.click(endTimeField);
|
||||
// Verify that no validation messages are shown.
|
||||
expect(
|
||||
screen.queryByLabelText("Earliest start time is required")
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByLabelText("Latest start time is required")
|
||||
).not.toBeInTheDocument();
|
||||
expect(saveButton).toBeEnabled();
|
||||
});
|
||||
|
||||
it("Requires window to be at least one hour", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
expect(enableAutoUpdatesCheckbox).not.toBeChecked();
|
||||
await user.click(enableAutoUpdatesCheckbox);
|
||||
await waitFor(() => {
|
||||
expect(enableAutoUpdatesCheckbox).toBeChecked();
|
||||
});
|
||||
const startTimeField = screen.getByLabelText("Earliest start time");
|
||||
const endTimeField = screen.getByLabelText("Latest start time");
|
||||
expect(startTimeField).toBeInTheDocument();
|
||||
expect(endTimeField).toBeInTheDocument();
|
||||
// Set a 59-minute window.
|
||||
await user.type(startTimeField, "12:00");
|
||||
await user.click(endTimeField);
|
||||
await user.type(endTimeField, "12:59");
|
||||
await user.click(startTimeField);
|
||||
// Verify that validation message is shown.
|
||||
const error = screen.getByText(
|
||||
"Update window must be at least 60 minutes long"
|
||||
);
|
||||
expect(error).toBeInTheDocument();
|
||||
const saveButton = screen.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
expect(saveButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Target options", () => {
|
||||
it("Shows 'All hosts' if no labels are configured for the title", async () => {
|
||||
render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByLabelText("All hosts")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Custom")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("All hosts")).toBeChecked();
|
||||
expect(screen.getByLabelText("Custom")).not.toBeChecked();
|
||||
});
|
||||
it("Shows label options if labels are configured for the title", async () => {
|
||||
render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails({
|
||||
app_store_app: createMockAppStoreApp({
|
||||
labels_include_any: [
|
||||
{ name: mockLabels[1].name, id: mockLabels[1].id },
|
||||
],
|
||||
}),
|
||||
})}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByLabelText("All hosts")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Custom")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("All hosts")).not.toBeChecked();
|
||||
expect(screen.getByLabelText("Custom")).toBeChecked();
|
||||
expect(screen.getByLabelText(mockLabels[1].name)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(mockLabels[1].name)).toBeChecked();
|
||||
expect(screen.getByLabelText(mockLabels[0].name)).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(mockLabels[0].name)).not.toBeChecked();
|
||||
const saveButton = screen.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
expect(saveButton).toBeEnabled();
|
||||
});
|
||||
|
||||
it("Requires at least one label to be selected if 'Custom' is selected", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails({
|
||||
app_store_app: createMockAppStoreApp({
|
||||
labels_include_any: [
|
||||
{ name: mockLabels[1].name, id: mockLabels[1].id },
|
||||
],
|
||||
}),
|
||||
})}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const customOption = screen.getByLabelText("Custom");
|
||||
expect(customOption).toBeChecked();
|
||||
const labelOption = screen.getByLabelText(mockLabels[1].name);
|
||||
expect(labelOption).toBeChecked();
|
||||
await user.click(labelOption);
|
||||
expect(labelOption).not.toBeChecked();
|
||||
const saveButton = screen.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
expect(saveButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Submitting the form", () => {
|
||||
const requestSpy = jest.fn();
|
||||
const submitHandler = http.patch(
|
||||
baseUrl("/software/titles/*/app_store_app"),
|
||||
async ({ request }) => {
|
||||
const requestData = (await request.json()) as ISoftwareAutoUpdateConfigFormData;
|
||||
requestSpy(requestData);
|
||||
return HttpResponse.json({});
|
||||
}
|
||||
);
|
||||
beforeEach(() => {
|
||||
mockServer.use(submitHandler);
|
||||
requestSpy.mockClear();
|
||||
});
|
||||
it("Sends the correct payload when 'Enable auto updates' is unchecked", async () => {
|
||||
render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const saveButton = screen.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
expect(saveButton).toBeEnabled();
|
||||
await act(() => {
|
||||
saveButton.click();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(requestSpy).toHaveBeenCalledWith({
|
||||
auto_update_enabled: false,
|
||||
labels_include_any: [],
|
||||
labels_exclude_any: [],
|
||||
team_id: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Sends the correct payload when 'Enable auto updates' is checked and a valid window is configured", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails()}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const enableAutoUpdatesCheckbox = screen.getByRole("checkbox", {
|
||||
name: "Enable auto updates",
|
||||
});
|
||||
await act(() => {
|
||||
enableAutoUpdatesCheckbox.click();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(enableAutoUpdatesCheckbox).toBeChecked();
|
||||
});
|
||||
const startTimeField = screen.getByLabelText("Earliest start time");
|
||||
const endTimeField = screen.getByLabelText("Latest start time");
|
||||
await user.type(startTimeField, "02:00");
|
||||
await user.type(endTimeField, "04:00");
|
||||
const saveButton = screen.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
expect(saveButton).toBeEnabled();
|
||||
await act(() => {
|
||||
saveButton.click();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(requestSpy).toHaveBeenCalledWith({
|
||||
auto_update_enabled: true,
|
||||
auto_update_start_time: "02:00",
|
||||
auto_update_end_time: "04:00",
|
||||
labels_include_any: [],
|
||||
labels_exclude_any: [],
|
||||
team_id: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Sends the correct payload when 'All hosts' is selected as the target", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails({
|
||||
app_store_app: createMockAppStoreApp({
|
||||
labels_include_any: [
|
||||
{ name: mockLabels[1].name, id: mockLabels[1].id },
|
||||
],
|
||||
}),
|
||||
})}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const allHostsRadio = screen.getByLabelText("All hosts");
|
||||
expect(allHostsRadio).toBeInTheDocument();
|
||||
await user.click(allHostsRadio);
|
||||
const saveButton = screen.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
expect(saveButton).toBeEnabled();
|
||||
await user.click(saveButton);
|
||||
await waitFor(() => {
|
||||
expect(requestSpy).toHaveBeenCalledWith({
|
||||
auto_update_enabled: false,
|
||||
labels_include_any: [],
|
||||
labels_exclude_any: [],
|
||||
team_id: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Sends the correct payload when specific labels are selected as the target", async () => {
|
||||
const { user } = render(
|
||||
<EditAutoUpdateConfigModal
|
||||
softwareTitle={createMockSoftwareTitleDetails({
|
||||
app_store_app: createMockAppStoreApp({
|
||||
labels_include_any: [
|
||||
{ name: mockLabels[1].name, id: mockLabels[1].id },
|
||||
],
|
||||
}),
|
||||
})}
|
||||
teamId={1}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onExit={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const saveButton = screen.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
expect(saveButton).toBeEnabled();
|
||||
await act(() => {
|
||||
user.click(saveButton);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(requestSpy).toHaveBeenCalledWith({
|
||||
auto_update_enabled: false,
|
||||
labels_include_any: [mockLabels[1].name],
|
||||
team_id: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
createMockSoftwareTitle,
|
||||
createMockSoftwarePackage,
|
||||
createMockAppStoreApp,
|
||||
createMockAppStoreAppAndroid,
|
||||
} from "__mocks__/softwareMock";
|
||||
|
||||
import { render as defaultRender, screen } from "@testing-library/react";
|
||||
import { UserEvent } from "@testing-library/user-event";
|
||||
import { createCustomRenderer, createMockRouter } from "test/test-utils";
|
||||
|
||||
import SoftwareSummaryCard from "./SoftwareSummaryCard";
|
||||
|
||||
const router = createMockRouter();
|
||||
|
||||
// Mock the SoftwareIcon component since it makes API calls.
|
||||
// We'll just check that it's called with the correct URL.
|
||||
const mockSoftwareIcon = jest.fn();
|
||||
jest.mock("../../components/icons/SoftwareIcon", () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: ({ url }: { url: string }) => {
|
||||
mockSoftwareIcon({ url });
|
||||
return <div />;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("Software Summary Card", () => {
|
||||
beforeEach(() => {
|
||||
mockSoftwareIcon.mockClear();
|
||||
});
|
||||
it("Shows the correct basic info about a software title", async () => {
|
||||
const softwareTitle = createMockSoftwareTitle({
|
||||
icon_url: "https://example.com/icon.png",
|
||||
});
|
||||
defaultRender(
|
||||
<SoftwareSummaryCard
|
||||
softwareTitle={softwareTitle}
|
||||
softwareId={1}
|
||||
router={router}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onToggleViewYaml={jest.fn()}
|
||||
/>
|
||||
);
|
||||
// Get the text with aria label "software display name"
|
||||
const displayNameElement = screen.getByLabelText("software display name");
|
||||
expect(displayNameElement).toHaveTextContent(softwareTitle.name);
|
||||
// Check for type "Application (macOS)"
|
||||
expect(screen.getByText("Application (macOS)")).toBeInTheDocument();
|
||||
// Check that the icon component is called with the correct URL.
|
||||
expect(mockSoftwareIcon).toHaveBeenCalledWith({
|
||||
url: "https://example.com/icon.png",
|
||||
});
|
||||
});
|
||||
|
||||
describe("Actions dropdown", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
isPremiumTier: true,
|
||||
isGlobalAdmin: true,
|
||||
config: {
|
||||
gitops: {
|
||||
gitops_mode_enabled: false,
|
||||
repository_url: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Shared helper function to open the actions dropdown and retrieve all visible options.
|
||||
const getDropdownOptions = async (user: UserEvent): Promise<string[]> => {
|
||||
const actionsButton = screen.getByText("Actions");
|
||||
expect(actionsButton).toBeInTheDocument();
|
||||
|
||||
await user.click(actionsButton);
|
||||
|
||||
// Get all options from the dropdown menu
|
||||
const options = screen.getAllByTestId("dropdown-option");
|
||||
return options.map((option) => option.textContent || "");
|
||||
};
|
||||
|
||||
it("displays Edit appearance and Edit software options for standard software packages", async () => {
|
||||
const { user } = render(
|
||||
<SoftwareSummaryCard
|
||||
softwareTitle={createMockSoftwareTitle({
|
||||
software_package: createMockSoftwarePackage(),
|
||||
})}
|
||||
softwareId={1}
|
||||
teamId={1}
|
||||
router={router}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onToggleViewYaml={jest.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
const options = await getDropdownOptions(user);
|
||||
|
||||
expect(options).toContain("Edit appearance");
|
||||
expect(options).toContain("Edit software");
|
||||
expect(options).not.toContain("Edit configuration");
|
||||
expect(options).not.toContain("Schedule auto updates");
|
||||
});
|
||||
|
||||
it("displays Edit appearance, Edit software, and Schedule auto updates for iOS/iPadOS apps", async () => {
|
||||
const { user } = render(
|
||||
<SoftwareSummaryCard
|
||||
softwareTitle={createMockSoftwareTitle({
|
||||
source: "ios_apps",
|
||||
app_store_app: createMockAppStoreApp(),
|
||||
})}
|
||||
softwareId={1}
|
||||
teamId={1}
|
||||
router={router}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onToggleViewYaml={jest.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
const options = await getDropdownOptions(user);
|
||||
|
||||
expect(options).toContain("Edit appearance");
|
||||
expect(options).toContain("Edit software");
|
||||
expect(options).toContain("Schedule auto updates");
|
||||
expect(options).not.toContain("Edit configuration");
|
||||
});
|
||||
|
||||
it("displays Edit appearance and Edit configuration (but not Edit software) for Android apps", async () => {
|
||||
const { user } = render(
|
||||
<SoftwareSummaryCard
|
||||
softwareTitle={createMockSoftwareTitle({
|
||||
source: "android_apps",
|
||||
app_store_app: createMockAppStoreAppAndroid(),
|
||||
software_package: null,
|
||||
})}
|
||||
softwareId={1}
|
||||
teamId={1}
|
||||
router={router}
|
||||
refetchSoftwareTitle={jest.fn()}
|
||||
onToggleViewYaml={jest.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
const options = await getDropdownOptions(user);
|
||||
|
||||
expect(options).toContain("Edit appearance");
|
||||
expect(options).toContain("Edit configuration");
|
||||
expect(options).not.toContain("Edit software");
|
||||
expect(options).not.toContain("Schedule auto updates");
|
||||
});
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -229,7 +229,7 @@ const SoftwareDetailsSummary = ({
|
||||
)}
|
||||
<dl className={`${baseClass}__info`}>
|
||||
<div className={`${baseClass}__title-actions`}>
|
||||
<h1>
|
||||
<h1 aria-label="software display name">
|
||||
{isRollingArch ? (
|
||||
// wrap a tooltip around the "rolling" suffix
|
||||
<>
|
||||
|
||||
@@ -33,6 +33,7 @@ const config: Config = {
|
||||
moduleNameMapper: {
|
||||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
|
||||
"<rootDir>/frontend/__mocks__/fileMock.js",
|
||||
"\\.(sh|ps1)$": "<rootDir>/frontend/__mocks__/fileMock.js",
|
||||
"\\.(css|scss|sass)$": "identity-obj-proxy",
|
||||
},
|
||||
testMatch: ["**/*tests.[jt]s?(x)"],
|
||||
|
||||
Reference in New Issue
Block a user