diff --git a/packages/optimizely-sdk/karma.base.conf.js b/packages/optimizely-sdk/karma.base.conf.js index b4c52db72..10cb10ea9 100644 --- a/packages/optimizely-sdk/karma.base.conf.js +++ b/packages/optimizely-sdk/karma.base.conf.js @@ -29,7 +29,7 @@ module.exports = { //browserStack setup browserStack: { username: process.env.BROWSER_STACK_USERNAME, - accessKey: process.env.BROWSER_STACK_ACCESS_KEY + accessKey: process.env.BROWSER_STACK_ACCESS_KEY, }, // to avoid DISCONNECTED messages when connecting to BrowserStack @@ -45,7 +45,7 @@ module.exports = { browser: 'chrome', browser_version: '21.0', os: 'OS X', - os_version: 'Mountain Lion' + os_version: 'Mountain Lion', }, bs_edge: { base: 'BrowserStack', @@ -53,14 +53,14 @@ module.exports = { os_version: '10', browser: 'edge', device: null, - browser_version: '15.0' + browser_version: '15.0', }, bs_firefox_mac: { base: 'BrowserStack', browser: 'firefox', browser_version: '21.0', os: 'OS X', - os_version: 'Mountain Lion' + os_version: 'Mountain Lion', }, bs_ie: { base: 'BrowserStack', @@ -68,14 +68,14 @@ module.exports = { os_version: '7', browser: 'ie', device: null, - browser_version: '10.0' + browser_version: '10.0', }, bs_opera_mac: { base: 'BrowserStack', browser: 'opera', browser_version: '37', os: 'OS X', - os_version: 'Mountain Lion' + os_version: 'Mountain Lion', }, bs_safari: { base: 'BrowserStack', @@ -83,8 +83,8 @@ module.exports = { os_version: 'Mountain Lion', browser: 'safari', device: null, - browser_version: '6.2' - } + browser_version: '6.2', + }, }, browsers: ['bs_chrome_mac', 'bs_edge', 'bs_firefox_mac', 'bs_ie', 'bs_opera_mac', 'bs_safari'], @@ -94,31 +94,25 @@ module.exports = { frameworks: ['mocha'], // list of files to exclude - exclude: [ - ], - + exclude: [], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { - './lib/**/*tests.js': ['webpack'] + './lib/**/*tests.js': ['webpack'], }, - // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter reporters: ['progress'], - // web server port port: 9876, - // enable / disable colors in the output (reporters and logs) colors: true, - // enable / disable watching file and executing tests whenever any file changes autoWatch: false, @@ -128,5 +122,5 @@ module.exports = { // Concurrency level // how many browser should be started simultaneous - concurrency: Infinity -} + concurrency: Infinity, +}; diff --git a/packages/optimizely-sdk/karma.bs.conf.js b/packages/optimizely-sdk/karma.bs.conf.js index d7b8df6ce..2e66ae7f3 100644 --- a/packages/optimizely-sdk/karma.bs.conf.js +++ b/packages/optimizely-sdk/karma.bs.conf.js @@ -15,7 +15,7 @@ */ // Karma configuration for cross-browser testing -const baseConfig = require('./karma.base.conf.js') +const baseConfig = require('./karma.base.conf.js'); module.exports = function(config) { config.set({ @@ -26,8 +26,6 @@ module.exports = function(config) { logLevel: config.LOG_INFO, // list of files / patterns to load in the browser - files: [ - './lib/index.browser.tests.js' - ], + files: ['./lib/index.browser.tests.js'], }); }; diff --git a/packages/optimizely-sdk/karma.umd.conf.js b/packages/optimizely-sdk/karma.umd.conf.js index ea0f59e27..dd034885f 100644 --- a/packages/optimizely-sdk/karma.umd.conf.js +++ b/packages/optimizely-sdk/karma.umd.conf.js @@ -15,7 +15,7 @@ */ // Karma configuration for UMD bundle testing -const baseConfig = require('./karma.base.conf.js') +const baseConfig = require('./karma.base.conf.js'); module.exports = function(config) { config.set({ @@ -26,9 +26,6 @@ module.exports = function(config) { logLevel: config.LOG_INFO, // list of files / patterns to load in the browser - files: [ - './dist/optimizely.browser.umd.min.js', - './lib/index.browser.umdtests.js' - ], + files: ['./dist/optimizely.browser.umd.min.js', './lib/index.browser.umdtests.js'], }); }; diff --git a/packages/optimizely-sdk/lib/core/audience_evaluator/index.js b/packages/optimizely-sdk/lib/core/audience_evaluator/index.js index 9d8af07d4..ad2ef2bb0 100644 --- a/packages/optimizely-sdk/lib/core/audience_evaluator/index.js +++ b/packages/optimizely-sdk/lib/core/audience_evaluator/index.js @@ -26,7 +26,6 @@ var LOG_LEVEL = enums.LOG_LEVEL; var LOG_MESSAGES = enums.LOG_MESSAGES; var MODULE_NAME = 'AUDIENCE_EVALUATOR'; - /** * Construct an instance of AudienceEvaluator with given options * @param {Object=} UNSTABLE_conditionEvaluators A map of condition evaluators provided by the consumer. This enables matching @@ -36,7 +35,7 @@ var MODULE_NAME = 'AUDIENCE_EVALUATOR'; */ function AudienceEvaluator(UNSTABLE_conditionEvaluators) { this.typeToEvaluatorMap = fns.assign({}, UNSTABLE_conditionEvaluators, { - 'custom_attribute': customAttributeConditionEvaluator + custom_attribute: customAttributeConditionEvaluator, }); } @@ -66,8 +65,14 @@ AudienceEvaluator.prototype.evaluate = function(audienceConditions, audiencesByI var evaluateAudience = function(audienceId) { var audience = audiencesById[audienceId]; if (audience) { - logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.EVALUATING_AUDIENCE, MODULE_NAME, audienceId, JSON.stringify(audience.conditions))); - var result = conditionTreeEvaluator.evaluate(audience.conditions, this.evaluateConditionWithUserAttributes.bind(this, userAttributes)); + logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.EVALUATING_AUDIENCE, MODULE_NAME, audienceId, JSON.stringify(audience.conditions)) + ); + var result = conditionTreeEvaluator.evaluate( + audience.conditions, + this.evaluateConditionWithUserAttributes.bind(this, userAttributes) + ); var resultText = result === null ? 'UNKNOWN' : result.toString().toUpperCase(); logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.AUDIENCE_EVALUATION_RESULT, MODULE_NAME, audienceId, resultText)); return result; @@ -95,7 +100,10 @@ AudienceEvaluator.prototype.evaluateConditionWithUserAttributes = function(userA try { return evaluator.evaluate(condition, userAttributes, logger); } catch (err) { - logger.log(LOG_LEVEL.ERROR, sprintf(ERROR_MESSAGES.CONDITION_EVALUATOR_ERROR, MODULE_NAME, condition.type, err.message)); + logger.log( + LOG_LEVEL.ERROR, + sprintf(ERROR_MESSAGES.CONDITION_EVALUATOR_ERROR, MODULE_NAME, condition.type, err.message) + ); } return null; }; diff --git a/packages/optimizely-sdk/lib/core/audience_evaluator/index.tests.js b/packages/optimizely-sdk/lib/core/audience_evaluator/index.tests.js index da3fc63e4..b9cea6baf 100644 --- a/packages/optimizely-sdk/lib/core/audience_evaluator/index.tests.js +++ b/packages/optimizely-sdk/lib/core/audience_evaluator/index.tests.js @@ -25,31 +25,43 @@ var enums = require('../../utils/enums'); var LOG_LEVEL = enums.LOG_LEVEL; var chromeUserAudience = { - conditions: ['and', { - name: 'browser_type', - value: 'chrome', - type: 'custom_attribute', - }], + conditions: [ + 'and', + { + name: 'browser_type', + value: 'chrome', + type: 'custom_attribute', + }, + ], }; var iphoneUserAudience = { - conditions: ['and', { - name: 'device_model', - value: 'iphone', - type: 'custom_attribute', - }], + conditions: [ + 'and', + { + name: 'device_model', + value: 'iphone', + type: 'custom_attribute', + }, + ], }; var specialConditionTypeAudience = { - conditions: ['and', { - match: 'interest_level', - value: 'special', - type: 'special_condition_type', - }], -} -var conditionsPassingWithNoAttrs = ['not', { - match: 'exists', - name: 'input_value', - type: 'custom_attribute', -}]; + conditions: [ + 'and', + { + match: 'interest_level', + value: 'special', + type: 'special_condition_type', + }, + ], +}; +var conditionsPassingWithNoAttrs = [ + 'not', + { + match: 'exists', + name: 'input_value', + type: 'custom_attribute', + }, +]; var conditionsPassingWithNoAttrsAudience = { conditions: conditionsPassingWithNoAttrs, }; @@ -57,7 +69,7 @@ var audiencesById = { 0: chromeUserAudience, 1: iphoneUserAudience, 2: conditionsPassingWithNoAttrsAudience, - 3: specialConditionTypeAudience + 3: specialConditionTypeAudience, }; describe('lib/core/audience_evaluator', function() { @@ -76,129 +88,107 @@ describe('lib/core/audience_evaluator', function() { beforeEach(function() { audienceEvaluator = new AudienceEvaluator(); }); - describe('evaluate', function() { + describe('evaluate', function() { it('should return true if there are no audiences', function() { assert.isTrue(audienceEvaluator.evaluate([], audiencesById, {})); }); - + it('should return false if there are audiences but no attributes', function() { assert.isFalse(audienceEvaluator.evaluate(['0'], audiencesById, {})); }); - + it('should return true if any of the audience conditions are met', function() { var iphoneUsers = { - 'device_model': 'iphone', + device_model: 'iphone', }; - + var chromeUsers = { - 'browser_type': 'chrome', + browser_type: 'chrome', }; - + var iphoneChromeUsers = { - 'browser_type': 'chrome', - 'device_model': 'iphone', + browser_type: 'chrome', + device_model: 'iphone', }; - + assert.isTrue(audienceEvaluator.evaluate(['0', '1'], audiencesById, iphoneUsers)); assert.isTrue(audienceEvaluator.evaluate(['0', '1'], audiencesById, chromeUsers)); assert.isTrue(audienceEvaluator.evaluate(['0', '1'], audiencesById, iphoneChromeUsers)); }); - + it('should return false if none of the audience conditions are met', function() { var nexusUsers = { - 'device_model': 'nexus5', + device_model: 'nexus5', }; - + var safariUsers = { - 'browser_type': 'safari', + browser_type: 'safari', }; - + var nexusSafariUsers = { - 'browser_type': 'safari', - 'device_model': 'nexus5', + browser_type: 'safari', + device_model: 'nexus5', }; - + assert.isFalse(audienceEvaluator.evaluate(['0', '1'], audiencesById, nexusUsers)); assert.isFalse(audienceEvaluator.evaluate(['0', '1'], audiencesById, safariUsers)); assert.isFalse(audienceEvaluator.evaluate(['0', '1'], audiencesById, nexusSafariUsers)); }); - + it('should return true if no attributes are passed and the audience conditions evaluate to true in the absence of attributes', function() { assert.isTrue(audienceEvaluator.evaluate(['2'], audiencesById, null)); }); - + describe('complex audience conditions', function() { it('should return true if any of the audiences in an "OR" condition pass', function() { - var result = audienceEvaluator.evaluate( - ['or', '0', '1'], - audiencesById, - { browser_type: 'chrome' } - ); + var result = audienceEvaluator.evaluate(['or', '0', '1'], audiencesById, { browser_type: 'chrome' }); assert.isTrue(result); }); - + it('should return true if all of the audiences in an "AND" condition pass', function() { - var result = audienceEvaluator.evaluate( - ['and', '0', '1'], - audiencesById, - { browser_type: 'chrome', device_model: 'iphone' } - ); + var result = audienceEvaluator.evaluate(['and', '0', '1'], audiencesById, { + browser_type: 'chrome', + device_model: 'iphone', + }); assert.isTrue(result); }); - + it('should return true if the audience in a "NOT" condition does not pass', function() { - var result = audienceEvaluator.evaluate( - ['not', '1'], - audiencesById, - { device_model: 'android' } - ); + var result = audienceEvaluator.evaluate(['not', '1'], audiencesById, { device_model: 'android' }); assert.isTrue(result); }); - }); - + describe('integration with dependencies', function() { var sandbox = sinon.sandbox.create(); - + beforeEach(function() { sandbox.stub(conditionTreeEvaluator, 'evaluate'); sandbox.stub(customAttributeConditionEvaluator, 'evaluate'); }); - + afterEach(function() { sandbox.restore(); }); - + it('returns true if conditionTreeEvaluator.evaluate returns true', function() { conditionTreeEvaluator.evaluate.returns(true); - var result = audienceEvaluator.evaluate( - ['or', '0', '1'], - audiencesById, - { browser_type: 'chrome' } - ); + var result = audienceEvaluator.evaluate(['or', '0', '1'], audiencesById, { browser_type: 'chrome' }); assert.isTrue(result); }); - + it('returns false if conditionTreeEvaluator.evaluate returns false', function() { conditionTreeEvaluator.evaluate.returns(false); - var result = audienceEvaluator.evaluate( - ['or', '0', '1'], - audiencesById, - { browser_type: 'safari' } - ); + var result = audienceEvaluator.evaluate(['or', '0', '1'], audiencesById, { browser_type: 'safari' }); assert.isFalse(result); }); - + it('returns false if conditionTreeEvaluator.evaluate returns null', function() { conditionTreeEvaluator.evaluate.returns(null); - var result = audienceEvaluator.evaluate( - ['or', '0', '1'], - audiencesById, - { state: 'California' } - ); + var result = audienceEvaluator.evaluate(['or', '0', '1'], audiencesById, { state: 'California' }); assert.isFalse(result); }); - + it('calls customAttributeConditionEvaluator.evaluate in the leaf evaluator for audience conditions', function() { conditionTreeEvaluator.evaluate.callsFake(function(conditions, leafEvaluator) { return leafEvaluator(conditions[1]); @@ -207,24 +197,29 @@ describe('lib/core/audience_evaluator', function() { var userAttributes = { device_model: 'android' }; var result = audienceEvaluator.evaluate(['or', '1'], audiencesById, userAttributes); sinon.assert.calledOnce(customAttributeConditionEvaluator.evaluate); - console.log('args: ', customAttributeConditionEvaluator.evaluate.firstCall.args) - sinon.assert.calledWithExactly(customAttributeConditionEvaluator.evaluate, iphoneUserAudience.conditions[1], userAttributes, mockLogger); + console.log('args: ', customAttributeConditionEvaluator.evaluate.firstCall.args); + sinon.assert.calledWithExactly( + customAttributeConditionEvaluator.evaluate, + iphoneUserAudience.conditions[1], + userAttributes, + mockLogger + ); assert.isFalse(result); }); }); - + describe('Audience evaluation logging', function() { var sandbox = sinon.sandbox.create(); - + beforeEach(function() { sandbox.stub(conditionTreeEvaluator, 'evaluate'); sandbox.stub(customAttributeConditionEvaluator, 'evaluate'); }); - + afterEach(function() { sandbox.restore(); }); - + it('logs correctly when conditionTreeEvaluator.evaluate returns null', function() { conditionTreeEvaluator.evaluate.callsFake(function(conditions, leafEvaluator) { return leafEvaluator(conditions[1]); @@ -233,13 +228,21 @@ describe('lib/core/audience_evaluator', function() { var userAttributes = { device_model: 5.5 }; var result = audienceEvaluator.evaluate(['or', '1'], audiencesById, userAttributes); sinon.assert.calledOnce(customAttributeConditionEvaluator.evaluate); - sinon.assert.calledWithExactly(customAttributeConditionEvaluator.evaluate, iphoneUserAudience.conditions[1], userAttributes, mockLogger); + sinon.assert.calledWithExactly( + customAttributeConditionEvaluator.evaluate, + iphoneUserAudience.conditions[1], + userAttributes, + mockLogger + ); assert.isFalse(result); assert.strictEqual(2, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].' + ); assert.strictEqual(mockLogger.log.args[1][1], 'AUDIENCE_EVALUATOR: Audience "1" evaluated to UNKNOWN.'); }); - + it('logs correctly when conditionTreeEvaluator.evaluate returns true', function() { conditionTreeEvaluator.evaluate.callsFake(function(conditions, leafEvaluator) { return leafEvaluator(conditions[1]); @@ -248,13 +251,21 @@ describe('lib/core/audience_evaluator', function() { var userAttributes = { device_model: 'iphone' }; var result = audienceEvaluator.evaluate(['or', '1'], audiencesById, userAttributes); sinon.assert.calledOnce(customAttributeConditionEvaluator.evaluate); - sinon.assert.calledWithExactly(customAttributeConditionEvaluator.evaluate, iphoneUserAudience.conditions[1], userAttributes, mockLogger); + sinon.assert.calledWithExactly( + customAttributeConditionEvaluator.evaluate, + iphoneUserAudience.conditions[1], + userAttributes, + mockLogger + ); assert.isTrue(result); assert.strictEqual(2, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].' + ); assert.strictEqual(mockLogger.log.args[1][1], 'AUDIENCE_EVALUATOR: Audience "1" evaluated to TRUE.'); }); - + it('logs correctly when conditionTreeEvaluator.evaluate returns false', function() { conditionTreeEvaluator.evaluate.callsFake(function(conditions, leafEvaluator) { return leafEvaluator(conditions[1]); @@ -263,64 +274,74 @@ describe('lib/core/audience_evaluator', function() { var userAttributes = { device_model: 'android' }; var result = audienceEvaluator.evaluate(['or', '1'], audiencesById, userAttributes); sinon.assert.calledOnce(customAttributeConditionEvaluator.evaluate); - console.log('args: ', customAttributeConditionEvaluator.evaluate.firstCall.args) - sinon.assert.calledWithExactly(customAttributeConditionEvaluator.evaluate, iphoneUserAudience.conditions[1], userAttributes, mockLogger); + console.log('args: ', customAttributeConditionEvaluator.evaluate.firstCall.args); + sinon.assert.calledWithExactly( + customAttributeConditionEvaluator.evaluate, + iphoneUserAudience.conditions[1], + userAttributes, + mockLogger + ); assert.isFalse(result); assert.strictEqual(2, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'AUDIENCE_EVALUATOR: Starting to evaluate audience "1" with conditions: ["and",{"name":"device_model","value":"iphone","type":"custom_attribute"}].' + ); assert.strictEqual(mockLogger.log.args[1][1], 'AUDIENCE_EVALUATOR: Audience "1" evaluated to FALSE.'); }); }); }); - }) + }); context('with additional custom condition evaluator', function() { - describe('when passing a valid additional evaluator' , function() { + describe('when passing a valid additional evaluator', function() { beforeEach(function() { const mockEnvironment = { - 'special': true + special: true, }; audienceEvaluator = new AudienceEvaluator({ special_condition_type: { evaluate: function(condition, userAttributes, logger) { const result = mockEnvironment[condition.value] && userAttributes[condition.match] > 0; - logger.log(`special_condition_type: ${result} for ${condition.value}:${condition.match}`) + logger.log(`special_condition_type: ${result} for ${condition.value}:${condition.match}`); return result; - } + }, }, }); }); it('should evaluate an audience properly using the custom condition evaluator', function() { - assert.isFalse(audienceEvaluator.evaluate(['3'], audiencesById, {interest_level: 0})); - assert.isTrue(audienceEvaluator.evaluate(['3'], audiencesById, {interest_level: 1})); - }) + assert.isFalse(audienceEvaluator.evaluate(['3'], audiencesById, { interest_level: 0 })); + assert.isTrue(audienceEvaluator.evaluate(['3'], audiencesById, { interest_level: 1 })); + }); it('should pass the logger instance to the custom condition evaluator', function() { - assert.isFalse(audienceEvaluator.evaluate(['3'], audiencesById, {interest_level: 0})); - assert.isTrue(audienceEvaluator.evaluate(['3'], audiencesById, {interest_level: 1})); + assert.isFalse(audienceEvaluator.evaluate(['3'], audiencesById, { interest_level: 0 })); + assert.isTrue(audienceEvaluator.evaluate(['3'], audiencesById, { interest_level: 1 })); sinon.assert.calledWithExactly(mockLogger.log, 'special_condition_type: false for special:interest_level'); sinon.assert.calledWithExactly(mockLogger.log, 'special_condition_type: true for special:interest_level'); - }) - }) + }); + }); - describe('when passing an invalid additional evaluator' , function() { + describe('when passing an invalid additional evaluator', function() { beforeEach(function() { audienceEvaluator = new AudienceEvaluator({ custom_attribute: { evaluate: function() { return false; - } - } + }, + }, }); }); it('should not be able to overwrite built in `custom_attribute` evaluator', function() { - assert.isTrue(audienceEvaluator.evaluate(['0'], audiencesById, { - 'browser_type': 'chrome', - })); - }) - }) - }) + assert.isTrue( + audienceEvaluator.evaluate(['0'], audiencesById, { + browser_type: 'chrome', + }) + ); + }); + }); + }); }); }); diff --git a/packages/optimizely-sdk/lib/core/bucketer/index.js b/packages/optimizely-sdk/lib/core/bucketer/index.js index ed02dff35..116801507 100644 --- a/packages/optimizely-sdk/lib/core/bucketer/index.js +++ b/packages/optimizely-sdk/lib/core/bucketer/index.js @@ -56,39 +56,68 @@ module.exports = { throw new Error(sprintf(ERROR_MESSAGES.INVALID_GROUP_ID, MODULE_NAME, groupId)); } if (group.policy === RANDOM_POLICY) { - var bucketedExperimentId = module.exports.bucketUserIntoExperiment(group, - bucketerParams.bucketingId, - bucketerParams.userId, - bucketerParams.logger); + var bucketedExperimentId = module.exports.bucketUserIntoExperiment( + group, + bucketerParams.bucketingId, + bucketerParams.userId, + bucketerParams.logger + ); // Return if user is not bucketed into any experiment if (bucketedExperimentId === null) { - var notbucketedInAnyExperimentLogMessage = sprintf(LOG_MESSAGES.USER_NOT_IN_ANY_EXPERIMENT, MODULE_NAME, bucketerParams.userId, groupId); + var notbucketedInAnyExperimentLogMessage = sprintf( + LOG_MESSAGES.USER_NOT_IN_ANY_EXPERIMENT, + MODULE_NAME, + bucketerParams.userId, + groupId + ); bucketerParams.logger.log(LOG_LEVEL.INFO, notbucketedInAnyExperimentLogMessage); return null; } // Return if user is bucketed into a different experiment than the one specified if (bucketedExperimentId !== bucketerParams.experimentId) { - var notBucketedIntoExperimentOfGroupLogMessage = sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP, MODULE_NAME, bucketerParams.userId, bucketerParams.experimentKey, groupId); + var notBucketedIntoExperimentOfGroupLogMessage = sprintf( + LOG_MESSAGES.USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP, + MODULE_NAME, + bucketerParams.userId, + bucketerParams.experimentKey, + groupId + ); bucketerParams.logger.log(LOG_LEVEL.INFO, notBucketedIntoExperimentOfGroupLogMessage); return null; } // Continue bucketing if user is bucketed into specified experiment - var bucketedIntoExperimentOfGroupLogMessage = sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP, MODULE_NAME, bucketerParams.userId, bucketerParams.experimentKey, groupId); + var bucketedIntoExperimentOfGroupLogMessage = sprintf( + LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP, + MODULE_NAME, + bucketerParams.userId, + bucketerParams.experimentKey, + groupId + ); bucketerParams.logger.log(LOG_LEVEL.INFO, bucketedIntoExperimentOfGroupLogMessage); } } var bucketingId = sprintf('%s%s', bucketerParams.bucketingId, bucketerParams.experimentId); var bucketValue = module.exports._generateBucketValue(bucketingId); - var bucketedUserLogMessage = sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, MODULE_NAME, bucketValue, bucketerParams.userId); + var bucketedUserLogMessage = sprintf( + LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, + MODULE_NAME, + bucketValue, + bucketerParams.userId + ); bucketerParams.logger.log(LOG_LEVEL.DEBUG, bucketedUserLogMessage); var entityId = module.exports._findBucket(bucketValue, bucketerParams.trafficAllocationConfig); if (!entityId) { - var userHasNoVariationLogMessage = sprintf(LOG_MESSAGES.USER_HAS_NO_VARIATION, MODULE_NAME, bucketerParams.userId, bucketerParams.experimentKey); + var userHasNoVariationLogMessage = sprintf( + LOG_MESSAGES.USER_HAS_NO_VARIATION, + MODULE_NAME, + bucketerParams.userId, + bucketerParams.experimentKey + ); bucketerParams.logger.log(LOG_LEVEL.DEBUG, userHasNoVariationLogMessage); } else if (!bucketerParams.variationIdMap.hasOwnProperty(entityId)) { var invalidVariationIdLogMessage = sprintf(LOG_MESSAGES.INVALID_VARIATION_ID, MODULE_NAME); @@ -96,7 +125,13 @@ module.exports = { return null; } else { var variationKey = bucketerParams.variationIdMap[entityId].key; - var userInVariationLogMessage = sprintf(LOG_MESSAGES.USER_HAS_VARIATION, MODULE_NAME, bucketerParams.userId, variationKey, bucketerParams.experimentKey); + var userInVariationLogMessage = sprintf( + LOG_MESSAGES.USER_HAS_VARIATION, + MODULE_NAME, + bucketerParams.userId, + variationKey, + bucketerParams.experimentKey + ); bucketerParams.logger.log(LOG_LEVEL.INFO, userInVariationLogMessage); } @@ -114,7 +149,10 @@ module.exports = { bucketUserIntoExperiment: function(group, bucketingId, userId, logger) { var bucketingKey = sprintf('%s%s', bucketingId, group.id); var bucketValue = module.exports._generateBucketValue(bucketingKey); - logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, MODULE_NAME, bucketValue, userId)); + logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, MODULE_NAME, bucketValue, userId) + ); var trafficAllocationConfig = group.trafficAllocation; var bucketedExperimentId = module.exports._findBucket(bucketValue, trafficAllocationConfig); return bucketedExperimentId; diff --git a/packages/optimizely-sdk/lib/core/bucketer/index.tests.js b/packages/optimizely-sdk/lib/core/bucketer/index.tests.js index aba0ec90f..f9afb920e 100644 --- a/packages/optimizely-sdk/lib/core/bucketer/index.tests.js +++ b/packages/optimizely-sdk/lib/core/bucketer/index.tests.js @@ -34,7 +34,7 @@ describe('lib/core/bucketer', function() { describe('APIs', function() { describe('bucket', function() { var configObj; - var createdLogger = logger.createLogger({logLevel: LOG_LEVEL.INFO}); + var createdLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO }); var bucketerParams; beforeEach(function() { @@ -57,9 +57,12 @@ describe('lib/core/bucketer', function() { groupIdMap: configObj.groupIdMap, logger: createdLogger, }; - sinon.stub(bucketer, '_generateBucketValue') - .onFirstCall().returns(50) - .onSecondCall().returns(50000); + sinon + .stub(bucketer, '_generateBucketValue') + .onFirstCall() + .returns(50) + .onSecondCall() + .returns(50000); }); afterEach(function() { @@ -74,8 +77,12 @@ describe('lib/core/bucketer', function() { var bucketedUser_log1 = createdLogger.log.args[0][1]; var bucketedUser_log2 = createdLogger.log.args[1][1]; - expect(bucketedUser_log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50', 'ppid1')); - expect(bucketedUser_log2).to.equal(sprintf(LOG_MESSAGES.USER_HAS_VARIATION, 'BUCKETER', 'ppid1', 'control', 'testExperiment')); + expect(bucketedUser_log1).to.equal( + sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50', 'ppid1') + ); + expect(bucketedUser_log2).to.equal( + sprintf(LOG_MESSAGES.USER_HAS_VARIATION, 'BUCKETER', 'ppid1', 'control', 'testExperiment') + ); var bucketerParamsTest2 = cloneDeep(bucketerParams); bucketerParamsTest2.userId = 'ppid2'; @@ -84,8 +91,12 @@ describe('lib/core/bucketer', function() { var notBucketedUser_log1 = createdLogger.log.args[2][1]; var notBucketedUser_log2 = createdLogger.log.args[3][1]; - expect(notBucketedUser_log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50000', 'ppid2')); - expect(notBucketedUser_log2).to.equal(sprintf(LOG_MESSAGES.USER_HAS_NO_VARIATION, 'BUCKETER', 'ppid2', 'testExperiment')); + expect(notBucketedUser_log1).to.equal( + sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50000', 'ppid2') + ); + expect(notBucketedUser_log2).to.equal( + sprintf(LOG_MESSAGES.USER_HAS_NO_VARIATION, 'BUCKETER', 'ppid2', 'testExperiment') + ); }); }); @@ -134,16 +145,30 @@ describe('lib/core/bucketer', function() { sinon.assert.callCount(createdLogger.log, 4); var log1 = createdLogger.log.args[0][1]; - expect(log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '50', 'testUser')); + expect(log1).to.equal( + sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '50', 'testUser') + ); var log2 = createdLogger.log.args[1][1]; - expect(log2).to.equal(sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP, 'BUCKETER', 'testUser', 'groupExperiment1', '666')); + expect(log2).to.equal( + sprintf( + LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP, + 'BUCKETER', + 'testUser', + 'groupExperiment1', + '666' + ) + ); var log3 = createdLogger.log.args[2][1]; - expect(log3).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50', 'testUser')); + expect(log3).to.equal( + sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '50', 'testUser') + ); var log4 = createdLogger.log.args[3][1]; - expect(log4).to.equal(sprintf(LOG_MESSAGES.USER_HAS_VARIATION, 'BUCKETER', 'testUser', 'var1exp1', 'groupExperiment1')); + expect(log4).to.equal( + sprintf(LOG_MESSAGES.USER_HAS_VARIATION, 'BUCKETER', 'testUser', 'var1exp1', 'groupExperiment1') + ); }); it('should return null when a user is bucketed into a different grouped experiment than the one speicfied', function() { @@ -155,9 +180,19 @@ describe('lib/core/bucketer', function() { sinon.assert.calledTwice(createdLogger.log); var log1 = createdLogger.log.args[0][1]; - expect(log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '5000', 'testUser')); + expect(log1).to.equal( + sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '5000', 'testUser') + ); var log2 = createdLogger.log.args[1][1]; - expect(log2).to.equal(sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP, 'BUCKETER', 'testUser', 'groupExperiment1', '666')); + expect(log2).to.equal( + sprintf( + LOG_MESSAGES.USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP, + 'BUCKETER', + 'testUser', + 'groupExperiment1', + '666' + ) + ); }); it('should return null when a user is not bucketed into any experiments in the random group', function() { @@ -169,7 +204,9 @@ describe('lib/core/bucketer', function() { sinon.assert.calledTwice(createdLogger.log); var log1 = createdLogger.log.args[0][1]; - expect(log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '50000', 'testUser')); + expect(log1).to.equal( + sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '50000', 'testUser') + ); var log2 = createdLogger.log.args[1][1]; expect(log2).to.equal(sprintf(LOG_MESSAGES.USER_NOT_IN_ANY_EXPERIMENT, 'BUCKETER', 'testUser', '666')); }); @@ -183,7 +220,9 @@ describe('lib/core/bucketer', function() { sinon.assert.calledTwice(createdLogger.log); var log1 = createdLogger.log.args[0][1]; - expect(log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '9000', 'testUser')); + expect(log1).to.equal( + sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_EXPERIMENT_BUCKET, 'BUCKETER', '9000', 'testUser') + ); var log2 = createdLogger.log.args[1][1]; expect(log2).to.equal(sprintf(LOG_MESSAGES.USER_NOT_IN_ANY_EXPERIMENT, 'BUCKETER', 'testUser', '666')); }); @@ -224,7 +263,15 @@ describe('lib/core/bucketer', function() { var log1 = createdLogger.log.args[0][1]; expect(log1).to.equal(sprintf(LOG_MESSAGES.USER_ASSIGNED_TO_VARIATION_BUCKET, 'BUCKETER', '0', 'testUser')); var log2 = createdLogger.log.args[1][1]; - expect(log2).to.equal(sprintf(LOG_MESSAGES.USER_HAS_VARIATION, 'BUCKETER', 'testUser', 'overlappingvar1', 'overlappingGroupExperiment1')); + expect(log2).to.equal( + sprintf( + LOG_MESSAGES.USER_HAS_VARIATION, + 'BUCKETER', + 'testUser', + 'overlappingvar1', + 'overlappingGroupExperiment1' + ) + ); }); it('should return null when a user does not fall into an experiment within an overlapping group', function() { @@ -241,13 +288,16 @@ describe('lib/core/bucketer', function() { bucketerParams = { experimentId: configObj.experiments[0].id, experimentKey: configObj.experiments[0].key, - trafficAllocationConfig: [{ - entityId: '', - endOfRange: 5000 - }, { - entityId: '', - endOfRange: 10000 - }], + trafficAllocationConfig: [ + { + entityId: '', + endOfRange: 5000, + }, + { + entityId: '', + endOfRange: 10000, + }, + ], variationIdMap: configObj.variationIdMap, experimentKeyMap: configObj.experimentKeyMap, groupIdMap: configObj.groupIdMap, @@ -268,13 +318,16 @@ describe('lib/core/bucketer', function() { bucketerParams = { experimentId: configObj.experiments[0].id, experimentKey: configObj.experiments[0].key, - trafficAllocationConfig: [{ - entityId: -1, - endOfRange: 5000 - }, { - entityId: -2, - endOfRange: 10000 - }], + trafficAllocationConfig: [ + { + entityId: -1, + endOfRange: 5000, + }, + { + entityId: -2, + endOfRange: 10000, + }, + ], variationIdMap: configObj.variationIdMap, experimentKeyMap: configObj.experimentKeyMap, groupIdMap: configObj.groupIdMap, @@ -307,7 +360,7 @@ describe('lib/core/bucketer', function() { it('should return an error if it cannot generate the hash value', function() { assert.throws(function() { bucketer._generateBucketValue(null); - }, sprintf(ERROR_MESSAGES.INVALID_BUCKETING_ID, 'BUCKETER', null, 'Cannot read property \'length\' of null')); + }, sprintf(ERROR_MESSAGES.INVALID_BUCKETING_ID, 'BUCKETER', null, "Cannot read property 'length' of null")); }); }); @@ -328,7 +381,7 @@ describe('lib/core/bucketer', function() { }; }); - it('check that a non null bucketingId buckets a variation different than the one expected with userId', function () { + it('check that a non null bucketingId buckets a variation different than the one expected with userId', function() { var bucketerParams1 = cloneDeep(bucketerParams); bucketerParams1['userId'] = 'testBucketingIdControl'; bucketerParams1['bucketingId'] = '123456789'; @@ -337,7 +390,7 @@ describe('lib/core/bucketer', function() { expect(bucketer.bucket(bucketerParams1)).to.equal('111129'); }); - it('check that a null bucketing ID defaults to bucketing with the userId', function () { + it('check that a null bucketing ID defaults to bucketing with the userId', function() { var bucketerParams2 = cloneDeep(bucketerParams); bucketerParams2['userId'] = 'testBucketingIdControl'; bucketerParams2['bucketingId'] = null; @@ -346,7 +399,7 @@ describe('lib/core/bucketer', function() { expect(bucketer.bucket(bucketerParams2)).to.equal('111128'); }); - it('check that bucketing works with an experiment in group', function () { + it('check that bucketing works with an experiment in group', function() { var bucketerParams4 = cloneDeep(bucketerParams); bucketerParams4['userId'] = 'testBucketingIdControl'; bucketerParams4['bucketingId'] = '123456789'; diff --git a/packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.js b/packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.js index 2f5a11c2b..0a47c800c 100644 --- a/packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.js +++ b/packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.js @@ -48,7 +48,8 @@ function evaluate(conditions, leafEvaluator) { return andEvaluator(restOfConditions, leafEvaluator); case NOT_CONDITION: return notEvaluator(restOfConditions, leafEvaluator); - default: // firstOperator is OR_CONDITION + default: + // firstOperator is OR_CONDITION return orEvaluator(restOfConditions, leafEvaluator); } } diff --git a/packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.tests.js b/packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.tests.js index 22ca3c6e0..c9af1ea43 100644 --- a/packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.tests.js +++ b/packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.tests.js @@ -40,66 +40,63 @@ describe('lib/core/condition_tree_evaluator', function() { describe('APIs', function() { describe('evaluate', function() { it('should return true for a leaf condition when the leaf condition evaluator returns true', function() { - assert.isTrue(conditionTreeEvaluator.evaluate(conditionA, function() { return true; })); + assert.isTrue( + conditionTreeEvaluator.evaluate(conditionA, function() { + return true; + }) + ); }); it('should return false for a leaf condition when the leaf condition evaluator returns false', function() { - assert.isFalse(conditionTreeEvaluator.evaluate(conditionA, function() { return false; })); + assert.isFalse( + conditionTreeEvaluator.evaluate(conditionA, function() { + return false; + }) + ); }); describe('and evaluation', function() { it('should return true when ALL conditions evaluate to true', function() { - assert.isTrue(conditionTreeEvaluator.evaluate( - ['and', conditionA, conditionB], - function() { return true; } - )); + assert.isTrue( + conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], function() { + return true; + }) + ); }); it('should return false if one condition evaluates to false', function() { var leafEvaluator = sinon.stub(); leafEvaluator.onCall(0).returns(true); leafEvaluator.onCall(1).returns(false); - assert.isFalse(conditionTreeEvaluator.evaluate( - ['and', conditionA, conditionB], - leafEvaluator - )); + assert.isFalse(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], leafEvaluator)); }); describe('null handling', function() { it('should return null when all operands evaluate to null', function() { - assert.isNull(conditionTreeEvaluator.evaluate( - ['and', conditionA, conditionB], - function() { return null; } - )); + assert.isNull( + conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], function() { + return null; + }) + ); }); it('should return null when operands evaluate to trues and nulls', function() { var leafEvaluator = sinon.stub(); leafEvaluator.onCall(0).returns(true); leafEvaluator.onCall(1).returns(null); - assert.isNull(conditionTreeEvaluator.evaluate( - ['and', conditionA, conditionB], - leafEvaluator - )); + assert.isNull(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], leafEvaluator)); }); it('should return false when operands evaluate to falses and nulls', function() { var leafEvaluator = sinon.stub(); leafEvaluator.onCall(0).returns(false); leafEvaluator.onCall(1).returns(null); - assert.isFalse(conditionTreeEvaluator.evaluate( - ['and', conditionA, conditionB], - leafEvaluator - )); + assert.isFalse(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], leafEvaluator)); leafEvaluator.reset(); leafEvaluator.onCall(0).returns(null); leafEvaluator.onCall(1).returns(false); - assert.isFalse(conditionTreeEvaluator.evaluate( - ['and', conditionA, conditionB], - leafEvaluator - )); - + assert.isFalse(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], leafEvaluator)); }); it('should return false when operands evaluate to trues, falses, and nulls', function() { @@ -107,10 +104,7 @@ describe('lib/core/condition_tree_evaluator', function() { leafEvaluator.onCall(0).returns(true); leafEvaluator.onCall(1).returns(false); leafEvaluator.onCall(2).returns(null); - assert.isFalse(conditionTreeEvaluator.evaluate( - ['and', conditionA, conditionB, conditionC], - leafEvaluator - )); + assert.isFalse(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB, conditionC], leafEvaluator)); }); }); }); @@ -120,53 +114,43 @@ describe('lib/core/condition_tree_evaluator', function() { var leafEvaluator = sinon.stub(); leafEvaluator.onCall(0).returns(false); leafEvaluator.onCall(1).returns(true); - assert.isTrue(conditionTreeEvaluator.evaluate( - ['or', conditionA, conditionB], - leafEvaluator - )); + assert.isTrue(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], leafEvaluator)); }); it('should return false if all conditions evaluate to false', function() { - assert.isFalse(conditionTreeEvaluator.evaluate( - ['or', conditionA, conditionB], - function() { return false; } - )); + assert.isFalse( + conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], function() { + return false; + }) + ); }); describe('null handling', function() { it('should return null when all operands evaluate to null', function() { - assert.isNull(conditionTreeEvaluator.evaluate( - ['or', conditionA, conditionB], - function() { return null; } - )); + assert.isNull( + conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], function() { + return null; + }) + ); }); it('should return true when operands evaluate to trues and nulls', function() { var leafEvaluator = sinon.stub(); leafEvaluator.onCall(0).returns(true); leafEvaluator.onCall(1).returns(null); - assert.isTrue(conditionTreeEvaluator.evaluate( - ['or', conditionA, conditionB], - leafEvaluator - )); + assert.isTrue(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], leafEvaluator)); }); it('should return null when operands evaluate to falses and nulls', function() { var leafEvaluator = sinon.stub(); leafEvaluator.onCall(0).returns(null); leafEvaluator.onCall(1).returns(false); - assert.isNull(conditionTreeEvaluator.evaluate( - ['or', conditionA, conditionB], - leafEvaluator - )); + assert.isNull(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], leafEvaluator)); leafEvaluator.reset(); leafEvaluator.onCall(0).returns(false); leafEvaluator.onCall(1).returns(null); - assert.isNull(conditionTreeEvaluator.evaluate( - ['or', conditionA, conditionB], - leafEvaluator - )); + assert.isNull(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], leafEvaluator)); }); it('should return true when operands evaluate to trues, falses, and nulls', function() { @@ -174,48 +158,58 @@ describe('lib/core/condition_tree_evaluator', function() { leafEvaluator.onCall(0).returns(true); leafEvaluator.onCall(1).returns(null); leafEvaluator.onCall(2).returns(false); - assert.isTrue(conditionTreeEvaluator.evaluate( - ['or', conditionA, conditionB, conditionC], - leafEvaluator - )); + assert.isTrue(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB, conditionC], leafEvaluator)); }); }); }); describe('not evaluation', function() { it('should return true if the condition evaluates to false', function() { - assert.isTrue(conditionTreeEvaluator.evaluate(['not', conditionA], function() { return false; })); + assert.isTrue( + conditionTreeEvaluator.evaluate(['not', conditionA], function() { + return false; + }) + ); }); it('should return false if the condition evaluates to true', function() { - assert.isFalse(conditionTreeEvaluator.evaluate(['not', conditionB], function() { return true; })); + assert.isFalse( + conditionTreeEvaluator.evaluate(['not', conditionB], function() { + return true; + }) + ); }); it('should return the result of negating the first condition, and ignore any additional conditions', function() { - var result = conditionTreeEvaluator.evaluate( - ['not', '1', '2', '1'], - function(id) { return id === '1'; } - ); + var result = conditionTreeEvaluator.evaluate(['not', '1', '2', '1'], function(id) { + return id === '1'; + }); assert.isFalse(result); - result = conditionTreeEvaluator.evaluate( - ['not', '1', '2', '1'], - function(id) { return id === '2'; } - ); + result = conditionTreeEvaluator.evaluate(['not', '1', '2', '1'], function(id) { + return id === '2'; + }); assert.isTrue(result); - result = conditionTreeEvaluator.evaluate( - ['not', '1', '2', '3'], - function(id) { return id === '1' ? null : id === '3'; } - ); + result = conditionTreeEvaluator.evaluate(['not', '1', '2', '3'], function(id) { + return id === '1' ? null : id === '3'; + }); assert.isNull(result); }); describe('null handling', function() { it('should return null when operand evaluates to null', function() { - assert.isNull(conditionTreeEvaluator.evaluate(['not', conditionA], function() { return null; })); + assert.isNull( + conditionTreeEvaluator.evaluate(['not', conditionA], function() { + return null; + }) + ); }); it('should return null when there are no operands', function() { - assert.isNull(conditionTreeEvaluator.evaluate(['not'], function() { return null; })); + assert.isNull( + conditionTreeEvaluator.evaluate(['not'], function() { + return null; + }) + ); }); }); }); @@ -225,14 +219,12 @@ describe('lib/core/condition_tree_evaluator', function() { var leafEvaluator = sinon.stub(); leafEvaluator.onCall(0).returns(true); leafEvaluator.onCall(1).returns(false); - assert.isTrue(conditionTreeEvaluator.evaluate( - [conditionA, conditionB], - leafEvaluator - )); - assert.isFalse(conditionTreeEvaluator.evaluate( - [conditionA, conditionB], - function() { return false; } - )); + assert.isTrue(conditionTreeEvaluator.evaluate([conditionA, conditionB], leafEvaluator)); + assert.isFalse( + conditionTreeEvaluator.evaluate([conditionA, conditionB], function() { + return false; + }) + ); }); }); }); diff --git a/packages/optimizely-sdk/lib/core/custom_attribute_condition_evaluator/index.js b/packages/optimizely-sdk/lib/core/custom_attribute_condition_evaluator/index.js index 91a304e07..a70cd3dd0 100644 --- a/packages/optimizely-sdk/lib/core/custom_attribute_condition_evaluator/index.js +++ b/packages/optimizely-sdk/lib/core/custom_attribute_condition_evaluator/index.js @@ -62,7 +62,10 @@ function evaluate(condition, userAttributes, logger) { var attributeKey = condition.name; if (!userAttributes.hasOwnProperty(attributeKey) && conditionMatch != EXISTS_MATCH_TYPE) { - logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.MISSING_ATTRIBUTE_VALUE, MODULE_NAME, JSON.stringify(condition), attributeKey)); + logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.MISSING_ATTRIBUTE_VALUE, MODULE_NAME, JSON.stringify(condition), attributeKey) + ); return null; } @@ -77,8 +80,7 @@ function evaluate(condition, userAttributes, logger) { * @returns {Boolean} */ function isValueTypeValidForExactConditions(value) { - return typeof value === 'string' || typeof value === 'boolean' || - fns.isNumber(value); + return typeof value === 'string' || typeof value === 'boolean' || fns.isNumber(value); } /** @@ -99,23 +101,38 @@ function exactEvaluator(condition, userAttributes, logger) { var userValue = userAttributes[conditionName]; var userValueType = typeof userValue; - if (!isValueTypeValidForExactConditions(conditionValue) || (fns.isNumber(conditionValue) && !fns.isSafeInteger(conditionValue))) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNEXPECTED_CONDITION_VALUE, MODULE_NAME, JSON.stringify(condition))); + if ( + !isValueTypeValidForExactConditions(conditionValue) || + (fns.isNumber(conditionValue) && !fns.isSafeInteger(conditionValue)) + ) { + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.UNEXPECTED_CONDITION_VALUE, MODULE_NAME, JSON.stringify(condition)) + ); return null; } if (userValue === null) { - logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.UNEXPECTED_TYPE_NULL, MODULE_NAME, JSON.stringify(condition), conditionName)); + logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.UNEXPECTED_TYPE_NULL, MODULE_NAME, JSON.stringify(condition), conditionName) + ); return null; } if (!isValueTypeValidForExactConditions(userValue) || conditionValueType !== userValueType) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNEXPECTED_TYPE, MODULE_NAME, JSON.stringify(condition), userValueType, conditionName)); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.UNEXPECTED_TYPE, MODULE_NAME, JSON.stringify(condition), userValueType, conditionName) + ); return null; } if (fns.isNumber(userValue) && !fns.isSafeInteger(userValue)) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.OUT_OF_BOUNDS, MODULE_NAME, JSON.stringify(condition), conditionName)); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.OUT_OF_BOUNDS, MODULE_NAME, JSON.stringify(condition), conditionName) + ); return null; } @@ -153,22 +170,34 @@ function greaterThanEvaluator(condition, userAttributes, logger) { var conditionValue = condition.value; if (!fns.isSafeInteger(conditionValue)) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNEXPECTED_CONDITION_VALUE, MODULE_NAME, JSON.stringify(condition))); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.UNEXPECTED_CONDITION_VALUE, MODULE_NAME, JSON.stringify(condition)) + ); return null; } if (userValue === null) { - logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.UNEXPECTED_TYPE_NULL, MODULE_NAME, JSON.stringify(condition), conditionName)); + logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.UNEXPECTED_TYPE_NULL, MODULE_NAME, JSON.stringify(condition), conditionName) + ); return null; } if (!fns.isNumber(userValue)) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNEXPECTED_TYPE, MODULE_NAME, JSON.stringify(condition), userValueType, conditionName)); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.UNEXPECTED_TYPE, MODULE_NAME, JSON.stringify(condition), userValueType, conditionName) + ); return null; } if (!fns.isSafeInteger(userValue)) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.OUT_OF_BOUNDS, MODULE_NAME, JSON.stringify(condition), conditionName)); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.OUT_OF_BOUNDS, MODULE_NAME, JSON.stringify(condition), conditionName) + ); return null; } @@ -192,22 +221,34 @@ function lessThanEvaluator(condition, userAttributes, logger) { var conditionValue = condition.value; if (!fns.isSafeInteger(conditionValue)) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNEXPECTED_CONDITION_VALUE, MODULE_NAME, JSON.stringify(condition))); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.UNEXPECTED_CONDITION_VALUE, MODULE_NAME, JSON.stringify(condition)) + ); return null; } if (userValue === null) { - logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.UNEXPECTED_TYPE_NULL, MODULE_NAME, JSON.stringify(condition), conditionName)); + logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.UNEXPECTED_TYPE_NULL, MODULE_NAME, JSON.stringify(condition), conditionName) + ); return null; } if (!fns.isNumber(userValue)) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNEXPECTED_TYPE, MODULE_NAME, JSON.stringify(condition), userValueType, conditionName)); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.UNEXPECTED_TYPE, MODULE_NAME, JSON.stringify(condition), userValueType, conditionName) + ); return null; } if (!fns.isSafeInteger(userValue)) { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.OUT_OF_BOUNDS, MODULE_NAME, JSON.stringify(condition), conditionName)); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.OUT_OF_BOUNDS, MODULE_NAME, JSON.stringify(condition), conditionName) + ); return null; } @@ -231,17 +272,26 @@ function substringEvaluator(condition, userAttributes, logger) { var conditionValue = condition.value; if (typeof conditionValue !== 'string') { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNEXPECTED_CONDITION_VALUE, MODULE_NAME, JSON.stringify(condition))); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.UNEXPECTED_CONDITION_VALUE, MODULE_NAME, JSON.stringify(condition)) + ); return null; } if (userValue === null) { - logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.UNEXPECTED_TYPE_NULL, MODULE_NAME, JSON.stringify(condition), conditionName)); + logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.UNEXPECTED_TYPE_NULL, MODULE_NAME, JSON.stringify(condition), conditionName) + ); return null; } if (typeof userValue !== 'string') { - logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNEXPECTED_TYPE, MODULE_NAME, JSON.stringify(condition), userValueType, conditionName)); + logger.log( + LOG_LEVEL.WARNING, + sprintf(LOG_MESSAGES.UNEXPECTED_TYPE, MODULE_NAME, JSON.stringify(condition), userValueType, conditionName) + ); return null; } @@ -249,5 +299,5 @@ function substringEvaluator(condition, userAttributes, logger) { } module.exports = { - evaluate: evaluate + evaluate: evaluate, }; diff --git a/packages/optimizely-sdk/lib/core/custom_attribute_condition_evaluator/index.tests.js b/packages/optimizely-sdk/lib/core/custom_attribute_condition_evaluator/index.tests.js index 417951487..a56b88693 100644 --- a/packages/optimizely-sdk/lib/core/custom_attribute_condition_evaluator/index.tests.js +++ b/packages/optimizely-sdk/lib/core/custom_attribute_condition_evaluator/index.tests.js @@ -45,13 +45,13 @@ var doubleCondition = { }; describe('lib/core/custom_attribute_condition_evaluator', function() { - var mockLogger = logger.createLogger({logLevel: LOG_LEVEL.INFO}); + var mockLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO }); - beforeEach(function () { + beforeEach(function() { sinon.stub(mockLogger, 'log'); }); - afterEach(function () { + afterEach(function() { mockLogger.log.restore(); }); @@ -93,8 +93,11 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { ); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.WARNING, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"weird","name":"weird_condition","type":"custom_attribute","value":"hi"} uses an unknown match type. You may need to upgrade to a newer release of the Optimizely SDK.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.WARNING, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"weird","name":"weird_condition","type":"custom_attribute","value":"hi"} uses an unknown match type. You may need to upgrade to a newer release of the Optimizely SDK.' + ); }); describe('exists match type', function() { @@ -146,12 +149,20 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { }; it('should return true if the user-provided value is equal to the condition value', function() { - var result = customAttributeEvaluator.evaluate(exactStringCondition, { favorite_constellation: 'Lacerta' }, mockLogger); + var result = customAttributeEvaluator.evaluate( + exactStringCondition, + { favorite_constellation: 'Lacerta' }, + mockLogger + ); assert.isTrue(result); }); it('should return false if the user-provided value is not equal to the condition value', function() { - var result = customAttributeEvaluator.evaluate(exactStringCondition, { favorite_constellation: 'The Big Dipper' }, mockLogger); + var result = customAttributeEvaluator.evaluate( + exactStringCondition, + { favorite_constellation: 'The Big Dipper' }, + mockLogger + ); assert.isFalse(result); }); @@ -162,43 +173,74 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { type: 'custom_attribute', value: [], }; - var result = customAttributeEvaluator.evaluate(invalidExactCondition, { favorite_constellation: 'Lacerta' }, mockLogger); + var result = customAttributeEvaluator.evaluate( + invalidExactCondition, + { favorite_constellation: 'Lacerta' }, + mockLogger + ); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.WARNING, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":[]} evaluated to UNKNOWN because the condition value is not supported.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.WARNING, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":[]} evaluated to UNKNOWN because the condition value is not supported.' + ); }); it('should log and return null if the user-provided value is of a different type than the condition value', function() { - var result = customAttributeEvaluator.evaluate(exactStringCondition, { favorite_constellation: false }, mockLogger); + var result = customAttributeEvaluator.evaluate( + exactStringCondition, + { favorite_constellation: false }, + mockLogger + ); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.WARNING, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":"Lacerta"} evaluated to UNKNOWN because a value of type "boolean" was passed for user attribute "favorite_constellation".'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.WARNING, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":"Lacerta"} evaluated to UNKNOWN because a value of type "boolean" was passed for user attribute "favorite_constellation".' + ); }); it('should log and return null if the user-provided value is null', function() { - var result = customAttributeEvaluator.evaluate(exactStringCondition, { favorite_constellation: null }, mockLogger); + var result = customAttributeEvaluator.evaluate( + exactStringCondition, + { favorite_constellation: null }, + mockLogger + ); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":"Lacerta"} evaluated to UNKNOWN because a null value was passed for user attribute "favorite_constellation".'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":"Lacerta"} evaluated to UNKNOWN because a null value was passed for user attribute "favorite_constellation".' + ); }); it('should log and return null if there is no user-provided value', function() { var result = customAttributeEvaluator.evaluate(exactStringCondition, {}, mockLogger); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":"Lacerta"} evaluated to UNKNOWN because no value was passed for user attribute "favorite_constellation".'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":"Lacerta"} evaluated to UNKNOWN because no value was passed for user attribute "favorite_constellation".' + ); }); it('should log and return null if the user-provided value is of an unexpected type', function() { - var result = customAttributeEvaluator.evaluate(exactStringCondition, { favorite_constellation: [] }, mockLogger); + var result = customAttributeEvaluator.evaluate( + exactStringCondition, + { favorite_constellation: [] }, + mockLogger + ); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.WARNING, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":"Lacerta"} evaluated to UNKNOWN because a value of type "object" was passed for user attribute "favorite_constellation".'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.WARNING, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"favorite_constellation","type":"custom_attribute","value":"Lacerta"} evaluated to UNKNOWN because a value of type "object" was passed for user attribute "favorite_constellation".' + ); }); }); @@ -229,27 +271,39 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { assert.strictEqual(2, mockLogger.log.callCount); assert.strictEqual(mockLogger.log.args[0][0], LOG_LEVEL.WARNING); - assert.strictEqual(mockLogger.log.args[0][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"lasers_count","type":"custom_attribute","value":9000} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "lasers_count".'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"lasers_count","type":"custom_attribute","value":9000} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "lasers_count".' + ); assert.strictEqual(mockLogger.log.args[1][0], LOG_LEVEL.WARNING); - assert.strictEqual(mockLogger.log.args[1][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"lasers_count","type":"custom_attribute","value":9000} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "lasers_count".'); + assert.strictEqual( + mockLogger.log.args[1][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"lasers_count","type":"custom_attribute","value":9000} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "lasers_count".' + ); }); it('should log and return null if the user-provided number value is out of bounds', function() { var result = customAttributeEvaluator.evaluate(exactNumberCondition, { lasers_count: -Infinity }, mockLogger); assert.isNull(result); - result = customAttributeEvaluator.evaluate(exactNumberCondition, { lasers_count: -Math.pow(2, 53) - 2 }, mockLogger); + result = customAttributeEvaluator.evaluate( + exactNumberCondition, + { lasers_count: -Math.pow(2, 53) - 2 }, + mockLogger + ); assert.isNull(result); assert.strictEqual(2, mockLogger.log.callCount); assert.strictEqual(mockLogger.log.args[0][0], LOG_LEVEL.WARNING); - assert.strictEqual(mockLogger.log.args[0][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"lasers_count","type":"custom_attribute","value":9000} evaluated to UNKNOWN because the number value for user attribute "lasers_count" is not in the range [-2^53, +2^53].'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"lasers_count","type":"custom_attribute","value":9000} evaluated to UNKNOWN because the number value for user attribute "lasers_count" is not in the range [-2^53, +2^53].' + ); assert.strictEqual(mockLogger.log.args[1][0], LOG_LEVEL.WARNING); - assert.strictEqual(mockLogger.log.args[1][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"lasers_count","type":"custom_attribute","value":9000} evaluated to UNKNOWN because the number value for user attribute "lasers_count" is not in the range [-2^53, +2^53].'); + assert.strictEqual( + mockLogger.log.args[1][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"exact","name":"lasers_count","type":"custom_attribute","value":9000} evaluated to UNKNOWN because the number value for user attribute "lasers_count" is not in the range [-2^53, +2^53].' + ); }); it('should return null if there is no user-provided value', function() { @@ -312,27 +366,42 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { }; it('should return true if the condition value is a substring of the user-provided value', function() { - var result = customAttributeEvaluator.evaluate(substringCondition, { - headline_text: 'Limited time, buy now!', - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + substringCondition, + { + headline_text: 'Limited time, buy now!', + }, + mockLogger + ); assert.isTrue(result); }); it('should return false if the user-provided value is not a substring of the condition value', function() { - var result = customAttributeEvaluator.evaluate(substringCondition, { - headline_text: 'Breaking news!', - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + substringCondition, + { + headline_text: 'Breaking news!', + }, + mockLogger + ); assert.isFalse(result); }); it('should log and return null if the user-provided value is not a string', function() { - var result = customAttributeEvaluator.evaluate(substringCondition, { - headline_text: 10, - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + substringCondition, + { + headline_text: 10, + }, + mockLogger + ); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.WARNING, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"substring","name":"headline_text","type":"custom_attribute","value":"buy now"} evaluated to UNKNOWN because a value of type "number" was passed for user attribute "headline_text".'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.WARNING, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"substring","name":"headline_text","type":"custom_attribute","value":"buy now"} evaluated to UNKNOWN because a value of type "number" was passed for user attribute "headline_text".' + ); }); it('should log and return null if the condition value is not a string', function() { @@ -343,19 +412,25 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { value: 10, }; - var result = customAttributeEvaluator.evaluate(nonStringCondition, {headline_text: 'hello'}, mockLogger); + var result = customAttributeEvaluator.evaluate(nonStringCondition, { headline_text: 'hello' }, mockLogger); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.WARNING, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"substring","name":"headline_text","type":"custom_attribute","value":10} evaluated to UNKNOWN because the condition value is not supported.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.WARNING, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"substring","name":"headline_text","type":"custom_attribute","value":10} evaluated to UNKNOWN because the condition value is not supported.' + ); }); it('should log and return null if the user-provided value is null', function() { var result = customAttributeEvaluator.evaluate(substringCondition, { headline_text: null }, mockLogger); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"substring","name":"headline_text","type":"custom_attribute","value":"buy now"} evaluated to UNKNOWN because a null value was passed for user attribute "headline_text".'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"substring","name":"headline_text","type":"custom_attribute","value":"buy now"} evaluated to UNKNOWN because a null value was passed for user attribute "headline_text".' + ); }); it('should return null if there is no user-provided value', function() { @@ -373,63 +448,98 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { }; it('should return true if the user-provided value is greater than the condition value', function() { - var result = customAttributeEvaluator.evaluate(gtCondition, { - meters_travelled: 58.4, - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + gtCondition, + { + meters_travelled: 58.4, + }, + mockLogger + ); assert.isTrue(result); }); it('should return false if the user-provided value is not greater than the condition value', function() { - var result = customAttributeEvaluator.evaluate(gtCondition, { - meters_travelled: 20, - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + gtCondition, + { + meters_travelled: 20, + }, + mockLogger + ); assert.isFalse(result); }); it('should log and return null if the user-provided value is not a number', function() { - var result = customAttributeEvaluator.evaluate(gtCondition, { - meters_travelled: 'a long way', - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + gtCondition, + { + meters_travelled: 'a long way', + }, + mockLogger + ); assert.isNull(result); - result = customAttributeEvaluator.evaluate(gtCondition, { - meters_travelled: '1000', - }, mockLogger); + result = customAttributeEvaluator.evaluate( + gtCondition, + { + meters_travelled: '1000', + }, + mockLogger + ); assert.isNull(result); assert.strictEqual(2, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "meters_travelled".'); - assert.strictEqual(mockLogger.log.args[1][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "meters_travelled".'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "meters_travelled".' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "meters_travelled".' + ); }); it('should log and return null if the user-provided number value is out of bounds', function() { - var result = customAttributeEvaluator.evaluate(gtCondition, { - meters_travelled: -Infinity, - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + gtCondition, + { + meters_travelled: -Infinity, + }, + mockLogger + ); assert.isNull(result); - result = customAttributeEvaluator.evaluate(gtCondition, { - meters_travelled: Math.pow(2, 53) + 2, - }, mockLogger); + result = customAttributeEvaluator.evaluate( + gtCondition, + { + meters_travelled: Math.pow(2, 53) + 2, + }, + mockLogger + ); assert.isNull(result); assert.strictEqual(2, mockLogger.log.callCount); assert.strictEqual(mockLogger.log.args[0][0], LOG_LEVEL.WARNING); - assert.strictEqual(mockLogger.log.args[0][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because the number value for user attribute "meters_travelled" is not in the range [-2^53, +2^53].'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because the number value for user attribute "meters_travelled" is not in the range [-2^53, +2^53].' + ); assert.strictEqual(mockLogger.log.args[1][0], LOG_LEVEL.WARNING); - assert.strictEqual(mockLogger.log.args[1][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because the number value for user attribute "meters_travelled" is not in the range [-2^53, +2^53].'); + assert.strictEqual( + mockLogger.log.args[1][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because the number value for user attribute "meters_travelled" is not in the range [-2^53, +2^53].' + ); }); it('should log and return null if the user-provided value is null', function() { var result = customAttributeEvaluator.evaluate(gtCondition, { meters_travelled: null }, mockLogger); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a null value was passed for user attribute "meters_travelled".'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a null value was passed for user attribute "meters_travelled".' + ); }); it('should return null if there is no user-provided value', function() { @@ -458,8 +568,10 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { sinon.assert.calledThrice(mockLogger.log); var logMessage = mockLogger.log.args[2][1]; - assert.strictEqual(logMessage, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":9007199254740994} evaluated to UNKNOWN because the condition value is not supported.'); + assert.strictEqual( + logMessage, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"gt","name":"meters_travelled","type":"custom_attribute","value":9007199254740994} evaluated to UNKNOWN because the condition value is not supported.' + ); }); }); @@ -472,63 +584,98 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { }; it('should return true if the user-provided value is less than the condition value', function() { - var result = customAttributeEvaluator.evaluate(ltCondition, { - meters_travelled: 10, - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + ltCondition, + { + meters_travelled: 10, + }, + mockLogger + ); assert.isTrue(result); }); it('should return false if the user-provided value is not less than the condition value', function() { - var result = customAttributeEvaluator.evaluate(ltCondition, { - meters_travelled: 64.64, - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + ltCondition, + { + meters_travelled: 64.64, + }, + mockLogger + ); assert.isFalse(result); }); it('should log and return null if the user-provided value is not a number', function() { - var result = customAttributeEvaluator.evaluate(ltCondition, { - meters_travelled: true, - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + ltCondition, + { + meters_travelled: true, + }, + mockLogger + ); assert.isNull(result); - result = customAttributeEvaluator.evaluate(ltCondition, { - meters_travelled: '48.2', - }, mockLogger); + result = customAttributeEvaluator.evaluate( + ltCondition, + { + meters_travelled: '48.2', + }, + mockLogger + ); assert.isNull(result); assert.strictEqual(2, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a value of type "boolean" was passed for user attribute "meters_travelled".'); - assert.strictEqual(mockLogger.log.args[1][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "meters_travelled".'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a value of type "boolean" was passed for user attribute "meters_travelled".' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a value of type "string" was passed for user attribute "meters_travelled".' + ); }); it('should log and return null if the user-provided number value is out of bounds', function() { - var result = customAttributeEvaluator.evaluate(ltCondition, { - meters_travelled: Infinity, - }, mockLogger); + var result = customAttributeEvaluator.evaluate( + ltCondition, + { + meters_travelled: Infinity, + }, + mockLogger + ); assert.isNull(result); - result = customAttributeEvaluator.evaluate(ltCondition, { - meters_travelled: Math.pow(2, 53) + 2, - }, mockLogger); + result = customAttributeEvaluator.evaluate( + ltCondition, + { + meters_travelled: Math.pow(2, 53) + 2, + }, + mockLogger + ); assert.isNull(result); assert.strictEqual(2, mockLogger.log.callCount); assert.strictEqual(mockLogger.log.args[0][0], LOG_LEVEL.WARNING); - assert.strictEqual(mockLogger.log.args[0][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because the number value for user attribute "meters_travelled" is not in the range [-2^53, +2^53].'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because the number value for user attribute "meters_travelled" is not in the range [-2^53, +2^53].' + ); assert.strictEqual(mockLogger.log.args[1][0], LOG_LEVEL.WARNING); - assert.strictEqual(mockLogger.log.args[1][1], - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because the number value for user attribute "meters_travelled" is not in the range [-2^53, +2^53].'); + assert.strictEqual( + mockLogger.log.args[1][1], + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because the number value for user attribute "meters_travelled" is not in the range [-2^53, +2^53].' + ); }); it('should log and return null if the user-provided value is null', function() { var result = customAttributeEvaluator.evaluate(ltCondition, { meters_travelled: null }, mockLogger); assert.isNull(result); sinon.assert.calledOnce(mockLogger.log); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a null value was passed for user attribute "meters_travelled".'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":48.2} evaluated to UNKNOWN because a null value was passed for user attribute "meters_travelled".' + ); }); it('should return null if there is no user-provided value', function() { @@ -557,8 +704,10 @@ describe('lib/core/custom_attribute_condition_evaluator', function() { sinon.assert.calledThrice(mockLogger.log); var logMessage = mockLogger.log.args[2][1]; - assert.strictEqual(logMessage, - 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":9007199254740994} evaluated to UNKNOWN because the condition value is not supported.'); + assert.strictEqual( + logMessage, + 'CUSTOM_ATTRIBUTE_CONDITION_EVALUATOR: Audience condition {"match":"lt","name":"meters_travelled","type":"custom_attribute","value":9007199254740994} evaluated to UNKNOWN because the condition value is not supported.' + ); }); }); }); diff --git a/packages/optimizely-sdk/lib/core/decision_service/index.js b/packages/optimizely-sdk/lib/core/decision_service/index.js index 6cca4a534..f0d1e4e25 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.js @@ -29,8 +29,6 @@ var LOG_LEVEL = enums.LOG_LEVEL; var LOG_MESSAGES = enums.LOG_MESSAGES; var DECISION_SOURCES = enums.DECISION_SOURCES; - - /** * Optimizely's decision service that determines which variation of an experiment the user will be allocated to. * @@ -85,7 +83,10 @@ DecisionService.prototype.getVariation = function(configObj, experimentKey, user var experimentBucketMap = this.__resolveExperimentBucketMap(userId, attributes); variation = this.__getStoredVariation(configObj, experiment, userId, experimentBucketMap); if (variation) { - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.RETURNING_STORED_VARIATION, MODULE_NAME, variation.key, experimentKey, userId)); + this.logger.log( + LOG_LEVEL.INFO, + sprintf(LOG_MESSAGES.RETURNING_STORED_VARIATION, MODULE_NAME, variation.key, experimentKey, userId) + ); return variation.key; } @@ -113,13 +114,12 @@ DecisionService.prototype.getVariation = function(configObj, experimentKey, user * @return {Object} finalized copy of experiment_bucket_map */ DecisionService.prototype.__resolveExperimentBucketMap = function(userId, attributes) { - attributes = attributes || {} + attributes = attributes || {}; var userProfile = this.__getUserProfile(userId) || {}; var attributeExperimentBucketMap = attributes[enums.CONTROL_ATTRIBUTES.STICKY_BUCKETING_KEY]; return fns.assign({}, userProfile.experiment_bucket_map, attributeExperimentBucketMap); }; - /** * Checks whether the experiment is running * @param {Object} configObj The parsed project configuration object @@ -147,11 +147,21 @@ DecisionService.prototype.__getWhitelistedVariation = function(experiment, userI if (experiment.forcedVariations && experiment.forcedVariations.hasOwnProperty(userId)) { var forcedVariationKey = experiment.forcedVariations[userId]; if (experiment.variationKeyMap.hasOwnProperty(forcedVariationKey)) { - var forcedBucketingSucceededMessageLog = sprintf(LOG_MESSAGES.USER_FORCED_IN_VARIATION, MODULE_NAME, userId, forcedVariationKey); + var forcedBucketingSucceededMessageLog = sprintf( + LOG_MESSAGES.USER_FORCED_IN_VARIATION, + MODULE_NAME, + userId, + forcedVariationKey + ); this.logger.log(LOG_LEVEL.INFO, forcedBucketingSucceededMessageLog); return experiment.variationKeyMap[forcedVariationKey]; } else { - var forcedBucketingFailedMessageLog = sprintf(LOG_MESSAGES.FORCED_BUCKETING_FAILED, MODULE_NAME, forcedVariationKey, userId); + var forcedBucketingFailedMessageLog = sprintf( + LOG_MESSAGES.FORCED_BUCKETING_FAILED, + MODULE_NAME, + forcedVariationKey, + userId + ); this.logger.log(LOG_LEVEL.ERROR, forcedBucketingFailedMessageLog); return null; } @@ -171,12 +181,33 @@ DecisionService.prototype.__getWhitelistedVariation = function(experiment, userI DecisionService.prototype.__checkIfUserIsInAudience = function(configObj, experimentKey, userId, attributes) { var experimentAudienceConditions = projectConfig.getExperimentAudienceConditions(configObj, experimentKey); var audiencesById = projectConfig.getAudiencesById(configObj); - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.EVALUATING_AUDIENCES_COMBINED, MODULE_NAME, experimentKey, JSON.stringify(experimentAudienceConditions))); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf( + LOG_MESSAGES.EVALUATING_AUDIENCES_COMBINED, + MODULE_NAME, + experimentKey, + JSON.stringify(experimentAudienceConditions) + ) + ); var result = this.audienceEvaluator.evaluate(experimentAudienceConditions, audiencesById, attributes); - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.AUDIENCE_EVALUATION_RESULT_COMBINED, MODULE_NAME, experimentKey, result.toString().toUpperCase())); + this.logger.log( + LOG_LEVEL.INFO, + sprintf( + LOG_MESSAGES.AUDIENCE_EVALUATION_RESULT_COMBINED, + MODULE_NAME, + experimentKey, + result.toString().toUpperCase() + ) + ); if (!result) { - var userDoesNotMeetConditionsLogMessage = sprintf(LOG_MESSAGES.USER_NOT_IN_EXPERIMENT, MODULE_NAME, userId, experimentKey); + var userDoesNotMeetConditionsLogMessage = sprintf( + LOG_MESSAGES.USER_NOT_IN_EXPERIMENT, + MODULE_NAME, + userId, + experimentKey + ); this.logger.log(LOG_LEVEL.INFO, userDoesNotMeetConditionsLogMessage); return false; } @@ -221,7 +252,10 @@ DecisionService.prototype.__getStoredVariation = function(configObj, experiment, if (configObj.variationIdMap.hasOwnProperty(variationId)) { return configObj.variationIdMap[decision.variation_id]; } else { - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.SAVED_VARIATION_NOT_FOUND, MODULE_NAME, userId, variationId, experiment.key)); + this.logger.log( + LOG_LEVEL.INFO, + sprintf(LOG_MESSAGES.SAVED_VARIATION_NOT_FOUND, MODULE_NAME, userId, variationId, experiment.key) + ); } } @@ -246,7 +280,10 @@ DecisionService.prototype.__getUserProfile = function(userId) { try { return this.userProfileService.lookup(userId); } catch (ex) { - this.logger.log(LOG_LEVEL.ERROR, sprintf(ERROR_MESSAGES.USER_PROFILE_LOOKUP_ERROR, MODULE_NAME, userId, ex.message)); + this.logger.log( + LOG_LEVEL.ERROR, + sprintf(ERROR_MESSAGES.USER_PROFILE_LOOKUP_ERROR, MODULE_NAME, userId, ex.message) + ); } }; @@ -265,7 +302,7 @@ DecisionService.prototype.__saveUserProfile = function(experiment, variation, us try { var newBucketMap = fns.cloneDeep(experimentBucketMap); newBucketMap[experiment.id] = { - variation_id: variation.id + variation_id: variation.id, }; this.userProfileService.save({ @@ -273,7 +310,10 @@ DecisionService.prototype.__saveUserProfile = function(experiment, variation, us experiment_bucket_map: newBucketMap, }); - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.SAVED_VARIATION, MODULE_NAME, variation.key, experiment.key, userId)); + this.logger.log( + LOG_LEVEL.INFO, + sprintf(LOG_MESSAGES.SAVED_VARIATION, MODULE_NAME, variation.key, experiment.key, userId) + ); } catch (ex) { this.logger.log(LOG_LEVEL.ERROR, sprintf(ERROR_MESSAGES.USER_PROFILE_SAVE_ERROR, MODULE_NAME, userId, ex.message)); } @@ -297,11 +337,24 @@ DecisionService.prototype.__saveUserProfile = function(experiment, variation, us DecisionService.prototype.getVariationForFeature = function(configObj, feature, userId, attributes) { var experimentDecision = this._getVariationForFeatureExperiment(configObj, feature, userId, attributes); if (experimentDecision.variation !== null) { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_IN_FEATURE_EXPERIMENT, MODULE_NAME, userId, experimentDecision.variation.key, experimentDecision.experiment.key, feature.key)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf( + LOG_MESSAGES.USER_IN_FEATURE_EXPERIMENT, + MODULE_NAME, + userId, + experimentDecision.variation.key, + experimentDecision.experiment.key, + feature.key + ) + ); return experimentDecision; } - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_NOT_IN_FEATURE_EXPERIMENT, MODULE_NAME, userId, feature.key)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_NOT_IN_FEATURE_EXPERIMENT, MODULE_NAME, userId, feature.key) + ); var rolloutDecision = this._getVariationForRollout(configObj, feature, userId, attributes); if (rolloutDecision.variation !== null) { @@ -350,14 +403,20 @@ DecisionService.prototype._getVariationForFeatureExperiment = function(configObj DecisionService.prototype._getExperimentInGroup = function(configObj, group, userId) { var experimentId = bucketer.bucketUserIntoExperiment(group, userId, userId, this.logger); if (experimentId) { - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP, MODULE_NAME, userId, experimentId, group.id)); + this.logger.log( + LOG_LEVEL.INFO, + sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP, MODULE_NAME, userId, experimentId, group.id) + ); var experiment = projectConfig.getExperimentFromId(configObj, experimentId, this.logger); if (experiment) { return experiment; } } - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_ANY_EXPERIMENT_IN_GROUP, MODULE_NAME, userId, group.id)); + this.logger.log( + LOG_LEVEL.INFO, + sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_ANY_EXPERIMENT_IN_GROUP, MODULE_NAME, userId, group.id) + ); return null; }; @@ -373,7 +432,10 @@ DecisionService.prototype._getVariationForRollout = function(configObj, feature, var rollout = configObj.rolloutIdMap[feature.rolloutId]; if (!rollout) { - this.logger.log(LOG_LEVEL.ERROR, sprintf(ERROR_MESSAGES.INVALID_ROLLOUT_ID, MODULE_NAME, feature.rolloutId, feature.key)); + this.logger.log( + LOG_LEVEL.ERROR, + sprintf(ERROR_MESSAGES.INVALID_ROLLOUT_ID, MODULE_NAME, feature.rolloutId, feature.key) + ); return { experiment: null, variation: null, @@ -404,23 +466,35 @@ DecisionService.prototype._getVariationForRollout = function(configObj, feature, experiment = configObj.experimentKeyMap[rollout.experiments[index].key]; if (!this.__checkIfUserIsInAudience(configObj, experiment.key, userId, attributes)) { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_DOESNT_MEET_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, index + 1)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_DOESNT_MEET_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, index + 1) + ); continue; } - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_MEETS_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, index + 1)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_MEETS_CONDITIONS_FOR_TARGETING_RULE, MODULE_NAME, userId, index + 1) + ); bucketerParams = this.__buildBucketerParams(configObj, experiment.key, bucketingId, userId); variationId = bucketer.bucket(bucketerParams); variation = configObj.variationIdMap[variationId]; if (variation) { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_TARGETING_RULE, MODULE_NAME, userId, index + 1)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_TARGETING_RULE, MODULE_NAME, userId, index + 1) + ); return { experiment: experiment, variation: variation, decisionSource: DECISION_SOURCES.ROLLOUT, }; } else { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_TARGETING_RULE, MODULE_NAME, userId, index + 1)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_TARGETING_RULE, MODULE_NAME, userId, index + 1) + ); break; } } @@ -431,14 +505,20 @@ DecisionService.prototype._getVariationForRollout = function(configObj, feature, variationId = bucketer.bucket(bucketerParams); variation = configObj.variationIdMap[variationId]; if (variation) { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_EVERYONE_TARGETING_RULE, MODULE_NAME, userId)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_EVERYONE_TARGETING_RULE, MODULE_NAME, userId) + ); return { experiment: everyoneElseExperiment, variation: variation, decisionSource: DECISION_SOURCES.ROLLOUT, }; } else { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_EVERYONE_TARGETING_RULE, MODULE_NAME, userId)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_EVERYONE_TARGETING_RULE, MODULE_NAME, userId) + ); } } @@ -459,7 +539,11 @@ DecisionService.prototype._getBucketingId = function(userId, attributes) { var bucketingId = userId; // If the bucketing ID key is defined in attributes, than use that in place of the userID for the murmur hash key - if ((attributes != null && typeof attributes === 'object') && attributes.hasOwnProperty(enums.CONTROL_ATTRIBUTES.BUCKETING_ID)) { + if ( + attributes != null && + typeof attributes === 'object' && + attributes.hasOwnProperty(enums.CONTROL_ATTRIBUTES.BUCKETING_ID) + ) { if (typeof attributes[enums.CONTROL_ATTRIBUTES.BUCKETING_ID] === 'string') { bucketingId = attributes[enums.CONTROL_ATTRIBUTES.BUCKETING_ID]; this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.VALID_BUCKETING_ID, MODULE_NAME, bucketingId)); @@ -485,7 +569,10 @@ DecisionService.prototype.removeForcedVariation = function(userId, experimentId, if (this.forcedVariationMap.hasOwnProperty(userId)) { delete this.forcedVariationMap[userId][experimentId]; - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.VARIATION_REMOVED_FOR_USER, MODULE_NAME, experimentKey, userId)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.VARIATION_REMOVED_FOR_USER, MODULE_NAME, experimentKey, userId) + ); } else { throw new Error(sprintf(ERROR_MESSAGES.USER_NOT_IN_FORCED_VARIATION, MODULE_NAME, userId)); } @@ -506,7 +593,10 @@ DecisionService.prototype.__setInForcedVariationMap = function(userId, experimen this.forcedVariationMap[userId][experimentId] = variationId; } - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_MAPPED_TO_FORCED_VARIATION, MODULE_NAME, variationId, experimentId, userId)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_MAPPED_TO_FORCED_VARIATION, MODULE_NAME, variationId, experimentId, userId) + ); }; /** @@ -530,7 +620,10 @@ DecisionService.prototype.getForcedVariation = function(configObj, experimentKey experimentId = experiment['id']; } else { // catching improperly formatted experiments - this.logger.log(LOG_LEVEL.ERROR, sprintf(ERROR_MESSAGES.IMPROPERLY_FORMATTED_EXPERIMENT, MODULE_NAME, experimentKey)); + this.logger.log( + LOG_LEVEL.ERROR, + sprintf(ERROR_MESSAGES.IMPROPERLY_FORMATTED_EXPERIMENT, MODULE_NAME, experimentKey) + ); return null; } } catch (ex) { @@ -541,15 +634,24 @@ DecisionService.prototype.getForcedVariation = function(configObj, experimentKey var variationId = experimentToVariationMap[experimentId]; if (!variationId) { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_HAS_NO_FORCED_VARIATION_FOR_EXPERIMENT, MODULE_NAME, experimentKey, userId)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_HAS_NO_FORCED_VARIATION_FOR_EXPERIMENT, MODULE_NAME, experimentKey, userId) + ); return null; } var variationKey = projectConfig.getVariationKeyFromId(configObj, variationId); if (variationKey) { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_HAS_FORCED_VARIATION, MODULE_NAME, variationKey, experimentKey, userId)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_HAS_FORCED_VARIATION, MODULE_NAME, variationKey, experimentKey, userId) + ); } else { - this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_HAS_NO_FORCED_VARIATION_FOR_EXPERIMENT, MODULE_NAME, experimentKey, userId)); + this.logger.log( + LOG_LEVEL.DEBUG, + sprintf(LOG_MESSAGES.USER_HAS_NO_FORCED_VARIATION_FOR_EXPERIMENT, MODULE_NAME, experimentKey, userId) + ); } return variationKey; @@ -576,7 +678,10 @@ DecisionService.prototype.setForcedVariation = function(configObj, experimentKey experimentId = experiment['id']; } else { // catching improperly formatted experiments - this.logger.log(LOG_LEVEL.ERROR, sprintf(ERROR_MESSAGES.IMPROPERLY_FORMATTED_EXPERIMENT, MODULE_NAME, experimentKey)); + this.logger.log( + LOG_LEVEL.ERROR, + sprintf(ERROR_MESSAGES.IMPROPERLY_FORMATTED_EXPERIMENT, MODULE_NAME, experimentKey) + ); return false; } } catch (ex) { @@ -598,7 +703,10 @@ DecisionService.prototype.setForcedVariation = function(configObj, experimentKey var variationId = projectConfig.getVariationIdFromExperimentAndVariationKey(configObj, experimentKey, variationKey); if (!variationId) { - this.logger.log(LOG_LEVEL.ERROR, sprintf(ERROR_MESSAGES.NO_VARIATION_FOR_EXPERIMENT_KEY, MODULE_NAME, variationKey, experimentKey)); + this.logger.log( + LOG_LEVEL.ERROR, + sprintf(ERROR_MESSAGES.NO_VARIATION_FOR_EXPERIMENT_KEY, MODULE_NAME, variationKey, experimentKey) + ); return false; } diff --git a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js index 233cfa685..ae661df5f 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js @@ -42,10 +42,10 @@ describe('lib/core/decision_service', function() { describe('APIs', function() { var configObj = projectConfig.createProjectConfig(testData); var decisionServiceInstance; - var mockLogger = logger.createLogger({logLevel: LOG_LEVEL.INFO}); + var mockLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO }); var bucketerStub; - beforeEach(function () { + beforeEach(function() { bucketerStub = sinon.stub(bucketer, 'bucket'); sinon.stub(mockLogger, 'log'); decisionServiceInstance = DecisionService.createDecisionService({ @@ -53,40 +53,69 @@ describe('lib/core/decision_service', function() { }); }); - afterEach(function () { + afterEach(function() { bucketer.bucket.restore(); mockLogger.log.restore(); }); - describe('#getVariation', function () { - it('should return the correct variation for the given experiment key and user ID for a running experiment', function () { + describe('#getVariation', function() { + it('should return the correct variation for the given experiment key and user ID for a running experiment', function() { bucketerStub.returns('111128'); // ID of the 'control' variation from `test_data` - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user')); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user') + ); sinon.assert.calledOnce(bucketerStub); }); - it('should return the whitelisted variation if the user is whitelisted', function () { - assert.strictEqual('variationWithAudience', decisionServiceInstance.getVariation(configObj, 'testExperimentWithAudiences', 'user2')); + it('should return the whitelisted variation if the user is whitelisted', function() { + assert.strictEqual( + 'variationWithAudience', + decisionServiceInstance.getVariation(configObj, 'testExperimentWithAudiences', 'user2') + ); sinon.assert.notCalled(bucketerStub); assert.strictEqual(2, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User user2 is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: User user2 is forced in variation variationWithAudience.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User user2 is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: User user2 is forced in variation variationWithAudience.' + ); }); - it('should return null if the user does not meet audience conditions', function () { - assert.isNull(decisionServiceInstance.getVariation(configObj, 'testExperimentWithAudiences', 'user3', {foo: 'bar'})); + it('should return null if the user does not meet audience conditions', function() { + assert.isNull( + decisionServiceInstance.getVariation(configObj, 'testExperimentWithAudiences', 'user3', { foo: 'bar' }) + ); assert.strictEqual(4, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User user3 is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Evaluating audiences for experiment "testExperimentWithAudiences": ["11154"].'); - assert.strictEqual(mockLogger.log.args[2][1], 'DECISION_SERVICE: Audiences for experiment testExperimentWithAudiences collectively evaluated to FALSE.'); - assert.strictEqual(mockLogger.log.args[3][1], 'DECISION_SERVICE: User user3 does not meet conditions to be in experiment testExperimentWithAudiences.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User user3 is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Evaluating audiences for experiment "testExperimentWithAudiences": ["11154"].' + ); + assert.strictEqual( + mockLogger.log.args[2][1], + 'DECISION_SERVICE: Audiences for experiment testExperimentWithAudiences collectively evaluated to FALSE.' + ); + assert.strictEqual( + mockLogger.log.args[3][1], + 'DECISION_SERVICE: User user3 does not meet conditions to be in experiment testExperimentWithAudiences.' + ); }); - it('should return null if the experiment is not running', function () { + it('should return null if the experiment is not running', function() { assert.isNull(decisionServiceInstance.getVariation(configObj, 'testExperimentNotRunning', 'user1')); sinon.assert.notCalled(bucketerStub); assert.strictEqual(1, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: Experiment testExperimentNotRunning is not running.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: Experiment testExperimentNotRunning is not running.' + ); }); describe('when attributes.$opt_experiment_bucket_map is supplied', function() { @@ -95,26 +124,27 @@ describe('lib/core/decision_service', function() { var attributes = { $opt_experiment_bucket_map: { '111127': { - 'variation_id': '111129' // ID of the 'variation' variation + variation_id: '111129', // ID of the 'variation' variation }, }, }; - assert.strictEqual('variation', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes)); + assert.strictEqual( + 'variation', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes) + ); sinon.assert.notCalled(bucketerStub); }); }); - describe('when a user profile service is provided', function () { + describe('when a user profile service is provided', function() { var userProfileServiceInstance = null; var userProfileLookupStub; var userProfileSaveStub; - beforeEach(function () { + beforeEach(function() { userProfileServiceInstance = { - lookup: function () { - }, - save: function () { - }, + lookup: function() {}, + save: function() {}, }; decisionServiceInstance = DecisionService.createDecisionService({ @@ -126,37 +156,49 @@ describe('lib/core/decision_service', function() { sinon.stub(decisionServiceInstance, '__getWhitelistedVariation').returns(null); }); - afterEach(function () { + afterEach(function() { userProfileServiceInstance.lookup.restore(); userProfileServiceInstance.save.restore(); decisionServiceInstance.__getWhitelistedVariation.restore(); }); - it('should return the previously bucketed variation', function () { + it('should return the previously bucketed variation', function() { userProfileLookupStub.returns({ user_id: 'decision_service_user', experiment_bucket_map: { '111127': { - 'variation_id': '111128' // ID of the 'control' variation + variation_id: '111128', // ID of the 'control' variation }, }, }); - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user')); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user') + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.notCalled(bucketerStub); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Returning previously activated variation \"control\" of experiment \"testExperiment\" for user \"decision_service_user\" from user profile.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Returning previously activated variation "control" of experiment "testExperiment" for user "decision_service_user" from user profile.' + ); }); - it('should bucket if there was no prevously bucketed variation', function () { + it('should bucket if there was no prevously bucketed variation', function() { bucketerStub.returns('111128'); // ID of the 'control' variation userProfileLookupStub.returns({ user_id: 'decision_service_user', experiment_bucket_map: {}, }); - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user')); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user') + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.calledOnce(bucketerStub); // make sure we save the decision @@ -164,17 +206,20 @@ describe('lib/core/decision_service', function() { user_id: 'decision_service_user', experiment_bucket_map: { '111127': { - 'variation_id': '111128', - } + variation_id: '111128', + }, }, }); }); - it('should bucket if the user profile service returns null', function () { + it('should bucket if the user profile service returns null', function() { bucketerStub.returns('111128'); // ID of the 'control' variation userProfileLookupStub.returns(null); - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user')); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user') + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.calledOnce(bucketerStub); // make sure we save the decision @@ -182,47 +227,59 @@ describe('lib/core/decision_service', function() { user_id: 'decision_service_user', experiment_bucket_map: { '111127': { - 'variation_id': '111128', - } + variation_id: '111128', + }, }, }); }); - it('should re-bucket if the stored variation is no longer valid', function () { + it('should re-bucket if the stored variation is no longer valid', function() { bucketerStub.returns('111128'); // ID of the 'control' variation userProfileLookupStub.returns({ user_id: 'decision_service_user', experiment_bucket_map: { '111127': { - 'variation_id': 'not valid variation', + variation_id: 'not valid variation', }, }, }); - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user')); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user') + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.calledOnce(bucketerStub); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: User decision_service_user was previously bucketed into variation with ID not valid variation for experiment testExperiment, but no matching variation was found.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: User decision_service_user was previously bucketed into variation with ID not valid variation for experiment testExperiment, but no matching variation was found.' + ); // make sure we save the decision sinon.assert.calledWith(userProfileSaveStub, { user_id: 'decision_service_user', experiment_bucket_map: { '111127': { - 'variation_id': '111128', - } + variation_id: '111128', + }, }, }); }); - it('should store the bucketed variation for the user', function () { + it('should store the bucketed variation for the user', function() { bucketerStub.returns('111128'); // ID of the 'control' variation userProfileLookupStub.returns({ user_id: 'decision_service_user', experiment_bucket_map: {}, // no decisions for user }); - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user')); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user') + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.calledOnce(bucketerStub); assert.strictEqual(4, mockLogger.log.callCount); @@ -234,52 +291,76 @@ describe('lib/core/decision_service', function() { }, }, }); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[3][1], 'DECISION_SERVICE: Saved variation "control" of experiment "testExperiment" for user "decision_service_user".'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[3][1], + 'DECISION_SERVICE: Saved variation "control" of experiment "testExperiment" for user "decision_service_user".' + ); }); - it('should log an error message if "lookup" throws an error', function () { + it('should log an error message if "lookup" throws an error', function() { bucketerStub.returns('111128'); // ID of the 'control' variation userProfileLookupStub.throws(new Error('I am an error')); - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user')); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user') + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.calledOnce(bucketerStub); // should still go through with bucketing - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Error while looking up user profile for user ID "decision_service_user": I am an error.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Error while looking up user profile for user ID "decision_service_user": I am an error.' + ); }); - it('should log an error message if "save" throws an error', function () { + it('should log an error message if "save" throws an error', function() { bucketerStub.returns('111128'); // ID of the 'control' variation userProfileLookupStub.returns(null); userProfileSaveStub.throws(new Error('I am an error')); - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user')); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user') + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.calledOnce(bucketerStub); // should still go through with bucketing assert.strictEqual(4, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[3][1], 'DECISION_SERVICE: Error while saving user profile for user ID "decision_service_user": I am an error.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[3][1], + 'DECISION_SERVICE: Error while saving user profile for user ID "decision_service_user": I am an error.' + ); // make sure that we save the decision sinon.assert.calledWith(userProfileSaveStub, { user_id: 'decision_service_user', experiment_bucket_map: { '111127': { - 'variation_id': '111128', - } + variation_id: '111128', + }, }, }); }); describe('when passing `attributes.$opt_experiment_bucket_map`', function() { - it('should respect attributes over the userProfileService for the matching experiment id', function () { + it('should respect attributes over the userProfileService for the matching experiment id', function() { userProfileLookupStub.returns({ user_id: 'decision_service_user', experiment_bucket_map: { '111127': { - 'variation_id': '111128' // ID of the 'control' variation + variation_id: '111128', // ID of the 'control' variation }, }, }); @@ -287,93 +368,137 @@ describe('lib/core/decision_service', function() { var attributes = { $opt_experiment_bucket_map: { '111127': { - 'variation_id': '111129' // ID of the 'variation' variation + variation_id: '111129', // ID of the 'variation' variation }, }, }; - - assert.strictEqual('variation', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes)); + assert.strictEqual( + 'variation', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes) + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.notCalled(bucketerStub); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Returning previously activated variation \"variation\" of experiment \"testExperiment\" for user \"decision_service_user\" from user profile.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Returning previously activated variation "variation" of experiment "testExperiment" for user "decision_service_user" from user profile.' + ); }); - it('should ignore attributes for a different experiment id', function () { + it('should ignore attributes for a different experiment id', function() { userProfileLookupStub.returns({ user_id: 'decision_service_user', experiment_bucket_map: { - '111127': { // 'testExperiment' ID - 'variation_id': '111128' // ID of the 'control' variation + '111127': { + // 'testExperiment' ID + variation_id: '111128', // ID of the 'control' variation }, }, }); var attributes = { $opt_experiment_bucket_map: { - '122227': { // other experiment ID - 'variation_id': '122229' // ID of the 'variationWithAudience' variation + '122227': { + // other experiment ID + variation_id: '122229', // ID of the 'variationWithAudience' variation }, }, }; - assert.strictEqual('control', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes)); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes) + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.notCalled(bucketerStub); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Returning previously activated variation \"control\" of experiment \"testExperiment\" for user \"decision_service_user\" from user profile.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Returning previously activated variation "control" of experiment "testExperiment" for user "decision_service_user" from user profile.' + ); }); - it('should use attributes when the userProfileLookup variations for other experiments', function () { + it('should use attributes when the userProfileLookup variations for other experiments', function() { userProfileLookupStub.returns({ user_id: 'decision_service_user', experiment_bucket_map: { - '122227': { // other experiment ID - 'variation_id': '122229' // ID of the 'variationWithAudience' variation + '122227': { + // other experiment ID + variation_id: '122229', // ID of the 'variationWithAudience' variation }, - } + }, }); var attributes = { $opt_experiment_bucket_map: { - '111127': { // 'testExperiment' ID - 'variation_id': '111129' // ID of the 'variation' variation + '111127': { + // 'testExperiment' ID + variation_id: '111129', // ID of the 'variation' variation }, }, }; - assert.strictEqual('variation', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes)); + assert.strictEqual( + 'variation', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes) + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.notCalled(bucketerStub); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Returning previously activated variation \"variation\" of experiment \"testExperiment\" for user \"decision_service_user\" from user profile.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Returning previously activated variation "variation" of experiment "testExperiment" for user "decision_service_user" from user profile.' + ); }); - it('should use attributes when the userProfileLookup returns null', function () { + it('should use attributes when the userProfileLookup returns null', function() { userProfileLookupStub.returns(null); var attributes = { $opt_experiment_bucket_map: { '111127': { - 'variation_id': '111129' // ID of the 'variation' variation + variation_id: '111129', // ID of the 'variation' variation }, }, }; - assert.strictEqual('variation', decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes)); + assert.strictEqual( + 'variation', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'decision_service_user', attributes) + ); sinon.assert.calledWith(userProfileLookupStub, 'decision_service_user'); sinon.assert.notCalled(bucketerStub); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Returning previously activated variation \"variation\" of experiment \"testExperiment\" for user \"decision_service_user\" from user profile.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: User decision_service_user is not in the forced variation map.' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Returning previously activated variation "variation" of experiment "testExperiment" for user "decision_service_user" from user profile.' + ); }); }); }); }); - describe('__buildBucketerParams', function () { - it('should return params object with correct properties', function () { - var bucketerParams = decisionServiceInstance.__buildBucketerParams(configObj, 'testExperiment', 'testUser', 'testUser'); + describe('__buildBucketerParams', function() { + it('should return params object with correct properties', function() { + var bucketerParams = decisionServiceInstance.__buildBucketerParams( + configObj, + 'testExperiment', + 'testUser', + 'testUser' + ); var expectedParams = { bucketingId: 'testUser', @@ -402,21 +527,24 @@ describe('lib/core/decision_service', function() { }); }); - describe('__checkIfExperimentIsActive', function () { - it('should return true if experiment is running', function () { + describe('__checkIfExperimentIsActive', function() { + it('should return true if experiment is running', function() { assert.isTrue(decisionServiceInstance.__checkIfExperimentIsActive(configObj, 'testExperiment')); sinon.assert.notCalled(mockLogger.log); }); - it('should return false when experiment is not running', function () { + it('should return false when experiment is not running', function() { assert.isFalse(decisionServiceInstance.__checkIfExperimentIsActive(configObj, 'testExperimentNotRunning')); sinon.assert.calledOnce(mockLogger.log); var logMessage = mockLogger.log.args[0][1]; - assert.strictEqual(logMessage, sprintf(LOG_MESSAGES.EXPERIMENT_NOT_RUNNING, 'DECISION_SERVICE', 'testExperimentNotRunning')); + assert.strictEqual( + logMessage, + sprintf(LOG_MESSAGES.EXPERIMENT_NOT_RUNNING, 'DECISION_SERVICE', 'testExperimentNotRunning') + ); }); }); - describe('__checkIfUserIsInAudience', function () { + describe('__checkIfUserIsInAudience', function() { var __audienceEvaluateSpy; beforeEach(function() { @@ -427,51 +555,94 @@ describe('lib/core/decision_service', function() { __audienceEvaluateSpy.restore(); }); - it('should return true when audience conditions are met', function () { - assert.isTrue(decisionServiceInstance.__checkIfUserIsInAudience(configObj, 'testExperimentWithAudiences', 'testUser', {browser_type: 'firefox'})); + it('should return true when audience conditions are met', function() { + assert.isTrue( + decisionServiceInstance.__checkIfUserIsInAudience(configObj, 'testExperimentWithAudiences', 'testUser', { + browser_type: 'firefox', + }) + ); assert.strictEqual(2, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: Evaluating audiences for experiment "testExperimentWithAudiences": ["11154"].'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Audiences for experiment testExperimentWithAudiences collectively evaluated to TRUE.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: Evaluating audiences for experiment "testExperimentWithAudiences": ["11154"].' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Audiences for experiment testExperimentWithAudiences collectively evaluated to TRUE.' + ); }); - it('should return true when experiment has no audience', function () { + it('should return true when experiment has no audience', function() { assert.isTrue(decisionServiceInstance.__checkIfUserIsInAudience(configObj, 'testExperiment', 'testUser')); assert.isTrue(__audienceEvaluateSpy.alwaysReturned(true)); assert.strictEqual(2, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: Evaluating audiences for experiment "testExperiment": [].'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Audiences for experiment testExperiment collectively evaluated to TRUE.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: Evaluating audiences for experiment "testExperiment": [].' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Audiences for experiment testExperiment collectively evaluated to TRUE.' + ); }); it('should return false when audience conditions can not be evaluated', function() { - assert.isFalse(decisionServiceInstance.__checkIfUserIsInAudience(configObj, 'testExperimentWithAudiences', 'testUser')); + assert.isFalse( + decisionServiceInstance.__checkIfUserIsInAudience(configObj, 'testExperimentWithAudiences', 'testUser') + ); assert.isTrue(__audienceEvaluateSpy.alwaysReturned(false)); assert.strictEqual(3, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: Evaluating audiences for experiment "testExperimentWithAudiences": ["11154"].'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Audiences for experiment testExperimentWithAudiences collectively evaluated to FALSE.'); - assert.strictEqual(mockLogger.log.args[2][1], 'DECISION_SERVICE: User testUser does not meet conditions to be in experiment testExperimentWithAudiences.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: Evaluating audiences for experiment "testExperimentWithAudiences": ["11154"].' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Audiences for experiment testExperimentWithAudiences collectively evaluated to FALSE.' + ); + assert.strictEqual( + mockLogger.log.args[2][1], + 'DECISION_SERVICE: User testUser does not meet conditions to be in experiment testExperimentWithAudiences.' + ); }); - it('should return false when audience conditions are not met', function () { - assert.isFalse(decisionServiceInstance.__checkIfUserIsInAudience(configObj, 'testExperimentWithAudiences', 'testUser', {browser_type: 'chrome'})); + it('should return false when audience conditions are not met', function() { + assert.isFalse( + decisionServiceInstance.__checkIfUserIsInAudience(configObj, 'testExperimentWithAudiences', 'testUser', { + browser_type: 'chrome', + }) + ); assert.isTrue(__audienceEvaluateSpy.alwaysReturned(false)); assert.strictEqual(3, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: Evaluating audiences for experiment "testExperimentWithAudiences": ["11154"].'); - assert.strictEqual(mockLogger.log.args[1][1], 'DECISION_SERVICE: Audiences for experiment testExperimentWithAudiences collectively evaluated to FALSE.'); - assert.strictEqual(mockLogger.log.args[2][1], 'DECISION_SERVICE: User testUser does not meet conditions to be in experiment testExperimentWithAudiences.'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: Evaluating audiences for experiment "testExperimentWithAudiences": ["11154"].' + ); + assert.strictEqual( + mockLogger.log.args[1][1], + 'DECISION_SERVICE: Audiences for experiment testExperimentWithAudiences collectively evaluated to FALSE.' + ); + assert.strictEqual( + mockLogger.log.args[2][1], + 'DECISION_SERVICE: User testUser does not meet conditions to be in experiment testExperimentWithAudiences.' + ); }); }); - describe('__getWhitelistedVariation', function () { - it('should return forced variation ID if forced variation is provided for the user ID', function () { + describe('__getWhitelistedVariation', function() { + it('should return forced variation ID if forced variation is provided for the user ID', function() { var testExperiment = configObj.experimentKeyMap['testExperiment']; var expectedVariation = configObj.variationIdMap['111128']; - assert.strictEqual(decisionServiceInstance.__getWhitelistedVariation(testExperiment, 'user1'), expectedVariation); + assert.strictEqual( + decisionServiceInstance.__getWhitelistedVariation(testExperiment, 'user1'), + expectedVariation + ); }); - it('should return null if forced variation is not provided for the user ID', function () { + it('should return null if forced variation is not provided for the user ID', function() { var testExperiment = configObj.experimentKeyMap['testExperiment']; assert.isNull(decisionServiceInstance.__getWhitelistedVariation(testExperiment, 'notInForcedVariations')); }); @@ -503,7 +674,12 @@ describe('lib/core/decision_service', function() { describe('#setForcedVariation', function() { it('should return true for a valid forcedVariation in setForcedVariation', function() { - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'control'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'control' + ); assert.strictEqual(didSetVariation, true); }); @@ -514,19 +690,34 @@ describe('lib/core/decision_service', function() { }); it('should not set for an invalid variation key', function() { - decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'definitely_not_valid_variation_key'); + decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'definitely_not_valid_variation_key' + ); var variation = decisionServiceInstance.getForcedVariation(configObj, 'testExperiment', 'user1'); assert.strictEqual(variation, null); }); it('should reset the forcedVariation if passed null', function() { - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'control'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'control' + ); assert.strictEqual(didSetVariation, true); var variation = decisionServiceInstance.getForcedVariation(configObj, 'testExperiment', 'user1'); assert.strictEqual(variation, 'control'); - var didSetVariationAgain = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', null); + var didSetVariationAgain = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + null + ); assert.strictEqual(didSetVariationAgain, true); var variation = decisionServiceInstance.getForcedVariation(configObj, 'testExperiment', 'user1'); @@ -534,10 +725,20 @@ describe('lib/core/decision_service', function() { }); it('should be able to add variations for multiple experiments for one user', function() { - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'control'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'control' + ); assert.strictEqual(didSetVariation, true); - var didSetVariation2 = decisionServiceInstance.setForcedVariation(configObj, 'testExperimentLaunched', 'user1', 'controlLaunched'); + var didSetVariation2 = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperimentLaunched', + 'user1', + 'controlLaunched' + ); assert.strictEqual(didSetVariation2, true); var variation = decisionServiceInstance.getForcedVariation(configObj, 'testExperiment', 'user1'); @@ -548,10 +749,20 @@ describe('lib/core/decision_service', function() { }); it('should be able to add experiments for multiple users', function() { - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'control'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'control' + ); assert.strictEqual(didSetVariation, true); - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user2', 'variation'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user2', + 'variation' + ); assert.strictEqual(didSetVariation, true); var variationControl = decisionServiceInstance.getForcedVariation(configObj, 'testExperiment', 'user1'); @@ -563,10 +774,20 @@ describe('lib/core/decision_service', function() { it('should be able to reset a variation for a user with multiple experiments', function() { //set the first time - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'control'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'control' + ); assert.strictEqual(didSetVariation, true); - var didSetVariation2 = decisionServiceInstance.setForcedVariation(configObj, 'testExperimentLaunched', 'user1', 'controlLaunched'); + var didSetVariation2 = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperimentLaunched', + 'user1', + 'controlLaunched' + ); assert.strictEqual(didSetVariation2, true); var variation = decisionServiceInstance.getForcedVariation(configObj, 'testExperiment', 'user1'); @@ -576,7 +797,12 @@ describe('lib/core/decision_service', function() { assert.strictEqual(variation2, 'controlLaunched'); //reset for one of the experiments - var didSetVariationAgain = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'variation'); + var didSetVariationAgain = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'variation' + ); assert.strictEqual(didSetVariationAgain, true); var variation = decisionServiceInstance.getForcedVariation(configObj, 'testExperiment', 'user1'); @@ -588,10 +814,20 @@ describe('lib/core/decision_service', function() { it('should be able to unset a variation for a user with multiple experiments', function() { //set the first time - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'control'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'control' + ); assert.strictEqual(didSetVariation, true); - var didSetVariation2 = decisionServiceInstance.setForcedVariation(configObj, 'testExperimentLaunched', 'user1', 'controlLaunched'); + var didSetVariation2 = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperimentLaunched', + 'user1', + 'controlLaunched' + ); assert.strictEqual(didSetVariation2, true); var variation = decisionServiceInstance.getForcedVariation(configObj, 'testExperiment', 'user1'); @@ -617,18 +853,27 @@ describe('lib/core/decision_service', function() { }); it('should return null when a variation was previously set, and that variation no longer exists on the config object', function() { - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'control'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'control' + ); assert.strictEqual(didSetVariation, true); var newDatafile = fns.cloneDeep(testData); // Remove 'control' variation from variations, traffic allocation, and datafile forcedVariations. - newDatafile.experiments[0].variations = [{ - key: 'variation', - id: '111129', - }]; - newDatafile.experiments[0].trafficAllocation = [{ - entityId: '111129', - endOfRange: 9000, - }]; + newDatafile.experiments[0].variations = [ + { + key: 'variation', + id: '111129', + }, + ]; + newDatafile.experiments[0].trafficAllocation = [ + { + entityId: '111129', + endOfRange: 9000, + }, + ]; newDatafile.experiments[0].forcedVariations = { user1: 'variation', user2: 'variation', @@ -639,8 +884,13 @@ describe('lib/core/decision_service', function() { assert.strictEqual(forcedVar, null); }); - it('should return null when a variation was previously set, and that variation\'s experiment no longer exists on the config object', function() { - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'testExperiment', 'user1', 'control'); + it("should return null when a variation was previously set, and that variation's experiment no longer exists on the config object", function() { + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'testExperiment', + 'user1', + 'control' + ); assert.strictEqual(didSetVariation, true); var newConfigObj = projectConfig.createProjectConfig(testDataWithFeatures); var forcedVar = decisionServiceInstance.getForcedVariation(newConfigObj, 'testExperiment', 'user1'); @@ -648,9 +898,18 @@ describe('lib/core/decision_service', function() { }); it('should return false from setForcedVariation and not set for invalid experiment key', function() { - var didSetVariation = decisionServiceInstance.setForcedVariation(configObj, 'definitelyNotAValidExperimentKey', 'user1', 'definitely_not_valid_variation_key'); + var didSetVariation = decisionServiceInstance.setForcedVariation( + configObj, + 'definitelyNotAValidExperimentKey', + 'user1', + 'definitely_not_valid_variation_key' + ); assert.strictEqual(didSetVariation, false); - var variation = decisionServiceInstance.getForcedVariation(configObj, 'definitelyNotAValidExperimentKey', 'user1'); + var variation = decisionServiceInstance.getForcedVariation( + configObj, + 'definitelyNotAValidExperimentKey', + 'user1' + ); assert.strictEqual(variation, null); }); }); @@ -664,7 +923,7 @@ describe('lib/core/decision_service', function() { logToConsole: false, }); var optlyInstance; - beforeEach(function () { + beforeEach(function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData, @@ -680,96 +939,84 @@ describe('lib/core/decision_service', function() { sinon.stub(errorHandler, 'handleError'); }); - afterEach(function () { + afterEach(function() { eventDispatcher.dispatchEvent.restore(); errorHandler.handleError.restore(); }); var testUserAttributes = { - 'browser_type': 'firefox', + browser_type: 'firefox', }; var userAttributesWithBucketingId = { - 'browser_type': 'firefox', - '$opt_bucketing_id': '123456789' + browser_type: 'firefox', + $opt_bucketing_id: '123456789', }; var invalidUserAttributesWithBucketingId = { - 'browser_type': 'safari', - '$opt_bucketing_id': 'testBucketingIdControl!' + browser_type: 'safari', + $opt_bucketing_id: 'testBucketingIdControl!', }; - it('confirm normal bucketing occurs before setting bucketingId', function () { - assert.strictEqual('variation', optlyInstance.getVariation( - 'testExperiment', - 'test_user', - testUserAttributes)); + it('confirm normal bucketing occurs before setting bucketingId', function() { + assert.strictEqual('variation', optlyInstance.getVariation('testExperiment', 'test_user', testUserAttributes)); }); - it('confirm valid bucketing with bucketing ID set in attributes', function () { - assert.strictEqual('variationWithAudience', optlyInstance.getVariation( - 'testExperimentWithAudiences', - 'test_user', - userAttributesWithBucketingId - )); + it('confirm valid bucketing with bucketing ID set in attributes', function() { + assert.strictEqual( + 'variationWithAudience', + optlyInstance.getVariation('testExperimentWithAudiences', 'test_user', userAttributesWithBucketingId) + ); }); - it('check invalid audience with bucketingId', function () { - assert.strictEqual(null, optlyInstance.getVariation( - 'testExperimentWithAudiences', - 'test_user', - invalidUserAttributesWithBucketingId - )); + it('check invalid audience with bucketingId', function() { + assert.strictEqual( + null, + optlyInstance.getVariation('testExperimentWithAudiences', 'test_user', invalidUserAttributesWithBucketingId) + ); }); - it('test that an experiment that is not running returns a null variation', function () { - assert.strictEqual(null, optlyInstance.getVariation( - 'testExperimentNotRunning', - 'test_user', - userAttributesWithBucketingId - )); + it('test that an experiment that is not running returns a null variation', function() { + assert.strictEqual( + null, + optlyInstance.getVariation('testExperimentNotRunning', 'test_user', userAttributesWithBucketingId) + ); }); - it('test that an invalid experiment key gets a null variation', function () { - assert.strictEqual(null, optlyInstance.getVariation( - 'invalidExperiment', - 'test_user', - userAttributesWithBucketingId - )); + it('test that an invalid experiment key gets a null variation', function() { + assert.strictEqual( + null, + optlyInstance.getVariation('invalidExperiment', 'test_user', userAttributesWithBucketingId) + ); }); - it('check forced variation', function () { - assert.isTrue(optlyInstance.setForcedVariation( - 'testExperiment', - 'test_user', - 'control'), - sprintf('Set variation to "%s" failed', 'control') + it('check forced variation', function() { + assert.isTrue( + optlyInstance.setForcedVariation('testExperiment', 'test_user', 'control'), + sprintf('Set variation to "%s" failed', 'control') + ); + assert.strictEqual( + 'control', + optlyInstance.getVariation('testExperiment', 'test_user', userAttributesWithBucketingId) ); - assert.strictEqual('control', optlyInstance.getVariation( - 'testExperiment', - 'test_user', - userAttributesWithBucketingId - )); }); - it('check whitelisted variation', function () { - assert.strictEqual('control', optlyInstance.getVariation( - 'testExperiment', - 'user1', - userAttributesWithBucketingId - )); + it('check whitelisted variation', function() { + assert.strictEqual( + 'control', + optlyInstance.getVariation('testExperiment', 'user1', userAttributesWithBucketingId) + ); }); - it('check user profile', function () { + it('check user profile', function() { var userProfileLookupStub; var userProfileServiceInstance = { - lookup: function () { - }, + lookup: function() {}, }; userProfileLookupStub = sinon.stub(userProfileServiceInstance, 'lookup'); userProfileLookupStub.returns({ user_id: 'test_user', experiment_bucket_map: { '111127': { - 'variation_id': '111128' // ID of the 'control' variation + variation_id: '111128', // ID of the 'control' variation }, }, }); @@ -779,12 +1026,10 @@ describe('lib/core/decision_service', function() { userProfileService: userProfileServiceInstance, }); - assert.strictEqual('control', decisionServiceInstance.getVariation( - configObj, - 'testExperiment', - 'test_user', - userAttributesWithBucketingId - )); + assert.strictEqual( + 'control', + decisionServiceInstance.getVariation(configObj, 'testExperiment', 'test_user', userAttributesWithBucketingId) + ); sinon.assert.calledWithExactly(userProfileLookupStub, 'test_user'); }); }); @@ -792,15 +1037,15 @@ describe('lib/core/decision_service', function() { describe('_getBucketingId', function() { var configObj; var decisionService; - var mockLogger = logger.createLogger({logLevel: LOG_LEVEL.INFO}); + var mockLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO }); var userId = 'testUser1'; var userAttributesWithBucketingId = { - 'browser_type': 'firefox', - '$opt_bucketing_id': '123456789' + browser_type: 'firefox', + $opt_bucketing_id: '123456789', }; var userAttributesWithInvalidBucketingId = { - 'browser_type': 'safari', - '$opt_bucketing_id': 50 + browser_type: 'safari', + $opt_bucketing_id: 50, }; beforeEach(function() { @@ -817,13 +1062,16 @@ describe('lib/core/decision_service', function() { it('should return userId if bucketingId is not defined in user attributes', function() { assert.strictEqual(userId, decisionService._getBucketingId(userId, null)); - assert.strictEqual(userId, decisionService._getBucketingId(userId, {'browser_type': 'safari'})); + assert.strictEqual(userId, decisionService._getBucketingId(userId, { browser_type: 'safari' })); }); it('should log warning in case of invalid bucketingId', function() { assert.strictEqual(userId, decisionService._getBucketingId(userId, userAttributesWithInvalidBucketingId)); assert.strictEqual(1, mockLogger.log.callCount); - assert.strictEqual(mockLogger.log.args[0][1], 'DECISION_SERVICE: BucketingID attribute is not a string. Defaulted to userId'); + assert.strictEqual( + mockLogger.log.args[0][1], + 'DECISION_SERVICE: BucketingID attribute is not a string. Defaulted to userId' + ); }); it('should return correct bucketingId when provided in attributes', function() { @@ -838,7 +1086,7 @@ describe('lib/core/decision_service', function() { var configObj; var decisionServiceInstance; var sandbox; - var mockLogger = logger.createLogger({logLevel: LOG_LEVEL.INFO}); + var mockLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO }); beforeEach(function() { configObj = projectConfig.createProjectConfig(testDataWithFeatures); sandbox = sinon.sandbox.create(); @@ -872,186 +1120,190 @@ describe('lib/core/decision_service', function() { }); var expectedDecision = { experiment: { - 'forcedVariations': {}, - 'status': 'Running', - 'key': 'testing_my_feature', - 'id': '594098', - 'variations': [ + forcedVariations: {}, + status: 'Running', + key: 'testing_my_feature', + id: '594098', + variations: [ { - 'id': '594096', - 'variables': [ + id: '594096', + variables: [ { - 'id': '4792309476491264', - 'value': '2' + id: '4792309476491264', + value: '2', }, { - 'id': '5073784453201920', - 'value': 'true' + id: '5073784453201920', + value: 'true', }, { - 'id': '5636734406623232', - 'value': 'Buy me NOW' + id: '5636734406623232', + value: 'Buy me NOW', }, { - 'id': '6199684360044544', - 'value': '20.25' - } + id: '6199684360044544', + value: '20.25', + }, ], - 'featureEnabled': true, - 'key': 'variation' + featureEnabled: true, + key: 'variation', }, { - 'id': '594097', - 'variables': [ + id: '594097', + variables: [ { - 'id': '4792309476491264', - 'value': '10' + id: '4792309476491264', + value: '10', }, { - 'id': '5073784453201920', - 'value': 'false' + id: '5073784453201920', + value: 'false', }, { - 'id': '5636734406623232', - 'value': 'Buy me' + id: '5636734406623232', + value: 'Buy me', }, { - 'id': '6199684360044544', - 'value': '50.55' - } + id: '6199684360044544', + value: '50.55', + }, ], - 'featureEnabled': true, - 'key': 'control' + featureEnabled: true, + key: 'control', }, { - 'id': '594099', - 'variables': [ + id: '594099', + variables: [ { - 'id': '4792309476491264', - 'value': '40' + id: '4792309476491264', + value: '40', }, { - 'id': '5073784453201920', - 'value': 'true' + id: '5073784453201920', + value: 'true', }, { - 'id': '5636734406623232', - 'value': 'Buy me Later' + id: '5636734406623232', + value: 'Buy me Later', }, { - 'id': '6199684360044544', - 'value': '99.99' - } + id: '6199684360044544', + value: '99.99', + }, ], - 'featureEnabled': false, - 'key': 'variation2' - } + featureEnabled: false, + key: 'variation2', + }, ], - 'audienceIds': [], - 'trafficAllocation': [ - { 'endOfRange': 5000, 'entityId': '594096' }, - { 'endOfRange': 10000, 'entityId': '594097' } + audienceIds: [], + trafficAllocation: [ + { endOfRange: 5000, entityId: '594096' }, + { endOfRange: 10000, entityId: '594097' }, ], - 'layerId': '594093', + layerId: '594093', variationKeyMap: { control: { - 'id': '594097', - 'variables': [ + id: '594097', + variables: [ { - 'id': '4792309476491264', - 'value': '10' + id: '4792309476491264', + value: '10', }, { - 'id': '5073784453201920', - 'value': 'false' + id: '5073784453201920', + value: 'false', }, { - 'id': '5636734406623232', - 'value': 'Buy me' + id: '5636734406623232', + value: 'Buy me', }, { - 'id': '6199684360044544', - 'value': '50.55' - } + id: '6199684360044544', + value: '50.55', + }, ], - 'featureEnabled': true, - 'key': 'control' + featureEnabled: true, + key: 'control', }, variation: { - 'id': '594096', - 'variables': [ + id: '594096', + variables: [ { - 'id': '4792309476491264', - 'value': '2' + id: '4792309476491264', + value: '2', }, { - 'id': '5073784453201920', - 'value': 'true' + id: '5073784453201920', + value: 'true', }, { - 'id': '5636734406623232', - 'value': 'Buy me NOW' + id: '5636734406623232', + value: 'Buy me NOW', }, { - 'id': '6199684360044544', - 'value': '20.25' - } + id: '6199684360044544', + value: '20.25', + }, ], - 'featureEnabled': true, - 'key': 'variation' + featureEnabled: true, + key: 'variation', }, variation2: { - 'id': '594099', - 'variables': [ + id: '594099', + variables: [ { - 'id': '4792309476491264', - 'value': '40' + id: '4792309476491264', + value: '40', }, { - 'id': '5073784453201920', - 'value': 'true' + id: '5073784453201920', + value: 'true', }, { - 'id': '5636734406623232', - 'value': 'Buy me Later' + id: '5636734406623232', + value: 'Buy me Later', }, { - 'id': '6199684360044544', - 'value': '99.99' - } + id: '6199684360044544', + value: '99.99', + }, ], - 'featureEnabled': false, - 'key': 'variation2' + featureEnabled: false, + key: 'variation2', }, }, }, variation: { - 'id': '594096', - 'variables': [ + id: '594096', + variables: [ { - 'id': '4792309476491264', - 'value': '2' + id: '4792309476491264', + value: '2', }, { - 'id': '5073784453201920', - 'value': 'true' + id: '5073784453201920', + value: 'true', }, { - 'id': '5636734406623232', - 'value': 'Buy me NOW' + id: '5636734406623232', + value: 'Buy me NOW', }, { - 'id': '6199684360044544', - 'value': '20.25' - } + id: '6199684360044544', + value: '20.25', + }, ], - 'featureEnabled': true, - 'key': 'variation' + featureEnabled: true, + key: 'variation', }, decisionSource: DECISION_SOURCES.FEATURE_TEST, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is in variation variation of experiment testing_my_feature on the feature test_feature_for_experiment.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is in variation variation of experiment testing_my_feature on the feature test_feature_for_experiment.' + ); sinon.assert.calledWithExactly(getVariationStub, configObj, 'testing_my_feature', 'user1', { test_attribute: 'test_value', }); @@ -1073,7 +1325,11 @@ describe('lib/core/decision_service', function() { decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is not in any experiment on the feature test_feature_for_experiment.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is not in any experiment on the feature test_feature_for_experiment.' + ); }); }); }); @@ -1096,37 +1352,47 @@ describe('lib/core/decision_service', function() { var decision = decisionServiceInstance.getVariationForFeature(configObj, feature, 'user1'); var expectedDecision = { experiment: { - 'forcedVariations': {}, - 'status': 'Running', - 'key': 'exp_with_group', - 'id': '595010', - 'variations': [{ 'id': '595008', 'variables': [], 'key': 'var' }, { 'id': '595009', 'variables': [], 'key': 'con' }], - 'audienceIds': [], - 'trafficAllocation': [{ 'endOfRange': 5000, 'entityId': '595008' }, { 'endOfRange': 10000, 'entityId': '595009' }], - 'layerId': '595005', + forcedVariations: {}, + status: 'Running', + key: 'exp_with_group', + id: '595010', + variations: [ + { id: '595008', variables: [], key: 'var' }, + { id: '595009', variables: [], key: 'con' }, + ], + audienceIds: [], + trafficAllocation: [ + { endOfRange: 5000, entityId: '595008' }, + { endOfRange: 10000, entityId: '595009' }, + ], + layerId: '595005', groupId: '595024', variationKeyMap: { con: { - 'id': '595009', - 'variables': [], - 'key': 'con', + id: '595009', + variables: [], + key: 'con', }, var: { - 'id': '595008', - 'variables': [], - 'key': 'var', + id: '595008', + variables: [], + key: 'var', }, }, }, variation: { - 'id': '595008', - 'variables': [], - 'key': 'var', + id: '595008', + variables: [], + key: 'var', }, decisionSource: DECISION_SOURCES.FEATURE_TEST, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is in variation var of experiment exp_with_group on the feature feature_with_group.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is in variation var of experiment exp_with_group on the feature feature_with_group.' + ); }); }); @@ -1145,7 +1411,11 @@ describe('lib/core/decision_service', function() { decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is not in any experiment on the feature feature_with_group.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is not in any experiment on the feature feature_with_group.' + ); }); it('returns null decision for group experiment not referenced by the feature', function() { @@ -1157,7 +1427,11 @@ describe('lib/core/decision_service', function() { decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is not in any experiment on the feature feature_exp_no_traffic.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is not in any experiment on the feature feature_exp_no_traffic.' + ); }); }); @@ -1176,7 +1450,11 @@ describe('lib/core/decision_service', function() { decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is not in any experiment on the feature feature_with_group.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is not in any experiment on the feature feature_with_group.' + ); }); }); }); @@ -1199,91 +1477,105 @@ describe('lib/core/decision_service', function() { var decision = decisionServiceInstance.getVariationForFeature(configObj, feature, 'user1', attributes); var expectedDecision = { experiment: { - 'forcedVariations': {}, - 'status': 'Not started', - 'key': '594031', - 'id': '594031', - 'variations': [{ - 'id': '594032', - 'variables': [ - { - 'id': '4919852825313280', - 'value': 'true' - }, - { - 'id': '5482802778734592', - 'value': '395' - }, - { - 'id': '6045752732155904', - 'value': '4.99' - }, - { - 'id': '6327227708866560', - 'value': 'Hello audience' - } - ], - 'featureEnabled': true, - 'key': '594032' - }], + forcedVariations: {}, + status: 'Not started', + key: '594031', + id: '594031', + variations: [ + { + id: '594032', + variables: [ + { + id: '4919852825313280', + value: 'true', + }, + { + id: '5482802778734592', + value: '395', + }, + { + id: '6045752732155904', + value: '4.99', + }, + { + id: '6327227708866560', + value: 'Hello audience', + }, + ], + featureEnabled: true, + key: '594032', + }, + ], variationKeyMap: { 594032: { - 'id': '594032', - 'variables': [ + id: '594032', + variables: [ { - 'id': '4919852825313280', - 'value': 'true' + id: '4919852825313280', + value: 'true', }, { - 'id': '5482802778734592', - 'value': '395' + id: '5482802778734592', + value: '395', }, { - 'id': '6045752732155904', - 'value': '4.99' + id: '6045752732155904', + value: '4.99', }, { - 'id': '6327227708866560', - 'value': 'Hello audience' - } + id: '6327227708866560', + value: 'Hello audience', + }, ], - 'featureEnabled': true, - 'key': '594032' + featureEnabled: true, + key: '594032', }, }, - 'audienceIds': ['594017'], - 'trafficAllocation': [{ 'endOfRange': 5000, 'entityId': '594032' }], - 'layerId': '594030' + audienceIds: ['594017'], + trafficAllocation: [{ endOfRange: 5000, entityId: '594032' }], + layerId: '594030', }, variation: { - 'id': '594032', - 'variables': [ + id: '594032', + variables: [ { - 'id': '4919852825313280', - 'value': 'true' + id: '4919852825313280', + value: 'true', }, { - 'id': '5482802778734592', - 'value': '395' + id: '5482802778734592', + value: '395', }, { - 'id': '6045752732155904', - 'value': '4.99' + id: '6045752732155904', + value: '4.99', }, { - 'id': '6327227708866560', - 'value': 'Hello audience' - } + id: '6327227708866560', + value: 'Hello audience', + }, ], - 'featureEnabled': true, - 'key': '594032' + featureEnabled: true, + key: '594032', }, decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 meets conditions for targeting rule 1.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 bucketed into targeting rule 1.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is in rollout of feature test_feature.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 meets conditions for targeting rule 1.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 bucketed into targeting rule 1.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is in rollout of feature test_feature.' + ); }); }); @@ -1297,91 +1589,105 @@ describe('lib/core/decision_service', function() { var decision = decisionServiceInstance.getVariationForFeature(configObj, feature, 'user1', attributes); var expectedDecision = { experiment: { - 'forcedVariations': {}, - 'status': 'Not started', - 'key': '594037', - 'id': '594037', - 'variations': [{ - 'id': '594038', - 'variables': [ - { - 'id': '4919852825313280', - 'value': 'false' - }, - { - 'id': '5482802778734592', - 'value': '400' - }, - { - 'id': '6045752732155904', - 'value': '14.99' - }, - { - 'id': '6327227708866560', - 'value': 'Hello' - } - ], - 'featureEnabled': false, - 'key': '594038' - }], - 'audienceIds': [], - 'trafficAllocation': [{ 'endOfRange': 0, 'entityId': '594038' }], - 'layerId': '594030', + forcedVariations: {}, + status: 'Not started', + key: '594037', + id: '594037', + variations: [ + { + id: '594038', + variables: [ + { + id: '4919852825313280', + value: 'false', + }, + { + id: '5482802778734592', + value: '400', + }, + { + id: '6045752732155904', + value: '14.99', + }, + { + id: '6327227708866560', + value: 'Hello', + }, + ], + featureEnabled: false, + key: '594038', + }, + ], + audienceIds: [], + trafficAllocation: [{ endOfRange: 0, entityId: '594038' }], + layerId: '594030', variationKeyMap: { 594038: { - 'id': '594038', - 'variables': [ + id: '594038', + variables: [ { - 'id': '4919852825313280', - 'value': 'false' + id: '4919852825313280', + value: 'false', }, { - 'id': '5482802778734592', - 'value': '400' + id: '5482802778734592', + value: '400', }, { - 'id': '6045752732155904', - 'value': '14.99' + id: '6045752732155904', + value: '14.99', }, { - 'id': '6327227708866560', - 'value': 'Hello' - } + id: '6327227708866560', + value: 'Hello', + }, ], - 'featureEnabled': false, - 'key': '594038' + featureEnabled: false, + key: '594038', }, }, }, variation: { - 'id': '594038', - 'variables': [ + id: '594038', + variables: [ { - 'id': '4919852825313280', - 'value': 'false' + id: '4919852825313280', + value: 'false', }, { - 'id': '5482802778734592', - 'value': '400' + id: '5482802778734592', + value: '400', }, { - 'id': '6045752732155904', - 'value': '14.99' + id: '6045752732155904', + value: '14.99', }, { - 'id': '6327227708866560', - 'value': 'Hello' - } + id: '6327227708866560', + value: 'Hello', + }, ], - 'featureEnabled': false, - 'key': '594038' + featureEnabled: false, + key: '594038', }, decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 does not meet conditions for targeting rule 1.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 bucketed into everyone targeting rule.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is in rollout of feature test_feature.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 does not meet conditions for targeting rule 1.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 bucketed into everyone targeting rule.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is in rollout of feature test_feature.' + ); }); }); @@ -1398,17 +1704,29 @@ describe('lib/core/decision_service', function() { decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 does not meet conditions for targeting rule 1.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is not in rollout of feature test_feature.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 does not meet conditions for targeting rule 1.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is not in rollout of feature test_feature.' + ); }); }); describe('user excluded from audience targeting rule due to traffic allocation, and bucketed into everyone else', function() { beforeEach(function() { bucketStub.returns(null); // returns no variation for other calls - bucketStub.withArgs(sinon.match({ - experimentKey: '594037', - })).returns('594038'); // returns variation from everyone else targeitng rule when called with everyone else experiment key; + bucketStub + .withArgs( + sinon.match({ + experimentKey: '594037', + }) + ) + .returns('594038'); // returns variation from everyone else targeitng rule when called with everyone else experiment key; }); it('returns a decision with a variation and experiment from the everyone else targeting rule', function() { @@ -1416,91 +1734,105 @@ describe('lib/core/decision_service', function() { var decision = decisionServiceInstance.getVariationForFeature(configObj, feature, 'user1', attributes); var expectedDecision = { experiment: { - 'forcedVariations': {}, - 'status': 'Not started', - 'key': '594037', - 'id': '594037', - 'variations': [{ - 'id': '594038', - 'variables': [ - { - 'id': '4919852825313280', - 'value': 'false' - }, - { - 'id': '5482802778734592', - 'value': '400' - }, - { - 'id': '6045752732155904', - 'value': '14.99' - }, - { - 'id': '6327227708866560', - 'value': 'Hello' - } - ], - 'featureEnabled': false, - 'key': '594038' - }], - 'audienceIds': [], - 'trafficAllocation': [{ 'endOfRange': 0, 'entityId': '594038' }], - 'layerId': '594030', + forcedVariations: {}, + status: 'Not started', + key: '594037', + id: '594037', + variations: [ + { + id: '594038', + variables: [ + { + id: '4919852825313280', + value: 'false', + }, + { + id: '5482802778734592', + value: '400', + }, + { + id: '6045752732155904', + value: '14.99', + }, + { + id: '6327227708866560', + value: 'Hello', + }, + ], + featureEnabled: false, + key: '594038', + }, + ], + audienceIds: [], + trafficAllocation: [{ endOfRange: 0, entityId: '594038' }], + layerId: '594030', variationKeyMap: { 594038: { - 'id': '594038', - 'variables': [ + id: '594038', + variables: [ { - 'id': '4919852825313280', - 'value': 'false' + id: '4919852825313280', + value: 'false', }, { - 'id': '5482802778734592', - 'value': '400' + id: '5482802778734592', + value: '400', }, { - 'id': '6045752732155904', - 'value': '14.99' + id: '6045752732155904', + value: '14.99', }, { - 'id': '6327227708866560', - 'value': 'Hello' - } + id: '6327227708866560', + value: 'Hello', + }, ], - 'featureEnabled': false, - 'key': '594038' + featureEnabled: false, + key: '594038', }, }, }, variation: { - 'id': '594038', - 'variables': [ + id: '594038', + variables: [ { - 'id': '4919852825313280', - 'value': 'false' + id: '4919852825313280', + value: 'false', }, { - 'id': '5482802778734592', - 'value': '400' + id: '5482802778734592', + value: '400', }, { - 'id': '6045752732155904', - 'value': '14.99' + id: '6045752732155904', + value: '14.99', }, { - 'id': '6327227708866560', - 'value': 'Hello' - } + id: '6327227708866560', + value: 'Hello', + }, ], - 'featureEnabled': false, - 'key': '594038' + featureEnabled: false, + key: '594038', }, decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 meets conditions for targeting rule 1.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE User user1 not bucketed into targeting rule 1 due to traffic allocation. Trying everyone rule.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 bucketed into everyone targeting rule.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 meets conditions for targeting rule 1.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE User user1 not bucketed into targeting rule 1 due to traffic allocation. Trying everyone rule.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 bucketed into everyone targeting rule.' + ); }); }); }); @@ -1523,74 +1855,86 @@ describe('lib/core/decision_service', function() { var decision = decisionServiceInstance.getVariationForFeature(configObj, feature, 'user1'); var expectedDecision = { experiment: { - 'trafficAllocation': [ + trafficAllocation: [ { - 'endOfRange': 10000, - 'entityId': '599057' - } + endOfRange: 10000, + entityId: '599057', + }, ], - 'layerId': '599055', - 'forcedVariations': {}, - 'audienceIds': [], - 'variations': [ + layerId: '599055', + forcedVariations: {}, + audienceIds: [], + variations: [ { - 'key': '599057', - 'id': '599057', - 'featureEnabled': true, - 'variables': [ + key: '599057', + id: '599057', + featureEnabled: true, + variables: [ { - 'id': '4937719889264640', - 'value': '200' + id: '4937719889264640', + value: '200', }, { - 'id': '6345094772817920', - 'value': 'i\'m a rollout' - } - ] - } + id: '6345094772817920', + value: "i'm a rollout", + }, + ], + }, ], - 'status': 'Not started', - 'key': '599056', - 'id': '599056', + status: 'Not started', + key: '599056', + id: '599056', variationKeyMap: { 599057: { - 'key': '599057', - 'id': '599057', - 'featureEnabled': true, - 'variables': [ + key: '599057', + id: '599057', + featureEnabled: true, + variables: [ { - 'id': '4937719889264640', - 'value': '200' + id: '4937719889264640', + value: '200', }, { - 'id': '6345094772817920', - 'value': 'i\'m a rollout' - } - ] - } - } + id: '6345094772817920', + value: "i'm a rollout", + }, + ], + }, + }, }, variation: { - 'key': '599057', - 'id': '599057', - 'featureEnabled': true, - 'variables': [ + key: '599057', + id: '599057', + featureEnabled: true, + variables: [ { - 'id': '4937719889264640', - 'value': '200' + id: '4937719889264640', + value: '200', }, { - 'id': '6345094772817920', - 'value': 'i\'m a rollout' - } - ] + id: '6345094772817920', + value: "i'm a rollout", + }, + ], }, decisionSource: DECISION_SOURCES.ROLLOUT, }; assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is not in any experiment on the feature shared_feature.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 bucketed into everyone targeting rule.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is in rollout of feature shared_feature.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is not in any experiment on the feature shared_feature.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 bucketed into everyone targeting rule.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is in rollout of feature shared_feature.' + ); }); }); @@ -1608,9 +1952,21 @@ describe('lib/core/decision_service', function() { decisionSource: DECISION_SOURCES.ROLLOUT, }; var expectedDecision = assert.deepEqual(decision, expectedDecision); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: Feature unused_flag is not attached to any experiments.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: User user1 is not in any experiment on the feature unused_flag.'); - sinon.assert.calledWithExactly(mockLogger.log, LOG_LEVEL.DEBUG, 'DECISION_SERVICE: There is no rollout of feature unused_flag.'); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: Feature unused_flag is not attached to any experiments.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: User user1 is not in any experiment on the feature unused_flag.' + ); + sinon.assert.calledWithExactly( + mockLogger.log, + LOG_LEVEL.DEBUG, + 'DECISION_SERVICE: There is no rollout of feature unused_flag.' + ); }); }); }); @@ -1625,7 +1981,7 @@ describe('lib/core/decision_service', function() { configObj = projectConfig.createProjectConfig(testDataWithFeatures); feature = configObj.featureKeyMap.test_feature; decisionService = DecisionService.createDecisionService({ - logger: logger.createLogger({logLevel: LOG_LEVEL.INFO}), + logger: logger.createLogger({ logLevel: LOG_LEVEL.INFO }), }); __buildBucketerParamsSpy = sinon.spy(decisionService, '__buildBucketerParams'); }); @@ -1634,7 +1990,7 @@ describe('lib/core/decision_service', function() { __buildBucketerParamsSpy.restore(); }); - it('should call __buildBucketerParams with user Id when bucketing Id is not provided in the attributes', function () { + it('should call __buildBucketerParams with user Id when bucketing Id is not provided in the attributes', function() { var attributes = { test_attribute: 'test_value' }; decisionService._getVariationForRollout(configObj, feature, 'testUser', attributes); @@ -1643,10 +1999,10 @@ describe('lib/core/decision_service', function() { sinon.assert.calledWithExactly(__buildBucketerParamsSpy, configObj, '594037', 'testUser', 'testUser'); }); - it('should call __buildBucketerParams with bucketing Id when bucketing Id is provided in the attributes', function () { + it('should call __buildBucketerParams with bucketing Id when bucketing Id is provided in the attributes', function() { var attributes = { test_attribute: 'test_value', - $opt_bucketing_id: 'abcdefg' + $opt_bucketing_id: 'abcdefg', }; decisionService._getVariationForRollout(configObj, feature, 'testUser', attributes); diff --git a/packages/optimizely-sdk/lib/core/event_builder/index.js b/packages/optimizely-sdk/lib/core/event_builder/index.js index ca27a7f29..575b86cea 100644 --- a/packages/optimizely-sdk/lib/core/event_builder/index.js +++ b/packages/optimizely-sdk/lib/core/event_builder/index.js @@ -46,7 +46,7 @@ function getCommonEventParams(options) { var visitor = { snapshots: [], visitor_id: options.userId, - attributes: [] + attributes: [], }; var commonParams = { @@ -96,19 +96,22 @@ function getCommonEventParams(options) { */ function getImpressionEventParams(configObj, experimentId, variationId) { var impressionEventParams = { - decisions: [{ + decisions: [ + { campaign_id: projectConfig.getLayerId(configObj, experimentId), experiment_id: experimentId, variation_id: variationId, - }], - events: [{ + }, + ], + events: [ + { entity_id: projectConfig.getLayerId(configObj, experimentId), timestamp: fns.currentTimestamp(), key: ACTIVATE_EVENT_KEY, uuid: fns.uuid(), - }] - - }; + }, + ], + }; return impressionEventParams; } @@ -122,7 +125,7 @@ function getImpressionEventParams(configObj, experimentId, variationId) { */ function getVisitorSnapshot(configObj, eventKey, eventTags, logger) { var snapshot = { - events: [] + events: [], }; var eventDict = { @@ -165,7 +168,7 @@ module.exports = { */ getImpressionEvent: function(options) { var impressionEvent = { - httpVerb: HTTP_VERB + httpVerb: HTTP_VERB, }; var commonParams = getCommonEventParams(options); @@ -201,10 +204,7 @@ module.exports = { var commonParams = getCommonEventParams(options); conversionEvent.url = ENDPOINT; - var snapshot = getVisitorSnapshot(options.configObj, - options.eventKey, - options.eventTags, - options.logger); + var snapshot = getVisitorSnapshot(options.configObj, options.eventKey, options.eventTags, options.logger); commonParams.visitors[0].snapshots = [snapshot]; conversionEvent.params = commonParams; diff --git a/packages/optimizely-sdk/lib/core/event_builder/index.tests.js b/packages/optimizely-sdk/lib/core/event_builder/index.tests.js index e54336658..d223136e9 100644 --- a/packages/optimizely-sdk/lib/core/event_builder/index.tests.js +++ b/packages/optimizely-sdk/lib/core/event_builder/index.tests.js @@ -23,10 +23,8 @@ var assert = chai.assert; var sinon = require('sinon'); var uuid = require('uuid'); - describe('lib/core/event_builder', function() { describe('APIs', function() { - var mockLogger; var configObj; var clock; @@ -47,35 +45,42 @@ describe('lib/core/event_builder', function() { describe('getImpressionEvent', function() { it('should create proper params for getImpressionEvent without attributes', function() { - var expectedParams = { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '111128', - 'experiment_id': '111127', - 'campaign_id': '4' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '4', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '111128', + experiment_id: '111127', + campaign_id: '4', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '4', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -98,39 +103,49 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '111094', - 'type': 'custom', - 'value': 'firefox', - 'key': 'browser_type' - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '111128', - 'experiment_id': '111127', - 'campaign_id': '4' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '4', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + attributes: [ + { + entity_id: '111094', + type: 'custom', + value: 'firefox', + key: 'browser_type', + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '111128', + experiment_id: '111127', + campaign_id: '4', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '4', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {browser_type: 'firefox'}, + attributes: { browser_type: 'firefox' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: configObj, @@ -149,40 +164,50 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '111094', - 'type': 'custom', - 'value': false, - 'key': 'browser_type' - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '111128', - 'experiment_id': '111127', - 'campaign_id': '4' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '4', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, - } + account_id: '12001', + project_id: '111001', + visitors: [ + { + attributes: [ + { + entity_id: '111094', + type: 'custom', + value: false, + key: 'browser_type', + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '111128', + experiment_id: '111127', + campaign_id: '4', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '4', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, + }, }; var eventOptions = { - attributes: {browser_type: false}, + attributes: { browser_type: false }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: configObj, @@ -201,40 +226,50 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '111094', - 'type': 'custom', - 'value': 0, - 'key': 'browser_type' - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '111128', - 'experiment_id': '111127', - 'campaign_id': '4' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '4', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + attributes: [ + { + entity_id: '111094', + type: 'custom', + value: 0, + key: 'browser_type', + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '111128', + experiment_id: '111127', + campaign_id: '4', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '4', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {browser_type: 0}, + attributes: { browser_type: 0 }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: configObj, @@ -253,35 +288,43 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '111128', - 'experiment_id': '111127', - 'campaign_id': '4' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '4', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '111128', + experiment_id: '111127', + campaign_id: '4', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '4', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {invalid_attribute: 'sorry_not_sorry'}, + attributes: { invalid_attribute: 'sorry_not_sorry' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: configObj, @@ -302,45 +345,56 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '572018', - 'project_id': '594001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '$opt_user_agent', - 'key': '$opt_user_agent', - 'type': 'custom', - 'value': 'Chrome' - }, { - 'entity_id': '$opt_bot_filtering', - 'key': '$opt_bot_filtering', - 'type': 'custom', - 'value': true - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '595008', - 'experiment_id': '595010', - 'campaign_id': '595005' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '595005', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '35', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': true, - 'enrich_decisions': true, + account_id: '572018', + project_id: '594001', + visitors: [ + { + attributes: [ + { + entity_id: '$opt_user_agent', + key: '$opt_user_agent', + type: 'custom', + value: 'Chrome', + }, + { + entity_id: '$opt_bot_filtering', + key: '$opt_bot_filtering', + type: 'custom', + value: true, + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '595008', + experiment_id: '595010', + campaign_id: '595005', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '595005', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '35', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: true, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {'$opt_user_agent': 'Chrome'}, + attributes: { $opt_user_agent: 'Chrome' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: v4ConfigObj, @@ -361,45 +415,56 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '572018', - 'project_id': '594001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '$opt_user_agent', - 'key': '$opt_user_agent', - 'type': 'custom', - 'value': 'Chrome' - }, { - 'entity_id': '$opt_bot_filtering', - 'key': '$opt_bot_filtering', - 'type': 'custom', - 'value': false - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '595008', - 'experiment_id': '595010', - 'campaign_id': '595005' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '595005', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '35', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': true, - 'enrich_decisions': true, + account_id: '572018', + project_id: '594001', + visitors: [ + { + attributes: [ + { + entity_id: '$opt_user_agent', + key: '$opt_user_agent', + type: 'custom', + value: 'Chrome', + }, + { + entity_id: '$opt_bot_filtering', + key: '$opt_bot_filtering', + type: 'custom', + value: false, + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '595008', + experiment_id: '595010', + campaign_id: '595005', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '595005', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '35', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: true, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {'$opt_user_agent': 'Chrome'}, + attributes: { $opt_user_agent: 'Chrome' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: v4ConfigObj, @@ -418,59 +483,72 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '111094', - 'key': 'browser_type', - 'type': 'custom', - 'value': 'Chrome' - }, { - 'entity_id': '323434545', - 'key': 'boolean_key', - 'type': 'custom', - 'value': true - }, { - 'entity_id': '616727838', - 'key': 'integer_key', - 'type': 'custom', - 'value': 10 - }, { - 'entity_id': '808797686', - 'key': 'double_key', - 'type': 'custom', - 'value': 3.14 - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '111128', - 'experiment_id': '111127', - 'campaign_id': '4' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '4', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + attributes: [ + { + entity_id: '111094', + key: 'browser_type', + type: 'custom', + value: 'Chrome', + }, + { + entity_id: '323434545', + key: 'boolean_key', + type: 'custom', + value: true, + }, + { + entity_id: '616727838', + key: 'integer_key', + type: 'custom', + value: 10, + }, + { + entity_id: '808797686', + key: 'double_key', + type: 'custom', + value: 3.14, + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '111128', + experiment_id: '111127', + campaign_id: '4', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '4', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; var eventOptions = { attributes: { - 'browser_type': 'Chrome', - 'boolean_key': true, - 'integer_key': 10, - 'double_key': 3.14, + browser_type: 'Chrome', + boolean_key: true, + integer_key: 10, + double_key: 3.14, }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, @@ -490,55 +568,67 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '111094', - 'key': 'browser_type', - 'type': 'custom', - 'value': 'Chrome' - }, { - 'entity_id': '808797687', - 'key': 'valid_positive_number', - 'type': 'custom', - 'value': Math.pow(2, 53) - }, { - 'entity_id': '808797688', - 'key': 'valid_negative_number', - 'type': 'custom', - 'value': -Math.pow(2, 53) - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'decisions': [{ - 'variation_id': '111128', - 'experiment_id': '111127', - 'campaign_id': '4' - }], - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '4', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'campaign_activated' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + attributes: [ + { + entity_id: '111094', + key: 'browser_type', + type: 'custom', + value: 'Chrome', + }, + { + entity_id: '808797687', + key: 'valid_positive_number', + type: 'custom', + value: Math.pow(2, 53), + }, + { + entity_id: '808797688', + key: 'valid_negative_number', + type: 'custom', + value: -Math.pow(2, 53), + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + decisions: [ + { + variation_id: '111128', + experiment_id: '111127', + campaign_id: '4', + }, + ], + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '4', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'campaign_activated', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; var eventOptions = { attributes: { - 'browser_type': 'Chrome', - 'valid_positive_number': Math.pow(2, 53), - 'valid_negative_number': -Math.pow(2, 53), - 'invalid_number': Math.pow(2, 53) + 2, - 'array': [1, 2, 3], + browser_type: 'Chrome', + valid_positive_number: Math.pow(2, 53), + valid_negative_number: -Math.pow(2, 53), + invalid_number: Math.pow(2, 53) + 2, + array: [1, 2, 3], }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, @@ -560,25 +650,31 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'visitor_id': 'testUser', - 'attributes': [], - 'snapshots': [{ - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '111095', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'testEvent' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + visitor_id: 'testUser', + attributes: [], + snapshots: [ + { + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '111095', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'testEvent', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -601,35 +697,43 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'visitor_id': 'testUser', - 'attributes': [{ - 'entity_id': '111094', - 'type': 'custom', - 'value': 'firefox', - 'key': 'browser_type' - }], - 'snapshots': [{ - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '111095', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'testEvent' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + visitor_id: 'testUser', + attributes: [ + { + entity_id: '111094', + type: 'custom', + value: 'firefox', + key: 'browser_type', + }, + ], + snapshots: [ + { + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '111095', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'testEvent', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {browser_type: 'firefox'}, + attributes: { browser_type: 'firefox' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: configObj, @@ -648,29 +752,35 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'revenue': 4200 + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + revenue: 4200, + }, + timestamp: Math.round(new Date().getTime()), + revenue: 4200, + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'revenue': 4200, - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -696,45 +806,53 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '111094', - 'type': 'custom', - 'value': 'firefox', - 'key': 'browser_type' - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'revenue': 4200 + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [ + { + entity_id: '111094', + type: 'custom', + value: 'firefox', + key: 'browser_type', + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + revenue: 4200, + }, + timestamp: Math.round(new Date().getTime()), + revenue: 4200, + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'revenue': 4200, - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {browser_type: 'firefox'}, + attributes: { browser_type: 'firefox' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: configObj, eventKey: 'testEvent', eventTags: { - revenue: 4200 + revenue: 4200, }, logger: mockLogger, userId: 'testUser', @@ -750,30 +868,36 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'timestamp': Math.round(new Date().getTime()), - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + timestamp: Math.round(new Date().getTime()), + key: 'testEvent', + entity_id: '111095', + }, + ], + }, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {invalid_attribute: 'sorry_not_sorry'}, + attributes: { invalid_attribute: 'sorry_not_sorry' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: configObj, @@ -793,40 +917,49 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '572018', - 'project_id': '594001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '$opt_user_agent', - 'key': '$opt_user_agent', - 'type': 'custom', - 'value': 'Chrome' - }, { - 'entity_id': '$opt_bot_filtering', - 'key': '$opt_bot_filtering', - 'type': 'custom', - 'value': true - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '594089', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'item_bought' - }] - }] - }], - 'revision': '35', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': true, - 'enrich_decisions': true, + account_id: '572018', + project_id: '594001', + visitors: [ + { + attributes: [ + { + entity_id: '$opt_user_agent', + key: '$opt_user_agent', + type: 'custom', + value: 'Chrome', + }, + { + entity_id: '$opt_bot_filtering', + key: '$opt_bot_filtering', + type: 'custom', + value: true, + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '594089', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'item_bought', + }, + ], + }, + ], + }, + ], + revision: '35', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: true, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {'$opt_user_agent': 'Chrome'}, + attributes: { $opt_user_agent: 'Chrome' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: v4ConfigObj, @@ -847,40 +980,49 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '572018', - 'project_id': '594001', - 'visitors': [{ - 'attributes': [{ - 'entity_id': '$opt_user_agent', - 'key': '$opt_user_agent', - 'type': 'custom', - 'value': 'Chrome' - }, { - 'entity_id': '$opt_bot_filtering', - 'key': '$opt_bot_filtering', - 'type': 'custom', - 'value': false - }], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '594089', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'item_bought' - }] - }] - }], - 'revision': '35', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': true, - 'enrich_decisions': true, + account_id: '572018', + project_id: '594001', + visitors: [ + { + attributes: [ + { + entity_id: '$opt_user_agent', + key: '$opt_user_agent', + type: 'custom', + value: 'Chrome', + }, + { + entity_id: '$opt_bot_filtering', + key: '$opt_bot_filtering', + type: 'custom', + value: false, + }, + ], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '594089', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'item_bought', + }, + ], + }, + ], + }, + ], + revision: '35', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: true, + enrich_decisions: true, }, }; var eventOptions = { - attributes: {'$opt_user_agent': 'Chrome'}, + attributes: { $opt_user_agent: 'Chrome' }, clientEngine: 'node-sdk', clientVersion: packageJSON.version, configObj: v4ConfigObj, @@ -899,25 +1041,31 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'visitor_id': 'testUser', - 'attributes': [], - 'snapshots': [{ - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '111100', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'testEventWithMultipleExperiments' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + visitor_id: 'testUser', + attributes: [], + snapshots: [ + { + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '111100', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'testEventWithMultipleExperiments', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -940,40 +1088,50 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'visitor_id': 'testUser', - 'attributes': [{ - 'entity_id': '111094', - 'key': 'browser_type', - 'type': 'custom', - 'value': 'Chrome' - }, { - 'entity_id': '808797687', - 'key': 'valid_positive_number', - 'type': 'custom', - 'value': Math.pow(2, 53) - }, { - 'entity_id': '808797688', - 'key': 'valid_negative_number', - 'type': 'custom', - 'value': -Math.pow(2, 53) - }], - 'snapshots': [{ - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '111100', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'testEventWithMultipleExperiments' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + visitor_id: 'testUser', + attributes: [ + { + entity_id: '111094', + key: 'browser_type', + type: 'custom', + value: 'Chrome', + }, + { + entity_id: '808797687', + key: 'valid_positive_number', + type: 'custom', + value: Math.pow(2, 53), + }, + { + entity_id: '808797688', + key: 'valid_negative_number', + type: 'custom', + value: -Math.pow(2, 53), + }, + ], + snapshots: [ + { + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '111100', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'testEventWithMultipleExperiments', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -985,11 +1143,11 @@ describe('lib/core/event_builder', function() { logger: mockLogger, userId: 'testUser', attributes: { - 'browser_type': 'Chrome', - 'valid_positive_number': Math.pow(2, 53), - 'valid_negative_number': -Math.pow(2, 53), - 'invalid_number': -Math.pow(2, 53) - 2, - 'array': [1, 2, 3], + browser_type: 'Chrome', + valid_positive_number: Math.pow(2, 53), + valid_negative_number: -Math.pow(2, 53), + invalid_number: -Math.pow(2, 53) - 2, + array: [1, 2, 3], }, }; @@ -1004,28 +1162,34 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'non-revenue': 'cool', + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + 'non-revenue': 'cool', + }, + timestamp: Math.round(new Date().getTime()), + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -1052,30 +1216,36 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'non-revenue': 'cool', - 'revenue': 4200 + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + 'non-revenue': 'cool', + revenue: 4200, + }, + timestamp: Math.round(new Date().getTime()), + revenue: 4200, + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'revenue': 4200, - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -1085,7 +1255,7 @@ describe('lib/core/event_builder', function() { configObj: configObj, eventKey: 'testEvent', eventTags: { - 'revenue': 4200, + revenue: 4200, 'non-revenue': 'cool', }, logger: mockLogger, @@ -1102,29 +1272,35 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'revenue': 0 + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + revenue: 0, + }, + timestamp: Math.round(new Date().getTime()), + revenue: 0, + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'revenue': 0, - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -1134,7 +1310,7 @@ describe('lib/core/event_builder', function() { configObj: configObj, eventKey: 'testEvent', eventTags: { - 'revenue': 0, + revenue: 0, }, logger: mockLogger, userId: 'testUser', @@ -1151,29 +1327,35 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'non-revenue': 'cool', - 'revenue': 'invalid revenue' + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + 'non-revenue': 'cool', + revenue: 'invalid revenue', + }, + timestamp: Math.round(new Date().getTime()), + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -1183,7 +1365,7 @@ describe('lib/core/event_builder', function() { configObj: configObj, eventKey: 'testEvent', eventTags: { - 'revenue': 'invalid revenue', + revenue: 'invalid revenue', 'non-revenue': 'cool', }, logger: mockLogger, @@ -1203,30 +1385,36 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'non-revenue': 'cool', - 'value': '13.37' + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + 'non-revenue': 'cool', + value: '13.37', + }, + timestamp: Math.round(new Date().getTime()), + value: 13.37, + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'value': 13.37, - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -1236,7 +1424,7 @@ describe('lib/core/event_builder', function() { configObj: configObj, eventKey: 'testEvent', eventTags: { - 'value': '13.37', + value: '13.37', 'non-revenue': 'cool', }, logger: mockLogger, @@ -1253,29 +1441,35 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'value': '0.0' + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + value: '0.0', + }, + timestamp: Math.round(new Date().getTime()), + value: 0.0, + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'value': 0.0, - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -1285,7 +1479,7 @@ describe('lib/core/event_builder', function() { configObj: configObj, eventKey: 'testEvent', eventTags: { - 'value': '0.0', + value: '0.0', }, logger: mockLogger, userId: 'testUser', @@ -1302,29 +1496,35 @@ describe('lib/core/event_builder', function() { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'client_version': packageJSON.version, - 'project_id': '111001', - 'visitors': [{ - 'attributes': [], - 'visitor_id': 'testUser', - 'snapshots': [{ - 'events': [{ - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'tags': { - 'non-revenue': 'cool', - 'value': 'invalid value' + client_version: packageJSON.version, + project_id: '111001', + visitors: [ + { + attributes: [], + visitor_id: 'testUser', + snapshots: [ + { + events: [ + { + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + tags: { + 'non-revenue': 'cool', + value: 'invalid value', + }, + timestamp: Math.round(new Date().getTime()), + key: 'testEvent', + entity_id: '111095', + }, + ], }, - 'timestamp': Math.round(new Date().getTime()), - 'key': 'testEvent', - 'entity_id': '111095' - }] - }] - }], - 'account_id': '12001', - 'client_name': 'node-sdk', - 'revision': '42', - 'anonymize_ip': false, - 'enrich_decisions': true, + ], + }, + ], + account_id: '12001', + client_name: 'node-sdk', + revision: '42', + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -1334,7 +1534,7 @@ describe('lib/core/event_builder', function() { configObj: configObj, eventKey: 'testEvent', eventTags: { - 'value': 'invalid value', + value: 'invalid value', 'non-revenue': 'cool', }, logger: mockLogger, @@ -1349,36 +1549,44 @@ describe('lib/core/event_builder', function() { }); }); - describe('createEventWithBucketingId', function () { - it('should send proper bucketingID with user attributes', function () { + describe('createEventWithBucketingId', function() { + it('should send proper bucketingID with user attributes', function() { var expectedParams = { url: 'https://logx.optimizely.com/v1/events', httpVerb: 'POST', params: { - 'account_id': '12001', - 'project_id': '111001', - 'visitors': [{ - 'visitor_id': 'testUser', - 'attributes': [{ - 'entity_id': '$opt_bucketing_id', - 'key': '$opt_bucketing_id', - 'type': 'custom', - 'value': 'variation', - }], - 'snapshots': [{ - 'events': [{ - 'timestamp': Math.round(new Date().getTime()), - 'entity_id': '111095', - 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', - 'key': 'testEvent' - }] - }] - }], - 'revision': '42', - 'client_name': 'node-sdk', - 'client_version': packageJSON.version, - 'anonymize_ip': false, - 'enrich_decisions': true, + account_id: '12001', + project_id: '111001', + visitors: [ + { + visitor_id: 'testUser', + attributes: [ + { + entity_id: '$opt_bucketing_id', + key: '$opt_bucketing_id', + type: 'custom', + value: 'variation', + }, + ], + snapshots: [ + { + events: [ + { + timestamp: Math.round(new Date().getTime()), + entity_id: '111095', + uuid: 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + key: 'testEvent', + }, + ], + }, + ], + }, + ], + revision: '42', + client_name: 'node-sdk', + client_version: packageJSON.version, + anonymize_ip: false, + enrich_decisions: true, }, }; @@ -1389,7 +1597,7 @@ describe('lib/core/event_builder', function() { eventKey: 'testEvent', logger: mockLogger, userId: 'testUser', - attributes: {'$opt_bucketing_id': 'variation'}, + attributes: { $opt_bucketing_id: 'variation' }, }; var actualParams = eventBuilder.getConversionEvent(eventOptions); diff --git a/packages/optimizely-sdk/lib/core/notification_center/index.js b/packages/optimizely-sdk/lib/core/notification_center/index.js index 42f5b86d5..6ae0748ea 100644 --- a/packages/optimizely-sdk/lib/core/notification_center/index.js +++ b/packages/optimizely-sdk/lib/core/notification_center/index.js @@ -37,9 +37,11 @@ function NotificationCenter(options) { this.errorHandler = options.errorHandler; this.__notificationListeners = {}; - jsSdkUtils.objectValues(enums.NOTIFICATION_TYPES).forEach(function(notificationTypeEnum) { - this.__notificationListeners[notificationTypeEnum] = []; - }.bind(this)); + jsSdkUtils.objectValues(enums.NOTIFICATION_TYPES).forEach( + function(notificationTypeEnum) { + this.__notificationListeners[notificationTypeEnum] = []; + }.bind(this) + ); this.__listenerId = 1; } @@ -53,10 +55,9 @@ function NotificationCenter(options) { * can happen if the first argument is not a valid notification type, or if the same callback * function was already added as a listener by a prior call to this function. */ -NotificationCenter.prototype.addNotificationListener = function (notificationType, callback) { +NotificationCenter.prototype.addNotificationListener = function(notificationType, callback) { try { - var isNotificationTypeValid = jsSdkUtils.objectValues(enums.NOTIFICATION_TYPES) - .indexOf(notificationType) > -1; + var isNotificationTypeValid = jsSdkUtils.objectValues(enums.NOTIFICATION_TYPES).indexOf(notificationType) > -1; if (!isNotificationTypeValid) { return -1; } @@ -97,26 +98,28 @@ NotificationCenter.prototype.addNotificationListener = function (notificationTyp * @returns {boolean} Returns true if the listener was found and removed, and false * otherwise. */ -NotificationCenter.prototype.removeNotificationListener = function (listenerId) { +NotificationCenter.prototype.removeNotificationListener = function(listenerId) { try { var indexToRemove; var typeToRemove; - - Object.keys(this.__notificationListeners).some(function(notificationType) { - var listenersForType = this.__notificationListeners[notificationType]; - (listenersForType || []).every(function(listenerEntry, i) { - if (listenerEntry.id === listenerId) { - indexToRemove = i; - typeToRemove = notificationType; - return false; + + Object.keys(this.__notificationListeners).some( + function(notificationType) { + var listenersForType = this.__notificationListeners[notificationType]; + (listenersForType || []).every(function(listenerEntry, i) { + if (listenerEntry.id === listenerId) { + indexToRemove = i; + typeToRemove = notificationType; + return false; + } + return true; + }); + if (indexToRemove !== undefined && typeToRemove !== undefined) { + return true; } - return true - }); - if (indexToRemove !== undefined && typeToRemove !== undefined) { - return true; - } - }.bind(this)); - + }.bind(this) + ); + if (indexToRemove !== undefined && typeToRemove !== undefined) { this.__notificationListeners[typeToRemove].splice(indexToRemove, 1); return true; @@ -131,11 +134,13 @@ NotificationCenter.prototype.removeNotificationListener = function (listenerId) /** * Removes all previously added notification listeners, for all notification types */ -NotificationCenter.prototype.clearAllNotificationListeners = function () { - try{ - jsSdkUtils.objectValues(enums.NOTIFICATION_TYPES).forEach(function (notificationTypeEnum) { - this.__notificationListeners[notificationTypeEnum] = []; - }.bind(this)); +NotificationCenter.prototype.clearAllNotificationListeners = function() { + try { + jsSdkUtils.objectValues(enums.NOTIFICATION_TYPES).forEach( + function(notificationTypeEnum) { + this.__notificationListeners[notificationTypeEnum] = []; + }.bind(this) + ); } catch (e) { this.logger.log(LOG_LEVEL.ERROR, e.message); this.errorHandler.handleError(e); @@ -146,7 +151,7 @@ NotificationCenter.prototype.clearAllNotificationListeners = function () { * Remove all previously added notification listeners for the argument type * @param {string} notificationType One of enums.NOTIFICATION_TYPES */ -NotificationCenter.prototype.clearNotificationListeners = function (notificationType) { +NotificationCenter.prototype.clearNotificationListeners = function(notificationType) { try { this.__notificationListeners[notificationType] = []; } catch (e) { @@ -161,16 +166,21 @@ NotificationCenter.prototype.clearNotificationListeners = function (notification * @param {string} notificationType One of enums.NOTIFICATION_TYPES * @param {Object} notificationData Will be passed to callbacks called */ -NotificationCenter.prototype.sendNotifications = function (notificationType, notificationData) { +NotificationCenter.prototype.sendNotifications = function(notificationType, notificationData) { try { - (this.__notificationListeners[notificationType] || []).forEach(function(listenerEntry) { - var callback = listenerEntry.callback; - try { - callback(notificationData); - } catch (ex) { - this.logger.log(LOG_LEVEL.ERROR, jsSdkUtils.sprintf(LOG_MESSAGES.NOTIFICATION_LISTENER_EXCEPTION, MODULE_NAME, notificationType, ex.message)); - } - }.bind(this)); + (this.__notificationListeners[notificationType] || []).forEach( + function(listenerEntry) { + var callback = listenerEntry.callback; + try { + callback(notificationData); + } catch (ex) { + this.logger.log( + LOG_LEVEL.ERROR, + jsSdkUtils.sprintf(LOG_MESSAGES.NOTIFICATION_LISTENER_EXCEPTION, MODULE_NAME, notificationType, ex.message) + ); + } + }.bind(this) + ); } catch (e) { this.logger.log(LOG_LEVEL.ERROR, e.message); this.errorHandler.handleError(e); diff --git a/packages/optimizely-sdk/lib/core/notification_center/index.tests.js b/packages/optimizely-sdk/lib/core/notification_center/index.tests.js index 40f21527f..1a5f119f5 100644 --- a/packages/optimizely-sdk/lib/core/notification_center/index.tests.js +++ b/packages/optimizely-sdk/lib/core/notification_center/index.tests.js @@ -14,7 +14,7 @@ * limitations under the License. * ***************************************************************************/ -var NotificationCenter = require('./') +var NotificationCenter = require('./'); var errorHandler = require('../../plugins/error_handler'); var logger = require('../../plugins/logger'); var enums = require('../../utils/enums'); @@ -25,95 +25,85 @@ var assert = chai.assert; describe('lib/core/notification_center', function() { describe('APIs', function() { - var mockLogger = logger.createLogger({logLevel: LOG_LEVEL.INFO}); + var mockLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO }); var mockErrorHandler = errorHandler.handleError; var mockLoggerStub; var mockErrorHandlerStub; var notificationCenterInstance; var sandbox; - beforeEach(function(){ + beforeEach(function() { sandbox = sinon.sandbox.create(); mockLoggerStub = sandbox.stub(mockLogger, 'log'); mockErrorHandlerStub = sandbox.stub(mockErrorHandler, 'handleError'); notificationCenterInstance = NotificationCenter.createNotificationCenter({ logger: mockLoggerStub, - errorHandler: mockErrorHandlerStub - }); + errorHandler: mockErrorHandlerStub, + }); }); - afterEach(function(){ + afterEach(function() { sandbox.restore(); }); describe('#addNotificationListener', function() { - context('the listener type is not a valid type', function(){ - it('should return -1 if notification type is not a valid type', function(){ - var INVALID_LISTENER_TYPE = 'INVALID_LISTENER_TYPE' + context('the listener type is not a valid type', function() { + it('should return -1 if notification type is not a valid type', function() { + var INVALID_LISTENER_TYPE = 'INVALID_LISTENER_TYPE'; var genericCallbackSpy = sinon.spy(); var listenerId = notificationCenterInstance.addNotificationListener( - INVALID_LISTENER_TYPE, + INVALID_LISTENER_TYPE, genericCallbackSpy ); assert.strictEqual(listenerId, -1); - }); }); - context('the listener type is a valid type', function(){ - it('should return -1 if that same callback is already added', function(){ + context('the listener type is a valid type', function() { + it('should return -1 if that same callback is already added', function() { var activateCallback; var decisionCallback; var logEventCallback; var configUpdateCallback; var trackCallback; // add a listener for each type + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallback); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallback); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallback); notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallback + enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, + configUpdateCallback ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallback + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallback); + // assertions + assert.strictEqual( + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallback), + -1 ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallback + assert.strictEqual( + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallback), + -1 ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - configUpdateCallback + assert.strictEqual( + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallback), + -1 ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallback + assert.strictEqual( + notificationCenterInstance.addNotificationListener( + enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, + configUpdateCallback + ), + -1 + ); + assert.strictEqual( + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallback), + -1 ); - // assertions - assert.strictEqual(notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallback - ), -1); - assert.strictEqual(notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallback - ), -1); - assert.strictEqual(notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallback - ), -1); - assert.strictEqual(notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - configUpdateCallback - ), -1); - assert.strictEqual(notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallback - ), -1); }); - it('should return an id (listenerId) > 0 of the notification listener if callback is not already added', function(){ + it('should return an id (listenerId) > 0 of the notification listener if callback is not already added', function() { var activateCallback; var decisionCallback; var logEventCallback; @@ -152,14 +142,14 @@ describe('lib/core/notification_center', function() { describe('#removeNotificationListener', function() { context('the listenerId does not exist', function() { - it('should return false if listenerId does not exist', function(){ + it('should return false if listenerId does not exist', function() { var notListenerId = notificationCenterInstance.removeNotificationListener(5); assert.isFalse(notListenerId); }); }); - context('listenerId exists', function(){ - it('should return true when existing listener is removed', function(){ + context('listenerId exists', function() { + it('should return true when existing listener is removed', function() { var activateCallback; var decisionCallback; var logEventCallback; @@ -201,7 +191,7 @@ describe('lib/core/notification_center', function() { assert.strictEqual(configListenerRemoved, true); }); - it('should only remove the specified listener', function(){ + it('should only remove the specified listener', function() { var activateCallbackSpy1 = sinon.spy(); var activateCallbackSpy2 = sinon.spy(); var decisionCallbackSpy1 = sinon.spy(); @@ -234,31 +224,21 @@ describe('lib/core/notification_center', function() { trackCallbackSpy1 ); // register second listeners for each type - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy2 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy2 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy2); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy2); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy2); notificationCenterInstance.addNotificationListener( enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, configUpdateCallbackSpy2 ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy2); // remove first listener var activateListenerRemoved1 = notificationCenterInstance.removeNotificationListener(activateListenerId1); var decisionListenerRemoved1 = notificationCenterInstance.removeNotificationListener(decisionListenerId1); var logEventListenerRemoved1 = notificationCenterInstance.removeNotificationListener(logeventlistenerId1); - var configUpdateListenerRemoved1 = notificationCenterInstance.removeNotificationListener(configUpdateListenerId1); + var configUpdateListenerRemoved1 = notificationCenterInstance.removeNotificationListener( + configUpdateListenerId1 + ); var trackListenerRemoved1 = notificationCenterInstance.removeNotificationListener(trackListenerId1); // send notifications notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, {}); @@ -282,146 +262,91 @@ describe('lib/core/notification_center', function() { assert.strictEqual(trackListenerRemoved1, true); sinon.assert.notCalled(trackCallbackSpy1); sinon.assert.calledOnce(trackCallbackSpy2); - }); }); }); describe('#clearAllNotificationListeners', function() { - it('should remove all notification listeners for all types', function(){ + it('should remove all notification listeners for all types', function() { var activateCallbackSpy1 = sinon.spy(); var decisionCallbackSpy1 = sinon.spy(); var logEventCallbackSpy1 = sinon.spy(); var configUpdateCallbackSpy1 = sinon.spy(); var trackCallbackSpy1 = sinon.spy(); // add a listener for each notification type - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy1); notificationCenterInstance.addNotificationListener( enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, configUpdateCallbackSpy1 ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy1); // remove all listeners notificationCenterInstance.clearAllNotificationListeners(); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.ACTIVATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.DECISION, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.LOG_EVENT, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.TRACK, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.DECISION, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.LOG_EVENT, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.TRACK, {}); // check that none of the now removed listeners were called sinon.assert.notCalled(activateCallbackSpy1); sinon.assert.notCalled(decisionCallbackSpy1); sinon.assert.notCalled(logEventCallbackSpy1); sinon.assert.notCalled(configUpdateCallbackSpy1); sinon.assert.notCalled(trackCallbackSpy1); - }) + }); }); describe('#clearNotificationListeners', function() { - context('there is only one type of listener added', function(){ - it('should remove all notification listeners for the ACTIVATE type', function(){ + context('there is only one type of listener added', function() { + it('should remove all notification listeners for the ACTIVATE type', function() { var activateCallbackSpy1 = sinon.spy(); var activateCallbackSpy2 = sinon.spy(); //add 2 different listeners for ACTIVATE - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy2); // remove ACTIVATE listeners notificationCenterInstance.clearNotificationListeners(enums.NOTIFICATION_TYPES.ACTIVATE); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.ACTIVATE, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, {}); // check that none of the ACTIVATE listeners were called sinon.assert.notCalled(activateCallbackSpy1); sinon.assert.notCalled(activateCallbackSpy2); }); - it('should remove all notification listeners for the DECISION type', function(){ + it('should remove all notification listeners for the DECISION type', function() { var decisionCallbackSpy1 = sinon.spy(); var decisionCallbackSpy2 = sinon.spy(); //add 2 different listeners for DECISION - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy2); // remove DECISION listeners notificationCenterInstance.clearAllNotificationListeners(enums.NOTIFICATION_TYPES.DECISION); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.DECISION, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.DECISION, {}); // check that none of the DECISION listeners were called sinon.assert.notCalled(decisionCallbackSpy1); sinon.assert.notCalled(decisionCallbackSpy2); }); - it('should remove all notification listeners for the LOG_EVENT type', function(){ + it('should remove all notification listeners for the LOG_EVENT type', function() { var logEventCallbackSpy1 = sinon.spy(); var logEventCallbackSpy2 = sinon.spy(); //add 2 different listeners for LOG_EVENT - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy2); // remove LOG_EVENT listeners notificationCenterInstance.clearAllNotificationListeners(enums.NOTIFICATION_TYPES.LOG_EVENT); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.LOG_EVENT, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.LOG_EVENT, {}); // check that none of the LOG_EVENT listeners were called sinon.assert.notCalled(logEventCallbackSpy1); sinon.assert.notCalled(logEventCallbackSpy2); }); - it('should remove all notification listeners for the OPTIMIZELY_CONFIG_UPDATE type', function(){ + it('should remove all notification listeners for the OPTIMIZELY_CONFIG_UPDATE type', function() { var configUpdateCallbackSpy1 = sinon.spy(); var configUpdateCallbackSpy2 = sinon.spy(); //add 2 different listeners for OPTIMIZELY_CONFIG_UPDATE @@ -436,42 +361,30 @@ describe('lib/core/notification_center', function() { // remove OPTIMIZELY_CONFIG_UPDATE listeners notificationCenterInstance.clearAllNotificationListeners(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, {}); // check that none of the OPTIMIZELY_CONFIG_UPDATE listeners were called sinon.assert.notCalled(configUpdateCallbackSpy1); sinon.assert.notCalled(configUpdateCallbackSpy2); }); - it('should remove all notification listeners for the TRACK type', function(){ + it('should remove all notification listeners for the TRACK type', function() { var trackCallbackSpy1 = sinon.spy(); var trackCallbackSpy2 = sinon.spy(); //add 2 different listeners for TRACK - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy2); // remove TRACK listeners notificationCenterInstance.clearAllNotificationListeners(enums.NOTIFICATION_TYPES.TRACK); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.TRACK, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.TRACK, {}); // check that none of the TRACK listeners were called sinon.assert.notCalled(trackCallbackSpy1); sinon.assert.notCalled(trackCallbackSpy2); }); }); - context('there is more than one type of listener added', function(){ - it('should only remove ACTIVATE type listeners and not any other types', function(){ + context('there is more than one type of listener added', function() { + it('should only remove ACTIVATE type listeners and not any other types', function() { var activateCallbackSpy1 = sinon.spy(); var activateCallbackSpy2 = sinon.spy(); var decisionCallbackSpy1 = sinon.spy(); @@ -479,65 +392,35 @@ describe('lib/core/notification_center', function() { var configUpdateCallbackSpy1 = sinon.spy(); var trackCallbackSpy1 = sinon.spy(); //add 2 different listeners for ACTIVATE - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy2); // add a listener for each notification type - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy1); notificationCenterInstance.addNotificationListener( enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, configUpdateCallbackSpy1 ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy1); // remove only ACTIVATE type notificationCenterInstance.clearNotificationListeners(enums.NOTIFICATION_TYPES.ACTIVATE); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.ACTIVATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.DECISION, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.LOG_EVENT, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.TRACK, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.DECISION, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.LOG_EVENT, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.TRACK, {}); // check that ACTIVATE listeners were note called sinon.assert.notCalled(activateCallbackSpy1); sinon.assert.notCalled(activateCallbackSpy2); - // check that all other listeners were called. + // check that all other listeners were called. sinon.assert.calledOnce(decisionCallbackSpy1); sinon.assert.calledOnce(logEventCallbackSpy1); sinon.assert.calledOnce(configUpdateCallbackSpy1); sinon.assert.calledOnce(trackCallbackSpy1); }); - it('should only remove DECISION type listeners and not any other types', function(){ + it('should only remove DECISION type listeners and not any other types', function() { var decisionCallbackSpy1 = sinon.spy(); var decisionCallbackSpy2 = sinon.spy(); var activateCallbackSpy1 = sinon.spy(); @@ -545,65 +428,35 @@ describe('lib/core/notification_center', function() { var configUpdateCallbackSpy1 = sinon.spy(); var trackCallbackSpy1 = sinon.spy(); // add 2 different listeners for DECISION - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy2); // add a listener for each notification type - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy1); notificationCenterInstance.addNotificationListener( enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, configUpdateCallbackSpy1 ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy1); // remove only DECISION type notificationCenterInstance.clearNotificationListeners(enums.NOTIFICATION_TYPES.DECISION); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.ACTIVATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.DECISION, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.LOG_EVENT, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.TRACK, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.DECISION, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.LOG_EVENT, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.TRACK, {}); // check that DECISION listeners were not called sinon.assert.notCalled(decisionCallbackSpy1); sinon.assert.notCalled(decisionCallbackSpy2); - // check that all other listeners were called. + // check that all other listeners were called. sinon.assert.calledOnce(activateCallbackSpy1); sinon.assert.calledOnce(logEventCallbackSpy1); sinon.assert.calledOnce(configUpdateCallbackSpy1); sinon.assert.calledOnce(trackCallbackSpy1); }); - it('should only remove LOG_EVENT type listeners and not any other types', function(){ + it('should only remove LOG_EVENT type listeners and not any other types', function() { var logEventCallbackSpy1 = sinon.spy(); var logEventCallbackSpy2 = sinon.spy(); var activateCallbackSpy1 = sinon.spy(); @@ -611,65 +464,35 @@ describe('lib/core/notification_center', function() { var configUpdateCallbackSpy1 = sinon.spy(); var trackCallbackSpy1 = sinon.spy(); // add 2 different listeners for LOG_EVENT - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy2); // add a listener for each notification type + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy1); notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - configUpdateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy1 + enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, + configUpdateCallbackSpy1 ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy1); // remove only LOG_EVENT type notificationCenterInstance.clearNotificationListeners(enums.NOTIFICATION_TYPES.LOG_EVENT); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.ACTIVATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.DECISION, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.LOG_EVENT, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.TRACK, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.DECISION, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.LOG_EVENT, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.TRACK, {}); // check that LOG_EVENT listeners were not called sinon.assert.notCalled(logEventCallbackSpy1); sinon.assert.notCalled(logEventCallbackSpy2); - // check that all other listeners were called. + // check that all other listeners were called. sinon.assert.calledOnce(activateCallbackSpy1); sinon.assert.calledOnce(decisionCallbackSpy1); sinon.assert.calledOnce(configUpdateCallbackSpy1); sinon.assert.calledOnce(trackCallbackSpy1); }); - it('should only remove OPTIMIZELY_CONFIG_UPDATE type listeners and not any other types', function(){ + it('should only remove OPTIMIZELY_CONFIG_UPDATE type listeners and not any other types', function() { var configUpdateCallbackSpy1 = sinon.spy(); var configUpdateCallbackSpy2 = sinon.spy(); var activateCallbackSpy1 = sinon.spy(); @@ -686,56 +509,29 @@ describe('lib/core/notification_center', function() { configUpdateCallbackSpy2 ); // add a listener for each notification type - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy1); // remove only OPTIMIZELY_CONFIG_UPDATE type notificationCenterInstance.clearNotificationListeners(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.ACTIVATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.DECISION, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.LOG_EVENT, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.TRACK, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.DECISION, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.LOG_EVENT, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.TRACK, {}); // check that OPTIMIZELY_CONFIG_UPDATE listeners were not called sinon.assert.notCalled(configUpdateCallbackSpy1); sinon.assert.notCalled(configUpdateCallbackSpy2); - // check that all other listeners were called. + // check that all other listeners were called. sinon.assert.calledOnce(activateCallbackSpy1); sinon.assert.calledOnce(decisionCallbackSpy1); sinon.assert.calledOnce(logEventCallbackSpy1); sinon.assert.calledOnce(trackCallbackSpy1); }); - it('should only remove TRACK type listeners and not any other types', function(){ + it('should only remove TRACK type listeners and not any other types', function() { var trackCallbackSpy1 = sinon.spy(); var trackCallbackSpy2 = sinon.spy(); var activateCallbackSpy1 = sinon.spy(); @@ -743,27 +539,12 @@ describe('lib/core/notification_center', function() { var logEventCallbackSpy1 = sinon.spy(); var configUpdateCallbackSpy1 = sinon.spy(); // add 2 different listeners for TRACK - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy2 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy2); // add a listener for each notification type - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy1); notificationCenterInstance.addNotificationListener( enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, configUpdateCallbackSpy1 @@ -771,30 +552,15 @@ describe('lib/core/notification_center', function() { // remove only TRACK type notificationCenterInstance.clearNotificationListeners(enums.NOTIFICATION_TYPES.TRACK); // trigger send notifications - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.ACTIVATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.DECISION, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.LOG_EVENT, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, - {} - ); - notificationCenterInstance.sendNotifications( - enums.NOTIFICATION_TYPES.TRACK, - {} - ); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.DECISION, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.LOG_EVENT, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, {}); + notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.TRACK, {}); // check that TRACK listeners were not called sinon.assert.notCalled(trackCallbackSpy1); sinon.assert.notCalled(trackCallbackSpy2); - // check that all other listeners were called. + // check that all other listeners were called. sinon.assert.calledOnce(activateCallbackSpy1); sinon.assert.calledOnce(decisionCallbackSpy1); sinon.assert.calledOnce(logEventCallbackSpy1); @@ -804,8 +570,8 @@ describe('lib/core/notification_center', function() { }); describe('#sendNotifications', function() { - context('send notification for each type ', function(){ - it('should call the listener callback with exact arguments', function(){ + context('send notification for each type ', function() { + it('should call the listener callback with exact arguments', function() { var activateCallbackSpy1 = sinon.spy(); var decisionCallbackSpy1 = sinon.spy(); var logEventCallbackSpy1 = sinon.spy(); @@ -814,55 +580,46 @@ describe('lib/core/notification_center', function() { // listener object data for each type var activateData = { experiment: {}, - userId: "", + userId: '', attributes: {}, variation: {}, - logEvent: {} + logEvent: {}, }; var decisionData = { type: '', userId: 'use1', attributes: {}, - decisionInfo: {} + decisionInfo: {}, }; var logEventData = { url: '', httpVerb: '', - params: {} + params: {}, }; var configUpdateData = {}; var trackData = { eventKey: '', userId: '', attributes: {}, - eventTags: {} + eventTags: {}, }; // add listeners - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.ACTIVATE, - activateCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.DECISION, - decisionCallbackSpy1 - ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.LOG_EVENT, - logEventCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.ACTIVATE, activateCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.DECISION, decisionCallbackSpy1); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventCallbackSpy1); notificationCenterInstance.addNotificationListener( enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, configUpdateCallbackSpy1 ); - notificationCenterInstance.addNotificationListener( - enums.NOTIFICATION_TYPES.TRACK, - trackCallbackSpy1 - ); + notificationCenterInstance.addNotificationListener(enums.NOTIFICATION_TYPES.TRACK, trackCallbackSpy1); // send notifications notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.ACTIVATE, activateData); notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.DECISION, decisionData); notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.LOG_EVENT, logEventData); - notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, configUpdateData); + notificationCenterInstance.sendNotifications( + enums.NOTIFICATION_TYPES.OPTIMIZELY_CONFIG_UPDATE, + configUpdateData + ); notificationCenterInstance.sendNotifications(enums.NOTIFICATION_TYPES.TRACK, trackData); // assertions sinon.assert.calledWithExactly(activateCallbackSpy1, activateData); diff --git a/packages/optimizely-sdk/lib/core/optimizely_config/index.js b/packages/optimizely-sdk/lib/core/optimizely_config/index.js index b202f68a0..e30c3d183 100644 --- a/packages/optimizely-sdk/lib/core/optimizely_config/index.js +++ b/packages/optimizely-sdk/lib/core/optimizely_config/index.js @@ -28,7 +28,7 @@ function getRolloutExperimentIds(rollouts) { // Gets Map of all experiments except rollouts function getExperimentsMap(configObj) { var rolloutExperimentIds = getRolloutExperimentIds(configObj.rollouts); - var featureVariablesMap = (configObj.featureFlags || []).reduce(function(resultMap, feature){ + var featureVariablesMap = (configObj.featureFlags || []).reduce(function(resultMap, feature) { resultMap[feature.id] = feature.variables; return resultMap; }, {}); @@ -42,7 +42,7 @@ function getExperimentsMap(configObj) { variations[variation.key] = { id: variation.id, key: variation.key, - variablesMap: getMergedVariablesMap(configObj, variation, experiment.id, featureVariablesMap) + variablesMap: getMergedVariablesMap(configObj, variation, experiment.id, featureVariablesMap), }; if (projectConfig.isFeatureExperiment(configObj, experiment.id)) { variations[variation.key].featureEnabled = variation.featureEnabled; @@ -71,7 +71,8 @@ function getMergedVariablesMap(configObj, variation, experimentId, featureVariab }, {}); variablesObject = (experimentFeatureVariables || []).reduce(function(variablesMap, featureVariable) { var variationVariable = tempVariablesIdMap[featureVariable.id]; - var variableValue = variation.featureEnabled && variationVariable ? variationVariable.value : featureVariable.defaultValue; + var variableValue = + variation.featureEnabled && variationVariable ? variationVariable.value : featureVariable.defaultValue; variablesMap[featureVariable.key] = { id: featureVariable.id, key: featureVariable.key, diff --git a/packages/optimizely-sdk/lib/core/optimizely_config/index.tests.js b/packages/optimizely-sdk/lib/core/optimizely_config/index.tests.js index 2e058e40f..5aedc01ac 100644 --- a/packages/optimizely-sdk/lib/core/optimizely_config/index.tests.js +++ b/packages/optimizely-sdk/lib/core/optimizely_config/index.tests.js @@ -45,7 +45,7 @@ describe('lib/core/optimizely_config', function() { var experimentsMap = optimizelyConfigObject.experimentsMap; var experimentsCount = Object.keys(optimizelyConfigObject.experimentsMap).length; assert.equal(experimentsCount, 6); - + var allExperiments = getAllExperimentsFromDatafile(datafile); allExperiments.forEach(function(experiment) { assert.include(experimentsMap[experiment.key], { @@ -57,15 +57,15 @@ describe('lib/core/optimizely_config', function() { assert.include(variationsMap[variation.key], { id: variation.id, key: variation.key, - }) + }); }); }); }); - + it('should return all the feature flags', function() { var featureFlagsCount = Object.keys(optimizelyConfigObject.featuresMap).length; assert.equal(featureFlagsCount, 7); - + var featuresMap = optimizelyConfigObject.featuresMap; datafile.featureFlags.forEach(function(featureFlag) { assert.include(featuresMap[featureFlag.key], { @@ -83,18 +83,17 @@ describe('lib/core/optimizely_config', function() { key: variable.key, type: variable.type, value: variable.defaultValue, - }) + }); }); }); }); - + it('should correctly merge all feature variables', function() { var featureFlags = datafile.featureFlags; - var datafileExperimentsMap = getAllExperimentsFromDatafile(datafile) - .reduce(function(experiments, experiment) { - experiments[experiment.key] = experiment; - return experiments; - }, {}); + var datafileExperimentsMap = getAllExperimentsFromDatafile(datafile).reduce(function(experiments, experiment) { + experiments[experiment.key] = experiment; + return experiments; + }, {}); featureFlags.forEach(function(featureFlag) { var experimentIds = featureFlag.experimentIds; experimentIds.forEach(function(experimentId) { @@ -111,10 +110,10 @@ describe('lib/core/optimizely_config', function() { type: variableToAssert.type, }); if (!variation.featureEnabled) { - assert.equal(variable.defaultValue, variableToAssert.value); + assert.equal(variable.defaultValue, variableToAssert.value); } - }); - }) + }); + }); }); }); }); diff --git a/packages/optimizely-sdk/lib/core/project_config/index.js b/packages/optimizely-sdk/lib/core/project_config/index.js index 29788e25b..61bf2ad40 100644 --- a/packages/optimizely-sdk/lib/core/project_config/index.js +++ b/packages/optimizely-sdk/lib/core/project_config/index.js @@ -55,12 +55,12 @@ module.exports = { Object.keys(projectConfig.groupIdMap || {}).forEach(function(Id) { experiments = fns.cloneDeep(projectConfig.groupIdMap[Id].experiments); (experiments || []).forEach(function(experiment) { - projectConfig.experiments.push(fns.assign(experiment, {groupId: Id})); + projectConfig.experiments.push(fns.assign(experiment, { groupId: Id })); }); }); projectConfig.rolloutIdMap = fns.keyBy(projectConfig.rollouts || [], 'id'); - jsSdkUtils.objectValues(projectConfig.rolloutIdMap || {}).forEach(function (rollout) { + jsSdkUtils.objectValues(projectConfig.rolloutIdMap || {}).forEach(function(rollout) { (rollout.experiments || []).forEach(function(experiment) { projectConfig.experiments.push(fns.cloneDeep(experiment)); // Creates { : } map inside of the experiment @@ -79,7 +79,7 @@ module.exports = { // Creates { : { key: , id: } } mapping for quick lookup fns.assign(projectConfig.variationIdMap, fns.keyBy(experiment.variations, 'id')); - jsSdkUtils.objectValues(experiment.variationKeyMap || {}).forEach(function (variation) { + jsSdkUtils.objectValues(experiment.variationKeyMap || {}).forEach(function(variation) { if (variation.variables) { projectConfig.variationVariableUsageMap[variation.id] = fns.keyBy(variation.variables, 'id'); } @@ -91,7 +91,7 @@ module.exports = { projectConfig.experimentFeatureMap = {}; projectConfig.featureKeyMap = fns.keyBy(projectConfig.featureFlags || [], 'key'); - jsSdkUtils.objectValues(projectConfig.featureKeyMap || {}).forEach(function (feature) { + jsSdkUtils.objectValues(projectConfig.featureKeyMap || {}).forEach(function(feature) { feature.variableKeyMap = fns.keyBy(feature.variables, 'key'); (feature.experimentIds || []).forEach(function(experimentId) { // Add this experiment in experiment-feature map. @@ -154,8 +154,14 @@ module.exports = { var hasReservedPrefix = attributeKey.indexOf(RESERVED_ATTRIBUTE_PREFIX) === 0; if (attribute) { if (hasReservedPrefix) { - logger.log(LOG_LEVEL.WARN, - jsSdkUtils.sprintf('Attribute %s unexpectedly has reserved prefix %s; using attribute ID instead of reserved attribute name.', attributeKey, RESERVED_ATTRIBUTE_PREFIX)); + logger.log( + LOG_LEVEL.WARN, + jsSdkUtils.sprintf( + 'Attribute %s unexpectedly has reserved prefix %s; using attribute ID instead of reserved attribute name.', + attributeKey, + RESERVED_ATTRIBUTE_PREFIX + ) + ); } return attribute.id; } else if (hasReservedPrefix) { @@ -351,7 +357,10 @@ module.exports = { var variable = feature.variableKeyMap[variableKey]; if (!variable) { - logger.log(LOG_LEVEL.ERROR, jsSdkUtils.sprintf(ERROR_MESSAGES.VARIABLE_KEY_NOT_IN_DATAFILE, MODULE_NAME, variableKey, featureKey)); + logger.log( + LOG_LEVEL.ERROR, + jsSdkUtils.sprintf(ERROR_MESSAGES.VARIABLE_KEY_NOT_IN_DATAFILE, MODULE_NAME, variableKey, featureKey) + ); return null; } @@ -376,7 +385,10 @@ module.exports = { } if (!projectConfig.variationVariableUsageMap.hasOwnProperty(variation.id)) { - logger.log(LOG_LEVEL.ERROR, jsSdkUtils.sprintf(ERROR_MESSAGES.VARIATION_ID_NOT_IN_DATAFILE_NO_EXPERIMENT, MODULE_NAME, variation.id)); + logger.log( + LOG_LEVEL.ERROR, + jsSdkUtils.sprintf(ERROR_MESSAGES.VARIATION_ID_NOT_IN_DATAFILE_NO_EXPERIMENT, MODULE_NAME, variation.id) + ); return null; } @@ -408,7 +420,10 @@ module.exports = { switch (variableType) { case FEATURE_VARIABLE_TYPES.BOOLEAN: if (variableValue !== 'true' && variableValue !== 'false') { - logger.log(LOG_LEVEL.ERROR, jsSdkUtils.sprintf(ERROR_MESSAGES.UNABLE_TO_CAST_VALUE, MODULE_NAME, variableValue, variableType)); + logger.log( + LOG_LEVEL.ERROR, + jsSdkUtils.sprintf(ERROR_MESSAGES.UNABLE_TO_CAST_VALUE, MODULE_NAME, variableValue, variableType) + ); castValue = null; } else { castValue = variableValue === 'true'; @@ -418,7 +433,10 @@ module.exports = { case FEATURE_VARIABLE_TYPES.INTEGER: castValue = parseInt(variableValue, 10); if (isNaN(castValue)) { - logger.log(LOG_LEVEL.ERROR, jsSdkUtils.sprintf(ERROR_MESSAGES.UNABLE_TO_CAST_VALUE, MODULE_NAME, variableValue, variableType)); + logger.log( + LOG_LEVEL.ERROR, + jsSdkUtils.sprintf(ERROR_MESSAGES.UNABLE_TO_CAST_VALUE, MODULE_NAME, variableValue, variableType) + ); castValue = null; } break; @@ -426,12 +444,16 @@ module.exports = { case FEATURE_VARIABLE_TYPES.DOUBLE: castValue = parseFloat(variableValue); if (isNaN(castValue)) { - logger.log(LOG_LEVEL.ERROR, jsSdkUtils.sprintf(ERROR_MESSAGES.UNABLE_TO_CAST_VALUE, MODULE_NAME, variableValue, variableType)); + logger.log( + LOG_LEVEL.ERROR, + jsSdkUtils.sprintf(ERROR_MESSAGES.UNABLE_TO_CAST_VALUE, MODULE_NAME, variableValue, variableType) + ); castValue = null; } break; - default: // type is STRING + default: + // type is STRING castValue = variableValue; break; } diff --git a/packages/optimizely-sdk/lib/core/project_config/index.tests.js b/packages/optimizely-sdk/lib/core/project_config/index.tests.js index 9a9276639..64e5b73dc 100644 --- a/packages/optimizely-sdk/lib/core/project_config/index.tests.js +++ b/packages/optimizely-sdk/lib/core/project_config/index.tests.js @@ -122,20 +122,34 @@ describe('lib/core/project_config', function() { assert.deepEqual(configObj.experimentIdMap, expectedExperimentIdMap); var expectedVariationKeyMap = {}; - expectedVariationKeyMap[testData.experiments[0].key + testData.experiments[0].variations[0].key] = testData.experiments[0].variations[0]; - expectedVariationKeyMap[testData.experiments[0].key + testData.experiments[0].variations[1].key] = testData.experiments[0].variations[1]; - expectedVariationKeyMap[testData.experiments[1].key + testData.experiments[1].variations[0].key] = testData.experiments[1].variations[0]; - expectedVariationKeyMap[testData.experiments[1].key + testData.experiments[1].variations[1].key] = testData.experiments[1].variations[1]; - expectedVariationKeyMap[testData.experiments[2].key + testData.experiments[2].variations[0].key] = testData.experiments[2].variations[0]; - expectedVariationKeyMap[testData.experiments[2].key + testData.experiments[2].variations[1].key] = testData.experiments[2].variations[1]; - expectedVariationKeyMap[configObj.experiments[3].key + configObj.experiments[3].variations[0].key] = configObj.experiments[3].variations[0]; - expectedVariationKeyMap[configObj.experiments[3].key + configObj.experiments[3].variations[1].key] = configObj.experiments[3].variations[1]; - expectedVariationKeyMap[configObj.experiments[4].key + configObj.experiments[4].variations[0].key] = configObj.experiments[4].variations[0]; - expectedVariationKeyMap[configObj.experiments[4].key + configObj.experiments[4].variations[1].key] = configObj.experiments[4].variations[1]; - expectedVariationKeyMap[configObj.experiments[5].key + configObj.experiments[5].variations[0].key] = configObj.experiments[5].variations[0]; - expectedVariationKeyMap[configObj.experiments[5].key + configObj.experiments[5].variations[1].key] = configObj.experiments[5].variations[1]; - expectedVariationKeyMap[configObj.experiments[6].key + configObj.experiments[6].variations[0].key] = configObj.experiments[6].variations[0]; - expectedVariationKeyMap[configObj.experiments[6].key + configObj.experiments[6].variations[1].key] = configObj.experiments[6].variations[1]; + expectedVariationKeyMap[testData.experiments[0].key + testData.experiments[0].variations[0].key] = + testData.experiments[0].variations[0]; + expectedVariationKeyMap[testData.experiments[0].key + testData.experiments[0].variations[1].key] = + testData.experiments[0].variations[1]; + expectedVariationKeyMap[testData.experiments[1].key + testData.experiments[1].variations[0].key] = + testData.experiments[1].variations[0]; + expectedVariationKeyMap[testData.experiments[1].key + testData.experiments[1].variations[1].key] = + testData.experiments[1].variations[1]; + expectedVariationKeyMap[testData.experiments[2].key + testData.experiments[2].variations[0].key] = + testData.experiments[2].variations[0]; + expectedVariationKeyMap[testData.experiments[2].key + testData.experiments[2].variations[1].key] = + testData.experiments[2].variations[1]; + expectedVariationKeyMap[configObj.experiments[3].key + configObj.experiments[3].variations[0].key] = + configObj.experiments[3].variations[0]; + expectedVariationKeyMap[configObj.experiments[3].key + configObj.experiments[3].variations[1].key] = + configObj.experiments[3].variations[1]; + expectedVariationKeyMap[configObj.experiments[4].key + configObj.experiments[4].variations[0].key] = + configObj.experiments[4].variations[0]; + expectedVariationKeyMap[configObj.experiments[4].key + configObj.experiments[4].variations[1].key] = + configObj.experiments[4].variations[1]; + expectedVariationKeyMap[configObj.experiments[5].key + configObj.experiments[5].variations[0].key] = + configObj.experiments[5].variations[0]; + expectedVariationKeyMap[configObj.experiments[5].key + configObj.experiments[5].variations[1].key] = + configObj.experiments[5].variations[1]; + expectedVariationKeyMap[configObj.experiments[6].key + configObj.experiments[6].variations[0].key] = + configObj.experiments[6].variations[0]; + expectedVariationKeyMap[configObj.experiments[6].key + configObj.experiments[6].variations[1].key] = + configObj.experiments[6].variations[1]; var expectedVariationIdMap = { '111128': testData.experiments[0].variations[0], @@ -168,7 +182,10 @@ describe('lib/core/project_config', function() { }); it('creates a variationVariableUsageMap from rollouts and experiments with features in the datafile', function() { - assert.deepEqual(configObj.variationVariableUsageMap, testDatafile.datafileWithFeaturesExpectedData.variationVariableUsageMap); + assert.deepEqual( + configObj.variationVariableUsageMap, + testDatafile.datafileWithFeaturesExpectedData.variationVariableUsageMap + ); }); it('creates a featureKeyMap from feature flags in the datafile', function() { @@ -177,48 +194,48 @@ describe('lib/core/project_config', function() { it('adds variations from rollout experiments to variationIdMap', function() { assert.deepEqual(configObj.variationIdMap['594032'], { - 'variables': [ - { 'value': 'true', 'id': '4919852825313280' }, - { 'value': '395', 'id': '5482802778734592' }, - { 'value': '4.99', 'id': '6045752732155904' }, - { 'value': 'Hello audience', 'id': '6327227708866560' } + variables: [ + { value: 'true', id: '4919852825313280' }, + { value: '395', id: '5482802778734592' }, + { value: '4.99', id: '6045752732155904' }, + { value: 'Hello audience', id: '6327227708866560' }, ], - 'featureEnabled': true, - 'key': '594032', - 'id': '594032' + featureEnabled: true, + key: '594032', + id: '594032', }); assert.deepEqual(configObj.variationIdMap['594038'], { - 'variables': [ - { 'value': 'false', 'id': '4919852825313280' }, - { 'value': '400', 'id': '5482802778734592' }, - { 'value': '14.99', 'id': '6045752732155904' }, - { 'value': 'Hello', 'id': '6327227708866560' }, + variables: [ + { value: 'false', id: '4919852825313280' }, + { value: '400', id: '5482802778734592' }, + { value: '14.99', id: '6045752732155904' }, + { value: 'Hello', id: '6327227708866560' }, ], - 'featureEnabled': false, - 'key': '594038', - 'id': '594038' + featureEnabled: false, + key: '594038', + id: '594038', }); assert.deepEqual(configObj.variationIdMap['594061'], { - 'variables': [ - { 'value': '27.34', 'id': '5060590313668608' }, - { 'value': 'Winter is NOT coming', 'id': '5342065290379264' }, - { 'value': '10003', 'id': '6186490220511232' }, - { 'value': 'false', 'id': '6467965197221888' }, + variables: [ + { value: '27.34', id: '5060590313668608' }, + { value: 'Winter is NOT coming', id: '5342065290379264' }, + { value: '10003', id: '6186490220511232' }, + { value: 'false', id: '6467965197221888' }, ], - 'featureEnabled': true, - 'key': '594061', - 'id': '594061' + featureEnabled: true, + key: '594061', + id: '594061', }); assert.deepEqual(configObj.variationIdMap['594067'], { - 'variables': [ - { 'value': '30.34', 'id': '5060590313668608' }, - { 'value': 'Winter is coming definitely', 'id': '5342065290379264' }, - { 'value': '500', 'id': '6186490220511232' }, - { 'value': 'true', 'id': '6467965197221888' } + variables: [ + { value: '30.34', id: '5060590313668608' }, + { value: 'Winter is coming definitely', id: '5342065290379264' }, + { value: '500', id: '6186490220511232' }, + { value: 'true', id: '6467965197221888' }, ], - 'featureEnabled': true, - 'key': '594067', - 'id': '594067', + featureEnabled: true, + key: '594067', + id: '594067', }); }); }); @@ -227,7 +244,7 @@ describe('lib/core/project_config', function() { describe('projectConfig helper methods', function() { var testData = testDatafile.getTestProjectConfig(); var configObj; - var createdLogger = loggerPlugin.createLogger({logLevel: LOG_LEVEL.INFO}); + var createdLogger = loggerPlugin.createLogger({ logLevel: LOG_LEVEL.INFO }); beforeEach(function() { configObj = projectConfig.createProjectConfig(testData); @@ -239,8 +256,10 @@ describe('lib/core/project_config', function() { }); it('should retrieve experiment ID for valid experiment key in getExperimentId', function() { - assert.strictEqual(projectConfig.getExperimentId(configObj, testData.experiments[0].key), - testData.experiments[0].id); + assert.strictEqual( + projectConfig.getExperimentId(configObj, testData.experiments[0].key), + testData.experiments[0].id + ); }); it('should throw error for invalid experiment key in getExperimentId', function() { @@ -269,21 +288,25 @@ describe('lib/core/project_config', function() { it('should return null for invalid attribute key in getAttributeId', function() { assert.isNull(projectConfig.getAttributeId(configObj, 'invalidAttributeKey', createdLogger)); - sinon.assert.calledWithExactly(createdLogger.log, - LOG_LEVEL.DEBUG, - 'PROJECT_CONFIG: Unrecognized attribute invalidAttributeKey provided. Pruning before sending event to Optimizely.'); + sinon.assert.calledWithExactly( + createdLogger.log, + LOG_LEVEL.DEBUG, + 'PROJECT_CONFIG: Unrecognized attribute invalidAttributeKey provided. Pruning before sending event to Optimizely.' + ); }); it('should return null for invalid attribute key in getAttributeId', function() { // Adding attribute in key map with reserved prefix configObj.attributeKeyMap['$opt_some_reserved_attribute'] = { id: '42', - key: '$opt_some_reserved_attribute' + key: '$opt_some_reserved_attribute', }; assert.strictEqual(projectConfig.getAttributeId(configObj, '$opt_some_reserved_attribute', createdLogger), '42'); - sinon.assert.calledWithExactly(createdLogger.log, - LOG_LEVEL.WARN, - 'Attribute $opt_some_reserved_attribute unexpectedly has reserved prefix $opt_; using attribute ID instead of reserved attribute name.'); + sinon.assert.calledWithExactly( + createdLogger.log, + LOG_LEVEL.WARN, + 'Attribute $opt_some_reserved_attribute unexpectedly has reserved prefix $opt_; using attribute ID instead of reserved attribute name.' + ); }); it('should retrieve event ID for valid event key in getEventId', function() { @@ -295,8 +318,10 @@ describe('lib/core/project_config', function() { }); it('should retrieve experiment status for valid experiment key in getExperimentStatus', function() { - assert.strictEqual(projectConfig.getExperimentStatus(configObj, testData.experiments[0].key), - testData.experiments[0].status); + assert.strictEqual( + projectConfig.getExperimentStatus(configObj, testData.experiments[0].key), + testData.experiments[0].status + ); }); it('should throw error for invalid experiment key in getExperimentStatus', function() { @@ -322,14 +347,17 @@ describe('lib/core/project_config', function() { }); it('should retrieve variation key for valid experiment key and variation ID in getVariationKeyFromId', function() { - assert.deepEqual(projectConfig.getVariationKeyFromId(configObj, - testData.experiments[0].variations[0].id), - testData.experiments[0].variations[0].key); + assert.deepEqual( + projectConfig.getVariationKeyFromId(configObj, testData.experiments[0].variations[0].id), + testData.experiments[0].variations[0].key + ); }); it('should retrieve traffic allocation given valid experiment key in getTrafficAllocation', function() { - assert.deepEqual(projectConfig.getTrafficAllocation(configObj, testData.experiments[0].key), - testData.experiments[0].trafficAllocation); + assert.deepEqual( + projectConfig.getTrafficAllocation(configObj, testData.experiments[0].key), + testData.experiments[0].trafficAllocation + ); }); it('should throw error for invalid experient key in getTrafficAllocation', function() { @@ -352,7 +380,7 @@ describe('lib/core/project_config', function() { }); describe('feature management', function() { - var featureManagementLogger = loggerPlugin.createLogger({logLevel: LOG_LEVEL.INFO}); + var featureManagementLogger = loggerPlugin.createLogger({ logLevel: LOG_LEVEL.INFO }); beforeEach(function() { configObj = projectConfig.createProjectConfig(testDatafile.getTestProjectConfigWithFeatures()); sinon.stub(featureManagementLogger, 'log'); @@ -381,7 +409,11 @@ describe('lib/core/project_config', function() { var result = projectConfig.getVariableForFeature(configObj, featureKey, variableKey, featureManagementLogger); assert.strictEqual(result, null); sinon.assert.calledOnce(featureManagementLogger.log); - sinon.assert.calledWithExactly(featureManagementLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Variable with key "notARealVariable____" associated with feature with key "test_feature_for_experiment" is not in datafile.'); + sinon.assert.calledWithExactly( + featureManagementLogger.log, + LOG_LEVEL.ERROR, + 'PROJECT_CONFIG: Variable with key "notARealVariable____" associated with feature with key "test_feature_for_experiment" is not in datafile.' + ); }); it('should return null for an invalid feature key', function() { @@ -390,7 +422,11 @@ describe('lib/core/project_config', function() { var result = projectConfig.getVariableForFeature(configObj, featureKey, variableKey, featureManagementLogger); assert.strictEqual(result, null); sinon.assert.calledOnce(featureManagementLogger.log); - sinon.assert.calledWithExactly(featureManagementLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Feature key notARealFeature_____ is not in datafile.'); + sinon.assert.calledWithExactly( + featureManagementLogger.log, + LOG_LEVEL.ERROR, + 'PROJECT_CONFIG: Feature key notARealFeature_____ is not in datafile.' + ); }); it('should return null for an invalid variable key and an invalid feature key', function() { @@ -399,7 +435,11 @@ describe('lib/core/project_config', function() { var result = projectConfig.getVariableForFeature(configObj, featureKey, variableKey, featureManagementLogger); assert.strictEqual(result, null); sinon.assert.calledOnce(featureManagementLogger.log); - sinon.assert.calledWithExactly(featureManagementLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Feature key notARealFeature_____ is not in datafile.'); + sinon.assert.calledWithExactly( + featureManagementLogger.log, + LOG_LEVEL.ERROR, + 'PROJECT_CONFIG: Feature key notARealFeature_____ is not in datafile.' + ); }); }); @@ -407,7 +447,12 @@ describe('lib/core/project_config', function() { it('returns a value for a valid variation and variable', function() { var variation = configObj.variationIdMap['594096']; var variable = configObj.featureKeyMap.test_feature_for_experiment.variableKeyMap.num_buttons; - var result = projectConfig.getVariableValueForVariation(configObj, variable, variation, featureManagementLogger); + var result = projectConfig.getVariableValueForVariation( + configObj, + variable, + variation, + featureManagementLogger + ); assert.strictEqual(result, '2'); variable = configObj.featureKeyMap.test_feature_for_experiment.variableKeyMap.is_button_animated; @@ -426,21 +471,36 @@ describe('lib/core/project_config', function() { it('returns null for a null variation', function() { var variation = null; var variable = configObj.featureKeyMap.test_feature_for_experiment.variableKeyMap.num_buttons; - var result = projectConfig.getVariableValueForVariation(configObj, variable, variation, featureManagementLogger); + var result = projectConfig.getVariableValueForVariation( + configObj, + variable, + variation, + featureManagementLogger + ); assert.strictEqual(result, null); }); it('returns null for a null variable', function() { var variation = configObj.variationIdMap['594096']; var variable = null; - var result = projectConfig.getVariableValueForVariation(configObj, variable, variation, featureManagementLogger); + var result = projectConfig.getVariableValueForVariation( + configObj, + variable, + variation, + featureManagementLogger + ); assert.strictEqual(result, null); }); it('returns null for a null variation and null variable', function() { var variation = null; var variable = null; - var result = projectConfig.getVariableValueForVariation(configObj, variable, variation, featureManagementLogger); + var result = projectConfig.getVariableValueForVariation( + configObj, + variable, + variation, + featureManagementLogger + ); assert.strictEqual(result, null); }); @@ -451,14 +511,24 @@ describe('lib/core/project_config', function() { variables: [], }; var variable = configObj.featureKeyMap.test_feature_for_experiment.variableKeyMap.num_buttons; - var result = projectConfig.getVariableValueForVariation(configObj, variable, variation, featureManagementLogger); + var result = projectConfig.getVariableValueForVariation( + configObj, + variable, + variation, + featureManagementLogger + ); assert.strictEqual(result, null); }); it('returns null if the variation does not have a value for this variable', function() { var variation = configObj.variationIdMap['595008']; // This variation has no variable values associated with it var variable = configObj.featureKeyMap.test_feature_for_experiment.variableKeyMap.num_buttons; - var result = projectConfig.getVariableValueForVariation(configObj, variable, variation, featureManagementLogger); + var result = projectConfig.getVariableValueForVariation( + configObj, + variable, + variation, + featureManagementLogger + ); assert.isNull(result); }); }); @@ -483,7 +553,11 @@ describe('lib/core/project_config', function() { it('can cast a double', function() { var result = projectConfig.getTypeCastValue('89.99', FEATURE_VARIABLE_TYPES.DOUBLE, featureManagementLogger); assert.strictEqual(result, 89.99); - var result = projectConfig.getTypeCastValue('-257.21', FEATURE_VARIABLE_TYPES.DOUBLE, featureManagementLogger); + var result = projectConfig.getTypeCastValue( + '-257.21', + FEATURE_VARIABLE_TYPES.DOUBLE, + featureManagementLogger + ); assert.strictEqual(result, -257.21); var result = projectConfig.getTypeCastValue('0', FEATURE_VARIABLE_TYPES.DOUBLE, featureManagementLogger); assert.strictEqual(result, 0); @@ -492,26 +566,54 @@ describe('lib/core/project_config', function() { }); it('can return a string unmodified', function() { - var result = projectConfig.getTypeCastValue('message', FEATURE_VARIABLE_TYPES.STRING, featureManagementLogger); + var result = projectConfig.getTypeCastValue( + 'message', + FEATURE_VARIABLE_TYPES.STRING, + featureManagementLogger + ); assert.strictEqual(result, 'message'); }); it('returns null and logs an error for an invalid boolean', function() { - var result = projectConfig.getTypeCastValue('notabool', FEATURE_VARIABLE_TYPES.BOOLEAN, featureManagementLogger); + var result = projectConfig.getTypeCastValue( + 'notabool', + FEATURE_VARIABLE_TYPES.BOOLEAN, + featureManagementLogger + ); assert.strictEqual(result, null); - sinon.assert.calledWithExactly(featureManagementLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value notabool to type boolean, returning null.'); + sinon.assert.calledWithExactly( + featureManagementLogger.log, + LOG_LEVEL.ERROR, + 'PROJECT_CONFIG: Unable to cast value notabool to type boolean, returning null.' + ); }); it('returns null and logs an error for an invalid integer', function() { - var result = projectConfig.getTypeCastValue('notanint', FEATURE_VARIABLE_TYPES.INTEGER, featureManagementLogger); + var result = projectConfig.getTypeCastValue( + 'notanint', + FEATURE_VARIABLE_TYPES.INTEGER, + featureManagementLogger + ); assert.strictEqual(result, null); - sinon.assert.calledWithExactly(featureManagementLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value notanint to type integer, returning null.'); + sinon.assert.calledWithExactly( + featureManagementLogger.log, + LOG_LEVEL.ERROR, + 'PROJECT_CONFIG: Unable to cast value notanint to type integer, returning null.' + ); }); it('returns null and logs an error for an invalid double', function() { - var result = projectConfig.getTypeCastValue('notadouble', FEATURE_VARIABLE_TYPES.DOUBLE, featureManagementLogger); + var result = projectConfig.getTypeCastValue( + 'notadouble', + FEATURE_VARIABLE_TYPES.DOUBLE, + featureManagementLogger + ); assert.strictEqual(result, null); - sinon.assert.calledWithExactly(featureManagementLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value notadouble to type double, returning null.'); + sinon.assert.calledWithExactly( + featureManagementLogger.log, + LOG_LEVEL.ERROR, + 'PROJECT_CONFIG: Unable to cast value notadouble to type double, returning null.' + ); }); }); }); @@ -522,18 +624,16 @@ describe('lib/core/project_config', function() { }); it('should retrieve audiences by checking first in typedAudiences, and then second in audiences', function() { - assert.deepEqual( - projectConfig.getAudiencesById(configObj), - testDatafile.typedAudiencesById - ); + assert.deepEqual(projectConfig.getAudiencesById(configObj), testDatafile.typedAudiencesById); }); }); describe('#getExperimentAudienceConditions', function() { it('should retrieve audiences for valid experiment key', function() { configObj = projectConfig.createProjectConfig(testData); - assert.deepEqual(projectConfig.getExperimentAudienceConditions(configObj, testData.experiments[1].key), - ['11154']); + assert.deepEqual(projectConfig.getExperimentAudienceConditions(configObj, testData.experiments[1].key), [ + '11154', + ]); }); it('should throw error for invalid experiment key', function() { @@ -546,7 +646,15 @@ describe('lib/core/project_config', function() { it('should return experiment audienceIds if experiment has no audienceConditions', function() { configObj = projectConfig.createProjectConfig(testDatafile.getTypedAudiencesConfig()); var result = projectConfig.getExperimentAudienceConditions(configObj, 'feat_with_var_test'); - assert.deepEqual(result, ['3468206642', '3988293898', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643']); + assert.deepEqual(result, [ + '3468206642', + '3988293898', + '3988293899', + '3468206646', + '3468206647', + '3468206644', + '3468206643', + ]); }); it('should return experiment audienceConditions if experiment has audienceConditions', function() { @@ -554,7 +662,11 @@ describe('lib/core/project_config', function() { // audience_combinations_experiment has both audienceConditions and audienceIds // audienceConditions should be preferred over audienceIds var result = projectConfig.getExperimentAudienceConditions(configObj, 'audience_combinations_experiment'); - assert.deepEqual(result, ['and', ['or', '3468206642', '3988293898'], ['or', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643']]); + assert.deepEqual(result, [ + 'and', + ['or', '3468206642', '3988293898'], + ['or', '3988293899', '3468206646', '3468206647', '3468206644', '3468206643'], + ]); }); }); @@ -606,7 +718,7 @@ describe('lib/core/project_config', function() { experimentKeyMap: { a: { key: 'a' }, b: { key: 'b' }, - } + }, }; projectConfig.createProjectConfig.returns(configObj); var result = projectConfig.tryCreatingProjectConfig({ @@ -661,7 +773,7 @@ describe('lib/core/project_config', function() { experimentKeyMap: { a: { key: 'a' }, b: { key: 'b' }, - } + }, }; projectConfig.createProjectConfig.returns(configObj); var result = projectConfig.tryCreatingProjectConfig({ diff --git a/packages/optimizely-sdk/lib/core/project_config/project_config_manager.js b/packages/optimizely-sdk/lib/core/project_config/project_config_manager.js index 4f53817ea..795c2a915 100644 --- a/packages/optimizely-sdk/lib/core/project_config/project_config_manager.js +++ b/packages/optimizely-sdk/lib/core/project_config/project_config_manager.js @@ -130,10 +130,9 @@ ProjectConfigManager.prototype.__initialize = function(config) { } this.datafileManager = new datafileManager.HttpPollingDatafileManager(datafileManagerConfig); this.datafileManager.start(); - this.__readyPromise = this.datafileManager.onReady().then( - this.__onDatafileManagerReadyFulfill.bind(this), - this.__onDatafileManagerReadyReject.bind(this) - ); + this.__readyPromise = this.datafileManager + .onReady() + .then(this.__onDatafileManagerReadyFulfill.bind(this), this.__onDatafileManagerReadyReject.bind(this)); this.datafileManager.on('update', this.__onDatafileManagerUpdate.bind(this)); } else if (this.__configObj) { this.__readyPromise = Promise.resolve({ @@ -286,7 +285,7 @@ ProjectConfigManager.prototype.__handleNewConfigObj = function(newConfigObj) { */ ProjectConfigManager.prototype.getConfig = function() { return this.__configObj; -} +}; /** * Returns the optimizely config object diff --git a/packages/optimizely-sdk/lib/core/project_config/project_config_manager.tests.js b/packages/optimizely-sdk/lib/core/project_config/project_config_manager.tests.js index fe43ecf55..ba5a0d445 100644 --- a/packages/optimizely-sdk/lib/core/project_config/project_config_manager.tests.js +++ b/packages/optimizely-sdk/lib/core/project_config/project_config_manager.tests.js @@ -95,7 +95,10 @@ describe('lib/core/project_config/project_config_manager', function() { }); sinon.assert.calledOnce(globalStubErrorHandler.handleError); var errorMessage = globalStubErrorHandler.handleError.lastCall.args[0].message; - assert.strictEqual(errorMessage, sprintf(ERROR_MESSAGES.INVALID_DATAFILE, 'JSON_SCHEMA_VALIDATOR', 'projectId', 'is missing and it is required')); + assert.strictEqual( + errorMessage, + sprintf(ERROR_MESSAGES.INVALID_DATAFILE, 'JSON_SCHEMA_VALIDATOR', 'projectId', 'is missing and it is required') + ); return manager.onReady().then(function(result) { assert.include(result, { success: false, @@ -155,10 +158,7 @@ describe('lib/core/project_config/project_config_manager', function() { var manager = new projectConfigManager.ProjectConfigManager({ datafile: configWithFeatures, }); - assert.deepEqual( - manager.getConfig(), - projectConfig.createProjectConfig(configWithFeatures) - ); + assert.deepEqual(manager.getConfig(), projectConfig.createProjectConfig(configWithFeatures)); return manager.onReady().then(function(result) { assert.include(result, { success: true, @@ -189,12 +189,15 @@ describe('lib/core/project_config/project_config_manager', function() { }, }); sinon.assert.calledOnce(datafileManager.HttpPollingDatafileManager); - sinon.assert.calledWithExactly(datafileManager.HttpPollingDatafileManager, sinon.match({ - datafile: testData.getTestProjectConfig(), - sdkKey: '12345', - autoUpdate: true, - updateInterval: 10000, - })); + sinon.assert.calledWithExactly( + datafileManager.HttpPollingDatafileManager, + sinon.match({ + datafile: testData.getTestProjectConfig(), + sdkKey: '12345', + autoUpdate: true, + updateInterval: 10000, + }) + ); }); describe('when constructed with sdkKey and without datafile', function() { @@ -205,7 +208,7 @@ describe('lib/core/project_config/project_config_manager', function() { stop: sinon.stub(), get: sinon.stub().returns(configWithFeatures), on: sinon.stub().returns(function() {}), - onReady: sinon.stub().returns(Promise.resolve()) + onReady: sinon.stub().returns(Promise.resolve()), }); var manager = new projectConfigManager.ProjectConfigManager({ sdkKey: '12345', @@ -215,10 +218,7 @@ describe('lib/core/project_config/project_config_manager', function() { assert.include(result, { success: true, }); - assert.deepEqual( - manager.getConfig(), - projectConfig.createProjectConfig(configWithFeatures) - ); + assert.deepEqual(manager.getConfig(), projectConfig.createProjectConfig(configWithFeatures)); var nextDatafile = testData.getTestProjectConfigWithFeatures(); nextDatafile.experiments.push({ @@ -236,10 +236,7 @@ describe('lib/core/project_config/project_config_manager', function() { fakeDatafileManager.get.returns(nextDatafile); var updateListener = fakeDatafileManager.on.getCall(0).args[1]; updateListener({ datafile: nextDatafile }); - assert.deepEqual( - manager.getConfig(), - projectConfig.createProjectConfig(nextDatafile) - ); + assert.deepEqual(manager.getConfig(), projectConfig.createProjectConfig(nextDatafile)); }); }); @@ -249,7 +246,7 @@ describe('lib/core/project_config/project_config_manager', function() { stop: sinon.stub(), get: sinon.stub().returns(testData.getTestProjectConfigWithFeatures()), on: sinon.stub().returns(function() {}), - onReady: sinon.stub().returns(Promise.resolve()) + onReady: sinon.stub().returns(Promise.resolve()), }); var manager = new projectConfigManager.ProjectConfigManager({ sdkKey: '12345', @@ -276,7 +273,7 @@ describe('lib/core/project_config/project_config_manager', function() { stop: sinon.stub(), get: sinon.stub().returns(testData.getTestProjectConfigWithFeatures()), on: sinon.stub().returns(function() {}), - onReady: sinon.stub().returns(Promise.resolve()) + onReady: sinon.stub().returns(Promise.resolve()), }); var manager = new projectConfigManager.ProjectConfigManager({ sdkKey: '12345', @@ -314,7 +311,7 @@ describe('lib/core/project_config/project_config_manager', function() { stop: sinon.stub(), get: sinon.stub().returns(invalidDatafile), on: sinon.stub().returns(function() {}), - onReady: sinon.stub().returns(Promise.resolve()) + onReady: sinon.stub().returns(Promise.resolve()), }); var manager = new projectConfigManager.ProjectConfigManager({ jsonSchemaValidator: jsonSchemaValidator, @@ -333,7 +330,7 @@ describe('lib/core/project_config/project_config_manager', function() { stop: sinon.stub(), get: sinon.stub().returns(null), on: sinon.stub().returns(function() {}), - onReady: sinon.stub().returns(Promise.reject(new Error('Failed to become ready'))) + onReady: sinon.stub().returns(Promise.reject(new Error('Failed to become ready'))), }); var manager = new projectConfigManager.ProjectConfigManager({ jsonSchemaValidator: jsonSchemaValidator, @@ -362,7 +359,7 @@ describe('lib/core/project_config/project_config_manager', function() { stop: sinon.stub(), get: sinon.stub().returns(testData.getTestProjectConfigWithFeatures()), on: sinon.stub().returns(function() {}), - onReady: sinon.stub().returns(Promise.resolve()) + onReady: sinon.stub().returns(Promise.resolve()), }); var configWithFeatures = testData.getTestProjectConfigWithFeatures(); var manager = new projectConfigManager.ProjectConfigManager({ @@ -389,7 +386,7 @@ describe('lib/core/project_config/project_config_manager', function() { stop: sinon.stub(), get: sinon.stub().returns(testData.getTestProjectConfigWithFeatures()), on: sinon.stub().returns(function() {}), - onReady: sinon.stub().returns(Promise.resolve()) + onReady: sinon.stub().returns(Promise.resolve()), }); var configWithFeatures = testData.getTestProjectConfigWithFeatures(); var manager = new projectConfigManager.ProjectConfigManager({ @@ -411,7 +408,7 @@ describe('lib/core/project_config/project_config_manager', function() { describe('test caching of optimizely config', function() { beforeEach(function() { - sinon.stub(optimizelyConfig, "getOptimizelyConfig"); + sinon.stub(optimizelyConfig, 'getOptimizelyConfig'); }); afterEach(function() { diff --git a/packages/optimizely-sdk/lib/core/project_config/project_config_schema.js b/packages/optimizely-sdk/lib/core/project_config/project_config_schema.js index a2e96ef6a..ecea71159 100644 --- a/packages/optimizely-sdk/lib/core/project_config/project_config_schema.js +++ b/packages/optimizely-sdk/lib/core/project_config/project_config_schema.js @@ -19,260 +19,260 @@ * Project Config JSON Schema file used to validate the project json datafile */ module.exports = { - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "projectId": { - "type": "string", - "required": true + $schema: 'http://json-schema.org/draft-04/schema#', + type: 'object', + properties: { + projectId: { + type: 'string', + required: true, }, - "accountId": { - "type": "string", - "required": true + accountId: { + type: 'string', + required: true, }, - "groups": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "required": true + groups: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + required: true, }, - "policy": { - "type": "string", - "required": true + policy: { + type: 'string', + required: true, }, - "trafficAllocation": { - "type": "array", - "items": { - "type": "object", - "properties": { - "entityId": { - "type": "string", - "required": true + trafficAllocation: { + type: 'array', + items: { + type: 'object', + properties: { + entityId: { + type: 'string', + required: true, }, - "endOfRange": { - "type": "integer", - "required": true - } - } + endOfRange: { + type: 'integer', + required: true, + }, + }, }, - "required": true + required: true, }, - "experiments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "required": true + experiments: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + required: true, }, - "key": { - "type": "string", - "required": true + key: { + type: 'string', + required: true, }, - "status": { - "type": "string", - "required": true + status: { + type: 'string', + required: true, }, - "layerId": { - "type": "string", - "required": true + layerId: { + type: 'string', + required: true, }, - "variations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "required": true + variations: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + required: true, + }, + key: { + type: 'string', + required: true, }, - "key": { - "type": "string", - "required": true - } - } + }, }, - "required": true + required: true, }, - "trafficAllocation": { - "type": "array", - "items": { - "type": "object", - "properties": { - "entityId": { - "type": "string", - "required": true + trafficAllocation: { + type: 'array', + items: { + type: 'object', + properties: { + entityId: { + type: 'string', + required: true, + }, + endOfRange: { + type: 'integer', + required: true, }, - "endOfRange": { - "type": "integer", - "required": true - } - } + }, }, - "required": true + required: true, }, - "audienceIds": { - "type": "array", - "items": { - "type": "string" + audienceIds: { + type: 'array', + items: { + type: 'string', }, - "required": true + required: true, }, - "forcedVariations": { - "type": "object", - "required": true - } - } + forcedVariations: { + type: 'object', + required: true, + }, + }, }, - "required": true - } - } + required: true, + }, + }, }, - "required": true + required: true, }, - "experiments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "required": true + experiments: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + required: true, }, - "key": { - "type": "string", - "required": true + key: { + type: 'string', + required: true, }, - "status": { - "type": "string", - "required": true + status: { + type: 'string', + required: true, }, - "layerId": { - "type": "string", - "required": true + layerId: { + type: 'string', + required: true, }, - "variations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "required": true + variations: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + required: true, + }, + key: { + type: 'string', + required: true, }, - "key": { - "type": "string", - "required": true - } - } + }, }, - "required": true + required: true, }, - "trafficAllocation": { - "type": "array", - "items": { - "type": "object", - "properties": { - "entityId": { - "type": "string", - "required": true + trafficAllocation: { + type: 'array', + items: { + type: 'object', + properties: { + entityId: { + type: 'string', + required: true, }, - "endOfRange": { - "type": "integer", - "required": true - } - } + endOfRange: { + type: 'integer', + required: true, + }, + }, }, - "required": true + required: true, }, - "audienceIds": { - "type": "array", - "items": { - "type": "string" + audienceIds: { + type: 'array', + items: { + type: 'string', }, - "required": true + required: true, + }, + forcedVariations: { + type: 'object', + required: true, }, - "forcedVariations": { - "type": "object", - "required": true - } - } + }, }, - "required": true + required: true, }, - "events": { - "type": "array", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string", - "required": true + events: { + type: 'array', + items: { + type: 'object', + properties: { + key: { + type: 'string', + required: true, }, - "experimentIds": { - "type": "array", - "items": { - "type": "string", - "required": true - } + experimentIds: { + type: 'array', + items: { + type: 'string', + required: true, + }, }, - "id": { - "type": "string", - "required": true - } - } + id: { + type: 'string', + required: true, + }, + }, }, - "required": true + required: true, }, - "audiences": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "required": true + audiences: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + required: true, + }, + name: { + type: 'string', + required: true, }, - "name": { - "type": "string", - "required": true + conditions: { + type: 'string', + required: true, }, - "conditions": { - "type": "string", - "required": true - } - } + }, }, - "required": true + required: true, }, - "attributes": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "required": true + attributes: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + required: true, }, - "key": { - "type": "string", - "required": true + key: { + type: 'string', + required: true, }, - } + }, }, - "required": true + required: true, }, - "version": { - "type": "string", - "required": true + version: { + type: 'string', + required: true, }, - "revision": { - "type": "string", - "required": true + revision: { + type: 'string', + required: true, }, - } + }, }; diff --git a/packages/optimizely-sdk/lib/index.browser.js b/packages/optimizely-sdk/lib/index.browser.js index af6ffba08..d23c09b0d 100644 --- a/packages/optimizely-sdk/lib/index.browser.js +++ b/packages/optimizely-sdk/lib/index.browser.js @@ -127,7 +127,11 @@ module.exports = { config.eventBatchSize = DEFAULT_EVENT_BATCH_SIZE; } if (!eventProcessorConfigValidator.validateEventFlushInterval(config.eventFlushInterval)) { - logger.warn('Invalid eventFlushInterval %s, defaulting to %s', config.eventFlushInterval, DEFAULT_EVENT_FLUSH_INTERVAL); + logger.warn( + 'Invalid eventFlushInterval %s, defaulting to %s', + config.eventFlushInterval, + DEFAULT_EVENT_FLUSH_INTERVAL + ); config.eventFlushInterval = DEFAULT_EVENT_FLUSH_INTERVAL; } diff --git a/packages/optimizely-sdk/lib/index.browser.tests.js b/packages/optimizely-sdk/lib/index.browser.tests.js index 3b1bccb62..6b292ac84 100644 --- a/packages/optimizely-sdk/lib/index.browser.tests.js +++ b/packages/optimizely-sdk/lib/index.browser.tests.js @@ -418,9 +418,12 @@ describe('javascript-sdk', function() { eventDispatcher: fakeEventDispatcher, logger: silentLogger, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 1000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 1000, + }) + ); }); describe('with an invalid flush interval', function() { @@ -440,9 +443,12 @@ describe('javascript-sdk', function() { logger: silentLogger, eventFlushInterval: ['invalid', 'flush', 'interval'], }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 1000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 1000, + }) + ); }); }); @@ -463,9 +469,12 @@ describe('javascript-sdk', function() { logger: silentLogger, eventFlushInterval: 9000, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 9000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 9000, + }) + ); }); }); @@ -476,9 +485,12 @@ describe('javascript-sdk', function() { eventDispatcher: fakeEventDispatcher, logger: silentLogger, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 10, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 10, + }) + ); }); describe('with an invalid event batch size', function() { @@ -498,9 +510,12 @@ describe('javascript-sdk', function() { logger: silentLogger, eventBatchSize: null, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 10, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 10, + }) + ); }); }); @@ -521,9 +536,12 @@ describe('javascript-sdk', function() { logger: silentLogger, eventBatchSize: 300, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 300, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 300, + }) + ); }); }); }); diff --git a/packages/optimizely-sdk/lib/index.d.ts b/packages/optimizely-sdk/lib/index.d.ts index 6aee95cf4..be6f9c816 100644 --- a/packages/optimizely-sdk/lib/index.d.ts +++ b/packages/optimizely-sdk/lib/index.d.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -declare module "@optimizely/optimizely-sdk" { - import { LogHandler, ErrorHandler } from "@optimizely/js-sdk-logging"; - import * as enums from "@optimizely/optimizely-sdk/lib/utils/enums"; - import * as logging from "@optimizely/optimizely-sdk/lib/plugins/logger"; +declare module '@optimizely/optimizely-sdk' { + import { LogHandler, ErrorHandler } from '@optimizely/js-sdk-logging'; + import * as enums from '@optimizely/optimizely-sdk/lib/utils/enums'; + import * as logging from '@optimizely/optimizely-sdk/lib/plugins/logger'; export { enums, logging }; export function setLogger(logger: LogHandler | null): void; @@ -59,33 +59,12 @@ declare module "@optimizely/optimizely-sdk" { export interface Client { notificationCenter: NotificationCenter; - activate( - experimentKey: string, - userId: string, - attributes?: UserAttributes - ): string | null; - track( - eventKey: string, - userId: string, - attributes?: UserAttributes, - eventTags?: EventTags - ): void; - getVariation( - experimentKey: string, - userId: string, - attributes?: UserAttributes - ): string | null; - setForcedVariation( - experimentKey: string, - userId: string, - variationKey: string | null - ): boolean; + activate(experimentKey: string, userId: string, attributes?: UserAttributes): string | null; + track(eventKey: string, userId: string, attributes?: UserAttributes, eventTags?: EventTags): void; + getVariation(experimentKey: string, userId: string, attributes?: UserAttributes): string | null; + setForcedVariation(experimentKey: string, userId: string, variationKey: string | null): boolean; getForcedVariation(experimentKey: string, userId: string): string | null; - isFeatureEnabled( - featureKey: string, - userId: string, - attributes?: UserAttributes - ): boolean; + isFeatureEnabled(featureKey: string, userId: string, attributes?: UserAttributes): boolean; getEnabledFeatures(userId: string, attributes?: UserAttributes): string[]; getFeatureVariableBoolean( featureKey: string, @@ -112,9 +91,7 @@ declare module "@optimizely/optimizely-sdk" { attributes?: UserAttributes ): string | null; getOptimizelyConfig(): OptimizelyConfig | null; - onReady(options?: { - timeout?: number; - }): Promise<{ success: boolean; reason?: string }>; + onReady(options?: { timeout?: number }): Promise<{ success: boolean; reason?: string }>; close(): Promise<{ success: boolean; reason?: string }>; } @@ -124,7 +101,7 @@ declare module "@optimizely/optimizely-sdk" { // URL to which to send the HTTP request. url: string; // HTTP method with which to send the event. - httpVerb: "POST"; + httpVerb: 'POST'; // Value to send in the request body, JSON-serialized. params: any; } @@ -153,14 +130,10 @@ declare module "@optimizely/optimizely-sdk" { ): number; removeNotificationListener(listenerId: number): boolean; clearAllNotificationListeners(): void; - clearNotificationListeners( - notificationType: enums.NOTIFICATION_TYPES - ): void; + clearNotificationListeners(notificationType: enums.NOTIFICATION_TYPES): void; } - export type NotificationListener = ( - notificationData: T - ) => void; + export type NotificationListener = (notificationData: T) => void; export interface ListenerPayload { userId: string; @@ -265,22 +238,22 @@ declare module "@optimizely/optimizely-sdk" { } } -declare module "@optimizely/optimizely-sdk/lib/utils/enums" { - import { LogLevel } from "@optimizely/js-sdk-logging"; +declare module '@optimizely/optimizely-sdk/lib/utils/enums' { + import { LogLevel } from '@optimizely/js-sdk-logging'; export { LogLevel as LOG_LEVEL }; export enum NOTIFICATION_TYPES { - ACTIVATE = "ACTIVATE:experiment, user_id,attributes, variation, event", - DECISION = "DECISION:type, userId, attributes, decisionInfo", - OPTIMIZELY_CONFIG_UPDATE = "OPTIMIZELY_CONFIG_UPDATE", - TRACK = "TRACK:event_key, user_id, attributes, event_tags, event" + ACTIVATE = 'ACTIVATE:experiment, user_id,attributes, variation, event', + DECISION = 'DECISION:type, userId, attributes, decisionInfo', + OPTIMIZELY_CONFIG_UPDATE = 'OPTIMIZELY_CONFIG_UPDATE', + TRACK = 'TRACK:event_key, user_id, attributes, event_tags, event', } } -declare module "@optimizely/optimizely-sdk/lib/plugins/logger" { - import * as enums from "@optimizely/optimizely-sdk/lib/utils/enums"; - import { LogHandler } from "@optimizely/js-sdk-logging"; +declare module '@optimizely/optimizely-sdk/lib/plugins/logger' { + import * as enums from '@optimizely/optimizely-sdk/lib/utils/enums'; + import { LogHandler } from '@optimizely/js-sdk-logging'; export interface LoggerConfig { logLevel?: enums.LOG_LEVEL; @@ -291,8 +264,8 @@ declare module "@optimizely/optimizely-sdk/lib/plugins/logger" { export function createNoOpLogger(): LogHandler; } -declare module "@optimizely/optimizely-sdk/lib/plugins/event_dispatcher" {} +declare module '@optimizely/optimizely-sdk/lib/plugins/event_dispatcher' {} -declare module "@optimizely/optimizely-sdk/lib/utils/json_schema_validator" {} +declare module '@optimizely/optimizely-sdk/lib/utils/json_schema_validator' {} -declare module "@optimizely/optimizely-sdk/lib/plugins/error_handler" {} +declare module '@optimizely/optimizely-sdk/lib/plugins/error_handler' {} diff --git a/packages/optimizely-sdk/lib/index.node.js b/packages/optimizely-sdk/lib/index.node.js index 14762774e..c130fbc92 100644 --- a/packages/optimizely-sdk/lib/index.node.js +++ b/packages/optimizely-sdk/lib/index.node.js @@ -109,7 +109,11 @@ module.exports = { config.eventBatchSize = DEFAULT_EVENT_BATCH_SIZE; } if (!eventProcessorConfigValidator.validateEventFlushInterval(config.eventFlushInterval)) { - logger.warn('Invalid eventFlushInterval %s, defaulting to %s', config.eventFlushInterval, DEFAULT_EVENT_FLUSH_INTERVAL); + logger.warn( + 'Invalid eventFlushInterval %s, defaulting to %s', + config.eventFlushInterval, + DEFAULT_EVENT_FLUSH_INTERVAL + ); config.eventFlushInterval = DEFAULT_EVENT_FLUSH_INTERVAL; } diff --git a/packages/optimizely-sdk/lib/index.node.tests.js b/packages/optimizely-sdk/lib/index.node.tests.js index 8811e4aa0..ac7cb0088 100644 --- a/packages/optimizely-sdk/lib/index.node.tests.js +++ b/packages/optimizely-sdk/lib/index.node.tests.js @@ -113,9 +113,12 @@ describe('optimizelyFactory', function() { logger: fakeLogger, eventFlushInterval: ['invalid', 'flush', 'interval'], }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 30000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 30000, + }) + ); }); it('should use default event flush interval when none is provided', function() { @@ -125,9 +128,12 @@ describe('optimizelyFactory', function() { eventDispatcher: fakeEventDispatcher, logger: fakeLogger, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 30000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 30000, + }) + ); }); it('should use provided event flush interval when valid', function() { @@ -138,9 +144,12 @@ describe('optimizelyFactory', function() { logger: fakeLogger, eventFlushInterval: 10000, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 10000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 10000, + }) + ); }); it('should ignore invalid event batch size and use default instead', function() { @@ -151,9 +160,12 @@ describe('optimizelyFactory', function() { logger: fakeLogger, eventBatchSize: null, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 10, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 10, + }) + ); }); it('should use default event batch size when none is provided', function() { @@ -163,9 +175,12 @@ describe('optimizelyFactory', function() { eventDispatcher: fakeEventDispatcher, logger: fakeLogger, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 10, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 10, + }) + ); }); it('should use provided event batch size when valid', function() { @@ -176,9 +191,12 @@ describe('optimizelyFactory', function() { logger: fakeLogger, eventBatchSize: 300, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 300, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 300, + }) + ); }); }); }); diff --git a/packages/optimizely-sdk/lib/index.react_native.js b/packages/optimizely-sdk/lib/index.react_native.js index dc4874915..774bbe0c2 100644 --- a/packages/optimizely-sdk/lib/index.react_native.js +++ b/packages/optimizely-sdk/lib/index.react_native.js @@ -106,7 +106,11 @@ module.exports = { config.eventBatchSize = DEFAULT_EVENT_BATCH_SIZE; } if (!eventProcessorConfigValidator.validateEventFlushInterval(config.eventFlushInterval)) { - logger.warn('Invalid eventFlushInterval %s, defaulting to %s', config.eventFlushInterval, DEFAULT_EVENT_FLUSH_INTERVAL); + logger.warn( + 'Invalid eventFlushInterval %s, defaulting to %s', + config.eventFlushInterval, + DEFAULT_EVENT_FLUSH_INTERVAL + ); config.eventFlushInterval = DEFAULT_EVENT_FLUSH_INTERVAL; } diff --git a/packages/optimizely-sdk/lib/index.react_native.tests.js b/packages/optimizely-sdk/lib/index.react_native.tests.js index 3270d7677..ea19632e5 100644 --- a/packages/optimizely-sdk/lib/index.react_native.tests.js +++ b/packages/optimizely-sdk/lib/index.react_native.tests.js @@ -137,7 +137,7 @@ describe('javascript-sdk/react-native', function() { sinon.stub(defaultEventDispatcher, 'dispatchEvent', function(evt, cb) { cb(); }); - }) + }); afterEach(function() { defaultEventDispatcher.dispatchEvent.restore(); @@ -212,9 +212,12 @@ describe('javascript-sdk/react-native', function() { eventDispatcher: fakeEventDispatcher, logger: silentLogger, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 1000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 1000, + }) + ); }); describe('with an invalid flush interval', function() { @@ -234,9 +237,12 @@ describe('javascript-sdk/react-native', function() { logger: silentLogger, eventFlushInterval: ['invalid', 'flush', 'interval'], }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 1000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 1000, + }) + ); }); }); @@ -257,9 +263,12 @@ describe('javascript-sdk/react-native', function() { logger: silentLogger, eventFlushInterval: 9000, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - flushInterval: 9000, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + flushInterval: 9000, + }) + ); }); }); @@ -270,9 +279,12 @@ describe('javascript-sdk/react-native', function() { eventDispatcher: fakeEventDispatcher, logger: silentLogger, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 10, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 10, + }) + ); }); describe('with an invalid event batch size', function() { @@ -292,9 +304,12 @@ describe('javascript-sdk/react-native', function() { logger: silentLogger, eventBatchSize: null, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 10, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 10, + }) + ); }); }); @@ -315,9 +330,12 @@ describe('javascript-sdk/react-native', function() { logger: silentLogger, eventBatchSize: 300, }); - sinon.assert.calledWithExactly(eventProcessorSpy, sinon.match({ - maxQueueSize: 300, - })); + sinon.assert.calledWithExactly( + eventProcessorSpy, + sinon.match({ + maxQueueSize: 300, + }) + ); }); }); }); diff --git a/packages/optimizely-sdk/lib/plugins/error_handler/index.js b/packages/optimizely-sdk/lib/plugins/error_handler/index.js index 9e4932c0e..211f90592 100644 --- a/packages/optimizely-sdk/lib/plugins/error_handler/index.js +++ b/packages/optimizely-sdk/lib/plugins/error_handler/index.js @@ -24,5 +24,5 @@ module.exports = { */ handleError: function() { // no-op - } + }, }; diff --git a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.browser.js b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.browser.js index 166ad2ff0..3772c82ad 100644 --- a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.browser.js +++ b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.browser.js @@ -66,7 +66,9 @@ module.exports = { }; var toQueryString = function(obj) { - return Object.keys(obj).map(function(k) { - return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]); - }).join('&'); + return Object.keys(obj) + .map(function(k) { + return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]); + }) + .join('&'); }; diff --git a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.browser.tests.js b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.browser.tests.js index a4b1294f3..6a774668b 100644 --- a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.browser.tests.js +++ b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.browser.tests.js @@ -27,8 +27,8 @@ describe('lib/plugins/event_dispatcher/browser', function() { xhr = sinon.useFakeXMLHttpRequest(); global.XMLHttpRequest = xhr; requests = []; - xhr.onCreate = function (req) { - requests.push(req); + xhr.onCreate = function(req) { + requests.push(req); }; }); @@ -37,14 +37,14 @@ describe('lib/plugins/event_dispatcher/browser', function() { }); it('should send a POST request with the specified params', function(done) { - var eventParams = {'testParam': 'testParamValue'}; + var eventParams = { testParam: 'testParamValue' }; var eventObj = { url: 'https://cdn.com/event', body: { id: 123, }, httpVerb: 'POST', - params: eventParams + params: eventParams, }; var callback = sinon.spy(); @@ -56,19 +56,23 @@ describe('lib/plugins/event_dispatcher/browser', function() { }); it('should execute the callback passed to event dispatcher with a post', function(done) { - var eventParams = {'testParam': 'testParamValue'}; + var eventParams = { testParam: 'testParamValue' }; var eventObj = { url: 'https://cdn.com/event', body: { id: 123, }, httpVerb: 'POST', - params: eventParams + params: eventParams, }; var callback = sinon.spy(); eventDispatcher.dispatchEvent(eventObj, callback); - requests[ 0 ].respond([ 200, {}, '{"url":"https://cdn.com/event","body":{"id":123},"httpVerb":"POST","params":{"testParam":"testParamValue"}}' ]); + requests[0].respond([ + 200, + {}, + '{"url":"https://cdn.com/event","body":{"id":123},"httpVerb":"POST","params":{"testParam":"testParamValue"}}', + ]); sinon.assert.calledOnce(callback); done(); }); @@ -76,16 +80,15 @@ describe('lib/plugins/event_dispatcher/browser', function() { it('should execute the callback passed to event dispatcher with a get', function(done) { var eventObj = { url: 'https://cdn.com/event', - httpVerb: 'GET' + httpVerb: 'GET', }; var callback = sinon.spy(); eventDispatcher.dispatchEvent(eventObj, callback); - requests[ 0 ].respond([ 200, {}, '{"url":"https://cdn.com/event","httpVerb":"GET"' ]); + requests[0].respond([200, {}, '{"url":"https://cdn.com/event","httpVerb":"GET"']); sinon.assert.calledOnce(callback); done(); }); - }); }); }); diff --git a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.js b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.js index 3e6d04da9..2e20cca1f 100644 --- a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.js +++ b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.js @@ -44,7 +44,7 @@ module.exports = { headers: { 'content-type': 'application/json', 'content-length': dataString.length.toString(), - } + }, }; var requestCallback = function(response) { @@ -59,5 +59,5 @@ module.exports = { req.write(dataString); req.end(); return req; - } + }, }; diff --git a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.tests.js b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.tests.js index 82ec57ee5..745ac89bd 100644 --- a/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.tests.js +++ b/packages/optimizely-sdk/lib/plugins/event_dispatcher/index.node.tests.js @@ -23,7 +23,7 @@ describe('lib/plugins/event_dispatcher/node', function() { describe('APIs', function() { describe('dispatchEvent', function() { var stubCallback = { - callback: function() {} + callback: function() {}, }; beforeEach(function() { @@ -64,14 +64,15 @@ describe('lib/plugins/event_dispatcher/node', function() { httpVerb: 'POST', }; - eventDispatcher.dispatchEvent(eventObj, stubCallback.callback) - .on('response', function(response) { - sinon.assert.calledOnce(stubCallback.callback); - done(); - }) - .on('error', function(error) { - assert.fail('status code okay', 'status code not okay', ''); - }); + eventDispatcher + .dispatchEvent(eventObj, stubCallback.callback) + .on('response', function(response) { + sinon.assert.calledOnce(stubCallback.callback); + done(); + }) + .on('error', function(error) { + assert.fail('status code okay', 'status code not okay', ''); + }); }); it('rejects GET httpVerb', function() { diff --git a/packages/optimizely-sdk/lib/plugins/logger/enums.js b/packages/optimizely-sdk/lib/plugins/logger/enums.js index 2d22c57b8..d5e657eda 100644 --- a/packages/optimizely-sdk/lib/plugins/logger/enums.js +++ b/packages/optimizely-sdk/lib/plugins/logger/enums.js @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -exports.LOG_LEVEL = require('@optimizely/js-sdk-logging').LogLevel; \ No newline at end of file +exports.LOG_LEVEL = require('@optimizely/js-sdk-logging').LogLevel; diff --git a/packages/optimizely-sdk/lib/plugins/logger/index.react_native.js b/packages/optimizely-sdk/lib/plugins/logger/index.react_native.js index 525f4e401..41168c686 100644 --- a/packages/optimizely-sdk/lib/plugins/logger/index.react_native.js +++ b/packages/optimizely-sdk/lib/plugins/logger/index.react_native.js @@ -17,12 +17,17 @@ var LogLevel = require('@optimizely/js-sdk-logging').LogLevel; var sprintf = require('@optimizely/js-sdk-utils').sprintf; function getLogLevelName(level) { - switch(level) { - case LogLevel.INFO: return 'INFO'; - case LogLevel.ERROR: return 'ERROR'; - case LogLevel.WARNING: return 'WARNING'; - case LogLevel.DEBUG: return 'DEBUG'; - default: return 'NOTSET'; + switch (level) { + case LogLevel.INFO: + return 'INFO'; + case LogLevel.ERROR: + return 'ERROR'; + case LogLevel.WARNING: + return 'WARNING'; + case LogLevel.DEBUG: + return 'DEBUG'; + default: + return 'NOTSET'; } } @@ -31,11 +36,17 @@ function ReactNativeLogger() {} ReactNativeLogger.prototype.log = function(level, message) { var formattedMessage = sprintf('[OPTIMIZELY] - %s %s %s', getLogLevelName(level), new Date().toISOString(), message); switch (level) { - case LogLevel.INFO: console.info(formattedMessage); break; + case LogLevel.INFO: + console.info(formattedMessage); + break; case LogLevel.ERROR: - case LogLevel.WARNING: console.warn(formattedMessage); break; + case LogLevel.WARNING: + console.warn(formattedMessage); + break; case LogLevel.DEBUG: - case LogLevel.NOTSET: console.log(formattedMessage); break; + case LogLevel.NOTSET: + console.log(formattedMessage); + break; } }; diff --git a/packages/optimizely-sdk/lib/plugins/logger/index.react_native.tests.js b/packages/optimizely-sdk/lib/plugins/logger/index.react_native.tests.js index 15553fbe8..11dd53619 100644 --- a/packages/optimizely-sdk/lib/plugins/logger/index.react_native.tests.js +++ b/packages/optimizely-sdk/lib/plugins/logger/index.react_native.tests.js @@ -50,7 +50,7 @@ describe('lib/plugins/logger/react_native', function() { it('shoud use console.info when log level is info', function() { defaultLogger.log(LOG_LEVEL.INFO, 'message'); sinon.assert.calledWithExactly(console.info, sinon.match(/.*INFO.*message.*/)); - sinon.assert.notCalled(console.log); + sinon.assert.notCalled(console.log); sinon.assert.notCalled(console.warn); sinon.assert.notCalled(console.error); }); @@ -58,7 +58,7 @@ describe('lib/plugins/logger/react_native', function() { it('shoud use console.log when log level is debug', function() { defaultLogger.log(LOG_LEVEL.DEBUG, 'message'); sinon.assert.calledWithExactly(console.log, sinon.match(/.*DEBUG.*message.*/)); - sinon.assert.notCalled(console.info); + sinon.assert.notCalled(console.info); sinon.assert.notCalled(console.warn); sinon.assert.notCalled(console.error); }); @@ -66,7 +66,7 @@ describe('lib/plugins/logger/react_native', function() { it('shoud use console.warn when log level is warn', function() { defaultLogger.log(LOG_LEVEL.WARNING, 'message'); sinon.assert.calledWithExactly(console.warn, sinon.match(/.*WARNING.*message.*/)); - sinon.assert.notCalled(console.log); + sinon.assert.notCalled(console.log); sinon.assert.notCalled(console.info); sinon.assert.notCalled(console.error); }); @@ -74,7 +74,7 @@ describe('lib/plugins/logger/react_native', function() { it('shoud use console.warn when log level is error', function() { defaultLogger.log(LOG_LEVEL.ERROR, 'message'); sinon.assert.calledWithExactly(console.warn, sinon.match(/.*ERROR.*message.*/)); - sinon.assert.notCalled(console.log); + sinon.assert.notCalled(console.log); sinon.assert.notCalled(console.info); sinon.assert.notCalled(console.error); }); diff --git a/packages/optimizely-sdk/lib/plugins/logger/index.tests.js b/packages/optimizely-sdk/lib/plugins/logger/index.tests.js index 006d39252..5ffae7924 100644 --- a/packages/optimizely-sdk/lib/plugins/logger/index.tests.js +++ b/packages/optimizely-sdk/lib/plugins/logger/index.tests.js @@ -26,7 +26,7 @@ describe('lib/plugins/logger', function() { var defaultLogger; describe('createLogger', function() { it('should return an instance of the default logger', function() { - defaultLogger = logger.createLogger({logLevel: LOG_LEVEL.NOTSET}); + defaultLogger = logger.createLogger({ logLevel: LOG_LEVEL.NOTSET }); assert.isObject(defaultLogger); expect(defaultLogger.logLevel).to.equal(LOG_LEVEL.NOTSET); }); @@ -34,7 +34,7 @@ describe('lib/plugins/logger', function() { describe('log', function() { beforeEach(function() { - defaultLogger = logger.createLogger({logLevel: LOG_LEVEL.INFO}); + defaultLogger = logger.createLogger({ logLevel: LOG_LEVEL.INFO }); sinon.stub(console, 'log'); sinon.stub(console, 'info'); @@ -81,7 +81,7 @@ describe('lib/plugins/logger', function() { describe('setLogLevel', function() { beforeEach(function() { - defaultLogger = logger.createLogger({logLevel: LOG_LEVEL.NOTSET}); + defaultLogger = logger.createLogger({ logLevel: LOG_LEVEL.NOTSET }); }); it('should set the log level to the specified log level', function() { diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index b5abb56f5..31c9097b7 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -1448,19 +1448,19 @@ var datafileWithFeaturesExpectedData = { 594099: { 4792309476491264: { value: '40', - id: '4792309476491264' + id: '4792309476491264', }, 5073784453201920: { value: 'true', - id: '5073784453201920' + id: '5073784453201920', }, 5636734406623232: { value: 'Buy me Later', - id: '5636734406623232' + id: '5636734406623232', }, 6199684360044544: { value: '99.99', - id: '6199684360044544' + id: '6199684360044544', }, }, 595008: {}, @@ -2503,10 +2503,10 @@ var mutexFeatureTestsConfig = { layerId: '17151011617', trafficAllocation: [{ entityId: '17138530965', endOfRange: 0 }], forcedVariations: {}, - } + }, ], id: '17151011617', - } + }, ], typedAudiences: [], anonymizeIP: false, @@ -2528,7 +2528,7 @@ var mutexFeatureTestsConfig = { policy: 'random', trafficAllocation: [ { entityId: '17139931304', endOfRange: 9900 }, - { entityId: '17128410791', endOfRange: 10000 } + { entityId: '17128410791', endOfRange: 10000 }, ], experiments: [ { @@ -2536,36 +2536,36 @@ var mutexFeatureTestsConfig = { audienceIds: [], variations: [ { variables: [], id: 17155031309, key: 'variation_1', featureEnabled: false }, - { variables: [], id: 17124610952, key: 'variation_2', featureEnabled: true } + { variables: [], id: 17124610952, key: 'variation_2', featureEnabled: true }, ], id: '17139931304', key: 'f_test2', layerId: '17149391594', trafficAllocation: [ { entityId: '17155031309', endOfRange: 5000 }, - { entityId: '17124610952', endOfRange: 10000 } + { entityId: '17124610952', endOfRange: 10000 }, ], - forcedVariations: {} + forcedVariations: {}, }, { status: 'Running', audienceIds: [], variations: [ { variables: [], id: '17175820099', key: 'variation_1', featureEnabled: false }, - { variables: [], id: '17144050391', key: 'variation_2', featureEnabled: true } + { variables: [], id: '17144050391', key: 'variation_2', featureEnabled: true }, ], id: '17128410791', key: 'f_test1', layerId: '17145581153', trafficAllocation: [ { entityId: '17175820099', endOfRange: 5000 }, - { entityId: '17144050391', endOfRange: 10000 } + { entityId: '17144050391', endOfRange: 10000 }, ], - forcedVariations: {} - } + forcedVariations: {}, + }, ], - id: '17142090293' - } + id: '17142090293', + }, ], attributes: [], botFiltering: false, diff --git a/packages/optimizely-sdk/lib/utils/attributes_validator/index.js b/packages/optimizely-sdk/lib/utils/attributes_validator/index.js index d2a066744..e2356118b 100644 --- a/packages/optimizely-sdk/lib/utils/attributes_validator/index.js +++ b/packages/optimizely-sdk/lib/utils/attributes_validator/index.js @@ -46,7 +46,11 @@ module.exports = { }, isAttributeValid: function(attributeKey, attributeValue) { - return (typeof attributeKey === 'string') && - (typeof attributeValue === 'string' || typeof attributeValue === 'boolean' || (fns.isNumber(attributeValue) && fns.isSafeInteger(attributeValue))); + return ( + typeof attributeKey === 'string' && + (typeof attributeValue === 'string' || + typeof attributeValue === 'boolean' || + (fns.isNumber(attributeValue) && fns.isSafeInteger(attributeValue))) + ); }, }; diff --git a/packages/optimizely-sdk/lib/utils/attributes_validator/index.tests.js b/packages/optimizely-sdk/lib/utils/attributes_validator/index.tests.js index 4e0c672ab..647695a72 100644 --- a/packages/optimizely-sdk/lib/utils/attributes_validator/index.tests.js +++ b/packages/optimizely-sdk/lib/utils/attributes_validator/index.tests.js @@ -25,7 +25,7 @@ describe('lib/utils/attributes_validator', function() { describe('APIs', function() { describe('validate', function() { it('should validate the given attributes if attributes is an object', function() { - assert.isTrue(attributesValidator.validate({testAttribute: 'testValue'})); + assert.isTrue(attributesValidator.validate({ testAttribute: 'testValue' })); }); it('should throw an error if attributes is an array', function() { @@ -50,7 +50,6 @@ describe('lib/utils/attributes_validator', function() { }, sprintf(ERROR_MESSAGES.INVALID_ATTRIBUTES, 'ATTRIBUTES_VALIDATOR')); }); - it('should throw an error if attributes contains a key with an undefined value', function() { var attributeKey = 'testAttribute'; var attributes = {}; @@ -65,31 +64,31 @@ describe('lib/utils/attributes_validator', function() { describe('isAttributeValid', function() { it('isAttributeValid returns true for valid values', function() { var userAttributes = { - 'browser_type': 'Chrome', - 'is_firefox': false, - 'num_users': 10, - 'pi_value': 3.14, + browser_type: 'Chrome', + is_firefox: false, + num_users: 10, + pi_value: 3.14, '': 'javascript', }; - Object.keys(userAttributes).forEach(function (key) { - var value = userAttributes[key]; + Object.keys(userAttributes).forEach(function(key) { + var value = userAttributes[key]; assert.isTrue(attributesValidator.isAttributeValid(key, value)); }); }); it('isAttributeValid returns false for invalid values', function() { var userAttributes = { - 'null': null, - 'objects': {a: 'b'}, - 'array': [1, 2, 3], - 'infinity': Infinity, - 'negativeInfinity': -Infinity, - 'NaN': NaN, - 'outOfBound': Math.pow(2, 53) + 2, + null: null, + objects: { a: 'b' }, + array: [1, 2, 3], + infinity: Infinity, + negativeInfinity: -Infinity, + NaN: NaN, + outOfBound: Math.pow(2, 53) + 2, }; - Object.keys(userAttributes).forEach(function (key) { + Object.keys(userAttributes).forEach(function(key) { var value = userAttributes[key]; assert.isFalse(attributesValidator.isAttributeValid(key, value)); }); diff --git a/packages/optimizely-sdk/lib/utils/config_validator/index.js b/packages/optimizely-sdk/lib/utils/config_validator/index.js index a6ae46500..499916a9d 100644 --- a/packages/optimizely-sdk/lib/utils/config_validator/index.js +++ b/packages/optimizely-sdk/lib/utils/config_validator/index.js @@ -19,11 +19,7 @@ var ERROR_MESSAGES = require('../enums').ERROR_MESSAGES; var MODULE_NAME = 'CONFIG_VALIDATOR'; var DATAFILE_VERSIONS = require('../enums').DATAFILE_VERSIONS; -var SUPPORTED_VERSIONS = [ - DATAFILE_VERSIONS.V2, - DATAFILE_VERSIONS.V3, - DATAFILE_VERSIONS.V4 -]; +var SUPPORTED_VERSIONS = [DATAFILE_VERSIONS.V2, DATAFILE_VERSIONS.V3, DATAFILE_VERSIONS.V4]; /** * Provides utility methods for validating that the configuration options are valid @@ -39,15 +35,15 @@ module.exports = { * @throws If any of the config options are not valid */ validate: function(config) { - if (config.errorHandler && (typeof config.errorHandler.handleError !== 'function')) { + if (config.errorHandler && typeof config.errorHandler.handleError !== 'function') { throw new Error(sprintf(ERROR_MESSAGES.INVALID_ERROR_HANDLER, MODULE_NAME)); } - if (config.eventDispatcher && (typeof config.eventDispatcher.dispatchEvent !== 'function')) { + if (config.eventDispatcher && typeof config.eventDispatcher.dispatchEvent !== 'function') { throw new Error(sprintf(ERROR_MESSAGES.INVALID_EVENT_DISPATCHER, MODULE_NAME)); } - if (config.logger && (typeof config.logger.log !== 'function')) { + if (config.logger && typeof config.logger.log !== 'function') { throw new Error(sprintf(ERROR_MESSAGES.INVALID_LOGGER, MODULE_NAME)); } @@ -82,5 +78,5 @@ module.exports = { } return true; - } + }, }; diff --git a/packages/optimizely-sdk/lib/utils/config_validator/index.tests.js b/packages/optimizely-sdk/lib/utils/config_validator/index.tests.js index 3ef4dc5e9..1776bdc1f 100644 --- a/packages/optimizely-sdk/lib/utils/config_validator/index.tests.js +++ b/packages/optimizely-sdk/lib/utils/config_validator/index.tests.js @@ -17,7 +17,7 @@ var chai = require('chai'); var assert = chai.assert; var configValidator = require('./'); var sprintf = require('@optimizely/js-sdk-utils').sprintf; -var testData = require('../../tests/test_data') +var testData = require('../../tests/test_data'); var ERROR_MESSAGES = require('../enums').ERROR_MESSAGES; diff --git a/packages/optimizely-sdk/lib/utils/enums/index.js b/packages/optimizely-sdk/lib/utils/enums/index.js index 9d9b74152..41aa6443c 100644 --- a/packages/optimizely-sdk/lib/utils/enums/index.js +++ b/packages/optimizely-sdk/lib/utils/enums/index.js @@ -88,10 +88,12 @@ exports.LOG_MESSAGES = { NOT_TRACKING_USER: '%s: Not tracking user %s.', PARSED_REVENUE_VALUE: '%s: Parsed revenue value "%s" from event tags.', PARSED_NUMERIC_VALUE: '%s: Parsed event value "%s" from event tags.', - RETURNING_STORED_VARIATION: '%s: Returning previously activated variation "%s" of experiment "%s" for user "%s" from user profile.', + RETURNING_STORED_VARIATION: + '%s: Returning previously activated variation "%s" of experiment "%s" for user "%s" from user profile.', ROLLOUT_HAS_NO_EXPERIMENTS: '%s: Rollout of feature %s has no experiments', SAVED_VARIATION: '%s: Saved variation "%s" of experiment "%s" for user "%s".', - SAVED_VARIATION_NOT_FOUND: '%s: User %s was previously bucketed into variation with ID %s for experiment %s, but no matching variation was found.', + SAVED_VARIATION_NOT_FOUND: + '%s: User %s was previously bucketed into variation with ID %s for experiment %s, but no matching variation was found.', SHOULD_NOT_DISPATCH_ACTIVATE: '%s: Experiment %s is not in "Running" state. Not activating user.', SKIPPING_JSON_VALIDATION: '%s: Skipping JSON schema validation.', TRACK_EVENT: '%s: Tracking event %s for user %s.', @@ -102,10 +104,12 @@ exports.LOG_MESSAGES = { USER_IN_FEATURE_EXPERIMENT: '%s: User %s is in variation %s of experiment %s on the feature %s.', USER_IN_ROLLOUT: '%s: User %s is in rollout of feature %s.', USER_BUCKETED_INTO_EVERYONE_TARGETING_RULE: '%s: User %s bucketed into everyone targeting rule.', - USER_NOT_BUCKETED_INTO_EVERYONE_TARGETING_RULE: '%s: User %s not bucketed into everyone targeting rule due to traffic allocation.', + USER_NOT_BUCKETED_INTO_EVERYONE_TARGETING_RULE: + '%s: User %s not bucketed into everyone targeting rule due to traffic allocation.', USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP: '%s: User %s is not in experiment %s of group %s.', USER_NOT_BUCKETED_INTO_ANY_EXPERIMENT_IN_GROUP: '%s: User %s is not in any experiment of group %s.', - USER_NOT_BUCKETED_INTO_TARGETING_RULE: '%s User %s not bucketed into targeting rule %s due to traffic allocation. Trying everyone rule.', + USER_NOT_BUCKETED_INTO_TARGETING_RULE: + '%s User %s not bucketed into targeting rule %s due to traffic allocation. Trying everyone rule.', USER_NOT_IN_FEATURE_EXPERIMENT: '%s: User %s is not in any experiment on the feature %s.', USER_NOT_IN_ROLLOUT: '%s: User %s is not in rollout of feature %s.', USER_FORCED_IN_VARIATION: '%s: User %s is forced in variation %s.', @@ -119,28 +123,39 @@ exports.LOG_MESSAGES = { USER_HAS_NO_FORCED_VARIATION_FOR_EXPERIMENT: '%s: No experiment %s mapped to user %s in the forced variation map.', USER_NOT_IN_ANY_EXPERIMENT: '%s: User %s is not in any experiment of group %s.', USER_NOT_IN_EXPERIMENT: '%s: User %s does not meet conditions to be in experiment %s.', - USER_RECEIVED_DEFAULT_VARIABLE_VALUE: '%s: User "%s" is not in any variation or rollout rule. Returning default value for variable "%s" of feature flag "%s".', - FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE: '%s: Feature "%s" is not enabled for user %s. Returning default value for variable "%s".', - VARIABLE_NOT_USED_RETURN_DEFAULT_VARIABLE_VALUE: '%s: Variable "%s" is not used in variation "%s". Returning default value.', + USER_RECEIVED_DEFAULT_VARIABLE_VALUE: + '%s: User "%s" is not in any variation or rollout rule. Returning default value for variable "%s" of feature flag "%s".', + FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE: + '%s: Feature "%s" is not enabled for user %s. Returning default value for variable "%s".', + VARIABLE_NOT_USED_RETURN_DEFAULT_VARIABLE_VALUE: + '%s: Variable "%s" is not used in variation "%s". Returning default value.', USER_RECEIVED_VARIABLE_VALUE: '%s: Value for variable "%s" of feature flag "%s" is %s for user "%s"', VALID_DATAFILE: '%s: Datafile is valid.', VALID_USER_PROFILE_SERVICE: '%s: Valid user profile service provided.', VARIATION_REMOVED_FOR_USER: '%s: Variation mapped to experiment %s has been removed for user %s.', - VARIABLE_REQUESTED_WITH_WRONG_TYPE: '%s: Requested variable type "%s", but variable is of type "%s". Use correct API to retrieve value. Returning None.', + VARIABLE_REQUESTED_WITH_WRONG_TYPE: + '%s: Requested variable type "%s", but variable is of type "%s". Use correct API to retrieve value. Returning None.', VALID_BUCKETING_ID: '%s: BucketingId is valid: "%s"', BUCKETING_ID_NOT_STRING: '%s: BucketingID attribute is not a string. Defaulted to userId', EVALUATING_AUDIENCE: '%s: Starting to evaluate audience "%s" with conditions: %s.', EVALUATING_AUDIENCES_COMBINED: '%s: Evaluating audiences for experiment "%s": %s.', AUDIENCE_EVALUATION_RESULT: '%s: Audience "%s" evaluated to %s.', AUDIENCE_EVALUATION_RESULT_COMBINED: '%s: Audiences for experiment %s collectively evaluated to %s.', - MISSING_ATTRIBUTE_VALUE: '%s: Audience condition %s evaluated to UNKNOWN because no value was passed for user attribute "%s".', - UNEXPECTED_CONDITION_VALUE: '%s: Audience condition %s evaluated to UNKNOWN because the condition value is not supported.', - UNEXPECTED_TYPE: '%s: Audience condition %s evaluated to UNKNOWN because a value of type "%s" was passed for user attribute "%s".', - UNEXPECTED_TYPE_NULL: '%s: Audience condition %s evaluated to UNKNOWN because a null value was passed for user attribute "%s".', - UNKNOWN_CONDITION_TYPE: '%s: Audience condition %s has an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK.', - UNKNOWN_MATCH_TYPE: '%s: Audience condition %s uses an unknown match type. You may need to upgrade to a newer release of the Optimizely SDK.', + MISSING_ATTRIBUTE_VALUE: + '%s: Audience condition %s evaluated to UNKNOWN because no value was passed for user attribute "%s".', + UNEXPECTED_CONDITION_VALUE: + '%s: Audience condition %s evaluated to UNKNOWN because the condition value is not supported.', + UNEXPECTED_TYPE: + '%s: Audience condition %s evaluated to UNKNOWN because a value of type "%s" was passed for user attribute "%s".', + UNEXPECTED_TYPE_NULL: + '%s: Audience condition %s evaluated to UNKNOWN because a null value was passed for user attribute "%s".', + UNKNOWN_CONDITION_TYPE: + '%s: Audience condition %s has an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK.', + UNKNOWN_MATCH_TYPE: + '%s: Audience condition %s uses an unknown match type. You may need to upgrade to a newer release of the Optimizely SDK.', UPDATED_OPTIMIZELY_CONFIG: '%s: Updated Optimizely config to revision %s (project id %s)', - OUT_OF_BOUNDS: '%s: Audience condition %s evaluated to UNKNOWN because the number value for user attribute "%s" is not in the range [-2^53, +2^53].', + OUT_OF_BOUNDS: + '%s: Audience condition %s evaluated to UNKNOWN because the number value for user attribute "%s" is not in the range [-2^53, +2^53].', UNABLE_TO_ATTACH_UNLOAD: '%s: unable to bind optimizely.close() to page unload event: "%s"', }; diff --git a/packages/optimizely-sdk/lib/utils/event_tag_utils/index.tests.js b/packages/optimizely-sdk/lib/utils/event_tag_utils/index.tests.js index 13add0db7..910689a01 100644 --- a/packages/optimizely-sdk/lib/utils/event_tag_utils/index.tests.js +++ b/packages/optimizely-sdk/lib/utils/event_tag_utils/index.tests.js @@ -30,18 +30,24 @@ describe('lib/utils/event_tag_utils', function() { describe('getRevenueValue', function() { describe('the revenue value is a valid number', function() { it('should return the parsed integer for the revenue value', function() { - var parsedRevenueValue = eventTagUtils.getRevenueValue({ - revenue: '1337', - }, mockLogger); + var parsedRevenueValue = eventTagUtils.getRevenueValue( + { + revenue: '1337', + }, + mockLogger + ); assert.strictEqual(parsedRevenueValue, 1337); var logMessage = mockLogger.log.args[0][1]; assert.strictEqual(logMessage, 'EVENT_TAG_UTILS: Parsed revenue value "1337" from event tags.'); // test out a float - parsedRevenueValue = eventTagUtils.getRevenueValue({ - revenue: '13.37', - }, mockLogger); + parsedRevenueValue = eventTagUtils.getRevenueValue( + { + revenue: '13.37', + }, + mockLogger + ); assert.strictEqual(parsedRevenueValue, 13); }); @@ -49,9 +55,12 @@ describe('lib/utils/event_tag_utils', function() { describe('the revenue value is not a valid number', function() { it('should return null and log a message', function() { - var parsedRevenueValue = eventTagUtils.getRevenueValue({ - revenue: 'invalid', - }, mockLogger); + var parsedRevenueValue = eventTagUtils.getRevenueValue( + { + revenue: 'invalid', + }, + mockLogger + ); assert.strictEqual(parsedRevenueValue, null); @@ -62,9 +71,12 @@ describe('lib/utils/event_tag_utils', function() { describe('the revenue value is not present in the event tags', function() { it('should return null', function() { - var parsedRevenueValue = eventTagUtils.getRevenueValue({ - not_revenue: '1337', - }, mockLogger); + var parsedRevenueValue = eventTagUtils.getRevenueValue( + { + not_revenue: '1337', + }, + mockLogger + ); assert.strictEqual(parsedRevenueValue, null); }); @@ -74,18 +86,24 @@ describe('lib/utils/event_tag_utils', function() { describe('getNumericValue', function() { describe('the event value is a valid number', function() { it('should return the parsed integer for the event value', function() { - var parsedEventValue = eventTagUtils.getEventValue({ - value: '1337', - }, mockLogger); + var parsedEventValue = eventTagUtils.getEventValue( + { + value: '1337', + }, + mockLogger + ); assert.strictEqual(parsedEventValue, 1337); var logMessage = mockLogger.log.args[0][1]; assert.strictEqual(logMessage, 'EVENT_TAG_UTILS: Parsed event value "1337" from event tags.'); // test out a float - parsedEventValue = eventTagUtils.getEventValue({ - value: '13.37', - }, mockLogger); + parsedEventValue = eventTagUtils.getEventValue( + { + value: '13.37', + }, + mockLogger + ); assert.strictEqual(parsedEventValue, 13.37); }); @@ -93,9 +111,12 @@ describe('lib/utils/event_tag_utils', function() { describe('the event value is not a valid number', function() { it('should return null and log a message', function() { - var parsedEventValue = eventTagUtils.getEventValue({ - value: 'invalid', - }, mockLogger); + var parsedEventValue = eventTagUtils.getEventValue( + { + value: 'invalid', + }, + mockLogger + ); assert.strictEqual(parsedEventValue, null); @@ -106,9 +127,12 @@ describe('lib/utils/event_tag_utils', function() { describe('the event value is not present in the event tags', function() { it('should return null', function() { - var parsedEventValue = eventTagUtils.getEventValue({ - not_value: '13.37', - }, mockLogger); + var parsedEventValue = eventTagUtils.getEventValue( + { + not_value: '13.37', + }, + mockLogger + ); assert.strictEqual(parsedEventValue, null); }); diff --git a/packages/optimizely-sdk/lib/utils/event_tags_validator/index.tests.js b/packages/optimizely-sdk/lib/utils/event_tags_validator/index.tests.js index 08e7fecd9..ce8a0be49 100644 --- a/packages/optimizely-sdk/lib/utils/event_tags_validator/index.tests.js +++ b/packages/optimizely-sdk/lib/utils/event_tags_validator/index.tests.js @@ -24,7 +24,7 @@ describe('lib/utils/event_tags_validator', function() { describe('APIs', function() { describe('validate', function() { it('should validate the given event tags if event tags is an object', function() { - assert.isTrue(eventTagsValidator.validate({testAttribute: 'testValue'})); + assert.isTrue(eventTagsValidator.validate({ testAttribute: 'testValue' })); }); it('should throw an error if event tags is an array', function() { diff --git a/packages/optimizely-sdk/lib/utils/fns/index.js b/packages/optimizely-sdk/lib/utils/fns/index.js index dcd46dc8d..d584fdca0 100644 --- a/packages/optimizely-sdk/lib/utils/fns/index.js +++ b/packages/optimizely-sdk/lib/utils/fns/index.js @@ -17,7 +17,7 @@ var uuid = require('uuid'); var MAX_SAFE_INTEGER_LIMIT = Math.pow(2, 53); var keyBy = require('@optimizely/js-sdk-utils').keyBy; module.exports = { - assign: function (target) { + assign: function(target) { if (!target) { return {}; } diff --git a/packages/optimizely-sdk/lib/utils/fns/index.tests.js b/packages/optimizely-sdk/lib/utils/fns/index.tests.js index 9a6d016fc..84286ddfd 100644 --- a/packages/optimizely-sdk/lib/utils/fns/index.tests.js +++ b/packages/optimizely-sdk/lib/utils/fns/index.tests.js @@ -55,7 +55,7 @@ describe('lib/utils/fns', function() { row1: { key1: 'row1', key2: 'key2row1' }, row2: { key1: 'row2', key2: 'key2row2' }, row3: { key1: 'row3', key2: 'key2row3' }, - row4: { key1: 'row4', key2: 'key2row4' } + row4: { key1: 'row4', key2: 'key2row4' }, }); }); @@ -65,7 +65,7 @@ describe('lib/utils/fns', function() { obj = fns.keyBy(undefined, 'key1'); assert.isEmpty(obj); - }); + }); }); describe('isNumber', function() { diff --git a/packages/optimizely-sdk/lib/utils/json_schema_validator/index.js b/packages/optimizely-sdk/lib/utils/json_schema_validator/index.js index d51ff10ea..a0cec952f 100644 --- a/packages/optimizely-sdk/lib/utils/json_schema_validator/index.js +++ b/packages/optimizely-sdk/lib/utils/json_schema_validator/index.js @@ -40,9 +40,11 @@ module.exports = { return true; } else { if (Array.isArray(result.errors)) { - throw new Error(sprintf(ERROR_MESSAGES.INVALID_DATAFILE, MODULE_NAME, result.errors[0].property, result.errors[0].message)); + throw new Error( + sprintf(ERROR_MESSAGES.INVALID_DATAFILE, MODULE_NAME, result.errors[0].property, result.errors[0].message) + ); } throw new Error(sprintf(ERROR_MESSAGES.INVALID_JSON, MODULE_NAME)); } - } + }, }; diff --git a/packages/optimizely-sdk/lib/utils/json_schema_validator/index.tests.js b/packages/optimizely-sdk/lib/utils/json_schema_validator/index.tests.js index 791bfd421..7e016ab67 100644 --- a/packages/optimizely-sdk/lib/utils/json_schema_validator/index.tests.js +++ b/packages/optimizely-sdk/lib/utils/json_schema_validator/index.tests.js @@ -26,12 +26,12 @@ describe('lib/utils/json_schema_validator', function() { describe('APIs', function() { describe('validate', function() { it('should validate the given object against the specified schema', function() { - assert.isTrue(jsonSchemaValidator.validate({'type': 'number'}, 4)); + assert.isTrue(jsonSchemaValidator.validate({ type: 'number' }, 4)); }); it('should throw an error if the object is not valid', function() { assert.throws(function() { - jsonSchemaValidator.validate({'type': 'number'}, 'not a number'); + jsonSchemaValidator.validate({ type: 'number' }, 'not a number'); }, 'string value found, but a number is required'); }); @@ -43,7 +43,7 @@ describe('lib/utils/json_schema_validator', function() { it('should throw an error if no json object is passed in', function() { assert.throws(function() { - jsonSchemaValidator.validate({'type': 'number'}); + jsonSchemaValidator.validate({ type: 'number' }); }, sprintf(ERROR_MESSAGES.NO_JSON_PROVIDED, 'JSON_SCHEMA_VALIDATOR')); }); diff --git a/packages/optimizely-sdk/lib/utils/string_value_validator/index.js b/packages/optimizely-sdk/lib/utils/string_value_validator/index.js index f968a50ab..e2cd063e6 100644 --- a/packages/optimizely-sdk/lib/utils/string_value_validator/index.js +++ b/packages/optimizely-sdk/lib/utils/string_value_validator/index.js @@ -21,6 +21,6 @@ module.exports = { * @return {boolean} True for non-empty string, false otherwise */ validate: function(input) { - return typeof input === 'string' && input !== ''; - } + return typeof input === 'string' && input !== ''; + }, }; diff --git a/packages/optimizely-sdk/lib/utils/user_profile_service_validator/index.js b/packages/optimizely-sdk/lib/utils/user_profile_service_validator/index.js index 7178bf4a7..61e7f6172 100644 --- a/packages/optimizely-sdk/lib/utils/user_profile_service_validator/index.js +++ b/packages/optimizely-sdk/lib/utils/user_profile_service_validator/index.js @@ -32,9 +32,9 @@ module.exports = { */ validate: function(userProfileServiceInstance) { if (typeof userProfileServiceInstance.lookup !== 'function') { - throw new Error(sprintf(ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, MODULE_NAME, 'Missing function \'lookup\'')); + throw new Error(sprintf(ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, MODULE_NAME, "Missing function 'lookup'")); } else if (typeof userProfileServiceInstance.save !== 'function') { - throw new Error(sprintf(ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, MODULE_NAME, 'Missing function \'save\'')); + throw new Error(sprintf(ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, MODULE_NAME, "Missing function 'save'")); } return true; }, diff --git a/packages/optimizely-sdk/lib/utils/user_profile_service_validator/index.tests.js b/packages/optimizely-sdk/lib/utils/user_profile_service_validator/index.tests.js index bb35e819d..90d36df2d 100644 --- a/packages/optimizely-sdk/lib/utils/user_profile_service_validator/index.tests.js +++ b/packages/optimizely-sdk/lib/utils/user_profile_service_validator/index.tests.js @@ -24,42 +24,58 @@ var ERROR_MESSAGES = require('../enums').ERROR_MESSAGES; describe('lib/utils/user_profile_service_validator', function() { describe('APIs', function() { describe('validate', function() { - it('should throw if the instance does not provide a \'lookup\' function', function() { + it("should throw if the instance does not provide a 'lookup' function", function() { var missingLookupFunction = { - save: function() {} + save: function() {}, }; assert.throws(function() { userProfileServiceValidator.validate(missingLookupFunction); - }, sprintf(ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, 'USER_PROFILE_SERVICE_VALIDATOR', 'Missing function \'lookup\'')); + }, sprintf( + ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, + 'USER_PROFILE_SERVICE_VALIDATOR', + "Missing function 'lookup'" + )); }); - it('should throw if \'lookup\' is not a function', function() { + it("should throw if 'lookup' is not a function", function() { var lookupNotFunction = { save: function() {}, lookup: 'notGonnaWork', }; assert.throws(function() { userProfileServiceValidator.validate(lookupNotFunction); - }, sprintf(ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, 'USER_PROFILE_SERVICE_VALIDATOR', 'Missing function \'lookup\'')); + }, sprintf( + ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, + 'USER_PROFILE_SERVICE_VALIDATOR', + "Missing function 'lookup'" + )); }); - it('should throw if the instance does not provide a \'save\' function', function() { + it("should throw if the instance does not provide a 'save' function", function() { var missingSaveFunction = { - lookup: function() {} + lookup: function() {}, }; assert.throws(function() { userProfileServiceValidator.validate(missingSaveFunction); - }, sprintf(ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, 'USER_PROFILE_SERVICE_VALIDATOR', 'Missing function \'save\'')); + }, sprintf( + ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, + 'USER_PROFILE_SERVICE_VALIDATOR', + "Missing function 'save'" + )); }); - it('should throw if \'save\' is not a function', function() { + it("should throw if 'save' is not a function", function() { var saveNotFunction = { lookup: function() {}, save: 'notGonnaWork', }; assert.throws(function() { userProfileServiceValidator.validate(saveNotFunction); - }, sprintf(ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, 'USER_PROFILE_SERVICE_VALIDATOR', 'Missing function \'save\'')); + }, sprintf( + ERROR_MESSAGES.INVALID_USER_PROFILE_SERVICE, + 'USER_PROFILE_SERVICE_VALIDATOR', + "Missing function 'save'" + )); }); it('should return true if the instance is valid', function() { diff --git a/packages/optimizely-sdk/webpack.config.js b/packages/optimizely-sdk/webpack.config.js index 1578d4416..a7b545b61 100644 --- a/packages/optimizely-sdk/webpack.config.js +++ b/packages/optimizely-sdk/webpack.config.js @@ -13,7 +13,7 @@ module.exports = [ node: { // set to not polyfill setImmediate in promise polyfill // it is already wrapped in a typeof check - setImmediate: false + setImmediate: false, }, }, { @@ -28,7 +28,7 @@ module.exports = [ node: { // set to not polyfill setImmediate in promise polyfill // it is already wrapped in a typeof check - setImmediate: false + setImmediate: false, }, }, ];