JJ_MKVMaker/test/archive_ui_test.dart

105 lines
4.8 KiB
Dart

import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/app/app_controller.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/archive_ui.dart';
import 'package:path/path.dart' as p;
/// 86: 압축 풀기 · ZIP 묶기 화면 흐름 - 확인 창에 막은 경로 · 링크, 취소는 아무 일 없음, 묶기는 만든 ZIP
void main() {
late Directory tmp;
late AppController c;
setUp(() {
tmp = Directory.systemTemp.createTempSync('jj_arch_ui_');
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
});
tearDown(() => tmp.deleteSync(recursive: true));
Future<void> wait(WidgetTester tester, bool Function() done) async {
for (var i = 0; i < 200 && !done(); i++) {
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 20)));
await tester.pump();
}
}
Future<Future<T?> Function(Future<T?> Function(BuildContext))> host<T>(WidgetTester tester) async {
tester.view.physicalSize = const Size(1000, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
final key = GlobalKey<NavigatorState>();
await tester.pumpWidget(MaterialApp(navigatorKey: key, home: const Scaffold()));
return (Future<T?> Function(BuildContext) f) => f(key.currentContext!);
}
testWidgets('풀기: 확인 창에 막은 경로 · 건너뛸 링크를 보이고, 풀면 대상 안에만', (tester) async {
final a = Archive()
..addFile(ArchiveFile.string('ok.txt', 'ok'))
..addFile(ArchiveFile.string('../evil.txt', 'evil'))
..addFile(ArchiveFile.string('lnk', '../x')..mode = 0xA1FF);
final zip = p.join(tmp.path, 'bad.zip');
File(zip).writeAsBytesSync(ZipEncoder().encode(a));
final run = await host<String>(tester);
String? out;
var finished = false;
run((ctx) => extractArchiveFlow(ctx, c, zip)).then((v) {
out = v;
finished = true;
});
await wait(tester, () => find.text('풀기').evaluate().isNotEmpty);
expect(find.textContaining('밖으로 나가는 경로라 풀지 않습니다 (1개)'), findsOneWidget);
expect(find.textContaining('링크 1개는 만들지 않고 건너뜁니다'), findsOneWidget);
await tester.tap(find.widgetWithText(FilledButton, '풀기'));
await wait(tester, () => finished);
expect(out, p.join(tmp.path, 'bad'));
expect(File(p.join(tmp.path, 'bad', 'ok.txt')).readAsStringSync(), 'ok');
expect(File(p.join(tmp.path, 'evil.txt')).existsSync(), isFalse);
});
testWidgets('풀기 [취소] 는 아무 일 없음 · 7z 를 풀 수 없는 기기면 다른 앱 안내 (막지 않음)', (tester) async {
final zip = p.join(tmp.path, 'a.zip');
File(zip).writeAsBytesSync(ZipEncoder().encode(Archive()..addFile(ArchiveFile.string('a.txt', 'a'))));
final run = await host<String>(tester);
var finished = false;
run((ctx) => extractArchiveFlow(ctx, c, zip)).then((_) => finished = true);
await wait(tester, () => find.text('풀기').evaluate().isNotEmpty);
await tester.tap(find.text('취소'));
await wait(tester, () => finished);
expect(Directory(p.join(tmp.path, 'a')).existsSync(), isFalse);
if (bundledSevenZip() == null) {
final sz = File(p.join(tmp.path, 'x.7z'))..writeAsStringSync('7z');
finished = false;
run((ctx) => extractArchiveFlow(ctx, c, sz.path)).then((_) => finished = true);
await wait(tester, () => find.text('다른 앱으로 열기').evaluate().isNotEmpty);
expect(find.textContaining('다른 앱으로 열어 주세요'), findsOneWidget);
await tester.tap(find.text('닫기'));
await wait(tester, () => finished);
}
});
testWidgets('ZIP 으로 묶기: 이름 · 압축 정도 (기억) → 만든 ZIP', (tester) async {
final src = Directory(p.join(tmp.path, 'photos'))..createSync();
File(p.join(src.path, 'a.txt')).writeAsStringSync('A');
final run = await host<String>(tester);
String? made;
var finished = false;
run((ctx) => createZipFlow(ctx, c, [src.path], dir: tmp.path)).then((v) {
made = v;
finished = true;
});
await wait(tester, () => find.text('묶기').evaluate().isNotEmpty);
expect(find.text('만들 파일: photos.zip'), findsOneWidget);
await tester.tap(find.text('작게'));
await tester.pump();
await tester.tap(find.widgetWithText(FilledButton, '묶기'));
await wait(tester, () => finished);
expect(made, p.join(tmp.path, 'photos.zip'));
expect(c.settings.zipLevel, 9, reason: '고른 압축 정도를 기억');
expect(File(made!).lengthSync() > 0, isTrue);
});
}