JJ_MKVMaker/test/explorer_hidden_find_test.dart

304 lines
17 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/app/settings.dart';
import 'package:jj_mkvmaker/ui/explorer_page.dart';
import 'package:jj_mkvmaker/ui/hidden_items_dialog.dart';
import 'package:jj_mkvmaker/ui/explorer_search.dart';
import 'package:path/path.dart' as p;
import 'support/explorer_clock_keys.dart';
import 'support/explorer_harness.dart';
/// E5e: 예전 explorer_test.dart 에서 나눈 탐색기 화면 시험 - 목록에서 숨기기 · .nomedia · 폴더 크기 · 찾기 결과
void main() {
useExplorerFixture();
testWidgets('X-plore 숨기기: 길게 누르기 [목록에서 숨기기] → 목록에서만 빠짐 (파일 그대로) · 맨 아래 [숨긴 항목 보이기] · [숨기기 풀기]', (tester) async {
// 폴더 + 파일 목록 배치: 오른쪽 목록은 그 폴더의 것만이라 짧다 (트리는 Temp 폴더 줄이 많아 맨 아래가 멀다)
c.settings.explorerLayout = 'split';
await open(tester);
final doc = p.join(left, 'doc.txt');
await tester.longPress(find.text('doc.txt'));
await calm(tester);
await act(tester, () => tester.tap(find.text('목록에서 숨기기')));
await calm(tester);
// X-plore 맞추기 (관리자 지시 10/20): 무엇이 바뀌는지 확인 창
expect(find.textContaining('파일 탐색기 목록에서만 빠집니다'), findsOneWidget);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '숨기기')));
await settle(tester, () => find.text('doc.txt').evaluate().isEmpty);
expect(find.text('doc.txt'), findsNothing);
expect(File(doc).existsSync(), isTrue, reason: '파일은 바꾸지 않는다');
expect(c.settings.explorerHiddenItems, [doc]);
// 목록 맨 아래에 [숨긴 항목 1개 보이기]
expect(find.text('숨긴 항목 1개 보이기'), findsOneWidget);
await idle(tester);
// 보이기 → 흐리게 보이고, 메뉴는 [숨기기 풀기]
await act(tester, () => tester.tap(find.text('숨긴 항목 1개 보이기')));
await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty);
expect(find.text('숨긴 항목 1개 다시 숨기기'), findsOneWidget);
expect(find.ancestor(of: find.text('doc.txt'), matching: find.byType(Opacity)), findsOneWidget);
await tester.longPress(find.text('doc.txt'));
await calm(tester);
expect(find.text('목록에서 숨기기'), findsNothing);
await act(tester, () => tester.tap(find.text('숨기기 풀기')));
await settle(tester, () => find.textContaining('숨긴 항목').evaluate().isEmpty);
expect(c.settings.explorerHiddenItems, isEmpty);
expect(find.text('doc.txt'), findsOneWidget);
expect(find.ancestor(of: find.text('doc.txt'), matching: find.byType(Opacity)), findsNothing);
});
testWidgets('X-plore 숨기기 확인 창: [취소] 면 그대로 · [다음부터 묻지 않기] 를 고르면 다음엔 묻지 않고 바로 숨김 (설정 저장)', (tester) async {
c.settings.explorerLayout = 'split';
expect(AppSettings().explorerHideConfirm, isTrue, reason: '처음은 묻기');
File(p.join(left, 'two.txt')).writeAsStringSync('2');
await open(tester);
await tester.longPress(find.text('doc.txt'));
await calm(tester);
await act(tester, () => tester.tap(find.text('목록에서 숨기기')));
await calm(tester);
await act(tester, () => tester.tap(find.text('취소')));
await calm(tester);
expect(c.settings.explorerHiddenItems, isEmpty, reason: '취소하면 숨기지 않는다');
expect(find.text('doc.txt'), findsOneWidget);
// 다음부터 묻지 않기 + 숨기기
await tester.longPress(find.text('doc.txt'));
await calm(tester);
await act(tester, () => tester.tap(find.text('목록에서 숨기기')));
await calm(tester);
await act(tester, () => tester.tap(find.text('다음부터 묻지 않기')));
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '숨기기')));
await settle(tester, () => find.text('doc.txt').evaluate().isEmpty);
expect(c.settings.explorerHideConfirm, isFalse);
expect(AppSettings.fromJson(c.settings.toJson()).explorerHideConfirm, isFalse);
expect(AppSettings.fromJson({}).explorerHideConfirm, isTrue, reason: '예전 설정 (값 없음) 은 묻기');
// 이제는 창 없이 바로
await tester.longPress(find.text('two.txt'));
await calm(tester);
await act(tester, () => tester.tap(find.text('목록에서 숨기기')));
await settle(tester, () => find.text('two.txt').evaluate().isEmpty);
expect(find.textContaining('파일 탐색기 목록에서만 빠집니다'), findsNothing);
expect(c.settings.explorerHiddenItems, [p.join(left, 'doc.txt'), p.join(left, 'two.txt')]);
});
testWidgets('X-plore 맞추기: 폴더 길게 누르기 [갤러리 · 음악 앱에서 숨기기 (.nomedia)] - 확인 창 뒤 빈 .nomedia, 다시 누르면 지우기 · 안의 파일은 그대로', (tester) async {
device.nomediaOn = true;
final mark = File(p.join(left, 'sub', '.nomedia'));
await open(tester);
expect(find.text('갤러리 · 음악 앱에서 숨기기 (.nomedia)'), findsNothing);
await tester.longPress(find.text('sub'));
await calm(tester);
await act(tester, () => tester.tap(find.text('갤러리 · 음악 앱에서 숨기기 (.nomedia)')));
await calm(tester);
expect(find.textContaining('빈 .nomedia 파일을 만듭니다'), findsOneWidget);
// 취소하면 아무것도 안 바뀜
await act(tester, () => tester.tap(find.text('취소')));
await calm(tester);
expect(mark.existsSync(), isFalse);
await tester.longPress(find.text('sub'));
await calm(tester);
await act(tester, () => tester.tap(find.text('갤러리 · 음악 앱에서 숨기기 (.nomedia)')));
await calm(tester);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '숨기기')));
await settle(tester, () => mark.existsSync() && shell.scanned.isNotEmpty);
expect(mark.lengthSync(), 0);
expect(shell.scanned, [
[mark.path, p.join(left, 'sub')]
], reason: '갤러리가 다시 읽게 (MediaScanner)');
expect(File(p.join(left, 'sub', 'inner.txt')).readAsStringSync(), 'inner', reason: '안의 파일은 그대로');
// 이제 메뉴는 [다시 보이기]
await idle(tester);
await tester.longPress(find.text('sub'));
await calm(tester);
expect(find.text('갤러리 · 음악 앱에서 숨기기 (.nomedia)'), findsNothing);
await act(tester, () => tester.tap(find.text('갤러리 · 음악 앱에 다시 보이기 (.nomedia 지우기)')));
await calm(tester);
expect(find.textContaining('.nomedia 파일을 지웁니다'), findsOneWidget);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '다시 보이기')));
await settle(tester, () => !mark.existsSync());
await settle(tester, () => shell.scanned.length == 2);
expect(shell.scanned.last, [mark.path, p.join(left, 'sub')], reason: '지울 때도 다시 읽게');
expect(File(p.join(left, 'sub', 'inner.txt')).existsSync(), isTrue);
// 파일에는 없음
await idle(tester);
await tester.longPress(find.text('doc.txt'));
await calm(tester);
expect(find.text('갤러리 · 음악 앱에서 숨기기 (.nomedia)'), findsNothing);
expect(find.text('갤러리 · 음악 앱에 다시 보이기 (.nomedia 지우기)'), findsNothing);
});
testWidgets('X-plore 숨기기: 숨긴 것이 든 폴더를 지울 때 확인 창에 알림 (숨긴 것도 함께 지워진다)', (tester) async {
c.settings.explorerHiddenItems = [p.join(left, 'sub', 'inner.txt')];
await open(tester);
await tester.longPress(find.text('sub'));
await calm(tester);
await act(tester, () => tester.tap(find.byTooltip('삭제').last));
await calm(tester);
expect(find.text('지우는 폴더 안에 목록에서 숨긴 항목 1개가 있습니다 - 함께 지워집니다'), findsOneWidget);
await act(tester, () => tester.tap(find.text('취소').last));
await calm(tester);
expect(File(p.join(left, 'sub', 'inner.txt')).existsSync(), isTrue);
// 다른 파일만 지울 때는 알리지 않는다
await tester.longPress(find.text('doc.txt'));
await calm(tester);
await act(tester, () => tester.tap(find.byTooltip('삭제').last));
await calm(tester);
expect(find.textContaining('목록에서 숨긴 항목'), findsNothing);
await act(tester, () => tester.tap(find.text('취소').last));
await calm(tester);
});
testWidgets('X-plore 숨기기: 이름을 바꾸면 숨긴 목록도 새 경로로 · 영구로 지우면 목록에서 뺀다 (관리자 결정)', (tester) async {
c.settings.explorerHiddenItems = [p.join(left, 'sub', 'inner.txt')];
await open(tester);
await tester.longPress(find.text('sub'));
await calm(tester);
await act(tester, () => tester.tap(find.byTooltip('이름 변경').last));
await calm(tester);
await act(tester, () => tester.enterText(find.byType(TextField), 'sub2'));
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '확인')));
await settle(tester, () => find.text('sub2').evaluate().isNotEmpty);
expect(c.settings.explorerHiddenItems, [p.join(left, 'sub2', 'inner.txt')]);
await idle(tester);
await tester.longPress(find.text('sub2'));
await calm(tester);
await act(tester, () => tester.tap(find.byTooltip('삭제').last));
await calm(tester);
await tester.tap(find.text('휴지통을 거치지 않고 영구 삭제 (Shift+Delete)'));
await calm(tester);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '영구 삭제')));
await settle(tester, () => find.text('sub2').evaluate().isEmpty);
await settle(tester, () => c.settings.explorerHiddenItems.isEmpty);
expect(c.settings.explorerHiddenItems, isEmpty);
});
testWidgets('X-plore 숨기기: 다른 창으로 옮기면 숨긴 목록도 새 자리로 (그 안의 것도)', (tester) async {
c.settings.explorerHiddenItems = [p.join(left, 'sub', 'inner.txt')];
await open(tester);
await tester.longPress(find.text('sub'));
await calm(tester);
await act(tester, () => tester.tap(find.byTooltip('다른 창으로 이동').last));
await settle(tester, () => find.widgetWithText(FilledButton, '이동').evaluate().isNotEmpty);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '이동')));
await settle(tester, () => c.settings.explorerHiddenItems.first != p.join(left, 'sub', 'inner.txt'));
expect(File(p.join(right, 'sub', 'inner.txt')).existsSync(), isTrue);
expect(c.settings.explorerHiddenItems, [p.join(right, 'sub', 'inner.txt')]);
await idle(tester);
});
testWidgets('X-plore 숨기기: 위 막대 ⋮ [목록에서 숨긴 항목 보이기] · 환경 설정의 [목록에서 숨긴 항목] 에서 풀기', (tester) async {
final doc = p.join(left, 'doc.txt');
c.settings
..explorerLayout = 'split'
..explorerHiddenItems = [doc];
tester.view.physicalSize = const Size(1600, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(device: device, c: c))));
await settle(tester, () => find.text('숨긴 항목 1개 보이기').evaluate().isNotEmpty);
expect(find.text('doc.txt'), findsNothing);
await act(tester, () => tester.tap(find.byTooltip('창 배치 · 버튼 구성')));
await calm(tester);
await act(tester, () => tester.tap(find.text('목록에서 숨긴 항목 보이기 (1개)')));
await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty);
expect(find.text('숨긴 항목 1개 다시 숨기기'), findsOneWidget);
await act(tester, () => tester.tap(find.byTooltip('창 배치 · 버튼 구성')));
await calm(tester);
await act(tester, () => tester.tap(find.text('목록에서 숨긴 항목 다시 숨기기')));
await settle(tester, () => find.text('doc.txt').evaluate().isEmpty);
// 설정 화면의 목록에서 풀면 탐색기 목록에 바로 보인다
final ctx = tester.element(find.byType(ExplorerPage));
unawaited(showHiddenItems(ctx, c));
await calm(tester);
expect(find.text('목록에서 숨긴 항목 (1개)'), findsOneWidget);
await act(tester, () => tester.tap(find.byTooltip('숨기기 풀기')));
await calm(tester);
expect(c.settings.explorerHiddenItems, isEmpty);
expect(find.text('숨긴 항목이 없습니다'), findsOneWidget);
await act(tester, () => tester.tap(find.text('닫기')));
await settle(tester, () => find.text('doc.txt').evaluate().isNotEmpty);
expect(find.textContaining('숨긴 항목'), findsNothing);
});
testWidgets('80: 크기순 정렬이면 폴더 크기를 계산해 보이고, "안 함" 이어도 길게 누르기 [폴더 크기 계산]', (tester) async {
File(p.join(left, 'sub', 'big.bin')).writeAsBytesSync(List.filled(3000, 1));
c.settings
..explorerSort = 'size'
..explorerSortDesc = true;
await open(tester);
// sub = inner.txt (5) + big.bin (3000)
await settle(tester, () => find.text('3KB').evaluate().isNotEmpty);
expect(find.text('3KB'), findsWidgets);
// 바뀌면 (새로 고침) 다시 계산
File(p.join(left, 'sub', 'more.bin')).writeAsBytesSync(List.filled(3000, 1));
await act(tester, () => refreshKey(tester));
await settle(tester, () => find.text('6KB').evaluate().isNotEmpty);
expect(find.text('6KB'), findsWidgets);
});
testWidgets('80: "계산 안 함" - 크기를 보이지 않다가 [폴더 크기 계산] 을 누르면', (tester) async {
File(p.join(left, 'sub', 'big.bin')).writeAsBytesSync(List.filled(3000, 1));
c.settings
..explorerSort = 'size'
..explorerFolderSize = 'off';
await open(tester);
await settle(tester, () => false, rounds: 15);
expect(find.text('3KB'), findsNothing);
await tester.longPress(find.text('sub').first);
await calm(tester);
await act(tester, () => tester.tap(find.text('폴더 크기 계산')));
await settle(tester, () => find.text('3KB').evaluate().isNotEmpty);
expect(find.text('3KB'), findsWidgets);
});
testWidgets('79-a: [그 자리로] 로 나가도 [찾기 결과로 돌아가기] 가 남는다 (관리자 판단) · ✕ 로 닫음', (tester) async {
await open(tester);
await act(tester, () => tester.tap(find.text('찾기').first));
await calm(tester);
await tester.enterText(find.widgetWithText(TextField, '이름 (비워도 됨)'), 'inner');
await tester.pump();
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '찾기')));
await settle(tester, () => find.text('찾기 결과 · 1개').evaluate().isNotEmpty);
await act(tester, () => tester.tap(find.byTooltip('그 자리로')));
await settle(tester, () => find.text('찾기 결과로 돌아가기').evaluate().isNotEmpty);
await calm(tester); // 결과 화면이 닫히는 움직임까지
expect(find.byType(SearchResultsPage), findsNothing);
await act(tester, () => tester.tap(find.text('찾기 결과로 돌아가기')));
await settle(tester, () => find.text('찾기 결과 · 1개').evaluate().isNotEmpty);
// 결과 화면을 그냥 닫으면 없어진다
await act(tester, () => tester.pageBack());
await calm(tester);
expect(find.text('찾기 결과로 돌아가기'), findsNothing);
});
testWidgets('79: 찾기 결과를 목록으로 - 이름 없이 종류 조건으로 찾고, 골라서 그대로 다른 창으로 복사', (tester) async {
await open(tester);
File(p.join(left, 'sub', 'big.mkv'))
..createSync(recursive: true)
..writeAsBytesSync(List.filled(2048, 1));
await act(tester, () => tester.tap(find.text('찾기').first));
await calm(tester);
// 이름은 비우고 종류만 (동영상) - 이름이 비면 [찾기] 가 꺼져 있다가 조건을 넣으면 켜진다
expect(tester.widget<FilledButton>(find.widgetWithText(FilledButton, '찾기')).onPressed, isNull);
await act(tester, () => tester.tap(find.widgetWithText(ChoiceChip, '동영상')));
await calm(tester);
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '찾기')));
await settle(tester, () => find.text('big.mkv').evaluate().isNotEmpty);
expect(find.text('찾기 결과 · 1개'), findsOneWidget);
// (뒤의 탐색기 목록에는 doc.txt 가 있다 - 결과 화면 안만 본다)
expect(find.descendant(of: find.byType(SearchResultsPage), matching: find.text('doc.txt')), findsNothing, reason: '종류가 맞지 않음');
// 고르면 아래에 할 일 줄 - 다른 창 (right) 으로 복사
await act(tester, () => tester.tap(find.descendant(of: find.byType(SearchResultsPage), matching: find.byType(Checkbox)).first));
await calm(tester);
await act(tester, () => tester.tap(find.text('복사 → right')));
await calm(tester);
// 탐색기의 복사 확인 창 그대로
await act(tester, () => tester.tap(find.widgetWithText(FilledButton, '복사')));
await settle(tester, () => File(p.join(right, 'big.mkv')).existsSync() && find.byType(LinearProgressIndicator).evaluate().isEmpty);
expect(File(p.join(right, 'big.mkv')).lengthSync(), 2048);
expect(File(p.join(left, 'sub', 'big.mkv')).existsSync(), isTrue);
});
}