183 lines
8.1 KiB
Dart
183 lines
8.1 KiB
Dart
import 'dart:io';
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:archive/archive_io.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:integration_test/integration_test.dart';
|
|
import 'package:jj_mkvmaker/app/app_controller.dart';
|
|
import 'package:jj_mkvmaker/app/i18n_controller.dart';
|
|
import 'package:jj_mkvmaker/core/file_ops.dart';
|
|
import 'package:jj_mkvmaker/core/models.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:jj_mkvmaker/ui/batch_rename_dialog.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer_drag.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer_page.dart' show TransferProgressChip;
|
|
import 'package:jj_mkvmaker/ui/explorer_search.dart';
|
|
import 'package:jj_mkvmaker/ui/quick_look.dart';
|
|
import 'package:jj_mkvmaker/ui/storage_page.dart';
|
|
import 'package:jj_mkvmaker/ui/theme.dart';
|
|
import 'support/temp_dir.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// 관리자 지시 5 (10/17): 기기 없이 15 가 볼 수 있게 묶음6 새 화면을 PNG 로 (JJ_SHOT_DIR, 없으면 임시 폴더).
|
|
/// 78 이름 일괄 창 · 79 찾기 결과 · 81 빠른 보기 · 82 놓기 묻기 창 · 85 작은 표시 (보통 · 멈춤) · 86 풀기 확인 창 (경고 줄) · 87 분석 화면.
|
|
/// 크기 412 (폰 세로) · 960, 언어 ko · en · ja. 시험 파일은 임시 폴더에만 (끝나면 지움), 설정은 메모리에만.
|
|
void main() {
|
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
final out = Platform.environment['JJ_SHOT_DIR'] ?? p.join(Directory.systemTemp.path, 'jj_shots_b6');
|
|
|
|
for (final lang in ['ko', 'en', 'ja']) {
|
|
for (final (w, h) in [(412, 900), (960, 600)]) {
|
|
testWidgets('묶음6 화면 $lang ${w}x$h', (t) async {
|
|
t.view.physicalSize = Size(w.toDouble(), h.toDouble());
|
|
t.view.devicePixelRatio = 1;
|
|
addTearDown(t.view.reset);
|
|
await t.runAsync(() => i18n.apply(lang, save: false));
|
|
addTearDown(() => i18n.apply('ko', save: false));
|
|
final tmp = testTempDir('jj_b6shot_');
|
|
// 시험 파일
|
|
File(p.join(tmp.path, 'Show', 'ep01.mkv'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(List.filled(4000, 0));
|
|
File(p.join(tmp.path, 'Show', 'ep02.mkv')).writeAsBytesSync(List.filled(3000, 0));
|
|
File(p.join(tmp.path, 'Show', 'Show 01.mkv')).writeAsBytesSync(List.filled(10, 0));
|
|
File(p.join(tmp.path, 'Show', 'ep01.srt')).writeAsStringSync('1\n00:00:01,000 --> 00:00:02,000\n안녕 hello\n');
|
|
File(p.join(tmp.path, 'docs', 'note.txt'))
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('메모 note');
|
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
|
final navKey = GlobalKey<NavigatorState>();
|
|
final shotKey = GlobalKey();
|
|
await t.pumpWidget(RepaintBoundary(
|
|
key: shotKey,
|
|
// DEBUG 띠가 오른쪽 위 단추 (79 [모두 선택]) 를 가리지 않게 (15 지적)
|
|
child: MaterialApp(
|
|
theme: buildTheme(), debugShowCheckedModeBanner: false, navigatorKey: navKey, home: const Scaffold(body: SizedBox.expand())),
|
|
));
|
|
BuildContext ctx() => navKey.currentContext!;
|
|
|
|
Future<void> settle([int n = 8]) async {
|
|
for (var i = 0; i < n; i++) {
|
|
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 60)));
|
|
await t.pump();
|
|
}
|
|
}
|
|
|
|
Future<void> shot(String name) async {
|
|
await settle(3);
|
|
final b = shotKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
|
final img = await t.runAsync(() => b.toImage());
|
|
final bytes = await t.runAsync(() => img!.toByteData(format: ui.ImageByteFormat.png));
|
|
Directory(out).createSync(recursive: true);
|
|
File(p.join(out, 'b6_${name}_${lang}_$w.png')).writeAsBytesSync(bytes!.buffer.asUint8List());
|
|
}
|
|
|
|
Future<void> closeAll() async {
|
|
navKey.currentState!.popUntil((r) => r.isFirst);
|
|
await settle(4);
|
|
}
|
|
|
|
// 78 이름 일괄 바꾸기 - 틀 "Show [C]" (첫 번째가 고르지 않은 "Show 01.mkv" 와 겹쳐 빨갛게)
|
|
final eps = [p.join(tmp.path, 'Show', 'ep01.mkv'), p.join(tmp.path, 'Show', 'ep02.mkv')];
|
|
showBatchRename(ctx(), eps, isDir: (_) => false);
|
|
await settle();
|
|
await t.enterText(find.byType(TextField).first, 'Show [C]');
|
|
await settle();
|
|
await shot('78_rename');
|
|
await closeAll();
|
|
|
|
// 79 찾기 결과 - 하나 골라 아래 할 일 줄까지
|
|
navKey.currentState!.push(MaterialPageRoute<void>(
|
|
builder: (_) => SearchResultsPage(
|
|
root: tmp.path,
|
|
cond: const SearchConditions(name: 'ep'),
|
|
showHidden: false,
|
|
otherDir: p.join(tmp.path, 'docs'),
|
|
onOpen: (_) async {},
|
|
),
|
|
));
|
|
await settle(12);
|
|
await t.tap(find.byType(Checkbox).first);
|
|
await settle();
|
|
await shot('79_search');
|
|
await closeAll();
|
|
|
|
// 81 빠른 보기 - 자막 (글 미리 보기)
|
|
final list = await t.runAsync(() => listEntries(p.join(tmp.path, 'Show')));
|
|
final srt = list!.indexWhere((e) => e.name == 'ep01.srt');
|
|
showDialog<void>(
|
|
context: ctx(),
|
|
builder: (_) => QuickLook(
|
|
entries: list,
|
|
index: srt,
|
|
probe: (_) async => const MediaInfo(streams: []),
|
|
thumbnail: (_) async => null,
|
|
imageExts: const ['jpg', 'png'],
|
|
onOpen: (_) {},
|
|
),
|
|
);
|
|
await settle(10);
|
|
await shot('81_quicklook');
|
|
await closeAll();
|
|
|
|
// 82 놓기 묻기 창
|
|
askDropAction(ctx(), eps, p.join(tmp.path, 'docs'));
|
|
await settle();
|
|
await shot('82_drop_ask');
|
|
await closeAll();
|
|
|
|
// 85 작은 표시 - 보통 · 멈춤
|
|
for (final (name, failed) in [('85_chip', false), ('85_chip_failed', true)]) {
|
|
navKey.currentState!.push(MaterialPageRoute<void>(
|
|
builder: (_) => Scaffold(
|
|
body: Align(
|
|
alignment: Alignment.bottomRight,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8),
|
|
child: TransferProgressChip(running: failed ? 0 : 2, percent: failed ? null : 45, failed: failed, onTap: () {}),
|
|
),
|
|
),
|
|
),
|
|
));
|
|
await settle();
|
|
await shot(name);
|
|
await closeAll();
|
|
}
|
|
|
|
// 86 풀기 확인 창 - 밖으로 나가는 경로 · 링크 · 쓸 수 없는 이름 · 덮어쓰기 경고
|
|
final a = Archive()
|
|
..addFile(ArchiveFile.string('ok/a.txt', 'a'))
|
|
..addFile(ArchiveFile.string('../evil.txt', 'x'))
|
|
..addFile(ArchiveFile.string('CON.txt', 'x'))
|
|
..addFile(ArchiveFile.string('lnk', '../x')..mode = 0xA1FF);
|
|
final zip = p.join(tmp.path, 'sample.zip');
|
|
File(zip).writeAsBytesSync(ZipEncoder().encode(a));
|
|
extractArchiveFlow(ctx(), c, zip);
|
|
await settle(15);
|
|
await shot('86_extract_confirm');
|
|
// 같은 이름: 덮어쓰기 → "되돌릴 수 없습니다"
|
|
await t.tap(find.byType(DropdownButton<NameConflict>));
|
|
await settle();
|
|
await t.tap(find.text(lang == 'ko' ? '덮어쓰기' : (lang == 'en' ? 'Overwrite' : '上書き')).last);
|
|
await settle();
|
|
await shot('86_extract_overwrite');
|
|
await closeAll();
|
|
|
|
// 87 분석 화면 - 큰 폴더 · 큰 파일
|
|
navKey.currentState!.push(MaterialPageRoute<void>(builder: (_) => StorageAnalysisPage(root: tmp.path)));
|
|
await settle(15);
|
|
await shot('87_storage_folders');
|
|
await t.tap(find.byType(ChoiceChip).last);
|
|
await settle();
|
|
await shot('87_storage_files');
|
|
await closeAll();
|
|
});
|
|
}
|
|
}
|
|
}
|