import 'dart:io'; import 'package:flutter/material.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/bookmarks_controller.dart'; import 'package:jj_mkvmaker/app/net_guard.dart'; import 'package:jj_mkvmaker/platform/windows/cef_runtime.dart'; import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart'; import 'package:jj_mkvmaker/platform/windows/elevation.dart'; import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart'; import 'package:jj_mkvmaker/services/platform_services.dart'; import 'package:jj_mkvmaker/ui/app_actions.dart'; import 'package:jj_mkvmaker/ui/browser_page.dart'; import 'support/temp_dir.dart'; import 'package:path/path.dart' as p; /// 실제 망 (Windows): DNS 강제 (8.8.8.8) 상태에서 Chrome 엔진 (CEF) 이 앱 안 프록시의 인증 (비밀값) 에 답해 페이지를 여는지. /// Chrome 엔진은 실행 파일 옆 cef 폴더에 설치되고 다음 실행부터 쓰이므로 두 번 돌린다: /// 1번째: 없으면 받아서 설치하고 끝 ("RESULT 설치함") /// 2번째: 엔진을 Chrome 으로 바꿔 example.com 이 열리고 8.8.8.8 이 이름을 푸는지 /// 데이터는 JJ_MKVMAKER_DATA (없으면 임시 폴더) 에만. 끝나면 시험한 사람이 cef 폴더 (빌드 폴더 안) 와 데이터 폴더를 지운다. void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('DNS 강제 · Chrome 엔진 실제 연결', (tester) async { if (!CefRuntime.installed) { await tester.runAsync(() => CefRuntime.install(onProgress: (_, _) {})); // ignore: avoid_print print('RESULT 설치함 (${CefRuntime.dir}) - 다시 실행하면 Chrome 엔진으로 시험'); expect(CefRuntime.installed, isTrue); return; } CefRuntime.rememberStartState(); expect(CefRuntime.readyThisRun, isTrue); final data = Platform.environment['JJ_MKVMAKER_DATA'] ?? testTempDir('jj_cef_real_').path; Directory(data).createSync(recursive: true); final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService())); c.settings ..webViewDataDir = p.join(data, 'webview') ..browserEngine = 'chrome' // 207: 이 PC 처럼 관리자 권한으로 실행 중이면 설정을 켠 때만 Chrome 엔진이 돈다 (--do-not-de-elevate) ..cefWhenElevated = true; final answered = []; final logs = []; final g = NetGuard.instance ..config = (() => c.settings.dnsConfig) ..log = logs.add; g.resolver.onAnswer = (h, s) => answered.add('$h@$s'); // 원인 가르기: JJ_CEF_NO_PROXY=1 이면 프록시 없이 (Chrome 엔진만) final noProxy = Platform.environment['JJ_CEF_NO_PROXY'] == '1'; if (noProxy) c.settings.dnsMode = 'system'; expect(c.settings.dnsMode, noProxy ? 'system' : 'custom'); await tester.runAsync(g.ensure); HttpOverrides.global = JjHttpOverrides(); Future wait(int ms) async { for (var i = 0; i < ms ~/ 200; i++) { await tester.runAsync(() => Future.delayed(const Duration(milliseconds: 200))); await tester.pump(); } } final bm = BookmarksController(p.join(data, 'bm.json')); await tester.pumpWidget(MaterialApp( builder: (context, child) => AppScope(controller: c, bookmarks: bm, child: child!), home: BrowserPage(c: c, bookmarks: bm, initialUrl: 'https://example.com/'), )); String title = ''; for (var i = 0; i < 90 && !title.contains('Example'); i++) { await wait(500); title = '${await tester.runAsync(() async => BrowserPage.debugNav?.evaluate('document.title'))}'; } // ignore: avoid_print print('RESULT 관리자 권한: ${Elevation.elevated}'); // ignore: avoid_print print('RESULT Chrome 엔진 example.com 제목: $title, 답한 DNS: ${answered.where((a) => a.startsWith('example.com'))}\n' 'RESULT 웹 뷰: ${BrowserPage.debugNav.runtimeType} / 기록: ${logs.take(8).join(' | ')}'); // ignore: avoid_print print('RESULT 화면 글: ${find.byType(Text).evaluate().map((e) => (e.widget as Text).data).whereType().take(30).join(' / ')}'); expect(title, contains('Example')); if (!noProxy) expect(answered, contains('example.com@8.8.8.8')); await tester.pumpWidget(const SizedBox()); await wait(1000); }, timeout: const Timeout(Duration(minutes: 10))); }