Skip to content

Commit 8784160

Browse files
Merge branch 'master' into junaed/fssdk-11034-tests-js-to-ts
2 parents 1c19944 + fd9c9ed commit 8784160

File tree

22 files changed

+303
-507
lines changed

22 files changed

+303
-507
lines changed
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/**
2+
* Copyright 2025, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { describe, it, vi, expect } from 'vitest';
17+
18+
import * as conditionTreeEvaluator from '.';
19+
20+
const conditionA = {
21+
name: 'browser_type',
22+
value: 'safari',
23+
type: 'custom_attribute',
24+
};
25+
const conditionB = {
26+
name: 'device_model',
27+
value: 'iphone6',
28+
type: 'custom_attribute',
29+
};
30+
const conditionC = {
31+
name: 'location',
32+
match: 'exact',
33+
type: 'custom_attribute',
34+
value: 'CA',
35+
};
36+
describe('evaluate', function() {
37+
it('should return true for a leaf condition when the leaf condition evaluator returns true', function() {
38+
expect(
39+
conditionTreeEvaluator.evaluate(conditionA, function() {
40+
return true;
41+
})
42+
).toBe(true);
43+
});
44+
45+
it('should return false for a leaf condition when the leaf condition evaluator returns false', function() {
46+
expect(
47+
conditionTreeEvaluator.evaluate(conditionA, function() {
48+
return false;
49+
})
50+
).toBe(false);
51+
});
52+
53+
describe('and evaluation', function() {
54+
it('should return true when ALL conditions evaluate to true', function() {
55+
expect(
56+
conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], function() {
57+
return true;
58+
})
59+
).toBe(true);
60+
});
61+
62+
it('should return false if one condition evaluates to false', function() {
63+
const leafEvaluator = vi.fn();
64+
leafEvaluator.mockImplementationOnce(() => true).mockImplementationOnce(() => false);
65+
expect(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], leafEvaluator)).toBe(false);
66+
});
67+
68+
describe('null handling', function() {
69+
it('should return null when all operands evaluate to null', function() {
70+
expect(
71+
conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], function() {
72+
return null;
73+
})
74+
).toBeNull();
75+
});
76+
77+
it('should return null when operands evaluate to trues and nulls', function() {
78+
const leafEvaluator = vi.fn();
79+
leafEvaluator.mockImplementationOnce(() => true).mockImplementationOnce(() => null);
80+
expect(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], leafEvaluator)).toBeNull();
81+
});
82+
83+
it('should return false when operands evaluate to falses and nulls', function() {
84+
const leafEvaluator = vi.fn();
85+
leafEvaluator.mockImplementationOnce(() => false).mockImplementationOnce(() => null);
86+
expect(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], leafEvaluator)).toBe(false);
87+
88+
leafEvaluator.mockReset();
89+
leafEvaluator.mockImplementationOnce(() => null).mockImplementationOnce(() => false);
90+
expect(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB], leafEvaluator)).toBe(false);
91+
});
92+
93+
it('should return false when operands evaluate to trues, falses, and nulls', function() {
94+
const leafEvaluator = vi.fn();
95+
leafEvaluator
96+
.mockImplementationOnce(() => true)
97+
.mockImplementationOnce(() => false)
98+
.mockImplementationOnce(() => null);
99+
expect(conditionTreeEvaluator.evaluate(['and', conditionA, conditionB, conditionC], leafEvaluator)).toBe(false);
100+
});
101+
});
102+
});
103+
104+
describe('or evaluation', function() {
105+
it('should return true if any condition evaluates to true', function() {
106+
const leafEvaluator = vi.fn();
107+
leafEvaluator.mockImplementationOnce(() => false).mockImplementationOnce(() => true);
108+
expect(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], leafEvaluator)).toBe(true);
109+
});
110+
111+
it('should return false if all conditions evaluate to false', function() {
112+
expect(
113+
conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], function() {
114+
return false;
115+
})
116+
).toBe(false);
117+
});
118+
119+
describe('null handling', function() {
120+
it('should return null when all operands evaluate to null', function() {
121+
expect(
122+
conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], function() {
123+
return null;
124+
})
125+
).toBeNull();
126+
});
127+
128+
it('should return true when operands evaluate to trues and nulls', function() {
129+
const leafEvaluator = vi.fn();
130+
leafEvaluator.mockImplementationOnce(() => true).mockImplementationOnce(() => null);
131+
expect(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], leafEvaluator)).toBe(true);
132+
});
133+
134+
it('should return null when operands evaluate to falses and nulls', function() {
135+
const leafEvaluator = vi.fn();
136+
leafEvaluator.mockImplementationOnce(() => null).mockImplementationOnce(() => false);
137+
expect(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], leafEvaluator)).toBeNull();
138+
139+
leafEvaluator.mockReset();
140+
leafEvaluator.mockImplementationOnce(() => false).mockImplementationOnce(() => null);
141+
expect(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB], leafEvaluator)).toBeNull();
142+
});
143+
144+
it('should return true when operands evaluate to trues, falses, and nulls', function() {
145+
const leafEvaluator = vi.fn();
146+
leafEvaluator
147+
.mockImplementationOnce(() => true)
148+
.mockImplementationOnce(() => null)
149+
.mockImplementationOnce(() => false);
150+
expect(conditionTreeEvaluator.evaluate(['or', conditionA, conditionB, conditionC], leafEvaluator)).toBe(true);
151+
});
152+
});
153+
});
154+
155+
describe('not evaluation', function() {
156+
it('should return true if the condition evaluates to false', function() {
157+
expect(
158+
conditionTreeEvaluator.evaluate(['not', conditionA], function() {
159+
return false;
160+
})
161+
).toBe(true);
162+
});
163+
164+
it('should return false if the condition evaluates to true', function() {
165+
expect(
166+
conditionTreeEvaluator.evaluate(['not', conditionB], function() {
167+
return true;
168+
})
169+
).toBe(false);
170+
});
171+
172+
it('should return the result of negating the first condition, and ignore any additional conditions', function() {
173+
let result = conditionTreeEvaluator.evaluate(['not', '1', '2', '1'], function(id: string) {
174+
return id === '1';
175+
});
176+
expect(result).toBe(false);
177+
result = conditionTreeEvaluator.evaluate(['not', '1', '2', '1'], function(id: string) {
178+
return id === '2';
179+
});
180+
expect(result).toBe(true);
181+
result = conditionTreeEvaluator.evaluate(['not', '1', '2', '3'], function(id: string) {
182+
return id === '1' ? null : id === '3';
183+
});
184+
expect(result).toBeNull();
185+
});
186+
187+
describe('null handling', function() {
188+
it('should return null when operand evaluates to null', function() {
189+
expect(
190+
conditionTreeEvaluator.evaluate(['not', conditionA], function() {
191+
return null;
192+
})
193+
).toBeNull();
194+
});
195+
196+
it('should return null when there are no operands', function() {
197+
expect(
198+
conditionTreeEvaluator.evaluate(['not'], function() {
199+
return null;
200+
})
201+
).toBeNull();
202+
});
203+
});
204+
});
205+
206+
describe('implicit operator', function() {
207+
it('should behave like an "or" operator when the first item in the array is not a recognized operator', function() {
208+
const leafEvaluator = vi.fn();
209+
leafEvaluator.mockImplementationOnce(() => true).mockImplementationOnce(() => false);
210+
expect(conditionTreeEvaluator.evaluate([conditionA, conditionB], leafEvaluator)).toBe(true);
211+
expect(
212+
conditionTreeEvaluator.evaluate([conditionA, conditionB], function() {
213+
return false;
214+
})
215+
).toBe(false);
216+
});
217+
});
218+
});

lib/core/decision_service/index.tests.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import Optimizely from '../../optimizely';
3030
import OptimizelyUserContext from '../../optimizely_user_context';
3131
import projectConfig, { createProjectConfig } from '../../project_config/project_config';
3232
import AudienceEvaluator from '../audience_evaluator';
33-
import errorHandler from '../../plugins/error_handler';
3433
import eventDispatcher from '../../event_processor/event_dispatcher/default_dispatcher.browser';
3534
import * as jsonSchemaValidator from '../../utils/json_schema_validator';
3635
import { getMockProjectConfigManager } from '../../tests/mock/mock_project_config_manager';
@@ -1053,17 +1052,14 @@ describe('lib/core/decision_service', function() {
10531052
isValidInstance: true,
10541053
logger: createdLogger,
10551054
eventProcessor: getForwardingEventProcessor(eventDispatcher),
1056-
notificationCenter: createNotificationCenter(createdLogger, errorHandler),
1057-
errorHandler: errorHandler,
1055+
notificationCenter: createNotificationCenter(createdLogger),
10581056
});
10591057

10601058
sinon.stub(eventDispatcher, 'dispatchEvent');
1061-
sinon.stub(errorHandler, 'handleError');
10621059
});
10631060

10641061
afterEach(function() {
10651062
eventDispatcher.dispatchEvent.restore();
1066-
errorHandler.handleError.restore();
10671063
});
10681064

10691065
var testUserAttributes = {

lib/core/decision_service/index.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ import {
6161
import {
6262
SAVED_USER_VARIATION,
6363
SAVED_VARIATION_NOT_FOUND,
64-
USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED,
65-
USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED_BUT_INVALID,
66-
USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED,
67-
USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED_BUT_INVALID,
6864
USER_HAS_NO_FORCED_VARIATION,
6965
USER_MAPPED_TO_FORCED_VARIATION,
7066
USER_HAS_NO_FORCED_VARIATION_FOR_EXPERIMENT,
@@ -98,6 +94,14 @@ export const IMPROPERLY_FORMATTED_EXPERIMENT = 'Experiment key %s is improperly
9894
export const USER_HAS_FORCED_VARIATION =
9995
'Variation %s is mapped to experiment %s and user %s in the forced variation map.';
10096
export const USER_MEETS_CONDITIONS_FOR_TARGETING_RULE = 'User %s meets conditions for targeting rule %s.';
97+
export const USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED =
98+
'Variation (%s) is mapped to flag (%s), rule (%s) and user (%s) in the forced decision map.';
99+
export const USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED =
100+
'Variation (%s) is mapped to flag (%s) and user (%s) in the forced decision map.';
101+
export const USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED_BUT_INVALID =
102+
'Invalid variation is mapped to flag (%s), rule (%s) and user (%s) in the forced decision map.';
103+
export const USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED_BUT_INVALID =
104+
'Invalid variation is mapped to flag (%s) and user (%s) in the forced decision map.';
101105

102106
export interface DecisionObj {
103107
experiment: Experiment | null;

lib/index.browser.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616

1717
import configValidator from './utils/config_validator';
18-
import defaultErrorHandler from './plugins/error_handler';
1918
import defaultEventDispatcher from './event_processor/event_dispatcher/default_dispatcher.browser';
2019
import sendBeaconEventDispatcher from './event_processor/event_dispatcher/send_beacon_dispatcher.browser';
2120
import * as enums from './utils/enums';
@@ -97,7 +96,6 @@ const __internalResetRetryState = function(): void {
9796
};
9897

9998
export {
100-
defaultErrorHandler as errorHandler,
10199
defaultEventDispatcher as eventDispatcher,
102100
sendBeaconEventDispatcher,
103101
enums,
@@ -119,7 +117,6 @@ export * from './common_exports';
119117

120118
export default {
121119
...commonExports,
122-
errorHandler: defaultErrorHandler,
123120
eventDispatcher: defaultEventDispatcher,
124121
sendBeaconEventDispatcher,
125122
enums,

lib/index.node.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import Optimizely from './optimizely';
1919
import * as enums from './utils/enums';
2020
import configValidator from './utils/config_validator';
21-
import defaultErrorHandler from './plugins/error_handler';
2221
import defaultEventDispatcher from './event_processor/event_dispatcher/default_dispatcher.node';
2322
import { createNotificationCenter } from './notification_center';
2423
import { OptimizelyDecideOption, Client, Config } from './shared_types';
@@ -73,7 +72,6 @@ const createInstance = function(config: Config): Client | null {
7372
* Entry point into the Optimizely Node testing SDK
7473
*/
7574
export {
76-
defaultErrorHandler as errorHandler,
7775
defaultEventDispatcher as eventDispatcher,
7876
enums,
7977
createInstance,
@@ -91,7 +89,6 @@ export * from './common_exports';
9189

9290
export default {
9391
...commonExports,
94-
errorHandler: defaultErrorHandler,
9592
eventDispatcher: defaultEventDispatcher,
9693
enums,
9794
createInstance,

lib/index.react_native.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ describe('javascript-sdk/react-native', () => {
4040

4141
describe('APIs', () => {
4242
it('should expose logger, errorHandler, eventDispatcher and enums', () => {
43-
expect(optimizelyFactory.errorHandler).toBeDefined();
4443
expect(optimizelyFactory.eventDispatcher).toBeDefined();
4544
expect(optimizelyFactory.enums).toBeDefined();
4645
});

lib/index.react_native.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import * as enums from './utils/enums';
1818
import Optimizely from './optimizely';
1919
import configValidator from './utils/config_validator';
20-
import defaultErrorHandler from './plugins/error_handler';
2120
import defaultEventDispatcher from './event_processor/event_dispatcher/default_dispatcher.browser';
2221
import { createNotificationCenter } from './notification_center';
2322
import { OptimizelyDecideOption, Client, Config } from './shared_types';
@@ -80,7 +79,6 @@ const createInstance = function(config: Config): Client | null {
8079
* Entry point into the Optimizely Javascript SDK for React Native
8180
*/
8281
export {
83-
defaultErrorHandler as errorHandler,
8482
defaultEventDispatcher as eventDispatcher,
8583
enums,
8684
createInstance,
@@ -98,7 +96,6 @@ export * from './common_exports';
9896

9997
export default {
10098
...commonExports,
101-
errorHandler: defaultErrorHandler,
10299
eventDispatcher: defaultEventDispatcher,
103100
enums,
104101
createInstance,

0 commit comments

Comments
 (0)