From b3b642500c1246f4f639833ff71c479fcaceb5da Mon Sep 17 00:00:00 2001 From: Prashant Sawant Date: Mon, 10 Jun 2024 23:12:44 -0400 Subject: [PATCH] - added testing guide on golden and unit tests --- CONTRIBUTING.md | 121 ++++++++++ pubspec.yaml | 1 + .../components/tooltip/golden/arrow_down.png | Bin 0 -> 3651 bytes .../components/tooltip/golden/arrow_left.png | Bin 0 -> 3651 bytes .../components/tooltip/golden/arrow_right.png | Bin 0 -> 3647 bytes .../components/tooltip/golden/arrow_up.png | Bin 0 -> 3658 bytes test/src/components/tooltip/tooltip_test.dart | 208 ++++++++++++++++++ 7 files changed, 330 insertions(+) create mode 100644 test/src/components/tooltip/golden/arrow_down.png create mode 100644 test/src/components/tooltip/golden/arrow_left.png create mode 100644 test/src/components/tooltip/golden/arrow_right.png create mode 100644 test/src/components/tooltip/golden/arrow_up.png create mode 100644 test/src/components/tooltip/tooltip_test.dart diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4b53985d..24c127c2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,6 +34,127 @@ For more information on widgetbook, [read the docs](https://docs.widgetbook.io/) We should also create a test for each widget created. +### Contributing Guide for Writing Tests + +#### Folder Structure + +To maintain consistency and ease of navigation, follow the same folder structure in the `test` directory as in the `lib` directory. For example, if you have a file located at `lib/src/components/tooltip/tooltip.dart`, your test file should be located at `test/src/components/tooltip/tooltip_test.dart`. + +##### Example Folder Structure + +``` +lib/ +└── src/ + └── components/ + └── tooltip/ + └── tooltip.dart + +test/ +└── src/ + └── components/ + └── tooltip/ + └── tooltip_test.dart +``` + +#### Writing Tests + +1. **Unit Tests**: Test individual functions and classes. +2. **Widget Tests**: Test individual widgets and their interactions. +3. **Integration Tests**: Test the complete app or large parts of it. + +##### Guidelines + +- Use descriptive test names. +- Test one thing per test. +- Mock dependencies using `mockito`. +- Write tests for edge cases. + +#### Measuring Code Coverage + +To ensure high code coverage (at least > 96%), use the following steps: + +##### With 'lcov' +1. **Run the script coverage.sh from the project root, which make use of `lcov` to generate the coverage report**: + ```sh + sh coverage.sh + ``` +##### Alternatively, with 'genhtml' +1. **Install dependencies**: + ```sh + flutter pub add --dev test coverage + ``` + +2. **Run tests with coverage**: + ```sh + flutter test --coverage + ``` + +3. **Generate coverage report**: + ```sh + genhtml coverage/lcov.info -o coverage/html + ``` + +4. **View coverage report**: + Open `coverage/html/index.html` in a web browser. + + +##### Maximizing Coverage + +- Write tests for all public methods and classes. +- Include tests for edge cases and error handling. +- Avoid excluding files from coverage unless necessary. + +#### Golden Tests + +Golden tests are used to ensure that the visual output of your widgets remains consistent over time. They are particularly useful for complex UI components. + +##### Why Golden Tests? + +In the `tooltip` example, the direction of the arrows in the tooltip is crucial. Golden tests help to ensure that any changes to the tooltip do not unintentionally alter the direction of the arrows or other visual aspects. + +##### Adding Golden Tests + +1. **Set up golden tests**: + ```dart + import 'package:flutter_test/flutter_test.dart'; + import 'package:zeta_flutter/zeta_flutter.dart'; + + import '../../../test_utils/test_app.dart'; + + void main() { + testWidgets('renders with arrow correctly in up direction', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaTooltip( + arrowDirection: ZetaTooltipArrowDirection.up, + child: Text('Tooltip up'), + ), + ), + ), + ); + + expect(find.text('Tooltip up'), findsOneWidget); + + // Verifying the CustomPaint with different arrow directions. + await expectLater( + find.byType(ZetaTooltip), + matchesGoldenFile(p.join('golden', 'arrow_up.png')), + ); + }); + } + ``` + +2. **Run golden tests**: + ```sh + flutter test --update-goldens + ``` + +3. **Verify golden tests**: + Ensure that the generated golden files are correct and commit them to the repository. + + + ## Code reviews All submissions, including submissions by project members, require review. We use GitHub pull requests (PRs) for this purpose. Consult [GitHub Help](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) for more information on using pull requests. diff --git a/pubspec.yaml b/pubspec.yaml index 4187fe6d..4b15abc6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,7 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.4.4 + path: ^1.9.0 zds_analysis: ^1.0.0 flutter: diff --git a/test/src/components/tooltip/golden/arrow_down.png b/test/src/components/tooltip/golden/arrow_down.png new file mode 100644 index 0000000000000000000000000000000000000000..aa99574828a6aa4e8b76f44e1cb35ae22cefdb9e GIT binary patch literal 3651 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sOhbzi(^Q|oVRxk^Fvb^jz9cvwx(qAgC`CL8(YpNI6hY2$jiZE*U84c zWXJ0GgBl-XZ?qWgICzUMXYK01wrMT*y{qS??^QSdKWEQr{qJ|?Tz$BE@nXge0siyt zrTgc->6>1?{@A~N|K3>6NqqhK_2W~=o)%_jSKqm@$F7?1fQdFElg`!m_5TdF?b~lz z&Bp!Z$-~DV-(J6eW*(!#(OYjA88+Met1SP$!sWW9WyUt7o zMzPlq9wZ=S85m|3Z)Rtx>AH69+Oe(K*ZXS!W^I*9-YVtZ-rl{VuE#KLPlX|$f5BhR zIp2QUA3MI+o?$uAl#2fIdNC6F|9pG>Msog^v+K={-!{*;J5@fv_4j|n_xuMkp9$(P zFeoTHFfcUqFfcMO7%{OhFr45JKoz{l%yfW#XVuqNKmI&AnjHWBXbnS;q65PZ2W$Rv zzmmP;pBW9>+&2Ktm;g3o_2F`N_s#AMXFeRAF1=ZOLqh%kt>)EC2Sn-^f&TjQ^D|H^ z;GEos6egC4HND=`)BF<_&aq86$-=QA!hHYjdA5zMMc*1!Kg$awT%2cDZzgAZQ&#_b zT<_n%e{U9B9KLq_`thl~rypOt_HD<;%Gz%-4Vf$)497nm_UAv=Z_R)2_szYx%a8xH zE`RLGFvm^qAk%}0clUQ%zq|XdKBa@7!Oy9Ip&;jfufRvf1DU z=6^;|5CC=H3J9#BGb)IdTt)+AG(hkdD5EK4G=+?&kkMKOv#Bv!GK`iCqb0*=$v|?+ b@R|Qt$y{yAc=>n0jv#}ltDnm{r-UW|7LTE= literal 0 HcmV?d00001 diff --git a/test/src/components/tooltip/golden/arrow_left.png b/test/src/components/tooltip/golden/arrow_left.png new file mode 100644 index 0000000000000000000000000000000000000000..7f66b33e576aa16865bf29dd67ae890685f00760 GIT binary patch literal 3651 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sOhbzi(^Q|oVR!P_l2a&G(5~#+qQcyi#E5Ij>ir*ag(@CZ-?n8rY8KD z9b5fXtcU5lgSt+u>^5)5?b(S=DaV_5&s9(OZ1Z%c{^WSSeb3IGvAqB2#nq1=Egn96 z?Ed)Qzk3x`A97^mH5c2d^mhhq_|-(JA(nUHe=C;KR*$?PoJKi)_lfL z&Cig)7RJQFz>pAYXLa_`(;`c~*j-<)y%z62D$#w^!p>^e(O-tgDn30qX<>Er*X1Ak z@BcTvR$b4)Q1O4RmV=#LaoV3BpZ#CYdfL3<@6T^PJ|FGgTJycV;Q#eXu$sLe#n(ia z=lPqmGXNd2p83hOxwjwx-VL%aV*XQ!N6ZWi89GOSk^vv3;ushdHt*S4om}(%ZSTtG zzyME6uskNA1XQcB$BzBUvv>CyA5~5$FaG%G=*N>cH|OuYcd&+m;mk>CcENjh_9{Bu zyLh*{+?0O&Dg2L_mHK%O5J91Rd~(i}|; zqq$?WWB``YqZwf|BaCK*(Tp(KG#YIyLR#&Eszx{yUtKUabN{b~3}C;6!PC{xWt~$( F699fgpi}?= literal 0 HcmV?d00001 diff --git a/test/src/components/tooltip/golden/arrow_right.png b/test/src/components/tooltip/golden/arrow_right.png new file mode 100644 index 0000000000000000000000000000000000000000..30d99000c0bec9ab95a4b899e8d2f3732b6d8e53 GIT binary patch literal 3647 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sOhDri(^Q|oVRy3_JyP}v^}hMi{3UlVc}V(vuf46dKr6|dd(H213K?D zmM?HTaDa8i-qude0{+{a-j0jdZr|0o+c#JBOxmGpo14!wpQn6!`Xv9}lQVOUp6tH* z>fqw`$M61DKmPb|a$!k{$%Fq%+1c4K`zkW;?AT_%?p^H6Dn14V>8L-SKCfS$JwJAS z75kn`f35HL^WXoU@Jf<_A?HEw8GFGxO+GD_i3^2qY~Xm zBj(PT)A{(%y*3^VR|Ge24T`T22v`@a#nGnelD{_R-& z`j_s1(m(#&f9^LY!+|e0O-I+%e7?JOr8)z{21dq1Yig>0PvZb;HDo;WrbB?C;ir(o znMoi4bw(xzh7=Z#&0n5AH$NU8e`ih=Fg}*6ILwg!$jD$2uV2g0v3}d;&Bpik6(qiW zy*DG@_z^Qh!abG~ZF%>1)~-T`*fg<%O=4h}5V_~`yKh;q1UIZp-!JQD%gE5ct8;#? zwfR@Ji0;i?3=vgcG>^{ literal 0 HcmV?d00001 diff --git a/test/src/components/tooltip/golden/arrow_up.png b/test/src/components/tooltip/golden/arrow_up.png new file mode 100644 index 0000000000000000000000000000000000000000..da590cb580efa4b72348a2bd02f04952cae12241 GIT binary patch literal 3658 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sOgKRi(^Q|oVRy3=1omyIR5av$-1{{2TwLOF?+5**mO*LBMXmO9On`d zkJsz&H3dG9yU}8_PkcY0Ke_u)(Z0#~h2P%&sbM%E zBxiF=KDp=P<-hXjaZCqx{mgmC$gpNQd;hoNhnxR?ukRLMn6}aII|IYD>wU#;pMuB}jrF zgv;B z()$@1BMxmAKYx7k;=>( + find.descendant( + of: find.byType(ZetaTooltip), + matching: find.byType(DecoratedBox), + ), + ); + + expect((tooltipBox.decoration as BoxDecoration).color, Colors.red); + + final padding = tester.widget( + find.descendant( + of: find.byType(ZetaTooltip), + matching: find.byType(Padding), + ), + ); + + expect(padding.padding, const EdgeInsets.all(20)); + }); + + testWidgets('renders with custom text style', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaTooltip( + textStyle: TextStyle(fontSize: 24, color: Colors.blue), + child: Text('Tooltip text'), + ), + ), + ), + ); + + // Find the RichText widget, which is a descendant of the Text widget + final richTextFinder = find.descendant( + of: find.text('Tooltip text'), + matching: find.byType(RichText), + ); + + expect(richTextFinder, findsOneWidget); + + // Verify the styles + final RichText richTextWidget = tester.widget(richTextFinder); + final TextSpan textSpan = richTextWidget.text as TextSpan; + + expect(textSpan.style?.fontSize, 24); + expect(textSpan.style?.color, Colors.blue); + }); + + testWidgets('renders with arrow correctly in up direction', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaTooltip( + arrowDirection: ZetaTooltipArrowDirection.up, + child: Text('Tooltip up'), + ), + ), + ), + ); + + expect(find.text('Tooltip up'), findsOneWidget); + + // Verifying the CustomPaint with different arrow directions. + await expectLater( + find.byType(ZetaTooltip), + matchesGoldenFile(p.join('golden', 'arrow_up.png')), + ); + }); + + testWidgets('renders with arrow correctly in down direction', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaTooltip( + child: Text('Tooltip down'), + ), + ), + ), + ); + + expect(find.text('Tooltip down'), findsOneWidget); + + // Verifying the CustomPaint with different arrow directions. + await expectLater( + find.byType(ZetaTooltip), + matchesGoldenFile(p.join('golden', 'arrow_down.png')), + ); + }); + + testWidgets('renders with arrow correctly in left direction', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaTooltip( + arrowDirection: ZetaTooltipArrowDirection.left, + child: Text('Tooltip left'), + ), + ), + ), + ); + + expect(find.text('Tooltip left'), findsOneWidget); + + // Verifying the CustomPaint with different arrow directions. + await expectLater( + find.byType(ZetaTooltip), + matchesGoldenFile(p.join('golden', 'arrow_left.png')), + ); + }); + + testWidgets('renders with arrow correctly in right direction', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaTooltip( + arrowDirection: ZetaTooltipArrowDirection.right, + child: Text('Tooltip right'), + ), + ), + ), + ); + + expect(find.text('Tooltip right'), findsOneWidget); + + // Verifying the CustomPaint with different arrow directions. + await expectLater( + find.byType(ZetaTooltip), + matchesGoldenFile(p.join('golden', 'arrow_right.png')), + ); + }); + + testWidgets('renders with rounded and sharp corners', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: Column( + children: [ + ZetaTooltip( + child: Text('Rounded tooltip'), + ), + ZetaTooltip( + rounded: false, + child: Text('Sharp tooltip'), + ), + ], + ), + ), + ), + ); + + expect(find.text('Rounded tooltip'), findsOneWidget); + expect(find.text('Sharp tooltip'), findsOneWidget); + + final roundedTooltipBox = tester.widget( + find.descendant( + of: find.widgetWithText(ZetaTooltip, 'Rounded tooltip'), + matching: find.byType(DecoratedBox), + ), + ); + + final sharpTooltipBox = tester.widget( + find.descendant( + of: find.widgetWithText(ZetaTooltip, 'Sharp tooltip'), + matching: find.byType(DecoratedBox), + ), + ); + + expect((roundedTooltipBox.decoration as BoxDecoration).borderRadius, ZetaRadius.minimal); + expect((sharpTooltipBox.decoration as BoxDecoration).borderRadius, null); + }); + }); +}