Files
paseo/packages/app/src/utils/time.test.ts
T
Mohamed Boudra 37bb168204 Polish agent stream spacing, alignment, and metadata
- Centralize horizontal alignment: StreamItemWrapper / contentWrapper own
  the chat rail (paddingHorizontal: 8). Stream items stop setting their
  own outer horizontal padding. Copy icons get negative margins so the
  visible icon sits flush with the rail edges.

- Unify the inline working indicator and assistant turn footer into a
  shared slot (minHeight: 24, paddingBottom: 24). No layout shift on
  streaming -> idle, and both gain breathing room above the composer
  and the next user bubble.

- Equalize the gap on both sides of an assistant message adjacent to a
  tool call (20px each). Drop assistantMessageStylesheet.containerSpacing,
  which was a redundant marginBottom that only fired in one direction
  and duplicated getGapBetween's responsibility.

- Scale up the chevron via transform (size stays 12, viewBox padding no
  longer dominates), iconBadge dimensions unchanged. Tool icon and
  chevron now read as similar visual weight.

- Shared STREAM_METADATA_FONT_SIZE (13) for user message timestamp,
  assistant footer duration/timestamp, and the live elapsed timer.

- formatDuration: one rule for static and live displays - 0-10s as
  X.Xs, 10-60s as Xs, minutes/hours integers only. Drops the `mode`
  parameter.

- formatMessageTimestamp: explicitly carry the resolved hourCycle so
  the runtime applies an OS-level 24h preference when the platform
  exposes it (e.g. AppleICUForce24HourTime on macOS).
2026-05-15 13:30:57 +07:00

60 lines
2.1 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { formatDuration, formatMessageTimestamp } from "./time";
describe("formatDuration", () => {
it("renders 0-10s with one decimal", () => {
expect(formatDuration(0)).toBe("0.0s");
expect(formatDuration(5_600)).toBe("5.6s");
expect(formatDuration(9_900)).toBe("9.9s");
});
it("renders 10s-60s as whole seconds", () => {
expect(formatDuration(10_400)).toBe("10s");
expect(formatDuration(12_340)).toBe("12s");
expect(formatDuration(47_000)).toBe("47s");
});
it("renders minutes and remainder seconds without decimals", () => {
expect(formatDuration(75_230)).toBe("1m 15s");
expect(formatDuration(132_000)).toBe("2m 12s");
expect(formatDuration(120_000)).toBe("2m");
});
it("renders hours and remainder minutes without decimals", () => {
expect(formatDuration(3_900_000)).toBe("1h 5m");
expect(formatDuration(3_600_000)).toBe("1h");
});
it("guards against negative and NaN", () => {
expect(formatDuration(-1)).toBe("0.0s");
expect(formatDuration(Number.NaN)).toBe("0.0s");
});
});
describe("formatMessageTimestamp", () => {
it("shows only time for same-day timestamps", () => {
const now = new Date(2026, 4, 14, 17, 30);
const date = new Date(2026, 4, 14, 12, 23);
const formatted = formatMessageTimestamp(date, now);
expect(formatted).toMatch(/12:23/);
expect(formatted).not.toMatch(/Thursday|Wednesday/);
});
it("includes weekday for timestamps within the last 6 days", () => {
// 2026-05-14 is a Thursday. 2026-05-11 is a Monday.
const now = new Date(2026, 4, 14, 17, 30);
const date = new Date(2026, 4, 11, 22, 12);
const formatted = formatMessageTimestamp(date, now);
expect(formatted).toMatch(/Monday/);
expect(formatted).toMatch(/10:12 PM|22:12/);
});
it("includes full date for older timestamps", () => {
const now = new Date(2026, 4, 14, 17, 30);
const date = new Date(2026, 3, 1, 9, 5);
const formatted = formatMessageTimestamp(date, now);
expect(formatted).toMatch(/Apr|April/);
expect(formatted).toMatch(/2026/);
});
});