From 5e3562ca651c32c7c82b81b26e0128897c22e1fd Mon Sep 17 00:00:00 2001 From: ndycode <405533+ndycode@users.noreply.github.com> Date: Sun, 22 Mar 2026 09:48:05 +0800 Subject: [PATCH 1/2] refactor: extract experimental settings entry wrapper --- .../experimental-settings-entry.ts | 114 ++++++++++++++++++ lib/codex-manager/settings-hub.ts | 4 +- test/experimental-settings-entry.test.ts | 42 +++++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 lib/codex-manager/experimental-settings-entry.ts create mode 100644 test/experimental-settings-entry.test.ts diff --git a/lib/codex-manager/experimental-settings-entry.ts b/lib/codex-manager/experimental-settings-entry.ts new file mode 100644 index 00000000..76f4e468 --- /dev/null +++ b/lib/codex-manager/experimental-settings-entry.ts @@ -0,0 +1,114 @@ +import type { PluginConfig } from "../types.js"; + +export async function promptExperimentalSettingsEntry(params: { + initialConfig: PluginConfig; + promptExperimentalSettingsMenu: (args: { + initialConfig: PluginConfig; + isInteractive: () => boolean; + ui: ReturnType; + cloneBackendPluginConfig: (config: PluginConfig) => PluginConfig; + select: ( + items: Array>, + options: Record, + ) => Promise; + getExperimentalSelectOptions: ( + ui: ReturnType, + help: string, + hotkeyMapper: (raw: string) => unknown, + ) => Record; + mapExperimentalMenuHotkey: (raw: string) => unknown; + mapExperimentalStatusHotkey: (raw: string) => unknown; + formatDashboardSettingState: (enabled: boolean) => string; + copy: Record; + input: NodeJS.ReadStream; + output: NodeJS.WriteStream; + runNamedBackupExport: (args: { + name: string; + }) => Promise<{ kind: string; path?: string; error?: unknown }>; + loadAccounts: () => Promise; + loadExperimentalSyncTarget: () => Promise; + planOcChatgptSync: (args: Record) => Promise; + applyOcChatgptSync: (args: Record) => Promise; + getTargetKind: (targetState: unknown) => string; + getTargetDestination: (targetState: unknown) => unknown; + getTargetDetection: (targetState: unknown) => unknown; + getTargetErrorMessage: (targetState: unknown) => string | null; + getPlanKind: (plan: unknown) => string; + getPlanBlockedReason: (plan: unknown) => string; + getPlanPreview: (plan: unknown) => { + toAdd: unknown[]; + toUpdate: unknown[]; + toSkip: unknown[]; + unchangedDestinationOnly: unknown[]; + activeSelectionBehavior: string; + }; + getAppliedLabel: (applied: unknown) => { label: string; color: string }; + }) => Promise; + isInteractive: () => boolean; + ui: ReturnType; + cloneBackendPluginConfig: (config: PluginConfig) => PluginConfig; + select: ( + items: Array>, + options: Record, + ) => Promise; + getExperimentalSelectOptions: ( + ui: ReturnType, + help: string, + hotkeyMapper: (raw: string) => unknown, + ) => Record; + mapExperimentalMenuHotkey: (raw: string) => unknown; + mapExperimentalStatusHotkey: (raw: string) => unknown; + formatDashboardSettingState: (enabled: boolean) => string; + copy: Record; + input: NodeJS.ReadStream; + output: NodeJS.WriteStream; + runNamedBackupExport: (args: { + name: string; + }) => Promise<{ kind: string; path?: string; error?: unknown }>; + loadAccounts: () => Promise; + loadExperimentalSyncTarget: () => Promise; + planOcChatgptSync: (args: Record) => Promise; + applyOcChatgptSync: (args: Record) => Promise; + getTargetKind: (targetState: unknown) => string; + getTargetDestination: (targetState: unknown) => unknown; + getTargetDetection: (targetState: unknown) => unknown; + getTargetErrorMessage: (targetState: unknown) => string | null; + getPlanKind: (plan: unknown) => string; + getPlanBlockedReason: (plan: unknown) => string; + getPlanPreview: (plan: unknown) => { + toAdd: unknown[]; + toUpdate: unknown[]; + toSkip: unknown[]; + unchangedDestinationOnly: unknown[]; + activeSelectionBehavior: string; + }; + getAppliedLabel: (applied: unknown) => { label: string; color: string }; +}): Promise { + return params.promptExperimentalSettingsMenu({ + initialConfig: params.initialConfig, + isInteractive: params.isInteractive, + ui: params.ui, + cloneBackendPluginConfig: params.cloneBackendPluginConfig, + select: params.select, + getExperimentalSelectOptions: params.getExperimentalSelectOptions, + mapExperimentalMenuHotkey: params.mapExperimentalMenuHotkey, + mapExperimentalStatusHotkey: params.mapExperimentalStatusHotkey, + formatDashboardSettingState: params.formatDashboardSettingState, + copy: params.copy, + input: params.input, + output: params.output, + runNamedBackupExport: params.runNamedBackupExport, + loadAccounts: params.loadAccounts, + loadExperimentalSyncTarget: params.loadExperimentalSyncTarget, + planOcChatgptSync: params.planOcChatgptSync, + applyOcChatgptSync: params.applyOcChatgptSync, + getTargetKind: params.getTargetKind, + getTargetDestination: params.getTargetDestination, + getTargetDetection: params.getTargetDetection, + getTargetErrorMessage: params.getTargetErrorMessage, + getPlanKind: params.getPlanKind, + getPlanBlockedReason: params.getPlanBlockedReason, + getPlanPreview: params.getPlanPreview, + getAppliedLabel: params.getAppliedLabel, + }); +} diff --git a/lib/codex-manager/settings-hub.ts b/lib/codex-manager/settings-hub.ts index a1baa02f..11d7628e 100644 --- a/lib/codex-manager/settings-hub.ts +++ b/lib/codex-manager/settings-hub.ts @@ -64,6 +64,7 @@ import { dashboardSettingsDataEqual, } from "./dashboard-settings-data.js"; import { configureDashboardSettingsEntry } from "./dashboard-settings-entry.js"; +import { promptExperimentalSettingsEntry } from "./experimental-settings-entry.js"; import { promptExperimentalSettingsMenu } from "./experimental-settings-prompt.js"; import { getExperimentalSelectOptions, @@ -663,8 +664,9 @@ async function loadExperimentalSyncTarget(): Promise< async function promptExperimentalSettings( initialConfig: PluginConfig, ): Promise { - return promptExperimentalSettingsMenu({ + return promptExperimentalSettingsEntry({ initialConfig, + promptExperimentalSettingsMenu: promptExperimentalSettingsMenu as never, isInteractive: () => input.isTTY && output.isTTY, ui: getUiRuntimeOptions(), cloneBackendPluginConfig, diff --git a/test/experimental-settings-entry.test.ts b/test/experimental-settings-entry.test.ts new file mode 100644 index 00000000..9936553c --- /dev/null +++ b/test/experimental-settings-entry.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it, vi } from "vitest"; +import { promptExperimentalSettingsEntry } from "../lib/codex-manager/experimental-settings-entry.js"; + +describe("experimental settings entry", () => { + it("passes all dependencies through to the experimental settings prompt helper", async () => { + const promptExperimentalSettingsMenu = vi.fn(async () => ({ + fetchTimeoutMs: 1000, + })); + + const result = await promptExperimentalSettingsEntry({ + initialConfig: { fetchTimeoutMs: 2000 }, + promptExperimentalSettingsMenu, + isInteractive: () => true, + ui: { theme: {} } as never, + cloneBackendPluginConfig: vi.fn((config) => config), + select: vi.fn(), + getExperimentalSelectOptions: vi.fn(() => ({})), + mapExperimentalMenuHotkey: vi.fn(), + mapExperimentalStatusHotkey: vi.fn(), + formatDashboardSettingState: vi.fn((enabled) => (enabled ? "on" : "off")), + copy: {}, + input: process.stdin, + output: process.stdout, + runNamedBackupExport: vi.fn(), + loadAccounts: vi.fn(), + loadExperimentalSyncTarget: vi.fn(), + planOcChatgptSync: vi.fn(), + applyOcChatgptSync: vi.fn(), + getTargetKind: vi.fn(), + getTargetDestination: vi.fn(), + getTargetDetection: vi.fn(), + getTargetErrorMessage: vi.fn(), + getPlanKind: vi.fn(), + getPlanBlockedReason: vi.fn(), + getPlanPreview: vi.fn(), + getAppliedLabel: vi.fn(), + }); + + expect(promptExperimentalSettingsMenu).toHaveBeenCalled(); + expect(result).toEqual({ fetchTimeoutMs: 1000 }); + }); +}); From 3e44d11d0eb6b8f627958ab4f525984f7820689a Mon Sep 17 00:00:00 2001 From: ndycode <405533+ndycode@users.noreply.github.com> Date: Sun, 22 Mar 2026 14:28:21 +0800 Subject: [PATCH 2/2] Preserve experimental settings entry types --- .../experimental-settings-entry.ts | 91 ++---------- .../experimental-settings-prompt.ts | 136 ++++++++++-------- lib/codex-manager/settings-hub.ts | 31 ++-- test/experimental-settings-entry.test.ts | 34 ++++- 4 files changed, 134 insertions(+), 158 deletions(-) diff --git a/lib/codex-manager/experimental-settings-entry.ts b/lib/codex-manager/experimental-settings-entry.ts index 76f4e468..306e0abb 100644 --- a/lib/codex-manager/experimental-settings-entry.ts +++ b/lib/codex-manager/experimental-settings-entry.ts @@ -1,89 +1,14 @@ import type { PluginConfig } from "../types.js"; +import type { ExperimentalSettingsPromptDeps } from "./experimental-settings-prompt.js"; -export async function promptExperimentalSettingsEntry(params: { - initialConfig: PluginConfig; - promptExperimentalSettingsMenu: (args: { +export async function promptExperimentalSettingsEntry( + params: { initialConfig: PluginConfig; - isInteractive: () => boolean; - ui: ReturnType; - cloneBackendPluginConfig: (config: PluginConfig) => PluginConfig; - select: ( - items: Array>, - options: Record, - ) => Promise; - getExperimentalSelectOptions: ( - ui: ReturnType, - help: string, - hotkeyMapper: (raw: string) => unknown, - ) => Record; - mapExperimentalMenuHotkey: (raw: string) => unknown; - mapExperimentalStatusHotkey: (raw: string) => unknown; - formatDashboardSettingState: (enabled: boolean) => string; - copy: Record; - input: NodeJS.ReadStream; - output: NodeJS.WriteStream; - runNamedBackupExport: (args: { - name: string; - }) => Promise<{ kind: string; path?: string; error?: unknown }>; - loadAccounts: () => Promise; - loadExperimentalSyncTarget: () => Promise; - planOcChatgptSync: (args: Record) => Promise; - applyOcChatgptSync: (args: Record) => Promise; - getTargetKind: (targetState: unknown) => string; - getTargetDestination: (targetState: unknown) => unknown; - getTargetDetection: (targetState: unknown) => unknown; - getTargetErrorMessage: (targetState: unknown) => string | null; - getPlanKind: (plan: unknown) => string; - getPlanBlockedReason: (plan: unknown) => string; - getPlanPreview: (plan: unknown) => { - toAdd: unknown[]; - toUpdate: unknown[]; - toSkip: unknown[]; - unchangedDestinationOnly: unknown[]; - activeSelectionBehavior: string; - }; - getAppliedLabel: (applied: unknown) => { label: string; color: string }; - }) => Promise; - isInteractive: () => boolean; - ui: ReturnType; - cloneBackendPluginConfig: (config: PluginConfig) => PluginConfig; - select: ( - items: Array>, - options: Record, - ) => Promise; - getExperimentalSelectOptions: ( - ui: ReturnType, - help: string, - hotkeyMapper: (raw: string) => unknown, - ) => Record; - mapExperimentalMenuHotkey: (raw: string) => unknown; - mapExperimentalStatusHotkey: (raw: string) => unknown; - formatDashboardSettingState: (enabled: boolean) => string; - copy: Record; - input: NodeJS.ReadStream; - output: NodeJS.WriteStream; - runNamedBackupExport: (args: { - name: string; - }) => Promise<{ kind: string; path?: string; error?: unknown }>; - loadAccounts: () => Promise; - loadExperimentalSyncTarget: () => Promise; - planOcChatgptSync: (args: Record) => Promise; - applyOcChatgptSync: (args: Record) => Promise; - getTargetKind: (targetState: unknown) => string; - getTargetDestination: (targetState: unknown) => unknown; - getTargetDetection: (targetState: unknown) => unknown; - getTargetErrorMessage: (targetState: unknown) => string | null; - getPlanKind: (plan: unknown) => string; - getPlanBlockedReason: (plan: unknown) => string; - getPlanPreview: (plan: unknown) => { - toAdd: unknown[]; - toUpdate: unknown[]; - toSkip: unknown[]; - unchangedDestinationOnly: unknown[]; - activeSelectionBehavior: string; - }; - getAppliedLabel: (applied: unknown) => { label: string; color: string }; -}): Promise { + promptExperimentalSettingsMenu: ( + args: ExperimentalSettingsPromptDeps, + ) => Promise; + } & ExperimentalSettingsPromptDeps, +): Promise { return params.promptExperimentalSettingsMenu({ initialConfig: params.initialConfig, isInteractive: params.isInteractive, diff --git a/lib/codex-manager/experimental-settings-prompt.ts b/lib/codex-manager/experimental-settings-prompt.ts index 345513f7..3f7c9110 100644 --- a/lib/codex-manager/experimental-settings-prompt.ts +++ b/lib/codex-manager/experimental-settings-prompt.ts @@ -1,75 +1,95 @@ import { createInterface } from "node:readline/promises"; +import type { + ApplyOcChatgptSyncOptions, + OcChatgptSyncApplyResult, + OcChatgptSyncPlanResult, + PlanOcChatgptSyncOptions, +} from "../oc-chatgpt-orchestrator.js"; +import type { AccountStorageV3 } from "../storage.js"; import type { PluginConfig } from "../types.js"; +import type { MenuItem, select } from "../ui/select.js"; import type { UiRuntimeOptions } from "../ui/runtime.js"; +import type { + ExperimentalSettingsAction, + getExperimentalSelectOptions, + mapExperimentalMenuHotkey, + mapExperimentalStatusHotkey, +} from "./experimental-settings-schema.js"; -export async function promptExperimentalSettingsMenu< - TAction, +export type ExperimentalSettingsCopy = { + experimentalSync: string; + experimentalBackup: string; + experimentalRefreshGuard: string; + experimentalRefreshInterval: string; + experimentalDecreaseInterval: string; + experimentalIncreaseInterval: string; + saveAndBack: string; + backNoSave: string; + experimentalHelpMenu: string; + experimentalBackupPrompt: string; + back: string; + experimentalHelpStatus: string; + experimentalApplySync: string; + experimentalHelpPreview: string; +}; + +export type ExperimentalSettingsPromptDeps< TTargetState, - TPlan, - TApplied, ->(params: { +> = { initialConfig: PluginConfig; isInteractive: () => boolean; ui: UiRuntimeOptions; cloneBackendPluginConfig: (config: PluginConfig) => PluginConfig; - select: ( - items: Array>, - options: Record, - ) => Promise; - getExperimentalSelectOptions: ( - ui: UiRuntimeOptions, - help: string, - hotkeyMapper: (raw: string) => TAction | undefined, - ) => Record; - mapExperimentalMenuHotkey: (raw: string) => TAction | undefined; - mapExperimentalStatusHotkey: (raw: string) => TAction | undefined; + select: typeof select; + getExperimentalSelectOptions: typeof getExperimentalSelectOptions; + mapExperimentalMenuHotkey: typeof mapExperimentalMenuHotkey; + mapExperimentalStatusHotkey: typeof mapExperimentalStatusHotkey; formatDashboardSettingState: (enabled: boolean) => string; - copy: { - experimentalSync: string; - experimentalBackup: string; - experimentalRefreshGuard: string; - experimentalRefreshInterval: string; - experimentalDecreaseInterval: string; - experimentalIncreaseInterval: string; - saveAndBack: string; - backNoSave: string; - experimentalHelpMenu: string; - experimentalBackupPrompt: string; - back: string; - experimentalHelpStatus: string; - experimentalApplySync: string; - experimentalHelpPreview: string; - }; + copy: ExperimentalSettingsCopy; input: NodeJS.ReadStream; output: NodeJS.WriteStream; runNamedBackupExport: (args: { name: string; }) => Promise<{ kind: string; path?: string; error?: unknown }>; - loadAccounts: () => Promise; + loadAccounts: () => Promise; loadExperimentalSyncTarget: () => Promise; - planOcChatgptSync: (args: Record) => Promise; - applyOcChatgptSync: (args: Record) => Promise; + planOcChatgptSync: ( + args: PlanOcChatgptSyncOptions, + ) => Promise; + applyOcChatgptSync: ( + args: ApplyOcChatgptSyncOptions, + ) => Promise; getTargetKind: (targetState: TTargetState) => string; - getTargetDestination: (targetState: TTargetState) => unknown; - getTargetDetection: (targetState: TTargetState) => unknown; + getTargetDestination: (targetState: TTargetState) => AccountStorageV3 | null; + getTargetDetection: ( + targetState: TTargetState, + ) => ReturnType< + typeof import("../oc-chatgpt-target-detection.js").detectOcChatgptMultiAuthTarget + >; getTargetErrorMessage: (targetState: TTargetState) => string | null; - getPlanKind: (plan: TPlan) => string; - getPlanBlockedReason: (plan: TPlan) => string; - getPlanPreview: (plan: TPlan) => { + getPlanKind: (plan: OcChatgptSyncPlanResult) => string; + getPlanBlockedReason: (plan: OcChatgptSyncPlanResult) => string; + getPlanPreview: (plan: OcChatgptSyncPlanResult) => { toAdd: unknown[]; toUpdate: unknown[]; toSkip: unknown[]; unchangedDestinationOnly: unknown[]; activeSelectionBehavior: string; }; - getAppliedLabel: (applied: TApplied) => { label: string; color: string }; -}): Promise { + getAppliedLabel: ( + applied: OcChatgptSyncApplyResult, + ) => { label: string; color: MenuItem["color"] }; +}; + +export async function promptExperimentalSettingsMenu( + params: ExperimentalSettingsPromptDeps, +): Promise { if (!params.isInteractive()) return null; let draft = params.cloneBackendPluginConfig(params.initialConfig); const copy = params.copy; while (true) { - const action = await params.select( + const action = await params.select( [ { label: copy.experimentalSync, @@ -164,7 +184,7 @@ export async function promptExperimentalSettingsMenu< : backupResult.error instanceof Error ? backupResult.error.message : String(backupResult.error); - await params.select( + await params.select( [ { label: backupLabel, @@ -184,7 +204,7 @@ export async function promptExperimentalSettingsMenu< } catch (error) { const message = error instanceof Error ? error.message : String(error); - await params.select( + await params.select( [ { label: message, @@ -212,7 +232,7 @@ export async function promptExperimentalSettingsMenu< const targetState = await params.loadExperimentalSyncTarget(); const targetError = params.getTargetErrorMessage(targetState); if (targetError) { - await params.select( + await params.select( [ { label: targetError, @@ -246,7 +266,7 @@ export async function promptExperimentalSettingsMenu< : undefined, }); if (params.getPlanKind(plan) !== "ready") { - await params.select( + await params.select( [ { label: params.getPlanBlockedReason(plan), @@ -267,7 +287,7 @@ export async function promptExperimentalSettingsMenu< } const preview = params.getPlanPreview(plan); - const review = await params.select( + const review = await params.select( [ { label: `Preview: add ${preview.toAdd.length} | update ${preview.toUpdate.length} | skip ${preview.toSkip.length}`, @@ -299,15 +319,15 @@ export async function promptExperimentalSettingsMenu< ], params.getExperimentalSelectOptions( params.ui, - copy.experimentalHelpPreview, - (raw) => { - const lower = raw.toLowerCase(); - if (lower === "q") return { type: "back" } as TAction; - if (lower === "a") return { type: "apply" } as TAction; - return undefined; - }, - ), - ); + copy.experimentalHelpPreview, + (raw) => { + const lower = raw.toLowerCase(); + if (lower === "q") return { type: "back" }; + if (lower === "a") return { type: "apply" }; + return undefined; + }, + ), + ); if (!review || (review as { type?: string }).type === "back") continue; const applied = await params.applyOcChatgptSync({ @@ -322,7 +342,7 @@ export async function promptExperimentalSettingsMenu< : undefined, }); const appliedLabel = params.getAppliedLabel(applied); - await params.select( + await params.select( [ { label: appliedLabel.label, diff --git a/lib/codex-manager/settings-hub.ts b/lib/codex-manager/settings-hub.ts index 11d7628e..c4e22328 100644 --- a/lib/codex-manager/settings-hub.ts +++ b/lib/codex-manager/settings-hub.ts @@ -666,14 +666,14 @@ async function promptExperimentalSettings( ): Promise { return promptExperimentalSettingsEntry({ initialConfig, - promptExperimentalSettingsMenu: promptExperimentalSettingsMenu as never, + promptExperimentalSettingsMenu, isInteractive: () => input.isTTY && output.isTTY, ui: getUiRuntimeOptions(), cloneBackendPluginConfig, - select: select as never, - getExperimentalSelectOptions: getExperimentalSelectOptions as never, - mapExperimentalMenuHotkey: mapExperimentalMenuHotkey as never, - mapExperimentalStatusHotkey: mapExperimentalStatusHotkey as never, + select, + getExperimentalSelectOptions, + mapExperimentalMenuHotkey, + mapExperimentalStatusHotkey, formatDashboardSettingState, copy: UI_COPY.settings, input, @@ -681,13 +681,22 @@ async function promptExperimentalSettings( runNamedBackupExport, loadAccounts, loadExperimentalSyncTarget, - planOcChatgptSync: planOcChatgptSync as never, - applyOcChatgptSync: applyOcChatgptSync as never, + planOcChatgptSync, + applyOcChatgptSync, getTargetKind: (targetState) => (targetState as { kind: string }).kind, - getTargetDestination: (targetState) => - (targetState as { kind: string; destination?: unknown }).destination, - getTargetDetection: (targetState) => - (targetState as { detection?: unknown }).detection, + getTargetDestination: ( + targetState, + ): import("../storage.js").AccountStorageV3 | null => + (targetState as { + kind: string; + destination?: import("../storage.js").AccountStorageV3 | null; + }).destination ?? null, + getTargetDetection: ( + targetState, + ): ReturnType => + (targetState as { + detection: ReturnType; + }).detection, getTargetErrorMessage: (targetState) => (targetState as { kind: string; message?: string }).kind === "error" ? ((targetState as { message?: string }).message ?? "Unknown error") diff --git a/test/experimental-settings-entry.test.ts b/test/experimental-settings-entry.test.ts index 9936553c..e81e4b9c 100644 --- a/test/experimental-settings-entry.test.ts +++ b/test/experimental-settings-entry.test.ts @@ -6,10 +6,7 @@ describe("experimental settings entry", () => { const promptExperimentalSettingsMenu = vi.fn(async () => ({ fetchTimeoutMs: 1000, })); - - const result = await promptExperimentalSettingsEntry({ - initialConfig: { fetchTimeoutMs: 2000 }, - promptExperimentalSettingsMenu, + const menuDeps = { isInteractive: () => true, ui: { theme: {} } as never, cloneBackendPluginConfig: vi.fn((config) => config), @@ -18,7 +15,22 @@ describe("experimental settings entry", () => { mapExperimentalMenuHotkey: vi.fn(), mapExperimentalStatusHotkey: vi.fn(), formatDashboardSettingState: vi.fn((enabled) => (enabled ? "on" : "off")), - copy: {}, + copy: { + experimentalSync: "Sync", + experimentalBackup: "Backup", + experimentalRefreshGuard: "Refresh guard", + experimentalRefreshInterval: "Refresh interval", + experimentalDecreaseInterval: "Decrease interval", + experimentalIncreaseInterval: "Increase interval", + saveAndBack: "Save", + backNoSave: "Back", + experimentalHelpMenu: "Help menu", + experimentalBackupPrompt: "Backup prompt", + back: "Back", + experimentalHelpStatus: "Help status", + experimentalApplySync: "Apply sync", + experimentalHelpPreview: "Help preview", + }, input: process.stdin, output: process.stdout, runNamedBackupExport: vi.fn(), @@ -34,9 +46,19 @@ describe("experimental settings entry", () => { getPlanBlockedReason: vi.fn(), getPlanPreview: vi.fn(), getAppliedLabel: vi.fn(), + }; + const initialConfig = { fetchTimeoutMs: 2000 }; + + const result = await promptExperimentalSettingsEntry({ + initialConfig, + promptExperimentalSettingsMenu, + ...menuDeps, }); - expect(promptExperimentalSettingsMenu).toHaveBeenCalled(); + expect(promptExperimentalSettingsMenu).toHaveBeenCalledWith({ + initialConfig, + ...menuDeps, + }); expect(result).toEqual({ fetchTimeoutMs: 1000 }); }); });