Skip to content

Commit ce6d41b

Browse files
committed
🤖 ci: add Tasks settings Storybook story
Extend the Storybook ORPC mock with config.getConfig/saveConfig so the Tasks settings section can render, and add an App/Settings story that opens the Tasks section with deterministic values. Signed-off-by: Thomas Kosiewski <tk@coder.com> --- _Generated with `codex cli` • Model: `gpt-5.2` • Thinking: `xhigh`_ <!-- mux-attribution: model=gpt-5.2 thinking=xhigh --> Change-Id: Iccd181b2879ca9ecca1955e00e79f44007f74992
1 parent 9dc544f commit ce6d41b

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

.storybook/mocks/orpc.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import type { ProjectConfig } from "@/node/config";
99
import type { WorkspaceChatMessage, ProvidersConfigMap } from "@/common/orpc/types";
1010
import type { ChatStats } from "@/common/types/chatStats";
1111
import { DEFAULT_RUNTIME_CONFIG } from "@/common/constants/workspace";
12+
import {
13+
DEFAULT_TASK_SETTINGS,
14+
normalizeTaskSettings,
15+
type TaskSettings,
16+
} from "@/common/types/tasks";
1217
import { createAsyncMessageQueue } from "@/common/utils/asyncMessageQueue";
1318

1419
/** Session usage data structure matching SessionUsageFileSchema */
@@ -42,6 +47,8 @@ export interface MockSessionUsage {
4247
export interface MockORPCClientOptions {
4348
projects?: Map<string, ProjectConfig>;
4449
workspaces?: FrontendWorkspaceMetadata[];
50+
/** Initial task settings for config.getConfig (e.g., Settings → Tasks section) */
51+
taskSettings?: Partial<TaskSettings>;
4552
/** Per-workspace chat callback. Return messages to emit, or use the callback for streaming. */
4653
onChat?: (workspaceId: string, emit: (msg: WorkspaceChatMessage) => void) => (() => void) | void;
4754
/** Mock for executeBash per workspace */
@@ -115,9 +122,11 @@ export function createMockORPCClient(options: MockORPCClientOptions = {}): APICl
115122
mcpServers = new Map(),
116123
mcpOverrides = new Map(),
117124
mcpTestResults = new Map(),
125+
taskSettings: initialTaskSettings,
118126
} = options;
119127

120128
const workspaceMap = new Map(workspaces.map((w) => [w.id, w]));
129+
let taskSettings = normalizeTaskSettings(initialTaskSettings ?? DEFAULT_TASK_SETTINGS);
121130

122131
const mockStats: ChatStats = {
123132
consumers: [],
@@ -140,6 +149,13 @@ export function createMockORPCClient(options: MockORPCClientOptions = {}): APICl
140149
getSshHost: async () => null,
141150
setSshHost: async () => undefined,
142151
},
152+
config: {
153+
getConfig: async () => ({ taskSettings }),
154+
saveConfig: async (input: { taskSettings: unknown }) => {
155+
taskSettings = normalizeTaskSettings(input.taskSettings);
156+
return undefined;
157+
},
158+
},
143159
providers: {
144160
list: async () => providersList,
145161
getConfig: async () => providersConfig,

src/browser/stories/App.settings.stories.tsx

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import { appMeta, AppWithMocks, type AppStory } from "./meta.js";
1414
import { createWorkspace, groupWorkspacesByProject } from "./mockFactory";
1515
import { selectWorkspace } from "./storyHelpers";
1616
import { createMockORPCClient } from "../../../.storybook/mocks/orpc";
17-
import { within, userEvent } from "@storybook/test";
17+
import { within, userEvent, waitFor } from "@storybook/test";
1818
import { getExperimentKey, EXPERIMENT_IDS } from "@/common/constants/experiments";
19+
import type { TaskSettings } from "@/common/types/tasks";
1920

2021
export default {
2122
...appMeta,
@@ -30,6 +31,7 @@ export default {
3031
function setupSettingsStory(options: {
3132
providersConfig?: Record<string, { apiKeySet: boolean; baseUrl?: string; models?: string[] }>;
3233
providersList?: string[];
34+
taskSettings?: Partial<TaskSettings>;
3335
/** Pre-set experiment states in localStorage before render */
3436
experiments?: Partial<Record<string, boolean>>;
3537
}): APIClient {
@@ -50,6 +52,7 @@ function setupSettingsStory(options: {
5052
workspaces,
5153
providersConfig: options.providersConfig ?? {},
5254
providersList: options.providersList ?? ["anthropic", "openai", "xai"],
55+
taskSettings: options.taskSettings,
5356
});
5457
}
5558

@@ -89,6 +92,47 @@ export const General: AppStory = {
8992
},
9093
};
9194

95+
/** Tasks settings section - task parallelism and nesting controls */
96+
export const Tasks: AppStory = {
97+
render: () => (
98+
<AppWithMocks
99+
setup={() =>
100+
setupSettingsStory({
101+
taskSettings: { maxParallelAgentTasks: 2, maxTaskNestingDepth: 4 },
102+
})
103+
}
104+
/>
105+
),
106+
play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
107+
await openSettingsToSection(canvasElement, "tasks");
108+
109+
const body = within(canvasElement.ownerDocument.body);
110+
111+
await body.findByText(/Max Parallel Agent Tasks/i);
112+
await body.findByText(/Max Task Nesting Depth/i);
113+
114+
const inputs = await body.findAllByRole("spinbutton");
115+
if (inputs.length !== 2) {
116+
throw new Error(`Expected 2 task settings inputs, got ${inputs.length}`);
117+
}
118+
119+
await waitFor(() => {
120+
const maxParallelAgentTasks = (inputs[0] as HTMLInputElement).value;
121+
const maxTaskNestingDepth = (inputs[1] as HTMLInputElement).value;
122+
if (maxParallelAgentTasks !== "2") {
123+
throw new Error(
124+
`Expected maxParallelAgentTasks=2, got ${JSON.stringify(maxParallelAgentTasks)}`
125+
);
126+
}
127+
if (maxTaskNestingDepth !== "4") {
128+
throw new Error(
129+
`Expected maxTaskNestingDepth=4, got ${JSON.stringify(maxTaskNestingDepth)}`
130+
);
131+
}
132+
});
133+
},
134+
};
135+
92136
/** Providers section - no providers configured */
93137
export const ProvidersEmpty: AppStory = {
94138
render: () => <AppWithMocks setup={() => setupSettingsStory({ providersConfig: {} })} />,

0 commit comments

Comments
 (0)