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:pyramid/core/theme.dart'; import 'package:pyramid/features/call_ui/voice_channel.dart' show CallBubblesDemo; /// Vorschaubilder der Anrufansicht mit erfundenen Teilnehmern (ohne Login): /// Standbilder für 1–7 Personen (Handy + PC) und eine Bildfolge der /// Animation (Sprecherwechsel, jemand kommt dazu, jemand geht) für ein GIF. /// Bilder: build/call_demo/. /// /// flutter test integration_test/call_bubbles_demo_test.dart -d windows void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('Anruf-Formen: Vorschaubilder', (tester) async { final count = ValueNotifier(5); final speaking = ValueNotifier(2); void phone() => tester.view ..physicalSize = const Size(1080, 2340) ..devicePixelRatio = 2.625; void desktop() => tester.view ..physicalSize = const Size(1600, 1000) ..devicePixelRatio = 1.0; addTearDown(tester.view.reset); Future smooth(Duration d) async { final end = DateTime.now().add(d); while (DateTime.now().isBefore(end)) { await tester.pump(const Duration(milliseconds: 16)); } } phone(); await tester.pumpWidget(PyramidTheme( child: MaterialApp( debugShowCheckedModeBanner: false, theme: buildPyramidThemeData(ThemeVariant.soft), home: ValueListenableBuilder( valueListenable: count, builder: (_, n, _) => ValueListenableBuilder( valueListenable: speaking, builder: (_, s, _) => CallBubblesDemo(count: n, speaking: s), ), ), ), )); // ── Standbilder ── for (final n in [1, 2, 3, 5, 7]) { count.value = n; speaking.value = n >= 3 ? 2 : -1; phone(); await smooth(const Duration(milliseconds: 2500)); await _shot(tester, 'p_${n}_personen'); desktop(); await smooth(const Duration(milliseconds: 2500)); await _shot(tester, 'd_${n}_personen'); } // ── Animation (Handy) ── phone(); count.value = 4; speaking.value = -1; await smooth(const Duration(seconds: 3)); var frame = 0; Future record(Duration d) async { final end = DateTime.now().add(d); while (DateTime.now().isBefore(end)) { await tester.pump(const Duration(milliseconds: 16)); await _shot(tester, 'anim/f${(frame++).toString().padLeft(4, '0')}', scale: 0.4, log: false); } } await record(const Duration(milliseconds: 600)); speaking.value = 1; // Anna spricht await record(const Duration(milliseconds: 1600)); speaking.value = 3; // Clara spricht await record(const Duration(milliseconds: 1600)); speaking.value = -1; count.value = 5; // David kommt dazu await record(const Duration(milliseconds: 1800)); count.value = 4; // David geht wieder await record(const Duration(milliseconds: 1800)); // ignore: avoid_print print('Animation: $frame Bilder'); }, timeout: const Timeout(Duration(minutes: 6))); } Future _shot(WidgetTester tester, String name, {double scale = 1, bool log = true}) async { final renderView = tester.binding.renderViews.first; final layer = renderView.debugLayer! as OffsetLayer; final physical = tester.view.physicalSize; final image = await tester.binding.runAsync( () => layer.toImage(Offset.zero & physical, pixelRatio: scale), ); final bytes = await tester.binding.runAsync( () => image!.toByteData(format: ui.ImageByteFormat.png), ); final file = File('build/call_demo/$name.png'); file.parent.createSync(recursive: true); file.writeAsBytesSync(bytes!.buffer.asUint8List()); // ignore: avoid_print if (log) print('Bild $name'); }