diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 4f6c5762..75d6f554 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -38,25 +38,26 @@ jobs: # Saves output to either success or failure variables. if [ "$failed" == "true" ]; then echo "FAILURE=true" >> $GITHUB_OUTPUT + exit 1 fi - name: Run tests id: test run: | - cd example - # Allows code to run after an error. set -e # Runs flutter test; saves output and if it fails. - out=$(flutter test) || failed='true' + out=$(flutter test --coverage) || failed='true' echo "$out" # Saves output to either success or failure variables. if [ "$failed" == "true" ]; then echo "FAILURE=true" >> $GITHUB_OUTPUT + exit 1 fi + - name: Check for modified files id: git-check run: echo "modified=$(if [ -n "$(git status --porcelain)" ]; then echo "true"; else echo "false"; fi)" >> $GITHUB_ENV @@ -68,8 +69,8 @@ jobs: git add -A git commit -m '[automated commit] lint format and import sort' git push - - name: Print outputs + if: always() env: GH_TOKEN: ${{ github.token }} run: | diff --git a/example/lib/pages/components/button_example.dart b/example/lib/pages/components/button_example.dart index fdea7179..48748d9f 100644 --- a/example/lib/pages/components/button_example.dart +++ b/example/lib/pages/components/button_example.dart @@ -29,7 +29,7 @@ class _ButtonExampleState extends State { void setFab(int index) => setState(() => fab = fabs[index]); - List fabs = []; + List fabs = []; @override Widget build(BuildContext context) { if (fabs.isEmpty) { @@ -38,12 +38,13 @@ class _ButtonExampleState extends State { scrollController: _scrollController, label: 'Small Circle Primary', size: ZetaFabSize.small, + initiallyExpanded: false, shape: ZetaWidgetBorder.full, type: ZetaFabType.primary, - onPressed: () => setFab(0), ), ZetaFAB( scrollController: _scrollController, + initiallyExpanded: false, label: 'Small Rounded Primary', size: ZetaFabSize.small, shape: ZetaWidgetBorder.rounded, @@ -55,6 +56,7 @@ class _ButtonExampleState extends State { label: 'Small Sharp Primary', size: ZetaFabSize.small, shape: ZetaWidgetBorder.sharp, + initiallyExpanded: false, type: ZetaFabType.inverse, onPressed: () => setFab(2), ), @@ -64,6 +66,7 @@ class _ButtonExampleState extends State { size: ZetaFabSize.large, shape: ZetaWidgetBorder.full, type: ZetaFabType.secondary, + initiallyExpanded: false, onPressed: () => setFab(3), ), ZetaFAB( @@ -71,6 +74,7 @@ class _ButtonExampleState extends State { label: 'Large Rounded Secondary', size: ZetaFabSize.large, shape: ZetaWidgetBorder.rounded, + initiallyExpanded: false, type: ZetaFabType.inverse, onPressed: () => setFab(4), ), @@ -80,14 +84,24 @@ class _ButtonExampleState extends State { size: ZetaFabSize.large, shape: ZetaWidgetBorder.sharp, type: ZetaFabType.primary, + initiallyExpanded: false, onPressed: () => setFab(5), ), ]; } - + final ZetaFAB theFab = (fab as ZetaFAB?) ?? (fabs.first); return ExampleScaffold( name: 'Button', - floatingActionButton: fab ?? fabs.first, + floatingActionButton: ZetaFAB( + initiallyExpanded: true, + icon: theFab.icon, + label: theFab.label, + scrollController: _scrollController, + size: theFab.size, + type: theFab.type, + shape: theFab.shape, + onPressed: theFab.onPressed, + ), child: SingleChildScrollView( controller: _scrollController, child: Row( diff --git a/example/test/badge_test.dart b/example/test/badge_test.dart deleted file mode 100644 index 9f54174b..00000000 --- a/example/test/badge_test.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - testWidgets('Initializes with correct parameters', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaLabel( - label: 'Test Label', - status: ZetaWidgetStatus.warning, - ), - ), - ); - - final zetaBadgeFinder = find.byType(ZetaLabel); - final ZetaLabel badge = tester.firstWidget(zetaBadgeFinder); - - expect(badge.rounded, null); - expect(badge.label, 'Test Label'); - expect(badge.status, ZetaWidgetStatus.warning); - }); -} diff --git a/example/test/button_test.dart b/example/test/button_test.dart deleted file mode 100644 index cbd6ed03..00000000 --- a/example/test/button_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - group('ZetaButton Tests', () { - testWidgets('Initializes with correct Label', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaButton( - onPressed: () {}, - label: 'Test Button', - ), - ), - ); - - expect(find.text('Test Button'), findsOneWidget); - }); - }); - - testWidgets('Triggers callback on tap', (WidgetTester tester) async { - bool callbackTriggered = false; - await tester.pumpWidget( - TestWidget( - widget: ZetaButton( - onPressed: () => callbackTriggered = true, - label: 'Test Button', - )), - ); - await tester.tap(find.byType(ZetaButton)); - await tester.pump(); - - expect(callbackTriggered, isTrue); - }); -} diff --git a/example/test/checkbox_test.dart b/example/test/checkbox_test.dart deleted file mode 100644 index 178f3bea..00000000 --- a/example/test/checkbox_test.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - group('ZetaCheckbox Tests', () { - testWidgets('Initializes with correct parameters', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaCheckbox( - value: true, - onChanged: (value) {}, - label: 'Test Checkbox', - ), - ), - ); - - final checkboxFinder = find.byType(ZetaCheckbox); - final ZetaCheckbox checkbox = tester.firstWidget(checkboxFinder); - - expect(checkbox.value, true); - expect(checkbox.rounded, null); - expect(checkbox.label, 'Test Checkbox'); - }); - - testWidgets('ZetaCheckbox changes state on tap', (WidgetTester tester) async { - bool? checkboxValue = true; - - await tester.pumpWidget( - TestWidget( - removeBody: true, - widget: ZetaCheckbox( - value: checkboxValue, - onChanged: (value) { - checkboxValue = value; - }, - ), - ), - ); - - await tester.tap(find.byType(ZetaCheckbox)); - await tester.pump(); - - expect(checkboxValue, false); - }); - }); -} - -class TestWidgetCB extends StatelessWidget { - final Widget widget; - - const TestWidgetCB({Key? key, required this.widget}); - - @override - Widget build(BuildContext context) { - return ZetaProvider( - builder: (context, theme, __) { - return Builder(builder: (context) { - return MaterialApp( - theme: ThemeData( - fontFamily: theme.fontFamily, - textTheme: zetaTextTheme, - ), - home: Scaffold( - body: widget, - ), - ); - }); - }, - ); - } -} diff --git a/example/test/color_test.dart b/example/test/color_test.dart deleted file mode 100644 index d33b0765..00000000 --- a/example/test/color_test.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -void main() { - testWidgets('Dark mode value', (tester) async { - final ZetaColors light = ZetaColors(); - final ZetaColors dark = ZetaColors(brightness: Brightness.dark); - - expect(light.primary.shade10, dark.primary.shade100); - expect(light.primary.shade20, dark.primary.shade90); - expect(light.primary.shade30, dark.primary.shade80); - expect(light.primary.shade40, dark.primary.shade70); - expect(light.primary.shade50, dark.primary.shade60); - }); - - testWidgets('AAA mode value', (tester) async { - final ZetaColors aa = ZetaColors(); - final ZetaColors aaa = ZetaColors(brightness: Brightness.dark, contrast: ZetaContrast.aaa); - - expect(aa.primary.value, aa.primary.shade60.value); - expect(aaa.primary.value, aaa.primary.shade80.value); - }); - - testWidgets('Scheme generator', (tester) async { - final blueSwatch = ZetaColorSwatch.fromColor(ZetaColorBase.blue); - final greySwatch = ZetaColorSwatch.fromColor(ZetaColorBase.cool); - final blackSwatch = ZetaColorSwatch.fromColor(ZetaColorBase.black); - - expect(blueSwatch.shade10 != blueSwatch.shade20, true); - expect(blueSwatch.shade20 != blueSwatch.shade30, true); - expect(blueSwatch.shade30 != blueSwatch.shade40, true); - expect(blueSwatch.shade40 != blueSwatch.shade50, true); - expect(blueSwatch.shade50 != blueSwatch.shade60, true); - expect(blueSwatch.shade60 != blueSwatch.shade70, true); - expect(blueSwatch.shade70 != blueSwatch.shade80, true); - expect(blueSwatch.shade80 != blueSwatch.shade90, true); - expect(blueSwatch.shade90 != blueSwatch.shade100, true); - - expect(greySwatch.shade10 != greySwatch.shade20, true); - expect(greySwatch.shade20 != greySwatch.shade30, true); - expect(greySwatch.shade30 != greySwatch.shade40, true); - expect(greySwatch.shade40 != greySwatch.shade50, true); - expect(greySwatch.shade50 != greySwatch.shade60, true); - expect(greySwatch.shade60 != greySwatch.shade70, true); - expect(greySwatch.shade70 != greySwatch.shade80, true); - expect(greySwatch.shade80 != greySwatch.shade90, true); - expect(greySwatch.shade90 != greySwatch.shade100, true); - - expect(blackSwatch.shade10 != blackSwatch.shade20, true); - expect(blackSwatch.shade20 != blackSwatch.shade30, true); - expect(blackSwatch.shade30 != blackSwatch.shade40, true); - expect(blackSwatch.shade40 != blackSwatch.shade50, true); - expect(blackSwatch.shade50 != blackSwatch.shade60, true); - expect(blackSwatch.shade60 != blackSwatch.shade70, true); - expect(blackSwatch.shade70 != blackSwatch.shade80, true); - expect(blackSwatch.shade80 != blackSwatch.shade90, true); - expect(blackSwatch.shade90 != blackSwatch.shade100, true); - }); -} diff --git a/example/test/dialpad_test.dart b/example/test/dialpad_test.dart deleted file mode 100644 index e0d5869e..00000000 --- a/example/test/dialpad_test.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - group('ZetaDialPad Tests', () { - testWidgets('Initializes with correct parameters', (WidgetTester tester) async { - String number = '', text = ''; - - Future debounceWait() => tester.binding.delayed(const Duration(milliseconds: 500)); - - await tester.pumpWidget( - TestWidget( - screenSize: Size(1000, 1000), - widget: ZetaDialPad( - onNumber: (value) => number += value, - onText: (value) => text += value, - ), - ), - ); - final dialPadFinder = find.byType(ZetaDialPad); - final buttonFinder = find.byType(ZetaDialPadButton); - - final oneFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '1'); - final twoFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '2'); - final threeFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '3'); - final starFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '*'); - final hashFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '#'); - - final ZetaDialPad dialPad = tester.firstWidget(dialPadFinder); - final List dialPadButtons = tester.widgetList(buttonFinder).toList(); - - final ZetaDialPadButton one = tester.firstWidget(oneFinder); - final ZetaDialPadButton two = tester.firstWidget(twoFinder); - final ZetaDialPadButton three = tester.firstWidget(threeFinder); - - /// Dial Pad built correctly. - expect(dialPad.buttonsPerRow, 3); - expect(dialPadButtons.length, 12); - - /// Dial Pad buttons built correctly. - expect(one.primary, '1'); - expect(one.secondary, ''); - expect(two.primary, '2'); - expect(two.secondary, 'ABC'); - expect(three.primary, '3'); - expect(three.secondary, 'DEF'); - - /// Tap button with only number. - await tester.tap(oneFinder); - await tester.pump(); - expect(number, '1'); - - /// Tap button with number and text. - await tester.tap(twoFinder); - await tester.pump(); - await debounceWait(); - expect(number, '12'); - expect(text, 'A'); - - /// Tap different button. - await tester.tap(threeFinder); - await tester.pump(); - await debounceWait(); - expect(number, '123'); - expect(text, 'AD'); - - /// Tap text button twice. - await tester.tap(twoFinder); - await tester.tap(twoFinder); - await tester.pump(); - await debounceWait(); - expect(text, 'ADB'); - - /// Tap text button thrice. - await tester.tap(twoFinder); - await tester.tap(twoFinder); - await tester.tap(twoFinder); - await tester.pump(); - await debounceWait(); - expect(text, 'ADBC'); - - /// Tap different text buttons to ensure debounce is cancelled. - await tester.tap(twoFinder); - await tester.tap(threeFinder); - await tester.tap(twoFinder); - await tester.pump(); - await debounceWait(); - expect(text, 'ADBCADA'); - - /// Tap text button more times than there is options to ensure it loops around correctly. - await tester.tap(threeFinder); - await tester.tap(threeFinder); - await tester.tap(threeFinder); - await tester.tap(threeFinder); - await tester.tap(threeFinder); - await tester.tap(threeFinder); - await tester.tap(oneFinder); - await tester.pump(); - expect(text, 'ADBCADAF'); - number = ''; - - /// Tap buttons with symbols - await tester.ensureVisible(starFinder); - await tester.tap(starFinder); - await tester.tap(hashFinder); - await tester.pump(); - expect(number, '*#'); - - /// Allow all timers to end in text debounce - await debounceWait(); - }); - }); -} diff --git a/example/test/fab_test.dart b/example/test/fab_test.dart deleted file mode 100644 index 63bebbc0..00000000 --- a/example/test/fab_test.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - group('ZetaFAB Tests', () { - testWidgets('Initializes with correct', (WidgetTester tester) async { - final scrollController = ScrollController(); - await tester.pumpWidget(TestWidget( - widget: ZetaFAB( - scrollController: scrollController, - label: 'Label', - ))); - - expect(find.byType(ZetaFAB), findsOneWidget); - }); - - testWidgets('OnPressed callback', (WidgetTester tester) async { - bool isPressed = false; - final scrollController = ScrollController(); - - await tester.pumpWidget(TestWidget( - widget: ZetaFAB( - scrollController: scrollController, - label: 'Label', - onPressed: () => isPressed = true, - ))); - - await tester.tap(find.byType(ZetaFAB)); - await tester.pumpAndSettle(); - expect(isPressed, isTrue); - }); - }); - - testWidgets('Icon Test', (WidgetTester tester) async { - final scrollController = ScrollController(); - await tester.pumpWidget(TestWidget( - widget: ZetaFAB( - scrollController: scrollController, - label: 'Label', - ))); - expect(find.byIcon(ZetaIcons.add_round), findsOneWidget); - }); -} diff --git a/example/test/in_page_banner_test.dart b/example/test/in_page_banner_test.dart deleted file mode 100644 index bf28f160..00000000 --- a/example/test/in_page_banner_test.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; -import 'package:flutter/material.dart'; - -import 'test_components.dart'; - -void main() { - group('ZetaInPageBanner Tests', () { - testWidgets('ZetaInPageBanner creation', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaInPageBanner( - content: Text('Test'), - )), - ); - - expect(find.byType(ZetaInPageBanner), findsOneWidget); - }); - }); - - testWidgets('ZetaInPageBanner displays content text', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaInPageBanner( - content: Text('Test'), - )), - ); - - expect(find.text('Test'), findsOneWidget); - }); - - testWidgets('ZetaInPageBanner shows/hides \'close icon\' correctly', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaInPageBanner( - content: Text('Test'), - onClose: () {}, - )), - ); - expect(find.byIcon(ZetaIcons.close_round), findsOneWidget); - - await tester.pumpWidget( - TestWidget( - widget: ZetaInPageBanner( - content: Text('Test'), - )), - ); - expect(find.byIcon(ZetaIcons.close_sharp), findsNothing); - }); - - testWidgets('ZetaInPageBanner button callbacks work', (WidgetTester tester) async { - bool onPressed = false; - final key = GlobalKey(); - await tester.pumpWidget( - TestWidget( - widget: ZetaInPageBanner( - content: Text('Test'), - actions: [ - ZetaButton( - label: 'Test button', - onPressed: () => onPressed = true, - key: key, - ), - ], - ), - ), - ); - - await tester.tap(find.byKey(key)); - await tester.pumpAndSettle(); - expect(onPressed, isTrue); - }); - - testWidgets('ZetaInPageBanner \'close\' icon tap test', (WidgetTester tester) async { - bool closeIconIsTapped = false; - await tester.pumpWidget( - TestWidget( - widget: ZetaInPageBanner( - onClose: () => closeIconIsTapped = true, - content: Text('Test'), - )), - ); - final closeIcon = find.byIcon(ZetaIcons.close_round); - await tester.tap(closeIcon); - await tester.pump(); - expect(closeIconIsTapped, isTrue); - }); -} diff --git a/example/test/password_input_test.dart b/example/test/password_input_test.dart deleted file mode 100644 index e6335a79..00000000 --- a/example/test/password_input_test.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - testWidgets('ZetaPasswordInput initializes correctly', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaPasswordInput(), - ), - ); - expect(find.byType(ZetaPasswordInput), findsOneWidget); - }); - - testWidgets('Test password visibility', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaPasswordInput(), - ), - ); - final obscureIconOff = find.byIcon(ZetaIcons.visibility_off_round); - expect(obscureIconOff, findsOneWidget); - await tester.tap(obscureIconOff); - await tester.pump(); - - final obscureIconOn = find.byIcon(ZetaIcons.visibility_round); - expect(obscureIconOn, findsOneWidget); - }); - - testWidgets('Test error message visibility', (WidgetTester tester) async { - String? testValidator(String? value) { - final regExp = RegExp(r'\d'); - if (value != null && regExp.hasMatch(value)) return 'Error'; - return null; - } - - final controller = TextEditingController(); - controller.text = 'password123'; - final formKey = GlobalKey(); - - await tester.pumpWidget( - TestWidget( - widget: Form( - key: formKey, - child: ZetaPasswordInput( - controller: controller, - validator: testValidator, - ), - ), - ), - ); - formKey.currentState?.validate(); - await tester.pump(); - - // There will be two matches for the error text as the form field itself contains a hidden one. - expect(find.text('Error'), findsExactly(2)); - }); -} diff --git a/example/test/priority_pill_test.dart b/example/test/priority_pill_test.dart deleted file mode 100644 index 32f6acad..00000000 --- a/example/test/priority_pill_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - testWidgets('Initializes with correct label and index', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaPriorityPill( - label: 'High', - index: '2', - ), - ), - ); - expect(find.text('High'), findsOneWidget); - expect(find.text('2'), findsOneWidget); - }); -} diff --git a/example/test/status_label_test.dart b/example/test/status_label_test.dart deleted file mode 100644 index b1908792..00000000 --- a/example/test/status_label_test.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; -import 'package:flutter/material.dart'; - -void main() { - group('ZetaStatusLabel Tests', () { - testWidgets('Initializes with correct properties', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidgetStatusLabel( - widget: ZetaStatusLabel(label: 'Test Label'), - ), - ); - expect(find.text('Test Label'), findsOneWidget); - }); - }); - - testWidgets('Initializes with correct label and custom icon', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidgetStatusLabel( - widget: ZetaStatusLabel( - label: 'Custom Icon', - customIcon: Icons.person, - ), - ), - ); - expect(find.text('Custom Icon'), findsOneWidget); - expect(find.byIcon(Icons.person), findsOneWidget); - }); -} - -class TestWidgetStatusLabel extends StatelessWidget { - final Widget widget; - - const TestWidgetStatusLabel({Key? key, required this.widget}); - - @override - Widget build(BuildContext context) { - return ZetaProvider( - builder: (context, theme, __) { - return Builder(builder: (context) { - return MaterialApp( - theme: ThemeData( - fontFamily: theme.fontFamily, - textTheme: zetaTextTheme, - ), - home: Scaffold( - body: widget, - ), - ); - }); - }, - ); - } -} diff --git a/example/test/tag_test.dart b/example/test/tag_test.dart deleted file mode 100644 index 6925b94c..00000000 --- a/example/test/tag_test.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - group('ZetaCheckbox Tests', () { - testWidgets('Initializes with correct parameters', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaTag.right( - label: 'Tag', - ), - ), - ); - - expect(find.text('Tag'), findsOneWidget); - }); - - testWidgets('ZetaTag handles direction correctly', (WidgetTester tester) async { - const widgetLeft = TestWidget( - widget: ZetaTag( - label: 'Tag', - direction: ZetaTagDirection.left, - )); - - const widgetRight = TestWidget( - widget: ZetaTag( - label: 'Tag', - direction: ZetaTagDirection.right, - )); - - await tester.pumpWidget(widgetLeft); - expect(find.byType(ZetaTag), findsOneWidget); - - await tester.pumpWidget(widgetRight); - expect(find.byType(ZetaTag), findsOneWidget); - }); - }); -} diff --git a/example/test/test_components.dart b/example/test/test_components.dart deleted file mode 100644 index ef4c9b7f..00000000 --- a/example/test/test_components.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -class TestWidget extends StatelessWidget { - final Size? screenSize; - final Widget widget; - final ThemeMode? themeMode; - final bool removeBody; - final bool? rounded; - - const TestWidget({ - required this.widget, - this.screenSize, - super.key, - this.themeMode, - this.removeBody = false, - this.rounded, - }); - - @override - Widget build(BuildContext context) { - final size = screenSize ?? const Size(1280, 720); - - return ZetaProvider( - initialThemeMode: themeMode ?? ThemeMode.system, - initialThemeData: ZetaThemeData(rounded: rounded ?? true), - builder: (context, theme, __) { - return Builder( - builder: (context) { - return MaterialApp( - debugShowCheckedModeBanner: false, - theme: ThemeData( - fontFamily: theme.fontFamily, - colorScheme: theme.colorsLight.toScheme(), - textTheme: zetaTextTheme, - ), - darkTheme: ThemeData( - fontFamily: theme.fontFamily, - colorScheme: theme.colorsDark.toScheme(), - textTheme: zetaTextTheme, - ), - home: Scaffold( - body: removeBody - ? widget - : SizedBox( - width: size.width, - height: size.height, - child: MediaQuery( - data: MediaQueryData(size: Size(size.width, size.height)), - child: SingleChildScrollView(child: widget), - ), - ), - ), - ); - }, - ); - }, - ); - } -} diff --git a/example/test/typography_test.dart b/example/test/typography_test.dart deleted file mode 100644 index 7132248e..00000000 --- a/example/test/typography_test.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_example/pages/theme/typography_example.dart'; - -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - const Key key1 = Key('1'); - const Key key2 = Key('2'); - const Key key3 = Key('3'); - const Key key4 = Key('4'); - - testWidgets('Text tokens', (tester) async { - await tester.pumpWidget( - TestWidget( - widget: Builder( - builder: (context) { - return Column( - children: [ - const Text(exampleText, key: key1), - Text(exampleText, style: ZetaTextStyles.bodyMedium, key: key2), - Text(exampleText, style: ZetaTextStyles.displayLarge, key: key3), - Text( - exampleText, - style: TextStyle(fontSize: 52, fontWeight: FontWeight.w300, height: 60 / 52), - key: key4, - ), - ], - ); - }, - ), - ), - ); - - final Finder zetaText1 = find.byKey(key1); - final Finder zetaText2 = find.byKey(key2); - final Finder zetaText3 = find.byKey(key3); - final Finder zetaText4 = find.byKey(key4); - - final InlineSpan text1 = - (find.descendant(of: zetaText1, matching: find.byType(RichText)).evaluate().first.widget as RichText).text; - final InlineSpan text2 = - (find.descendant(of: zetaText2, matching: find.byType(RichText)).evaluate().first.widget as RichText).text; - final InlineSpan text3 = - (find.descendant(of: zetaText3, matching: find.byType(RichText)).evaluate().first.widget as RichText).text; - final InlineSpan text4 = - (find.descendant(of: zetaText4, matching: find.byType(RichText)).evaluate().first.widget as RichText).text; - - /// Test default in [Text] widget is [ZetaTextStyles.bodyMedium]. - expect(text1.style, text2.style); - - /// Test that [ZetaTextStyles.displayLarge] has not changed. - expect(text3.style, text4.style); - - /// Test font size of [ZetaTextStyles.bodyMedium] is correct - expect(text1.style!.fontSize, 16); - - /// Test line height of [ZetaTextStyles.bodyMedium] is correct - expect(text1.style!.height, 24 / 16); - - /// Test font weight of [ZetaTextStyles.bodyMedium] is correct - expect(text1.style!.fontWeight, FontWeight.w400); - }); -} diff --git a/lib/src/components/badges/indicator.dart b/lib/src/components/badges/indicator.dart index 93286d81..5ca26932 100644 --- a/lib/src/components/badges/indicator.dart +++ b/lib/src/components/badges/indicator.dart @@ -14,10 +14,11 @@ enum ZetaIndicatorType { /// ZetaIndicator. /// /// Indicators are used to show the status of a user or any messages/notifications they might have. -class ZetaIndicator extends StatelessWidget { +class ZetaIndicator extends ZetaStatelessWidget { /// Constructor for [ZetaIndicator]. const ZetaIndicator({ super.key, + super.rounded, this.type = ZetaIndicatorType.notification, this.size = ZetaWidgetSize.large, this.icon, @@ -29,6 +30,7 @@ class ZetaIndicator extends StatelessWidget { /// Constructor for [ZetaIndicator] of type [ZetaIndicatorType.icon]. const ZetaIndicator.icon({ super.key, + super.rounded, this.size = ZetaWidgetSize.large, this.inverse = false, this.icon, @@ -39,12 +41,13 @@ class ZetaIndicator extends StatelessWidget { /// Constructor for [ZetaIndicator] of type [ZetaIndicatorType.notification]. const ZetaIndicator.notification({ super.key, + super.rounded, this.size = ZetaWidgetSize.large, this.inverse = false, - this.icon, this.value, this.color, - }) : type = ZetaIndicatorType.notification; + }) : type = ZetaIndicatorType.notification, + icon = null; /// The type of the [ZetaIndicator] - icon or notification. /// @@ -75,8 +78,10 @@ class ZetaIndicator extends StatelessWidget { IconData? icon, int? value, bool? inverse, + Key? key, }) { return ZetaIndicator( + key: key ?? this.key, type: type ?? this.type, size: size ?? this.size, icon: icon ?? this.icon, @@ -151,14 +156,8 @@ class ZetaIndicator extends StatelessWidget { } double _getIconSize(ZetaWidgetSize size) { - switch (size) { - case ZetaWidgetSize.large: - return ZetaSpacing.medium; - case ZetaWidgetSize.medium: - return ZetaSpacing.small; - case ZetaWidgetSize.small: - return ZetaSpacing.none; - } + if (size == ZetaWidgetSize.large) return ZetaSpacing.medium; + return ZetaSpacing.small; } @override @@ -169,7 +168,7 @@ class ZetaIndicator extends StatelessWidget { ..add(DiagnosticsProperty('size', size)) ..add(DiagnosticsProperty('value', value)) ..add(DiagnosticsProperty('icon', icon)) - ..add(DiagnosticsProperty('inverseBorder', inverse)) + ..add(DiagnosticsProperty('inverse', inverse)) ..add(ColorProperty('color', color)); } } diff --git a/lib/src/components/badges/status_label.dart b/lib/src/components/badges/status_label.dart index e89138e2..346f7137 100644 --- a/lib/src/components/badges/status_label.dart +++ b/lib/src/components/badges/status_label.dart @@ -64,6 +64,6 @@ class ZetaStatusLabel extends ZetaStatelessWidget { ..add(StringProperty('label', label)) ..add(DiagnosticsProperty('rounded', rounded)) ..add(DiagnosticsProperty('customIcon', customIcon)) - ..add(EnumProperty('severity', status)); + ..add(EnumProperty('status', status)); } } diff --git a/lib/src/components/buttons/button.dart b/lib/src/components/buttons/button.dart index 2a4aac53..45c553c8 100644 --- a/lib/src/components/buttons/button.dart +++ b/lib/src/components/buttons/button.dart @@ -15,6 +15,7 @@ class ZetaButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, super.key, + this.focusNode, }); /// Constructs [ZetaButton] with Primary theme. @@ -26,6 +27,7 @@ class ZetaButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, super.key, + this.focusNode, }) : type = ZetaButtonType.primary; /// Constructs [ZetaButton] with Secondary theme. @@ -37,6 +39,7 @@ class ZetaButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, super.key, + this.focusNode, }) : type = ZetaButtonType.secondary; /// Constructs [ZetaButton] with Positive theme. @@ -48,6 +51,7 @@ class ZetaButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, super.key, + this.focusNode, }) : type = ZetaButtonType.positive; /// Constructs [ZetaButton] with Negative theme. @@ -59,6 +63,7 @@ class ZetaButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, super.key, + this.focusNode, }) : type = ZetaButtonType.negative; /// Constructs [ZetaButton] with Outline theme. @@ -70,6 +75,7 @@ class ZetaButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, super.key, + this.focusNode, }) : type = ZetaButtonType.outline; /// Constructs [ZetaButton] with Outline Subtle theme. @@ -81,6 +87,7 @@ class ZetaButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, super.key, + this.focusNode, }) : type = ZetaButtonType.outlineSubtle; /// Constructs [ZetaButton] with text theme. @@ -92,6 +99,7 @@ class ZetaButton extends StatelessWidget { this.leadingIcon, this.trailingIcon, super.key, + this.focusNode, }) : type = ZetaButtonType.text; /// Button label @@ -118,6 +126,9 @@ class ZetaButton extends StatelessWidget { /// Trailing icon of button. Goes behind button. final IconData? trailingIcon; + /// {@macro flutter.widgets.Focus.focusNode} + final FocusNode? focusNode; + /// Creates a clone. ZetaButton copyWith({ String? label, @@ -147,6 +158,7 @@ class ZetaButton extends StatelessWidget { return ConstrainedBox( constraints: BoxConstraints(minHeight: _minConstraints, minWidth: _minConstraints), child: FilledButton( + focusNode: focusNode, onPressed: onPressed, style: buttonStyle( colors, @@ -234,6 +246,7 @@ class ZetaButton extends StatelessWidget { ..add(EnumProperty('borderType', borderType)) ..add(EnumProperty('size', size)) ..add(DiagnosticsProperty('leadingIcon', leadingIcon)) - ..add(DiagnosticsProperty('trailingIcon', trailingIcon)); + ..add(DiagnosticsProperty('trailingIcon', trailingIcon)) + ..add(DiagnosticsProperty('focusNode', focusNode)); } } diff --git a/lib/src/components/checkbox/checkbox.dart b/lib/src/components/checkbox/checkbox.dart index 113839c7..d77e71a2 100644 --- a/lib/src/components/checkbox/checkbox.dart +++ b/lib/src/components/checkbox/checkbox.dart @@ -14,11 +14,12 @@ import '../../../zeta_flutter.dart'; class ZetaCheckbox extends FormField { /// Constructs a [ZetaCheckbox]. ZetaCheckbox({ - required this.value, + this.value = false, this.label, this.onChanged, this.rounded, this.useIndeterminate = false, + this.focusNode, super.validator, super.autovalidateMode, super.restorationId, @@ -27,7 +28,7 @@ class ZetaCheckbox extends FormField { initialValue: value, enabled: onChanged != null, builder: (field) { - return _Checkbox( + return ZetaInternalCheckbox( label: label, onChanged: (changedValue) { field.didChange(changedValue); @@ -38,6 +39,7 @@ class ZetaCheckbox extends FormField { value: value, error: !field.isValid, disabled: onChanged == null, + focusNode: focusNode, ); }, ); @@ -61,17 +63,21 @@ class ZetaCheckbox extends FormField { /// The label displayed next to the checkbox final String? label; + /// {@macro flutter.widgets.Focus.focusNode} + final FocusNode? focusNode; + @override ZetaCheckboxFormFieldState createState() => ZetaCheckboxFormFieldState(); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('isChecked', value)) + ..add(DiagnosticsProperty('value', value)) ..add(StringProperty('label', label)) ..add(DiagnosticsProperty('useIndeterminate', useIndeterminate)) ..add(DiagnosticsProperty('rounded', rounded)) - ..add(ObjectFlagProperty?>.has('onChanged', onChanged)); + ..add(ObjectFlagProperty?>.has('onChanged', onChanged)) + ..add(DiagnosticsProperty('focusNode', focusNode)); } } @@ -81,14 +87,20 @@ class ZetaCheckboxFormFieldState extends FormFieldState { ZetaCheckbox get widget => super.widget as ZetaCheckbox; } -class _Checkbox extends ZetaStatefulWidget { - const _Checkbox({ +/// Internal checkbox. Not for external use. +@visibleForTesting +@protected +class ZetaInternalCheckbox extends ZetaStatefulWidget { + /// Constructs a [ZetaInternalCheckbox]. + const ZetaInternalCheckbox({ + super.key, required this.onChanged, this.disabled = false, this.value = false, this.label, this.useIndeterminate = false, this.error = false, + this.focusNode, super.rounded, }); @@ -106,13 +118,17 @@ class _Checkbox extends ZetaStatefulWidget { /// Defaults to false. final bool useIndeterminate; + /// Whether field is value. final bool error; /// {@macro zeta-widget-disabled} final bool disabled; + /// {@macro flutter.widgets.Focus.focusNode} + final FocusNode? focusNode; + @override - State<_Checkbox> createState() => _CheckboxState(); + State createState() => _CheckboxState(); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); @@ -122,12 +138,13 @@ class _Checkbox extends ZetaStatefulWidget { ..add(DiagnosticsProperty('rounded', rounded)) ..add(DiagnosticsProperty('useIndeterminate', useIndeterminate)) ..add(DiagnosticsProperty('error', error)) - ..add(DiagnosticsProperty('enabled', disabled)) - ..add(ObjectFlagProperty>.has('onChanged', onChanged)); + ..add(DiagnosticsProperty('disabled', disabled)) + ..add(ObjectFlagProperty>.has('onChanged', onChanged)) + ..add(DiagnosticsProperty('focusNode', focusNode)); } } -class _CheckboxState extends State<_Checkbox> { +class _CheckboxState extends State { bool get _checked => widget.value; bool _isFocused = false; @@ -153,12 +170,14 @@ class _CheckboxState extends State<_Checkbox> { child: Semantics( mixed: widget.useIndeterminate, enabled: !widget.disabled, + focusable: true, child: MouseRegion( cursor: !widget.disabled ? SystemMouseCursors.click : SystemMouseCursors.forbidden, onEnter: (event) => _setHovered(true), onExit: (event) => _setHovered(false), child: !widget.disabled ? FocusableActionDetector( + focusNode: widget.focusNode, onFocusChange: (bool focus) => setState(() => _isFocused = focus), child: _buildContent(context), ) @@ -225,7 +244,7 @@ class _CheckboxState extends State<_Checkbox> { Color _getBackground(Zeta theme) { final ZetaColorSwatch color = widget.error ? theme.colors.error : theme.colors.primary; if (widget.disabled) return theme.colors.surfaceDisabled; - if (!_checked) return Colors.transparent; + if (!_checked) return theme.colors.surfacePrimary; if (_isHovered) return theme.colors.borderHover; return color; @@ -241,15 +260,4 @@ class _CheckboxState extends State<_Checkbox> { return theme.colors.cool.shade70; } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('value', widget.value)) - ..add(ObjectFlagProperty>.has('onChanged', widget.onChanged)) - ..add(DiagnosticsProperty('rounded', widget.rounded)) - ..add(StringProperty('label', widget.label)) - ..add(DiagnosticsProperty('useIndeterminate', widget.useIndeterminate)); - } } diff --git a/lib/src/components/dial_pad/dial_pad.dart b/lib/src/components/dial_pad/dial_pad.dart index 7bbdc32d..bd85c779 100644 --- a/lib/src/components/dial_pad/dial_pad.dart +++ b/lib/src/components/dial_pad/dial_pad.dart @@ -61,7 +61,7 @@ class ZetaDialPad extends StatefulWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(ObjectFlagProperty?>.has('onInput', onNumber)) + ..add(ObjectFlagProperty?>.has('onNumber', onNumber)) ..add(IntProperty('buttonsPerRow', buttonsPerRow)) ..add(DiagnosticsProperty>('buttonValues', buttonValues)) ..add(ObjectFlagProperty?>.has('onText', onText)); @@ -148,7 +148,7 @@ class ZetaDialPadButton extends StatelessWidget { super.key, required this.primary, this.secondary = '', - required this.onTap, + this.onTap, this.topPadding = 3, }); @@ -175,8 +175,8 @@ class ZetaDialPadButton extends StatelessWidget { super.debugFillProperties(properties); properties ..add(ObjectFlagProperty>.has('onTap', onTap)) - ..add(StringProperty('letters', secondary)) - ..add(StringProperty('number', primary)) + ..add(StringProperty('secondary', secondary)) + ..add(StringProperty('primary', primary)) ..add(DoubleProperty('topPadding', topPadding)); } diff --git a/lib/src/components/fabs/fab.dart b/lib/src/components/fabs/fab.dart index f3c6b08d..f091510c 100644 --- a/lib/src/components/fabs/fab.dart +++ b/lib/src/components/fabs/fab.dart @@ -36,7 +36,8 @@ class ZetaFAB extends StatefulWidget { this.size = ZetaFabSize.small, this.shape = ZetaWidgetBorder.full, this.icon = ZetaIcons.add_round, - this.initiallyExpanded = false, + this.initiallyExpanded, + this.focusNode, super.key, }); @@ -78,7 +79,10 @@ class ZetaFAB extends StatefulWidget { /// Whether the FAB starts as expanded. /// /// If [scrollController] or [label] are null, this is the permanent state of the FAB. - final bool initiallyExpanded; + final bool? initiallyExpanded; + + /// {@macro flutter.widgets.Focus.focusNode} + final FocusNode? focusNode; @override State createState() => _ZetaFABState(); @@ -87,61 +91,36 @@ class ZetaFAB extends StatefulWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(EnumProperty('buttonType', type)) - ..add(EnumProperty('buttonSize', size)) - ..add(EnumProperty('buttonShape', shape)) + ..add(EnumProperty('type', type)) + ..add(EnumProperty('size', size)) + ..add(EnumProperty('shape', shape)) ..add(ObjectFlagProperty.has('onPressed', onPressed)) ..add(DiagnosticsProperty('scrollController', scrollController)) - ..add(StringProperty('buttonLabel', label)) - ..add(DiagnosticsProperty('buttonIcon', icon)) - ..add(DiagnosticsProperty('initiallyExpanded', initiallyExpanded)); + ..add(StringProperty('label', label)) + ..add(DiagnosticsProperty('icon', icon)) + ..add(DiagnosticsProperty('initiallyExpanded', initiallyExpanded)) + ..add(DiagnosticsProperty('focusNode', focusNode)); } } class _ZetaFABState extends State { - bool __isExpanded = false; - bool get _isExpanded => __isExpanded; - set _isExpanded(bool value) { - if (value && widget.label != null || !value) __isExpanded = value; - } - - @override - void initState() { - super.initState(); - _isExpanded = (widget.initiallyExpanded && widget.label != null) || false; - widget.scrollController?.addListener(_scrollListener); - } - - void _scrollListener() { - final expanded = widget.scrollController?.position.userScrollDirection == ScrollDirection.reverse; - if (_isExpanded != expanded) { - setState(() => _isExpanded = expanded); - } - } - - @override - void dispose() { - widget.scrollController?.removeListener(_scrollListener); - super.dispose(); - } - @override Widget build(BuildContext context) { + final bool isExpanded = (widget.initiallyExpanded != null ? widget.initiallyExpanded! : widget.label != null); final colors = widget.type.colors(context); final backgroundColor = widget.type == ZetaFabType.inverse ? colors.shade80 : colors.shade60; - return ElevatedButton( + return FilledButton( onPressed: widget.onPressed, - style: ElevatedButton.styleFrom( - padding: EdgeInsets.zero, - shape: widget.shape.buttonShape(isExpanded: _isExpanded, size: widget.size), - backgroundColor: backgroundColor, - foregroundColor: backgroundColor.onColor, - ).copyWith( - overlayColor: WidgetStateProperty.resolveWith((Set states) { + focusNode: widget.focusNode, + style: ButtonStyle( + padding: const WidgetStatePropertyAll(EdgeInsets.zero), + shape: WidgetStatePropertyAll(widget.shape.buttonShape(isExpanded: isExpanded, size: widget.size)), + backgroundColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.disabled)) return Zeta.of(context).colors.surfaceDisabled; if (states.contains(WidgetState.hovered)) return colors.hover; if (states.contains(WidgetState.pressed)) return colors.selected; - return null; + return backgroundColor; }), side: WidgetStateProperty.resolveWith( (Set states) { @@ -156,14 +135,14 @@ class _ZetaFABState extends State { child: AnimatedContainer( duration: ZetaAnimationLength.normal, child: Padding( - padding: _isExpanded + padding: isExpanded ? const EdgeInsets.symmetric(horizontal: ZetaSpacingBase.x3_5, vertical: ZetaSpacing.medium) : EdgeInsets.all(widget.size.padding), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(widget.icon, size: widget.size.iconSize), - if (_isExpanded && widget.label != null) + if (isExpanded && widget.label != null) Row( mainAxisSize: MainAxisSize.min, children: [Text(widget.label!, style: ZetaTextStyles.labelLarge)], diff --git a/lib/src/components/in_page_banner/in_page_banner.dart b/lib/src/components/in_page_banner/in_page_banner.dart index cd48dc0f..39173d05 100644 --- a/lib/src/components/in_page_banner/in_page_banner.dart +++ b/lib/src/components/in_page_banner/in_page_banner.dart @@ -113,14 +113,8 @@ class ZetaInPageBanner extends ZetaStatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add( - ObjectFlagProperty.has( - 'onCloseFunction', - onClose, - ), - ) - ..add(DiagnosticsProperty('rounded', rounded)) - ..add(EnumProperty('severity', status)) + ..add(ObjectFlagProperty.has('onClose', onClose)) + ..add(EnumProperty('status', status)) ..add(StringProperty('title', title)) ..add(DiagnosticsProperty('customIcon', customIcon)); } diff --git a/lib/src/components/password/password_input.dart b/lib/src/components/password/password_input.dart index 775c94f1..88adbde0 100644 --- a/lib/src/components/password/password_input.dart +++ b/lib/src/components/password/password_input.dart @@ -67,14 +67,8 @@ class ZetaPasswordInput extends ZetaFormField { ..add(StringProperty('hintText', hintText)) ..add(StringProperty('label', label)) ..add(StringProperty('footerText', hintText)) - ..add( - ObjectFlagProperty.has( - 'validator', - validator, - ), - ) + ..add(ObjectFlagProperty.has('validator', validator)) ..add(EnumProperty('size', size)) - ..add(DiagnosticsProperty('rounded', rounded)) ..add(StringProperty('placeholder', placeholder)) ..add(ObjectFlagProperty.has('onSubmit', onSubmit)) ..add(StringProperty('errorText', errorText)); diff --git a/golden/arrow_left.png b/test/src/components/badge/golden/badge.png similarity index 80% rename from golden/arrow_left.png rename to test/src/components/badge/golden/badge.png index 7f66b33e..5b6326c1 100644 Binary files a/golden/arrow_left.png and b/test/src/components/badge/golden/badge.png differ diff --git a/test/src/components/badge/golden/badge_dark.png b/test/src/components/badge/golden/badge_dark.png new file mode 100644 index 00000000..1e514a9b Binary files /dev/null and b/test/src/components/badge/golden/badge_dark.png differ diff --git a/golden/arrow_up.png b/test/src/components/badge/golden/badge_default.png similarity index 80% rename from golden/arrow_up.png rename to test/src/components/badge/golden/badge_default.png index da590cb5..8f60d7c5 100644 Binary files a/golden/arrow_up.png and b/test/src/components/badge/golden/badge_default.png differ diff --git a/test/src/components/badge/golden/badge_negative.png b/test/src/components/badge/golden/badge_negative.png new file mode 100644 index 00000000..045c3ab3 Binary files /dev/null and b/test/src/components/badge/golden/badge_negative.png differ diff --git a/test/src/components/badge/golden/badge_neutral.png b/test/src/components/badge/golden/badge_neutral.png new file mode 100644 index 00000000..24046acd Binary files /dev/null and b/test/src/components/badge/golden/badge_neutral.png differ diff --git a/test/src/components/badge/golden/badge_positive.png b/test/src/components/badge/golden/badge_positive.png new file mode 100644 index 00000000..711c8308 Binary files /dev/null and b/test/src/components/badge/golden/badge_positive.png differ diff --git a/test/src/components/badge/golden/badge_warning.png b/test/src/components/badge/golden/badge_warning.png new file mode 100644 index 00000000..5b6326c1 Binary files /dev/null and b/test/src/components/badge/golden/badge_warning.png differ diff --git a/test/src/components/badge/golden/indicator_default.png b/test/src/components/badge/golden/indicator_default.png new file mode 100644 index 00000000..1c0aa6a8 Binary files /dev/null and b/test/src/components/badge/golden/indicator_default.png differ diff --git a/test/src/components/badge/golden/indicator_icon_default.png b/test/src/components/badge/golden/indicator_icon_default.png new file mode 100644 index 00000000..34bf283a Binary files /dev/null and b/test/src/components/badge/golden/indicator_icon_default.png differ diff --git a/test/src/components/badge/golden/indicator_icon_values.png b/test/src/components/badge/golden/indicator_icon_values.png new file mode 100644 index 00000000..9e930cb5 Binary files /dev/null and b/test/src/components/badge/golden/indicator_icon_values.png differ diff --git a/test/src/components/badge/golden/indicator_notification_default.png b/test/src/components/badge/golden/indicator_notification_default.png new file mode 100644 index 00000000..1c0aa6a8 Binary files /dev/null and b/test/src/components/badge/golden/indicator_notification_default.png differ diff --git a/test/src/components/badge/golden/indicator_notification_values.png b/test/src/components/badge/golden/indicator_notification_values.png new file mode 100644 index 00000000..5f3eb489 Binary files /dev/null and b/test/src/components/badge/golden/indicator_notification_values.png differ diff --git a/test/src/components/badge/golden/label_dark.png b/test/src/components/badge/golden/label_dark.png new file mode 100644 index 00000000..1e514a9b Binary files /dev/null and b/test/src/components/badge/golden/label_dark.png differ diff --git a/test/src/components/badge/golden/label_default.png b/test/src/components/badge/golden/label_default.png new file mode 100644 index 00000000..8f60d7c5 Binary files /dev/null and b/test/src/components/badge/golden/label_default.png differ diff --git a/test/src/components/badge/golden/label_negative.png b/test/src/components/badge/golden/label_negative.png new file mode 100644 index 00000000..045c3ab3 Binary files /dev/null and b/test/src/components/badge/golden/label_negative.png differ diff --git a/test/src/components/badge/golden/label_neutral.png b/test/src/components/badge/golden/label_neutral.png new file mode 100644 index 00000000..24046acd Binary files /dev/null and b/test/src/components/badge/golden/label_neutral.png differ diff --git a/test/src/components/badge/golden/label_positive.png b/test/src/components/badge/golden/label_positive.png new file mode 100644 index 00000000..711c8308 Binary files /dev/null and b/test/src/components/badge/golden/label_positive.png differ diff --git a/golden/arrow_down.png b/test/src/components/badge/golden/label_sharp.png similarity index 80% rename from golden/arrow_down.png rename to test/src/components/badge/golden/label_sharp.png index aa995748..ebfea1a7 100644 Binary files a/golden/arrow_down.png and b/test/src/components/badge/golden/label_sharp.png differ diff --git a/test/src/components/badge/golden/label_warning.png b/test/src/components/badge/golden/label_warning.png new file mode 100644 index 00000000..5b6326c1 Binary files /dev/null and b/test/src/components/badge/golden/label_warning.png differ diff --git a/test/src/components/badge/golden/priority_pill_default.png b/test/src/components/badge/golden/priority_pill_default.png new file mode 100644 index 00000000..358111a4 Binary files /dev/null and b/test/src/components/badge/golden/priority_pill_default.png differ diff --git a/test/src/components/badge/golden/priority_pill_high.png b/test/src/components/badge/golden/priority_pill_high.png new file mode 100644 index 00000000..11402303 Binary files /dev/null and b/test/src/components/badge/golden/priority_pill_high.png differ diff --git a/test/src/components/badge/golden/priority_pill_low.png b/test/src/components/badge/golden/priority_pill_low.png new file mode 100644 index 00000000..ebdc35f8 Binary files /dev/null and b/test/src/components/badge/golden/priority_pill_low.png differ diff --git a/test/src/components/badge/golden/priority_pill_medium.png b/test/src/components/badge/golden/priority_pill_medium.png new file mode 100644 index 00000000..c56cd408 Binary files /dev/null and b/test/src/components/badge/golden/priority_pill_medium.png differ diff --git a/test/src/components/badge/golden/status_label_custom.png b/test/src/components/badge/golden/status_label_custom.png new file mode 100644 index 00000000..7e5e24da Binary files /dev/null and b/test/src/components/badge/golden/status_label_custom.png differ diff --git a/test/src/components/badge/golden/status_label_default.png b/test/src/components/badge/golden/status_label_default.png new file mode 100644 index 00000000..2b8606dc Binary files /dev/null and b/test/src/components/badge/golden/status_label_default.png differ diff --git a/test/src/components/badge/golden/tag_left.png b/test/src/components/badge/golden/tag_left.png new file mode 100644 index 00000000..4ac7894a Binary files /dev/null and b/test/src/components/badge/golden/tag_left.png differ diff --git a/test/src/components/badge/golden/tag_right.png b/test/src/components/badge/golden/tag_right.png new file mode 100644 index 00000000..e078c030 Binary files /dev/null and b/test/src/components/badge/golden/tag_right.png differ diff --git a/test/src/components/badge/indicator_test.dart b/test/src/components/badge/indicator_test.dart new file mode 100644 index 00000000..a52a34d7 --- /dev/null +++ b/test/src/components/badge/indicator_test.dart @@ -0,0 +1,175 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('badge')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + testWidgets('Default constructor initializes with correct parameters', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaIndicator())); + + final zetaIndicatorFinder = find.byType(ZetaIndicator); + final ZetaIndicator indicator = tester.firstWidget(zetaIndicatorFinder); + + expect(indicator.rounded, null); + expect(indicator.type, ZetaIndicatorType.notification); + expect(indicator.size, ZetaWidgetSize.large); + expect(indicator.icon, null); + expect(indicator.value, null); + expect(indicator.inverse, false); + expect(indicator.color, null); + + await expectLater( + find.byType(ZetaIndicator), + matchesGoldenFile(join(getCurrentPath('badge'), 'indicator_default.png')), + ); + }); + + testWidgets('Copy with function works', (WidgetTester tester) async { + const Key key1 = Key('1'); + const Key key2 = Key('2'); + + const ZetaIndicator one = ZetaIndicator(key: key1); + final ZetaIndicator two = one.copyWith( + key: key2, + ); + await tester.pumpWidget(TestApp(home: Column(children: [one, two]))); + + final zetaIndicatorFinder1 = find.byKey(key1); + final ZetaIndicator indicator1 = tester.firstWidget(zetaIndicatorFinder1); + final zetaIndicatorFinder2 = find.byKey(key2); + final ZetaIndicator indicator2 = tester.firstWidget(zetaIndicatorFinder2); + + expect(indicator1.rounded, null); + expect(indicator1.type, ZetaIndicatorType.notification); + expect(indicator1.size, ZetaWidgetSize.large); + expect(indicator1.icon, null); + expect(indicator1.value, null); + expect(indicator1.inverse, false); + expect(indicator1.color, null); + + expect(indicator2.rounded, null); + }); + testWidgets('Icon constructor initializes with correct parameters', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaIndicator.icon())); + + final zetaIndicatorFinder = find.byType(ZetaIndicator); + final ZetaIndicator indicator = tester.firstWidget(zetaIndicatorFinder); + + expect(indicator.rounded, null); + expect(indicator.type, ZetaIndicatorType.icon); + expect(indicator.size, ZetaWidgetSize.large); + expect(indicator.icon, null); + expect(indicator.value, null); + expect(indicator.inverse, false); + expect(indicator.color, null); + + await expectLater( + find.byType(ZetaIndicator), + matchesGoldenFile(join(getCurrentPath('badge'), 'indicator_icon_default.png')), + ); + }); + testWidgets('Icon constructor with values', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaIndicator.icon( + color: Colors.purple, + icon: Icons.abc, + inverse: true, + size: ZetaWidgetSize.medium, + ), + ), + ); + + final zetaIndicatorFinder = find.byType(ZetaIndicator); + final ZetaIndicator indicator = tester.firstWidget(zetaIndicatorFinder); + + expect(indicator.rounded, null); + expect(indicator.type, ZetaIndicatorType.icon); + expect(indicator.size, ZetaWidgetSize.medium); + expect(indicator.icon, Icons.abc); + expect(indicator.value, null); + expect(indicator.inverse, true); + expect(indicator.color, Colors.purple); + + await expectLater( + find.byType(ZetaIndicator), + matchesGoldenFile(join(getCurrentPath('badge'), 'indicator_icon_values.png')), + ); + }); + testWidgets('Notification constructor initializes with correct parameters', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaIndicator.notification())); + + final zetaIndicatorFinder = find.byType(ZetaIndicator); + final ZetaIndicator indicator = tester.firstWidget(zetaIndicatorFinder); + + expect(indicator.rounded, null); + expect(indicator.type, ZetaIndicatorType.notification); + expect(indicator.size, ZetaWidgetSize.large); + expect(indicator.icon, null); + expect(indicator.value, null); + expect(indicator.inverse, false); + expect(indicator.color, null); + + await expectLater( + find.byType(ZetaIndicator), + matchesGoldenFile(join(getCurrentPath('badge'), 'indicator_notification_default.png')), + ); + }); + testWidgets('Notification constructor with values', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaIndicator.notification( + rounded: false, + color: Colors.green, + inverse: true, + size: ZetaWidgetSize.small, + value: 1, + ), + ), + ); + + final zetaIndicatorFinder = find.byType(ZetaIndicator); + final ZetaIndicator indicator = tester.firstWidget(zetaIndicatorFinder); + + expect(indicator.rounded, false); + expect(indicator.type, ZetaIndicatorType.notification); + expect(indicator.size, ZetaWidgetSize.small); + expect(indicator.icon, null); + expect(indicator.value, 1); + expect(indicator.inverse, true); + expect(indicator.color, Colors.green); + + await expectLater( + find.byType(ZetaIndicator), + matchesGoldenFile(join(getCurrentPath('badge'), 'indicator_notification_values.png')), + ); + }); + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaIndicator( + color: Colors.orange, + icon: Icons.abc, + inverse: true, + rounded: false, + size: ZetaWidgetSize.small, + type: ZetaIndicatorType.icon, + value: 1, + ).debugFillProperties(diagnostics); + + expect(diagnostics.finder('color'), 'MaterialColor(primary value: Color(0xffff9800))'); + expect(diagnostics.finder('icon'), 'IconData(U+F04B6)'); + expect(diagnostics.finder('inverse'), 'true'); + expect(diagnostics.finder('rounded'), 'false'); + expect(diagnostics.finder('size'), 'ZetaWidgetSize.small'); + expect(diagnostics.finder('type'), 'ZetaIndicatorType.icon'); + expect(diagnostics.finder('value'), '1'); + }); +} diff --git a/test/src/components/badge/label_test.dart b/test/src/components/badge/label_test.dart new file mode 100644 index 00000000..f7f777fe --- /dev/null +++ b/test/src/components/badge/label_test.dart @@ -0,0 +1,111 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('badge')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + testWidgets('Initializes with correct parameters', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaLabel(label: 'Test Label'))); + + final zetaBadgeFinder = find.byType(ZetaLabel); + final ZetaLabel label = tester.firstWidget(zetaBadgeFinder); + + expect(label.rounded, null); + expect(label.label, 'Test Label'); + expect(label.status, ZetaWidgetStatus.info); + + await expectLater(find.byType(ZetaLabel), matchesGoldenFile(join(getCurrentPath('badge'), 'label_default.png'))); + }); + + testWidgets('Positive status', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.positive))); + + final zetaBadgeFinder = find.byType(ZetaLabel); + final ZetaLabel label = tester.firstWidget(zetaBadgeFinder); + + expect(label.status, ZetaWidgetStatus.positive); + + await expectLater(find.byType(ZetaLabel), matchesGoldenFile(join(getCurrentPath('badge'), 'label_positive.png'))); + }); + + testWidgets('Warning status', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.warning))); + + final zetaBadgeFinder = find.byType(ZetaLabel); + final ZetaLabel label = tester.firstWidget(zetaBadgeFinder); + + expect(label.status, ZetaWidgetStatus.warning); + + await expectLater(find.byType(ZetaLabel), matchesGoldenFile(join(getCurrentPath('badge'), 'label_warning.png'))); + }); + testWidgets('Negative status', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.negative))); + + final zetaBadgeFinder = find.byType(ZetaLabel); + final ZetaLabel label = tester.firstWidget(zetaBadgeFinder); + + expect(label.status, ZetaWidgetStatus.negative); + + await expectLater(find.byType(ZetaLabel), matchesGoldenFile(join(getCurrentPath('badge'), 'label_negative.png'))); + }); + testWidgets('Neutral status', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.neutral))); + + final zetaBadgeFinder = find.byType(ZetaLabel); + final ZetaLabel label = tester.firstWidget(zetaBadgeFinder); + + expect(label.status, ZetaWidgetStatus.neutral); + + await expectLater(find.byType(ZetaLabel), matchesGoldenFile(join(getCurrentPath('badge'), 'label_neutral.png'))); + }); + + testWidgets('Dark mode', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + themeMode: ThemeMode.dark, + home: ZetaLabel(label: 'Test Label'), + ), + ); + + final zetaBadgeFinder = find.byType(ZetaLabel); + final ZetaLabel label = tester.firstWidget(zetaBadgeFinder); + + expect(label.status, ZetaWidgetStatus.info); + + await expectLater(find.byType(ZetaLabel), matchesGoldenFile(join(getCurrentPath('badge'), 'label_dark.png'))); + }); + + testWidgets('Sharp', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp(home: ZetaLabel(label: 'Test Label', rounded: false)), + ); + + final zetaBadgeFinder = find.byType(ZetaLabel); + final ZetaLabel label = tester.firstWidget(zetaBadgeFinder); + + expect(label.rounded, false); + + await expectLater(find.byType(ZetaLabel), matchesGoldenFile(join(getCurrentPath('badge'), 'label_sharp.png'))); + }); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaLabel( + label: 'Test label', + rounded: false, + status: ZetaWidgetStatus.positive, + ).debugFillProperties(diagnostics); + + expect(diagnostics.finder('label'), '"Test label"'); + expect(diagnostics.finder('status'), 'positive'); + expect(diagnostics.finder('rounded'), 'false'); + }); +} diff --git a/test/src/components/badge/priority_pill_test.dart b/test/src/components/badge/priority_pill_test.dart new file mode 100644 index 00000000..28c53e1b --- /dev/null +++ b/test/src/components/badge/priority_pill_test.dart @@ -0,0 +1,129 @@ +// ignore_for_file: avoid_dynamic_calls + +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('badge')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + testWidgets('Initializes with correct label and index', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaPriorityPill(), + ), + ); + + final zetaPriorityPillFinder = find.byType(ZetaPriorityPill); + final ZetaPriorityPill zetaPriorityPill = tester.firstWidget(zetaPriorityPillFinder); + expect(zetaPriorityPill.rounded, null); + expect(zetaPriorityPill.index, null); + expect(zetaPriorityPill.isBadge, false); + expect(zetaPriorityPill.customColor, null); + expect(zetaPriorityPill.label, null); + expect(zetaPriorityPill.size, ZetaPriorityPillSize.large); + expect(zetaPriorityPill.type, ZetaPriorityPillType.urgent); + + expect(find.text('Urgent'), findsOneWidget); + expect(find.text('U'), findsOneWidget); + + await expectLater( + find.byType(ZetaPriorityPill), + matchesGoldenFile(join(getCurrentPath('badge'), 'priority_pill_default.png')), + ); + }); + testWidgets('High priority pill', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaPriorityPill( + type: ZetaPriorityPillType.high, + index: '10', + label: 'test label', + rounded: false, + size: ZetaPriorityPillSize.small, + ), + ), + ); + + final zetaPriorityPillFinder = find.byType(ZetaPriorityPill); + final ZetaPriorityPill zetaPriorityPill = tester.firstWidget(zetaPriorityPillFinder); + expect(zetaPriorityPill.rounded, false); + expect(zetaPriorityPill.index, '10'); + expect(zetaPriorityPill.isBadge, false); + expect(zetaPriorityPill.type, ZetaPriorityPillType.high); + expect(zetaPriorityPill.size, ZetaPriorityPillSize.small); + + expect(find.text('test label'), findsOneWidget); + + await expectLater( + find.byType(ZetaPriorityPill), + matchesGoldenFile(join(getCurrentPath('badge'), 'priority_pill_high.png')), + ); + }); + testWidgets('Medium priority pill', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaPriorityPill( + type: ZetaPriorityPillType.medium, + isBadge: true, + ), + ), + ); + + final zetaPriorityPillFinder = find.byType(ZetaPriorityPill); + final ZetaPriorityPill zetaPriorityPill = tester.firstWidget(zetaPriorityPillFinder); + expect(zetaPriorityPill.type, ZetaPriorityPillType.medium); + expect(zetaPriorityPill.isBadge, true); + + await expectLater( + find.byType(ZetaPriorityPill), + matchesGoldenFile(join(getCurrentPath('badge'), 'priority_pill_medium.png')), + ); + }); + testWidgets('Low priority pill', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaPriorityPill( + type: ZetaPriorityPillType.low, + size: ZetaPriorityPillSize.small, + isBadge: true, + ), + ), + ); + + final zetaPriorityPillFinder = find.byType(ZetaPriorityPill); + final ZetaPriorityPill zetaPriorityPill = tester.firstWidget(zetaPriorityPillFinder); + expect(zetaPriorityPill.type, ZetaPriorityPillType.low); + expect(zetaPriorityPill.isBadge, true); + expect(zetaPriorityPill.size, ZetaPriorityPillSize.small); + + await expectLater( + find.byType(ZetaPriorityPill), + matchesGoldenFile(join(getCurrentPath('badge'), 'priority_pill_low.png')), + ); + }); + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaPriorityPill( + label: 'Test label', + rounded: false, + customColor: ZetaColorBase.blue, + index: '1', + ).debugFillProperties(diagnostics); + + expect(diagnostics.finder('label'), '"Test label"'); + expect(diagnostics.finder('rounded'), 'false'); + expect(diagnostics.finder('isBadge'), 'false'); + expect(diagnostics.finder('index'), '"1"'); + expect(diagnostics.finder('customColor').split('(').first, 'ZetaColorSwatch'); + expect(diagnostics.finder('type'), 'urgent'); + expect(diagnostics.finder('size'), 'large'); + }); +} diff --git a/test/src/components/badge/status_label_test.dart b/test/src/components/badge/status_label_test.dart new file mode 100644 index 00000000..27c9d278 --- /dev/null +++ b/test/src/components/badge/status_label_test.dart @@ -0,0 +1,62 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('badge')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + group('ZetaStatusLabel Tests', () { + testWidgets('Initializes with correct properties', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaStatusLabel(label: 'Test Label'), + ), + ); + expect(find.text('Test Label'), findsOneWidget); + + await expectLater( + find.byType(ZetaStatusLabel), + matchesGoldenFile(join(getCurrentPath('badge'), 'status_label_default.png')), + ); + }); + }); + + testWidgets('Initializes with correct label and custom icon', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaStatusLabel( + label: 'Custom Icon', + customIcon: Icons.person, + ), + ), + ); + expect(find.text('Custom Icon'), findsOneWidget); + expect(find.byIcon(Icons.person), findsOneWidget); + + await expectLater( + find.byType(ZetaStatusLabel), + matchesGoldenFile(join(getCurrentPath('badge'), 'status_label_custom.png')), + ); + }); + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaStatusLabel( + label: 'Test label', + rounded: false, + customIcon: Icons.abc, + ).debugFillProperties(diagnostics); + + expect(diagnostics.finder('label'), '"Test label"'); + expect(diagnostics.finder('rounded'), 'false'); + expect(diagnostics.finder('customIcon'), 'IconData(U+F04B6)'); + expect(diagnostics.finder('status'), 'info'); + }); +} diff --git a/test/src/components/badge/tag_test.dart b/test/src/components/badge/tag_test.dart new file mode 100644 index 00000000..6f6f4fcf --- /dev/null +++ b/test/src/components/badge/tag_test.dart @@ -0,0 +1,57 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('badge')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + group('ZetaTag', () { + testWidgets('Initializes right with correct parameters', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaTag.right(label: 'Tag', rounded: false), + ), + ); + + expect(find.text('Tag'), findsOneWidget); + + await expectLater( + find.byType(ZetaTag), + matchesGoldenFile(join(getCurrentPath('badge'), 'tag_right.png')), + ); + }); + + testWidgets('Initializes left with correct parameters', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaTag.left(label: 'Tag', rounded: true), + ), + ); + expect(find.byType(ZetaTag), findsOneWidget); + + await expectLater( + find.byType(ZetaTag), + matchesGoldenFile(join(getCurrentPath('badge'), 'tag_left.png')), + ); + }); + }); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaTag( + label: 'Test label', + rounded: false, + ).debugFillProperties(diagnostics); + + expect(diagnostics.finder('label'), '"Test label"'); + expect(diagnostics.finder('rounded'), 'false'); + expect(diagnostics.finder('direction'), 'left'); + }); +} diff --git a/test/src/components/button/button_test.dart b/test/src/components/button/button_test.dart new file mode 100644 index 00000000..73e7ffa6 --- /dev/null +++ b/test/src/components/button/button_test.dart @@ -0,0 +1,280 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('button')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + + group('ZetaButton Tests', () { + testWidgets('Initializes with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton(onPressed: () {}, label: 'Test Button'), + ), + ); + + expect(find.text('Test Button'), findsOneWidget); + }); + }); + + testWidgets('Triggers callback on tap', (WidgetTester tester) async { + bool callbackTriggered = false; + await tester.pumpWidget( + TestApp(home: ZetaButton(onPressed: () => callbackTriggered = true, label: 'Test Button')), + ); + await tester.tap(find.byType(ZetaButton)); + await tester.pump(); + + expect(callbackTriggered, isTrue); + }); + + testWidgets('Initializes primary with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton.primary(onPressed: () {}, label: 'Test Button'), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + expect(button.borderType, null); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.medium); + expect(button.type, ZetaButtonType.primary); + + await expectLater(find.byType(ZetaButton), matchesGoldenFile(join(getCurrentPath('button'), 'button_primary.png'))); + }); + testWidgets('Initializes secondary with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton.secondary( + onPressed: () {}, + label: 'Test Button', + leadingIcon: Icons.abc, + size: ZetaWidgetSize.small, + ), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + expect(button.borderType, null); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, Icons.abc); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.small); + expect(button.type, ZetaButtonType.secondary); + + await expectLater( + find.byType(ZetaButton), + matchesGoldenFile(join(getCurrentPath('button'), 'button_secondary.png')), + ); + }); + testWidgets('Initializes positive with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton.positive(onPressed: () {}, label: 'Test Button', trailingIcon: Icons.abc), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + expect(button.borderType, null); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, Icons.abc); + expect(button.size, ZetaWidgetSize.medium); + expect(button.type, ZetaButtonType.positive); + + await expectLater( + find.byType(ZetaButton), + matchesGoldenFile(join(getCurrentPath('button'), 'button_positive.png')), + ); + }); + testWidgets('Initializes negative with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton.negative(onPressed: () {}, label: 'Test Button', size: ZetaWidgetSize.small), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + expect(button.borderType, null); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.small); + expect(button.type, ZetaButtonType.negative); + + await expectLater( + find.byType(ZetaButton), + matchesGoldenFile(join(getCurrentPath('button'), 'button_negative.png')), + ); + }); + testWidgets('Initializes outline with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton.outline(onPressed: () {}, label: 'Test Button', size: ZetaWidgetSize.large), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + expect(button.borderType, null); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.large); + expect(button.type, ZetaButtonType.outline); + + await expectLater(find.byType(ZetaButton), matchesGoldenFile(join(getCurrentPath('button'), 'button_outline.png'))); + }); + testWidgets('Initializes outlineSubtle with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaButton.outlineSubtle(label: 'Test Button', borderType: ZetaWidgetBorder.sharp), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + expect(button.borderType, ZetaWidgetBorder.sharp); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.medium); + expect(button.type, ZetaButtonType.outlineSubtle); + + await expectLater( + find.byType(ZetaButton), + matchesGoldenFile(join(getCurrentPath('button'), 'button_outline_subtle.png')), + ); + }); + testWidgets('Initializes text with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton.text(onPressed: () {}, label: 'Test Button', borderType: ZetaWidgetBorder.full), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + expect(button.borderType, ZetaWidgetBorder.full); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.medium); + expect(button.type, ZetaButtonType.text); + + await expectLater( + find.byType(ZetaButton), + matchesGoldenFile(join(getCurrentPath('button'), 'button_text.png')), + ); + }); + + testWidgets('Disabled button', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaButton.text(label: 'Test Button', borderType: ZetaWidgetBorder.full), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + expect(button.borderType, ZetaWidgetBorder.full); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.onPressed, null); + expect(button.size, ZetaWidgetSize.medium); + expect(button.type, ZetaButtonType.text); + + await expectLater( + find.byType(ZetaButton), + matchesGoldenFile(join(getCurrentPath('button'), 'button_disabled.png')), + ); + }); + testWidgets('Interaction with button', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton(label: 'Test Button', onPressed: () {}), + ), + ); + + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + + final filledButtonFinder = find.byType(FilledButton); + final FilledButton filledButton = tester.firstWidget(filledButtonFinder); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(ZetaButton))); + await tester.pumpAndSettle(); + + expect(filledButton.style?.backgroundColor?.resolve({WidgetState.hovered}), ZetaColorBase.blue.shade50); + + await gesture.down(tester.getCenter(find.byType(ZetaButton))); + await tester.pumpAndSettle(); + expect(filledButton.style?.backgroundColor?.resolve({WidgetState.pressed}), ZetaColorBase.blue.shade70); + + await gesture.up(); + + await tester.pumpAndSettle(); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.medium); + }); + testWidgets('Interaction with button', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + await tester.pumpWidget( + TestApp( + home: ZetaButton(label: 'Test Button', onPressed: () {}, focusNode: focusNode), + ), + ); + final buttonFinder = find.byType(ZetaButton); + final ZetaButton button = tester.firstWidget(buttonFinder); + focusNode.requestFocus(); + await tester.pump(); + final filledButtonFinder = find.byType(FilledButton); + final FilledButton filledButton = tester.firstWidget(filledButtonFinder); + + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.medium); + expect( + filledButton.style?.side?.resolve({WidgetState.focused}), + const BorderSide(color: ZetaColorBase.blue, width: ZetaSpacingBase.x0_5), + ); + }); + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaButton(label: 'Test label').debugFillProperties(diagnostics); + + expect(diagnostics.finder('label'), '"Test label"'); + expect(diagnostics.finder('onPressed'), 'null'); + expect(diagnostics.finder('type'), 'primary'); + expect(diagnostics.finder('borderType'), 'null'); + expect(diagnostics.finder('size'), 'medium'); + expect(diagnostics.finder('leadingIcon'), 'null'); + expect(diagnostics.finder('trailingIcon'), 'null'); + }); +} diff --git a/test/src/components/button/golden/button_disabled.png b/test/src/components/button/golden/button_disabled.png new file mode 100644 index 00000000..fd362469 Binary files /dev/null and b/test/src/components/button/golden/button_disabled.png differ diff --git a/test/src/components/button/golden/button_negative.png b/test/src/components/button/golden/button_negative.png new file mode 100644 index 00000000..b0283c1e Binary files /dev/null and b/test/src/components/button/golden/button_negative.png differ diff --git a/test/src/components/button/golden/button_outline.png b/test/src/components/button/golden/button_outline.png new file mode 100644 index 00000000..ce0f9fa6 Binary files /dev/null and b/test/src/components/button/golden/button_outline.png differ diff --git a/test/src/components/button/golden/button_outline_subtle.png b/test/src/components/button/golden/button_outline_subtle.png new file mode 100644 index 00000000..536c285b Binary files /dev/null and b/test/src/components/button/golden/button_outline_subtle.png differ diff --git a/test/src/components/button/golden/button_positive.png b/test/src/components/button/golden/button_positive.png new file mode 100644 index 00000000..ba493d0a Binary files /dev/null and b/test/src/components/button/golden/button_positive.png differ diff --git a/test/src/components/button/golden/button_primary.png b/test/src/components/button/golden/button_primary.png new file mode 100644 index 00000000..c2dd825e Binary files /dev/null and b/test/src/components/button/golden/button_primary.png differ diff --git a/test/src/components/button/golden/button_secondary.png b/test/src/components/button/golden/button_secondary.png new file mode 100644 index 00000000..22ca2480 Binary files /dev/null and b/test/src/components/button/golden/button_secondary.png differ diff --git a/golden/arrow_right.png b/test/src/components/button/golden/button_text.png similarity index 79% rename from golden/arrow_right.png rename to test/src/components/button/golden/button_text.png index 30d99000..7276f3b3 100644 Binary files a/golden/arrow_right.png and b/test/src/components/button/golden/button_text.png differ diff --git a/test/src/components/checkbox/checkbox_test.dart b/test/src/components/checkbox/checkbox_test.dart new file mode 100644 index 00000000..c9926017 --- /dev/null +++ b/test/src/components/checkbox/checkbox_test.dart @@ -0,0 +1,143 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('checkbox')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + + group('ZetaCheckbox Tests', () { + testWidgets('Initializes with correct parameters', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaCheckbox( + value: true, + onChanged: (value) {}, + label: 'Test Checkbox', + ), + ), + ); + + final checkboxFinder = find.byType(ZetaCheckbox); + final ZetaCheckbox checkbox = tester.firstWidget(checkboxFinder); + + expect(checkbox.value, true); + expect(checkbox.rounded, null); + expect(checkbox.label, 'Test Checkbox'); + }); + + testWidgets('ZetaCheckbox changes state on tap', (WidgetTester tester) async { + bool? checkboxValue = true; + + await tester.pumpWidget( + TestApp( + home: ZetaCheckbox( + value: checkboxValue, + onChanged: (value) { + checkboxValue = value; + }, + ), + ), + ); + + final checkboxFinder = find.byType(ZetaCheckbox); + await expectLater( + checkboxFinder, + matchesGoldenFile(join(getCurrentPath('checkbox'), 'checkbox_enabled.png')), + ); + await tester.tap(find.byType(ZetaCheckbox)); + await tester.pump(); + + expect(checkboxValue, false); + }); + testWidgets("Disabled ZetaCheckbox doesn't change state on tap", (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaCheckbox( + value: true, + ), + ), + ); + + final checkboxFinder = find.byType(ZetaCheckbox); + await expectLater( + checkboxFinder, + matchesGoldenFile(join(getCurrentPath('checkbox'), 'checkbox_disabled.png')), + ); + await tester.tap(find.byType(ZetaCheckbox)); + await tester.pump(); + final ZetaCheckbox checkbox = tester.firstWidget(checkboxFinder); + + expect(checkbox.value, true); + }); + + testWidgets('ZetaCheckbox interaction', (WidgetTester tester) async { + final FocusNode node = FocusNode(); + + await tester.pumpWidget( + TestApp( + home: ZetaCheckbox( + focusNode: node, + onChanged: (value) {}, + value: true, + ), + ), + ); + + node.requestFocus(); + await tester.pump(); + + final animatedContainerFinder = find.byType(AnimatedContainer); + final AnimatedContainer animatedContainer = tester.firstWidget(animatedContainerFinder); + expect((animatedContainer.decoration as BoxDecoration?)?.boxShadow?.first.color, ZetaColorBase.blue.shade50); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + await tester.pump(); + await gesture.moveTo(tester.getCenter(find.byType(ZetaInternalCheckbox))); + await gesture.moveTo(tester.getCenter(find.byType(ZetaCheckbox))); + await gesture.moveTo(Offset.zero); + await gesture.moveTo(tester.getCenter(find.byType(ZetaInternalCheckbox))); + + await tester.pumpAndSettle(); + + // TODO(test): Luke hover state on checkbox + // expect((animatedContainer.decoration as BoxDecoration?)?.border?.top.color, ZetaColorBase.cool.shade90); + + await tester.tap(find.byType(ZetaCheckbox)); + await tester.pump(); + }); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + ZetaCheckbox().debugFillProperties(diagnostics); + + expect(diagnostics.finder('value'), 'false'); + expect(diagnostics.finder('label'), 'null'); + expect(diagnostics.finder('onChanged'), 'null'); + expect(diagnostics.finder('rounded'), 'null'); + expect(diagnostics.finder('useIndeterminate'), 'false'); + expect(diagnostics.finder('focusNode'), 'null'); + + final internalDiagnostics = DiagnosticPropertiesBuilder(); + ZetaInternalCheckbox(onChanged: (_) {}).debugFillProperties(internalDiagnostics); + expect(internalDiagnostics.finder('value'), 'false'); + expect(internalDiagnostics.finder('label'), 'null'); + expect(internalDiagnostics.finder('rounded'), 'null'); + expect(internalDiagnostics.finder('useIndeterminate'), 'false'); + expect(internalDiagnostics.finder('error'), 'false'); + expect(internalDiagnostics.finder('disabled'), 'false'); + expect(internalDiagnostics.finder('focusNode'), 'null'); + }); + }); +} diff --git a/test/src/components/checkbox/golden/checkbox_disabled.png b/test/src/components/checkbox/golden/checkbox_disabled.png new file mode 100644 index 00000000..0ecfd7bf Binary files /dev/null and b/test/src/components/checkbox/golden/checkbox_disabled.png differ diff --git a/test/src/components/checkbox/golden/checkbox_enabled.png b/test/src/components/checkbox/golden/checkbox_enabled.png new file mode 100644 index 00000000..eac35ed1 Binary files /dev/null and b/test/src/components/checkbox/golden/checkbox_enabled.png differ diff --git a/test/src/components/dialpad/dialpad_test.dart b/test/src/components/dialpad/dialpad_test.dart new file mode 100644 index 00000000..1b19da10 --- /dev/null +++ b/test/src/components/dialpad/dialpad_test.dart @@ -0,0 +1,285 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('dialpad')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + + group('ZetaDialPad Tests', () { + testWidgets('Initializes with correct parameters and is enabled', (WidgetTester tester) async { + String number = ''; + String text = ''; + + Future debounceWait() => tester.binding.delayed(const Duration(milliseconds: 500)); + + await tester.pumpWidget( + TestApp( + screenSize: const Size(1000, 1000), + home: ZetaDialPad( + onNumber: (value) => number += value, + onText: (value) => text += value, + ), + ), + ); + final dialPadFinder = find.byType(ZetaDialPad); + final buttonFinder = find.byType(ZetaDialPadButton); + + final oneFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '1'); + final twoFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '2'); + final threeFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '3'); + final starFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '*'); + final hashFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '#'); + + final ZetaDialPad dialPad = tester.firstWidget(dialPadFinder); + final List dialPadButtons = tester.widgetList(buttonFinder).toList(); + + final ZetaDialPadButton one = tester.firstWidget(oneFinder); + final ZetaDialPadButton two = tester.firstWidget(twoFinder); + final ZetaDialPadButton three = tester.firstWidget(threeFinder); + + /// Dial Pad built correctly. + expect(dialPad.buttonsPerRow, 3); + expect(dialPadButtons.length, 12); + + /// Dial Pad buttons built correctly. + expect(one.primary, '1'); + expect(one.secondary, ''); + expect(two.primary, '2'); + expect(two.secondary, 'ABC'); + expect(three.primary, '3'); + expect(three.secondary, 'DEF'); + + /// Tap button with only number. + await tester.tap(oneFinder); + await tester.pump(); + expect(number, '1'); + + /// Tap button with number and text. + await tester.tap(twoFinder); + await tester.pump(); + await debounceWait(); + expect(number, '12'); + expect(text, 'A'); + + /// Tap different button. + await tester.tap(threeFinder); + await tester.pump(); + await debounceWait(); + expect(number, '123'); + expect(text, 'AD'); + + /// Tap text button twice. + await tester.tap(twoFinder); + await tester.tap(twoFinder); + await tester.pump(); + await debounceWait(); + expect(text, 'ADB'); + + /// Tap text button thrice. + await tester.tap(twoFinder); + await tester.tap(twoFinder); + await tester.tap(twoFinder); + await tester.pump(); + await debounceWait(); + expect(text, 'ADBC'); + + /// Tap different text buttons to ensure debounce is cancelled. + await tester.tap(twoFinder); + await tester.tap(threeFinder); + await tester.tap(twoFinder); + await tester.pump(); + await debounceWait(); + expect(text, 'ADBCADA'); + + /// Tap text button more times than there is options to ensure it loops around correctly. + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(oneFinder); + await tester.pump(); + expect(text, 'ADBCADAF'); + number = ''; + + /// Tap buttons with symbols + await tester.ensureVisible(starFinder); + await tester.tap(starFinder); + await tester.tap(hashFinder); + await tester.pump(); + expect(number, '*#'); + + /// Allow all timers to end in text debounce + await debounceWait(); + + await expectLater( + dialPadFinder, + matchesGoldenFile(join(getCurrentPath('dialpad'), 'dialpad_enabled.png')), + ); + }); + }); + testWidgets('Initializes with correct parameters and is disabled', (WidgetTester tester) async { + const String number = ''; + const String text = ''; + + Future debounceWait() => tester.binding.delayed(const Duration(milliseconds: 500)); + + await tester.pumpWidget( + const TestApp( + screenSize: Size(1000, 1000), + home: ZetaDialPad(), + ), + ); + final dialPadFinder = find.byType(ZetaDialPad); + final buttonFinder = find.byType(ZetaDialPadButton); + + final oneFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '1'); + final twoFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '2'); + final threeFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '3'); + final starFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '*'); + final hashFinder = find.byWidgetPredicate((widget) => widget is ZetaDialPadButton && widget.primary == '#'); + + final ZetaDialPad dialPad = tester.firstWidget(dialPadFinder); + final List dialPadButtons = tester.widgetList(buttonFinder).toList(); + + final ZetaDialPadButton one = tester.firstWidget(oneFinder); + final ZetaDialPadButton two = tester.firstWidget(twoFinder); + final ZetaDialPadButton three = tester.firstWidget(threeFinder); + + /// Dial Pad built correctly. + expect(dialPad.buttonsPerRow, 3); + expect(dialPadButtons.length, 12); + + /// Dial Pad buttons built correctly. + expect(one.primary, '1'); + expect(one.secondary, ''); + expect(two.primary, '2'); + expect(two.secondary, 'ABC'); + expect(three.primary, '3'); + expect(three.secondary, 'DEF'); + + /// Tap button with only number. + await tester.tap(oneFinder); + await tester.pump(); + expect(number, ''); + + /// Tap button with number and text. + await tester.tap(twoFinder); + await tester.pump(); + await debounceWait(); + expect(number, ''); + expect(text, ''); + + /// Tap different button. + await tester.tap(threeFinder); + await tester.pump(); + await debounceWait(); + expect(number, ''); + expect(text, ''); + + /// Tap text button twice. + await tester.tap(twoFinder); + await tester.tap(twoFinder); + await tester.pump(); + await debounceWait(); + expect(text, ''); + + /// Tap text button thrice. + await tester.tap(twoFinder); + await tester.tap(twoFinder); + await tester.tap(twoFinder); + await tester.pump(); + await debounceWait(); + expect(text, ''); + + /// Tap different text buttons to ensure debounce is cancelled. + await tester.tap(twoFinder); + await tester.tap(threeFinder); + await tester.tap(twoFinder); + await tester.pump(); + await debounceWait(); + expect(text, ''); + + /// Tap text button more times than there is options to ensure it loops around correctly. + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(threeFinder); + await tester.tap(oneFinder); + await tester.pump(); + expect(text, ''); + + /// Tap buttons with symbols + await tester.ensureVisible(starFinder); + await tester.tap(starFinder); + await tester.tap(hashFinder); + await tester.pump(); + expect(number, ''); + + /// Allow all timers to end in text debounce + await debounceWait(); + + await expectLater( + dialPadFinder, + matchesGoldenFile(join(getCurrentPath('dialpad'), 'dialpad_disabled.png')), + ); + }); + testWidgets('ZetaDialPadButton interaction', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + screenSize: Size(1000, 1000), + home: ZetaDialPadButton(primary: '1'), + ), + ); + final buttonFinder = find.byType(ZetaDialPadButton); + final inkwellFinder = find.byType(InkWell); + final InkWell inkWell = tester.firstWidget(inkwellFinder); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + await tester.pump(); + await gesture.moveTo(tester.getCenter(buttonFinder)); + await tester.pumpAndSettle(); + + expect(inkWell.overlayColor?.resolve({WidgetState.hovered}), ZetaColorBase.cool.shade20); + + await expectLater( + buttonFinder, + matchesGoldenFile(join(getCurrentPath('dialpad'), 'dialpadbutton.png')), + ); + }); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaDialPad().debugFillProperties(diagnostics); + + expect(diagnostics.finder('onNumber'), 'null'); + expect(diagnostics.finder('onText'), 'null'); + expect(diagnostics.finder('buttonsPerRow'), '3'); + expect( + diagnostics.finder('buttonValues'), + '{1: , 2: ABC, 3: DEF, 4: GHI, 5: JKL, 6: MNO, 7: PQRS, 8: TUV, 9: WXYZ, *: , 0: +, #: }', + ); + + final internalDiagnostics = DiagnosticPropertiesBuilder(); + const ZetaDialPadButton(primary: '1').debugFillProperties(internalDiagnostics); + expect(internalDiagnostics.finder('primary'), '"1"'); + expect(internalDiagnostics.finder('secondary'), '""'); + expect(internalDiagnostics.finder('onTap'), 'null'); + expect(internalDiagnostics.finder('topPadding'), '3.0'); + }); +} diff --git a/test/src/components/dialpad/golden/dialpad_disabled.png b/test/src/components/dialpad/golden/dialpad_disabled.png new file mode 100644 index 00000000..e2357d55 Binary files /dev/null and b/test/src/components/dialpad/golden/dialpad_disabled.png differ diff --git a/test/src/components/dialpad/golden/dialpad_enabled.png b/test/src/components/dialpad/golden/dialpad_enabled.png new file mode 100644 index 00000000..e2357d55 Binary files /dev/null and b/test/src/components/dialpad/golden/dialpad_enabled.png differ diff --git a/test/src/components/dialpad/golden/dialpadbutton.png b/test/src/components/dialpad/golden/dialpadbutton.png new file mode 100644 index 00000000..b7c289fc Binary files /dev/null and b/test/src/components/dialpad/golden/dialpadbutton.png differ diff --git a/test/src/components/fabs/fab_test.dart b/test/src/components/fabs/fab_test.dart new file mode 100644 index 00000000..b87fe1ce --- /dev/null +++ b/test/src/components/fabs/fab_test.dart @@ -0,0 +1,160 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('fabs')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + + group('ZetaFAB Tests', () { + testWidgets('Initializes with correct parameters', (WidgetTester tester) async { + final scrollController = ScrollController(); + await tester.pumpWidget( + TestApp( + home: ZetaFAB(scrollController: scrollController, label: 'Label', onPressed: () {}), + ), + ); + + expect(find.byType(ZetaFAB), findsOneWidget); + + await expectLater( + find.byType(ZetaFAB), + matchesGoldenFile(join(getCurrentPath('fabs'), 'FAB_default.png')), + ); + }); + + testWidgets('OnPressed callback', (WidgetTester tester) async { + bool isPressed = false; + final scrollController = ScrollController(); + + await tester.pumpWidget( + TestApp( + home: ZetaFAB(scrollController: scrollController, label: 'Label', onPressed: () => isPressed = true), + ), + ); + + await tester.tap(find.byType(ZetaFAB)); + await tester.pumpAndSettle(); + expect(isPressed, isTrue); + }); + }); + + testWidgets('Icon Test', (WidgetTester tester) async { + final scrollController = ScrollController(); + await tester.pumpWidget( + TestApp( + home: ZetaFAB( + scrollController: scrollController, + onPressed: () {}, + type: ZetaFabType.inverse, + shape: ZetaWidgetBorder.rounded, + size: ZetaFabSize.large, + ), + ), + ); + expect(find.byIcon(ZetaIcons.add_round), findsOneWidget); + final fabFinder = find.byType(ZetaFAB); + final ZetaFAB fab = tester.firstWidget(fabFinder); + + expect(fab.initiallyExpanded, null); + expect(fab.type, ZetaFabType.inverse); + expect(fab.shape, ZetaWidgetBorder.rounded); + + await expectLater( + find.byType(ZetaFAB), + matchesGoldenFile(join(getCurrentPath('fabs'), 'FAB_inverse.png')), + ); + }); + + testWidgets('Expanded', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaFAB( + initiallyExpanded: true, + onPressed: () {}, + label: 'Label', + type: ZetaFabType.secondary, + shape: ZetaWidgetBorder.sharp, + ), + ), + ); + + final fabFinder = find.byType(ZetaFAB); + final ZetaFAB fab = tester.firstWidget(fabFinder); + + expect(fab.initiallyExpanded, true); + expect(fab.type, ZetaFabType.secondary); + expect(fab.shape, ZetaWidgetBorder.sharp); + + await expectLater( + find.byType(ZetaFAB), + matchesGoldenFile(join(getCurrentPath('fabs'), 'FAB_secondary.png')), + ); + }); + testWidgets('ZetaFAB interactive', (WidgetTester tester) async { + final FocusNode node = FocusNode(); + + await tester.pumpWidget( + TestApp( + home: ZetaFAB( + initiallyExpanded: true, + onPressed: () {}, + label: 'Label', + type: ZetaFabType.secondary, + shape: ZetaWidgetBorder.sharp, + focusNode: node, + ), + ), + ); + + final fabFinder = find.byType(ZetaFAB); + final ZetaFAB fab = tester.firstWidget(fabFinder); + final filledButtonFinder = find.byType(FilledButton); + final FilledButton filledButton = tester.firstWidget(filledButtonFinder); + + expect(fab.initiallyExpanded, true); + expect(fab.type, ZetaFabType.secondary); + expect(fab.shape, ZetaWidgetBorder.sharp); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + await tester.pumpAndSettle(); + await gesture.moveTo(tester.getCenter(fabFinder)); + await tester.pumpAndSettle(); + + expect(filledButton.style?.backgroundColor?.resolve({WidgetState.hovered}), ZetaColorBase.yellow.shade70); + + await gesture.moveTo(Offset.zero); + await tester.pumpAndSettle(); + + node.requestFocus(); + await tester.pumpAndSettle(); + expect( + filledButton.style?.side?.resolve({WidgetState.focused}), + BorderSide(color: ZetaColorBase.blue[50]!, width: ZetaSpacingBase.x0_5), + ); + }); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaFAB().debugFillProperties(diagnostics); + + expect(diagnostics.finder('label'), 'null'); + expect(diagnostics.finder('onPressed'), 'null'); + expect(diagnostics.finder('type'), 'primary'); + expect(diagnostics.finder('size'), 'small'); + expect(diagnostics.finder('shape'), 'full'); + expect(diagnostics.finder('icon'), 'IconData(U+0E009)'); + expect(diagnostics.finder('initiallyExpanded'), 'null'); + expect(diagnostics.finder('focusNode'), 'null'); + }); +} diff --git a/test/src/components/fabs/golden/FAB_default.png b/test/src/components/fabs/golden/FAB_default.png new file mode 100644 index 00000000..e9fd5e47 Binary files /dev/null and b/test/src/components/fabs/golden/FAB_default.png differ diff --git a/test/src/components/fabs/golden/FAB_inverse.png b/test/src/components/fabs/golden/FAB_inverse.png new file mode 100644 index 00000000..fed07d53 Binary files /dev/null and b/test/src/components/fabs/golden/FAB_inverse.png differ diff --git a/test/src/components/fabs/golden/FAB_secondary.png b/test/src/components/fabs/golden/FAB_secondary.png new file mode 100644 index 00000000..df672743 Binary files /dev/null and b/test/src/components/fabs/golden/FAB_secondary.png differ diff --git a/test/src/components/in_page_banner/golden/in_page_banner_buttons.png b/test/src/components/in_page_banner/golden/in_page_banner_buttons.png new file mode 100644 index 00000000..bfc5644e Binary files /dev/null and b/test/src/components/in_page_banner/golden/in_page_banner_buttons.png differ diff --git a/test/src/components/in_page_banner/golden/in_page_banner_default.png b/test/src/components/in_page_banner/golden/in_page_banner_default.png new file mode 100644 index 00000000..641ee16b Binary files /dev/null and b/test/src/components/in_page_banner/golden/in_page_banner_default.png differ diff --git a/test/src/components/in_page_banner/golden/in_page_banner_negative.png b/test/src/components/in_page_banner/golden/in_page_banner_negative.png new file mode 100644 index 00000000..406c1b0f Binary files /dev/null and b/test/src/components/in_page_banner/golden/in_page_banner_negative.png differ diff --git a/test/src/components/in_page_banner/golden/in_page_banner_positive.png b/test/src/components/in_page_banner/golden/in_page_banner_positive.png new file mode 100644 index 00000000..7c12198b Binary files /dev/null and b/test/src/components/in_page_banner/golden/in_page_banner_positive.png differ diff --git a/test/src/components/in_page_banner/in_page_banner_test.dart b/test/src/components/in_page_banner/in_page_banner_test.dart new file mode 100644 index 00000000..a15b4205 --- /dev/null +++ b/test/src/components/in_page_banner/in_page_banner_test.dart @@ -0,0 +1,126 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('in_page_banner')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + + group('ZetaInPageBanner Tests', () { + testWidgets('ZetaInPageBanner creation', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaInPageBanner(content: Text('Test'), title: 'Title'), + ), + ); + final Finder decoratedBoxFinder = find.byType(DecoratedBox); + final DecoratedBox box = tester.firstWidget(decoratedBoxFinder); + + expect(find.byType(ZetaInPageBanner), findsOneWidget); + expect(find.text('Test'), findsOneWidget); + + expect(box.decoration.runtimeType, BoxDecoration); + final BoxDecoration decoration = box.decoration as BoxDecoration; + expect(decoration.color, ZetaColorBase.purple.shade10); + + await expectLater( + find.byType(ZetaInPageBanner), + matchesGoldenFile(join(getCurrentPath('in_page_banner'), 'in_page_banner_default.png')), + ); + }); + }); + + testWidgets("ZetaInPageBanner shows 'close icon' correctly", (WidgetTester tester) async { + await tester.pumpWidget( + TestApp(home: ZetaInPageBanner(content: const Text('Test'), onClose: () {}, status: ZetaWidgetStatus.negative)), + ); + + expect(find.byIcon(ZetaIcons.close_round), findsOneWidget); + final Finder decoratedBoxFinder = find.byType(DecoratedBox); + final DecoratedBox box = tester.firstWidget(decoratedBoxFinder); + expect(box.decoration.runtimeType, BoxDecoration); + final BoxDecoration decoration = box.decoration as BoxDecoration; + expect(decoration.color, ZetaColorBase.red.shade10); + await expectLater( + find.byType(ZetaInPageBanner), + matchesGoldenFile(join(getCurrentPath('in_page_banner'), 'in_page_banner_negative.png')), + ); + }); + + testWidgets("ZetaInPageBanner hides 'close icon' correctly", (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp(home: ZetaInPageBanner(content: Text('Test'), status: ZetaWidgetStatus.positive)), + ); + expect(find.byIcon(ZetaIcons.close_round), findsNothing); + final Finder decoratedBoxFinder = find.byType(DecoratedBox); + final DecoratedBox box = tester.firstWidget(decoratedBoxFinder); + expect(box.decoration.runtimeType, BoxDecoration); + final BoxDecoration decoration = box.decoration as BoxDecoration; + expect(decoration.color, ZetaColorBase.green.shade10); + await expectLater( + find.byType(ZetaInPageBanner), + matchesGoldenFile(join(getCurrentPath('in_page_banner'), 'in_page_banner_positive.png')), + ); + }); + + testWidgets('ZetaInPageBanner button callbacks work', (WidgetTester tester) async { + bool onPressed = false; + final key = GlobalKey(); + await tester.pumpWidget( + TestApp( + home: ZetaInPageBanner( + content: const Text('Test'), + status: ZetaWidgetStatus.neutral, + actions: [ + ZetaButton( + label: 'Test button', + onPressed: () => onPressed = true, + key: key, + ), + ], + ), + ), + ); + + final Finder decoratedBoxFinder = find.byType(DecoratedBox); + final DecoratedBox box = tester.firstWidget(decoratedBoxFinder); + expect(box.decoration.runtimeType, BoxDecoration); + final BoxDecoration decoration = box.decoration as BoxDecoration; + expect(decoration.color, ZetaColorBase.cool.shade10); + + await tester.tap(find.byKey(key)); + await tester.pumpAndSettle(); + expect(onPressed, isTrue); + + await expectLater( + find.byType(ZetaInPageBanner), + matchesGoldenFile(join(getCurrentPath('in_page_banner'), 'in_page_banner_buttons.png')), + ); + }); + testWidgets("ZetaInPageBanner 'close' icon tap test", (WidgetTester tester) async { + bool closeIconIsTapped = false; + await tester.pumpWidget( + TestApp(home: ZetaInPageBanner(onClose: () => closeIconIsTapped = true, content: const Text('Test'))), + ); + final closeIcon = find.byIcon(ZetaIcons.close_round); + await tester.tap(closeIcon); + await tester.pump(); + expect(closeIconIsTapped, isTrue); + }); + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaInPageBanner(content: Placeholder()).debugFillProperties(diagnostics); + + expect(diagnostics.finder('onClose'), 'null'); + expect(diagnostics.finder('status'), 'info'); + expect(diagnostics.finder('title'), 'null'); + expect(diagnostics.finder('customIcon'), 'null'); + }); +} diff --git a/test/src/components/password/golden/password_default.png b/test/src/components/password/golden/password_default.png new file mode 100644 index 00000000..91231e82 Binary files /dev/null and b/test/src/components/password/golden/password_default.png differ diff --git a/test/src/components/password/golden/password_error.png b/test/src/components/password/golden/password_error.png new file mode 100644 index 00000000..b7ed8586 Binary files /dev/null and b/test/src/components/password/golden/password_error.png differ diff --git a/test/src/components/password/password_input_test.dart b/test/src/components/password/password_input_test.dart new file mode 100644 index 00000000..cfac9d7c --- /dev/null +++ b/test/src/components/password/password_input_test.dart @@ -0,0 +1,97 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.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'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('password')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + + testWidgets('ZetaPasswordInput initializes correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaPasswordInput(), + ), + ); + expect(find.byType(ZetaPasswordInput), findsOneWidget); + + await expectLater( + find.byType(ZetaPasswordInput), + matchesGoldenFile(join(getCurrentPath('password'), 'password_default.png')), + ); + }); + + testWidgets('Test password visibility', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaPasswordInput(), + ), + ); + final obscureIconOff = find.byIcon(ZetaIcons.visibility_off_round); + expect(obscureIconOff, findsOneWidget); + await tester.tap(obscureIconOff); + await tester.pump(); + + final obscureIconOn = find.byIcon(ZetaIcons.visibility_round); + expect(obscureIconOn, findsOneWidget); + }); + + testWidgets('Test error message visibility', (WidgetTester tester) async { + String? testValidator(String? value) { + final regExp = RegExp(r'\d'); + if (value != null && regExp.hasMatch(value)) return 'Error'; + return null; + } + + final controller = TextEditingController()..text = 'password123'; + final formKey = GlobalKey(); + + await tester.pumpWidget( + TestApp( + home: Form( + key: formKey, + child: ZetaPasswordInput( + controller: controller, + validator: testValidator, + rounded: false, + ), + ), + ), + ); + formKey.currentState?.validate(); + await tester.pump(); + + // There will be two matches for the error text as the form field itself contains a hidden one. + expect(find.text('Error'), findsExactly(2)); + final obscureIconOn = find.byIcon(ZetaIcons.visibility_off_sharp); + expect(obscureIconOn, findsOneWidget); + + await expectLater( + find.byType(ZetaPasswordInput), + matchesGoldenFile(join(getCurrentPath('password'), 'password_error.png')), + ); + }); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaPasswordInput().debugFillProperties(diagnostics); + + expect(diagnostics.finder('controller'), 'null'); + expect(diagnostics.finder('obscureText'), 'true'); + expect(diagnostics.finder('hintText'), 'null'); + expect(diagnostics.finder('label'), 'null'); + expect(diagnostics.finder('footerText'), 'null'); + expect(diagnostics.finder('validator'), 'null'); + expect(diagnostics.finder('size'), 'large'); + expect(diagnostics.finder('placeholder'), 'null'); + expect(diagnostics.finder('onSubmit'), 'null'); + expect(diagnostics.finder('errorText'), 'null'); + }); +} diff --git a/test/src/components/tooltip/golden/arrow_down.png b/test/src/components/tooltip/golden/arrow_down.png index aa995748..4635ff7c 100644 Binary files a/test/src/components/tooltip/golden/arrow_down.png and b/test/src/components/tooltip/golden/arrow_down.png differ diff --git a/test/src/components/tooltip/golden/arrow_left.png b/test/src/components/tooltip/golden/arrow_left.png index 7f66b33e..e15b340e 100644 Binary files a/test/src/components/tooltip/golden/arrow_left.png and b/test/src/components/tooltip/golden/arrow_left.png differ diff --git a/test/src/components/tooltip/golden/arrow_right.png b/test/src/components/tooltip/golden/arrow_right.png index 30d99000..12a63dee 100644 Binary files a/test/src/components/tooltip/golden/arrow_right.png and b/test/src/components/tooltip/golden/arrow_right.png differ diff --git a/test/src/components/tooltip/golden/arrow_up.png b/test/src/components/tooltip/golden/arrow_up.png index da590cb5..6ea5556f 100644 Binary files a/test/src/components/tooltip/golden/arrow_up.png and b/test/src/components/tooltip/golden/arrow_up.png differ diff --git a/test/src/components/tooltip/tooltip_test.dart b/test/src/components/tooltip/tooltip_test.dart index 6f055db4..fa0ea5fd 100644 --- a/test/src/components/tooltip/tooltip_test.dart +++ b/test/src/components/tooltip/tooltip_test.dart @@ -1,17 +1,18 @@ -import 'dart:io'; +// ignore_for_file: avoid_dynamic_calls import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:path/path.dart' as p; +import 'package:path/path.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'; void main() { setUpAll(() { - final testUri = Uri.parse(p.join(Directory.current.path, 'golden').replaceAll(r'\', '/')); + final testUri = Uri.parse(getCurrentPath('tooltip')); goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); }); @@ -108,7 +109,7 @@ void main() { // Verifying the CustomPaint with different arrow directions. await expectLater( find.byType(ZetaTooltip), - matchesGoldenFile(p.join('golden', 'arrow_up.png')), + matchesGoldenFile(join(getCurrentPath('tooltip'), 'arrow_up.png')), ); }); @@ -128,7 +129,7 @@ void main() { // Verifying the CustomPaint with different arrow directions. await expectLater( find.byType(ZetaTooltip), - matchesGoldenFile(p.join('golden', 'arrow_down.png')), + matchesGoldenFile(join(getCurrentPath('tooltip'), 'arrow_down.png')), ); }); @@ -149,7 +150,7 @@ void main() { // Verifying the CustomPaint with different arrow directions. await expectLater( find.byType(ZetaTooltip), - matchesGoldenFile(p.join('golden', 'arrow_left.png')), + matchesGoldenFile(join(getCurrentPath('tooltip'), 'arrow_left.png')), ); }); @@ -170,7 +171,7 @@ void main() { // Verifying the CustomPaint with different arrow directions. await expectLater( find.byType(ZetaTooltip), - matchesGoldenFile(p.join('golden', 'arrow_right.png')), + matchesGoldenFile(join(getCurrentPath('tooltip'), 'arrow_right.png')), ); }); @@ -224,24 +225,12 @@ void main() { child: Text('Rounded tooltip'), ).debugFillProperties(diagnostics); - final rounded = diagnostics.properties.where((p) => p.name == 'rounded').map((p) => p.toDescription()).first; - expect(rounded, 'null'); - - final padding = diagnostics.properties.where((p) => p.name == 'padding').map((p) => p.toDescription()).first; - expect(padding, 'EdgeInsets.all(8.0)'); - - final color = diagnostics.properties.where((p) => p.name == 'color').map((p) => p.toDescription()).first; - expect(color.toLowerCase(), contains(Colors.amber.hexCode.toLowerCase())); - - final textStyle = diagnostics.properties.where((p) => p.name == 'textStyle').map((p) => p.toDescription()).first; - expect(textStyle, contains('size: 9.0')); - - final direction = - diagnostics.properties.where((p) => p.name == 'arrowDirection').map((p) => p.toDescription()).first; - expect(direction, 'down'); - - final maxWidth = diagnostics.properties.where((p) => p.name == 'maxWidth').map((p) => p.toDescription()).first; - expect(maxWidth, '170.0'); + expect(diagnostics.finder('rounded'), 'null'); + expect(diagnostics.finder('padding'), 'EdgeInsets.all(8.0)'); + expect(diagnostics.finder('color').toLowerCase(), contains(Colors.amber.hexCode.toLowerCase())); + expect(diagnostics.finder('textStyle'), contains('size: 9.0')); + expect(diagnostics.finder('arrowDirection'), 'down'); + expect(diagnostics.finder('maxWidth'), '170.0'); }); }); } diff --git a/test/src/theme/color_swatch_test.dart b/test/src/theme/color_swatch_test.dart index 8bf33200..11eadba5 100644 --- a/test/src/theme/color_swatch_test.dart +++ b/test/src/theme/color_swatch_test.dart @@ -160,4 +160,23 @@ void main() { expect(aaaSwatch.surface.value, Colors.blue.shade100.value); }); }); + + testWidgets('Light / Dark mode are inverted', (tester) async { + final ZetaColors light = ZetaColors(); + final ZetaColors dark = ZetaColors(brightness: Brightness.dark); + + expect(light.primary.shade10, dark.primary.shade100); + expect(light.primary.shade20, dark.primary.shade90); + expect(light.primary.shade30, dark.primary.shade80); + expect(light.primary.shade40, dark.primary.shade70); + expect(light.primary.shade50, dark.primary.shade60); + }); + + testWidgets('AAA mode value colors are 2 shades darker', (tester) async { + final ZetaColors aa = ZetaColors(); + final ZetaColors aaa = ZetaColors(brightness: Brightness.dark, contrast: ZetaContrast.aaa); + + expect(aa.primary.value, aa.primary.shade60.value); + expect(aaa.primary.value, aaa.primary.shade80.value); + }); } diff --git a/example/test/rounded_test.dart b/test/src/utils/rounded_test.dart similarity index 73% rename from example/test/rounded_test.dart rename to test/src/utils/rounded_test.dart index 1d5aa5b8..60ee61a4 100644 --- a/example/test/rounded_test.dart +++ b/test/src/utils/rounded_test.dart @@ -1,19 +1,19 @@ +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_components.dart'; +import '../../test_utils/test_app.dart'; void main() { testWidgets('Global round inherited', (WidgetTester tester) async { - GlobalKey key = GlobalKey(); + final GlobalKey key = GlobalKey(); await tester.pumpWidget( - TestWidget( + TestApp( rounded: true, - widget: ZetaAccordion( + home: ZetaAccordion( key: key, title: 'Test Accordion', - child: ZetaStatusLabel(label: 'Label'), + child: const ZetaStatusLabel(label: 'Label'), ), ), ); @@ -34,9 +34,9 @@ void main() { testWidgets('Global sharp inherited', (WidgetTester tester) async { await tester.pumpWidget( - TestWidget( + const TestApp( rounded: false, - widget: ZetaAccordion( + home: ZetaAccordion( title: 'Test Accordion', child: ZetaStatusLabel(label: 'Label'), ), @@ -56,14 +56,14 @@ void main() { }); testWidgets('Global sharp, local round, not inherited', (WidgetTester tester) async { - GlobalKey key = GlobalKey(); + final GlobalKey key = GlobalKey(); await tester.pumpWidget( - TestWidget( + TestApp( rounded: false, - widget: ZetaAccordion( + home: ZetaAccordion( key: key, title: 'Test Accordion', - child: ZetaStatusLabel(label: 'Label', rounded: true), + child: const ZetaStatusLabel(label: 'Label', rounded: true), ), ), ); @@ -82,13 +82,13 @@ void main() { }); testWidgets('Global sharp, scoped round inherited', (WidgetTester tester) async { - final Key sharpKey = Key('sharp'); - final Key roundKey = Key('round'); + const Key sharpKey = Key('sharp'); + const Key roundKey = Key('round'); await tester.pumpWidget( - TestWidget( + const TestApp( rounded: false, - widget: Column( + home: Column( children: [ ZetaStatusLabel(label: 'Label', key: sharpKey), ZetaRoundedScope( @@ -126,13 +126,13 @@ void main() { }); testWidgets('Global sharp, scoped round, scoped sharp inherited', (WidgetTester tester) async { - final Key sharpKey = Key('sharp'); - final Key sharpKey2 = Key('round'); + const Key sharpKey = Key('sharp'); + const Key sharpKey2 = Key('round'); await tester.pumpWidget( - TestWidget( + const TestApp( rounded: false, - widget: Column( + home: Column( children: [ ZetaStatusLabel(label: 'Label', key: sharpKey), ZetaRoundedScope( @@ -168,4 +168,35 @@ void main() { expect(roundBox.decoration.runtimeType, BoxDecoration); expect((roundBox.decoration as BoxDecoration).borderRadius, ZetaRadius.none); }); + + testWidgets('ZetaRoundedScope debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + + const ZetaRoundedScope(rounded: true, child: SizedBox()).debugFillProperties(diagnostics); + + final rounded = diagnostics.properties.where((p) => p.name == 'rounded').map((p) => p.toDescription()).first; + expect(rounded, 'true'); + }); + + testWidgets('ZetaStatefulWidget debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const _RoundedStateTestWidget(rounded: true).debugFillProperties(diagnostics); + + final rounded = diagnostics.properties.where((p) => p.name == 'rounded').map((p) => p.toDescription()).first; + expect(rounded, 'true'); + }); +} + +class _RoundedStateTestWidget extends ZetaStatefulWidget { + const _RoundedStateTestWidget({super.rounded}); + + @override + State<_RoundedStateTestWidget> createState() => _RoundedStateTestWidgetState(); +} + +class _RoundedStateTestWidgetState extends State<_RoundedStateTestWidget> { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } } diff --git a/test/test_utils/test_app.dart b/test/test_utils/test_app.dart index cbb5180b..6e2396fb 100644 --- a/test/test_utils/test_app.dart +++ b/test/test_utils/test_app.dart @@ -1,22 +1,64 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; class TestApp extends StatelessWidget { - const TestApp({super.key, required this.home}); + const TestApp({ + super.key, + required this.home, + this.screenSize, + this.themeMode, + this.removeBody = true, + this.rounded, + }); + final Widget home; + final Size? screenSize; + final ThemeMode? themeMode; + final bool removeBody; + final bool? rounded; @override Widget build(BuildContext context) { return ZetaProvider( + initialThemeMode: themeMode ?? ThemeMode.system, + initialThemeData: ZetaThemeData(rounded: rounded ?? true), builder: (context, themeData, themeMode) { return MaterialApp( - home: Builder( - builder: (context) { - return home; - }, + theme: ThemeData( + fontFamily: themeData.fontFamily, + colorScheme: themeData.colorsLight.toScheme(), + textTheme: zetaTextTheme, + ), + darkTheme: ThemeData( + fontFamily: themeData.fontFamily, + colorScheme: themeData.colorsDark.toScheme(), + textTheme: zetaTextTheme, + ), + home: Scaffold( + body: removeBody + ? home + : SizedBox( + width: screenSize?.width, + height: screenSize?.height, + child: MediaQuery( + data: MediaQueryData(size: Size(screenSize?.width ?? 0, screenSize?.height ?? 0)), + child: SingleChildScrollView(child: home), + ), + ), ), ); }, ); } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('screenSize', screenSize)) + ..add(EnumProperty('themeMode', themeMode)) + ..add(DiagnosticsProperty('removeBody', removeBody)) + ..add(DiagnosticsProperty('rounded', rounded)); + } } diff --git a/test/test_utils/utils.dart b/test/test_utils/utils.dart new file mode 100644 index 00000000..21ea3356 --- /dev/null +++ b/test/test_utils/utils.dart @@ -0,0 +1,14 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:path/path.dart'; + +String getCurrentPath(String component, {String type = 'components'}) { + return join(Directory.current.path, 'test', 'src', type, component, 'golden'); +} + +extension Util on DiagnosticPropertiesBuilder { + dynamic finder(String finder) { + return properties.where((p) => p.name == finder).map((p) => p.toDescription()).first; + } +}