@segment/analytics-browser-actions-fullsession 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.
Files changed (71) hide show
  1. package/README.md +31 -0
  2. package/dist/cjs/generated-types.d.ts +3 -0
  3. package/dist/cjs/generated-types.js +3 -0
  4. package/dist/cjs/generated-types.js.map +1 -0
  5. package/dist/cjs/identifyUser/generated-types.d.ts +7 -0
  6. package/dist/cjs/identifyUser/generated-types.js +3 -0
  7. package/dist/cjs/identifyUser/generated-types.js.map +1 -0
  8. package/dist/cjs/identifyUser/index.d.ts +6 -0
  9. package/dist/cjs/identifyUser/index.js +53 -0
  10. package/dist/cjs/identifyUser/index.js.map +1 -0
  11. package/dist/cjs/index.d.ts +11 -0
  12. package/dist/cjs/index.js +58 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/recordEvent/generated-types.d.ts +6 -0
  15. package/dist/cjs/recordEvent/generated-types.js +3 -0
  16. package/dist/cjs/recordEvent/generated-types.js.map +1 -0
  17. package/dist/cjs/recordEvent/index.d.ts +6 -0
  18. package/dist/cjs/recordEvent/index.js +34 -0
  19. package/dist/cjs/recordEvent/index.js.map +1 -0
  20. package/dist/cjs/types.d.ts +11 -0
  21. package/dist/cjs/types.js +6 -0
  22. package/dist/cjs/types.js.map +1 -0
  23. package/dist/cjs/visitPage/generated-types.d.ts +5 -0
  24. package/dist/cjs/visitPage/generated-types.js +3 -0
  25. package/dist/cjs/visitPage/generated-types.js.map +1 -0
  26. package/dist/cjs/visitPage/index.d.ts +6 -0
  27. package/dist/cjs/visitPage/index.js +27 -0
  28. package/dist/cjs/visitPage/index.js.map +1 -0
  29. package/dist/esm/generated-types.d.ts +3 -0
  30. package/dist/esm/generated-types.js +2 -0
  31. package/dist/esm/generated-types.js.map +1 -0
  32. package/dist/esm/identifyUser/generated-types.d.ts +7 -0
  33. package/dist/esm/identifyUser/generated-types.js +2 -0
  34. package/dist/esm/identifyUser/generated-types.js.map +1 -0
  35. package/dist/esm/identifyUser/index.d.ts +6 -0
  36. package/dist/esm/identifyUser/index.js +51 -0
  37. package/dist/esm/identifyUser/index.js.map +1 -0
  38. package/dist/esm/index.d.ts +11 -0
  39. package/dist/esm/index.js +54 -0
  40. package/dist/esm/index.js.map +1 -0
  41. package/dist/esm/recordEvent/generated-types.d.ts +6 -0
  42. package/dist/esm/recordEvent/generated-types.js +2 -0
  43. package/dist/esm/recordEvent/generated-types.js.map +1 -0
  44. package/dist/esm/recordEvent/index.d.ts +6 -0
  45. package/dist/esm/recordEvent/index.js +32 -0
  46. package/dist/esm/recordEvent/index.js.map +1 -0
  47. package/dist/esm/types.d.ts +11 -0
  48. package/dist/esm/types.js +3 -0
  49. package/dist/esm/types.js.map +1 -0
  50. package/dist/esm/visitPage/generated-types.d.ts +5 -0
  51. package/dist/esm/visitPage/generated-types.js +2 -0
  52. package/dist/esm/visitPage/generated-types.js.map +1 -0
  53. package/dist/esm/visitPage/index.d.ts +6 -0
  54. package/dist/esm/visitPage/index.js +25 -0
  55. package/dist/esm/visitPage/index.js.map +1 -0
  56. package/dist/tsconfig.tsbuildinfo +1 -0
  57. package/package.json +25 -0
  58. package/src/__tests__/index.test.ts +177 -0
  59. package/src/generated-types.ts +8 -0
  60. package/src/identifyUser/__tests__/index.test.ts +315 -0
  61. package/src/identifyUser/generated-types.ts +18 -0
  62. package/src/identifyUser/index.ts +66 -0
  63. package/src/index.ts +68 -0
  64. package/src/recordEvent/__tests__/index.test.ts +274 -0
  65. package/src/recordEvent/generated-types.ts +14 -0
  66. package/src/recordEvent/index.ts +37 -0
  67. package/src/types.ts +14 -0
  68. package/src/visitPage/__tests__/index.test.ts +312 -0
  69. package/src/visitPage/generated-types.ts +10 -0
  70. package/src/visitPage/index.ts +31 -0
  71. package/tsconfig.json +9 -0
@@ -0,0 +1,274 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import fullSessionDestination from '../../index'
3
+ import { Subscription } from '@segment/browser-destination-runtime/types'
4
+
5
+ // Mock the browser destination runtime functions
6
+ jest.mock('@segment/browser-destination-runtime/load-script', () => ({
7
+ loadScript: (_src: any, _attributes: any) => Promise.resolve()
8
+ }))
9
+
10
+ jest.mock('@segment/browser-destination-runtime/resolve-when', () => ({
11
+ resolveWhen: (_fn: any, _timeout: any) => Promise.resolve()
12
+ }))
13
+
14
+ // Mock the fullsession package
15
+ jest.mock('fullsession', () => ({
16
+ fullSessionTracker: {
17
+ initialize: jest.fn(),
18
+ identify: jest.fn(),
19
+ setSessionAttributes: jest.fn(),
20
+ event: jest.fn()
21
+ }
22
+ }))
23
+
24
+ const trackSubscription: Subscription = {
25
+ partnerAction: 'recordEvent',
26
+ name: 'Record Event',
27
+ enabled: true,
28
+ subscribe: 'type = "track"',
29
+ mapping: {
30
+ name: {
31
+ '@path': '$.event'
32
+ },
33
+ properties: {
34
+ '@path': '$.properties'
35
+ }
36
+ }
37
+ }
38
+
39
+ describe('FullSession recordEvent action', () => {
40
+ const mockCustomerId = 'test-customer-id'
41
+
42
+ beforeEach(() => {
43
+ jest.clearAllMocks()
44
+ // Setup window.FUS mock if needed
45
+ if (typeof window !== 'undefined') {
46
+ // @ts-ignore
47
+ window.FUS = {}
48
+ }
49
+ })
50
+
51
+ beforeEach(() => {
52
+ jest.clearAllMocks()
53
+ })
54
+
55
+ test('should record event with name and properties', async () => {
56
+ const { fullSessionTracker } = await import('fullsession')
57
+
58
+ const [event] = await fullSessionDestination({
59
+ customerId: mockCustomerId,
60
+ subscriptions: [trackSubscription] as any
61
+ })
62
+
63
+ await event.load(Context.system(), {} as Analytics)
64
+
65
+ const eventName = 'Product Purchased'
66
+ const properties = {
67
+ product_id: 'ABC123',
68
+ product_name: 'Widget',
69
+ price: 29.99,
70
+ currency: 'USD',
71
+ category: 'Electronics'
72
+ }
73
+
74
+ await event.track?.(
75
+ new Context({
76
+ type: 'track',
77
+ event: eventName,
78
+ properties
79
+ })
80
+ )
81
+
82
+ expect(fullSessionTracker.event).toHaveBeenCalledWith(eventName, properties)
83
+ })
84
+
85
+ test('should record event with only name', async () => {
86
+ const { fullSessionTracker } = await import('fullsession')
87
+
88
+ const [event] = await fullSessionDestination({
89
+ customerId: mockCustomerId,
90
+ subscriptions: [trackSubscription] as any
91
+ })
92
+
93
+ await event.load(Context.system(), {} as Analytics)
94
+
95
+ const eventName = 'Simple Event'
96
+
97
+ await event.track?.(
98
+ new Context({
99
+ type: 'track',
100
+ event: eventName
101
+ })
102
+ )
103
+
104
+ expect(fullSessionTracker.event).toHaveBeenCalledWith(eventName, {})
105
+ })
106
+
107
+ test('should record event with string and number properties', async () => {
108
+ const { fullSessionTracker } = await import('fullsession')
109
+
110
+ const [event] = await fullSessionDestination({
111
+ customerId: mockCustomerId,
112
+ subscriptions: [trackSubscription] as any
113
+ })
114
+
115
+ await event.load(Context.system(), {} as Analytics)
116
+
117
+ const eventName = 'Page View'
118
+ const properties = {
119
+ page_url: 'https://example.com/product/123',
120
+ page_title: 'Product Details',
121
+ load_time: 1.5,
122
+ session_id: 'sess_123',
123
+ user_count: 42
124
+ }
125
+
126
+ await event.track?.(
127
+ new Context({
128
+ type: 'track',
129
+ event: eventName,
130
+ properties
131
+ })
132
+ )
133
+
134
+ expect(fullSessionTracker.event).toHaveBeenCalledWith(eventName, properties)
135
+ })
136
+
137
+ test('should handle empty properties object', async () => {
138
+ const { fullSessionTracker } = await import('fullsession')
139
+
140
+ const [event] = await fullSessionDestination({
141
+ customerId: mockCustomerId,
142
+ subscriptions: [trackSubscription] as any
143
+ })
144
+
145
+ await event.load(Context.system(), {} as Analytics)
146
+
147
+ const eventName = 'Button Clicked'
148
+
149
+ await event.track?.(
150
+ new Context({
151
+ type: 'track',
152
+ event: eventName,
153
+ properties: {}
154
+ })
155
+ )
156
+
157
+ expect(fullSessionTracker.event).toHaveBeenCalledWith(eventName, {})
158
+ })
159
+
160
+ test('should handle null properties', async () => {
161
+ const { fullSessionTracker } = await import('fullsession')
162
+
163
+ const [event] = await fullSessionDestination({
164
+ customerId: mockCustomerId,
165
+ subscriptions: [trackSubscription] as any
166
+ })
167
+
168
+ await event.load(Context.system(), {} as Analytics)
169
+
170
+ const eventName = 'Null Properties Event'
171
+
172
+ await event.track?.(
173
+ new Context({
174
+ type: 'track',
175
+ event: eventName,
176
+ properties: undefined
177
+ })
178
+ )
179
+
180
+ expect(fullSessionTracker.event).toHaveBeenCalledWith(eventName, {})
181
+ })
182
+
183
+ test('should record multiple events in sequence', async () => {
184
+ const { fullSessionTracker } = await import('fullsession')
185
+
186
+ const [event] = await fullSessionDestination({
187
+ customerId: mockCustomerId,
188
+ subscriptions: [trackSubscription] as any
189
+ })
190
+
191
+ await event.load(Context.system(), {} as Analytics)
192
+
193
+ // First event
194
+ await event.track?.(
195
+ new Context({
196
+ type: 'track',
197
+ event: 'Event 1',
198
+ properties: { prop1: 'value1' }
199
+ })
200
+ )
201
+
202
+ // Second event
203
+ await event.track?.(
204
+ new Context({
205
+ type: 'track',
206
+ event: 'Event 2',
207
+ properties: { prop2: 'value2', count: 10 }
208
+ })
209
+ )
210
+
211
+ expect(fullSessionTracker.event).toHaveBeenCalledTimes(2)
212
+ expect(fullSessionTracker.event).toHaveBeenNthCalledWith(1, 'Event 1', { prop1: 'value1' })
213
+ expect(fullSessionTracker.event).toHaveBeenNthCalledWith(2, 'Event 2', { prop2: 'value2', count: 10 })
214
+ })
215
+
216
+ test('should record event with mixed property types', async () => {
217
+ const { fullSessionTracker } = await import('fullsession')
218
+
219
+ const [event] = await fullSessionDestination({
220
+ customerId: mockCustomerId,
221
+ subscriptions: [trackSubscription] as any
222
+ })
223
+
224
+ await event.load(Context.system(), {} as Analytics)
225
+
226
+ const eventName = 'Mixed Properties Event'
227
+ const properties = {
228
+ string_prop: 'test string',
229
+ number_prop: 123.45,
230
+ boolean_prop: true,
231
+ array_prop: [1, 2, 3],
232
+ object_prop: { nested: 'value' },
233
+ null_prop: null,
234
+ undefined_prop: undefined
235
+ }
236
+
237
+ await event.track?.(
238
+ new Context({
239
+ type: 'track',
240
+ event: eventName,
241
+ properties
242
+ })
243
+ )
244
+
245
+ expect(fullSessionTracker.event).toHaveBeenCalledWith(eventName, properties)
246
+ })
247
+
248
+ test('should handle event with special characters in name', async () => {
249
+ const { fullSessionTracker } = await import('fullsession')
250
+
251
+ const [event] = await fullSessionDestination({
252
+ customerId: mockCustomerId,
253
+ subscriptions: [trackSubscription] as any
254
+ })
255
+
256
+ await event.load(Context.system(), {} as Analytics)
257
+
258
+ const eventName = 'Event with Special Characters! @#$%^&*()'
259
+ const properties = {
260
+ special_chars: 'åäöüñ',
261
+ unicode: '🎉🚀'
262
+ }
263
+
264
+ await event.track?.(
265
+ new Context({
266
+ type: 'track',
267
+ event: eventName,
268
+ properties
269
+ })
270
+ )
271
+
272
+ expect(fullSessionTracker.event).toHaveBeenCalledWith(eventName, properties)
273
+ })
274
+ })
@@ -0,0 +1,14 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * The name of the event being tracked.
6
+ */
7
+ name: string
8
+ /**
9
+ * Additional properties and metadata associated with the event.
10
+ */
11
+ properties?: {
12
+ [k: string]: unknown
13
+ }
14
+ }
@@ -0,0 +1,37 @@
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 { FUS, CustomEventData } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, FUS, Payload> = {
7
+ title: 'Track Event',
8
+ description: 'Track custom events and user interactions in FullSession for behavioral analysis.',
9
+ platform: 'web',
10
+ defaultSubscription: 'type = "track"',
11
+ fields: {
12
+ name: {
13
+ description: 'The name of the event being tracked.',
14
+ label: 'Event Name',
15
+ required: true,
16
+ type: 'string',
17
+ default: {
18
+ '@path': '$.event'
19
+ }
20
+ },
21
+ properties: {
22
+ description: 'Additional properties and metadata associated with the event.',
23
+ label: 'Event Properties',
24
+ required: false,
25
+ type: 'object',
26
+ default: {
27
+ '@path': '$.properties'
28
+ }
29
+ }
30
+ },
31
+ perform: (FUS, data) => {
32
+ const { name, properties } = data.payload
33
+ FUS.event(name, (properties ?? {}) as CustomEventData)
34
+ }
35
+ }
36
+
37
+ export default action
package/src/types.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { fullSessionTracker } from 'fullsession'
2
+
3
+ type CustomEventData = {
4
+ [key: string]: string | number
5
+ [stringKey: `${string}_str`]: string
6
+ [numberKey: `${string}_real`]: number
7
+ }
8
+
9
+ type UserCustomAttributes = {
10
+ [attribute: string]: string
11
+ }
12
+
13
+ export type FUS = typeof fullSessionTracker
14
+ export { fullSessionTracker, CustomEventData, UserCustomAttributes }
@@ -0,0 +1,312 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import fullSessionDestination from '../../index'
3
+ import { Subscription } from '@segment/browser-destination-runtime/types'
4
+
5
+ // Mock the browser destination runtime functions
6
+ jest.mock('@segment/browser-destination-runtime/load-script', () => ({
7
+ loadScript: (_src: any, _attributes: any) => Promise.resolve()
8
+ }))
9
+
10
+ jest.mock('@segment/browser-destination-runtime/resolve-when', () => ({
11
+ resolveWhen: (_fn: any, _timeout: any) => Promise.resolve()
12
+ }))
13
+
14
+ // Mock the fullsession package
15
+ jest.mock('fullsession', () => ({
16
+ fullSessionTracker: {
17
+ initialize: jest.fn(),
18
+ identify: jest.fn(),
19
+ setPageAttributes: jest.fn(),
20
+ event: jest.fn()
21
+ }
22
+ }))
23
+
24
+ const pageSubscription: Subscription = {
25
+ partnerAction: 'visitPage',
26
+ name: 'Visit Page',
27
+ enabled: true,
28
+ subscribe: 'type = "page"',
29
+ mapping: {
30
+ properties: {
31
+ '@path': '$.properties'
32
+ }
33
+ }
34
+ }
35
+
36
+ describe('FullSession visitPage action', () => {
37
+ const mockCustomerId = 'test-customer-id'
38
+
39
+ beforeEach(() => {
40
+ jest.clearAllMocks()
41
+ // Setup window.FUS mock if needed
42
+ if (typeof window !== 'undefined') {
43
+ // @ts-ignore
44
+ window.FUS = {}
45
+ }
46
+ })
47
+
48
+ test('should set session attributes with page properties', async () => {
49
+ const { fullSessionTracker } = await import('fullsession')
50
+
51
+ const [event] = await fullSessionDestination({
52
+ customerId: mockCustomerId,
53
+ subscriptions: [pageSubscription] as any
54
+ })
55
+
56
+ await event.load(Context.system(), {} as Analytics)
57
+
58
+ const properties = {
59
+ title: 'Welcome to Our Site',
60
+ url: 'https://example.com',
61
+ path: '/',
62
+ referrer: 'https://google.com',
63
+ search: '?utm_source=google'
64
+ }
65
+
66
+ await event.page?.(
67
+ new Context({
68
+ type: 'page',
69
+ name: 'Home Page',
70
+ properties
71
+ })
72
+ )
73
+
74
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({
75
+ ...properties
76
+ })
77
+ })
78
+
79
+ test('should handle page with category and properties', async () => {
80
+ const { fullSessionTracker } = await import('fullsession')
81
+
82
+ const [event] = await fullSessionDestination({
83
+ customerId: mockCustomerId,
84
+ subscriptions: [pageSubscription] as any
85
+ })
86
+
87
+ await event.load(Context.system(), {} as Analytics)
88
+
89
+ const properties = {
90
+ title: 'Amazing Product',
91
+ url: 'https://example.com/products/amazing-product',
92
+ product_id: 'prod_123'
93
+ }
94
+
95
+ await event.page?.(
96
+ new Context({
97
+ type: 'page',
98
+ name: 'Product Details',
99
+ category: 'Products',
100
+ properties
101
+ })
102
+ )
103
+
104
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({
105
+ ...properties
106
+ })
107
+ })
108
+
109
+ test('should handle page with properties', async () => {
110
+ const { fullSessionTracker } = await import('fullsession')
111
+
112
+ const [event] = await fullSessionDestination({
113
+ customerId: mockCustomerId,
114
+ subscriptions: [pageSubscription] as any
115
+ })
116
+
117
+ await event.load(Context.system(), {} as Analytics)
118
+
119
+ const properties = {
120
+ title: 'About Our Company',
121
+ url: 'https://example.com/about',
122
+ section: 'company'
123
+ }
124
+
125
+ await event.page?.(
126
+ new Context({
127
+ type: 'page',
128
+ name: 'About Us',
129
+ properties
130
+ })
131
+ )
132
+
133
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({
134
+ ...properties
135
+ })
136
+ })
137
+
138
+ test('should handle page with empty properties', async () => {
139
+ const { fullSessionTracker } = await import('fullsession')
140
+
141
+ const [event] = await fullSessionDestination({
142
+ customerId: mockCustomerId,
143
+ subscriptions: [pageSubscription] as any
144
+ })
145
+
146
+ await event.load(Context.system(), {} as Analytics)
147
+
148
+ await event.page?.(
149
+ new Context({
150
+ type: 'page',
151
+ name: 'Simple Page',
152
+ properties: {}
153
+ })
154
+ )
155
+
156
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({})
157
+ })
158
+
159
+ test('should handle page with null properties', async () => {
160
+ const { fullSessionTracker } = await import('fullsession')
161
+
162
+ const [event] = await fullSessionDestination({
163
+ customerId: mockCustomerId,
164
+ subscriptions: [pageSubscription] as any
165
+ })
166
+
167
+ await event.load(Context.system(), {} as Analytics)
168
+
169
+ await event.page?.(
170
+ new Context({
171
+ type: 'page',
172
+ name: 'Null Properties Page',
173
+ properties: undefined
174
+ })
175
+ )
176
+
177
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({})
178
+ })
179
+
180
+ test('should handle page without name and category', async () => {
181
+ const { fullSessionTracker } = await import('fullsession')
182
+
183
+ const [event] = await fullSessionDestination({
184
+ customerId: mockCustomerId,
185
+ subscriptions: [pageSubscription] as any
186
+ })
187
+
188
+ await event.load(Context.system(), {} as Analytics)
189
+
190
+ const properties = {
191
+ title: 'Unnamed Page',
192
+ url: 'https://example.com/unnamed',
193
+ path: '/unnamed'
194
+ }
195
+
196
+ await event.page?.(
197
+ new Context({
198
+ type: 'page',
199
+ properties
200
+ })
201
+ )
202
+
203
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({
204
+ ...properties
205
+ })
206
+ })
207
+
208
+ test('should handle complex properties with various data types', async () => {
209
+ const { fullSessionTracker } = await import('fullsession')
210
+
211
+ const [event] = await fullSessionDestination({
212
+ customerId: mockCustomerId,
213
+ subscriptions: [pageSubscription] as any
214
+ })
215
+
216
+ await event.load(Context.system(), {} as Analytics)
217
+
218
+ const properties = {
219
+ title: 'Complex Page Title',
220
+ url: 'https://example.com/complex',
221
+ load_time: 2.5,
222
+ user_agent: 'Mozilla/5.0...',
223
+ is_authenticated: true,
224
+ visitor_count: 1500,
225
+ tags: ['homepage', 'featured'],
226
+ metadata: {
227
+ experiment_id: 'exp_123',
228
+ variant: 'A'
229
+ }
230
+ }
231
+
232
+ await event.page?.(
233
+ new Context({
234
+ type: 'page',
235
+ name: 'Complex Page',
236
+ properties
237
+ })
238
+ )
239
+
240
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({
241
+ ...properties
242
+ })
243
+ })
244
+
245
+ test('should track multiple page visits in sequence', async () => {
246
+ const { fullSessionTracker } = await import('fullsession')
247
+
248
+ const [event] = await fullSessionDestination({
249
+ customerId: mockCustomerId,
250
+ subscriptions: [pageSubscription] as any
251
+ })
252
+
253
+ await event.load(Context.system(), {} as Analytics)
254
+
255
+ // First page visit
256
+ await event.page?.(
257
+ new Context({
258
+ type: 'page',
259
+ name: 'Home',
260
+ properties: { url: 'https://example.com' }
261
+ })
262
+ )
263
+
264
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({
265
+ url: 'https://example.com'
266
+ })
267
+
268
+ // Second page visit
269
+ await event.page?.(
270
+ new Context({
271
+ type: 'page',
272
+ name: 'Products',
273
+ properties: { url: 'https://example.com/products' }
274
+ })
275
+ )
276
+
277
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({
278
+ url: 'https://example.com/products'
279
+ })
280
+
281
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledTimes(2)
282
+ })
283
+
284
+ test('should handle page visit with special characters in properties', async () => {
285
+ const { fullSessionTracker } = await import('fullsession')
286
+
287
+ const [event] = await fullSessionDestination({
288
+ customerId: mockCustomerId,
289
+ subscriptions: [pageSubscription] as any
290
+ })
291
+
292
+ await event.load(Context.system(), {} as Analytics)
293
+
294
+ const properties = {
295
+ title: 'Title with émojis 🚀 and symbols!',
296
+ url: 'https://example.com/special-chars?q=test&lang=en',
297
+ description: 'A page with special characters: àáâãäåæçèéêë'
298
+ }
299
+
300
+ await event.page?.(
301
+ new Context({
302
+ type: 'page',
303
+ name: 'Page with "Special" Characters & Symbols',
304
+ properties
305
+ })
306
+ )
307
+
308
+ expect(fullSessionTracker.setPageAttributes).toHaveBeenCalledWith({
309
+ ...properties
310
+ })
311
+ })
312
+ })
@@ -0,0 +1,10 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * Properties and metadata associated with the page being viewed
6
+ */
7
+ properties?: {
8
+ [k: string]: unknown
9
+ }
10
+ }
@@ -0,0 +1,31 @@
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 { FUS, UserCustomAttributes } from '../types'
5
+
6
+ // Change from unknown to the partner SDK types
7
+ const action: BrowserActionDefinition<Settings, FUS, Payload> = {
8
+ title: 'Page View',
9
+ description: 'Track page views and set page-specific attributes in FullSession for navigation analysis.',
10
+ defaultSubscription: 'type = "page"',
11
+ platform: 'web',
12
+ fields: {
13
+ properties: {
14
+ type: 'object',
15
+ required: false,
16
+ description: 'Properties and metadata associated with the page being viewed',
17
+ label: 'Page Properties',
18
+ default: {
19
+ '@path': '$.properties'
20
+ }
21
+ }
22
+ },
23
+ perform: (FUS, data) => {
24
+ const { properties } = data.payload
25
+ FUS.setPageAttributes({
26
+ ...properties
27
+ } as UserCustomAttributes)
28
+ }
29
+ }
30
+
31
+ export default action
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.build.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "baseUrl": "."
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["dist", "**/__tests__"]
9
+ }