// Explicit host-rendered component evidence, not Android-device verification. import 'dart:io'; import 'dart:ui' as ui; import 'package:background_downloader/background_downloader.dart'; 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/data/models.dart'; import 'package:reelbox/media/download_button.dart'; import 'package:reelbox/media/download_service.dart'; import 'package:reelbox/media/downloads_page.dart'; import 'package:reelbox/state/app_state.dart'; import '../test/catalog_widget_test.dart' show CatalogApi, mountCatalog; void main() { testWidgets('download components at 390px in English and Persian', ( tester, ) async { await tester.runAsync(() async { final font = await File('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf') .readAsBytes(); for (final family in ['Roboto', 'Ahem']) { await (FontLoader( family, )..addFont(Future.value(font.buffer.asByteData()))).load(); } final icons = await File( '/root/developer-tools/flutter/bin/cache/artifacts/material_fonts/MaterialIcons-Regular.otf', ).readAsBytes(); await (FontLoader( 'MaterialIcons', )..addFont(Future.value(icons.buffer.asByteData()))).load(); }); tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final state = await mountCatalog(tester, CatalogApi(), const Scaffold()); addTearDown(state.dispose); final boundaryKey = GlobalKey(); final paused = DownloadEntry.fromTask( DownloadTask(url: 'https://media.example/movie.mp4'), taskStatus: TaskStatus.paused, progress: .25, totalBytes: 104857600, ); final failed = DownloadEntry.fromTask( DownloadTask(url: 'https://media.example/expired.mp4'), taskStatus: TaskStatus.failed, error: 'HTTP 403: this media URL has expired.', ); for (final locale in ['en', 'fa']) { await state.updateSettings(state.prefs.copyWith(locale: locale)); await tester.pumpWidget( AppScope( state: state, child: MaterialApp( theme: ThemeData.dark(useMaterial3: true).copyWith( appBarTheme: const AppBarTheme( titleTextStyle: TextStyle(fontFamily: 'Roboto', fontSize: 20), ), ), home: RepaintBoundary( key: boundaryKey, child: Directionality( textDirection: locale == 'fa' ? TextDirection.rtl : TextDirection.ltr, child: Builder( builder: (context) => Scaffold( appBar: AppBar( title: Text(tr(context, 'Downloads', 'دانلودها')), ), body: ListView( padding: const EdgeInsets.all(16), children: [ Text( tr(context, 'Source · 720p', 'منبع · 720p'), style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 8), DownloadButton( title: const TitleRecord( id: 7, title: 'Fixture movie', ), source: const MediaSource( quality: '720p', url: 'https://media.example/movie.mp4', ), enqueue: ({ required title, required source, episode, }) async {}, ), const SizedBox(height: 18), for (final entry in [paused, failed]) Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( tr( context, 'Fixture movie · 720p', 'فیلم نمونه · 720p', ), style: Theme.of(context) .textTheme .titleMedium, ), const SizedBox(height: 12), DownloadTransferDetails(entry: entry), const SizedBox(height: 8), DownloadControls( entry: entry, onPause: () async {}, onResume: () async {}, onRetry: () async {}, onCancel: () async {}, ), ], ), ), ), ], ), ), ), ), ), ), ), ); await tester.pumpAndSettle(); expect(find.byType(DownloadButton), findsOneWidget); expect(find.byType(DownloadTransferDetails), findsNWidgets(2)); expect(tester.takeException(), isNull); final boundary = boundaryKey.currentContext!.findRenderObject()! as RenderRepaintBoundary; await tester.runAsync(() async { final directory = Directory( '/root/reelbox/build/download-ui-verification', ); await directory.create(recursive: true); final image = await boundary.toImage(pixelRatio: 2); final bytes = await image.toByteData(format: ui.ImageByteFormat.png); await File('${directory.path}/downloads-$locale.png') .writeAsBytes(bytes!.buffer.asUint8List()); image.dispose(); }); } await tester.pumpWidget(const SizedBox()); }); }