feat: 재고 상태 전이 플래그 적용 및 실패 메시지 정비

This commit is contained in:
JiWoong Sul
2025-10-14 18:06:40 +09:00
parent 9f61b305d4
commit c072eb1328
9 changed files with 5069 additions and 1287 deletions

View File

@@ -3,11 +3,19 @@ import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/config/environment.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/network/api_error.dart';
import 'package:superport_v2/features/masters/warehouse/domain/entities/warehouse.dart';
import 'package:superport_v2/features/masters/warehouse/domain/repositories/warehouse_repository.dart';
import 'package:superport_v2/features/inventory/lookups/domain/entities/lookup_item.dart';
import 'package:superport_v2/features/inventory/lookups/domain/repositories/inventory_lookup_repository.dart';
import 'package:superport_v2/features/reporting/domain/entities/report_download_result.dart';
import 'package:superport_v2/features/reporting/domain/entities/report_export_format.dart';
import 'package:superport_v2/features/reporting/domain/entities/report_export_request.dart';
import 'package:superport_v2/features/reporting/domain/repositories/reporting_repository.dart';
import 'package:superport_v2/features/reporting/presentation/pages/reporting_page.dart';
import 'package:superport_v2/widgets/components/empty_state.dart';
import '../../helpers/test_app.dart';
@@ -25,6 +33,10 @@ void main() {
testWidgets('보고서 화면은 창고 목록 재시도 흐름을 제공한다', (tester) async {
final repo = _FlakyWarehouseRepository();
GetIt.I.registerSingleton<WarehouseRepository>(repo);
GetIt.I.registerSingleton<InventoryLookupRepository>(
_StubLookupRepository(),
);
GetIt.I.registerSingleton<ReportingRepository>(_FakeReportingRepository());
final view = tester.view;
view.physicalSize = const Size(1280, 800);
@@ -38,13 +50,16 @@ void main() {
await tester.pumpAndSettle();
expect(repo.attempts, 1);
expect(find.text('창고 목록을 불러오지 못했습니다. 잠시 후 다시 시도하세요.'), findsOneWidget);
expect(
find.text('창고 목록을 불러오지 못했습니다. 잠시 후 다시 시도하세요.'),
findsWidgets,
);
await tester.tap(find.widgetWithText(ShadButton, '재시도'));
await tester.pumpAndSettle();
await tester.pump(const Duration(seconds: 4));
expect(repo.attempts, 2);
expect(find.text('창고 목록을 불러오지 못했습니다. 잠시 후 다시 시도하세요.'), findsNothing);
});
}
@@ -57,10 +72,14 @@ class _FlakyWarehouseRepository implements WarehouseRepository {
int pageSize = 20,
String? query,
bool? isActive,
bool includeZipcode = true,
}) async {
attempts += 1;
if (attempts == 1) {
throw Exception('network down');
throw const ApiException(
code: ApiErrorCode.network,
message: '창고 목록을 불러오지 못했습니다. 잠시 후 다시 시도하세요.',
);
}
return PaginatedResult<Warehouse>(
items: [
@@ -98,3 +117,77 @@ class _FlakyWarehouseRepository implements WarehouseRepository {
throw UnimplementedError();
}
}
class _StubLookupRepository implements InventoryLookupRepository {
@override
Future<List<LookupItem>> fetchTransactionTypes({
bool activeOnly = true,
}) async {
return [
LookupItem(id: 1, name: '입고'),
LookupItem(id: 2, name: '출고'),
LookupItem(id: 3, name: '대여'),
];
}
@override
Future<List<LookupItem>> fetchTransactionStatuses({
bool activeOnly = true,
}) async {
return [
LookupItem(id: 11, name: '작성중'),
LookupItem(id: 12, name: '완료'),
LookupItem(id: 13, name: '취소'),
];
}
@override
Future<List<LookupItem>> fetchApprovalStatuses({
bool activeOnly = true,
}) async {
return [
LookupItem(id: 21, name: '진행중'),
LookupItem(id: 22, name: '완료'),
LookupItem(id: 23, name: '취소'),
];
}
@override
Future<List<LookupItem>> fetchApprovalActions({
bool activeOnly = true,
}) async {
return const [];
}
}
class _FakeReportingRepository implements ReportingRepository {
ReportExportRequest? lastRequest;
ReportExportFormat? lastFormat;
@override
Future<ReportDownloadResult> exportApprovals(
ReportExportRequest request,
) async {
lastRequest = request;
lastFormat = request.format;
return ReportDownloadResult(
downloadUrl: Uri.parse('https://example.com/approvals.pdf'),
filename: 'approvals.pdf',
mimeType: 'application/pdf',
);
}
@override
Future<ReportDownloadResult> exportTransactions(
ReportExportRequest request,
) async {
lastRequest = request;
lastFormat = request.format;
return ReportDownloadResult(
downloadUrl: Uri.parse('https://example.com/transactions.xlsx'),
filename: 'transactions.xlsx',
mimeType:
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
}
}