Fleet UI: [small released bug] Fix time cell with tooltip component to not have a date restriction, add unit tests for edge cases (#13824)

This commit is contained in:
RachelElysia
2023-09-11 09:45:15 -04:00
committed by GitHub
parent 11e019eb20
commit ab263bb76a
7 changed files with 121 additions and 58 deletions
+1
View File
@@ -0,0 +1 @@
- Fleet UI: Fix software vulnerabilities time ago column for old CVEs
@@ -0,0 +1,36 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import { renderWithSetup } from "test/test-utils";
import HumanTimeDiffWithDateTip from "./HumanTimeDiffWithDateTip";
const EMPTY_STRING = "Unavailable";
const INVALID_STRING = "Invalid date";
describe("HumanTimeDiffWithDateTip - component", () => {
it("renders tooltip on hover", async () => {
const { user } = renderWithSetup(
<HumanTimeDiffWithDateTip timeString="2015-12-06T10:30:00Z" />
);
// Note: number of years varies over time
await user.hover(screen.getByText(/years ago/i));
// Note: hour of day varies for timezones
expect(screen.getByText(/12\/6\/2015/i)).toBeInTheDocument();
});
it("handles empty string error", async () => {
render(<HumanTimeDiffWithDateTip timeString="" />);
const emptyStringText = screen.getByText(EMPTY_STRING);
expect(emptyStringText).toBeInTheDocument();
});
it("handles invalid string error", async () => {
render(<HumanTimeDiffWithDateTip timeString="foobar" />);
const invalidStringText = screen.getByText(INVALID_STRING);
expect(invalidStringText).toBeInTheDocument();
});
});
@@ -1,40 +1,44 @@
import React from "react";
import { uniqueId } from "lodash";
import { humanHostLastSeen } from "utilities/helpers";
import { humanLastSeen, internationalTimeFormat } from "utilities/helpers";
import ReactTooltip from "react-tooltip";
import intlFormat from "date-fns/intlFormat";
export default ({ timeString }: { timeString: string }): JSX.Element => {
interface IHumanTimeDiffWithDateTip {
timeString: string;
}
/** Returns "Unavailable" if date is empty string or "Unavailable"
* Returns "Invalid date" if date is invalid */
export default ({ timeString }: IHumanTimeDiffWithDateTip): JSX.Element => {
const id = uniqueId();
return timeString === "Unavailable" ? (
<span>Unavailable</span>
) : (
<>
<span className={"date-tooltip"} data-tip data-for={`tooltip-${id}`}>
{humanHostLastSeen(timeString)}
</span>
<ReactTooltip
className="date-tooltip-text"
place="top"
type="dark"
effect="solid"
id={`tooltip-${id}`}
backgroundColor="#3e4771"
>
{intlFormat(
new Date(timeString),
{
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
},
{ locale: window.navigator.languages[0] }
)}
</ReactTooltip>
</>
);
if (timeString === "Unavailable" || timeString === "") {
return <span>Unavailable</span>;
}
try {
return (
<>
<span className={"date-tooltip"} data-tip data-for={`tooltip-${id}`}>
{humanLastSeen(timeString)}
</span>
<ReactTooltip
className="date-tooltip-text"
place="top"
type="dark"
effect="solid"
id={`tooltip-${id}`}
backgroundColor="#3e4771"
>
{internationalTimeFormat(new Date(timeString))}
</ReactTooltip>
</>
);
} catch (e) {
if (e instanceof RangeError) {
return <span>Invalid date</span>;
}
return <span>Unavailable</span>;
}
};
@@ -1,16 +1,18 @@
import React from "react";
import { find, lowerCase, noop } from "lodash";
import { intlFormat, formatDistanceToNowStrict } from "date-fns";
import { formatDistanceToNowStrict } from "date-fns";
import { ActivityType, IActivity, IActivityDetails } from "interfaces/activity";
import { addGravatarUrlToResource } from "utilities/helpers";
import {
addGravatarUrlToResource,
internationalTimeFormat,
} from "utilities/helpers";
import { DEFAULT_GRAVATAR_LINK } from "utilities/constants";
import Avatar from "components/Avatar";
import Button from "components/buttons/Button";
import Icon from "components/Icon";
import ReactTooltip from "react-tooltip";
import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip";
import { act } from "react-dom/test-utils";
const baseClass = "activity-item";
@@ -705,18 +707,7 @@ const ActivityItem = ({
id={`activity-${activity.id}`}
backgroundColor="#3e4771"
>
{intlFormat(
activityCreatedAt,
{
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
},
{ locale: window.navigator.languages[0] }
)}
{internationalTimeFormat(activityCreatedAt)}
</ReactTooltip>
</p>
</div>
@@ -50,7 +50,6 @@ const Vulnerabilities = ({
return (
<div className="section section--vulnerabilities">
<p className="section__header">Vulnerabilities</p>
{software?.vulnerabilities?.length ? (
<>
{software && (
+3
View File
@@ -39,6 +39,9 @@ export const FREQUENCY_DROPDOWN_OPTIONS = [
export const GITHUB_NEW_ISSUE_LINK =
"https://github.com/fleetdm/fleet/issues/new?assignees=&labels=bug%2C%3Areproduce&template=bug-report.md";
/** July 28, 2016 is the date of the initial commit to fleet/fleet. */
export const INITIAL_FLEET_DATE = "2016-07-28T00:00:00Z";
export const LOGGING_TYPE_OPTIONS = [
{ label: "Snapshot", value: "snapshot" },
{ label: "Differential", value: "differential" },
+38 -9
View File
@@ -15,9 +15,10 @@ import { buildQueryStringFromParams } from "utilities/url";
import md5 from "js-md5";
import {
formatDistanceToNow,
isAfter,
intervalToDuration,
formatDuration,
intlFormat,
intervalToDuration,
isAfter,
} from "date-fns";
import yaml from "js-yaml";
@@ -44,6 +45,7 @@ import {
DEFAULT_GRAVATAR_LINK_FALLBACK,
DEFAULT_GRAVATAR_LINK_DARK,
DEFAULT_GRAVATAR_LINK_DARK_FALLBACK,
INITIAL_FLEET_DATE,
PLATFORM_LABEL_DISPLAY_TYPES,
} from "utilities/constants";
import { IScheduledQueryStats } from "interfaces/scheduled_query_stats";
@@ -586,7 +588,7 @@ export const humanHostLastRestart = (
!detailUpdatedAt ||
!uptime ||
detailUpdatedAt === DEFAULT_EMPTY_CELL_VALUE ||
detailUpdatedAt < "2016-07-28T00:00:00Z" ||
detailUpdatedAt < INITIAL_FLEET_DATE ||
typeof uptime !== "number"
) {
return "Unavailable";
@@ -613,7 +615,7 @@ export const humanHostLastRestart = (
};
export const humanHostLastSeen = (lastSeen: string): string => {
if (!lastSeen || lastSeen < "2016-07-28T00:00:00Z") {
if (!lastSeen || lastSeen < INITIAL_FLEET_DATE) {
return "Never";
}
if (lastSeen === "Unavailable") {
@@ -623,7 +625,7 @@ export const humanHostLastSeen = (lastSeen: string): string => {
};
export const humanHostEnrolled = (enrolled: string): string => {
if (!enrolled || enrolled < "2016-07-28T00:00:00Z") {
if (!enrolled || enrolled < INITIAL_FLEET_DATE) {
return "Never";
}
return formatDistanceToNow(new Date(enrolled), { addSuffix: true });
@@ -636,8 +638,7 @@ export const humanHostMemory = (bytes: number): string => {
export const humanHostDetailUpdated = (detailUpdated?: string): string => {
// Handles the case when a host has checked in to Fleet but
// its details haven't been updated.
// July 28, 2016 is the date of the initial commit to fleet/fleet.
if (!detailUpdated || detailUpdated < "2016-07-28T00:00:00Z") {
if (!detailUpdated || detailUpdated < INITIAL_FLEET_DATE) {
return "unavailable";
}
try {
@@ -647,6 +648,33 @@ export const humanHostDetailUpdated = (detailUpdated?: string): string => {
}
};
/** Unlike humanHost helper functions, there are no Fleet-related date restrictions */
export const humanLastSeen = (lastSeen: string): string => {
if (!lastSeen) {
return "Never";
}
if (lastSeen === "Unavailable") {
return "Unavailable";
}
return formatDistanceToNow(new Date(lastSeen), { addSuffix: true });
};
export const internationalTimeFormat = (date: number | Date): string => {
return intlFormat(
date,
{
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
},
{ locale: window.navigator.languages[0] }
);
};
const MAC_WINDOWS_DISK_ENCRYPTION_MESSAGES = {
darwin: {
enabled:
@@ -687,8 +715,7 @@ export const hostTeamName = (teamName: string | null): string => {
export const humanQueryLastRun = (lastRun: string): string => {
// Handles the case when a query has never been ran.
// July 28, 2016 is the date of the initial commit to fleet/fleet.
if (!lastRun || lastRun < "2016-07-28T00:00:00Z") {
if (!lastRun || lastRun < INITIAL_FLEET_DATE) {
return "Has not run";
}
@@ -893,6 +920,8 @@ export default {
humanHostEnrolled,
humanHostMemory,
humanHostDetailUpdated,
humanLastSeen,
internationalTimeFormat,
getHostDiskEncryptionTooltipMessage,
hostTeamName,
humanQueryLastRun,