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/i18n_controller.dart'; import 'package:jj_mkvmaker/core/sync_tools.dart'; import 'package:jj_mkvmaker/ui/sync_preview_view.dart'; import 'package:jj_mkvmaker/ui/theme.dart'; import 'package:path/path.dart' as p; /// 관리자 지시 (10/20, 28 지적): 알아볼 수 없는 옵션 안내 - 끝 문구가 문제 종류에 맞는지, 412 에서 넘치지 않는지. /// 확인 창의 빨간 글 (OptionProblemNote) 과 설정 칸 아래 글 (errorText, 3줄까지) 을 크기 412 · 960, 언어 ko · en · ja 로 찍는다. /// 실행: flutter test integration_test\windows_option_problem_shot_test.dart -d windows (JJ_SHOT_DIR 에 PNG) void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); final out = Platform.environment['JJ_SHOT_DIR'] ?? p.join(Directory.systemTemp.path, 'jj_shots_optproblem'); const cases = [ '-a --delete --exclude="a.tmp --exclude=keep.txt', // 닫히지 않은 따옴표 '-a delete', // 경로로 읽힘 '-a --delete --exclude', // 값 없음 '-a ---delete «x»', // 이상한 글자 '-av delete --exclude', // 여러 종류 ]; for (final lang in ['ko', 'en', 'ja']) { for (final (w, hh) in [(412, 900), (960, 600)]) { testWidgets('옵션 안내 $lang ${w}x$hh', (t) async { t.view.physicalSize = Size(w.toDouble(), hh.toDouble()); t.view.devicePixelRatio = 1; addTearDown(t.view.reset); await t.runAsync(() => i18n.apply(lang, save: false)); addTearDown(() => i18n.apply('ko', save: false)); final shotKey = GlobalKey(); await t.pumpWidget(RepaintBoundary( key: shotKey, child: MaterialApp( theme: buildTheme(), debugShowCheckedModeBanner: false, home: Scaffold( body: ListView(padding: const EdgeInsets.all(16), children: [ for (final o in cases) ...[ Text(o, style: const TextStyle(fontFamily: 'monospace', fontSize: 12)), OptionProblemNote(optionProblems(o)), const SizedBox(height: 8), ], // 설정 칸 (copy_sync_settings 의 rsync 옵션 칸과 같은 폭 · 글 줄 수) SizedBox( width: 380, child: TextField( controller: TextEditingController(text: cases.last), decoration: InputDecoration( isDense: true, border: const OutlineInputBorder(), errorText: optionProblemText(optionProblems(cases.last)), errorMaxLines: 3, ), ), ), ]), ), ), )); for (var i = 0; i < 4; i++) { await t.runAsync(() => Future.delayed(const Duration(milliseconds: 60))); await t.pump(const Duration(milliseconds: 60)); } expect(t.takeException(), isNull, reason: '넘침'); expect(find.byType(OptionProblemNote), findsNWidgets(cases.length)); final b = shotKey.currentContext!.findRenderObject()! as RenderRepaintBoundary; final img = await t.runAsync(() => b.toImage()); final bytes = await t.runAsync(() => img!.toByteData(format: ui.ImageByteFormat.png)); Directory(out).createSync(recursive: true); File(p.join(out, 'optproblem_${lang}_$w.png')).writeAsBytesSync(bytes!.buffer.asUint8List()); }); } } }