import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:superport/services/equipment_service.dart'; import 'package:superport/models/equipment_unified_model.dart'; import 'base_screen_test.dart'; import '../../framework/models/test_models.dart'; /// BaseScreenTest를 상속받아 구현하는 예제 /// /// 이 예제는 Equipment 화면 테스트를 구현하는 방법을 보여줍니다. /// 다른 화면들도 이와 유사한 방식으로 구현할 수 있습니다. class ExampleEquipmentScreenTest extends BaseScreenTest { late EquipmentService equipmentService; ExampleEquipmentScreenTest({ required super.apiClient, required super.getIt, required super.testContext, required super.errorDiagnostics, required super.autoFixer, required super.dataGenerator, required super.reportCollector, }); // ===== 필수 구현 메서드들 ===== @override ScreenMetadata getScreenMetadata() { return ScreenMetadata( screenName: 'EquipmentListScreen', controllerType: EquipmentService, relatedEndpoints: [ ApiEndpoint( path: '/api/v1/equipment', method: 'GET', description: '장비 목록 조회', ), ApiEndpoint( path: '/api/v1/equipment', method: 'POST', description: '장비 생성', ), ApiEndpoint( path: '/api/v1/equipment/{id}', method: 'PUT', description: '장비 수정', ), ApiEndpoint( path: '/api/v1/equipment/{id}', method: 'DELETE', description: '장비 삭제', ), ], screenCapabilities: { 'crud': { 'create': true, 'read': true, 'update': true, 'delete': true, }, 'search': { 'enabled': true, 'fields': ['name', 'serialNumber', 'manufacturer'], }, 'filter': { 'enabled': true, 'fields': ['status', 'category', 'location'], }, 'pagination': { 'enabled': true, 'defaultPerPage': 20, }, }, ); } @override Future initializeServices() async { equipmentService = getIt(); } @override dynamic getService() => equipmentService; @override String getResourceType() => 'equipment'; @override Map getDefaultFilters() { return { 'status': 'active', 'category': 'all', }; } // ===== CRUD 작업 구현 ===== @override Future performCreateOperation(TestData data) async { // TestData에서 Equipment 객체로 변환 final equipmentData = data.data; final equipment = Equipment( manufacturer: equipmentData['manufacturer'] ?? 'Unknown', name: equipmentData['name'] ?? 'Test Equipment', category: equipmentData['category'] ?? '미분류', subCategory: equipmentData['subCategory'] ?? '', subSubCategory: equipmentData['subSubCategory'] ?? '', serialNumber: equipmentData['serialNumber'] ?? 'SN-${DateTime.now().millisecondsSinceEpoch}', quantity: equipmentData['quantity'] ?? 1, inDate: equipmentData['inDate'] ?? DateTime.now().toIso8601String(), remark: equipmentData['remark'], ); return await equipmentService.createEquipment(equipment); } @override Future performReadOperation(TestData data) async { // 페이지네이션 파라미터 사용 final page = data.data['page'] ?? 1; final perPage = data.data['perPage'] ?? 20; return await equipmentService.getEquipments( page: page, perPage: perPage, ); } @override Future performUpdateOperation(dynamic resourceId, Map updateData) async { // 기존 장비 조회 final existing = await equipmentService.getEquipment(resourceId); // 업데이트할 Equipment 객체 생성 final updated = Equipment( id: existing.id, manufacturer: updateData['manufacturer'] ?? existing.manufacturer, name: updateData['name'] ?? existing.name, category: updateData['category'] ?? existing.category, subCategory: updateData['subCategory'] ?? existing.subCategory, subSubCategory: updateData['subSubCategory'] ?? existing.subSubCategory, serialNumber: updateData['serialNumber'] ?? existing.serialNumber, quantity: updateData['quantity'] ?? existing.quantity, inDate: updateData['inDate'] ?? existing.inDate, remark: updateData['remark'] ?? existing.remark, ); return await equipmentService.updateEquipment(resourceId, updated); } @override Future performDeleteOperation(dynamic resourceId) async { await equipmentService.deleteEquipment(resourceId); } @override dynamic extractResourceId(dynamic resource) { if (resource is Equipment) { return resource.id; } return resource['id'] ?? resource.id; } // ===== 선택적 구현 메서드들 (필요시 오버라이드) ===== @override Future validateDataBeforeCreate(TestData data) async { // 장비 생성 전 데이터 검증 final equipmentData = data.data; // 필수 필드 검증 if (equipmentData['manufacturer'] == null || equipmentData['manufacturer'].isEmpty) { throw ValidationError('제조사는 필수 입력 항목입니다'); } if (equipmentData['name'] == null || equipmentData['name'].isEmpty) { throw ValidationError('장비명은 필수 입력 항목입니다'); } // 시리얼 번호 형식 검증 final serialNumber = equipmentData['serialNumber']; if (serialNumber != null && !RegExp(r'^[A-Z0-9\-]+$').hasMatch(serialNumber)) { throw ValidationError('시리얼 번호는 영문 대문자, 숫자, 하이픈만 사용 가능합니다'); } } @override Future> prepareUpdateData(TestData data, dynamic resourceId) async { // 기본 구현에 추가로 장비별 특수 로직 적용 final updateData = await super.prepareUpdateData(data, resourceId); // 장비 상태 업데이트 시 이력 추가 if (updateData.containsKey('status')) { updateData['statusChangeReason'] = '테스트 상태 변경'; updateData['statusChangedAt'] = DateTime.now().toIso8601String(); } return updateData; } @override Future performAdditionalSetup() async { // 장비 테스트를 위한 추가 설정 _log('장비 테스트용 카테고리 마스터 데이터 확인'); // 필요한 경우 카테고리 마스터 데이터 생성 // await _ensureCategoryMasterData(); } @override Future performAdditionalCleanup() async { // 장비 테스트 후 추가 정리 _log('장비 관련 임시 파일 정리'); // 테스트 중 생성된 임시 파일이나 캐시 정리 // await _cleanupTempFiles(); } // ===== 커스텀 기능 테스트 ===== @override Future> detectCustomFeatures(ScreenMetadata metadata) async { final features = []; // 장비 입출고 기능 테스트 features.add(TestableFeature( featureName: 'Equipment In/Out', type: FeatureType.custom, testCases: [ TestCase( name: 'Equipment check-in', execute: (data) async { await performEquipmentCheckIn(data); }, verify: (data) async { await verifyEquipmentCheckIn(data); }, ), TestCase( name: 'Equipment check-out', execute: (data) async { await performEquipmentCheckOut(data); }, verify: (data) async { await verifyEquipmentCheckOut(data); }, ), ], metadata: { 'description': '장비 입출고 프로세스 테스트', }, )); // 장비 이력 조회 기능 테스트 features.add(TestableFeature( featureName: 'Equipment History', type: FeatureType.custom, testCases: [ TestCase( name: 'View equipment history', execute: (data) async { await performViewHistory(data); }, verify: (data) async { await verifyViewHistory(data); }, ), ], metadata: { 'description': '장비 이력 조회 테스트', }, )); return features; } // 장비 입고 테스트 Future performEquipmentCheckIn(TestData data) async { // 먼저 장비 생성 await performCreate(data); final equipmentId = testContext.getData('lastCreatedId'); // 입고 처리 final checkInResult = await equipmentService.equipmentIn( equipmentId: equipmentId, quantity: 1, warehouseLocationId: testContext.getData('testWarehouseId') ?? 1, notes: '테스트 입고', ); testContext.setData('checkInResult', checkInResult); } Future verifyEquipmentCheckIn(TestData data) async { final checkInResult = testContext.getData('checkInResult'); expect(checkInResult, isNotNull, reason: '장비 입고 실패'); expect(checkInResult.success, isTrue, reason: '입고 처리가 성공하지 못했습니다'); } // 장비 출고 테스트 Future performEquipmentCheckOut(TestData data) async { // 입고된 장비가 있는지 확인 final equipmentId = testContext.getData('lastCreatedId'); if (equipmentId == null) { await performEquipmentCheckIn(data); } // 출고 처리 final checkOutResult = await equipmentService.equipmentOut( equipmentId: equipmentId, quantity: 1, companyId: testContext.getData('testCompanyId') ?? 1, notes: '테스트 출고', ); testContext.setData('checkOutResult', checkOutResult); } Future verifyEquipmentCheckOut(TestData data) async { final checkOutResult = testContext.getData('checkOutResult'); expect(checkOutResult, isNotNull, reason: '장비 출고 실패'); expect(checkOutResult.success, isTrue, reason: '출고 처리가 성공하지 못했습니다'); } // 장비 이력 조회 테스트 Future performViewHistory(TestData data) async { final equipmentId = testContext.getData('lastCreatedId'); if (equipmentId == null) { await performCreate(data); } final history = await equipmentService.getEquipmentHistory(equipmentId); testContext.setData('equipmentHistory', history); } Future verifyViewHistory(TestData data) async { final history = testContext.getData('equipmentHistory'); expect(history, isNotNull, reason: '장비 이력 조회 실패'); expect(history, isA(), reason: '이력이 리스트 형식이 아닙니다'); } // 로깅을 위한 헬퍼 메서드 void _log(String message) { debugPrint('[ExampleEquipmentScreenTest] $message'); } } /// 검증 오류 class ValidationError implements Exception { final String message; ValidationError(this.message); @override String toString() => 'ValidationError: $message'; } // 테스트 실행 예제 void main() { group('Example Equipment Screen Test', () { test('BaseScreenTest를 상속받아 구현하는 방법 예제', () { // 이것은 예제 구현입니다. // 실제 테스트는 프레임워크를 통해 실행됩니다. expect(true, isTrue); }); }); }