Demo: https://www.youtube.com/watch?v=cWxZlu9WuwA Guide updates: https://github.com/fleetdm/fleet/pull/49603/changes IT admins can configure the fleet that hosts enrolling through user-driven Windows MDM enrollment (Windows Autopilot, Entra join) are automatically assigned to, via the Windows MDM settings page, the mdm.windows_enrollment.default_fleet config setting, or GitOps. - New windows_enrollment_config row stores the default team; the config API surfaces it by fleet name and hydrates reads from the row so team renames and deletions never serve a stale name. Deleting the fleet clears the setting. - New edited_windows_enrollment_default_fleet activity, emitted only when the value changes. - The OMA-DM session persists the device-reported SMBIOS serial on still-unlinked enrollments, and orbit enrollment reverse-links by that serial and assigns the default fleet before orbit's one-shot setup-experience init, so the default fleet's software, scripts, and profiles apply during the Autopilot ESP. The DevDetail and osquery link paths keep the same assignment as fallbacks, and the EUA-token link path now shares the same post-link bookkeeping. - Hosts are only assigned when new to Fleet in this enrollment cycle: existing hosts, including ones parked in Unassigned, keep their fleet on re-enrollment, matching macOS ABM behavior. - GitOps defers applying the setting until teams declared in the same run are created, and fleetctl generate-gitops exports it. - Windows MDM settings page redesign per Figma: programmatic enrollment toggle, User driven enrollment section with the Entra-gated Default fleet dropdown, and a Migration section. <!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #41787 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] 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. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually ## Database migrations - [x] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [x] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). ## New Fleet configuration settings - [x] Verified that the setting is exported via `fleetctl generate-gitops` - [x] Verified the setting is documented in a separate PR to [the GitOps documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485) - [x] Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional) - [x] Verified that any relevant UI is disabled when GitOps mode is enabled <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for assigning a default Fleet Premium fleet to new Windows MDM enrollments, including Autopilot and Entra join. * Default-fleet settings can be configured, cleared, and managed through Windows MDM settings and GitOps. * Assigned fleet software, scripts, and profiles can apply during out-of-box setup. * Added activity-feed visibility for default-fleet changes. * Improved Windows enrollment matching using hardware serial numbers. * **Documentation** * Documented default-fleet assignment for Windows enrollment. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
153 lines
4.1 KiB
TypeScript
153 lines
4.1 KiB
TypeScript
import React from "react";
|
|
import { render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import DropdownWrapper, { CustomOptionType } from "./DropdownWrapper";
|
|
|
|
const sampleOptions: CustomOptionType[] = [
|
|
{
|
|
label: "Option 1",
|
|
value: "option1",
|
|
tooltipContent: "Tooltip 1",
|
|
helpText: "Help text 1",
|
|
},
|
|
{
|
|
label: "Option 2",
|
|
value: "option2",
|
|
tooltipContent: "Tooltip 2",
|
|
helpText: "Help text 2",
|
|
},
|
|
];
|
|
|
|
describe("DropdownWrapper Component", () => {
|
|
const mockOnChange = jest.fn();
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
test("renders with help text", () => {
|
|
render(
|
|
<DropdownWrapper
|
|
options={sampleOptions}
|
|
value="option1"
|
|
onChange={mockOnChange}
|
|
name="test-dropdown"
|
|
label="Test Dropdown"
|
|
helpText="This is a help text."
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText(/test dropdown/i)).toBeInTheDocument();
|
|
expect(screen.getByText(/this is a help text/i)).toBeInTheDocument();
|
|
});
|
|
|
|
test("calls onChange when an option is selected", async () => {
|
|
render(
|
|
<DropdownWrapper
|
|
options={sampleOptions}
|
|
value="option1"
|
|
onChange={mockOnChange}
|
|
name="test-dropdown"
|
|
label="Test Dropdown"
|
|
placeholder="Choose option"
|
|
/>
|
|
);
|
|
|
|
// Open the dropdown
|
|
await userEvent.click(screen.getByText(/option 1/i));
|
|
|
|
// Select Option 2
|
|
await userEvent.click(screen.getByText(/option 2/i));
|
|
|
|
expect(mockOnChange).toHaveBeenCalledWith({
|
|
helpText: "Help text 2",
|
|
label: "Option 2",
|
|
tooltipContent: "Tooltip 2",
|
|
value: "option2",
|
|
});
|
|
});
|
|
|
|
test("renders error message when provided", () => {
|
|
render(
|
|
<DropdownWrapper
|
|
options={sampleOptions}
|
|
value="option1"
|
|
onChange={mockOnChange}
|
|
name="test-dropdown"
|
|
label="Test Dropdown"
|
|
error="This is an error message."
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText(/this is an error message/i)).toBeInTheDocument();
|
|
});
|
|
|
|
test("displays no options message when no options are available", async () => {
|
|
render(
|
|
<DropdownWrapper
|
|
options={[]}
|
|
value=""
|
|
onChange={mockOnChange}
|
|
name="test-dropdown"
|
|
label="Test Dropdown"
|
|
placeholder="Choose option"
|
|
/>
|
|
);
|
|
|
|
// Open dropdown
|
|
await userEvent.click(screen.getByText(/choose option/i));
|
|
|
|
expect(screen.getByText(/no results found/i)).toBeInTheDocument();
|
|
});
|
|
|
|
test("shows the disabled tooltip on hover when disabled with content provided", async () => {
|
|
const { container } = render(
|
|
<DropdownWrapper
|
|
options={sampleOptions}
|
|
value="option1"
|
|
onChange={mockOnChange}
|
|
name="test-dropdown"
|
|
label="Test Dropdown"
|
|
isDisabled
|
|
disabledTooltipContent="Reason it is disabled"
|
|
/>
|
|
);
|
|
|
|
const tooltipAnchor = container.querySelector(
|
|
".dropdown-wrapper__disabled-tooltip .component__tooltip-wrapper__element"
|
|
);
|
|
expect(tooltipAnchor).toBeInTheDocument();
|
|
|
|
// react-tooltip only mounts the tip content once the anchor is hovered
|
|
await userEvent.hover(tooltipAnchor as Element);
|
|
expect(
|
|
await screen.findByText(/reason it is disabled/i)
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
// The tooltip wraps the control only when isDisabled and disabledTooltipContent are both set.
|
|
// Each case below drops one of those two operands, so neither can be removed from the condition.
|
|
test.each([
|
|
{
|
|
caseName: "enabled",
|
|
props: { disabledTooltipContent: "Reason it is disabled" },
|
|
},
|
|
{ caseName: "disabled without content", props: { isDisabled: true } },
|
|
])("does not render the disabled tooltip when $caseName", ({ props }) => {
|
|
const { container } = render(
|
|
<DropdownWrapper
|
|
options={sampleOptions}
|
|
value="option1"
|
|
onChange={mockOnChange}
|
|
name="test-dropdown"
|
|
label="Test Dropdown"
|
|
{...props}
|
|
/>
|
|
);
|
|
|
|
expect(
|
|
container.querySelector(".dropdown-wrapper__disabled-tooltip")
|
|
).not.toBeInTheDocument();
|
|
});
|
|
});
|