@segment/analytics-browser-actions-bucket 1.1.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/dist/cjs/generated-types.d.ts +3 -0
- package/dist/cjs/generated-types.js +3 -0
- package/dist/cjs/generated-types.js.map +1 -0
- package/dist/cjs/group/generated-types.d.ts +7 -0
- package/dist/cjs/group/generated-types.js +3 -0
- package/dist/cjs/group/generated-types.js.map +1 -0
- package/dist/cjs/group/index.d.ts +6 -0
- package/dist/cjs/group/index.js +45 -0
- package/dist/cjs/group/index.js.map +1 -0
- package/dist/cjs/identifyUser/generated-types.d.ts +6 -0
- package/dist/cjs/identifyUser/generated-types.js +3 -0
- package/dist/cjs/identifyUser/generated-types.js.map +1 -0
- package/dist/cjs/identifyUser/index.d.ts +6 -0
- package/dist/cjs/identifyUser/index.js +33 -0
- package/dist/cjs/identifyUser/index.js.map +1 -0
- package/dist/cjs/index.d.ts +11 -0
- package/dist/cjs/index.js +66 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/test-utils.d.ts +6 -0
- package/dist/cjs/test-utils.js +58 -0
- package/dist/cjs/test-utils.js.map +1 -0
- package/dist/cjs/trackEvent/generated-types.d.ts +7 -0
- package/dist/cjs/trackEvent/generated-types.js +3 -0
- package/dist/cjs/trackEvent/generated-types.js.map +1 -0
- package/dist/cjs/trackEvent/index.d.ts +6 -0
- package/dist/cjs/trackEvent/index.js +45 -0
- package/dist/cjs/trackEvent/index.js.map +1 -0
- package/dist/cjs/types.d.ts +2 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/generated-types.d.ts +3 -0
- package/dist/esm/generated-types.js +2 -0
- package/dist/esm/generated-types.js.map +1 -0
- package/dist/esm/group/generated-types.d.ts +7 -0
- package/dist/esm/group/generated-types.js +2 -0
- package/dist/esm/group/generated-types.js.map +1 -0
- package/dist/esm/group/index.d.ts +6 -0
- package/dist/esm/group/index.js +43 -0
- package/dist/esm/group/index.js.map +1 -0
- package/dist/esm/identifyUser/generated-types.d.ts +6 -0
- package/dist/esm/identifyUser/generated-types.js +2 -0
- package/dist/esm/identifyUser/generated-types.js.map +1 -0
- package/dist/esm/identifyUser/index.d.ts +6 -0
- package/dist/esm/identifyUser/index.js +31 -0
- package/dist/esm/identifyUser/index.js.map +1 -0
- package/dist/esm/index.d.ts +11 -0
- package/dist/esm/index.js +62 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/test-utils.d.ts +6 -0
- package/dist/esm/test-utils.js +52 -0
- package/dist/esm/test-utils.js.map +1 -0
- package/dist/esm/trackEvent/generated-types.d.ts +7 -0
- package/dist/esm/trackEvent/generated-types.js +2 -0
- package/dist/esm/trackEvent/generated-types.js.map +1 -0
- package/dist/esm/trackEvent/index.d.ts +6 -0
- package/dist/esm/trackEvent/index.js +43 -0
- package/dist/esm/trackEvent/index.js.map +1 -0
- package/dist/esm/types.d.ts +2 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +26 -0
- package/src/__tests__/index.test.ts +116 -0
- package/src/generated-types.ts +8 -0
- package/src/group/__tests__/index.test.ts +186 -0
- package/src/group/generated-types.ts +18 -0
- package/src/group/index.ts +49 -0
- package/src/identifyUser/__tests__/index.test.ts +78 -0
- package/src/identifyUser/generated-types.ts +14 -0
- package/src/identifyUser/index.ts +36 -0
- package/src/index.ts +85 -0
- package/src/test-utils.ts +60 -0
- package/src/trackEvent/__tests__/index.test.ts +159 -0
- package/src/trackEvent/generated-types.ts +18 -0
- package/src/trackEvent/index.ts +49 -0
- package/src/types.ts +3 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Analytics, Context, User } from '@segment/analytics-next'
|
|
2
|
+
import bucketWebDestination, { destination } from '../../index'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
import { JSONArray } from '@segment/actions-core/*'
|
|
5
|
+
import { bucketTestHooks, getBucketCallLog } from '../../test-utils'
|
|
6
|
+
|
|
7
|
+
const subscriptions: Subscription[] = [
|
|
8
|
+
{
|
|
9
|
+
partnerAction: 'group',
|
|
10
|
+
name: 'Identify Company',
|
|
11
|
+
enabled: true,
|
|
12
|
+
subscribe: 'type = "group"',
|
|
13
|
+
mapping: {
|
|
14
|
+
groupId: {
|
|
15
|
+
'@path': '$.groupId'
|
|
16
|
+
},
|
|
17
|
+
userId: {
|
|
18
|
+
'@path': '$.userId'
|
|
19
|
+
},
|
|
20
|
+
traits: {
|
|
21
|
+
'@path': '$.traits'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
describe('Bucket.company', () => {
|
|
28
|
+
bucketTestHooks()
|
|
29
|
+
|
|
30
|
+
describe('when logged in', () => {
|
|
31
|
+
describe('from analytics.js previous session', () => {
|
|
32
|
+
it('maps parameters correctly to Bucket', async () => {
|
|
33
|
+
const [bucketPlugin] = await bucketWebDestination({
|
|
34
|
+
trackingKey: 'testTrackingKey',
|
|
35
|
+
subscriptions: subscriptions as unknown as JSONArray
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const analyticsInstance = new Analytics({ writeKey: 'test-writekey' })
|
|
39
|
+
jest.spyOn(analyticsInstance, 'user').mockImplementation(
|
|
40
|
+
() =>
|
|
41
|
+
({
|
|
42
|
+
id: () => 'user-id-1'
|
|
43
|
+
} as User)
|
|
44
|
+
)
|
|
45
|
+
await bucketPlugin.load(Context.system(), analyticsInstance)
|
|
46
|
+
|
|
47
|
+
jest.spyOn(destination.actions.group, 'perform')
|
|
48
|
+
|
|
49
|
+
await bucketPlugin.group?.(
|
|
50
|
+
new Context({
|
|
51
|
+
type: 'group',
|
|
52
|
+
userId: 'user-id-1',
|
|
53
|
+
groupId: 'group-id-1',
|
|
54
|
+
traits: {
|
|
55
|
+
name: 'ACME INC'
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
expect(destination.actions.group.perform).toHaveBeenCalledWith(
|
|
61
|
+
expect.anything(),
|
|
62
|
+
expect.objectContaining({
|
|
63
|
+
payload: {
|
|
64
|
+
userId: 'user-id-1',
|
|
65
|
+
groupId: 'group-id-1',
|
|
66
|
+
traits: {
|
|
67
|
+
name: 'ACME INC'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
expect(getBucketCallLog()).toStrictEqual([
|
|
74
|
+
{ method: 'init', args: ['testTrackingKey'] },
|
|
75
|
+
{
|
|
76
|
+
method: 'user',
|
|
77
|
+
args: ['user-id-1', {}, { active: false }]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
method: 'company',
|
|
81
|
+
args: [
|
|
82
|
+
'group-id-1',
|
|
83
|
+
{
|
|
84
|
+
name: 'ACME INC'
|
|
85
|
+
},
|
|
86
|
+
'user-id-1'
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
])
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('from am identify call', () => {
|
|
94
|
+
it('maps parameters correctly to Bucket', async () => {
|
|
95
|
+
const [bucketPlugin] = await bucketWebDestination({
|
|
96
|
+
trackingKey: 'testTrackingKey',
|
|
97
|
+
subscriptions: subscriptions as unknown as JSONArray
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
await bucketPlugin.load(Context.system(), new Analytics({ writeKey: 'test-writekey' }))
|
|
101
|
+
|
|
102
|
+
jest.spyOn(destination.actions.group, 'perform')
|
|
103
|
+
|
|
104
|
+
// Bucket rejects group calls without previous identify calls
|
|
105
|
+
await window.bucket.user('user-id-1')
|
|
106
|
+
|
|
107
|
+
await bucketPlugin.group?.(
|
|
108
|
+
new Context({
|
|
109
|
+
type: 'group',
|
|
110
|
+
userId: 'user-id-1',
|
|
111
|
+
groupId: 'group-id-1',
|
|
112
|
+
traits: {
|
|
113
|
+
name: 'ACME INC'
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
expect(destination.actions.group.perform).toHaveBeenCalledWith(
|
|
119
|
+
expect.anything(),
|
|
120
|
+
expect.objectContaining({
|
|
121
|
+
payload: {
|
|
122
|
+
userId: 'user-id-1',
|
|
123
|
+
groupId: 'group-id-1',
|
|
124
|
+
traits: {
|
|
125
|
+
name: 'ACME INC'
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
expect(getBucketCallLog()).toStrictEqual([
|
|
132
|
+
{ method: 'init', args: ['testTrackingKey'] },
|
|
133
|
+
{
|
|
134
|
+
method: 'user',
|
|
135
|
+
args: ['user-id-1']
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
method: 'company',
|
|
139
|
+
args: [
|
|
140
|
+
'group-id-1',
|
|
141
|
+
{
|
|
142
|
+
name: 'ACME INC'
|
|
143
|
+
},
|
|
144
|
+
'user-id-1'
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
])
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
describe('when not logged in', () => {
|
|
153
|
+
it('should not call Bucket.group', async () => {
|
|
154
|
+
const [bucketPlugin] = await bucketWebDestination({
|
|
155
|
+
trackingKey: 'testTrackingKey',
|
|
156
|
+
subscriptions: subscriptions as unknown as JSONArray
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
const analyticsInstance = new Analytics({ writeKey: 'test-writekey' })
|
|
160
|
+
await bucketPlugin.load(Context.system(), analyticsInstance)
|
|
161
|
+
|
|
162
|
+
jest.spyOn(destination.actions.group, 'perform')
|
|
163
|
+
|
|
164
|
+
// Manually mimicking a group call without a userId.
|
|
165
|
+
// The analytics client will probably never do this if
|
|
166
|
+
// userId doesn't exist, since the subscription marks it as required
|
|
167
|
+
await bucketPlugin.group?.(
|
|
168
|
+
new Context({
|
|
169
|
+
type: 'group',
|
|
170
|
+
anonymousId: 'anonymous-id-1',
|
|
171
|
+
groupId: 'group-id-1',
|
|
172
|
+
traits: {
|
|
173
|
+
name: 'ACME INC'
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
// TODO: Ideally we should be able to assert that the destination action was never
|
|
179
|
+
// called, but couldn't figure out how to create an anlytics instance with the plugin
|
|
180
|
+
// and then trigger the full flow trhough analytics.group() with only an anonymous ID
|
|
181
|
+
// expect(destination.actions.group.perform).not.toHaveBeenCalled()
|
|
182
|
+
|
|
183
|
+
expect(getBucketCallLog()).toStrictEqual([{ method: 'init', args: ['testTrackingKey'] }])
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* Unique identifier for the company
|
|
6
|
+
*/
|
|
7
|
+
groupId: string
|
|
8
|
+
/**
|
|
9
|
+
* Unique identifier for the user
|
|
10
|
+
*/
|
|
11
|
+
userId: string
|
|
12
|
+
/**
|
|
13
|
+
* Additional information to associate with the Company in Bucket
|
|
14
|
+
*/
|
|
15
|
+
traits?: {
|
|
16
|
+
[k: string]: unknown
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
|
|
2
|
+
import type { Settings } from '../generated-types'
|
|
3
|
+
import type { Payload } from './generated-types'
|
|
4
|
+
import type { Bucket } from '../types'
|
|
5
|
+
|
|
6
|
+
const action: BrowserActionDefinition<Settings, Bucket, Payload> = {
|
|
7
|
+
title: 'Identify Company',
|
|
8
|
+
description: 'Creates or updates a Company in Bucket and associates the user with it',
|
|
9
|
+
platform: 'web',
|
|
10
|
+
defaultSubscription: 'type = "group"',
|
|
11
|
+
fields: {
|
|
12
|
+
groupId: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
required: true,
|
|
15
|
+
description: 'Unique identifier for the company',
|
|
16
|
+
label: 'Company ID',
|
|
17
|
+
default: {
|
|
18
|
+
'@path': '$.groupId'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
userId: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
required: true,
|
|
24
|
+
allowNull: false,
|
|
25
|
+
description: 'Unique identifier for the user',
|
|
26
|
+
label: 'User ID',
|
|
27
|
+
default: {
|
|
28
|
+
'@path': '$.userId'
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
traits: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
required: false,
|
|
34
|
+
description: 'Additional information to associate with the Company in Bucket',
|
|
35
|
+
label: 'Company Attributes',
|
|
36
|
+
default: {
|
|
37
|
+
'@path': '$.traits'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
perform: (bucket, { payload }) => {
|
|
42
|
+
// Ensure we never call Bucket.company() without a user ID
|
|
43
|
+
if (payload.userId) {
|
|
44
|
+
void bucket.company(payload.groupId, payload.traits, payload.userId)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default action
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import bucketWebDestination, { destination } from '../../index'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
import { JSONArray } from '@segment/actions-core/*'
|
|
5
|
+
import { bucketTestHooks, getBucketCallLog } from '../../test-utils'
|
|
6
|
+
|
|
7
|
+
const subscriptions: Subscription[] = [
|
|
8
|
+
{
|
|
9
|
+
partnerAction: 'identifyUser',
|
|
10
|
+
name: 'Identify User',
|
|
11
|
+
enabled: true,
|
|
12
|
+
subscribe: 'type = "identify"',
|
|
13
|
+
mapping: {
|
|
14
|
+
userId: {
|
|
15
|
+
'@path': '$.userId'
|
|
16
|
+
},
|
|
17
|
+
traits: {
|
|
18
|
+
'@path': '$.traits'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
describe('Bucket.user', () => {
|
|
25
|
+
bucketTestHooks()
|
|
26
|
+
|
|
27
|
+
test('it maps event parameters correctly to bucket.user', async () => {
|
|
28
|
+
const [identifyEvent] = await bucketWebDestination({
|
|
29
|
+
trackingKey: 'testTrackingKey',
|
|
30
|
+
subscriptions: subscriptions as unknown as JSONArray
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
await identifyEvent.load(Context.system(), new Analytics({ writeKey: 'test-writekey' }))
|
|
34
|
+
|
|
35
|
+
jest.spyOn(destination.actions.identifyUser, 'perform')
|
|
36
|
+
|
|
37
|
+
await identifyEvent.identify?.(
|
|
38
|
+
new Context({
|
|
39
|
+
type: 'identify',
|
|
40
|
+
userId: 'user-id-1',
|
|
41
|
+
traits: {
|
|
42
|
+
name: 'John Doe',
|
|
43
|
+
email: 'test-email-2@gmail.com',
|
|
44
|
+
age: 42
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
expect(destination.actions.identifyUser.perform).toHaveBeenCalledWith(
|
|
50
|
+
expect.anything(),
|
|
51
|
+
expect.objectContaining({
|
|
52
|
+
payload: {
|
|
53
|
+
userId: 'user-id-1',
|
|
54
|
+
traits: {
|
|
55
|
+
name: 'John Doe',
|
|
56
|
+
email: 'test-email-2@gmail.com',
|
|
57
|
+
age: 42
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
expect(getBucketCallLog()).toStrictEqual([
|
|
64
|
+
{ method: 'init', args: ['testTrackingKey'] },
|
|
65
|
+
{
|
|
66
|
+
method: 'user',
|
|
67
|
+
args: [
|
|
68
|
+
'user-id-1',
|
|
69
|
+
{
|
|
70
|
+
name: 'John Doe',
|
|
71
|
+
email: 'test-email-2@gmail.com',
|
|
72
|
+
age: 42
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
])
|
|
77
|
+
})
|
|
78
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* Unique identifier for the User
|
|
6
|
+
*/
|
|
7
|
+
userId: string
|
|
8
|
+
/**
|
|
9
|
+
* Additional information to associate with the User in Bucket
|
|
10
|
+
*/
|
|
11
|
+
traits?: {
|
|
12
|
+
[k: string]: unknown
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
|
|
2
|
+
import type { Settings } from '../generated-types'
|
|
3
|
+
import type { Payload } from './generated-types'
|
|
4
|
+
import type { Bucket } from '../types'
|
|
5
|
+
|
|
6
|
+
const action: BrowserActionDefinition<Settings, Bucket, Payload> = {
|
|
7
|
+
title: 'Identify User',
|
|
8
|
+
description: 'Creates or updates a user profile in Bucket. Also initializes Live Satisfaction',
|
|
9
|
+
platform: 'web',
|
|
10
|
+
defaultSubscription: 'type = "identify"',
|
|
11
|
+
fields: {
|
|
12
|
+
userId: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
required: true,
|
|
15
|
+
description: 'Unique identifier for the User',
|
|
16
|
+
label: 'User ID',
|
|
17
|
+
default: {
|
|
18
|
+
'@path': '$.userId'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
traits: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
required: false,
|
|
24
|
+
description: 'Additional information to associate with the User in Bucket',
|
|
25
|
+
label: 'User Attributes',
|
|
26
|
+
default: {
|
|
27
|
+
'@path': '$.traits'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
perform: (bucket, { payload }) => {
|
|
32
|
+
void bucket.user(payload.userId, payload.traits)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default action
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
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 { Bucket } from './types'
|
|
5
|
+
import identifyUser from './identifyUser'
|
|
6
|
+
import trackEvent from './trackEvent'
|
|
7
|
+
import { defaultValues } from '@segment/actions-core'
|
|
8
|
+
import group from './group'
|
|
9
|
+
|
|
10
|
+
declare global {
|
|
11
|
+
interface Window {
|
|
12
|
+
bucket: Bucket
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const destination: BrowserDestinationDefinition<Settings, Bucket> = {
|
|
17
|
+
name: 'Bucket Web (Actions)',
|
|
18
|
+
description:
|
|
19
|
+
'Loads the Bucket browser SDK, maps identify(), group() and track() events and enables LiveSatisfaction connections',
|
|
20
|
+
slug: 'bucket-web',
|
|
21
|
+
mode: 'device',
|
|
22
|
+
|
|
23
|
+
presets: [
|
|
24
|
+
{
|
|
25
|
+
name: 'Identify User',
|
|
26
|
+
subscribe: 'type = "identify"',
|
|
27
|
+
partnerAction: 'identifyUser',
|
|
28
|
+
mapping: defaultValues(identifyUser.fields),
|
|
29
|
+
type: 'automatic'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Group',
|
|
33
|
+
subscribe: 'type = "group"',
|
|
34
|
+
partnerAction: 'group',
|
|
35
|
+
mapping: defaultValues(group.fields),
|
|
36
|
+
type: 'automatic'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Track Event',
|
|
40
|
+
subscribe: 'type = "track"',
|
|
41
|
+
partnerAction: 'trackEvent',
|
|
42
|
+
mapping: defaultValues(trackEvent.fields),
|
|
43
|
+
type: 'automatic'
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
|
|
47
|
+
settings: {
|
|
48
|
+
trackingKey: {
|
|
49
|
+
description: 'Your Bucket App tracking key, found on the tracking page.',
|
|
50
|
+
label: 'Tracking Key',
|
|
51
|
+
type: 'string',
|
|
52
|
+
required: true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
actions: {
|
|
57
|
+
identifyUser,
|
|
58
|
+
group,
|
|
59
|
+
trackEvent
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
initialize: async ({ settings, analytics }, deps) => {
|
|
63
|
+
await deps.loadScript('https://cdn.jsdelivr.net/npm/@bucketco/tracking-sdk@2')
|
|
64
|
+
await deps.resolveWhen(() => window.bucket != undefined, 100)
|
|
65
|
+
|
|
66
|
+
window.bucket.init(settings.trackingKey)
|
|
67
|
+
|
|
68
|
+
// If the analytics client already has a logged in user from a
|
|
69
|
+
// previous session or page, consider the user logged in.
|
|
70
|
+
// In this case we need to call `bucket.user()` to set the persisted
|
|
71
|
+
// user id in bucket and initialize Live Satisfaction
|
|
72
|
+
const segmentPersistedUserId = analytics.user().id()
|
|
73
|
+
if (segmentPersistedUserId) {
|
|
74
|
+
void window.bucket.user(segmentPersistedUserId, {}, { active: false })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
analytics.on('reset', () => {
|
|
78
|
+
window.bucket.reset()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
return window.bucket
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default browserDestination(destination)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import nock from 'nock'
|
|
2
|
+
import { Bucket } from 'src/types'
|
|
3
|
+
|
|
4
|
+
const bucketTestMock = `
|
|
5
|
+
(() => {
|
|
6
|
+
const noop = () => {};
|
|
7
|
+
|
|
8
|
+
const bucketTestInterface = {
|
|
9
|
+
init: noop,
|
|
10
|
+
user: noop,
|
|
11
|
+
company: noop,
|
|
12
|
+
track: noop,
|
|
13
|
+
reset: noop
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const callLog = [];
|
|
17
|
+
|
|
18
|
+
window.bucket = new Proxy(bucketTestInterface, {
|
|
19
|
+
get(bucket, property) {
|
|
20
|
+
if (typeof bucket[property] === 'function') {
|
|
21
|
+
return (...args) => {
|
|
22
|
+
callLog.push({ method: property, args })
|
|
23
|
+
return bucket[property](...args)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (property === 'callLog') {
|
|
28
|
+
return callLog;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
})();
|
|
33
|
+
`
|
|
34
|
+
|
|
35
|
+
export function bucketTestHooks() {
|
|
36
|
+
beforeAll(() => {
|
|
37
|
+
nock.disableNetConnect()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
nock('https://cdn.jsdelivr.net').get('/npm/@bucketco/tracking-sdk@2').reply(200, bucketTestMock)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
afterEach(function () {
|
|
45
|
+
if (!nock.isDone()) {
|
|
46
|
+
// @ts-expect-error no-unsafe-call
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
48
|
+
this.test.error(new Error('Not all nock interceptors were used!'))
|
|
49
|
+
nock.cleanAll()
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
afterAll(() => {
|
|
54
|
+
nock.enableNetConnect()
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getBucketCallLog() {
|
|
59
|
+
return (window.bucket as unknown as { callLog: Array<{ method: keyof Bucket; args: Array<unknown> }> }).callLog
|
|
60
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Analytics, Context, User } from '@segment/analytics-next'
|
|
2
|
+
import bucketWebDestination, { destination } from '../../index'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
import { JSONArray } from '@segment/actions-core/*'
|
|
5
|
+
import { bucketTestHooks, getBucketCallLog } from '../../test-utils'
|
|
6
|
+
|
|
7
|
+
const subscriptions: Subscription[] = [
|
|
8
|
+
{
|
|
9
|
+
partnerAction: 'trackEvent',
|
|
10
|
+
name: 'Track Event',
|
|
11
|
+
enabled: true,
|
|
12
|
+
subscribe: 'type = "track"',
|
|
13
|
+
mapping: {
|
|
14
|
+
name: {
|
|
15
|
+
'@path': '$.name'
|
|
16
|
+
},
|
|
17
|
+
userId: {
|
|
18
|
+
'@path': '$.userId'
|
|
19
|
+
},
|
|
20
|
+
properties: {
|
|
21
|
+
'@path': '$.properties'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
describe('trackEvent', () => {
|
|
28
|
+
bucketTestHooks()
|
|
29
|
+
|
|
30
|
+
describe('when logged in', () => {
|
|
31
|
+
describe('from analytics.js previous session', () => {
|
|
32
|
+
it('maps parameters correctly to Bucket', async () => {
|
|
33
|
+
const [bucketPlugin] = await bucketWebDestination({
|
|
34
|
+
trackingKey: 'testTrackingKey',
|
|
35
|
+
subscriptions: subscriptions as unknown as JSONArray
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const analyticsInstance = new Analytics({ writeKey: 'test-writekey' })
|
|
39
|
+
jest.spyOn(analyticsInstance, 'user').mockImplementation(
|
|
40
|
+
() =>
|
|
41
|
+
({
|
|
42
|
+
id: () => 'user-id-1'
|
|
43
|
+
} as User)
|
|
44
|
+
)
|
|
45
|
+
await bucketPlugin.load(Context.system(), analyticsInstance)
|
|
46
|
+
|
|
47
|
+
jest.spyOn(destination.actions.trackEvent, 'perform')
|
|
48
|
+
|
|
49
|
+
const properties = { property1: 'value1', property2: false }
|
|
50
|
+
await bucketPlugin.track?.(
|
|
51
|
+
new Context({
|
|
52
|
+
type: 'track',
|
|
53
|
+
name: 'Button Clicked',
|
|
54
|
+
userId: 'user-id-1',
|
|
55
|
+
properties
|
|
56
|
+
})
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
expect(destination.actions.trackEvent.perform).toHaveBeenCalledWith(
|
|
60
|
+
expect.anything(),
|
|
61
|
+
expect.objectContaining({
|
|
62
|
+
payload: { name: 'Button Clicked', userId: 'user-id-1', properties }
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
expect(getBucketCallLog()).toStrictEqual([
|
|
67
|
+
{ method: 'init', args: ['testTrackingKey'] },
|
|
68
|
+
{
|
|
69
|
+
method: 'user',
|
|
70
|
+
args: ['user-id-1', {}, { active: false }]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
method: 'track',
|
|
74
|
+
args: ['Button Clicked', properties, 'user-id-1']
|
|
75
|
+
}
|
|
76
|
+
])
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('from am identify call', () => {
|
|
81
|
+
it('maps parameters correctly to Bucket', async () => {
|
|
82
|
+
const [bucketPlugin] = await bucketWebDestination({
|
|
83
|
+
trackingKey: 'testTrackingKey',
|
|
84
|
+
subscriptions: subscriptions as unknown as JSONArray
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
await bucketPlugin.load(Context.system(), new Analytics({ writeKey: 'test-writekey' }))
|
|
88
|
+
|
|
89
|
+
jest.spyOn(destination.actions.trackEvent, 'perform')
|
|
90
|
+
|
|
91
|
+
// Bucket rejects group calls without previous identify calls
|
|
92
|
+
await window.bucket.user('user-id-1')
|
|
93
|
+
|
|
94
|
+
const properties = { property1: 'value1', property2: false }
|
|
95
|
+
await bucketPlugin.track?.(
|
|
96
|
+
new Context({
|
|
97
|
+
type: 'track',
|
|
98
|
+
name: 'Button Clicked',
|
|
99
|
+
userId: 'user-id-1',
|
|
100
|
+
properties
|
|
101
|
+
})
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
expect(destination.actions.trackEvent.perform).toHaveBeenCalledWith(
|
|
105
|
+
expect.anything(),
|
|
106
|
+
expect.objectContaining({
|
|
107
|
+
payload: { name: 'Button Clicked', userId: 'user-id-1', properties }
|
|
108
|
+
})
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
expect(getBucketCallLog()).toStrictEqual([
|
|
112
|
+
{ method: 'init', args: ['testTrackingKey'] },
|
|
113
|
+
{
|
|
114
|
+
method: 'user',
|
|
115
|
+
args: ['user-id-1']
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
method: 'track',
|
|
119
|
+
args: ['Button Clicked', properties, 'user-id-1']
|
|
120
|
+
}
|
|
121
|
+
])
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('when not logged in', () => {
|
|
127
|
+
it('should not call Bucket.group', async () => {
|
|
128
|
+
const [bucketPlugin] = await bucketWebDestination({
|
|
129
|
+
trackingKey: 'testTrackingKey',
|
|
130
|
+
subscriptions: subscriptions as unknown as JSONArray
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const analyticsInstance = new Analytics({ writeKey: 'test-writekey' })
|
|
134
|
+
await bucketPlugin.load(Context.system(), analyticsInstance)
|
|
135
|
+
|
|
136
|
+
jest.spyOn(destination.actions.trackEvent, 'perform')
|
|
137
|
+
|
|
138
|
+
// Manually mimicking a track call without a userId.
|
|
139
|
+
// The analytics client will probably never do this if
|
|
140
|
+
// userId doesn't exist, since the subscription marks it as required
|
|
141
|
+
const properties = { property1: 'value1', property2: false }
|
|
142
|
+
await bucketPlugin.track?.(
|
|
143
|
+
new Context({
|
|
144
|
+
type: 'track',
|
|
145
|
+
name: 'Button Clicked',
|
|
146
|
+
anonymousId: 'user-id-1',
|
|
147
|
+
properties
|
|
148
|
+
})
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
// TODO: Ideally we should be able to assert that the destination action was never
|
|
152
|
+
// called, but couldn't figure out how to create an anlytics instance with the plugin
|
|
153
|
+
// and then trigger the full flow trhough analytics.track() with only an anonymous ID
|
|
154
|
+
// expect(destination.actions.trackEvent.perform).not.toHaveBeenCalled()
|
|
155
|
+
|
|
156
|
+
expect(getBucketCallLog()).toStrictEqual([{ method: 'init', args: ['testTrackingKey'] }])
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
})
|