Skip to content

Commit

Permalink
Add snapshotRenderBox(), snapshotState() (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
passsy authored Nov 22, 2024
1 parent 8ca6229 commit 594724d
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
22 changes: 22 additions & 0 deletions lib/src/spot/selectors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,19 @@ extension ReadSingleSnapshot<W extends Widget> on WidgetSelector<W> {
return snapshot_file.snapshot(this).single.widget;
}

/// Convenience getter to access the [State] of a Widget found by the [WidgetSelector]
S snapshotState<S extends State>() {
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;
Expand All @@ -692,6 +705,15 @@ extension ReadSingleSnapshot<W extends Widget> on WidgetSelector<W> {
// 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<W>] providing methods to specify the
Expand Down
76 changes: 76 additions & 0 deletions test/selectors/selector_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<DefaultTextStyle>().snapshotState(),
throwsA(
isA<StateError>().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);
}
}

0 comments on commit 594724d

Please sign in to comment.