Files
roboco/panel/src/components/tg/__tests__/tg-inbox-tab.test.tsx
T
3c5ee46347 feat(tg): Mini App V6 — premium overhaul (#609)
* feat(tg): Mini App V6 — premium overhaul (design system, Chat parity, Metrics drilldown, CEO verbs)

Design system: native type with tabular-numeral heroes (mono demoted to
the wordmark), borderless elevated cards, floating dock, Telegram
window-chrome painting via the theme bridge; Inbox moves behind a header
bell with humanized notifications (UUIDs resolve to task names).

Chat: honest Mine/Fleet split — participant-scoped CEO threads with real
unread counts and mark-read, watched fleet threads with reply-as-CEO on
task-linked conversations (watch-only otherwise), markdown transcripts,
live pulse flashes, and a pinned Secretary live chat on the panel's SSE
session runtime.

Metrics: new tab with period-segmented spend hero, by-agent/team/model
breakdowns, delivery + efficiency health, and a per-agent drilldown over
usage time-series (agent_slug) + member scorecard.

Board: tg-native grouped pipeline replacing the MobileTaskBoard wrapper;
task sheet gains the CEO decide verbs (approve / request changes /
unblock).

Security: /api/dashboard router now require_panel_token-gated at router
level (mirrors /api/usage), closing unauthenticated metrics exposure.

* fix(tg): restore Share Tech Mono brand voice, Phosphor icon set, borderless avatars

The mono returns as the numeral/brand voice (.tg-display — heroes, stat
values, wordmark) while labels stay native sentence case. The hand-drawn
duotone glyphs and lucide feature icons are replaced by Phosphor (MIT):
duotone at rest via an IconContext at the shell, filled weight on the
dock's active tab; row glyph maps (board statuses, inbox kinds, approval
kinds, quick actions) all move over. Team avatar tiles drop their borders
— tint-only squircles.

* fix(tg): fleet avatar strip breathes — spaced tiles instead of overlap

* polish(tg): taste-skill audit pass — em-dash purge, one icon family, separator rationing

Applied the design-taste audit against the cockpit: every em-dash in
visible UI copy rewritten (periods/commas/colons), the remaining lucide
chrome (carets, arrows, send, close, spinners) moved to Phosphor so the
tg tree ships one icon family (send is the native paper-plane, carets
bold), the hand-rolled chevron SVG deleted, and metadata lines rationed
to a single middle-dot separator.

* polish(tg): pipeline chip strip scrolls without a visible scrollbar

* fix(tests): metrics observability fixture uses a relative timestamp

The hardcoded _T0 (2026-06-20) aged out of the service's 30-day window
exactly 30 days later, detonating the suite on every branch. Two days
back from now() stays inside every window (30d metrics, 7d scorecards)
permanently.

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 17:29:22 +02:00

88 lines
2.4 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { TgInboxTab } from "../tg-inbox-tab";
import { NotificationPriority, NotificationType } from "@/types";
const { ackMock, items } = vi.hoisted(() => ({
ackMock: vi.fn(),
items: { current: [] as Array<Record<string, unknown>> },
}));
// No task in the shared index — every UUID falls back to the #id8 handle.
vi.mock("@/hooks/use-tasks", () => ({
useTasks: () => ({ data: [] }),
}));
vi.mock("@/hooks/use-notifications", () => ({
notificationKeys: { all: ["notifications"] },
useNotifications: () => ({
data: { items: items.current },
isLoading: false,
}),
useAcknowledgeNotification: () => ({ mutate: ackMock, isPending: false }),
}));
function notification(overrides: Record<string, unknown>) {
return {
id: "n1",
type: NotificationType.BROADCAST,
priority: NotificationPriority.NORMAL,
from_agent: "main-pm",
to_agents: ["ceo"],
subject: "A notification",
body: "body",
requires_ack: false,
is_acknowledged: false,
is_fully_acknowledged: false,
is_read: false,
related_task_id: null,
related_message_ids: [],
timestamp: new Date().toISOString(),
expires_at: null,
acked_by: [],
acked_at: {},
...overrides,
};
}
function renderTab() {
const client = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
render(
<QueryClientProvider client={client}>
<TgInboxTab />
</QueryClientProvider>,
);
}
describe("TgInboxTab", () => {
it("humanizes an unresolved uuid subject to a short id handle", () => {
items.current = [
notification({
id: "n1",
subject: "Task 123e4567-e89b-12d3-a456-426614174000 needs review",
}),
];
renderTab();
expect(screen.getByText(/#123e4567/)).toBeInTheDocument();
});
it("splits a bracketed prefix into its own chip", () => {
items.current = [
notification({
id: "n1",
subject: "[strategy engine] weekly digest ready",
}),
];
renderTab();
expect(screen.getByText("Strategy engine")).toBeInTheDocument();
expect(screen.getByText("Weekly digest ready")).toBeInTheDocument();
});
it("shows inbox zero when there is nothing", () => {
items.current = [];
renderTab();
expect(screen.getByText(/inbox zero/i)).toBeInTheDocument();
});
});