JJ_MKVMaker/integration_test/windows_bigtext_shot_test.dart

127 lines
7.0 KiB
Dart
Raw 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_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:jj_mkvmaker/app/app_controller.dart';
import 'package:jj_mkvmaker/app/download_manager.dart';
import 'package:jj_mkvmaker/app/i18n_controller.dart';
import 'package:jj_mkvmaker/app/settings.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/downloads_page.dart';
import 'package:jj_mkvmaker/ui/explorer_page.dart';
import 'package:jj_mkvmaker/ui/home_page.dart';
import 'package:jj_mkvmaker/ui/monitor_page.dart';
import 'package:jj_mkvmaker/ui/settings_page.dart';
import 'package:jj_mkvmaker/ui/theme.dart';
import 'package:path/path.dart' as p;
import 'support/shot_services.dart';
import 'support/temp_dir.dart';
/// 253 · 255 · 256 (관리자 지시 10/21): test/large_text_test 의 화면들을 실제 글꼴로 - 글자 1.6배 (412×900 · 900×412 · 1280×800) 와
/// 폰 크기 글자 1.0 (412×900 세로 · 900×412 가로) 에서 넘치지 않고, 누르는 것 (단추 · 고르기 · 입력 칸) 이 화면 폭 안에 있다. ko · ja
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// ⑦ 밝은 화면 캡처: JJ_SHOT_THEME=light (JJ_SHOT_DIR 를 따로 두어 어두운 것과 견줌)
if (Platform.environment['JJ_SHOT_THEME'] == 'light') JjColors.use(Brightness.light);
final out = Platform.environment['JJ_SHOT_DIR'] ?? '';
final pages = <String, Widget Function(AppController, DownloadManager)>{
'mkv': (c, d) => HomePage(c: c),
'explorer': (c, d) => ExplorerPage(c: c, device: shotDevice()),
'rsync': (c, d) => ExplorerPage(c: c, rsync: true, device: shotDevice()),
'monitor': (c, d) => MonitorPage(c: c),
'downloads': (c, d) => DownloadsPage(d: d),
'settings': (c, d) => SettingsPage(c: c),
};
final cases = [
for (final s in [const Size(412, 900), const Size(900, 412), const Size(1280, 800)]) (s, 1.6),
for (final s in [const Size(412, 900), const Size(900, 412)]) (s, 1.0),
];
for (final lang in ['ko', 'ja']) {
for (final MapEntry(key: name, value: page) in pages.entries) {
for (final (size, scale) in cases) {
final tag = '${name}_${lang}_${size.width.round()}x${size.height.round()}_s$scale';
testWidgets('253 · 255 · 256 $tag', (t) async {
t.view.physicalSize = size;
t.view.devicePixelRatio = 1;
addTearDown(t.view.reset);
await t.runAsync(() => i18n.apply(lang, save: false));
addTearDown(() => i18n.apply('ko', save: false));
// 관리자 지시 (10/24): 폴더 이름 · 기록 시각을 실행마다 같게 (캡처가 흔들리지 않게 - 시험 쪽에서만)
// 트리에 둘레 폴더도 보여 JJ_SHOT_TMP (캡처용으로 따로 둔 폴더) 아래에 둔다
final base = Platform.environment['JJ_SHOT_TMP'] ?? '';
final tmp = testFixedTempDir('jj_bigtext_fixed', base: base);
appTempInto(tmp);
final video = p.join(tmp.path, '아주 긴 이름의 동영상 파일 - 시험용 2026.mp4');
File(video).writeAsStringSync('v');
Directory(p.join(tmp.path, 'sub')).createSync();
// 대상 디스크 줄은 이 PC 의 실제 남은 용량이라 실행마다 달라 정한 값으로 (시험 쪽에서만)
final c = AppController(PlatformServices(
mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService(), diskSpace: (_) async => (107374182400, 536870912000)));
c.settings
..explorer.paths = [tmp.path, tmp.path]
..copy.rsyncPaths = [tmp.path, tmp.path]
..copy.tasks = [CopyTask(id: '1', sources: [tmp.path], dest: p.join(tmp.path, 'sub'), method: 'rsync', options: '-av')]
..copy.liveSyncPairs = [LiveSyncPair(tmp.path, p.join(tmp.path, 'x'))];
c.videos.add(VideoItem(video));
// 256: 폰 가로에서 작업 기록을 켜도 목록이 여러 줄 보이는지 - 동영상 5개 · 기록 몇 줄
for (var i = 2; i <= 5; i++) {
final f = p.join(tmp.path, '에피소드 0$i.mp4');
File(f).writeAsStringSync('v');
c.videos.add(VideoItem(f));
}
pinTestTimes(base.isEmpty ? tmp : Directory(base));
for (var i = 1; i <= 3; i++) {
c.note('시험 기록 $i', at: DateTime(2026, 10, 22, 9, i));
}
c.selected = c.videos.first;
final d = DownloadManager(backends: const [], settings: () => c.settings, readClipboard: () async => null);
addTearDown(d.dispose);
final shotKey = GlobalKey();
await t.pumpWidget(RepaintBoundary(
key: shotKey,
child: MaterialApp(
theme: buildTheme(),
debugShowCheckedModeBanner: false,
builder: (ctx, child) => MediaQuery(data: MediaQuery.of(ctx).copyWith(textScaler: TextScaler.linear(scale)), child: child!),
home: page(c, d),
),
));
for (var i = 0; i < 15; i++) {
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 40)));
await t.pump();
}
if (out.isNotEmpty) {
final r = shotKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
final img = await t.runAsync(() => r.toImage());
final bytes = await t.runAsync(() => img!.toByteData(format: ui.ImageByteFormat.png));
Directory(out).createSync(recursive: true);
File(p.join(out, 'bigtext_$tag.png')).writeAsBytesSync(bytes!.buffer.asUint8List());
}
expect(t.takeException(), isNull, reason: '$tag 넘침');
// 넘침 오류 없이 옆으로 밀려나는 것도 본다: 누르는 것은 화면 폭 안 (가로로 넘기는 막대 안의 것은 빼고)
final hit = find.byWidgetPredicate((w) =>
w is ButtonStyleButton || w is IconButton || w is Checkbox || w is DropdownButton || w is TextField || w is ChoiceChip || w is Radio);
final off = <String>[];
for (final e in hit.evaluate()) {
final inRow = e.findAncestorWidgetOfExactType<SingleChildScrollView>()?.scrollDirection == Axis.horizontal ||
e.findAncestorWidgetOfExactType<ListView>()?.scrollDirection == Axis.horizontal;
if (inRow) continue;
final r = t.getRect(find.byElementPredicate((x) => x == e));
if (r.left < -0.5 || r.right > size.width + 0.5) off.add('${e.widget.runtimeType} $r');
}
expect(off, isEmpty, reason: '$tag: 화면 밖 ${off.join(' · ')}');
await t.pumpWidget(const SizedBox());
await t.pump(const Duration(seconds: 1));
});
}
}
}
}