Files
food-ai/client/test/features/auth/login_screen_test.dart
dbastrikin 24219b611e feat: implement Iteration 0 foundation (backend + Flutter client)
Backend (Go):
- Project structure with chi router, pgxpool, goose migrations
- JWT auth (access/refresh tokens) with Firebase token verification
- NoopTokenVerifier for local dev without Firebase credentials
- PostgreSQL user repository with atomic profile updates (transactions)
- Mifflin-St Jeor calorie calculation based on profile data
- REST API: POST /auth/login, /auth/refresh, /auth/logout, GET/PUT /profile, GET /health
- Middleware: auth, CORS (localhost wildcard), logging, recovery, request_id
- Unit tests (51 passing) and integration tests (testcontainers)
- Docker Compose setup with postgres healthcheck and graceful shutdown

Flutter client:
- Riverpod state management with GoRouter navigation
- Firebase Auth (email/password + Google sign-in with web popup support)
- Platform-aware API URLs (web/Android/iOS)
- Dio HTTP client with JWT auth interceptor and concurrent refresh handling
- Secure token storage
- Screens: Login, Register, Home (tabs: Menu, Recipes, Products, Profile)
- Unit tests (17 passing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:14:58 +02:00

116 lines
3.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
import 'package:food_ai/core/auth/auth_provider.dart';
import 'package:food_ai/features/auth/login_screen.dart';
class _TestAuthNotifier extends AuthNotifier {
_TestAuthNotifier(super.initialState) : super.test();
@override
Future<void> signInWithEmail(String email, String password) async {}
@override
Future<void> signInWithGoogle() async {}
@override
Future<void> register(String email, String password, String name) async {}
@override
Future<void> signOut() async {}
}
Widget _buildTestWidget({AuthState? initialState}) {
final authState = initialState ??
const AuthState(status: AuthStatus.unauthenticated);
final router = GoRouter(
initialLocation: '/auth/login',
routes: [
GoRoute(
path: '/auth/login',
builder: (_, __) => const LoginScreen(),
),
GoRoute(
path: '/auth/register',
builder: (_, __) =>
const Scaffold(body: Text('Register Screen')),
),
GoRoute(
path: '/home',
builder: (_, __) =>
const Scaffold(body: Text('Home Screen')),
),
],
);
return ProviderScope(
overrides: [
authProvider.overrideWith(
(ref) => _TestAuthNotifier(authState),
),
],
child: MaterialApp.router(routerConfig: router),
);
}
void main() {
testWidgets('renders email and password fields and buttons',
(tester) async {
await tester.pumpWidget(_buildTestWidget());
await tester.pumpAndSettle();
expect(find.text('Email'), findsOneWidget);
expect(find.text('Пароль'), findsOneWidget);
expect(find.text('Войти'), findsOneWidget);
expect(find.text('Войти через Google'), findsOneWidget);
expect(find.text('Нет аккаунта? Регистрация'), findsOneWidget);
});
testWidgets('validates empty email', (tester) async {
await tester.pumpWidget(_buildTestWidget());
await tester.pumpAndSettle();
await tester.tap(find.text('Войти'));
await tester.pumpAndSettle();
expect(find.text('Введите email'), findsOneWidget);
});
testWidgets('validates invalid email format', (tester) async {
await tester.pumpWidget(_buildTestWidget());
await tester.pumpAndSettle();
await tester.enterText(
find.widgetWithText(TextFormField, 'Email'), 'abc');
await tester.tap(find.text('Войти'));
await tester.pumpAndSettle();
expect(find.text('Некорректный email'), findsOneWidget);
});
testWidgets('validates short password', (tester) async {
await tester.pumpWidget(_buildTestWidget());
await tester.pumpAndSettle();
await tester.enterText(
find.widgetWithText(TextFormField, 'Email'), 'test@test.com');
await tester.enterText(
find.widgetWithText(TextFormField, 'Пароль'), '123');
await tester.tap(find.text('Войти'));
await tester.pumpAndSettle();
expect(find.text('Минимум 6 символов'), findsOneWidget);
});
testWidgets('shows FoodAI branding', (tester) async {
await tester.pumpWidget(_buildTestWidget());
await tester.pumpAndSettle();
expect(find.text('FoodAI'), findsOneWidget);
expect(find.text('Управляйте питанием\nс помощью AI'), findsOneWidget);
});
}