From 594724d5b53dd6dec3b1e568bb686bfdcd21e5fd Mon Sep 17 00:00:00 2001 From: Pascal Welsch Date: Fri, 22 Nov 2024 02:34:30 +0100 Subject: [PATCH] Add snapshotRenderBox(), snapshotState() (#74) --- lib/src/spot/selectors.dart | 22 +++++++++ test/selectors/selector_test.dart | 76 +++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/lib/src/spot/selectors.dart b/lib/src/spot/selectors.dart index 19eb2d18..aa74eb93 100644 --- a/lib/src/spot/selectors.dart +++ b/lib/src/spot/selectors.dart @@ -681,6 +681,19 @@ extension ReadSingleSnapshot on WidgetSelector { return snapshot_file.snapshot(this).single.widget; } + /// Convenience getter to access the [State] of a Widget found by the [WidgetSelector] + S snapshotState() { + final matcher = snapshot_file.snapshot(this).single; + final element = matcher.element; + if (element is! StatefulElement) { + throw StateError( + '${element.widget.toStringShort()} is not a StatefulWidget and does not have a State', + ); + } + final state = element.state as S; + return state; + } + /// Convenience getter to access the [Element] when evaluating the [WidgetSelector] Element snapshotElement() { return snapshot_file.snapshot(this).single.element; @@ -692,6 +705,15 @@ extension ReadSingleSnapshot on WidgetSelector { // So we can safely assume that this cast never fails. return snapshot_file.snapshot(this).single.element.renderObject!; } + + /// Convenience getter to access the [RenderBox] when evaluating the [WidgetSelector] + RenderBox snapshotRenderBox() { + final renderObject = snapshotRenderObject(); + if (renderObject is! RenderBox) { + throw StateError('RenderObject $renderObject is not a RenderBox'); + } + return renderObject; + } } /// Extension on [WidgetSelector] providing methods to specify the diff --git a/test/selectors/selector_test.dart b/test/selectors/selector_test.dart index 96fb1ff2..7b9a52e7 100644 --- a/test/selectors/selector_test.dart +++ b/test/selectors/selector_test.dart @@ -79,4 +79,80 @@ void main() { final AnyText singleMap = singleSelector.mapElementToWidget(centerElement); expect(multiMap.runtimeType, singleMap.runtimeType); }); + + testWidgets('snapshotWidget()', (tester) async { + await tester.pumpWidget(const MaterialApp(home: Text('home'))); + final widget = spotText('home').snapshotWidget(); + expect(widget.text, 'home'); + }); + + testWidgets('snapshotState()', (tester) async { + await tester.pumpWidget( + const MaterialApp(home: _MyContainer(color: Colors.red)), + ); + final state = spot<_MyContainer>().snapshotState<_MyContainerState>(); + expect(state.innerValue, 'stateValue'); + final plainState = spot<_MyContainer>().snapshotState(); + expect(plainState.mounted, isTrue); + expect( + () => spot().snapshotState(), + throwsA( + isA().having( + (e) => e.message, + 'message', + 'DefaultTextStyle is not a StatefulWidget and does not have a State', + ), + ), + ); + }); + + testWidgets('snapshotElement()', (tester) async { + await tester.pumpWidget( + WidgetsApp( + builder: (_, __) => const Center(child: Text('home')), + color: Colors.red, + ), + ); + final element = spotText('home').snapshotElement(); + expect(element.size?.height, 14); + }); + + testWidgets('snapshotRenderObject()', (tester) async { + await tester.pumpWidget( + WidgetsApp( + builder: (_, __) => const Center(child: Text('home')), + color: Colors.red, + ), + ); + final renderObject = spotText('home').snapshotRenderObject(); + expect(renderObject.isRepaintBoundary, isFalse); + }); + + testWidgets('snapshotRenderBox()', (tester) async { + await tester.pumpWidget( + WidgetsApp( + builder: (_, __) => const Center(child: Text('home')), + color: Colors.red, + ), + ); + final renderBox = spotText('home').snapshotRenderBox(); + expect(renderBox.localToGlobal(Offset.zero), const Offset(372.0, 293.0)); + }); +} + +class _MyContainer extends StatefulWidget { + const _MyContainer({required this.color}); + + final Color color; + + @override + State<_MyContainer> createState() => _MyContainerState(); +} + +class _MyContainerState extends State<_MyContainer> { + final innerValue = 'stateValue'; + @override + Widget build(BuildContext context) { + return Placeholder(color: widget.color); + } }