Skip to content

Commit 930af49

Browse files
authored
rename 'shouldLoad' -> 'shouldLoadSegment'; add shouldLoadWrapper API (#983)
1 parent fca97a2 commit 930af49

File tree

11 files changed

+168
-192
lines changed

11 files changed

+168
-192
lines changed

.changeset/fair-pillows-sin.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@segment/analytics-consent-tools': major
3+
---
4+
5+
* Rename `shouldLoad` -> `shouldLoadSegment`
6+
* Remove redundant `shouldDisableConsentRequirement` setting, in favor of shouldLoad's `ctx.abort({loadSegmentNormally: true})`
7+
* Create `shouldLoadWrapper` API for waiting for consent script initialization.

packages/consent/consent-tools/README.md

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
import { createWrapper, resolveWhen } from '@segment/analytics-consent-tools'
88

99
export const withCMP = createWrapper({
10+
// Wait to load wrapper or call "shouldLoadSegment" until window.CMP exists.
11+
shouldLoadWrapper: async () => {
12+
await resolveWhen(() => window.CMP !== undefined, 500)
13+
},
1014

1115
// Wrapper waits to load segment / get categories until this function returns / resolves
12-
shouldLoad: async (ctx) => {
13-
const CMP = await getCMP()
16+
shouldLoadSegment: async (ctx) => {
1417
await resolveWhen(
15-
() => !CMP.popUpVisible(),
18+
() => !window.CMP.popUpVisible(),
1619
500
1720
)
1821

@@ -24,24 +27,16 @@ export const withCMP = createWrapper({
2427
}
2528
},
2629

27-
getCategories: async () => {
28-
const CMP = await getCMP()
29-
return normalizeCategories(CMP.consentedCategories()) // Expected format: { foo: true, bar: false }
30+
getCategories: () => {
31+
return normalizeCategories(window.CMP.consentedCategories()) // Expected format: { foo: true, bar: false }
3032
},
3133

32-
registerOnConsentChanged: async (setCategories) => {
33-
const CMP = await getCMP()
34-
CMP.onConsentChanged((event) => {
34+
registerOnConsentChanged: (setCategories) => {
35+
window.CMP.onConsentChanged((event) => {
3536
setCategories(normalizeCategories(event.detail))
3637
})
3738
},
3839
})
39-
40-
41-
const getCMP = async () => {
42-
await resolveWhen(() => window.CMP !== undefined, 500)
43-
return window.CMP
44-
}
4540
```
4641

4742
## Wrapper Usage API

packages/consent/consent-tools/src/domain/__tests__/create-wrapper.test.ts

Lines changed: 49 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -123,29 +123,31 @@ describe(createWrapper, () => {
123123
expect(args.length).toBeTruthy()
124124
})
125125

126-
describe('shouldLoad', () => {
126+
describe('shouldLoadSegment', () => {
127127
describe('Throwing errors / aborting load', () => {
128128
const createShouldLoadThatThrows = (
129129
...args: Parameters<LoadContext['abort']>
130130
) => {
131131
let err: Error
132-
const shouldLoad = jest.fn().mockImplementation((ctx: LoadContext) => {
133-
try {
134-
ctx.abort(...args)
135-
throw new Error('Fail')
136-
} catch (_err: any) {
137-
err = _err
138-
}
139-
})
140-
return { shouldLoad, getError: () => err }
132+
const shouldLoadSegment = jest
133+
.fn()
134+
.mockImplementation((ctx: LoadContext) => {
135+
try {
136+
ctx.abort(...args)
137+
throw new Error('Fail')
138+
} catch (_err: any) {
139+
err = _err
140+
}
141+
})
142+
return { shouldLoadSegment, getError: () => err }
141143
}
142144

143145
it('should throw a special error if ctx.abort is called', async () => {
144-
const { shouldLoad, getError } = createShouldLoadThatThrows({
146+
const { shouldLoadSegment, getError } = createShouldLoadThatThrows({
145147
loadSegmentNormally: true,
146148
})
147149
wrapTestAnalytics({
148-
shouldLoad,
150+
shouldLoadSegment,
149151
})
150152
await analytics.load(DEFAULT_LOAD_SETTINGS)
151153
expect(getError() instanceof AbortLoadError).toBeTruthy()
@@ -155,7 +157,7 @@ describe(createWrapper, () => {
155157
`should not log a console error or throw an error if ctx.abort is called (%p)`,
156158
async (args) => {
157159
wrapTestAnalytics({
158-
shouldLoad: (ctx) => ctx.abort(args),
160+
shouldLoadSegment: (ctx) => ctx.abort(args),
159161
})
160162
const result = await analytics.load(DEFAULT_LOAD_SETTINGS)
161163
expect(result).toBeUndefined()
@@ -164,21 +166,31 @@ describe(createWrapper, () => {
164166
)
165167

166168
it('should allow segment to be loaded normally (with all consent wrapper behavior disabled) via ctx.abort', async () => {
169+
const mockCdnSettings = settingsBuilder.build()
170+
167171
wrapTestAnalytics({
168-
shouldLoad: (ctx) => {
172+
shouldLoadSegment: (ctx) => {
169173
ctx.abort({
170-
loadSegmentNormally: true, // magic config option
174+
loadSegmentNormally: true,
171175
})
172176
},
173177
})
174178

175-
await analytics.load(DEFAULT_LOAD_SETTINGS)
179+
const loadArgs: [any, any] = [
180+
{
181+
...DEFAULT_LOAD_SETTINGS,
182+
cdnSettings: mockCdnSettings,
183+
},
184+
{},
185+
]
186+
await analytics.load(...loadArgs)
176187
expect(analyticsLoadSpy).toBeCalled()
188+
expect(getAnalyticsLoadLastCall().args).toEqual(loadArgs)
177189
})
178190

179191
it('should allow segment loading to be completely aborted via ctx.abort', async () => {
180192
wrapTestAnalytics({
181-
shouldLoad: (ctx) => {
193+
shouldLoadSegment: (ctx) => {
182194
ctx.abort({
183195
loadSegmentNormally: false, // magic config option
184196
})
@@ -189,11 +201,11 @@ describe(createWrapper, () => {
189201
expect(analyticsLoadSpy).not.toBeCalled()
190202
})
191203
it('should throw a validation error if ctx.abort is called incorrectly', async () => {
192-
const { getError, shouldLoad } = createShouldLoadThatThrows(
204+
const { getError, shouldLoadSegment } = createShouldLoadThatThrows(
193205
undefined as any
194206
)
195207
wrapTestAnalytics({
196-
shouldLoad,
208+
shouldLoadSegment,
197209
})
198210
await analytics.load(DEFAULT_LOAD_SETTINGS)
199211
expect(getError().message).toMatch(/validation/i)
@@ -202,7 +214,7 @@ describe(createWrapper, () => {
202214
it('An unrecognized Error (non-consent) error should bubble up, but we should not log any additional console error', async () => {
203215
const err = new Error('hello')
204216
wrapTestAnalytics({
205-
shouldLoad: () => {
217+
shouldLoadSegment: () => {
206218
throw err
207219
},
208220
})
@@ -215,7 +227,7 @@ describe(createWrapper, () => {
215227
expect(analyticsLoadSpy).not.toBeCalled()
216228
})
217229
})
218-
it('should first call shouldLoad(), then wait for it to resolve/return before calling analytics.load()', async () => {
230+
it('should first call shouldLoadSegment(), then wait for it to resolve/return before calling analytics.load()', async () => {
219231
const fnCalls: string[] = []
220232
analyticsLoadSpy.mockImplementationOnce(() => {
221233
fnCalls.push('analytics.load')
@@ -224,31 +236,31 @@ describe(createWrapper, () => {
224236
const shouldLoadMock: jest.Mock<undefined> = jest
225237
.fn()
226238
.mockImplementationOnce(async () => {
227-
fnCalls.push('shouldLoad')
239+
fnCalls.push('shouldLoadSegment')
228240
})
229241

230242
wrapTestAnalytics({
231-
shouldLoad: shouldLoadMock,
243+
shouldLoadSegment: shouldLoadMock,
232244
})
233245

234246
await analytics.load(DEFAULT_LOAD_SETTINGS)
235-
expect(fnCalls).toEqual(['shouldLoad', 'analytics.load'])
247+
expect(fnCalls).toEqual(['shouldLoadSegment', 'analytics.load'])
236248
})
237249
})
238250

239251
describe('getCategories', () => {
240252
test.each([
241253
{
242-
shouldLoad: () => undefined,
254+
shouldLoadSegment: () => undefined,
243255
returnVal: 'undefined',
244256
},
245257
{
246-
shouldLoad: () => Promise.resolve(undefined),
258+
shouldLoadSegment: () => Promise.resolve(undefined),
247259
returnVal: 'Promise<undefined>',
248260
},
249261
])(
250-
'if shouldLoad() returns nil ($returnVal), intial categories will come from getCategories()',
251-
async ({ shouldLoad }) => {
262+
'if shouldLoadSegment() returns nil ($returnVal), intial categories will come from getCategories()',
263+
async ({ shouldLoadSegment }) => {
252264
const mockCdnSettings = {
253265
integrations: {
254266
mockIntegration: {
@@ -258,7 +270,7 @@ describe(createWrapper, () => {
258270
}
259271

260272
wrapTestAnalytics({
261-
shouldLoad: shouldLoad,
273+
shouldLoadSegment: shouldLoadSegment,
262274
})
263275
await analytics.load({
264276
...DEFAULT_LOAD_SETTINGS,
@@ -282,7 +294,7 @@ describe(createWrapper, () => {
282294
returnVal: 'Promise<Categories>',
283295
},
284296
])(
285-
'if shouldLoad() returns categories ($returnVal), those will be the initial categories',
297+
'if shouldLoadSegment() returns categories ($returnVal), those will be the initial categories',
286298
async ({ getCategories }) => {
287299
const mockCdnSettings = {
288300
integrations: {
@@ -296,7 +308,7 @@ describe(createWrapper, () => {
296308

297309
wrapTestAnalytics({
298310
getCategories: mockGetCategories,
299-
shouldLoad: () => undefined,
311+
shouldLoadSegment: () => undefined,
300312
})
301313
await analytics.load({
302314
...DEFAULT_LOAD_SETTINGS,
@@ -321,7 +333,7 @@ describe(createWrapper, () => {
321333

322334
test('analytics.load should reject if categories are in the wrong format', async () => {
323335
wrapTestAnalytics({
324-
shouldLoad: () => Promise.resolve('sup' as any),
336+
shouldLoadSegment: () => Promise.resolve('sup' as any),
325337
})
326338
await expect(() => analytics.load(DEFAULT_LOAD_SETTINGS)).rejects.toThrow(
327339
/validation/i
@@ -331,7 +343,7 @@ describe(createWrapper, () => {
331343
test('analytics.load should reject if categories are undefined', async () => {
332344
wrapTestAnalytics({
333345
getCategories: () => undefined as any,
334-
shouldLoad: () => undefined,
346+
shouldLoadSegment: () => undefined,
335347
})
336348
await expect(() => analytics.load(DEFAULT_LOAD_SETTINGS)).rejects.toThrow(
337349
/validation/i
@@ -390,7 +402,7 @@ describe(createWrapper, () => {
390402
.build()
391403

392404
wrapTestAnalytics({
393-
shouldLoad: () => ({ Foo: true }),
405+
shouldLoadSegment: () => ({ Foo: true }),
394406
})
395407
await analytics.load({
396408
...DEFAULT_LOAD_SETTINGS,
@@ -415,7 +427,7 @@ describe(createWrapper, () => {
415427
.build()
416428

417429
wrapTestAnalytics({
418-
shouldLoad: () => ({ Foo: true, Bar: true }),
430+
shouldLoadSegment: () => ({ Foo: true, Bar: true }),
419431
})
420432
await analytics.load({
421433
...DEFAULT_LOAD_SETTINGS,
@@ -440,7 +452,7 @@ describe(createWrapper, () => {
440452
.build()
441453

442454
wrapTestAnalytics({
443-
shouldLoad: () => ({ Foo: true }),
455+
shouldLoadSegment: () => ({ Foo: true }),
444456
})
445457
await analytics.load({
446458
...DEFAULT_LOAD_SETTINGS,
@@ -456,75 +468,6 @@ describe(createWrapper, () => {
456468
})
457469
})
458470

459-
describe('shouldDisableConsentRequirement', () => {
460-
describe('if true on wrapper initialization', () => {
461-
it('should load analytics as usual', async () => {
462-
wrapTestAnalytics({
463-
shouldDisableConsentRequirement: () => true,
464-
})
465-
await analytics.load(DEFAULT_LOAD_SETTINGS)
466-
expect(analyticsLoadSpy).toBeCalled()
467-
})
468-
469-
it('should not call shouldLoad if called on first', async () => {
470-
const shouldLoad = jest.fn()
471-
wrapTestAnalytics({
472-
shouldDisableConsentRequirement: () => true,
473-
shouldLoad,
474-
})
475-
await analytics.load(DEFAULT_LOAD_SETTINGS)
476-
expect(shouldLoad).not.toBeCalled()
477-
})
478-
479-
it('should work with promises if false', async () => {
480-
const shouldLoad = jest.fn()
481-
wrapTestAnalytics({
482-
shouldDisableConsentRequirement: () => Promise.resolve(false),
483-
shouldLoad,
484-
})
485-
await analytics.load(DEFAULT_LOAD_SETTINGS)
486-
expect(shouldLoad).toBeCalled()
487-
})
488-
489-
it('should work with promises if true', async () => {
490-
const shouldLoad = jest.fn()
491-
wrapTestAnalytics({
492-
shouldDisableConsentRequirement: () => Promise.resolve(true),
493-
shouldLoad,
494-
})
495-
await analytics.load(DEFAULT_LOAD_SETTINGS)
496-
expect(shouldLoad).not.toBeCalled()
497-
})
498-
499-
it('should forward all arguments to the original analytics.load method', async () => {
500-
const mockCdnSettings = settingsBuilder.build()
501-
502-
wrapTestAnalytics({
503-
shouldDisableConsentRequirement: () => true,
504-
})
505-
506-
const loadArgs: [any, any] = [
507-
{
508-
...DEFAULT_LOAD_SETTINGS,
509-
cdnSettings: mockCdnSettings,
510-
},
511-
{},
512-
]
513-
await analytics.load(...loadArgs)
514-
expect(analyticsLoadSpy).toBeCalled()
515-
expect(getAnalyticsLoadLastCall().args).toEqual(loadArgs)
516-
})
517-
518-
it('should not stamp the event with consent info', async () => {
519-
wrapTestAnalytics({
520-
shouldDisableConsentRequirement: () => true,
521-
})
522-
await analytics.load(DEFAULT_LOAD_SETTINGS)
523-
expect(addSourceMiddlewareSpy).not.toBeCalled()
524-
})
525-
})
526-
})
527-
528471
describe('shouldDisableSegment', () => {
529472
it('should load analytics if disableAll returns false', async () => {
530473
wrapTestAnalytics({
@@ -567,7 +510,7 @@ describe(createWrapper, () => {
567510

568511
wrapTestAnalytics({
569512
getCategories,
570-
shouldLoad: () => {
513+
shouldLoadSegment: () => {
571514
// on first load, we should not get an error because this is a valid category setting
572515
return { invalidCategory: true }
573516
},

0 commit comments

Comments
 (0)