118 lines
5.1 KiB
Dart
118 lines
5.1 KiB
Dart
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/core/explorer/tree_rows.dart';
|
|
import 'package:jj_mkvmaker/core/file_ops.dart' show FileEntry;
|
|
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/explorer/explorer_controller.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer/explorer_host.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer/explorer_pane.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer/explorer_select.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// E5d-2 약속 시험: lib/ui/explorer/explorer_select.dart 맨 위 약속 (실제 시험 폴더 · Host 는 실제 화면).
|
|
void main() {
|
|
late Directory tmp;
|
|
late String root, show, ep1, ep2;
|
|
setUp(() {
|
|
tmp = Directory.systemTemp.createTempSync('jj_sel_ui_');
|
|
root = tmp.path;
|
|
show = p.join(root, 'Show');
|
|
Directory(show).createSync();
|
|
ep1 = p.join(show, 'ep1.mkv');
|
|
ep2 = p.join(show, 'ep2.mkv');
|
|
File(ep1).writeAsStringSync('1');
|
|
File(ep2).writeAsStringSync('2');
|
|
});
|
|
tearDown(() => tmp.deleteSync(recursive: true));
|
|
|
|
var now = DateTime(2026, 10, 22, 9);
|
|
Future<(ExplorerSelect, ExplorerPane, List<String>)> open(WidgetTester t, {bool rsync = false}) async {
|
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
|
final key = GlobalKey<_HostState>();
|
|
await t.pumpWidget(MaterialApp(home: _Host(key: key)));
|
|
final ctl = ExplorerController(c, rsync: rsync, host: key.currentState!)..local = [(root, 'T:')];
|
|
final opened = <String>[];
|
|
final sel = ExplorerSelect(ctl, clock: () => now, openFile: (path, _) async => opened.add(path));
|
|
addTearDown(() {
|
|
sel.dispose();
|
|
ctl.dispose();
|
|
});
|
|
final q = ctl.panes[0];
|
|
await t.runAsync(() => ctl.goTo(q, root));
|
|
return (sel, q, opened);
|
|
}
|
|
|
|
TreeRow rowOf(String path, {bool dir = false}) => TreeRow(FileEntry(path, isDir: dir, modified: DateTime(0)), 1);
|
|
|
|
testWidgets('폴더 한 번 = 펼침 · 다시 = 접힘 (두 번 누르기의 두 번째는 아님) · 파일 한 번 = 지금 줄 · 두 번 = 열기', (t) async {
|
|
final (sel, q, opened) = await open(t);
|
|
await t.runAsync(() => sel.onTap(q, rowOf(show, dir: true)));
|
|
expect(q.expanded.contains(show), isTrue);
|
|
now = now.add(const Duration(milliseconds: 100));
|
|
await t.runAsync(() => sel.onTap(q, rowOf(show, dir: true)));
|
|
expect(q.expanded.contains(show), isTrue, reason: '두 번 누르기의 두 번째');
|
|
now = now.add(const Duration(seconds: 1));
|
|
await t.runAsync(() => sel.onTap(q, rowOf(show, dir: true)));
|
|
expect(q.expanded.contains(show), isFalse, reason: '규칙 3');
|
|
await t.runAsync(() => sel.onTap(q, rowOf(show, dir: true)));
|
|
await t.runAsync(() => sel.onTap(q, rowOf(ep1)));
|
|
expect([q.focused, opened], [ep1, <String>[]]);
|
|
await t.runAsync(() => sel.onOpen(q, rowOf(ep1)));
|
|
expect(opened, [ep1]);
|
|
});
|
|
|
|
testWidgets('선택 모드: 파일 고름 · 폴더 = 자기 + 안 · 범위 · 뒤집기 · 끄면 모두 풀림', (t) async {
|
|
final (sel, q, _) = await open(t);
|
|
sel.setSelecting(true);
|
|
await t.runAsync(() => sel.onTap(q, rowOf(show, dir: true)));
|
|
expect([sel.isMarked(q, ep1), q.expanded.contains(show)], [true, true]);
|
|
await t.runAsync(() => sel.onTap(q, rowOf(show, dir: true)));
|
|
expect(q.sel.isEmpty, isTrue);
|
|
await t.runAsync(() => sel.toggleMark(q, FileEntry(ep1, isDir: false, modified: DateTime(0))));
|
|
expect(sel.canRange(q, FileEntry(ep2, isDir: false, modified: DateTime(0))), isTrue);
|
|
sel.markRange(q, FileEntry(ep2, isDir: false, modified: DateTime(0)));
|
|
expect(q.sel.marked.length, 2);
|
|
sel.invertMarks(q, show);
|
|
expect(q.sel.isEmpty, isTrue);
|
|
sel.markWhere(q, show, (e) => e.path.endsWith('2.mkv'));
|
|
expect(q.sel.marked, [ep2]);
|
|
sel.setSelecting(false);
|
|
expect([sel.selecting, q.sel.isEmpty], [false, true]);
|
|
});
|
|
|
|
testWidgets('Rsync: 폴더만 하나 · 고르면 펼침', (t) async {
|
|
final (sel, q, _) = await open(t, rsync: true);
|
|
await t.runAsync(() => sel.onTap(q, rowOf(ep1)));
|
|
expect(q.sel.isEmpty, isTrue);
|
|
await t.runAsync(() => sel.onTap(q, rowOf(show, dir: true)));
|
|
expect([q.sel.marked, q.expanded.contains(show)], [
|
|
[show],
|
|
true
|
|
]);
|
|
});
|
|
}
|
|
|
|
class _Host extends StatefulWidget {
|
|
const _Host({super.key});
|
|
@override
|
|
State<_Host> createState() => _HostState();
|
|
}
|
|
|
|
class _HostState extends State<_Host> implements ExplorerHost {
|
|
@override
|
|
bool get alive => mounted;
|
|
@override
|
|
void snack(String text) => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(text)));
|
|
@override
|
|
void reveal(ExplorerPane pane, String path) {}
|
|
@override
|
|
void afterFrame(VoidCallback f) => WidgetsBinding.instance.addPostFrameCallback((_) => f());
|
|
@override
|
|
Widget build(BuildContext context) => const Scaffold(body: SizedBox());
|
|
}
|