diff --git a/lib/src/spot/effective/effective_text.dart b/lib/src/spot/effective/effective_text.dart index 93cac119..20808441 100644 --- a/lib/src/spot/effective/effective_text.dart +++ b/lib/src/spot/effective/effective_text.dart @@ -3,6 +3,9 @@ import 'package:spot/spot.dart'; import 'package:spot/src/spot/element_extensions.dart'; import 'package:spot/src/spot/selectors.dart'; +/// Matchers for the [Text] widget to make assertions about: +/// - [Text.maxLines] +/// - [Text.textStyle] extension EffectiveTextMatcher on WidgetMatcher { /// Matches the [Text] widget when it has the given [maxLines]. /// @@ -20,6 +23,7 @@ extension EffectiveTextMatcher on WidgetMatcher { ); } + /// Matches the [Text] widget for given maxLines. WidgetMatcher hasEffectiveMaxLines(int? value) { return hasEffectiveMaxLinesWhere((it) { if (value == null) { @@ -30,6 +34,7 @@ extension EffectiveTextMatcher on WidgetMatcher { }); } + /// Matches the [Text] widget when it has the given [TextStyle]. WidgetMatcher hasEffectiveTextStyleWhere(MatchProp match) { return hasProp( elementSelector: (subject) => subject.context.nest( @@ -82,7 +87,11 @@ extension TextStyleSubject on Subject { } } +/// Selectors for the [Text] widget like +/// - [Text.maxLines] +/// - [Text.textStyle] extension EffectiveTextSelector on WidgetSelector { + /// Selects the [Text] widget where given `maxLines` properties match. WidgetSelector withEffectiveMaxLinesMatching(MatchProp match) { return withProp( elementSelector: (subject) => subject.context.nest( @@ -93,9 +102,34 @@ extension EffectiveTextSelector on WidgetSelector { ); } + /// Selects a [Text] widget with given `maxLines`. WidgetSelector withEffectiveMaxLines(int? value) { return withEffectiveMaxLinesMatching((it) => it.equals(value)); } + + /// Selects the [Text] widget where given [TextStyle] properties match. + WidgetSelector withEffectiveTextStyleMatching( + MatchProp match, + ) { + return withProp( + elementSelector: (subject) => subject.context.nest( + () => ['with "textStyle"'], + (Element element) => Extracted.value(_extractTextStyle(element)), + ), + match: match, + ); + } + + /// Selects a [Text] widget with a given [TextStyle]. + WidgetSelector withEffectiveTextStyle(TextStyle? value) { + return withEffectiveTextStyleMatching((it) { + if (value == null) { + it.isNull(); + } else { + it.equals(value); + } + }); + } } int? _extractMaxLines(Element element) { diff --git a/test/widgets/effective_text_test.dart b/test/widgets/effective_text_test.dart index a26cb33e..86b9b123 100644 --- a/test/widgets/effective_text_test.dart +++ b/test/widgets/effective_text_test.dart @@ -223,5 +223,94 @@ void main() { ]), ); }); + + testWidgets( + 'Select with TextStyle', + (widgetTester) async { + final style = TextStyle( + fontSize: 20, + fontStyle: FontStyle.italic, + fontWeight: FontWeight.bold, + letterSpacing: 2, + ); + + await widgetTester.pumpWidget( + MaterialApp( + home: Column( + children: [ + Text( + 'Great Text', + style: TextStyle(fontSize: 20), + ), + DefaultTextStyle( + style: style, + child: Text('Great Text'), + ), + ], + ), + ), + ); + + // Select with single props + spot().withText('Great Text').withEffectiveTextStyleMatching( + (style) { + style.fontSize.equals(20); + style.fontStyle.equals(FontStyle.italic); + style.fontWeight.equals(FontWeight.bold); + style.letterSpacing.equals(2); + }, + ).existsOnce(); + + // Select with complete TextStyle + spot() + .withText('Great Text') + .withEffectiveTextStyle(style) + .existsOnce(); + }, + ); + + testWidgets('Failed selection with TextStyle shows missing values', + (widgetTester) async { + final style = TextStyle( + fontSize: 20, + fontStyle: FontStyle.italic, + fontWeight: FontWeight.bold, + letterSpacing: 2, + ); + + await widgetTester.pumpWidget( + MaterialApp( + home: DefaultTextStyle( + style: style, + child: Text('Great Text'), + ), + ), + ); + + expect( + () => + spot().withText('Great Text').withEffectiveTextStyleMatching( + (style) { + style.fontSize.equals(20); + style.fontStyle.equals(FontStyle.normal); + style.fontWeight.equals(FontWeight.bold); + style.letterSpacing.equals(2); + }, + ).existsOnce(), + throwsSpotErrorContaining([ + 'with "textStyle" that: has fontSize that: equals <20.0> has fontStyle that: equals has fontWeight that: equals has letterSpacing that: equals <2.0>', + ]), + ); + + expect( + () => spot() + .withText('Great Text') + .withEffectiveTextStyle(style.copyWith(fontStyle: FontStyle.normal)) + .existsOnce(), + throwsSpotErrorContaining([ + 'with "textStyle" that: equals ', + ]), + ); + }); }); }