mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
update host details and my device page to show users card for android devices (#32975)
resolves #32356 This updates the host details and my device pages to show the users card that will show the idp info on android devices this also adds some tests for the various rendering states of the users card component - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually
This commit is contained in:
parent
3d0a0639f6
commit
697a0bdd0a
7 changed files with 227 additions and 269 deletions
1
changes/issue-32356-update-ui-android-user-card
Normal file
1
changes/issue-32356-update-ui-android-user-card
Normal file
|
|
@ -0,0 +1 @@
|
|||
- update host details and my device UI to show the users card for android hosts
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { IHost } from "interfaces/host";
|
||||
import { IHost, IHostEndUser } from "interfaces/host";
|
||||
import { IHostMdmProfile } from "interfaces/mdm";
|
||||
import { pick } from "lodash";
|
||||
|
||||
|
|
@ -240,4 +240,19 @@ export const createMockGetHostSoftwareResponse = (
|
|||
};
|
||||
};
|
||||
|
||||
export const DEFAULT_HOST_END_USER_MOCK: IHostEndUser = {
|
||||
idp_department: "Engineering",
|
||||
idp_info_updated_at: "2025-09-15T12:00:00Z",
|
||||
idp_username: "jdoe",
|
||||
idp_full_name: "John Doe",
|
||||
idp_groups: ["GroupA", "GroupB"],
|
||||
other_emails: [{ email: "other@example.com", source: "chrome" }],
|
||||
};
|
||||
|
||||
export const createMockHostEndUser = (
|
||||
overrides?: Partial<IHostEndUser>
|
||||
): IHostEndUser => {
|
||||
return { ...DEFAULT_HOST_END_USER_MOCK, ...overrides };
|
||||
};
|
||||
|
||||
export default createMockHost;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import { screen, waitFor } from "@testing-library/react";
|
||||
|
||||
import { IDeviceUserResponse, IHostDevice } from "interfaces/host";
|
||||
import createMockHost from "__mocks__/hostMock";
|
||||
import createMockHost, { createMockHostEndUser } from "__mocks__/hostMock";
|
||||
import mockServer from "test/mock-server";
|
||||
import { createCustomRenderer, createMockRouter } from "test/test-utils";
|
||||
import createMockLicense from "__mocks__/licenseMock";
|
||||
|
|
@ -106,6 +106,34 @@ describe("Device User Page", () => {
|
|||
|
||||
expect(screen.queryByText(/Certificates/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("hides the user card if the device is not apple or android device", async () => {
|
||||
const host = createMockHost() as IHostDevice;
|
||||
host.platform = "windows";
|
||||
host.end_users = [];
|
||||
|
||||
mockServer.use(customDeviceHandler({ host }));
|
||||
mockServer.use(defaultDeviceCertificatesHandler);
|
||||
mockServer.use(emptySetupExperienceHandler);
|
||||
|
||||
const render = createCustomRenderer({
|
||||
withBackendMock: true,
|
||||
});
|
||||
|
||||
render(
|
||||
<DeviceUserPage
|
||||
router={mockRouter}
|
||||
params={{ device_auth_token: "testToken" }}
|
||||
location={mockLocation}
|
||||
/>
|
||||
);
|
||||
|
||||
// waiting for the device data to render
|
||||
await screen.findByText(/Details/);
|
||||
|
||||
expect(screen.queryByText(/User/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("Setup experience software installation", () => {
|
||||
const REGULAR_DUP_MATCHER = /Last fetched/;
|
||||
const SETTING_UP_YOUR_DEVICE_MATCHER = /Setting up your device/;
|
||||
|
|
@ -268,269 +296,4 @@ describe("Device User Page", () => {
|
|||
expect(btn).toBeNull();
|
||||
});
|
||||
});
|
||||
// // FIXME: revisit these tests when we have a better way to test modals
|
||||
// describe("AutoEnrollMDMModal", () => {
|
||||
// it("shows the pre-Sonoma body when the host is pre-Sonoma", async () => {
|
||||
// const host = createMockHost() as IHostDevice;
|
||||
// host.platform = "darwin";
|
||||
// host.os_version = "macOS 13.1.1";
|
||||
// host.dep_assigned_to_fleet = true;
|
||||
|
||||
// mockServer.use(
|
||||
// customDeviceHandler({
|
||||
// host,
|
||||
// global_config: {
|
||||
// mdm: { enabled_and_configured: true },
|
||||
// features: { enable_software_inventory: false },
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
|
||||
// const render = createCustomRenderer({
|
||||
// withBackendMock: true,
|
||||
// });
|
||||
|
||||
// const { user } = render(
|
||||
// <DeviceUserPage
|
||||
// router={mockRouter}
|
||||
// params={{ device_auth_token: "testToken" }}
|
||||
// location={mockLocation}
|
||||
// />
|
||||
// );
|
||||
|
||||
// // waiting for the device data to render
|
||||
// await screen.findByText("About");
|
||||
|
||||
// // open the modal
|
||||
// await user.click(screen.getByRole("button", { name: "Turn on MDM" }));
|
||||
|
||||
// // waiting for the modal to render
|
||||
// await screen.findByText("To turn on MDM,");
|
||||
|
||||
// // autoenroll-specific copy
|
||||
// expect(
|
||||
// screen.getByText("sudo profiles renew -type enrollment")
|
||||
// ).toBeInTheDocument();
|
||||
// // version-specific copy
|
||||
// expect(screen.getByText("notification center")).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
// it("shows the Sonoma-and-above body when the host is Sonoma", async () => {
|
||||
// const host = createMockHost() as IHostDevice;
|
||||
// host.platform = "darwin";
|
||||
// host.os_version = "macOS 14.7";
|
||||
// host.dep_assigned_to_fleet = true;
|
||||
|
||||
// mockServer.use(
|
||||
// customDeviceHandler({
|
||||
// host,
|
||||
// global_config: {
|
||||
// mdm: { enabled_and_configured: true },
|
||||
// features: { enable_software_inventory: false },
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
|
||||
// const render = createCustomRenderer({
|
||||
// withBackendMock: true,
|
||||
// });
|
||||
|
||||
// const { user } = render(
|
||||
// <DeviceUserPage
|
||||
// router={mockRouter}
|
||||
// params={{ device_auth_token: "testToken" }}
|
||||
// location={mockLocation}
|
||||
// />
|
||||
// );
|
||||
|
||||
// // waiting for the device data to render
|
||||
// await screen.findByText("About");
|
||||
|
||||
// // open the modal
|
||||
// await user.click(screen.getByRole("button", { name: "Turn on MDM" }));
|
||||
|
||||
// // waiting for the modal to render
|
||||
// await screen.findByText("To turn on MDM,");
|
||||
|
||||
// // autoenroll-specific copy
|
||||
// expect(
|
||||
// screen.getByText("sudo profiles renew -type enrollment")
|
||||
// ).toBeInTheDocument();
|
||||
// // version-specific copy
|
||||
// expect(screen.getByText("System Settings")).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
// it("shows the Sonoma-and-above body when the host is post-Sonoma", async () => {
|
||||
// const host = createMockHost() as IHostDevice;
|
||||
// host.platform = "darwin";
|
||||
// host.os_version = "macOS 15.3";
|
||||
// host.dep_assigned_to_fleet = true;
|
||||
|
||||
// mockServer.use(
|
||||
// customDeviceHandler({
|
||||
// host,
|
||||
// global_config: {
|
||||
// mdm: { enabled_and_configured: true },
|
||||
// features: { enable_software_inventory: false },
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
|
||||
// const render = createCustomRenderer({
|
||||
// withBackendMock: true,
|
||||
// });
|
||||
|
||||
// const { user } = render(
|
||||
// <DeviceUserPage
|
||||
// router={mockRouter}
|
||||
// params={{ device_auth_token: "testToken" }}
|
||||
// location={mockLocation}
|
||||
// />
|
||||
// );
|
||||
|
||||
// // waiting for the device data to render
|
||||
// await screen.findByText("About");
|
||||
|
||||
// // open the modal
|
||||
// await user.click(screen.getByRole("button", { name: "Turn on MDM" }));
|
||||
|
||||
// // waiting for the modal to render
|
||||
// await screen.findByText("To turn on MDM,");
|
||||
|
||||
// // autoenroll-specific copy
|
||||
// expect(
|
||||
// screen.getByText("sudo profiles renew -type enrollment")
|
||||
// ).toBeInTheDocument();
|
||||
// // version-specific copy
|
||||
// expect(screen.getByText("System Settings")).toBeInTheDocument();
|
||||
// });
|
||||
// });
|
||||
// // FIXME: revisit these tests when we have a better way to test modals
|
||||
// describe("ManualEnrollMDMModal", () => {
|
||||
// it("shows the pre-Seqouia body when the host is pre-Seqouia", async () => {
|
||||
// const host = createMockHost() as IHostDevice;
|
||||
// host.platform = "darwin";
|
||||
// host.os_version = "macOS 14.1.1";
|
||||
|
||||
// mockServer.use(
|
||||
// customDeviceHandler({
|
||||
// host,
|
||||
// global_config: {
|
||||
// mdm: { enabled_and_configured: false },
|
||||
// features: { enable_software_inventory: true },
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
|
||||
// const render = createCustomRenderer({
|
||||
// withBackendMock: true,
|
||||
// });
|
||||
|
||||
// const { user } = render(
|
||||
// <DeviceUserPage
|
||||
// router={mockRouter}
|
||||
// params={{ device_auth_token: "testToken" }}
|
||||
// location={mockLocation}
|
||||
// />
|
||||
// );
|
||||
|
||||
// // waiting for the device data to render
|
||||
// await screen.findByText("About");
|
||||
|
||||
// // open the modal
|
||||
// await user.click(screen.getByRole("button", { name: "Turn on MDM" }));
|
||||
|
||||
// // waiting for the modal to render
|
||||
// await screen.findByText("To turn on MDM,");
|
||||
|
||||
// // manualenroll-specific copy
|
||||
// expect(screen.getByText("Download your profile.")).toBeInTheDocument();
|
||||
// // version-specific copy
|
||||
// expect(screen.getByText("In the search bar")).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
// it("shows the Sequoia-and-above body when the host is Sequoia", async () => {
|
||||
// const host = createMockHost() as IHostDevice;
|
||||
// host.platform = "darwin";
|
||||
// host.os_version = "macOS 15.3";
|
||||
|
||||
// mockServer.use(
|
||||
// customDeviceHandler({
|
||||
// host,
|
||||
// global_config: {
|
||||
// mdm: { enabled_and_configured: false },
|
||||
// features: { enable_software_inventory: true },
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
|
||||
// const render = createCustomRenderer({
|
||||
// withBackendMock: true,
|
||||
// });
|
||||
|
||||
// const { user } = render(
|
||||
// <DeviceUserPage
|
||||
// router={mockRouter}
|
||||
// params={{ device_auth_token: "testToken" }}
|
||||
// location={mockLocation}
|
||||
// />
|
||||
// );
|
||||
|
||||
// // waiting for the device data to render
|
||||
// await screen.findByText("About");
|
||||
|
||||
// // open the modal
|
||||
// await user.click(screen.getByRole("button", { name: "Turn on MDM" }));
|
||||
|
||||
// // waiting for the modal to render
|
||||
// await screen.findByText("To turn on MDM,");
|
||||
|
||||
// // manualenroll-specific copy
|
||||
// expect(screen.getByText("Download your profile.")).toBeInTheDocument();
|
||||
// // version-specific copy
|
||||
// expect(screen.getByText("In the sidebar menu")).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
// it("shows the Sequoia-and-above body when the host is post-Sequoia", async () => {
|
||||
// const host = createMockHost() as IHostDevice;
|
||||
// host.platform = "darwin";
|
||||
// host.os_version = "macOS 16.0";
|
||||
|
||||
// mockServer.use(
|
||||
// customDeviceHandler({
|
||||
// host,
|
||||
// global_config: {
|
||||
// mdm: { enabled_and_configured: false },
|
||||
// features: { enable_software_inventory: true },
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
|
||||
// const render = createCustomRenderer({
|
||||
// withBackendMock: true,
|
||||
// });
|
||||
|
||||
// const { user } = render(
|
||||
// <DeviceUserPage
|
||||
// router={mockRouter}
|
||||
// params={{ device_auth_token: "testToken" }}
|
||||
// location={mockLocation}
|
||||
// />
|
||||
// );
|
||||
|
||||
// // waiting for the device data to render
|
||||
// await screen.findByText("About");
|
||||
|
||||
// // open the modal
|
||||
// await user.click(screen.getByRole("button", { name: "Turn on MDM" }));
|
||||
|
||||
// // waiting for the modal to render
|
||||
// await screen.findByText("To turn on MDM,");
|
||||
|
||||
// // manual-specific copy
|
||||
// expect(screen.getByText("Download your profile.")).toBeInTheDocument();
|
||||
// // version-specific copy
|
||||
// expect(screen.getByText("In the sidebar menu")).toBeInTheDocument();
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -469,6 +469,7 @@ const DeviceUserPage = ({
|
|||
|
||||
const showUsersCard =
|
||||
host?.platform === "darwin" ||
|
||||
host?.platform === "android" ||
|
||||
generateChromeProfilesValues(host?.end_users ?? []).length > 0 ||
|
||||
generateOtherEmailsValues(host?.end_users ?? []).length > 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -964,6 +964,7 @@ const HostDetailsPage = ({
|
|||
|
||||
const showUsersCard =
|
||||
isAppleDevice(host.platform) ||
|
||||
isAndroidHost ||
|
||||
generateChromeProfilesValues(host.end_users ?? []).length > 0 ||
|
||||
generateOtherEmailsValues(host.end_users ?? []).length > 0;
|
||||
const showActivityCard = !isAndroidHost;
|
||||
|
|
|
|||
177
frontend/pages/hosts/details/cards/User/User.tests.tsx
Normal file
177
frontend/pages/hosts/details/cards/User/User.tests.tsx
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import React from "react";
|
||||
import { screen, render } from "@testing-library/react";
|
||||
import { noop } from "lodash";
|
||||
|
||||
import { createMockHostEndUser } from "__mocks__/hostMock";
|
||||
|
||||
import User from ".";
|
||||
|
||||
describe("User card", () => {
|
||||
it("renders the username field when the platform is Apple", () => {
|
||||
const endUsers = [createMockHostEndUser()];
|
||||
render(
|
||||
<User
|
||||
platform="darwin"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Username (IdP)")).toBeInTheDocument();
|
||||
expect(screen.getByText("jdoe")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the username field when the platform is android", () => {
|
||||
const endUsers = [createMockHostEndUser()];
|
||||
render(
|
||||
<User
|
||||
platform="android"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Username (IdP)")).toBeInTheDocument();
|
||||
expect(screen.getByText("jdoe")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the full name field when the platform is Apple and has full name values", () => {
|
||||
const endUsers = [createMockHostEndUser()];
|
||||
render(
|
||||
<User
|
||||
platform="darwin"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Full name (IdP)")).toBeInTheDocument();
|
||||
expect(screen.getByText("John Doe")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the full name field when the platform is Android and has full name values", () => {
|
||||
const endUsers = [createMockHostEndUser()];
|
||||
render(
|
||||
<User
|
||||
platform="darwin"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Full name (IdP)")).toBeInTheDocument();
|
||||
expect(screen.getByText("John Doe")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the groups field when the platform is Apple and has groups values", () => {
|
||||
const endUsers = [createMockHostEndUser()];
|
||||
render(
|
||||
<User
|
||||
platform="darwin"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Groups (IdP)")).toBeInTheDocument();
|
||||
expect(screen.getByText("GroupA")).toBeInTheDocument();
|
||||
expect(screen.getByText("+ 1 more")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the groups field when the platform is Android and has groups values", () => {
|
||||
const endUsers = [createMockHostEndUser()];
|
||||
render(
|
||||
<User
|
||||
platform="darwin"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Groups (IdP)")).toBeInTheDocument();
|
||||
expect(screen.getByText("GroupA")).toBeInTheDocument();
|
||||
expect(screen.getByText("+ 1 more")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the chrome profiles field when has chrome profile values", () => {
|
||||
const endUsers = [
|
||||
createMockHostEndUser({
|
||||
other_emails: [
|
||||
{ email: "Profile1", source: "google_chrome_profiles" },
|
||||
{ email: "Profile2", source: "google_chrome_profiles" },
|
||||
],
|
||||
}),
|
||||
];
|
||||
render(
|
||||
<User
|
||||
platform="windows"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Google Chrome profiles")).toBeInTheDocument();
|
||||
expect(screen.getByText("Profile1")).toBeInTheDocument();
|
||||
expect(screen.getByText("+ 1 more")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders other emails field when has other email values", () => {
|
||||
const endUsers = [
|
||||
createMockHostEndUser({
|
||||
other_emails: [
|
||||
{ email: "other1@example.com", source: "custom" },
|
||||
{ email: "other2@example.com", source: "custom" },
|
||||
],
|
||||
}),
|
||||
];
|
||||
render(
|
||||
<User
|
||||
platform="windows"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Other emails")).toBeInTheDocument();
|
||||
expect(screen.getByText("other1@example.com")).toBeInTheDocument();
|
||||
expect(screen.getByText("+ 1 more")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the department field when the platform is Apple and it has department value", () => {
|
||||
const endUsers = [createMockHostEndUser()];
|
||||
render(
|
||||
<User
|
||||
platform="darwin"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Department (IdP)")).toBeInTheDocument();
|
||||
expect(screen.getByText("Engineering")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the department field when the platform is Android and it has department value", () => {
|
||||
const endUsers = [createMockHostEndUser()];
|
||||
render(
|
||||
<User
|
||||
platform="android"
|
||||
endUsers={endUsers}
|
||||
enableAddEndUser={false}
|
||||
onAddEndUser={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Department (IdP)")).toBeInTheDocument();
|
||||
expect(screen.getByText("Engineering")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -3,7 +3,7 @@ import classnames from "classnames";
|
|||
import { noop } from "lodash";
|
||||
|
||||
import { IHostEndUser } from "interfaces/host";
|
||||
import { HostPlatform, isAppleDevice } from "interfaces/platform";
|
||||
import { HostPlatform, isAndroid, isAppleDevice } from "interfaces/platform";
|
||||
|
||||
import Card from "components/Card";
|
||||
import CardHeader from "components/CardHeader";
|
||||
|
|
@ -50,7 +50,7 @@ const User = ({
|
|||
const otherEmailsDisplayValues = generateOtherEmailsValues(endUsers);
|
||||
|
||||
const endUser = endUsers[0];
|
||||
const showUsername = isAppleDevice(platform);
|
||||
const showUsername = isAppleDevice(platform) || isAndroid(platform);
|
||||
const showFullName = showUsername && userNameDisplayValues.length > 0;
|
||||
const showGroups = showUsername && userNameDisplayValues.length > 0;
|
||||
const showChromeProfiles = chromeProfilesDisplayValues.length > 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue