134 lines
4.5 KiB
Dart
134 lines
4.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_test/flutter_test.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';
|
|
|
|
class RecoveringService implements VerificationService {
|
|
int sends = 0;
|
|
int confirmations = 0;
|
|
@override
|
|
Future<VerificationResult> send({
|
|
required String email,
|
|
required String locale,
|
|
}) async {
|
|
if (++sends == 1) throw StateError('private error');
|
|
return const VerificationResult(isSimulated: true);
|
|
}
|
|
|
|
@override
|
|
Future<VerificationResult> confirm({required String token}) async {
|
|
if (++confirmations == 1) throw StateError('private error');
|
|
return const VerificationResult(isSimulated: true);
|
|
}
|
|
}
|
|
|
|
Future<void> openVerification(
|
|
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.pumpAndSettle();
|
|
}
|
|
|
|
void main() {
|
|
for (final scenario in {
|
|
'demo-valid': 'verified',
|
|
'demo-expired': 'expired',
|
|
'wrong': 'invalid',
|
|
'': 'invalid',
|
|
'demo-internal': 'error',
|
|
}.entries) {
|
|
testWidgets(
|
|
'confirmation link resolves ${scenario.key} as ${scenario.value}',
|
|
(tester) async {
|
|
await openVerification(tester, '/verify-email?token=${scenario.key}');
|
|
expect(
|
|
find.byKey(ValueKey('verification-${scenario.value}')),
|
|
findsOneWidget,
|
|
);
|
|
expect(find.textContaining('DEMO'), findsNothing);
|
|
expect(tester.takeException(), isNull);
|
|
},
|
|
);
|
|
}
|
|
testWidgets('internal confirmation error can retry without entering email', (
|
|
tester,
|
|
) async {
|
|
final service = RecoveringService();
|
|
await openVerification(
|
|
tester,
|
|
'/verify-email?token=opaque',
|
|
service: service,
|
|
);
|
|
expect(find.text('private error'), findsNothing);
|
|
final retry = find.byKey(const ValueKey('verification-retry'));
|
|
await tester.ensureVisible(retry);
|
|
await tester.tap(retry);
|
|
await tester.pumpAndSettle();
|
|
expect(find.byKey(const ValueKey('verification-verified')), findsOneWidget);
|
|
expect(service.confirmations, 2);
|
|
});
|
|
testWidgets(
|
|
'request validates email, reports send error and resends successfully',
|
|
(tester) async {
|
|
final service = RecoveringService();
|
|
await openVerification(tester, '/verification-sent', service: service);
|
|
final resend = find.byKey(const ValueKey('verification-resend'));
|
|
await tester.ensureVisible(resend);
|
|
await tester.tap(resend);
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Enter your email address.'), findsOneWidget);
|
|
expect(service.sends, 0);
|
|
await tester.enterText(
|
|
find.byKey(const ValueKey('verification-email')),
|
|
'alex@example.com',
|
|
);
|
|
await tester.ensureVisible(resend);
|
|
await tester.tap(resend);
|
|
await tester.pumpAndSettle();
|
|
expect(find.byKey(const ValueKey('verification-error')), findsOneWidget);
|
|
await tester.ensureVisible(resend);
|
|
await tester.tap(resend);
|
|
await tester.pumpAndSettle();
|
|
expect(find.byKey(const ValueKey('verification-sent')), findsOneWidget);
|
|
expect(service.sends, 2);
|
|
},
|
|
);
|
|
for (final route in [
|
|
'/verification-sent',
|
|
'/verify-email?token=demo-valid',
|
|
'/verify-email?token=demo-expired',
|
|
'/verify-email?token=bad',
|
|
'/verify-email?token=demo-internal',
|
|
]) {
|
|
testWidgets('$route fits narrow viewport at 200% in EN and RU', (
|
|
tester,
|
|
) async {
|
|
tester.view.physicalSize = const Size(320, 740);
|
|
tester.view.devicePixelRatio = 1;
|
|
tester.platformDispatcher.textScaleFactorTestValue = 2;
|
|
addTearDown(tester.view.resetPhysicalSize);
|
|
addTearDown(tester.view.resetDevicePixelRatio);
|
|
addTearDown(tester.platformDispatcher.clearTextScaleFactorTestValue);
|
|
await openVerification(tester, route);
|
|
expect(tester.takeException(), isNull);
|
|
final element = tester.element(find.byType(Scaffold).first);
|
|
await element.read<LocaleProvider>().setLocale(const Locale('ru'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byKey(const ValueKey('locale-selector')), findsNothing);
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
}
|
|
}
|