Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions src/Rokt-Kit.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var constructor = function () {
self.name = name;
self.moduleId = moduleId;
self.isInitialized = false;

self.launcher = null;
self.filters = {};
self.filteredUser = {};
Expand All @@ -39,6 +40,7 @@ var constructor = function () {
var accountId = settings.accountId;
var sandboxMode = window.mParticle.getEnvironment() === 'development';
self.userAttributes = filteredUserAttributes;
self.onboardingExpProvider = settings.onboardingExpProvider;

if (testMode) {
attachLauncher(accountId, sandboxMode);
Expand Down Expand Up @@ -116,9 +118,18 @@ var constructor = function () {

self.userAttributes = filteredAttributes;

var selectPlacementsAttributes = mergeObjects(filteredAttributes, {
mpid: mpid,
});
var optimizelyAttributes =
self.onboardingExpProvider === 'Optimizely'
? fetchOptimizely()
: {};

var selectPlacementsAttributes = mergeObjects(
filteredAttributes,
optimizelyAttributes,
{
mpid: mpid,
}
);

var selectPlacementsOptions = mergeObjects(options, {
attributes: selectPlacementsAttributes,
Expand Down Expand Up @@ -187,6 +198,46 @@ var constructor = function () {
this.selectPlacements = selectPlacements;

// mParticle Kit Callback Methods
function fetchOptimizely() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have a testing framework, I would like to see some tests around this logic. I can help you with mocking if you need it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexs-mparticle can you take a look if these test cases are sufficient?

var forwarders = window.mParticle
._getActiveForwarders()
.filter(function (forwarder) {
return forwarder.name === 'Optimizely';
});

try {
if (forwarders.length > 0 && window.optimizely) {
// Get the state object
var optimizelyState = window.optimizely.get('state');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should add some handlers in case optimizelyState returns undefined.

if (
!optimizelyState ||
!optimizelyState.getActiveExperimentIds
) {
return {};
}
// Get active experiment IDs
var activeExperimentIds =
optimizelyState.getActiveExperimentIds();
// Get variations for each active experiment
var activeExperiments = activeExperimentIds.reduce(function (
acc,
expId
) {
acc[
'rokt.custom.optimizely.experiment.' +
expId +
'.variationId'
] = optimizelyState.getVariationMap()[expId].id;
return acc;
},
{});
return activeExperiments;
}
} catch (error) {
console.error('Error fetching Optimizely attributes:', error);
}
return {};
}
this.init = initForwarder;
this.setUserAttribute = setUserAttribute;
this.onUserIdentified = onUserIdentified;
Expand Down
204 changes: 204 additions & 0 deletions test/src/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ describe('Rokt Forwarder', () => {
};
},
};
mParticle._getActiveForwarders = function () {
return [];
};
// -------------------START EDITING BELOW:-----------------------
var MockRoktForwarder = function () {
var self = this;
Expand Down Expand Up @@ -413,4 +416,205 @@ describe('Rokt Forwarder', () => {
.should.equal('123');
});
});

describe('#fetchOptimizely', () => {
// Helper functions for setting up Optimizely mocks
function setupValidOptimizelyMock(experiments) {
window.optimizely = {
get: function (key) {
if (key === 'state') {
return {
getActiveExperimentIds: function () {
return Object.keys(experiments);
},
getVariationMap: function () {
return experiments;
},
};
}
},
};
}

function setupInvalidOptimizelyMock(stateObject) {
window.optimizely = {
get: function (key) {
if (key === 'state') {
return stateObject;
}
},
};
}

// Common test setup
async function initAndSelectPlacements(settings = {}) {
await window.mParticle.forwarder.init(
{
accountId: '123456',
...settings,
},
reportService.cb,
true,
null,
{}
);

await window.mParticle.forwarder.selectPlacements({
identifier: 'test-placement',
attributes: {
test: 'test',
},
});
}

beforeEach(() => {
window.Rokt = new MockRoktForwarder();
window.mParticle.Rokt = window.Rokt;
window.mParticle.Rokt.attachKitCalled = false;
window.mParticle.Rokt.attachKit = async (kit) => {
window.mParticle.Rokt.attachKitCalled = true;
window.mParticle.Rokt.kit = kit;
Promise.resolve();
};
window.mParticle.forwarder.launcher = {
selectPlacements: function (options) {
window.mParticle.Rokt.selectPlacementsOptions = options;
window.mParticle.Rokt.selectPlacementsCalled = true;
},
};
window.mParticle.Rokt.filters = {
userAttributesFilters: [],
filterUserAttributes: function (attributes) {
return attributes;
},
filteredUser: {
getMPID: function () {
return '123';
},
},
};
window.mParticle._getActiveForwarders = function () {
return [{ name: 'Optimizely' }];
};
});

afterEach(() => {
delete window.optimizely;
});

describe('when Optimizely is properly configured', () => {
it('should fetch experiment data for single experiment', async () => {
setupValidOptimizelyMock({
exp1: { id: 'var1' },
});

await initAndSelectPlacements({
onboardingExpProvider: 'Optimizely',
});

window.Rokt.selectPlacementsOptions.attributes.should.have.property(
'rokt.custom.optimizely.experiment.exp1.variationId',
'var1'
);
});

it('should fetch experiment data for multiple experiments', async () => {
setupValidOptimizelyMock({
exp1: { id: 'var1' },
exp2: { id: 'var2' },
});

await initAndSelectPlacements({
onboardingExpProvider: 'Optimizely',
});

const attributes =
window.Rokt.selectPlacementsOptions.attributes;
attributes.should.have.property(
'rokt.custom.optimizely.experiment.exp1.variationId',
'var1'
);
attributes.should.have.property(
'rokt.custom.optimizely.experiment.exp2.variationId',
'var2'
);
});
});

describe('when Optimizely is not properly configured', () => {
it('should return empty object when Optimizely is not available', async () => {
delete window.optimizely;

await initAndSelectPlacements({
onboardingExpProvider: 'Optimizely',
});

window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
'rokt.custom.optimizely'
);
});

it('should return empty object when Optimizely state is undefined', async () => {
setupInvalidOptimizelyMock(undefined);

await initAndSelectPlacements({
onboardingExpProvider: 'Optimizely',
});

window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
'rokt.custom.optimizely'
);
});

it('should return empty object when Optimizely state has invalid format', async () => {
setupInvalidOptimizelyMock({
someOtherProperty: 'value',
invalidFunction: function () {
return null;
},
});

await initAndSelectPlacements({
onboardingExpProvider: 'Optimizely',
});

window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
'rokt.custom.optimizely'
);
});

it('should return empty object when Optimizely state is missing required methods', async () => {
setupInvalidOptimizelyMock({
getVariationMap: function () {
return {};
},
// Mocking a scenario for when getActiveExperimentIds() method is missing
});

await initAndSelectPlacements({
onboardingExpProvider: 'Optimizely',
});

window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
'rokt.custom.optimizely'
);
});
});

describe('when Optimizely is not the provider', () => {
it('should not fetch Optimizely data', async () => {
setupValidOptimizelyMock({
exp1: { id: 'var1' },
});

await initAndSelectPlacements({
onboardingExpProvider: 'NotOptimizely',
});

window.Rokt.selectPlacementsOptions.attributes.should.not.have.property(
'rokt.custom.optimizely'
);
});
});
});
});