// Mobile layout checks; optional captures are host-rendered, not device evidence. import 'dart:io'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:reelbox/app.dart'; import 'package:reelbox/data/api_service.dart'; import 'package:reelbox/data/models.dart'; import 'package:reelbox/state/app_state.dart'; import 'package:reelbox/ui/library.dart'; import 'package:shared_preferences/shared_preferences.dart'; class _LocalApi extends ApiService { @override Future initialize({bool discover = true}) async {} @override Future> home() async => { 'genres': [], 'actors': [], 'countries': [], }; @override Future>> actors({ int page = 1, String search = '', }) async => []; @override Future> countries() async => []; } void main() { for (final locale in ['en', 'fa']) { testWidgets( 'mobile intro, privacy and useful settings remain reachable in $locale', (tester) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final output = Platform.environment['REELBOX_CLEANUP_SCREENSHOTS']; if (output != null) { await tester.runAsync(() async { final bytes = await File( Platform.environment['REELBOX_TEST_FONT'] ?? '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', ).readAsBytes(); for (final name in ['Roboto', 'Ahem']) { await (FontLoader( name, )..addFont(Future.value(bytes.buffer.asByteData()))).load(); } final icons = await File( Platform.environment['REELBOX_ICON_FONT'] ?? '/root/developer-tools/flutter/bin/cache/artifacts/material_fonts/MaterialIcons-Regular.otf', ).readAsBytes(); await (FontLoader( 'MaterialIcons', )..addFont(Future.value(icons.buffer.asByteData()))).load(); await Directory(output).create(recursive: true); }); } SharedPreferences.setMockInitialValues({}); final state = AppState(api: _LocalApi()); addTearDown(state.dispose); await state.initialize(discover: false); await state.updateSettings(state.prefs.copyWith(locale: locale)); final boundaryKey = GlobalKey(); await tester.pumpWidget( RepaintBoundary( key: boundaryKey, child: MyApp(state: state), ), ); await tester.pumpAndSettle(); Future capture(String name) async { expect(tester.takeException(), isNull); if (output == null) return; final boundary = boundaryKey.currentContext!.findRenderObject()! as RenderRepaintBoundary; await tester.runAsync(() async { final image = await boundary.toImage(pixelRatio: 2); final data = await image.toByteData(format: ui.ImageByteFormat.png); await File('$output/$name-$locale.png') .writeAsBytes(data!.buffer.asUint8List()); image.dispose(); }); } String label(String en, String fa) => locale == 'en' ? en : fa; Future reveal(String text) async { final scrollable = find.byType(Scrollable).hitTestable().first; await tester.scrollUntilVisible( find.text(text), 350, scrollable: scrollable, ); await tester.ensureVisible(find.text(text)); await tester.pumpAndSettle(); } expect(find.byType(FirstRunIntroPage), findsOneWidget); await capture('intro'); await tester.tap(find.byKey(const Key('intro-skip'))); await tester.pumpAndSettle(); await tester.tap( find.descendant( of: find.byType(NavigationBar), matching: find.text(label('More', 'بیشتر')), ), ); await tester.pumpAndSettle(); expect(find.byType(MorePage), findsOneWidget); await capture('more'); await reveal(label('Privacy & your data', 'حریم خصوصی و اطلاعات شما')); await tester.tap( find.text(label('Privacy & your data', 'حریم خصوصی و اطلاعات شما')), ); await tester.pumpAndSettle(); await capture('privacy-top'); await reveal(label('Unencrypted media', 'رسانه بدون رمزنگاری')); expect(find.textContaining('HTTP'), findsWidgets); await capture('privacy-bottom'); await tester.tap(find.byType(BackButton)); await tester.pumpAndSettle(); await reveal(label('Settings', 'تنظیمات')); await tester.tap(find.text(label('Settings', 'تنظیمات'))); await tester.pumpAndSettle(); expect(find.byType(SettingsPage), findsOneWidget); expect(find.byKey(const Key('catalog-api-endpoint')), findsNothing); await capture('settings-top'); final dataSaver = label('Data saver artwork', 'کاهش مصرف داده تصاویر'); await reveal(dataSaver); await tester.tap(find.text(dataSaver)); await tester.pumpAndSettle(); expect(state.prefs.dataSaver, true); await reveal(label('Clear disposable cache', 'پاک کردن حافظه موقت')); expect( find.text(label('Export local library', 'خروجی کتابخانه محلی')), findsOneWidget, ); expect( find.text(label('Import local library', 'ورود کتابخانه محلی')), findsOneWidget, ); await capture('settings-data'); await tester.pumpWidget(const SizedBox.shrink()); }, ); } }