messaging-api-messenger
Version:
Messaging API client for Messenger
963 lines (822 loc) • 22.8 kB
text/typescript
import fs from 'fs';
import FormData from 'form-data';
import MockAdapter from 'axios-mock-adapter';
import MessengerClient from '../MessengerClient';
const USER_ID = '1QAZ2WSX';
const ACCESS_TOKEN = '1234567890';
const APP_ID = '987654321';
const APP_SECRET = '1WDVGY78';
const APP_ACCESS_TOKEN = 'APP_ACCESS_TOKEN';
let axios;
let _create;
beforeEach(() => {
axios = require('axios');
_create = axios.create;
});
afterEach(() => {
axios.create = _create;
});
const createMock = (): { client: MessengerClient; mock: MockAdapter } => {
const client = new MessengerClient({
appId: APP_ID,
appSecret: APP_SECRET,
accessToken: ACCESS_TOKEN,
skipAppSecretProof: true,
});
const mock = new MockAdapter(client.axios);
return { client, mock };
};
describe('page info', () => {
describe('#getPageInfo', () => {
it('should response page info', async () => {
const { client, mock } = createMock();
const reply = {
name: 'Bot Demo',
id: '1895382890692546',
};
let url;
let params;
mock.onGet().reply((config) => {
url = config.url;
params = config.params;
return [200, reply];
});
const res = await client.getPageInfo();
expect(url).toEqual('/me');
expect(params).toEqual({
access_token: ACCESS_TOKEN,
fields: undefined,
});
expect(res).toEqual(reply);
});
});
it('should support fields', async () => {
const { client, mock } = createMock();
const reply = {
name: 'Bot Demo',
id: '1895382890692546',
};
let url;
let params;
mock.onGet().reply((config) => {
url = config.url;
params = config.params;
return [200, reply];
});
const res = await client.getPageInfo({
fields: ['id', 'name'],
});
expect(url).toEqual('/me');
expect(params).toEqual({
access_token: ACCESS_TOKEN,
fields: 'id,name',
});
expect(res).toEqual(reply);
});
});
describe('token', () => {
describe('#debugToken', () => {
it('should response token info', async () => {
expect.assertions(3);
const { client, mock } = createMock();
const reply = {
data: {
app_id: '000000000000000',
application: 'Social Cafe',
expires_at: 1352419328,
is_valid: true,
issued_at: 1347235328,
scopes: ['email', 'user_location'],
user_id: 1207059,
},
};
let url;
let params;
mock.onGet().reply((config) => {
url = config.url;
params = config.params;
return [200, reply];
});
const res = await client.debugToken();
expect(url).toEqual('/debug_token');
expect(params).toEqual({
input_token: ACCESS_TOKEN,
access_token: `${APP_ID}|${APP_SECRET}`,
});
expect(res).toEqual({
appId: '000000000000000',
application: 'Social Cafe',
expiresAt: 1352419328,
isValid: true,
issuedAt: 1347235328,
scopes: ['email', 'user_location'],
userId: 1207059,
});
});
});
});
describe('subscription', () => {
describe('#createSubscription', () => {
it('should set default fields', async () => {
const { client, mock } = createMock();
const reply = {
success: true,
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.createSubscription({
appId: '54321',
callbackUrl: 'https://mycallback.com',
verifyToken: '1234567890',
accessToken: APP_ACCESS_TOKEN,
});
expect(url).toEqual(
`/${APP_ID}/subscriptions?access_token=${APP_ACCESS_TOKEN}`
);
expect(JSON.parse(data)).toEqual({
object: 'page',
callback_url: 'https://mycallback.com',
fields:
'messages,messaging_postbacks,messaging_optins,messaging_referrals,messaging_handovers,messaging_policy_enforcement',
verify_token: '1234567890',
});
expect(res).toEqual(reply);
});
it('should set other optional parameters', async () => {
const { client, mock } = createMock();
const reply = {
success: true,
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.createSubscription({
appId: '54321',
callbackUrl: 'https://mycallback.com',
verifyToken: '1234567890',
object: 'user',
fields: ['messages', 'messaging_postbacks'],
includeValues: true,
accessToken: APP_ACCESS_TOKEN,
});
expect(url).toEqual(
`/${APP_ID}/subscriptions?access_token=${APP_ACCESS_TOKEN}`
);
expect(JSON.parse(data)).toEqual({
object: 'user',
callback_url: 'https://mycallback.com',
fields: 'messages,messaging_postbacks',
verify_token: '1234567890',
include_values: true,
});
expect(res).toEqual(reply);
});
});
describe('#getSubscriptions', () => {
it('should get current subscriptions', async () => {
const { client, mock } = createMock();
const reply = {
data: [
{
object: 'page',
callback_url: 'https://mycallback.com',
active: true,
fields: [
{
name: 'messages',
version: 'v2.12',
},
],
},
],
};
let url;
mock.onGet().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.getSubscriptions();
expect(url).toEqual(
`/${APP_ID}/subscriptions?access_token=${APP_ID}|${APP_SECRET}`
);
expect(res).toEqual([
{
object: 'page',
callbackUrl: 'https://mycallback.com',
active: true,
fields: [
{
name: 'messages',
version: 'v2.12',
},
],
},
]);
});
});
describe('#getPageSubscription', () => {
it('should get current page subscription', async () => {
const { client, mock } = createMock();
const reply = {
data: [
{
object: 'page',
callback_url: 'https://mycallback.com',
active: true,
fields: [
{
name: 'messages',
version: 'v2.12',
},
],
},
{
object: 'user',
callback_url: 'https://mycallback.com',
active: true,
fields: [
{
name: 'feed',
version: 'v2.12',
},
],
},
],
};
let url;
mock.onGet().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.getPageSubscription();
expect(url).toEqual(
`/${APP_ID}/subscriptions?access_token=${APP_ID}|${APP_SECRET}`
);
expect(res).toEqual({
object: 'page',
callbackUrl: 'https://mycallback.com',
active: true,
fields: [
{
name: 'messages',
version: 'v2.12',
},
],
});
});
});
});
describe('#getMessagingFeatureReview', () => {
it('should response feature array', async () => {
const { client, mock } = createMock();
const reply = {
data: [
{
feature: 'subscription_messaging',
status: 'approved',
},
],
};
let url;
mock.onGet().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.getMessagingFeatureReview();
expect(url).toEqual(
`/me/messaging_feature_review?access_token=${ACCESS_TOKEN}`
);
expect(res).toEqual([
{
feature: 'subscription_messaging',
status: 'approved',
},
]);
});
});
describe('user profile', () => {
describe('#getUserProfile', () => {
it('should get user profile with default fields', async () => {
const { client, mock } = createMock();
const reply = {
id: '1',
first_name: 'Kevin',
last_name: 'Durant',
profile_pic: 'https://example.com/pic.png',
};
let url;
mock.onGet().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.getUserProfile('1');
expect(url).toEqual(
`/1?fields=id,name,first_name,last_name,profile_pic&access_token=${ACCESS_TOKEN}`
);
expect(res).toEqual({
id: '1',
firstName: 'Kevin',
lastName: 'Durant',
profilePic: 'https://example.com/pic.png',
});
});
it('should get user profile with given fields', async () => {
const { client, mock } = createMock();
const reply = {
id: '1',
first_name: 'Kevin',
last_name: 'Durant',
profile_pic: 'https://example.com/pic.png',
locale: 'en_US',
timezone: 8,
gender: 'male',
};
let url;
mock.onGet().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.getUserProfile('1', {
fields: [
'id',
'name',
'first_name',
'last_name',
'profile_pic',
'locale',
'timezone',
'gender',
],
});
expect(url).toEqual(
`/1?fields=id,name,first_name,last_name,profile_pic,locale,timezone,gender&access_token=${ACCESS_TOKEN}`
);
expect(res).toEqual({
id: '1',
firstName: 'Kevin',
lastName: 'Durant',
profilePic: 'https://example.com/pic.png',
locale: 'en_US',
timezone: 8,
gender: 'male',
});
});
});
});
describe('upload api', () => {
describe('#uploadAttachment', () => {
it('should call messages api to upload attachment', async () => {
const { client, mock } = createMock();
const reply = {
attachment_id: '1854626884821032',
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.uploadAttachment(
'image',
'http://www.yoctol-rocks.com/image.jpg'
);
expect(url).toEqual(
`/me/message_attachments?access_token=${ACCESS_TOKEN}`
);
expect(JSON.parse(data)).toEqual({
message: {
attachment: {
type: 'image',
payload: {
url: 'http://www.yoctol-rocks.com/image.jpg',
is_reusable: false,
},
},
},
});
expect(res).toEqual({
attachmentId: '1854626884821032',
});
});
it('can upload reusable attachment', async () => {
const { client, mock } = createMock();
const reply = {
attachment_id: '1854626884821032',
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.uploadAttachment(
'image',
'http://www.yoctol-rocks.com/image.jpg',
{ isReusable: true }
);
expect(url).toEqual(
`/me/message_attachments?access_token=${ACCESS_TOKEN}`
);
expect(JSON.parse(data)).toEqual({
message: {
attachment: {
type: 'image',
payload: {
url: 'http://www.yoctol-rocks.com/image.jpg',
is_reusable: true,
},
},
},
});
expect(res).toEqual({
attachmentId: '1854626884821032',
});
});
it('can call api with file stream', async () => {
const { client, mock } = createMock();
const reply = {
attachment_id: '1854626884821032',
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.uploadAttachment(
'file',
fs.createReadStream('./')
);
expect(url).toEqual(
`/me/message_attachments?access_token=${ACCESS_TOKEN}`
);
expect(data).toBeInstanceOf(FormData);
expect(res).toEqual({
attachmentId: '1854626884821032',
});
});
});
describe('#uploadAudio', () => {
it('should call messages api to upload audio', async () => {
const { client, mock } = createMock();
const reply = {
attachment_id: '1854626884821032',
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.uploadAudio(
'http://www.yoctol-rocks.com/audio.mp3'
);
expect(url).toEqual(
`/me/message_attachments?access_token=${ACCESS_TOKEN}`
);
expect(JSON.parse(data)).toEqual({
message: {
attachment: {
type: 'audio',
payload: {
url: 'http://www.yoctol-rocks.com/audio.mp3',
is_reusable: false,
},
},
},
});
expect(res).toEqual({
attachmentId: '1854626884821032',
});
});
});
describe('#uploadImage', () => {
it('should call messages api to upload image', async () => {
const { client, mock } = createMock();
const reply = {
attachment_id: '1854626884821032',
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.uploadImage(
'http://www.yoctol-rocks.com/image.jpg'
);
expect(url).toEqual(
`/me/message_attachments?access_token=${ACCESS_TOKEN}`
);
expect(JSON.parse(data)).toEqual({
message: {
attachment: {
type: 'image',
payload: {
url: 'http://www.yoctol-rocks.com/image.jpg',
is_reusable: false,
},
},
},
});
expect(res).toEqual({
attachmentId: '1854626884821032',
});
});
});
describe('#uploadVideo', () => {
it('should call messages api to upload video', async () => {
const { client, mock } = createMock();
const reply = {
attachment_id: '1854626884821032',
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.uploadVideo(
'http://www.yoctol-rocks.com/video.mp4'
);
expect(url).toEqual(
`/me/message_attachments?access_token=${ACCESS_TOKEN}`
);
expect(JSON.parse(data)).toEqual({
message: {
attachment: {
type: 'video',
payload: {
url: 'http://www.yoctol-rocks.com/video.mp4',
is_reusable: false,
},
},
},
});
expect(res).toEqual({
attachmentId: '1854626884821032',
});
});
});
describe('#uploadFile', () => {
it('should call messages api to upload file', async () => {
const { client, mock } = createMock();
const reply = {
attachment_id: '1854626884821032',
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.uploadFile(
'http://www.yoctol-rocks.com/file.pdf'
);
expect(url).toEqual(
`/me/message_attachments?access_token=${ACCESS_TOKEN}`
);
expect(JSON.parse(data)).toEqual({
message: {
attachment: {
type: 'file',
payload: {
url: 'http://www.yoctol-rocks.com/file.pdf',
is_reusable: false,
},
},
},
});
expect(res).toEqual({
attachmentId: '1854626884821032',
});
});
});
});
describe('Built-in NLP API', () => {
describe('#setNLPConfigs', () => {
it('should call api to set NLP configs', async () => {
const { client, mock } = createMock();
const reply = {
success: true,
};
let url;
mock.onPost().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.setNLPConfigs({
nlpEnabled: true,
customToken: '1234567890',
});
expect(url).toEqual(
`/me/nlp_configs?nlp_enabled=true&custom_token=1234567890&access_token=1234567890`
);
expect(res).toEqual(reply);
});
});
describe('#enableNLP', () => {
it('should call api to enable NLP', async () => {
const { client, mock } = createMock();
const reply = {
success: true,
};
let url;
mock.onPost().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.enableNLP();
expect(url).toEqual(
`/me/nlp_configs?nlp_enabled=true&access_token=1234567890`
);
expect(res).toEqual(reply);
});
});
describe('#disableNLP', () => {
it('should call api to disable NLP', async () => {
const { client, mock } = createMock();
const reply = {
success: true,
};
let url;
mock.onPost().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.disableNLP();
expect(url).toEqual(
`/me/nlp_configs?nlp_enabled=false&access_token=1234567890`
);
expect(res).toEqual(reply);
});
});
});
describe('Event Logging API', () => {
describe('#logCustomEvents', () => {
it('should call api to log events', async () => {
const { client, mock } = createMock();
const reply = {
success: true,
};
let url;
let data;
mock.onPost().reply((config) => {
url = config.url;
data = config.data;
return [200, reply];
});
const res = await client.logCustomEvents({
appId: 12345,
pageId: 67890,
pageScopedUserId: USER_ID,
events: [
{
_eventName: 'fb_mobile_purchase',
_valueToSum: 55.22,
_fb_currency: 'USD',
},
],
});
expect(url).toEqual(`/12345/activities`);
expect(JSON.parse(data)).toEqual({
event: 'CUSTOM_APP_EVENTS',
custom_events:
'[{"_eventName":"fb_mobile_purchase","_valueToSum":55.22,"_fb_currency":"USD"}]',
advertiser_tracking_enabled: 0,
application_tracking_enabled: 0,
extinfo: '["mb1"]',
page_id: 67890,
page_scoped_user_id: USER_ID,
});
expect(res).toEqual(reply);
});
});
});
describe('ID Matching', () => {
describe('getIdsForApps', () => {
it('should call api with appsecret_proof', async () => {
const { client, mock } = createMock();
const reply = {
data: [
{
id: '10152368852405295',
app: {
category: 'Business',
link: 'https://www.facebook.com/games/?app_id=1419232575008550',
name: "John's Game App",
id: '1419232575008550',
},
},
{
id: '645195294',
app: {
link: 'https://apps.facebook.com/johnsmovieappns/',
name: 'JohnsMovieApp',
namespace: 'johnsmovieappns',
id: '259773517400382',
},
},
],
paging: {
cursors: {
before: 'MTQ4OTU4MjQ5Nzc4NjY4OAZDZDA',
after: 'NDAwMDExOTA3MDM1ODMwA',
},
},
};
let url;
mock.onGet().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.getIdsForApps({
userId: '12345123',
appSecret: APP_SECRET,
page: '5678',
});
expect(url).toEqual(
`/12345123/ids_for_apps?access_token=${ACCESS_TOKEN}&appsecret_proof=4894f81b47c53ccf240a1130d119db2c69833eac9be09adeebc8e7226fb73e73&page=5678`
);
expect(res).toEqual(reply);
});
});
describe('getIdsForPages', () => {
it('should call api with appsecret_proof', async () => {
const { client, mock } = createMock();
const reply = {
data: [
{
id: '12345123', // The psid for the user for that page
page: {
category: 'Musician',
link: 'https://www.facebook.com/Johns-Next-Great-Thing-380374449010653/',
name: "John's Next Great Thing",
id: '380374449010653',
},
},
],
paging: {
cursors: {
before: 'MTQ4OTU4MjQ5Nzc4NjY4OAZDZDA',
after: 'NDAwMDExOTA3MDM1ODMwA',
},
},
};
let url;
mock.onGet().reply((config) => {
url = config.url;
return [200, reply];
});
const res = await client.getIdsForPages({
userId: '12345123',
appSecret: APP_SECRET,
app: '5678',
});
expect(url).toEqual(
`/12345123/ids_for_pages?access_token=${ACCESS_TOKEN}&appsecret_proof=4894f81b47c53ccf240a1130d119db2c69833eac9be09adeebc8e7226fb73e73&app=5678`
);
expect(res).toEqual(reply);
});
});
});
describe('Error', () => {
it('should be formatted correctly', async () => {
const { client, mock } = createMock();
const reply = {
error: {
message: 'Invalid OAuth access token.',
type: 'OAuthException',
code: 190,
error_subcode: 1234567,
fbtrace_id: 'BLBz/WZt8dN',
},
};
mock.onAny().reply(400, reply);
let error;
try {
await client.sendText(USER_ID, 'Hello!');
} catch (err) {
error = err;
}
expect(error.message).toEqual(
'Messenger API - 190 OAuthException Invalid OAuth access token.'
);
});
});