JJ_MKVMaker/integration_test/windows_move_left_shot_test...

132 lines
7.4 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'dart:io';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.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/l10n/tr.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/explorer_page.dart';
import 'package:jj_mkvmaker/ui/theme.dart';
import 'package:path/path.dart' as p;
import 'support/temp_dir.dart';
import '../test/support/test_explorer_device.dart';
/// 264 · 265 (관리자 지시 10/21): 폴더를 합쳐 옮긴 뒤 - 알림에 "n개는 원본에 남음 (건너뜀)" · 펼쳐 둔 원본 폴더를 다시 읽어
/// 옮겨진 파일은 보이지 않고 남은 것만 보인다. ko · en · ja × 960 · 1400 (두 창). 시험 폴더 안에서만 (실제 이동, 휴지통 쓰지 않음)
void main() {
late TestExplorerDevice device;
setUp(() => device = TestExplorerDevice());
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
final out = Platform.environment['JJ_SHOT_DIR'] ?? '';
// 두 모양: X-plore (예전 캡처 그대로) · Total Commander (Windows 처음 값 - 관리자 10/24: 사용자가 TC 로 쓰므로 같은 일을 TC 로도)
for (final style in ['xplore', 'totalcmd']) {
final tc = style == 'totalcmd';
for (final lang in ['ko', 'en', 'ja']) {
for (final (w, h) in [(960, 600), (1400, 800)]) {
testWidgets('264 · 265 이동 뒤 $style $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_moveleft_');
final left = p.join(tmp.path, 'left'), right = p.join(tmp.path, 'right');
final d = p.join(left, 'D');
File(p.join(d, 'same.txt'))
..createSync(recursive: true)
..writeAsStringSync('old')
..setLastModifiedSync(DateTime(2026, 1, 1));
File(p.join(d, 'add.txt')).writeAsStringSync('a');
File(p.join(right, 'D', 'same.txt'))
..createSync(recursive: true)
..writeAsStringSync('NEWER');
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings
..explorer.style = style
..explorer.paths = [left, right]
..copy.conflict = 'overwrite';
final root = p.rootPrefix(tmp.path);
device.volumes = () => [(root, root.replaceAll(RegExp(r'[\\/]+$'), ''))];
final shotKey = GlobalKey();
await t.pumpWidget(RepaintBoundary(
key: shotKey,
child: MaterialApp(theme: buildTheme(), debugShowCheckedModeBanner: false, home: ExplorerPage(device: device, c: c)),
));
Future<void> settle(bool Function() done, {int rounds = 150}) async {
for (var i = 0; i < rounds && !done(); i++) {
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 40)));
await t.pump();
}
await t.pump(const Duration(milliseconds: 300));
}
Future<void> shot(String name) async {
expect(t.takeException(), isNull, reason: '$name 넘침');
if (out.isEmpty) return;
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, 'moveleft_${tc ? 'tc_' : ''}${name}_${lang}_$w.png')).writeAsBytesSync(bytes!.buffer.asUint8List());
}
if (!tc) {
await settle(() => find.text('D').evaluate().length >= 2);
// 왼쪽 D 를 눌러 펼치고 (고른 것) 안의 add.txt 가 보이게
await t.tap(find.text('D').first);
await settle(() => find.text('add.txt').evaluate().isNotEmpty);
await shot('1_before');
await t.tap(find.text(tr('이동')).first);
await settle(() => find.widgetWithText(FilledButton, tr('이동')).evaluate().isNotEmpty);
await t.tap(find.widgetWithText(FilledButton, tr('이동')));
await settle(() => find.byType(LinearProgressIndicator).evaluate().isEmpty && find.byType(SnackBar).evaluate().isNotEmpty);
await settle(() => false, rounds: 10); // 알림이 다 올라오게
expect(File(p.join(right, 'D', 'add.txt')).existsSync(), isTrue);
expect(File(p.join(d, 'same.txt')).readAsStringSync(), 'old', reason: '받는 쪽이 더 새것이라 원본에 남음');
expect(find.textContaining(trf('{0}개는 원본에 남았습니다 (건너뜀).', [1])), findsOneWidget, reason: '264: 알림에');
expect(find.text('add.txt'), findsNothing, reason: '265: 원본 D 를 다시 읽음');
expect(find.text('same.txt'), findsOneWidget);
} else {
// TC: 폴더는 [D], 파일은 이름 · 확장자가 다른 칸 (좁으면 한 칸) - 이름 글로 찾는다
Finder named(String stem, String ext) =>
find.byWidgetPredicate((w) => w is Text && (w.data == stem || w.data == '$stem.$ext'));
await settle(() => find.text('[D]').evaluate().length >= 2);
// 왼쪽 [D] 를 한 번 눌러 고르고 (TC: 한 번 = 고르기) F6 = 이동
await t.tap(find.text('[D]').first);
await settle(() => false, rounds: 5);
await shot('1_before');
await t.sendKeyEvent(LogicalKeyboardKey.f6);
await settle(() => find.widgetWithText(FilledButton, tr('이동')).evaluate().isNotEmpty);
await t.tap(find.widgetWithText(FilledButton, tr('이동')));
await settle(() => find.byType(LinearProgressIndicator).evaluate().isEmpty && find.byType(SnackBar).evaluate().isNotEmpty);
await settle(() => false, rounds: 10); // 알림이 다 올라오게
expect(File(p.join(right, 'D', 'add.txt')).existsSync(), isTrue);
expect(File(p.join(d, 'same.txt')).readAsStringSync(), 'old', reason: '받는 쪽이 더 새것이라 원본에 남음');
expect(find.textContaining(trf('{0}개는 원본에 남았습니다 (건너뜀).', [1])), findsOneWidget, reason: '264: 알림에');
// 265: 원본 D 로 들어가면 (TC: Enter = 열기) 옮겨진 것은 없고 남은 것만
ScaffoldMessenger.of(t.element(find.byType(ExplorerPage))).clearSnackBars();
await t.tap(find.text('[D]').first);
await settle(() => false, rounds: 5);
await t.sendKeyEvent(LogicalKeyboardKey.enter);
await settle(() => named('same', 'txt').evaluate().isNotEmpty);
expect(named('add', 'txt'), findsNothing, reason: '265: 원본 D 를 다시 읽음');
expect(named('same', 'txt'), findsOneWidget);
}
await shot('2_after');
ScaffoldMessenger.of(t.element(find.byType(ExplorerPage))).clearSnackBars();
await settle(() => false, rounds: 20);
});
}
}
}
}