From eabb3834daae8986f2ab6b32c8cb9d2afc0782d2 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Fri, 8 Mar 2019 20:27:55 +0500 Subject: [PATCH 01/17] feat(api): OnDecision Notification Listener for feature variable APIs. --- .../lib/core/decision_service/index.tests.js | 20 +- .../optimizely-sdk/lib/optimizely/index.js | 44 +- .../lib/optimizely/index.tests.js | 725 ++++++++++++++---- .../optimizely-sdk/lib/tests/test_data.js | 18 +- .../optimizely-sdk/lib/utils/enums/index.js | 10 +- 5 files changed, 654 insertions(+), 163 deletions(-) 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 1b8151be2..d51a3a9a0 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js @@ -729,22 +729,22 @@ describe('lib/core/decision_service', function() { 'variables': [ { 'id': '4792309476491264', - 'value': '10' + 'value': '40' }, { 'id': '5073784453201920', - 'value': 'false' + 'value': 'true' }, { 'id': '5636734406623232', - 'value': 'Buy me' + 'value': 'Buy me Later' }, { 'id': '6199684360044544', - 'value': '50.55' + 'value': '99.99' } ], - 'featureEnabled': true, + 'featureEnabled': false, 'key': 'control' } ], @@ -760,22 +760,22 @@ describe('lib/core/decision_service', function() { 'variables': [ { 'id': '4792309476491264', - 'value': '10' + 'value': '40' }, { 'id': '5073784453201920', - 'value': 'false' + 'value': 'true' }, { 'id': '5636734406623232', - 'value': 'Buy me' + 'value': 'Buy me Later' }, { 'id': '6199684360044544', - 'value': '50.55' + 'value': '99.99' } ], - 'featureEnabled': true, + 'featureEnabled': false, 'key': 'control' }, variation: { diff --git a/packages/optimizely-sdk/lib/optimizely/index.js b/packages/optimizely-sdk/lib/optimizely/index.js index a2090225d..f641e00e3 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.js +++ b/packages/optimizely-sdk/lib/optimizely/index.js @@ -34,6 +34,7 @@ var LOG_MESSAGES = enums.LOG_MESSAGES; var MODULE_NAME = 'OPTIMIZELY'; var DECISION_SOURCES = enums.DECISION_SOURCES; var FEATURE_VARIABLE_TYPES = enums.FEATURE_VARIABLE_TYPES; +var DECISION_INFO_TYPES = enums.DECISION_INFO_TYPES; /** * The Optimizely class @@ -563,17 +564,50 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK return null; } + var featureEnabled = false; + var variableValue = variable.defaultValue; var decision = this.decisionService.getVariationForFeature(featureFlag, userId, attributes); - var variableValue; + if (decision.variation !== null) { - variableValue = projectConfig.getVariableValueForVariation(this.configObj, variable, decision.variation, this.logger); - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId)); + featureEnabled = decision.variation.featureEnabled; + if (featureEnabled) { + variableValue = projectConfig.getVariableValueForVariation(this.configObj, variable, decision.variation, this.logger); + this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId)); + } else { + this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE, MODULE_NAME, featureFlag.key, userId, variableKey)); + } } else { - variableValue = variable.defaultValue; this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE, MODULE_NAME, userId, variableKey, featureFlag.key)); } - return projectConfig.getTypeCastValue(variableValue, variableType, this.logger); + var experimentKey = null; + var variationKey = null; + var decisionSource = decision.decisionSource || DECISION_SOURCES.ROLLOUT; + if (decisionSource === DECISION_SOURCES.EXPERIMENT) { + experimentKey = decision.experiment.key; + variationKey = decision.variation.key; + } + + var typeCastedValue = projectConfig.getTypeCastValue(variableValue, variableType, this.logger); + this.notificationCenter.sendNotifications( + enums.NOTIFICATION_TYPES.ON_DECISION, + { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: userId, + attributes: attributes, + decisionInfo: { + featureKey: featureKey, + featureEnabled: featureEnabled, + variableKey: variableKey, + variableValue: typeCastedValue, + variableType: variableType, + source: decisionSource, + sourceExperimentKey: experimentKey, + sourceVariationKey: variationKey + } + } + ); + return typeCastedValue; }; /** diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index 1585653db..fac16ec0a 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -40,6 +40,9 @@ var ERROR_MESSAGES = enums.ERROR_MESSAGES; var LOG_LEVEL = enums.LOG_LEVEL; var LOG_MESSAGES = enums.LOG_MESSAGES; var DECISION_SOURCES = enums.DECISION_SOURCES; +var NOTIFICATION_TYPES = enums.NOTIFICATION_TYPES; +var DECISION_INFO_TYPES = enums.DECISION_INFO_TYPES; +var FEATURE_VARIABLE_TYPES = enums.FEATURE_VARIABLE_TYPES; describe('lib/optimizely', function() { describe('constructor', function() { @@ -2590,6 +2593,8 @@ describe('lib/optimizely', function() { }); var optlyInstance; var clock; + var onDecisionListener; + beforeEach(function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', @@ -2608,6 +2613,7 @@ describe('lib/optimizely', function() { sandbox.stub(uuid, 'v4').returns('a68cf1ad-0393-4e18-af87-efe8f01a7c9c'); sandbox.stub(fns, 'currentTimestamp').returns(1509489766569); clock = sinon.useFakeTimers(new Date().getTime()); + onDecisionListener = sinon.spy(); }); afterEach(function() { @@ -3061,177 +3067,414 @@ describe('lib/optimizely', function() { }); describe('feature variable APIs', function() { - describe('bucketed into variation in an experiment with variable values', function() { - beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; - var variation = experiment.variations[0]; - sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ - experiment: experiment, - variation: variation, - decisionSource: DECISION_SOURCES.EXPERIMENT, + describe('bucketed into variation of an experiment with variable values', function() { + describe('when the variation is toggled ON', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; + var variation = experiment.variations[0]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.EXPERIMENT, + }); }); - }); - - it('returns the right value from getFeatureVariableBoolean', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, true); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "is_button_animated" of feature flag "test_feature_for_experiment" is true for user "user1"'); - }); - - it('returns the right value from getFeatureVariableDouble', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 20.25); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_width" of feature flag "test_feature_for_experiment" is 20.25 for user "user1"'); - }); - it('returns the right value from getFeatureVariableInteger', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 2); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "num_buttons" of feature flag "test_feature_for_experiment" is 2 for user "user1"'); - }); - - it('returns the right value from getFeatureVariableString', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 'Buy me NOW'); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_txt" of feature flag "test_feature_for_experiment" is Buy me NOW for user "user1"'); - }); - - it('returns null from getFeatureVariableBoolean when called with a non-boolean variable', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'button_width', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "boolean", but variable is of type "double". Use correct API to retrieve value. Returning None.'); - }); - - it('returns null from getFeatureVariableDouble when called with a non-double variable', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'is_button_animated', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "double", but variable is of type "boolean". Use correct API to retrieve value. Returning None.'); - }); - - it('returns null from getFeatureVariableInteger when called with a non-integer variable', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'button_width', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "integer", but variable is of type "double". Use correct API to retrieve value. Returning None.'); - }); - - it('returns null from getFeatureVariableString when called with a non-string variable', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'num_buttons', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "string", but variable is of type "integer". Use correct API to retrieve value. Returning None.'); - }); + it('returns the right value from getFeatureVariableBoolean', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, true); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "is_button_animated" of feature flag "test_feature_for_experiment" is true for user "user1"'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: true, + variableKey: 'is_button_animated', + variableValue: true, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'variation' + } + }); + }); - it('returns null from getFeatureVariableBoolean if user id is null', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', null, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + it('returns the right value from getFeatureVariableDouble', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 20.25); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_width" of feature flag "test_feature_for_experiment" is 20.25 for user "user1"'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: true, + variableKey: 'button_width', + variableValue: 20.25, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'variation' + } + }); + }); - it('returns null from getFeatureVariableBoolean if user id is undefined', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', undefined, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + it('returns the right value from getFeatureVariableInteger', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 2); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "num_buttons" of feature flag "test_feature_for_experiment" is 2 for user "user1"'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: true, + variableKey: 'num_buttons', + variableValue: 2, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'variation' + } + }); + }); - it('returns null from getFeatureVariableBoolean if user id is not provided', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + it('returns the right value from getFeatureVariableString', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Buy me NOW'); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_txt" of feature flag "test_feature_for_experiment" is Buy me NOW for user "user1"'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: true, + variableKey: 'button_txt', + variableValue: 'Buy me NOW', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'variation' + } + }); + }); }); - it('returns null from getFeatureVariableDouble if user id is null', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', null, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + describe('when the variation is toggled OFF', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; + var variation = experiment.variations[1]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.EXPERIMENT, + }); + optlyInstance.notificationCenter.addNotificationListener( + NOTIFICATION_TYPES.ON_DECISION, + onDecisionListener + ); + }); - it('returns null from getFeatureVariableDouble if user id is undefined', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', undefined, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + it('returns the default value from getFeatureVariableBoolean', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, false); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "is_button_animated".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'is_button_animated', + variableValue: false, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'control' + } + }); + }); - it('returns null from getFeatureVariableDouble if user id is not provided', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + it('returns the default value from getFeatureVariableDouble', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 50.55); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "button_width".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'button_width', + variableValue: 50.55, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'control' + } + }); + }); - it('returns null from getFeatureVariableInteger if user id is null', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', null, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + it('returns the default value from getFeatureVariableInteger', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 10); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "num_buttons".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'num_buttons', + variableValue: 10, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'control' + } + }); + }); - it('returns null from getFeatureVariableInteger if user id is undefined', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', undefined, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + it('returns the default value from getFeatureVariableString', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Buy me'); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "button_txt".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'button_txt', + variableValue: 'Buy me', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'control' + } + }); + }); }); + }); - it('returns null from getFeatureVariableInteger if user id is not provided', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + describe('bucketed into variation of a rollout with variable values', function() { + describe('when the variation is toggled ON', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap['594031']; + var variation = experiment.variations[0]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.ROLLOUT, + }); + }); - it('returns null from getFeatureVariableString if user id is null', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', null, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + it('should return the right value and send notification from getFeatureVariableBoolean', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, true); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "new_content" of feature flag "test_feature" is true for user "user1"'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: true, + variableKey: 'new_content', + variableValue: true, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); - it('returns null from getFeatureVariableString if user id is undefined', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', undefined, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + it('should return the right value and send notification from getFeatureVariableDouble', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 4.99); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "price" of feature flag "test_feature" is 4.99 for user "user1"'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: true, + variableKey: 'price', + variableValue: 4.99, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); - it('returns null from getFeatureVariableString if user id is not provided', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); + it('should return the right value and send notification from getFeatureVariableInteger', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 395); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "lasers" of feature flag "test_feature" is 395 for user "user1"'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: true, + variableKey: 'lasers', + variableValue: 395, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); - describe('type casting failures', function() { - describe('invalid boolean', function() { - beforeEach(function() { - sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('falsezzz'); + it('should return the right value and send notification from getFeatureVariableString', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Hello audience'); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "message" of feature flag "test_feature" is Hello audience for user "user1"'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: true, + variableKey: 'message', + variableValue: 'Hello audience', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } }); + }); + }); - it('should return null and log an error', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value falsezzz to type boolean, returning null.'); + describe('when the variation is toggled OFF', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap['594037']; + var variation = experiment.variations[0]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.ROLLOUT, }); }); - describe('invalid integer', function() { - beforeEach(function() { - sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('zzz123'); + it('should return the default value and send notification from getFeatureVariableBoolean', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, false); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "new_content".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: false, + variableKey: 'new_content', + variableValue: false, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } }); + }); - it('should return null and log an error', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value zzz123 to type integer, returning null.'); + it('should return the default value and send notification from getFeatureVariableDouble', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 14.99); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "price".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: false, + variableKey: 'price', + variableValue: 14.99, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } }); }); - describe('invalid double', function() { - beforeEach(function() { - sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('zzz44.55'); + it('should return the default value and send notification from getFeatureVariableInteger', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 400); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "lasers".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: false, + variableKey: 'lasers', + variableValue: 400, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } }); + }); - it('should return null and log an error', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value zzz44.55 to type double, returning null.'); + it('should return the default value and send notification from getFeatureVariableString', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Hello'); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "message".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: false, + variableKey: 'message', + variableValue: 'Hello', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } }); }); }); }); - describe('not bucketed into a variation', function() { + describe('not bucketed into an experiment or a rollout', function() { beforeEach(function() { sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: null, @@ -3241,27 +3484,235 @@ describe('lib/optimizely', function() { }); it('returns the variable default value from getFeatureVariableBoolean', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, false); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "is_button_animated" of feature flag "test_feature_for_experiment".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'is_button_animated', + variableValue: false, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); }); it('returns the variable default value from getFeatureVariableDouble', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 50.55); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "button_width" of feature flag "test_feature_for_experiment".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'button_width', + variableValue: 50.55, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); }); it('returns the variable default value from getFeatureVariableInteger', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 10); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "num_buttons" of feature flag "test_feature_for_experiment".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'num_buttons', + variableValue: 10, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); }); it('returns the variable default value from getFeatureVariableString', function() { + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "button_txt" of feature flag "test_feature_for_experiment".'); + sinon.assert.calledWith(onDecisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'button_txt', + variableValue: 'Buy me', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + }); + + it('returns null from getFeatureVariableBoolean when called with a non-boolean variable', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'button_width', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "boolean", but variable is of type "double". Use correct API to retrieve value. Returning None.'); + }); + + it('returns null from getFeatureVariableDouble when called with a non-double variable', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'is_button_animated', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "double", but variable is of type "boolean". Use correct API to retrieve value. Returning None.'); + }); + + it('returns null from getFeatureVariableInteger when called with a non-integer variable', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'button_width', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "integer", but variable is of type "double". Use correct API to retrieve value. Returning None.'); + }); + + it('returns null from getFeatureVariableString when called with a non-string variable', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'num_buttons', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "string", but variable is of type "integer". Use correct API to retrieve value. Returning None.'); + }); + + it('returns null from getFeatureVariableBoolean if user id is null', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', null, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableBoolean if user id is undefined', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', undefined, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableBoolean if user id is not provided', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableDouble if user id is null', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', null, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableDouble if user id is undefined', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', undefined, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableDouble if user id is not provided', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableInteger if user id is null', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', null, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableInteger if user id is undefined', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', undefined, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableInteger if user id is not provided', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableString if user id is null', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', null, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableString if user id is undefined', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', undefined, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableString if user id is not provided', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + describe('type casting failures', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; + var variation = experiment.variations[0]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.EXPERIMENT, + }); + }); + + describe('invalid boolean', function() { + beforeEach(function() { + sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('falsezzz'); + }); + + it('should return null and log an error', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value falsezzz to type boolean, returning null.'); + }); + }); + + describe('invalid integer', function() { + beforeEach(function() { + sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('zzz123'); + }); + + it('should return null and log an error', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value zzz123 to type integer, returning null.'); + }); + }); + + describe('invalid double', function() { + beforeEach(function() { + sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('zzz44.55'); + }); + + it('should return null and log an error', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value zzz44.55 to type double, returning null.'); + }); }); }); diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index bfac5e239..e854dc287 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -482,23 +482,23 @@ var configWithFeatures = { { 'key': 'control', 'id': '594097', - 'featureEnabled': true, + 'featureEnabled': false, 'variables': [ { 'id': '4792309476491264', - 'value': '10' + 'value': '40' }, { 'id': '5073784453201920', - 'value': 'false' + 'value': 'true' }, { 'id': '5636734406623232', - 'value': 'Buy me' + 'value': 'Buy me Later' }, { 'id': '6199684360044544', - 'value': '50.55' + 'value': '99.99' } ] } @@ -1360,19 +1360,19 @@ var datafileWithFeaturesExpectedData = { }, 594097: { 4792309476491264: { - 'value': '10', + 'value': '40', 'id': '4792309476491264' }, 5073784453201920: { - 'value': 'false', + 'value': 'true', 'id': '5073784453201920' }, 5636734406623232: { - 'value': 'Buy me', + 'value': 'Buy me Later', 'id': '5636734406623232' }, 6199684360044544: { - 'value': '50.55', + 'value': '99.99', 'id': '6199684360044544' }, }, diff --git a/packages/optimizely-sdk/lib/utils/enums/index.js b/packages/optimizely-sdk/lib/utils/enums/index.js index 91bc01871..8ae4a6a4e 100644 --- a/packages/optimizely-sdk/lib/utils/enums/index.js +++ b/packages/optimizely-sdk/lib/utils/enums/index.js @@ -116,6 +116,7 @@ exports.LOG_MESSAGES = { 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".', 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.', @@ -175,8 +176,13 @@ exports.NODE_CLIENT_VERSION = '3.1.0-beta1'; * - logEvent {Object} */ exports.NOTIFICATION_TYPES = { - ACTIVATE: 'ACTIVATE:experiment, user_id,attributes, variation, event', - TRACK: 'TRACK:event_key, user_id, attributes, event_tags, event', + ACTIVATE: 'ACTIVATE:experiment, userId, attributes, variation, logEvent', + TRACK: 'TRACK:eventKey, userId, attributes, eventTags, logEvent', + ON_DECISION: 'ON_DECISION:type, userId, attributes, decisionInfo', +}; + +exports.DECISION_INFO_TYPES = { + FEATURE_VARIABLE: 'feature_variable', }; /* From f401d2192d6325ab3a1731637bee8c901118be38 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Tue, 12 Mar 2019 18:54:25 +0500 Subject: [PATCH 02/17] Implement suggested changes. --- .../optimizely-sdk/lib/optimizely/index.js | 7 ++-- .../lib/optimizely/index.tests.js | 40 +++++++++---------- .../optimizely-sdk/lib/utils/enums/index.js | 13 ++++-- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/packages/optimizely-sdk/lib/optimizely/index.js b/packages/optimizely-sdk/lib/optimizely/index.js index f641e00e3..88fb1a9d3 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.js +++ b/packages/optimizely-sdk/lib/optimizely/index.js @@ -570,11 +570,12 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK if (decision.variation !== null) { featureEnabled = decision.variation.featureEnabled; - if (featureEnabled) { + if (featureEnabled === true) { variableValue = projectConfig.getVariableValueForVariation(this.configObj, variable, decision.variation, this.logger); this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId)); } else { this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE, MODULE_NAME, featureFlag.key, userId, variableKey)); + featureEnabled = false; } } else { this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE, MODULE_NAME, userId, variableKey, featureFlag.key)); @@ -590,11 +591,11 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK var typeCastedValue = projectConfig.getTypeCastValue(variableValue, variableType, this.logger); this.notificationCenter.sendNotifications( - enums.NOTIFICATION_TYPES.ON_DECISION, + enums.NOTIFICATION_TYPES.DECISION, { type: DECISION_INFO_TYPES.FEATURE_VARIABLE, userId: userId, - attributes: attributes, + attributes: attributes || {}, decisionInfo: { featureKey: featureKey, featureEnabled: featureEnabled, diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index fac16ec0a..d8f614a80 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -3080,7 +3080,7 @@ describe('lib/optimizely', function() { }); it('returns the right value from getFeatureVariableBoolean', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, true); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "is_button_animated" of feature flag "test_feature_for_experiment" is true for user "user1"'); @@ -3102,7 +3102,7 @@ describe('lib/optimizely', function() { }); it('returns the right value from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 20.25); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_width" of feature flag "test_feature_for_experiment" is 20.25 for user "user1"'); @@ -3124,7 +3124,7 @@ describe('lib/optimizely', function() { }); it('returns the right value from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 2); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "num_buttons" of feature flag "test_feature_for_experiment" is 2 for user "user1"'); @@ -3146,7 +3146,7 @@ describe('lib/optimizely', function() { }); it('returns the right value from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me NOW'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_txt" of feature flag "test_feature_for_experiment" is Buy me NOW for user "user1"'); @@ -3178,7 +3178,7 @@ describe('lib/optimizely', function() { decisionSource: DECISION_SOURCES.EXPERIMENT, }); optlyInstance.notificationCenter.addNotificationListener( - NOTIFICATION_TYPES.ON_DECISION, + NOTIFICATION_TYPES.DECISION, onDecisionListener ); }); @@ -3205,7 +3205,7 @@ describe('lib/optimizely', function() { }); it('returns the default value from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 50.55); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "button_width".'); @@ -3227,7 +3227,7 @@ describe('lib/optimizely', function() { }); it('returns the default value from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 10); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "num_buttons".'); @@ -3249,7 +3249,7 @@ describe('lib/optimizely', function() { }); it('returns the default value from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "button_txt".'); @@ -3285,7 +3285,7 @@ describe('lib/optimizely', function() { }); it('should return the right value and send notification from getFeatureVariableBoolean', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, true); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "new_content" of feature flag "test_feature" is true for user "user1"'); @@ -3307,7 +3307,7 @@ describe('lib/optimizely', function() { }); it('should return the right value and send notification from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 4.99); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "price" of feature flag "test_feature" is 4.99 for user "user1"'); @@ -3329,7 +3329,7 @@ describe('lib/optimizely', function() { }); it('should return the right value and send notification from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 395); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "lasers" of feature flag "test_feature" is 395 for user "user1"'); @@ -3351,7 +3351,7 @@ describe('lib/optimizely', function() { }); it('should return the right value and send notification from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Hello audience'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "message" of feature flag "test_feature" is Hello audience for user "user1"'); @@ -3385,7 +3385,7 @@ describe('lib/optimizely', function() { }); it('should return the default value and send notification from getFeatureVariableBoolean', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, false); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "new_content".'); @@ -3407,7 +3407,7 @@ describe('lib/optimizely', function() { }); it('should return the default value and send notification from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 14.99); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "price".'); @@ -3429,7 +3429,7 @@ describe('lib/optimizely', function() { }); it('should return the default value and send notification from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 400); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "lasers".'); @@ -3451,7 +3451,7 @@ describe('lib/optimizely', function() { }); it('should return the default value and send notification from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Hello'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "message".'); @@ -3484,7 +3484,7 @@ describe('lib/optimizely', function() { }); it('returns the variable default value from getFeatureVariableBoolean', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, false); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "is_button_animated" of feature flag "test_feature_for_experiment".'); @@ -3506,7 +3506,7 @@ describe('lib/optimizely', function() { }); it('returns the variable default value from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 50.55); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "button_width" of feature flag "test_feature_for_experiment".'); @@ -3528,7 +3528,7 @@ describe('lib/optimizely', function() { }); it('returns the variable default value from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 10); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "num_buttons" of feature flag "test_feature_for_experiment".'); @@ -3550,7 +3550,7 @@ describe('lib/optimizely', function() { }); it('returns the variable default value from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.ON_DECISION, onDecisionListener); + optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "button_txt" of feature flag "test_feature_for_experiment".'); diff --git a/packages/optimizely-sdk/lib/utils/enums/index.js b/packages/optimizely-sdk/lib/utils/enums/index.js index 8ae4a6a4e..89de68908 100644 --- a/packages/optimizely-sdk/lib/utils/enums/index.js +++ b/packages/optimizely-sdk/lib/utils/enums/index.js @@ -174,11 +174,18 @@ exports.NODE_CLIENT_VERSION = '3.1.0-beta1'; * - attributes {Object|undefined} * - eventTags {Object|undefined} * - logEvent {Object} + * DECISION: A decision is made in the system. i.e. user activation, + * feature access or feature-variable value retrieval + * Callbacks will receive an object argument with the following properties: + * - type {string} + * - userId {string} + * - attributes {Object|undefined} + * - decisionInfo {Object|undefined} */ exports.NOTIFICATION_TYPES = { - ACTIVATE: 'ACTIVATE:experiment, userId, attributes, variation, logEvent', - TRACK: 'TRACK:eventKey, userId, attributes, eventTags, logEvent', - ON_DECISION: 'ON_DECISION:type, userId, attributes, decisionInfo', + ACTIVATE: 'ACTIVATE:experiment, user_id,attributes, variation, event', + TRACK: 'TRACK:event_key, user_id, attributes, event_tags, event', + DECISION: 'DECISION:type, userId, attributes, decisionInfo', }; exports.DECISION_INFO_TYPES = { From 167294cdeaee1ee6ba005f7c559ca874939fa2cd Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Tue, 12 Mar 2019 20:43:23 +0500 Subject: [PATCH 03/17] Separate out the unit tests for decision listener. --- .../lib/optimizely/index.tests.js | 894 +++++++++++------- 1 file changed, 534 insertions(+), 360 deletions(-) diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index d8f614a80..2c9bc3052 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -40,7 +40,6 @@ var ERROR_MESSAGES = enums.ERROR_MESSAGES; var LOG_LEVEL = enums.LOG_LEVEL; var LOG_MESSAGES = enums.LOG_MESSAGES; var DECISION_SOURCES = enums.DECISION_SOURCES; -var NOTIFICATION_TYPES = enums.NOTIFICATION_TYPES; var DECISION_INFO_TYPES = enums.DECISION_INFO_TYPES; var FEATURE_VARIABLE_TYPES = enums.FEATURE_VARIABLE_TYPES; @@ -2067,15 +2066,15 @@ describe('lib/optimizely', function() { }); describe('notification listeners', function() { - var decisionListener; + var activateListener; var trackListener; - var decisionListener2; + var activateListener2; var trackListener2; beforeEach(function() { - decisionListener = sinon.spy(); + activateListener = sinon.spy(); trackListener = sinon.spy(); - decisionListener2 = sinon.spy(); + activateListener2 = sinon.spy(); trackListener2 = sinon.spy(); bucketStub.returns('111129'); sinon.stub(fns, 'currentTimestamp').returns(1509489766569); @@ -2088,11 +2087,11 @@ describe('lib/optimizely', function() { it('should call a listener added for activate when activate is called', function() { optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); var variationKey = optlyInstance.activate('testExperiment', 'testUser'); assert.strictEqual(variationKey, 'variation'); - sinon.assert.calledOnce(decisionListener); + sinon.assert.calledOnce(activateListener); }); it('should call a listener added for track when track is called', function() { @@ -2108,12 +2107,12 @@ describe('lib/optimizely', function() { it('should not call a removed activate listener when activate is called', function() { var listenerId = optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); optlyInstance.notificationCenter.removeNotificationListener(listenerId); var variationKey = optlyInstance.activate('testExperiment', 'testUser'); assert.strictEqual(variationKey, 'variation'); - sinon.assert.notCalled(decisionListener); + sinon.assert.notCalled(activateListener); }); it('should not call a removed track listener when track is called', function() { @@ -2130,7 +2129,7 @@ describe('lib/optimizely', function() { it('removeNotificationListener should only remove the listener with the argument ID', function() { optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); var trackListenerId = optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.TRACK, @@ -2139,13 +2138,13 @@ describe('lib/optimizely', function() { optlyInstance.notificationCenter.removeNotificationListener(trackListenerId); optlyInstance.activate('testExperiment', 'testUser'); optlyInstance.track('testEvent', 'testUser'); - sinon.assert.calledOnce(decisionListener); + sinon.assert.calledOnce(activateListener); }); it('should clear all notification listeners when clearAllNotificationListeners is called', function() { optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.TRACK, @@ -2155,14 +2154,14 @@ describe('lib/optimizely', function() { optlyInstance.activate('testExperiment', 'testUser'); optlyInstance.track('testEvent', 'testUser'); - sinon.assert.notCalled(decisionListener); + sinon.assert.notCalled(activateListener); sinon.assert.notCalled(trackListener); }); it('should clear listeners of certain notification type when clearNotificationListeners is called', function() { optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.TRACK, @@ -2172,47 +2171,47 @@ describe('lib/optimizely', function() { optlyInstance.activate('testExperiment', 'testUser'); optlyInstance.track('testEvent', 'testUser'); - sinon.assert.notCalled(decisionListener); + sinon.assert.notCalled(activateListener); sinon.assert.calledOnce(trackListener); }); it('should only call the listener once after the same listener was added twice', function() { optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); optlyInstance.activate('testExperiment', 'testUser'); - sinon.assert.calledOnce(decisionListener); + sinon.assert.calledOnce(activateListener); }); it('should not add a listener with an invalid type argument', function() { var listenerId = optlyInstance.notificationCenter.addNotificationListener( 'not a notification type', - decisionListener + activateListener ); assert.strictEqual(listenerId, -1); optlyInstance.activate('testExperiment', 'testUser'); - sinon.assert.notCalled(decisionListener); + sinon.assert.notCalled(activateListener); optlyInstance.track('testEvent', 'testUser'); - sinon.assert.notCalled(decisionListener); + sinon.assert.notCalled(activateListener); }); it('should call multiple notification listeners for activate when activate is called', function() { optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener2 + activateListener2 ); optlyInstance.activate('testExperiment', 'testUser'); - sinon.assert.calledOnce(decisionListener); - sinon.assert.calledOnce(decisionListener2); + sinon.assert.calledOnce(activateListener); + sinon.assert.calledOnce(activateListener2); }); it('should call multiple notification listeners for track when track is called', function() { @@ -2233,7 +2232,7 @@ describe('lib/optimizely', function() { it('should pass the correct arguments to an activate listener when activate is called', function() { optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); optlyInstance.activate('testExperiment', 'testUser'); var expectedImpressionEvent = { @@ -2282,7 +2281,7 @@ describe('lib/optimizely', function() { variation: instanceExperiments[0].variations[1], logEvent: expectedImpressionEvent, }; - sinon.assert.calledWith(decisionListener, expectedArgument); + sinon.assert.calledWith(activateListener, expectedArgument); }); it('should pass the correct arguments to an activate listener when activate is called with attributes', function() { @@ -2291,7 +2290,7 @@ describe('lib/optimizely', function() { }; optlyInstance.notificationCenter.addNotificationListener( enums.NOTIFICATION_TYPES.ACTIVATE, - decisionListener + activateListener ); optlyInstance.activate('testExperiment', 'testUser', attributes); var expectedImpressionEvent = { @@ -2347,7 +2346,7 @@ describe('lib/optimizely', function() { variation: instanceExperiments[0].variations[1], logEvent: expectedImpressionEvent, }; - sinon.assert.calledWith(decisionListener, expectedArgument); + sinon.assert.calledWith(activateListener, expectedArgument); }); it('should pass the correct arguments to a track listener when track is called', function() { @@ -2522,6 +2521,504 @@ describe('lib/optimizely', function() { }; sinon.assert.calledWith(trackListener, expectedArgument); }); + + describe('Decision Listener', function() { + var decisionListener; + beforeEach(function() { + decisionListener = sinon.spy(); + }); + + describe('feature management', function() { + var sandbox = sinon.sandbox.create(); + + beforeEach(function() { + optlyInstance = new Optimizely({ + clientEngine: 'node-sdk', + datafile: testData.getTestProjectConfigWithFeatures(), + eventBuilder: eventBuilder, + errorHandler: errorHandler, + eventDispatcher: eventDispatcher, + jsonSchemaValidator: jsonSchemaValidator, + logger: createdLogger, + isValidInstance: true, + }); + + optlyInstance.notificationCenter.addNotificationListener( + enums.NOTIFICATION_TYPES.DECISION, + decisionListener + ); + }); + + afterEach(function() { + sandbox.restore(); + }); + + describe('feature variable APIs', function() { + describe('bucketed into variation of an experiment with variable values', function() { + describe('when the variation is toggled ON', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; + var variation = experiment.variations[0]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.EXPERIMENT, + }); + }); + + it('returns the right value from getFeatureVariableBoolean and send notification', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, true); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: true, + variableKey: 'is_button_animated', + variableValue: true, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'variation' + } + }); + }); + + it('returns the right value from getFeatureVariableDouble and send notification', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 20.25); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: true, + variableKey: 'button_width', + variableValue: 20.25, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'variation' + } + }); + }); + + it('returns the right value from getFeatureVariableInteger and send notification', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 2); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: true, + variableKey: 'num_buttons', + variableValue: 2, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'variation' + } + }); + }); + + it('returns the right value from getFeatureVariableString and send notification', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Buy me NOW'); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: true, + variableKey: 'button_txt', + variableValue: 'Buy me NOW', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'variation' + } + }); + }); + }); + + describe('when the variation is toggled OFF', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; + var variation = experiment.variations[1]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.EXPERIMENT, + }); + }); + + it('returns the default value from getFeatureVariableBoolean and send notification', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, false); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'is_button_animated', + variableValue: false, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'control' + } + }); + }); + + it('returns the default value from getFeatureVariableDouble and send notification', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 50.55); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'button_width', + variableValue: 50.55, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'control' + } + }); + }); + + it('returns the default value from getFeatureVariableInteger and send notification', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 10); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'num_buttons', + variableValue: 10, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'control' + } + }); + }); + + it('returns the default value from getFeatureVariableString and send notification', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Buy me'); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'button_txt', + variableValue: 'Buy me', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.EXPERIMENT, + sourceExperimentKey: 'testing_my_feature', + sourceVariationKey: 'control' + } + }); + }); + }); + }); + + describe('bucketed into variation of a rollout with variable values', function() { + describe('when the variation is toggled ON', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap['594031']; + var variation = experiment.variations[0]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.ROLLOUT, + }); + }); + + it('should return the right value from getFeatureVariableBoolean and send notification', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, true); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: true, + variableKey: 'new_content', + variableValue: true, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('should return the right value from getFeatureVariableDouble and send notification', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 4.99); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: true, + variableKey: 'price', + variableValue: 4.99, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('should return the right value from getFeatureVariableInteger and send notification', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 395); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: true, + variableKey: 'lasers', + variableValue: 395, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('should return the right value from getFeatureVariableString and send notification', function() { + var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Hello audience'); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: true, + variableKey: 'message', + variableValue: 'Hello audience', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + }); + + describe('when the variation is toggled OFF', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap['594037']; + var variation = experiment.variations[0]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.ROLLOUT, + }); + }); + + it('should return the default value from getFeatureVariableBoolean and send notification', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, false); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: false, + variableKey: 'new_content', + variableValue: false, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('should return the default value from getFeatureVariableDouble and send notification', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 14.99); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: false, + variableKey: 'price', + variableValue: 14.99, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('should return the default value from getFeatureVariableInteger and send notification', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 400); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: false, + variableKey: 'lasers', + variableValue: 400, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('should return the default value from getFeatureVariableString and send notification', function() { + var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Hello'); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature', + featureEnabled: false, + variableKey: 'message', + variableValue: 'Hello', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + }); + }); + + describe('not bucketed into an experiment or a rollout', function() { + beforeEach(function() { + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: null, + variation: null, + decisionSource: null, + }); + }); + + it('returns the variable default value from getFeatureVariableBoolean and send notification', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, false); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'is_button_animated', + variableValue: false, + variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('returns the variable default value from getFeatureVariableDouble and send notification', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 50.55); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'button_width', + variableValue: 50.55, + variableType: FEATURE_VARIABLE_TYPES.DOUBLE, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('returns the variable default value from getFeatureVariableInteger and send notification', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 10); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "num_buttons" of feature flag "test_feature_for_experiment".'); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'num_buttons', + variableValue: 10, + variableType: FEATURE_VARIABLE_TYPES.INTEGER, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + + it('returns the variable default value from getFeatureVariableString', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Buy me'); + sinon.assert.calledWith(decisionListener, { + type: DECISION_INFO_TYPES.FEATURE_VARIABLE, + userId: 'user1', + attributes: { test_attribute: 'test_value' }, + decisionInfo: { + featureKey: 'test_feature_for_experiment', + featureEnabled: false, + variableKey: 'button_txt', + variableValue: 'Buy me', + variableType: FEATURE_VARIABLE_TYPES.STRING, + source: DECISION_SOURCES.ROLLOUT, + sourceExperimentKey: null, + sourceVariationKey: null + } + }); + }); + }); + }); + }); + }); }); }); @@ -3080,91 +3577,27 @@ describe('lib/optimizely', function() { }); it('returns the right value from getFeatureVariableBoolean', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, true); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "is_button_animated" of feature flag "test_feature_for_experiment" is true for user "user1"'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: true, - variableKey: 'is_button_animated', - variableValue: true, - variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, - source: DECISION_SOURCES.EXPERIMENT, - sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'variation' - } - }); }); it('returns the right value from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 20.25); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_width" of feature flag "test_feature_for_experiment" is 20.25 for user "user1"'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: true, - variableKey: 'button_width', - variableValue: 20.25, - variableType: FEATURE_VARIABLE_TYPES.DOUBLE, - source: DECISION_SOURCES.EXPERIMENT, - sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'variation' - } - }); }); it('returns the right value from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 2); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "num_buttons" of feature flag "test_feature_for_experiment" is 2 for user "user1"'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: true, - variableKey: 'num_buttons', - variableValue: 2, - variableType: FEATURE_VARIABLE_TYPES.INTEGER, - source: DECISION_SOURCES.EXPERIMENT, - sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'variation' - } - }); }); it('returns the right value from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me NOW'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_txt" of feature flag "test_feature_for_experiment" is Buy me NOW for user "user1"'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: true, - variableKey: 'button_txt', - variableValue: 'Buy me NOW', - variableType: FEATURE_VARIABLE_TYPES.STRING, - source: DECISION_SOURCES.EXPERIMENT, - sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'variation' - } - }); }); }); @@ -3177,97 +3610,30 @@ describe('lib/optimizely', function() { variation: variation, decisionSource: DECISION_SOURCES.EXPERIMENT, }); - optlyInstance.notificationCenter.addNotificationListener( - NOTIFICATION_TYPES.DECISION, - onDecisionListener - ); }); it('returns the default value from getFeatureVariableBoolean', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, false); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "is_button_animated".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: false, - variableKey: 'is_button_animated', - variableValue: false, - variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, - source: DECISION_SOURCES.EXPERIMENT, - sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' - } - }); }); it('returns the default value from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 50.55); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "button_width".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: false, - variableKey: 'button_width', - variableValue: 50.55, - variableType: FEATURE_VARIABLE_TYPES.DOUBLE, - source: DECISION_SOURCES.EXPERIMENT, - sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' - } - }); }); it('returns the default value from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 10); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "num_buttons".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: false, - variableKey: 'num_buttons', - variableValue: 10, - variableType: FEATURE_VARIABLE_TYPES.INTEGER, - source: DECISION_SOURCES.EXPERIMENT, - sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' - } - }); }); it('returns the default value from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "button_txt".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: false, - variableKey: 'button_txt', - variableValue: 'Buy me', - variableType: FEATURE_VARIABLE_TYPES.STRING, - source: DECISION_SOURCES.EXPERIMENT, - sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' - } - }); }); }); }); @@ -3284,92 +3650,28 @@ describe('lib/optimizely', function() { }); }); - it('should return the right value and send notification from getFeatureVariableBoolean', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); + it('should return the right value from getFeatureVariableBoolean', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, true); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "new_content" of feature flag "test_feature" is true for user "user1"'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature', - featureEnabled: true, - variableKey: 'new_content', - variableValue: true, - variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); - it('should return the right value and send notification from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); + it('should return the right value from getFeatureVariableDouble', function() { var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 4.99); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "price" of feature flag "test_feature" is 4.99 for user "user1"'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature', - featureEnabled: true, - variableKey: 'price', - variableValue: 4.99, - variableType: FEATURE_VARIABLE_TYPES.DOUBLE, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); - it('should return the right value and send notification from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); + it('should return the right value from getFeatureVariableInteger', function() { var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 395); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "lasers" of feature flag "test_feature" is 395 for user "user1"'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature', - featureEnabled: true, - variableKey: 'lasers', - variableValue: 395, - variableType: FEATURE_VARIABLE_TYPES.INTEGER, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); - it('should return the right value and send notification from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); + it('should return the right value from getFeatureVariableString', function() { var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Hello audience'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "message" of feature flag "test_feature" is Hello audience for user "user1"'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature', - featureEnabled: true, - variableKey: 'message', - variableValue: 'Hello audience', - variableType: FEATURE_VARIABLE_TYPES.STRING, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); }); @@ -3384,92 +3686,28 @@ describe('lib/optimizely', function() { }); }); - it('should return the default value and send notification from getFeatureVariableBoolean', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); + it('should return the default value from getFeatureVariableBoolean', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, false); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "new_content".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature', - featureEnabled: false, - variableKey: 'new_content', - variableValue: false, - variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); - it('should return the default value and send notification from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); + it('should return the default value from getFeatureVariableDouble', function() { var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 14.99); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "price".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature', - featureEnabled: false, - variableKey: 'price', - variableValue: 14.99, - variableType: FEATURE_VARIABLE_TYPES.DOUBLE, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); - it('should return the default value and send notification from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); + it('should return the default value from getFeatureVariableInteger', function() { var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 400); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "lasers".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature', - featureEnabled: false, - variableKey: 'lasers', - variableValue: 400, - variableType: FEATURE_VARIABLE_TYPES.INTEGER, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); - it('should return the default value and send notification from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); + it('should return the default value from getFeatureVariableString', function() { var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Hello'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "message".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature', - featureEnabled: false, - variableKey: 'message', - variableValue: 'Hello', - variableType: FEATURE_VARIABLE_TYPES.STRING, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); }); }); @@ -3484,91 +3722,27 @@ describe('lib/optimizely', function() { }); it('returns the variable default value from getFeatureVariableBoolean', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, false); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "is_button_animated" of feature flag "test_feature_for_experiment".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: false, - variableKey: 'is_button_animated', - variableValue: false, - variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); it('returns the variable default value from getFeatureVariableDouble', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 50.55); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "button_width" of feature flag "test_feature_for_experiment".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: false, - variableKey: 'button_width', - variableValue: 50.55, - variableType: FEATURE_VARIABLE_TYPES.DOUBLE, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); it('returns the variable default value from getFeatureVariableInteger', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 10); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "num_buttons" of feature flag "test_feature_for_experiment".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: false, - variableKey: 'num_buttons', - variableValue: 10, - variableType: FEATURE_VARIABLE_TYPES.INTEGER, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); it('returns the variable default value from getFeatureVariableString', function() { - optlyInstance.notificationCenter.addNotificationListener(NOTIFICATION_TYPES.DECISION, onDecisionListener); var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me'); sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "button_txt" of feature flag "test_feature_for_experiment".'); - sinon.assert.calledWith(onDecisionListener, { - type: DECISION_INFO_TYPES.FEATURE_VARIABLE, - userId: 'user1', - attributes: { test_attribute: 'test_value' }, - decisionInfo: { - featureKey: 'test_feature_for_experiment', - featureEnabled: false, - variableKey: 'button_txt', - variableValue: 'Buy me', - variableType: FEATURE_VARIABLE_TYPES.STRING, - source: DECISION_SOURCES.ROLLOUT, - sourceExperimentKey: null, - sourceVariationKey: null - } - }); }); }); From 26f63e38918c3cda6a7ca01380017ba6b12d56fc Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Tue, 12 Mar 2019 22:14:15 +0500 Subject: [PATCH 04/17] Reverted existing unit tests. --- .../lib/optimizely/index.tests.js | 404 ++++++------------ 1 file changed, 141 insertions(+), 263 deletions(-) diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index 2c9bc3052..7072c67cb 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -3564,155 +3564,177 @@ describe('lib/optimizely', function() { }); describe('feature variable APIs', function() { - describe('bucketed into variation of an experiment with variable values', function() { - describe('when the variation is toggled ON', function() { - beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; - var variation = experiment.variations[0]; - sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ - experiment: experiment, - variation: variation, - decisionSource: DECISION_SOURCES.EXPERIMENT, - }); + describe('bucketed into variation in an experiment with variable values', function() { + beforeEach(function() { + var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; + var variation = experiment.variations[0]; + sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ + experiment: experiment, + variation: variation, + decisionSource: DECISION_SOURCES.EXPERIMENT, }); + }); - it('returns the right value from getFeatureVariableBoolean', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, true); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "is_button_animated" of feature flag "test_feature_for_experiment" is true for user "user1"'); - }); + it('returns the right value from getFeatureVariableBoolean', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, true); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "is_button_animated" of feature flag "test_feature_for_experiment" is true for user "user1"'); + }); - it('returns the right value from getFeatureVariableDouble', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 20.25); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_width" of feature flag "test_feature_for_experiment" is 20.25 for user "user1"'); - }); + it('returns the right value from getFeatureVariableDouble', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 20.25); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_width" of feature flag "test_feature_for_experiment" is 20.25 for user "user1"'); + }); - it('returns the right value from getFeatureVariableInteger', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 2); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "num_buttons" of feature flag "test_feature_for_experiment" is 2 for user "user1"'); - }); + it('returns the right value from getFeatureVariableInteger', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 2); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "num_buttons" of feature flag "test_feature_for_experiment" is 2 for user "user1"'); + }); - it('returns the right value from getFeatureVariableString', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 'Buy me NOW'); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_txt" of feature flag "test_feature_for_experiment" is Buy me NOW for user "user1"'); - }); + it('returns the right value from getFeatureVariableString', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); + assert.strictEqual(result, 'Buy me NOW'); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "button_txt" of feature flag "test_feature_for_experiment" is Buy me NOW for user "user1"'); }); - describe('when the variation is toggled OFF', function() { - beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; - var variation = experiment.variations[1]; - sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ - experiment: experiment, - variation: variation, - decisionSource: DECISION_SOURCES.EXPERIMENT, - }); - }); + it('returns null from getFeatureVariableBoolean when called with a non-boolean variable', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'button_width', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "boolean", but variable is of type "double". Use correct API to retrieve value. Returning None.'); + }); - it('returns the default value from getFeatureVariableBoolean', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, false); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "is_button_animated".'); - }); + it('returns null from getFeatureVariableDouble when called with a non-double variable', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'is_button_animated', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "double", but variable is of type "boolean". Use correct API to retrieve value. Returning None.'); + }); - it('returns the default value from getFeatureVariableDouble', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 50.55); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "button_width".'); - }); + it('returns null from getFeatureVariableInteger when called with a non-integer variable', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'button_width', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "integer", but variable is of type "double". Use correct API to retrieve value. Returning None.'); + }); - it('returns the default value from getFeatureVariableInteger', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 10); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "num_buttons".'); - }); + it('returns null from getFeatureVariableString when called with a non-string variable', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'num_buttons', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "string", but variable is of type "integer". Use correct API to retrieve value. Returning None.'); + }); - it('returns the default value from getFeatureVariableString', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 'Buy me'); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature_for_experiment is not enabled for user user1. Returning default value for variable "button_txt".'); - }); + it('returns null from getFeatureVariableBoolean if user id is null', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', null, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); }); - }); - describe('bucketed into variation of a rollout with variable values', function() { - describe('when the variation is toggled ON', function() { - beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap['594031']; - var variation = experiment.variations[0]; - sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ - experiment: experiment, - variation: variation, - decisionSource: DECISION_SOURCES.ROLLOUT, - }); - }); + it('returns null from getFeatureVariableBoolean if user id is undefined', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', undefined, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); - it('should return the right value from getFeatureVariableBoolean', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, true); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "new_content" of feature flag "test_feature" is true for user "user1"'); - }); + it('returns null from getFeatureVariableBoolean if user id is not provided', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); - it('should return the right value from getFeatureVariableDouble', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 4.99); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "price" of feature flag "test_feature" is 4.99 for user "user1"'); - }); + it('returns null from getFeatureVariableDouble if user id is null', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', null, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); - it('should return the right value from getFeatureVariableInteger', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 395); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "lasers" of feature flag "test_feature" is 395 for user "user1"'); - }); + it('returns null from getFeatureVariableDouble if user id is undefined', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', undefined, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); - it('should return the right value from getFeatureVariableString', function() { - var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 'Hello audience'); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Value for variable "message" of feature flag "test_feature" is Hello audience for user "user1"'); - }); + it('returns null from getFeatureVariableDouble if user id is not provided', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); }); - describe('when the variation is toggled OFF', function() { - beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap['594037']; - var variation = experiment.variations[0]; - sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ - experiment: experiment, - variation: variation, - decisionSource: DECISION_SOURCES.ROLLOUT, + it('returns null from getFeatureVariableInteger if user id is null', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', null, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableInteger if user id is undefined', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', undefined, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableInteger if user id is not provided', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableString if user id is null', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', null, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableString if user id is undefined', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', undefined, { test_attribute: 'test_value' }); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + it('returns null from getFeatureVariableString if user id is not provided', function() { + var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); + }); + + describe('type casting failures', function() { + describe('invalid boolean', function() { + beforeEach(function() { + sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('falsezzz'); }); - }); - it('should return the default value from getFeatureVariableBoolean', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, false); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "new_content".'); + it('should return null and log an error', function() { + var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value falsezzz to type boolean, returning null.'); + }); }); - it('should return the default value from getFeatureVariableDouble', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 14.99); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "price".'); - }); + describe('invalid integer', function() { + beforeEach(function() { + sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('zzz123'); + }); - it('should return the default value from getFeatureVariableInteger', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 400); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "lasers".'); + it('should return null and log an error', function() { + var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value zzz123 to type integer, returning null.'); + }); }); - it('should return the default value from getFeatureVariableString', function() { - var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 'Hello'); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: Feature test_feature is not enabled for user user1. Returning default value for variable "message".'); + describe('invalid double', function() { + beforeEach(function() { + sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('zzz44.55'); + }); + + it('should return null and log an error', function() { + var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1'); + assert.strictEqual(result, null); + sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value zzz44.55 to type double, returning null.'); + }); }); }); }); - describe('not bucketed into an experiment or a rollout', function() { + describe('not bucketed into a variation', function() { beforeEach(function() { sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: null, @@ -3746,150 +3768,6 @@ describe('lib/optimizely', function() { }); }); - it('returns null from getFeatureVariableBoolean when called with a non-boolean variable', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'button_width', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "boolean", but variable is of type "double". Use correct API to retrieve value. Returning None.'); - }); - - it('returns null from getFeatureVariableDouble when called with a non-double variable', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'is_button_animated', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "double", but variable is of type "boolean". Use correct API to retrieve value. Returning None.'); - }); - - it('returns null from getFeatureVariableInteger when called with a non-integer variable', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'button_width', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "integer", but variable is of type "double". Use correct API to retrieve value. Returning None.'); - }); - - it('returns null from getFeatureVariableString when called with a non-string variable', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'num_buttons', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.WARNING, 'OPTIMIZELY: Requested variable type "string", but variable is of type "integer". Use correct API to retrieve value. Returning None.'); - }); - - it('returns null from getFeatureVariableBoolean if user id is null', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', null, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableBoolean if user id is undefined', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', undefined, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableBoolean if user id is not provided', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableDouble if user id is null', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', null, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableDouble if user id is undefined', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', undefined, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableDouble if user id is not provided', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableInteger if user id is null', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', null, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableInteger if user id is undefined', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', undefined, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableInteger if user id is not provided', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableString if user id is null', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', null, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableString if user id is undefined', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', undefined, { test_attribute: 'test_value' }); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - it('returns null from getFeatureVariableString if user id is not provided', function() { - var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'OPTIMIZELY: Provided user_id is in an invalid format.'); - }); - - describe('type casting failures', function() { - beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; - var variation = experiment.variations[0]; - sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ - experiment: experiment, - variation: variation, - decisionSource: DECISION_SOURCES.EXPERIMENT, - }); - }); - - describe('invalid boolean', function() { - beforeEach(function() { - sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('falsezzz'); - }); - - it('should return null and log an error', function() { - var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value falsezzz to type boolean, returning null.'); - }); - }); - - describe('invalid integer', function() { - beforeEach(function() { - sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('zzz123'); - }); - - it('should return null and log an error', function() { - var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value zzz123 to type integer, returning null.'); - }); - }); - - describe('invalid double', function() { - beforeEach(function() { - sandbox.stub(projectConfig, 'getVariableValueForVariation').returns('zzz44.55'); - }); - - it('should return null and log an error', function() { - var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1'); - assert.strictEqual(result, null); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.ERROR, 'PROJECT_CONFIG: Unable to cast value zzz44.55 to type double, returning null.'); - }); - }); - }); - it('returns null from getFeatureVariableBoolean if the argument feature key is invalid', function() { var result = optlyInstance.getFeatureVariableBoolean('thisIsNotAValidKey<><><>', 'is_button_animated', 'user1'); assert.strictEqual(result, null); From 8c80556756156b981303607a4cd750e0201e5ff9 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Wed, 13 Mar 2019 17:13:17 +0500 Subject: [PATCH 05/17] Capitalize decision source values. --- packages/optimizely-sdk/lib/utils/enums/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/optimizely-sdk/lib/utils/enums/index.js b/packages/optimizely-sdk/lib/utils/enums/index.js index 89de68908..9beb619f1 100644 --- a/packages/optimizely-sdk/lib/utils/enums/index.js +++ b/packages/optimizely-sdk/lib/utils/enums/index.js @@ -199,8 +199,8 @@ exports.DECISION_INFO_TYPES = { * Optimizely. */ exports.DECISION_SOURCES = { - EXPERIMENT: 'experiment', - ROLLOUT: 'rollout', + EXPERIMENT: 'EXPERIMENT', + ROLLOUT: 'ROLLOUT', }; /* From cd0e313a9fa25f56b6546e516af9657c45f36f35 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Wed, 13 Mar 2019 21:13:17 +0500 Subject: [PATCH 06/17] Revert decision_service tests change. --- .../lib/core/decision_service/index.tests.js | 20 +++++++++---------- .../lib/optimizely/index.tests.js | 17 +++++++++++----- .../optimizely-sdk/lib/tests/test_data.js | 18 ++++++++--------- 3 files changed, 31 insertions(+), 24 deletions(-) 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 4e1797daa..20693e4a5 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js @@ -729,22 +729,22 @@ describe('lib/core/decision_service', function() { 'variables': [ { 'id': '4792309476491264', - 'value': '40' + 'value': '10' }, { 'id': '5073784453201920', - 'value': 'true' + 'value': 'false' }, { 'id': '5636734406623232', - 'value': 'Buy me Later' + 'value': 'Buy me' }, { 'id': '6199684360044544', - 'value': '99.99' + 'value': '50.55' } ], - 'featureEnabled': false, + 'featureEnabled': true, 'key': 'control' } ], @@ -760,22 +760,22 @@ describe('lib/core/decision_service', function() { 'variables': [ { 'id': '4792309476491264', - 'value': '40' + 'value': '10' }, { 'id': '5073784453201920', - 'value': 'true' + 'value': 'false' }, { 'id': '5636734406623232', - 'value': 'Buy me Later' + 'value': 'Buy me' }, { 'id': '6199684360044544', - 'value': '99.99' + 'value': '50.55' } ], - 'featureEnabled': false, + 'featureEnabled': true, 'key': 'control' }, variation: { diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index d1c21d207..1577e8f02 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -2649,14 +2649,21 @@ describe('lib/optimizely', function() { describe('when the variation is toggled OFF', function() { beforeEach(function() { + // Setting featureEnabled to false for verifying feature toggle off. + optlyInstance.configObj.experimentKeyMap.testing_my_feature.variations[0].featureEnabled = false; var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; - var variation = experiment.variations[1]; + var variation = experiment.variations[0]; sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: experiment, variation: variation, decisionSource: DECISION_SOURCES.EXPERIMENT, }); }); + + afterEach(function() { + // Resetting featureEnabled value to true. + optlyInstance.configObj.experimentKeyMap.testing_my_feature.variations[0].featureEnabled = true; + }) it('returns the default value from getFeatureVariableBoolean and send notification', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); @@ -2673,7 +2680,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' + sourceVariationKey: 'variation' } }); }); @@ -2693,7 +2700,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.DOUBLE, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' + sourceVariationKey: 'variation' } }); }); @@ -2713,7 +2720,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.INTEGER, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' + sourceVariationKey: 'variation' } }); }); @@ -2733,7 +2740,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.STRING, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' + sourceVariationKey: 'variation' } }); }); diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index e854dc287..bfac5e239 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -482,23 +482,23 @@ var configWithFeatures = { { 'key': 'control', 'id': '594097', - 'featureEnabled': false, + 'featureEnabled': true, 'variables': [ { 'id': '4792309476491264', - 'value': '40' + 'value': '10' }, { 'id': '5073784453201920', - 'value': 'true' + 'value': 'false' }, { 'id': '5636734406623232', - 'value': 'Buy me Later' + 'value': 'Buy me' }, { 'id': '6199684360044544', - 'value': '99.99' + 'value': '50.55' } ] } @@ -1360,19 +1360,19 @@ var datafileWithFeaturesExpectedData = { }, 594097: { 4792309476491264: { - 'value': '40', + 'value': '10', 'id': '4792309476491264' }, 5073784453201920: { - 'value': 'true', + 'value': 'false', 'id': '5073784453201920' }, 5636734406623232: { - 'value': 'Buy me Later', + 'value': 'Buy me', 'id': '5636734406623232' }, 6199684360044544: { - 'value': '99.99', + 'value': '50.55', 'id': '6199684360044544' }, }, From c6dd63af9b953ab53c08550c9ea63448deb9a5c1 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Thu, 14 Mar 2019 17:30:47 +0500 Subject: [PATCH 07/17] getVariationForFeature method now returns decision source as rollout if the user is not evaluated into the experiment. --- .../lib/core/decision_service/index.js | 7 +----- .../lib/core/decision_service/index.tests.js | 22 +++++++++---------- .../optimizely-sdk/lib/optimizely/index.js | 5 ++--- .../lib/optimizely/index.tests.js | 4 ++-- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/packages/optimizely-sdk/lib/core/decision_service/index.js b/packages/optimizely-sdk/lib/core/decision_service/index.js index d0daa6f9d..1ef78b311 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.js @@ -303,12 +303,7 @@ DecisionService.prototype.getVariationForFeature = function(feature, userId, att } this.logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.USER_NOT_IN_ROLLOUT, MODULE_NAME, userId, feature.key)); - - return { - experiment: null, - variation: null, - decisionSource: null, - }; + return rolloutDecision; }; DecisionService.prototype._getVariationForFeatureExperiment = function(feature, userId, attributes) { 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 20693e4a5..a65c47133 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js @@ -843,12 +843,12 @@ describe('lib/core/decision_service', function() { getVariationStub.returns(null); }); - it('returns a decision with no variation', function() { + it('returns a decision with no variation and source rollout', function() { var decision = decisionServiceInstance.getVariationForFeature(feature, 'user1'); var expectedDecision = { experiment: null, variation: null, - decisionSource: null, + 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.'); @@ -915,12 +915,12 @@ describe('lib/core/decision_service', function() { getVariationStub.returns(null); }); - it('returns a decision with no experiment and no variation', function() { + it('returns a decision with no experiment, no variation and source rollout', function() { var decision = decisionServiceInstance.getVariationForFeature(feature, 'user1'); var expectedDecision = { experiment: null, variation: null, - decisionSource: null, + 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.'); @@ -932,7 +932,7 @@ describe('lib/core/decision_service', function() { var expectedDecision = { experiment: null, variation: null, - decisionSource: null, + 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.'); @@ -946,12 +946,12 @@ describe('lib/core/decision_service', function() { bucketUserIntoExperimentStub.returns(null); }); - it('returns a decision with no experiment and no variation', function() { + it('returns a decision with no experiment, no variation and source rollout', function() { var decision = decisionServiceInstance.getVariationForFeature(feature, 'user1'); var expectedDecision = { experiment: null, variation: null, - decisionSource: null, + 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.'); @@ -1168,12 +1168,12 @@ describe('lib/core/decision_service', function() { bucketStub.returns(null); }); - it('returns a decision with no variation and no experiment', function() { + it('returns a decision with no variation, no experiment and source rollout', function() { var decision = decisionServiceInstance.getVariationForFeature(feature, 'user1'); var expectedDecision = { experiment: null, variation: null, - decisionSource: null, + 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.'); @@ -1378,12 +1378,12 @@ describe('lib/core/decision_service', function() { feature = configObj.featureKeyMap.unused_flag; }); - it('returns a decision with no variation and no experiment', function() { + it('returns a decision with no variation, no experiment and source rollout', function() { var decision = decisionServiceInstance.getVariationForFeature(feature, 'user1'); var expectedDecision = { experiment: null, variation: null, - decisionSource: null, + 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.'); diff --git a/packages/optimizely-sdk/lib/optimizely/index.js b/packages/optimizely-sdk/lib/optimizely/index.js index c9c08afc4..0cde90d65 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.js +++ b/packages/optimizely-sdk/lib/optimizely/index.js @@ -583,8 +583,7 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK var experimentKey = null; var variationKey = null; - var decisionSource = decision.decisionSource || DECISION_SOURCES.ROLLOUT; - if (decisionSource === DECISION_SOURCES.EXPERIMENT) { + if (decision.decisionSource === DECISION_SOURCES.EXPERIMENT) { experimentKey = decision.experiment.key; variationKey = decision.variation.key; } @@ -602,7 +601,7 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK variableKey: variableKey, variableValue: typeCastedValue, variableType: variableType, - source: decisionSource, + source: decision.decisionSource, sourceExperimentKey: experimentKey, sourceVariationKey: variationKey } diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index 1577e8f02..0cfb9df41 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -2938,7 +2938,7 @@ describe('lib/optimizely', function() { sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: null, variation: null, - decisionSource: null, + decisionSource: DECISION_SOURCES.ROLLOUT, }); }); @@ -3480,7 +3480,7 @@ describe('lib/optimizely', function() { sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: null, variation: null, - decisionSource: null, + decisionSource: DECISION_SOURCES.ROLLOUT, }); }); From 79ea03dee44b0ddfd2084b20d6afd860a625d0a2 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Thu, 14 Mar 2019 18:51:47 +0500 Subject: [PATCH 08/17] Address code review comments. --- packages/optimizely-sdk/lib/optimizely/index.tests.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index 0cfb9df41..f1e26b4d2 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -3097,7 +3097,6 @@ describe('lib/optimizely', function() { }); var optlyInstance; var clock; - var onDecisionListener; beforeEach(function() { optlyInstance = new Optimizely({ @@ -3117,7 +3116,6 @@ describe('lib/optimizely', function() { sandbox.stub(uuid, 'v4').returns('a68cf1ad-0393-4e18-af87-efe8f01a7c9c'); sandbox.stub(fns, 'currentTimestamp').returns(1509489766569); clock = sinon.useFakeTimers(new Date().getTime()); - onDecisionListener = sinon.spy(); }); afterEach(function() { From 2dec469d359249cea6e2efa9e7f53bbe9b7641b9 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Thu, 14 Mar 2019 19:01:38 +0500 Subject: [PATCH 09/17] Revert "Revert decision_service tests change." commit. --- .../lib/core/decision_service/index.tests.js | 20 +++++++++---------- .../lib/optimizely/index.tests.js | 17 +++++----------- .../optimizely-sdk/lib/tests/test_data.js | 18 ++++++++--------- 3 files changed, 24 insertions(+), 31 deletions(-) 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 a65c47133..4c9329c22 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js @@ -729,22 +729,22 @@ describe('lib/core/decision_service', function() { 'variables': [ { 'id': '4792309476491264', - 'value': '10' + 'value': '40' }, { 'id': '5073784453201920', - 'value': 'false' + 'value': 'true' }, { 'id': '5636734406623232', - 'value': 'Buy me' + 'value': 'Buy me Later' }, { 'id': '6199684360044544', - 'value': '50.55' + 'value': '99.99' } ], - 'featureEnabled': true, + 'featureEnabled': false, 'key': 'control' } ], @@ -760,22 +760,22 @@ describe('lib/core/decision_service', function() { 'variables': [ { 'id': '4792309476491264', - 'value': '10' + 'value': '40' }, { 'id': '5073784453201920', - 'value': 'false' + 'value': 'true' }, { 'id': '5636734406623232', - 'value': 'Buy me' + 'value': 'Buy me Later' }, { 'id': '6199684360044544', - 'value': '50.55' + 'value': '99.99' } ], - 'featureEnabled': true, + 'featureEnabled': false, 'key': 'control' }, variation: { diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index f1e26b4d2..07ec49d89 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -2649,21 +2649,14 @@ describe('lib/optimizely', function() { describe('when the variation is toggled OFF', function() { beforeEach(function() { - // Setting featureEnabled to false for verifying feature toggle off. - optlyInstance.configObj.experimentKeyMap.testing_my_feature.variations[0].featureEnabled = false; var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; - var variation = experiment.variations[0]; + var variation = experiment.variations[1]; sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: experiment, variation: variation, decisionSource: DECISION_SOURCES.EXPERIMENT, }); }); - - afterEach(function() { - // Resetting featureEnabled value to true. - optlyInstance.configObj.experimentKeyMap.testing_my_feature.variations[0].featureEnabled = true; - }) it('returns the default value from getFeatureVariableBoolean and send notification', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); @@ -2680,7 +2673,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'variation' + sourceVariationKey: 'control' } }); }); @@ -2700,7 +2693,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.DOUBLE, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'variation' + sourceVariationKey: 'control' } }); }); @@ -2720,7 +2713,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.INTEGER, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'variation' + sourceVariationKey: 'control' } }); }); @@ -2740,7 +2733,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.STRING, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'variation' + sourceVariationKey: 'control' } }); }); diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index bfac5e239..e854dc287 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -482,23 +482,23 @@ var configWithFeatures = { { 'key': 'control', 'id': '594097', - 'featureEnabled': true, + 'featureEnabled': false, 'variables': [ { 'id': '4792309476491264', - 'value': '10' + 'value': '40' }, { 'id': '5073784453201920', - 'value': 'false' + 'value': 'true' }, { 'id': '5636734406623232', - 'value': 'Buy me' + 'value': 'Buy me Later' }, { 'id': '6199684360044544', - 'value': '50.55' + 'value': '99.99' } ] } @@ -1360,19 +1360,19 @@ var datafileWithFeaturesExpectedData = { }, 594097: { 4792309476491264: { - 'value': '10', + 'value': '40', 'id': '4792309476491264' }, 5073784453201920: { - 'value': 'false', + 'value': 'true', 'id': '5073784453201920' }, 5636734406623232: { - 'value': 'Buy me', + 'value': 'Buy me Later', 'id': '5636734406623232' }, 6199684360044544: { - 'value': '50.55', + 'value': '99.99', 'id': '6199684360044544' }, }, From 82b3ce43320dd5f7bca9eca7eb1d37265fb73a51 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Thu, 14 Mar 2019 21:46:00 +0500 Subject: [PATCH 10/17] Address code review comments. --- packages/optimizely-sdk/lib/optimizely/index.js | 1 - packages/optimizely-sdk/lib/optimizely/index.tests.js | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/optimizely-sdk/lib/optimizely/index.js b/packages/optimizely-sdk/lib/optimizely/index.js index 0cde90d65..c15e520ef 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.js +++ b/packages/optimizely-sdk/lib/optimizely/index.js @@ -575,7 +575,6 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId)); } else { this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE, MODULE_NAME, featureFlag.key, userId, variableKey)); - featureEnabled = false; } } else { this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE, MODULE_NAME, userId, variableKey, featureFlag.key)); diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index 07ec49d89..b647da7cf 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -2535,9 +2535,7 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfigWithFeatures(), - eventBuilder: eventBuilder, errorHandler: errorHandler, - eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, logger: createdLogger, isValidInstance: true, @@ -2557,7 +2555,7 @@ describe('lib/optimizely', function() { describe('bucketed into variation of an experiment with variable values', function() { describe('when the variation is toggled ON', function() { beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; + var experiment = projectConfig.getExperimentFromKey(optlyInstance.configObj, 'testing_my_feature'); var variation = experiment.variations[0]; sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: experiment, @@ -2649,7 +2647,7 @@ describe('lib/optimizely', function() { describe('when the variation is toggled OFF', function() { beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature; + var experiment = projectConfig.getExperimentFromKey(optlyInstance.configObj, 'testing_my_feature'); var variation = experiment.variations[1]; sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: experiment, @@ -2743,7 +2741,7 @@ describe('lib/optimizely', function() { describe('bucketed into variation of a rollout with variable values', function() { describe('when the variation is toggled ON', function() { beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap['594031']; + var experiment = projectConfig.getExperimentFromKey(optlyInstance.configObj, '594031'); var variation = experiment.variations[0]; sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: experiment, @@ -2835,7 +2833,7 @@ describe('lib/optimizely', function() { describe('when the variation is toggled OFF', function() { beforeEach(function() { - var experiment = optlyInstance.configObj.experimentKeyMap['594037']; + var experiment = projectConfig.getExperimentFromKey(optlyInstance.configObj, '594037'); var variation = experiment.variations[0]; sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: experiment, From 01f02889a3a1ee4d36198c2c39e324b3e7d08c2e Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Tue, 19 Mar 2019 13:10:45 +0500 Subject: [PATCH 11/17] Adds new variation with featureEnabled set to false for feature variable tests. --- .../lib/core/decision_service/index.tests.js | 58 +++++++++++++++++-- .../lib/optimizely/index.tests.js | 10 ++-- .../optimizely-sdk/lib/tests/test_data.js | 41 +++++++++++++ 3 files changed, 98 insertions(+), 11 deletions(-) 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 4c9329c22..2a2009626 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js @@ -726,6 +726,29 @@ describe('lib/core/decision_service', function() { }, { 'id': '594097', + 'variables': [ + { + 'id': '4792309476491264', + 'value': '10' + }, + { + 'id': '5073784453201920', + 'value': 'false' + }, + { + 'id': '5636734406623232', + 'value': 'Buy me' + }, + { + 'id': '6199684360044544', + 'value': '50.55' + } + ], + 'featureEnabled': true, + 'key': 'control' + }, + { + 'id': '594099', 'variables': [ { 'id': '4792309476491264', @@ -745,7 +768,7 @@ describe('lib/core/decision_service', function() { } ], 'featureEnabled': false, - 'key': 'control' + 'key': 'variation2' } ], 'audienceIds': [], @@ -760,22 +783,22 @@ describe('lib/core/decision_service', function() { 'variables': [ { 'id': '4792309476491264', - 'value': '40' + 'value': '10' }, { 'id': '5073784453201920', - 'value': 'true' + 'value': 'false' }, { 'id': '5636734406623232', - 'value': 'Buy me Later' + 'value': 'Buy me' }, { 'id': '6199684360044544', - 'value': '99.99' + 'value': '50.55' } ], - 'featureEnabled': false, + 'featureEnabled': true, 'key': 'control' }, variation: { @@ -801,6 +824,29 @@ describe('lib/core/decision_service', function() { 'featureEnabled': true, 'key': 'variation' }, + variation2: { + 'id': '594099', + 'variables': [ + { + 'id': '4792309476491264', + 'value': '40' + }, + { + 'id': '5073784453201920', + 'value': 'true' + }, + { + 'id': '5636734406623232', + 'value': 'Buy me Later' + }, + { + 'id': '6199684360044544', + 'value': '99.99' + } + ], + 'featureEnabled': false, + 'key': 'variation2' + } }, }, variation: { diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index 6a2b2c9a1..1e59d2be2 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -2746,7 +2746,7 @@ describe('lib/optimizely', function() { describe('when the variation is toggled OFF', function() { beforeEach(function() { var experiment = projectConfig.getExperimentFromKey(optlyInstance.configObj, 'testing_my_feature'); - var variation = experiment.variations[1]; + var variation = experiment.variations[2]; sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({ experiment: experiment, variation: variation, @@ -2769,7 +2769,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' + sourceVariationKey: 'variation2' } }); }); @@ -2789,7 +2789,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.DOUBLE, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' + sourceVariationKey: 'variation2' } }); }); @@ -2809,7 +2809,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.INTEGER, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' + sourceVariationKey: 'variation2' } }); }); @@ -2829,7 +2829,7 @@ describe('lib/optimizely', function() { variableType: FEATURE_VARIABLE_TYPES.STRING, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', - sourceVariationKey: 'control' + sourceVariationKey: 'variation2' } }); }); diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index e854dc287..19895166e 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -482,6 +482,29 @@ var configWithFeatures = { { 'key': 'control', 'id': '594097', + 'featureEnabled': true, + 'variables': [ + { + 'id': '4792309476491264', + 'value': '10' + }, + { + 'id': '5073784453201920', + 'value': 'false' + }, + { + 'id': '5636734406623232', + 'value': 'Buy me' + }, + { + 'id': '6199684360044544', + 'value': '50.55' + } + ] + }, + { + 'key': 'variation2', + 'id': '594099', 'featureEnabled': false, 'variables': [ { @@ -1359,6 +1382,24 @@ var datafileWithFeaturesExpectedData = { }, }, 594097: { + 4792309476491264: { + 'value': '10', + 'id': '4792309476491264' + }, + 5073784453201920: { + 'value': 'false', + 'id': '5073784453201920' + }, + 5636734406623232: { + 'value': 'Buy me', + 'id': '5636734406623232' + }, + 6199684360044544: { + 'value': '50.55', + 'id': '6199684360044544' + }, + }, + 594099: { 4792309476491264: { 'value': '40', 'id': '4792309476491264' From 24a61e3abc549a2a376a5f0ab5cd24b661f61805 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Tue, 19 Mar 2019 16:17:36 +0500 Subject: [PATCH 12/17] Modified data file. --- .../optimizely-sdk/lib/tests/test_data.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index 7a27019ff..a8ba78d4a 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -552,31 +552,31 @@ var configWithFeatures = { value: 'Buy me', }, { - 'id': '6199684360044544', - 'value': '50.55' + id: '6199684360044544', + value: '50.55' } ] }, { - 'key': 'variation2', - 'id': '594099', - 'featureEnabled': false, - 'variables': [ + key: 'variation2', + id: '594099', + featureEnabled: false, + 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' } ] } From d66dc1ef39f59ede43be260c2dd4e09d6e20272f Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Tue, 19 Mar 2019 16:20:39 +0500 Subject: [PATCH 13/17] Modified data file. --- .../optimizely-sdk/lib/tests/test_data.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index a8ba78d4a..dc13a1ba8 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -553,9 +553,9 @@ var configWithFeatures = { }, { id: '6199684360044544', - value: '50.55' - } - ] + value: '50.55', + }, + ], }, { key: 'variation2', @@ -564,22 +564,22 @@ var configWithFeatures = { variables: [ { id: '4792309476491264', - value: '40' + value: '40', }, { id: '5073784453201920', - value: 'true' + value: 'true', }, { id: '5636734406623232', - value: 'Buy me Later' + value: 'Buy me Later', }, { id: '6199684360044544', - value: '99.99' - } - ] - } + value: '99.99', + }, + ], + }, ], status: 'Running', key: 'testing_my_feature', From 56a07f10b3cf3a29823384bcc29c2560eae41f91 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Tue, 19 Mar 2019 16:23:24 +0500 Subject: [PATCH 14/17] Remove quotes. --- packages/optimizely-sdk/lib/tests/test_data.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index dc13a1ba8..19841d048 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -1447,20 +1447,20 @@ var datafileWithFeaturesExpectedData = { }, 594099: { 4792309476491264: { - 'value': '40', - 'id': '4792309476491264' + value: '40', + id: '4792309476491264' }, 5073784453201920: { - 'value': 'true', - 'id': '5073784453201920' + value: 'true', + id: '5073784453201920' }, 5636734406623232: { - 'value': 'Buy me Later', - 'id': '5636734406623232' + value: 'Buy me Later', + id: '5636734406623232' }, 6199684360044544: { - 'value': '99.99', - 'id': '6199684360044544' + value: '99.99', + id: '6199684360044544' }, }, 595008: {}, From 1f4987b6732fa697eb208f2f35ccef91ea731877 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Mon, 25 Mar 2019 16:09:32 +0500 Subject: [PATCH 15/17] Return right variable value from variation when featureEnabled is false. --- .../optimizely-sdk/lib/optimizely/index.js | 8 +-- .../lib/optimizely/index.tests.js | 57 +++++++++---------- .../optimizely-sdk/lib/utils/enums/index.js | 1 - 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/packages/optimizely-sdk/lib/optimizely/index.js b/packages/optimizely-sdk/lib/optimizely/index.js index c23cfb499..4fc089a38 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.js +++ b/packages/optimizely-sdk/lib/optimizely/index.js @@ -620,12 +620,8 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK if (decision.variation !== null) { featureEnabled = decision.variation.featureEnabled; - if (featureEnabled === true) { - variableValue = projectConfig.getVariableValueForVariation(this.configObj, variable, decision.variation, this.logger); - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId)); - } else { - this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE, MODULE_NAME, featureFlag.key, userId, variableKey)); - } + variableValue = projectConfig.getVariableValueForVariation(this.configObj, variable, decision.variation, this.logger); + this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId)); } else { this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE, MODULE_NAME, userId, variableKey, featureFlag.key)); } diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index c783f0d99..fa54900a3 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -2478,7 +2478,7 @@ describe('lib/optimizely', function() { }); }); - it('returns the right value from getFeatureVariableBoolean and send notification', function() { + it('returns the right value from getFeatureVariableBoolean and send notification with featureEnabled true', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, true); sinon.assert.calledWith(decisionListener, { @@ -2498,7 +2498,7 @@ describe('lib/optimizely', function() { }); }); - it('returns the right value from getFeatureVariableDouble and send notification', function() { + it('returns the right value from getFeatureVariableDouble and send notification with featureEnabled true', function() { var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 20.25); sinon.assert.calledWith(decisionListener, { @@ -2518,7 +2518,7 @@ describe('lib/optimizely', function() { }); }); - it('returns the right value from getFeatureVariableInteger and send notification', function() { + it('returns the right value from getFeatureVariableInteger and send notification with featureEnabled true', function() { var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 2); sinon.assert.calledWith(decisionListener, { @@ -2538,7 +2538,7 @@ describe('lib/optimizely', function() { }); }); - it('returns the right value from getFeatureVariableString and send notification', function() { + it('returns the right value from getFeatureVariableString and send notification with featureEnabled true', function() { var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me NOW'); sinon.assert.calledWith(decisionListener, { @@ -2570,9 +2570,9 @@ describe('lib/optimizely', function() { }); }); - it('returns the default value from getFeatureVariableBoolean and send notification', function() { + it('returns the right value from getFeatureVariableBoolean and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, false); + assert.strictEqual(result, true); sinon.assert.calledWith(decisionListener, { type: DECISION_INFO_TYPES.FEATURE_VARIABLE, userId: 'user1', @@ -2581,7 +2581,7 @@ describe('lib/optimizely', function() { featureKey: 'test_feature_for_experiment', featureEnabled: false, variableKey: 'is_button_animated', - variableValue: false, + variableValue: true, variableType: FEATURE_VARIABLE_TYPES.BOOLEAN, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', @@ -2590,9 +2590,9 @@ describe('lib/optimizely', function() { }); }); - it('returns the default value from getFeatureVariableDouble and send notification', function() { + it('returns the right value from getFeatureVariableDouble and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 50.55); + assert.strictEqual(result, 99.99); sinon.assert.calledWith(decisionListener, { type: DECISION_INFO_TYPES.FEATURE_VARIABLE, userId: 'user1', @@ -2601,7 +2601,7 @@ describe('lib/optimizely', function() { featureKey: 'test_feature_for_experiment', featureEnabled: false, variableKey: 'button_width', - variableValue: 50.55, + variableValue: 99.99, variableType: FEATURE_VARIABLE_TYPES.DOUBLE, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', @@ -2610,9 +2610,9 @@ describe('lib/optimizely', function() { }); }); - it('returns the default value from getFeatureVariableInteger and send notification', function() { + it('returns the right value from getFeatureVariableInteger and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 10); + assert.strictEqual(result, 40); sinon.assert.calledWith(decisionListener, { type: DECISION_INFO_TYPES.FEATURE_VARIABLE, userId: 'user1', @@ -2621,7 +2621,7 @@ describe('lib/optimizely', function() { featureKey: 'test_feature_for_experiment', featureEnabled: false, variableKey: 'num_buttons', - variableValue: 10, + variableValue: 40, variableType: FEATURE_VARIABLE_TYPES.INTEGER, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', @@ -2630,9 +2630,9 @@ describe('lib/optimizely', function() { }); }); - it('returns the default value from getFeatureVariableString and send notification', function() { + it('returns the right value from getFeatureVariableString and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); - assert.strictEqual(result, 'Buy me'); + assert.strictEqual(result, 'Buy me Later'); sinon.assert.calledWith(decisionListener, { type: DECISION_INFO_TYPES.FEATURE_VARIABLE, userId: 'user1', @@ -2641,7 +2641,7 @@ describe('lib/optimizely', function() { featureKey: 'test_feature_for_experiment', featureEnabled: false, variableKey: 'button_txt', - variableValue: 'Buy me', + variableValue: 'Buy me Later', variableType: FEATURE_VARIABLE_TYPES.STRING, source: DECISION_SOURCES.EXPERIMENT, sourceExperimentKey: 'testing_my_feature', @@ -2664,7 +2664,7 @@ describe('lib/optimizely', function() { }); }); - it('should return the right value from getFeatureVariableBoolean and send notification', function() { + it('should return the right value from getFeatureVariableBoolean and send notification with featureEnabled true', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, true); sinon.assert.calledWith(decisionListener, { @@ -2684,7 +2684,7 @@ describe('lib/optimizely', function() { }); }); - it('should return the right value from getFeatureVariableDouble and send notification', function() { + it('should return the right value from getFeatureVariableDouble and send notification with featureEnabled true', function() { var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 4.99); sinon.assert.calledWith(decisionListener, { @@ -2704,7 +2704,7 @@ describe('lib/optimizely', function() { }); }); - it('should return the right value from getFeatureVariableInteger and send notification', function() { + it('should return the right value from getFeatureVariableInteger and send notification with featureEnabled true', function() { var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 395); sinon.assert.calledWith(decisionListener, { @@ -2724,7 +2724,7 @@ describe('lib/optimizely', function() { }); }); - it('should return the right value from getFeatureVariableString and send notification', function() { + it('should return the right value from getFeatureVariableString and send notification with featureEnabled true', function() { var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Hello audience'); sinon.assert.calledWith(decisionListener, { @@ -2756,7 +2756,7 @@ describe('lib/optimizely', function() { }); }); - it('should return the default value from getFeatureVariableBoolean and send notification', function() { + it('should return the right value from getFeatureVariableBoolean and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature', 'new_content', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, false); sinon.assert.calledWith(decisionListener, { @@ -2776,7 +2776,7 @@ describe('lib/optimizely', function() { }); }); - it('should return the default value from getFeatureVariableDouble and send notification', function() { + it('should return the right value from getFeatureVariableDouble and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableDouble('test_feature', 'price', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 14.99); sinon.assert.calledWith(decisionListener, { @@ -2796,7 +2796,7 @@ describe('lib/optimizely', function() { }); }); - it('should return the default value from getFeatureVariableInteger and send notification', function() { + it('should return the default value from getFeatureVariableInteger and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableInteger('test_feature', 'lasers', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 400); sinon.assert.calledWith(decisionListener, { @@ -2816,7 +2816,7 @@ describe('lib/optimizely', function() { }); }); - it('should return the default value from getFeatureVariableString and send notification', function() { + it('should return the default value from getFeatureVariableString and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableString('test_feature', 'message', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Hello'); sinon.assert.calledWith(decisionListener, { @@ -2847,7 +2847,7 @@ describe('lib/optimizely', function() { }); }); - it('returns the variable default value from getFeatureVariableBoolean and send notification', function() { + it('returns the variable default value from getFeatureVariableBoolean and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableBoolean('test_feature_for_experiment', 'is_button_animated', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, false); sinon.assert.calledWith(decisionListener, { @@ -2867,7 +2867,7 @@ describe('lib/optimizely', function() { }); }); - it('returns the variable default value from getFeatureVariableDouble and send notification', function() { + it('returns the variable default value from getFeatureVariableDouble and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableDouble('test_feature_for_experiment', 'button_width', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 50.55); sinon.assert.calledWith(decisionListener, { @@ -2887,10 +2887,9 @@ describe('lib/optimizely', function() { }); }); - it('returns the variable default value from getFeatureVariableInteger and send notification', function() { + it('returns the variable default value from getFeatureVariableInteger and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableInteger('test_feature_for_experiment', 'num_buttons', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 10); - sinon.assert.calledWith(createdLogger.log, LOG_LEVEL.INFO, 'OPTIMIZELY: User "user1" is not in any variation or rollout rule. Returning default value for variable "num_buttons" of feature flag "test_feature_for_experiment".'); sinon.assert.calledWith(decisionListener, { type: DECISION_INFO_TYPES.FEATURE_VARIABLE, userId: 'user1', @@ -2908,7 +2907,7 @@ describe('lib/optimizely', function() { }); }); - it('returns the variable default value from getFeatureVariableString', function() { + it('returns the variable default value from getFeatureVariableString and send notification with featureEnabled false', function() { var result = optlyInstance.getFeatureVariableString('test_feature_for_experiment', 'button_txt', 'user1', { test_attribute: 'test_value' }); assert.strictEqual(result, 'Buy me'); sinon.assert.calledWith(decisionListener, { diff --git a/packages/optimizely-sdk/lib/utils/enums/index.js b/packages/optimizely-sdk/lib/utils/enums/index.js index 440f1a9cc..aa6d1027e 100644 --- a/packages/optimizely-sdk/lib/utils/enums/index.js +++ b/packages/optimizely-sdk/lib/utils/enums/index.js @@ -116,7 +116,6 @@ exports.LOG_MESSAGES = { 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".', 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.', From 473686feff5177e5a2c046725daabdb081dcefa9 Mon Sep 17 00:00:00 2001 From: mfahadahmed Date: Mon, 25 Mar 2019 16:36:29 +0500 Subject: [PATCH 16/17] Return right variable value from variation when featureEnabled is false. --- packages/optimizely-sdk/lib/optimizely/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/optimizely-sdk/lib/optimizely/index.js b/packages/optimizely-sdk/lib/optimizely/index.js index 4fc089a38..0d6aca4dd 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.js +++ b/packages/optimizely-sdk/lib/optimizely/index.js @@ -614,8 +614,8 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK return null; } + var variableValue; var featureEnabled = false; - var variableValue = variable.defaultValue; var decision = this.decisionService.getVariationForFeature(featureFlag, userId, attributes); if (decision.variation !== null) { @@ -623,6 +623,7 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK variableValue = projectConfig.getVariableValueForVariation(this.configObj, variable, decision.variation, this.logger); this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId)); } else { + variableValue = variable.defaultValue; this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE, MODULE_NAME, userId, variableKey, featureFlag.key)); } From 1b191cf146db7b5a71c991076fe5d1c96f923ad1 Mon Sep 17 00:00:00 2001 From: Muhammad Fahad Ahmed Date: Mon, 25 Mar 2019 23:53:14 +0500 Subject: [PATCH 17/17] Fix indentation. --- packages/optimizely-sdk/lib/optimizely/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/optimizely-sdk/lib/optimizely/index.js b/packages/optimizely-sdk/lib/optimizely/index.js index 0d6aca4dd..556fc1356 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.js +++ b/packages/optimizely-sdk/lib/optimizely/index.js @@ -623,7 +623,7 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK variableValue = projectConfig.getVariableValueForVariation(this.configObj, variable, decision.variation, this.logger); this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId)); } else { - variableValue = variable.defaultValue; + variableValue = variable.defaultValue; this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE, MODULE_NAME, userId, variableKey, featureFlag.key)); }