refactor(tests): share setup wizard prompter

This commit is contained in:
Peter Steinberger
2026-03-17 06:29:38 +00:00
parent cc35627c8f
commit d28cb8d821
9 changed files with 52 additions and 180 deletions

View File

@@ -1,31 +1,10 @@
import type { OpenClawConfig, WizardPrompter } from "openclaw/plugin-sdk/googlechat";
import type { OpenClawConfig } from "openclaw/plugin-sdk/googlechat";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { googlechatPlugin } from "./channel.js";
const selectFirstOption = async <T>(params: { options: Array<{ value: T }> }): Promise<T> => {
const first = params.options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
};
function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: selectFirstOption as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}
const googlechatConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: googlechatPlugin,
wizard: googlechatPlugin.setupWizard!,
@@ -33,7 +12,7 @@ const googlechatConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard
describe("googlechat setup wizard", () => {
it("configures service-account auth and webhook audience", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "Service account JSON path") {
return "/tmp/googlechat-service-account.json";

View File

@@ -1,32 +1,11 @@
import type { RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/irc";
import type { RuntimeEnv } from "openclaw/plugin-sdk/irc";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { ircPlugin } from "./channel.js";
import type { CoreConfig } from "./types.js";
const selectFirstOption = async <T>(params: { options: Array<{ value: T }> }): Promise<T> => {
const first = params.options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
};
function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: selectFirstOption as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}
const ircConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: ircPlugin,
wizard: ircPlugin.setupWizard!,
@@ -34,7 +13,7 @@ const ircConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
describe("irc setup wizard", () => {
it("configures host and nick via setup prompts", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "IRC server host") {
return "irc.libera.chat";
@@ -93,7 +72,7 @@ describe("irc setup wizard", () => {
});
it("writes DM allowFrom to top-level config for non-default account prompts", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "IRC allowFrom (nick or nick!user@host)") {
return "Alice, Bob!ident@example.org";

View File

@@ -6,30 +6,10 @@ import {
resolveDefaultLineAccountId,
resolveLineAccount,
} from "../../../src/line/accounts.js";
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { lineSetupAdapter, lineSetupWizard } from "./setup-surface.js";
function createPrompter(overrides: Partial<WizardPrompter> = {}): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: vi.fn(async ({ options }: { options: Array<{ value: string }> }) => {
const first = options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
}) as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}
const lineConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: {
id: "line",
@@ -47,7 +27,7 @@ const lineConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
describe("line setup wizard", () => {
it("configures token and secret for the default account", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "Enter LINE channel access token") {
return "line-token";

View File

@@ -1,30 +1,10 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/nostr";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { nostrPlugin } from "./channel.js";
function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: vi.fn(async ({ options }: { options: Array<{ value: string }> }) => {
const first = options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
}) as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}
const nostrConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: nostrPlugin,
wizard: nostrPlugin.setupWizard!,
@@ -32,7 +12,7 @@ const nostrConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
describe("nostr setup wizard", () => {
it("configures a private key and relay URLs", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "Nostr private key (nsec... or hex)") {
return "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";

View File

@@ -1,31 +1,11 @@
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { WizardPrompter } from "../../../src/wizard/prompts.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { synologyChatPlugin } from "./channel.js";
import { synologyChatSetupWizard } from "./setup-surface.js";
function createPrompter(overrides: Partial<WizardPrompter> = {}): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: vi.fn(async ({ options }: { options: Array<{ value: string }> }) => {
const first = options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
}) as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}
const synologyChatConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: synologyChatPlugin,
wizard: synologyChatSetupWizard,
@@ -33,7 +13,7 @@ const synologyChatConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWiza
describe("synology-chat setup wizard", () => {
it("configures token and incoming webhook for the default account", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "Enter Synology Chat outgoing webhook token") {
return "synology-token";
@@ -67,7 +47,7 @@ describe("synology-chat setup wizard", () => {
});
it("records allowed user ids when setup forces allowFrom", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "Enter Synology Chat outgoing webhook token") {
return "synology-token";

View File

@@ -0,0 +1,28 @@
import { vi } from "vitest";
import type { WizardPrompter } from "../../src/wizard/prompts.js";
export type { WizardPrompter } from "../../src/wizard/prompts.js";
export async function selectFirstWizardOption<T>(params: {
options: Array<{ value: T }>;
}): Promise<T> {
const first = params.options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
}
export function createTestWizardPrompter(overrides: Partial<WizardPrompter> = {}): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: selectFirstWizardOption as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}

View File

@@ -1,31 +1,10 @@
import type { OpenClawConfig, RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/tlon";
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk/tlon";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { tlonPlugin } from "./channel.js";
const selectFirstOption = async <T>(params: { options: Array<{ value: T }> }): Promise<T> => {
const first = params.options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
};
function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: selectFirstOption as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}
const tlonConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: tlonPlugin,
wizard: tlonPlugin.setupWizard!,
@@ -33,7 +12,7 @@ const tlonConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
describe("tlon setup wizard", () => {
it("configures ship, auth, and discovery settings", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "Ship name") {
return "sampel-palnet";

View File

@@ -1,23 +1,10 @@
import type { OpenClawConfig, RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/zalo";
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk/zalo";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { zaloPlugin } from "./channel.js";
function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: vi.fn(async () => "plaintext") as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}
const zaloConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: zaloPlugin,
wizard: zaloPlugin.setupWizard!,
@@ -25,7 +12,8 @@ const zaloConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
describe("zalo setup wizard", () => {
it("configures a polling token flow", async () => {
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
select: vi.fn(async () => "plaintext") as WizardPrompter["select"],
text: vi.fn(async ({ message }: { message: string }) => {
if (message === "Enter Zalo bot token") {
return "12345689:abc-xyz";

View File

@@ -1,7 +1,8 @@
import type { OpenClawConfig, WizardPrompter } from "openclaw/plugin-sdk/zalouser";
import type { OpenClawConfig } from "openclaw/plugin-sdk/zalouser";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter } from "../../test-utils/setup-wizard.js";
vi.mock("./zalo-js.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./zalo-js.js")>();
@@ -28,28 +29,6 @@ vi.mock("./zalo-js.js", async (importOriginal) => {
import { zalouserPlugin } from "./channel.js";
const selectFirstOption = async <T>(params: { options: Array<{ value: T }> }): Promise<T> => {
const first = params.options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
};
function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
return {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note: vi.fn(async () => {}),
select: selectFirstOption as WizardPrompter["select"],
multiselect: vi.fn(async () => []),
text: vi.fn(async () => "") as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
...overrides,
};
}
const zalouserConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
plugin: zalouserPlugin,
wizard: zalouserPlugin.setupWizard!,
@@ -58,7 +37,7 @@ const zalouserConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
describe("zalouser setup wizard", () => {
it("enables the account without forcing QR login", async () => {
const runtime = createRuntimeEnv();
const prompter = createPrompter({
const prompter = createTestWizardPrompter({
confirm: vi.fn(async ({ message }: { message: string }) => {
if (message === "Login via QR code now?") {
return false;