Skip to content

Commit ee838db

Browse files
authored
Fix argument resolver interpreting settings arg correctly if first and/or second arg is null (#1246)
1 parent d5829da commit ee838db

File tree

6 files changed

+162
-39
lines changed

6 files changed

+162
-39
lines changed

.changeset/lazy-snakes-add.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@segment/analytics-next': minor
3+
'@segment/analytics-signals': patch
4+
---
5+
6+
Fix argument resolver bug where the following would not set the correct options:
7+
```ts
8+
analytics.page(
9+
null,
10+
'foo',
11+
{ url: "https://foo.com" },
12+
{ context: { __eventOrigin: { type: 'Signal' } } // would not be set correctly
13+
)
14+
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"build": "turbo run build --filter='./packages/**'",
2121
"watch": "turbo run watch --filter='./packages/**'",
2222
"dev": "yarn workspace @playground/next-playground run dev",
23-
"prepush": "turbo run lint --filter='...[master...HEAD]' && CI=true yarn turbo run test --filter='...[master...HEAD]' -- --colors --silent",
23+
"prepush": "turbo run lint --filter='...[master...HEAD]'",
2424
"postinstall": "husky install",
2525
"changeset": "changeset",
2626
"update-versions-and-changelogs": "changeset version && yarn version-run-all && bash scripts/update-lockfile.sh",

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

Lines changed: 96 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -195,24 +195,7 @@ describe(resolveArguments, () => {
195195
})
196196

197197
describe(resolvePageArguments, () => {
198-
test('should accept (category, name, properties, options, callback)', () => {
199-
const fn = jest.fn()
200-
const [category, name, properties, options, cb] = resolvePageArguments(
201-
'category',
202-
'name',
203-
bananaPhone,
204-
baseOptions,
205-
fn
206-
)
207-
208-
expect(category).toEqual('category')
209-
expect(name).toEqual('name')
210-
expect(properties).toEqual(bananaPhone)
211-
expect(options).toEqual(baseOptions)
212-
expect(cb).toEqual(fn)
213-
})
214-
215-
test('empty strings ("", "", "", { integrations })', () => {
198+
test('empty strings ("", "", null, { integrations })', () => {
216199
const [category, name, properties, options] = resolvePageArguments(
217200
'',
218201
'',
@@ -302,6 +285,23 @@ describe(resolvePageArguments, () => {
302285
expect(options).toEqual({})
303286
})
304287

288+
it('should accept (category, name, properties, options, cb)', () => {
289+
const fn = jest.fn()
290+
const [category, name, properties, options, cb] = resolvePageArguments(
291+
'foo',
292+
'name',
293+
bananaPhone,
294+
baseOptions,
295+
fn
296+
)
297+
298+
expect(category).toEqual('foo')
299+
expect(name).toEqual('name')
300+
expect(properties).toEqual(bananaPhone)
301+
expect(options).toEqual(baseOptions)
302+
expect(cb).toEqual(fn)
303+
})
304+
305305
it('should accept (name, callback)', () => {
306306
const fn = jest.fn()
307307
const [category, name, properties, options, cb] = resolvePageArguments(
@@ -347,6 +347,84 @@ describe(resolvePageArguments, () => {
347347
expect(name).toEqual(null)
348348
expect(category).toEqual(null)
349349
})
350+
351+
test('should accept (category = null, name, properties, options, callback)', () => {
352+
const fn = jest.fn()
353+
const [category, name, properties, options, cb] = resolvePageArguments(
354+
null,
355+
'name',
356+
bananaPhone,
357+
baseOptions,
358+
fn
359+
)
360+
361+
expect(category).toEqual(null)
362+
expect(name).toEqual('name')
363+
expect(properties).toEqual(bananaPhone)
364+
expect(options).toEqual(baseOptions)
365+
expect(cb).toEqual(fn)
366+
})
367+
368+
test('should accept (null, null, properties, options, callback)', () => {
369+
const fn = jest.fn()
370+
const [category, name, properties, options, cb] = resolvePageArguments(
371+
null,
372+
null,
373+
bananaPhone,
374+
baseOptions,
375+
fn
376+
)
377+
378+
expect(category).toEqual(null)
379+
expect(name).toEqual(null)
380+
expect(properties).toEqual(bananaPhone)
381+
expect(options).toEqual(baseOptions)
382+
expect(cb).toEqual(fn)
383+
})
384+
385+
test('should accept (null, null, null, options, callback)', () => {
386+
const fn = jest.fn()
387+
const [category, name, properties, options, cb] = resolvePageArguments(
388+
null,
389+
null,
390+
null,
391+
baseOptions,
392+
fn
393+
)
394+
395+
expect(category).toEqual(null)
396+
expect(name).toEqual(null)
397+
expect(properties).toEqual({})
398+
expect(options).toEqual(baseOptions)
399+
expect(cb).toEqual(fn)
400+
})
401+
402+
test('should accept (undefined, undefined, undefined, options, callback)', () => {
403+
const fn = jest.fn()
404+
const [category, name, properties, options, cb] = resolvePageArguments(
405+
undefined,
406+
undefined,
407+
undefined,
408+
baseOptions,
409+
fn
410+
)
411+
412+
expect(category).toEqual(null)
413+
expect(name).toEqual(null)
414+
expect(properties).toEqual({})
415+
expect(options).toEqual(baseOptions)
416+
expect(cb).toEqual(fn)
417+
})
418+
419+
test('should accept (callback)', () => {
420+
const fn = jest.fn()
421+
const [category, name, properties, options, cb] = resolvePageArguments(fn)
422+
expect(category).toEqual(null)
423+
expect(name).toEqual(null)
424+
expect(properties).toEqual({})
425+
expect(options).toEqual({})
426+
expect(cb).toEqual(fn)
427+
})
350428
})
351429

352430
describe(resolveUserArguments, () => {

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

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
import { Context } from '../context'
88
import {
99
Callback,
10-
JSONObject,
1110
Options,
1211
EventProperties,
1312
SegmentEvent,
@@ -52,12 +51,15 @@ export function resolveArguments(
5251
return [name, data, opts, cb]
5352
}
5453

54+
const isNil = (val: any): val is null | undefined =>
55+
val === null || val === undefined
56+
5557
/**
5658
* Helper for page, screen methods
5759
*/
5860
export function resolvePageArguments(
59-
category?: string | object,
60-
name?: string | object | Callback,
61+
category?: string | object | null,
62+
name?: string | object | Callback | null,
6163
properties?: EventProperties | Options | Callback | null,
6264
options?: Options | Callback,
6365
callback?: Callback
@@ -68,38 +70,67 @@ export function resolvePageArguments(
6870
Options,
6971
Callback | undefined
7072
] {
73+
let resolvedProperties: EventProperties
74+
let resolvedOptions: Options
7175
let resolvedCategory: string | undefined | null = null
7276
let resolvedName: string | undefined | null = null
7377
const args = [category, name, properties, options, callback]
7478

79+
// handle:
80+
// - analytics.page('name')
81+
// - analytics.page('category', 'name')
7582
const strings = args.filter(isString)
76-
if (strings[0] !== undefined && strings[1] !== undefined) {
77-
resolvedCategory = strings[0]
78-
resolvedName = strings[1]
79-
}
80-
8183
if (strings.length === 1) {
8284
resolvedCategory = null
8385
resolvedName = strings[0]
86+
} else if (strings.length === 2) {
87+
if (typeof args[0] === 'string') {
88+
resolvedCategory = args[0]
89+
}
90+
if (typeof args[1] === 'string') {
91+
resolvedName = args[1]
92+
}
8493
}
8594

95+
// handle: analytics.page('category', 'name', properties, options, callback)
8696
const resolvedCallback = args.find(isFunction) as Callback | undefined
8797

88-
const objects = args.filter((obj) => {
89-
if (resolvedName === null) {
90-
return isPlainObject(obj)
91-
}
92-
return isPlainObject(obj) || obj === null
93-
}) as Array<JSONObject | null>
98+
// handle:
99+
// - analytics.page('name')
100+
// - analytics.page('category', 'name')
101+
// - analytics.page(properties)
102+
// - analytics.page(properties, options)
103+
// - analytics.page('name', properties)
104+
// - analytics.page('name', properties, options)
105+
// - analytics.page('category', 'name', properties, options)
106+
// - analytics.page('category', 'name', properties, options, callback)
107+
// - analytics.page('category', 'name', callback)
108+
// - analytics.page(callback), etc
109+
args.forEach((obj, argIdx) => {
110+
if (isPlainObject(obj)) {
111+
if (argIdx === 0) {
112+
resolvedProperties = obj
113+
}
114+
115+
if (argIdx === 1 || argIdx == 2) {
116+
if (isNil(resolvedProperties)) {
117+
resolvedProperties = obj
118+
} else {
119+
resolvedOptions = obj
120+
}
121+
}
94122

95-
const resolvedProperties = (objects[0] ?? {}) as EventProperties
96-
const resolvedOptions = (objects[1] ?? {}) as Options
123+
if (argIdx === 3) {
124+
resolvedOptions = obj
125+
}
126+
}
127+
})
97128

98129
return [
99130
resolvedCategory,
100131
resolvedName,
101-
resolvedProperties,
102-
resolvedOptions,
132+
(resolvedProperties ??= {}),
133+
(resolvedOptions ??= {}),
103134
resolvedCallback,
104135
]
105136
}

packages/signals/signals/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"tslib": "^2.4.1"
5050
},
5151
"peerDependencies": {
52-
"@segment/analytics-next": ">1.72.0"
52+
"@segment/analytics-next": ">1.78.0"
5353
},
5454
"peerDependenciesMeta": {
5555
"@segment/analytics-next": {

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6026,7 +6026,7 @@ __metadata:
60266026
node-fetch: ^2.6.7
60276027
tslib: ^2.4.1
60286028
peerDependencies:
6029-
"@segment/analytics-next": ">1.72.0"
6029+
"@segment/analytics-next": ">1.78.0"
60306030
peerDependenciesMeta:
60316031
"@segment/analytics-next":
60326032
optional: true

0 commit comments

Comments
 (0)