authorgravatar for 269035359+t3-code[bot]@users.noreply.github.comt3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com> 2026-08-16 10:06:38+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-16 10:06:38+02:00
log3583cd27dccda1b35fe80d9411a95a7217cc8176
treec74afae1579278f99e783f3fe9f8f9396cd221ce
parent27732293373fbb081a966b437ae022afe77db16b
signature Signed by PGP key B5690EEEBB952194

test: favor behavior over implementation details (#7157)


9 files changed, 9 insertions(+), 87 deletions(-)

apps/mobile/src/features/terminal/terminalLaunchContext.test.ts+1-9
...@@ -2,7 +2,6 @@ import { describe, expect, it } from "vite-plus/test";...@@ -2,7 +2,6 @@ import { describe, expect, it } from "vite-plus/test";
2import { EnvironmentId, ThreadId } from "@t3tools/contracts";2import { EnvironmentId, ThreadId } from "@t3tools/contracts";
33
4import {4import {
5 peekPendingTerminalLaunch,
6 resolvePreferredThreadWorktreePath,5 resolvePreferredThreadWorktreePath,
7 resolveTerminalOpenLocation,6 resolveTerminalOpenLocation,
8 stagePendingTerminalLaunch,7 stagePendingTerminalLaunch,
...@@ -82,19 +81,13 @@ describe("pending terminal launches", () => {...@@ -82,19 +81,13 @@ describe("pending terminal launches", () => {
82 },81 },
83 });82 });
8483
85 expect(peekPendingTerminalLaunch(target)).toEqual({
86 cwd: "/repo/worktrees/feature",
87 worktreePath: "/repo/worktrees/feature",
88 env: { FOO: "bar" },
89 initialInput: "pnpm dev\r",
90 });
91 expect(takePendingTerminalLaunch(target)).toEqual({84 expect(takePendingTerminalLaunch(target)).toEqual({
92 cwd: "/repo/worktrees/feature",85 cwd: "/repo/worktrees/feature",
93 worktreePath: "/repo/worktrees/feature",86 worktreePath: "/repo/worktrees/feature",
94 env: { FOO: "bar" },87 env: { FOO: "bar" },
95 initialInput: "pnpm dev\r",88 initialInput: "pnpm dev\r",
96 });89 });
97 expect(peekPendingTerminalLaunch(target)).toBeNull();90 expect(takePendingTerminalLaunch(target)).toBeNull();
98 });91 });
9992
100 it("keeps pending launches isolated per terminal target", () => {93 it("keeps pending launches isolated per terminal target", () => {
...@@ -118,7 +111,6 @@ describe("pending terminal launches", () => {...@@ -118,7 +111,6 @@ describe("pending terminal launches", () => {
118 },111 },
119 });112 });
120113
121 expect(peekPendingTerminalLaunch(otherTarget)).toBeNull();
122 expect(takePendingTerminalLaunch(otherTarget)).toBeNull();114 expect(takePendingTerminalLaunch(otherTarget)).toBeNull();
123 expect(takePendingTerminalLaunch(primaryTarget)).toEqual({115 expect(takePendingTerminalLaunch(primaryTarget)).toEqual({
124 cwd: "/repo/root",116 cwd: "/repo/root",
apps/mobile/src/features/terminal/terminalLaunchContext.ts-6
...@@ -36,12 +36,6 @@ export function stagePendingTerminalLaunch(input: {...@@ -36,12 +36,6 @@ export function stagePendingTerminalLaunch(input: {
36 });36 });
37}37}
3838
39export function peekPendingTerminalLaunch(
40 target: PendingTerminalLaunchTarget,
41): PendingTerminalLaunch | null {
42 return pendingTerminalLaunches.get(pendingTerminalLaunchKey(target)) ?? null;
43}
44
45export function takePendingTerminalLaunch(39export function takePendingTerminalLaunch(
46 target: PendingTerminalLaunchTarget,40 target: PendingTerminalLaunchTarget,
47): PendingTerminalLaunch | null {41): PendingTerminalLaunch | null {
apps/mobile/src/features/terminal/threadTerminalPanelModel.test.ts deleted-40
...@@ -1,40 +0,0 @@
1import { EnvironmentId, ThreadId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import {
5 buildThreadTerminalAttachInput,
6 threadTerminalSubscriptionKey,
7 type ThreadTerminalSubscriptionIdentity,
8} from "./threadTerminalPanelModel";
9
10const identity: ThreadTerminalSubscriptionIdentity = {
11 environmentId: EnvironmentId.make("env-1"),
12 threadId: ThreadId.make("thread-1"),
13 terminalId: "default",
14 cwd: "/repo",
15 worktreePath: "/repo",
16};
17
18describe("threadTerminalSubscriptionKey", () => {
19 it("does not include mutable terminal dimensions", () => {
20 const initialAttach = buildThreadTerminalAttachInput(identity, { cols: 80, rows: 24 });
21 const resizedAttach = buildThreadTerminalAttachInput(identity, { cols: 132, rows: 40 });
22
23 expect(initialAttach).not.toEqual(resizedAttach);
24 expect(threadTerminalSubscriptionKey({ ...identity, ...initialAttach })).toBe(
25 threadTerminalSubscriptionKey({ ...identity, ...resizedAttach }),
26 );
27 });
28
29 it.each([
30 ["environment", { environmentId: EnvironmentId.make("env-2") }],
31 ["thread", { threadId: ThreadId.make("thread-2") }],
32 ["terminal", { terminalId: "term-2" }],
33 ["cwd", { cwd: "/repo/packages/app" }],
34 ["worktree", { worktreePath: "/repo/worktrees/feature" }],
35 ])("changes when the %s identity changes", (_label, update) => {
36 expect(threadTerminalSubscriptionKey({ ...identity, ...update })).not.toBe(
37 threadTerminalSubscriptionKey(identity),
38 );
39 });
40});
apps/mobile/src/features/terminal/threadTerminalPanelModel.ts-12
...@@ -13,18 +13,6 @@ export interface TerminalGridSize {...@@ -13,18 +13,6 @@ export interface TerminalGridSize {
13 readonly rows: number;13 readonly rows: number;
14}14}
1515
16export function threadTerminalSubscriptionKey(
17 identity: ThreadTerminalSubscriptionIdentity,
18): string {
19 return JSON.stringify([
20 identity.environmentId,
21 identity.threadId,
22 identity.terminalId,
23 identity.cwd,
24 identity.worktreePath,
25 ]);
26}
27
28export function buildThreadTerminalAttachInput(16export function buildThreadTerminalAttachInput(
29 identity: ThreadTerminalSubscriptionIdentity,17 identity: ThreadTerminalSubscriptionIdentity,
30 gridSize: TerminalGridSize,18 gridSize: TerminalGridSize,
apps/web/src/components/chat/MessagesTimeline.test.tsx+1-7
...@@ -134,7 +134,6 @@ function matchMedia() {...@@ -134,7 +134,6 @@ function matchMedia() {
134}134}
135135
136let MessagesTimeline: typeof import("./MessagesTimeline").MessagesTimeline;136let MessagesTimeline: typeof import("./MessagesTimeline").MessagesTimeline;
137let toolCallExpandedBodyClassName: typeof import("./MessagesTimeline").toolCallExpandedBodyClassName;
138137
139beforeAll(async () => {138beforeAll(async () => {
140 const classList = {139 const classList = {
...@@ -168,7 +167,7 @@ beforeAll(async () => {...@@ -168,7 +167,7 @@ beforeAll(async () => {
168 },167 },
169 });168 });
170169
171 ({ MessagesTimeline, toolCallExpandedBodyClassName } = await import("./MessagesTimeline"));170 ({ MessagesTimeline } = await import("./MessagesTimeline"));
172}, 30_000);171}, 30_000);
173172
174const ACTIVE_THREAD_ENVIRONMENT_ID = EnvironmentId.make("environment-local");173const ACTIVE_THREAD_ENVIRONMENT_ID = EnvironmentId.make("environment-local");
...@@ -238,11 +237,6 @@ function buildAssistantTimelineEntry(text: string) {...@@ -238,11 +237,6 @@ function buildAssistantTimelineEntry(text: string) {
238}237}
239238
240describe("MessagesTimeline", () => {239describe("MessagesTimeline", () => {
241 it("sizes expanded tool details with the configured code font size", () => {
242 expect(toolCallExpandedBodyClassName).toContain("var(--font-size-code");
243 expect(toolCallExpandedBodyClassName).not.toContain("text-[11px]");
244 });
245
246 it("uses the larger leading inset only when the top fade is enabled", () => {240 it("uses the larger leading inset only when the top fade is enabled", () => {
247 const timelineEntries = [buildUserTimelineEntry("Hello")];241 const timelineEntries = [buildUserTimelineEntry("Hello")];
248242
apps/web/src/components/chat/MessagesTimeline.tsx+1-1
...@@ -2063,7 +2063,7 @@ function buildToolCallExpandedBody(...@@ -2063,7 +2063,7 @@ function buildToolCallExpandedBody(
2063 return blocks.length > 0 ? blocks.join("\n\n") : null;2063 return blocks.length > 0 ? blocks.join("\n\n") : null;
2064}2064}
20652065
2066export const toolCallExpandedBodyClassName =2066const toolCallExpandedBodyClassName =
2067 "max-h-64 cursor-text overflow-auto whitespace-pre-wrap break-words font-mono text-secondary-label text-[length:var(--font-size-code,0.6875rem)] leading-relaxed select-text";2067 "max-h-64 cursor-text overflow-auto whitespace-pre-wrap break-words font-mono text-secondary-label text-[length:var(--font-size-code,0.6875rem)] leading-relaxed select-text";
20682068
2069function workEntryIconName(workEntry: TimelineWorkEntry): WorkEntryIconName {2069function workEntryIconName(workEntry: TimelineWorkEntry): WorkEntryIconName {
apps/web/src/components/composerFooterLayout.test.ts+5-8
...@@ -3,7 +3,6 @@ import { describe, expect, it } from "vite-plus/test";...@@ -3,7 +3,6 @@ import { describe, expect, it } from "vite-plus/test";
3import {3import {
4 COMPOSER_FOOTER_COMPACT_BREAKPOINT_PX,4 COMPOSER_FOOTER_COMPACT_BREAKPOINT_PX,
5 COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX,5 COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX,
6 COMPOSER_PRIMARY_ACTIONS_COMPACT_BREAKPOINT_PX,
7 shouldUseCompactComposerPrimaryActions,6 shouldUseCompactComposerPrimaryActions,
8 shouldUseCompactComposerFooter,7 shouldUseCompactComposerFooter,
9} from "./composerFooterLayout";8} from "./composerFooterLayout";
...@@ -38,16 +37,14 @@ describe("shouldUseCompactComposerFooter", () => {...@@ -38,16 +37,14 @@ describe("shouldUseCompactComposerFooter", () => {
3837
39describe("shouldUseCompactComposerPrimaryActions", () => {38describe("shouldUseCompactComposerPrimaryActions", () => {
40 it("matches the wide footer breakpoint", () => {39 it("matches the wide footer breakpoint", () => {
41 expect(COMPOSER_PRIMARY_ACTIONS_COMPACT_BREAKPOINT_PX).toBe(
42 COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX,
43 );
44 expect(40 expect(
45 shouldUseCompactComposerPrimaryActions(COMPOSER_PRIMARY_ACTIONS_COMPACT_BREAKPOINT_PX - 1, {41 shouldUseCompactComposerPrimaryActions(
46 hasWideActions: true,42 COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX - 1,
47 }),43 { hasWideActions: true },
44 ),
48 ).toBe(true);45 ).toBe(true);
49 expect(46 expect(
50 shouldUseCompactComposerPrimaryActions(COMPOSER_PRIMARY_ACTIONS_COMPACT_BREAKPOINT_PX, {47 shouldUseCompactComposerPrimaryActions(COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX, {
51 hasWideActions: true,48 hasWideActions: true,
52 }),49 }),
53 ).toBe(false);50 ).toBe(false);
apps/web/src/components/composerFooterLayout.ts+1-3
...@@ -1,7 +1,5 @@...@@ -1,7 +1,5 @@
1export const COMPOSER_FOOTER_COMPACT_BREAKPOINT_PX = 620;1export const COMPOSER_FOOTER_COMPACT_BREAKPOINT_PX = 620;
2export const COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX = 780;2export const COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX = 780;
3export const COMPOSER_PRIMARY_ACTIONS_COMPACT_BREAKPOINT_PX =
4 COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX;
53
6export function shouldUseCompactComposerFooter(4export function shouldUseCompactComposerFooter(
7 width: number | null,5 width: number | null,
...@@ -20,5 +18,5 @@ export function shouldUseCompactComposerPrimaryActions(...@@ -20,5 +18,5 @@ export function shouldUseCompactComposerPrimaryActions(
20 if (!options?.hasWideActions) {18 if (!options?.hasWideActions) {
21 return false;19 return false;
22 }20 }
23 return width !== null && width < COMPOSER_PRIMARY_ACTIONS_COMPACT_BREAKPOINT_PX;21 return width !== null && width < COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX;
24}22}
packages/shared/src/terminalLabels.test.ts-1
...@@ -34,7 +34,6 @@ describe("resolveTerminalSessionLabel", () => {...@@ -34,7 +34,6 @@ describe("resolveTerminalSessionLabel", () => {
34describe("nextTerminalId", () => {34describe("nextTerminalId", () => {
35 it("allocates term-1 when no terminals are listed yet", () => {35 it("allocates term-1 when no terminals are listed yet", () => {
36 expect(nextTerminalId([])).toBe(DEFAULT_TERMINAL_ID);36 expect(nextTerminalId([])).toBe(DEFAULT_TERMINAL_ID);
37 expect(nextTerminalId([])).toBe("term-1");
38 });37 });
3938
40 it("allocates term-2 when only term-1 exists", () => {39 it("allocates term-2 when only term-1 exists", () => {