Show zeroes on software/software OSes/software vulns tables (#21584)
#18897 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/` - [x] Added/updated tests - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
co-authored by
Jacob Shandling
parent
6d1de32713
commit
ebf1a2d8f5
@@ -0,0 +1 @@
|
||||
Added "0 items" description on empty software tables for UI consistency
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
ISoftwareVersionsResponse,
|
||||
ISoftwareVersionResponse,
|
||||
} from "services/entities/software";
|
||||
import { IOSVersionsResponse } from "../services/entities/operating_systems";
|
||||
import { IOperatingSystemVersion } from "../interfaces/operating_system";
|
||||
|
||||
const DEFAULT_SOFTWARE_MOCK: ISoftware = {
|
||||
hosts_count: 1,
|
||||
@@ -93,12 +95,48 @@ const DEFAULT_SOFTWARE_VERSIONS_RESPONSE_MOCK: ISoftwareVersionsResponse = {
|
||||
},
|
||||
};
|
||||
|
||||
export const createMockSoftwareVersionsReponse = (
|
||||
export const createMockSoftwareVersionsResponse = (
|
||||
overrides?: Partial<ISoftwareVersionsResponse>
|
||||
): ISoftwareVersionsResponse => {
|
||||
return { ...DEFAULT_SOFTWARE_VERSIONS_RESPONSE_MOCK, ...overrides };
|
||||
};
|
||||
|
||||
const DEFAULT_OS_VERSION_MOCK = {
|
||||
os_version_id: 1,
|
||||
name: "macOS 14.6.1",
|
||||
name_only: "macOS",
|
||||
version: "14.6.1",
|
||||
platform: "darwin",
|
||||
hosts_count: 42,
|
||||
generated_cpes: [],
|
||||
vulnerabilities: [],
|
||||
};
|
||||
|
||||
export const createMockOSVersion = (
|
||||
overrides?: Partial<IOperatingSystemVersion>
|
||||
): IOperatingSystemVersion => {
|
||||
return {
|
||||
...DEFAULT_OS_VERSION_MOCK,
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULT_OS_VERSIONS_RESPONSE_MOCK: IOSVersionsResponse = {
|
||||
counts_updated_at: "2020-01-01T00:00:00.000Z",
|
||||
count: 1,
|
||||
os_versions: [createMockOSVersion()],
|
||||
meta: {
|
||||
has_next_results: false,
|
||||
has_previous_results: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const createMockOSVersionsResponse = (
|
||||
overrides?: Partial<IOSVersionsResponse>
|
||||
): IOSVersionsResponse => {
|
||||
return { ...DEFAULT_OS_VERSIONS_RESPONSE_MOCK, ...overrides };
|
||||
};
|
||||
|
||||
const DEFAULT_APP_STORE_APP_MOCK: IAppStoreApp = {
|
||||
name: "test app",
|
||||
app_store_id: 1,
|
||||
@@ -208,7 +246,7 @@ const DEFAULT_SOFTWARE_TITLES_RESPONSE_MOCK: ISoftwareTitlesResponse = {
|
||||
},
|
||||
};
|
||||
|
||||
export const createMockSoftwareTitlesReponse = (
|
||||
export const createMockSoftwareTitlesResponse = (
|
||||
overrides?: Partial<ISoftwareTitlesResponse>
|
||||
): ISoftwareTitlesResponse => {
|
||||
return { ...DEFAULT_SOFTWARE_TITLES_RESPONSE_MOCK, ...overrides };
|
||||
|
||||
@@ -4,7 +4,7 @@ import { render, screen } from "@testing-library/react";
|
||||
import OSTable from "./OSTable";
|
||||
|
||||
describe("Dashboard OS table", () => {
|
||||
it("renders data normally when present", async () => {
|
||||
it("renders data normally when present", () => {
|
||||
render(
|
||||
<OSTable
|
||||
currentTeamId={undefined}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { createMockOSVersionsResponse } from "__mocks__/softwareMock";
|
||||
|
||||
import SoftwareOSTable from "./SoftwareOSTable";
|
||||
|
||||
// TODO: figure out how to mock the router properly.
|
||||
const mockRouter = {
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
goBack: jest.fn(),
|
||||
goForward: jest.fn(),
|
||||
go: jest.fn(),
|
||||
setRouteLeaveHook: jest.fn(),
|
||||
isActive: jest.fn(),
|
||||
createHref: jest.fn(),
|
||||
createPath: jest.fn(),
|
||||
};
|
||||
|
||||
describe("Software operating systems table", () => {
|
||||
it("Renders the page-wide disabled state when software inventory is disabled", async () => {
|
||||
render(
|
||||
<SoftwareOSTable
|
||||
router={mockRouter}
|
||||
isSoftwareEnabled={false} // Set to false
|
||||
data={createMockOSVersionsResponse({
|
||||
count: 0,
|
||||
os_versions: [],
|
||||
})}
|
||||
perPage={20}
|
||||
orderDirection="asc"
|
||||
orderKey="hosts_count"
|
||||
currentPage={0}
|
||||
teamId={1}
|
||||
isLoading={false}
|
||||
resetPageIndex={false}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Software inventory disabled")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("Renders the page-wide empty state when no software is present", () => {
|
||||
render(
|
||||
<SoftwareOSTable
|
||||
router={mockRouter}
|
||||
isSoftwareEnabled
|
||||
data={createMockOSVersionsResponse({
|
||||
count: 0,
|
||||
os_versions: [],
|
||||
})}
|
||||
perPage={20}
|
||||
orderDirection="asc"
|
||||
orderKey="hosts_count"
|
||||
currentPage={0}
|
||||
teamId={1}
|
||||
isLoading={false}
|
||||
resetPageIndex={false}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText("No operating systems detected")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("0 items")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Search")).toBeNull();
|
||||
expect(screen.queryByText("Updated")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -129,7 +129,7 @@ const SoftwareOSTable = ({
|
||||
};
|
||||
|
||||
const renderSoftwareCount = () => {
|
||||
if (!data?.os_versions || !data?.count) return null;
|
||||
if (!data) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -4,8 +4,8 @@ import { createCustomRenderer } from "test/test-utils";
|
||||
|
||||
import createMockUser from "__mocks__/userMock";
|
||||
import {
|
||||
createMockSoftwareTitlesReponse,
|
||||
createMockSoftwareVersionsReponse,
|
||||
createMockSoftwareTitlesResponse,
|
||||
createMockSoftwareVersionsResponse,
|
||||
} from "__mocks__/softwareMock";
|
||||
import { noop } from "lodash";
|
||||
|
||||
@@ -25,7 +25,7 @@ const mockRouter = {
|
||||
};
|
||||
|
||||
describe("Software table", () => {
|
||||
it("Renders the page-wide disabled state when software inventory is disabled", async () => {
|
||||
it("Renders the page-wide disabled state when software inventory is disabled", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -40,7 +40,7 @@ describe("Software table", () => {
|
||||
router={mockRouter}
|
||||
isSoftwareEnabled={false} // Set to false
|
||||
showVersions={false}
|
||||
data={createMockSoftwareTitlesReponse({
|
||||
data={createMockSoftwareTitlesResponse({
|
||||
counts_updated_at: null,
|
||||
software_titles: [],
|
||||
})}
|
||||
@@ -68,7 +68,7 @@ describe("Software table", () => {
|
||||
expect(screen.queryByText("Vulnerability")).toBeNull();
|
||||
});
|
||||
|
||||
it("Renders the page-wide empty state when no software are present", async () => {
|
||||
it("Renders the page-wide empty state when no software are present", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -83,7 +83,8 @@ describe("Software table", () => {
|
||||
router={mockRouter}
|
||||
isSoftwareEnabled
|
||||
showVersions={false}
|
||||
data={createMockSoftwareTitlesReponse({
|
||||
data={createMockSoftwareTitlesResponse({
|
||||
count: 0,
|
||||
counts_updated_at: null,
|
||||
software_titles: [],
|
||||
})}
|
||||
@@ -111,11 +112,12 @@ describe("Software table", () => {
|
||||
expect(
|
||||
screen.getByText("Expecting to see software? Check back later.")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("0 items")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Search")).toBeNull();
|
||||
expect(screen.queryByText("Updated")).toBeNull();
|
||||
});
|
||||
|
||||
it("Renders the page-wide empty state when search query does not exist but versions toggle is applied", async () => {
|
||||
it("Renders the page-wide empty state when search query does not exist but versions toggle is applied", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -130,7 +132,7 @@ describe("Software table", () => {
|
||||
router={mockRouter}
|
||||
isSoftwareEnabled
|
||||
showVersions // Versions toggle applied
|
||||
data={createMockSoftwareVersionsReponse({
|
||||
data={createMockSoftwareVersionsResponse({
|
||||
counts_updated_at: null,
|
||||
software: [],
|
||||
})}
|
||||
@@ -160,7 +162,7 @@ describe("Software table", () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("Renders the empty search state when search query does not exist but dropdown is applied", async () => {
|
||||
it("Renders the empty search state when search query does not exist but dropdown is applied", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -175,7 +177,7 @@ describe("Software table", () => {
|
||||
router={mockRouter}
|
||||
isSoftwareEnabled
|
||||
showVersions={false}
|
||||
data={createMockSoftwareTitlesReponse({
|
||||
data={createMockSoftwareTitlesResponse({
|
||||
counts_updated_at: null,
|
||||
software_titles: [],
|
||||
})}
|
||||
@@ -209,7 +211,7 @@ describe("Software table", () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("Renders the empty search state when search query does not exist but vulnerability filter is applied", async () => {
|
||||
it("Renders the empty search state when search query does not exist but vulnerability filter is applied", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -224,7 +226,7 @@ describe("Software table", () => {
|
||||
router={mockRouter}
|
||||
isSoftwareEnabled
|
||||
showVersions={false}
|
||||
data={createMockSoftwareTitlesReponse({
|
||||
data={createMockSoftwareTitlesResponse({
|
||||
counts_updated_at: null,
|
||||
software_titles: [],
|
||||
})}
|
||||
|
||||
@@ -269,7 +269,7 @@ const SoftwareTable = ({
|
||||
};
|
||||
|
||||
const renderSoftwareCount = () => {
|
||||
if (!tableData || !data?.count) return null;
|
||||
if (!tableData || !data) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
+8
-7
@@ -24,7 +24,7 @@ const mockRouter = {
|
||||
};
|
||||
|
||||
describe("Software Vulnerabilities table", () => {
|
||||
it("Renders the page-wide disabled state when software inventory is disabled", async () => {
|
||||
it("Renders the page-wide disabled state when software inventory is disabled", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -62,7 +62,7 @@ describe("Software Vulnerabilities table", () => {
|
||||
});
|
||||
|
||||
// TODO: Reinstate collecting software view
|
||||
it("Renders the page-wide empty state when no software vulnerabilities are present", async () => {
|
||||
it("Renders the page-wide empty state when no software vulnerabilities are present", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -97,13 +97,14 @@ describe("Software Vulnerabilities table", () => {
|
||||
);
|
||||
|
||||
expect(screen.getByText("No vulnerabilities detected")).toBeInTheDocument();
|
||||
expect(screen.getByText("0 items")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("Expecting to see vulnerabilities? Check back later.")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText("Vulnerability")).toBeNull();
|
||||
});
|
||||
|
||||
it("Renders the empty search state when search query does not exist but exploited vulnerabilities dropdown is applied", async () => {
|
||||
it("Renders the empty search state when search query does not exist but exploited vulnerabilities dropdown is applied", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -145,7 +146,7 @@ describe("Software Vulnerabilities table", () => {
|
||||
expect(screen.queryByText("Vulnerability")).toBeNull();
|
||||
});
|
||||
|
||||
it("Renders the invalid CVE empty search state when search query wrapped in quotes is invalid with no results", async () => {
|
||||
it("Renders the invalid CVE empty search state when search query wrapped in quotes is invalid with no results", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -188,7 +189,7 @@ describe("Software Vulnerabilities table", () => {
|
||||
expect(screen.queryByText("Vulnerability")).toBeNull();
|
||||
});
|
||||
|
||||
it("Renders the valid known CVE empty search state when search query wrapped in quotes is valid known CVE with no results", async () => {
|
||||
it("Renders the valid known CVE empty search state when search query wrapped in quotes is valid known CVE with no results", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -233,7 +234,7 @@ describe("Software Vulnerabilities table", () => {
|
||||
expect(screen.queryByText("Vulnerability")).toBeNull();
|
||||
});
|
||||
|
||||
it("Renders the valid unknown CVE empty search state when search query wrapped in quotes is not a valid known CVE with no results", async () => {
|
||||
it("Renders the valid unknown CVE empty search state when search query wrapped in quotes is not a valid known CVE with no results", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
@@ -276,7 +277,7 @@ describe("Software Vulnerabilities table", () => {
|
||||
expect(screen.queryByText("Vulnerability")).toBeNull();
|
||||
});
|
||||
|
||||
it("Renders premium columns", async () => {
|
||||
it("Renders premium columns", () => {
|
||||
const render = createCustomRenderer({
|
||||
context: {
|
||||
app: {
|
||||
|
||||
+2
-2
@@ -197,9 +197,9 @@ const SoftwareVulnerabilitiesTable = ({
|
||||
};
|
||||
|
||||
const renderVulnerabilityCount = () => {
|
||||
if (!data?.count) return null;
|
||||
if (!data) return null;
|
||||
|
||||
const count = data.count;
|
||||
const count = data?.count;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user