// Widget-level checks on the explorer. These need the native library on the // loader path: // // cd example && LD_LIBRARY_PATH=../build flutter test import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:nimflutter_ffi_example/main.dart'; /// pumpAndSettle cannot be used here: the in-flight-render spinner animates /// indefinitely, and rendering runs on real isolates outside the fake clock. Future settle(WidgetTester tester) async { for (var i = 0; i < 20; i++) { await tester.pump(const Duration(milliseconds: 50)); } } void main() { /// The side panel only exists in the wide layout, so size the surface like a /// desktop window rather than the 800x600 test default. Future pumpWide(WidgetTester tester) async { tester.view.physicalSize = const Size(1400, 900); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.reset); await tester.pumpWidget(const ExampleApp()); await tester.pump(const Duration(milliseconds: 100)); } testWidgets('starts at the documented defaults, not the slider maxima', (tester) async { await pumpWide(tester); final sliders = tester.widgetList(find.byType(Slider)).toList(); expect(sliders, hasLength(2)); expect(sliders[0].value, 400, reason: 'iterations should default to 400'); expect(sliders[1].value, 16, reason: 'isolates should default to 16'); }); testWidgets('reset restores the framing without touching iterations', (tester) async { await pumpWide(tester); // Move the iteration slider off its default, then reset the view. await tester.drag(find.byType(Slider).first, const Offset(200, 0)); await settle(tester); final changed = tester.widgetList(find.byType(Slider)).first.value; expect(changed, greaterThan(400), reason: 'the drag should have raised the iteration count'); await tester.tap(find.text('Reset view')); await settle(tester); // Reset owns pan/zoom only: the iteration setting must survive it. expect(tester.widgetList(find.byType(Slider)).first.value, changed); expect(find.textContaining('zoom 1.0x'), findsOneWidget); }); testWidgets('dragging pans without also zooming', (tester) async { await pumpWide(tester); String zoomLabel() => (tester .widget(find.textContaining('zoom ').first) .data)!; final before = zoomLabel(); // Guards the gesture arena's behaviour: a pan must claim the pointer so // the tap recognizer never fires. (This passes with the zoom on either // onTapDown or onTapUp — the arena, not the callback choice, is what // separates them.) await tester.drag(find.byKey(canvasKey), const Offset(-120, -60)); await settle(tester); expect(zoomLabel(), before, reason: 'a drag must not change the zoom'); }); testWidgets('a tap still zooms', (tester) async { await pumpWide(tester); String zoomLabel() => (tester .widget(find.textContaining('zoom ').first) .data)!; final before = zoomLabel(); await tester.tap(find.byKey(canvasKey)); await settle(tester); expect(zoomLabel(), isNot(before)); }); testWidgets('narrow layout lays out without overflowing', (tester) async { // Regression guard: the controls used to be a ListView nested inside the // page's ListView, which threw "Vertical viewport was given unbounded // height" and broke the whole mobile layout. tester.view.physicalSize = const Size(420, 900); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.reset); await tester.pumpWidget(const ExampleApp()); await tester.pump(const Duration(milliseconds: 100)); expect(tester.takeException(), isNull); }); }