diff --git a/.gitignore b/.gitignore index 2b52f74..365dfae 100644 --- a/.gitignore +++ b/.gitignore @@ -599,4 +599,7 @@ MigrationBackup/ # Remove env files *.env +# Remove test coverage +.coverage/ + # End of https://www.gitignore.io/api/git,dart,flutter,intellij,webstorm,visualstudio diff --git a/.releaserc.json b/.releaserc.json index 94f5d9c..1271d48 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -2,35 +2,57 @@ "tagFormat": "${version}", "branches": [ "main", - { "name": "beta", "prerelease": true }, - { "name": "alpha", "prerelease": true } + { + "name": "beta", + "prerelease": true + }, + { + "name": "alpha", + "prerelease": true + } ], "plugins": [ - ["@semantic-release/commit-analyzer", { - "preset": "conventionalcommits" - }], - ["@semantic-release/release-notes-generator", { - "preset": "conventionalcommits" - }], - ["@semantic-release/changelog", { - "changelogFile": "CHANGELOG.md" - }], - ["@semantic-release/exec",{ - "verifyReleaseCmd": "./scripts/update-version.sh ${nextRelease.version}" - } + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits" + } ], - ["@semantic-release/git", { - "assets": [ - "CHANGELOG.md", - "pubspec.yaml" - ], - "message": "chore: prepare for ${nextRelease.version}\n\n${nextRelease.notes}" - } + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/changelog", + { + "changelogFile": "CHANGELOG.md" + } + ], + [ + "@semantic-release/exec", + { + "verifyReleaseCmd": "./scripts/update-version.sh ${nextRelease.version}, ./scripts/update-podspec.sh ${nextRelease.version}, ./scripts/update-plugin.sh ${nextRelease.version}" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "pubspec.yaml" + ], + "message": "chore: prepare for ${nextRelease.version}\n\n${nextRelease.notes}" + } ], - ["@semantic-release/github", { - "labels": false, - "successComment": false, - "failTitle": false - }] + [ + "@semantic-release/github", + { + "labels": false, + "successComment": false, + "failTitle": false + } + ] ] } \ No newline at end of file diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 3078504..526fffd 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -5,22 +5,22 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:customer_io_example/main.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; +// import 'package:customer_io_example/main.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_test/flutter_test.dart'; void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => - widget is Text && widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); + // testWidgets('Verify Platform version', (WidgetTester tester) async { + // // Build our app and trigger a frame. + // await tester.pumpWidget(const MyApp()); + // + // // Verify that platform version is retrieved. + // expect( + // find.byWidgetPredicate( + // (Widget widget) => + // widget is Text && widget.data!.startsWith('Running on:'), + // ), + // findsOneWidget, + // ); + // }); } diff --git a/ios/customer_io.podspec b/ios/customer_io.podspec index 1b0722c..293245b 100755 --- a/ios/customer_io.podspec +++ b/ios/customer_io.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'customer_io' - s.version = '1.0.0-alpha.8' + s.version = '1.0.0-beta.1' s.summary = 'Customer.io plugin for Flutter' s.homepage = 'https://customer.io/' s.license = { :file => '../LICENSE' } diff --git a/lib/customer_io_platform_interface.dart b/lib/customer_io_platform_interface.dart index 27f9dc1..2ab9be0 100644 --- a/lib/customer_io_platform_interface.dart +++ b/lib/customer_io_platform_interface.dart @@ -31,7 +31,7 @@ abstract class CustomerIOPlatform extends PlatformInterface { Future initialize({ required CustomerIOConfig config, }) { - throw UnimplementedError('config() has not been implemented.'); + throw UnimplementedError('initialize() has not been implemented.'); } void identify( diff --git a/lib/customer_io_plugin_version.dart b/lib/customer_io_plugin_version.dart index d0dcd2b..0ac9719 100755 --- a/lib/customer_io_plugin_version.dart +++ b/lib/customer_io_plugin_version.dart @@ -1,2 +1,2 @@ // Don't modify this line - it's automatically updated -const version = "1.0.0-alpha.8"; +const version = "1.0.0-beta.1"; diff --git a/pubspec.yaml b/pubspec.yaml index 3e5f198..dea7193 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,8 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 + build_runner: ^2.2.0 + mockito: ^5.0.15 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/scripts/update-plugin b/scripts/update-plugin new file mode 100644 index 0000000..cd2c44c --- /dev/null +++ b/scripts/update-plugin @@ -0,0 +1,21 @@ +#!/bin/bash + +# Script that updates the pubspec.yaml file in the SDK to newest semantic version. +# +# Designed to be run from CI server or manually. +# +# Use script: ./scripts/update-plugin.sh "0.1.1" + +set -e + +NEW_VERSION="$1" + +echo "Updating files to new version: $NEW_VERSION" + +echo "Updating customer_io_plugin_version.dart" +# Given line: `const version = "1.0.0-alpha.4";` +# Regex string will match the line of the file that we can then substitute. +DART_LINE_PATTERN="const version = \"\(.*\)\"" +sed -i "s/$DART_LINE_PATTERN/const version = \"$NEW_VERSION\"/" "./lib/customer_io_plugin_version.dart" + +echo "Check file, you should see version inside has been updated!" \ No newline at end of file diff --git a/scripts/update-podspec.sh b/scripts/update-podspec.sh new file mode 100644 index 0000000..024e130 --- /dev/null +++ b/scripts/update-podspec.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Script that updates the pubspec.yaml file in the SDK to newest semantic version. +# +# Designed to be run from CI server or manually. +# +# Use script: ./scripts/update-podspec.sh "0.1.1" + +set -e + +NEW_VERSION="$1" + +echo "Updating files to new version: $NEW_VERSION" + +echo "Updating customer_io.podspec" +LINE_PATTERN="s.version\s*=.*" +sed -i "s/$LINE_PATTERN/s.version = \'$NEW_VERSION\'/" "./ios/customer_io.podspec" + +echo "Check file, you should see version inside has been updated!" \ No newline at end of file diff --git a/scripts/update-version.sh b/scripts/update-version.sh index 0e1ab80..a963ff7 100755 --- a/scripts/update-version.sh +++ b/scripts/update-version.sh @@ -15,14 +15,4 @@ echo "Updating files to new version: $NEW_VERSION" echo "Updating pubspec.yaml" sed -i 's/^\(version: \).*$/\1'"$NEW_VERSION"'/' pubspec.yaml -echo "Updating customer_io.podspec" -LINE_PATTERN="s.version\s*=.*" -sed -i "s/$LINE_PATTERN/s.version = \'$NEW_VERSION\'/" "./ios/customer_io.podspec" - -echo "Updating customer_io_plugin_version.dart" -# Given line: `const version = "1.0.0-alpha.4";` -# Regex string will match the line of the file that we can then substitute. -DART_LINE_PATTERN="const version = \"\(.*\)\"" -sed -i "s/$DART_LINE_PATTERN/const version = \"$NEW_VERSION\"/" "./lib/customer_io_plugin_version.dart" - -echo "Check files, you should see version inside has been updated!" +echo "Check file, you should see version inside has been updated!" diff --git a/test/customer_io_method_channel_test.dart b/test/customer_io_method_channel_test.dart index 9437524..082f4c5 100644 --- a/test/customer_io_method_channel_test.dart +++ b/test/customer_io_method_channel_test.dart @@ -1,18 +1,132 @@ +import 'package:customer_io/customer_io_config.dart'; +import 'package:customer_io/customer_io_enums.dart'; +import 'package:customer_io/customer_io_method_channel.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +/// This is more of test of what our Native platform is expecting. void main() { const MethodChannel channel = MethodChannel('customer_io'); + final Map methodInvocations = {}; TestWidgetsFlutterBinding.ensureInitialized(); setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; + methodInvocations[methodCall.method] = methodCall.arguments; + switch (methodCall.method) { + case 'initialize': + return Future + .value(); // Simulate a successful response from the platform. + case 'identify': + case 'track': + case 'trackMetric': + case 'screen': + case 'registerDeviceToken': + case 'clearIdentify': + case 'setProfileAttributes': + case 'setDeviceAttributes': + return; + default: + throw MissingPluginException(); + } }); }); tearDown(() { channel.setMockMethodCallHandler(null); }); + + void expectMethodInvocationArguments(String methodKey, Map arguments) { + expect(methodInvocations.containsKey(methodKey), true, reason: 'method `$methodKey` was called'); + arguments.forEach((key, value) { + expect(methodInvocations[methodKey][key], value, reason: 'method arg $key matches'); + }); + } + + test('initialize() should call platform method with correct arguments', () async { + final customerIO = CustomerIOMethodChannel(); + final config = CustomerIOConfig(siteId: 'site_id', apiKey: 'api_key'); + await customerIO.initialize(config: config); + + expectMethodInvocationArguments('initialize', { 'siteId': config.siteId, 'apiKey': config.apiKey }); + }); + + test('identify() should call platform method with correct arguments', () async { + final Map args = { 'identifier': 'Customer 1', 'attributes': { 'email': 'customer@email.com' } }; + + final customerIO = CustomerIOMethodChannel(); + customerIO.identify(identifier: args['identifier'] as String, attributes: args['attributes']); + + expectMethodInvocationArguments('identify', args); + }); + + test('track() should call platform method with correct arguments', () async { + final Map args = { 'eventName': 'test_event', 'attributes': { 'eventData': 2 }}; + + final customerIO = CustomerIOMethodChannel(); + customerIO.track(name: args['eventName'], attributes: args['attributes']); + + expectMethodInvocationArguments('track', args); + }); + + test('trackMetric() should call platform method with correct arguments', () async { + final Map args = { + 'deliveryId': '123', + 'deliveryToken': 'asdf', + 'metricEvent': 'clicked'}; + + final customerIO = CustomerIOMethodChannel(); + customerIO.trackMetric( + deliveryID: args['deliveryId'], + deviceToken: args['deliveryToken'], + event: MetricEvent.values.byName(args['metricEvent'])); + + expectMethodInvocationArguments('trackMetric', args); + }); + + test('screen() should call platform method with correct arguments', () async { + final Map args = { 'eventName': 'screen_event', 'attributes': { 'screenName': '你好' }}; + + final customerIO = CustomerIOMethodChannel(); + customerIO.screen(name: args['eventName'], attributes: args['attributes']); + + expectMethodInvocationArguments('screen', args); + }); + + test('registerDeviceToken() should call platform method with correct arguments', () async { + final Map args = { 'token': 'asdf' }; + + final customerIO = CustomerIOMethodChannel(); + customerIO.registerDeviceToken(deviceToken: args['token'] as String); + + expectMethodInvocationArguments('registerDeviceToken', args); + }); + + test('clearIdentify() should call platform method with correct arguments', () async { + final Map args = {}; + + final customerIO = CustomerIOMethodChannel(); + customerIO.clearIdentify(); + + expectMethodInvocationArguments('clearIdentify', args); + }); + + test('setProfileAttributes() should call platform method with correct arguments', () async { + final Map args = { 'attributes': { 'age': 1 }}; + + final customerIO = CustomerIOMethodChannel(); + customerIO.setProfileAttributes(attributes: args['attributes']); + + expectMethodInvocationArguments('setProfileAttributes', args); + }); + + test('setDeviceAttributes() should call platform method with correct arguments', () async { + final Map args = { 'attributes': { 'os': 'Android' }}; + + final customerIO = CustomerIOMethodChannel(); + customerIO.setDeviceAttributes(attributes: args['attributes']); + + expectMethodInvocationArguments('setDeviceAttributes', args); + }); } diff --git a/test/customer_io_test.dart b/test/customer_io_test.dart index b0b10f3..aba1676 100644 --- a/test/customer_io_test.dart +++ b/test/customer_io_test.dart @@ -1,87 +1,217 @@ -import 'dart:async'; - +import 'package:customer_io/customer_io.dart'; import 'package:customer_io/customer_io_config.dart'; import 'package:customer_io/customer_io_enums.dart'; -import 'package:customer_io/customer_io_inapp.dart'; -import 'package:customer_io/customer_io_method_channel.dart'; import 'package:customer_io/customer_io_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -///The MockCustomerIoPlatform class is a mock implementation of the CustomerIOPlatform interface. -/// It provides stubbed implementations of all the methods defined in the CustomerIOPlatform interface, +import 'customer_io_test.mocks.dart'; + +///The TestCustomerIoPlatform class is a mock implementation of the CustomerIOPlatform interface. +/// It provides mock implementations of the methods defined in the CustomerIOPlatform interface, /// which are intended to be overridden in tests. The purpose of this class is to simulate the behavior /// of the actual platform implementation, so that tests can run without making actual calls to the platform, -class MockCustomerIoPlatform +class TestCustomerIoPlatform extends Mock with MockPlatformInterfaceMixin implements CustomerIOPlatform { @override Future initialize({required CustomerIOConfig config}) { - // TODO: implement config - throw UnimplementedError(); + return Future.value(); } +} - @override - void clearIdentify() { - // TODO: implement clearIdentify - } +// The following test suite makes sure when any CustomerIO class method is called, +// the correct corresponding platform methods are called and with the correct arguments. +@GenerateMocks([TestCustomerIoPlatform]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); - @override - void identify( - {required String identifier, - Map attributes = const {}}) { - // TODO: implement identify - } + group('CustomerIO', () { + late MockTestCustomerIoPlatform mockPlatform; - @override - void screen( - {required String name, Map attributes = const {}}) { - // TODO: implement screen - } + setUp(() { + mockPlatform = MockTestCustomerIoPlatform(); + CustomerIOPlatform.instance = mockPlatform; + }); - @override - void setDeviceAttributes({required Map attributes}) { - // TODO: implement setDeviceAttributes - } + // initialize + test('initialize() calls platform', () async { + final config = CustomerIOConfig(siteId: '123', apiKey: '456'); + await CustomerIO.initialize(config: config); - @override - void setProfileAttributes({required Map attributes}) { - // TODO: implement setProfileAttributes - } + verify(mockPlatform.initialize(config: config)).called(1); + }); - @override - void track( - {required String name, Map attributes = const {}}) { - // TODO: implement track - } + test('initialize() correct arguments are passed', () async { + final givenConfig = CustomerIOConfig( + siteId: '123', + apiKey: '456', + region: Region.eu, + autoTrackPushEvents: false); + await CustomerIO.initialize(config: givenConfig); + expect( + verify(mockPlatform.initialize(config: captureAnyNamed("config"))) + .captured + .single, + givenConfig); + }); - @override - StreamSubscription subscribeToInAppEventListener( - void Function(InAppEvent p1) onEvent) { - // TODO: implement subscribeToInAppEventListener - throw UnimplementedError(); - } + // identify + test('identify() calls platform', () { + const givenIdentifier = 'user@example.com'; + final givenAttributes = {'name': 'John Doe'}; + CustomerIO.identify( + identifier: givenIdentifier, attributes: givenAttributes); - @override - void registerDeviceToken({required String deviceToken}) { - // TODO: implement registerDeviceToken - } + verify(mockPlatform.identify( + identifier: givenIdentifier, attributes: givenAttributes)) + .called(1); + }); - @override - void trackMetric( - {required String deliveryID, - required String deviceToken, - required MetricEvent event}) { - // TODO: implement trackMetric - } -} + test('identify() correct arguments are passed', () { + const givenIdentifier = 'user@example.com'; + final givenAttributes = {'name': 'John Doe'}; + CustomerIO.identify( + identifier: givenIdentifier, attributes: givenAttributes); + expect( + verify(mockPlatform.identify( + identifier: captureAnyNamed("identifier"), + attributes: captureAnyNamed("attributes"))) + .captured, + [givenIdentifier, givenAttributes]); + }); -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); + // clearIdentify + test('clearIdentify() calls platform', () { + CustomerIO.clearIdentify(); + verify(mockPlatform.clearIdentify()).called(1); + }); + + // track + test('track() calls platform', () { + const name = 'itemAddedToCart'; + final attributes = {'item': 'shoes'}; + CustomerIO.track(name: name, attributes: attributes); + verify(mockPlatform.track(name: name, attributes: attributes)).called(1); + }); + + test('track() correct arguments are passed', () { + const name = 'itemAddedToCart'; + final givenAttributes = {'name': 'John Doe'}; + CustomerIO.track(name: name, attributes: givenAttributes); + expect( + verify(mockPlatform.track( + name: captureAnyNamed("name"), + attributes: captureAnyNamed("attributes"))) + .captured, + [name, givenAttributes]); + }); + + // trackMetric + test('trackMetric() calls platform', () { + const deliveryID = '123'; + const deviceToken = 'abc'; + const event = MetricEvent.opened; + CustomerIO.trackMetric( + deliveryID: deliveryID, deviceToken: deviceToken, event: event); + verify(mockPlatform.trackMetric( + deliveryID: deliveryID, deviceToken: deviceToken, event: event)) + .called(1); + }); + + test('trackMetric() correct arguments are passed', () { + const deliveryID = '123'; + const deviceToken = 'abc'; + const event = MetricEvent.opened; + CustomerIO.trackMetric( + deliveryID: deliveryID, deviceToken: deviceToken, event: event); + expect( + verify(mockPlatform.trackMetric( + deliveryID: captureAnyNamed("deliveryID"), + deviceToken: captureAnyNamed("deviceToken"), + event: captureAnyNamed("event"))) + .captured, + [deliveryID, deviceToken, event]); + }); + + // registerDeviceToken + test('registerDeviceToken() calls platform', () { + const deviceToken = 'token'; + CustomerIO.registerDeviceToken(deviceToken: deviceToken); + verify(mockPlatform.registerDeviceToken(deviceToken: deviceToken)) + .called(1); + }); + + test('registerDeviceToken() correct arguments are passed', () { + const deviceToken = 'token'; + CustomerIO.registerDeviceToken(deviceToken: deviceToken); + expect( + verify(mockPlatform.registerDeviceToken( + deviceToken: captureAnyNamed("deviceToken"))) + .captured + .first, + deviceToken); + }); + + // screen + test('screen() calls platform', () { + const name = 'home'; + final givenAttributes = {'user': 'John Doe'}; + CustomerIO.screen(name: name, attributes: givenAttributes); + verify(mockPlatform.screen(name: name, attributes: givenAttributes)) + .called(1); + }); + + test('screen() correct arguments are passed', () { + const name = 'itemAddedToCart'; + final givenAttributes = {'name': 'John Doe'}; + CustomerIO.screen(name: name, attributes: givenAttributes); + expect( + verify(mockPlatform.screen( + name: captureAnyNamed("name"), + attributes: captureAnyNamed("attributes"))) + .captured, + [name, givenAttributes]); + }); + + // setDeviceAttributes + test('setDeviceAttributes() calls platform', () { + final givenAttributes = {'area': 'US'}; + CustomerIO.setDeviceAttributes(attributes: givenAttributes); + verify(mockPlatform.setDeviceAttributes(attributes: givenAttributes)) + .called(1); + }); + + test('setDeviceAttributes() correct arguments are passed', () { + final givenAttributes = {'area': 'US'}; + CustomerIO.setDeviceAttributes(attributes: givenAttributes); + expect( + verify(mockPlatform.setDeviceAttributes( + attributes: captureAnyNamed("attributes"))) + .captured + .first, + givenAttributes); + }); - final CustomerIOPlatform initialPlatform = CustomerIOPlatform.instance; + // setProfileAttributes + test('setProfileAttributes() calls platform', () { + final givenAttributes = {'age': 10}; + CustomerIO.setProfileAttributes(attributes: givenAttributes); + verify(mockPlatform.setProfileAttributes(attributes: givenAttributes)) + .called(1); + }); - test('$CustomerIOMethodChannel is the default instance', () { - expect(initialPlatform, isInstanceOf()); + test('setProfileAttributes() correct arguments are passed', () { + final givenAttributes = {'age': 10}; + CustomerIO.setProfileAttributes(attributes: givenAttributes); + expect( + verify(mockPlatform.setProfileAttributes( + attributes: captureAnyNamed("attributes"))) + .captured + .first, + givenAttributes); + }); }); } diff --git a/test/customer_io_test.mocks.dart b/test/customer_io_test.mocks.dart new file mode 100644 index 0000000..b745f01 --- /dev/null +++ b/test/customer_io_test.mocks.dart @@ -0,0 +1,177 @@ +// Mocks generated by Mockito 5.3.2 from annotations +// in customer_io/example/ios/.symlinks/plugins/customer_io/test/customer_io_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i2; + +import 'package:customer_io/customer_io_config.dart' as _i4; +import 'package:customer_io/customer_io_enums.dart' as _i5; +import 'package:customer_io/customer_io_inapp.dart' as _i6; +import 'package:mockito/mockito.dart' as _i1; + +import 'customer_io_test.dart' as _i3; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeStreamSubscription_0 extends _i1.SmartFake + implements _i2.StreamSubscription { + _FakeStreamSubscription_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [TestCustomerIoPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestCustomerIoPlatform extends _i1.Mock + implements _i3.TestCustomerIoPlatform { + MockTestCustomerIoPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.Future initialize({required _i4.CustomerIOConfig? config}) => + (super.noSuchMethod( + Invocation.method( + #initialize, + [], + {#config: config}, + ), + returnValue: _i2.Future.value(), + returnValueForMissingStub: _i2.Future.value(), + ) as _i2.Future); + @override + void identify({ + required String? identifier, + Map? attributes = const {}, + }) => + super.noSuchMethod( + Invocation.method( + #identify, + [], + { + #identifier: identifier, + #attributes: attributes, + }, + ), + returnValueForMissingStub: null, + ); + @override + void clearIdentify() => super.noSuchMethod( + Invocation.method( + #clearIdentify, + [], + ), + returnValueForMissingStub: null, + ); + @override + void track({ + required String? name, + Map? attributes = const {}, + }) => + super.noSuchMethod( + Invocation.method( + #track, + [], + { + #name: name, + #attributes: attributes, + }, + ), + returnValueForMissingStub: null, + ); + @override + void trackMetric({ + required String? deliveryID, + required String? deviceToken, + required _i5.MetricEvent? event, + }) => + super.noSuchMethod( + Invocation.method( + #trackMetric, + [], + { + #deliveryID: deliveryID, + #deviceToken: deviceToken, + #event: event, + }, + ), + returnValueForMissingStub: null, + ); + @override + void registerDeviceToken({required String? deviceToken}) => + super.noSuchMethod( + Invocation.method( + #registerDeviceToken, + [], + {#deviceToken: deviceToken}, + ), + returnValueForMissingStub: null, + ); + @override + void screen({ + required String? name, + Map? attributes = const {}, + }) => + super.noSuchMethod( + Invocation.method( + #screen, + [], + { + #name: name, + #attributes: attributes, + }, + ), + returnValueForMissingStub: null, + ); + @override + void setDeviceAttributes({required Map? attributes}) => + super.noSuchMethod( + Invocation.method( + #setDeviceAttributes, + [], + {#attributes: attributes}, + ), + returnValueForMissingStub: null, + ); + @override + void setProfileAttributes({required Map? attributes}) => + super.noSuchMethod( + Invocation.method( + #setProfileAttributes, + [], + {#attributes: attributes}, + ), + returnValueForMissingStub: null, + ); + @override + _i2.StreamSubscription subscribeToInAppEventListener( + void Function(_i6.InAppEvent)? onEvent) => + (super.noSuchMethod( + Invocation.method( + #subscribeToInAppEventListener, + [onEvent], + ), + returnValue: _FakeStreamSubscription_0( + this, + Invocation.method( + #subscribeToInAppEventListener, + [onEvent], + ), + ), + ) as _i2.StreamSubscription); +}