Skip to content

Commit e7b001b

Browse files
patrick-wualexs-mparticle
authored andcommitted
feat: Add optimizely attribute fetching functionality (#20)
1 parent 3b68927 commit e7b001b

File tree

2 files changed

+258
-3
lines changed

2 files changed

+258
-3
lines changed

src/Rokt-Kit.js

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var constructor = function () {
2424
self.name = name;
2525
self.moduleId = moduleId;
2626
self.isInitialized = false;
27+
2728
self.launcher = null;
2829
self.filters = {};
2930
self.filteredUser = {};
@@ -39,6 +40,7 @@ var constructor = function () {
3940
var accountId = settings.accountId;
4041
var sandboxMode = window.mParticle.getEnvironment() === 'development';
4142
self.userAttributes = filteredUserAttributes;
43+
self.onboardingExpProvider = settings.onboardingExpProvider;
4244

4345
if (testMode) {
4446
attachLauncher(accountId, sandboxMode);
@@ -116,9 +118,18 @@ var constructor = function () {
116118

117119
self.userAttributes = filteredAttributes;
118120

119-
var selectPlacementsAttributes = mergeObjects(filteredAttributes, {
120-
mpid: mpid,
121-
});
121+
var optimizelyAttributes =
122+
self.onboardingExpProvider === 'Optimizely'
123+
? fetchOptimizely()
124+
: {};
125+
126+
var selectPlacementsAttributes = mergeObjects(
127+
filteredAttributes,
128+
optimizelyAttributes,
129+
{
130+
mpid: mpid,
131+
}
132+
);
122133

123134
var selectPlacementsOptions = mergeObjects(options, {
124135
attributes: selectPlacementsAttributes,
@@ -187,6 +198,46 @@ var constructor = function () {
187198
this.selectPlacements = selectPlacements;
188199

189200
// mParticle Kit Callback Methods
201+
function fetchOptimizely() {
202+
var forwarders = window.mParticle
203+
._getActiveForwarders()
204+
.filter(function (forwarder) {
205+
return forwarder.name === 'Optimizely';
206+
});
207+
208+
try {
209+
if (forwarders.length > 0 && window.optimizely) {
210+
// Get the state object
211+
var optimizelyState = window.optimizely.get('state');
212+
if (
213+
!optimizelyState ||
214+
!optimizelyState.getActiveExperimentIds
215+
) {
216+
return {};
217+
}
218+
// Get active experiment IDs
219+
var activeExperimentIds =
220+
optimizelyState.getActiveExperimentIds();
221+
// Get variations for each active experiment
222+
var activeExperiments = activeExperimentIds.reduce(function (
223+
acc,
224+
expId
225+
) {
226+
acc[
227+
'rokt.custom.optimizely.experiment.' +
228+
expId +
229+
'.variationId'
230+
] = optimizelyState.getVariationMap()[expId].id;
231+
return acc;
232+
},
233+
{});
234+
return activeExperiments;
235+
}
236+
} catch (error) {
237+
console.error('Error fetching Optimizely attributes:', error);
238+
}
239+
return {};
240+
}
190241
this.init = initForwarder;
191242
this.setUserAttribute = setUserAttribute;
192243
this.onUserIdentified = onUserIdentified;

test/src/tests.js

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ describe('Rokt Forwarder', () => {
5454
};
5555
},
5656
};
57+
mParticle._getActiveForwarders = function () {
58+
return [];
59+
};
5760
// -------------------START EDITING BELOW:-----------------------
5861
var MockRoktForwarder = function () {
5962
var self = this;
@@ -413,4 +416,205 @@ describe('Rokt Forwarder', () => {
413416
.should.equal('123');
414417
});
415418
});
419+
420+
describe('#fetchOptimizely', () => {
421+
// Helper functions for setting up Optimizely mocks
422+
function setupValidOptimizelyMock(experiments) {
423+
window.optimizely = {
424+
get: function (key) {
425+
if (key === 'state') {
426+
return {
427+
getActiveExperimentIds: function () {
428+
return Object.keys(experiments);
429+
},
430+
getVariationMap: function () {
431+
return experiments;
432+
},
433+
};
434+
}
435+
},
436+
};
437+
}
438+
439+
function setupInvalidOptimizelyMock(stateObject) {
440+
window.optimizely = {
441+
get: function (key) {
442+
if (key === 'state') {
443+
return stateObject;
444+
}
445+
},
446+
};
447+
}
448+
449+
// Common test setup
450+
async function initAndSelectPlacements(settings = {}) {
451+
await window.mParticle.forwarder.init(
452+
{
453+
accountId: '123456',
454+
...settings,
455+
},
456+
reportService.cb,
457+
true,
458+
null,
459+
{}
460+
);
461+
462+
await window.mParticle.forwarder.selectPlacements({
463+
identifier: 'test-placement',
464+
attributes: {
465+
test: 'test',
466+
},
467+
});
468+
}
469+
470+
beforeEach(() => {
471+
window.Rokt = new MockRoktForwarder();
472+
window.mParticle.Rokt = window.Rokt;
473+
window.mParticle.Rokt.attachKitCalled = false;
474+
window.mParticle.Rokt.attachKit = async (kit) => {
475+
window.mParticle.Rokt.attachKitCalled = true;
476+
window.mParticle.Rokt.kit = kit;
477+
Promise.resolve();
478+
};
479+
window.mParticle.forwarder.launcher = {
480+
selectPlacements: function (options) {
481+
window.mParticle.Rokt.selectPlacementsOptions = options;
482+
window.mParticle.Rokt.selectPlacementsCalled = true;
483+
},
484+
};
485+
window.mParticle.Rokt.filters = {
486+
userAttributesFilters: [],
487+
filterUserAttributes: function (attributes) {
488+
return attributes;
489+
},
490+
filteredUser: {
491+
getMPID: function () {
492+
return '123';
493+
},
494+
},
495+
};
496+
window.mParticle._getActiveForwarders = function () {
497+
return [{ name: 'Optimizely' }];
498+
};
499+
});
500+
501+
afterEach(() => {
502+
delete window.optimizely;
503+
});
504+
505+
describe('when Optimizely is properly configured', () => {
506+
it('should fetch experiment data for single experiment', async () => {
507+
setupValidOptimizelyMock({
508+
exp1: { id: 'var1' },
509+
});
510+
511+
await initAndSelectPlacements({
512+
onboardingExpProvider: 'Optimizely',
513+
});
514+
515+
window.Rokt.selectPlacementsOptions.attributes.should.have.property(
516+
'rokt.custom.optimizely.experiment.exp1.variationId',
517+
'var1'
518+
);
519+
});
520+
521+
it('should fetch experiment data for multiple experiments', async () => {
522+
setupValidOptimizelyMock({
523+
exp1: { id: 'var1' },
524+
exp2: { id: 'var2' },
525+
});
526+
527+
await initAndSelectPlacements({
528+
onboardingExpProvider: 'Optimizely',
529+
});
530+
531+
const attributes =
532+
window.Rokt.selectPlacementsOptions.attributes;
533+
attributes.should.have.property(
534+
'rokt.custom.optimizely.experiment.exp1.variationId',
535+
'var1'
536+
);
537+
attributes.should.have.property(
538+
'rokt.custom.optimizely.experiment.exp2.variationId',
539+
'var2'
540+
);
541+
});
542+
});
543+
544+
describe('when Optimizely is not properly configured', () => {
545+
it('should return empty object when Optimizely is not available', async () => {
546+
delete window.optimizely;
547+
548+
await initAndSelectPlacements({
549+
onboardingExpProvider: 'Optimizely',
550+
});
551+
552+
window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
553+
'rokt.custom.optimizely'
554+
);
555+
});
556+
557+
it('should return empty object when Optimizely state is undefined', async () => {
558+
setupInvalidOptimizelyMock(undefined);
559+
560+
await initAndSelectPlacements({
561+
onboardingExpProvider: 'Optimizely',
562+
});
563+
564+
window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
565+
'rokt.custom.optimizely'
566+
);
567+
});
568+
569+
it('should return empty object when Optimizely state has invalid format', async () => {
570+
setupInvalidOptimizelyMock({
571+
someOtherProperty: 'value',
572+
invalidFunction: function () {
573+
return null;
574+
},
575+
});
576+
577+
await initAndSelectPlacements({
578+
onboardingExpProvider: 'Optimizely',
579+
});
580+
581+
window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
582+
'rokt.custom.optimizely'
583+
);
584+
});
585+
586+
it('should return empty object when Optimizely state is missing required methods', async () => {
587+
setupInvalidOptimizelyMock({
588+
getVariationMap: function () {
589+
return {};
590+
},
591+
// Mocking a scenario for when getActiveExperimentIds() method is missing
592+
});
593+
594+
await initAndSelectPlacements({
595+
onboardingExpProvider: 'Optimizely',
596+
});
597+
598+
window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
599+
'rokt.custom.optimizely'
600+
);
601+
});
602+
});
603+
604+
describe('when Optimizely is not the provider', () => {
605+
it('should not fetch Optimizely data', async () => {
606+
setupValidOptimizelyMock({
607+
exp1: { id: 'var1' },
608+
});
609+
610+
await initAndSelectPlacements({
611+
onboardingExpProvider: 'NotOptimizely',
612+
});
613+
614+
window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
615+
'rokt.custom.optimizely'
616+
);
617+
});
618+
});
619+
});
416620
});

0 commit comments

Comments
 (0)