Files
superport/test/integration/automated/user_actions_test.dart
JiWoong Sul 731dcd816b
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled
refactor: Repository 패턴 적용 및 Clean Architecture 완성
## 주요 변경사항

### 🏗️ Architecture
- Repository 패턴 전면 도입 (인터페이스/구현체 분리)
- Domain Layer에 Repository 인터페이스 정의
- Data Layer에 Repository 구현체 배치
- UseCase 의존성을 Service에서 Repository로 전환

### 📦 Dependency Injection
- GetIt 기반 DI Container 재구성 (lib/injection_container.dart)
- Repository 인터페이스와 구현체 등록
- Service와 Repository 공존 (마이그레이션 기간)

### 🔄 Migration Status
완료:
- License 모듈 (6개 UseCase)
- Warehouse Location 모듈 (5개 UseCase)

진행중:
- Auth 모듈 (2/5 UseCase)
- Company 모듈 (1/6 UseCase)

대기:
- User 모듈 (7개 UseCase)
- Equipment 모듈 (4개 UseCase)

### 🎯 Controller 통합
- 중복 Controller 제거 (with_usecase 버전)
- 단일 Controller로 통합
- UseCase 패턴 직접 적용

### 🧹 코드 정리
- 임시 파일 제거 (test_*.md, task.md)
- Node.js 아티팩트 제거 (package.json)
- 불필요한 테스트 파일 정리

###  테스트 개선
- Real API 중심 테스트 구조
- Mock 제거, 실제 API 엔드포인트 사용
- 통합 테스트 프레임워크 강화

## 기술적 영향
- 의존성 역전 원칙 적용
- 레이어 간 결합도 감소
- 테스트 용이성 향상
- 확장성 및 유지보수성 개선

## 다음 단계
1. User/Equipment 모듈 Repository 마이그레이션
2. Service Layer 점진적 제거
3. 캐싱 전략 구현
4. 성능 최적화
2025-08-11 20:14:10 +09:00

440 lines
15 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:superport/main.dart' as app;
import 'package:get_it/get_it.dart';
import 'package:superport/injection_container.dart' as di;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:superport/services/company_service.dart';
import 'package:superport/services/equipment_service.dart';
import 'package:superport/services/warehouse_service.dart';
/// 전체 화면 사용자 액션 통합 테스트
///
/// 모든 화면에서 가능한 사용자 액션을 테스트:
/// - 버튼 클릭
/// - 드롭다운 선택
/// - 폼 제출
/// - 검색 기능
/// - 페이지네이션
/// - 삭제 기능
/// - 수정 기능
void main() {
late GetIt getIt;
setUpAll(() async {
TestWidgetsFlutterBinding.ensureInitialized();
try {
await dotenv.load(fileName: '.env.test');
} catch (e) {
// .env.test 파일이 없어도 계속 진행
}
getIt = GetIt.instance;
await di.setupDependencies();
});
tearDown(() async {
await getIt.reset();
});
group('User Actions Integration Tests', () {
group('Button Click Tests', () {
testWidgets('Overview screen button interactions', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// 대시보드 새로고침 버튼 테스트
final refreshButton = find.byIcon(Icons.refresh);
if (refreshButton.evaluate().items.isNotEmpty) {
await tester.tap(refreshButton);
await tester.pumpAndSettle();
// expect(find.byType(CircularProgressIndicator), findsNothing);
}
// 필터 버튼 테스트
final filterButton = find.byIcon(Icons.filter_list);
if (filterButton.evaluate().items.isNotEmpty) {
await tester.tap(filterButton);
await tester.pumpAndSettle();
}
});
testWidgets('Equipment screen button interactions', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Equipment 화면으로 이동
await navigateToScreen(tester, 'equipment');
// 장비 추가 버튼 테스트
final addButton = find.byIcon(Icons.add);
if (addButton.evaluate().items.isNotEmpty) {
await tester.tap(addButton);
await tester.pumpAndSettle();
// 뒤로가기
await tester.pageBack();
await tester.pumpAndSettle();
}
// 검색 버튼 테스트
final searchButton = find.byIcon(Icons.search);
if (searchButton.evaluate().items.isNotEmpty) {
await tester.tap(searchButton);
await tester.pumpAndSettle();
}
});
testWidgets('Company screen button interactions', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Company 화면으로 이동
await navigateToScreen(tester, 'company');
// 회사 추가 버튼 테스트
final addCompanyButton = find.text('회사 등록');
if (addCompanyButton.evaluate().items.isNotEmpty) {
await tester.tap(addCompanyButton);
await tester.pumpAndSettle();
// 폼에서 취소 버튼 클릭
final cancelButton = find.byIcon(Icons.arrow_back);
if (cancelButton.evaluate().items.isNotEmpty) {
await tester.tap(cancelButton);
await tester.pumpAndSettle();
}
}
});
});
group('Dropdown Selection Tests', () {
testWidgets('Equipment status dropdown test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Equipment 화면으로 이동
await navigateToScreen(tester, 'equipment');
// 상태 드롭다운 찾기
final statusDropdown = find.byKey(Key('status_dropdown'));
if (statusDropdown.evaluate().items.isNotEmpty) {
await tester.tap(statusDropdown);
await tester.pumpAndSettle();
// 드롭다운 옵션 선택
final availableOption = find.text('재고').last;
if (availableOption.evaluate().items.isNotEmpty) {
await tester.tap(availableOption);
await tester.pumpAndSettle();
}
}
});
testWidgets('Company type dropdown test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Company 등록 화면으로 이동
await navigateToScreen(tester, 'company/form');
// 회사 유형 체크박스 테스트
final customerCheckbox = find.text('고객사');
if (customerCheckbox.evaluate().items.isNotEmpty) {
await tester.tap(customerCheckbox);
await tester.pumpAndSettle();
}
final partnerCheckbox = find.text('파트너사');
if (partnerCheckbox.evaluate().items.isNotEmpty) {
await tester.tap(partnerCheckbox);
await tester.pumpAndSettle();
}
});
});
group('Form Submission Tests', () {
testWidgets('Equipment In form submission test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Equipment In 화면으로 이동
await navigateToScreen(tester, 'equipment/in');
// 필수 필드 입력
await enterText(tester, 'manufacturer_field', 'Samsung');
await enterText(tester, 'name_field', 'Test Equipment');
await enterText(tester, 'category_field', 'Electronics');
// 저장 버튼 클릭
final saveButton = find.text('저장');
if (saveButton.evaluate().items.isNotEmpty) {
await tester.tap(saveButton);
await tester.pumpAndSettle();
// 에러 메시지나 성공 메시지 확인
// expect(
// find.byType(SnackBar).evaluate().items.isNotEmpty ||
// find.byType(AlertDialog).evaluate().items.isNotEmpty,
// isTrue,
// );
}
});
testWidgets('Warehouse Location form submission test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Warehouse Location 추가 화면으로 이동
await navigateToScreen(tester, 'warehouse/form');
// 필수 필드 입력
await enterText(tester, 'name_field', 'Test Warehouse');
await enterText(tester, 'address_field', '서울시 강남구');
// 저장 버튼 클릭
final saveButton = find.text('저장');
if (saveButton.evaluate().items.isNotEmpty) {
await tester.tap(saveButton);
await tester.pumpAndSettle();
}
});
});
group('Search Functionality Tests', () {
testWidgets('Equipment search test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Equipment 화면으로 이동
await navigateToScreen(tester, 'equipment');
// 검색 필드에 텍스트 입력
final searchField = find.byType(TextField).items.first;
if (searchField.evaluate().items.isNotEmpty) {
await tester.enterText(searchField, 'Samsung');
await tester.pumpAndSettle();
// 검색 버튼 클릭
final searchButton = find.byIcon(Icons.search);
if (searchButton.evaluate().items.isNotEmpty) {
await tester.tap(searchButton);
await tester.pumpAndSettle();
}
}
});
testWidgets('Company search test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Company 화면으로 이동
await navigateToScreen(tester, 'company');
// 검색 필드에 텍스트 입력
final searchField = find.byType(TextField).items.first;
if (searchField.evaluate().items.isNotEmpty) {
await tester.enterText(searchField, '삼성');
await tester.pumpAndSettle();
// Enter 키 시뮬레이션 또는 검색 버튼 클릭
await tester.testTextInput.receiveAction(TextInputAction.search);
await tester.pumpAndSettle();
}
});
});
group('Pagination Tests', () {
testWidgets('Equipment list pagination test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Equipment 화면으로 이동
await navigateToScreen(tester, 'equipment');
// 다음 페이지 버튼 찾기
final nextPageButton = find.byIcon(Icons.arrow_forward);
if (nextPageButton.evaluate().items.isNotEmpty) {
await tester.tap(nextPageButton);
await tester.pumpAndSettle();
}
// 이전 페이지 버튼 찾기
final prevPageButton = find.byIcon(Icons.arrow_back);
if (prevPageButton.evaluate().items.isNotEmpty) {
await tester.tap(prevPageButton);
await tester.pumpAndSettle();
}
// 페이지 번호 직접 선택
final pageNumber = find.text('2');
if (pageNumber.evaluate().items.isNotEmpty) {
await tester.tap(pageNumber);
await tester.pumpAndSettle();
}
});
});
group('Delete Functionality Tests', () {
testWidgets('Equipment delete test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Equipment 화면으로 이동
await navigateToScreen(tester, 'equipment');
// 삭제 버튼 찾기 (보통 각 행에 있음)
final deleteButton = find.byIcon(Icons.delete).items.first;
if (deleteButton.evaluate().items.isNotEmpty) {
await tester.tap(deleteButton);
await tester.pumpAndSettle();
// 확인 다이얼로그 처리
final confirmButton = find.text('삭제');
if (confirmButton.evaluate().items.isNotEmpty) {
await tester.tap(confirmButton);
await tester.pumpAndSettle();
}
}
});
});
group('Edit Functionality Tests', () {
testWidgets('Company edit test', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// Company 화면으로 이동
await navigateToScreen(tester, 'company');
// 수정 버튼 찾기 (보통 각 행에 있음)
final editButton = find.byIcon(Icons.edit).items.first;
if (editButton.evaluate().items.isNotEmpty) {
await tester.tap(editButton);
await tester.pumpAndSettle();
// 수정 폼에서 필드 변경
final nameField = find.byType(TextField).items.first;
if (nameField.evaluate().items.isNotEmpty) {
await tester.enterText(nameField, 'Updated Company Name');
await tester.pumpAndSettle();
}
// 저장 버튼 클릭
final saveButton = find.text('수정 완료');
if (saveButton.evaluate().items.isNotEmpty) {
await tester.tap(saveButton);
await tester.pumpAndSettle();
}
}
});
});
group('Complex User Flow Tests', () {
testWidgets('Complete equipment in-out flow', (tester) async {
await tester.pumpWidget(MaterialApp(home: app.SuperportApp()));
await tester.pumpAndSettle();
// 1. Equipment In 화면으로 이동
await navigateToScreen(tester, 'equipment/in');
// 2. 장비 입고 정보 입력
await enterText(tester, 'manufacturer_field', 'LG');
await enterText(tester, 'name_field', 'Monitor');
await enterText(tester, 'serial_field', 'SN123456');
// 3. 저장
final saveButton = find.text('저장');
if (saveButton.evaluate().items.isNotEmpty) {
await tester.tap(saveButton);
await tester.pumpAndSettle();
}
// 4. Equipment 리스트로 이동
await navigateToScreen(tester, 'equipment');
// 5. 방금 입고한 장비 찾기
final equipmentRow = find.text('SN123456');
// expect(equipmentRow, findsOneWidget);
// 6. 출고 버튼 클릭
final checkoutButton = find.text('출고');
if (checkoutButton.evaluate().items.isNotEmpty) {
await tester.tap(checkoutButton.items.first);
await tester.pumpAndSettle();
}
});
});
});
}
// Helper functions
Future<void> navigateToScreen(WidgetTester tester, String route) async {
// Navigation implementation based on your app's routing
switch (route) {
case 'equipment':
final equipmentNav = find.text('장비관리');
if (equipmentNav.evaluate().items.isNotEmpty) {
await tester.tap(equipmentNav);
await tester.pumpAndSettle();
}
break;
case 'company':
final companyNav = find.text('회사관리');
if (companyNav.evaluate().items.isNotEmpty) {
await tester.tap(companyNav);
await tester.pumpAndSettle();
}
break;
case 'equipment/in':
final equipmentInNav = find.text('장비입고');
if (equipmentInNav.evaluate().items.isNotEmpty) {
await tester.tap(equipmentInNav);
await tester.pumpAndSettle();
}
break;
case 'warehouse/form':
final warehouseNav = find.text('입고지관리');
if (warehouseNav.evaluate().items.isNotEmpty) {
await tester.tap(warehouseNav);
await tester.pumpAndSettle();
}
final addButton = find.byIcon(Icons.add);
if (addButton.evaluate().items.isNotEmpty) {
await tester.tap(addButton);
await tester.pumpAndSettle();
}
break;
case 'company/form':
final companyNav = find.text('회사관리');
if (companyNav.evaluate().items.isNotEmpty) {
await tester.tap(companyNav);
await tester.pumpAndSettle();
}
final addButton = find.text('회사 등록');
if (addButton.evaluate().items.isNotEmpty) {
await tester.tap(addButton);
await tester.pumpAndSettle();
}
break;
}
}
Future<void> enterText(WidgetTester tester, String fieldKey, String text) async {
final field = find.byKey(Key(fieldKey));
if (field.evaluate().items.isEmpty) {
// If not found by key, try by type
final textField = find.byType(TextField);
if (textField.evaluate().items.isNotEmpty) {
await tester.enterText(textField.items.first, text);
await tester.pumpAndSettle();
}
} else {
await tester.enterText(field, text);
await tester.pumpAndSettle();
}
}
Future<void> setupTestDependencies() async {
// 이미 di.setupDependencies()에서 처리됨
// 추가 테스트 환경 설정이 필요한 경우 여기에 작성
}