JJ_MKVMaker/test/explorer_text_scale_test.dart

220 lines
12 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 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/app/app_controller.dart';
import 'package:jj_mkvmaker/app/settings.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_look.dart';
import 'package:jj_mkvmaker/ui/explorer_page.dart';
import 'package:path/path.dart' as p;
/// X-plore 비교 40 (관리자 지시 10/20): 파일 탐색기 목록만의 글자 크기 (80~150%) · 줄 간격 (좁게 · 보통 · 넓게)
void main() {
late Directory tmp;
setUp(() => tmp = Directory.systemTemp.createTempSync('jj_textscale_'));
tearDown(() => tmp.deleteSync(recursive: true));
test('설정: 처음은 100% · 보통 (지금 그대로), 저장 · 읽기, 틀린 값은 그대로 쪽으로 · 범위 밖은 끝으로', () {
final s = AppSettings();
expect((s.explorer.textScale, s.explorer.rowSpacing), (100, 'normal'));
s
..explorer.textScale = 130
..explorer.rowSpacing = 'wide';
final back = AppSettings.fromJson(s.toJson());
expect((back.explorer.textScale, back.explorer.rowSpacing), (130, 'wide'));
expect(AppSettings.fromJson({}).explorer.textScale, 100);
expect(AppSettings.fromJson({'explorerTextScale': 'x', 'explorerRowSpacing': 'huge'}).explorer.rowSpacing, 'normal');
expect(AppSettings.fromJson({'explorerTextScale': 300}).explorer.textScale, 150);
expect(AppSettings.fromJson({'explorerTextScale': 10}).explorer.textScale, 80);
});
/// [name] 글자의 실제 글자 크기 (기기 · 목록 글자 크기를 곱한 것)
double shownSize(WidgetTester tester, String name) {
final para = tester.renderObject<RenderParagraph>(find.text(name).last);
final size = para.text.style?.fontSize ?? 14;
return para.textScaler.scale(size);
}
/// 이름 줄 높이
double rowHeight(WidgetTester tester, String name) =>
tester.getSize(find.ancestor(of: find.text(name).last, matching: find.byType(KeyedSubtree)).first).height;
Future<AppController> open(WidgetTester tester, String style, String wait,
{double device = 1, int scale = 100, String spacing = 'normal'}) async {
const pp = MethodChannel('plugins.flutter.io/path_provider');
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(pp, (_) async => tmp.path);
addTearDown(() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(pp, null));
File(p.join(tmp.path, '아주 긴 이름의 동영상 파일 - 글자 크기 시험용 2026 최종본.mp4')).writeAsStringSync('v' * 2000);
File(p.join(tmp.path, 'a.txt')).writeAsStringSync('a');
Directory(p.join(tmp.path, 'sub')).createSync();
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
c.settings
..explorer.paths = [tmp.path, tmp.path]
// 폴더 + 파일 목록: 오른쪽 목록은 그 폴더의 것만이라 짧다 (트리는 Temp 의 다른 폴더가 많아 아래로 밀린다)
..explorer.layout = 'split'
..explorer.style = style
// ✔ 칸 자리를 보는 시험: 표시를 늘 보이게 (설정 - 처음 값은 규칙 4 로 선택 모드에서만)
..explorer.alwaysMarks = true
..explorer.textScale = scale
..explorer.rowSpacing = spacing;
await tester.runAsync(() => tester.pumpWidget(MaterialApp(
builder: (ctx, child) => MediaQuery(data: MediaQuery.of(ctx).copyWith(textScaler: TextScaler.linear(device)), child: child!),
home: ExplorerPage(c: c))));
// 목록을 다 읽을 때까지 (다른 시험과 함께 돌면 느리다 - 10초까지)
for (var i = 0; i < 200 && (i < 15 || find.text(wait).evaluate().isEmpty); i++) {
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 50)));
await tester.pump();
}
return c;
}
Future<void> close(WidgetTester tester) async {
await tester.pumpWidget(const SizedBox());
await tester.pump(const Duration(seconds: 1));
}
// 관리자 지시 10/20: 좁은 화면의 "폴더 + 파일 목록" - 트리 창 최소 폭 (120px 또는 38%), 트리에는 ✔ 칸 · 크기 줄 없이 이름이 보임
for (final width in [412.0, 360.0]) {
for (final style in ['xplore', 'windows', 'totalcmd']) {
testWidgets('좁은 폴더 + 파일 목록 ($width 폭, $style): 트리 창이 넓어 폴더 이름이 보이고 ✔ 칸은 오른쪽 목록에만', (tester) async {
tester.view.physicalSize = Size(width, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await open(tester, style, 'sub');
expect(tester.takeException(), isNull);
// 트리 (먼저 그려짐) 의 'sub' 이름이 다 보인다
final tree = find.text(style == 'totalcmd' ? '[sub]' : 'sub').first;
final para = tester.renderObject<RenderParagraph>(tree);
// 시험 글꼴 (Ahem) 은 글자마다 글자 크기만큼 넓어 실제보다 훨씬 길다 - 이름 칸 폭으로 본다 (실제 글꼴은 캡처 시험)
expect(para.constraints.maxWidth, greaterThanOrEqualTo(20), reason: '트리의 폴더 이름 칸이 남는다 (예전 412 폭은 0, 실제 글꼴로 360 폭에서 한 줄 5글자쯤)');
final treeRight = tester.getTopRight(tree).dx;
// 관리자 결정 10/23 (E6 ③): 좁아 TC 의 확장자 칸이 없으면 이름에 확장자를 붙인다 - 이 폭에서는 TC 도 'a.txt'
final listLeft = tester.getTopLeft(find.text('a.txt')).dx;
expect(listLeft, greaterThan(treeRight), reason: '트리는 왼쪽, 목록은 오른쪽');
// 트리 줄에는 ✔ 단추가 없다 (오른쪽 목록 줄에만) - X-plore 는 늘 ✔
if (style == 'xplore') {
final marks = find.byTooltip('표시 (여러 개 고르기)');
expect(marks, findsWidgets);
for (final m in marks.evaluate()) {
expect(tester.getTopLeft(find.byWidget(m.widget)).dx, greaterThan(treeRight), reason: '✔ 는 오른쪽 목록에만');
}
}
await close(tester);
});
}
}
// 관리자 지시 10/20 (28 검토): 글자가 아주 크면 (기기 1.3 × 150%) 좁은 화면에 두 창이 다 들어가지 않는다 - 그동안만 한 창 (설정은 그대로)
for (final style in ['xplore', 'windows', 'totalcmd']) {
testWidgets('글자가 아주 크면 (기기 1.3 × 150% · 넓게, 412 폭, $style) 폴더 + 파일 목록 대신 한 창 - 설정은 split 그대로 · 넘침 없음', (tester) async {
tester.view.physicalSize = const Size(412, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
final sub = style == 'totalcmd' ? '[sub]' : 'sub';
// 관리자 결정 10/23 (E6 ③): 좁아 TC 의 확장자 칸이 없으면 이름에 확장자를 붙인다 - 이 폭에서는 TC 도 'a.txt'
const a = 'a.txt';
final c = await open(tester, style, sub, device: 1.3, scale: 150, spacing: 'wide');
await tester.pump();
expect(tester.takeException(), isNull);
expect(c.settings.explorer.layout, 'split', reason: '고른 배치는 바꾸지 않는다');
expect(find.text(sub), findsOneWidget, reason: '트리 · 목록 두 창이 아니라 한 창');
expect(find.text(a), findsOneWidget, reason: '한 창 트리에 파일도 보인다');
final para = tester.renderObject<RenderParagraph>(find.text(sub));
// X-plore 는 이름이 줄 폭을 다 쓰고, Windows · TC 는 열 (날짜 · 유형 · 크기) 이 있어 이름 열이 6/13 쯤
expect(para.constraints.maxWidth, greaterThan(style == 'xplore' ? 100 : 55), reason: '이름이 보인다');
await close(tester);
// 글자가 보통이면 같은 폭에서 두 창
await open(tester, style, sub);
await tester.pump();
expect(find.text(sub), findsNWidgets(2), reason: '보통 글자면 폴더 + 파일 목록');
await close(tester);
});
}
// 관리자 지시 10/20: 좁은 창의 열 - 이름 열이 120 보다 좁으면 유형 (TC 확장자) → 날짜 순으로 숨김, 크기는 남김
test('숨길 열 정하기: 넓으면 0 · 좁아질수록 유형 → 날짜 · 열 없는 X-plore 는 0', () {
expect(columnHideLevel(ExplorerStyle.windows, 1280), 0);
expect(columnHideLevel(ExplorerStyle.windows, 300), 0, reason: '이름 열 (300-16)*6/13 = 131');
expect(columnHideLevel(ExplorerStyle.windows, 300, marks: true), 1, reason: '✔ 칸 (36) 이 있으면 114');
expect(columnHideLevel(ExplorerStyle.windows, 260), 1);
expect(columnHideLevel(ExplorerStyle.windows, 180), 2);
expect(columnHideLevel(ExplorerStyle.totalcmd, 1280), 0);
expect(columnHideLevel(ExplorerStyle.totalcmd, 180), 2);
expect(columnHideLevel(ExplorerStyle.xplore, 100), 0);
});
for (final style in ['windows', 'totalcmd']) {
testWidgets('좁은 한 창 ($style, 412 · 기기 글자 1.3): 이름 열이 좁으면 유형 → 날짜 열을 숨기고 크기는 남김 · 넓으면 다 보임', (tester) async {
tester.view.physicalSize = const Size(412, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
final type = style == 'windows' ? '유형' : '확장자';
final date = style == 'windows' ? '수정한 날짜' : '날짜';
final sub = style == 'totalcmd' ? '[sub]' : 'sub';
final c = await open(tester, style, sub, device: 1.3, scale: 150);
await tester.pump();
expect(c.settings.explorer.layout, 'split');
expect(tester.takeException(), isNull);
expect(find.text(type), findsNothing, reason: '좁아서 유형 열 숨김');
expect(find.text('크기'), findsOneWidget, reason: '크기는 남김');
await close(tester);
tester.view.physicalSize = const Size(1600, 900);
await open(tester, style, sub);
await tester.pump();
expect(find.text(type), findsOneWidget);
expect(find.text(date), findsOneWidget);
expect(find.text('크기'), findsOneWidget);
await close(tester);
});
}
testWidgets('넓은 화면 (1280) 의 폴더 + 파일 목록은 그대로 (2 : 3, 트리에도 ✔)', (tester) async {
tester.view.physicalSize = const Size(1280, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await open(tester, 'xplore', 'sub');
final treeMarks = find.byTooltip('표시 (여러 개 고르기)').evaluate().where((m) => tester.getTopLeft(find.byWidget(m.widget)).dx < 500);
expect(treeMarks, isNotEmpty, reason: '넓은 화면은 지금 그대로');
await close(tester);
});
for (final style in ['xplore', 'windows', 'totalcmd']) {
// 관리자 결정 10/23 (E6 ③): 좁아 TC 의 확장자 칸이 없으면 이름에 확장자를 붙인다 - 이 폭에서는 TC 도 'a.txt'
const a = 'a.txt';
testWidgets('40: $style - 처음 값 (100% · 보통) 은 지금 그대로 · 150% · 넓게는 글자 1.5배 · 줄 높이 늘어남 · 412 폭에서 넘치지 않음', (tester) async {
tester.view.physicalSize = const Size(412, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await open(tester, style, a);
expect(tester.takeException(), isNull);
final base = shownSize(tester, a), baseH = rowHeight(tester, a);
expect(baseH, {'xplore': 52.0, 'windows': 34.0, 'totalcmd': 26.0}[style], reason: '처음 값은 예전 줄 높이 그대로');
await close(tester);
await open(tester, style, a, scale: 150, spacing: 'wide');
expect(tester.takeException(), isNull, reason: '412 폭 · 150% · 넓게에서 넘치지 않는다');
expect(shownSize(tester, a), closeTo(base * 1.5, 0.01));
expect(rowHeight(tester, a), (baseH * 1.5 * 1.25).roundToDouble());
await close(tester);
// 기기 글자 크기 1.3 과 곱해진다 · 좁게
await open(tester, style, a, device: 1.3, scale: 120, spacing: 'compact');
expect(tester.takeException(), isNull);
expect(shownSize(tester, a), closeTo(base * 1.3 * 1.2, 0.01));
expect(rowHeight(tester, a), (baseH * 1.3 * 1.2 * 0.8).roundToDouble());
await close(tester);
// 80% 는 글자만 작게 (줄은 예전 높이 - 누르기 크기를 줄이지 않음)
await open(tester, style, a, scale: 80);
expect(shownSize(tester, a), closeTo(base * 0.8, 0.01));
expect(rowHeight(tester, a), baseH);
await close(tester);
});
}
}