@segment/analytics-browser-actions-screeb 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 +20 -0
- package/src/__tests__/index.test.ts +41 -0
- package/src/alias/__tests__/index.test.ts +93 -0
- package/src/alias/generated-types.ts +12 -0
- package/src/alias/index.ts +45 -0
- package/src/alias.types.ts +18 -0
- package/src/generated-types.ts +8 -0
- package/src/group/__tests__/index.test.ts +109 -0
- package/src/group/generated-types.ts +18 -0
- package/src/group/index.ts +49 -0
- package/src/identify/__tests__/index.test.ts +116 -0
- package/src/identify/generated-types.ts +18 -0
- package/src/identify/index.ts +56 -0
- package/src/index.ts +83 -0
- package/src/track/__tests__/index.test.ts +69 -0
- package/src/track/generated-types.ts +14 -0
- package/src/track/index.ts +42 -0
- package/src/types.ts +4 -0
- package/tsconfig.json +9 -0
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@segment/analytics-browser-actions-screeb",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "./dist/cjs",
|
|
6
|
+
"module": "./dist/esm",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "yarn build:esm && yarn build:cjs",
|
|
9
|
+
"build:cjs": "tsc --module commonjs --outDir ./dist/cjs",
|
|
10
|
+
"build:esm": "tsc --outDir ./dist/esm"
|
|
11
|
+
},
|
|
12
|
+
"typings": "./dist/esm",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@segment/actions-core": "^3.71.0",
|
|
15
|
+
"@segment/browser-destination-runtime": "^1.0.0"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@segment/analytics-next": "*"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import screebDestination, { destination } from '../index'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
|
|
5
|
+
const subscriptions: Subscription[] = [
|
|
6
|
+
{
|
|
7
|
+
partnerAction: 'track',
|
|
8
|
+
name: 'Track',
|
|
9
|
+
enabled: true,
|
|
10
|
+
subscribe: 'type = "track"',
|
|
11
|
+
mapping: {
|
|
12
|
+
name: {
|
|
13
|
+
'@path': '$.name'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
describe('Screeb initialization', () => {
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
jest.mock('@segment/browser-destination-runtime/load-script', () => ({
|
|
22
|
+
loadScript: (_src: any, _attributes: any) => {}
|
|
23
|
+
}))
|
|
24
|
+
jest.mock('@segment/browser-destination-runtime/resolve-when', () => ({
|
|
25
|
+
resolveWhen: (_fn: any, _timeout: any) => {}
|
|
26
|
+
}))
|
|
27
|
+
})
|
|
28
|
+
test('can load Screeb', async () => {
|
|
29
|
+
const [event] = await screebDestination({
|
|
30
|
+
websiteId: 'fake-website-id',
|
|
31
|
+
subscriptions
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
jest.spyOn(destination, 'initialize')
|
|
35
|
+
|
|
36
|
+
await event.load(Context.system(), {} as Analytics)
|
|
37
|
+
expect(destination.initialize).toHaveBeenCalled()
|
|
38
|
+
|
|
39
|
+
expect(window.$screeb.q).toStrictEqual([['init', 'fake-website-id']])
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import screebDestination, { destination } from '../../index'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
|
|
5
|
+
const subscriptions: Subscription[] = [
|
|
6
|
+
{
|
|
7
|
+
partnerAction: 'alias',
|
|
8
|
+
name: 'Alias',
|
|
9
|
+
enabled: true,
|
|
10
|
+
subscribe: 'type = "alias"',
|
|
11
|
+
mapping: {
|
|
12
|
+
userId: {
|
|
13
|
+
'@path': '$.userId'
|
|
14
|
+
},
|
|
15
|
+
anonymousId: {
|
|
16
|
+
'@path': '$.anonymousId'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
describe('alias', () => {
|
|
23
|
+
beforeAll(() => {
|
|
24
|
+
jest.mock('@segment/browser-destination-runtime/load-script', () => ({
|
|
25
|
+
loadScript: (_src: any, _attributes: any) => {}
|
|
26
|
+
}))
|
|
27
|
+
jest.mock('@segment/browser-destination-runtime/resolve-when', () => ({
|
|
28
|
+
resolveWhen: (_fn: any, _timeout: any) => {}
|
|
29
|
+
}))
|
|
30
|
+
})
|
|
31
|
+
test('it maps event parameters correctly to alias function', async () => {
|
|
32
|
+
const [alias] = await screebDestination({
|
|
33
|
+
websiteId: 'fake-website-id',
|
|
34
|
+
subscriptions
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
jest.spyOn(destination.actions.alias, 'perform')
|
|
38
|
+
await alias.load(Context.system(), {} as Analytics)
|
|
39
|
+
|
|
40
|
+
await alias.alias?.(
|
|
41
|
+
new Context({
|
|
42
|
+
type: 'alias',
|
|
43
|
+
userId: 'user-id',
|
|
44
|
+
anonymousId: 'anonymous-id'
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
expect(destination.actions.alias.perform).toHaveBeenCalledWith(
|
|
49
|
+
expect.anything(),
|
|
50
|
+
expect.objectContaining({
|
|
51
|
+
payload: {
|
|
52
|
+
userId: 'user-id',
|
|
53
|
+
anonymousId: 'anonymous-id'
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
expect(window.$screeb.q).toStrictEqual([
|
|
59
|
+
['init', 'fake-website-id'],
|
|
60
|
+
['identity', 'user-id']
|
|
61
|
+
])
|
|
62
|
+
})
|
|
63
|
+
test('it maps event parameters correctly to alias function without user id but anonymous id', async () => {
|
|
64
|
+
const [alias] = await screebDestination({
|
|
65
|
+
websiteId: 'fake-website-id',
|
|
66
|
+
subscriptions
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
jest.spyOn(destination.actions.alias, 'perform')
|
|
70
|
+
await alias.load(Context.system(), {} as Analytics)
|
|
71
|
+
|
|
72
|
+
await alias.alias?.(
|
|
73
|
+
new Context({
|
|
74
|
+
type: 'alias',
|
|
75
|
+
anonymousId: 'anonymous-id'
|
|
76
|
+
})
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
expect(destination.actions.alias.perform).toHaveBeenCalledWith(
|
|
80
|
+
expect.anything(),
|
|
81
|
+
expect.objectContaining({
|
|
82
|
+
payload: {
|
|
83
|
+
anonymousId: 'anonymous-id'
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
expect(window.$screeb.q).toStrictEqual([
|
|
89
|
+
['init', 'fake-website-id'],
|
|
90
|
+
['identity', 'anonymous-id']
|
|
91
|
+
])
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
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 { Screeb } from '../types'
|
|
5
|
+
|
|
6
|
+
const action: BrowserActionDefinition<Settings, Screeb, Payload> = {
|
|
7
|
+
title: 'Alias',
|
|
8
|
+
description: 'Update user identity with new user ID.',
|
|
9
|
+
platform: 'web',
|
|
10
|
+
defaultSubscription: 'type = "alias"',
|
|
11
|
+
fields: {
|
|
12
|
+
userId: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
required: false,
|
|
15
|
+
description: 'New unique identifier for the user',
|
|
16
|
+
label: 'User ID',
|
|
17
|
+
default: {
|
|
18
|
+
'@path': '$.userId'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
anonymousId: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
required: false,
|
|
24
|
+
description: 'New anonymous identifier for the user',
|
|
25
|
+
label: 'Anonymous ID',
|
|
26
|
+
default: {
|
|
27
|
+
'@path': '$.anonymousId'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
perform: (Screeb, event) => {
|
|
32
|
+
const payload = event.payload
|
|
33
|
+
if (!payload || typeof payload !== 'object' || !(payload.userId || payload.anonymousId)) {
|
|
34
|
+
console.warn(
|
|
35
|
+
'[Screeb] received invalid payload (expected userId or anonymousId to be present); skipping alias',
|
|
36
|
+
payload
|
|
37
|
+
)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Screeb('identity', payload.userId ?? payload.anonymousId)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default action
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The group id
|
|
6
|
+
*/
|
|
7
|
+
groupId: string
|
|
8
|
+
/**
|
|
9
|
+
* The group type
|
|
10
|
+
*/
|
|
11
|
+
groupType: string
|
|
12
|
+
/**
|
|
13
|
+
* Traits to associate with the group
|
|
14
|
+
*/
|
|
15
|
+
properties?: {
|
|
16
|
+
[k: string]: unknown
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import screebDestination, { destination } from '../../index'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
|
|
5
|
+
const subscriptions: Subscription[] = [
|
|
6
|
+
{
|
|
7
|
+
partnerAction: 'group',
|
|
8
|
+
name: 'Group',
|
|
9
|
+
enabled: true,
|
|
10
|
+
subscribe: 'type = "group"',
|
|
11
|
+
mapping: {
|
|
12
|
+
groupId: {
|
|
13
|
+
'@path': '$.groupId'
|
|
14
|
+
},
|
|
15
|
+
groupType: {
|
|
16
|
+
'@path': '$.traits.group_type'
|
|
17
|
+
},
|
|
18
|
+
properties: {
|
|
19
|
+
'@path': '$.traits'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
describe('group', () => {
|
|
26
|
+
beforeAll(() => {
|
|
27
|
+
jest.mock('@segment/browser-destination-runtime/load-script', () => ({
|
|
28
|
+
loadScript: (_src: any, _attributes: any) => {}
|
|
29
|
+
}))
|
|
30
|
+
jest.mock('@segment/browser-destination-runtime/resolve-when', () => ({
|
|
31
|
+
resolveWhen: (_fn: any, _timeout: any) => {}
|
|
32
|
+
}))
|
|
33
|
+
})
|
|
34
|
+
test('it maps event parameters correctly to group function ', async () => {
|
|
35
|
+
const [group] = await screebDestination({
|
|
36
|
+
websiteId: 'fake-website-id',
|
|
37
|
+
subscriptions
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
jest.spyOn(destination.actions.group, 'perform')
|
|
41
|
+
await group.load(Context.system(), {} as Analytics)
|
|
42
|
+
|
|
43
|
+
await group.group?.(
|
|
44
|
+
new Context({
|
|
45
|
+
type: 'group',
|
|
46
|
+
groupId: 'group-name',
|
|
47
|
+
traits: {
|
|
48
|
+
plan: 'free'
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
expect(destination.actions.group.perform).toHaveBeenCalledWith(
|
|
54
|
+
expect.anything(),
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
payload: {
|
|
57
|
+
groupId: 'group-name',
|
|
58
|
+
properties: {
|
|
59
|
+
plan: 'free'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
expect(window.$screeb.q).toStrictEqual([
|
|
66
|
+
['init', 'fake-website-id'],
|
|
67
|
+
['identity.group.assign', undefined, 'group-name', { plan: 'free' }]
|
|
68
|
+
])
|
|
69
|
+
})
|
|
70
|
+
test('it maps event parameters correctly to group function with group type', async () => {
|
|
71
|
+
const [group] = await screebDestination({
|
|
72
|
+
websiteId: 'fake-website-id',
|
|
73
|
+
subscriptions
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
jest.spyOn(destination.actions.group, 'perform')
|
|
77
|
+
await group.load(Context.system(), {} as Analytics)
|
|
78
|
+
|
|
79
|
+
await group.group?.(
|
|
80
|
+
new Context({
|
|
81
|
+
type: 'group',
|
|
82
|
+
groupId: 'group-name',
|
|
83
|
+
traits: {
|
|
84
|
+
plan: 'free',
|
|
85
|
+
group_type: 'cohort'
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
expect(destination.actions.group.perform).toHaveBeenCalledWith(
|
|
91
|
+
expect.anything(),
|
|
92
|
+
expect.objectContaining({
|
|
93
|
+
payload: {
|
|
94
|
+
groupId: 'group-name',
|
|
95
|
+
groupType: 'cohort',
|
|
96
|
+
properties: {
|
|
97
|
+
plan: 'free',
|
|
98
|
+
group_type: 'cohort'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
expect(window.$screeb.q).toStrictEqual([
|
|
105
|
+
['init', 'fake-website-id'],
|
|
106
|
+
['identity.group.assign', 'cohort', 'group-name', { plan: 'free', group_type: 'cohort' }]
|
|
107
|
+
])
|
|
108
|
+
})
|
|
109
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The group id
|
|
6
|
+
*/
|
|
7
|
+
groupId: string
|
|
8
|
+
/**
|
|
9
|
+
* The group type
|
|
10
|
+
*/
|
|
11
|
+
groupType: string
|
|
12
|
+
/**
|
|
13
|
+
* Traits to associate with the group
|
|
14
|
+
*/
|
|
15
|
+
properties?: {
|
|
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 { Screeb } from '../types'
|
|
5
|
+
|
|
6
|
+
const action: BrowserActionDefinition<Settings, Screeb, Payload> = {
|
|
7
|
+
title: 'Group',
|
|
8
|
+
description: 'Set user group and/or attributes.',
|
|
9
|
+
platform: 'web',
|
|
10
|
+
defaultSubscription: 'type = "group"',
|
|
11
|
+
fields: {
|
|
12
|
+
groupId: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'The group id',
|
|
15
|
+
label: 'Group ID',
|
|
16
|
+
required: true,
|
|
17
|
+
default: { '@path': '$.groupId' }
|
|
18
|
+
},
|
|
19
|
+
groupType: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'The group type',
|
|
22
|
+
label: 'Group type',
|
|
23
|
+
required: true,
|
|
24
|
+
default: { '@path': '$.traits.group_type' }
|
|
25
|
+
},
|
|
26
|
+
properties: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
label: 'Traits',
|
|
29
|
+
description: 'Traits to associate with the group',
|
|
30
|
+
default: { '@path': '$.traits' }
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
perform: (Screeb, event) => {
|
|
34
|
+
const payload = event.payload
|
|
35
|
+
if (!payload || typeof payload !== 'object' || !(payload.groupId || payload.properties)) {
|
|
36
|
+
console.warn(
|
|
37
|
+
'[Screeb] received invalid payload (expected userId, anonymousId, or properties to be present); skipping identify',
|
|
38
|
+
payload
|
|
39
|
+
)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const properties = payload.properties && Object.keys(payload.properties).length > 0 ? payload.properties : undefined
|
|
44
|
+
|
|
45
|
+
Screeb('identity.group.assign', payload.groupType, payload.groupId, properties)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default action
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import screebDestination, { destination } from '../../index'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
|
|
5
|
+
const subscriptions: Subscription[] = [
|
|
6
|
+
{
|
|
7
|
+
partnerAction: 'identify',
|
|
8
|
+
name: 'Identify',
|
|
9
|
+
enabled: true,
|
|
10
|
+
subscribe: 'type = "identify"',
|
|
11
|
+
mapping: {
|
|
12
|
+
userId: {
|
|
13
|
+
'@path': '$.userId'
|
|
14
|
+
},
|
|
15
|
+
anonymousId: {
|
|
16
|
+
'@path': '$.anonymousId'
|
|
17
|
+
},
|
|
18
|
+
properties: {
|
|
19
|
+
'@path': '$.traits'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
describe('identify', () => {
|
|
26
|
+
beforeAll(() => {
|
|
27
|
+
jest.mock('@segment/browser-destination-runtime/load-script', () => ({
|
|
28
|
+
loadScript: (_src: any, _attributes: any) => {}
|
|
29
|
+
}))
|
|
30
|
+
jest.mock('@segment/browser-destination-runtime/resolve-when', () => ({
|
|
31
|
+
resolveWhen: (_fn: any, _timeout: any) => {}
|
|
32
|
+
}))
|
|
33
|
+
})
|
|
34
|
+
test('it maps event parameters correctly to identify function without user id but anonymous id', async () => {
|
|
35
|
+
const [identify] = await screebDestination({
|
|
36
|
+
websiteId: 'fake-website-id',
|
|
37
|
+
subscriptions
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
jest.spyOn(destination.actions.identify, 'perform')
|
|
41
|
+
await identify.load(Context.system(), {} as Analytics)
|
|
42
|
+
|
|
43
|
+
await identify.identify?.(
|
|
44
|
+
new Context({
|
|
45
|
+
type: 'identify',
|
|
46
|
+
userId: 'user-id',
|
|
47
|
+
anonymousId: 'anonymous-id',
|
|
48
|
+
traits: {
|
|
49
|
+
firstname: 'Frida',
|
|
50
|
+
lastname: 'Khalo',
|
|
51
|
+
email: 'frida.khalo@screeb.app'
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
expect(destination.actions.identify.perform).toHaveBeenCalledWith(
|
|
57
|
+
expect.anything(),
|
|
58
|
+
expect.objectContaining({
|
|
59
|
+
payload: {
|
|
60
|
+
userId: 'user-id',
|
|
61
|
+
anonymousId: 'anonymous-id',
|
|
62
|
+
properties: {
|
|
63
|
+
firstname: 'Frida',
|
|
64
|
+
lastname: 'Khalo',
|
|
65
|
+
email: 'frida.khalo@screeb.app'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
expect(window.$screeb.q).toStrictEqual([
|
|
72
|
+
['init', 'fake-website-id'],
|
|
73
|
+
['identity', 'user-id', { firstname: 'Frida', lastname: 'Khalo', email: 'frida.khalo@screeb.app' }]
|
|
74
|
+
])
|
|
75
|
+
})
|
|
76
|
+
test('it maps event parameters correctly to identify function ', async () => {
|
|
77
|
+
const [identify] = await screebDestination({
|
|
78
|
+
websiteId: 'fake-website-id',
|
|
79
|
+
subscriptions
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
jest.spyOn(destination.actions.identify, 'perform')
|
|
83
|
+
await identify.load(Context.system(), {} as Analytics)
|
|
84
|
+
|
|
85
|
+
await identify.identify?.(
|
|
86
|
+
new Context({
|
|
87
|
+
type: 'identify',
|
|
88
|
+
anonymousId: 'anonymous-id',
|
|
89
|
+
traits: {
|
|
90
|
+
firstname: 'Frida',
|
|
91
|
+
lastname: 'Khalo',
|
|
92
|
+
email: 'frida.khalo@screeb.app'
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
expect(destination.actions.identify.perform).toHaveBeenCalledWith(
|
|
98
|
+
expect.anything(),
|
|
99
|
+
expect.objectContaining({
|
|
100
|
+
payload: {
|
|
101
|
+
anonymousId: 'anonymous-id',
|
|
102
|
+
properties: {
|
|
103
|
+
firstname: 'Frida',
|
|
104
|
+
lastname: 'Khalo',
|
|
105
|
+
email: 'frida.khalo@screeb.app'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
expect(window.$screeb.q).toStrictEqual([
|
|
112
|
+
['init', 'fake-website-id'],
|
|
113
|
+
['identity', 'anonymous-id', { firstname: 'Frida', lastname: 'Khalo', email: 'frida.khalo@screeb.app' }]
|
|
114
|
+
])
|
|
115
|
+
})
|
|
116
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
* Anonymous identifier for the user
|
|
10
|
+
*/
|
|
11
|
+
anonymousId?: string
|
|
12
|
+
/**
|
|
13
|
+
* The Segment user traits to be forwarded to Screeb and set as attributes
|
|
14
|
+
*/
|
|
15
|
+
properties?: {
|
|
16
|
+
[k: string]: unknown
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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 { Screeb } from '../types'
|
|
5
|
+
|
|
6
|
+
const action: BrowserActionDefinition<Settings, Screeb, Payload> = {
|
|
7
|
+
title: 'Identify',
|
|
8
|
+
description: 'Set user ID and/or attributes.',
|
|
9
|
+
platform: 'web',
|
|
10
|
+
defaultSubscription: 'type = "identify"',
|
|
11
|
+
fields: {
|
|
12
|
+
userId: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
required: false,
|
|
15
|
+
description: 'Unique identifier for the user',
|
|
16
|
+
label: 'User ID',
|
|
17
|
+
default: {
|
|
18
|
+
'@path': '$.userId'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
anonymousId: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
required: false,
|
|
24
|
+
description: 'Anonymous identifier for the user',
|
|
25
|
+
label: 'Anonymous ID',
|
|
26
|
+
default: {
|
|
27
|
+
'@path': '$.anonymousId'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
properties: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
required: false,
|
|
33
|
+
description: 'The Segment user traits to be forwarded to Screeb and set as attributes',
|
|
34
|
+
label: 'User Attributes',
|
|
35
|
+
default: {
|
|
36
|
+
'@path': '$.traits'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
perform: (Screeb, event) => {
|
|
41
|
+
const payload = event.payload
|
|
42
|
+
if (!payload || typeof payload !== 'object' || !(payload.userId || payload.anonymousId || payload.properties)) {
|
|
43
|
+
console.warn(
|
|
44
|
+
'[Screeb] received invalid payload (expected userId, anonymousId, or properties to be present); skipping identify',
|
|
45
|
+
payload
|
|
46
|
+
)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const properties = payload.properties && Object.keys(payload.properties).length > 0 ? payload.properties : undefined
|
|
51
|
+
|
|
52
|
+
Screeb('identity', payload.userId ?? payload.anonymousId, properties)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default action
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
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 { Screeb } from './types'
|
|
5
|
+
import { defaultValues } from '@segment/actions-core'
|
|
6
|
+
import identify from './identify'
|
|
7
|
+
import track from './track'
|
|
8
|
+
import group from './group'
|
|
9
|
+
import alias from './alias'
|
|
10
|
+
|
|
11
|
+
declare global {
|
|
12
|
+
interface Window {
|
|
13
|
+
$screeb: Screeb
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const destination: BrowserDestinationDefinition<Settings, Screeb> = {
|
|
18
|
+
name: 'Screeb Web (Actions)',
|
|
19
|
+
slug: 'actions-screeb-web',
|
|
20
|
+
mode: 'device',
|
|
21
|
+
|
|
22
|
+
settings: {
|
|
23
|
+
websiteId: {
|
|
24
|
+
description: 'Your website ID (given in Screeb app).',
|
|
25
|
+
label: 'Website ID',
|
|
26
|
+
type: 'string',
|
|
27
|
+
required: true
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
presets: [
|
|
32
|
+
{
|
|
33
|
+
name: 'Identify',
|
|
34
|
+
subscribe: 'type = "identify"',
|
|
35
|
+
partnerAction: 'identify',
|
|
36
|
+
mapping: defaultValues(identify.fields)
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Track',
|
|
40
|
+
subscribe: 'type = "track"',
|
|
41
|
+
partnerAction: 'track',
|
|
42
|
+
mapping: defaultValues(track.fields)
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'Group',
|
|
46
|
+
subscribe: 'type = "group"',
|
|
47
|
+
partnerAction: 'group',
|
|
48
|
+
mapping: defaultValues(group.fields)
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Alias',
|
|
52
|
+
subscribe: 'type = "alias"',
|
|
53
|
+
partnerAction: 'alias',
|
|
54
|
+
mapping: defaultValues(alias.fields)
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
|
|
58
|
+
initialize: async ({ settings }, deps) => {
|
|
59
|
+
const preloadFunction = function (...args: unknown[]) {
|
|
60
|
+
if (window.$screeb.q) {
|
|
61
|
+
window.$screeb.q.push(args)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
window.$screeb = preloadFunction
|
|
65
|
+
window.$screeb.q = []
|
|
66
|
+
|
|
67
|
+
await deps.loadScript('https://t.screeb.app/tag.js')
|
|
68
|
+
await deps.resolveWhen(() => window.$screeb !== preloadFunction, 500)
|
|
69
|
+
|
|
70
|
+
window.$screeb('init', settings.websiteId)
|
|
71
|
+
|
|
72
|
+
return window.$screeb
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
actions: {
|
|
76
|
+
identify,
|
|
77
|
+
track,
|
|
78
|
+
group,
|
|
79
|
+
alias
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default browserDestination(destination)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import screebDestination, { destination } from '../../index'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
|
|
5
|
+
const subscriptions: Subscription[] = [
|
|
6
|
+
{
|
|
7
|
+
partnerAction: 'track',
|
|
8
|
+
name: 'Track',
|
|
9
|
+
enabled: true,
|
|
10
|
+
subscribe: 'type = "track"',
|
|
11
|
+
mapping: {
|
|
12
|
+
name: {
|
|
13
|
+
'@path': '$.event'
|
|
14
|
+
},
|
|
15
|
+
properties: {
|
|
16
|
+
'@path': '$.properties'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
describe('track', () => {
|
|
23
|
+
beforeAll(() => {
|
|
24
|
+
jest.mock('@segment/browser-destination-runtime/load-script', () => ({
|
|
25
|
+
loadScript: (_src: any, _attributes: any) => {}
|
|
26
|
+
}))
|
|
27
|
+
jest.mock('@segment/browser-destination-runtime/resolve-when', () => ({
|
|
28
|
+
resolveWhen: (_fn: any, _timeout: any) => {}
|
|
29
|
+
}))
|
|
30
|
+
})
|
|
31
|
+
test('it maps event parameters correctly to track function', async () => {
|
|
32
|
+
const [track] = await screebDestination({
|
|
33
|
+
websiteId: 'fake-website-id',
|
|
34
|
+
subscriptions
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
jest.spyOn(destination.actions.track, 'perform')
|
|
38
|
+
await track.load(Context.system(), {} as Analytics)
|
|
39
|
+
|
|
40
|
+
await track.track?.(
|
|
41
|
+
new Context({
|
|
42
|
+
type: 'track',
|
|
43
|
+
event: 'event-name',
|
|
44
|
+
properties: {
|
|
45
|
+
prop1: 1,
|
|
46
|
+
prop2: 'pickle sandwish'
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
expect(destination.actions.track.perform).toHaveBeenCalledWith(
|
|
52
|
+
expect.anything(),
|
|
53
|
+
expect.objectContaining({
|
|
54
|
+
payload: {
|
|
55
|
+
name: 'event-name',
|
|
56
|
+
properties: {
|
|
57
|
+
prop1: 1,
|
|
58
|
+
prop2: 'pickle sandwish'
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
expect(window.$screeb.q).toStrictEqual([
|
|
65
|
+
['init', 'fake-website-id'],
|
|
66
|
+
['event.track', 'event-name', { prop1: 1, prop2: 'pickle sandwish' }]
|
|
67
|
+
])
|
|
68
|
+
})
|
|
69
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The event name that will be shown on Screeb's dashboard
|
|
6
|
+
*/
|
|
7
|
+
name: string
|
|
8
|
+
/**
|
|
9
|
+
* Object containing the properties of the event
|
|
10
|
+
*/
|
|
11
|
+
properties?: {
|
|
12
|
+
[k: string]: unknown
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
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 { Screeb } from '../types'
|
|
5
|
+
|
|
6
|
+
const action: BrowserActionDefinition<Settings, Screeb, Payload> = {
|
|
7
|
+
title: 'Track',
|
|
8
|
+
description: 'Track event to potentially filter user studies (microsurveys) later, or trigger a study now.',
|
|
9
|
+
platform: 'web',
|
|
10
|
+
defaultSubscription: 'type = "track" and event != "Signed Out"',
|
|
11
|
+
fields: {
|
|
12
|
+
name: {
|
|
13
|
+
description: "The event name that will be shown on Screeb's dashboard",
|
|
14
|
+
label: 'Event name',
|
|
15
|
+
required: true,
|
|
16
|
+
type: 'string',
|
|
17
|
+
default: {
|
|
18
|
+
'@path': '$.event'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
properties: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
required: false,
|
|
24
|
+
description: 'Object containing the properties of the event',
|
|
25
|
+
label: 'Event Properties',
|
|
26
|
+
default: {
|
|
27
|
+
'@path': '$.properties'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
perform: (Screeb, event) => {
|
|
32
|
+
const payload = event.payload
|
|
33
|
+
if (!payload || typeof payload !== 'object' || !payload.name) {
|
|
34
|
+
console.warn('[Screeb] received invalid payload (expected name to be present); skipping track', payload)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Screeb('event.track', payload.name, payload.properties)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default action
|
package/src/types.ts
ADDED