JJ_MKVMaker/integration_test/windows_datecol_shot_test.dart

123 lines
6.8 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_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/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';
/// 관리자 지시 (10/21, 28 지적): Windows · TC 스타일의 날짜 열이 시간까지 들어가지 않으면 "2026-10-2…" 로 잘리던 것 -
/// 모자라면 짧은 꼴 (올해 "10-21 14:05" · 아니면 "2025-10-21"), 그래도 모자라면 숨김. 실제 글꼴로 312 조합
/// (폭 13 × 스타일 2 × 언어 3 × 글자 배율 4) 에서 날짜 칸이 잘리지 않는지 (RenderParagraph.didExceedMaxLines) 와
/// 날짜를 보일 수 있는 넓은 창에서는 다 쓴 꼴이 나오는지 본다. 좁은 몇 개는 PNG (JJ_SHOT_DIR).
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
if (Platform.environment['JJ_SHOT_THEME'] == 'light') JjColors.use(Brightness.light);
final out = Platform.environment['JJ_SHOT_DIR'] ?? p.join(Directory.systemTemp.path, 'jj_shots_datecol');
final dateLike = RegExp(r'^(\d{4}[-/])?\d{2}[-/]\d{2}( \d{2}:\d{2})?$');
testWidgets('날짜 열: 312 조합에서 잘림 0', (t) async {
final tmp = testTempDir('jj_datecol_');
final dir = Directory(p.join(tmp.path, 'files'))..createSync();
final now = DateTime.now();
for (final (n, at) in [
('올해 사진.jpg', DateTime(now.year, 12, 28, 23, 58).isAfter(now) ? now : DateTime(now.year, 12, 28, 23, 58)),
('작년 문서.pdf', DateTime(now.year - 1, 12, 28, 23, 58)),
]) {
File(p.join(dir.path, n))
..writeAsStringSync('x')
..setLastModifiedSync(at);
}
ExplorerPage.debugVolumes = () => [(dir.path, 'T:')];
addTearDown(() => ExplorerPage.debugVolumes = null);
final key = GlobalKey();
var checked = 0, short = 0, hidden = 0;
final emptyOnce = <String>[];
final widths = <double>[300, 340, 360, 390, 412, 460, 520, 600, 700, 800, 960, 1280, 1600];
final scales = [(1.0, 80), (1.0, 100), (1.0, 150), (1.3, 150)];
for (final lang in ['ko', 'ja', 'zh-Hans']) {
await t.runAsync(() => i18n.apply(lang, save: false));
for (final style in ['windows', 'totalcmd']) {
for (final (dev, list) in scales) {
for (final w in widths) {
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings
..explorerStyle = style
..explorerLayout = 'single'
..explorerTextScale = list
..explorerPaths = [dir.path, dir.path];
t.view.physicalSize = Size(w, 800);
t.view.devicePixelRatio = 1;
await t.runAsync(() => t.pumpWidget(RepaintBoundary(
key: key,
child: MaterialApp(
theme: buildTheme(),
debugShowCheckedModeBanner: false,
builder: (ctx, child) => MediaQuery(data: MediaQuery.of(ctx).copyWith(textScaler: TextScaler.linear(dev)), child: child!),
home: ExplorerPage(key: UniqueKey(), c: c),
),
)));
for (var i = 0; i < 200 && find.textContaining('작년 문서').evaluate().isEmpty; i++) {
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 50)));
await t.pump();
}
await t.pump(const Duration(milliseconds: 100));
final name = '${lang}_${style}_${w.round()}_d${dev}_l$list';
if (find.textContaining('작년 문서').evaluate().isEmpty) {
// 목록이 빈 채로 남는 일이 가끔 있다 (빙글이 없이 - 3a 에 알림). 그림을 남기고 [새로 고침] 으로 한 번 더
emptyOnce.add(name);
final r = key.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, 'datecol_EMPTY_$name.png')).writeAsBytesSync(bytes!.buffer.asUint8List());
await t.tap(find.byIcon(Icons.refresh).first);
for (var i = 0; i < 200 && find.textContaining('작년 문서').evaluate().isEmpty; i++) {
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 50)));
await t.pump();
}
await t.pump(const Duration(milliseconds: 100));
}
expect(find.textContaining('작년 문서'), findsWidgets, reason: '$name 목록');
final dates = find.byWidgetPredicate((x) => x is Text && x.data != null && dateLike.hasMatch(x.data!));
for (final e in dates.evaluate()) {
final para = e.findRenderObject()! as RenderParagraph;
expect(para.didExceedMaxLines, isFalse, reason: '$name: "${(e.widget as Text).data}" 잘림');
if (!(e.widget as Text).data!.contains(':') || !(e.widget as Text).data!.startsWith('20')) short++;
}
if (dates.evaluate().isEmpty) hidden++;
// 넓은 창 (1280 이상) 에서는 다 쓴 꼴 (연도 + 시각)
if (w >= 1280 && dev == 1.0 && list <= 100) {
expect(dates.evaluate().map((e) => (e.widget as Text).data!).every((x) => x.length == 16), isTrue, reason: '$name 다 쓴 꼴');
}
if (w == 360 || w == 412) {
final r = key.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, 'datecol_$name.png')).writeAsBytesSync(bytes!.buffer.asUint8List());
}
checked++;
await t.pumpWidget(const SizedBox());
}
}
}
}
await t.runAsync(() => i18n.apply('ko', save: false));
t.view.reset();
expect(checked, 312);
// ignore: avoid_print
print('날짜 열 조합 $checked · 짧은 꼴 칸 $short · 날짜 숨긴 조합 $hidden · 목록이 빈 채였던 조합 $emptyOnce');
}, timeout: const Timeout(Duration(minutes: 30)));
}