결재 템플릿 단계 적용 구현
- ApprovalTemplate 엔티티·DTO·원격 리포지토리 추가 - ApprovalController에 템플릿 로딩/적용 상태와 assignSteps 호출 연동 - ApprovalPage 단계 탭에 템플릿 선택 UI 및 적용 확인 다이얼로그 구현 - 템플릿 적용 단위 테스트와 IMPLEMENTATION_TASKS 현황 갱신
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/features/masters/group/domain/entities/group.dart';
|
||||
import 'package:superport_v2/features/masters/group/domain/repositories/group_repository.dart';
|
||||
import 'package:superport_v2/features/masters/group/presentation/controllers/group_controller.dart';
|
||||
|
||||
class _MockGroupRepository extends Mock implements GroupRepository {}
|
||||
|
||||
class _FakeGroupInput extends Fake implements GroupInput {}
|
||||
|
||||
void main() {
|
||||
late GroupController controller;
|
||||
late _MockGroupRepository repository;
|
||||
|
||||
final sampleGroup = Group(
|
||||
id: 1,
|
||||
groupName: '관리자',
|
||||
description: '전체 권한',
|
||||
isDefault: true,
|
||||
isActive: true,
|
||||
isDeleted: false,
|
||||
);
|
||||
|
||||
PaginatedResult<Group> createResult({List<Group>? items}) {
|
||||
final list = items ?? [sampleGroup];
|
||||
return PaginatedResult<Group>(
|
||||
items: list,
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: list.length,
|
||||
);
|
||||
}
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeGroupInput());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
repository = _MockGroupRepository();
|
||||
controller = GroupController(repository: repository);
|
||||
});
|
||||
|
||||
group('fetch', () {
|
||||
test('정상 조회 시 결과가 갱신된다', () async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult());
|
||||
|
||||
await controller.fetch();
|
||||
|
||||
expect(controller.isLoading, isFalse);
|
||||
expect(controller.errorMessage, isNull);
|
||||
expect(controller.result?.items, isNotEmpty);
|
||||
verify(
|
||||
() => repository.list(
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
query: null,
|
||||
isDefault: null,
|
||||
isActive: null,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('에러 발생 시 errorMessage에 저장한다', () async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenThrow(Exception('fail'));
|
||||
|
||||
await controller.fetch();
|
||||
|
||||
expect(controller.isLoading, isFalse);
|
||||
expect(controller.errorMessage, isNotNull);
|
||||
});
|
||||
|
||||
test('필터 상태를 파라미터에 반영한다', () async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult());
|
||||
|
||||
controller.updateQuery('admin');
|
||||
controller.updateDefaultFilter(GroupDefaultFilter.defaultOnly);
|
||||
controller.updateStatusFilter(GroupStatusFilter.inactiveOnly);
|
||||
|
||||
await controller.fetch();
|
||||
|
||||
verify(
|
||||
() => repository.list(
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
query: 'admin',
|
||||
isDefault: true,
|
||||
isActive: false,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
test('필터 업데이트 메서드', () {
|
||||
controller.updateQuery('abc');
|
||||
controller.updateDefaultFilter(GroupDefaultFilter.nonDefault);
|
||||
controller.updateStatusFilter(GroupStatusFilter.activeOnly);
|
||||
|
||||
expect(controller.query, 'abc');
|
||||
expect(controller.defaultFilter, GroupDefaultFilter.nonDefault);
|
||||
expect(controller.statusFilter, GroupStatusFilter.activeOnly);
|
||||
});
|
||||
|
||||
group('mutations', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult());
|
||||
});
|
||||
|
||||
final input = GroupInput(groupName: '관리자');
|
||||
|
||||
test('create 성공 시 목록 재조회', () async {
|
||||
when(() => repository.create(any())).thenAnswer((_) async => sampleGroup);
|
||||
|
||||
final created = await controller.create(input);
|
||||
|
||||
expect(created, isNotNull);
|
||||
verify(() => repository.create(any())).called(1);
|
||||
verify(
|
||||
() => repository.list(
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
query: null,
|
||||
isDefault: null,
|
||||
isActive: null,
|
||||
),
|
||||
).called(greaterThanOrEqualTo(1));
|
||||
});
|
||||
|
||||
test('update 성공 시 현재 페이지 유지', () async {
|
||||
when(
|
||||
() => repository.update(any(), any()),
|
||||
).thenAnswer((_) async => sampleGroup);
|
||||
|
||||
final updated = await controller.update(1, input);
|
||||
|
||||
expect(updated, isNotNull);
|
||||
verify(() => repository.update(1, any())).called(1);
|
||||
});
|
||||
|
||||
test('delete 성공 시 true 반환', () async {
|
||||
when(() => repository.delete(any())).thenAnswer((_) async {});
|
||||
|
||||
final deleted = await controller.delete(1);
|
||||
|
||||
expect(deleted, isTrue);
|
||||
verify(() => repository.delete(1)).called(1);
|
||||
});
|
||||
|
||||
test('restore 성공 시 그룹 반환', () async {
|
||||
when(
|
||||
() => repository.restore(any()),
|
||||
).thenAnswer((_) async => sampleGroup);
|
||||
|
||||
final restored = await controller.restore(1);
|
||||
|
||||
expect(restored, isNotNull);
|
||||
verify(() => repository.restore(1)).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/features/masters/group/domain/entities/group.dart';
|
||||
import 'package:superport_v2/features/masters/group/domain/repositories/group_repository.dart';
|
||||
import 'package:superport_v2/features/masters/group/presentation/pages/group_page.dart';
|
||||
|
||||
class _MockGroupRepository extends Mock implements GroupRepository {}
|
||||
|
||||
class _FakeGroupInput extends Fake implements GroupInput {}
|
||||
|
||||
Widget _buildApp(Widget child) {
|
||||
return MaterialApp(
|
||||
home: ShadTheme(
|
||||
data: ShadThemeData(
|
||||
colorScheme: const ShadSlateColorScheme.light(),
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(body: child),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeGroupInput());
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await GetIt.I.reset();
|
||||
dotenv.clean();
|
||||
});
|
||||
|
||||
testWidgets('플래그 Off 시 스펙 페이지 표시', (tester) async {
|
||||
dotenv.testLoad(fileInput: 'FEATURE_GROUPS_ENABLED=false\n');
|
||||
|
||||
await tester.pumpWidget(_buildApp(const GroupPage()));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('그룹 관리'), findsOneWidget);
|
||||
expect(find.text('비활성화 (백엔드 준비 중)'), findsOneWidget);
|
||||
});
|
||||
|
||||
group('플래그 On', () {
|
||||
late _MockGroupRepository repository;
|
||||
|
||||
setUp(() {
|
||||
dotenv.testLoad(fileInput: 'FEATURE_GROUPS_ENABLED=true\n');
|
||||
repository = _MockGroupRepository();
|
||||
GetIt.I.registerLazySingleton<GroupRepository>(() => repository);
|
||||
});
|
||||
|
||||
testWidgets('목록을 조회하여 표에 렌더링한다', (tester) async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<Group>(
|
||||
items: [
|
||||
Group(
|
||||
id: 1,
|
||||
groupName: '관리자',
|
||||
description: '전체 권한',
|
||||
isDefault: true,
|
||||
),
|
||||
],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(const GroupPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('관리자'), findsOneWidget);
|
||||
verify(
|
||||
() => repository.list(
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
query: null,
|
||||
isDefault: null,
|
||||
isActive: null,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
testWidgets('신규 등록 폼 검증 오류를 노출한다', (tester) async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<Group>(
|
||||
items: const [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(const GroupPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('신규 등록'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('등록'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('그룹명을 입력하세요.'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('신규 등록 성공 시 목록이 갱신된다', (tester) async {
|
||||
var listCallCount = 0;
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer((_) async {
|
||||
listCallCount += 1;
|
||||
if (listCallCount == 1) {
|
||||
return PaginatedResult<Group>(
|
||||
items: const [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
);
|
||||
}
|
||||
return PaginatedResult<Group>(
|
||||
items: [Group(id: 2, groupName: '운영팀', description: '운영 담당')],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
);
|
||||
});
|
||||
|
||||
GroupInput? capturedInput;
|
||||
when(() => repository.create(any())).thenAnswer((invocation) async {
|
||||
capturedInput = invocation.positionalArguments.first as GroupInput;
|
||||
return Group(
|
||||
id: 2,
|
||||
groupName: capturedInput!.groupName,
|
||||
description: capturedInput!.description,
|
||||
);
|
||||
});
|
||||
|
||||
await tester.pumpWidget(_buildApp(const GroupPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('신규 등록'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final dialog = find.byType(Dialog);
|
||||
final editableTexts = find.descendant(
|
||||
of: dialog,
|
||||
matching: find.byType(EditableText),
|
||||
);
|
||||
|
||||
await tester.enterText(editableTexts.at(0), '운영팀');
|
||||
await tester.enterText(editableTexts.at(1), '운영 담당');
|
||||
|
||||
await tester.tap(find.text('등록'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(capturedInput, isNotNull);
|
||||
expect(capturedInput?.groupName, '운영팀');
|
||||
expect(find.byType(Dialog), findsNothing);
|
||||
expect(find.text('운영팀'), findsOneWidget);
|
||||
verify(() => repository.create(any())).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/features/masters/group/domain/entities/group.dart';
|
||||
import 'package:superport_v2/features/masters/group/domain/repositories/group_repository.dart';
|
||||
import 'package:superport_v2/features/masters/group_permission/domain/entities/group_permission.dart';
|
||||
import 'package:superport_v2/features/masters/group_permission/domain/repositories/group_permission_repository.dart';
|
||||
import 'package:superport_v2/features/masters/group_permission/presentation/controllers/group_permission_controller.dart';
|
||||
import 'package:superport_v2/features/masters/menu/domain/entities/menu.dart';
|
||||
import 'package:superport_v2/features/masters/menu/domain/repositories/menu_repository.dart';
|
||||
|
||||
class _MockPermissionRepository extends Mock
|
||||
implements GroupPermissionRepository {}
|
||||
|
||||
class _MockGroupRepository extends Mock implements GroupRepository {}
|
||||
|
||||
class _MockMenuRepository extends Mock implements MenuRepository {}
|
||||
|
||||
class _FakeGroupPermissionInput extends Fake implements GroupPermissionInput {}
|
||||
|
||||
void main() {
|
||||
late GroupPermissionController controller;
|
||||
late _MockPermissionRepository permissionRepository;
|
||||
late _MockGroupRepository groupRepository;
|
||||
late _MockMenuRepository menuRepository;
|
||||
|
||||
final samplePermission = GroupPermission(
|
||||
id: 1,
|
||||
group: GroupPermissionGroup(id: 1, groupName: '관리자'),
|
||||
menu: GroupPermissionMenu(id: 10, menuName: '대시보드'),
|
||||
canCreate: true,
|
||||
canRead: true,
|
||||
canUpdate: false,
|
||||
canDelete: false,
|
||||
);
|
||||
|
||||
PaginatedResult<GroupPermission> createResult(List<GroupPermission> items) {
|
||||
return PaginatedResult<GroupPermission>(
|
||||
items: items,
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: items.length,
|
||||
);
|
||||
}
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeGroupPermissionInput());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
permissionRepository = _MockPermissionRepository();
|
||||
groupRepository = _MockGroupRepository();
|
||||
menuRepository = _MockMenuRepository();
|
||||
controller = GroupPermissionController(
|
||||
permissionRepository: permissionRepository,
|
||||
groupRepository: groupRepository,
|
||||
menuRepository: menuRepository,
|
||||
);
|
||||
});
|
||||
|
||||
group('loadGroups/loadMenus', () {
|
||||
test('그룹 목록을 로드한다', () async {
|
||||
when(
|
||||
() => groupRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<Group>(
|
||||
items: [Group(id: 1, groupName: '관리자')],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
),
|
||||
);
|
||||
|
||||
await controller.loadGroups();
|
||||
|
||||
expect(controller.groups, isNotEmpty);
|
||||
});
|
||||
|
||||
test('메뉴 목록을 로드한다', () async {
|
||||
when(
|
||||
() => menuRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<MenuItem>(
|
||||
items: [MenuItem(id: 10, menuCode: 'MENU001', menuName: '대시보드')],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
),
|
||||
);
|
||||
|
||||
await controller.loadMenus();
|
||||
|
||||
expect(controller.menus, isNotEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group('fetch', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => permissionRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
groupId: any(named: 'groupId'),
|
||||
menuId: any(named: 'menuId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult([samplePermission]));
|
||||
});
|
||||
|
||||
test('정상 조회 시 데이터를 보관한다', () async {
|
||||
await controller.fetch();
|
||||
|
||||
expect(controller.result?.items, isNotEmpty);
|
||||
expect(controller.errorMessage, isNull);
|
||||
});
|
||||
|
||||
test('필터 값을 전달한다', () async {
|
||||
controller.updateGroupFilter(2);
|
||||
controller.updateMenuFilter(5);
|
||||
controller.updateStatusFilter(GroupPermissionStatusFilter.inactiveOnly);
|
||||
controller.updateIncludeDeleted(true);
|
||||
|
||||
await controller.fetch(page: 3);
|
||||
|
||||
verify(
|
||||
() => permissionRepository.list(
|
||||
page: 3,
|
||||
pageSize: 20,
|
||||
groupId: 2,
|
||||
menuId: 5,
|
||||
isActive: false,
|
||||
includeDeleted: true,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('에러 발생 시 errorMessage에 저장', () async {
|
||||
when(
|
||||
() => permissionRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
groupId: any(named: 'groupId'),
|
||||
menuId: any(named: 'menuId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenThrow(Exception('fail'));
|
||||
|
||||
await controller.fetch();
|
||||
|
||||
expect(controller.errorMessage, isNotNull);
|
||||
});
|
||||
});
|
||||
|
||||
test('필터 업데이트 메서드', () {
|
||||
controller.updateGroupFilter(3);
|
||||
controller.updateMenuFilter(7);
|
||||
controller.updateStatusFilter(GroupPermissionStatusFilter.activeOnly);
|
||||
controller.updateIncludeDeleted(true);
|
||||
|
||||
expect(controller.groupFilter, 3);
|
||||
expect(controller.menuFilter, 7);
|
||||
expect(controller.statusFilter, GroupPermissionStatusFilter.activeOnly);
|
||||
expect(controller.includeDeleted, isTrue);
|
||||
});
|
||||
|
||||
group('mutations', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => permissionRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
groupId: any(named: 'groupId'),
|
||||
menuId: any(named: 'menuId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult([samplePermission]));
|
||||
});
|
||||
|
||||
final input = GroupPermissionInput(groupId: 1, menuId: 2);
|
||||
|
||||
test('create 성공', () async {
|
||||
when(
|
||||
() => permissionRepository.create(any()),
|
||||
).thenAnswer((_) async => samplePermission);
|
||||
|
||||
final created = await controller.create(input);
|
||||
|
||||
expect(created, isNotNull);
|
||||
verify(() => permissionRepository.create(any())).called(1);
|
||||
});
|
||||
|
||||
test('update 성공', () async {
|
||||
when(
|
||||
() => permissionRepository.update(any(), any()),
|
||||
).thenAnswer((_) async => samplePermission);
|
||||
|
||||
final updated = await controller.update(1, input);
|
||||
|
||||
expect(updated, isNotNull);
|
||||
verify(() => permissionRepository.update(1, any())).called(1);
|
||||
});
|
||||
|
||||
test('delete 성공', () async {
|
||||
when(() => permissionRepository.delete(any())).thenAnswer((_) async {});
|
||||
|
||||
final result = await controller.delete(1);
|
||||
|
||||
expect(result, isTrue);
|
||||
verify(() => permissionRepository.delete(1)).called(1);
|
||||
});
|
||||
|
||||
test('restore 성공', () async {
|
||||
when(
|
||||
() => permissionRepository.restore(any()),
|
||||
).thenAnswer((_) async => samplePermission);
|
||||
|
||||
final restored = await controller.restore(1);
|
||||
|
||||
expect(restored, isNotNull);
|
||||
verify(() => permissionRepository.restore(1)).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/features/masters/group/domain/entities/group.dart';
|
||||
import 'package:superport_v2/features/masters/group/domain/repositories/group_repository.dart';
|
||||
import 'package:superport_v2/features/masters/group_permission/domain/entities/group_permission.dart';
|
||||
import 'package:superport_v2/features/masters/group_permission/domain/repositories/group_permission_repository.dart';
|
||||
import 'package:superport_v2/features/masters/group_permission/presentation/pages/group_permission_page.dart';
|
||||
import 'package:superport_v2/features/masters/menu/domain/entities/menu.dart';
|
||||
import 'package:superport_v2/features/masters/menu/domain/repositories/menu_repository.dart';
|
||||
|
||||
class _MockPermissionRepository extends Mock
|
||||
implements GroupPermissionRepository {}
|
||||
|
||||
class _MockGroupRepository extends Mock implements GroupRepository {}
|
||||
|
||||
class _MockMenuRepository extends Mock implements MenuRepository {}
|
||||
|
||||
class _FakeGroupPermissionInput extends Fake implements GroupPermissionInput {}
|
||||
|
||||
Widget _buildApp(Widget child) {
|
||||
return MaterialApp(
|
||||
home: ShadTheme(
|
||||
data: ShadThemeData(
|
||||
colorScheme: const ShadSlateColorScheme.light(),
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(body: child),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeGroupPermissionInput());
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await GetIt.I.reset();
|
||||
dotenv.clean();
|
||||
});
|
||||
|
||||
testWidgets('플래그 Off 시 스펙 화면 표시', (tester) async {
|
||||
dotenv.testLoad(fileInput: 'FEATURE_GROUP_PERMISSIONS_ENABLED=false\n');
|
||||
|
||||
await tester.pumpWidget(_buildApp(const GroupPermissionPage()));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('그룹 권한 관리'), findsOneWidget);
|
||||
expect(find.text('비활성화 (백엔드 준비 중)'), findsOneWidget);
|
||||
});
|
||||
|
||||
group('플래그 On', () {
|
||||
late _MockPermissionRepository permissionRepository;
|
||||
late _MockGroupRepository groupRepository;
|
||||
late _MockMenuRepository menuRepository;
|
||||
|
||||
setUp(() {
|
||||
dotenv.testLoad(fileInput: 'FEATURE_GROUP_PERMISSIONS_ENABLED=true\n');
|
||||
permissionRepository = _MockPermissionRepository();
|
||||
groupRepository = _MockGroupRepository();
|
||||
menuRepository = _MockMenuRepository();
|
||||
GetIt.I.registerLazySingleton<GroupPermissionRepository>(
|
||||
() => permissionRepository,
|
||||
);
|
||||
GetIt.I.registerLazySingleton<GroupRepository>(() => groupRepository);
|
||||
GetIt.I.registerLazySingleton<MenuRepository>(() => menuRepository);
|
||||
|
||||
when(
|
||||
() => groupRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<Group>(
|
||||
items: [Group(id: 1, groupName: '관리자')],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
),
|
||||
);
|
||||
|
||||
when(
|
||||
() => menuRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<MenuItem>(
|
||||
items: [MenuItem(id: 10, menuCode: 'MENU001', menuName: '대시보드')],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('목록을 조회해 테이블에 렌더링한다', (tester) async {
|
||||
when(
|
||||
() => permissionRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
groupId: any(named: 'groupId'),
|
||||
menuId: any(named: 'menuId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<GroupPermission>(
|
||||
items: [
|
||||
GroupPermission(
|
||||
id: 1,
|
||||
group: GroupPermissionGroup(id: 1, groupName: '관리자'),
|
||||
menu: GroupPermissionMenu(id: 10, menuName: '대시보드'),
|
||||
canCreate: true,
|
||||
canRead: true,
|
||||
),
|
||||
],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(const GroupPermissionPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('대시보드'), findsOneWidget);
|
||||
expect(find.text('관리자'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('신규 등록 폼 검증 메시지를 표시한다', (tester) async {
|
||||
when(
|
||||
() => permissionRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
groupId: any(named: 'groupId'),
|
||||
menuId: any(named: 'menuId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<GroupPermission>(
|
||||
items: const [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(const GroupPermissionPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('신규 등록'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('등록'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('그룹을 선택하세요.'), findsOneWidget);
|
||||
expect(find.text('메뉴를 선택하세요.'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('신규 등록 성공 시 repository.create 호출', (tester) async {
|
||||
var listCall = 0;
|
||||
when(
|
||||
() => permissionRepository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
groupId: any(named: 'groupId'),
|
||||
menuId: any(named: 'menuId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer((_) async {
|
||||
listCall += 1;
|
||||
if (listCall == 1) {
|
||||
return PaginatedResult<GroupPermission>(
|
||||
items: const [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
);
|
||||
}
|
||||
return PaginatedResult<GroupPermission>(
|
||||
items: [
|
||||
GroupPermission(
|
||||
id: 5,
|
||||
group: GroupPermissionGroup(id: 1, groupName: '관리자'),
|
||||
menu: GroupPermissionMenu(id: 10, menuName: '대시보드'),
|
||||
canCreate: true,
|
||||
canRead: true,
|
||||
),
|
||||
],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
);
|
||||
});
|
||||
|
||||
GroupPermissionInput? capturedInput;
|
||||
when(() => permissionRepository.create(any())).thenAnswer((
|
||||
invocation,
|
||||
) async {
|
||||
capturedInput =
|
||||
invocation.positionalArguments.first as GroupPermissionInput;
|
||||
return GroupPermission(
|
||||
id: 5,
|
||||
group: GroupPermissionGroup(
|
||||
id: capturedInput!.groupId,
|
||||
groupName: '관리자',
|
||||
),
|
||||
menu: GroupPermissionMenu(
|
||||
id: capturedInput!.menuId,
|
||||
menuName: '대시보드',
|
||||
),
|
||||
canCreate: capturedInput!.canCreate,
|
||||
canRead: capturedInput!.canRead,
|
||||
);
|
||||
});
|
||||
|
||||
await tester.pumpWidget(_buildApp(const GroupPermissionPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('신규 등록'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final dialog = find.byType(Dialog);
|
||||
final selects = find.descendant(
|
||||
of: dialog,
|
||||
matching: find.byType(ShadSelect<int?>),
|
||||
);
|
||||
|
||||
// 그룹 선택
|
||||
await tester.tap(selects.at(0));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('관리자').last);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// 메뉴 선택
|
||||
await tester.tap(selects.at(1));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('대시보드').last);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// 권한 체크 (생성, 수정, 삭제 on)
|
||||
final switches = find.descendant(
|
||||
of: dialog,
|
||||
matching: find.byType(ShadSwitch),
|
||||
);
|
||||
await tester.tap(switches.at(0));
|
||||
await tester.pump();
|
||||
await tester.tap(switches.at(2));
|
||||
await tester.pump();
|
||||
await tester.tap(switches.at(3));
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('등록'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(capturedInput, isNotNull);
|
||||
expect(capturedInput?.groupId, 1);
|
||||
expect(capturedInput?.menuId, 10);
|
||||
expect(capturedInput?.canCreate, isTrue);
|
||||
expect(capturedInput?.canUpdate, isTrue);
|
||||
expect(find.byType(Dialog), findsNothing);
|
||||
expect(find.text('대시보드'), findsOneWidget);
|
||||
verify(() => permissionRepository.create(any())).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/features/masters/menu/domain/entities/menu.dart';
|
||||
import 'package:superport_v2/features/masters/menu/domain/repositories/menu_repository.dart';
|
||||
import 'package:superport_v2/features/masters/menu/presentation/controllers/menu_controller.dart';
|
||||
|
||||
class _MockMenuRepository extends Mock implements MenuRepository {}
|
||||
|
||||
class _FakeMenuInput extends Fake implements MenuInput {}
|
||||
|
||||
void main() {
|
||||
late MenuController controller;
|
||||
late _MockMenuRepository repository;
|
||||
|
||||
final sampleMenu = MenuItem(
|
||||
id: 1,
|
||||
menuCode: 'MENU001',
|
||||
menuName: '대시보드',
|
||||
isActive: true,
|
||||
isDeleted: false,
|
||||
);
|
||||
|
||||
PaginatedResult<MenuItem> createResult({List<MenuItem>? items}) {
|
||||
final list = items ?? [sampleMenu];
|
||||
return PaginatedResult<MenuItem>(
|
||||
items: list,
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: list.length,
|
||||
);
|
||||
}
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeMenuInput());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
repository = _MockMenuRepository();
|
||||
controller = MenuController(repository: repository);
|
||||
});
|
||||
|
||||
group('loadParents', () {
|
||||
test('상위 메뉴를 로드한다', () async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult());
|
||||
|
||||
await controller.loadParents();
|
||||
|
||||
expect(controller.parents, isNotEmpty);
|
||||
verify(
|
||||
() => repository.list(
|
||||
page: 1,
|
||||
pageSize: 200,
|
||||
query: null,
|
||||
parentId: null,
|
||||
isActive: null,
|
||||
includeDeleted: false,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('fetch', () {
|
||||
test('정상 조회 시 결과 저장', () async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult());
|
||||
|
||||
await controller.fetch();
|
||||
|
||||
expect(controller.result?.items, isNotEmpty);
|
||||
expect(controller.errorMessage, isNull);
|
||||
});
|
||||
|
||||
test('필터 조건 적용', () async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult());
|
||||
|
||||
controller.updateQuery('dash');
|
||||
controller.updateParentFilter(10);
|
||||
controller.updateStatusFilter(MenuStatusFilter.activeOnly);
|
||||
controller.updateIncludeDeleted(true);
|
||||
|
||||
await controller.fetch(page: 2);
|
||||
|
||||
verify(
|
||||
() => repository.list(
|
||||
page: 2,
|
||||
pageSize: 20,
|
||||
query: 'dash',
|
||||
parentId: 10,
|
||||
isActive: true,
|
||||
includeDeleted: true,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('에러 시 errorMessage 세팅', () async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenThrow(Exception('fail'));
|
||||
|
||||
await controller.fetch();
|
||||
|
||||
expect(controller.errorMessage, isNotNull);
|
||||
});
|
||||
});
|
||||
|
||||
test('필터 업데이트', () {
|
||||
controller.updateQuery('code');
|
||||
controller.updateParentFilter(1);
|
||||
controller.updateStatusFilter(MenuStatusFilter.inactiveOnly);
|
||||
controller.updateIncludeDeleted(true);
|
||||
|
||||
expect(controller.query, 'code');
|
||||
expect(controller.parentFilter, 1);
|
||||
expect(controller.statusFilter, MenuStatusFilter.inactiveOnly);
|
||||
expect(controller.includeDeleted, isTrue);
|
||||
});
|
||||
|
||||
group('mutations', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer((_) async => createResult());
|
||||
});
|
||||
|
||||
final input = MenuInput(menuCode: 'MENU001', menuName: '대시보드');
|
||||
|
||||
test('create 성공', () async {
|
||||
when(() => repository.create(any())).thenAnswer((_) async => sampleMenu);
|
||||
|
||||
final created = await controller.create(input);
|
||||
|
||||
expect(created, isNotNull);
|
||||
verify(() => repository.create(any())).called(1);
|
||||
});
|
||||
|
||||
test('update 성공', () async {
|
||||
when(
|
||||
() => repository.update(any(), any()),
|
||||
).thenAnswer((_) async => sampleMenu);
|
||||
|
||||
final updated = await controller.update(1, input);
|
||||
|
||||
expect(updated, isNotNull);
|
||||
verify(() => repository.update(1, any())).called(1);
|
||||
});
|
||||
|
||||
test('delete 성공', () async {
|
||||
when(() => repository.delete(any())).thenAnswer((_) async {});
|
||||
|
||||
final success = await controller.delete(1);
|
||||
|
||||
expect(success, isTrue);
|
||||
verify(() => repository.delete(1)).called(1);
|
||||
});
|
||||
|
||||
test('restore 성공', () async {
|
||||
when(() => repository.restore(any())).thenAnswer((_) async => sampleMenu);
|
||||
|
||||
final restored = await controller.restore(1);
|
||||
|
||||
expect(restored, isNotNull);
|
||||
verify(() => repository.restore(1)).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/features/masters/menu/domain/entities/menu.dart';
|
||||
import 'package:superport_v2/features/masters/menu/domain/repositories/menu_repository.dart';
|
||||
import 'package:superport_v2/features/masters/menu/presentation/pages/menu_page.dart';
|
||||
|
||||
class _MockMenuRepository extends Mock implements MenuRepository {}
|
||||
|
||||
class _FakeMenuInput extends Fake implements MenuInput {}
|
||||
|
||||
Widget _buildApp(Widget child) {
|
||||
return MaterialApp(
|
||||
home: ShadTheme(
|
||||
data: ShadThemeData(
|
||||
colorScheme: const ShadSlateColorScheme.light(),
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(body: child),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeMenuInput());
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await GetIt.I.reset();
|
||||
dotenv.clean();
|
||||
});
|
||||
|
||||
testWidgets('플래그 Off 시 스펙 화면 노출', (tester) async {
|
||||
dotenv.testLoad(fileInput: 'FEATURE_MENUS_ENABLED=false\n');
|
||||
|
||||
await tester.pumpWidget(_buildApp(const MenuPage()));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('메뉴 관리'), findsOneWidget);
|
||||
expect(find.text('비활성화 (백엔드 준비 중)'), findsOneWidget);
|
||||
});
|
||||
|
||||
group('플래그 On', () {
|
||||
late _MockMenuRepository repository;
|
||||
|
||||
setUp(() {
|
||||
dotenv.testLoad(fileInput: 'FEATURE_MENUS_ENABLED=true\n');
|
||||
repository = _MockMenuRepository();
|
||||
GetIt.I.registerLazySingleton<MenuRepository>(() => repository);
|
||||
});
|
||||
|
||||
testWidgets('목록을 조회해 테이블을 렌더링한다', (tester) async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<MenuItem>(
|
||||
items: [MenuItem(id: 1, menuCode: 'MENU001', menuName: '대시보드')],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(const MenuPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('MENU001'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('신규 등록 폼 검증 에러 표시', (tester) async {
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => PaginatedResult<MenuItem>(
|
||||
items: const [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(_buildApp(const MenuPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('신규 등록'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('등록'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('메뉴코드를 입력하세요.'), findsOneWidget);
|
||||
expect(find.text('메뉴명을 입력하세요.'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('신규 등록 성공 시 repository.create 호출', (tester) async {
|
||||
var listCall = 0;
|
||||
when(
|
||||
() => repository.list(
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
parentId: any(named: 'parentId'),
|
||||
isActive: any(named: 'isActive'),
|
||||
includeDeleted: any(named: 'includeDeleted'),
|
||||
),
|
||||
).thenAnswer((_) async {
|
||||
listCall += 1;
|
||||
if (listCall == 1) {
|
||||
return PaginatedResult<MenuItem>(
|
||||
items: const [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
);
|
||||
}
|
||||
return PaginatedResult<MenuItem>(
|
||||
items: [MenuItem(id: 10, menuCode: 'MENU010', menuName: '신규 메뉴')],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 1,
|
||||
);
|
||||
});
|
||||
|
||||
MenuInput? capturedInput;
|
||||
when(() => repository.create(any())).thenAnswer((invocation) async {
|
||||
capturedInput = invocation.positionalArguments.first as MenuInput;
|
||||
return MenuItem(
|
||||
id: 10,
|
||||
menuCode: capturedInput!.menuCode,
|
||||
menuName: capturedInput!.menuName,
|
||||
);
|
||||
});
|
||||
|
||||
await tester.pumpWidget(_buildApp(const MenuPage()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('신규 등록'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final dialog = find.byType(Dialog);
|
||||
final editableTexts = find.descendant(
|
||||
of: dialog,
|
||||
matching: find.byType(EditableText),
|
||||
);
|
||||
|
||||
await tester.enterText(editableTexts.at(0), 'MENU010');
|
||||
await tester.enterText(editableTexts.at(1), '신규 메뉴');
|
||||
|
||||
await tester.tap(find.text('등록'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(capturedInput, isNotNull);
|
||||
expect(capturedInput?.menuCode, 'MENU010');
|
||||
expect(find.byType(Dialog), findsNothing);
|
||||
expect(find.text('MENU010'), findsOneWidget);
|
||||
verify(() => repository.create(any())).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -56,6 +56,7 @@ void main() {
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer(
|
||||
@@ -79,6 +80,7 @@ void main() {
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer(
|
||||
|
||||
@@ -68,6 +68,7 @@ void main() {
|
||||
page: any(named: 'page'),
|
||||
pageSize: any(named: 'pageSize'),
|
||||
query: any(named: 'query'),
|
||||
isDefault: any(named: 'isDefault'),
|
||||
isActive: any(named: 'isActive'),
|
||||
),
|
||||
).thenAnswer(
|
||||
|
||||
Reference in New Issue
Block a user