Skip to content

Commit 73ac593

Browse files
authored
Fix identify and group signals calls (#1115)
1 parent 8d06af2 commit 73ac593

File tree

16 files changed

+425
-73
lines changed

16 files changed

+425
-73
lines changed

.changeset/breezy-knives-arrive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@segment/analytics-next': patch
3+
---
4+
5+
Update argument resolver to user interface rather than full User

.changeset/thing-chicken-sun.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@segment/analytics-signals': patch
3+
---
4+
5+
Fix group and identify calls.

packages/browser/src/core/arguments-resolver/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
GroupTraits,
1616
UserTraits,
1717
} from '../events'
18-
import { ID, User } from '../user'
18+
import { ID, WithId } from '../user'
1919

2020
/**
2121
* Helper for the track method
@@ -107,7 +107,7 @@ export function resolvePageArguments(
107107
/**
108108
* Helper for group, identify methods
109109
*/
110-
export const resolveUserArguments = <T extends Traits, U extends User>(
110+
export const resolveUserArguments = <T extends Traits, U extends WithId>(
111111
user: U
112112
): ResolveUser<T> => {
113113
return (...args): ReturnType<ResolveUser<T>> => {

packages/browser/src/core/user/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ const defaults = {
5050
},
5151
}
5252

53-
export class User {
53+
export interface WithId {
54+
id(id?: ID): ID
55+
}
56+
57+
export class User implements WithId {
5458
static defaults = defaults
5559

5660
private idKey: string

packages/signals/signals-integration-tests/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"watch:test": "yarn test --watch",
1616
"tsc": "yarn run -T tsc",
1717
"eslint": "yarn run -T eslint",
18-
"server": "http-server --port 3000",
18+
"server": "http-server --port 5432",
1919
"browser": "playwright test --debug"
2020
},
2121
"packageManager": "[email protected]",

packages/signals/signals-integration-tests/playwright.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import path from 'path'
88
const config: PlaywrightTestConfig = {
99
webServer: {
1010
command: 'yarn run server',
11-
url: 'http://127.0.0.1:3000',
11+
url: 'http://127.0.0.1:5432',
1212
reuseExistingServer: !process.env.CI,
1313
},
1414
testDir: './src',

packages/signals/signals-integration-tests/src/helpers/base-page-object.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
import { CDNSettingsBuilder } from '@internal/test-helpers'
22
import { Page, Request } from '@playwright/test'
3+
import { logConsole } from './log-console'
4+
import { SegmentEvent } from '@segment/analytics-next'
35

46
export class BasePage {
57
protected page!: Page
6-
public signalsApiReq!: Request
7-
public trackingApiReq!: Request
8+
public lastSignalsApiReq!: Request
9+
public signalsApiReqs: SegmentEvent[] = []
10+
public lastTrackingApiReq!: Request
11+
public trackingApiReqs: SegmentEvent[] = []
12+
813
public url: string
914
public edgeFnDownloadURL = 'https://cdn.edgefn.segment.com/MY-WRITEKEY/foo.js'
10-
public edgeFn: string
15+
public edgeFn!: string
1116

12-
constructor(path: string, edgeFn: string) {
13-
this.edgeFn = edgeFn
14-
this.url = 'http://localhost:3000/src/tests' + path
17+
constructor(path: string) {
18+
this.url = 'http://localhost:5432/src/tests' + path
1519
}
1620

1721
/**
1822
* load and setup routes
23+
* @param page
24+
* @param edgeFn - edge function to be loaded
1925
*/
20-
async load(page: Page) {
26+
async load(page: Page, edgeFn: string) {
27+
logConsole(page)
2128
this.page = page
29+
this.edgeFn = edgeFn
2230
await this.setupMockedRoutes()
2331
await this.page.goto(this.url)
2432
// expect analytics to be loaded
@@ -30,8 +38,10 @@ export class BasePage {
3038

3139
private async setupMockedRoutes() {
3240
// clear any existing saved requests
33-
this.signalsApiReq = undefined as any as Request
34-
this.trackingApiReq = undefined as any as Request
41+
this.signalsApiReqs = []
42+
this.trackingApiReqs = []
43+
this.lastSignalsApiReq = undefined as any as Request
44+
this.lastTrackingApiReq = undefined as any as Request
3545

3646
await Promise.all([
3747
this.mockSignalsApi(),
@@ -42,7 +52,8 @@ export class BasePage {
4252

4353
async mockTrackingApi() {
4454
await this.page.route('https://api.segment.io/v1/*', (route, request) => {
45-
this.trackingApiReq = request
55+
this.lastTrackingApiReq = request
56+
this.trackingApiReqs.push(request.postDataJSON())
4657
if (request.method().toLowerCase() !== 'post') {
4758
throw new Error(`Unexpected method: ${request.method()}`)
4859
}
@@ -56,15 +67,16 @@ export class BasePage {
5667
})
5768
}
5869

59-
waitForTrackingApiFlush() {
60-
return this.page.waitForResponse('https://api.segment.io/v1/*')
70+
waitForTrackingApiFlush(timeout = 5000) {
71+
return this.page.waitForResponse('https://api.segment.io/v1/*', { timeout })
6172
}
6273

6374
async mockSignalsApi() {
6475
await this.page.route(
6576
'https://signals.segment.io/v1/*',
6677
(route, request) => {
67-
this.signalsApiReq = request
78+
this.lastSignalsApiReq = request
79+
this.signalsApiReqs.push(request.postDataJSON())
6880
if (request.method().toLowerCase() !== 'post') {
6981
throw new Error(`Unexpected method: ${request.method()}`)
7082
}
@@ -79,8 +91,10 @@ export class BasePage {
7991
)
8092
}
8193

82-
waitForSignalsApiFlush() {
83-
return this.page.waitForResponse('https://signals.segment.io/v1/*')
94+
waitForSignalsApiFlush(timeout = 5000) {
95+
return this.page.waitForResponse('https://signals.segment.io/v1/*', {
96+
timeout,
97+
})
8498
}
8599

86100
async mockCDNSettings() {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { test, expect } from '@playwright/test'
2+
import { IndexPage } from './index-page'
3+
import fs from 'fs'
4+
import path from 'path'
5+
import { SegmentEvent } from '@segment/analytics-next'
6+
/**
7+
* This test ensures that
8+
*/
9+
const indexPage = new IndexPage()
10+
11+
const normalizeSnapshotEvent = (el: SegmentEvent) => {
12+
return {
13+
type: el.properties?.type,
14+
event: el.event,
15+
userId: el.userId,
16+
groupId: el.groupId,
17+
anonymousId: expect.any(String),
18+
integrations: el.integrations,
19+
properties: el.properties,
20+
context: {
21+
page: el.context?.page,
22+
},
23+
}
24+
}
25+
26+
const snapshot = (
27+
JSON.parse(
28+
fs.readFileSync(
29+
path.join(__dirname, 'snapshots/all-segment-events-snapshot.json'),
30+
{
31+
encoding: 'utf-8',
32+
}
33+
)
34+
) as SegmentEvent[]
35+
).map(normalizeSnapshotEvent)
36+
37+
test('Segment events', async ({ page }) => {
38+
const basicEdgeFn = `
39+
// this is a process signal function
40+
const processSignal = (signal) => {
41+
analytics.identify('john', { found: true })
42+
analytics.group('foo', { hello: 'world' })
43+
analytics.alias('john', 'johnsmith')
44+
analytics.track('a track call', {foo: 'bar'})
45+
analytics.page('Retail Page', 'Home', { url: 'http://my-home.com', title: 'Some Title' });
46+
}`
47+
48+
await indexPage.load(page, basicEdgeFn)
49+
await Promise.all([
50+
indexPage.clickButton(),
51+
indexPage.waitForSignalsApiFlush(),
52+
indexPage.waitForTrackingApiFlush(),
53+
])
54+
55+
const trackingApiReqs = indexPage.trackingApiReqs.map(normalizeSnapshotEvent)
56+
57+
expect(trackingApiReqs).toEqual(snapshot)
58+
})

packages/signals/signals-integration-tests/src/tests/signals-vanilla/basic.test.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,17 @@ import { IndexPage } from './index-page'
44

55
const indexPage = new IndexPage()
66

7+
const basicEdgeFn = `
8+
// this is a process signal function
9+
const processSignal = (signal) => {
10+
if (signal.type === 'interaction') {
11+
const eventName = signal.data.eventType + ' ' + '[' + signal.type + ']'
12+
analytics.track(eventName, signal.data)
13+
}
14+
}`
15+
716
test.beforeEach(async ({ page }) => {
8-
await indexPage.load(page)
17+
await indexPage.load(page, basicEdgeFn)
918
})
1019

1120
test('network signals', async () => {
@@ -15,7 +24,8 @@ test('network signals', async () => {
1524
await indexPage.mockRandomJSONApi()
1625
await indexPage.makeFetchCallToRandomJSONApi()
1726
await indexPage.waitForSignalsApiFlush()
18-
const batch = indexPage.signalsApiReq.postDataJSON().batch as SegmentEvent[]
27+
const batch = indexPage.lastSignalsApiReq.postDataJSON()
28+
.batch as SegmentEvent[]
1929
const networkEvents = batch.filter(
2030
(el: SegmentEvent) => el.properties!.type === 'network'
2131
)
@@ -41,7 +51,7 @@ test('instrumentation signals', async () => {
4151
indexPage.waitForSignalsApiFlush(),
4252
])
4353

44-
const signalReqJSON = indexPage.signalsApiReq.postDataJSON()
54+
const signalReqJSON = indexPage.lastSignalsApiReq.postDataJSON()
4555

4656
const isoDateRegEx = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
4757
const instrumentationEvents = signalReqJSON.batch.filter(
@@ -59,19 +69,19 @@ test('instrumentation signals', async () => {
5969
})
6070
})
6171

62-
test('interaction signals', async ({ page }) => {
72+
test('interaction signals', async () => {
6373
/**
6474
* Make a button click, see if it:
6575
* - creates an interaction signal that sends to the signals endpoint
6676
* - creates an analytics event that sends to the tracking endpoint
6777
*/
6878
await Promise.all([
69-
page.click('button'),
79+
indexPage.clickButton(),
7080
indexPage.waitForSignalsApiFlush(),
7181
indexPage.waitForTrackingApiFlush(),
7282
])
7383

74-
const signalsReqJSON = indexPage.signalsApiReq.postDataJSON()
84+
const signalsReqJSON = indexPage.lastSignalsApiReq.postDataJSON()
7585
const interactionSignals = signalsReqJSON.batch.filter(
7686
(el: SegmentEvent) => el.properties!.type === 'interaction'
7787
)
@@ -104,7 +114,7 @@ test('interaction signals', async ({ page }) => {
104114
},
105115
})
106116

107-
const analyticsReqJSON = indexPage.trackingApiReq.postDataJSON()
117+
const analyticsReqJSON = indexPage.lastTrackingApiReq.postDataJSON()
108118

109119
expect(analyticsReqJSON).toMatchObject({
110120
writeKey: '<SOME_WRITE_KEY>',
@@ -128,7 +138,7 @@ test('navigation signals', async ({ page }) => {
128138
{
129139
// on page load, a navigation signal should be sent
130140
await indexPage.waitForSignalsApiFlush()
131-
const signalReqJSON = indexPage.signalsApiReq.postDataJSON()
141+
const signalReqJSON = indexPage.lastSignalsApiReq.postDataJSON()
132142
const navigationEvents = signalReqJSON.batch.filter(
133143
(el: SegmentEvent) => el.properties!.type === 'navigation'
134144
)
@@ -153,7 +163,7 @@ test('navigation signals', async ({ page }) => {
153163
window.location.hash = '#foo'
154164
})
155165
await indexPage.waitForSignalsApiFlush()
156-
const signalReqJSON = indexPage.signalsApiReq.postDataJSON()
166+
const signalReqJSON = indexPage.lastSignalsApiReq.postDataJSON()
157167

158168
const navigationEvents = signalReqJSON.batch.filter(
159169
(el: SegmentEvent) => el.properties!.type === 'navigation'
Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
import { BasePage } from '../../helpers/base-page-object'
22
import { promiseTimeout } from '@internal/test-helpers'
33

4-
const edgeFn = `
5-
// this is a process signal function
6-
const processSignal = (signal) => {
7-
if (signal.type === 'interaction') {
8-
const eventName = signal.data.eventType + ' ' + '[' + signal.type + ']'
9-
analytics.track(eventName, signal.data)
10-
}
11-
}`
12-
134
export class IndexPage extends BasePage {
145
constructor() {
15-
super(`/signals-vanilla/index.html`, edgeFn)
6+
super(`/signals-vanilla/index.html`)
167
}
178

189
async makeAnalyticsPageCall(): Promise<unknown> {
@@ -24,7 +15,7 @@ export class IndexPage extends BasePage {
2415
}
2516

2617
async mockRandomJSONApi() {
27-
await this.page.route('http://localhost:3000/api/foo', (route) => {
18+
await this.page.route('http://localhost:5432/api/foo', (route) => {
2819
return route.fulfill({
2920
contentType: 'application/json',
3021
status: 200,
@@ -37,7 +28,7 @@ export class IndexPage extends BasePage {
3728

3829
async makeFetchCallToRandomJSONApi(): Promise<void> {
3930
return this.page.evaluate(() => {
40-
return fetch('http://localhost:3000/api/foo', {
31+
return fetch('http://localhost:5432/api/foo', {
4132
method: 'POST',
4233
headers: {
4334
'Content-Type': 'application/json',
@@ -48,4 +39,8 @@ export class IndexPage extends BasePage {
4839
.catch(console.error)
4940
})
5041
}
42+
43+
async clickButton() {
44+
return this.page.click('#some-button')
45+
}
5146
}

0 commit comments

Comments
 (0)