Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add first unit test to project #87

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/core/lib/analytics_pigeon.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import 'package:segment_analytics/analytics_platform_interface.dart';
import 'package:flutter/services.dart';

Expand All @@ -7,11 +8,13 @@ import 'native_context.dart';
class AnalyticsPlatformImpl extends AnalyticsPlatform {
static const EventChannel _eChannel =
EventChannel('analytics/deep_link_events');
final NativeContextApi _api = NativeContextApi();
NativeContextApi api;

AnalyticsPlatformImpl({NativeContextApi? api}) : api = api ?? NativeContextApi();

@override
Future<NativeContext> getContext({bool collectDeviceId = false}) {
return _api.getContext(collectDeviceId);
return api.getContext(collectDeviceId);
}

@override
Expand Down
10 changes: 5 additions & 5 deletions packages/core/lib/event.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/core/lib/flush_policies/count_flush_policy.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:segment_analytics/event.dart';
import 'package:segment_analytics/flush_policies/flush_policy.dart';

Expand All @@ -7,6 +8,9 @@ class CountFlushPolicy extends FlushPolicy {

CountFlushPolicy(this._flushAt, {int? count}) : _count = count ?? 0;

@visibleForTesting
int get count => _count;

@override
void start() {
_count = 0;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/lib/plugins/segment_destination.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class SegmentDestination extends DestinationPlugin with Flushable {
String? _apiHost;

SegmentDestination() : super(segmentDestinationKey) {
_queuePlugin = QueueFlushingPlugin(_sendEvents);
_queuePlugin = QueueFlushingPlugin(sendEvents);
}

Future _sendEvents(List<RawEvent> events) async {
Future sendEvents(List<RawEvent> events) async {
if (events.isEmpty) {
return;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/lib/state.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions packages/core/lib/utils/lifecycle/fgbg.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import 'package:segment_analytics/utils/lifecycle/lifecycle.dart';
import 'package:flutter_fgbg/flutter_fgbg.dart';

class FGBGLifecycle extends LifeCycle {
final _stream = FGBGEvents.stream;
final Stream<FGBGType> stream;

FGBGLifecycle(this.stream);

@override
StreamSubscription<AppStatus> listen(void Function(AppStatus event)? onData,
{Function? onError, void Function()? onDone, bool? cancelOnError}) {
return _stream
return stream
.map((event) => (event == FGBGType.foreground)
? AppStatus.foreground
: AppStatus.background)
Expand Down
8 changes: 5 additions & 3 deletions packages/core/lib/utils/lifecycle/lifecycle.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';

import 'package:flutter_fgbg/flutter_fgbg.dart';
import 'package:segment_analytics/utils/lifecycle/fgbg.dart';
import 'package:segment_analytics/utils/lifecycle/widget_observer.dart';
import 'package:flutter/foundation.dart';
Expand All @@ -9,16 +10,17 @@ enum AppStatus { foreground, background }

abstract class LifeCycle extends Stream<AppStatus> {}

LifeCycle _getLifecycleStream() {
@visibleForTesting
LifeCycle getLifecycleStream() {
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
// For iOS and Android we will use the FgBg Lifecycle listener as it reports directly from native level
// ignoring native popups
return FGBGLifecycle();
return FGBGLifecycle(FGBGEvents.stream);
} else {
// For Web and Desktop we use the WidgetObserver implementation directly from Flutter
// TBF Flutter doesn't have a very reliable background signal for those platforms
return WidgetObserverLifecycle();
}
}

final LifeCycle lifecycle = _getLifecycleStream();
final LifeCycle lifecycle = getLifecycleStream();
1 change: 1 addition & 0 deletions packages/core/lib/utils/store/io.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// coverage:ignore-file
import 'dart:async';
import 'dart:convert';
import 'dart:io';
Expand Down
3 changes: 3 additions & 0 deletions packages/core/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ dev_dependencies:
build_runner: ^2.3.3
flutter_test:
sdk: flutter
test: ^1.25.7
fake_async: ^1.0.0
state_notifier_test: ^0.0.10
flutter_lints: ^2.0.0
json_serializable: ^6.6.0
pigeon: ^7.2.1
Expand Down
33 changes: 33 additions & 0 deletions packages/core/test/analytics_pigeon_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// test/analytics_platform_impl_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:segment_analytics/analytics_pigeon.dart';

import 'package:segment_analytics/native_context.dart';
import 'mocks/mocks.mocks.dart';

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

group('AnalyticsPlatformImpl Tests', () {
late AnalyticsPlatformImpl analyticsPlatform;
late MockNativeContextApi mockNativeContextApi;

setUp(() {
mockNativeContextApi = MockNativeContextApi();
analyticsPlatform = AnalyticsPlatformImpl();
analyticsPlatform.api = mockNativeContextApi;
});

test('getContext returns NativeContext', () async {
final nativeContext = NativeContext();
when(mockNativeContextApi.getContext(any))
.thenAnswer((_) async => nativeContext);

final result = await analyticsPlatform.getContext(collectDeviceId: true);

expect(result, isA<NativeContext>());
verify(mockNativeContextApi.getContext(true)).called(1);
});
});
}
116 changes: 92 additions & 24 deletions packages/core/test/analytics_test.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import 'package:segment_analytics/analytics.dart';
import 'package:segment_analytics/analytics_platform_interface.dart';
import 'package:segment_analytics/client.dart';
import 'package:segment_analytics/event.dart';
import 'package:segment_analytics/flush_policies/count_flush_policy.dart';
import 'package:segment_analytics/flush_policies/flush_policy.dart';
import 'package:segment_analytics/logger.dart';
import 'package:segment_analytics/plugins/event_logger.dart';
import 'package:segment_analytics/state.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'mocks/mocks.dart';
import 'mocks/mocks.mocks.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -21,55 +26,118 @@ void main() {
];

group("analytics", () {

setUp(() {
late Analytics analytics;
late MockHTTPClient httpClient;
setUp(() async {
AnalyticsPlatform.instance = MockPlatform();

// Prevents spamming the test console. Eventually logging info will be behind a debug flag so this won't be needed
LogFactory.logger = Mocks.logTarget();

SharedPreferences.setMockInitialValues({});
});

test(
"it fetches settings but does not fire track event when not tracking lifecycle events",
() async {
final httpClient = Mocks.httpClient();
httpClient = Mocks.httpClient();
when(httpClient.settingsFor(writeKey))
.thenAnswer((_) => Future.value(SegmentAPISettings({})));
when(httpClient.startBatchUpload(writeKey, batch))
.thenAnswer((_) => Future.value(true));

Analytics analytics = Analytics(
analytics = Analytics(
Configuration("123",
trackApplicationLifecycleEvents: false,
appStateStream: () => Mocks.streamSubscription()),
token: "abcdef12345"),
Mocks.store(),
httpClient: (_) => httpClient);
await analytics.init();
});

test(
"it fetches settings but does not fire track event when not tracking lifecycle events",
() async {

verify(httpClient.settingsFor(writeKey));
verifyNever(httpClient.startBatchUpload(writeKey, batch));
});
test(
"it fetches settings and fires track event when tracking lifecycle events",
() async {
final httpClient = Mocks.httpClient();
when(httpClient.settingsFor(writeKey))
.thenAnswer((_) => Future.value(SegmentAPISettings({})));
when(httpClient.startBatchUpload(writeKey, batch))
.thenAnswer((_) => Future.value(true));

Analytics analytics = Analytics(
Configuration("123",
trackApplicationLifecycleEvents: true,
appStateStream: () => Mocks.streamSubscription()),
Mocks.store(),
httpClient: (_) => httpClient);
await analytics.init();

verify(httpClient.settingsFor(writeKey));
verifyNever(httpClient.startBatchUpload(writeKey, batch));
});

test('it analytics track should be callable', () {
analytics.track("test track");
});
test('it analytics screen should be callable', () {
analytics.screen("test screem");
});
test('it analytics identify should be callable', () {
analytics.identify();
});
test('it analytics group should be callable', () {
analytics.group("test group");
});
test('it analytics alias should be callable', () {
analytics.alias("test alias");
});
test('it analytics cleanup should be callable', () {
analytics.cleanup();
});
test('it analytics reset should be callable', () {
analytics.reset();
});
test('it analytics addFlushPolicy should be callable', () {
List<FlushPolicy> policies = [];
policies.add(CountFlushPolicy(5));
analytics.addFlushPolicy(policies);
});
test('it analytics getFlushPolicies should be callable', () {
analytics.getFlushPolicies();
});
test('it analytics removeFlushPolicy should be callable', () {
List<FlushPolicy> policies = [];
policies.add(CountFlushPolicy(5));
analytics.removeFlushPolicy(policies);
});
test('it analytics removePlugin should be callable', () {
analytics.addPlugin(EventLogger(), settings: {"event":"Track Event"});
});
test('it analytics removePlugin should be callable', () {
analytics.removePlugin(EventLogger());
});
test('it analytics onContextLoaded should be callable', () {
analytics.onContextLoaded((p0) { });
});
test('it analytics onPluginLoaded should be callable', () {
analytics.onPluginLoaded((p0) { });
});

test("Test analytics platform getContext", () {
AnalyticsPlatform analyticsPlatform = MockAnalyticsPlatform();

expect(
() async => await analyticsPlatform.getContext(),
throwsA(isA<UnimplementedError>()),
);
});
test("Test analytics platform linkStream", () {
AnalyticsPlatform analyticsPlatform = MockAnalyticsPlatform();

expect(
() async => analyticsPlatform.linkStream,
throwsA(isA<UnimplementedError>()),
);
});

test("it createClient", () async {
Analytics analytics = createClient(Configuration("123",
debug: false,
trackApplicationLifecycleEvents: true,
trackDeeplinks: true,
token: "abcdef12345")
);
expect(analytics, isA<Analytics>());
});
});
}

class MockAnalyticsPlatform extends AnalyticsPlatform { }
Loading