summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/utils
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/utils')
-rw-r--r--packages/cli/src/ui/utils/formatters.test.ts72
-rw-r--r--packages/cli/src/ui/utils/formatters.ts47
2 files changed, 119 insertions, 0 deletions
diff --git a/packages/cli/src/ui/utils/formatters.test.ts b/packages/cli/src/ui/utils/formatters.test.ts
new file mode 100644
index 00000000..cb3d1324
--- /dev/null
+++ b/packages/cli/src/ui/utils/formatters.test.ts
@@ -0,0 +1,72 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { describe, it, expect } from 'vitest';
+import { formatDuration, formatMemoryUsage } from './formatters.js';
+
+describe('formatters', () => {
+ describe('formatMemoryUsage', () => {
+ it('should format bytes into KB', () => {
+ expect(formatMemoryUsage(12345)).toBe('12.1 KB');
+ });
+
+ it('should format bytes into MB', () => {
+ expect(formatMemoryUsage(12345678)).toBe('11.8 MB');
+ });
+
+ it('should format bytes into GB', () => {
+ expect(formatMemoryUsage(12345678901)).toBe('11.50 GB');
+ });
+ });
+
+ describe('formatDuration', () => {
+ it('should format milliseconds less than a second', () => {
+ expect(formatDuration(500)).toBe('500ms');
+ });
+
+ it('should format a duration of 0', () => {
+ expect(formatDuration(0)).toBe('0s');
+ });
+
+ it('should format an exact number of seconds', () => {
+ expect(formatDuration(5000)).toBe('5.0s');
+ });
+
+ it('should format a duration in seconds with one decimal place', () => {
+ expect(formatDuration(12345)).toBe('12.3s');
+ });
+
+ it('should format an exact number of minutes', () => {
+ expect(formatDuration(120000)).toBe('2m');
+ });
+
+ it('should format a duration in minutes and seconds', () => {
+ expect(formatDuration(123000)).toBe('2m 3s');
+ });
+
+ it('should format an exact number of hours', () => {
+ expect(formatDuration(3600000)).toBe('1h');
+ });
+
+ it('should format a duration in hours and seconds', () => {
+ expect(formatDuration(3605000)).toBe('1h 5s');
+ });
+
+ it('should format a duration in hours, minutes, and seconds', () => {
+ expect(formatDuration(3723000)).toBe('1h 2m 3s');
+ });
+
+ it('should handle large durations', () => {
+ expect(formatDuration(86400000 + 3600000 + 120000 + 1000)).toBe(
+ '25h 2m 1s',
+ );
+ });
+
+ it('should handle negative durations', () => {
+ expect(formatDuration(-100)).toBe('0s');
+ });
+ });
+});
diff --git a/packages/cli/src/ui/utils/formatters.ts b/packages/cli/src/ui/utils/formatters.ts
index ab02160e..82a78109 100644
--- a/packages/cli/src/ui/utils/formatters.ts
+++ b/packages/cli/src/ui/utils/formatters.ts
@@ -14,3 +14,50 @@ export const formatMemoryUsage = (bytes: number): string => {
}
return `${gb.toFixed(2)} GB`;
};
+
+/**
+ * Formats a duration in milliseconds into a concise, human-readable string (e.g., "1h 5s").
+ * It omits any time units that are zero.
+ * @param milliseconds The duration in milliseconds.
+ * @returns A formatted string representing the duration.
+ */
+export const formatDuration = (milliseconds: number): string => {
+ if (milliseconds <= 0) {
+ return '0s';
+ }
+
+ if (milliseconds < 1000) {
+ return `${milliseconds}ms`;
+ }
+
+ const totalSeconds = milliseconds / 1000;
+
+ if (totalSeconds < 60) {
+ return `${totalSeconds.toFixed(1)}s`;
+ }
+
+ const hours = Math.floor(totalSeconds / 3600);
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
+ const seconds = Math.floor(totalSeconds % 60);
+
+ const parts: string[] = [];
+
+ if (hours > 0) {
+ parts.push(`${hours}h`);
+ }
+ if (minutes > 0) {
+ parts.push(`${minutes}m`);
+ }
+ if (seconds > 0) {
+ parts.push(`${seconds}s`);
+ }
+
+ // If all parts are zero (e.g., exactly 1 hour), return the largest unit.
+ if (parts.length === 0) {
+ if (hours > 0) return `${hours}h`;
+ if (minutes > 0) return `${minutes}m`;
+ return `${seconds}s`;
+ }
+
+ return parts.join(' ');
+};