Deliver localized auth app with verification and go_router
Integrate themed login/signup, EN/RU localization, shared dummy providers, verification recovery screens and named URL routes. Store frontend guidelines, workflow skills, validation and delivery documentation. Related to #2, #3, #5 and #6.
This commit is contained in:
1 parent
27424ed886
commit
10a278c65a
44 files changed
+4128
-28
No files matched your search
@@ -0,0 +1,157 @@
|
||||
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() {
|
||||
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);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user