217 lines
12 KiB
Dart
217 lines
12 KiB
Dart
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.explorerTextScale, s.explorerRowSpacing), (100, 'normal'));
|
||
s
|
||
..explorerTextScale = 130
|
||
..explorerRowSpacing = 'wide';
|
||
final back = AppSettings.fromJson(s.toJson());
|
||
expect((back.explorerTextScale, back.explorerRowSpacing), (130, 'wide'));
|
||
expect(AppSettings.fromJson({}).explorerTextScale, 100);
|
||
expect(AppSettings.fromJson({'explorerTextScale': 'x', 'explorerRowSpacing': 'huge'}).explorerRowSpacing, 'normal');
|
||
expect(AppSettings.fromJson({'explorerTextScale': 300}).explorerTextScale, 150);
|
||
expect(AppSettings.fromJson({'explorerTextScale': 10}).explorerTextScale, 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
|
||
..explorerPaths = [tmp.path, tmp.path]
|
||
// 폴더 + 파일 목록: 오른쪽 목록은 그 폴더의 것만이라 짧다 (트리는 Temp 의 다른 폴더가 많아 아래로 밀린다)
|
||
..explorerLayout = 'split'
|
||
..explorerStyle = style
|
||
// ✔ 칸 자리를 보는 시험: 표시를 늘 보이게 (설정 - 처음 값은 규칙 4 로 선택 모드에서만)
|
||
..explorerAlwaysMarks = true
|
||
..explorerTextScale = scale
|
||
..explorerRowSpacing = 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;
|
||
final listLeft = tester.getTopLeft(find.text(style == 'totalcmd' ? 'a' : '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';
|
||
final a = style == 'totalcmd' ? '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.explorerLayout, '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.explorerLayout, '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']) {
|
||
final a = style == 'totalcmd' ? '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);
|
||
});
|
||
}
|
||
}
|