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

243 lines
8.2 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:sxchnge_analytics/sxchnge_analytics.dart';
import 'package:sxchnge_auth/sxchnge_auth.dart';
import 'package:sxchnge_app/main.dart';
import 'package:sxchnge_app/providers/locale.dart';
class ControlledLogin implements LoginService {
final calls = <Completer<AuthResult>>[];
LoginRequest? request;
@override
Future<AuthResult> login(LoginRequest input) {
request = input;
final completer = Completer<AuthResult>();
calls.add(completer);
return completer.future;
}
}
class UnverifiedBackend extends BackendAuthServices {
UnverifiedBackend()
: super(baseUri: Uri.parse('https://api.example/api/v1'), clientId: 'web');
int sends = 0;
@override
Future<AuthResult> login(LoginRequest request) async =>
throw const AuthException(AuthError.accountNotVerified);
@override
Future<VerificationResult> send({
required String email,
required String locale,
}) async {
sends++;
return const VerificationResult(isSimulated: false);
}
}
void main() {
for (final language in ['en', 'ru']) {
testWidgets(
'signup requiring assistance preserves input and records failure ($language)',
(tester) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
tester.platformDispatcher.textScaleFactorTestValue = 1.5;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
addTearDown(tester.platformDispatcher.clearTextScaleFactorTestValue);
final analytics = DummyAnalyticsService();
final locale = LocaleProvider(initialLocale: Locale(language));
await tester.pumpWidget(
SxchngeApp(
localeProvider: locale,
analyticsService: analytics,
signupService: const DummySignupService(
delay: Duration.zero,
failure: AuthError.signupNeedsAssistance,
),
),
);
await tester.pumpAndSettle();
final signupLabel = language == 'ru' ? 'Регистрация' : 'Sign up';
await tester.ensureVisible(find.text(signupLabel));
await tester.tap(find.text(signupLabel));
await tester.pumpAndSettle();
for (final entry in {
'name': 'Ada Lovelace',
'email': 'ada@example.com',
'password': 'password preserved',
'confirmation': 'password preserved',
}.entries) {
await tester.enterText(find.byKey(ValueKey(entry.key)), entry.value);
}
await tester.ensureVisible(find.byKey(const ValueKey('submit')));
await tester.tap(find.byKey(const ValueKey('submit')));
await tester.pumpAndSettle();
final feedback = find.byKey(const ValueKey('auth-error'));
await tester.ensureVisible(feedback);
expect(
find.text(
language == 'ru'
? 'Не удалось завершить создание аккаунта. Обратитесь в службу поддержки.'
: 'We could not finish setting up your account. Please contact support.',
),
findsOneWidget,
);
expect(find.byKey(const ValueKey('verification-sent')), findsNothing);
expect(
tester
.widget<TextFormField>(find.byKey(const ValueKey('email')))
.controller!
.text,
'ada@example.com',
);
expect(
tester
.widget<TextFormField>(find.byKey(const ValueKey('password')))
.controller!
.text,
'password preserved',
);
expect(
analytics.events.where((event) => event.name == 'auth_signup_failed'),
hasLength(1),
);
expect(
analytics.events.where(
(event) => event.name == 'auth_signup_succeeded',
),
isEmpty,
);
expect(tester.takeException(), isNull);
},
);
}
testWidgets('backend mode gates workspace and recovers unverified login', (
tester,
) async {
tester.view.physicalSize = const Size(1440, 1200);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final backend = UnverifiedBackend();
await tester.pumpWidget(SxchngeApp(backend: backend));
await tester.pumpAndSettle();
final router = GoRouter.of(
tester.element(find.byKey(const ValueKey('email'))),
);
router.go('/accounts');
await tester.pumpAndSettle();
expect(router.routeInformationProvider.value.uri.path, '/');
await tester.enterText(
find.byKey(const ValueKey('email')),
'user@example.com',
);
await tester.enterText(
find.byKey(const ValueKey('password')),
'password-value',
);
await tester.ensureVisible(find.byKey(const ValueKey('submit')));
await tester.tap(find.byKey(const ValueKey('submit')));
await tester.pumpAndSettle();
expect(
router.routeInformationProvider.value.uri.path,
'/verification-sent',
);
expect(backend.sends, 1);
expect(
find.textContaining('a verification email is on its way'),
findsOneWidget,
);
expect(tester.takeException(), isNull);
});
testWidgets(
'app shows loading, localized failure, and retries through injected service',
(tester) async {
tester.view.physicalSize = const Size(1440, 1200);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final service = ControlledLogin();
await tester.pumpWidget(
SxchngeApp(
loginService: service,
localeProvider: LocaleProvider(initialLocale: const Locale('ru')),
),
);
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const ValueKey('email')),
'a@example.com',
);
await tester.enterText(
find.byKey(const ValueKey('password')),
'example-password',
);
final button = find.byKey(const ValueKey('submit'));
await tester.ensureVisible(button);
await tester.tap(button);
await tester.pump();
expect(tester.widget<FilledButton>(button).onPressed, isNull);
expect(find.text('Подождите…'), findsOneWidget);
expect(service.request!.locale, 'ru');
service.calls.single.completeError(
const AuthException(AuthError.unavailable),
);
await tester.pumpAndSettle();
expect(
find.text('Сервис недоступен. Попробуйте ещё раз.'),
findsOneWidget,
);
await tester.tap(button);
await tester.pump();
expect(service.calls, hasLength(2));
service.calls.last.complete(
const AuthResult(userId: 'demo', isSimulated: true),
);
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('preview-result')), findsOneWidget);
expect(find.byKey(const ValueKey('auth-error')), findsNothing);
expect(tester.takeException(), isNull);
},
);
testWidgets('changing mode while loading ignores the old login result', (
tester,
) async {
final service = ControlledLogin();
await tester.pumpWidget(SxchngeApp(loginService: service));
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const ValueKey('email')),
'a@example.com',
);
await tester.enterText(
find.byKey(const ValueKey('password')),
'example-password',
);
await tester.ensureVisible(find.byKey(const ValueKey('submit')));
await tester.tap(find.byKey(const ValueKey('submit')));
await tester.pump();
await tester.ensureVisible(find.text('Sign up'));
await tester.tap(find.text('Sign up'));
await tester.pumpAndSettle();
service.calls.single.complete(
const AuthResult(userId: 'demo', isSimulated: true),
);
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('confirmation')), findsOneWidget);
expect(find.byKey(const ValueKey('preview-result')), findsNothing);
expect(tester.takeException(), isNull);
});
}