Skip to content

Commit f1c2518

Browse files
Merge branch 'FME-11223-web-support-test-improvements' into FME-11223-web-support-fix-onUpdated-with-async-callbacks
2 parents 35bcd3f + 4f72823 commit f1c2518

File tree

3 files changed

+112
-60
lines changed

3 files changed

+112
-60
lines changed

splitio_web/lib/splitio_web.dart

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -319,28 +319,19 @@ class SplitioWeb extends SplitioPlatform {
319319
required String matchingKey,
320320
required String? bucketingKey,
321321
}) async {
322-
await this._initFuture;
323-
324-
final key = buildKeyString(matchingKey, bucketingKey);
325-
326-
if (_clients.containsKey(key)) {
327-
return;
328-
}
329-
330-
final client = this._factory.client(buildJsKey(matchingKey, bucketingKey));
331-
332-
_clients[key] = client;
322+
await _getClient(matchingKey: matchingKey, bucketingKey: bucketingKey);
333323
}
334324

335325
Future<JS_IBrowserClient> _getClient({
336326
required String matchingKey,
337327
required String? bucketingKey,
338328
}) async {
339-
await getClient(matchingKey: matchingKey, bucketingKey: bucketingKey);
329+
await this._initFuture;
340330

341331
final key = buildKeyString(matchingKey, bucketingKey);
342332

343-
return _clients[key]!;
333+
return (_clients[key] ??=
334+
_factory.client(buildJsKey(matchingKey, bucketingKey)));
344335
}
345336

346337
Future<JS_IManager> _getManager() async {

splitio_web/test/splitio_web_test.dart

Lines changed: 99 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,30 @@ void main() {
4343
mock.calls.last.methodArguments.map(jsAnyToDart), ['split', {}, {}]);
4444
});
4545

46+
test('getTreatment in multiple clients', () async {
47+
final result = await _platform.getTreatment(
48+
matchingKey: 'matching-key',
49+
bucketingKey: 'bucketing-key',
50+
splitName: 'split');
51+
52+
final result2 = await _platform.getTreatment(
53+
matchingKey: 'matching-key-getTreatment',
54+
bucketingKey: null,
55+
splitName: 'split-getTreatment');
56+
57+
expect(result, 'on');
58+
expect(result2, 'on');
59+
expect(mock.calls[mock.calls.length - 3].methodName, 'getTreatment');
60+
expect(mock.calls[mock.calls.length - 3].methodArguments.map(jsAnyToDart),
61+
['split', {}, {}]);
62+
expect(mock.calls[mock.calls.length - 2].methodName, 'client');
63+
expect(mock.calls[mock.calls.length - 2].methodArguments.map(jsAnyToDart),
64+
['matching-key-getTreatment']);
65+
expect(mock.calls.last.methodName, 'getTreatment');
66+
expect(mock.calls.last.methodArguments.map(jsAnyToDart),
67+
['split-getTreatment', {}, {}]);
68+
});
69+
4670
test('getTreatment with attributes', () async {
4771
final result = await _platform.getTreatment(
4872
matchingKey: 'matching-key',
@@ -87,7 +111,7 @@ void main() {
87111
'Invalid attribute value: null, for key: attrNull, will be ignored'));
88112
});
89113

90-
test('getTreatment with evaluation properties', () async {
114+
test('getTreatment with evaluation options', () async {
91115
final result = await _platform.getTreatment(
92116
matchingKey: 'matching-key',
93117
bucketingKey: 'bucketing-key',
@@ -141,7 +165,7 @@ void main() {
141165
'Invalid property value: [value1, 100, false], for key: propList, will be ignored'));
142166
});
143167

144-
test('getTreatments without attributes', () async {
168+
test('getTreatments', () async {
145169
final result = await _platform.getTreatments(
146170
matchingKey: 'matching-key',
147171
bucketingKey: 'bucketing-key',
@@ -156,51 +180,58 @@ void main() {
156180
]);
157181
});
158182

159-
test('getTreatments with attributes and evaluation properties', () async {
183+
test('getTreatments with attributes and evaluation options', () async {
160184
final result = await _platform.getTreatments(
161185
matchingKey: 'matching-key',
162186
bucketingKey: 'bucketing-key',
163187
splitNames: ['split1', 'split2'],
164-
attributes: {'attr1': true});
188+
attributes: {'attr1': true},
189+
evaluationOptions: EvaluationOptions({'prop1': true}));
165190

166191
expect(result, {'split1': 'on', 'split2': 'on'});
167192
expect(mock.calls.last.methodName, 'getTreatments');
168193
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
169194
['split1', 'split2'],
170195
{'attr1': true},
171-
{}
196+
{
197+
'properties': {'prop1': true}
198+
}
172199
]);
173200
});
174201

175-
test('getTreatmentWithConfig with attributes', () async {
202+
test('getTreatmentWithConfig', () async {
176203
final result = await _platform.getTreatmentWithConfig(
177204
matchingKey: 'matching-key',
178205
bucketingKey: 'bucketing-key',
179-
splitName: 'split1',
180-
attributes: {'attr1': true});
206+
splitName: 'split1');
181207

182208
expect(result.toString(), SplitResult('on', 'some-config').toString());
183209
expect(mock.calls.last.methodName, 'getTreatmentWithConfig');
184-
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
185-
'split1',
186-
{'attr1': true},
187-
{}
188-
]);
210+
expect(
211+
mock.calls.last.methodArguments.map(jsAnyToDart), ['split1', {}, {}]);
189212
});
190213

191-
test('getTreatmentWithConfig without attributes', () async {
214+
test('getTreatmentWithConfig with attributes and evaluation options',
215+
() async {
192216
final result = await _platform.getTreatmentWithConfig(
193217
matchingKey: 'matching-key',
194218
bucketingKey: 'bucketing-key',
195-
splitName: 'split1');
219+
splitName: 'split1',
220+
attributes: {'attr1': true},
221+
evaluationOptions: EvaluationOptions({'prop1': true}));
196222

197223
expect(result.toString(), SplitResult('on', 'some-config').toString());
198224
expect(mock.calls.last.methodName, 'getTreatmentWithConfig');
199-
expect(
200-
mock.calls.last.methodArguments.map(jsAnyToDart), ['split1', {}, {}]);
225+
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
226+
'split1',
227+
{'attr1': true},
228+
{
229+
'properties': {'prop1': true}
230+
}
231+
]);
201232
});
202233

203-
test('getTreatmentsWithConfig without attributes', () async {
234+
test('getTreatmentsWithConfig', () async {
204235
final result = await _platform.getTreatmentsWithConfig(
205236
matchingKey: 'matching-key',
206237
bucketingKey: 'bucketing-key',
@@ -221,12 +252,14 @@ void main() {
221252
]);
222253
});
223254

224-
test('getTreatmentsWithConfig with attributes', () async {
255+
test('getTreatmentsWithConfig with attributes and evaluation options',
256+
() async {
225257
final result = await _platform.getTreatmentsWithConfig(
226258
matchingKey: 'matching-key',
227259
bucketingKey: 'bucketing-key',
228260
splitNames: ['split1', 'split2'],
229-
attributes: {'attr1': true});
261+
attributes: {'attr1': true},
262+
evaluationOptions: EvaluationOptions({'prop1': true}));
230263

231264
expect(result, predicate<Map<String, SplitResult>>((result) {
232265
return result.length == 2 &&
@@ -239,11 +272,13 @@ void main() {
239272
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
240273
['split1', 'split2'],
241274
{'attr1': true},
242-
{}
275+
{
276+
'properties': {'prop1': true}
277+
}
243278
]);
244279
});
245280

246-
test('getTreatmentsByFlagSet without attributes', () async {
281+
test('getTreatmentsByFlagSet', () async {
247282
final result = await _platform.getTreatmentsByFlagSet(
248283
matchingKey: 'matching-key',
249284
bucketingKey: 'bucketing-key',
@@ -255,23 +290,27 @@ void main() {
255290
mock.calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]);
256291
});
257292

258-
test('getTreatmentsByFlagSet with attributes', () async {
293+
test('getTreatmentsByFlagSet with attributes and evaluation options',
294+
() async {
259295
final result = await _platform.getTreatmentsByFlagSet(
260296
matchingKey: 'matching-key',
261297
bucketingKey: 'bucketing-key',
262298
flagSet: 'set_1',
263-
attributes: {'attr1': true});
299+
attributes: {'attr1': true},
300+
evaluationOptions: EvaluationOptions({'prop1': true}));
264301

265302
expect(result, {'split1': 'on', 'split2': 'on'});
266303
expect(mock.calls.last.methodName, 'getTreatmentsByFlagSet');
267304
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
268305
'set_1',
269306
{'attr1': true},
270-
{}
307+
{
308+
'properties': {'prop1': true}
309+
}
271310
]);
272311
});
273312

274-
test('getTreatmentsByFlagSets without attributes', () async {
313+
test('getTreatmentsByFlagSets', () async {
275314
final result = await _platform.getTreatmentsByFlagSets(
276315
matchingKey: 'matching-key',
277316
bucketingKey: 'bucketing-key',
@@ -286,23 +325,27 @@ void main() {
286325
]);
287326
});
288327

289-
test('getTreatmentsByFlagSets with attributes', () async {
328+
test('getTreatmentsByFlagSets with attributes and evaluation options',
329+
() async {
290330
final result = await _platform.getTreatmentsByFlagSets(
291331
matchingKey: 'matching-key',
292332
bucketingKey: 'bucketing-key',
293333
flagSets: ['set_1', 'set_2'],
294-
attributes: {'attr1': true});
334+
attributes: {'attr1': true},
335+
evaluationOptions: EvaluationOptions({'prop1': true}));
295336

296337
expect(result, {'split1': 'on', 'split2': 'on'});
297338
expect(mock.calls.last.methodName, 'getTreatmentsByFlagSets');
298339
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
299340
['set_1', 'set_2'],
300341
{'attr1': true},
301-
{}
342+
{
343+
'properties': {'prop1': true}
344+
}
302345
]);
303346
});
304347

305-
test('getTreatmentsWithConfigByFlagSet without attributes', () async {
348+
test('getTreatmentsWithConfigByFlagSet', () async {
306349
final result = await _platform.getTreatmentsWithConfigByFlagSet(
307350
matchingKey: 'matching-key',
308351
bucketingKey: 'bucketing-key',
@@ -320,12 +363,15 @@ void main() {
320363
mock.calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]);
321364
});
322365

323-
test('getTreatmentsWithConfigByFlagSet with attributes', () async {
366+
test(
367+
'getTreatmentsWithConfigByFlagSet with attributes and evaluation options',
368+
() async {
324369
final result = await _platform.getTreatmentsWithConfigByFlagSet(
325370
matchingKey: 'matching-key',
326371
bucketingKey: 'bucketing-key',
327372
flagSet: 'set_1',
328-
attributes: {'attr1': true});
373+
attributes: {'attr1': true},
374+
evaluationOptions: EvaluationOptions({'prop1': true}));
329375

330376
expect(result, predicate<Map<String, SplitResult>>((result) {
331377
return result.length == 2 &&
@@ -338,11 +384,13 @@ void main() {
338384
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
339385
'set_1',
340386
{'attr1': true},
341-
{}
387+
{
388+
'properties': {'prop1': true}
389+
}
342390
]);
343391
});
344392

345-
test('getTreatmentsWithConfigByFlagSets without attributes', () async {
393+
test('getTreatmentsWithConfigByFlagSets', () async {
346394
final result = await _platform.getTreatmentsWithConfigByFlagSets(
347395
matchingKey: 'matching-key',
348396
bucketingKey: 'bucketing-key',
@@ -363,12 +411,15 @@ void main() {
363411
]);
364412
});
365413

366-
test('getTreatmentsWithConfigByFlagSets with attributes', () async {
414+
test(
415+
'getTreatmentsWithConfigByFlagSets with attributes and evaluation options',
416+
() async {
367417
final result = await _platform.getTreatmentsWithConfigByFlagSets(
368418
matchingKey: 'matching-key',
369419
bucketingKey: 'bucketing-key',
370420
flagSets: ['set_1', 'set_2'],
371-
attributes: {'attr1': true});
421+
attributes: {'attr1': true},
422+
evaluationOptions: EvaluationOptions({'prop1': true}));
372423

373424
expect(result, predicate<Map<String, SplitResult>>((result) {
374425
return result.length == 2 &&
@@ -381,7 +432,9 @@ void main() {
381432
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
382433
['set_1', 'set_2'],
383434
{'attr1': true},
384-
{}
435+
{
436+
'properties': {'prop1': true}
437+
}
385438
]);
386439
});
387440
});
@@ -918,18 +971,24 @@ void main() {
918971
expect(onReadyFromCache, completion(equals(true)));
919972
});
920973

921-
test('onTimeout', () {
974+
test('onTimeout (in multiple clients)', () async {
922975
Future<void>? onTimeout = _platform
923976
.onTimeout(matchingKey: 'matching-key', bucketingKey: 'bucketing-key')
924977
?.then((value) => true);
925978

926-
// Emit SDK_READY_TIMED_OUT event
979+
Future<void>? onTimeoutClient2 = _platform
980+
.onTimeout(matchingKey: 'matching-key-2', bucketingKey: null)
981+
?.then((value) => false);
982+
983+
// Emit SDK_READY_TIMED_OUT event on the first client
927984
final mockClient =
928985
mock.mockFactory.client(buildJsKey('matching-key', 'bucketing-key'));
929986
mockClient.emit
930987
.callAsFunction(null, mockClient.Event.SDK_READY_TIMED_OUT);
931988

932-
expect(onTimeout, completion(equals(true)));
989+
// Assert that onTimeout is completed for the first client only
990+
await expectLater(onTimeout, completion(isTrue));
991+
await expectLater(onTimeoutClient2, doesNotComplete);
933992
});
934993

935994
test('onUpdated', () async {

splitio_web/test/utils/js_interop_test_utils.dart

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,6 @@ class SplitioMock {
2525

2626
JS_Configuration? _config;
2727
JSString _userConsent = 'UNKNOWN'.toJS;
28-
JS_ReadinessStatus _readinessStatus = {
29-
'isReady': false,
30-
'isReadyFromCache': false,
31-
'hasTimedout': false,
32-
}.jsify() as JS_ReadinessStatus;
33-
Map<JSString, Set<JSFunction>> _eventListeners = {};
3428

3529
JSObject _createSplitViewJSObject(JSString splitName) {
3630
return {
@@ -115,7 +109,8 @@ class SplitioMock {
115109
calls.add((methodName: 'client', methodArguments: [splitKey]));
116110

117111
final dartKey = buildDartKey(splitKey ?? _config!.core.key);
118-
final stringKey = buildKeyString(dartKey.matchingKey, dartKey.bucketingKey);
112+
final stringKey =
113+
buildKeyString(dartKey.matchingKey, dartKey.bucketingKey);
119114
_mockClients[stringKey] ??= _buildMockClient();
120115
return _mockClients[stringKey];
121116
}.toJS);
@@ -146,7 +141,14 @@ class SplitioMock {
146141
}
147142

148143
JS_IBrowserClient _buildMockClient() {
144+
final JS_ReadinessStatus _readinessStatus = {
145+
'isReady': false,
146+
'isReadyFromCache': false,
147+
'hasTimedout': false,
148+
}.jsify() as JS_ReadinessStatus;
149+
final Map<JSString, Set<JSFunction>> _eventListeners = {};
149150
final mockClient = JSObject() as JS_IBrowserClient;
151+
150152
mockClient.Event = _mockEvents;
151153
mockClient.on = (JSString event, JSFunction listener) {
152154
calls.add((methodName: 'on', methodArguments: [event, listener]));

0 commit comments

Comments
 (0)