Files
openclaw/extensions/whatsapp/src/setup-test-helpers.ts
Vincent Koc 68a500c465 fix(whatsapp): normalize onboarding allowlist numbers
Normalize WhatsApp onboarding allowlist entries to digit-only WhatsApp IDs and reject invalid owner-phone inputs during prompt validation.
2026-05-04 20:59:01 -07:00

218 lines
6.1 KiB
TypeScript

import { expect } from "vitest";
type WhatsAppSetupConfig = {
channels?: {
whatsapp?: {
selfChatMode?: boolean;
dmPolicy?: string;
allowFrom?: string[];
accounts?: Record<string, { dmPolicy?: string; allowFrom?: string[]; authDir?: string }>;
};
};
};
type WizardPromptHarness = {
text: (...args: unknown[]) => unknown;
select: (...args: unknown[]) => unknown;
note: (...args: unknown[]) => unknown;
};
type QueuedWizardPrompterFactory<T extends WizardPromptHarness> = (params: {
confirmValues?: boolean[];
selectValues?: string[];
textValues?: string[];
}) => T;
const WHATSAPP_OWNER_NUMBER_INPUT = "+1 (555) 555-0123";
const WHATSAPP_OWNER_NUMBER_E164 = "+15555550123";
const WHATSAPP_OWNER_NUMBER = "15555550123";
const WHATSAPP_PERSONAL_NUMBER_INPUT = "+1 (555) 111-2222";
const WHATSAPP_PERSONAL_NUMBER = "15551112222";
const WHATSAPP_ACCESS_NOTE_TITLE = "WhatsApp DM access";
const WHATSAPP_LOGIN_NOTE_TITLE = "WhatsApp";
export function createWhatsAppRootAllowFromConfig(): WhatsAppSetupConfig {
return {
channels: {
whatsapp: {
allowFrom: [WHATSAPP_OWNER_NUMBER_E164],
},
},
};
}
export function createWhatsAppOwnerAllowlistHarness<T extends WizardPromptHarness>(
createPrompter: QueuedWizardPrompterFactory<T>,
): T {
return createPrompter({
confirmValues: [false],
textValues: [WHATSAPP_OWNER_NUMBER_INPUT],
});
}
export function createWhatsAppPersonalPhoneHarness<T extends WizardPromptHarness>(
createPrompter: QueuedWizardPrompterFactory<T>,
): T {
return createPrompter({
confirmValues: [false],
selectValues: ["personal"],
textValues: [WHATSAPP_PERSONAL_NUMBER_INPUT],
});
}
export function createWhatsAppLinkingHarness<T extends WizardPromptHarness>(
createPrompter: QueuedWizardPrompterFactory<T>,
): T {
return createPrompter({
confirmValues: [true],
selectValues: ["separate", "disabled"],
});
}
export function createWhatsAppWorkAccountConfig(
params: {
defaultAccount?: string;
} = {},
): WhatsAppSetupConfig {
return {
channels: {
whatsapp: {
...(params.defaultAccount ? { defaultAccount: params.defaultAccount } : {}),
dmPolicy: "disabled",
allowFrom: [WHATSAPP_OWNER_NUMBER_E164],
accounts: {
work: {
authDir: "/tmp/work",
},
},
},
},
};
}
export function createWhatsAppAllowlistModeInput(): {
selectValues: string[];
textValues: string[];
} {
return {
selectValues: ["separate", "allowlist", "list"],
textValues: [`${WHATSAPP_OWNER_NUMBER_INPUT}, ${WHATSAPP_OWNER_NUMBER}, *`],
};
}
function expectWhatsAppDmAccess(
cfg: WhatsAppSetupConfig,
expected: {
selfChatMode: boolean;
dmPolicy: string;
allowFrom?: string[];
},
): void {
expect(cfg.channels?.whatsapp?.selfChatMode).toBe(expected.selfChatMode);
expect(cfg.channels?.whatsapp?.dmPolicy).toBe(expected.dmPolicy);
if ("allowFrom" in expected) {
expect(cfg.channels?.whatsapp?.allowFrom).toEqual(expected.allowFrom);
} else {
expect(cfg.channels?.whatsapp?.allowFrom).toBeUndefined();
}
}
export function expectWhatsAppWorkAccountOpenAccess(cfg: WhatsAppSetupConfig): void {
expect(cfg.channels?.whatsapp?.dmPolicy).toBe("disabled");
expect(cfg.channels?.whatsapp?.allowFrom).toEqual([WHATSAPP_OWNER_NUMBER_E164]);
expect(cfg.channels?.whatsapp?.accounts?.work?.dmPolicy).toBe("open");
expect(cfg.channels?.whatsapp?.accounts?.work?.allowFrom).toEqual(["*", WHATSAPP_OWNER_NUMBER]);
}
function expectWhatsAppOwnerNumberPrompt(harness: WizardPromptHarness): void {
expect(harness.text).toHaveBeenCalledWith(
expect.objectContaining({
message: "Your personal WhatsApp number (the phone you will message from)",
}),
);
}
export function expectWhatsAppOwnerAllowlistSetup(
cfg: WhatsAppSetupConfig,
harness: WizardPromptHarness,
): void {
expectWhatsAppDmAccess(cfg, {
selfChatMode: true,
dmPolicy: "allowlist",
allowFrom: [WHATSAPP_OWNER_NUMBER],
});
expectWhatsAppOwnerNumberPrompt(harness);
}
export function expectWhatsAppSeparatePhoneDisabledSetup(
cfg: WhatsAppSetupConfig,
harness: WizardPromptHarness,
): void {
expectWhatsAppDmAccess(cfg, {
selfChatMode: false,
dmPolicy: "disabled",
});
expect(harness.text).not.toHaveBeenCalled();
}
export function expectWhatsAppAllowlistModeSetup(cfg: WhatsAppSetupConfig): void {
expectWhatsAppDmAccess(cfg, {
selfChatMode: false,
dmPolicy: "allowlist",
allowFrom: [WHATSAPP_OWNER_NUMBER, "*"],
});
}
export function expectWhatsAppPersonalPhoneSetup(cfg: WhatsAppSetupConfig): void {
expectWhatsAppDmAccess(cfg, {
selfChatMode: true,
dmPolicy: "allowlist",
allowFrom: [WHATSAPP_PERSONAL_NUMBER],
});
}
export function expectWhatsAppOpenPolicySetup(
cfg: WhatsAppSetupConfig,
harness: WizardPromptHarness,
): void {
expectWhatsAppDmAccess(cfg, {
selfChatMode: false,
dmPolicy: "open",
allowFrom: ["*", WHATSAPP_OWNER_NUMBER],
});
expect(harness.select).toHaveBeenCalledTimes(2);
expect(harness.text).not.toHaveBeenCalled();
}
export function expectNoWhatsAppLoginFollowup(harness: WizardPromptHarness): void {
expect(harness.note).not.toHaveBeenCalledWith(
expect.stringContaining("openclaw channels login"),
WHATSAPP_LOGIN_NOTE_TITLE,
);
}
export function expectWhatsAppLoginFollowup(harness: WizardPromptHarness): void {
expect(harness.note).toHaveBeenCalledWith(
expect.stringContaining("openclaw channels login"),
WHATSAPP_LOGIN_NOTE_TITLE,
);
}
export function expectWhatsAppWorkAccountAccessNote(harness: WizardPromptHarness): void {
expect(harness.note).toHaveBeenCalledWith(
expect.stringContaining(
"`channels.whatsapp.accounts.work.dmPolicy` + `channels.whatsapp.accounts.work.allowFrom`",
),
WHATSAPP_ACCESS_NOTE_TITLE,
);
}
export function expectWhatsAppDefaultAccountAccessNote(harness: WizardPromptHarness): void {
expect(harness.note).toHaveBeenCalledWith(
expect.stringContaining(
"`channels.whatsapp.accounts.default.dmPolicy` + `channels.whatsapp.accounts.default.allowFrom`",
),
WHATSAPP_ACCESS_NOTE_TITLE,
);
}