캡처 시험: 253 · 255 · 256 - 큰 글자 · 폰 크기에서 6개 화면 실제 글꼴로 (관리자 지시 10/21)
- windows_bigtext_shot_test: test/large_text_test 의 화면 6개 (MKV · 파일 탐색기 · Rsync · 모니터링 · 다운로드 · 환경 설정) 를 실제 글꼴로,
글자 1.6배 (412×900 · 900×412 · 1280×800) · 글자 1.0 (412×900 · 900×412) × ko · ja = 60개. 넘침 없음 · 누르는 것이 화면 폭 안 (가로로 넘기는 막대 안은 뺌).
MKV 는 동영상 5개 · 작업 기록 3줄 (256: 폰 가로에서 기록을 켜도 목록이 3줄 보임)
- 정식 판 후보 1b31609 로 60개 통과 (release\test\shots_v20261020_001 bigtext_*)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rc2
parent
ebaa054922
commit
af77dc56d2
|
|
@ -0,0 +1,117 @@
|
||||||
|
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/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();
|
||||||
|
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),
|
||||||
|
'rsync': (c, d) => ExplorerPage(c: c, rsync: true),
|
||||||
|
'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));
|
||||||
|
final tmp = testTempDir('jj_bigtext_');
|
||||||
|
appTempInto(tmp);
|
||||||
|
final video = p.join(tmp.path, '아주 긴 이름의 동영상 파일 - 시험용 2026.mp4');
|
||||||
|
File(video).writeAsStringSync('v');
|
||||||
|
Directory(p.join(tmp.path, 'sub')).createSync();
|
||||||
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||||
|
c.settings
|
||||||
|
..explorerPaths = [tmp.path, tmp.path]
|
||||||
|
..rsyncPaths = [tmp.path, tmp.path]
|
||||||
|
..copyTasks = [CopyTask(id: '1', sources: [tmp.path], dest: p.join(tmp.path, 'sub'), method: 'rsync', options: '-av')]
|
||||||
|
..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));
|
||||||
|
}
|
||||||
|
for (var i = 1; i <= 3; i++) {
|
||||||
|
c.note('시험 기록 $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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue