Skip to content

Commit

Permalink
Improve the channel observer and split the tests better
Browse files Browse the repository at this point in the history
  • Loading branch information
Abestanis committed May 11, 2022
1 parent 851a370 commit 1670f19
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 121 deletions.
30 changes: 0 additions & 30 deletions test/observer/app.dart

This file was deleted.

2 changes: 1 addition & 1 deletion test/observer/observer.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export 'toast.dart';
export 'app.dart';
export 'system.dart';
31 changes: 31 additions & 0 deletions test/observer/system.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:flutter/services.dart';

import '../test.dart';

/// An observer for the system channel.
class SystemChannelObserver {

int _closeRequests = 0; /// How many close request was recorded since the last observation.
int get closeRequests {
final numRequests = _closeRequests;
clearCloseRequest();
return numRequests;
}

/// Create a new system channel observer, which automatically
/// unregisters any previously created observer.
SystemChannelObserver(WidgetTester tester) {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (call) {
if (call.method == 'SystemNavigator.pop') {
_closeRequests++;
return null;
}
return null; // Ignore unimplemented method calls
});
}

/// Reset the number of recorded close requests to zero.
void clearCloseRequest() {
_closeRequests = 0;
}
}
43 changes: 14 additions & 29 deletions test/observer/toast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,30 @@ import 'package:flutter/services.dart';
import '../test.dart';

/// An observer for toast messages from the flutter toast package.
class ToastObserver {
class ToastChannelObserver {
/// The method channel used by the flutter toast package
static const MethodChannel _channel = MethodChannel('PonnamKarthik/fluttertoast');
/// The arguments for the last requested toast message.
Map<String, dynamic>? lastToastMessage;

/// Create a new toast observer, which automatically
Map<String, dynamic>? _lastToastArguments; /// The arguments for the last requested toast message.
String? get lastToastMessage {
final lastArguments = _lastToastArguments;
clearLastToast();
return lastArguments?['msg'] as String?;
}

/// Create a new toast channel observer, which automatically
/// unregisters any previously created observer.
ToastObserver(WidgetTester tester) {
ToastChannelObserver(WidgetTester tester) {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(_channel, (call) {
if (call.method == 'showToast') {
lastToastMessage = Map.castFrom(call.arguments);
_lastToastArguments = Map.castFrom(call.arguments);
return null;
}
return null; // Ignore unimplemented method calls
});
}

/// Verify that the provided [callable] shows a toast.
/// If provided, also verify that the toast showed the given [message].
/// If no toast was shown or the message doesn't match,
/// fail with the given [reason].
Future<void> expectShowsToast(Future<void> Function() callable, {String? message, String? reason}) async {
lastToastMessage = null;
await callable();
expect(lastToastMessage, isNotNull,
reason: 'Expected a toast to be shown' + (reason != null ? ': $reason' : ''));
if (message != null) {
expect(lastToastMessage!['msg'] as String?, equals(message),
reason: 'Expected a toast to show the expected message' + (reason != null ? ': $reason' : ''));
}
}

/// Verify that the provided [callable] doesn't show a toast.
/// Otherwise fail with the given [reason].
Future<void> expectShowsNoToast(Future<void> Function() callable, {String? reason}) async {
lastToastMessage = null;
await callable();
expect(lastToastMessage, isNull,
reason: 'Expected no toast to be shown' + (reason != null ? ': $reason' : ''));
/// Forget the last received toast.
void clearLastToast() {
_lastToastArguments = null;
}
}
91 changes: 30 additions & 61 deletions test/routes/home_route_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:back_button_interceptor/back_button_interceptor.dart';
import 'package:sweyer/constants.dart';

import '../observer/observer.dart';
import '../test.dart';
Expand Down Expand Up @@ -71,74 +72,42 @@ void main() {
});
});

testWidgets('app shows exit confirmation toast', (WidgetTester tester) async {
testWidgets('app shows exit confirmation toast if enabled in the preferences', (WidgetTester tester) async {
await Prefs.confirmExitingWithBackButton.set(true);
final ToastObserver toastObserver = ToastObserver(tester);
final AppObserver appObserver = AppObserver(tester);
await tester.runAppTest(() async {
expect(
await appObserver.haveCloseRequest(
() => toastObserver.expectShowsToast(
BackButtonInterceptor.popRoute,
message: l10n.pressOnceAgainToExit,
reason: 'Expected the app to ask for confirmation before exiting',
),
),
isFalse,
reason: 'Expected the app not to close after showing the toast',
);
await tester.binding.delayed(const Duration(seconds: 5));
expect(
await appObserver.haveCloseRequest(
() => toastObserver.expectShowsToast(
BackButtonInterceptor.popRoute,
message: l10n.pressOnceAgainToExit,
reason: 'Expected the app to ask for confirmation before exiting after the previous message timed out',
),
),
isFalse,
reason: 'Expected the app not to close after showing the toast',
);
expect(
await appObserver.haveCloseRequest(
() => toastObserver.expectShowsNoToast(
BackButtonInterceptor.popRoute,
final SystemChannelObserver systemObserver = SystemChannelObserver(tester);
final ToastChannelObserver toastObserver = ToastChannelObserver(tester);
await BackButtonInterceptor.popRoute();
expect(toastObserver.lastToastMessage, l10n.pressOnceAgainToExit,
reason: 'Expected the app to ask for confirmation before exiting');
expect(systemObserver.closeRequests, 0,
reason: 'Expected the app not to close after showing the toast');
await tester.binding.delayed(Config.BACK_PRESS_CLOSE_TIMEOUT + const Duration(milliseconds: 1));
await BackButtonInterceptor.popRoute();
expect(toastObserver.lastToastMessage, l10n.pressOnceAgainToExit,
reason: 'Expected the app to ask for confirmation before exiting after the previous message timed out');
expect(systemObserver.closeRequests, 0,
reason: 'Expected the app not to close after showing the toast');
await tester.binding.delayed(Config.BACK_PRESS_CLOSE_TIMEOUT - const Duration(milliseconds: 1));
await BackButtonInterceptor.popRoute();
expect(toastObserver.lastToastMessage, null,
reason: 'Expected the app to show no toast when pressing the back button a second time',
),
),
isTrue,
);
);
expect(systemObserver.closeRequests, 1,
reason: 'Expected the app to close if back is pressed after the confirmation toast was shown');
});
});

testWidgets('app respects the exit confirmation preference', (WidgetTester tester) async {
await Prefs.confirmExitingWithBackButton.set(true);
final ToastObserver toastObserver = ToastObserver(tester);
final AppObserver appObserver = AppObserver(tester);
testWidgets('app does not ask for exit confirmation if disabled in the preferences', (WidgetTester tester) async {
await Prefs.confirmExitingWithBackButton.set(false);
await tester.runAppTest(() async {
expect(
await appObserver.haveCloseRequest(
() => toastObserver.expectShowsToast(
BackButtonInterceptor.popRoute,
message: l10n.pressOnceAgainToExit,
reason: 'Expected the app to ask for confirmation before exiting',
),
),
isFalse,
reason: 'Expected the app not to close after showing the toast',
);
await Prefs.confirmExitingWithBackButton.set(false);
await tester.binding.delayed(const Duration(seconds: 5));
expect(
await appObserver.haveCloseRequest(
() => toastObserver.expectShowsNoToast(
BackButtonInterceptor.popRoute,
reason: 'Expected the app to show no toast when pressing the back button after disabling it',
),
),
isTrue,
reason: 'Expected the confirmation toast to be disabled',
);
final SystemChannelObserver systemObserver = SystemChannelObserver(tester);
final ToastChannelObserver toastObserver = ToastChannelObserver(tester);
await BackButtonInterceptor.popRoute();
expect(toastObserver.lastToastMessage, null,
reason: 'Expected the app to show no toast when pressing the back button after disabling it');
expect(systemObserver.closeRequests, 1,
reason: 'Expected the app to close if the confirmation toast is disabled');
});
});
}

0 comments on commit 1670f19

Please sign in to comment.