

















@@ -201,6 +201,34 @@ function setupBaseWizardState(config: OpenClawConfig = {}) {
201201});
202202}
203203204+function requireRecord(value: unknown, label: string): Record<string, unknown> {
205+expect(value, label).toBeTypeOf("object");
206+expect(value, label).not.toBeNull();
207+return value as Record<string, unknown>;
208+}
209+210+function requireWriteConfig(callIndex = 0) {
211+const call = mocks.writeConfigFile.mock.calls.at(callIndex);
212+expect(call, "writeConfigFile call").toBeDefined();
213+return requireRecord(call?.[0], "written config");
214+}
215+216+function getGateway(config: Record<string, unknown>) {
217+return requireRecord(config.gateway, "gateway config");
218+}
219+220+function getWebSearch(config: Record<string, unknown>) {
221+const tools = requireRecord(config.tools, "tools config");
222+const web = requireRecord(tools.web, "web config");
223+return requireRecord(web.search, "web search config");
224+}
225+226+function getPluginEntry(config: Record<string, unknown>, pluginId: string) {
227+const plugins = requireRecord(config.plugins, "plugins config");
228+const entries = requireRecord(plugins.entries, "plugin entries");
229+return requireRecord(entries[pluginId], `${pluginId} entry`);
230+}
231+204232function queueWizardPrompts(params: { select: string[]; confirm: boolean[]; text?: string }) {
205233const selectQueue = [...params.select];
206234const confirmQueue = [...params.confirm];
@@ -245,11 +273,7 @@ describe("runConfigureWizard", () => {
245273246274await runConfigureWizard({ command: "configure" }, createRuntime());
247275248-expect(mocks.writeConfigFile).toHaveBeenCalledWith(
249-expect.objectContaining({
250-gateway: expect.objectContaining({ mode: "local" }),
251-}),
252-);
276+expect(getGateway(requireWriteConfig()).mode).toBe("local");
253277});
254278it("keeps startup gateway hint probes bounded", async () => {
255279setupBaseWizardState({
@@ -268,19 +292,16 @@ describe("runConfigureWizard", () => {
268292269293await runConfigureWizard({ command: "configure" }, createRuntime());
270294271-expect(mocks.probeGatewayReachable).toHaveBeenCalledWith(
272-expect.objectContaining({
273-url: "ws://127.0.0.1:18789",
274-timeoutMs: 300,
275-}),
295+const probeRequests = mocks.probeGatewayReachable.mock.calls.map(([request]) =>
296+requireRecord(request, "probe request"),
276297);
277-expect(mocks.probeGatewayReachable).toHaveBeenCalledWith(
278-expect.objectContaining({
279-url: "wss://gateway.example.test",
280-token: "token",
281-timeoutMs: 300,
282-}),
298+const localProbe = probeRequests.find((request) => request.url === "ws://127.0.0.1:18789");
299+const remoteProbe = probeRequests.find(
300+(request) => request.url === "wss://gateway.example.test",
283301);
302+expect(localProbe?.timeoutMs).toBe(300);
303+expect(remoteProbe?.token).toBe("token");
304+expect(remoteProbe?.timeoutMs).toBe(300);
284305});
285306286307it("exits with code 1 when configure wizard is cancelled", async () => {
@@ -308,34 +329,17 @@ describe("runConfigureWizard", () => {
308329309330await runWebConfigureWizard();
310331311-expect(mocks.setupSearch).toHaveBeenCalledWith(
312-expect.objectContaining({
313-gateway: expect.objectContaining({ mode: "local" }),
314-}),
315-expect.anything(),
316-expect.anything(),
317-);
318-expect(mocks.writeConfigFile).toHaveBeenCalledWith(
319-expect.objectContaining({
320-tools: expect.objectContaining({
321-web: expect.objectContaining({
322-search: expect.objectContaining({
323-provider: "firecrawl",
324-enabled: true,
325-}),
326-}),
327-}),
328-plugins: expect.objectContaining({
329-entries: expect.objectContaining({
330-firecrawl: expect.objectContaining({
331-enabled: true,
332-config: expect.objectContaining({
333-webSearch: expect.objectContaining({ apiKey: "fc-entered-key" }),
334-}),
335-}),
336-}),
337-}),
338-}),
332+const setupConfig = requireRecord(mocks.setupSearch.mock.calls[0]?.[0], "setupSearch config");
333+expect(getGateway(setupConfig).mode).toBe("local");
334+const written = requireWriteConfig();
335+const search = getWebSearch(written);
336+expect(search.provider).toBe("firecrawl");
337+expect(search.enabled).toBe(true);
338+const firecrawl = getPluginEntry(written, "firecrawl");
339+expect(firecrawl.enabled).toBe(true);
340+const firecrawlConfig = requireRecord(firecrawl.config, "firecrawl config");
341+expect(requireRecord(firecrawlConfig.webSearch, "firecrawl web search").apiKey).toBe(
342+"fc-entered-key",
339343);
340344expect(mocks.setupSearch).toHaveBeenCalledOnce();
341345});
@@ -356,17 +360,7 @@ describe("runConfigureWizard", () => {
356360),
357361"Web search",
358362);
359-expect(mocks.writeConfigFile).toHaveBeenCalledWith(
360-expect.objectContaining({
361-tools: expect.objectContaining({
362-web: expect.objectContaining({
363-search: expect.objectContaining({
364-enabled: false,
365-}),
366-}),
367-}),
368-}),
369-);
363+expect(getWebSearch(requireWriteConfig()).enabled).toBe(false);
370364});
371365372366it("does not load managed search provider options when web search is disabled", async () => {
@@ -378,12 +372,12 @@ describe("runConfigureWizard", () => {
378372379373await runWebConfigureWizard();
380374381-expect(mocks.resolvePluginContributionOwners).toHaveBeenCalledWith(
382-expect.objectContaining({
383-contribution: "contracts",
384-matches: "webSearchProviders",
385-}),
375+const ownersRequest = requireRecord(
376+mocks.resolvePluginContributionOwners.mock.calls[0]?.[0],
377+"plugin owner request",
386378);
379+expect(ownersRequest.contribution).toBe("contracts");
380+expect(ownersRequest.matches).toBe("webSearchProviders");
387381expect(mocks.resolveSearchProviderOptions).not.toHaveBeenCalled();
388382expect(mocks.setupSearch).not.toHaveBeenCalled();
389383});
@@ -397,17 +391,12 @@ describe("runConfigureWizard", () => {
397391398392await runConfigureWizard({ command: "configure", sections: ["channels"] }, createRuntime());
399393400-expect(mocks.setupChannels).toHaveBeenCalledWith(
401-expect.objectContaining({
402-gateway: expect.objectContaining({ mode: "local" }),
403-}),
404-expect.anything(),
405-expect.anything(),
406-expect.objectContaining({
407-deferStatusUntilSelection: true,
408-skipStatusNote: true,
409-}),
410-);
394+const setupChannelsCall = mocks.setupChannels.mock.calls[0] as Array<unknown> | undefined;
395+const setupChannelsConfig = requireRecord(setupChannelsCall?.[0], "setupChannels config");
396+expect(getGateway(setupChannelsConfig).mode).toBe("local");
397+const setupChannelsOptions = requireRecord(setupChannelsCall?.[3], "setupChannels options");
398+expect(setupChannelsOptions.deferStatusUntilSelection).toBe(true);
399+expect(setupChannelsOptions.skipStatusNote).toBe(true);
411400});
412401413402it("still supports keyless web search providers through the shared setup flow", async () => {
@@ -459,21 +448,11 @@ describe("runConfigureWizard", () => {
459448460449await runWebConfigureWizard();
461450462-expect(mocks.writeConfigFile).toHaveBeenCalledWith(
463-expect.objectContaining({
464-tools: expect.objectContaining({
465-web: expect.objectContaining({
466-search: expect.objectContaining({
467-enabled: true,
468-openaiCodex: expect.objectContaining({
469-enabled: true,
470-mode: "cached",
471-}),
472-}),
473-}),
474-}),
475-}),
476-);
451+const search = getWebSearch(requireWriteConfig());
452+expect(search.enabled).toBe(true);
453+const codexSearch = requireRecord(search.openaiCodex, "Codex native search");
454+expect(codexSearch.enabled).toBe(true);
455+expect(codexSearch.mode).toBe("cached");
477456expect(mocks.setupSearch).not.toHaveBeenCalled();
478457});
479458@@ -506,21 +485,11 @@ describe("runConfigureWizard", () => {
506485507486await runWebConfigureWizard();
508487509-expect(mocks.writeConfigFile).toHaveBeenCalledWith(
510-expect.objectContaining({
511-tools: expect.objectContaining({
512-web: expect.objectContaining({
513-search: expect.objectContaining({
514-enabled: true,
515-openaiCodex: expect.objectContaining({
516-enabled: false,
517-mode: "live",
518-}),
519-}),
520-}),
521-}),
522-}),
523-);
488+const search = getWebSearch(requireWriteConfig());
489+expect(search.enabled).toBe(true);
490+const codexSearch = requireRecord(search.openaiCodex, "Codex native search");
491+expect(codexSearch.enabled).toBe(false);
492+expect(codexSearch.mode).toBe("live");
524493expect(mocks.setupSearch).toHaveBeenCalledOnce();
525494});
526495@@ -623,23 +592,13 @@ describe("runConfigureWizard", () => {
623592const retryCall = mocks.replaceConfigFile.mock.calls[1][0] as {
624593nextConfig: Record<string, unknown>;
625594};
626-expect(retryCall.nextConfig).toMatchObject({
627-agents: {
628-defaults: {
629-workspace: expect.stringContaining("/.openclaw/workspace"),
630-},
631-},
632-plugins: {
633-entries: {
634-"github-copilot": {
635-enabled: false,
636-config: {
637-region: "us-east-1",
638-accessToken: "plugin-wrote-this",
639-},
640-},
641-},
642-},
643-});
595+const agents = requireRecord(retryCall.nextConfig.agents, "agents config");
596+const defaults = requireRecord(agents.defaults, "agent defaults");
597+expect(String(defaults.workspace)).toContain("/.openclaw/workspace");
598+const githubCopilot = getPluginEntry(retryCall.nextConfig, "github-copilot");
599+expect(githubCopilot.enabled).toBe(false);
600+const pluginConfig = requireRecord(githubCopilot.config, "github-copilot config");
601+expect(pluginConfig.region).toBe("us-east-1");
602+expect(pluginConfig.accessToken).toBe("plugin-wrote-this");
644603});
645604});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。