Files
Stephan D 10a278c65a 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.
2026-09-11 15:37:07 +02:00

161 lines
5.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sxchnge_app/auth/validation.dart';
import 'package:sxchnge_app/main.dart';
import 'package:sxchnge_app/generated/i18n/app_localizations_en.dart';
Future<void> openApp(
WidgetTester tester, {
Size size = const Size(390, 844),
double scale = 1,
}) async {
tester.view.physicalSize = size;
tester.view.devicePixelRatio = 1;
tester.platformDispatcher.textScaleFactorTestValue = scale;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
addTearDown(tester.platformDispatcher.clearTextScaleFactorTestValue);
await tester.pumpWidget(const SxchngeApp());
await tester.pumpAndSettle();
}
Future<void> submit(WidgetTester tester) async {
final button = find.byKey(const ValueKey('submit'));
await tester.ensureVisible(button);
await tester.tap(button);
await tester.pumpAndSettle();
}
void main() {
final l = AppLocalizationsEn();
test(
'validation rejects malformed email and mismatched signup passwords',
() {
for (final email in ['', 'user', 'user@', 'user @example.com']) {
expect(AuthValidation.email(email, l), isNotNull);
}
expect(AuthValidation.email(' user+tag@example.com ', l), isNull);
expect(AuthValidation.password('short', l, signUp: true), isNotNull);
expect(AuthValidation.password('short', l, signUp: false), isNull);
expect(
AuthValidation.confirmation('different', 'password', l),
isNotNull,
);
expect(AuthValidation.name(' ', l), isNotNull);
},
);
testWidgets(
'login validates, toggles password, and submits with the keyboard',
(tester) async {
await openApp(tester);
await submit(tester);
expect(find.text('Enter your email address.'), findsOneWidget);
expect(find.text('Enter your password.'), findsOneWidget);
await tester.enterText(
find.byKey(const ValueKey('email')),
'test@example.com',
);
await tester.enterText(
find.byKey(const ValueKey('password')),
'example-password',
);
await tester.tap(find.byTooltip('Show password'));
await tester.pump();
expect(
tester
.widget<TextField>(
find.descendant(
of: find.byKey(const ValueKey('password')),
matching: find.byType(TextField),
),
)
.obscureText,
isFalse,
);
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('preview-result')), findsOneWidget);
expect(tester.takeException(), isNull);
},
);
testWidgets(
'signup validates confirmation and clears credentials when switching',
(tester) async {
await openApp(tester);
await tester.tap(find.text('Sign up'));
await tester.pumpAndSettle();
await tester.enterText(find.byKey(const ValueKey('name')), 'Alex');
await tester.enterText(
find.byKey(const ValueKey('email')),
'alex@example.com',
);
await tester.enterText(
find.byKey(const ValueKey('password')),
'example-password',
);
await tester.enterText(
find.byKey(const ValueKey('confirmation')),
'different',
);
await submit(tester);
expect(find.text('Passwords do not match.'), findsOneWidget);
await tester.enterText(
find.byKey(const ValueKey('confirmation')),
'example-password',
);
await submit(tester);
expect(find.byKey(const ValueKey('verification-sent')), findsOneWidget);
await tester.ensureVisible(
find.byKey(const ValueKey('demo-confirmation')),
);
await tester.tap(find.byKey(const ValueKey('demo-confirmation')));
await tester.pumpAndSettle();
expect(
find.byKey(const ValueKey('verification-verified')),
findsOneWidget,
);
await tester.ensureVisible(find.text('Back to login'));
await tester.tap(find.text('Back to login'));
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('confirmation')), findsNothing);
expect(find.text('example-password'), findsNothing);
expect(find.byKey(const ValueKey('preview-result')), findsNothing);
expect(tester.takeException(), isNull);
},
);
testWidgets('resizing preserves the selected mode and entered values', (
tester,
) async {
await openApp(tester, size: const Size(1440, 1000));
await tester.tap(find.text('Sign up'));
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const ValueKey('email')),
'alex@example.com',
);
tester.view.physicalSize = const Size(390, 844);
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('name')), findsOneWidget);
expect(find.text('alex@example.com'), findsOneWidget);
expect(tester.takeException(), isNull);
});
for (final size in [const Size(320, 740), const Size(1440, 1000)]) {
testWidgets('both modes fit $size at 200% text scale', (tester) async {
await openApp(tester, size: size, scale: 2);
expect(tester.takeException(), isNull);
await tester.ensureVisible(find.text('Sign up'));
await tester.tap(find.text('Sign up'));
await tester.pumpAndSettle();
await submit(tester);
expect(tester.takeException(), isNull);
});
}
}