diff --git a/splitio/lib/split_client.dart b/splitio/lib/split_client.dart index ec333b6..673c978 100644 --- a/splitio/lib/split_client.dart +++ b/splitio/lib/split_client.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:splitio_platform_interface/split_result.dart'; import 'package:splitio_platform_interface/splitio_platform_interface.dart'; @@ -146,10 +145,6 @@ class DefaultSplitClient implements SplitClient { DefaultSplitClient(this._platform, this._matchingKey, this._bucketingKey); - @visibleForTesting - DefaultSplitClient.withEventListener( - this._platform, this._matchingKey, this._bucketingKey); - @override Future getTreatment(String splitName, [Map attributes = const {}]) async { diff --git a/splitio/lib/splitio.dart b/splitio/lib/splitio.dart index 203153e..d5620cb 100644 --- a/splitio/lib/splitio.dart +++ b/splitio/lib/splitio.dart @@ -18,9 +18,9 @@ class Splitio { final String _defaultMatchingKey; - late final String? _defaultBucketingKey; + late String? _defaultBucketingKey; - late final SplitConfiguration? _splitConfiguration; + late SplitConfiguration? _splitConfiguration; final SplitioPlatform _platform = SplitioPlatform.instance; @@ -103,15 +103,13 @@ class Splitio { } Future> splitNames() async { - List splitNames = await _platform.splitNames( - matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + List splitNames = await _platform.splitNames(); return splitNames; } Future> splits() async { - return _platform.splits( - matchingKey: _defaultMatchingKey, bucketingKey: _defaultBucketingKey); + return _platform.splits(); } /// If the impressionListener configuration has been enabled, @@ -121,10 +119,7 @@ class Splitio { } Future split(String splitName) async { - return _platform.split( - matchingKey: _defaultMatchingKey, - bucketingKey: _defaultBucketingKey, - splitName: splitName); + return _platform.split(splitName: splitName); } Future _init() { diff --git a/splitio/test/splitio_client_test.dart b/splitio/test/splitio_client_test.dart index 0babeb2..25e638f 100644 --- a/splitio/test/splitio_client_test.dart +++ b/splitio/test/splitio_client_test.dart @@ -1,59 +1,24 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/split_client.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -void main() { - const MethodChannel _channel = MethodChannel('splitio'); - - String methodName = ''; - dynamic methodArguments; +import 'splitio_platform_stub.dart'; +void main() { TestWidgetsFlutterBinding.ensureInitialized(); - MethodChannelManager _methodChannelWrapper = MethodChannelManager(_channel); + SplitioPlatformStub _platform = SplitioPlatformStub(); - SplitClient _getClient([SplitEventsListener? splitEventsListener]) { - if (splitEventsListener != null) { - return DefaultSplitClient.withEventListener(_methodChannelWrapper, - 'matching-key', 'bucketing-key', splitEventsListener); + SplitClient _getClient([SplitioPlatform? platform]) { + if (platform != null) { + return DefaultSplitClient(platform, 'matching-key', 'bucketing-key'); } - return DefaultSplitClient( - _methodChannelWrapper, 'matching-key', 'bucketing-key'); + return DefaultSplitClient(_platform, 'matching-key', 'bucketing-key'); } setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) async { - methodName = methodCall.method; - methodArguments = methodCall.arguments; - - switch (methodCall.method) { - case 'getTreatment': - return ''; - case 'getTreatments': - return {'split1': 'on', 'split2': 'off'}; - case 'getTreatmentsWithConfig': - return { - 'split1': {'treatment': 'on', 'config': null}, - 'split2': {'treatment': 'off', 'config': null} - }; - case 'track': - return true; - case 'getAttribute': - return true; - case 'getAllAttributes': - return { - 'attr_1': true, - 'attr_2': ['list-element'], - 'attr_3': 28.20 - }; - case 'setAttribute': - case 'setAttributes': - case 'removeAttribute': - case 'clearAttributes': - return true; - } - }); + _platform = SplitioPlatformStub(); }); group('evaluation', () { @@ -62,8 +27,8 @@ void main() { client.getTreatment('split'); - expect(methodName, 'getTreatment'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatment'); + expect(_platform.methodArguments, { 'splitName': 'split', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -76,8 +41,8 @@ void main() { client.getTreatment('split', {'attr1': true}); - expect(methodName, 'getTreatment'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatment'); + expect(_platform.methodArguments, { 'splitName': 'split', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -90,8 +55,8 @@ void main() { client.getTreatments(['split1', 'split2']); - expect(methodName, 'getTreatments'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatments'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -104,8 +69,8 @@ void main() { client.getTreatments(['split1', 'split2'], {'attr1': true}); - expect(methodName, 'getTreatments'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatments'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -118,8 +83,8 @@ void main() { client.getTreatmentWithConfig('split1', {'attr1': true}); - expect(methodName, 'getTreatmentWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentWithConfig'); + expect(_platform.methodArguments, { 'splitName': 'split1', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -132,8 +97,8 @@ void main() { client.getTreatmentWithConfig('split1'); - expect(methodName, 'getTreatmentWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentWithConfig'); + expect(_platform.methodArguments, { 'splitName': 'split1', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -146,8 +111,8 @@ void main() { client.getTreatmentsWithConfig(['split1', 'split2']); - expect(methodName, 'getTreatmentsWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentsWithConfig'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -160,8 +125,8 @@ void main() { client.getTreatmentsWithConfig(['split1', 'split2'], {'attr1': true}); - expect(methodName, 'getTreatmentsWithConfig'); - expect(methodArguments, { + expect(_platform.methodName, 'getTreatmentsWithConfig'); + expect(_platform.methodArguments, { 'splitName': ['split1', 'split2'], 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -175,8 +140,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', trafficType: 'my_traffic_type', value: 25.10); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -189,8 +154,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', value: 25.10); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -202,8 +167,8 @@ void main() { SplitClient client = _getClient(); client.track('my_event', trafficType: 'my_traffic_type'); - expect(methodName, 'track'); - expect(methodArguments, { + expect(_platform.methodName, 'track'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'eventType': 'my_event', @@ -217,8 +182,8 @@ void main() { SplitClient client = _getClient(); client.getAttribute('attribute-name'); - expect(methodName, 'getAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'getAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'attribute-name', @@ -229,8 +194,8 @@ void main() { SplitClient client = _getClient(); client.getAttributes(); - expect(methodName, 'getAllAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'getAllAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); @@ -240,8 +205,8 @@ void main() { SplitClient client = _getClient(); client.setAttribute('my_attr', 'attr_value'); - expect(methodName, 'setAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'setAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'my_attr', @@ -258,8 +223,8 @@ void main() { 'string_attr': 'attr-value', 'list_attr': ['one', 'two'], }); - expect(methodName, 'setAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'setAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributes': { @@ -275,8 +240,8 @@ void main() { SplitClient client = _getClient(); client.removeAttribute('attr-name'); - expect(methodName, 'removeAttribute'); - expect(methodArguments, { + expect(_platform.methodName, 'removeAttribute'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', 'attributeName': 'attr-name', @@ -287,8 +252,8 @@ void main() { SplitClient client = _getClient(); client.clearAttributes(); - expect(methodName, 'clearAttributes'); - expect(methodArguments, { + expect(_platform.methodName, 'clearAttributes'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); @@ -298,122 +263,58 @@ void main() { SplitClient client = _getClient(); client.flush(); - expect(methodName, 'flush'); - expect(methodArguments, { + expect(_platform.methodName, 'flush'); + expect(_platform.methodArguments, { 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', }); }); - test('destroy', () async { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); + test('destroy', () { + _platform.destroy( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - client.destroy(); - - expect(splitEventsListenerStub.calledMethods['destroy'], 1); - - client.destroy(); - expect(methodName, 'destroy'); - expect(methodArguments, { - 'matchingKey': 'matching-key', - 'bucketingKey': 'bucketing-key', - }); + expect(_platform.methodName, 'destroy'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); }); }); group('events', () { - test('onReady is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenReady().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], 1); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); - }); + test('onReady', () { + _platform.onReady( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - test('onReadyFromCache is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenReadyFromCache().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], 1); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); + expect(_platform.methodName, 'onReady'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); }); - test('onTimeout is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = client.whenTimeout().then((value) => client == value); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], 1); - expect(splitEventsListenerStub.calledMethods['onUpdated'], null); - expect(future, completion(equals(true))); - }); + test('onReadyFromCache', () { + _platform.onReadyFromCache( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - test('onUpdated is returned from events listener', () { - var splitEventsListenerStub = SplitEventsListenerStub(); - SplitClient client = _getClient(splitEventsListenerStub); - splitEventsListenerStub.attachClient(client); - - var future = - client.whenUpdated().first.then(((value) => client == value)); - expect(splitEventsListenerStub.calledMethods['onReady'], null); - expect(splitEventsListenerStub.calledMethods['onReadyFromCache'], null); - expect(splitEventsListenerStub.calledMethods['onTimeout'], null); - expect(splitEventsListenerStub.calledMethods['onUpdated'], 1); - expect(future, completion(equals(true))); + expect(_platform.methodName, 'onReadyFromCache'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); }); - }); -} -class SplitEventsListenerStub extends SplitEventsListener { - Map calledMethods = {}; + test('onTimeout', () { + _platform.onTimeout( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - late final Future _clientFuture; - - void attachClient(SplitClient splitClient) { - _clientFuture = Future.value(splitClient); - } - - @override - Future onReady() { - calledMethods.update('onReady', (value) => value + 1, ifAbsent: () => 1); - return _clientFuture; - } - - @override - Future onReadyFromCache() { - calledMethods.update('onReadyFromCache', (value) => value + 1, - ifAbsent: () => 1); - return _clientFuture; - } - - @override - Future onTimeout() { - calledMethods.update('onTimeout', (value) => value + 1, ifAbsent: () => 1); - return _clientFuture; - } + expect(_platform.methodName, 'onTimeout'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); + }); - @override - Stream onUpdated() { - calledMethods.update('onUpdated', (value) => value + 1, ifAbsent: () => 1); - return Stream.fromFuture(_clientFuture); - } + test('onUpdated', () { + _platform.onUpdated( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); - @override - void destroy() { - calledMethods.update('destroy', (value) => value + 1, ifAbsent: () => 1); - } + expect(_platform.methodName, 'onUpdated'); + expect(_platform.methodArguments, + {'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key'}); + }); + }); } diff --git a/splitio/test/splitio_platform_stub.dart b/splitio/test/splitio_platform_stub.dart new file mode 100644 index 0000000..13e27f7 --- /dev/null +++ b/splitio/test/splitio_platform_stub.dart @@ -0,0 +1,346 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; +import 'package:splitio_platform_interface/split_result.dart'; +import 'package:splitio_platform_interface/split_view.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; + +class SplitioPlatformStub + with MockPlatformInterfaceMixin + implements SplitioPlatform { + String methodName = ''; + Map methodArguments = {}; + + @override + Future clearAttributes( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'clearAttributes'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + return Future.value(true); + } + + @override + Future destroy( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'destroy'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future flush( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'flush'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + return Future.value(); + } + + @override + Future> getAllAttributes( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'getAllAttributes'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value({}); + } + + @override + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + methodName = 'getAttribute'; + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName + }; + + return Future.value(); + } + + @override + Future getClient( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'getClient'; + + methodArguments = { + 'matchingKey': matchingKey, + }; + + if (bucketingKey != null) { + methodArguments['bucketingKey'] = bucketingKey; + } + + return Future.value(); + } + + @override + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + methodName = 'getTreatment'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitName, + 'attributes': attributes + }; + + return Future.value(''); + } + + @override + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) { + methodName = 'getTreatmentWithConfig'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitName, + 'attributes': attributes, + }; + + return Future.value(const SplitResult('on', null)); + } + + @override + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + methodName = 'getTreatments'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitNames, + 'attributes': attributes, + }; + + return Future.value({}); + } + + @override + Future> getTreatmentsWithConfig( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) { + methodName = 'getTreatmentsWithConfig'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'splitName': splitNames, + 'attributes': attributes, + }; + + return Future.value({}); + } + + @override + Stream impressionsStream() { + methodName = 'impressionsStream'; + + return const Stream.empty(); + } + + @override + Future init( + {required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { + methodName = 'init'; + + methodArguments = { + 'matchingKey': matchingKey, + 'apiKey': apiKey, + 'sdkConfiguration': sdkConfiguration?.configurationMap ?? {}, + }; + + if (bucketingKey != null) { + methodArguments['bucketingKey'] = bucketingKey; + } + + return Future.value(); + } + + @override + Future? onReady( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onReady'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future? onReadyFromCache( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onReadyFromCache'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Future? onTimeout( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onTimeout'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return Future.value(); + } + + @override + Stream? onUpdated( + {required String matchingKey, required String? bucketingKey}) { + methodName = 'onUpdated'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey + }; + + return const Stream.empty(); + } + + @override + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { + methodName = 'removeAttribute'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName + }; + + return Future.value(true); + } + + @override + Future setAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) { + methodName = 'setAttribute'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributeName': attributeName, + 'value': value, + }; + + return Future.value(true); + } + + @override + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) { + methodName = 'setAttributes'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'attributes': attributes + }; + + return Future.value(true); + } + + @override + Future split({required String splitName}) { + methodName = 'split'; + + methodArguments = {'splitName': splitName}; + + return Future.value(null); + } + + @override + Future> splitNames() { + methodName = 'splitNames'; + + return Future.value([]); + } + + @override + Future> splits() { + methodName = 'splits'; + + return Future.value([]); + } + + @override + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) { + methodName = 'track'; + + methodArguments = { + 'matchingKey': matchingKey, + 'bucketingKey': bucketingKey, + 'eventType': eventType + }; + + if (properties.isNotEmpty) { + methodArguments['properties'] = properties; + } + + if (trafficType != null) { + methodArguments['trafficType'] = trafficType; + } + + if (value != null) { + methodArguments['value'] = value; + } + + return Future.value(true); + } +} diff --git a/splitio/test/splitio_test.dart b/splitio/test/splitio_test.dart index 6d46e08..37cd1c5 100644 --- a/splitio/test/splitio_test.dart +++ b/splitio/test/splitio_test.dart @@ -1,37 +1,20 @@ -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:splitio/splitio.dart'; +import 'package:splitio_platform_interface/splitio_platform_interface.dart'; -void main() { - const MethodChannel channel = MethodChannel('splitio'); - - var methodArguments; - var methodName; +import 'splitio_platform_stub.dart'; +void main() { TestWidgetsFlutterBinding.ensureInitialized(); - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - methodName = methodCall.method; - methodArguments = methodCall.arguments; - - if (methodCall.method == 'splitNames') { - List emptyList = []; - return Future.value(emptyList); - } - }); - }); - - tearDown(() async { - methodName = null; - methodArguments = null; - }); + SplitioPlatformStub _platform = SplitioPlatformStub(); + SplitioPlatform.instance = _platform; group('initialization', () { test('init with matching key only', () { Splitio('api-key', 'matching-key'); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'sdkConfiguration': {} @@ -40,8 +23,8 @@ void main() { test('init with bucketing key', () { Splitio('api-key', 'matching-key', bucketingKey: 'bucketing-key'); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -54,8 +37,8 @@ void main() { bucketingKey: 'bucketing-key', configuration: SplitConfiguration(enableDebug: true, streamingEnabled: false)); - expect(methodName, 'init'); - expect(methodArguments, { + expect(_platform.methodName, 'init'); + expect(_platform.methodArguments, { 'apiKey': 'api-key', 'matchingKey': 'matching-key', 'bucketingKey': 'bucketing-key', @@ -70,8 +53,8 @@ void main() { splitio.client(); - expect(methodName, 'getClient'); - expect(methodArguments, {'matchingKey': 'matching-key'}); + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'matching-key'}); }); test('get client with new matching key', () { @@ -79,8 +62,8 @@ void main() { splitio.client(matchingKey: 'new-matching-key'); - expect(methodName, 'getClient'); - expect(methodArguments, {'matchingKey': 'new-matching-key'}); + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'new-matching-key'}); }); test('get client with new matching key and bucketing key', () { @@ -89,8 +72,8 @@ void main() { splitio.client( matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); - expect(methodName, 'getClient'); - expect(methodArguments, + expect(_platform.methodName, 'getClient'); + expect(_platform.methodArguments, {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'}); }); }); @@ -100,22 +83,22 @@ void main() { var splitio = Splitio('api-key', 'matching-key'); splitio.splitNames(); - expect(methodName, 'splitNames'); + expect(_platform.methodName, 'splitNames'); }); test('get splits', () { var splitio = Splitio('api-key', 'matching-key'); splitio.splits(); - expect(methodName, 'splits'); + expect(_platform.methodName, 'splits'); }); test('get split', () { var splitio = Splitio('api-key', 'matching-key'); splitio.split('my_split'); - expect(methodName, 'split'); - expect(methodArguments, {'splitName': 'my_split'}); + expect(_platform.methodName, 'split'); + expect(_platform.methodArguments, {'splitName': 'my_split'}); }); }); } diff --git a/splitio_platform_interface/lib/method_channel_platform.dart b/splitio_platform_interface/lib/method_channel_platform.dart index 95ff186..b9a0513 100644 --- a/splitio_platform_interface/lib/method_channel_platform.dart +++ b/splitio_platform_interface/lib/method_channel_platform.dart @@ -1,4 +1,3 @@ - import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; @@ -14,17 +13,20 @@ const String _controlTreatment = 'control'; const SplitResult _controlResult = SplitResult(_controlTreatment, null); class MethodChannelPlatform extends SplitioPlatform { - final MethodChannel _methodChannel = const MethodChannel('splitio'); final Map _handlers = {}; - final ImpressionsMethodCallHandler _impressionsMethodCallHandler = ImpressionsMethodCallHandler(); + final ImpressionsMethodCallHandler _impressionsMethodCallHandler = + ImpressionsMethodCallHandler(); MethodChannelPlatform() { _methodChannel.setMethodCallHandler((call) => handle(call)); } + @visibleForTesting + MethodChannelPlatform.withoutHandler(); + @visibleForTesting Future handle(MethodCall call) async { _impressionsMethodCallHandler.handle(call.method, call.arguments); @@ -34,10 +36,11 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future init({required String apiKey, - required String matchingKey, - required String? bucketingKey, - SplitConfiguration? sdkConfiguration}) { + Future init( + {required String apiKey, + required String matchingKey, + required String? bucketingKey, + SplitConfiguration? sdkConfiguration}) { Map arguments = { 'apiKey': apiKey, 'matchingKey': matchingKey, @@ -54,11 +57,10 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future getClient( {required String matchingKey, required String? bucketingKey}) { - _handlers.addAll( - { - _buildMapKey(matchingKey, bucketingKey): SplitEventMethodCallHandler( - matchingKey, bucketingKey) - }); + _handlers.addAll({ + _buildMapKey(matchingKey, bucketingKey): + SplitEventMethodCallHandler(matchingKey, bucketingKey) + }); return _methodChannel.invokeMethod( 'getClient', _buildParameters(matchingKey, bucketingKey)); @@ -92,42 +94,46 @@ class MethodChannelPlatform extends SplitioPlatform { @override Future> getAllAttributes( {required String matchingKey, required String? bucketingKey}) async { - return (await _methodChannel.invokeMapMethod( - 'getAllAttributes', _buildParameters(matchingKey, bucketingKey))) - ?.map((key, value) => MapEntry(key, value)) ?? + return (await _methodChannel.invokeMapMethod('getAllAttributes', + _buildParameters(matchingKey, bucketingKey))) + ?.map((key, value) => MapEntry(key, value)) ?? {}; } @override - Future getAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName}) { + Future getAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) { return _methodChannel.invokeMethod( - 'getAttribute', _buildParameters( - matchingKey, bucketingKey, {'attributeName': attributeName})); + 'getAttribute', + _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); } @override - Future getTreatment({required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) async { + Future getTreatment( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { return await _methodChannel.invokeMethod( - 'getTreatment', - _buildParameters(matchingKey, bucketingKey, - {'splitName': splitName, 'attributes': attributes})) ?? + 'getTreatment', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitName, 'attributes': attributes})) ?? _controlTreatment; } @override - Future getTreatmentWithConfig({required String matchingKey, - required String? bucketingKey, - required String splitName, - Map attributes = const {}}) async { + Future getTreatmentWithConfig( + {required String matchingKey, + required String? bucketingKey, + required String splitName, + Map attributes = const {}}) async { Map? treatment = (await _methodChannel.invokeMapMethod( - 'getTreatmentWithConfig', - _buildParameters(matchingKey, bucketingKey - {'splitName': splitName, 'attributes': attributes}))) + 'getTreatmentWithConfig', + _buildParameters(matchingKey, bucketingKey, + {'splitName': splitName, 'attributes': attributes}))) ?.entries .first .value; @@ -139,51 +145,56 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> getTreatments({required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) async { + Future> getTreatments( + {required String matchingKey, + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { Map? treatments = await _methodChannel.invokeMapMethod( 'getTreatments', _buildParameters(matchingKey, bucketingKey, {'splitName': splitNames, 'attributes': attributes})); return treatments - ?.map((key, value) => MapEntry(key, value)) ?? + ?.map((key, value) => MapEntry(key, value)) ?? {for (var item in splitNames) item: _controlTreatment}; } @override Future> getTreatmentsWithConfig( {required String matchingKey, - required String? bucketingKey, - required List splitNames, - Map attributes = const {}}) async { + required String? bucketingKey, + required List splitNames, + Map attributes = const {}}) async { Map? treatments = await _methodChannel.invokeMapMethod( 'getTreatmentsWithConfig', _buildParameters(matchingKey, bucketingKey, {'splitName': splitNames, 'attributes': attributes})); return treatments?.map((key, value) => - MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? + MapEntry(key, SplitResult(value['treatment'], value['config']))) ?? {for (var item in splitNames) item: _controlResult}; } @override - Future removeAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName}) async { + Future removeAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName}) async { return await _methodChannel.invokeMethod( - 'removeAttribute', _buildParameters( - matchingKey, bucketingKey, {'attributeName': attributeName})); + 'removeAttribute', + _buildParameters( + matchingKey, bucketingKey, {'attributeName': attributeName})); } @override - Future setAttribute({required String matchingKey, - required String? bucketingKey, - required String attributeName, - required value}) async { - var result = await _methodChannel.invokeMethod('setAttribute', + Future setAttribute( + {required String matchingKey, + required String? bucketingKey, + required String attributeName, + required value}) async { + var result = await _methodChannel.invokeMethod( + 'setAttribute', _buildParameters(matchingKey, bucketingKey, {'attributeName': attributeName, 'value': value})); @@ -195,12 +206,14 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future setAttributes({required String matchingKey, - required String? bucketingKey, - required Map attributes}) async { + Future setAttributes( + {required String matchingKey, + required String? bucketingKey, + required Map attributes}) async { var result = await _methodChannel.invokeMethod( - 'setAttributes', _buildParameters( - matchingKey, bucketingKey, {'attributes': attributes})); + 'setAttributes', + _buildParameters( + matchingKey, bucketingKey, {'attributes': attributes})); if (result is bool) { return result; @@ -210,11 +223,9 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future split({required String matchingKey, - required String? bucketingKey, - required String splitName}) async { + Future split({required String splitName}) async { Map? mapResult = - await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); + await _methodChannel.invokeMapMethod('split', {'splitName': splitName}); if (mapResult == null) { return null; @@ -224,8 +235,7 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) async { + Future> splitNames() async { List splitNames = await _methodChannel.invokeListMethod('splitNames') ?? []; @@ -233,10 +243,9 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future> splits( - {required String matchingKey, required String? bucketingKey}) async { + Future> splits() async { List callResult = (await _methodChannel - .invokeListMethod>('splits') ?? + .invokeListMethod>('splits') ?? []); List splits = []; @@ -251,14 +260,15 @@ class MethodChannelPlatform extends SplitioPlatform { } @override - Future track({required String matchingKey, - required String? bucketingKey, - required String eventType, - String? trafficType, - double? value, - Map properties = const {}}) async { - var parameters = _buildParameters( - matchingKey, bucketingKey, {'eventType': eventType}); + Future track( + {required String matchingKey, + required String? bucketingKey, + required String eventType, + String? trafficType, + double? value, + Map properties = const {}}) async { + var parameters = + _buildParameters(matchingKey, bucketingKey, {'eventType': eventType}); if (trafficType != null) { parameters['trafficType'] = trafficType; @@ -269,15 +279,14 @@ class MethodChannelPlatform extends SplitioPlatform { } try { - return await _methodChannel.invokeMethod("track", parameters) - as bool; + return await _methodChannel.invokeMethod('track', parameters) as bool; } on Exception catch (_) { return false; } } - Map _buildParameters(String matchingKey, - String? bucketingKey, + Map _buildParameters( + String matchingKey, String? bucketingKey, [Map parameters = const {}]) { Map result = {}; result.addAll(parameters); diff --git a/splitio_platform_interface/lib/splitio_platform_interface.dart b/splitio_platform_interface/lib/splitio_platform_interface.dart index e368763..71c97ea 100644 --- a/splitio_platform_interface/lib/splitio_platform_interface.dart +++ b/splitio_platform_interface/lib/splitio_platform_interface.dart @@ -22,20 +22,16 @@ abstract class _FactoryPlatform { throw UnimplementedError(); } - Future split( - {required String matchingKey, - required String? bucketingKey, + Future split({ required String splitName}) { throw UnimplementedError(); } - Future> splitNames( - {required String matchingKey, required String? bucketingKey}) { + Future> splitNames() { throw UnimplementedError(); } - Future> splits( - {required String matchingKey, required String? bucketingKey}) { + Future> splits() { throw UnimplementedError(); } diff --git a/splitio_platform_interface/test/impressions_method_call_handler_test.dart b/splitio_platform_interface/test/impressions_method_call_handler_test.dart index 6ae0cbb..8222156 100644 --- a/splitio_platform_interface/test/impressions_method_call_handler_test.dart +++ b/splitio_platform_interface/test/impressions_method_call_handler_test.dart @@ -1,8 +1,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/split_impression.dart'; - -import '../lib/impressions/impressions_method_call_handler.dart'; +import 'package:splitio_platform_interface/impressions/impressions_method_call_handler.dart'; void main() { test('correct impressionLog method call emits value on stream', () async { diff --git a/splitio_platform_interface/test/method_channel_platform_test.dart b/splitio_platform_interface/test/method_channel_platform_test.dart new file mode 100644 index 0000000..ee51457 --- /dev/null +++ b/splitio_platform_interface/test/method_channel_platform_test.dart @@ -0,0 +1,517 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_platform_interface/method_channel_platform.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; + +void main() { + const MethodChannel _channel = MethodChannel('splitio'); + + String methodName = ''; + dynamic methodArguments; + + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelPlatform _platform = MethodChannelPlatform.withoutHandler(); + + void _simulateMethodInvocation(String methodName, + {String key = 'key', + String? bucketingKey, + Map? arguments}) { + if (arguments == null) { + arguments = {}; + } + arguments.addAll({'matchingKey': key, 'bucketingKey': bucketingKey}); + _channel.invokeMethod(methodName, arguments); + } + + setUp(() { + _channel.setMockMethodCallHandler((MethodCall methodCall) async { + methodName = methodCall.method; + methodArguments = methodCall.arguments; + + _platform.handle(methodCall); + switch (methodCall.method) { + case 'getTreatment': + return ''; + case 'getTreatments': + return {'split1': 'on', 'split2': 'off'}; + case 'getTreatmentsWithConfig': + return { + 'split1': {'treatment': 'on', 'config': null}, + 'split2': {'treatment': 'off', 'config': null} + }; + case 'track': + return true; + case 'getAttribute': + return true; + case 'getAllAttributes': + return { + 'attr_1': true, + 'attr_2': ['list-element'], + 'attr_3': 28.20 + }; + case 'setAttribute': + case 'setAttributes': + case 'removeAttribute': + case 'clearAttributes': + return true; + } + }); + }); + + group('evaluation', () { + test('getTreatment without attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split'); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatment with attributes', () async { + _platform.getTreatment( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatment'); + expect(methodArguments, { + 'splitName': 'split', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatments without attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatments with attributes', () async { + _platform.getTreatments( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatments'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig with attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1', + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + + test('getTreatmentWithConfig without attributes', () async { + _platform.getTreatmentWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitName: 'split1'); + + expect(methodName, 'getTreatmentWithConfig'); + expect(methodArguments, { + 'splitName': 'split1', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig without attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2']); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {} + }); + }); + + test('getTreatmentsWithConfig with attributes', () async { + _platform.getTreatmentsWithConfig( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + splitNames: ['split1', 'split2'], + attributes: {'attr1': true}); + + expect(methodName, 'getTreatmentsWithConfig'); + expect(methodArguments, { + 'splitName': ['split1', 'split2'], + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': {'attr1': true} + }); + }); + }); + + group('track', () { + test('track with traffic type & value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + 'value': 25.10 + }); + }); + + test('track with value', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + value: 25.10); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'value': 25.10 + }); + }); + + test('track with traffic type', () async { + _platform.track( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + eventType: 'my_event', + trafficType: 'my_traffic_type'); + expect(methodName, 'track'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'eventType': 'my_event', + 'trafficType': 'my_traffic_type', + }); + }); + }); + + group('attributes', () { + test('get single attribute', () async { + _platform.getAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attribute-name'); + expect(methodName, 'getAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attribute-name', + }); + }); + + test('get all attributes', () async { + _platform.getAllAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'getAllAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('set attribute', () async { + _platform.setAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'my_attr', + value: 'attr_value'); + expect(methodName, 'setAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'my_attr', + 'value': 'attr_value', + }); + }); + + test('set multiple attributes', () async { + _platform.setAttributes( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributes: { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + }); + expect(methodName, 'setAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributes': { + 'bool_attr': true, + 'number_attr': 25.56, + 'string_attr': 'attr-value', + 'list_attr': ['one', 'two'], + } + }); + }); + + test('remove attribute', () async { + _platform.removeAttribute( + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + attributeName: 'attr-name'); + expect(methodName, 'removeAttribute'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'attributeName': 'attr-name', + }); + }); + + test('clear attributes', () async { + _platform.clearAttributes( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'clearAttributes'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('flush', () async { + _platform.flush( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'flush'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + + test('destroy', () async { + _platform.destroy( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + expect(methodName, 'destroy'); + expect(methodArguments, { + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + }); + }); + }); + + group('initialization', () { + test('init with matching key only', () { + _platform.init( + apiKey: 'api-key', matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'sdkConfiguration': {} + }); + }); + + test('init with bucketing key', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key'); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {} + }); + }); + + test('init with config', () { + _platform.init( + apiKey: 'api-key', + matchingKey: 'matching-key', + bucketingKey: 'bucketing-key', + sdkConfiguration: + SplitConfiguration(enableDebug: true, streamingEnabled: false)); + expect(methodName, 'init'); + expect(methodArguments, { + 'apiKey': 'api-key', + 'matchingKey': 'matching-key', + 'bucketingKey': 'bucketing-key', + 'sdkConfiguration': {'enableDebug': true, 'streamingEnabled': false}, + }); + }); + }); + + group('client', () { + test('get client with no keys', () { + _platform.getClient(matchingKey: 'matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'matching-key'}); + }); + + test('get client with new matching key', () { + _platform.getClient(matchingKey: 'new-matching-key', bucketingKey: null); + + expect(methodName, 'getClient'); + expect(methodArguments, {'matchingKey': 'new-matching-key'}); + }); + + test('get client with new matching key and bucketing key', () { + _platform.getClient( + matchingKey: 'new-matching-key', bucketingKey: 'bucketing-key'); + + expect(methodName, 'getClient'); + expect(methodArguments, + {'matchingKey': 'new-matching-key', 'bucketingKey': 'bucketing-key'}); + }); + }); + + group('manager', () { + test('get split names', () { + _platform.splitNames(); + + expect(methodName, 'splitNames'); + }); + + test('get splits', () { + _platform.splits(); + + expect(methodName, 'splits'); + }); + + test('get split', () { + _platform.split(splitName: 'my_split'); + + expect(methodName, 'split'); + expect(methodArguments, {'splitName': 'my_split'}); + }); + }); + + group('events', () { + test('onReady', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReady = _platform + .onReady(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReady', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReady, completion(equals(true))); + }); + + test('onReadyFromCache', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onReadyFromCache = _platform + .onReadyFromCache( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientReadyFromCache', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onReadyFromCache, completion(equals(true))); + }); + + test('onTimeout', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onTimeout = _platform + .onTimeout(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.then((value) => true); + + _simulateMethodInvocation('clientTimeout', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onTimeout, completion(equals(true))); + }); + + test('onUpdated', () { + _platform.getClient( + matchingKey: 'matching-key', bucketingKey: 'bucketing-key'); + + Future? onUpdated = _platform + .onUpdated(matchingKey: 'matching-key', bucketingKey: 'bucketing-key') + ?.first + .then((value) => true); + + _simulateMethodInvocation('clientUpdated', + key: 'matching-key', bucketingKey: 'bucketing-key'); + + expect(onUpdated, completion(equals(true))); + }); + }); + + test('impressions', () { + _platform.impressionsStream().listen( + expectAsync1((impression) { + expect(impression.key, 'key'); + expect(impression.bucketingKey, null); + expect(impression.split, 'split'); + expect(impression.treatment, 'treatment'); + expect(impression.time, 3000); + expect(impression.appliedRule, 'appliedRule'); + expect(impression.changeNumber, 200); + expect(impression.attributes, {}); + }), + ); + _simulateMethodInvocation('impressionLog', key: 'matching-key', arguments: { + 'key': 'key', + 'bucketingKey': 'bucketingKey', + 'split': 'split', + 'treatment': 'treatment', + 'time': 3000, + 'appliedRule': 'appliedRule', + 'changeNumber': 200, + 'attributes': {} + }); + }); +} diff --git a/splitio_platform_interface/test/split_event_listener_test.dart b/splitio_platform_interface/test/split_event_listener_test.dart deleted file mode 100644 index f06c463..0000000 --- a/splitio_platform_interface/test/split_event_listener_test.dart +++ /dev/null @@ -1,239 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/channel/method_channel_manager.dart'; -import 'package:splitio/events/split_events_listener.dart'; -import 'package:splitio/events/split_method_call_handler.dart'; -import 'package:splitio/split_result.dart'; - -import '../lib/split_client.dart'; - -void main() { - const MethodChannel _channel = MethodChannel('splitio'); - - TestWidgetsFlutterBinding.ensureInitialized(); - - SplitClientMock splitClientMock = SplitClientMock(); - SplitEventMethodCallHandler splitEventMethodCallHandler = - SplitEventMethodCallHandler('key', 'bucketing', splitClientMock); - late final MethodChannelManager _methodChannelWrapper = - MethodChannelManager(_channel); - - void _simulateMethodInvocation(String methodName, - {String key = 'key', String bucketingKey = 'bucketing'}) { - _channel.invokeMethod( - methodName, {'matchingKey': key, 'bucketingKey': bucketingKey}); - } - - setUp(() { - _channel.setMockMethodCallHandler((MethodCall methodCall) { - splitEventMethodCallHandler.handle( - methodCall.method, methodCall.arguments); - }); - }); - - group('client events', () { - test('test client ready', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); - _simulateMethodInvocation('clientReady'); - - expect(future, completion(equals(true))); - }); - - test('test multiple client ready', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); - _simulateMethodInvocation('clientReady'); - - expect(future, completion(equals(true))); - - _simulateMethodInvocation('clientReady'); - expect(future, completion(equals(true))); - }); - - test('test client ready from cache', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = - eventListener.onReadyFromCache().then((value) => true); - _simulateMethodInvocation('clientReadyFromCache'); - - expect(future, completion(equals(true))); - }); - - test('test multiple client ready from cache', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); - _simulateMethodInvocation('clientReadyFromCache'); - - expect(future, completion(equals(true))); - - _simulateMethodInvocation('clientReadyFromCache'); - expect(future, completion(equals(true))); - }); - - test('test client timeout', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onTimeout().then((value) => true); - _simulateMethodInvocation('clientTimeout'); - - expect(future, completion(equals(true))); - }); - - test('test multiple client timeout', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - Future future = eventListener.onReady().then((value) => true); - _simulateMethodInvocation('clientTimeout'); - - expect(future, completion(equals(true))); - - _simulateMethodInvocation('clientTimeout'); - expect(future, completion(equals(true))); - }); - - test('test client updated', () async { - SplitEventsListener eventListener = DefaultEventsListener( - _methodChannelWrapper, splitEventMethodCallHandler); - var count = 0; - eventListener - .onUpdated() - .map((event) => ++count) - .take(3) - .toList() - .then((value) => expect(value, [1, 2])); - - _simulateMethodInvocation('clientUpdated'); - _simulateMethodInvocation('clientUpdated'); - }); - }); -} - -class SplitClientMock extends SplitClient { - Map calledMethods = {}; - - @override - Future clearAttributes() { - calledMethods.update('clearAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future destroy() { - calledMethods.update('destroy', (value) => value + 1, ifAbsent: () => 1); - return Future.value(null); - } - - @override - Future flush() { - calledMethods.update('flush', (value) => value + 1, ifAbsent: () => 1); - return Future.value(null); - } - - @override - Future getAttribute(String attributeName) { - calledMethods.update('getAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value('value'); - } - - @override - Future> getAttributes() { - calledMethods.update('getAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'attr1': 'value1'}); - } - - @override - Future getTreatment(String splitName, - [Map attributes = const {}]) { - calledMethods.update('getTreatment', (value) => value + 1, - ifAbsent: () => 1); - return Future.value('treatment'); - } - - @override - Future getTreatmentWithConfig(String splitName, - [Map attributes = const {}]) { - calledMethods.update('getTreatmentWithConfig', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(const SplitResult('treatment', null)); - } - - @override - Future> getTreatments(List splitNames, - [Map attributes = const {}]) { - calledMethods.update('getTreatments', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'split1': 'treatment'}); - } - - @override - Future> getTreatmentsWithConfig( - List splitNames, - [Map attributes = const {}]) { - calledMethods.update('getTreatmentsWithConfig', (value) => value + 1, - ifAbsent: () => 1); - return Future.value({'split1': const SplitResult('treatment', null)}); - } - - @override - Future whenReady() { - calledMethods.update('onReady', (value) => value + 1, ifAbsent: () => 1); - return Future.value(this); - } - - @override - Future whenReadyFromCache() { - calledMethods.update('onReadyFromCache', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(this); - } - - @override - Future whenTimeout() { - calledMethods.update('onTimeout', (value) => value + 1, ifAbsent: () => 1); - return Future.value(this); - } - - @override - Stream whenUpdated() { - calledMethods.update('onUpdated', (value) => value + 1, ifAbsent: () => 1); - return Stream.value(this); - } - - @override - Future removeAttribute(String attributeName) { - calledMethods.update('removeAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future setAttribute(String attributeName, value) { - calledMethods.update('setAttribute', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future setAttributes(Map attributes) { - calledMethods.update('setAttributes', (value) => value + 1, - ifAbsent: () => 1); - return Future.value(true); - } - - @override - Future track(String eventType, - {String? trafficType, - double? value, - Map properties = const {}}) { - calledMethods.update('track', (value) => value + 1, ifAbsent: () => 1); - return Future.value(true); - } -} diff --git a/splitio_platform_interface/test/split_event_method_call_handler_test.dart b/splitio_platform_interface/test/split_event_method_call_handler_test.dart new file mode 100644 index 0000000..2388fe3 --- /dev/null +++ b/splitio_platform_interface/test/split_event_method_call_handler_test.dart @@ -0,0 +1,97 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:splitio_platform_interface/events/split_method_call_handler.dart'; + +void main() { + const MethodChannel _channel = MethodChannel('splitio'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + SplitEventMethodCallHandler splitEventMethodCallHandler = + SplitEventMethodCallHandler('key', 'bucketing'); + + void _simulateMethodInvocation(String methodName, + {String key = 'key', String bucketingKey = 'bucketing'}) { + _channel.invokeMethod( + methodName, {'matchingKey': key, 'bucketingKey': bucketingKey}); + } + + setUp(() { + _channel.setMockMethodCallHandler((MethodCall methodCall) { + splitEventMethodCallHandler.handle( + methodCall.method, methodCall.arguments); + }); + }); + + group('client events', () { + test('test client ready', () async { + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); + _simulateMethodInvocation('clientReady'); + + expect(future, completion(equals(true))); + }); + + test('test multiple client ready', () async { + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); + _simulateMethodInvocation('clientReady'); + + expect(future, completion(equals(true))); + + _simulateMethodInvocation('clientReady'); + expect(future, completion(equals(true))); + }); + + test('test client ready from cache', () async { + Future future = + splitEventMethodCallHandler.onReadyFromCache().then((value) => true); + _simulateMethodInvocation('clientReadyFromCache'); + + expect(future, completion(equals(true))); + }); + + test('test multiple client ready from cache', () async { + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); + _simulateMethodInvocation('clientReadyFromCache'); + + expect(future, completion(equals(true))); + + _simulateMethodInvocation('clientReadyFromCache'); + expect(future, completion(equals(true))); + }); + + test('test client timeout', () async { + Future future = + splitEventMethodCallHandler.onTimeout().then((value) => true); + _simulateMethodInvocation('clientTimeout'); + + expect(future, completion(equals(true))); + }); + + test('test multiple client timeout', () async { + Future future = + splitEventMethodCallHandler.onReady().then((value) => true); + _simulateMethodInvocation('clientTimeout'); + + expect(future, completion(equals(true))); + + _simulateMethodInvocation('clientTimeout'); + expect(future, completion(equals(true))); + }); + + test('test client updated', () async { + var count = 0; + splitEventMethodCallHandler + .onUpdated() + .map((event) => ++count) + .take(3) + .toList() + .then((value) => expect(value, [1, 2])); + + _simulateMethodInvocation('clientUpdated'); + _simulateMethodInvocation('clientUpdated'); + }); + }); +} diff --git a/splitio_platform_interface/test/split_impression_test.dart b/splitio_platform_interface/test/split_impression_test.dart index e792441..b993d2a 100644 --- a/splitio_platform_interface/test/split_impression_test.dart +++ b/splitio_platform_interface/test/split_impression_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/impressions/split_impression.dart'; +import 'package:splitio_platform_interface/split_impression.dart'; void main() { test('from map creates correct Impression', () { diff --git a/splitio_platform_interface/test/split_view_test.dart b/splitio_platform_interface/test/split_view_test.dart index af0578a..0268b42 100644 --- a/splitio_platform_interface/test/split_view_test.dart +++ b/splitio_platform_interface/test/split_view_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/split_view.dart'; +import 'package:splitio_platform_interface/split_view.dart'; void main() { test('split view from empty map results in null object', () { diff --git a/splitio_platform_interface/test/splitio_configuration_test.dart b/splitio_platform_interface/test/splitio_configuration_test.dart index c04ed72..d1c44f5 100644 --- a/splitio_platform_interface/test/splitio_configuration_test.dart +++ b/splitio_platform_interface/test/splitio_configuration_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:splitio/split_configuration.dart'; +import 'package:splitio_platform_interface/split_configuration.dart'; +import 'package:splitio_platform_interface/split_sync_config.dart'; void main() { test('valuesAreMappedCorrectly', () async { @@ -21,7 +22,9 @@ void main() { eventsEndpoint: 'eventsEndpoint.split.io', authServiceEndpoint: 'authServiceEndpoint.split.io', streamingServiceEndpoint: 'streamingServiceEndpoint.split.io', - telemetryServiceEndpoint: 'telemetryServiceEndpoint.split.io'); + telemetryServiceEndpoint: 'telemetryServiceEndpoint.split.io', + syncConfig: + SyncConfig(names: ['one', 'two', 'three'], prefixes: ['pre1'])); expect(config.configurationMap['eventFlushInterval'], 2000); expect(config.configurationMap['eventsPerPush'], 300); @@ -44,6 +47,10 @@ void main() { expect(config.configurationMap['telemetryServiceEndpoint'], 'telemetryServiceEndpoint.split.io'); expect(config.configurationMap['impressionListener'], true); + expect(config.configurationMap['syncConfig']['syncConfigNames'], + ['one', 'two', 'three']); + expect( + config.configurationMap['syncConfig']['syncConfigPrefixes'], ['pre1']); }); test('noSpecialValuesLeavesMapEmpty', () async {