128 lines
6.0 KiB
Dart
128 lines
6.0 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, {Size size = const Size(1000, 900)}) async {
|
|
tester.view.physicalSize = size;
|
|
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 (await tester.runAsync(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('풀 곳: 좁은 화면 (412) 은 글이 다 보이는 세로 목록 · 고른 곳에 풂 (15 캡처: 칩 글 잘림)', (tester) async {
|
|
final zip = p.join(tmp.path, 'sample.zip');
|
|
File(zip).writeAsBytesSync(ZipEncoder().encode(Archive()..addFile(ArchiveFile.string('a.txt', 'a'))));
|
|
final other = Directory(p.join(tmp.path, 'other'))..createSync();
|
|
final run = await host<String>(tester, size: const Size(412, 900));
|
|
String? out;
|
|
var finished = false;
|
|
run((ctx) => extractArchiveFlow(ctx, c, zip, otherDir: other.path)).then((v) {
|
|
out = v;
|
|
finished = true;
|
|
});
|
|
await wait(tester, () => find.text('풀기').evaluate().isNotEmpty);
|
|
expect(find.byType(ChoiceChip), findsNothing);
|
|
expect(find.byType(RadioListTile<String>), findsNWidgets(3));
|
|
expect(find.text('압축 옆 새 폴더 "sample"'), findsOneWidget);
|
|
await tester.tap(find.text('다른 창 폴더'));
|
|
await tester.pump();
|
|
await tester.tap(find.widgetWithText(FilledButton, '풀기'));
|
|
await wait(tester, () => finished);
|
|
expect(out, other.path);
|
|
expect(File(p.join(other.path, 'a.txt')).existsSync(), isTrue);
|
|
});
|
|
|
|
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);
|
|
});
|
|
}
|