mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-07 07:42:13 +03:00
* fix(dashboard): resolve broken Card import breaking next build (base-red from #6061) CoolingConnectionsPanel imported `Card` from `@/components/ui/card`, a path that does not exist in this repo (there is no shadcn-style `src/components/ui/`). The PR->release fast-gates do not run `next build`, so the broken import slipped in and `next build` failed with: Module not found: Can't resolve '@/components/ui/card' Fix: the <Card> here was only a styled container, so replace it with a <div> carrying the equivalent Tailwind classes (border/bg/padding + rounded-card shadow-sm). Also normalize the file from CRLF to LF (it shipped with CRLF). Adds a vitest/jsdom regression test (tests/unit/ui/CoolingConnectionsPanel.test.tsx) that fails-without-fix (Vite: 'Failed to resolve import @/components/ui/card') and passes with it, plus renders/empty-state coverage. Rule #18. * fix(dashboard): stop client CoolingConnectionsPanel dragging server DB barrel into browser bundle Second base-red from #6061, surfaced once the broken Card import was fixed: ./node_modules/ioredis/built/connectors/StandaloneConnector.js Module not found: Can't resolve 'net' Import trace: ioredis <- rateLimiter.ts <- apiKeys.ts <- @/lib/localDb <- CoolingConnectionsPanel.tsx (a "use client" component) The client panel imported `formatResetCountdown` from `@/lib/localDb` — the server-side DB re-export barrel — which transitively pulls better-sqlite3/ioredis (node:net) into the browser bundle. That violates the CLAUDE.md rule 'never barrel-import from localDb'. `formatResetCountdown` is a pure date-formatting function, so move its implementation to the client-safe `@/shared/utils/formatting` (alongside formatTime/formatDuration) and re-export it from db/providers/rateLimit.ts for the existing server callers + barrel. The panel now imports it directly from the shared util — no server code in the client bundle. Tests (Rule #18): - tests/unit/format-reset-countdown.test.ts (node:test, blocking test:unit) — pure-function coverage: null/past/invalid, s, m+s, h+m, ISO string. - tests/unit/ui/CoolingConnectionsPanel.test.tsx mock updated to the new module.
39 lines
1.5 KiB
TypeScript
39 lines
1.5 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { formatResetCountdown } from "@/shared/utils/formatting";
|
|
|
|
// Guards both the pure formatting behavior and the client-safe home of this
|
|
// helper: it MUST live in @/shared/utils/formatting (not db/providers/rateLimit)
|
|
// so client components can import it without pulling the server-only DB barrel
|
|
// (better-sqlite3/ioredis → node:net) into the browser bundle. See PR #6155.
|
|
|
|
test("returns null for missing / past / invalid reset times", () => {
|
|
assert.equal(formatResetCountdown(null), null);
|
|
assert.equal(formatResetCountdown(undefined), null);
|
|
assert.equal(formatResetCountdown(0), null);
|
|
assert.equal(formatResetCountdown("not-a-date"), null);
|
|
assert.equal(formatResetCountdown(Date.now() - 60_000), null);
|
|
});
|
|
|
|
test("formats seconds-only remaining", () => {
|
|
const out = formatResetCountdown(Date.now() + 30_000);
|
|
assert.match(out ?? "", /^\d+s$/);
|
|
});
|
|
|
|
test("formats minutes + seconds", () => {
|
|
const out = formatResetCountdown(Date.now() + 5 * 60_000 + 30_000);
|
|
assert.match(out ?? "", /^\d+m \d+s$/);
|
|
});
|
|
|
|
test("formats hours + minutes", () => {
|
|
const out = formatResetCountdown(Date.now() + 2 * 3_600_000 + 35 * 60_000);
|
|
assert.match(out ?? "", /^\d+h \d+m$/);
|
|
assert.ok((out ?? "").startsWith("2h"));
|
|
});
|
|
|
|
test("accepts an ISO string as well as an epoch number", () => {
|
|
const iso = new Date(Date.now() + 90_000).toISOString();
|
|
assert.match(formatResetCountdown(iso) ?? "", /^(1m \d+s|\d+s)$/);
|
|
});
|