Files
sxchnge/frontend/app/test/routing_test.dart

219 lines
7.8 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:sxchnge_auth/sxchnge_auth.dart';
import 'package:sxchnge_app/main.dart';
import 'package:sxchnge_app/providers/locale.dart';
import 'package:sxchnge_app/routing/routes.dart';
import 'package:sxchnge_app/verification/page.dart';
class PendingConfirmation implements VerificationService {
final results = <String, Completer<VerificationResult>>{};
@override
Future<VerificationResult> confirm({required String token}) =>
(results[token] = Completer<VerificationResult>()).future;
@override
Future<VerificationResult> send({
required String email,
required String locale,
}) async => const VerificationResult(isSimulated: true);
}
Future<GoRouter> openRoute(
WidgetTester tester,
String route, {
VerificationService service = const DummyVerificationService(
delay: Duration.zero,
),
}) async {
tester.platformDispatcher.defaultRouteNameTestValue = route;
addTearDown(tester.platformDispatcher.clearDefaultRouteNameTestValue);
await tester.pumpWidget(SxchngeApp(verificationService: service));
await tester.pump();
return GoRouter.of(tester.element(find.byType(Scaffold).last));
}
void main() {
for (final language in ['en', 'ru']) {
testWidgets(
'verified link keeps success visible and prefills login ($language)',
(tester) async {
tester.platformDispatcher.defaultRouteNameTestValue =
'/verify-email?token=demo-valid';
addTearDown(tester.platformDispatcher.clearDefaultRouteNameTestValue);
await tester.pumpWidget(
SxchngeApp(
localeProvider: LocaleProvider(initialLocale: Locale(language)),
verificationService: const DummyVerificationService(
delay: Duration.zero,
verifiedEmail: 'private-alias@example.com',
),
),
);
await tester.pumpAndSettle();
expect(
find.byKey(const ValueKey('verification-verified')),
findsOneWidget,
);
expect(find.byKey(const ValueKey('email')), findsNothing);
final router = GoRouter.of(
tester.element(find.byType(VerificationPage)),
);
expect(router.routeInformationProvider.value.uri.path, '/verify-email');
await tester.ensureVisible(
find.byKey(const ValueKey('verification-continue')),
);
await tester.tap(find.byKey(const ValueKey('verification-continue')));
await tester.pumpAndSettle();
final email = tester.widget<TextFormField>(
find.byKey(const ValueKey('email')),
);
expect(email.controller!.text, 'private-alias@example.com');
final password = tester.state<EditableTextState>(
find.descendant(
of: find.byKey(const ValueKey('password')),
matching: find.byType(EditableText),
),
);
expect(password.currentTextEditingValue.text, isEmpty);
expect(password.widget.focusNode.hasFocus, isTrue);
expect(router.routeInformationProvider.value.uri.toString(), '/');
final restored = router.routeInformationParser.restoreRouteInformation(
router.routerDelegate.currentConfiguration,
)!;
expect(
restored.state.toString(),
isNot(contains('private-alias@example.com')),
);
expect(restored.state.toString(), isNot(contains('demo-valid')));
await tester.enterText(
find.byKey(const ValueKey('email')),
'changed@example.com',
);
expect(email.controller!.text, 'changed@example.com');
expect(find.byKey(const ValueKey('confirmation')), findsNothing);
},
);
}
testWidgets('unknown deep link redirects to canonical login URL', (
tester,
) async {
final router = await openRoute(tester, '/unknown?token=private');
await tester.pumpAndSettle();
expect(router.routeInformationProvider.value.uri.toString(), '/');
expect(find.byKey(const ValueKey('email')), findsOneWidget);
});
testWidgets(
'changing confirmation token ignores the previous pending result',
(tester) async {
final service = PendingConfirmation();
final router = await openRoute(
tester,
'/verify-email?token=first',
service: service,
);
await tester.pump();
expect(service.results.containsKey('first'), isTrue);
router.goNamed(
AppRoutes.verifyEmail,
queryParameters: {'token': 'second'},
);
await tester.pump();
await tester.pump();
service.results['second']!.completeError(
const VerificationException(VerificationError.expired),
);
await tester.pumpAndSettle();
expect(
find.byKey(const ValueKey('verification-expired')),
findsOneWidget,
);
service.results['first']!.complete(
const VerificationResult(isSimulated: true),
);
await tester.pumpAndSettle();
expect(
find.byKey(const ValueKey('verification-expired')),
findsOneWidget,
);
expect(find.byKey(const ValueKey('verification-verified')), findsNothing);
},
);
testWidgets(
'platform back and forward route updates recheck confirmation tokens',
(tester) async {
final router = await openRoute(tester, '/verify-email?token=demo-valid');
await tester.pumpAndSettle();
await router.routeInformationProvider.didPushRouteInformation(
RouteInformation(uri: Uri.parse('/verify-email?token=demo-expired')),
);
await tester.pumpAndSettle();
expect(
find.byKey(const ValueKey('verification-expired')),
findsOneWidget,
);
await router.routeInformationProvider.didPushRouteInformation(
RouteInformation(uri: Uri.parse('/verify-email?token=demo-valid')),
);
await tester.pumpAndSettle();
expect(
find.byKey(const ValueKey('verification-verified')),
findsOneWidget,
);
expect(
router.routeInformationProvider.value.uri.queryParameters['token'],
'demo-valid',
);
},
);
testWidgets('locale rebuild preserves router, URL and confirmation state', (
tester,
) async {
final router = await openRoute(tester, '/verify-email?token=demo-valid');
await tester.pumpAndSettle();
final context = tester.element(find.byType(VerificationPage));
await context.read<LocaleProvider>().setLocale(const Locale('ru'));
await tester.pumpAndSettle();
expect(
GoRouter.of(tester.element(find.byType(VerificationPage))),
same(router),
);
expect(find.byKey(const ValueKey('verification-verified')), findsOneWidget);
expect(find.text('Ваша почта\nподтверждена.'), findsOneWidget);
});
testWidgets(
'signup email stays in memory and is excluded from serialized history',
(tester) async {
final router = await openRoute(tester, '/');
await tester.pumpAndSettle();
router.goNamed(AppRoutes.verificationSent, extra: 'alex@example.com');
await tester.pumpAndSettle();
expect(find.text('alex@example.com'), findsOneWidget);
expect(find.byKey(const ValueKey('verification-sent')), findsOneWidget);
final restored = router.routeInformationParser.restoreRouteInformation(
router.routerDelegate.currentConfiguration,
)!;
expect(restored.state.toString(), isNot(contains('alex@example.com')));
expect(restored.uri.toString(), '/verification-sent');
router.goNamed(AppRoutes.login);
await tester.pumpAndSettle();
router.goNamed(AppRoutes.verificationSent);
await tester.pumpAndSettle();
expect(find.text('alex@example.com'), findsNothing);
expect(find.byKey(const ValueKey('verification-idle')), findsOneWidget);
},
);
}