diff --git a/splitio_web/lib/splitio_web.dart b/splitio_web/lib/splitio_web.dart index 764d5d3..dda1bea 100644 --- a/splitio_web/lib/splitio_web.dart +++ b/splitio_web/lib/splitio_web.dart @@ -349,13 +349,13 @@ class SplitioWeb extends SplitioPlatform { return _clients[key]!; } - JSAny? _convertValue(dynamic value, bool isAttributes) { + JSAny? _convertValue(dynamic value, bool isAttribute) { if (value is bool) return value.toJS; if (value is num) return value.toJS; // covers int + double if (value is String) return value.toJS; // properties do not support lists and sets - if (isAttributes) { + if (isAttribute) { if (value is List) return value.jsify(); if (value is Set) return value.jsify(); } @@ -363,18 +363,18 @@ class SplitioWeb extends SplitioPlatform { return null; } - JSObject _convertMap(Map dartMap, bool isAttributes) { + JSObject _convertMap(Map dartMap, bool isAttribute) { final jsMap = JSObject(); dartMap.forEach((key, value) { - final jsValue = _convertValue(value, isAttributes); + final jsValue = _convertValue(value, isAttribute); if (jsValue != null) { jsMap.setProperty(key.toJS, jsValue); } else { this._factory.settings.log.warn.callAsFunction( null, - 'Invalid ${isAttributes ? 'attribute' : 'property'} value: $value, for key: $key, will be ignored' + 'Invalid ${isAttribute ? 'attribute' : 'property'} value: $value, for key: $key, will be ignored' .toJS); } }); @@ -569,4 +569,31 @@ class SplitioWeb extends SplitioPlatform { return jsTreatmentsWithConfigToMap(result); } + + @override + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) async { + final client = await _getClient( + matchingKey: matchingKey, + bucketingKey: bucketingKey, + ); + + final result = client.track.callAsFunction( + null, + trafficType != null + ? trafficType.toJS + : this._trafficType != null + ? this._trafficType!.toJS + : null, + eventType.toJS, + value != null ? value.toJS : null, + _convertMap(properties, false)) as JSBoolean; + + return result.toDart; + } } diff --git a/splitio_web/lib/src/js_interop.dart b/splitio_web/lib/src/js_interop.dart index 6ec78e3..a8838b4 100644 --- a/splitio_web/lib/src/js_interop.dart +++ b/splitio_web/lib/src/js_interop.dart @@ -23,6 +23,7 @@ extension type JS_IBrowserClient._(JSObject _) implements JSObject { external JSFunction getTreatmentsByFlagSets; external JSFunction getTreatmentsWithConfigByFlagSet; external JSFunction getTreatmentsWithConfigByFlagSets; + external JSFunction track; } @JS() diff --git a/splitio_web/test/splitio_web_test.dart b/splitio_web/test/splitio_web_test.dart index 8af0a9e..df48f8a 100644 --- a/splitio_web/test/splitio_web_test.dart +++ b/splitio_web/test/splitio_web_test.dart @@ -10,6 +10,7 @@ import 'package:splitio_platform_interface/split_evaluation_options.dart'; import 'package:splitio_platform_interface/split_sync_config.dart'; import 'package:splitio_platform_interface/split_result.dart'; import 'package:splitio_platform_interface/split_rollout_cache_configuration.dart'; +import 'utils/js_interop_test_utils.dart'; extension on web.Window { @JS() @@ -17,143 +18,12 @@ extension on web.Window { } void main() { - final List<({String methodName, List methodArguments})> calls = []; - - final mockClient = JSObject(); - mockClient['getTreatment'] = - (JSAny? flagName, JSAny? attributes, JSAny? evaluationOptions) { - calls.add(( - methodName: 'getTreatment', - methodArguments: [flagName, attributes, evaluationOptions] - )); - return 'on'.toJS; - }.toJS; - mockClient['getTreatments'] = - (JSAny? flagNames, JSAny? attributes, JSAny? evaluationOptions) { - calls.add(( - methodName: 'getTreatments', - methodArguments: [flagNames, attributes, evaluationOptions] - )); - if (flagNames is JSArray) { - return flagNames.toDart.fold(JSObject(), (previousValue, element) { - if (element is JSString) { - previousValue.setProperty(element, 'on'.toJS); - } - return previousValue; - }); - } - return JSObject(); - }.toJS; - mockClient['getTreatmentWithConfig'] = - (JSAny? flagName, JSAny? attributes, JSAny? evaluationOptions) { - calls.add(( - methodName: 'getTreatmentWithConfig', - methodArguments: [flagName, attributes, evaluationOptions] - )); - final result = JSObject(); - result.setProperty('treatment'.toJS, 'on'.toJS); - result.setProperty('config'.toJS, 'some-config'.toJS); - return result; - }.toJS; - mockClient['getTreatmentsWithConfig'] = - (JSAny? flagNames, JSAny? attributes, JSAny? evaluationOptions) { - calls.add(( - methodName: 'getTreatmentsWithConfig', - methodArguments: [flagNames, attributes, evaluationOptions] - )); - if (flagNames is JSArray) { - return flagNames.toDart.fold(JSObject(), (previousValue, element) { - if (element is JSString) { - final result = JSObject(); - result.setProperty('treatment'.toJS, 'on'.toJS); - result.setProperty('config'.toJS, 'some-config'.toJS); - previousValue.setProperty(element, result); - } - return previousValue; - }); - } - return JSObject(); - }.toJS; - mockClient['getTreatmentsByFlagSet'] = - (JSAny? flagSetName, JSAny? attributes, JSAny? evaluationOptions) { - calls.add(( - methodName: 'getTreatmentsByFlagSet', - methodArguments: [flagSetName, attributes, evaluationOptions] - )); - final result = JSObject(); - result.setProperty('split1'.toJS, 'on'.toJS); - result.setProperty('split2'.toJS, 'on'.toJS); - return result; - }.toJS; - mockClient['getTreatmentsByFlagSets'] = - (JSAny? flagSetNames, JSAny? attributes, JSAny? evaluationOptions) { - calls.add(( - methodName: 'getTreatmentsByFlagSets', - methodArguments: [flagSetNames, attributes, evaluationOptions] - )); - final result = JSObject(); - result.setProperty('split1'.toJS, 'on'.toJS); - result.setProperty('split2'.toJS, 'on'.toJS); - return result; - }.toJS; - mockClient['getTreatmentsWithConfigByFlagSet'] = - (JSAny? flagSetName, JSAny? attributes, JSAny? evaluationOptions) { - calls.add(( - methodName: 'getTreatmentsWithConfigByFlagSet', - methodArguments: [flagSetName, attributes, evaluationOptions] - )); - - final treatmentWithConfig = JSObject(); - treatmentWithConfig.setProperty('treatment'.toJS, 'on'.toJS); - treatmentWithConfig.setProperty('config'.toJS, 'some-config'.toJS); - - final result = JSObject(); - result.setProperty('split1'.toJS, treatmentWithConfig); - result.setProperty('split2'.toJS, treatmentWithConfig); - return result; - }.toJS; - mockClient['getTreatmentsWithConfigByFlagSets'] = - (JSAny? flagSetNames, JSAny? attributes, JSAny? evaluationOptions) { - calls.add(( - methodName: 'getTreatmentsWithConfigByFlagSets', - methodArguments: [flagSetNames, attributes, evaluationOptions] - )); - - final treatmentWithConfig = JSObject(); - treatmentWithConfig.setProperty('treatment'.toJS, 'on'.toJS); - treatmentWithConfig.setProperty('config'.toJS, 'some-config'.toJS); - - final result = JSObject(); - result.setProperty('split1'.toJS, treatmentWithConfig); - result.setProperty('split2'.toJS, treatmentWithConfig); - return result; - }.toJS; - - final mockLog = JSObject(); - mockLog['warn'] = (JSAny? arg1) { - calls.add((methodName: 'warn', methodArguments: [arg1])); - }.toJS; - - final mockSettings = JSObject(); - mockSettings['log'] = mockLog; - - final mockFactory = JSObject(); - mockFactory['settings'] = mockSettings; - mockFactory['client'] = (JSAny? splitKey) { - calls.add((methodName: 'client', methodArguments: [splitKey])); - return mockClient; - }.toJS; - - final mockSplitio = JSObject(); - mockSplitio['SplitFactory'] = (JSAny? arg1) { - calls.add((methodName: 'SplitFactory', methodArguments: [arg1])); - return mockFactory; - }.toJS; SplitioWeb _platform = SplitioWeb(); + final mock = SplitioMock(); setUp(() { - (web.window as JSObject)['splitio'] = mockSplitio; + (web.window as JSObject)['splitio'] = mock.splitio; _platform.init( apiKey: 'apiKey', @@ -169,8 +39,8 @@ void main() { splitName: 'split'); expect(result, 'on'); - expect(calls.last.methodName, 'getTreatment'); - expect(calls.last.methodArguments.map(jsAnyToDart), ['split', {}, {}]); + expect(mock.calls.last.methodName, 'getTreatment'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['split', {}, {}]); }); test('getTreatment with attributes', () async { @@ -190,8 +60,8 @@ void main() { }); expect(result, 'on'); - expect(calls.last.methodName, 'getTreatment'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatment'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ 'split', { 'attrBool': true, @@ -205,14 +75,14 @@ void main() { ]); // assert warnings - expect(calls[calls.length - 2].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 2].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 2].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 2].methodArguments[0]), equals( 'Invalid attribute value: {value5: true}, for key: attrMap, will be ignored')); - expect(calls[calls.length - 3].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 3].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 3].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 3].methodArguments[0]), equals( 'Invalid attribute value: null, for key: attrNull, will be ignored')); }); @@ -234,8 +104,8 @@ void main() { })); expect(result, 'on'); - expect(calls.last.methodName, 'getTreatment'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatment'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ 'split', {}, { @@ -249,24 +119,24 @@ void main() { ]); // assert warnings - expect(calls[calls.length - 2].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 2].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 2].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 2].methodArguments[0]), equals( 'Invalid property value: {value5: true}, for key: propMap, will be ignored')); - expect(calls[calls.length - 3].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 3].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 3].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 3].methodArguments[0]), equals( 'Invalid property value: null, for key: propNull, will be ignored')); - expect(calls[calls.length - 4].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 4].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 4].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 4].methodArguments[0]), equals( 'Invalid property value: {value3, 100, true}, for key: propSet, will be ignored')); - expect(calls[calls.length - 5].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 5].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 5].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 5].methodArguments[0]), equals( 'Invalid property value: [value1, 100, false], for key: propList, will be ignored')); }); @@ -278,8 +148,8 @@ void main() { splitNames: ['split1', 'split2']); expect(result, {'split1': 'on', 'split2': 'on'}); - expect(calls.last.methodName, 'getTreatments'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatments'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ ['split1', 'split2'], {}, {} @@ -294,8 +164,8 @@ void main() { attributes: {'attr1': true}); expect(result, {'split1': 'on', 'split2': 'on'}); - expect(calls.last.methodName, 'getTreatments'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatments'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ ['split1', 'split2'], {'attr1': true}, {} @@ -310,8 +180,8 @@ void main() { attributes: {'attr1': true}); expect(result.toString(), SplitResult('on', 'some-config').toString()); - expect(calls.last.methodName, 'getTreatmentWithConfig'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentWithConfig'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ 'split1', {'attr1': true}, {} @@ -325,8 +195,8 @@ void main() { splitName: 'split1'); expect(result.toString(), SplitResult('on', 'some-config').toString()); - expect(calls.last.methodName, 'getTreatmentWithConfig'); - expect(calls.last.methodArguments.map(jsAnyToDart), ['split1', {}, {}]); + expect(mock.calls.last.methodName, 'getTreatmentWithConfig'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['split1', {}, {}]); }); test('getTreatmentsWithConfig without attributes', () async { @@ -342,8 +212,8 @@ void main() { result['split2'].toString() == SplitResult('on', 'some-config').toString(); })); - expect(calls.last.methodName, 'getTreatmentsWithConfig'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentsWithConfig'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ ['split1', 'split2'], {}, {} @@ -364,8 +234,8 @@ void main() { result['split2'].toString() == SplitResult('on', 'some-config').toString(); })); - expect(calls.last.methodName, 'getTreatmentsWithConfig'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentsWithConfig'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ ['split1', 'split2'], {'attr1': true}, {} @@ -379,8 +249,8 @@ void main() { flagSet: 'set_1'); expect(result, {'split1': 'on', 'split2': 'on'}); - expect(calls.last.methodName, 'getTreatmentsByFlagSet'); - expect(calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]); + expect(mock.calls.last.methodName, 'getTreatmentsByFlagSet'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]); }); test('getTreatmentsByFlagSet with attributes', () async { @@ -391,8 +261,8 @@ void main() { attributes: {'attr1': true}); expect(result, {'split1': 'on', 'split2': 'on'}); - expect(calls.last.methodName, 'getTreatmentsByFlagSet'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentsByFlagSet'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ 'set_1', {'attr1': true}, {} @@ -406,8 +276,8 @@ void main() { flagSets: ['set_1', 'set_2']); expect(result, {'split1': 'on', 'split2': 'on'}); - expect(calls.last.methodName, 'getTreatmentsByFlagSets'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentsByFlagSets'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ ['set_1', 'set_2'], {}, {} @@ -422,8 +292,8 @@ void main() { attributes: {'attr1': true}); expect(result, {'split1': 'on', 'split2': 'on'}); - expect(calls.last.methodName, 'getTreatmentsByFlagSets'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentsByFlagSets'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ ['set_1', 'set_2'], {'attr1': true}, {} @@ -443,8 +313,8 @@ void main() { result['split2'].toString() == SplitResult('on', 'some-config').toString(); })); - expect(calls.last.methodName, 'getTreatmentsWithConfigByFlagSet'); - expect(calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]); + expect(mock.calls.last.methodName, 'getTreatmentsWithConfigByFlagSet'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]); }); test('getTreatmentsWithConfigByFlagSet with attributes', () async { @@ -461,8 +331,8 @@ void main() { result['split2'].toString() == SplitResult('on', 'some-config').toString(); })); - expect(calls.last.methodName, 'getTreatmentsWithConfigByFlagSet'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentsWithConfigByFlagSet'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ 'set_1', {'attr1': true}, {} @@ -482,8 +352,8 @@ void main() { result['split2'].toString() == SplitResult('on', 'some-config').toString(); })); - expect(calls.last.methodName, 'getTreatmentsWithConfigByFlagSets'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentsWithConfigByFlagSets'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ ['set_1', 'set_2'], {}, {} @@ -504,8 +374,8 @@ void main() { result['split2'].toString() == SplitResult('on', 'some-config').toString(); })); - expect(calls.last.methodName, 'getTreatmentsWithConfigByFlagSets'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'getTreatmentsWithConfigByFlagSets'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ ['set_1', 'set_2'], {'attr1': true}, {} @@ -513,6 +383,74 @@ void main() { }); }); + group('track', () { + test('track with traffic type, value & properties', () async { + final result = await _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type', + value: 25.10, + properties: { + 'propBool': true, + 'propString': 'value', + 'propInt': 1, + 'propDouble': 1.1, + 'propList': ['value1', 100, false], // not valid property value + 'propSet': {'value3', 100, true}, // not valid property value + 'propNull': null, // not valid property value + 'propMap': {'value5': true} // not valid property value + }); + + expect(result, true); + expect(mock.calls.last.methodName, 'track'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ + 'my_traffic_type', + 'my_event', + 25.10, + { + 'propBool': true, + 'propString': 'value', + 'propInt': 1, + 'propDouble': 1.1, + } + ]); + }); + + test('track with value, and no traffic type in config', () async { + final result = await _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + value: 25.20); + + expect(result, false); // false because no traffic type is provided + expect(mock.calls.last.methodName, 'track'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), + [null, 'my_event', 25.20, {}]); + }); + + test('track without value, and traffic type in config', () async { + SplitioWeb _platform = SplitioWeb(); + await _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: null, + sdkConfiguration: + SplitConfiguration(trafficType: 'my_traffic_type_in_config')); + + final result = await _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event'); + + expect(result, true); + expect(mock.calls.last.methodName, 'track'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), + ['my_traffic_type_in_config', 'my_event', null, {}]); + }); + }); + group('initialization', () { test('init with matching key only', () async { SplitioWeb _platform = SplitioWeb(); @@ -520,9 +458,9 @@ void main() { await _platform.init( apiKey: 'api-key', matchingKey: 'matching-key', bucketingKey: null); - expect(calls.last.methodName, 'SplitFactory'); + expect(mock.calls.last.methodName, 'SplitFactory'); expect( - jsAnyToDart(calls.last.methodArguments[0]), + jsAnyToDart(mock.calls.last.methodArguments[0]), equals({ 'core': { 'authorizationKey': 'api-key', @@ -539,9 +477,9 @@ void main() { matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - expect(calls.last.methodName, 'SplitFactory'); + expect(mock.calls.last.methodName, 'SplitFactory'); expect( - jsAnyToDart(calls.last.methodArguments[0]), + jsAnyToDart(mock.calls.last.methodArguments[0]), equals({ 'core': { 'authorizationKey': 'api-key', @@ -562,9 +500,9 @@ void main() { bucketingKey: 'bucketing-key', sdkConfiguration: SplitConfiguration()); - expect(calls.last.methodName, 'SplitFactory'); + expect(mock.calls.last.methodName, 'SplitFactory'); expect( - jsAnyToDart(calls.last.methodArguments[0]), + jsAnyToDart(mock.calls.last.methodArguments[0]), equals({ 'core': { 'authorizationKey': 'api-key', @@ -625,9 +563,9 @@ void main() { clearOnInit: true, ))); - expect(calls[calls.length - 5].methodName, 'SplitFactory'); + expect(mock.calls[mock.calls.length - 5].methodName, 'SplitFactory'); expect( - jsAnyToDart(calls[calls.length - 5].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 5].methodArguments[0]), equals({ 'core': { 'authorizationKey': 'api-key', @@ -679,27 +617,27 @@ void main() { } })); - expect(calls[calls.length - 4].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 4].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 4].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 4].methodArguments[0]), equals( 'Config certificatePinningConfiguration is not supported by the Web package. This config will be ignored.')); - expect(calls[calls.length - 3].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 3].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 3].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 3].methodArguments[0]), equals( 'Config encryptionEnabled is not supported by the Web package. This config will be ignored.')); - expect(calls[calls.length - 2].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 2].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 2].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 2].methodArguments[0]), equals( 'Config eventsPerPush is not supported by the Web package. This config will be ignored.')); - expect(calls[calls.length - 1].methodName, 'warn'); + expect(mock.calls[mock.calls.length - 1].methodName, 'warn'); expect( - jsAnyToDart(calls[calls.length - 1].methodArguments[0]), + jsAnyToDart(mock.calls[mock.calls.length - 1].methodArguments[0]), equals( 'Config persistentAttributesEnabled is not supported by the Web package. This config will be ignored.')); }); @@ -714,9 +652,9 @@ void main() { sdkConfiguration: SplitConfiguration( syncConfig: SyncConfig.flagSets(['flag_set_1', 'flag_set_2']))); - expect(calls.last.methodName, 'SplitFactory'); + expect(mock.calls.last.methodName, 'SplitFactory'); expect( - jsAnyToDart(calls.last.methodArguments[0]), + jsAnyToDart(mock.calls.last.methodArguments[0]), equals({ 'core': { 'authorizationKey': 'api-key', @@ -748,24 +686,24 @@ void main() { await _platform.getClient( matchingKey: 'matching-key', bucketingKey: null); - expect(calls.last.methodName, 'client'); - expect(calls.last.methodArguments.map(jsAnyToDart), ['matching-key']); + expect(mock.calls.last.methodName, 'client'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['matching-key']); }); test('get client with new matching key', () async { await _platform.getClient( matchingKey: 'new-matching-key', bucketingKey: null); - expect(calls.last.methodName, 'client'); - expect(calls.last.methodArguments.map(jsAnyToDart), ['new-matching-key']); + expect(mock.calls.last.methodName, 'client'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['new-matching-key']); }); test('get client with new matching key and bucketing key', () async { await _platform.getClient( matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); - expect(calls.last.methodName, 'client'); - expect(calls.last.methodArguments.map(jsAnyToDart), [ + expect(mock.calls.last.methodName, 'client'); + expect(mock.calls.last.methodArguments.map(jsAnyToDart), [ {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'} ]); }); diff --git a/splitio_web/test/utils/js_interop_test_utils.dart b/splitio_web/test/utils/js_interop_test_utils.dart new file mode 100644 index 0000000..cd90c15 --- /dev/null +++ b/splitio_web/test/utils/js_interop_test_utils.dart @@ -0,0 +1,147 @@ +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; + +class SplitioMock { + final List<({String methodName, List methodArguments})> calls = []; + final JSObject splitio = JSObject(); + + SplitioMock() { + final mockClient = JSObject(); + mockClient['getTreatment'] = + (JSAny? flagName, JSAny? attributes, JSAny? evaluationOptions) { + calls.add(( + methodName: 'getTreatment', + methodArguments: [flagName, attributes, evaluationOptions] + )); + return 'on'.toJS; + }.toJS; + mockClient['getTreatments'] = + (JSAny? flagNames, JSAny? attributes, JSAny? evaluationOptions) { + calls.add(( + methodName: 'getTreatments', + methodArguments: [flagNames, attributes, evaluationOptions] + )); + if (flagNames is JSArray) { + return flagNames.toDart.fold(JSObject(), (previousValue, element) { + if (element is JSString) { + previousValue.setProperty(element, 'on'.toJS); + } + return previousValue; + }); + } + return JSObject(); + }.toJS; + mockClient['getTreatmentWithConfig'] = + (JSAny? flagName, JSAny? attributes, JSAny? evaluationOptions) { + calls.add(( + methodName: 'getTreatmentWithConfig', + methodArguments: [flagName, attributes, evaluationOptions] + )); + final result = JSObject(); + result.setProperty('treatment'.toJS, 'on'.toJS); + result.setProperty('config'.toJS, 'some-config'.toJS); + return result; + }.toJS; + mockClient['getTreatmentsWithConfig'] = + (JSAny? flagNames, JSAny? attributes, JSAny? evaluationOptions) { + calls.add(( + methodName: 'getTreatmentsWithConfig', + methodArguments: [flagNames, attributes, evaluationOptions] + )); + if (flagNames is JSArray) { + return flagNames.toDart.fold(JSObject(), (previousValue, element) { + if (element is JSString) { + final result = JSObject(); + result.setProperty('treatment'.toJS, 'on'.toJS); + result.setProperty('config'.toJS, 'some-config'.toJS); + previousValue.setProperty(element, result); + } + return previousValue; + }); + } + return JSObject(); + }.toJS; + mockClient['getTreatmentsByFlagSet'] = + (JSAny? flagSetName, JSAny? attributes, JSAny? evaluationOptions) { + calls.add(( + methodName: 'getTreatmentsByFlagSet', + methodArguments: [flagSetName, attributes, evaluationOptions] + )); + final result = JSObject(); + result.setProperty('split1'.toJS, 'on'.toJS); + result.setProperty('split2'.toJS, 'on'.toJS); + return result; + }.toJS; + mockClient['getTreatmentsByFlagSets'] = + (JSAny? flagSetNames, JSAny? attributes, JSAny? evaluationOptions) { + calls.add(( + methodName: 'getTreatmentsByFlagSets', + methodArguments: [flagSetNames, attributes, evaluationOptions] + )); + final result = JSObject(); + result.setProperty('split1'.toJS, 'on'.toJS); + result.setProperty('split2'.toJS, 'on'.toJS); + return result; + }.toJS; + mockClient['getTreatmentsWithConfigByFlagSet'] = + (JSAny? flagSetName, JSAny? attributes, JSAny? evaluationOptions) { + calls.add(( + methodName: 'getTreatmentsWithConfigByFlagSet', + methodArguments: [flagSetName, attributes, evaluationOptions] + )); + + final treatmentWithConfig = JSObject(); + treatmentWithConfig.setProperty('treatment'.toJS, 'on'.toJS); + treatmentWithConfig.setProperty('config'.toJS, 'some-config'.toJS); + + final result = JSObject(); + result.setProperty('split1'.toJS, treatmentWithConfig); + result.setProperty('split2'.toJS, treatmentWithConfig); + return result; + }.toJS; + mockClient['getTreatmentsWithConfigByFlagSets'] = + (JSAny? flagSetNames, JSAny? attributes, JSAny? evaluationOptions) { + calls.add(( + methodName: 'getTreatmentsWithConfigByFlagSets', + methodArguments: [flagSetNames, attributes, evaluationOptions] + )); + + final treatmentWithConfig = JSObject(); + treatmentWithConfig.setProperty('treatment'.toJS, 'on'.toJS); + treatmentWithConfig.setProperty('config'.toJS, 'some-config'.toJS); + + final result = JSObject(); + result.setProperty('split1'.toJS, treatmentWithConfig); + result.setProperty('split2'.toJS, treatmentWithConfig); + return result; + }.toJS; + mockClient['track'] = (JSAny? trafficType, JSAny? eventType, JSAny? value, + JSAny? properties) { + calls.add(( + methodName: 'track', + methodArguments: [trafficType, eventType, value, properties] + )); + return trafficType != null ? true.toJS : false.toJS; + }.toJS; + + final mockLog = JSObject(); + mockLog['warn'] = (JSAny? arg1) { + calls.add((methodName: 'warn', methodArguments: [arg1])); + }.toJS; + + final mockSettings = JSObject(); + mockSettings['log'] = mockLog; + + final mockFactory = JSObject(); + mockFactory['settings'] = mockSettings; + mockFactory['client'] = (JSAny? splitKey) { + calls.add((methodName: 'client', methodArguments: [splitKey])); + return mockClient; + }.toJS; + + splitio['SplitFactory'] = (JSAny? arg1) { + calls.add((methodName: 'SplitFactory', methodArguments: [arg1])); + return mockFactory; + }.toJS; + } +}