@segment/analytics-browser-actions-braze 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 ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@segment/analytics-browser-actions-braze",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "exports": {
6
+ ".": {
7
+ "require": "./dist/cjs/index.js",
8
+ "import": "./dist/esm/index.js"
9
+ },
10
+ "./debounce": {
11
+ "require": "./dist/cjs/debounce/index.js",
12
+ "import": "./dist/esm/debounce/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "yarn build:esm && yarn build:cjs",
17
+ "build:cjs": "tsc --module commonjs --outDir ./dist/cjs",
18
+ "build:esm": "tsc --outDir ./dist/esm"
19
+ },
20
+ "typesVersions": {
21
+ "*": {
22
+ "*": [
23
+ "dist/esm/index.d.ts"
24
+ ],
25
+ "debounce": [
26
+ "dist/esm/debounce/index.d.ts"
27
+ ]
28
+ }
29
+ },
30
+ "typings": "./dist/esm",
31
+ "dependencies": {
32
+ "@braze/web-sdk": "npm:@braze/web-sdk@^4.1.0",
33
+ "@braze/web-sdk-v3": "npm:@braze/web-sdk@^3.5.1",
34
+ "@segment/actions-core": "^3.71.0",
35
+ "@segment/browser-destination-runtime": "^1.0.0"
36
+ },
37
+ "peerDependencies": {
38
+ "@segment/analytics-next": "*"
39
+ }
40
+ }
@@ -0,0 +1,24 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`initialization can load braze:
4
+ NodeList [
5
+ <script
6
+ src="https://js.appboycdn.com/web-sdk/3.5/appboy.no-amd.min.js"
7
+ type="text/javascript"
8
+ />,
9
+ <script>
10
+ // the emptiness
11
+ </script>,
12
+ ]
13
+ 1`] = `
14
+ NodeList [
15
+ <script
16
+ src="https://js.appboycdn.com/web-sdk/3.5/appboy.no-amd.min.js"
17
+ status="loaded"
18
+ type="text/javascript"
19
+ />,
20
+ <script>
21
+ // the emptiness
22
+ </script>,
23
+ ]
24
+ `;
@@ -0,0 +1,96 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`loads different versions from CDN 3.0:
4
+ NodeList [
5
+ <script
6
+ src="https://js.appboycdn.com/web-sdk/3.0/appboy.no-amd.min.js"
7
+ type="text/javascript"
8
+ />,
9
+ <script>
10
+ // the emptiness
11
+ </script>,
12
+ ]
13
+ 1`] = `
14
+ NodeList [
15
+ <script
16
+ src="https://js.appboycdn.com/web-sdk/3.0/appboy.no-amd.min.js"
17
+ status="loaded"
18
+ type="text/javascript"
19
+ />,
20
+ <script>
21
+ // the emptiness
22
+ </script>,
23
+ ]
24
+ `;
25
+
26
+ exports[`loads different versions from CDN 3.1:
27
+ NodeList [
28
+ <script
29
+ src="https://js.appboycdn.com/web-sdk/3.1/appboy.no-amd.min.js"
30
+ status="loaded"
31
+ type="text/javascript"
32
+ />,
33
+ <script>
34
+ // the emptiness
35
+ </script>,
36
+ ]
37
+ 1`] = `
38
+ NodeList [
39
+ <script
40
+ src="https://js.appboycdn.com/web-sdk/3.1/appboy.no-amd.min.js"
41
+ status="loaded"
42
+ type="text/javascript"
43
+ />,
44
+ <script>
45
+ // the emptiness
46
+ </script>,
47
+ ]
48
+ `;
49
+
50
+ exports[`loads different versions from CDN 3.5:
51
+ NodeList [
52
+ <script
53
+ src="https://js.appboycdn.com/web-sdk/3.5/appboy.no-amd.min.js"
54
+ status="loaded"
55
+ type="text/javascript"
56
+ />,
57
+ <script>
58
+ // the emptiness
59
+ </script>,
60
+ ]
61
+ 1`] = `
62
+ NodeList [
63
+ <script
64
+ src="https://js.appboycdn.com/web-sdk/3.5/appboy.no-amd.min.js"
65
+ status="loaded"
66
+ type="text/javascript"
67
+ />,
68
+ <script>
69
+ // the emptiness
70
+ </script>,
71
+ ]
72
+ `;
73
+
74
+ exports[`loads different versions from CDN undefined version:
75
+ NodeList [
76
+ <script
77
+ src="https://js.appboycdn.com/web-sdk/4.6/braze.no-module.min.js"
78
+ status="loaded"
79
+ type="text/javascript"
80
+ />,
81
+ <script>
82
+ // the emptiness
83
+ </script>,
84
+ ]
85
+ 1`] = `
86
+ NodeList [
87
+ <script
88
+ src="https://js.appboycdn.com/web-sdk/4.6/braze.no-module.min.js"
89
+ status="loaded"
90
+ type="text/javascript"
91
+ />,
92
+ <script>
93
+ // the emptiness
94
+ </script>,
95
+ ]
96
+ `;
@@ -0,0 +1,97 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`reports products when present (v3.5) 1`] = `
4
+ Array [
5
+ "p_123",
6
+ 399,
7
+ "BGP",
8
+ 2,
9
+ Object {
10
+ "banana": "yellow",
11
+ "products": Array [
12
+ Object {
13
+ "currency": "BGP",
14
+ "price": 399,
15
+ "product_id": "p_123",
16
+ "quantity": 2,
17
+ },
18
+ Object {
19
+ "price": 0,
20
+ "product_id": "p_456",
21
+ },
22
+ ],
23
+ },
24
+ ]
25
+ `;
26
+
27
+ exports[`reports products when present (v3.5) 2`] = `
28
+ Array [
29
+ "p_456",
30
+ 0,
31
+ "USD",
32
+ 1,
33
+ Object {
34
+ "banana": "yellow",
35
+ "products": Array [
36
+ Object {
37
+ "currency": "BGP",
38
+ "price": 399,
39
+ "product_id": "p_123",
40
+ "quantity": 2,
41
+ },
42
+ Object {
43
+ "price": 0,
44
+ "product_id": "p_456",
45
+ },
46
+ ],
47
+ },
48
+ ]
49
+ `;
50
+
51
+ exports[`reports products when present (v4.1) 1`] = `
52
+ Array [
53
+ "p_123",
54
+ 399,
55
+ "BGP",
56
+ 2,
57
+ Object {
58
+ "banana": "yellow",
59
+ "products": Array [
60
+ Object {
61
+ "currency": "BGP",
62
+ "price": 399,
63
+ "product_id": "p_123",
64
+ "quantity": 2,
65
+ },
66
+ Object {
67
+ "price": 0,
68
+ "product_id": "p_456",
69
+ },
70
+ ],
71
+ },
72
+ ]
73
+ `;
74
+
75
+ exports[`reports products when present (v4.1) 2`] = `
76
+ Array [
77
+ "p_456",
78
+ 0,
79
+ "USD",
80
+ 1,
81
+ Object {
82
+ "banana": "yellow",
83
+ "products": Array [
84
+ Object {
85
+ "currency": "BGP",
86
+ "price": 399,
87
+ "product_id": "p_123",
88
+ "quantity": 2,
89
+ },
90
+ Object {
91
+ "price": 0,
92
+ "product_id": "p_456",
93
+ },
94
+ ],
95
+ },
96
+ ]
97
+ `;
@@ -0,0 +1,133 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import brazeDestination from '../index'
3
+
4
+ let ajs: Analytics
5
+
6
+ describe('debounce', () => {
7
+ beforeEach(async () => {
8
+ ajs = new Analytics({
9
+ writeKey: 'w_123'
10
+ })
11
+ })
12
+
13
+ test('changes the integration object', async () => {
14
+ const [debounce] = await brazeDestination({
15
+ api_key: 'b_123',
16
+ endpoint: 'endpoint',
17
+ doNotLoadFontAwesome: true,
18
+ sdkVersion: '3.5',
19
+ subscriptions: [
20
+ {
21
+ partnerAction: 'debounce',
22
+ name: 'Debounce',
23
+ enabled: true,
24
+ subscribe: 'type = "identify"',
25
+ mapping: {}
26
+ }
27
+ ]
28
+ })
29
+
30
+ await debounce.load(Context.system(), ajs)
31
+ const ctx = await debounce.identify?.(
32
+ new Context({
33
+ type: 'identify',
34
+ userId: 'hasbulla',
35
+ anonymousId: 'the goat',
36
+ traits: {}
37
+ })
38
+ )
39
+
40
+ expect(ctx.event.integrations['Braze Web Mode (Actions)']).toBe(true)
41
+ })
42
+
43
+ test('does not send the event to braze if IDs are the same', async () => {
44
+ const [debounce] = await brazeDestination({
45
+ api_key: 'b_123',
46
+ endpoint: 'endpoint',
47
+ doNotLoadFontAwesome: true,
48
+ sdkVersion: '3.5',
49
+ subscriptions: [
50
+ {
51
+ partnerAction: 'debounce',
52
+ name: 'Debounce',
53
+ enabled: true,
54
+ subscribe: 'type = "identify"',
55
+ mapping: {}
56
+ }
57
+ ]
58
+ })
59
+
60
+ await ajs.register(debounce)
61
+
62
+ const ctx = await ajs.identify('hasbulla', {
63
+ goat: true
64
+ })
65
+ expect(ctx.event.integrations['Braze Web Mode (Actions)']).toBe(true)
66
+
67
+ const secondCtx = await ajs.identify('hasbulla', {
68
+ goat: true
69
+ })
70
+ expect(secondCtx.event.integrations['Braze Web Mode (Actions)']).toBe(false)
71
+ })
72
+
73
+ test('ignores blank anonymous ids', async () => {
74
+ const [debounce] = await brazeDestination({
75
+ api_key: 'b_123',
76
+ endpoint: 'endpoint',
77
+ doNotLoadFontAwesome: true,
78
+ sdkVersion: '4.1',
79
+ subscriptions: [
80
+ {
81
+ partnerAction: 'debounce',
82
+ name: 'Debounce',
83
+ enabled: true,
84
+ subscribe: 'type = "identify"',
85
+ mapping: {}
86
+ }
87
+ ]
88
+ })
89
+
90
+ await ajs.register(debounce)
91
+
92
+ const ctx = await ajs.identify()
93
+ expect(ctx.event.integrations['Braze Web Mode (Actions)']).toBe(true)
94
+
95
+ const secondCtx = await ajs.identify()
96
+ expect(secondCtx.event.integrations['Braze Web Mode (Actions)']).toBe(false)
97
+ })
98
+
99
+ test('send events on trait changes', async () => {
100
+ const [debounce] = await brazeDestination({
101
+ api_key: 'b_123',
102
+ endpoint: 'endpoint',
103
+ doNotLoadFontAwesome: true,
104
+ sdkVersion: '4.1',
105
+ subscriptions: [
106
+ {
107
+ partnerAction: 'debounce',
108
+ name: 'Debounce',
109
+ enabled: true,
110
+ subscribe: 'type = "identify"',
111
+ mapping: {}
112
+ }
113
+ ]
114
+ })
115
+
116
+ await ajs.register(debounce)
117
+
118
+ const ctx = await ajs.identify('hasbulla', {
119
+ goat: true
120
+ })
121
+ expect(ctx.event.integrations['Braze Web Mode (Actions)']).toBe(true)
122
+
123
+ const sameCtx = await ajs.identify('hasbulla', {
124
+ goat: true
125
+ })
126
+ expect(sameCtx.event.integrations['Braze Web Mode (Actions)']).toBe(false)
127
+
128
+ const changedTraits = await ajs.identify('hasbulla', {
129
+ weight: 'feather'
130
+ })
131
+ expect(changedTraits.event.integrations['Braze Web Mode (Actions)']).toBe(true)
132
+ })
133
+ })
@@ -0,0 +1,139 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import { Subscription } from '@segment/browser-destination-runtime'
3
+ import brazeDestination, { destination } from '../index'
4
+
5
+ describe('initialization', () => {
6
+ const settings = {
7
+ safariWebsitePushId: 'safari',
8
+ allowCrawlerActivity: true,
9
+ doNotLoadFontAwesome: true,
10
+ enableLogging: false,
11
+ localization: 'pt',
12
+ minimumIntervalBetweenTriggerActionsInSeconds: 60,
13
+ openInAppMessagesInNewTab: true,
14
+ sessionTimeoutInSeconds: 60,
15
+ requireExplicitInAppMessageDismissal: true,
16
+ allowUserSuppliedJavascript: true,
17
+ contentSecurityNonce: 'bar',
18
+ endpoint: 'endpoint',
19
+ sdkVersion: '3.5'
20
+ }
21
+
22
+ beforeEach(async () => {
23
+ jest.restoreAllMocks()
24
+ jest.resetAllMocks()
25
+ })
26
+
27
+ test('can load braze', async () => {
28
+ const [event] = await brazeDestination({
29
+ api_key: 'b_123',
30
+ subscriptions: [
31
+ {
32
+ partnerAction: 'trackPurchase',
33
+ name: 'Log Custom Event',
34
+ enabled: true,
35
+ subscribe: 'type = "track"',
36
+ mapping: {
37
+ eventName: {
38
+ '@path': '$.event'
39
+ },
40
+ eventProperties: {
41
+ '@path': '$.properties'
42
+ }
43
+ }
44
+ }
45
+ ],
46
+ ...settings
47
+ })
48
+
49
+ jest.spyOn(destination.actions.trackPurchase, 'perform')
50
+ jest.spyOn(destination, 'initialize')
51
+
52
+ await event.load(Context.system(), {} as Analytics)
53
+ expect(destination.initialize).toHaveBeenCalled()
54
+
55
+ const ctx = await event.track?.(
56
+ new Context({
57
+ type: 'track',
58
+ properties: {
59
+ banana: '📞'
60
+ }
61
+ })
62
+ )
63
+
64
+ expect(destination.actions.trackPurchase.perform).toHaveBeenCalled()
65
+ expect(ctx).not.toBeUndefined()
66
+
67
+ const scripts = window.document.querySelectorAll('script')
68
+
69
+ expect(scripts).toMatchSnapshot(`
70
+ NodeList [
71
+ <script
72
+ src="https://js.appboycdn.com/web-sdk/3.5/appboy.no-amd.min.js"
73
+ type="text/javascript"
74
+ />,
75
+ <script>
76
+ // the emptiness
77
+ </script>,
78
+ ]
79
+ `)
80
+ })
81
+
82
+ test('can defer braze initialization when deferUntilIdentified is on', async () => {
83
+ const [updateUserProfile, trackEvent] = await brazeDestination({
84
+ api_key: 'b_123',
85
+ deferUntilIdentified: true,
86
+ subscriptions: destination.presets?.map((sub) => ({ ...sub, enabled: true })) as Subscription[],
87
+ ...settings
88
+ })
89
+
90
+ jest.spyOn(destination.actions.trackEvent, 'perform')
91
+ const initializeSpy = jest.spyOn(destination, 'initialize')
92
+
93
+ const analytics = new Analytics({ writeKey: '123' })
94
+
95
+ await analytics.register(updateUserProfile, trackEvent)
96
+
97
+ // Spy on the braze APIs now that braze has been loaded.
98
+ const { instance: braze } = await initializeSpy.mock.results[0].value
99
+ const openSessionSpy = jest.spyOn(braze, 'openSession')
100
+ const logCustomEventSpy = jest.spyOn(braze, 'logCustomEvent')
101
+
102
+ await analytics.track?.({
103
+ type: 'track',
104
+ event: 'UFC',
105
+ properties: {
106
+ goat: 'hasbulla'
107
+ }
108
+ })
109
+
110
+ expect(destination.actions.trackEvent.perform).toHaveBeenCalledWith(
111
+ expect.objectContaining({
112
+ instance: expect.objectContaining({
113
+ logCustomEvent: expect.any(Function)
114
+ })
115
+ }),
116
+
117
+ expect.objectContaining({
118
+ payload: { eventName: 'UFC', eventProperties: { goat: 'hasbulla' } }
119
+ })
120
+ )
121
+
122
+ expect(analytics.user().id()).toBe(null)
123
+ expect(openSessionSpy).not.toHaveBeenCalled()
124
+ expect(logCustomEventSpy).not.toHaveBeenCalled()
125
+
126
+ await analytics.identify('27413')
127
+
128
+ await analytics.track?.({
129
+ type: 'track',
130
+ event: 'FIFA',
131
+ properties: {
132
+ goat: 'deno'
133
+ }
134
+ })
135
+
136
+ expect(openSessionSpy).toHaveBeenCalled()
137
+ expect(logCustomEventSpy).toHaveBeenCalledWith('FIFA', { goat: 'deno' })
138
+ })
139
+ })
@@ -0,0 +1,156 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import braze, { destination } from '..'
3
+ import type { Subscription } from '@segment/browser-destination-runtime/types'
4
+
5
+ const example: Subscription[] = [
6
+ {
7
+ partnerAction: 'trackEvent',
8
+ name: 'Log Custom Event',
9
+ enabled: true,
10
+ subscribe: 'type = "track"',
11
+ mapping: {
12
+ name: {
13
+ '@path': '$.name'
14
+ },
15
+ properties: {
16
+ '@path': '$.properties'
17
+ }
18
+ }
19
+ }
20
+ ]
21
+
22
+ test('can load braze', async () => {
23
+ const [trackEvent] = await braze({
24
+ api_key: 'api_key',
25
+ endpoint: 'sdk.iad-01.braze.com',
26
+ subscriptions: example,
27
+ doNotLoadFontAwesome: true,
28
+ sdkVersion: '3.5'
29
+ })
30
+
31
+ jest.spyOn(destination.actions.trackEvent, 'perform')
32
+ jest.spyOn(destination, 'initialize')
33
+
34
+ await trackEvent.load(Context.system(), {} as Analytics)
35
+ expect(destination.initialize).toHaveBeenCalled()
36
+
37
+ const ctx = await trackEvent.track?.(
38
+ new Context({
39
+ type: 'track',
40
+ properties: {
41
+ banana: '📞'
42
+ }
43
+ })
44
+ )
45
+
46
+ expect(destination.actions.trackEvent.perform).toHaveBeenCalled()
47
+ expect(ctx).not.toBeUndefined()
48
+ })
49
+
50
+ describe('loads different versions from CDN', () => {
51
+ test('3.0', async () => {
52
+ const [trackEvent] = await braze({
53
+ api_key: 'api_key',
54
+ endpoint: 'sdk.iad-01.braze.com',
55
+ sdkVersion: '3.0',
56
+ doNotLoadFontAwesome: true,
57
+ subscriptions: example
58
+ })
59
+
60
+ await trackEvent.load(Context.system(), {} as Analytics)
61
+
62
+ const scripts = window.document.querySelectorAll('script')
63
+ expect(scripts).toMatchSnapshot(`
64
+ NodeList [
65
+ <script
66
+ src="https://js.appboycdn.com/web-sdk/3.0/appboy.no-amd.min.js"
67
+ type="text/javascript"
68
+ />,
69
+ <script>
70
+ // the emptiness
71
+ </script>,
72
+ ]
73
+ `)
74
+ })
75
+
76
+ test('3.1', async () => {
77
+ const [trackEvent] = await braze({
78
+ api_key: 'api_key',
79
+ endpoint: 'sdk.iad-01.braze.com',
80
+ sdkVersion: '3.1',
81
+ doNotLoadFontAwesome: true,
82
+ subscriptions: example
83
+ })
84
+
85
+ await trackEvent.load(Context.system(), {} as Analytics)
86
+
87
+ const scripts = window.document.querySelectorAll('script')
88
+ // loads the service worker
89
+ expect(scripts).toMatchSnapshot(`
90
+ NodeList [
91
+ <script
92
+ src="https://js.appboycdn.com/web-sdk/3.1/appboy.no-amd.min.js"
93
+ status="loaded"
94
+ type="text/javascript"
95
+ />,
96
+ <script>
97
+ // the emptiness
98
+ </script>,
99
+ ]
100
+ `)
101
+ })
102
+
103
+ test('3.5', async () => {
104
+ const [trackEvent] = await braze({
105
+ api_key: 'api_key',
106
+ endpoint: 'sdk.iad-01.braze.com',
107
+ sdkVersion: '3.5',
108
+ doNotLoadFontAwesome: true,
109
+ subscriptions: example
110
+ })
111
+
112
+ await trackEvent.load(Context.system(), {} as Analytics)
113
+
114
+ const scripts = window.document.querySelectorAll('script')
115
+ // loads the service worker
116
+ expect(scripts).toMatchSnapshot(`
117
+ NodeList [
118
+ <script
119
+ src="https://js.appboycdn.com/web-sdk/3.5/appboy.no-amd.min.js"
120
+ status="loaded"
121
+ type="text/javascript"
122
+ />,
123
+ <script>
124
+ // the emptiness
125
+ </script>,
126
+ ]
127
+ `)
128
+ })
129
+
130
+ test('undefined version', async () => {
131
+ //@ts-expect-error sdkVersion is expected but undefined
132
+ const [trackEvent] = await braze({
133
+ api_key: 'api_key',
134
+ endpoint: 'sdk.iad-01.braze.com',
135
+ doNotLoadFontAwesome: true,
136
+ subscriptions: example
137
+ })
138
+
139
+ await trackEvent.load(Context.system(), {} as Analytics)
140
+
141
+ const scripts = window.document.querySelectorAll('script')
142
+ // loads the service worker
143
+ expect(scripts).toMatchSnapshot(`
144
+ NodeList [
145
+ <script
146
+ src="https://js.appboycdn.com/web-sdk/4.6/braze.no-module.min.js"
147
+ status="loaded"
148
+ type="text/javascript"
149
+ />,
150
+ <script>
151
+ // the emptiness
152
+ </script>,
153
+ ]
154
+ `)
155
+ })
156
+ })