@segment/analytics-browser-actions-intercom 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +21 -0
- package/src/__tests__/index.test.ts +55 -0
- package/src/__tests__/utils.test.ts +90 -0
- package/src/api.ts +11 -0
- package/src/generated-types.ts +20 -0
- package/src/identifyCompany/__tests__/index.test.ts +308 -0
- package/src/identifyCompany/generated-types.ts +51 -0
- package/src/identifyCompany/index.ts +82 -0
- package/src/identifyUser/__tests__/index.test.ts +460 -0
- package/src/identifyUser/generated-types.ts +136 -0
- package/src/identifyUser/index.ts +237 -0
- package/src/index.ts +109 -0
- package/src/init-script.ts +49 -0
- package/src/sharedCompanyProperties.ts +59 -0
- package/src/trackEvent/__tests__/index.test.ts +179 -0
- package/src/trackEvent/generated-types.ts +22 -0
- package/src/trackEvent/index.ts +92 -0
- package/src/utils.ts +43 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { InputField } from '@segment/actions-core'
|
|
2
|
+
import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
|
|
3
|
+
import { Intercom } from '../api'
|
|
4
|
+
import type { Settings } from '../generated-types'
|
|
5
|
+
import { getCompanyProperties } from '../sharedCompanyProperties'
|
|
6
|
+
import { convertDateToUnix, filterCustomTraits, getWidgetOptions, isEmpty } from '../utils'
|
|
7
|
+
import type { Payload } from './generated-types'
|
|
8
|
+
|
|
9
|
+
const companyProperties: Record<string, InputField> = getCompanyProperties()
|
|
10
|
+
|
|
11
|
+
const action: BrowserActionDefinition<Settings, Intercom, Payload> = {
|
|
12
|
+
title: 'Identify User',
|
|
13
|
+
description: 'Create or update a user in Intercom.',
|
|
14
|
+
defaultSubscription: 'type = "identify" or type = "page"',
|
|
15
|
+
platform: 'web',
|
|
16
|
+
fields: {
|
|
17
|
+
user_id: {
|
|
18
|
+
description: 'A unique identifier for the user.',
|
|
19
|
+
label: 'User ID',
|
|
20
|
+
type: 'string',
|
|
21
|
+
required: false,
|
|
22
|
+
default: {
|
|
23
|
+
'@path': '$.userId'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
custom_traits: {
|
|
27
|
+
description: "The user's custom attributes.",
|
|
28
|
+
label: 'Custom Attributes',
|
|
29
|
+
type: 'object',
|
|
30
|
+
required: false,
|
|
31
|
+
defaultObjectUI: 'keyvalue'
|
|
32
|
+
},
|
|
33
|
+
name: {
|
|
34
|
+
description: "The user's name.",
|
|
35
|
+
label: 'Name',
|
|
36
|
+
type: 'string',
|
|
37
|
+
required: false,
|
|
38
|
+
default: {
|
|
39
|
+
'@path': '$.traits.name'
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
phone: {
|
|
43
|
+
description: "The user's phone number.",
|
|
44
|
+
label: 'Phone Number',
|
|
45
|
+
type: 'string',
|
|
46
|
+
required: false,
|
|
47
|
+
default: {
|
|
48
|
+
'@path': '$.traits.phone'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
unsubscribed_from_emails: {
|
|
52
|
+
description: "The user's email unsubscribe status.",
|
|
53
|
+
label: 'Unsubscribed From Emails',
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
required: false
|
|
56
|
+
},
|
|
57
|
+
language_override: {
|
|
58
|
+
description: "The user's messenger language (instead of relying on browser language settings).",
|
|
59
|
+
label: 'Language Override',
|
|
60
|
+
type: 'string',
|
|
61
|
+
required: false
|
|
62
|
+
},
|
|
63
|
+
email: {
|
|
64
|
+
description: "The user's email address.",
|
|
65
|
+
label: 'Email Address',
|
|
66
|
+
type: 'string',
|
|
67
|
+
required: false,
|
|
68
|
+
default: {
|
|
69
|
+
'@path': '$.traits.email'
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
created_at: {
|
|
73
|
+
description: 'The time the user was created in your system.',
|
|
74
|
+
label: 'User Creation Time',
|
|
75
|
+
type: 'datetime',
|
|
76
|
+
required: false,
|
|
77
|
+
default: {
|
|
78
|
+
'@if': {
|
|
79
|
+
exists: { '@path': '$.traits.createdAt' },
|
|
80
|
+
then: { '@path': '$.traits.createdAt' },
|
|
81
|
+
else: { '@path': '$.traits.created_at' }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
avatar_image_url: {
|
|
86
|
+
description: "The URL for the user's avatar/profile image.",
|
|
87
|
+
label: 'Avatar',
|
|
88
|
+
type: 'string',
|
|
89
|
+
required: false,
|
|
90
|
+
default: { '@path': '$.traits.avatar' }
|
|
91
|
+
},
|
|
92
|
+
user_hash: {
|
|
93
|
+
description:
|
|
94
|
+
'The user hash used for identity verification. See [Intercom docs](https://www.intercom.com/help/en/articles/183-enable-identity-verification-for-web-and-mobile) for more information on how to set this field.',
|
|
95
|
+
label: 'User Hash',
|
|
96
|
+
type: 'string',
|
|
97
|
+
required: false,
|
|
98
|
+
default: {
|
|
99
|
+
'@if': {
|
|
100
|
+
exists: { '@path': '$.context.Intercom.user_hash' },
|
|
101
|
+
then: { '@path': '$.context.Intercom.user_hash' },
|
|
102
|
+
else: { '@path': '$.context.Intercom.userHash' }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
company: {
|
|
107
|
+
description: "The user's company.",
|
|
108
|
+
label: 'Company',
|
|
109
|
+
type: 'object',
|
|
110
|
+
required: false,
|
|
111
|
+
properties: companyProperties,
|
|
112
|
+
default: {
|
|
113
|
+
company_id: { '@path': '$.traits.company.id' },
|
|
114
|
+
name: { '@path': '$.traits.company.name' },
|
|
115
|
+
created_at: {
|
|
116
|
+
'@if': {
|
|
117
|
+
exists: { '@path': '$.traits.company.createdAt' },
|
|
118
|
+
then: { '@path': '$.traits.company.createdAt' },
|
|
119
|
+
else: { '@path': '$.traits.company.created_at' }
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
plan: { '@path': '$.traits.company.plan' },
|
|
123
|
+
size: { '@path': '$.traits.company.size' },
|
|
124
|
+
website: { '@path': '$.traits.company.website' },
|
|
125
|
+
industry: { '@path': '$.traits.company.industry' },
|
|
126
|
+
monthly_spend: { '@path': '$.traits.company.monthly_spend' }
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
companies: {
|
|
130
|
+
description: 'The array of companies the user is associated to.',
|
|
131
|
+
label: 'Companies',
|
|
132
|
+
type: 'object',
|
|
133
|
+
multiple: true,
|
|
134
|
+
required: false,
|
|
135
|
+
properties: companyProperties,
|
|
136
|
+
default: {
|
|
137
|
+
'@arrayPath': [
|
|
138
|
+
'$.traits.companies',
|
|
139
|
+
{
|
|
140
|
+
company_id: { '@path': '$.id' },
|
|
141
|
+
name: { '@path': '$.name' },
|
|
142
|
+
created_at: {
|
|
143
|
+
'@if': {
|
|
144
|
+
exists: { '@path': '$.createdAt' },
|
|
145
|
+
then: { '@path': '$.createdAt' },
|
|
146
|
+
else: { '@path': '$.created_at' }
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
plan: { '@path': '$.plan' },
|
|
150
|
+
size: { '@path': '$.size' },
|
|
151
|
+
website: { '@path': '$.website' },
|
|
152
|
+
industry: { '@path': '$.industry' },
|
|
153
|
+
monthly_spend: { '@path': '$.monthly_spend' }
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
hide_default_launcher: {
|
|
159
|
+
description:
|
|
160
|
+
'Selectively show the chat widget. As per [Intercom docs](https://www.intercom.com/help/en/articles/189-turn-off-show-or-hide-the-intercom-messenger), you want to first hide the Messenger for all users inside the Intercom UI using Messenger settings. Then think about how you want to programmatically decide which users you would like to show the widget to.',
|
|
161
|
+
label: 'Hide Default Launcher',
|
|
162
|
+
type: 'boolean',
|
|
163
|
+
required: false,
|
|
164
|
+
default: {
|
|
165
|
+
'@if': {
|
|
166
|
+
exists: { '@path': '$.context.Intercom.hideDefaultLauncher' },
|
|
167
|
+
then: { '@path': '$.context.Intercom.hideDefaultLauncher' },
|
|
168
|
+
else: { '@path': '$.context.Intercom.hide_default_launcher' }
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
perform: (Intercom, event) => {
|
|
174
|
+
// remove properties that require extra handling
|
|
175
|
+
const { custom_traits, avatar_image_url, ...rest } = event.payload
|
|
176
|
+
const payload = { ...rest }
|
|
177
|
+
|
|
178
|
+
// remove company if it is empty
|
|
179
|
+
if (isEmpty(payload.company?.company_custom_traits)) {
|
|
180
|
+
delete payload.company?.company_custom_traits
|
|
181
|
+
}
|
|
182
|
+
if (isEmpty(payload.company)) {
|
|
183
|
+
delete payload.company
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// convert 'created_at' date properties from ISO-8601 to UNIX
|
|
187
|
+
const companies = Array.isArray(payload.companies) ? [...payload.companies] : []
|
|
188
|
+
const datesToConvert = [payload, payload.company, ...companies]
|
|
189
|
+
for (const objectWithDateProp of datesToConvert) {
|
|
190
|
+
if (objectWithDateProp && objectWithDateProp?.created_at) {
|
|
191
|
+
objectWithDateProp.created_at = convertDateToUnix(objectWithDateProp.created_at)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// drop custom objects & arrays
|
|
196
|
+
const filteredCustomTraits = filterCustomTraits(custom_traits)
|
|
197
|
+
|
|
198
|
+
// drop custom objects & arrays
|
|
199
|
+
if (payload.company) {
|
|
200
|
+
const { company_custom_traits, ...rest } = payload.company
|
|
201
|
+
const companyFilteredCustomTraits = filterCustomTraits(company_custom_traits)
|
|
202
|
+
payload.company = { ...rest, ...companyFilteredCustomTraits }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// drop custom objects & arrays
|
|
206
|
+
if (payload.companies) {
|
|
207
|
+
payload.companies = payload.companies.map((company) => {
|
|
208
|
+
const { company_custom_traits, ...rest } = company
|
|
209
|
+
const companyFilteredCustomTraits = filterCustomTraits(company_custom_traits)
|
|
210
|
+
company = { ...rest, ...companyFilteredCustomTraits }
|
|
211
|
+
return company
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// get user's widget options
|
|
216
|
+
const widgetOptions = getWidgetOptions(payload.hide_default_launcher, Intercom.activator)
|
|
217
|
+
|
|
218
|
+
// create the avatar object
|
|
219
|
+
let avatar = {}
|
|
220
|
+
if (avatar_image_url) {
|
|
221
|
+
avatar = {
|
|
222
|
+
image_url: avatar_image_url,
|
|
223
|
+
type: 'avatar'
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// API call
|
|
228
|
+
Intercom('update', {
|
|
229
|
+
...payload,
|
|
230
|
+
...filteredCustomTraits,
|
|
231
|
+
...widgetOptions,
|
|
232
|
+
...(!isEmpty(avatar) && { avatar })
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export default action
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Settings } from './generated-types'
|
|
2
|
+
import type { BrowserDestinationDefinition } from '@segment/browser-destination-runtime/types'
|
|
3
|
+
import { browserDestination } from '@segment/browser-destination-runtime/shim'
|
|
4
|
+
import { initialBoot, initScript } from './init-script'
|
|
5
|
+
|
|
6
|
+
import { Intercom } from './api'
|
|
7
|
+
import trackEvent from './trackEvent'
|
|
8
|
+
import identifyUser from './identifyUser'
|
|
9
|
+
import identifyCompany from './identifyCompany'
|
|
10
|
+
import { defaultValues } from '@segment/actions-core'
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface Window {
|
|
14
|
+
Intercom: Intercom
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const destination: BrowserDestinationDefinition<Settings, Intercom> = {
|
|
19
|
+
name: 'Intercom Web (Actions)',
|
|
20
|
+
slug: 'actions-intercom-web',
|
|
21
|
+
mode: 'device',
|
|
22
|
+
presets: [
|
|
23
|
+
{
|
|
24
|
+
name: 'Track Event',
|
|
25
|
+
subscribe: 'type = "track"',
|
|
26
|
+
partnerAction: 'trackEvent',
|
|
27
|
+
mapping: defaultValues(trackEvent.fields)
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Identify User',
|
|
31
|
+
subscribe: 'type = "identify" or type = "page"',
|
|
32
|
+
partnerAction: 'identifyUser',
|
|
33
|
+
mapping: defaultValues(identifyUser.fields)
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Identify Company',
|
|
37
|
+
subscribe: 'type = "group"',
|
|
38
|
+
partnerAction: 'identifyCompany',
|
|
39
|
+
mapping: defaultValues(identifyCompany.fields)
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
settings: {
|
|
43
|
+
appId: {
|
|
44
|
+
description: 'The app_id of your Intercom app which will indicate where to store any data.',
|
|
45
|
+
label: 'App ID',
|
|
46
|
+
type: 'string',
|
|
47
|
+
required: true
|
|
48
|
+
},
|
|
49
|
+
activator: {
|
|
50
|
+
description:
|
|
51
|
+
'By default, Intercom will inject their own inbox button onto the page, but you can choose to use your own custom button instead by providing a CSS selector, e.g. #my-button. You must have the "Show the Intercom Inbox" setting enabled for this to work. The default value is #IntercomDefaultWidget.',
|
|
52
|
+
label: 'Custom Inbox Button Selector',
|
|
53
|
+
type: 'string',
|
|
54
|
+
required: false,
|
|
55
|
+
default: '#IntercomDefaultWidget'
|
|
56
|
+
},
|
|
57
|
+
richLinkProperties: {
|
|
58
|
+
description: 'A list of rich link property keys.',
|
|
59
|
+
label: 'Rich Link Properties',
|
|
60
|
+
type: 'string',
|
|
61
|
+
multiple: true,
|
|
62
|
+
required: false
|
|
63
|
+
},
|
|
64
|
+
apiBase: {
|
|
65
|
+
description: 'The regional API to use for processing the data',
|
|
66
|
+
label: 'Regional Data Hosting',
|
|
67
|
+
type: 'string',
|
|
68
|
+
choices: [
|
|
69
|
+
{
|
|
70
|
+
label: 'US',
|
|
71
|
+
value: 'https://api-iam.intercom.io'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
label: 'EU',
|
|
75
|
+
value: 'https://api-iam.eu.intercom.io'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
label: 'Australia',
|
|
79
|
+
value: 'https://api-iam.au.intercom.io'
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
default: 'https://api-iam.intercom.io',
|
|
83
|
+
required: false
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
initialize: async ({ settings }, deps) => {
|
|
88
|
+
//initialize Intercom
|
|
89
|
+
initScript({ appId: settings.appId })
|
|
90
|
+
const preloadedIntercom = window.Intercom
|
|
91
|
+
initialBoot(settings.appId, { api_base: settings.apiBase })
|
|
92
|
+
|
|
93
|
+
await deps.resolveWhen(() => window.Intercom !== preloadedIntercom, 100)
|
|
94
|
+
|
|
95
|
+
window.Intercom.richLinkProperties = settings.richLinkProperties
|
|
96
|
+
window.Intercom.appId = settings.appId
|
|
97
|
+
window.Intercom.activator = settings.activator
|
|
98
|
+
|
|
99
|
+
return window.Intercom
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
actions: {
|
|
103
|
+
trackEvent,
|
|
104
|
+
identifyUser,
|
|
105
|
+
identifyCompany
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default browserDestination(destination)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
|
|
4
|
+
export function initScript({ appId }) {
|
|
5
|
+
//Set your APP_ID
|
|
6
|
+
var APP_ID = appId
|
|
7
|
+
;(function () {
|
|
8
|
+
var w = window
|
|
9
|
+
var ic = w.Intercom
|
|
10
|
+
if (typeof ic === 'function') {
|
|
11
|
+
ic('reattach_activator')
|
|
12
|
+
ic('update', w.intercomSettings)
|
|
13
|
+
} else {
|
|
14
|
+
var d = document
|
|
15
|
+
var i = function () {
|
|
16
|
+
i.c(arguments)
|
|
17
|
+
}
|
|
18
|
+
i.q = []
|
|
19
|
+
i.c = function (args) {
|
|
20
|
+
i.q.push(args)
|
|
21
|
+
}
|
|
22
|
+
w.Intercom = i
|
|
23
|
+
var l = function () {
|
|
24
|
+
var s = d.createElement('script')
|
|
25
|
+
s.type = 'text/javascript'
|
|
26
|
+
s.async = true
|
|
27
|
+
s.src = 'https://widget.intercom.io/widget/' + APP_ID
|
|
28
|
+
var x = d.getElementsByTagName('script')[0]
|
|
29
|
+
x.parentNode.insertBefore(s, x)
|
|
30
|
+
}
|
|
31
|
+
if (document.readyState === 'complete') {
|
|
32
|
+
l()
|
|
33
|
+
} else if (w.attachEvent) {
|
|
34
|
+
w.attachEvent('onload', l)
|
|
35
|
+
} else {
|
|
36
|
+
w.addEventListener('load', l, false)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function initialBoot(appId: string, options = {}) {
|
|
43
|
+
window &&
|
|
44
|
+
window.Intercom &&
|
|
45
|
+
window.Intercom('boot', {
|
|
46
|
+
app_id: appId,
|
|
47
|
+
...options
|
|
48
|
+
})
|
|
49
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { InputField } from '@segment/actions-core'
|
|
2
|
+
|
|
3
|
+
export const getCompanyProperties = (): Record<string, InputField> => ({
|
|
4
|
+
company_id: {
|
|
5
|
+
description: 'The unique identifier of the company.',
|
|
6
|
+
label: 'Company ID',
|
|
7
|
+
type: 'string',
|
|
8
|
+
required: true
|
|
9
|
+
},
|
|
10
|
+
name: {
|
|
11
|
+
description: 'The name of the company.',
|
|
12
|
+
label: 'Company Name',
|
|
13
|
+
type: 'string',
|
|
14
|
+
required: true
|
|
15
|
+
},
|
|
16
|
+
created_at: {
|
|
17
|
+
description: 'The time the company was created in your system.',
|
|
18
|
+
label: 'Company Creation Time',
|
|
19
|
+
type: 'datetime',
|
|
20
|
+
required: false
|
|
21
|
+
},
|
|
22
|
+
plan: {
|
|
23
|
+
description: 'The name of the plan you have associated with the company.',
|
|
24
|
+
label: 'Company Plan',
|
|
25
|
+
type: 'string',
|
|
26
|
+
required: false
|
|
27
|
+
},
|
|
28
|
+
monthly_spend: {
|
|
29
|
+
description: 'The monthly spend of the company, e.g. how much revenue the company generates for your business.',
|
|
30
|
+
label: 'Monthly Spend',
|
|
31
|
+
type: 'integer',
|
|
32
|
+
required: false
|
|
33
|
+
},
|
|
34
|
+
size: {
|
|
35
|
+
description: 'The number of employees in the company.',
|
|
36
|
+
label: 'Company Size',
|
|
37
|
+
type: 'integer',
|
|
38
|
+
required: false
|
|
39
|
+
},
|
|
40
|
+
website: {
|
|
41
|
+
description: 'The URL for the company website.',
|
|
42
|
+
label: 'Company Website',
|
|
43
|
+
type: 'string',
|
|
44
|
+
required: false
|
|
45
|
+
},
|
|
46
|
+
industry: {
|
|
47
|
+
description: 'The industry that the company operates in.',
|
|
48
|
+
label: 'Industry',
|
|
49
|
+
type: 'string',
|
|
50
|
+
required: false
|
|
51
|
+
},
|
|
52
|
+
company_custom_traits: {
|
|
53
|
+
description: 'The custom attributes for the company object.',
|
|
54
|
+
label: 'Company Custom Attributes',
|
|
55
|
+
type: 'object',
|
|
56
|
+
required: false,
|
|
57
|
+
defaultObjectUI: 'keyvalue'
|
|
58
|
+
}
|
|
59
|
+
})
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import { Subscription } from '@segment/browser-destination-runtime'
|
|
3
|
+
import intercomDestination, { destination } from '../../index'
|
|
4
|
+
|
|
5
|
+
const subscriptions: Subscription[] = [
|
|
6
|
+
{
|
|
7
|
+
partnerAction: 'trackEvent',
|
|
8
|
+
name: 'Show',
|
|
9
|
+
enabled: true,
|
|
10
|
+
subscribe: 'type = "track"',
|
|
11
|
+
mapping: {
|
|
12
|
+
event_name: {
|
|
13
|
+
'@path': '$.event'
|
|
14
|
+
},
|
|
15
|
+
event_metadata: {
|
|
16
|
+
'@path': '$.properties'
|
|
17
|
+
},
|
|
18
|
+
revenue: {
|
|
19
|
+
'@path': '$.properties.revenue'
|
|
20
|
+
},
|
|
21
|
+
currency: {
|
|
22
|
+
'@path': '$.properties.currency'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
describe('Intercom.trackEvent', () => {
|
|
29
|
+
const settings = {
|
|
30
|
+
appId: 'superSecretAppID'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let mockIntercom: jest.Mock<any, any>
|
|
34
|
+
let trackEvent: any
|
|
35
|
+
beforeEach(async () => {
|
|
36
|
+
jest.restoreAllMocks()
|
|
37
|
+
|
|
38
|
+
const [trackEventPlugin] = await intercomDestination({
|
|
39
|
+
...settings,
|
|
40
|
+
subscriptions
|
|
41
|
+
})
|
|
42
|
+
trackEvent = trackEventPlugin
|
|
43
|
+
|
|
44
|
+
mockIntercom = jest.fn()
|
|
45
|
+
jest.spyOn(destination, 'initialize').mockImplementation(() => {
|
|
46
|
+
const mockedWithProps = Object.assign(mockIntercom as any, settings)
|
|
47
|
+
return Promise.resolve(mockedWithProps)
|
|
48
|
+
})
|
|
49
|
+
await trackEvent.load(Context.system(), {} as Analytics)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('maps custom traits correctly', async () => {
|
|
53
|
+
const context = new Context({
|
|
54
|
+
type: 'track',
|
|
55
|
+
event: 'surfboard-bought',
|
|
56
|
+
properties: {
|
|
57
|
+
surfer: 'kelly slater'
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
await trackEvent.track?.(context)
|
|
61
|
+
|
|
62
|
+
expect(mockIntercom).toHaveBeenCalledWith('trackEvent', 'surfboard-bought', {
|
|
63
|
+
surfer: 'kelly slater'
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('maps price correctly', async () => {
|
|
68
|
+
const context = new Context({
|
|
69
|
+
type: 'track',
|
|
70
|
+
event: 'surfboard-bought',
|
|
71
|
+
properties: {
|
|
72
|
+
revenue: 100,
|
|
73
|
+
currency: 'USD'
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
await trackEvent.track?.(context)
|
|
77
|
+
|
|
78
|
+
expect(mockIntercom).toHaveBeenCalledWith('trackEvent', 'surfboard-bought', {
|
|
79
|
+
price: {
|
|
80
|
+
amount: 10000,
|
|
81
|
+
currency: 'USD'
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('currency defaults to USD if omitted', async () => {
|
|
87
|
+
const context = new Context({
|
|
88
|
+
type: 'track',
|
|
89
|
+
event: 'surfboard-bought',
|
|
90
|
+
properties: {
|
|
91
|
+
revenue: 100
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
await trackEvent.track?.(context)
|
|
95
|
+
|
|
96
|
+
expect(mockIntercom).toHaveBeenCalledWith('trackEvent', 'surfboard-bought', {
|
|
97
|
+
price: {
|
|
98
|
+
amount: 10000,
|
|
99
|
+
currency: 'USD'
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('drops arrays or objects in properties', async () => {
|
|
105
|
+
const context = new Context({
|
|
106
|
+
type: 'track',
|
|
107
|
+
event: 'surfboard-bought',
|
|
108
|
+
properties: {
|
|
109
|
+
surfer: 'kelly slater',
|
|
110
|
+
dropMe: {
|
|
111
|
+
foo: 'bar',
|
|
112
|
+
ahoy: {
|
|
113
|
+
okay: 'hello'
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
arr: ['hi', 'sup', 'yo']
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
await trackEvent.track?.(context)
|
|
121
|
+
|
|
122
|
+
expect(mockIntercom).toHaveBeenCalledWith('trackEvent', 'surfboard-bought', {
|
|
123
|
+
surfer: 'kelly slater'
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
describe('Intercom.trackEvent with rich link properties', () => {
|
|
129
|
+
const settings = {
|
|
130
|
+
appId: 'superSecretAppID',
|
|
131
|
+
richLinkProperties: ['article']
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let mockIntercom: jest.Mock<any, any>
|
|
135
|
+
let trackEvent: any
|
|
136
|
+
beforeEach(async () => {
|
|
137
|
+
jest.restoreAllMocks()
|
|
138
|
+
|
|
139
|
+
const [trackEventPlugin] = await intercomDestination({
|
|
140
|
+
...settings,
|
|
141
|
+
subscriptions
|
|
142
|
+
})
|
|
143
|
+
trackEvent = trackEventPlugin
|
|
144
|
+
|
|
145
|
+
mockIntercom = jest.fn()
|
|
146
|
+
jest.spyOn(destination, 'initialize').mockImplementation(() => {
|
|
147
|
+
const mockedWithProps = Object.assign(mockIntercom as any, settings)
|
|
148
|
+
return Promise.resolve(mockedWithProps)
|
|
149
|
+
})
|
|
150
|
+
await trackEvent.load(Context.system(), {} as Analytics)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('rich link properties are permitted', async () => {
|
|
154
|
+
const context = new Context({
|
|
155
|
+
type: 'track',
|
|
156
|
+
event: 'surfboard-bought',
|
|
157
|
+
properties: {
|
|
158
|
+
surfer: 'kelly slater',
|
|
159
|
+
dropMe: {
|
|
160
|
+
foo: 'bar'
|
|
161
|
+
},
|
|
162
|
+
article: {
|
|
163
|
+
url: 'im a link',
|
|
164
|
+
value: 'hi'
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
await trackEvent.track?.(context)
|
|
170
|
+
|
|
171
|
+
expect(mockIntercom).toHaveBeenCalledWith('trackEvent', 'surfboard-bought', {
|
|
172
|
+
surfer: 'kelly slater',
|
|
173
|
+
article: {
|
|
174
|
+
url: 'im a link',
|
|
175
|
+
value: 'hi'
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The name of the event.
|
|
6
|
+
*/
|
|
7
|
+
event_name: string
|
|
8
|
+
/**
|
|
9
|
+
* The amount associated with a purchase. Segment will multiply by 100 as Intercom requires the amount in cents.
|
|
10
|
+
*/
|
|
11
|
+
revenue?: number
|
|
12
|
+
/**
|
|
13
|
+
* The currency of the purchase amount. Segment will default to USD if revenue is provided without a currency.
|
|
14
|
+
*/
|
|
15
|
+
currency?: string
|
|
16
|
+
/**
|
|
17
|
+
* Optional metadata describing the event.
|
|
18
|
+
*/
|
|
19
|
+
event_metadata?: {
|
|
20
|
+
[k: string]: unknown
|
|
21
|
+
}
|
|
22
|
+
}
|