시험 흔들림: 탐색기 시험의 pumpAndSettle 을 calm (목록 읽기가 끝난 뒤) 으로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
묶음5
RyuJongJin 2026-10-12 01:31:22 +09:00
parent 70e6071991
commit a20a7bfe79
7 changed files with 94 additions and 80 deletions

View File

@ -627,8 +627,8 @@ void main() {
}); });
expect(c.busy, isTrue); expect(c.busy, isTrue);
await tester.pumpWidget(MaterialApp(home: HomePage(c: c))); await tester.pumpWidget(MaterialApp(home: HomePage(c: c)));
// 첫 그림에서 재어 보고 모자라면 다음 그림부터 두 줄 (첫 그림의 넘침 알림은 그 재기) // 첫 그림에서 재어 보고 모자라면 다음 그림부터 두 줄 (첫 그림도 넘침 오류 없이 잘라 그림)
tester.takeException(); expect(tester.takeException(), isNull);
await tester.pump(); await tester.pump();
await tester.pump(); await tester.pump();
expect(tester.takeException(), isNull, reason: '두 줄로 바뀐 뒤에는 넘침 없음'); expect(tester.takeException(), isNull, reason: '두 줄로 바뀐 뒤에는 넘침 없음');

View File

@ -14,6 +14,7 @@ import 'support/fake_recycle_bin.dart';
import 'support/recycle_leftovers.dart'; import 'support/recycle_leftovers.dart';
import 'support/elevated.dart'; import 'support/elevated.dart';
import 'explorer_test.dart' show calm;
/// 48 · 154 · 155: 같은 이름이 있으면 덮어쓰기 · 건너뛰기 · 이름 바꾸기를 고르고, 확인 창에 항목 이름이 보인다 /// 48 · 154 · 155: 같은 이름이 있으면 덮어쓰기 · 건너뛰기 · 이름 바꾸기를 고르고, 확인 창에 항목 이름이 보인다
void main() { void main() {
late Directory tmp; late Directory tmp;
@ -165,7 +166,7 @@ void main() {
await tester.runAsync(() => tester.tap(find.text('doc.txt').first)); await tester.runAsync(() => tester.tap(find.text('doc.txt').first));
await settle(tester, () => false, rounds: 10); await settle(tester, () => false, rounds: 10);
await tester.runAsync(() => tester.tap(find.text('삭제').first)); await tester.runAsync(() => tester.tap(find.text('삭제').first));
await tester.pumpAndSettle(); await calm(tester);
await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '휴지통으로'))); await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '휴지통으로')));
await settle(tester, () => find.text('되돌리기').evaluate().isNotEmpty); await settle(tester, () => find.text('되돌리기').evaluate().isNotEmpty);
expect(File(p.join(left, 'doc.txt')).existsSync(), isFalse); expect(File(p.join(left, 'doc.txt')).existsSync(), isFalse);
@ -179,7 +180,7 @@ void main() {
if (bin != null) expect(bin!.items, isEmpty, reason: '가짜 휴지통에서 되돌림'); if (bin != null) expect(bin!.items, isEmpty, reason: '가짜 휴지통에서 되돌림');
// 알림 (10초) 이 시험이 끝난 뒤에 닫히지 않게 정리 // 알림 (10초) 이 시험이 끝난 뒤에 닫히지 않게 정리
ScaffoldMessenger.of(tester.element(find.byType(ExplorerPage))).clearSnackBars(); ScaffoldMessenger.of(tester.element(find.byType(ExplorerPage))).clearSnackBars();
await tester.pumpAndSettle(); await calm(tester);
}, skip: !Platform.isWindows); }, skip: !Platform.isWindows);
testWidgets('144: 긴 경로는 휴지통에 넣지 못한 이유 한 줄과 [영구 삭제] - 누르면 지운다 · [그대로 두기] 면 남는다', (tester) async { testWidgets('144: 긴 경로는 휴지통에 넣지 못한 이유 한 줄과 [영구 삭제] - 누르면 지운다 · [그대로 두기] 면 남는다', (tester) async {
@ -199,7 +200,7 @@ void main() {
await tester.runAsync(() => tester.tap(find.text('deep').first)); await tester.runAsync(() => tester.tap(find.text('deep').first));
await settle(tester, () => false, rounds: 10); await settle(tester, () => false, rounds: 10);
await tester.runAsync(() => tester.tap(find.text('삭제').first)); await tester.runAsync(() => tester.tap(find.text('삭제').first));
await tester.pumpAndSettle(); await calm(tester);
await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '휴지통으로'))); await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '휴지통으로')));
await settle(tester, () => find.textContaining('경로가 너무 길어').evaluate().isNotEmpty); await settle(tester, () => find.textContaining('경로가 너무 길어').evaluate().isNotEmpty);
} }
@ -210,7 +211,7 @@ void main() {
expect(find.text('· deep'), findsOneWidget); expect(find.text('· deep'), findsOneWidget);
expect(find.textContaining('영구 삭제로 지울 수 있습니다'), findsNothing, reason: '긴 설명을 항목마다 되풀이하지 않음'); expect(find.textContaining('영구 삭제로 지울 수 있습니다'), findsNothing, reason: '긴 설명을 항목마다 되풀이하지 않음');
await tester.tap(find.text('그대로 두기')); await tester.tap(find.text('그대로 두기'));
await tester.pumpAndSettle(); await calm(tester);
expect(Directory(lp(deep)).existsSync(), isTrue); expect(Directory(lp(deep)).existsSync(), isTrue);
// 다시 → [영구 삭제] // 다시 → [영구 삭제]
await deleteDeep(); await deleteDeep();
@ -249,7 +250,7 @@ void main() {
expect(find.byType(ExplorerPage), findsOneWidget); expect(find.byType(ExplorerPage), findsOneWidget);
expect(find.textContaining(p.join(tmp.path, 'left')), findsNothing); expect(find.textContaining(p.join(tmp.path, 'left')), findsNothing);
await back(); // 3) 맨 위에서는 화면 닫기 await back(); // 3) 맨 위에서는 화면 닫기
await tester.pumpAndSettle(); // 닫히는 전환 (가짜 시간을 흘려야 끝난다) await calm(tester); // 닫히는 전환 (가짜 시간을 흘려야 끝난다)
expect(find.byType(ExplorerPage), findsNothing); expect(find.byType(ExplorerPage), findsNothing);
expect(find.text('앞 화면'), findsOneWidget); expect(find.text('앞 화면'), findsOneWidget);
}); });
@ -287,14 +288,14 @@ void main() {
expect(find.descendant(of: find.byType(AlertDialog), matching: find.text('이미 이 폴더에 있습니다')), findsOneWidget); expect(find.descendant(of: find.byType(AlertDialog), matching: find.text('이미 이 폴더에 있습니다')), findsOneWidget);
expect(find.textContaining('doc.txt 은(는) 이미 이 폴더에 있어 옮길 것이 없습니다'), findsOneWidget); expect(find.textContaining('doc.txt 은(는) 이미 이 폴더에 있어 옮길 것이 없습니다'), findsOneWidget);
await tester.tap(find.widgetWithText(FilledButton, '확인')); await tester.tap(find.widgetWithText(FilledButton, '확인'));
await tester.pumpAndSettle(); await calm(tester);
expect(File(p.join(left, 'doc.txt')).existsSync(), isTrue); expect(File(p.join(left, 'doc.txt')).existsSync(), isTrue);
// 같은 폴더로 [복사] 는 사본을 만들지 묻는다: [취소] → 그대로, [사본 만들기] → "doc (2).txt" // 같은 폴더로 [복사] 는 사본을 만들지 묻는다: [취소] → 그대로, [사본 만들기] → "doc (2).txt"
await tester.runAsync(() => tester.tap(find.text('복사').first)); await tester.runAsync(() => tester.tap(find.text('복사').first));
await settle(tester, () => find.byType(AlertDialog).evaluate().isNotEmpty, rounds: 30); await settle(tester, () => find.byType(AlertDialog).evaluate().isNotEmpty, rounds: 30);
expect(find.textContaining('같은 폴더에 사본을 만들까요? (이름 (2))'), findsOneWidget); expect(find.textContaining('같은 폴더에 사본을 만들까요? (이름 (2))'), findsOneWidget);
await tester.tap(find.text('취소')); await tester.tap(find.text('취소'));
await tester.pumpAndSettle(); await calm(tester);
expect(File(p.join(left, 'doc (2).txt')).existsSync(), isFalse); expect(File(p.join(left, 'doc (2).txt')).existsSync(), isFalse);
await tester.runAsync(() => tester.tap(find.text('복사').first)); await tester.runAsync(() => tester.tap(find.text('복사').first));
await settle(tester, () => find.byType(AlertDialog).evaluate().isNotEmpty, rounds: 30); await settle(tester, () => find.byType(AlertDialog).evaluate().isNotEmpty, rounds: 30);
@ -346,7 +347,7 @@ void main() {
expect(find.text('더 보기'), findsOneWidget); expect(find.text('더 보기'), findsOneWidget);
expect(find.text('숨은 항목 표시'), findsNothing, reason: '들어가지 않는 버튼은 줄에 없음'); expect(find.text('숨은 항목 표시'), findsNothing, reason: '들어가지 않는 버튼은 줄에 없음');
await tester.tap(find.text('더 보기')); await tester.tap(find.text('더 보기'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.text('숨은 항목 표시'), findsOneWidget); expect(find.text('숨은 항목 표시'), findsOneWidget);
await tester.runAsync(() => tester.tap(find.text('숨은 항목 표시'))); await tester.runAsync(() => tester.tap(find.text('숨은 항목 표시')));
await settle(tester, () => c.settings.explorerShowHidden != before, rounds: 20); await settle(tester, () => c.settings.explorerShowHidden != before, rounds: 20);
@ -391,7 +392,7 @@ void main() {
await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c)))); await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c))));
await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty); await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty);
await tester.tap(find.byTooltip('경로 입력').first); await tester.tap(find.byTooltip('경로 입력').first);
await tester.pumpAndSettle(); await calm(tester);
await tester.enterText(find.byType(TextField).last, p.join(tmp.path, 'nope')); await tester.enterText(find.byType(TextField).last, p.join(tmp.path, 'nope'));
await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '확인'))); await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '확인')));
await settle(tester, () => find.text('폴더가 없거나 열 수 없습니다').evaluate().isNotEmpty); await settle(tester, () => find.text('폴더가 없거나 열 수 없습니다').evaluate().isNotEmpty);
@ -431,7 +432,7 @@ void main() {
await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c)))); await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c))));
await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty); await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty);
await tester.tap(find.byTooltip('경로 입력').first); await tester.tap(find.byTooltip('경로 입력').first);
await tester.pumpAndSettle(); await calm(tester);
expect(find.widgetWithText(TextButton, 'WebDAV 서버 추가'), findsNothing); expect(find.widgetWithText(TextButton, 'WebDAV 서버 추가'), findsNothing);
await tester.enterText(find.byType(TextField).last, 'smb://nas/영상'); await tester.enterText(find.byType(TextField).last, 'smb://nas/영상');
await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '확인'))); await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '확인')));
@ -444,7 +445,7 @@ void main() {
expect(find.widgetWithText(AlertDialog, 'WebDAV 서버 추가'), findsOneWidget); expect(find.widgetWithText(AlertDialog, 'WebDAV 서버 추가'), findsOneWidget);
expect(find.textContaining('SMB'), findsNothing, reason: '경로 창은 닫힘'); expect(find.textContaining('SMB'), findsNothing, reason: '경로 창은 닫힘');
await tester.tap(find.text('취소').last); await tester.tap(find.text('취소').last);
await tester.pumpAndSettle(); await calm(tester);
expect(find.byType(AlertDialog), findsNothing); expect(find.byType(AlertDialog), findsNothing);
}); });

View File

@ -12,6 +12,7 @@ import 'package:jj_mkvmaker/services/platform_services.dart';
import 'package:jj_mkvmaker/ui/explorer_page.dart'; import 'package:jj_mkvmaker/ui/explorer_page.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'explorer_test.dart' show calm;
void main() { void main() {
late Directory tmp; late Directory tmp;
late AppController c; late AppController c;
@ -93,10 +94,10 @@ void main() {
expect(tester.getRect(find.byWidget(e.widget).first).right, lessThanOrEqualTo(1280)); expect(tester.getRect(find.byWidget(e.widget).first).right, lessThanOrEqualTo(1280));
} }
await tester.tap(pathBtn.first); await tester.tap(pathBtn.first);
await tester.pumpAndSettle(); await calm(tester);
expect(find.byType(AlertDialog), findsOneWidget); expect(find.byType(AlertDialog), findsOneWidget);
await tester.tap(find.text('취소').last); await tester.tap(find.text('취소').last);
await tester.pumpAndSettle(); await calm(tester);
// 정상 드라이브 (T:) 가 빈 드라이브들보다 앞 // 정상 드라이브 (T:) 가 빈 드라이브들보다 앞
expect(tester.getTopLeft(find.text('T:').first).dx, lessThan(tester.getTopLeft(find.textContaining('G: ·').first).dx)); expect(tester.getTopLeft(find.text('T:').first).dx, lessThan(tester.getTopLeft(find.textContaining('G: ·').first).dx));
// 휠 (위 · 아래) 로 탭 줄이 옆으로 (왼쪽 창의 T: 탭이 왼쪽으로 밀린다) // 휠 (위 · 아래) 로 탭 줄이 옆으로 (왼쪽 창의 T: 탭이 왼쪽으로 밀린다)
@ -108,15 +109,15 @@ void main() {
expect(tester.getTopLeft(find.text('T:').first).dx, lessThan(before - 10)); expect(tester.getTopLeft(find.text('T:').first).dx, lessThan(before - 10));
// 좁은 창 (위아래 두 창 · 폰): 끝 버튼은 [⋮] 하나로 늘 보임 → 경로 입력 // 좁은 창 (위아래 두 창 · 폰): 끝 버튼은 [⋮] 하나로 늘 보임 → 경로 입력
tester.view.physicalSize = const Size(560, 900); tester.view.physicalSize = const Size(560, 900);
await tester.pumpAndSettle(); await calm(tester);
expect(find.byTooltip('경로 입력 · WebDAV'), findsWidgets); expect(find.byTooltip('경로 입력 · WebDAV'), findsWidgets);
await tester.tap(find.byTooltip('경로 입력 · WebDAV').first); await tester.tap(find.byTooltip('경로 입력 · WebDAV').first);
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.text('경로 입력').last); await tester.tap(find.text('경로 입력').last);
await tester.pumpAndSettle(); await calm(tester);
expect(find.byType(AlertDialog), findsOneWidget); expect(find.byType(AlertDialog), findsOneWidget);
await tester.tap(find.text('취소').last); await tester.tap(find.text('취소').last);
await tester.pumpAndSettle(); await calm(tester);
expect(tester.takeException(), isNull, reason: '넘침 (RenderFlex overflow) 없음'); expect(tester.takeException(), isNull, reason: '넘침 (RenderFlex overflow) 없음');
}); });

View File

@ -11,7 +11,7 @@ import 'package:jj_mkvmaker/services/platform_services.dart';
import 'package:jj_mkvmaker/ui/explorer_page.dart'; import 'package:jj_mkvmaker/ui/explorer_page.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'explorer_test.dart' show act, settle; import 'explorer_test.dart' show act, settle, calm;
/// 23: 앞의 복사가 끝나지 않아도 다음 복사를 함께 시작한다 (막지 않음, 진행 막대가 둘) /// 23: 앞의 복사가 끝나지 않아도 다음 복사를 함께 시작한다 (막지 않음, 진행 막대가 둘)
void main() { void main() {
@ -161,7 +161,7 @@ void main() {
await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c, rsync: true)))); await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c, rsync: true))));
await settle(tester, () => find.text('sub').evaluate().isNotEmpty); await settle(tester, () => find.text('sub').evaluate().isNotEmpty);
await tester.longPress(find.text('sub')); await tester.longPress(find.text('sub'));
await tester.pumpAndSettle(); await calm(tester);
for (final t in ['rsync 에 고르기', '열기', '다른 창에서 열기', '이름 변경', '삭제', '정보']) { for (final t in ['rsync 에 고르기', '열기', '다른 창에서 열기', '이름 변경', '삭제', '정보']) {
expect(find.text(t), findsOneWidget, reason: t); expect(find.text(t), findsOneWidget, reason: t);
} }
@ -194,7 +194,7 @@ void main() {
await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c)))); await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c))));
await settle(tester, () => find.text('top.txt').evaluate().isNotEmpty); await settle(tester, () => find.text('top.txt').evaluate().isNotEmpty);
await tester.tap(find.byTooltip('경로 입력').first); await tester.tap(find.byTooltip('경로 입력').first);
await tester.pumpAndSettle(); await calm(tester);
await tester.enterText(find.byType(TextField).last, p.join(tmp.path, 'none')); await tester.enterText(find.byType(TextField).last, p.join(tmp.path, 'none'));
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인')));
await settle(tester, () => find.textContaining('폴더가 없거나 열 수 없습니다').evaluate().isNotEmpty, rounds: 20); await settle(tester, () => find.textContaining('폴더가 없거나 열 수 없습니다').evaluate().isNotEmpty, rounds: 20);
@ -232,7 +232,7 @@ void main() {
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsBytesSync([1, 2, 3]); ..writeAsBytesSync([1, 2, 3]);
await tester.tap(find.byTooltip('경로 입력').first); await tester.tap(find.byTooltip('경로 입력').first);
await tester.pumpAndSettle(); await calm(tester);
await tester.enterText(find.byType(TextField).last, made); await tester.enterText(find.byType(TextField).last, made);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인')));
await settle(tester, () => find.text('pg1.png').evaluate().isNotEmpty, rounds: 300); await settle(tester, () => find.text('pg1.png').evaluate().isNotEmpty, rounds: 300);
@ -308,7 +308,7 @@ void main() {
await act(tester, () => tester.tap(find.byTooltip(tooltip))); await act(tester, () => tester.tap(find.byTooltip(tooltip)));
} else { } else {
await tester.tap(find.text('더 보기')); await tester.tap(find.text('더 보기'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.text(label), findsOneWidget, reason: '[더 보기] 메뉴에 지금 배치가 보임: $label'); expect(find.text(label), findsOneWidget, reason: '[더 보기] 메뉴에 지금 배치가 보임: $label');
await act(tester, () => tester.tap(find.text(label))); await act(tester, () => tester.tap(find.text(label)));
} }

View File

@ -12,6 +12,7 @@ import 'package:jj_mkvmaker/ui/explorer_page.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'support/fake_recycle_bin.dart'; import 'support/fake_recycle_bin.dart';
import 'explorer_test.dart' show calm;
/// 180: 복사 · 이동이 시작 · 진행 · 끝나도 목록의 자리와 스크롤은 그대로 (다음 것을 누르려다 엉뚱한 것을 누르지 않게) /// 180: 복사 · 이동이 시작 · 진행 · 끝나도 목록의 자리와 스크롤은 그대로 (다음 것을 누르려다 엉뚱한 것을 누르지 않게)
void main() { void main() {
late Directory tmp; late Directory tmp;
@ -62,7 +63,7 @@ void main() {
// 왼쪽 목록을 조금 내려 둔다 // 왼쪽 목록을 조금 내려 둔다
final list = find.ancestor(of: find.text('f05.txt'), matching: find.byType(Scrollable)).first; final list = find.ancestor(of: find.text('f05.txt'), matching: find.byType(Scrollable)).first;
await tester.drag(find.text('f05.txt'), const Offset(0, -200)); await tester.drag(find.text('f05.txt'), const Offset(0, -200));
await tester.pumpAndSettle(); await calm(tester);
final probe = find.text('f07.txt'); final probe = find.text('f07.txt');
expect(probe, findsOneWidget); expect(probe, findsOneWidget);
final where = tester.getTopLeft(probe); final where = tester.getTopLeft(probe);
@ -84,7 +85,7 @@ void main() {
await act(tester, () => tester.tap(find.text('새로 고침').first)); await act(tester, () => tester.tap(find.text('새로 고침').first));
} else { } else {
await act(tester, () => tester.tap(find.text('복사').first)); await act(tester, () => tester.tap(find.text('복사').first));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사')));
} }
// 시작 · 진행 · 끝 (진행 막대가 내려갈 때까지) 내내 같은 자리 // 시작 · 진행 · 끝 (진행 막대가 내려갈 때까지) 내내 같은 자리
@ -126,7 +127,7 @@ void main() {
await settle(tester, () => find.text('f05.txt').evaluate().isNotEmpty); await settle(tester, () => find.text('f05.txt').evaluate().isNotEmpty);
final list = find.ancestor(of: find.text('f05.txt'), matching: find.byType(Scrollable)).first; final list = find.ancestor(of: find.text('f05.txt'), matching: find.byType(Scrollable)).first;
await tester.drag(find.text('f05.txt'), const Offset(0, -200)); await tester.drag(find.text('f05.txt'), const Offset(0, -200));
await tester.pumpAndSettle(); await calm(tester);
final at = tester.state<ScrollableState>(list).position.pixels; final at = tester.state<ScrollableState>(list).position.pixels;
expect(at, greaterThan(0)); expect(at, greaterThan(0));
final w = tester.getSize(list).width; final w = tester.getSize(list).width;

View File

@ -112,7 +112,7 @@ void main() {
// 길게 누르기 → 메뉴 → 다른 앱으로 열기 (고르기 창) // 길게 누르기 → 메뉴 → 다른 앱으로 열기 (고르기 창)
await tester.longPress(find.text('doc.txt')); await tester.longPress(find.text('doc.txt'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.text('다른 창으로 복사'), findsOneWidget); expect(find.text('다른 창으로 복사'), findsOneWidget);
expect(find.text('삭제'), findsWidgets); expect(find.text('삭제'), findsWidgets);
await act(tester, () => tester.tap(find.text('다른 앱으로 열기'))); await act(tester, () => tester.tap(find.text('다른 앱으로 열기')));
@ -123,7 +123,7 @@ void main() {
await act(tester, () => tester.tap(find.text('doc.txt'))); await act(tester, () => tester.tap(find.text('doc.txt')));
await settle(tester, () => false, rounds: 15); await settle(tester, () => false, rounds: 15);
await act(tester, () => tester.tap(find.text('복사').first)); await act(tester, () => tester.tap(find.text('복사').first));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사')));
await settle(tester, () => File(p.join(right, 'doc.txt')).existsSync() && find.byType(LinearProgressIndicator).evaluate().isEmpty); await settle(tester, () => File(p.join(right, 'doc.txt')).existsSync() && find.byType(LinearProgressIndicator).evaluate().isEmpty);
expect(File(p.join(right, 'doc.txt')).readAsStringSync(), 'hello'); expect(File(p.join(right, 'doc.txt')).readAsStringSync(), 'hello');
@ -138,17 +138,17 @@ void main() {
await act(tester, () => tester.tap(find.text('doc.txt'))); await act(tester, () => tester.tap(find.text('doc.txt')));
await settle(tester, () => false, rounds: 15); await settle(tester, () => false, rounds: 15);
await act(tester, () => tester.tap(find.text('복사').first)); await act(tester, () => tester.tap(find.text('복사').first));
await tester.pumpAndSettle(); await calm(tester);
expect(find.textContaining('방법: robocopy'), findsOneWidget); expect(find.textContaining('방법: robocopy'), findsOneWidget);
expect(find.text('환경 설정의 /MOV 는 복사에서는 쓰지 않습니다 (이동은 [이동] 으로)'), findsOneWidget); expect(find.text('환경 설정의 /MOV 는 복사에서는 쓰지 않습니다 (이동은 [이동] 으로)'), findsOneWidget);
await act(tester, () => tester.tap(find.text('취소').last)); await act(tester, () => tester.tap(find.text('취소').last));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.tap(find.text('이동').first)); await act(tester, () => tester.tap(find.text('이동').first));
await tester.pumpAndSettle(); await calm(tester);
expect(find.textContaining('옮길까요?'), findsOneWidget); expect(find.textContaining('옮길까요?'), findsOneWidget);
expect(find.textContaining('복사에서는 쓰지 않습니다'), findsNothing); expect(find.textContaining('복사에서는 쓰지 않습니다'), findsNothing);
await act(tester, () => tester.tap(find.text('취소').last)); await act(tester, () => tester.tap(find.text('취소').last));
await tester.pumpAndSettle(); await calm(tester);
expect(File(p.join(left, 'doc.txt')).existsSync(), isTrue); expect(File(p.join(left, 'doc.txt')).existsSync(), isTrue);
expect(File(p.join(right, 'doc.txt')).existsSync(), isFalse); expect(File(p.join(right, 'doc.txt')).existsSync(), isFalse);
}, skip: !Platform.isWindows); }, skip: !Platform.isWindows);
@ -192,17 +192,17 @@ void main() {
// 이 배치에도 복사 · 이동: 길게 눌러 [복사] 로 담고 → 다른 폴더를 골라 [붙여넣기] // 이 배치에도 복사 · 이동: 길게 눌러 [복사] 로 담고 → 다른 폴더를 골라 [붙여넣기]
await tester.longPress(find.text('inner.txt')); await tester.longPress(find.text('inner.txt'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.text('다른 창으로 복사'), findsNothing); expect(find.text('다른 창으로 복사'), findsNothing);
await tester.tap(find.text('복사').last); await tester.tap(find.text('복사').last);
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.tap(find.text('..'))); await act(tester, () => tester.tap(find.text('..')));
await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty); await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty);
// 73 (관리자): 담아 둔 것이 있으면 [붙여넣기] 는 좁아도 [더 보기] 로 가지 않고 줄에 남는다 // 73 (관리자): 담아 둔 것이 있으면 [붙여넣기] 는 좁아도 [더 보기] 로 가지 않고 줄에 남는다
expect(find.text('더 보기'), findsOneWidget, reason: '이 너비에서는 줄이 넘친다'); expect(find.text('더 보기'), findsOneWidget, reason: '이 너비에서는 줄이 넘친다');
expect(find.text('붙여넣기'), findsOneWidget); expect(find.text('붙여넣기'), findsOneWidget);
await act(tester, () => tester.tap(find.text('붙여넣기'))); await act(tester, () => tester.tap(find.text('붙여넣기')));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사')));
await settle(tester, () => File(p.join(left, 'inner.txt')).existsSync() && find.byType(LinearProgressIndicator).evaluate().isEmpty); await settle(tester, () => File(p.join(left, 'inner.txt')).existsSync() && find.byType(LinearProgressIndicator).evaluate().isEmpty);
expect(File(p.join(left, 'inner.txt')).readAsStringSync(), 'inner'); expect(File(p.join(left, 'inner.txt')).readAsStringSync(), 'inner');
@ -220,7 +220,7 @@ void main() {
expect(find.text('확장자'), findsOneWidget); expect(find.text('확장자'), findsOneWidget);
// 알림 (복사 끝 등) 이 시험이 끝난 뒤에 닫히지 않게 정리 // 알림 (복사 끝 등) 이 시험이 끝난 뒤에 닫히지 않게 정리
ScaffoldMessenger.of(tester.element(find.byType(ExplorerPage))).clearSnackBars(); ScaffoldMessenger.of(tester.element(find.byType(ExplorerPage))).clearSnackBars();
await tester.pumpAndSettle(); await calm(tester);
}); });
testWidgets('선택 모드: 파일 누르기 = 선택 · 취소, 폴더 누르기 = 자기 자신과 안의 것 모두 선택 · 다시 누르면 모두 취소', (tester) async { testWidgets('선택 모드: 파일 누르기 = 선택 · 취소, 폴더 누르기 = 자기 자신과 안의 것 모두 선택 · 다시 누르면 모두 취소', (tester) async {
@ -278,9 +278,9 @@ void main() {
await act(tester, () => tester.tap(markOf('sub'))); await act(tester, () => tester.tap(markOf('sub')));
await settle(tester, () => find.textContaining('3개 표시함').evaluate().isNotEmpty); await settle(tester, () => find.textContaining('3개 표시함').evaluate().isNotEmpty);
await act(tester, () => tester.tap(find.text('삭제').first)); await act(tester, () => tester.tap(find.text('삭제').first));
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.text('휴지통을 거치지 않고 영구 삭제 (Shift+Delete)')); await tester.tap(find.text('휴지통을 거치지 않고 영구 삭제 (Shift+Delete)'));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '영구 삭제'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '영구 삭제')));
await settle(tester, () => find.textContaining('지우지 못했습니다').evaluate().isNotEmpty); await settle(tester, () => find.textContaining('지우지 못했습니다').evaluate().isNotEmpty);
expect(find.text('3개 중 1개를 지우지 못했습니다'), findsOneWidget); expect(find.text('3개 중 1개를 지우지 못했습니다'), findsOneWidget);
@ -289,43 +289,43 @@ void main() {
expect(Directory(p.join(left, 'sub')).existsSync(), isFalse); expect(Directory(p.join(left, 'sub')).existsSync(), isFalse);
expect(locked.existsSync(), isTrue); expect(locked.existsSync(), isTrue);
await tester.tap(find.widgetWithText(FilledButton, '확인')); await tester.tap(find.widgetWithText(FilledButton, '확인'));
await tester.pumpAndSettle(); await calm(tester);
}, skip: !Platform.isWindows); }, skip: !Platform.isWindows);
testWidgets('73: 즐겨찾기 - 지금 폴더 넣기 (이름 고침) · 한 번에 가기 · 빼기 (폴더는 그대로)', (tester) async { testWidgets('73: 즐겨찾기 - 지금 폴더 넣기 (이름 고침) · 한 번에 가기 · 빼기 (폴더는 그대로)', (tester) async {
await open(tester); await open(tester);
// 창 위의 ★ (왼쪽 창) // 창 위의 ★ (왼쪽 창)
await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first)); await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first));
await tester.pumpAndSettle(); await calm(tester);
expect(find.text('아직 없습니다. 아래에서 지금 폴더를 넣으세요.'), findsOneWidget); expect(find.text('아직 없습니다. 아래에서 지금 폴더를 넣으세요.'), findsOneWidget);
await tester.tap(find.text('지금 폴더 추가')); await tester.tap(find.text('지금 폴더 추가'));
await tester.pumpAndSettle(); await calm(tester);
await tester.enterText(find.byType(TextField).last, '왼쪽 폴더'); await tester.enterText(find.byType(TextField).last, '왼쪽 폴더');
await tester.tap(find.widgetWithText(FilledButton, '확인')); await tester.tap(find.widgetWithText(FilledButton, '확인'));
await tester.pumpAndSettle(); await calm(tester);
expect([for (final b in c.settings.folderBookmarks) (b.name, b.path)], [('왼쪽 폴더', left)]); expect([for (final b in c.settings.folderBookmarks) (b.name, b.path)], [('왼쪽 폴더', left)]);
expect(find.text('왼쪽 폴더'), findsOneWidget); expect(find.text('왼쪽 폴더'), findsOneWidget);
await tester.tap(find.text('닫기')); await tester.tap(find.text('닫기'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.byIcon(Icons.star), findsOneWidget, reason: '즐겨찾기한 폴더를 보는 창의 ★ 는 채움'); expect(find.byIcon(Icons.star), findsOneWidget, reason: '즐겨찾기한 폴더를 보는 창의 ★ 는 채움');
// 오른쪽 폴더를 즐겨찾기에 두고 왼쪽 창에서 한 번에 가기 // 오른쪽 폴더를 즐겨찾기에 두고 왼쪽 창에서 한 번에 가기
await c.updateSettings((x) => x.folderBookmarks = [...x.folderBookmarks, FolderBookmark(name: 'R', path: right)]); await c.updateSettings((x) => x.folderBookmarks = [...x.folderBookmarks, FolderBookmark(name: 'R', path: right)]);
await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first)); await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.tap(find.text('R'))); await act(tester, () => tester.tap(find.text('R')));
await settle(tester, () => false, rounds: 20); await settle(tester, () => false, rounds: 20);
expect(find.byIcon(Icons.star), findsNWidgets(2), reason: '두 창 모두 즐겨찾기한 폴더 (왼쪽 창이 R 로 감)'); expect(find.byIcon(Icons.star), findsNWidgets(2), reason: '두 창 모두 즐겨찾기한 폴더 (왼쪽 창이 R 로 감)');
// 빼기: 즐겨찾기에서만 (폴더는 그대로) // 빼기: 즐겨찾기에서만 (폴더는 그대로)
await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first)); await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first));
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.byTooltip('즐겨찾기에서 빼기 (폴더는 그대로)').first); await tester.tap(find.byTooltip('즐겨찾기에서 빼기 (폴더는 그대로)').first);
await tester.pumpAndSettle(); await calm(tester);
expect([for (final b in c.settings.folderBookmarks) b.name], ['R']); expect([for (final b in c.settings.folderBookmarks) b.name], ['R']);
expect(Directory(left).existsSync(), isTrue); expect(Directory(left).existsSync(), isTrue);
await tester.tap(find.text('닫기')); await tester.tap(find.text('닫기'));
await tester.pumpAndSettle(); await calm(tester);
}); });
testWidgets('73: Rsync 화면 - "왼쪽 + 오른쪽" 짝을 넣고 한 번에 연다', (tester) async { testWidgets('73: Rsync 화면 - "왼쪽 + 오른쪽" 짝을 넣고 한 번에 연다', (tester) async {
@ -336,19 +336,19 @@ void main() {
await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c, rsync: true)))); await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c, rsync: true))));
await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty); await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty);
await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first)); await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first));
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.text('지금 짝 추가')); await tester.tap(find.text('지금 짝 추가'));
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.widgetWithText(FilledButton, '확인')); await tester.tap(find.widgetWithText(FilledButton, '확인'));
await tester.pumpAndSettle(); await calm(tester);
final pair = c.settings.folderBookmarks.single; final pair = c.settings.folderBookmarks.single;
expect((pair.path, pair.right, pair.name), (left, right, 'left ⇄ right')); expect((pair.path, pair.right, pair.name), (left, right, 'left ⇄ right'));
await tester.tap(find.text('닫기')); await tester.tap(find.text('닫기'));
await tester.pumpAndSettle(); await calm(tester);
// 반대 짝을 넣어 두고 열면 두 창이 함께 바뀐다 // 반대 짝을 넣어 두고 열면 두 창이 함께 바뀐다
await c.updateSettings((x) => x.folderBookmarks = [...x.folderBookmarks, FolderBookmark(name: '거꾸로', path: right, right: left)]); await c.updateSettings((x) => x.folderBookmarks = [...x.folderBookmarks, FolderBookmark(name: '거꾸로', path: right, right: left)]);
await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first)); await act(tester, () => tester.tap(find.byTooltip('즐겨찾기').first));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.tap(find.text('거꾸로'))); await act(tester, () => tester.tap(find.text('거꾸로')));
await settle(tester, () => c.settings.rsyncPaths.length == 2 && c.settings.rsyncPaths[0] == right && c.settings.rsyncPaths[1] == left, await settle(tester, () => c.settings.rsyncPaths.length == 2 && c.settings.rsyncPaths[0] == right && c.settings.rsyncPaths[1] == left,
rounds: 100); rounds: 100);
@ -368,9 +368,9 @@ void main() {
Future<void> tool(String label) async { Future<void> tool(String label) async {
await tester.tap(find.text('고르기 도구').first); await tester.tap(find.text('고르기 도구').first);
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.textContaining(label).last); await tester.tap(find.textContaining(label).last);
await tester.pumpAndSettle(); await calm(tester);
} }
// 같은 확장자 모두 (.srt) - 마지막으로 누른 a.srt 기준 // 같은 확장자 모두 (.srt) - 마지막으로 누른 a.srt 기준
@ -386,7 +386,7 @@ void main() {
await tool('패턴으로 고르기'); await tool('패턴으로 고르기');
await tester.enterText(find.byType(TextField).last, '*.MKV; doc.*'); await tester.enterText(find.byType(TextField).last, '*.MKV; doc.*');
await tester.tap(find.widgetWithText(FilledButton, '고르기')); await tester.tap(find.widgetWithText(FilledButton, '고르기'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.text('2개 표시함 · 13B'), findsOneWidget); expect(find.text('2개 표시함 · 13B'), findsOneWidget);
// 범위: a.srt 를 표시한 뒤 Shift + c.mkv → a.srt · b.srt · c.mkv (보이는 순서) // 범위: a.srt 를 표시한 뒤 Shift + c.mkv → a.srt · b.srt · c.mkv (보이는 순서)
@ -404,9 +404,9 @@ void main() {
await tester.pump(); await tester.pump();
await act(tester, () => tester.tap(markOf('b.srt'))); await act(tester, () => tester.tap(markOf('b.srt')));
await tester.longPress(find.text('doc.txt').first); await tester.longPress(find.text('doc.txt').first);
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.text('여기까지 범위 고르기')); await tester.tap(find.text('여기까지 범위 고르기'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.textContaining('3개 표시함'), findsOneWidget, reason: 'b.srt · c.mkv · doc.txt'); expect(find.textContaining('3개 표시함'), findsOneWidget, reason: 'b.srt · c.mkv · doc.txt');
}); });
@ -548,14 +548,14 @@ void main() {
await settle(tester, () => find.textContaining('2개 표시함').evaluate().isNotEmpty); await settle(tester, () => find.textContaining('2개 표시함').evaluate().isNotEmpty);
expect(find.textContaining('2개 표시함'), findsOneWidget); expect(find.textContaining('2개 표시함'), findsOneWidget);
await act(tester, () => tester.tap(find.text('삭제').first)); await act(tester, () => tester.tap(find.text('삭제').first));
await tester.pumpAndSettle(); await calm(tester);
// 지울 항목 이름이 확인 창에 보인다. 65: Windows 는 기본이 휴지통 (시험에서는 영구 삭제를 골라 휴지통을 어지럽히지 않음) // 지울 항목 이름이 확인 창에 보인다. 65: Windows 는 기본이 휴지통 (시험에서는 영구 삭제를 골라 휴지통을 어지럽히지 않음)
expect(find.textContaining('2개 항목을 휴지통으로 보낼까요?'), findsOneWidget); expect(find.textContaining('2개 항목을 휴지통으로 보낼까요?'), findsOneWidget);
expect(find.widgetWithText(FilledButton, '휴지통으로'), findsOneWidget); expect(find.widgetWithText(FilledButton, '휴지통으로'), findsOneWidget);
expect(find.textContaining('· sub'), findsOneWidget); expect(find.textContaining('· sub'), findsOneWidget);
expect(find.textContaining('· doc.txt'), findsOneWidget); expect(find.textContaining('· doc.txt'), findsOneWidget);
await tester.tap(find.text('휴지통을 거치지 않고 영구 삭제 (Shift+Delete)')); await tester.tap(find.text('휴지통을 거치지 않고 영구 삭제 (Shift+Delete)'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.textContaining('2개 항목을 지울까요? 되돌릴 수 없습니다.'), findsOneWidget); expect(find.textContaining('2개 항목을 지울까요? 되돌릴 수 없습니다.'), findsOneWidget);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '영구 삭제'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '영구 삭제')));
await settle(tester, () => find.text('doc.txt').evaluate().isEmpty); await settle(tester, () => find.text('doc.txt').evaluate().isEmpty);
@ -564,14 +564,14 @@ void main() {
// 새 폴더 → 이름 변경 (지운 뒤 창들이 목록을 다시 읽는 동안은 진행 막대가 돌아 pumpAndSettle 이 끝나지 않는다 - 무거울 때 흔들림) // 새 폴더 → 이름 변경 (지운 뒤 창들이 목록을 다시 읽는 동안은 진행 막대가 돌아 pumpAndSettle 이 끝나지 않는다 - 무거울 때 흔들림)
await idle(tester); await idle(tester);
await act(tester, () => tester.tap(find.text('새 폴더').first)); await act(tester, () => tester.tap(find.text('새 폴더').first));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.enterText(find.byType(TextField), 'films')); await act(tester, () => tester.enterText(find.byType(TextField), 'films'));
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인')));
await settle(tester, () => find.text('films').evaluate().isNotEmpty); await settle(tester, () => find.text('films').evaluate().isNotEmpty);
expect(Directory(p.join(left, 'films')).existsSync(), isTrue); expect(Directory(p.join(left, 'films')).existsSync(), isTrue);
await idle(tester); await idle(tester);
await act(tester, () => tester.tap(find.text('이름 변경').first)); await act(tester, () => tester.tap(find.text('이름 변경').first));
await tester.pumpAndSettle(); await calm(tester);
await act(tester, () => tester.enterText(find.byType(TextField), 'movies')); await act(tester, () => tester.enterText(find.byType(TextField), 'movies'));
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인')));
await settle(tester, () => find.text('movies').evaluate().isNotEmpty); await settle(tester, () => find.text('movies').evaluate().isNotEmpty);
@ -581,33 +581,33 @@ void main() {
testWidgets('창 배치: 한 창 · 버튼 줄 숨김, 버튼 구성: 버튼 빼기 → 설정에 저장', (tester) async { testWidgets('창 배치: 한 창 · 버튼 줄 숨김, 버튼 구성: 버튼 빼기 → 설정에 저장', (tester) async {
await open(tester); await open(tester);
await tester.tap(find.byTooltip('창 배치 · 버튼 구성')); await tester.tap(find.byTooltip('창 배치 · 버튼 구성'));
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.text('창 배치').last); await tester.tap(find.text('창 배치').last);
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.widgetWithText(ChoiceChip, '한 창')); await tester.tap(find.widgetWithText(ChoiceChip, '한 창'));
await tester.pump(); await tester.pump();
await tester.tap(find.widgetWithText(ChoiceChip, '숨김')); await tester.tap(find.widgetWithText(ChoiceChip, '숨김'));
await tester.pump(); await tester.pump();
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인')));
await tester.pumpAndSettle(); await calm(tester);
expect([c.settings.explorerLayout, c.settings.explorerToolbar], ['single', 'hidden']); expect([c.settings.explorerLayout, c.settings.explorerToolbar], ['single', 'hidden']);
expect(find.text('상위 폴더'), findsNothing); // 버튼 줄 숨김 expect(find.text('상위 폴더'), findsNothing); // 버튼 줄 숨김
expect(find.text('doc.txt'), findsOneWidget); // 한 창 (왼쪽만) expect(find.text('doc.txt'), findsOneWidget); // 한 창 (왼쪽만)
// ⋮ 메뉴에서 버튼 구성: "찾기" 빼기 // ⋮ 메뉴에서 버튼 구성: "찾기" 빼기
await c.updateSettings((s) => s.explorerToolbar = 'middle'); await c.updateSettings((s) => s.explorerToolbar = 'middle');
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.byTooltip('창 배치 · 버튼 구성')); await tester.tap(find.byTooltip('창 배치 · 버튼 구성'));
await tester.pumpAndSettle(); await calm(tester);
await tester.tap(find.text('버튼 구성').last); await tester.tap(find.text('버튼 구성').last);
await tester.pumpAndSettle(); await calm(tester);
// 처음 값 순서 (자주 쓰는 것부터) 에서 뒤쪽이라 목록을 내려 보이게 한 뒤 // 처음 값 순서 (자주 쓰는 것부터) 에서 뒤쪽이라 목록을 내려 보이게 한 뒤
await tester.scrollUntilVisible(find.widgetWithText(CheckboxListTile, 'MKV 목록에 추가'), 80, await tester.scrollUntilVisible(find.widgetWithText(CheckboxListTile, 'MKV 목록에 추가'), 80,
scrollable: find.descendant(of: find.byType(AlertDialog), matching: find.byType(Scrollable)).first); scrollable: find.descendant(of: find.byType(AlertDialog), matching: find.byType(Scrollable)).first);
await tester.tap(find.widgetWithText(CheckboxListTile, 'MKV 목록에 추가')); await tester.tap(find.widgetWithText(CheckboxListTile, 'MKV 목록에 추가'));
await tester.pump(); await tester.pump();
await tester.tap(find.widgetWithText(FilledButton, '확인')); await tester.tap(find.widgetWithText(FilledButton, '확인'));
await tester.pumpAndSettle(); await calm(tester);
expect(c.settings.explorerButtons, isNot(contains('addMkv'))); expect(c.settings.explorerButtons, isNot(contains('addMkv')));
expect(c.settings.explorerButtons, contains('copy')); expect(c.settings.explorerButtons, contains('copy'));
expect(find.text('MKV 목록에 추가'), findsNothing); expect(find.text('MKV 목록에 추가'), findsNothing);
@ -673,7 +673,7 @@ void main() {
expect(find.text('$rs/ → $ls/'), findsOneWidget); expect(find.text('$rs/ → $ls/'), findsOneWidget);
expect(find.textContaining('-avPog -u'), findsNWidgets(2)); expect(find.textContaining('-avPog -u'), findsNWidgets(2));
await tester.tap(find.text('취소')); await tester.tap(find.text('취소'));
await tester.pumpAndSettle(); await calm(tester);
expect(c.settings.copyTasks, isEmpty); expect(c.settings.copyTasks, isEmpty);
expect(find.text('원본 파일 지우기 (--remove-source-files)'), findsNothing); // 양쪽에는 없음 expect(find.text('원본 파일 지우기 (--remove-source-files)'), findsNothing); // 양쪽에는 없음
// → 는 한 방향 · -u 없이 · 원본 파일 지우기를 고를 수 있다 (고르면 원본 폴더 남김 / 지움) // → 는 한 방향 · -u 없이 · 원본 파일 지우기를 고를 수 있다 (고르면 원본 폴더 남김 / 지움)
@ -684,13 +684,13 @@ void main() {
expect(find.textContaining('-u'), findsNothing); expect(find.textContaining('-u'), findsNothing);
expect(find.text('빈 폴더 지움 · 원본 폴더는 남김'), findsNothing); expect(find.text('빈 폴더 지움 · 원본 폴더는 남김'), findsNothing);
await tester.tap(find.text('원본 파일 지우기 (--remove-source-files)')); await tester.tap(find.text('원본 파일 지우기 (--remove-source-files)'));
await tester.pumpAndSettle(); await calm(tester);
expect(find.textContaining('-avPog --remove-source-files'), findsOneWidget); expect(find.textContaining('-avPog --remove-source-files'), findsOneWidget);
expect(find.text('빈 폴더 지움 · 원본 폴더는 남김'), findsOneWidget); expect(find.text('빈 폴더 지움 · 원본 폴더는 남김'), findsOneWidget);
expect(find.text('빈 폴더 지움 · 원본 폴더도 지움'), findsOneWidget); expect(find.text('빈 폴더 지움 · 원본 폴더도 지움'), findsOneWidget);
expect(find.widgetWithText(FilledButton, '이동'), findsOneWidget); expect(find.widgetWithText(FilledButton, '이동'), findsOneWidget);
await tester.tap(find.text('취소')); await tester.tap(find.text('취소'));
await tester.pumpAndSettle(); await calm(tester);
// 고른 폴더는 기억 (다음에 열 때 그 폴더부터) // 고른 폴더는 기억 (다음에 열 때 그 폴더부터)
expect(c.settings.rsyncPaths, [p.join(left, 'sub'), p.join(right, 'rsub')]); expect(c.settings.rsyncPaths, [p.join(left, 'sub'), p.join(right, 'rsub')]);
@ -700,7 +700,7 @@ void main() {
await act(tester, () => tester.tap(find.text('rsub'))); await act(tester, () => tester.tap(find.text('rsub')));
await settle(tester, () => false, rounds: 5); // 다시 골라 지금 폴더로 await settle(tester, () => false, rounds: 5); // 다시 골라 지금 폴더로
await act(tester, () => tester.tap(find.text('새 폴더'))); await act(tester, () => tester.tap(find.text('새 폴더')));
await tester.pumpAndSettle(); await calm(tester);
await tester.enterText(find.byType(TextField), '새로만든'); await tester.enterText(find.byType(TextField), '새로만든');
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인')));
// 만든 폴더가 보이고 골라질 때까지 (전체 시험 중 바쁠 때는 고르기가 조금 늦다) // 만든 폴더가 보이고 골라질 때까지 (전체 시험 중 바쁠 때는 고르기가 조금 늦다)
@ -743,10 +743,21 @@ Future<void> doubleTap(WidgetTester tester, Finder f) async {
await tester.pump(); await tester.pump();
} }
/// 창들이 목록을 다시 읽는 중 (맨 위 진행 막대) 이 끝날 때까지 실제 시간으로 기다린다. /// 창들이 목록을 다시 읽는 중 (맨 위 진행 막대 · 도는 표시) 이 끝날 때까지 실제 시간으로 기다린다.
/// 그사이 pumpAndSettle 을 하면 막대가 계속 움직여, 디스크 읽기가 늦을 때 (전체 시험이 무거울 때) 시간 초과가 난다 /// 그사이 pumpAndSettle 을 하면 막대가 계속 움직여, 디스크 읽기가 늦을 때 (전체 시험이 무거울 때) 시간 초과가 난다.
Future<void> idle(WidgetTester tester) => /// 값이 있는 막대 (디스크 용량 · 진행률) 는 움직이지 않으므로 세지 않는다
settle(tester, () => find.byType(LinearProgressIndicator).evaluate().isEmpty && find.byType(CircularProgressIndicator).evaluate().isEmpty); Future<void> idle(WidgetTester tester) => settle(
tester,
() => find
.byWidgetPredicate((w) => w is ProgressIndicator && w.value == null)
.evaluate()
.isEmpty);
/// 탐색기 시험의 pumpAndSettle: 목록 읽기가 끝난 뒤에 (경로를 바꾸거나 다시 읽은 뒤 무거울 때 흔들리던 것을 한 번에)
Future<void> calm(WidgetTester tester) async {
await idle(tester);
await tester.pumpAndSettle();
}
/// 실제 파일 작업 (비동기 IO) 이 끝나기를 기다리며 화면을 갱신 /// 실제 파일 작업 (비동기 IO) 이 끝나기를 기다리며 화면을 갱신
// 끝나면 바로 나온다 - 넉넉한 횟수는 전체 시험 중 PC 가 바쁠 때 (실제 파일 입출력이 느려짐) 흔들리지 않게 // 끝나면 바로 나온다 - 넉넉한 횟수는 전체 시험 중 PC 가 바쁠 때 (실제 파일 입출력이 느려짐) 흔들리지 않게

View File

@ -13,7 +13,7 @@ import 'package:jj_mkvmaker/services/platform_services.dart';
import 'package:jj_mkvmaker/ui/explorer_page.dart'; import 'package:jj_mkvmaker/ui/explorer_page.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'explorer_test.dart' show act, doubleTap, settle; import 'explorer_test.dart' show act, doubleTap, settle, calm;
import 'support/dav_server.dart'; import 'support/dav_server.dart';
class _Shell extends NoopShell { class _Shell extends NoopShell {
@ -76,7 +76,7 @@ void main() {
// 서버가 없으면 위쪽 저장 장치 옆에 "WebDAV" 탭 (창마다) // 서버가 없으면 위쪽 저장 장치 옆에 "WebDAV" 탭 (창마다)
expect(find.text('WebDAV'), findsNWidgets(2)); expect(find.text('WebDAV'), findsNWidgets(2));
await act(tester, () => tester.tap(find.text('WebDAV').last)); await act(tester, () => tester.tap(find.text('WebDAV').last));
await tester.pumpAndSettle(); await calm(tester);
expect(find.text('WebDAV 서버 추가'), findsOneWidget); expect(find.text('WebDAV 서버 추가'), findsOneWidget);
await tester.enterText(find.widgetWithText(TextField, '이름 (탭에 보임, 비우면 주소)'), '집 NAS'); await tester.enterText(find.widgetWithText(TextField, '이름 (탭에 보임, 비우면 주소)'), '집 NAS');
await tester.enterText(find.widgetWithText(TextField, '주소'), server.url); await tester.enterText(find.widgetWithText(TextField, '주소'), server.url);
@ -108,7 +108,7 @@ void main() {
await act(tester, () => tester.tap(find.text('doc.txt'))); await act(tester, () => tester.tap(find.text('doc.txt')));
await settle(tester, () => false, rounds: 15); await settle(tester, () => false, rounds: 15);
await act(tester, () => tester.tap(find.text('복사').first)); await act(tester, () => tester.tap(find.text('복사').first));
await tester.pumpAndSettle(); await calm(tester);
expect(find.textContaining('☁ 집 NAS/원격'), findsOneWidget); expect(find.textContaining('☁ 집 NAS/원격'), findsOneWidget);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사'))); await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사')));
final up = File(p.join(remote.path, '원격', 'doc.txt')); final up = File(p.join(remote.path, '원격', 'doc.txt'));