<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #46965 Relative "time ago" timestamps switched to months at ~30 days, so a timestamp 45 days ago read "about 2 months ago" (even 89 days showed "3 months ago"). This centralizes the day/month cutoff in a new `timeAgo` helper and routes existing call sites through it, so anything under 90 days is shown in days. # Checklist for submitter - [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. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Relative “time ago” timestamps now keep values in **days** for items under **90 days**, switching to **months** later for more accurate wording. * Improved consistency of relative time labels across status modals, activity feeds, host details, and management screens (including “last updated,” “uploaded,” and “added” text). * **Tests** * Added/updated coverage for the shared relative-time cutoff and formatting behavior to prevent regressions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
115 lines
3.8 KiB
TypeScript
115 lines
3.8 KiB
TypeScript
import {
|
|
dateAgo,
|
|
monthDayYearFormat,
|
|
addedFromNow,
|
|
uploadedFromNow,
|
|
monthDayTimeFormat,
|
|
timeAgo,
|
|
} from ".";
|
|
|
|
describe("date_format utilities", () => {
|
|
beforeEach(() => {
|
|
jest.useFakeTimers().setSystemTime(new Date("2026-06-15T12:00:00Z"));
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
describe("uploadedFromNow util", () => {
|
|
it("returns an user friendly uploaded message", () => {
|
|
const currentDate = new Date();
|
|
currentDate.setDate(currentDate.getDate() - 2);
|
|
const twoDaysAgo = currentDate.toISOString();
|
|
|
|
expect(uploadedFromNow(twoDaysAgo)).toEqual("Uploaded 2 days ago");
|
|
});
|
|
});
|
|
|
|
describe("addedFromNow util", () => {
|
|
it("returns an user friendly added message", () => {
|
|
const currentDate = new Date();
|
|
currentDate.setDate(currentDate.getDate() - 2);
|
|
const twoDaysAgo = currentDate.toISOString();
|
|
|
|
expect(addedFromNow(twoDaysAgo)).toEqual("Added 2 days ago");
|
|
});
|
|
});
|
|
|
|
describe("monthDayYearFormat util", () => {
|
|
it("returns a date in the format of 'MonthName Date, Year' (e.g. January 01, 2024)", () => {
|
|
const date = "2024-11-29T00:00:00Z";
|
|
expect(monthDayYearFormat(date)).toEqual("November 29, 2024");
|
|
});
|
|
});
|
|
|
|
describe("dateAgo util", () => {
|
|
it("returns a user friendly date ago message from a date string", () => {
|
|
const currentDate = new Date();
|
|
currentDate.setDate(currentDate.getDate() - 2);
|
|
const twoDaysAgo = currentDate.toISOString();
|
|
|
|
expect(dateAgo(twoDaysAgo)).toEqual("2 days ago");
|
|
});
|
|
|
|
it("returns a user friendly date ago message from a Date object", () => {
|
|
const date = new Date();
|
|
date.setDate(date.getDate() - 2);
|
|
|
|
expect(dateAgo(date)).toEqual("2 days ago");
|
|
});
|
|
|
|
const daysAgo = (n: number) =>
|
|
new Date(Date.now() - n * 24 * 60 * 60 * 1000).toISOString();
|
|
|
|
it("uses days below the month threshold", () => {
|
|
expect(dateAgo(daysAgo(5))).toEqual("5 days ago");
|
|
expect(dateAgo(daysAgo(29))).toEqual("29 days ago");
|
|
expect(dateAgo(daysAgo(30))).toEqual("30 days ago");
|
|
expect(dateAgo(daysAgo(40))).toEqual("40 days ago");
|
|
expect(dateAgo(daysAgo(60))).toEqual("60 days ago");
|
|
expect(dateAgo(daysAgo(89))).toEqual("89 days ago");
|
|
});
|
|
|
|
it("uses months at or beyond 90 days", () => {
|
|
expect(dateAgo(daysAgo(90))).toEqual("3 months ago");
|
|
expect(dateAgo(daysAgo(100))).toEqual("3 months ago");
|
|
});
|
|
});
|
|
|
|
describe("timeAgo util", () => {
|
|
const daysAgo = (n: number) =>
|
|
new Date(Date.now() - n * 24 * 60 * 60 * 1000);
|
|
|
|
it("omits the `ago` suffix by default and adds it when requested", () => {
|
|
expect(timeAgo(daysAgo(40))).toEqual("40 days");
|
|
expect(timeAgo(daysAgo(40), { addSuffix: true })).toEqual("40 days ago");
|
|
expect(timeAgo(daysAgo(89), { addSuffix: true })).toEqual("89 days ago");
|
|
});
|
|
|
|
it("switches to months at 90 days", () => {
|
|
expect(timeAgo(daysAgo(90), { addSuffix: true })).toEqual("3 months ago");
|
|
});
|
|
|
|
// strict avoids the "about" prefix outside the day window.
|
|
it("supports the strict variant outside the window", () => {
|
|
expect(timeAgo(daysAgo(100), { addSuffix: true, strict: true })).toEqual(
|
|
"3 months ago"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("monthDayTimeFormat util", () => {
|
|
it("returns a formatted date string matching pattern 'Mon D, H:MM AM/PM'", () => {
|
|
const date = "2024-03-20T13:35:00Z";
|
|
const result = monthDayTimeFormat(date);
|
|
// Match pattern like "Mar 20, 1:35 PM" (exact time varies by timezone)
|
|
expect(result).toMatch(/^[A-Z][a-z]{2} \d{1,2}, \d{1,2}:\d{2} (AM|PM)$/);
|
|
});
|
|
|
|
it("returns an empty string for invalid dates", () => {
|
|
expect(monthDayTimeFormat("invalid-date")).toEqual("");
|
|
});
|
|
});
|
|
});
|