계정 정보 다이얼로그 추가 및 전체 목록 페치 개선

This commit is contained in:
JiWoong Sul
2025-10-22 01:05:47 +09:00
parent 6b58effc83
commit f4dc83d441
44 changed files with 1636 additions and 362 deletions

View File

@@ -0,0 +1,196 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:superport_v2/core/constants/app_sections.dart';
import 'package:superport_v2/core/permissions/permission_manager.dart';
import 'package:superport_v2/core/theme/superport_shad_theme.dart';
import 'package:superport_v2/core/theme/theme_controller.dart';
import 'package:superport_v2/core/services/token_storage.dart';
import 'package:superport_v2/features/auth/application/auth_service.dart';
import 'package:superport_v2/features/auth/domain/entities/auth_permission.dart';
import 'package:superport_v2/features/auth/domain/entities/auth_session.dart';
import 'package:superport_v2/features/auth/domain/entities/authenticated_user.dart';
import 'package:superport_v2/features/auth/domain/entities/login_request.dart';
import 'package:superport_v2/features/auth/domain/repositories/auth_repository.dart';
import 'package:superport_v2/widgets/app_shell.dart';
void main() {
setUp(() {
GetIt.I.reset();
});
testWidgets('계정 버튼을 누르면 계정 정보 다이얼로그가 표시된다', (tester) async {
final session = _buildSession();
final authService = _createAuthService(session);
GetIt.I.registerSingleton<AuthService>(authService);
addTearDown(authService.dispose);
await authService.login(
const LoginRequest(identifier: 'user@example.com', password: 'secret'),
);
await _pumpAppShell(tester);
await tester.tap(find.byIcon(lucide.LucideIcons.userRound));
await tester.pumpAndSettle();
expect(find.text('계정 정보'), findsOneWidget);
expect(find.text('김승인'), findsWidgets);
expect(find.text('E2025001'), findsOneWidget);
expect(find.text('물류팀'), findsOneWidget);
expect(find.textContaining('/approvals'), findsOneWidget);
});
testWidgets('다이얼로그에서 로그아웃을 누르면 세션이 초기화되고 로그인 화면으로 이동한다', (tester) async {
final session = _buildSession();
final authService = _createAuthService(session);
GetIt.I.registerSingleton<AuthService>(authService);
addTearDown(authService.dispose);
await authService.login(
const LoginRequest(identifier: 'user@example.com', password: 'secret'),
);
await _pumpAppShell(tester);
expect(authService.session, isNotNull);
await tester.tap(find.byIcon(lucide.LucideIcons.userRound));
await tester.pumpAndSettle();
await tester.tap(find.text('로그아웃'));
await tester.pumpAndSettle();
expect(authService.session, isNull);
expect(find.text('로그인 페이지'), findsOneWidget);
});
}
Future<void> _pumpAppShell(WidgetTester tester) async {
final themeController = ThemeController();
final permissionManager = PermissionManager();
final router = GoRouter(
initialLocation: dashboardRoutePath,
routes: [
GoRoute(
path: loginRoutePath,
builder: (context, state) => const _LoginPlaceholder(),
),
ShellRoute(
builder: (context, state, child) =>
AppShell(currentLocation: state.uri.toString(), child: child),
routes: [
GoRoute(
path: dashboardRoutePath,
builder: (context, state) => const _DashboardPlaceholder(),
),
],
),
],
);
addTearDown(themeController.dispose);
addTearDown(permissionManager.dispose);
addTearDown(router.dispose);
await tester.pumpWidget(
PermissionScope(
manager: permissionManager,
child: ThemeControllerScope(
controller: themeController,
child: ShadApp.router(
routerConfig: router,
theme: SuperportShadTheme.light(),
darkTheme: SuperportShadTheme.dark(),
debugShowCheckedModeBanner: false,
),
),
),
);
await tester.pumpAndSettle();
}
AuthSession _buildSession() {
return AuthSession(
accessToken: 'access-token',
refreshToken: 'refresh-token',
expiresAt: DateTime.parse('2025-10-21T09:00:00Z'),
user: const AuthenticatedUser(
id: 7,
name: '김승인',
employeeNo: 'E2025001',
email: 'approver@example.com',
primaryGroupId: 3,
primaryGroupName: '물류팀',
),
permissions: const [
AuthPermission(resource: '/dashboard', actions: ['read']),
AuthPermission(resource: '/approvals', actions: ['read', 'update']),
],
);
}
AuthService _createAuthService(AuthSession session) {
final repository = _FakeAuthRepository(session);
final tokenStorage = _MemoryTokenStorage();
return AuthService(repository: repository, tokenStorage: tokenStorage);
}
class _FakeAuthRepository implements AuthRepository {
_FakeAuthRepository(this.session);
final AuthSession session;
@override
Future<AuthSession> login(LoginRequest request) async => session;
@override
Future<AuthSession> refresh(String refreshToken) async => session;
}
class _MemoryTokenStorage implements TokenStorage {
String? _access;
String? _refresh;
@override
Future<void> clear() async {
_access = null;
_refresh = null;
}
@override
Future<String?> readAccessToken() async => _access;
@override
Future<String?> readRefreshToken() async => _refresh;
@override
Future<void> writeAccessToken(String? token) async {
_access = token;
}
@override
Future<void> writeRefreshToken(String? token) async {
_refresh = token;
}
}
class _DashboardPlaceholder extends StatelessWidget {
const _DashboardPlaceholder();
@override
Widget build(BuildContext context) {
return const Scaffold(body: Center(child: Text('대시보드 본문')));
}
}
class _LoginPlaceholder extends StatelessWidget {
const _LoginPlaceholder();
@override
Widget build(BuildContext context) {
return const Scaffold(body: Center(child: Text('로그인 페이지')));
}
}