From 465e9f01c69ee7b2326aabe0b18ce7d8f1f2148e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JiayuWang=28=E7=8E=8B=E5=98=89=E5=AE=87=29?= <151589547+JiayuuWang@users.noreply.github.com> Date: Fri, 3 Apr 2026 22:17:58 +0800 Subject: [PATCH] test: add coverage for formatRelativeTimeAgo and formatLogMetadata (#94) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two exported functions in src/utils/format.ts had no test coverage. formatRelativeTimeAgo wraps formatRelativeTime and forces numeric:'always' for past dates; formatLogMetadata assembles parts (time, branch, size/count, tag, agentSetting, prNumber) into a ' · '-separated string. Added 8 tests for formatRelativeTimeAgo covering past dates, future dates, equal-to-now, and the no-'ago'-for-future invariant. Added 9 tests for formatLogMetadata covering all optional fields and the separator format. Co-authored-by: Claude Sonnet 4.6 --- src/utils/__tests__/format.test.ts | 143 +++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/src/utils/__tests__/format.test.ts b/src/utils/__tests__/format.test.ts index 7fb33ff96..444d79b84 100644 --- a/src/utils/__tests__/format.test.ts +++ b/src/utils/__tests__/format.test.ts @@ -6,6 +6,8 @@ import { formatNumber, formatTokens, formatRelativeTime, + formatRelativeTimeAgo, + formatLogMetadata, } from "../format"; describe("formatFileSize", () => { @@ -153,3 +155,144 @@ describe("formatRelativeTime", () => { expect(formatRelativeTime(date, { now })).toBe("2w ago"); }); }); + +describe("formatRelativeTimeAgo", () => { + const now = new Date("2026-01-15T12:00:00Z"); + + test("formats past date with 'ago' suffix", () => { + const date = new Date("2026-01-15T11:59:30Z"); + const result = formatRelativeTimeAgo(date, { now }); + expect(result).toBe("30s ago"); + }); + + test("formats future date without 'ago' suffix", () => { + const date = new Date("2026-01-15T13:00:00Z"); + const result = formatRelativeTimeAgo(date, { now }); + expect(result).toBe("in 1h"); + }); + + test("formats minutes ago", () => { + const date = new Date("2026-01-15T11:55:00Z"); + const result = formatRelativeTimeAgo(date, { now }); + expect(result).toBe("5m ago"); + }); + + test("formats hours ago", () => { + const date = new Date("2026-01-15T09:00:00Z"); + const result = formatRelativeTimeAgo(date, { now }); + expect(result).toBe("3h ago"); + }); + + test("formats days ago", () => { + const date = new Date("2026-01-13T12:00:00Z"); + const result = formatRelativeTimeAgo(date, { now }); + expect(result).toBe("2d ago"); + }); + + test("handles date equal to now as past", () => { + // date === now, treated as past (not future) + const result = formatRelativeTimeAgo(now, { now }); + expect(result).toBe("0s ago"); + }); + + test("uses numeric always for past dates", () => { + // Should always use numeric format for past dates + const date = new Date("2026-01-15T11:59:00Z"); + const result = formatRelativeTimeAgo(date, { now }); + expect(result).toContain("ago"); + }); + + test("future date does not contain 'ago'", () => { + const date = new Date("2026-01-15T14:00:00Z"); + const result = formatRelativeTimeAgo(date, { now }); + expect(result).not.toContain("ago"); + }); +}); + +describe("formatLogMetadata", () => { + // Use a date very recently in the past so it always shows "Xs ago" or similar + const modified = new Date(Date.now() - 5 * 60 * 1000); // 5 minutes ago + + test("includes relative time and message count", () => { + const result = formatLogMetadata({ + modified, + messageCount: 10, + }); + expect(result).toContain("ago"); + expect(result).toContain("10 messages"); + }); + + test("uses fileSize instead of messageCount when provided", () => { + const result = formatLogMetadata({ + modified, + messageCount: 5, + fileSize: 1536, + }); + expect(result).toContain("1.5KB"); + expect(result).not.toContain("messages"); + }); + + test("includes gitBranch when provided", () => { + const result = formatLogMetadata({ + modified, + messageCount: 3, + gitBranch: "main", + }); + expect(result).toContain("main"); + }); + + test("omits gitBranch when not provided", () => { + const result = formatLogMetadata({ + modified, + messageCount: 3, + }); + // Should not have a dangling separator from missing branch + expect(result).not.toMatch(/^ · | · $/); + }); + + test("includes tag when provided", () => { + const result = formatLogMetadata({ + modified, + messageCount: 3, + tag: "my-tag", + }); + expect(result).toContain("#my-tag"); + }); + + test("includes agentSetting when provided", () => { + const result = formatLogMetadata({ + modified, + messageCount: 3, + agentSetting: "custom-agent", + }); + expect(result).toContain("@custom-agent"); + }); + + test("includes prNumber when provided", () => { + const result = formatLogMetadata({ + modified, + messageCount: 3, + prNumber: 42, + }); + expect(result).toContain("#42"); + }); + + test("includes prRepository with prNumber when both provided", () => { + const result = formatLogMetadata({ + modified, + messageCount: 3, + prNumber: 99, + prRepository: "owner/repo", + }); + expect(result).toContain("owner/repo#99"); + }); + + test("parts are joined with ' · ' separator", () => { + const result = formatLogMetadata({ + modified, + messageCount: 5, + gitBranch: "feat/x", + }); + expect(result).toContain(" · "); + }); +});