// 모니터링 모듈 (lib/ui/monitor/) 의 약속 시험. 실제 AppController (settingsStore 없음 - 사용자 설정 저장소를 쓰지 않음) · // 실제 시험 폴더 · 실제 위젯으로 확인한다. import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:jj_mkvmaker/app/app_controller.dart'; import 'package:jj_mkvmaker/app/copy_center.dart'; import 'package:jj_mkvmaker/app/settings.dart'; import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart'; import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/monitor/copy_task_card.dart'; import 'package:jj_mkvmaker/ui/monitor/live_pair_card.dart'; import 'package:jj_mkvmaker/ui/monitor/live_sync_flow.dart'; import 'package:jj_mkvmaker/ui/monitor/transfer_widgets.dart'; import 'package:path/path.dart' as p; AppController _controller() => AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); void main() { late Directory tmp; setUp(() => tmp = Directory.systemTemp.createTempSync('jj_monmod_')); tearDown(() => tmp.deleteSync(recursive: true)); test('transfer_widgets: formatRunStamp 는 날짜면 "M/D HH:MM", 아니면 빈 글', () { expect(formatRunStamp('2026-10-22T09:05:00'), '10/22 09:05'); expect(formatRunStamp('x'), ''); expect(formatRunStamp(''), ''); }); testWidgets('transfer_widgets: PruneChoice 는 고른 값을 돌려주고, allowNone 일 때만 "그대로" 칩', (tester) async { String? got; await tester.pumpWidget(MaterialApp(home: Scaffold(body: PruneChoice(value: 'keep', onChanged: (v) => got = v)))); expect(find.byType(ChoiceChip), findsNWidgets(2)); await tester.tap(find.text(pruneLabel('all'))); expect(got, 'all'); await tester.pumpWidget(MaterialApp(home: Scaffold(body: PruneChoice(value: '', allowNone: true, onChanged: (v) => got = v)))); expect(find.byType(ChoiceChip), findsNWidgets(3)); await tester.tap(find.text(pruneLabel(''))); expect(got, ''); }); testWidgets('transfer_widgets: DiskSpaceInfo 는 없는 경로면 있는 위 폴더의 디스크를 잰다', (tester) async { await tester.pumpWidget(MaterialApp(home: Scaffold(body: DiskSpaceInfo(path: p.join(tmp.path, 'none', 'deeper'))))); await tester.runAsync(() => Future.delayed(const Duration(seconds: 2))); await tester.pump(); expect(find.textContaining('대상 디스크: 남은'), findsOneWidget); }); test('live_sync_flow: replaceLiveSyncPair 는 같은 쌍만 바꾸고 순서는 그대로', () async { final c = _controller(); final a = LiveSyncPair(p.join(tmp.path, 'a'), p.join(tmp.path, 'A')); final b = LiveSyncPair(p.join(tmp.path, 'b'), p.join(tmp.path, 'B')); final d = LiveSyncPair(p.join(tmp.path, 'd'), p.join(tmp.path, 'D')); c.settings.copy.liveSyncPairs = [a, b, d]; await replaceLiveSyncPair(c, b, b.copyWith(enabled: false)); expect(c.settings.copy.liveSyncPairs.map((x) => x.source), [a.source, b.source, d.source]); expect(c.settings.copy.liveSyncPairs.map((x) => x.enabled), [true, false, true]); }); testWidgets('copy_task_card: 작업이 없으면 안내, 있으면 작업마다 카드', (tester) async { final c = _controller(); tester.view.physicalSize = const Size(1500, 1000); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); await tester.pumpWidget(MaterialApp(home: Scaffold(body: CopyTaskList(c: c)))); expect(find.textContaining('실행한 rsync 가 없습니다'), findsOneWidget); await CopyCenter.of(c).remember([tmp.path], p.join(tmp.path, '..'), method: 'builtin'); await tester.pump(); expect(find.byType(CopyTaskCard), findsOneWidget); expect(find.text('lsync 로 이동'), findsOneWidget); }); testWidgets('live_pair_card: 화면 일은 부르는 쪽 콜백으로 (최종 정리 · 복사 목록으로)', (tester) async { final c = _controller(); final x = LiveSyncPair(tmp.path, p.join(tmp.path, 'x'), method: 'builtin'); c.settings.copy.liveSyncPairs = [x]; tester.view.physicalSize = const Size(1500, 1000); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); var finalize = 0, shown = 0; await tester.pumpWidget(MaterialApp( home: Scaffold( body: ListView(children: [ LivePairCard(c: c, live: null, pair: x, onFinalize: () => finalize++, onShowCopyList: () => shown++), ]), ), )); // live 가 없으면 세기 · 맞추기는 꺼짐 expect(tester.widget(find.ancestor(of: find.text('지금 맞추기'), matching: find.byWidgetPredicate((w) => w is FilledButton))).onPressed, isNull); await tester.tap(find.text('최종 정리')); expect(finalize, 1); // 스위치는 replaceLiveSyncPair 로 저장 await tester.tap(find.byType(Switch)); await tester.pump(); expect(c.settings.copy.liveSyncPairs.single.enabled, isFalse); // 복사 목록으로 옮기면 lsync 쌍은 빠지고 복사 작업이 생긴 뒤 부르는 쪽 콜백 await tester.tap(find.text('복사 · rsync 로 이동')); await tester.pumpAndSettle(); expect(shown, 1); expect(c.settings.copy.liveSyncPairs, isEmpty); expect(c.settings.copy.tasks.single.dest, x.target); }); }