2026-01-06 15:32:36 +00:00
|
|
|
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,
|
2026-01-12 17:08:26 +00:00
|
|
|
auto_update_window_start: "02:00",
|
|
|
|
|
auto_update_window_end: "04:00",
|
2026-01-06 15:32:36 +00: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,
|
2026-01-12 17:08:26 +00:00
|
|
|
auto_update_window_start: "02:00",
|
|
|
|
|
auto_update_window_end: "04:00",
|
2026-01-06 15:32:36 +00: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()}
|
|
|
|
|
/>
|
|
|
|
|
);
|
2026-03-27 18:43:26 +00:00
|
|
|
|
|
|
|
|
// Wait until target section has rendered and request has had a chance to resolve
|
|
|
|
|
await screen.findByLabelText("Custom");
|
|
|
|
|
|
|
|
|
|
// Now wait specifically for one label to appear
|
2026-04-09 13:16:54 +00:00
|
|
|
const freshLabel = await screen.findByRole("checkbox", {
|
|
|
|
|
name: mockLabels[1].name,
|
|
|
|
|
});
|
2026-03-27 18:43:26 +00:00
|
|
|
expect(freshLabel).toBeInTheDocument();
|
|
|
|
|
expect(freshLabel).toBeChecked();
|
|
|
|
|
|
2026-04-09 13:16:54 +00:00
|
|
|
const funLabel = screen.getByRole("checkbox", {
|
|
|
|
|
name: mockLabels[0].name,
|
|
|
|
|
});
|
2026-03-27 18:43:26 +00:00
|
|
|
expect(funLabel).toBeInTheDocument();
|
|
|
|
|
expect(funLabel).not.toBeChecked();
|
2026-01-06 15:32:36 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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()}
|
|
|
|
|
/>
|
|
|
|
|
);
|
2026-03-27 18:43:26 +00:00
|
|
|
// Wait for labels to load
|
2026-04-09 13:16:54 +00:00
|
|
|
await screen.findByRole("checkbox", { name: mockLabels[1].name });
|
2026-01-06 15:32:36 +00:00
|
|
|
const customOption = screen.getByLabelText("Custom");
|
|
|
|
|
expect(customOption).toBeChecked();
|
2026-04-09 13:16:54 +00:00
|
|
|
const labelOption = screen.getByRole("checkbox", {
|
|
|
|
|
name: mockLabels[1].name,
|
|
|
|
|
});
|
2026-01-06 15:32:36 +00:00
|
|
|
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 () => {
|
2026-03-27 18:43:26 +00:00
|
|
|
const { user } = render(
|
2026-01-06 15:32:36 +00:00
|
|
|
<EditAutoUpdateConfigModal
|
|
|
|
|
softwareTitle={createMockSoftwareTitleDetails()}
|
|
|
|
|
teamId={1}
|
|
|
|
|
refetchSoftwareTitle={jest.fn()}
|
|
|
|
|
onExit={jest.fn()}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
const saveButton = screen.getByRole("button", {
|
|
|
|
|
name: "Save",
|
|
|
|
|
});
|
|
|
|
|
expect(saveButton).toBeEnabled();
|
2026-03-27 18:43:26 +00:00
|
|
|
|
|
|
|
|
await user.click(saveButton);
|
2026-01-06 15:32:36 +00:00
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(requestSpy).toHaveBeenCalledWith({
|
|
|
|
|
auto_update_enabled: false,
|
|
|
|
|
labels_include_any: [],
|
|
|
|
|
labels_exclude_any: [],
|
2026-03-18 19:16:25 +00:00
|
|
|
labels_include_all: [],
|
Update API calls in front-end to use new, non-deprecated URLs and params (#41515)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41391
# Details
This PR updates front-end API calls to use new URLs and API params, so
that the front end doesn't cause deprecation warnings to appear on the
server.
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
n/a, should not be user-visible
## Testing
- [X] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually
The biggest risk here is not that we missed a spot that still causes a
deprecation warning, but that we might inadvertently make a change that
breaks the front end, for instance by sending `fleet_id` to a function
that drops it silently and thus sends no ID to the server. Fortunately
we use TypeScript in virtually every place affected by these changes, so
the code would not compile if there were mismatches between the API
expectation and what we're sending. Still, spot checking as many places
as possible both for deprecation-warning leaks and loss of functionality
is important.
## Summary by CodeRabbit
* **Refactor**
* Updated API nomenclature across the application to use "fleets"
instead of "teams" and "reports" instead of "queries" in endpoint paths
and request/response payloads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-13 03:26:48 +00:00
|
|
|
fleet_id: 1,
|
2026-01-06 15:32:36 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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,
|
2026-01-12 17:08:26 +00:00
|
|
|
auto_update_window_start: "02:00",
|
|
|
|
|
auto_update_window_end: "04:00",
|
2026-01-06 15:32:36 +00:00
|
|
|
labels_include_any: [],
|
|
|
|
|
labels_exclude_any: [],
|
2026-03-18 19:16:25 +00:00
|
|
|
labels_include_all: [],
|
Update API calls in front-end to use new, non-deprecated URLs and params (#41515)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41391
# Details
This PR updates front-end API calls to use new URLs and API params, so
that the front end doesn't cause deprecation warnings to appear on the
server.
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
n/a, should not be user-visible
## Testing
- [X] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually
The biggest risk here is not that we missed a spot that still causes a
deprecation warning, but that we might inadvertently make a change that
breaks the front end, for instance by sending `fleet_id` to a function
that drops it silently and thus sends no ID to the server. Fortunately
we use TypeScript in virtually every place affected by these changes, so
the code would not compile if there were mismatches between the API
expectation and what we're sending. Still, spot checking as many places
as possible both for deprecation-warning leaks and loss of functionality
is important.
## Summary by CodeRabbit
* **Refactor**
* Updated API nomenclature across the application to use "fleets"
instead of "teams" and "reports" instead of "queries" in endpoint paths
and request/response payloads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-13 03:26:48 +00:00
|
|
|
fleet_id: 1,
|
2026-01-06 15:32:36 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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: [],
|
2026-03-18 19:16:25 +00:00
|
|
|
labels_include_all: [],
|
Update API calls in front-end to use new, non-deprecated URLs and params (#41515)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41391
# Details
This PR updates front-end API calls to use new URLs and API params, so
that the front end doesn't cause deprecation warnings to appear on the
server.
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
n/a, should not be user-visible
## Testing
- [X] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually
The biggest risk here is not that we missed a spot that still causes a
deprecation warning, but that we might inadvertently make a change that
breaks the front end, for instance by sending `fleet_id` to a function
that drops it silently and thus sends no ID to the server. Fortunately
we use TypeScript in virtually every place affected by these changes, so
the code would not compile if there were mismatches between the API
expectation and what we're sending. Still, spot checking as many places
as possible both for deprecation-warning leaks and loss of functionality
is important.
## Summary by CodeRabbit
* **Refactor**
* Updated API nomenclature across the application to use "fleets"
instead of "teams" and "reports" instead of "queries" in endpoint paths
and request/response payloads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-13 03:26:48 +00:00
|
|
|
fleet_id: 1,
|
2026-01-06 15:32:36 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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],
|
Update API calls in front-end to use new, non-deprecated URLs and params (#41515)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41391
# Details
This PR updates front-end API calls to use new URLs and API params, so
that the front end doesn't cause deprecation warnings to appear on the
server.
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
n/a, should not be user-visible
## Testing
- [X] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually
The biggest risk here is not that we missed a spot that still causes a
deprecation warning, but that we might inadvertently make a change that
breaks the front end, for instance by sending `fleet_id` to a function
that drops it silently and thus sends no ID to the server. Fortunately
we use TypeScript in virtually every place affected by these changes, so
the code would not compile if there were mismatches between the API
expectation and what we're sending. Still, spot checking as many places
as possible both for deprecation-warning leaks and loss of functionality
is important.
## Summary by CodeRabbit
* **Refactor**
* Updated API nomenclature across the application to use "fleets"
instead of "teams" and "reports" instead of "queries" in endpoint paths
and request/response payloads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-13 03:26:48 +00:00
|
|
|
fleet_id: 1,
|
2026-01-06 15:32:36 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|