From 7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c Mon Sep 17 00:00:00 2001 From: Daniel Eshkeri Date: Thu, 10 Oct 2024 11:28:43 +0100 Subject: [PATCH] test: banner (#184) test: started making banner tests test: banner tests fix: added styles to banner text docs: added testing_conventions.mdx to keep track how we are testing in flutter test: improved banner tests test: added background color test test: removed unused import chore(automated): Lint commit and format fix: added golden group to testing_conventions.mdx test: replaced IconButton with Icon from golden tests test: changed the iconbutton to a ZetaIcon for golden tests --- .../Flutter/GeneratedPluginRegistrant.swift | 2 +- lib/src/components/banner/banner.dart | 9 +- test/src/components/banner/banner_test.dart | 359 ++++++++++++++++++ .../banner/golden/banner_negative.png | Bin 0 -> 3373 bytes .../banner/golden/banner_positive.png | Bin 0 -> 3374 bytes .../banner/golden/banner_primary.png | Bin 0 -> 3372 bytes .../banner/golden/banner_warning.png | Bin 0 -> 3372 bytes test/test_utils/utils.dart | 6 + test/testing_conventions.mdx | 9 + 9 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 test/src/components/banner/banner_test.dart create mode 100644 test/src/components/banner/golden/banner_negative.png create mode 100644 test/src/components/banner/golden/banner_positive.png create mode 100644 test/src/components/banner/golden/banner_primary.png create mode 100644 test/src/components/banner/golden/banner_warning.png create mode 100644 test/testing_conventions.mdx diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 63ad0d13..8c72a8c7 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,7 +7,7 @@ import Foundation import path_provider_foundation import shared_preferences_foundation -import sqflite +import sqflite_darwin import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { diff --git a/lib/src/components/banner/banner.dart b/lib/src/components/banner/banner.dart index 3bb738ab..ae7e5ebd 100644 --- a/lib/src/components/banner/banner.dart +++ b/lib/src/components/banner/banner.dart @@ -101,7 +101,14 @@ class ZetaBanner extends MaterialBanner { size: Zeta.of(context).spacing.xl_2, ), ), - Flexible(child: Text(title)), + Flexible( + child: Text( + title, + style: ZetaTextStyles.labelLarge.copyWith( + color: Zeta.of(context).colors.textInverse, + ), + ), + ), ], ), ), diff --git a/test/src/components/banner/banner_test.dart b/test/src/components/banner/banner_test.dart new file mode 100644 index 00000000..c733a75f --- /dev/null +++ b/test/src/components/banner/banner_test.dart @@ -0,0 +1,359 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; +import '../../../test_utils/utils.dart'; + +ZetaColorSwatch _backgroundColorFromType(BuildContext context, ZetaBannerStatus type) { + final zeta = Zeta.of(context); + + switch (type) { + case ZetaBannerStatus.primary: + return zeta.colors.primary; + case ZetaBannerStatus.positive: + return zeta.colors.surfacePositive; + case ZetaBannerStatus.warning: + return zeta.colors.orange; + case ZetaBannerStatus.negative: + return zeta.colors.surfaceNegative; + } +} + +void main() { + const goldenFile = GoldenFiles(component: 'banner'); + + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('ZetaBanner Accessibility Tests', () { + for (final type in ZetaBannerStatus.values) { + testWidgets('meets contrast ratio guideline for $type', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + leadingIcon: Icons.info, + trailing: const ZetaIconButton(icon: Icons.close), + type: type, + ); + }, + ), + ), + ); + + await expectLater(tester, meetsGuideline(textContrastGuideline)); + }); + } + + testWidgets('semantic label works correctly', (WidgetTester tester) async { + String semanticLabelText = 'Banner Title'; + StateSetter? setState; + + await tester.pumpWidget( + StatefulBuilder( + builder: (context, setState2) { + setState = setState2; + return TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + semanticLabel: semanticLabelText, + ); + }, + ), + ); + }, + ), + ); + + final Semantics titleSematicLabel = tester.widgetList(find.byType(Semantics)).last; + expect(titleSematicLabel.properties.label, equals('Banner Title')); + + setState?.call(() => semanticLabelText = ''); + await tester.pumpAndSettle(); + + final Semantics titleSematicLabel2 = tester.widgetList(find.byType(Semantics)).last; + expect(titleSematicLabel2.properties.label, equals('')); + }); + + testWidgets('uses title for sematic label if nessaccary', (WidgetTester tester) async { + String titleText = 'Banner Title'; + StateSetter? setState; + + await tester.pumpWidget( + StatefulBuilder( + builder: (context, setState2) { + setState = setState2; + return TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: titleText, + ); + }, + ), + ); + }, + ), + ); + + final Semantics titleSematicLabel = tester.widgetList(find.byType(Semantics)).last; + expect(titleSematicLabel.properties.label, equals('Banner Title')); + + setState?.call(() => titleText = ''); + await tester.pumpAndSettle(); + + final Semantics titleSematicLabel2 = tester.widgetList(find.byType(Semantics)).last; + expect(titleSematicLabel2.properties.label, equals('')); + }); + }); + + group('ZetaBanner Content Tests', () { + testWidgets('ZetaBanner title is correct', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + ); + }, + ), + ), + ); + final Finder textFinder = find.text('Banner Title'); + expect(textFinder, findsOneWidget); + }); + + testWidgets('ZetaBanner leading icon is correct', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + leadingIcon: Icons.info, + ); + }, + ), + ), + ); + final Finder iconFinder = find.byIcon(Icons.info); + expect(iconFinder, findsOneWidget); + + final Icon iconWidget = tester.widget(iconFinder); + expect(iconWidget.icon, equals(Icons.info)); + }); + + testWidgets('trailing widget is correct', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + trailing: const ZetaIconButton(icon: ZetaIcons.close), + ); + }, + ), + ), + ); + + final Finder iconButtonFinder = find.byType(ZetaIconButton); + expect(iconButtonFinder, findsOneWidget); + + final ZetaIconButton button = tester.widget(iconButtonFinder); + expect(button.icon, equals(ZetaIcons.close)); + }); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaAccordion( + title: 'Title', + ).debugFillProperties(diagnostics); + + expect(diagnostics.finder('title'), '"Title"'); + expect(diagnostics.finder('rounded'), 'null'); + expect(diagnostics.finder('contained'), 'false'); + expect(diagnostics.finder('isOpen'), 'false'); + }); + }); + + group('ZetaBanner Dimension Tests', () { + testWidgets('icon is the correct size', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + leadingIcon: ZetaIcons.info, + ); + }, + ), + ), + ); + final Finder iconFinder = find.byIcon(ZetaIcons.info); + + final Icon iconWidget = tester.widget(iconFinder); + + expect(iconWidget.size, Zeta.of(tester.element(iconFinder)).spacing.xl_2); + }); + + testWidgets('icon padding is correct', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + leadingIcon: ZetaIcons.info, + ); + }, + ), + ), + ); + final Finder paddingFinder = find.widgetWithIcon(Padding, ZetaIcons.info); + + final Padding paddingWidget = tester.firstWidget(paddingFinder); + + expect(paddingWidget.padding, equals(const EdgeInsets.only(right: 8))); + }); + + testWidgets('banner padding is correct', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + ); + }, + ), + ), + ); + final Finder paddingFinder = find.byType(Padding); + + final Padding paddingWidget = tester.widgetList(paddingFinder).elementAt(1); + + expect(paddingWidget.padding, equals(const EdgeInsetsDirectional.only(start: 16, top: 2))); + }); + }); + + group('ZetaBanner Styling Tests', () { + for (final type in ZetaBannerStatus.values) { + testWidgets('title styles are correct for $type', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + type: type, + ); + }, + ), + ), + ); + final Finder textFinder = find.text('Banner Title'); + final Text textWidget = tester.widget(find.byType(Text)); + expect( + textWidget.style, + equals( + ZetaTextStyles.labelLarge.copyWith( + color: Zeta.of(tester.element(textFinder)).colors.textInverse, + ), + ), + ); + }); + + testWidgets('icon color is correct for $type', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + leadingIcon: Icons.info, + type: type, + ); + }, + ), + ), + ); + + final Finder iconFinder = find.byIcon(Icons.info); + + final Icon iconWidget = tester.widget(iconFinder); + expect(iconWidget.color, _backgroundColorFromType(tester.element(iconFinder), type).onColor); + }); + + testWidgets('background colors are correct for $type', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + type: type, + ); + }, + ), + ), + ); + final Finder finder = find.byType(ZetaBanner); + final ZetaBanner widget = tester.firstWidget(finder); + + expect(widget.backgroundColor, equals(_backgroundColorFromType(tester.element(finder), type))); + }); + } + }); + + group('ZetaBanner Interaction Tests', () {}); + + group('ZetaBanner Golden Tests', () { + for (final type in ZetaBannerStatus.values) { + testWidgets('ZetaBanner ${type.toString().split('.').last} golden', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return ZetaBanner( + context: context, + title: 'Banner Title', + leadingIcon: Icons.info, + trailing: const ZetaIcon(Icons.chevron_right), + type: type, + ); + }, + ), + ), + ); + + await expectLater( + find.byType(ZetaBanner), + matchesGoldenFile(goldenFile.getFileUri('banner_${type.toString().split('.').last}')), + ); + }); + } + }); +} diff --git a/test/src/components/banner/golden/banner_negative.png b/test/src/components/banner/golden/banner_negative.png new file mode 100644 index 0000000000000000000000000000000000000000..31ff7fd7b04e7da7706bc027f011b01c07bee682 GIT binary patch literal 3373 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr00`r;B4q#hkZy4f75;2si}FEcN2|En;|AFKOX$sepmY+;6rT>!)A4 zKUCW;7B%^8s%?1J@A*>}1_ptO+rNS49Vp}wU|>*Cc3@y==wV=FU@&3=N}eD`@XYTM ziPxVRrktzY#WRzcf#FYn|L@(r91IK-1Qi$<9MWD0uh}10>t>qu7bv-B-LCz&8-Naw zWMpDsNHME!3jJC2?@eus^Y4E@V!l@Hzi*Yjzn-CC7Qz5GgmMI;QU+8@ay68hB?+ z&VSzYT@e_z$r}*PAt!LL8b2yXc48e3y3wGcR>3!#mPXUkXj-DY0Wq47M)T2VJ{rwO dz)r~i6}MHYdshCQ3T%`!c)I$ztaD0e0s!;*^Zft- literal 0 HcmV?d00001 diff --git a/test/src/components/banner/golden/banner_positive.png b/test/src/components/banner/golden/banner_positive.png new file mode 100644 index 0000000000000000000000000000000000000000..b96308fbdd5035c3fa0297ce4193cca729a1d819 GIT binary patch literal 3374 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr00Sr;B4q#hkZy74r@|2sm8aq4oBr(531r&zLyh+IkgjHxQmaO~fUx zo_YUq<0#dX{b@^mmfxNAL|I1@$Vqi#N;b35xAo+{s_4@GnfxS`xfs*T2zk6?{z`)?( z)&S(qJUcHTbY8rDwfw^icKhtZYwx|^e(u(Ld4>gP2m=J+$|V`$rXmDqp8eNRu+wgv z6fZDb!tUPBb%6WEd47WL_tPf|85j<I|N)elF{r5}E*^==7uj literal 0 HcmV?d00001 diff --git a/test/src/components/banner/golden/banner_primary.png b/test/src/components/banner/golden/banner_primary.png new file mode 100644 index 0000000000000000000000000000000000000000..c1ff44d84e96296579e8428ddcfb069b1f7959f4 GIT binary patch literal 3372 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr00mr;B4q#hkZy7Zx3I5OD~+v~un0ht>zfu6~-E@T$OY<=T}An|2CX z9{-%ie&$}K_2H6Tccu$7FgQHOea{Ru?zo}@14Ba(10w^25fcjo!wC)n1_lLXas=Ni zx9e{IJi+qvSHl!O28Iv&ZQs2&Qvh1!*1*8PFms{*hJRIGC9kgd$`6#>_3B?9GZOvS53Eq6u&oTXW zVJ3^^J7zjyP3tVEbG^JU3S{c3rj=@x9#Wcwa# z;9a&kfBIfK(D!`@5zZkiaIqRcDoEYv8V%3U@T690IGUD5)6!^KqP$fxnvX{F(P%yz dVfp9}lg|#-(2V&V-+^ss22WQ%mvv4FO#m)V{6hc$ literal 0 HcmV?d00001 diff --git a/test/src/components/banner/golden/banner_warning.png b/test/src/components/banner/golden/banner_warning.png new file mode 100644 index 0000000000000000000000000000000000000000..18786b1427a8b352f48e26f5bb9d14d6eb18d60e GIT binary patch literal 3372 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr00mr;B4q#hkZyH|91wNHhd?EXtY2Xwz~xZQ2&OLvjkUFHSJ?lxlyDTgW3@11QfL=BJ z!gcMlS$_;41H+Arg3rr^L4wK-K;QQ;FfuS0eSU0J8LtQQwgDrE%LLbmq=pc z6Zw0|Z_QjwXiD#4wr|s9FDyR=cCsF16Z{ dBPlJN(LWKWa&n$l@ec+d@O1TaS?83{1OQ2r`qBUZ literal 0 HcmV?d00001 diff --git a/test/test_utils/utils.dart b/test/test_utils/utils.dart index e8e7acc2..1bfb59f1 100644 --- a/test/test_utils/utils.dart +++ b/test/test_utils/utils.dart @@ -2,6 +2,8 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart'; extension Util on DiagnosticPropertiesBuilder { @@ -27,3 +29,7 @@ class GoldenFiles { .replace(scheme: 'file'); } } + +BuildContext getBuildContext(WidgetTester tester, Type type) { + return tester.element(find.byType(type)); +} diff --git a/test/testing_conventions.mdx b/test/testing_conventions.mdx new file mode 100644 index 00000000..4146566c --- /dev/null +++ b/test/testing_conventions.mdx @@ -0,0 +1,9 @@ +## Groups + +- Accessibility Tests +- Content Tests +- Dimensions Tests +- Styling Tests +- Interaction Tests +- Golden Tests +- Performance Tests