From 2205dac7d1eaf6f555caa6d591bb839b14afa2af Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Thu, 26 Feb 2026 01:04:11 -0500 Subject: [PATCH] Onboarding: unify hook context and share adapter test helper --- src/channels/plugins/onboarding-types.ts | 2 +- src/commands/channel-test-helpers.ts | 24 +++++++++++++++ src/commands/onboard-channels.test.ts | 38 +++++++----------------- src/commands/onboard-channels.ts | 2 ++ 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/channels/plugins/onboarding-types.ts b/src/channels/plugins/onboarding-types.ts index fe75aeb0990..342f29bf5b5 100644 --- a/src/channels/plugins/onboarding-types.ts +++ b/src/channels/plugins/onboarding-types.ts @@ -91,7 +91,7 @@ export type ChannelOnboardingAdapter = { ctx: ChannelOnboardingInteractiveContext, ) => Promise; configureWhenConfigured?: ( - ctx: ChannelOnboardingConfigureContext, + ctx: ChannelOnboardingInteractiveContext, ) => Promise; dmPolicy?: ChannelOnboardingDmPolicy; onAccountRecorded?: (accountId: string, options?: SetupChannelsOptions) => void; diff --git a/src/commands/channel-test-helpers.ts b/src/commands/channel-test-helpers.ts index fd7e6f36278..65745a55d5e 100644 --- a/src/commands/channel-test-helpers.ts +++ b/src/commands/channel-test-helpers.ts @@ -6,6 +6,9 @@ import { telegramPlugin } from "../../extensions/telegram/src/channel.js"; import { whatsappPlugin } from "../../extensions/whatsapp/src/channel.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import { createTestRegistry } from "../test-utils/channel-plugins.js"; +import type { ChannelChoice } from "./onboard-types.js"; +import { getChannelOnboardingAdapter } from "./onboarding/registry.js"; +import type { ChannelOnboardingAdapter } from "./onboarding/types.js"; export function setDefaultChannelPluginRegistryForTests(): void { const channels = [ @@ -18,3 +21,24 @@ export function setDefaultChannelPluginRegistryForTests(): void { ] as unknown as Parameters[0]; setActivePluginRegistry(createTestRegistry(channels)); } + +export function patchChannelOnboardingAdapter( + channel: ChannelChoice, + patch: Pick, +): () => void { + const adapter = getChannelOnboardingAdapter(channel); + if (!adapter) { + throw new Error(`missing onboarding adapter for ${channel}`); + } + const keys = Object.keys(patch) as K[]; + const previous = {} as Pick; + for (const key of keys) { + previous[key] = adapter[key]; + adapter[key] = patch[key]; + } + return () => { + for (const key of keys) { + adapter[key] = previous[key]; + } + }; +} diff --git a/src/commands/onboard-channels.test.ts b/src/commands/onboard-channels.test.ts index 681a9a06433..a6142faf843 100644 --- a/src/commands/onboard-channels.test.ts +++ b/src/commands/onboard-channels.test.ts @@ -3,11 +3,11 @@ import type { OpenClawConfig } from "../config/config.js"; import { createEmptyPluginRegistry } from "../plugins/registry.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import type { WizardPrompter } from "../wizard/prompts.js"; -import { setDefaultChannelPluginRegistryForTests } from "./channel-test-helpers.js"; +import { + patchChannelOnboardingAdapter, + setDefaultChannelPluginRegistryForTests, +} from "./channel-test-helpers.js"; import { setupChannels } from "./onboard-channels.js"; -import type { ChannelChoice } from "./onboard-types.js"; -import { getChannelOnboardingAdapter } from "./onboarding/registry.js"; -import type { ChannelOnboardingAdapter } from "./onboarding/types.js"; import { createExitThrowingRuntime, createWizardPrompter } from "./test-wizard-helpers.js"; function createPrompter(overrides: Partial): WizardPrompter { @@ -31,27 +31,6 @@ function createUnexpectedPromptGuards() { }; } -function patchOnboardingAdapter( - channel: ChannelChoice, - patch: Pick, -): () => void { - const adapter = getChannelOnboardingAdapter(channel); - if (!adapter) { - throw new Error(`missing onboarding adapter for ${channel}`); - } - const keys = Object.keys(patch) as K[]; - const previous = {} as Pick; - for (const key of keys) { - previous[key] = adapter[key]; - adapter[key] = patch[key]; - } - return () => { - for (const key of keys) { - adapter[key] = previous[key]; - } - }; -} - vi.mock("node:fs/promises", () => ({ default: { access: vi.fn(async () => { @@ -284,7 +263,7 @@ describe("setupChannels", () => { const selection = vi.fn(); const onAccountId = vi.fn(); const configureInteractive = vi.fn(async () => "skip" as const); - const restore = patchOnboardingAdapter("telegram", { + const restore = patchChannelOnboardingAdapter("telegram", { getStatus: vi.fn(async ({ cfg }) => ({ channel: "telegram", configured: Boolean(cfg.channels?.telegram?.botToken), @@ -342,7 +321,7 @@ describe("setupChannels", () => { const configure = vi.fn(async () => { throw new Error("configure should not be called when configureInteractive is present"); }); - const restore = patchOnboardingAdapter("telegram", { + const restore = patchChannelOnboardingAdapter("telegram", { getStatus: vi.fn(async ({ cfg }) => ({ channel: "telegram", configured: Boolean(cfg.channels?.telegram?.botToken), @@ -402,7 +381,7 @@ describe("setupChannels", () => { "configure should not be called when configureWhenConfigured handles updates", ); }); - const restore = patchOnboardingAdapter("telegram", { + const restore = patchChannelOnboardingAdapter("telegram", { getStatus: vi.fn(async ({ cfg }) => ({ channel: "telegram", configured: Boolean(cfg.channels?.telegram?.botToken), @@ -441,6 +420,9 @@ describe("setupChannels", () => { ); expect(configureWhenConfigured).toHaveBeenCalledTimes(1); + expect(configureWhenConfigured).toHaveBeenCalledWith( + expect.objectContaining({ configured: true, label: expect.any(String) }), + ); expect(configure).not.toHaveBeenCalled(); expect(selection).toHaveBeenCalledWith(["telegram"]); expect(onAccountId).toHaveBeenCalledWith("telegram", "acct-2"); diff --git a/src/commands/onboard-channels.ts b/src/commands/onboard-channels.ts index 457ae008c07..6e79379e1f1 100644 --- a/src/commands/onboard-channels.ts +++ b/src/commands/onboard-channels.ts @@ -540,6 +540,8 @@ export async function setupChannels( accountOverrides, shouldPromptAccountIds, forceAllowFrom: forceAllowFromChannels.has(channel), + configured: true, + label, }); if (!(await applyCustomOnboardingResult(channel, custom))) { return;