2026-10-21 21:40:00 +08:00
import {
test , expect , connectMessenger , connectWhatsAppNumber , digits , expectUnchanged , metaVerifiesCallback , ownMetaApp ,
pageChat , pastedCallback , routeToAgent , whereMetaSends , FakeExternals , WhatsAppChat , AI_REPLY ,
} from '@e2e'
// A WhatsApp number or Facebook Page connected with credentials from the
// customer's OWN Meta app — the only way to try Meta's free test number. Meta
// signs that app's webhooks with its own secret, so each such account has its
// own webhook address, checked with that secret only (issue #353).
//
// Connected through the API: the org is shared, and once any test has a number
// the Integrations card offers Manage, not Connect. The card itself is covered
// by the dashboard's unit tests.
test . describe ( 'a number or Page from the customer\'s own Meta app' , () => {
2026-10-26 00:50:43 +08:00
test ( 'a WhatsApp number receives messages: its webhooks are pointed at ReplyPier on connect @channels' , async ({ agent , llm , adminApi , request }) => {
2026-10-21 21:40:00 +08:00
await llm . script ( agent , ( s ) => s
. when ( /opening hours/i ). reply ( 'We are open 9am to 5pm, Monday to Friday.' )
. otherwise (). reply ( 'Happy to help!' ))
const app = ownMetaApp ()
const number = await connectWhatsAppNumber ( adminApi , digits ( 15 ), app )
// Meta took the per-number callback, after checking it answers: nothing to paste
expect ( number . account . webhook_auto_configured ). toBe ( true )
await routeToAgent ( adminApi , number . account . id , agent . id )
const externals = new FakeExternals ( request )
const chat = new WhatsAppChat ( request , externals , number , 'Tess' , () => whereMetaSends ( externals , number . phoneNumberId , app ))
await chat . send ( 'What are your opening hours?' )
await expect . poll (() => chat . replies (), AI_REPLY ). toEqual ([ 'We are open 9am to 5pm, Monday to Friday.' ])
})
test ( 'a Messenger Page receives messages once its callback URL and verify token are pasted into the app @channels' , async ({ agent , llm , adminApi , request }) => {
await llm . script ( agent , ( s ) => s . otherwise (). reply ( 'Hi! How can we help?' ))
const app = ownMetaApp ()
const pageId = digits ( 15 )
const token = app . token ( pageId )
const account = await connectMessenger ( adminApi , pageId , token , app . secret )
await routeToAgent ( adminApi , account . id , agent . id )
const url = account . webhook_url ?? ''
const verifyToken = account . webhook_verify_token ?? ''
// The customer pastes both into their app; Meta checks the address before saving
expect ( await metaVerifiesCallback ( request , url , verifyToken )). toBe ( true )
expect ( await metaVerifiesCallback ( request , url , 'a-guessed-token' )). toBe ( false )
const chat = pageChat ( 'Messenger' , request , new FakeExternals ( request ), 'page' , pageId , token , 'Remi' , pastedCallback ( url , app ))
await chat . send ( 'Hello there' )
await expect . poll (() => chat . replies (), AI_REPLY ). toEqual ([ 'Hi! How can we help?' ])
})
test ( 'a wrong app secret is refused when connecting @channels' , async ({ adminApi }) => {
const app = ownMetaApp ()
await expect ( connectWhatsAppNumber ( adminApi , digits ( 15 ), { ... app , secret : 'not-the-app-secret' }))
. rejects . toThrow ( /400: .*That app secret is not the one for the Meta app this token comes from/ )
})
test ( 'its address takes only its own app\'s accounts, never another app\'s number @channels @security' , async ({ agent , llm , adminApi , request }) => {
await llm . script ( agent , ( s ) => s . otherwise (). reply ( 'This should never be sent.' ))
const mine = ownMetaApp ()
const ourNumber = await connectWhatsAppNumber ( adminApi , digits ( 15 ), mine )
const theirNumber = await connectWhatsAppNumber ( adminApi , digits ( 15 ), ownMetaApp ())
await routeToAgent ( adminApi , theirNumber . account . id , agent . id )
const externals = new FakeExternals ( request )
// Signed with our app's secret and sent to our address, but naming their number
const forged = new WhatsAppChat ( request , externals , theirNumber , 'Mallory' , () => whereMetaSends ( externals , ourNumber . phoneNumberId , mine ))
await forged . send ( 'Hi, I am not who I say I am' )
await expectUnchanged ( 'replies on the other app\'s number' , () => forged . replies ())
})
test ( 'without the app secret a Page has no address of its own @channels' , async ({ adminApi }) => {
const pageId = digits ( 15 )
const account = await connectMessenger ( adminApi , pageId , ownMetaApp (). token ( pageId ))
expect ( account . webhook_url ). toBeFalsy ()
expect ( account . webhook_verify_token ). toBeFalsy ()
})
})