halkyon/packages/halkyon_app/test/auth/widget/wait_screen_test.dart

234 lines
7.3 KiB
Dart
Raw Normal View History

import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_app/halkyon_app.dart';
import 'package:halkyon_app/testing.dart';
import 'package:material_ui/material_ui.dart';
/// A signed-in person's app as a test makes one: whose it is.
class const _PersonApp(final SignedIn person) extends StatelessWidget {
@override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(body: Text('App of ${person.authUserId}')),
);
}
/// The wait screen (Halkyon Learn's ADR 0021, D31, D33), as the session gate
/// shows it while an account signed in or up to here waits for the
/// instance's operator.
void main() {
late FakeAuthGateway gateway;
late TestAppDependencies dependencies;
final anna = fakeWaitOf('anna', handle: 'anna');
setUp(() {
gateway = FakeAuthGateway(status: Waiting(anna));
dependencies = TestAppDependencies(gateway: gateway);
});
Future<void> start(WidgetTester tester) async {
tester.view.physicalSize = const Size(412, 1200);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await tester.pumpWidget(
AppFrameScope(
frame: testFrame(),
child: AppScope(
dependencies: dependencies,
child: SessionGate(
look: (context) => (themeMode: ThemeMode.light, locale: null),
app: _PersonApp.new,
),
),
),
);
await tester.pumpAndSettle();
}
Future<void> tap(WidgetTester tester, String label) async {
final button = find.text(label);
await tester.ensureVisible(button);
await tester.tap(button);
await tester.pumpAndSettle();
}
const waiting =
'Your account waits until the operator of test.example.org approves it.';
testWidgets('shows the handle, the words to match and what to do with '
'them: only check them, never read them out (D33)', (tester) async {
await start(tester);
expect(find.text('Halkyon Test'), findsOneWidget);
expect(find.text('@anna'), findsOneWidget);
expect(find.text(waiting), findsOneWidget);
expect(find.text('Matching code'), findsOneWidget);
for (final word in fakeMatchingWords) {
expect(find.text(word), findsOneWidget);
}
expect(
find.text(
'When the operator of test.example.org reads you four words, check '
'that they are these. Never read them out yourself: Halkyon never '
'asks anyone to.',
),
findsOneWidget,
);
expect(find.text(anna.pass), findsNothing, reason: 'the pass is a secret');
expect(find.text('Start on test.example.org:'), findsNothing);
});
testWidgets('asks the instance as it opens, every 30 seconds, as the app '
'comes back, and when asked', (tester) async {
await start(tester);
expect(gateway.looks, 1);
await tester.pump(const Duration(seconds: 30));
expect(gateway.looks, 2);
// Away and back, as the system moves an app.
[
AppLifecycleState.inactive,
AppLifecycleState.hidden,
AppLifecycleState.paused,
AppLifecycleState.hidden,
AppLifecycleState.inactive,
AppLifecycleState.resumed,
].forEach(tester.binding.handleAppLifecycleStateChanged);
await tester.pump();
expect(gateway.looks, 3);
await tap(tester, 'Check now');
expect(gateway.looks, 4);
});
testWidgets('approved, it says so and leads to the usual sign-in, once '
'more as before (D31): the pass holds no session', (tester) async {
await start(tester);
gateway.waitAnswer = AccountWaitState.approved;
await tester.pump(const Duration(seconds: 30));
await tester.pumpAndSettle();
expect(
find.text(
'The operator of test.example.org has approved your account. Sign '
'in once more, as you did before.',
),
findsOneWidget,
);
expect(find.text('Matching code'), findsNothing);
expect(gateway.usedSessions, isEmpty);
// Nothing left to ask.
await tester.pump(const Duration(seconds: 60));
expect(gateway.looks, 2);
await tap(tester, 'Sign in');
expect(find.text('Start on test.example.org:'), findsOneWidget);
expect(gateway.waitsStopped, 1);
});
testWidgets('a wait the instance knows no more ends: refused, or too old', (
tester,
) async {
await start(tester);
gateway.waitAnswer = AccountWaitState.ended;
await tap(tester, 'Check now');
expect(
find.text(
'This sign-up has ended: the operator of test.example.org refused '
'it, or it waited too long.',
),
findsOneWidget,
);
await tap(tester, 'Back to the start');
expect(find.text('Start on test.example.org:'), findsOneWidget);
});
testWidgets('a new account is deleted with its pass, after asking', (
tester,
) async {
await start(tester);
await tap(tester, 'Delete this account');
expect(find.text('Delete this account?'), findsOneWidget);
await tap(tester, 'Cancel');
expect(gateway.waitingDeletions, 0);
expect(find.text(waiting), findsOneWidget);
await tap(tester, 'Delete this account');
await tester.tap(find.widgetWithText(FilledButton, 'Delete this account'));
await tester.pumpAndSettle();
expect(gateway.waitingDeletions, 1);
expect(find.text('Start on test.example.org:'), findsOneWidget);
});
testWidgets('a deletion that fails says so, and the wait stays', (
tester,
) async {
gateway.waitingDeletionError = Exception('No connection');
await start(tester);
await tap(tester, 'Delete this account');
await tester.tap(find.widgetWithText(FilledButton, 'Delete this account'));
await tester.pumpAndSettle();
expect(
find.text("Couldn't delete the account. Try again."),
findsOneWidget,
);
expect(find.text(waiting), findsOneWidget);
});
testWidgets('a suspended account shows its words but offers no deletion: '
"that takes its owner's password and a code (ADR 0017)", (tester) async {
gateway = FakeAuthGateway(
status: Waiting(anna.standing(AccountWaitState.suspended)),
);
dependencies = TestAppDependencies(gateway: gateway);
await start(tester);
expect(
find.text(
'The operator of test.example.org has suspended your account. Only '
'they can lift it.',
),
findsOneWidget,
);
expect(find.text('abandon'), findsOneWidget);
expect(find.text('Delete this account'), findsNothing);
expect(find.text('Sign out'), findsOneWidget);
});
testWidgets('an instance out of reach is said so, and asked again', (
tester,
) async {
gateway.lookError = Exception('No connection');
await start(tester);
expect(
find.text(
"test.example.org couldn't be reached. It's asked again every 30 "
'seconds.',
),
findsOneWidget,
);
expect(find.text(waiting), findsOneWidget);
gateway.lookError = null;
await tester.pump(const Duration(seconds: 30));
await tester.pumpAndSettle();
expect(find.textContaining("couldn't be reached"), findsNothing);
});
testWidgets('signing out stops waiting on this device: the start screen '
'follows, where signing in again waits anew', (tester) async {
await start(tester);
await tap(tester, 'Sign out');
expect(gateway.waitsStopped, 1);
expect(find.text('Start on test.example.org:'), findsOneWidget);
});
}