From ba4901d74fff20d6319050f331c5f6d60f99b438 Mon Sep 17 00:00:00 2001 From: Daniel Eshkeri Date: Tue, 15 Oct 2024 15:25:59 +0100 Subject: [PATCH 1/8] test: organizing tests into groups (#188) test: standardizing tests for badges test: added debugFillProperties test helper function docs: added helper function section to TESTING_README docs: added link to excel sheet in TESTING_README test: added test count script fix: changed storybook to widgetbook in name of deploy preview in pull request github action docs: created test counter script docs: test_counter script improvements test: moved script files to different PR test: implemented helper function in avatar and banner tests tests: organised button, chat_item, and checkbox tests tests: organised tests for chip and comms button tests: organised chat item, checkbox, chip, dialpad, fab, icon, in page banner, password, search bar, slider, stepper input, and, tooltip chore(automated): Lint commit and format fix: removed '$componentName' from all test files fix: removed analyzer from dependencies --- .github/workflows/pull-request.yml | 2 +- .../{TESTING_README.mdx => TESTING_README.md} | 45 +- test/scripts/output/test_table.md | 26 + test/scripts/test_counter.dart | 19 +- test/scripts/utils/utils.dart | 20 +- .../components/accordion/accordion_test.dart | 256 +++++----- test/src/components/avatar/avatar_test.dart | 181 +++---- .../avatar/golden/avatar_from_name_xs.png | Bin 3671 -> 3666 bytes .../avatar/golden/avatar_initials_xs.png | Bin 3671 -> 3666 bytes .../badge/golden/indicator_default.png | Bin 3998 -> 4192 bytes .../badge/golden/indicator_icon_default.png | Bin 4135 -> 4265 bytes .../badge/golden/indicator_icon_values.png | Bin 3896 -> 3865 bytes .../golden/indicator_notification_default.png | Bin 3998 -> 4192 bytes .../golden/indicator_notification_values.png | Bin 3599 -> 3599 bytes .../components/badge/golden/label_dark.png | Bin 3677 -> 3580 bytes .../components/badge/golden/label_default.png | Bin 3630 -> 3558 bytes .../badge/golden/label_negative.png | Bin 3677 -> 3568 bytes .../components/badge/golden/label_neutral.png | Bin 3625 -> 3546 bytes .../badge/golden/label_positive.png | Bin 3685 -> 3578 bytes .../components/badge/golden/label_sharp.png | Bin 3457 -> 3388 bytes .../components/badge/golden/label_warning.png | Bin 3671 -> 3571 bytes .../badge/golden/priority_pill_default.png | Bin 4502 -> 4329 bytes .../badge/golden/priority_pill_high.png | Bin 3681 -> 3450 bytes .../badge/golden/priority_pill_low.png | Bin 3870 -> 3817 bytes .../badge/golden/priority_pill_medium.png | Bin 4059 -> 4001 bytes .../badge/golden/status_label_custom.png | Bin 4609 -> 4407 bytes .../badge/golden/status_label_default.png | Bin 4561 -> 4392 bytes test/src/components/badge/golden/tag_left.png | Bin 3580 -> 3543 bytes .../src/components/badge/golden/tag_right.png | Bin 3536 -> 3487 bytes test/src/components/badge/indicator_test.dart | 319 ++++++------ test/src/components/badge/label_test.dart | 175 ++++--- .../components/badge/priority_pill_test.dart | 226 ++++---- .../components/badge/status_label_test.dart | 78 +-- test/src/components/badge/tag_test.dart | 50 +- test/src/components/banner/banner_test.dart | 56 +- test/src/components/button/button_test.dart | 481 +++++++++--------- .../components/chat_item/chat_item_test.dart | 442 ++++++++++++---- .../golden/chat_item_slidable_actions.png | Bin 3424 -> 2975 bytes .../components/checkbox/checkbox_test.dart | 135 ++--- test/src/components/chips/chip_test.dart | 68 ++- .../comms_button/comms_button_test.dart | 193 ++++--- .../golden/CommsButton_default.png | Bin 6293 -> 0 bytes test/src/components/dialpad/dialpad_test.dart | 328 ++++++------ .../dialpad/golden/dialpad_disabled.png | Bin 11188 -> 8944 bytes .../dialpad/golden/dialpad_enabled.png | Bin 11188 -> 8944 bytes .../dialpad/golden/dialpadbutton.png | Bin 4708 -> 4315 bytes test/src/components/fab/fab_test.dart | 270 ++++++++++ test/src/components/fabs/fab_test.dart | 219 -------- .../components/fabs/golden/FAB_default.png | Bin 4754 -> 0 bytes .../components/fabs/golden/FAB_disabled.png | Bin 4446 -> 0 bytes .../components/fabs/golden/FAB_inverse.png | Bin 4352 -> 0 bytes .../components/fabs/golden/FAB_pressed.png | Bin 4883 -> 0 bytes .../components/fabs/golden/FAB_secondary.png | Bin 3599 -> 0 bytes test/src/components/icon/icon_test.dart | 103 ++-- .../in_page_banner/in_page_banner_test.dart | 227 +++++---- .../password/password_input_test.dart | 158 +++--- .../search_bar/search_bar_test.dart | 180 +++---- test/src/components/slider/slider_test.dart | 77 ++- .../stepper input/stepper_input_test.dart | 62 --- .../stepper_input/stepper_input_test.dart | 94 ++++ test/src/components/tooltip/tooltip_test.dart | 225 ++++---- test/test_utils/utils.dart | 72 +++ 62 files changed, 2733 insertions(+), 2054 deletions(-) rename test/{TESTING_README.mdx => TESTING_README.md} (72%) create mode 100644 test/scripts/output/test_table.md delete mode 100644 test/src/components/comms_button/golden/CommsButton_default.png create mode 100644 test/src/components/fab/fab_test.dart delete mode 100644 test/src/components/fabs/fab_test.dart delete mode 100644 test/src/components/fabs/golden/FAB_default.png delete mode 100644 test/src/components/fabs/golden/FAB_disabled.png delete mode 100644 test/src/components/fabs/golden/FAB_inverse.png delete mode 100644 test/src/components/fabs/golden/FAB_pressed.png delete mode 100644 test/src/components/fabs/golden/FAB_secondary.png delete mode 100644 test/src/components/stepper input/stepper_input_test.dart create mode 100644 test/src/components/stepper_input/stepper_input_test.dart diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index c58f8ab5..a55af954 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -41,7 +41,7 @@ jobs: fi deploy-preview: - name: Deploy preview version of the storybook on firebase + name: Deploy preview version of the widgetbook on firebase needs: [code-quality, check-secret] if: needs.check-secret.outputs.secret-exists == 'true' runs-on: ubuntu-latest diff --git a/test/TESTING_README.mdx b/test/TESTING_README.md similarity index 72% rename from test/TESTING_README.mdx rename to test/TESTING_README.md index 0789e872..a98b8042 100644 --- a/test/TESTING_README.mdx +++ b/test/TESTING_README.md @@ -11,24 +11,34 @@ As you are writing tests think about helper function you could write and add the ### Groups -- Accessibility Tests +- **Accessibility Tests** Semantic labels, touch areas, contrast ratios, etc. -- Content Tests - Finds the widget, parameter statuses, etc. -- Dimensions Tests - Size, padding, margin, alignment, etc. -- Styling Tests + +- **Content Tests** + Finds the widget, parameter statuses, etc. + Checking for the value of props and attributes of the widget. Checking for the presence of widgets. + +- **Dimensions Tests** + Size, padding, margin, alignment, etc. + getSize(). + +- **Styling Tests** Rendered colors, fonts, borders, radii etc. -- Interaction Tests + Checking the style of widgets and child widgets. + +- **Interaction Tests** Gesture recognizers, taps, drags, etc. -- Golden Tests + For example, using a boolean to check if the widgets interaction function runs. + +- **Golden Tests** Compares the rendered widget with the golden file. Use the `goldenTest()` function from test_utils/utils.dart. -- Performance Tests + +- **Performance Tests** Animation performance, rendering performance, data manupulation performance, etc. ### Testing File Template -``` +```dart import 'dart:ui'; import 'package:flutter/foundation.dart'; @@ -41,7 +51,6 @@ import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const String componentName = 'ENTER_COMPONENT_NAME (e.g. ZetaButton)'; const String parentFolder = 'ENTER_PARENT_FOLDER (e.g. button)'; const goldenFile = GoldenFiles(component: parentFolder); @@ -49,8 +58,8 @@ void main() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - group('$componentName Accessibility Tests', () {}); - group('$componentName Content Tests', () { + group('Accessibility Tests', () {}); + group('Content Tests', () { final debugFillProperties = { '': '', }; @@ -59,13 +68,13 @@ void main() { debugFillProperties, ); }); - group('$componentName Dimensions Tests', () {}); - group('$componentName Styling Tests', () {}); - group('$componentName Interaction Tests', () {}); - group('$componentName Golden Tests', () { + group('Dimensions Tests', () {}); + group('Styling Tests', () {}); + group('Interaction Tests', () {}); + group('Golden Tests', () { goldenTest(goldenFile, widget, widgetType, 'PNG_FILE_NAME'); }); - group('$componentName Performance Tests', () {}); + group('Performance Tests', () {}); } ``` diff --git a/test/scripts/output/test_table.md b/test/scripts/output/test_table.md new file mode 100644 index 00000000..06d85966 --- /dev/null +++ b/test/scripts/output/test_table.md @@ -0,0 +1,26 @@ +| Component | Accessibility | Content | Dimensions | Styling | Interaction | Golden | Performance | Unorganised | Total Tests | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Accordion | 0 | 2 | 0 | 1 | 2 | 0 | 0 | 0 | 5 | +| Avatar | 1 | 3 | 6 | 5 | 0 | 6 | 0 | 0 | 21 | +| Indicator | 0 | 7 | 0 | 0 | 0 | 5 | 0 | 0 | 12 | +| Label | 0 | 8 | 0 | 0 | 0 | 7 | 0 | 0 | 15 | +| Priority Pill | 0 | 5 | 0 | 0 | 0 | 4 | 0 | 0 | 9 | +| Status Label | 0 | 3 | 0 | 0 | 0 | 2 | 0 | 0 | 5 | +| Tag | 0 | 3 | 0 | 0 | 0 | 2 | 0 | 0 | 5 | +| Banner | 3 | 4 | 0 | 3 | 0 | 1 | 0 | 3 | 14 | +| Button | 0 | 10 | 0 | 2 | 1 | 8 | 0 | 0 | 21 | +| Chat Item | 0 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 10 | +| Checkbox | 0 | 3 | 0 | 0 | 3 | 2 | 0 | 0 | 8 | +| Chip | 0 | 1 | 0 | 0 | 5 | 0 | 0 | 0 | 6 | +| Comms Button | 2 | 7 | 0 | 0 | 0 | 1 | 0 | 0 | 10 | +| Dialpad | 0 | 2 | 0 | 1 | 2 | 3 | 0 | 0 | 8 | +| Fab | 0 | 6 | 0 | 1 | 1 | 4 | 0 | 0 | 12 | +| Icon | 1 | 4 | 1 | 6 | 0 | 0 | 0 | 0 | 12 | +| In Page Banner | 0 | 4 | 0 | 4 | 2 | 4 | 0 | 0 | 14 | +| Password Input | 0 | 4 | 0 | 0 | 0 | 1 | 0 | 0 | 5 | +| Search Bar | 0 | 5 | 0 | 0 | 5 | 5 | 0 | 0 | 15 | +| Slider | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | +| Stepper Input | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 2 | +| Tooltip | 0 | 3 | 1 | 3 | 0 | 4 | 0 | 0 | 11 | +| Extended Top App Bar | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 3 | +| Total Tests | 7 | 95 | 8 | 26 | 23 | 59 | 0 | 6 | 224 | \ No newline at end of file diff --git a/test/scripts/test_counter.dart b/test/scripts/test_counter.dart index 72dafe74..a74a69d3 100644 --- a/test/scripts/test_counter.dart +++ b/test/scripts/test_counter.dart @@ -124,7 +124,7 @@ class TestVisitor extends RecursiveAstVisitor { } } -/// Generates an MDX (Markdown Extended) table representation of the test counts. +/// Generates an MD (Markdown) table representation of the test counts. /// /// The function takes a nested map where the outer map's keys are test group names, /// and the inner map's keys are test names with their corresponding integer counts. @@ -139,7 +139,7 @@ class TestVisitor extends RecursiveAstVisitor { /// ``` /// /// Example output: -/// ```mdx +/// ```md /// | Component | Accessibility | Content | Dimensions | Styling | Interaction | Golden | Performance | Unorganised | Total Tests | /// | ----------- | ------------- | ------- | ---------- | ------- | ----------- | ------ | ----------- | ----------- | ----------- | /// | Banner | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | @@ -151,8 +151,8 @@ class TestVisitor extends RecursiveAstVisitor { /// of test names with their corresponding counts. /// /// Returns: -/// - A string in MDX format representing the test counts in a table with totals. -String generateMDX(Map> testCount) { +/// - A string in MD format representing the test counts in a table with totals. +String generateMD(Map> testCount) { final Map groupTotals = { 'Accessibility': 0, 'Content': 0, @@ -228,14 +228,17 @@ void main() async { final TestGroups testGroups = await parseTestFiles(testFiles); // write test groups to file - await writeJSONToFile('${outputDirectory.path}/test_groups.json', testGroups); + // await writeJSONToFile('${outputDirectory.path}/test_groups.json', testGroups); // count the number of tests in each group final TestCount testCount = countTests(testGroups); // write test counts to file - await writeJSONToFile('${outputDirectory.path}/test_counts.json', testCount); + // await writeJSONToFile('${outputDirectory.path}/test_counts.json', testCount); - // generate MDX table - await writeMDXToFile('${outputDirectory.path}/test_table.mdx', generateMDX(testCount)); + // generate MD table + await writeMDToFile('${outputDirectory.path}/test_table.md', generateMD(testCount)); + + // ignore: avoid_print + print('Test table generated successfully!'); } diff --git a/test/scripts/utils/utils.dart b/test/scripts/utils/utils.dart index bcfc30c3..d5796eb3 100644 --- a/test/scripts/utils/utils.dart +++ b/test/scripts/utils/utils.dart @@ -58,11 +58,7 @@ extension NodeExtension on MethodInvocation { /// Returns: /// A [String] containing the sanitized name of the group. String getGroupName() { - return argumentList.arguments.first - .toString() - .replaceAll("'", '') - .replaceAll(r'$componentName ', '') - .replaceAll(' Tests', ''); + return argumentList.arguments.first.toString().replaceAll("'", '').replaceAll(' Tests', ''); } /// Retrieves and sanitizes the name of the test. @@ -185,16 +181,16 @@ Future writeJSONToFile(String path, dynamic content) async { await outputFileGroups.writeAsString(jsonOutputGroups); } -/// Writes the given MDX data to a file at the specified path. +/// Writes the given MD data to a file at the specified path. /// -/// This function asynchronously writes the provided MDX data to a file +/// This function asynchronously writes the provided MD data to a file /// located at the given path. If the file does not exist, it will be created. /// -/// [path] The file path where the MDX data should be written. -/// [mdxData] The MDX data to write to the file. -Future writeMDXToFile(String path, String mdxData) async { - final mdxFile = File(path); - await mdxFile.writeAsString(mdxData); +/// [path] The file path where the MD data should be written. +/// [mdData] The MD data to write to the file. +Future writeMDToFile(String path, String mdData) async { + final mdFile = File(path); + await mdFile.writeAsString(mdData); } extension ListExtension on List { diff --git a/test/src/components/accordion/accordion_test.dart b/test/src/components/accordion/accordion_test.dart index 7cc78fe4..6ad08458 100644 --- a/test/src/components/accordion/accordion_test.dart +++ b/test/src/components/accordion/accordion_test.dart @@ -1,6 +1,5 @@ import 'dart:ui'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; @@ -9,145 +8,154 @@ import '../../../test_utils/test_app.dart'; import '../../../test_utils/utils.dart'; void main() { - testWidgets('ZetaAccordion expands and collapses correctly', (WidgetTester tester) async { - await tester.pumpWidget( - const TestApp( - home: ZetaAccordion( - title: 'Accordion Title', - child: Text('Accordion Content'), - ), + group('Accessibility Tests', () {}); + group('Content Tests', () { + final debugFillProperties = { + 'title': '"Title"', + 'rounded': 'null', + 'contained': 'false', + 'isOpen': 'false', + }; + debugFillPropertiesTest( + const ZetaAccordion( + title: 'Title', ), + debugFillProperties, ); - // Verify that the accordion is initially collapsed - final Finder accordionContent = find.byType(SizeTransition); - expect(accordionContent, findsOneWidget); - - final SizeTransition sizeTransition = tester.widget(accordionContent); - expect(sizeTransition.sizeFactor.value, 0); - - // Tap on the accordion to expand it - await tester.tap(find.text('Accordion Title')); - await tester.pumpAndSettle(); - - // Verify that the accordion is now expanded - expect(sizeTransition.sizeFactor.value, 1); - - // Tap on the accordion again to collapse it - await tester.tap(find.text('Accordion Title')); - await tester.pumpAndSettle(); - - expect(sizeTransition.sizeFactor.value, 0); - }); - - testWidgets('ZetaAccordion changes isOpen property correctly', (WidgetTester tester) async { - bool isOpen = false; - StateSetter? setState; + testWidgets('Programatically change child', (WidgetTester tester) async { + Widget? child = const Text('Text 1'); + StateSetter? setState; - await tester.pumpWidget( - TestApp( - home: StatefulBuilder( + await tester.pumpWidget( + StatefulBuilder( builder: (context, setState2) { setState = setState2; - return ZetaAccordion( - title: 'Accordion Title', - isOpen: isOpen, - child: const Text('Accordion Content'), + return TestApp( + home: ZetaAccordion( + title: 'Accordion Title', + child: child, + ), ); }, ), - ), - ); - - // Verify that the accordion is initially closed - final Finder accordionContent = find.byType(SizeTransition); - expect(accordionContent, findsOneWidget); - - final SizeTransition sizeTransition = tester.widget(accordionContent); - expect(sizeTransition.sizeFactor.value, 0); - - // Change isOpen property to true - setState?.call(() => isOpen = true); - - await tester.pumpAndSettle(); - - // Verify that the accordion is now open - expect(sizeTransition.sizeFactor.value, 1); - - // Change isOpen property to false - setState?.call(() => isOpen = false); + ); + + final Finder accordionContent = find.text('Text 1'); + expect(accordionContent, findsOneWidget); + setState?.call(() => child = null); + await tester.pumpAndSettle(); + expect(accordionContent, findsNothing); + }); + }); + group('Dimensions Tests', () {}); + group('Styling Tests', () { + testWidgets('ZetaAccordion changes color on hover', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaAccordion( + title: 'Accordion Title', + child: Text('Accordion Content'), + ), + ), + ); + + final textButtonFinder = find.byType(TextButton); + final textButton = tester.widget(textButtonFinder); + 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(ZetaAccordion))); + await tester.pumpAndSettle(); + // Verify that the textButton color matches the hover color + expect( + textButton.style!.overlayColor?.resolve({WidgetState.hovered}), + ZetaColorBase.cool.shade20, + ); + expect( + textButton.style!.overlayColor?.resolve({WidgetState.focused}), + Colors.transparent, + ); + expect(textButton.style!.side?.resolve({WidgetState.focused})?.color, ZetaColorBase.blue.shade50); + }); + }); + group('Interaction Tests', () { + testWidgets('ZetaAccordion expands and collapses correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaAccordion( + title: 'Accordion Title', + child: Text('Accordion Content'), + ), + ), + ); + + // Verify that the accordion is initially collapsed + final Finder accordionContent = find.byType(SizeTransition); + expect(accordionContent, findsOneWidget); + + final SizeTransition sizeTransition = tester.widget(accordionContent); + expect(sizeTransition.sizeFactor.value, 0); + + // Tap on the accordion to expand it + await tester.tap(find.text('Accordion Title')); + await tester.pumpAndSettle(); + + // Verify that the accordion is now expanded + expect(sizeTransition.sizeFactor.value, 1); + + // Tap on the accordion again to collapse it + await tester.tap(find.text('Accordion Title')); + await tester.pumpAndSettle(); + + expect(sizeTransition.sizeFactor.value, 0); + }); + + testWidgets('ZetaAccordion changes isOpen property correctly', (WidgetTester tester) async { + bool isOpen = false; + StateSetter? setState; + + await tester.pumpWidget( + TestApp( + home: StatefulBuilder( + builder: (context, setState2) { + setState = setState2; + return ZetaAccordion( + title: 'Accordion Title', + isOpen: isOpen, + child: const Text('Accordion Content'), + ); + }, + ), + ), + ); - await tester.pumpAndSettle(); + // Verify that the accordion is initially closed + final Finder accordionContent = find.byType(SizeTransition); + expect(accordionContent, findsOneWidget); - // Verify that the accordion is now closed - expect(sizeTransition.sizeFactor.value, 0); - }); + final SizeTransition sizeTransition = tester.widget(accordionContent); + expect(sizeTransition.sizeFactor.value, 0); - testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { - final diagnostics = DiagnosticPropertiesBuilder(); - const ZetaAccordion( - title: 'Title', - ).debugFillProperties(diagnostics); + // Change isOpen property to true + setState?.call(() => isOpen = true); - expect(diagnostics.finder('title'), '"Title"'); - expect(diagnostics.finder('rounded'), 'null'); - expect(diagnostics.finder('contained'), 'false'); - expect(diagnostics.finder('isOpen'), 'false'); - }); + await tester.pumpAndSettle(); - testWidgets('Programatically change child', (WidgetTester tester) async { - Widget? child = const Text('Text 1'); - StateSetter? setState; - - await tester.pumpWidget( - StatefulBuilder( - builder: (context, setState2) { - setState = setState2; - return TestApp( - home: ZetaAccordion( - title: 'Accordion Title', - child: child, - ), - ); - }, - ), - ); + // Verify that the accordion is now open + expect(sizeTransition.sizeFactor.value, 1); - final Finder accordionContent = find.text('Text 1'); - expect(accordionContent, findsOneWidget); - setState?.call(() => child = null); - await tester.pumpAndSettle(); - expect(accordionContent, findsNothing); - }); + // Change isOpen property to false + setState?.call(() => isOpen = false); - testWidgets('ZetaAccordion changes color on hover', (WidgetTester tester) async { - await tester.pumpWidget( - const TestApp( - home: ZetaAccordion( - title: 'Accordion Title', - child: Text('Accordion Content'), - ), - ), - ); + await tester.pumpAndSettle(); - final textButtonFinder = find.byType(TextButton); - final textButton = tester.widget(textButtonFinder); - 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(ZetaAccordion))); - await tester.pumpAndSettle(); - // Verify that the textButton color matches the hover color - expect( - textButton.style!.overlayColor?.resolve({WidgetState.hovered}), - ZetaColorBase.cool.shade20, - ); - expect( - textButton.style!.overlayColor?.resolve({WidgetState.focused}), - Colors.transparent, - ); - expect(textButton.style!.side?.resolve({WidgetState.focused})?.color, ZetaColorBase.blue.shade50); + // Verify that the accordion is now closed + expect(sizeTransition.sizeFactor.value, 0); + }); }); + group('Golden Tests', () {}); + group('Performance Tests', () {}); } diff --git a/test/src/components/avatar/avatar_test.dart b/test/src/components/avatar/avatar_test.dart index 9376f890..7e02d661 100644 --- a/test/src/components/avatar/avatar_test.dart +++ b/test/src/components/avatar/avatar_test.dart @@ -9,13 +9,14 @@ import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const goldenFile = GoldenFiles(component: 'avatar'); + const String parentFolder = 'avatar'; + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - group('ZetaAvatar Accessibility Tests', () { + group('Accessibility Tests', () { testWidgets('ZetaAvatar meets accessibility requirements', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); await tester.pumpWidget( @@ -43,7 +44,24 @@ void main() { }); }); - group('ZetaAvatar Content Tests', () { + group('Content Tests', () { + final debugFillProperties = { + 'size': 'ZetaAvatarSize.xl', + 'name': 'null', + 'specialStatus': 'null', + 'badge': 'null', + 'backgroundColor': 'null', + 'statusColor': 'null', + 'semanticUpperBadgeValue': '"upperBadge"', + 'semanticValue': '"avatar"', + 'semanticLowerBadgeValue': '"lowerBadge"', + 'initialTextStyle': 'null', + }; + debugFillPropertiesTest( + const ZetaAvatar(), + debugFillProperties, + ); + const names = [ 'John Doe', 'Jane Doe', @@ -96,7 +114,7 @@ void main() { } }); - group('ZetaAvatar Dimensions Tests', () { + group('Dimensions Tests', () { for (final size in ZetaAvatarSize.values) { testWidgets( 'ZetaAvatar size $size with upper badge', @@ -211,7 +229,7 @@ void main() { } }); - group('ZetaAvatar Styling Tests', () { + group('Styling Tests', () { for (final size in ZetaAvatarSize.values) { testWidgets('ZetaAvatar with initials $size text size is correct', (WidgetTester tester) async { await tester.pumpWidget( @@ -295,106 +313,65 @@ void main() { } }); - group('ZetaAvatar Interaction Tests', () {}); + group('Interaction Tests', () {}); - group('ZetaAvatar Golden Tests', () { + group('Golden Tests', () { for (final size in ZetaAvatarSize.values) { - testWidgets('ZetaAvatar default ${size.toString().split('.').last}', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaAvatar( - size: size, - ), - ), - ); - - await expectLater( - find.byType(ZetaAvatar), - matchesGoldenFile(goldenFile.getFileUri('avatar_default_${size.toString().split('.').last}')), - ); - }); - - testWidgets('ZetaAvatar with initials', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaAvatar.initials( - initials: 'AB', - size: size, - ), - ), - ); - - await expectLater( - find.byType(ZetaAvatar), - matchesGoldenFile(goldenFile.getFileUri('avatar_initials_${size.toString().split('.').last}')), - ); - }); - - testWidgets('ZetaAvatar with image', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaAvatar.image( - image: Image.file(File('/assets/maxresdefault.jpg')), - size: size, - ), - ), - ); - - await expectLater( - find.byType(ZetaAvatar), - matchesGoldenFile(goldenFile.getFileUri('avatar_image_${size.toString().split('.').last}')), - ); - }); - - testWidgets('ZetaAvatar with fromName', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaAvatar.fromName( - name: 'John Doe', - size: size, - ), - ), - ); - - await expectLater( - find.byType(ZetaAvatar), - matchesGoldenFile(goldenFile.getFileUri('avatar_from_name_${size.toString().split('.').last}')), - ); - }); - - testWidgets('ZetaAvatar with upper badge', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaAvatar( - upperBadge: const ZetaAvatarBadge.notification(value: 3), - size: size, - ), - ), - ); - - await expectLater( - find.byType(ZetaAvatar), - matchesGoldenFile(goldenFile.getFileUri('avatar_upper_badge_${size.toString().split('.').last}')), - ); - }); - - testWidgets('ZetaAvatar with lower badge', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaAvatar( - lowerBadge: const ZetaAvatarBadge.icon(icon: Icons.star), - size: size, - ), - ), - ); - - await expectLater( - find.byType(ZetaAvatar), - matchesGoldenFile(goldenFile.getFileUri('avatar_lower_badge_${size.toString().split('.').last}')), - ); - }); + goldenTest( + goldenFile, + ZetaAvatar( + size: size, + ), + ZetaAvatar, + 'avatar_default_${size.toString().split('.').last}', + ); + goldenTest( + goldenFile, + ZetaAvatar.initials( + initials: 'AB', + size: size, + ), + ZetaAvatar, + 'avatar_initials_${size.toString().split('.').last}', + ); + goldenTest( + goldenFile, + ZetaAvatar.image( + image: Image.file(File('/assets/maxresdefault.jpg')), + size: size, + ), + ZetaAvatar, + 'avatar_image_${size.toString().split('.').last}', + ); + goldenTest( + goldenFile, + ZetaAvatar.fromName( + name: 'John Doe', + size: size, + ), + ZetaAvatar, + 'avatar_from_name_${size.toString().split('.').last}', + ); + goldenTest( + goldenFile, + ZetaAvatar( + upperBadge: const ZetaAvatarBadge.notification(value: 3), + size: size, + ), + ZetaAvatar, + 'avatar_upper_badge_${size.toString().split('.').last}', + ); + goldenTest( + goldenFile, + ZetaAvatar( + lowerBadge: const ZetaAvatarBadge.icon(icon: Icons.star), + size: size, + ), + ZetaAvatar, + 'avatar_lower_badge_${size.toString().split('.').last}', + ); } }); - group('ZetaAvatar Performance Tests', () {}); + group('Performance Tests', () {}); } diff --git a/test/src/components/avatar/golden/avatar_from_name_xs.png b/test/src/components/avatar/golden/avatar_from_name_xs.png index ec8f22c79689751a94985fe9676a84f77eaab318..9f715e2a00a9a72d5d3ee6fc0b97ba178eb5cdf3 100644 GIT binary patch delta 654 zcmV;90&)G<9MT++Kz|MVNklo0ssKq zryu6P;rRRU@zUez{F?v(zA8PPa{&MVE^j_==oYFi%>@7exV(9do3GxorMUnA0GH?3Hvj;TnYXm)u%)>G005U~ z1(N{;6o2!y_6%Ow(p&%lfXkbw4L$X5X)XW&z~#-m4Q;~O?be2NqKV~p|naovghv0rz5007|np4T1LT!GkI{Qv-fyYT$z zzn|B+g=(5?zXAY&d-2Vq|HBHmP^~d0006L#SAS~{58+viax4G z#sB~Sw&%O2FP;P83WYFD;sXEx?$r;Ee?ROldqdTJcIE>B0PfjOPhsrl^XJQPjMMUK zgUr(ypM(bh0NlNI9|Qjy5T|8~<7td#8RM-UnOi_@%07*qoM6N<$f{l delta 678 zcmca4b6sYFLp>j(r;B4q#hkZyqw{XNF*saQT)HGWbNBzPTkd*^c)n>DQf^E>?zhC- z$%_3$yJEEQ{5><+mmhw(p{LwJKY7l%WS!Fz`hOpr9GkJsuRUf&i?HY$5aE(EkFJKv&pgcyX*Y~85nkm+w)GiT-o#Z=XW;Cdi~@YtG?tCvT@|cB8yq!H01H+y5>5N7H zggCZu={YTtZyO8}YENhUWO+xabc-xEQ0J*~#-RDSx=be@2*;JT7R64fx+tc$799v_5Xif&*%MezW)EdUq3(ARQhu;Fcj>6X1J|T zdTMS2kp2IeuI=~yOg*4Ozn)#rd)H~}(wz(p4A;%q7k<9#z0E`%X!0+a-#`P6K2H;q zg$k@`0IHE>1UfK<1!&>~K?Mc|hqQUu<{dN#s;CF)d6wzy|2KLT(A5RCpYxn&Zz$Wn zH65ty&ZRv|Z6l~ENH-+Ahm(O}&EuQ54j=!Z<7O!a zw130CbVj3E&gSTM$Fz+ zz>srz_xpvjmK&z6mIMZ%{s%4x1_lM?&8p1e+>-@Z^2Po$Gg?(loO^Z0Z=f3(JYD@< J);T3K0RZ^wBUS(a diff --git a/test/src/components/avatar/golden/avatar_initials_xs.png b/test/src/components/avatar/golden/avatar_initials_xs.png index ec8f22c79689751a94985fe9676a84f77eaab318..9f715e2a00a9a72d5d3ee6fc0b97ba178eb5cdf3 100644 GIT binary patch delta 654 zcmV;90&)G<9MT++Kz|MVNklo0ssKq zryu6P;rRRU@zUez{F?v(zA8PPa{&MVE^j_==oYFi%>@7exV(9do3GxorMUnA0GH?3Hvj;TnYXm)u%)>G005U~ z1(N{;6o2!y_6%Ow(p&%lfXkbw4L$X5X)XW&z~#-m4Q;~O?be2NqKV~p|naovghv0rz5007|np4T1LT!GkI{Qv-fyYT$z zzn|B+g=(5?zXAY&d-2Vq|HBHmP^~d0006L#SAS~{58+viax4G z#sB~Sw&%O2FP;P83WYFD;sXEx?$r;Ee?ROldqdTJcIE>B0PfjOPhsrl^XJQPjMMUK zgUr(ypM(bh0NlNI9|Qjy5T|8~<7td#8RM-UnOi_@%07*qoM6N<$f{l delta 678 zcmca4b6sYFLp>j(r;B4q#hkZyqw{XNF*saQT)HGWbNBzPTkd*^c)n>DQf^E>?zhC- z$%_3$yJEEQ{5><+mmhw(p{LwJKY7l%WS!Fz`hOpr9GkJsuRUf&i?HY$5aE(EkFJKv&pgcyX*Y~85nkm+w)GiT-o#Z=XW;Cdi~@YtG?tCvT@|cB8yq!H01H+y5>5N7H zggCZu={YTtZyO8}YENhUWO+xabc-xEQ0J*~#-RDSx=be@2*;JT7R64fx+tc$799v_5Xif&*%MezW)EdUq3(ARQhu;Fcj>6X1J|T zdTMS2kp2IeuI=~yOg*4Ozn)#rd)H~}(wz(p4A;%q7k<9#z0E`%X!0+a-#`P6K2H;q zg$k@`0IHE>1UfK<1!&>~K?Mc|hqQUu<{dN#s;CF)d6wzy|2KLT(A5RCpYxn&Zz$Wn zH65ty&ZRv|Z6l~ENH-+Ahm(O}&EuQ54j=!Z<7O!a zw130CbVj3E&gSTM$Fz+ zz>srz_xpvjmK&z6mIMZ%{s%4x1_lM?&8p1e+>-@Z^2Po$Gg?(loO^Z0Z=f3(JYD@< J);T3K0RZ^wBUS(a diff --git a/test/src/components/badge/golden/indicator_default.png b/test/src/components/badge/golden/indicator_default.png index 291fd461ab4aae8e99d280eb7b52d4f9637da77b..51235d00ae515dbfc88c380e460bc7b18d4c3405 100644 GIT binary patch literal 4192 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 bPz|NPG>qlqYucFT2L$APoR>A6`E}EO0>jYz diff --git a/test/src/components/badge/golden/indicator_icon_values.png b/test/src/components/badge/golden/indicator_icon_values.png index 96e73834b042dec5f2f598fb4ceda7e9902bbd26..9c17abf9476753af2fae9342457b12aed984a7c7 100644 GIT binary patch literal 3865 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr0O^r;B4q#hkZy3|HSxmp$I zj(5M*mP}sIy?Xs&ae)^?Ru(rGE}m>6!qlqJ^aqW{fECEi@H4{>QCE}`RBjQ z`TU1}|JTz$?ayzznN#+eQ~ma3OS^k#PJS!#+qT@;cyrv_^HqE8+r|0Q_TG|bU}*S% zv`9{2_uVtii`$F;AN(jRzoY7DfnNMBi_<~991IM5G^U+R+Z;VD_1~xM)thhTq^CW5 z%O6(!`di+;-yd@RzB;|S^6@XD{C!2+>?|VAn(wbGE!%xpxL$^V;lX5(&gz0M8Sn1e z&)O}o`+4?=Bxd^7G!?P6kGx(7k)pcllip-NV3eKzu{g z`tahaT{ESxe|vuZ{r}gdYtCDD*clf;zV+`+jSc(jP!lzvW0WFf*L-{a@XVh-)zv_+ zrpwp;V)(WE@0(*c7Uu8^ZD2IZJ!svU-SPpv;Dt!2W$S{UcOo9 zbj+@b2|S#@P?z}j?c22D;V=Jv+Ilx8qW0gxK5h2Xe|~Mr_?q?qrY|=GgU$7S$__wB{EPoK?<i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr0Olr;B4q#hkZy47;C(OCEUmz5e;Vn{!X5IiGRkJR*|ls*^NbZ-Lmd zPL63!b*$Rj2St9x=CnG@N=Qs?nG!ZjRe-Z)hKuU%nV#>?yxA&IE_(Jz?}PlCKWy$c z7VG8zemw7gEPqo?mzi+(f@A!GE`~8`4`S`Q?`^x1s;`e+zvF3Z&%?%6;2by#LKKxjg zmKL{bD4{{6M*+uyy}``JqjXy)FCyPX~P_tw5QEKGm@vXcSmop*PC zEqY$EKK}OX<9+AXC)-vP98~lF$i%=f|6tYT$n!UEmd&e7%D-3nSN{5jy5Ebl#pSA= zb?%+H)Asl5QUfIhh7Xk+@5R4A>u&t{@cQ-7!q=bQQ(sa&O@9C1mF;yO&iss*mu3VS zR>QXXaI^PjozpdsPcQ%cc74vj$Bpl6a_jf~ymC|Lv{C!?@6Ce1fI8oOHEZjgTCeZw z=iffN^y?A7^}09upC1)JzrJOiU5&x&sr?QN3=P%WbGF~Ud*)1a{Z2~)QDR|lpoC`6!ok2WK~RB#!NIKo zC?E-vP8k&(4Uo~qFq#oYONP->akNGltqn(O!_nGsv^E?Lwc)dTZk~((!-~_U15-4E Mr>mdKI;Vst0FF2ZVE_OC diff --git a/test/src/components/badge/golden/indicator_notification_default.png b/test/src/components/badge/golden/indicator_notification_default.png index 291fd461ab4aae8e99d280eb7b52d4f9637da77b..51235d00ae515dbfc88c380e460bc7b18d4c3405 100644 GIT binary patch literal 4192 zcmeHJ?N3uz7(Wy?I}(MM3C1!YGt&$DmY?Uk1%y3H(|R%Cz$%C(yv8g6MER;;D%0NaX4Z7H=tTka0oKd}!zU!I)joSf%* ze$UJAT-dcUZ+YyRSOCEC{4aC&01%xD0HcWcKGa+no~?!=3fq&n4cr+@nuMDeY+L?5 zCR`27qvrvLkIm2Bx=-9PZ8geX&VMuD746?4GMuVHh1(0BoR8Ul?USf2cVpJpJ?B3d zt$9qQ?*2fL%3&U1Z2fSYj=#Lh`)cE{go93KTh^4mPdQB0VlwtrR| zpM0&iI6Pd4(y!UBnPxiErS8w_D57-I5`{_Hrqn|@XkU0mm2qVHBaO}>?{6c^=z2Q@ zYmPrHWxTY74SincBt@msQ(VmZtC2EqD8UE5_shsk6*<>orb9si>KqDQa?TwOA6Bve z5XWv*FJ@a$*ng0l>>+DQ|ExEKm4YAwb%P`;L(*^{gAP;FsKmF}QrY#^K}3Nwx1QFZ z&hhb@!({zt1W{#>yg-iMY_@W1$FEqmO`1Ru=PhXw1O`ROWP^d%6aWwQmM!6Dr*+yT z>}D)>BA5}jy(w^$z3me>HU2o98!Qpiqdm3RMk}ZPU34@61!_lSceBAfPgSxLaStK- z2)Y(*3S#q8WvhAGd8(t;>TadaB>>MF5Lg(3w76uxc8XQ=|?r;O)G_@ubm?vTQ9$S&V?Tn zl0gfCV5`{BF6L)GTk!n#rpns#C%F(xYdm|rC@Gm5E5T2`V~=#?@DwbSqca(XFYehW zK-;tu9`w_Fjh~&T49@l@a~DQ$2?Dy3>-r;^Mq5tL5C+4h`*^4tqK>*`(5kzDYeL9dg6<8QuO8hG+<0y*$SLB3>{Zj_*vvkaj zR8bDHp<;-fF%jX$6*&u6inVji9rPo$(VfWmlpb$axZFubL#NCIAF%|l0D{o;jT+)? z_xS>gZ~YpLCP%^TmZP1s&sRYx#Z|1-!Y+%9V`($K3)Q4&h%k-C!0SWqeVy*|lx~^w z>yqlmQsK=QTju8w+&fKzUtr391h5S^GhzUE#Eb_ZbuB!LC?kMGiXqYnk;xF5iV;SL ouwjG^BWxI9!~ZiIhS=Xuf3@}E$vqF@&mQD&-2G4x0$f6XaYV*Ckl6MF=yupg0^G zobc)&0Ej+XkaxJaSz#6&zhk2|D^Y}YIwT0&A zzj`xq_lJLGOlPRCA2{&7H$wPUc7&fGyHQbwI1Tj7k3KkDSa@On`}%J-wyMV-WZF*I zkiQsd9_0l-&7^B=O`VgO7=IvQF{xYuaeuwG<*s;58oD?A1ptDkJKHv3uy(wX#-?Z+ zTUpO8iT)NU14avVu&aqB1Yq2>lt`$zJl3<9w4cBWHu{z~lIh({f}D@f!CZ?PMQJUR1MO_3>Ddo(X~E>bbpuMyE& znqzW2mfL%|XS25+!5vlDf%H2PG23TW$rF(lhtiplL?rg8Ml%NJ(jFf#M?o89O{Bd6 zfHASF$2!dzMyP(5OX;0Sscju!?&p!Fx>Hc7HMOU#ygUqm?D?+evNHix3Uyqr<*V)X zHNIyP{SoXqR--hOBWHP=&tn&you*4FEs8dJKI@*vFbGm+-F3{GSFe;}n5H}J^`H{x#uI$DNi`Q0pk8c7}8TV&#cS}8_=)iY>Z3X_C3Q{ zi!()i5?`>iPpGedO*-!(1Ovd~B*(UEjQj-)H5uwq`36beb(xKAcYF84o|SZH=&I`> zmkte`G~OQ!0wAejN_>vouj?Zngiq=26IFfBig>k9!_P~_19}P1jKF?hEmSf^YF(L0 z$l&dGt*(R(qdvQFs#LDiq$^OKN~+&TLG3nw2GULIwHC%g5afF*gxy{`-L+i>X9iZ2 zW7bReaL}AQI?&9Rz0MyORAH-4vvKhJ}EYEQSETHO;Z~!`Y?*rgaz5v5; zBc2ev81RpPcLuyG;);N)A+CnF8scjBe^WzObX90TWcfD!J&Ze0aD6e*soc$9kBSXXY;}g#%u-{B#U^wvm`D9Z@XZ9RJ28IWJH&3o-)MneO&%p43 aZ}SF5e;xr1u8ma;K;Y@>=d#Wzp$P!dq!}6j delta 70 zcmeB|>6e*soc+;P28M?3CnuguU_Tkez;NLA%gLsU&g?0g3=9wcZkb%qsLi%VkAdNX a-sTOA{yYL*hFhu`fWXt$&t;ucLK6TA_ZoQs diff --git a/test/src/components/badge/golden/label_dark.png b/test/src/components/badge/golden/label_dark.png index 56650a3549980dd8fac82044a82968ea850fb4b9..a694b52a9d7014a191797f7b3032d1cd3065a51a 100644 GIT binary patch literal 3580 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sA-v}i(^Q|oVR!OcgdDAv^~5(FXvEGci@wZFy<~LjVRVNI)3w|J6tbi zPBhz_pt`e}ql$Z*&;{-Q>x~8i-7IGH9WttFEv9f*8K3cJOhK!=9TQgKzi-m03;l4-(eTnJ*cwK3{sax}Wdf&J&+5zWed>TlwSH@9pLN zzkgpgkD1|s)D2Kj2KNJmdKI;Vst0475+zyJUM literal 3677 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq{?R)5S5QV$Rz;ySrmj8QLG_Z{GacD~V@f?>a8QT#>ab+TBO%4_rGo zW!trBNs8NEvPd61bV_aNg5J5)ymsbDT_WAFRSI7O2^+r!$K98B< z08_-Rny+hr-kvS*ySJG$_x(R3yMLeN7UtLGztUu2_;X(NR?W{VSEOg$*zCW@B<@B< z?khq@8|M4{jaBg`}g~S{XL7Vc7Ahe zQtm(RK3{Ml!}7)Zg1Xn&EQ+h1|J#~g&%mG|Gph<1^cyT#I2afv2r4izIJh+c1tb}n z7#LCr2&R>NKDTw_wxTEhxaaTNZu)Re<{Znt#$Q`b-rZB1`-&432F~Jo`f&vx@}IB& zWB8SYfkEuR!u+~_Ij?wq-rRY2^W*Px{K4lL7!nwD?%&w`1`-QJHfbUx;0j6Y9USa)2@;Y! zu(k0P%q66@u&~m~G~1>S!9QS;fQ<&C)n-_k6sgjALe4u2`#jZQ_?Y<(=PS+&hnZ=f z=jG+YX&m1@7;m0UL{f4bbed9d~ztmw-1 zW&H5ybWFAZ004XT$?;E{e;v5>bd1YifBXsl{C$keU+u8|w;j*Fm%ZnOFH(%K_%7JPQeThIXj*uTve z-}vFl7@vP@9{>Qbf76rur^~&Y2W!v4ioU$Qj2|DJj>$Fv0ASCid7hV-52tZ__h7tv zHjR^aYmY?b@MsxVx69bx{xB{au00I^0O!Fp&$IvD00009Pr(ASaRa9cv+x2-0T%N& Xm77~){^mqB00000NkvXXu0mjfs$kd& delta 549 zcmaDRy-sF=WBmXL}Ewc4T>jaLhMBxpa3Ln>R*s@{6 zHve4hBUV$lwwAJR&kkYao}Ho4>LR!(Ad(&gUVU?4wy$&U=h>Fqch9`*-T&;%xl5NB>KO`T*3|uZ_VC&3 zet#WXcJ8XLhtlkRetWpahK+$iX5-rI_&i-(X71l_+ppgFRk!tv1W=UucKx3}Uygl# zv)lN*P5B3In;+@Vo&>JDKi{(Q)q~gRX?gMUZ`#=X_$7XCM&-Y!&SymK9B!^QR`-{` z>r?;V?(=nh|Gxi!GEMXAc39T`U}a!+{G)kTj!Z$8btd5X69^mz%D%g^OZ zlVfCPNKSaYbbF4?%*v0s=NTCo-1A@mJujyZ43z4phtkf~D>yJPSX8b4TD5m)WvS_P zz4~j}x}Wt>60#7`8(HSh*P(HYYRX ga!+E)6FalNkfmrhLwLqdpwk&VUHx3vIVCg!0Js?ixc~qF diff --git a/test/src/components/badge/golden/label_negative.png b/test/src/components/badge/golden/label_negative.png index efd6692b2a77176498a8310b0528ad7245a17308..7f57efd07505d2280d982a1212b44b9a1ee238ce 100644 GIT binary patch delta 466 zcmV;@0WJRB9Pk^EKz|LINkl z_by(VdIu9a^3exl=lb3li^bGa004Nw&Hni0v(=N|ZahD+`{#|Z_0GQ0ssJH=JVsPO}&9NusD8f>InbEa=BbhJpup#*bM#!3$sB31`Ctm0_+ydzw^Ibfxj}cVgLXD07*qo IM6N<$g2L44;{X5v delta 556 zcmV+{0@MBQ8{Hg`Kz|MgNkloKHuK>^6PQ!%m-us!;fR@`k#a4uzuqB*z@X3u(SMz93ZBEF_gVCHxbxc|_gQq) z=J^Zb@~P8zZ~uM6Z+`#)xc_ET4*&oFmWKcV001%q1d})eEq|=d4m>!vZrr?k;irAa zCY&>k%co9HJz;XI2lh`r0RVs{Fni$f$EM!Ef55u;0RRAj*}-?;nR){g+x5&-WA7_3 zPCWwvfF&^7_xkB^=<|=q?iZdLvxB~dvrN`c93T7Nesdi9;*&8}SErr>0KgJhna}6j uQ;z@u0G5M)!2trZVFGyzlQ08h7V{6ipnutE4==O;0000i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sHw%%#WAE}&fD9LS<&GPZ5LOkpHvel7dSEP60b>i#@-GgC-DnqQ_6Tx zvdzl$oF?p~)U0zd==j`7=KAvsirXG}{o(Jm+mXNVZeIWF-FGAI=N+r7x92H6!Q&tqgbAQ`bP-~9jY z2j}hUNNYU)A?_2cOsP+bLN;zu@2U>+Ah*86T7QU-zr#($9YR|C0On z*8YC*`Ka-nUvq>1x5wY#Be_2Auf;Km}t{qg(OJ->IbCv0{1$4^K3i_bGK zJmAsUcKdB%@pkFgx6j^q|Ni*HiaFK76Q3S6S*I8N_}9zopr0IUgFhyVZp literal 3625 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sOhq&i(^Q|oVT|f^P)o;+Ad}v_sf{f!O2<5{L<-#*$buy!wrg;Y#Ew2 z^P^Ll>Pn0=LDVBkCO=KJic@2=m^TYm3g%e2!+fBabdd1X2SLjl7x zpw5-%1{=?&S--nJ8zLrl`|a-idkbr8ZOvx?t?$tj>)v=b@4Map7`S{%t>= z*W2y;_v-Y=$Ft3=>kq2y$Mwj^?XSy!u0PLSK0N>ayV|-Q!+n3>{VuF3+xGou`1Jcd z;nSyA&(~k~@X@2BnAq68+kNNntFg5E{ioYL-rmN>CQF`y;g9s^mF&RKZFg<}l9G%} z3=AnOK>H>LDljlOxDgOcv;X_&^t;6Ev#Z#Je}DM%(xSG?=qnEc!@uXxx{qGGBK?Cu zLgx+-GsASBx1LEEqO_t=hYL_x+yRSMT1)yU*L7EZcYV zV$B>G{o_AxPBxr>e%U-`h69W<)?Uv(%E-W=V)KKaB-at|k5QGx#P(4n5- zvKdV_qseBp<|499Fn{i5H%_-F8R^>bP0l+XkK`G;3v diff --git a/test/src/components/badge/golden/label_positive.png b/test/src/components/badge/golden/label_positive.png index 5df40805b9263022208ce20bb1730ddb95241b4d..537944fa46dcc277d0dbcf31358783fa35f42dd8 100644 GIT binary patch delta 450 zcmV;z0X_cZ9QqrOKz|LSNkl`0Iofmon000|cescQP)Eig>C;t5b03hJdKa-IHB7d3r*2TA{-oP5zy7OTC>VE_OC07*qoM6N<$f(Yf=od5s; delta 605 zcmew*{ZwXxLp>kAr;B4q#hkZy4t7r|WjOxueqQ#XZ5g>I!rZvsDx#X0So=M%Td{CD zPXE{X>CIB-oa6r(^JT4W986x`6!7Viql*yNS*0y8KFP~7c5~h}}@UyX)7ZAD`y(A5Z4)4)$kccu=2L`1sND;QWN7`+xr2{qd+h zfB8H{h68#VO1@s7-5(QSzsHR4&CT79rEPu`n|*s_`bUDi+9?^+}~54E)y9y^YNdzg(*K@%Fh39E`M+S|0jD3Ki``R zi1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr00lr;B4q#hkY{H+r%-N;F)w+!^pt>cW95a!1(Kv1J<=T^3q%P%7tu znh)<5#>dRI#meCyqYLhHwN>e+{N_7QtNzwR%r`)l`If`Q>rF?%sT&>o%R zzg{1@dRb!ju0ckGWzYE#9uDu3-P;RFXUg0I(em#2T`FOv-g`-o&w2N0%5!m==@O1TaS?83{1OOh1!omOm literal 3457 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|s7c$?#WAE}&f8mtSt5lpZ4cvB1Vu%6b98NOU0SNp{()nj;@qig)jgg% z7B$atQi|wmn&`4wtZd^=#V4@^^Owu~J5zW0?CCe>cDJv-y5TEd-@0cHUVLAc&cN_t z{_f2$85tN3d=mgV$RX|fuhzfe#^v&B>YGo?8&_|y{P+CKy}e)0pFcBa?zuJVuG#;8 za`epN<=keh=L;(BHkbeZW>)*(==0ahn|v7<3WEQ$oVYzXRe#fcyYn`6W!2B?=bnF~ zS0Olm=FbnG-oIb-`t0$U&(~kKkGDB|DI;>%%Qq#@eiYnPV_?{!S$~j;fx(E0g@NG& zhX4bEg0cexLqiV(&}oDOH&_2X^!u;xnHy^!ANJJ;>U#Ttn}dO2f*{C4ZVe0!3^V6; zcXwZ1<_=`uJ}6F_>BPHZR3$O7H5y8zp+rU|A5Au+$!0X!FpyF&j%JzBEHj#AMzai& g&FJA$T+}msH@BWDa^iR=u({9R>FVdQ&MBb@01X%`HUIzs diff --git a/test/src/components/badge/golden/label_warning.png b/test/src/components/badge/golden/label_warning.png index b4091b2c8114cc7c128e58ea44c0f5fddf6cb811..2342243e61dc1aca18a18e4dd83733d5bbe531d9 100644 GIT binary patch literal 3571 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sA;aJi(^Q|oVT|PyJJEb+8!3CZ$5i$#RL(yEyceTI;Q<&-srz@{{geP zu>xB*Zn$=@;|IUWTd|GeN^7DyRxj9*U1pHvW~C#XpzT%rw4ibN+Nm zoe|$WJE_Y@UrO9QeXuT~?_J;1Z+k!f`SDMpefza}%nS$QW^7CEpWdv_fBoLUu4&{By^{?f-l{q+jvj-`tNczweK|`Lq9Cb?I{3 z$COS|qHU-RSEj|Xq->%Y$U_b1b5|9^{V*7qe|~DGXsMpBNGEd3JV7V z!vsNKcsjT>0D}%m@Me#+dv^W0TZ!H-Z z-ZW;;d!G6h=i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq{?F)5S5QV$Rz;d%I#%8IC{v{wA;ESg)drZ;#R<#;skMdzhsi9dC<> znZ-4kCoJB&vonIt(fW47#t(}HlvFMrS|D_TW$u}G8-p*M75mqhGJpB(hjVBCPh2Jc z*>G*x>dIZ!wK@0qSS{NdoxJ8fe_z|rBRdV}@3i=)A0zkp#>u7685jy!d|q3}pS^8v z7gfd1JRX{)W?_9ffzFl+`GxzLwe=U~X-xpa0^q2dAb6d)PPqePv zyIqd|JO4+P+m|EWpO>C*zs~+jy-}N#J;&?aSNiels&1s6RjbA0tCU4+A3ugAo%81H%ap z0R{#IWd{brfsoUd!k%+PTB&b2+i?_84pAwOe&RoP2U z1_l|%XmN0O0=fgyzjC_F(>VN`H5 zKt>b8Xhs+<8AeOR(Hdd2HXN-DM{C2;+HeTghX2^~IXiY=x@>s}*u-b>boFyt=akR{ E0DB5^egFUf diff --git a/test/src/components/badge/golden/priority_pill_default.png b/test/src/components/badge/golden/priority_pill_default.png index 3ec6f2a10b12024413243aa1ca650a7d880dea75..6ff50feb40d4126d7ff766e3ec823afef4ea8496 100644 GIT binary patch literal 4329 zcmeHJ?N5_e6u$^jgdrLYL?PCJ3rj?BBD93kPDKI9e4h%~;sn}F8s3Tz)Y8&f4Kfvs zZa@LsxrI4p(y%8jFlYfMU}$N5L+GFs25o`90R;*a`s`8nVSmCt^nSUyC-kJ*2^pvI)m`c=yywa+Lc^@^e9sY}mrN0X0V^*&D;{HT^6Q#7H}btRR= ze6xSQTSCd{YbQ1yYB<#OIJzXselWdh=awTUCb%9u_q_TpF6L2OB?Th=MCw;X>G`jJ zCM6wKM}^s>mZvPq2)?O^tx>Ks-?Qud*=?N;hXaz{Fp1{zOIQp)$T546K93Y$!yUwm_!$w~jouy+o+h~I*3)VmZJXOLP>--Uq$(b%9k1?>Et;bR&kMf`Atjiy@y4D^_A;qr_p}ip&wHGzg;5DX}PP*%#|Q?aAXDK>PZF!yNn!dcPK{#=0s&;*yHQ?^M9o*ic$m` z`xvo#l$XUc9Fq&BV_4Y0Ui?B+bc#9LBf_>AqAZAZiO(eGt8EqbehRG5Oa~yZyc%H@ z608YrIY#bwDRLiX7-v!ie~#`J4Tit`g&?`Ha7FErpL zy->P8DX-h(0fp2k(SU=Sb^x%iyd_@};3u{}&h#m6BzbHEV4F;@Vdh5|uV*dRzk@`2 zQt0LhIPzSJIg6)mAv26z$kLj+K(>c>6N>#{RpR|Qv?Xf-WQoDLgd+2kZozA12*D97)RyKD&vzH>O;;P{w3EJ@w<*&U1@b;;*y`A~Q>}L-{^$Qwy z%VI&PDx~6XSHRgkb1Ucp(Q@0SFnLGgWD-d-p&-XRRM^R6XJmR>e(!__>&xL`^@l>N z{5+)m8kA zFQ%prvowMF^0~XpcuX&MmZBz+RMk@Z4#165=cdXCpW#gL5i66~U>7PBnC@ ip;Hb27iuU+TzX26{8D;m`WpI=2l0oF#?&9UaP=SR14D5D literal 4502 zcmeHJYg1E47~Y@=N-0_`v|(ExjPPvz0s0At7Vr#t=B?J*pP;Nq|Bmt6GdqItg z)(Qfel=^|0f*>3NB7{Ioq)0f&juH~1gaS<>=0ZFWAmJoEwtu1@vLAM5pWU5(=iT>t zo<03}N|NuY^{W5?zR8~?>;Yg|5C9%ud9NUl+LDvUiGjr0le8T)BO9I)ljW@K$$Pzt zR_>j33V=27$q66r_nw5rbWLtN$;Xz#bG-*_5wiy0QFNcoDr8O2JCKFlmZ7_5yWL1m4R{NH z4YhjDKQ2-5p%@&Ng*U>?tvNFj3)W8C?`-66+S0Os@QQCG2SXu;;qKWLBrv|38XPaO zs|NHByDV4KPvWjT70T1fMz68tl~OE2R}{2gD9@v4qK&ykI4esd;}PCpLX4qhX*^xJ zNv_ZNH|h=uo||KA_EEASEmUDHj)@IS@^h>QAT85BEDVOj7-g0>yvUTWFd`cLLll3m zK#*@x$mJg!3(pjKfcQKfq_fiOu-ZQF5M#Z?*u74(cw1{rJ%c-R1IF%eMES1SYI9wZ zYxX+!tJpbMO?C8X9r&W3;`G*C)8s2+k+(j_r|=1C*p zVpm$6&7CG?2hE}v-eAFHkGn3D!2MJ$&v=?U)v;}%+si*VRJ>7ZpXT|})A(wZM;4^3 zpLdzliX8cHbkU+vgimR4y!>g8ONAlqZc{!BmCi%@9#ce1jf2d|K=q+h#7%d&e_!7E zT2OA|NC=XTHlqb35Y)z*J|c*V^%Hj*geBzCEV66C$-n-a99E5OGj4v7*VJx2*iGu2;ItYDh4x9xv12!{tip`r@mZPgq>qeT%&Q)nF5{EBC0@_P!T&_ z+8I;G!H3Ie>=7p-oj$V`GaRY3EIxK!lV*~6#@0Bo#-g=W)uk^;qPmil^Hd)ILi_*a zG6X$ixgr_(80FfC8uscMSG9JU$7EN|)%D=<`r^YcD5s7tRgD5L{t9OFT0`bdKGW6u zX}P@*0$3&rKfb(M(d6%AC4t_04ia21o3LqgkGd?ia_eLOm19!1E>=}8^I%EZeTt8lR5qc= z#f=p_Un1tiL;(?OF`2j%m9!6-d%w0R@8Q|d1@6slsRv> zn7Y{Q2f)kfsFyAcWgd5wg1gxmb7yIxo<(71^{| zWsFl7h19b5f0&k=3zzD03B@XYlL`Pw%^3tF0HWU!Ry#Km;_UVD38H_UZg&R5p?iH0M>*D*ylh diff --git a/test/src/components/badge/golden/priority_pill_high.png b/test/src/components/badge/golden/priority_pill_high.png index 0384a884819a8556936e2f3bc99c6439885b2af2..02c9e2525d7041c54ceea259f2381399c0a5dcc5 100644 GIT binary patch literal 3450 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|s7b}s#WAE}&fD7?vqBst8ZJied}`+*=dxvEMDALhNnzW#Z`|m}Z0d-W z($-Ggxb*rErgg@7#b|^SbqV!_@ zIB&6gMu=eS`L9xL0A-n18+1yMpFnVzmFz{(UJjP_>U%r(Zn#C7)DD)h7+UBgVE-} eplcqS(P#W??{XuW-5J=>XYh3Ob6Mw<&;$U%jU9#n literal 3681 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq{?P)5S5QV$R#U`&WmR${hIk{_Jbj*I8ejb(ESkc4j&SI8PCjkT7jo ztLw{`%h~AtzoEH0_=1pQ!h;10DjaOOyo(}CSR`H2Bw3xj;(jwPnU`$4Z~ep9rgk;w ze%4vsmOghl(&G2Yec8!Mt6D1`*q*{-g&kyXJ7rC{U1&*k-cC2{q4prTk4+O+4y*V|K#|Zood#8 zbLUolJNJB^dg$BU0Mi>d~hg zcUf3(V_Pwszn X>MNGU@BIjD(ldCv`njxgN@xNA3&3x| diff --git a/test/src/components/badge/golden/priority_pill_low.png b/test/src/components/badge/golden/priority_pill_low.png index 6199e19f5fe7c15228ce57418b33df4f680f3eb1..ff465b652cee4db023bf3f7481e45c51c32e3b29 100644 GIT binary patch delta 740 zcmV=(}=uOe+u8RnSBBCz5+KTA1i;x(h*&pbaAR=klj6p89(M=14`RmWmD+gQ5 z#{B-C;py?XIyav;uiGbw!`ax{+S;Cr{A>H)^Z1EpX7lv%d4KlIiFx+)iP_kgxqa{c zxqa`=bJx2+F!$el$L!izUHlXP0N|1~E)I}Cojo&$9{YUDo40yRV%`@BP9&c-uWYxe)*WSQF<1dz#7^( z_u6aQAN%pwF8IyI_E&#$d5^#G+4G-2vhPFl)aSlFtBs8v843UZy#1{AB W&91P7h*fj|00001KIrP9iASLL4U|eL_t(|obBDu>)!<(!134j>zvvm5n3x-mMcsbEfF*-sZj8s z{Wx}t71E`H=%pW{TZagQBBBnSwng*@gvAKWGH3}yBn_J~(B(F|G-2`eJ8hjBY_YNI z^L@rXJs$Vhi`VtZoA>*@vcA54X3q1*nSacqM-I)w+z6MskM>ElOcWo72pZ9C@H zZ8y#x@BF~rd*f|$#megWqW}N^7qoJIfV_C}#O!|b2lI!&{Q07Tcf9+i*?aE;^ZskM zZRmjj0KoD%CqN$guZ-Zyk+y%^YGnYp8G#^*M=Pk001nHvjOs>XAaDR z2OfD-OTK^4SAXWKyY5-&VE_QY@>!jkdEw-V+4K0mH@#%f}HA005W2)!!ex_}8xf z6N}sh001nX)hAwlc45m79y`3ST>t>U@>zZAmBmHFQ?Gae0ssJ9*g}70zV`a*MXmz? z0G7<^u6Mj|Vapb|3jhFEKC8R7Eigb9xC;OPSU#&C*}AxBSmZ7M0ATs7es0I@vvcbW z|MjAq-+#V+KELDk1)Kx`04%FbZ@u!W*>~Rq^YQ)PnDsYapHrvT<`*v>x}@P#r`Kj? zX7d%B=BJ;1XtrE=)kfY5003O(R@T?o&&yPbyQ%k;k*Y0`fw$Ci|FaQ8x z`J4@qnVBCyb71!TdjFhSU%RB?Yp>oi58wTz*?)b@r#I|C003ZloD(23Ge=(i>)ij# zAI_66J$KQ;J8#%IKe_M0xqi!a8+srB0I)p%EkOR|`M({T-yD5*e*5yD=66S*n=@x- zKJlJg=FaUq=TqBv&dt|dU*8G<09@uS7$5)u0E@Qzzdir}0QmnQKmY)Mi~s=u05Spu k0JGi$^$L@~1K1Y-1jB{G0g3O!0000007*qoM6N<$f{zfW;s5{u diff --git a/test/src/components/badge/golden/priority_pill_medium.png b/test/src/components/badge/golden/priority_pill_medium.png index e5a1ab11c0bc8dc12d0426bfb0a9b30818a57f97..196178dfa775074cfb924f6b89b1462dd79e2a33 100644 GIT binary patch literal 4001 zcmeHJ|1;ZX9DjTp_2p8xp4=+LwqBhp*~V3boR)Ittc^#{gCIE=brby@BMzgU-x>R z*Tapz6yaol(jEZd6!~#j3; zY1p=ogiN)F;++6^a?fWdt}H7Xk?USe>NgRZM3%ac@%Y8st#Nq?Hr$8({w#wnoV^Qq zNL)?Adn~l2yBnqra%}c(ta2o$`Hvsxl6$DS;zFgoFgLBfzF_UL1t@b+tXnQu(gX-I z;<;+2T14e(d-nw1(^i!@i4VEGY2D8f06dprBd%3uOB#rWb6Na^qNd*1M)u;?ElTlf zS99EP2<|~MhyHqUbCq(w-s1|M5^NPI@G)%cd9Oxk+Zx~ zP0vR!WOm<3*9fg(2jL2jS40e=&_*8V`#+$SFl$2fI!o#@ON03w+^odK^5hCB{cn- z12t3dbZPB~S&`UXO7n+V0VZ!j-+PWVxh^or7)OX_O24k_h6pfZ2_p`AyB6$6lmE>N z#)j)CU#h1BxWFg;(m;Xr^YUh8~vm2UcG>9@f0krWrpHueJ1P zo?8VC?9WJ!L~h540E|j9O~;OUaUGHiqGU=Dqh88>HFPEl+j3(_@wmW38vq8EcAeWz zx~KUG`3Du5*g=Coa#gn88Q(%Iesf#D^-D7j!Yh6G;raA*e^JI}>EUuQn&GKh6{`K> zv$Uh14sTDEV0Cwsf}a_82>{%zCOg%b=3g}GSNLRfg4T)rC-XBQd0OLmVo?er-)*VW zZyYdGsdGNa?Tdf6xHx#N3{Jdp!nPHX`7N*jz}FJ;CANbi^LYQfIcO%tEC%yMFux4u qSJCVUW;Zmuq1g@1Zuq}(!$Vg>@(!Z)okHO!9z=#;3KN8;l>Y@Jx^{~I literal 4059 zcmeHI`#0NX7=IOG&9ELl9M#d&AKLC9M%;1|qAp7vJM47K-P9$K;}%U3$0f#eU0Xzt zEmWo3T5QuKi%f}D&>po-A_sGcMBEcS2}-iWH$nFGU+jmxzr5#t-sgEf&*goe&v&24 z;O$PII}HG^i@FwZ9RSM<0Gvv+eGisAEUhKOn+4@MJ`AuY&%cEaR+O-)I9qsC+GgJY zz@a`W;%XfA(e{$4N0H+)vZul_QLp{Ht?d?VlfH?&GPCij^~Xq;t7lv2z8Qnf^xUbC zr}dFqn?G`YvMqejLUKkvxJY_Zdn4+K78!)j`f~EVuTqGf$;|z1JW#PY5u|}UA1hY} zX;N?fk!-kgc6QcL<1{x%bb}@qv0C9G_E^1#CR3~I-6XATZ4>=ogn8YB!r*`uGjIY@ zh-P&&iLYjLSpdN3?F>ZF*IiM1Y-xV=LC+J)N@qivU;cdKn5_Ku9RJ85;=K&&7QYWb zs#Oev-r$IGI4msLF^o+?Gq`zi?$#`Zp=5jKceIysgwxaL@)!VCE|QaL4z+*H^0Q2< zZS@02Mz0ZT_Zv(&p_2s2dovFG&P;H_S*42yaEo0NdDr2co5Zpc~ z3WeRIZU2MwXC96lWXCJfO3x5NjGJ=s#N`?vh56eW4k^_Mg;Keq`j=?Bakl(VkG#tY zosGTNfHAsb03e@9VYcy&+$@cDbe#ibD(A+qhV2fb->u!I^ww~Ex>q9(ORL zHCWn0d?Fm>$0_Hr(~+YZyt)Q}kY|7I=B+lxL~1tATf(f}5hQd~Q|6VBZO_c#^-+G%x^wW+pzA_dzFTMCd z`6f?nLc&nw8!5?YX~8}yjnq=5k+_>G(vvX7G0qFUy?#xVsw$q+lD%RLLoNMUQOuE+ zNMw|mMhd2qq>#6l-EjaQgOIMjq;1D>eH~;5xum!E)?8ixGfz=n|4^CWxpc|{&h6JAY%Bycq=%5WG#vLwFkNlHR0->=;C^H^#p;7Oq$*&m! z@~z9=lD;gTWk@y$z3C1R_cEB40B|&27W=q76>1n#?Lp8?kLG|kt{hqY7fs#Brl$%D zd=hpB@^#g@IRY*Bdq^8U!ONV1?Wm4Lt{33Ht-Mc#H_IHA&8@lgi+LKvZN*1UoFF_CMK9 BX^;Q_ diff --git a/test/src/components/badge/golden/status_label_custom.png b/test/src/components/badge/golden/status_label_custom.png index dd961c4f1bbffd4975d3b52d00c761a1312e1666..b3fd2c0b8728c4c67a059fd4d882dbd881048c01 100644 GIT binary patch literal 4407 zcmeH}{a4ag9LFzOX?4=s!(3rWKQzxWvtlZyNOM;6*kr3t=)#Ax!AYTTUsH%DS8Mig zvIoyde4^%xIwabdHZ-%^ii$7L$rJ$-%=(g3!9YVEgwg(r{lNX@p8GlHp7Xh%^M1cy z_tZDp+ZQcZu>b(DDD$g~od5)500_zno#(IAl^QG8DH#XHcY(aU$=(jDc(7pO=I5;Mdy`4UHSB+iiFs~jE%osi?fAf>rY=l zG_QD+uqKyJ%RLaWER%(fSiXAiMNG|~>75sr<&oDduFul%IJ{vow%^idI`LB3($&az z7w!1;?8FaNCa;6NDjYXC`@mtxw?eoOb&bZsA(X@rn^N(_SFxg8Rd)ypGMcr>eK)n5 zu)+T9R7}c(#HxiAFs++OQCNid)|3RcvFzx2mhQ$?Dhi~=wcx+49CQv(I~8aWH2hu| z*~3D%k~B19f^33?jik}59`yE|It)jpY=WjFSjVb{9acbw$(vkGUpr` zQ)SH+1s^U@6)RyhpN(|!7{b!Y9Y@S6Y{j^UH7ub@-LfjPL1h0eW(5i)p72Ft3cRmf zgRV0Y=Z_Bc9VAkKo~J2`saA7bBuX{sAR4^az&Eoz-zKmb}-Hj-J;WGoVInJFT~4`a>tHPxgHqKk`A!574frp=2~9Z zkngT>2AL&wvk`9>HOz|$Sjw~EqG@{&NbRKyh4L|t)_l=g@3fSt$1fMSTe0lHkbK7a zUX#ZrAk;d0wNjPDD62D*Emw2EJ>-wgP7^%QbGuG!Y@1q&>!HgTNV`?&G&ow~@pD1p zMoIi^pNl+oQD|;5#8N7;leXI`kAeWFGL8D@y8!t3mP>E$r&z<|@xh5zR6H9wVI3xnZv=7{3BQqDSk^op&bY%Q7 zT*17(_VM^Y0<@zNfch83^aMzsax9(F{?Hl)+E^_e`Kgd+EK5_V8X34Cq-*vboeS=5 z>Y#PMy+Alw&NB<6Uk4p$ysuu_q$_{&JQe`KE&a5u7cxc)no#u$Zb2=}8=LKK_Cs@* zypL}rmyP-RfN(4tQX;CCdlsRyh$xWrw?HY~oZw^AxL&tKm`7yb&&N9OHdI4*%HcZs ze&c9vPvPhq0F*f`EtGtpM2uMZC8M7)cqyf2h`OFC?eq>`75iUBNx<5>wRG4Q)#&tW z7c>Q%TMfK}$oB-6o^UxB)a&(6YGQ~Qc#!WXPIL87>nfCfHLk-iRLA?K6j(=8Ez2IK<-Wfx>JQ^I_P-|m7bi609tm$Rg39)2}u{OE<0P>bZ4SSggS?>!QFR47w+)0Qn2{(rFo1FLw=A7&4 zghcMJ?tzRwH&w?SnkarDa%G8X3$wF}p@93(P|L@gbWq<7x#w%K`FZS5A>iIfax*{y zz+8J%ZQAHhPvU%35C8|}gaU9r0t3L7ZGP>5<$+>gAOSH1j1WkMKq>}^5TIdzh5;G| eX!yUNp_4G|*uux5uSNSm>L7D#c7`;)@YuhMfI!s% literal 4609 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 delta 550 zcmV+>0@?l78~huPL4T%6L_t(|obBC9jv7}OfYDn8^e*g)vyvV=tBuFKl6d0m0t{q; z)R7-FG>@<4_#BB56uPR}r~2#OX}w%- zf4JNK-tjTU;T~+y&NTo4;Cp8U$bLV@W_$0Y{2KNG1ONaoc7HNJ_CtIC006gfGC(%l zLxcbT09qn0M`SN9~*PDwMw0c;L%^P0-@UR;1-oL;2dH?|6xars~SexzP zrR&%F^W(?y`R~scFX-{{aXdXez1j62{`fHd`t<4O_wzKyYH{)30|3Bp?WZGH+uddH z0RRB-k{<%Zc7MA70KmO`6CgX=?E(M*ck~<}&iViV0Pbxb!&x5y06=3NV~nrP`Tzg` z0`qQf$14B;pmFjfbN~Q=TbbwSaJU`-0KmP?V;oNQoy@Bme*a diff --git a/test/src/components/badge/golden/tag_right.png b/test/src/components/badge/golden/tag_right.png index 2f860de3baeb6371bad0a5641371cd8ab1b103cc..1951b8be5341c0f66287353da2090570e36f82ce 100644 GIT binary patch delta 446 zcmca0Jzsi)W4))Ri(^Q|oVRx!{h|UH+AjWPxa@n)_r^@w?VERT?sgVt=eohj*VEbc z=|{$wIW3Yc)_daRe$LFxdoOoPynLHXvf4e(L(wHvB(WWqfSipG{cK`qWsfqcwr{;S4wcCcvP8x|ZFq}CnJVE?UewqDA zpc&7U9pVz_i31s`2*$kxu!yq5HwZ(~;adtwgiqb!UgF-ZJ**52?E(r63=VGf4SNn} zJv`6J$nZdbDaHTv(RV<(nmO8E<-{((d&$ngP#|&rmATJliS*MDC$lqt>dNfkXJD8y znW6G%*3NDL28IMVrYEfF-reC03hICnP+ls7GRcP b6#K)Rvde8c%l}DrKq&@KS3j3^P6^UiX?U@ zOo5Py9eaF@!a34(^E9t~*JFJ%egAR)H2!nzJ z0099qcJcABx%4go0O0i#Aiq34i4Xt)I5z?Eo9)vWkK0S{0)GGiUa!@@8@}D$jr;qD zqaU!oT91bduD`lkkGJpM9en?0JH|A|SV}Vh0GQphZwl6@j~~bF*W05XaD9C}Zfw@mUIJtf z<9Pu9U_Jum0Ds2w0sz1~1jvCl+ua2K031(%yujnuWGnyxIE4TK001%qA14F= zfKv#N11%T33jhE(o+I@E7SrzS2LJ$Y8prBArfH1z^3uBi0D#v)fIMRw;sXEx<|II# zv0CmT1ONb-$KC*0EmtQ!V46-l!k@bS*!{1UCSCymz)0Mt??3K+TJH}l6R!XO;4B=o z|6trM005kiqxNr%+XVoC^K#t&#kgGn060GZGK|{=0Dy`B8OH4b06=B+=Cmoh00007 l^9HlA0@w callbackTriggered = true, label: 'Test Button')), - ); - await tester.tap(find.byType(ZetaButton)); - await tester.pump(); + testWidgets('Initializes primary with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton.primary(onPressed: () {}, label: 'Test Button'), + ), + ); - expect(callbackTriggered, isTrue); - }); + 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); + }); - testWidgets('Initializes primary with correct Label', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaButton.primary(onPressed: () {}, label: 'Test Button'), - ), - ); + 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, null); - expect(button.trailingIcon, null); - expect(button.size, ZetaWidgetSize.medium); - expect(button.type, ZetaButtonType.primary); + 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); + }); + testWidgets('Initializes positive with correct Label', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton.positive(onPressed: () {}, label: 'Test Button', trailingIcon: Icons.abc), + ), + ); - await expectLater(find.byType(ZetaButton), matchesGoldenFile(goldenFile.getFileUri('button_primary'))); - }); - 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, null); + expect(button.trailingIcon, Icons.abc); + expect(button.size, ZetaWidgetSize.medium); + expect(button.type, ZetaButtonType.positive); + }); + 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, Icons.abc); - expect(button.trailingIcon, null); - expect(button.size, ZetaWidgetSize.small); - expect(button.type, ZetaButtonType.secondary); - - await expectLater( - find.byType(ZetaButton), - matchesGoldenFile(goldenFile.getFileUri('button_secondary')), - ); - }); - 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, null); + expect(button.size, ZetaWidgetSize.small); + expect(button.type, ZetaButtonType.negative); + }); + 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, Icons.abc); - expect(button.size, ZetaWidgetSize.medium); - expect(button.type, ZetaButtonType.positive); - - await expectLater( - find.byType(ZetaButton), - matchesGoldenFile(goldenFile.getFileUri('button_positive')), - ); - }); - 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.large); + expect(button.type, ZetaButtonType.outline); + }); + 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, 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(goldenFile.getFileUri('button_negative')), - ); - }); - 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, 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); + }); + 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, null); - expect(button.label, 'Test Button'); - expect(button.leadingIcon, null); - expect(button.trailingIcon, null); - expect(button.size, ZetaWidgetSize.large); - expect(button.type, ZetaButtonType.outline); + 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(goldenFile.getFileUri('button_outline'))); - }); - testWidgets('Initializes outlineSubtle with correct Label', (WidgetTester tester) async { - await tester.pumpWidget( - const TestApp( - home: ZetaButton.outlineSubtle(label: 'Test Button', borderType: ZetaWidgetBorder.sharp), - ), - ); + 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.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(goldenFile.getFileUri('button_outline_subtle')), - ); + 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); + }); }); - 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(goldenFile.getFileUri('button_text')), - ); - }); + group('Dimensions Tests', () {}); - testWidgets('Disabled button', (WidgetTester tester) async { - await tester.pumpWidget( - const TestApp( - home: ZetaButton.text(label: 'Test Button', borderType: ZetaWidgetBorder.full), - ), - ); + group('Styling Tests', () { + testWidgets('Hover states are correct', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaButton(label: 'Test Button', onPressed: () {}), + ), + ); - 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(goldenFile.getFileUri('button_disabled')), - ); - }); - 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(); - final buttonFinder = find.byType(ZetaButton); - final ZetaButton button = tester.firstWidget(buttonFinder); + expect(filledButton.style?.backgroundColor?.resolve({WidgetState.hovered}), ZetaColorBase.blue.shade50); - final filledButtonFinder = find.byType(FilledButton); - final FilledButton filledButton = tester.firstWidget(filledButtonFinder); + await gesture.down(tester.getCenter(find.byType(ZetaButton))); + await tester.pumpAndSettle(); + expect(filledButton.style?.backgroundColor?.resolve({WidgetState.pressed}), ZetaColorBase.blue.shade70); - 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(); + await gesture.up(); - expect(filledButton.style?.backgroundColor?.resolve({WidgetState.hovered}), ZetaColorBase.blue.shade50); + await tester.pumpAndSettle(); + expect(button.label, 'Test Button'); + expect(button.leadingIcon, null); + expect(button.trailingIcon, null); + expect(button.size, ZetaWidgetSize.medium); + }); - await gesture.down(tester.getCenter(find.byType(ZetaButton))); - await tester.pumpAndSettle(); - expect(filledButton.style?.backgroundColor?.resolve({WidgetState.pressed}), ZetaColorBase.blue.shade70); + testWidgets('Focus state is correct', (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}), + BorderSide(color: ZetaColorBase.blue, width: ZetaBorders.medium), + ); + }); + }); - await gesture.up(); + group('Interaction Tests', () { + 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(); - await tester.pumpAndSettle(); - expect(button.label, 'Test Button'); - expect(button.leadingIcon, null); - expect(button.trailingIcon, null); - expect(button.size, ZetaWidgetSize.medium); + expect(callbackTriggered, isTrue); + }); }); - testWidgets('Interaction with button', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - await tester.pumpWidget( - TestApp( - home: ZetaButton(label: 'Test Button', onPressed: () {}, focusNode: focusNode), - ), + + group('Golden Tests', () { + goldenTest(goldenFile, ZetaButton.primary(onPressed: () {}, label: 'Test Button'), ZetaButton, 'button_primary'); + goldenTest( + goldenFile, + ZetaButton.secondary(onPressed: () {}, label: 'Test Button', leadingIcon: Icons.abc, size: ZetaWidgetSize.small), + ZetaButton, + 'button_secondary', ); - 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}), - BorderSide(color: ZetaColorBase.blue, width: ZetaBorders.medium), + goldenTest( + goldenFile, + ZetaButton.positive(onPressed: () {}, label: 'Test Button', trailingIcon: Icons.abc), + ZetaButton, + 'button_positive', + ); + goldenTest( + goldenFile, + ZetaButton.negative(onPressed: () {}, label: 'Test Button', size: ZetaWidgetSize.small), + ZetaButton, + 'button_negative', + ); + goldenTest( + goldenFile, + ZetaButton.outline(onPressed: () {}, label: 'Test Button', size: ZetaWidgetSize.large), + ZetaButton, + 'button_outline', + ); + goldenTest( + goldenFile, + const ZetaButton.outlineSubtle(label: 'Test Button', borderType: ZetaWidgetBorder.sharp), + ZetaButton, + 'button_outline_subtle', + ); + goldenTest( + goldenFile, + ZetaButton.text(onPressed: () {}, label: 'Test Button', borderType: ZetaWidgetBorder.full), + ZetaButton, + 'button_text', + ); + goldenTest( + goldenFile, + const ZetaButton.text(label: 'Test Button', borderType: ZetaWidgetBorder.full), + ZetaButton, + 'button_disabled', ); }); - 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'); - }); + + group('Performance Tests', () {}); } diff --git a/test/src/components/chat_item/chat_item_test.dart b/test/src/components/chat_item/chat_item_test.dart index 8bafbc00..e72a00bf 100644 --- a/test/src/components/chat_item/chat_item_test.dart +++ b/test/src/components/chat_item/chat_item_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart'; @@ -9,13 +8,67 @@ import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const goldenFile = GoldenFiles(component: 'chat_item'); + const String parentFolder = 'chat_item'; + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - group('ZetaChatItem Tests', () { + group('Accessibility Tests', () {}); + group('Content Tests', () { + final time = DateTime.now(); + final debugFillPropertiesChatItem = { + 'rounded': 'null', + 'highlighted': 'true', + 'time': time.toString(), + 'timeFormat': 'null', + 'enabledWarningIcon': 'true', + 'enabledNotificationIcon': 'true', + 'count': '1', + 'onTap': 'null', + 'starred': 'true', + 'onMenuMoreTap': 'null', + 'onCallTap': 'null', + 'onDeleteTap': 'null', + 'onPttTap': 'null', + 'explicitChildNodes': 'true', + 'paleButtonColors': 'null', + }; + debugFillPropertiesTest( + ZetaChatItem( + title: const Text('Title'), + subtitle: const Text('Subtitle'), + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + count: 1, + enabledNotificationIcon: true, + highlighted: true, + enabledWarningIcon: true, + starred: true, + ), + debugFillPropertiesChatItem, + ); + final debugFillPropertiesSlidableAction = { + 'onPressed': 'null', + 'icon': 'IconData(U+0E5F9)', + 'foregroundColor': null, + 'backgroundColor': null, + 'color': ZetaColorBase.blue.toString(), + 'semanticLabel': 'null', + 'paleColor': 'false', + }; + debugFillPropertiesTest( + const ZetaSlidableAction(icon: Icons.star), + debugFillPropertiesSlidableAction, + ); + testWidgets('ZetaChatItem displays correctly', (WidgetTester tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); @@ -64,11 +117,6 @@ void main() { await tester.tap(chatItemFinder); await tester.pumpAndSettle(); expect(tester.takeException(), isNull); - - await expectLater( - chatItemFinder, - matchesGoldenFile(goldenFile.getFileUri('chat_item_default')), - ); }); testWidgets('ZetaChatItem highlighted', (WidgetTester tester) async { @@ -119,11 +167,6 @@ void main() { await tester.tap(chatItemFinder); await tester.pumpAndSettle(); expect(tester.takeException(), isNull); - - await expectLater( - chatItemFinder, - matchesGoldenFile(goldenFile.getFileUri('chat_item_highlighted')), - ); }); testWidgets('ZetaChatItem slidable actions', (WidgetTester tester) async { @@ -181,11 +224,6 @@ void main() { await tester.tap(find.byIcon(ZetaIcons.delete_round)); await tester.pumpAndSettle(); expect(tester.takeException(), isNull); - - await expectLater( - chatItemFinder, - matchesGoldenFile(goldenFile.getFileUri('chat_item_slidable_actions')), - ); }); testWidgets('ZetaChatItem with pale slidable button colors', (WidgetTester tester) async { @@ -235,11 +273,6 @@ void main() { expect(find.byIcon(ZetaIcons.phone_round), findsOneWidget); expect(find.byIcon(ZetaIcons.ptt_round), findsOneWidget); expect(find.byIcon(ZetaIcons.delete_round), findsOneWidget); - - await expectLater( - chatItemFinder, - matchesGoldenFile(goldenFile.getFileUri('chat_item_pale_slidable_buttons')), - ); }); testWidgets('ZetaChatItem with 2 pale buttons and 2 regular buttons', (WidgetTester tester) async { @@ -323,11 +356,6 @@ void main() { await tester.tap(find.byIcon(Icons.message)); await tester.pumpAndSettle(); expect(tester.takeException(), isNull); - - await expectLater( - chatItemFinder, - matchesGoldenFile(goldenFile.getFileUri('chat_item_pale_and_regular_buttons')), - ); }); testWidgets('ZetaChatItem with custom leading widget', (WidgetTester tester) async { @@ -362,16 +390,9 @@ void main() { ), ); - final chatItemFinder = find.byType(ZetaChatItem); - // Verify that the custom leading widget is displayed correctly expect(find.byType(Container), findsOneWidget); expect(find.byWidgetPredicate((widget) => widget is Container && widget.color == Colors.blue), findsOneWidget); - - await expectLater( - chatItemFinder, - matchesGoldenFile(goldenFile.getFileUri('chat_item_custom_leading')), - ); }); testWidgets('ZetaChatItem with custom slidable buttons', (WidgetTester tester) async { @@ -425,60 +446,6 @@ void main() { await tester.tap(find.byIcon(Icons.delete)); await tester.pumpAndSettle(); expect(tester.takeException(), isNull); - - await expectLater( - chatItemFinder, - matchesGoldenFile(goldenFile.getFileUri('chat_item_custom_slidable_buttons')), - ); - }); - - testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { - final diagnosticsZetaChatItem = DiagnosticPropertiesBuilder(); - final time = DateTime.now(); - ZetaChatItem( - title: const Text('Title'), - subtitle: const Text('Subtitle'), - time: time, - leading: const ZetaAvatar(initials: 'AZ'), - slidableActions: [ - ZetaSlidableAction.menuMore(onPressed: () {}), - ZetaSlidableAction.call(onPressed: () {}), - ZetaSlidableAction.ptt(onPressed: () {}), - ZetaSlidableAction.delete(onPressed: () {}), - ], - count: 1, - enabledNotificationIcon: true, - highlighted: true, - enabledWarningIcon: true, - starred: true, - ).debugFillProperties(diagnosticsZetaChatItem); - - expect(diagnosticsZetaChatItem.finder('rounded'), 'null'); - expect(diagnosticsZetaChatItem.finder('highlighted'), 'true'); - expect(diagnosticsZetaChatItem.finder('time'), time.toString()); - expect(diagnosticsZetaChatItem.finder('timeFormat'), 'null'); - expect(diagnosticsZetaChatItem.finder('enabledWarningIcon'), 'true'); - expect(diagnosticsZetaChatItem.finder('enabledNotificationIcon'), 'true'); - expect(diagnosticsZetaChatItem.finder('count'), '1'); - expect(diagnosticsZetaChatItem.finder('onTap'), 'null'); - expect(diagnosticsZetaChatItem.finder('starred'), 'true'); - expect(diagnosticsZetaChatItem.finder('onMenuMoreTap'), 'null'); - expect(diagnosticsZetaChatItem.finder('onCallTap'), 'null'); - expect(diagnosticsZetaChatItem.finder('onDeleteTap'), 'null'); - expect(diagnosticsZetaChatItem.finder('onPttTap'), 'null'); - expect(diagnosticsZetaChatItem.finder('explicitChildNodes'), 'true'); - expect(diagnosticsZetaChatItem.finder('paleButtonColors'), 'null'); - - final diagnosticsZetaSlidableAction = DiagnosticPropertiesBuilder(); - const ZetaSlidableAction(icon: Icons.star).debugFillProperties(diagnosticsZetaSlidableAction); - - expect(diagnosticsZetaSlidableAction.finder('onPressed'), 'null'); - expect(diagnosticsZetaSlidableAction.finder('icon'), 'IconData(U+0E5F9)'); - expect(diagnosticsZetaSlidableAction.finder('foregroundColor'), null); - expect(diagnosticsZetaSlidableAction.finder('backgroundColor'), null); - expect(diagnosticsZetaSlidableAction.finder('color'), ZetaColorBase.blue.toString()); - expect(diagnosticsZetaSlidableAction.finder('semanticLabel'), 'null'); - expect(diagnosticsZetaSlidableAction.finder('paleColor'), 'false'); }); testWidgets('ZetaChatItem displays correctly', (WidgetTester tester) async { @@ -541,11 +508,298 @@ void main() { await tester.tap(find.byIcon(ZetaIcons.delete_round)); await tester.pumpAndSettle(); expect(tester.takeException(), isNull); - - await expectLater( - chatItemFinder, - matchesGoldenFile(goldenFile.getFileUri('chat_item_small_screen_slidable_button')), - ); }); }); + group('Dimensions Tests', () {}); + group('Styling Tests', () {}); + group('Interaction Tests', () {}); + group('Golden Tests', () { + const title = Text('John Doe'); + const subtitle = Text('Hello, how are you?'); + final time = DateTime.now(); + + goldenTestWithCallbacks( + goldenFile, + Scaffold( + body: Column( + children: [ + ZetaChatItem( + explicitChildNodes: false, + time: time, + enabledWarningIcon: true, + enabledNotificationIcon: true, + leading: const ZetaAvatar(initials: 'AZ'), + count: 100, + onTap: () {}, + paleButtonColors: true, + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ), + ZetaChatItem, + 'chat_item_default', + before: (tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + }, + ); + + goldenTestWithCallbacks( + goldenFile, + Column( + children: [ + ZetaChatItem( + explicitChildNodes: false, + time: time, + enabledWarningIcon: true, + enabledNotificationIcon: true, + leading: const ZetaAvatar(initials: 'AZ'), + count: 100, + onTap: () {}, + paleButtonColors: false, + starred: true, + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + highlighted: true, + title: title, + subtitle: subtitle, + ), + ], + ), + ZetaChatItem, + 'chat_item_highlighted', + before: (tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + }, + ); + + goldenTestWithCallbacks( + goldenFile, + Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ZetaChatItem, + 'chat_item_slidable_actions', + before: (tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + }, + after: (tester) async { + final chatItemFinder = find.byType(ZetaChatItem); + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + }, + ); + + goldenTestWithCallbacks( + goldenFile, + Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + paleButtonColors: true, + slidableActions: [ + ZetaSlidableAction.menuMore( + onPressed: () {}, + ), + ZetaSlidableAction.call( + onPressed: () {}, + ), + ZetaSlidableAction.ptt( + onPressed: () {}, + ), + ZetaSlidableAction.delete( + onPressed: () {}, + ), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ZetaChatItem, + 'chat_item_pale_slidable_buttons', + before: (tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + }, + after: (tester) async { + final chatItemFinder = find.byType(ZetaChatItem); + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + }, + ); + + goldenTestWithCallbacks( + goldenFile, + Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction( + onPressed: () {}, + paleColor: true, + icon: Icons.star, + ), + ZetaSlidableAction( + onPressed: () {}, + paleColor: true, + icon: Icons.delete, + ), + ZetaSlidableAction( + onPressed: () {}, + icon: Icons.call, + ), + ZetaSlidableAction( + onPressed: () {}, + icon: Icons.message, + ), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ZetaChatItem, + 'chat_item_pale_and_regular_buttons', + before: (tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + }, + after: (tester) async { + final chatItemFinder = find.byType(ZetaChatItem); + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + }, + ); + + goldenTestWithCallbacks( + goldenFile, + Column( + children: [ + ZetaChatItem( + time: time, + leading: Container( + width: 40, + height: 40, + color: Colors.blue, + ), + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ZetaChatItem, + 'chat_item_custom_leading', + before: (tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + }, + ); + + goldenTestWithCallbacks( + goldenFile, + Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction( + onPressed: () {}, + color: ZetaColorBase.orange, + icon: Icons.star, + ), + ZetaSlidableAction( + onPressed: () {}, + color: ZetaColorBase.red, + icon: Icons.delete, + ), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ZetaChatItem, + 'chat_item_custom_slidable_buttons', + before: (tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + }, + after: (tester) async { + final chatItemFinder = find.byType(ZetaChatItem); + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + }, + ); + + goldenTestWithCallbacks( + goldenFile, + Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + title: title, + count: 1, + subtitle: subtitle, + ), + ], + ), + ZetaChatItem, + 'chat_item_small_screen_slidable_button', + before: (tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(315, 480); + }, + after: (tester) async { + final chatItemFinder = find.byType(ZetaChatItem); + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + }, + ); + }); + group('Performance Tests', () {}); } diff --git a/test/src/components/chat_item/golden/chat_item_slidable_actions.png b/test/src/components/chat_item/golden/chat_item_slidable_actions.png index 797a78f24e97b0068a080388e735bd8582c63cae..c990b5936d3ccb8f6d1feef7c3a05a31a3b4d34e 100644 GIT binary patch literal 2975 zcmeAS@N?(olHy`uVBq!ia0y~yV0;L~4>;I>B9p|ft_D(!#X;^)4C~IxyaaMsik&<| zIDnvrBc+3Zf!ov5#WAE}&fB}bJ+Y~>2OfUkwsmcSy-E#R4CCBet&^k-6BlzINnkEx zlon3vP;oQ7$U6B!Q@|spNt*(cta%Mp@)8zWuE8eopnL^Rst;K5d!*Y~IOBK+teuW$^NIm2aQk_c_LGA7%fqG{eYG z#`{>*X0v?h$6a&1A5VKfU(&oT_1E$52jc(PeB1xyj>V5-6AQmQxh?;*C;R3%-|2m^ z)9bU&+}~6FX6L`+;C{=3jKnwp5AS~bz}~-qvHG9Rn)PpIt0!L%l|J@ox}Dv(H+L+) z&M4>mQ}-wRzyGmA2M;HId@&nr)~>hF$B)GpKc4*gzU=fjJ4=0BX59L%T=V0=u8(JI zUvI8VJ9*cCzGQK=*!ilrKf~i>%5AsX&(54?v%9?RPUYdV>3hHZIoMiw{qmuWGUxAY zF1@q!^&5fxfB&pMZvXCAn%QNYoxg6*?=Af*@S#rs&yDp@i+rP3|E>R(YIIs>*0!+U z|9|*FSnl8LXPzp8Cv~rH*=m)>UvU$t{L#k8Cv`Swe7sR8$kx!s!R+AD z!l@H=*UQcKUrNB=rlyt?Y& z$;H*-U;k`fdVGE4)zAKN6}h43<8SZ$@@v(+{}-N|3bpEGud~&z88d7 z_y7D~sEX=-bnl5@nOHWfwC-$|Udu@CF=eO-)Uw?d_`@DHy`p#$1%I178w_g9u zhC$po9DBf=VEwA^wLwVy_veQ2S4s79AG~7UoM(<(`Kt1O_ERrl07}36_p4U^ROh^( zNB!-md}cUs^w3mb*uLQ`zF1Y^ZF?|hw#}~Xa*4})k48+6N=J-ax=1e=jW$UeNFDE5UnQ4-{c3bW|Xllv3Jq?sv@@|U(Q_Iep z<3YBE&dk^rX05+lZ)U0(Fy+h*U+14T)AjPTtjFi1Cr74TJT}+7%OLx_?)~UB?lUBT z;{Ukgy&)?8b-z>50j9EBcJ=kZgbSqpN9@nPzpwVILd+d8@$-BqpIT2lW@@h6YaTzh zr**s9<1Z(3wwPvq-e$ONXJ+P|e?Mk!eEh9=eamyb-DPj~z4}%7^|uZBWMXBG`|I@zFQ069eGMpkf8Q!E z?f>TQksXgOOP~Mw^Vm-5W6$o+mtEffr&dRc^|qq;|fg_zQIG%;bVghqp9G+2<@BtsxM{1NZG XtnA|V&psH~$Y$_#^>bP0l+XkK6As=K literal 3424 zcmeHKeNfVA9{+i6hP7s$SGl%o=ic7Sl&P^;>MG?bq{mHWqZDqlBB>MNb0(kf{1`1BEtQgcINJ8ZszW<`^)dlXFkvK zndkd`X1??JJ~K~QDhlbdAz%Xl06s~F5{>|X$0Udeh!?2b*rwov54+ zPCKU8U7e?-ppwOC^djSZ@$`)Q_V_(%JM*>EJrqU4fU)jic6N5K*zoe2MmL~|)>X!m zVCQ!3RX$L3KgdrTKT5E7m*F+z_CC5@0apx#qbWZ?@2?j36ngAymS-Lg z+V5ZY%~p{9*aLhK(gSS%Z+Ko81pJzjp3Xc$DNxz9sftM^Rg8GTo2pR^ZO?=?5y#9w zcUya7Hd1;10iO=16_DyuD5z7QmBZSC#rW5>C=mv{3_rdO(CEhiIcboxG~v%mjP z_tF{5_%Y~G04m1NmQQ4llY1zR3uz52vN54aN9JcTiVIYS{(e#2r7m=JL=mRs(zuMr zB4;6LRqg$&+O$-^s@7DE&TmA%I$!zaR3w?_S9GxirP}YD_M_$6$phxW<4X!#;G6H4tZ>e>2kM`>>@Sb^?HG0f9?x;!aj~<}442CWU%Y7ejRx zJ&NBWUi<@8ay91eE2woM4Svg7haWgim%yfoot`_obdU6?+pChkh7b!%$zFTDQ~17s z>Y=d5&|m7V*8WexrG_a@!}BZ16JKdqJpt4lePV{rc&X(5STCU7+w(vb#PdMuzo7p! zhd$T-yFHy8oghp$E7_$QvVG9}y3yjLyMewF%U!lLAB)vn+HSGqRG1op{+67%j570` zm)Sy5eR9X(w4{7lDRX={WQMPIx4!ym(G>w*$*Io(!4Be{wu+2Amlawbo)){3!Ed@m zWK`r)qXU5WX!6nw$#^L|DhijDW-yG9Extu#seLs1GaUuE`von|GD~eWAIRorhon5M z_0RGreq9lGC2THNaD8uNOStk-UBHfv&BQN>J@4`}nF3v3@(89)h;*4#+O>?+jEbAq z{$wQ2aLt{@8XR@AaLSL{_BEoU{ed|2%^(||)*!&? z2kG;0%d;mm%$u3l#T3#`-Ao_N6**A1T;*E>`C$4_lJ?d20i=*YCv~!FU;z@!m1p{7 zQWD>s^}4sedRTxcQN$EyuPPwXX2IIH>*lSWz42GseN4@PBKi>#xK7#U9!CXsYB zN4c{bvZOq2Wb>RrWkV|;y@!GA4SI+ALT4!8A8rccX++15Xd-vJetf-Ml!eiOujU&Ux?@Pu!^k&sSc#}W7i(so7%^S&B^MBZ8U{0q>;_1Kwkc@1klSo6S|2i82W g=7Im82Y!|wn`E(jD)+q&{*r*CL{x$>{*%xC0e-vq=l}o! diff --git a/test/src/components/checkbox/checkbox_test.dart b/test/src/components/checkbox/checkbox_test.dart index c2c00c56..b718cef9 100644 --- a/test/src/components/checkbox/checkbox_test.dart +++ b/test/src/components/checkbox/checkbox_test.dart @@ -1,6 +1,5 @@ import 'dart:ui'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/src/components/checkbox/checkbox.dart'; @@ -11,13 +10,41 @@ import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const goldenFile = GoldenFiles(component: 'checkbox'); + const String parentFolder = 'checkbox'; + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - group('ZetaCheckbox Tests', () { + group('Accessibility Tests', () {}); + group('Content Tests', () { + final debugFillPropertiesCheckbox = { + 'value': 'false', + 'label': 'null', + 'onChanged': 'null', + 'rounded': 'null', + 'useIndeterminate': 'false', + 'focusNode': 'null', + }; + debugFillPropertiesTest( + ZetaCheckbox(), + debugFillPropertiesCheckbox, + ); + final debugFillPropertiesCheckboxInternal = { + 'value': 'false', + 'label': 'null', + 'rounded': 'null', + 'useIndeterminate': 'false', + 'error': 'false', + 'disabled': 'false', + 'focusNode': 'null', + }; + debugFillPropertiesTest( + ZetaInternalCheckbox(onChanged: (_) {}), + debugFillPropertiesCheckboxInternal, + ); + testWidgets('Initializes with correct parameters', (WidgetTester tester) async { await tester.pumpWidget( TestApp( @@ -36,7 +63,10 @@ void main() { expect(checkbox.rounded, null); expect(checkbox.label, 'Test Checkbox'); }); - + }); + group('Dimensions Tests', () {}); + group('Styling Tests', () {}); + group('Interaction Tests', () { testWidgets('ZetaCheckbox changes state on tap', (WidgetTester tester) async { bool? checkboxValue = true; @@ -51,11 +81,6 @@ void main() { ), ); - final checkboxFinder = find.byType(ZetaCheckbox); - await expectLater( - checkboxFinder, - matchesGoldenFile(goldenFile.getFileUri('checkbox_enabled')), - ); await tester.tap(find.byType(ZetaCheckbox)); await tester.pump(); @@ -71,10 +96,7 @@ void main() { ); final checkboxFinder = find.byType(ZetaCheckbox); - await expectLater( - checkboxFinder, - matchesGoldenFile(goldenFile.getFileUri('checkbox_disabled')), - ); + await tester.tap(find.byType(ZetaCheckbox)); await tester.pump(); final ZetaCheckbox checkbox = tester.firstWidget(checkboxFinder); @@ -115,51 +137,46 @@ void main() { await tester.tap(find.byType(ZetaCheckbox)); await tester.pump(); }); - - testWidgets('ZetaCheckbox UI changes on hover', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaCheckbox( - onChanged: (value) {}, - ), - ), - ); - - final checkboxFinder = find.byType(ZetaCheckbox); - - // Hover state - 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(checkboxFinder)); - await tester.pumpAndSettle(); - await expectLater( - checkboxFinder, - matchesGoldenFile(goldenFile.getFileUri('checkbox_hover')), - ); - }); - - 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'); - }); }); + group('Golden Tests', () { + goldenTestWithCallbacks( + goldenFile, + ZetaCheckbox( + onChanged: (value) {}, + ), + ZetaCheckbox, + 'checkbox_hover', + after: (tester) async { + final checkboxFinder = find.byType(ZetaCheckbox); + + // Hover state + 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(checkboxFinder)); + await tester.pumpAndSettle(); + }, + ); + + goldenTest( + goldenFile, + ZetaCheckbox( + value: true, + onChanged: print, + ), + ZetaCheckbox, + 'checkbox_enabled', + ); + + goldenTest( + goldenFile, + ZetaCheckbox( + value: true, + ), + ZetaCheckbox, + 'checkbox_disabled', + ); + }); + group('Performance Tests', () {}); } diff --git a/test/src/components/chips/chip_test.dart b/test/src/components/chips/chip_test.dart index 0d1eb2d1..9a9781e2 100644 --- a/test/src/components/chips/chip_test.dart +++ b/test/src/components/chips/chip_test.dart @@ -3,9 +3,27 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; +import '../../../test_utils/utils.dart'; void main() { - group('ZetaChip', () { + const String parentFolder = 'chips'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('Accessibility Tests', () {}); + group('Content Tests', () { + // final debugFillProperties = { + // '': '', + // }; + // debugFillPropertiesTest( + // widget, + // debugFillProperties, + // ); + testWidgets('renders label correctly', (WidgetTester tester) async { await tester.pumpWidget( const TestApp(home: ZetaChip(label: 'Test Chip')), @@ -13,7 +31,10 @@ void main() { expect(find.text('Test Chip'), findsOneWidget); }); - + }); + group('Dimensions Tests', () {}); + group('Styling Tests', () {}); + group('Interaction Tests', () { testWidgets('triggers onTap callback when tapped', (WidgetTester tester) async { bool tapped = false; @@ -72,30 +93,33 @@ void main() { expect(find.byIcon(Icons.close), findsOneWidget); }); - }); + testWidgets('ZetaChip changes selected property correctly', (WidgetTester tester) async { + bool selected = false; + StateSetter? setState; - testWidgets('ZetaChip changes selected property correctly', (WidgetTester tester) async { - bool selected = false; - StateSetter? setState; - - await tester.pumpWidget( - TestApp( - home: StatefulBuilder( - builder: (context, setState2) { - setState = setState2; - return ZetaChip(label: 'Chip', selected: selected); - }, + await tester.pumpWidget( + TestApp( + home: StatefulBuilder( + builder: (context, setState2) { + setState = setState2; + return ZetaChip(label: 'Chip', selected: selected); + }, + ), ), - ), - ); + ); - final Finder iconFinder = find.byIcon(ZetaIcons.check_mark_round); - expect(iconFinder, findsNothing); + final Finder iconFinder = find.byIcon(ZetaIcons.check_mark_round); + expect(iconFinder, findsNothing); - // Change isOpen property to true - setState?.call(() => selected = true); - await tester.pumpAndSettle(); + // Change isOpen property to true + setState?.call(() => selected = true); + await tester.pumpAndSettle(); - expect(iconFinder, findsOne); + expect(iconFinder, findsOne); + }); + }); + group('Golden Tests', () { + // goldenTest(goldenFile, widget, widgetType, 'PNG_FILE_NAME'); }); + group('Performance Tests', () {}); } diff --git a/test/src/components/comms_button/comms_button_test.dart b/test/src/components/comms_button/comms_button_test.dart index 0322682e..2ff9c648 100644 --- a/test/src/components/comms_button/comms_button_test.dart +++ b/test/src/components/comms_button/comms_button_test.dart @@ -1,19 +1,95 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; + import '../../../test_utils/test_app.dart'; import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const goldenFile = GoldenFiles(component: 'comms_button'); + const String parentFolder = 'comms_button'; + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - group('ZetaCommsButton Tests', () { + group('Accessibility Tests', () { + testWidgets('Button meets accessibility requirements', (WidgetTester tester) async { + final SemanticsHandle handle = tester.ensureSemantics(); + await tester.pumpWidget( + const TestApp( + home: ZetaCommsButton( + label: 'Label', + semanticLabel: 'Phone', + icon: ZetaIcons.phone, + type: ZetaCommsButtonType.positive, + ), + ), + ); + await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); + await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); + await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); + await expectLater(tester, meetsGuideline(textContrastGuideline)); + + handle.dispose(); + }); + + testWidgets('Button meets accessibility requirements when toggled', (WidgetTester tester) async { + final SemanticsHandle handle = tester.ensureSemantics(); + await tester.pumpWidget( + TestApp( + home: ZetaCommsButton( + label: 'Label', + semanticLabel: 'Phone', + icon: ZetaIcons.phone, + type: ZetaCommsButtonType.positive, + toggledLabel: 'Toggled Label', + toggledIcon: ZetaIcons.end_call, + toggledType: ZetaCommsButtonType.negative, + onToggle: (isToggled) {}, + ), + ), + ); + await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); + await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); + await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); + await expectLater(tester, meetsGuideline(textContrastGuideline)); + + await tester.tap(find.byType(ZetaCommsButton)); + await tester.pumpAndSettle(); + + await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); + await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); + await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); + await expectLater(tester, meetsGuideline(textContrastGuideline)); + + handle.dispose(); + }); + }); + group('Content Tests', () { + final debugFillProperties = { + 'label': '"Label"', + 'onPressed': 'null', + 'onToggle': 'null', + 'toggledIcon': 'null', + 'toggledLabel': 'null', + 'toggleType': null, + 'focusNode': 'null', + 'semanticLabel': 'null', + 'type': 'positive', + 'size': 'medium', + 'icon': 'IconData(U+0E16B)', + }; + debugFillPropertiesTest( + const ZetaCommsButton( + label: 'Label', + icon: ZetaIcons.phone, + type: ZetaCommsButtonType.positive, + ), + debugFillProperties, + ); + testWidgets('Initializes with correct label', (WidgetTester tester) async { await tester.pumpWidget( const TestApp( @@ -22,11 +98,6 @@ void main() { ); expect(find.text('Label'), findsOneWidget); - - await expectLater( - find.byType(ZetaCommsButton), - matchesGoldenFile(goldenFile.getFileUri('CommsButton_default')), - ); }); testWidgets('Initializes with correct icon', (WidgetTester tester) async { @@ -133,99 +204,23 @@ void main() { expect(pressed, isTrue); }); - - testWidgets('debugFillProperties Test', (WidgetTester tester) async { - final diagnostic = DiagnosticPropertiesBuilder(); - const ZetaCommsButton( - label: 'Label', - icon: ZetaIcons.phone, - type: ZetaCommsButtonType.positive, - ).debugFillProperties(diagnostic); - - expect(diagnostic.finder('label'), '"Label"'); - expect(diagnostic.finder('onPressed'), 'null'); - expect(diagnostic.finder('onToggle'), 'null'); - expect(diagnostic.finder('toggledIcon'), 'null'); - expect(diagnostic.finder('toggledLabel'), 'null'); - expect(diagnostic.finder('toggleType'), null); - expect(diagnostic.finder('focusNode'), 'null'); - expect(diagnostic.finder('semanticLabel'), 'null'); - expect(diagnostic.finder('type'), 'positive'); - expect(diagnostic.finder('size'), 'medium'); - expect(diagnostic.finder('icon'), 'IconData(U+0E16B)'); - }); - - testWidgets('Button meets accessibility requirements', (WidgetTester tester) async { - final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget( - const TestApp( - home: ZetaCommsButton( - label: 'Label', - semanticLabel: 'Phone', - icon: ZetaIcons.phone, - type: ZetaCommsButtonType.positive, - ), - ), - ); - await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); - await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); - await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); - await expectLater(tester, meetsGuideline(textContrastGuideline)); - - handle.dispose(); - }); - - testWidgets('Button meets accessibility requirements when toggled', (WidgetTester tester) async { - final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget( - TestApp( - home: ZetaCommsButton( - label: 'Label', - semanticLabel: 'Phone', - icon: ZetaIcons.phone, - type: ZetaCommsButtonType.positive, - toggledLabel: 'Toggled Label', - toggledIcon: ZetaIcons.end_call, - toggledType: ZetaCommsButtonType.negative, - onToggle: (isToggled) {}, - ), - ), - ); - await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); - await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); - await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); - await expectLater(tester, meetsGuideline(textContrastGuideline)); - - await tester.tap(find.byType(ZetaCommsButton)); - await tester.pumpAndSettle(); - - await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); - await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); - await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); - await expectLater(tester, meetsGuideline(textContrastGuideline)); - - handle.dispose(); - }); }); - - group('ZetaCommsButton Golden Tests', () { + group('Dimensions Tests', () {}); + group('Styling Tests', () {}); + group('Interaction Tests', () {}); + group('Golden Tests', () { for (final type in ZetaCommsButtonType.values) { - testWidgets('ZetaCommsButton with type $type', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaCommsButton( - label: 'Label', - icon: ZetaIcons.phone, - type: type, - ), - ), - ); - - await expectLater( - find.byType(ZetaCommsButton), - matchesGoldenFile(goldenFile.getFileUri('CommsButton_${type.name}')), - ); - }); + goldenTest( + goldenFile, + ZetaCommsButton( + label: 'Label', + icon: ZetaIcons.phone, + type: type, + ), + ZetaCommsButton, + 'CommsButton_${type.name}', + ); } }); + group('Performance Tests', () {}); } diff --git a/test/src/components/comms_button/golden/CommsButton_default.png b/test/src/components/comms_button/golden/CommsButton_default.png deleted file mode 100644 index 3f4c2f7354bb91acf0bc3d256c9faebf71ec5d94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6293 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 U!yeuQEH?!I!TtO4Gk$mf51myUZvX%Q diff --git a/test/src/components/dialpad/dialpad_test.dart b/test/src/components/dialpad/dialpad_test.dart index f85a2d54..b4f5cc7e 100644 --- a/test/src/components/dialpad/dialpad_test.dart +++ b/test/src/components/dialpad/dialpad_test.dart @@ -1,6 +1,5 @@ import 'dart:ui'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; @@ -10,13 +9,64 @@ import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const goldenFile = GoldenFiles(component: 'dialpad'); + const String parentFolder = 'dialpad'; + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - group('ZetaDialPad Tests', () { + group('Accessibility Tests', () {}); + + group('Content Tests', () { + final debugFillProperties = { + 'onNumber': 'null', + 'onText': 'null', + 'buttonsPerRow': '3', + 'buttonValues': '{1: , 2: ABC, 3: DEF, 4: GHI, 5: JKL, 6: MNO, 7: PQRS, 8: TUV, 9: WXYZ, *: , 0: +, #: }', + }; + debugFillPropertiesTest( + const ZetaDialPad(), + debugFillProperties, + ); + + final debugFillPropertiesSingleButton = { + 'primary': '"1"', + 'secondary': '""', + 'onTap': 'null', + 'topPadding': '3.0', + }; + debugFillPropertiesTest( + const ZetaDialPadButton(primary: '1'), + debugFillPropertiesSingleButton, + ); + }); + + group('Dimensions Tests', () {}); + + group('Styling Tests', () { + testWidgets('Hover styles for button are correct', (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); + }); + }); + + group('Interaction Tests', () { testWidgets('Initializes with correct parameters and is enabled', (WidgetTester tester) async { String number = ''; String text = ''; @@ -123,163 +173,139 @@ void main() { /// Allow all timers to end in text debounce await debounceWait(); + }); - await expectLater( - dialPadFinder, - matchesGoldenFile(goldenFile.getFileUri('dialpad_enabled')), + 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(), + ), ); - }); - }); - testWidgets('Initializes with correct parameters and is disabled', (WidgetTester tester) async { - const String number = ''; - const String text = ''; + final dialPadFinder = find.byType(ZetaDialPad); + final buttonFinder = find.byType(ZetaDialPadButton); - Future debounceWait() => tester.binding.delayed(const Duration(milliseconds: 500)); + 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 == '#'); - 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(goldenFile.getFileUri('dialpad_disabled')), - ); - }); - 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 ZetaDialPad dialPad = tester.firstWidget(dialPadFinder); + final List dialPadButtons = tester.widgetList(buttonFinder).toList(); - 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(); + final ZetaDialPadButton one = tester.firstWidget(oneFinder); + final ZetaDialPadButton two = tester.firstWidget(twoFinder); + final ZetaDialPadButton three = tester.firstWidget(threeFinder); - expect(inkWell.overlayColor?.resolve({WidgetState.hovered}), ZetaColorBase.cool.shade20); + /// Dial Pad built correctly. + expect(dialPad.buttonsPerRow, 3); + expect(dialPadButtons.length, 12); - await expectLater( - buttonFinder, - matchesGoldenFile(goldenFile.getFileUri('dialpadbutton')), - ); - }); + /// 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'); - testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { - final diagnostics = DiagnosticPropertiesBuilder(); - const ZetaDialPad().debugFillProperties(diagnostics); + /// Tap button with only number. + await tester.tap(oneFinder); + await tester.pump(); + expect(number, ''); - 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: +, #: }', - ); + /// 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(); + }); + }); - 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'); + group('Golden Tests', () { + goldenTest( + goldenFile, + const ZetaDialPad( + onNumber: print, + onText: print, + ), + ZetaDialPad, + 'dialpad_enabled', + screenSize: const Size(1000, 1000), + ); + goldenTest( + goldenFile, + const ZetaDialPad(), + ZetaDialPad, + 'dialpad_disabled', + screenSize: const Size(1000, 1000), + ); + goldenTest( + goldenFile, + const ZetaDialPadButton(primary: '1'), + ZetaDialPadButton, + 'dialpadbutton', + screenSize: const Size(1000, 1000), + ); }); + + group('Performance Tests', () {}); } diff --git a/test/src/components/dialpad/golden/dialpad_disabled.png b/test/src/components/dialpad/golden/dialpad_disabled.png index 5ee42187fe4268ea5ce61ab1da2bb74cea5c4acc..2e70f9684a5b9acdaa7b08d32d0641753a4bc297 100644 GIT binary patch literal 8944 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 P2IRP@rAf)rv)BF$Tg3QZ diff --git a/test/src/components/dialpad/golden/dialpad_enabled.png b/test/src/components/dialpad/golden/dialpad_enabled.png index 5ee42187fe4268ea5ce61ab1da2bb74cea5c4acc..2e70f9684a5b9acdaa7b08d32d0641753a4bc297 100644 GIT binary patch literal 8944 zcmeI2Ygkj)wuTp4rCurpTf8A~Py`i0Q31nETLdkF8!HIPttg;!69GZ)Ej<-Ph>D65 zAg!@rgMb(nh=i01A|Q!K1tW$?B9{;#h8RK;Le5wwR;v5iPfzzgd!HZm#{$V*i#f*} zbG+X-78!qYb^2)b;@Jp-e6)R=!!87wu8$zoJU^TXKgm1x%@O#b7PZUiGo*~B!-fBR z5cS#i-50A5d$&Cd!@!q^y0bq-kkgK8Y6$YkSRX+)=XHOn_?k62qEWJa zbenym!iPYmZWwdZmLW)7ZtP|RN!{@7EsJM-01qUj!t)M&@-ZwTYuYpfx#IS}AIwZo z?>)+h&W<*udmp_Wv+{V~SyHL%P38yjsxbA=h@6u++Rv zv|XIycH+ct>>^4Lz-_uk%{seR=5=L)SGc-E!?_m z$^#CGZ1vwCx3g~5j*Lq;&RHeE_2%(oh<_3}Ha6BJ_~4EVQtri-&dMt;SRLso-p3{! zBq@hZz2VvPT^F_12d<19gXg*82qvXz=J4@QRH@Q08qV_{WBO?||Ikxb#b(ePJWuKwhHSZd< zLsDBihodnNiTb0}r?i6k(U{Vj7mdGtmX$uv&NO59^%ZeV4uyw@2LuNfa~J9z&udb~%m5!bz5{C%-V`|8C45X27`GTCL<>3(#I2!3#Wo2b#RMdQmZ#mca z?X9(vyEK}sx3~8O+={w}h6cxc<}K`vCusO;*65!&abjJ8>wXK|M%=-yKH>2lD{`!? ztPWzD%Wu3LRN!hfPeX&#+mhS5VlM%oEM1wMoo&J9+9?l=XVwT^VXiZeP{yN=Ms0y0 z{2H_LZqIeDG5&#FbATR#?Chi{ejx?Z*$tTs@Q;J_OtdrO1njLg31io|#u`D&RTdvF zPu=bQ>ArpYB99&G*l~Gd+~maZ(6F$r?(Qy5$Jfe?Ghr8PT!QWJW_+p@p3ZskV)>X@ zY&m&tpe5IgGdgNsDX-JQTsb#QnklaEVz<@+}*ZYbGPKKforb zHe|mAyT-y}i9Mw<$4vh+qQS`Eo5aZoV|BrPsXBK1G)!up$yqayT5e-L=s9 z=4H(*9*}1(+7mcGi23a)WV3bRF16zvHrs$kr|*ZX76>9kBO=NznMOn&#&zdT*v|%7 zMu_R^3gs@AC;Uw)UOcr_M$j1przd*3@?p26ess>tM%>FK4V zrI2>%a=CoqSc)iFO3zaUJDEY4bAL%o@96N?9zLC#nr*|!_6G+WL2QUb-K4(CGTuUL zBkaI_^Go$dCE}}HU0vwH?)E&)x+#9*B-kH!JQd4{2N*S-xk4Mgi4Hrr#^tMq_RhYaB$U_$kr>v&k*vh5PW4@4`sBQs zX^@`0?2a##+3|Gwk0?zX9O5?2`#qlib*%Zb6nj-D)tDHIP4RR&>m==w@fXYGKdO@H zPR;Z`3BEr#8Lp#)Np4F~)RomrGB&Nx%@6Z=YPB9YO<1DY#*NOSoheh2QAyB4oCnC} zdAa_8_d{gUTgJ}s;I1Qv8U5Y8_hGYFKJR`&#X-z33GX1JvegoI;WfslvWd(LPvC!U z_8Lu70cEWJ-z(oGFJDbLV(PfK*&bhg@!;H#^$cgYwvwhJymrc+J0Y0Q_bYM!JE&{v zZN;gEPPu`jd-|7i@1RutYuYKjT19x^S2xbbeOK8kGZv(FzDCpIT>tZNsgD7I?=ZR4 z=AoyF_EzoF7JNcIu5frd;(se1PVhZWT_m3gB{nE0dth_w4w&n_PmCQID<-Mz=!DT2- zP-dVi6bY{}hlj(SJ$vR*-Wh^vetT=gZ3_X zO9^ep*;uTl^m+4}v6;X3=Y*9`W zjYhlN&aI!EOxDrSsqg6MkZL8@Gnqah7SI6p9$nPf*jOqvnXa5v;esO zdE~UOJ<2=x?QBi}L57Nr$B*5ijtz+71+A8>djGZCB)`?r6ShPUCZL1R#{x=$!J3-u zbmr}kh=^E&6?vQHf_!94rI@m^G6REvkdW1ob^$3V_GG3X1wTnrX(qq1foY@nj#eio zeDB^Fbmq*t zPxtZhLA6nU`K20RJe7ro-GGR-@kHH{#pPy;e&R`oJIQ6Ju1K^~I6ZVVI(F<>KtzPu zcXxY;1}Pn?!@!2g1XQGRqord*L%8)+jmJC@%;X7^nMEY#w2+&cR@J5nvSuha)VYHI3cP5*OF>kk6wPk1;=@#J)> zoT=xL^WseM_~aeOl$1lNWHRNWRR1SE;y;=>SyQv6_XhZlFAA$)gz|QcJ}?a4muo?r zi@X?cbcn_j{9Sb3HS|P{Z<+HNaDl%ELX3}P!4K=+Gjwhv3)CVAN}3sFo=p;nl% zwdnof;gwz8vWL#Eo#|AR&EEDN_68F+zmL4aZ`|MYe$o7qk-Wd_Z7b`Y+JfF^Y?bsXpeh7S zEQOwe&*JzgM0i$0l8tWT*w~nn!rE(Q+7`gdRpMYyN>RP7oSvfCGXZzKYGT=gm|eTj z#`w$cu)~CUMfV@Ci1N#?C*%EG_&h z+qoMGet-lqhX8{u9X+e}+h>*r;4*8|0ifW3tE;Os3JMB}_9vcJajJO9nwkeMq*C%g zi!+gU1N#l?5(l9r5l`_2T31mhVHo;TW_S0!)>fBS@suLM+goLQ8rIgr{fHL~uJ*M^_glKo%pkBlPmdJbFJ`+@3%Go@-21(FGj@iECwJb1K)&&jQS7 zvXonFg~2>cFP15Vy#GeBy`lvy1i8$foIW-%~IarP6 zowp1+J3CQlH9j6k&?tK|mg)(p`EiAllaqjhwGV$ma0(`Q0n)*-wFL{RXR69_z6RJ) zIYZqqSrV9^pAVf2nzP6OjG?QmE7VkoR49hltnxTLc6&D)7}QD!OTb`6#>X%2@9*FA zQ{nT82^+A3EZ-HvYL$4uJHd7t5U%DdOw_K{`F{-TgPsYcI~sqrjAvzQ3;U@J%P6hBRc|$*2=<%$ zhlWg`jWxvQH`di{fl!y4^*DhAY&f{#Bm~gYG#8Nkj7&GbuJl!nwpFS1Jfi_H$c{dp ze4T@$c#7ysP5;=K1$akqU=wg3dL67pp$oKY&awO!s{s6jFngOrJun9z3;HE8HgdIQp|UFrjt0brNQ{4Vi3Q|?vkRi5>3~*CFN*+tM;A8t#OIcey7wz}PpoZq>clfZ zeoee+)ry=Z>vFM74!Z#nTT1ce7nBR6XU~w!Sjhn8Ad=UEeqqW7!_m;_F@m`USm~6l z8Vzn$hH*7I8SPByN=49VwVQ;pzfpY6Ksqs*%&Wv^#xo~nZ{}f8$Vz>2YrhcW3_rgi zNx0EhZyVEIX1#FkWfs5Odhcqbay*!G4((Katl86M1qArdd&}1ySmiX@Loi5*sEZ~4 zp^c@09^Xc(Obs!{{lN}wYmU8oeZMQv7T;{G|J&MxFPyKZYD8riAV2>BuiBoK4hiYf zzip3_qi2InH+7M3Mg}4JPGBL>a=Oe%X+f2|eRS`VTB?p|9mMoew9jF;3-7?Olt<>y z&3YeAH>FEYHw3gre=ENEd*FoQA}rRt=PE(Vjdh>!ecg4P+Iw}|Jp0SvH)!!5tm(=T zQ^yHDx%Qq@Oo)x`{JTz=Y-o4VLe-z0(xq**ec#%ax2i7!j3y1*p}OaMV3P-1q5am- z4mw}|!sh40?{uia_Ic{>xtbJYg`Zs1W zL~e_UEZtXfUhT~g_}qW3CyyU@>RQsdqF=$};mZG0%~#5s<}1uyH*x=1QPN2L?+m7G zpdJpIVK-)>RNg0U)dR)MX0tCdHq0_LHML+5m8q!92~*ny*aKTgVq9Z5glxdm(RLQp zmv*=0^KS+X$7sK8!wPV$i6X}CkkR&?_9!+SZJ5wY9(|G&`pEDk?k3pjsF;+=+@2!~ zV|-FB;OCeZxI%LldRZ<`k^?HfkVoVT3T+d#&3tRdui44`D?ICgn`{OtcuKdmHDSgT z(C<&5o+Ob-L0YAC!hZUGFe60b;VH%xBa|c_S2Tb7#cx^DnWOG`f>v(P>r2_DX7czrNp1dAKnT@8ud&9fxuDETV;6#$rZo_XBTKU zvm5nNp51rC(t=1<(8L6TO=m}aq++U-o(Z%mNgA~iqb;cI6~yJ|4rYM$0dP9FyGO>w zJ&Ddg|Av4`H^F)(iXQJlf&J>$1~8jZyKL*AGYkBwrR?a7QkC=*k_OyE{yx)O!GuHz zjYe?V&>meN{wff6I?~SR(yfTzqn#@d_4c_r64O9pyACMT>w6rt`^hmuWhLVFHzx08i>}Y+ zZm5_M8aKgBr>BQkE&=5SvDxh|8dy(--UfgYbTWYVLJQRiJ=^#fMM1_3$18 z=Gt(?%G>?Ln+~yuH%!Jv8(w0t3Ja2=`ouqgScTUJP#4_X&y%XL%6BN=w|4B>Jc0w| ziEHiNlqu^Asj)jvP8o|`yU>Bk@{7IcpJk^i?Z0soP~`{!OS{`KTP z{bu&B(ES@jm)bY74E|~$8~s~=DX-@LU+L3-#q0jRlIq`>RR3He2^DIGXHAC)YAxz=OsUSl@kx8bO zXi=t!5g7tRjzB;#ND&YrL z;q}H3mS$hA-n<$?kgtxLo18+B726O*^2{nJxN|4y+Eutn_@6R6ij>e4N8ra7{zs3W zUIqWcR=LL@$TsA-$&u5+Srhc{{oO}0P}8cKO&Z(Qd?~4RQYR!&L4KE28|I<;IuB{; z`e$o!UpQTJ)*WB9tH>(vCsp^6?ZNMUzI*QV`v1myZNUQbtK{Y^U7bV zws9q+l*hV$a;Rm!Fr>KDe{pTq&tt;YA2-|-w(_$&?z}jUe!0=ZM9#zDxq4n~%qYM1 z*ki$5e!+#Z5@Gj??x5Xyy?Y2wR^usXQxm(gi1O%64#g9t!>H`}o{X*$e2GMQw4XX` z!#Fvj_B;kV^)Rg{+hzm(iT31Qbndx1JK@4(f(m2t+LDNd>`M2aoKlJ_)E#8@D4~A4 zh%W7_6&M?N3(W5^zw>@>p6`J|4%f;hZ8&{fw&PLFLxn0IukwQ7@|NK_^T8U3?!NtT ze)|=4`}ECKNYXFt?;@~<8TBp;18IpVL(YOhGvoM#sVVz?g9BmvZwRMwLgT!mk-e4% z1v8ognvAReS(L0SfvX}YcUjQ3;|4pB(G55O0o8c+nGq5hW;pzH60vsf`?<|RCuRQR zf-R22>4fLAwnBBmuur&76~0*l$q0U_y6qJ)KIZsMc17nE$CP5V!XU$ugPfyN>fZ0$ zJW-~dv5(@Q>us%^OCNKPD0d5Andm+gJUHs6*er>RKE6bhw(Z~FvE2yuBXmx)c{sG~K?j^{f3Dv1x&p4(Wobk6R!JslU?iI zpXAvOgd<3cN1$_O$;0MqrNV&9s~d{%?Ky3W_8OY@9a57=lA0!KS6`0tAV2%<%8SAw z!`K5v8-J9?(xdB=w<eONF1G{^7FM7`>6kKY!{3YJC zcrG8Mqn>*WLDr-ScT=w0H?Dci@!E7gTW2`=uRC9?M(*ZEy5En#@QVDBSFk}3lcest zgA+FOjYsEdr7w^(aw}E7Ygn&%Jmj)oTi@MDU&4)jE0Bwiw<{M&8A<7}GUB9+lD=VO zur_%*+}=rBi`;F;$6P48!}s>2?Mp+~=1OUtpzX8$YT@UHOD_he+{`F^l)>T@Ht}3P zV!tCsSJbla!R$U&zr+fYdvN1Um~@_EZoSjN8HQQ1aE~Ms*RdxykG;06*>_r*nkcaz zu6gO*O1~UDnWKc9$vTFUJe4|I^R{?1_ASdEPtuu%_dKN$`TT5N!|S+I*0|pJ6FZ*u zkQNMXnJ+!~mF}&>*Z-*D>En^UD`lq`E9UABC_G) z#{=82v>dlzPfunZYNKqwxE~vYob~NnxqDxyA!QBoM+nHTzt7;vfu;Lzcv6(6Cg15t z&pD>S>UuQi)Y(O)lA#ARldvd z?BfXYh;+80D_&oFaON6cLTIGFX`Fdvlivbg0?{CMwYOWBRaV+z@b+2R*$r29iwqjK z)K*r+TjGAe#M`J}@~s)5wtXCn=|b7NLxc4LFFeo-mjEaAZN4^jIO0HRkxv| z+UP7@Q6v{9$C0N40W2Q&wns*TZ^7;7k6KBQavc>$u>tep7MhyOnVI1(W5z^@skI}0 zSGN<+)6-LD&Wb&G@z`uPi8VE1W#?#$PQiqzF@}ab6j3O~9E(h6XLFi@x*0t^JvUS_ zug%aY*8N=0D&*;vfceFU_+x|%Z?uZqFUWa$c|ojZ6h>lUkA{9tFXhZaIb{yLP;R^! zT`rh4o*RgpDb%D?|5zgY7^gWPtbN8=AQ{i+4j9i1_&$rghRd55BZQV2r-u3Sz6WaJ z`;&W7rgKYO=m>{V^f?w~2D+HU&sU7C6H;-4Hl`2b9E#L(JaryHQe8sb+_vvbK2F!W zm1(iFwDcxpb~+&;q2dmH41cq)b4a%rW+1aX@#3#bt;wC2It@%=Q(CjhVr~TzYeGUzXcW1qB5=l;)od8!B_%e(5*=GxTVE`86WzOY;ZmG_Hh5WQ=tE(lR=RpG|_057CV#EEp7Ql^Oq0Caf20mdwT=T zX76WbXQw-69XsGcx4Kt=m1XjLQaw?GxOC!&uO#0$pyM@ZX|AFv3UNAYUu-izK3-pF zOB`(_{h;Ym8&9Xx?^Tf7UKr6Rm5VE+(q7E!5L3NTjI1IyCD8|;#$re#53Bowt9e&- zzYwl3XDL+y$b!+1+_*=^AxT6rDJMEl_U z4`wz91QD07T={m9oJVx7iS6jp7%6I|iY_4hjm$?SHx9-larU>Dt6TFH}^ zz1drY&O%XQ+IxrHptLFAVj#c&o9+VVQtuatHvD;Hea%Ba?j8F7Er8g6Kp z=$RWPKVezUHkXyPPBpF8H;fq>x@!$9JsFTMx!yP2^TD%c&n}eNmzI@nfV0>>@{0F& zj8QJNx%qfjPR=njy0pB!yTWtRf;TFABik@eHR_zn7(sPRo}kY33y}Au67ve)f5k8} zxZZ2P4e&2~;(8{9SD|a1KuTubFL-oQG?G6{G*a{f2wW|8AVK-JiC?i){^+Q>rKRP- z__#(vL9BQAmfY}ZyN~mWD!2&m{vIj~Wg;B0dAxqZ&4Mf!V+M0DCA>a#y0m6X)H_UG z_WhXKJmoz!csuiJ7~zpWQF1+ep9h582$8q4crh^>*F~3BR#qnUhS;DtK&IQXXOCzw zWo7+r%ga7K)UGa@$Grz?#&U1)*>3Z8A9W5ANKaxEa?q`wq;yLfb9@kO@|wyCA=nUKP=&90@I@@z^Ws!OC{FuSDlgr#QEQV+{meigaQJ&4-bzl+(`dAT zg$2V#rcUmSmt#|T-D3@J-q;6_={mK?98nL{V+W?X^VD4Rvl}l@lclT6?2FGpu72_2 z1x!-n2JF4?t0=33dEL16^fKoFefkAVS04SM*%*;H^x!>DjpcEhSD4-wm#$!|Rg+cC zEm|{QL6%_?geoD65dqyH?Gpin4yQQCXcLq@Y!B=DFr!4Q?}mo4aL2~Wb7arD*KpW0 zww6WqskLbDOl}^t()8}1tto^>kE+{t@Zdo>D8V}Xobm0N(-O%3o};Uqde4*WEb=>a zjD{*^H_LLu)PI`@A}}~+#BPUx$HyKeRzsIBRgSM-g&>Db{^r00dD*PC4Sr~gZY9M_ zN+O5~A@WbU9e|o_VK%D>1{5e^MH>7`RpYsHmy3wqcfL`WzNr8kHrQ5_y7`Mk|10(7 z_}a{Qd34d1NfB4fxth}JWz|2ptEzAFjX*2eH7U|m)UwcX+zg;jhd2C-?<*$;) zYt;j)p5;l?>&@Hmdmh>LmDxiCiFAL1w;#OMJP*`Wao@dV>Z@RCjk*42aET@Y&Hd+t zkE+`|?7EcRDV^*$!;@IRqn8M@CAnE4^Zn<|%X?qctUE;Q@o&9-;*;jUw=l^Am_VVI6783}BOaA^OwqlvCfkB*zM#Sr6<>s3G z$iMlAu(qGkt9{lx82)KkV$O`sCu~kjOY;o~2%9&)+grm^3as*?tCe^Q4aj5z?qGAW zgD}i4)lNQXCuch~E@uKZgKwai1RE%o&9qP!&D|1qGKdS(k{}q3mmmTog{0 zBAA-Mv0Mak#&M{HxAVY<#f%yA{Ps~`%+2oWqw3yvQAq0Zz<%N8HOOLyCDVMu#Nk zEBS^i98_RNKQ`HH8!@&9%*-UbgT!o8=si?b}DYSxa&Sf=o<6-DeGh) z#WQnb#I(sSLdVGdbuuz;7z}1dj@Manob$71C2mwiOxJbfTe;zI#>WWO1r{;~0jl7K zRMZie(N{{W_T@Ch{+ub{Pq~LA!8y>?)eR1}IN!P4(z`!YcCHr?9at^(!v5I0vws23 z*mJ1p@+NJgRFCw)-!GELi>rl_exMU|@pyb5gF^+bR8(C2t#)tm22)vSX=$x&O9;AL zi+W;t0ems)+hn^jG(AJZc$>lv4h{~bH8o1vmY&wLwVePuqNzD_D50Rh)yIeE+ot_+ z2`8SGll|daYolm&bw}h(xs7Xzz01il@tlgPs#wu0aD)pA@_Hv4J#)k57bA?Db91+d zXEW?|&1!QK;Q78GA)8fIRkd>4wkyM?Xngl@A|WMZl=x`m($mvq#r?}CEa2tk&#`4W${a@=-#kpMTfoa>|1(Y=rX*z zui3TDCld}+wk0E2f&bpiq?%XaQ{I~pK|$3x`?R-jZZ?{PGkiE%Wfwp%A}i~iu;R~z z-OmU>M|hKFqH6L?oXf&w>7%}u+=b!j($ZRIuj{|#YjMwI8#+IYGB>KjWg0IwyS=X@ z(?m$IgqC6}%Sq0ZzUfl( z5+LpP^g(-WvLK80mVl}q6b0>suBL69?!)fdoKrO@0 zVXU!JDMB#PkzZOi#m-msusd&exb_MPb&Hu3gux6nC#wRuKGGmn4_7t|jr{t6<}Or0 z0y#!#4^6aR7;+J)3k1e9BgH~DmoTnr*z8amX7vXuZozb6uDe{AO__HQ=nAGNv$MrG zK|Xj_Tq|KTZj#0~BUnQwq|Z^3Pke&d$;=$V5M_omQzui48_Pu{90P60xxMQrpI$S( z5Ql5Uq4dNc4Q3Ra??9Hhw{}K706R=eX=$k}lUu>w*bI2=2Ws`D>)$QRA_bJCj|8ce&W-%CaP zHQc@42tl@5fvdzeTym94Mrow0f3TI@mywt^*Pk~wHU{c;-XV5RA(P3x7mVp%$|Oi4 zGf+b0{Q*bm7hyFF-P-8+KO0>H#Y!T54Q5BdzP6(^VYAw>AZY_^ zqvaf@=wsNA)m1!ZEw_8tF$uaewXVN9!~QYXf0+69dY6CQ_KfCw7!Sn28UUSZ#)Ke5PpGoS`nCA1{z?vLkYzUtk zMT^8JD;Kx5#+k_0<-0E9ZYb;Pq?j0IqC#%O+{LV{ta?c3qZWzpffs=s2H^5};NrO4 zwV>bOuyRS_opCn1vD*b#bGv+Nwq-sLi;cisc5@w9EMtC~vr+* zi`8O*BgBB~13@R~*(^^)BKb!VDErgiy*npjW^dlaPE1SygvLGR7r*CWMN=~}auJ9o z@P~xZ)W3!gHozXMrDu!2RViTIx^;PKu{zEd0?Bk!_b_T_CtCDMPmdkE0x8RAew+mS z_PAi7Z<(4rW1|l%D?fh}5B8X@p5Bewu&uJP&))4lsH3Cc=g>_&{EHg~!M35WU zPH^VeO0D3$?@wNuw#c0h<`URmuv|w1<{HTrIxgD>vN7Rgx;Hp3!&4z~_%3tt$+@F) zTmlqlOw3g;MX1qkTKNTnm>(0}x@CP>r1<3i^5@*n@TN-y8!oHrq65!<$8sn)Pj-q* zy7X^?6p*JI3 zH$a)^2)S+^l?rXyaC_+8DtYLtHOfdr!l+f&6p6&_+V*f8UQH6IZjsga-_6UfO0w2y zG}H`Q#1dPK<#%-VAxXy!l=DPIg6)-Ji9NVP0q!jozjT+FM@P^rWzB!p&1$h#`(Ew* zOd(qmj3supJYuinT$nWagUCHu`+EQ9&0!m`1BXA?#BxckgCNqy$iG}Tb6FvV_D=}~ z)#;&jGAQOh48@zR7T4eYRjV(Rvwn96Qog;g0g8;LME@7ycc;MjEA#VPZ|#bXyNFH9=KH2@wt2% z)ae7TSm0H#yF~o}LT^l@LJ5cVyL@&YEWMM3Mwh+ZZp!cczm?W(T&qg-8h=jmyMGAm zpRK78BH1pgy?E1OV!&+!WQJtN2{<>cZG;&B0~Sh6gx(2|xKV6!q6Z+f-E|TAPfSsz z#Src2nW}Qn@Ca z2K%}J%0AU0Q|f{bGQx*6i#5E-p_bQlx>GE99x@`El;~lQS78CRSZencpTu?1&(vaF zcjT_o&ai+C4-O?D@`2^i`wUq7at?*cYdTk-dEkk+^XgxDPiHoC69{)w9-M}Yo9+2ZY#RtDNlOfSe-j_I&ZVS##%F>Wht zMv;=AnDF2~eB{wIb0+m%*YB5A$o^-=tD6#le?f1tu&)7UOP@S>(sa4pM$Qx}AoT(I z3E(0$D()^7gAJAbAkxH&j0vvKZyUDnwrK# zEl7= z99&ab?1@H$(Zhrd5B|5Zavm@y*jo981uSh?qf8`)lGKel0z*mr!od_}z z>0&_1vwJJ}1{9!%U{nBV!|^B>)gJ}tTvQ*psvGdlgu`+mze+14F)%?v@+=k$#$?Py zz(N3O1KVq#2x81^_6>Kxs}|es0{9P1htxBUIpXjyCi^m}y9xZ^k%pUgMs);c`h7No zJQ_q6)v%zX3``p1&-sU{{`PKU^%bb?1?Yz}o%%9~vRoRu7MRW4VgzhXQ!_6W@#Z*r z>uw~-ZxfS>1MB{L@B1?m{-?R{|B2-PiDJlqmf=6k@Mjm~{=eIUtwI@hNwpNt2l_dH P2IRP@rAf)rv)BF$Tg3QZ diff --git a/test/src/components/dialpad/golden/dialpadbutton.png b/test/src/components/dialpad/golden/dialpadbutton.png index 7bbc925e25b8127a7558cc62e7e3f9cc4306d4e2..efb8765e5ae7bc476f4a3d1f77798bfd9fa81061 100644 GIT binary patch literal 4315 zcmeHK>r)d~6u$|KP>N#52S{QPr!%%z3nB_pd1*CBTcG2kg`&V1gz_j6Q4?Yy(T<`L zi8dW0f@G8_DI&y}3WNj@kwj}EV|k<+6LdBy6#^PGK*+0i{m_4)_$~Wk@7%lh{?0kS zbAD%c&wP^pRVvrGpww3xQYD4Ti3qK^#84m zTgY9X>b=AKtROBKQE$YpSaQ-}uncJR(j%p%9Rvt{{KTJ z$4(PxdWi0~2QIR%b-7G^d{-F&&$Y!^EC5;Fz5ty6hy*|!g#$sBVdh{lo641>i$#VD zx_kNa0H;Bt45H@e=Z`LzB`UH3xO6$~CUjA)ZrTCO9%%W#sot=*DzWn@d~}y0i0Cy-=%kg+_MMXse-53%QqU`SO9tZ~@ zvrgU8(gI^RV`WB1SW)AjNyC&ge^=9#CX;C(ZJj_Mpt9NQ(PaSKEQ0J59Xz=9QD2|P z@N78N zj}6V}JUk0oT4W0%1rVZw0s{k)(b2r8+v)$z&N6F+LS9{6-MfR7*&tF{S{l;*lMiBh zGtwW@{za_wZ9h3CW@>6RYgm#f<~5iJGi{Od{;6CaL#S8n*sBEg@V6!AC-0|^o%iEXzxl8rOKS{1dr;?O4 z_5bbQh7qEf>mj}?wAQD@<*>CW6nGvX`Z5y=s6fHSk>qkYVt>^|%*n|?G}V#L>!HSR zT`OWX7u3|$aP@k9!wAZRy%n?0Fnsn1ixpMou&9PjjV0pdW@W!9S!J`Bzr^TdxW_#ohlxY*SkNA5XD$)#X}`@xPwVlu0>BDJ_junF`1jv z6^zF>X8l9c6+t4Ah)DYw3agWlrqyc4+*ZNRn3XYUe2@|%pu(8>e7<<1$59r5gL&_F zUI(`4riTKgw<3^AqtQ^eVfo1SO|NeHwh_f{hK@ofbv64Yje| zF7l>L!8)Ce-uk+$O(t80GBMcr9knB!$LB8+T4BaBB*S6V_B`2%$ZqKHkh9x3T zwSD-?(1AnuTgSBMej6*MZ01F|q_tJxKo#H5+R5RDDPdL^*3m)o=FPas$Ve(TY(YSP zs^i%7gzT^z~6Q)|ZUovClqpKGmnjk-DHQ&&(h3!&lcx7d!^2Lj*J{k;oPB%)ujKYpG zCsR@KCL7kO+dKkOPo>l8X0w@VW4WYa#QFIiSdoYpqM@8wt=8C6g+HQ}ieR8c493i_ zE1@@6LxV{~sM|`YOP(l@m_#XSgpn_;=p~cMK^47sbA14CJklp{9DhQLw)XbfC#zKD zVx;s3$K>HGm?QJVgu%KVHjrP?W_Txv$~dxk?wjKKXtW+ow69ZOE1#PemxoUepPpKJ vPhW)|ae2h$5tm0?bN;*OX)OP*vDk17)<9wA!uEYoo*-d+(zfQUnJ4}QjZ7Nk literal 4708 zcmeH~`&Uy}7Jv^RNJWe~BX0th&I{UUbvz z_W{_u>-6zs!FL}irKBJ`d~xS{lBM)YOXT5=m*{3A+ux zu-9xOZyga5d6rgQ*gxvNe^2o9$@|XMsYl21!gyBB{D=(-YHc}5#PO8cC^26jmPGZf zCNC}Ly?rH@r^%11(xGP~X=94jAlD~l%HDR&q+IWLO2!xnL6tRxaDtfJvA>V_2?aTPQy|g@`MZY{?B#d@w4uHv?KU`LrGMn+WXG$o1-W zF%s$EZi8D_*6PFRr&E26VAKU}x_ZQ8R|z|PL#>}t$W&C!5ji0PEB7l9X#h9ulXYPx&bs@>bB2+sYt2~5D$F@!+1C$H|bjZ z!FAtRN~;G%Dvid6{MtkQbnR!P-@3qEvZzbS;4;6?)u(-w;C4Bk4a_dGZ#s z)^DKB@VbkiWG;5j;yJ4<6aZnP+_;M<`=Y6bVg#7OqZuGf){mb{jZ$q9bhYLeHrsym zpXK|x)&S^|k&Z?qyR80zJEc@Z2>pI^=34e@zIYb^j9Zy&hra`xHp)zbqcUG%20*HC z=ljJMdDe>(Xo5JGbCS^~EZJXc|7j=KTw5@|`My}x22p~{04BVDeNL2L@)G&U!m-wR zsk$`tw`2gOjt!WvBJ(%Zb;3l_Q!`=BlHRnt;LZF23N&`1E|K-6gu)t)PoS$sC6$ck zi&v}wIASG;Z=RxM%APBqsrCSH<$O{7z=O7^DM9@0!4*@ea8MwTg!7C>jH3&(i_ZRy zk!JuvM*rE$0ne0#cPnq8)+aPSNeY+uGB9G#5V+&2oBz_Hh|K&Gg(Tk00 zo4ig20;VBDJ!i8(TbUEReSfT^{RzITtU3`B z8@ucSz2Dj23aoQZ@_0O4Er~yyP)yOJs&2$kskD##pbO<%4GE1#&rD6t$F}XMrYNSS zp2D@R80OBTTB}4PCnwKn`}%Z0e+c){Yr~YYvrC(SN>ql|(GPKAUQ%3E?lr!Qc7Z4! z{1&aC{S^SK=W^pyI8jFyf|8zIiRQ9pLC%A`fmBU$~HU8ec@LREdF@dLimVD=q zR0B)Q<9`)K@u%_PujPS>N#4FSmVN2mh8T)BQtTn8 zU!qVbFJKokdvuo!F5*NDF-nz6ER#iiRWM2If=6B81Vy39A>70+Gh%DaSXqjX{zE03 z9Uc}|B@hTw=SPpB1kNRu5LW7PrO~**I7#Jyy)HBuCwl$5361prb$Yfs+&X>8nA!6x z+FpR5=tU8{7y=Fl(V;y&D3OsJ851xi+4S4YHHku8s~8OcEu)JNhDZ8Egpv1}mu3wtMO~;d) z!)9}e?QsOa??*Vhwp6zWY!TQZuti{tz!rfm0$T*O2>iJSOy~`I&XeYO7}_oPFAh$h Lz#ixNU%B^xa#P#` diff --git a/test/src/components/fab/fab_test.dart b/test/src/components/fab/fab_test.dart new file mode 100644 index 00000000..2820f8ef --- /dev/null +++ b/test/src/components/fab/fab_test.dart @@ -0,0 +1,270 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; +import '../../../test_utils/utils.dart'; + +void main() { + const String parentFolder = 'fab'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('Accessibility Tests', () {}); + + group('Content Tests', () { + final debugFillProperties = { + 'label': 'null', + 'onPressed': 'null', + 'type': 'primary', + 'size': 'small', + 'shape': 'full', + 'icon': 'IconData(U+0E009)', + 'initiallyExpanded': 'false', + 'focusNode': 'null', + }; + debugFillPropertiesTest( + const ZetaFAB(), + debugFillProperties, + ); + + 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); + }); + + 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.expanded, false); + expect(fab.type, ZetaFabType.inverse); + expect(fab.shape, ZetaWidgetBorder.rounded); + }); + + testWidgets('Expanded', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaFAB( + expanded: true, + onPressed: () {}, + label: 'Label', + type: ZetaFabType.secondary, + shape: ZetaWidgetBorder.sharp, + ), + ), + ); + + final fabFinder = find.byType(ZetaFAB); + final ZetaFAB fab = tester.firstWidget(fabFinder); + + expect(fab.expanded, true); + expect(fab.type, ZetaFabType.secondary); + expect(fab.shape, ZetaWidgetBorder.sharp); + }); + + testWidgets('Disabled FAB', (WidgetTester tester) async { + final scrollController = ScrollController(); + await tester.pumpWidget( + TestApp( + home: ZetaFAB(scrollController: scrollController, label: 'Disabled'), + ), + ); + + final fabFinder = find.byType(ZetaFAB); + final ZetaFAB fab = tester.firstWidget(fabFinder); + + expect(fab.onPressed, isNull); + expect(fab.type, ZetaFabType.primary); + expect(fab.shape, ZetaWidgetBorder.full); + }); + + testWidgets('Label is correct', (WidgetTester tester) async { + final scrollController = ScrollController(); + StateSetter? setState; + bool expanded = false; + + await tester.pumpWidget( + TestApp( + home: StatefulBuilder( + builder: (context, setState2) { + setState = setState2; + return ZetaFAB( + scrollController: scrollController, + expanded: expanded, + label: 'Label', + onPressed: () {}, + ); + }, + ), + ), + ); + + final labelFinder = find.text('Label'); + + expect(labelFinder, findsOne); + + setState?.call(() => expanded = true); + + await tester.pumpAndSettle(); + expect(labelFinder, findsOne); + }); + }); + + group('Dimensions Tests', () {}); + + group('Styling Tests', () { + testWidgets('hover colours are correct', (WidgetTester tester) async { + final FocusNode node = FocusNode(); + + await tester.pumpWidget( + TestApp( + home: ZetaFAB( + expanded: 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.expanded, 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: ZetaBorders.medium), + ); + }); + }); + + group('Interaction Tests', () { + 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), + ), + ); + final TestGesture e = await tester.press(find.byType(ZetaFAB)); + + await tester.pumpAndSettle(); + + await e.up(); + expect(isPressed, isTrue); + }); + }); + + group('Golden Tests', () { + goldenTest( + goldenFile, + ZetaFAB( + scrollController: ScrollController(), + label: 'Label', + onPressed: () {}, + ), + ZetaFAB, + 'FAB_default', + ); + goldenTestWithCallbacks( + goldenFile, + ZetaFAB(scrollController: ScrollController(), label: 'Label', onPressed: () => {}), + ZetaFAB, + 'FAB_pressed', + after: (tester) async { + await tester.press(find.byType(ZetaFAB)); + await tester.pumpAndSettle(); + }, + ); + goldenTest( + goldenFile, + ZetaFAB( + scrollController: ScrollController(), + onPressed: () {}, + type: ZetaFabType.inverse, + shape: ZetaWidgetBorder.rounded, + size: ZetaFabSize.large, + ), + ZetaFAB, + 'FAB_inverse', + ); + goldenTestWithCallbacks( + goldenFile, + ZetaFAB(scrollController: ScrollController(), label: 'Label', onPressed: () => {}), + ZetaFAB, + 'FAB_pressed', + after: (tester) async { + await tester.press(find.byType(ZetaFAB)); + await tester.pumpAndSettle(); + }, + ); + goldenTest( + goldenFile, + ZetaFAB( + expanded: true, + onPressed: () {}, + label: 'Label', + type: ZetaFabType.secondary, + shape: ZetaWidgetBorder.sharp, + ), + ZetaFAB, + 'FAB_secondary', + ); + goldenTest( + goldenFile, + ZetaFAB(scrollController: ScrollController(), label: 'Disabled'), + ZetaFAB, + 'FAB_disabled', + ); + }); + + group('Performance Tests', () {}); +} diff --git a/test/src/components/fabs/fab_test.dart b/test/src/components/fabs/fab_test.dart deleted file mode 100644 index f82fd5ed..00000000 --- a/test/src/components/fabs/fab_test.dart +++ /dev/null @@ -1,219 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; -import '../../../test_utils/test_app.dart'; -import '../../../test_utils/tolerant_comparator.dart'; -import '../../../test_utils/utils.dart'; - -void main() { - const goldenFile = GoldenFiles(component: 'fab'); - - setUpAll(() { - goldenFileComparator = TolerantComparator(goldenFile.uri); - }); - - 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(goldenFile.getFileUri('FAB_default')), - ); - }); - - 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), - ), - ); - final TestGesture e = await tester.press(find.byType(ZetaFAB)); - - await tester.pumpAndSettle(); - - await expectLater( - find.byType(ZetaFAB), - matchesGoldenFile(goldenFile.getFileUri('FAB_pressed')), - ); - - await e.up(); - 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.expanded, false); - expect(fab.type, ZetaFabType.inverse); - expect(fab.shape, ZetaWidgetBorder.rounded); - - await expectLater( - find.byType(ZetaFAB), - matchesGoldenFile(goldenFile.getFileUri('FAB_inverse')), - ); - }); - - testWidgets('Expanded', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaFAB( - expanded: true, - onPressed: () {}, - label: 'Label', - type: ZetaFabType.secondary, - shape: ZetaWidgetBorder.sharp, - ), - ), - ); - - final fabFinder = find.byType(ZetaFAB); - final ZetaFAB fab = tester.firstWidget(fabFinder); - - expect(fab.expanded, true); - expect(fab.type, ZetaFabType.secondary); - expect(fab.shape, ZetaWidgetBorder.sharp); - - await expectLater( - find.byType(ZetaFAB), - matchesGoldenFile(goldenFile.getFileUri('FAB_secondary')), - ); - }); - testWidgets('ZetaFAB interactive', (WidgetTester tester) async { - final FocusNode node = FocusNode(); - - await tester.pumpWidget( - TestApp( - home: ZetaFAB( - expanded: 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.expanded, 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: ZetaBorders.medium), - ); - }); - - testWidgets('Disabled FAB', (WidgetTester tester) async { - final scrollController = ScrollController(); - await tester.pumpWidget( - TestApp( - home: ZetaFAB(scrollController: scrollController, label: 'Disabled'), - ), - ); - - final fabFinder = find.byType(ZetaFAB); - final ZetaFAB fab = tester.firstWidget(fabFinder); - - expect(fab.onPressed, isNull); - expect(fab.type, ZetaFabType.primary); - expect(fab.shape, ZetaWidgetBorder.full); - - await expectLater( - find.byType(ZetaFAB), - matchesGoldenFile(goldenFile.getFileUri('FAB_disabled')), - ); - }); - - 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'), 'false'); - expect(diagnostics.finder('focusNode'), 'null'); - }); - - testWidgets('Label is correct', (WidgetTester tester) async { - final scrollController = ScrollController(); - StateSetter? setState; - bool expanded = false; - - await tester.pumpWidget( - TestApp( - home: StatefulBuilder( - builder: (context, setState2) { - setState = setState2; - return ZetaFAB( - scrollController: scrollController, - expanded: expanded, - label: 'Label', - onPressed: () {}, - ); - }, - ), - ), - ); - - final labelFinder = find.text('Label'); - - expect(labelFinder, findsOne); - - setState?.call(() => expanded = true); - - await tester.pumpAndSettle(); - expect(labelFinder, findsOne); - }); -} diff --git a/test/src/components/fabs/golden/FAB_default.png b/test/src/components/fabs/golden/FAB_default.png deleted file mode 100644 index 8ab4dac26b1a97669fe4749481a7a4ce46ed0c4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4754 zcmeH}>sM1(8pd}50?JKsbwmV0WfUz8j0NN#E}}pcQbkCp1W;ID3<)X(Dj@`6Dj)(n zZ9(o>I-&_ANDAS8mq^tBB_tx3R8fH(2*nW60FgP&tTlhZe9ZoG&OYlod%y4RefGO{ z?$<$n`nyba0RZ#^{C&>>pkV<35~rgDe<@AQyb3=MiRb)$0E=(B08ca%eFDzwz#~m3 zCI zo4B%mdq2N?2g&_=r_4BOt)5QxM4{5kKyd5C2Gzp^fal@jG>vyF(@Sl#H!8|-bLjL| z;p5W#MkaaFE7=NBYl7(`8OF3ukzleYcLu=bM54jn+Dej0zuTNWX>P(6uKMIZ^>KUu zDi+^;gHl23^J4oUK-B&cO2#7TrJ#8E0Q-Uz#jU@x(`N`@(VBX z2^V~EdP1G~1Ys!ps03oRrOPC3YaZ5A54aeor8J{t=HsVYB zXhj{9HR!~Liy{rfY5w(Q5=edoPUx7OQaoi?5G0ICw?!8*Np7{!ZItwJGd=}?q=`J_ zf7mQisaJkeeR%%b4n-KBTTX2jMZ;?kbXa%|t_=&ayX-^VswiXHAx<-H5h#_6qME84F;E$>#O?G3hf6qIk#^sP&2P2E}Iuv)R$ z$t9Sn^}%y%p0%(p`<~M+p5wz#2}I%WH1ZAvsMgR%zKi;}-d`%uV;Vs7#`Oy&CW}>D zQp**kFfMH1l$sd=7svW3-cuphRbF#x6m zrDaa~!YFzOv66dpyU21{vR_K=rL%4GKlery0?^~ldXoHz%}?#`rCgkl_W15KFG3;*$u9<62m!om7?MmU z{m`m)ID zVTR#iOlTq8NZ0TaflL(aMxHJ*rn+0AjS359dkloyFuJZvC8=8vv!FdM;lIgtU*ucf zIzXBG%+hg0oOcapV@ri@xABYI0ccx1RO~vFp2Q({MLB!u<_%01kcCUd>k4g9y%CYj z9_Iz27;LVG1*!7;G(V@0??a}hpF6^f9>7E~J z4aTu{MiubhG)O-g^OnoqT=|RQy zcn(Y4(Z)yjQX>I)R+5;pIXv4wDX)PKjt9N0Zo|B+F83(&;D4L03*2-K%bn*{<^Ze? zbZER&9)1`r>EazGKA%6IkG988rf{L$C&?a9+VNC3Pxr(mfqzZGeu7}sGT$|sRCD}) zJ)Xotf@*G?kp=8*&PwAl0s&X?I06a4fX;3JEKb3G1;>z@0Oag}HC}fBEV!fzTX(mm z7XTT?YN?ulnt+;snt+;snt+;snt+;sn!x{&z$abSledV%18!6BO&kQA4Dx09L}mUP D)R5gM diff --git a/test/src/components/fabs/golden/FAB_disabled.png b/test/src/components/fabs/golden/FAB_disabled.png deleted file mode 100644 index 24e1062f26bee35450fec28d77f284b33d665c59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4446 zcmeH}`%}_c7{*_`B)KJ(ZM14=xmjD2=}b58bkp1owF_mI+M3B);&lZB6}F4j4=RQ)}e~r${sj$gIo~Swp-)D4S&~px^~Gkle5PH|&p`U(THOeP+&?&v~9R=W0|$ z*kVT)M*vtHzHiS#09y|Ln`ra`q;i9DJ{_4*SqH;*0jJY#1X5Bk7~-Rob$>8E}0sm9KqvVSROc zR9#l}L+3vtiP8ENj6RL07>x>ZDX8S}^T>^6b+fQpSkG~<)|gxhZbeF*r91*b&Xe=% z+TRaJ5`ybKrT80v0C2naD2@4JY`jLy+g6%KrqU~0u)RFOreSVO2dN>jlqlVDie(2+ zesXfUE_7G7o{8PAAtXD!c*7$E@uPb_WghUi1J!s_7IRT+RBw%OxT6p51^C z9`qZdyUTNCDQ3<E<>$#J8gT1BO({PC;N9*H=3ey5lT&Ib0{5AL7 z%}q(CST=yStihXv^)m6W$~k3QscD)O68zGKF=tf7c(XB?-ZR=(Lktt3>`DdOHMlml zX8Lhpe5F!YqY$TReF5%9qTWpBDQg=~No?XfE^7>Zo&Zr5xR#hS3dTs@c{wn&A+BCi zG00%(2UiwU9;uPFv{?>Hw}e?Fd4ui-;j8;?tf09*hKQ{24+#-X>U73fuMoNfS#<2{EDpBID@%{@7VvIWX6 z6ujsj!@Qlg#c`FL1}E^T>RI)D`7fL-fh`o?Q=0KFoYyoVBAHQu#pn3bP&VLG&1YVT#11C>R4=!{9P~ph|T^@m}iajau4xq}B^2oTUi)kE=dRVKC5b^yI)@3%aE(-?` z^K?vA>dUz^&fgF)KEdi(u%if=d~{R0hTX?N~6S9(!Xm0zmhw04M_>n*RsE@rt!D)HsJgTl<@Un6B4PfKw%|-OJjsx6pcZ^GHIO$3 zBUZ&9H)Le-NLZ?ZZ;QLdmPp!gC}5hFdk~3OEJ-F`d!zoZ+-(i793Xv7zIPEXx%RxC zNc)`PHxD^?qGl~?a&oWuV|npMO~(BbIn%bFx`-I}Mm5dy?tzyxD`9*_Op?;Zlo((Z zPcZ)MnvY3F^3hCOL;LNz-p#RX$YF=Kv;C8}R|DDtV+hi24Vx{)J?dW#fYDLii$u}h z)zxatu1>0f3psVhZ2V`nYe*m~AjNo8m5|%af$xg8Y$HUcfwL zyn{MOl>HYhj#Btthn+*f@eZ}@+O;z-tXLG3JqWPhN0j*-uIp*c7Bn}wJZ&$DSZA2c zOun@fm|^caQ4_}oF-WIQX6oUuF03M#(kS<9uT4ImPk%b|g6(@QM{`!t-;>0xA5D$F z;PUquXBAZ|CXda(Q03ylRC~~)l>zwt^_S8xgmUS#2(JBljr{h Dy$(uB diff --git a/test/src/components/fabs/golden/FAB_inverse.png b/test/src/components/fabs/golden/FAB_inverse.png deleted file mode 100644 index 41bf5c0f2bb19182a90763d2cac9cf67d3ab9219..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4352 zcmeHJX;2eq82&<}poo#7YB?fUuUa`oNu)r6B1*9ZtmqJp1)Mk>$}I{3HbFWH)|l8L zAXgZVh6x}Tkwb%oA_V1%ILINl0a7Cnu8?4^E~6c%|D4LNPWI34zTbO&&-XmfCq6%~$Nj??u~uh_Qeg6gRSAAt@?@(k(SsQVO>sUlBI-=v~7=li9? zp+7B-aw(;Ei;HPz+wi1usGltCCRw;h%>n4Pr-UV$JH`Z`fLhCiGu!byC%KXoX^Hjl zqK8fOkaAk=o3|??BZI|60+6OiM???RUWjTZs-yZ6XQ`f?kY@7q6+ef81$rf8baa$m z06^^>%CTopa4J!cTR_`t@7d2nO%cML$!8c4E9V-`~fPPXt69P{VJ=JI5cMs%!t z@On-~v9Wpz#b1-jix}_bV5MIIaHD?wCJKwi@^ftBi{x=D`zeii!q$j|6b^@D_uj!9 zInu=N!>DCugSpYou5yWHfnPOq+t(8Zx4eZDhR=726X4zoTgv=#tIVApq|A%=s;iyx z8&_VK%7JNY=L}IFD8p(9*p6RNq*`$4IYdt-2=j zi|>)Qc;@47$s8s+c;=EvBd^^MwBfq@)siy zGV?G}c(X%x6(JNP+)60)>98(^XG=wdfA&Wbl>E&7NP>zpU2W{$R#rhMP+G#CoTul<(=;7Vo*NU6A2$`?7X+;L6l`j0auCjCdOs!&=aTu& zjW2$mLV^HJXpTUHn(U30#WiG!=nIMXMKM5%`6!`b7d_7@_D38H(%~{Y%1qadHk_!K zU1>$@QnnV=>j7Z!ra-e?Wmu}=a)Jat1%3BdtK|%)olUpm6*~xK)TWA7(X5_D{Pbh) zq^XEEDHSHf_G3m_)^FuPhM6x#exLK`k>dwZ58E=8ceY-o#tSx+$YgRMj@T&{i>v1H z2d(h{5Q5A6mGw-VH#%QEf)O4QDATshhk6L0wr9e3XI$`f7j@N#Q8T%=IK?SZJ|*uk z+)ZEPVRx*oAD&<*VQCAmMpQ-SLSt`8Sj8WmNRV`K_6%}lgWjt3=9=gRcRnV+D%{*z ze*1Knq&z)6J!*2hz;xX>G(o_R-C&F0EwQFk5i^ng$`@3cpl|MBRP)_R`r*~$0Vn&;r9D$1m-%bQ7N2O;1Lb56Q2A!~n|3V?aGbQ1VX z1!=>Vb|Pu<)Z)2RUpFn6v|Q41>GS^*wK~%3=zpxEEDL1A^B?G6qtm6ZR)EKTFSioc HfaJdcV>>IR diff --git a/test/src/components/fabs/golden/FAB_pressed.png b/test/src/components/fabs/golden/FAB_pressed.png deleted file mode 100644 index 10365d1177bd0e25fb188f97bef163c7039ead94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4883 zcmeH~X;hO}8pkh-f*|TRl|=}lbw+0fSwv_Q2%(}djDvJ!3m^nVma-T?)<6ihwMZ2g z90ftcRznGfNCClw1W+U{n1GQ)2q9Q0n1m21yhH*debIiIGxKpi=6<=$dG0;W`Q7LL zzvq^GdBo4mWV;Cfz|8;e2af_^UG;?jF@Wk-+0smlQ zxD^jD=_?!_3@#a(z|8i6>IBHdl{1X~w> zL$o!@m~U3zNJ_l0r*1t`lMznY^u+Z>+5?Ytm->IPO@lDNpk4bo z2I1}=Y;`eVG)>v_)evD^l;$3#B;(Ys9^DsEjdHmIYC0)-r~sG=QfEVm4J~6m7IY zHj`d0)vNoPwe+F6lCUHT`rX4$*l=Wl{Q4+6j>r!Jpu%S-!fscwpWIY39;GFyVjY`g zpG7)(c6hBUY8XlLm6QemoQ?kZH}6HStUKeYXQQCL+N&i2-$Dz)EbVvCWJJ_T^x8ph zGZM@$Gm%S&R>kGBO!)TncE z7$H9zo*H^bIBwH6qNZ4=L7kP-PyQSGs)r`#myx zF!v#@Kh76ooEN_2UW&&2B7A#GNX2xetp2-Nb)vzSlia(K$1g0DtmL`5xUZ60v#T+# zE}`eu1fHg+JbmJEjADe8`QlRde62y29LHu82$a<_0Nx!d8iU>i1t=@Z!f5*&*y=Ni zR77lMO5K!-JoouuuDW=oGp5=pT2U^Ohbc(u6zs0nh@X-cCY|K!NcLVSXS0oU^Rmk9 zrFC>GVU)fB0NNX$`Ydhi`1|K0MD3N4ZBzgTJZVaFZjCQToO!vGIX%=+MFyZ}w@@wR zbE?LBcDE#G_q3P*@5l%Y(lUi$(AxK^?;tv>k29waoQbrCLaD_+b}CCA?)tb-uzg=4 zRcbcyS@Fe9ZfHGF6n-abvM-HqU2%wP4jV|@aW(1z>=qSu0_tsk_YK5pUY@=A?~yss zPwSw`*MCHKOo-@=yUm@6!#~*O4>95p`B5$clPv&@4l{k@&LNx?Oa9pO+7tO zgP^%RGk0a32|sxYz*94ty;Fo(px3|4Jttx2i0yf7?ONko#u2O5Nt&+E z(u;x0a&56K=BTw+Ch_n*eE}oLa zxbxVWr>3KX09bZ3>S#-rkGNwxh99|8QS!~Mn^_wo+cS@+GCNt%AOLDYLZIvRJUW?7 zX>h)p+FiiX?tmfCLPFX#V~?N?yhcpvaQ1NsAB;pUx7t^d^x*Y`Y8rCz%Z}zG%x&1(wiAe01z(-a~7y5=KWEYld-sdm>*P4 z6yA&2NBU?ETb(vsUYd`{5W~sRudVN1ec4 zN`sm9tslDMP!%drC|MM9C{JPpz%Aik7nclm2YP~}vTarsq=zA_ZTwT~KE*R-IkHye zSSXR@*lhqNuB8h=BbD&$;1hz%LP@K(*;o1MAhu~Z2ub2xgSZNdkGe%Q!cp6;s(uu8 zGaJ?V(54mpf3~?ac-1ZRh2~(+;%cWt4!@KJAJ&14a9rcj)QE_HN~c-v+?BKfvnnz5v%R zIALfEz?L1}02EoSMf4fNm2c-;1j}ZRzM(EuM?gnFM?gnFM?gnFM?gnFM?gp5|4U#h Z3={fmy&l&R{)GeJf9S};<^wTb{|4I!Gm!uQ diff --git a/test/src/components/fabs/golden/FAB_secondary.png b/test/src/components/fabs/golden/FAB_secondary.png deleted file mode 100644 index 40f2cf3ba1d760f5282a78a9f44b82509eaf100d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3599 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|sA;FCi(^Q|oVRzayXT}zG(0R%FZcb>prJYGnDb;O4yP{BU`|GFPwgd_ z{B*XS@ec^Paf|aGV}0`>GqI>!M-(De7F2Mg@f&YT+B);+j&$|v*snI{zi%_Y|JyKn zeyx!hvtQ=>XLCPZJ8S*g4(KB3tZna@fsT0I&cMjPV8q12z;J>?fPq0l*@1zfp$E61 z^=iEvdq4cyvF-aqk#tG>H}#f%a{oM<}pcoS*qpclK(>|W*jcR)G*x_k2c?`q8< za)qqiFe^5H5?%BE_pfg0<@?-9pI+U5{I`7l?<41DP<7@f%MP?p9 zdGg_ndv$3qfByWFawhF|_U!+^&o_U(!##cay#HUPzds&czFyy!pMl|Y0m3zeJPZyR zpMU-@@?-YfuG_PzeAWGZB`;rH>1StPIJe;>^OLjE^1ge87#J$Nb~LdvFrO*r}| zrK{LyAJE4Z)nWUZN(<`iYyQ2wCXx;e>v=`em8{ttXZUYSxyqV7FaGMT`TPtADouX6 zzi7QL|L@6Kpst(+y*12CKw6CvNT#rGFfdFIRA69maBBd@nIt)aH?3@JV%GcyM)854 z4|u6yAcgK3)lPO)jt1Rm&{3;c7)?u~X=yYqQC_`{=A+SkG@6e_Vm_K#&luktl~{Py RXb}Stc)I$ztaD0e0swT;O2hyF diff --git a/test/src/components/icon/icon_test.dart b/test/src/components/icon/icon_test.dart index 05cbbb5e..3afa42bd 100644 --- a/test/src/components/icon/icon_test.dart +++ b/test/src/components/icon/icon_test.dart @@ -1,28 +1,60 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - group('Zeta Icon', () { - testWidgets('renders icon correctly', (WidgetTester tester) async { - await tester.pumpWidget(const TestApp(home: ZetaIcon(ZetaIcons.add_round))); + const String parentFolder = 'icon'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('Accessibility Tests', () { + testWidgets('applies correct semantic label to icon', (WidgetTester tester) async { + const String semanticLabel = 'Add Icon'; + await tester.pumpWidget(const TestApp(home: ZetaIcon(ZetaIcons.add_round, semanticLabel: semanticLabel))); final iconFinder = find.byIcon(ZetaIcons.add_round); - expect(iconFinder, findsOneWidget); + final iconWidget = tester.widget(iconFinder); + expect(iconWidget.semanticLabel, equals(semanticLabel)); }); + }); + group('Content Tests', () { + final debugFillProperties = { + 'icon': 'IconData(U+0E045)', + 'rounded': 'false', + 'size': '10.0', + 'fill': 'null', + 'weight': 'null', + 'grade': 'null', + 'opticalSize': 'null', + 'color': 'null', + 'shadows': 'null', + 'semanticLabel': '"Cached"', + 'textDirection': 'null', + 'applyTextScaling': 'null', + }; + debugFillPropertiesTest( + const ZetaIcon( + ZetaIcons.cached, + rounded: false, + size: 10, + semanticLabel: 'Cached', + ), + debugFillProperties, + ); - testWidgets('applies correct size to icon', (WidgetTester tester) async { - const double iconSize = 24; - await tester.pumpWidget(const TestApp(home: ZetaIcon(ZetaIcons.add_round, size: iconSize))); + testWidgets('renders icon correctly', (WidgetTester tester) async { + await tester.pumpWidget(const TestApp(home: ZetaIcon(ZetaIcons.add_round))); final iconFinder = find.byIcon(ZetaIcons.add_round); - final iconWidget = tester.widget(iconFinder); - expect(iconWidget.size, equals(iconSize)); + expect(iconFinder, findsOneWidget); }); - testWidgets('applies correct color to icon', (WidgetTester tester) async { + testWidgets('color value is correct', (WidgetTester tester) async { const Color iconColor = Colors.red; await tester.pumpWidget(const TestApp(home: ZetaIcon(ZetaIcons.add_round, color: iconColor))); final iconFinder = find.byIcon(ZetaIcons.add_round); @@ -30,14 +62,6 @@ void main() { expect(iconWidget.color, equals(iconColor)); }); - testWidgets('applies correct semantic label to icon', (WidgetTester tester) async { - const String semanticLabel = 'Add Icon'; - await tester.pumpWidget(const TestApp(home: ZetaIcon(ZetaIcons.add_round, semanticLabel: semanticLabel))); - final iconFinder = find.byIcon(ZetaIcons.add_round); - final iconWidget = tester.widget(iconFinder); - expect(iconWidget.semanticLabel, equals(semanticLabel)); - }); - testWidgets('applies sharp family to icon', (WidgetTester tester) async { await tester.pumpWidget( const TestApp( @@ -58,7 +82,18 @@ void main() { expect(iconFinderRound, findsOneWidget); expect(iconFinderSharp, findsExactly(1)); }); - + }); + group('Dimensions Tests', () { + testWidgets('applies correct size to icon', (WidgetTester tester) async { + const double iconSize = 24; + await tester.pumpWidget(const TestApp(home: ZetaIcon(ZetaIcons.add_round, size: iconSize))); + final iconFinder = find.byIcon(ZetaIcons.add_round); + final sizeOfIcon = tester.getSize(iconFinder); + expect(sizeOfIcon.width, equals(iconSize)); + expect(sizeOfIcon.height, equals(iconSize)); + }); + }); + group('Styling Tests', () { testWidgets('applies correct font family to icon', (WidgetTester tester) async { await tester.pumpWidget(const TestApp(home: ZetaIcon(ZetaIcons.add_round))); final iconFinder = find.byIcon(ZetaIcons.add_round); @@ -145,28 +180,10 @@ void main() { final iconWidget = tester.widget(iconFinder); expect(iconWidget.icon?.fontFamily, equals('MaterialIcons')); }); - - testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { - final diagnostics = DiagnosticPropertiesBuilder(); - const ZetaIcon( - ZetaIcons.cached, - rounded: false, - size: 10, - semanticLabel: 'Cached', - ).debugFillProperties(diagnostics); - - expect(diagnostics.finder('icon'), 'IconData(U+0E045)'); - expect(diagnostics.finder('rounded'), 'false'); - expect(diagnostics.finder('size'), '10.0'); - expect(diagnostics.finder('fill'), 'null'); - expect(diagnostics.finder('weight'), 'null'); - expect(diagnostics.finder('grade'), 'null'); - expect(diagnostics.finder('opticalSize'), 'null'); - expect(diagnostics.finder('color'), 'null'); - expect(diagnostics.finder('shadows'), 'null'); - expect(diagnostics.finder('semanticLabel'), '"Cached"'); - expect(diagnostics.finder('textDirection'), 'null'); - expect(diagnostics.finder('applyTextScaling'), 'null'); - }); }); + group('Interaction Tests', () {}); + group('Golden Tests', () { + // goldenTest(goldenFile, widget, widgetType, 'PNG_FILE_NAME'); + }); + group('Performance Tests', () {}); } 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 index 4c825515..f29bffed 100644 --- a/test/src/components/in_page_banner/in_page_banner_test.dart +++ b/test/src/components/in_page_banner/in_page_banner_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; @@ -8,120 +7,174 @@ import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const goldenFile = GoldenFiles(component: 'in_page_banner'); + const String parentFolder = 'in_page_banner'; + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - group('ZetaInPageBanner Tests', () { - testWidgets('ZetaInPageBanner creation', (WidgetTester tester) async { + group('Accessibility Tests', () {}); + group('Content Tests', () { + final debugFillProperties = { + 'onClose': 'null', + 'status': 'info', + 'title': 'null', + 'customIcon': 'null', + }; + debugFillPropertiesTest( + const ZetaInPageBanner(content: Placeholder()), + debugFillProperties, + ); + + testWidgets('renders correct icon and text', (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); + }); + + 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); + }); + + 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); + }); + }); + group('Dimensions Tests', () {}); + group('Styling Tests', () { + testWidgets('default background colour is correct', (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(box.decoration.runtimeType, BoxDecoration); final BoxDecoration decoration = box.decoration as BoxDecoration; expect(decoration.color, ZetaColorBase.purple.shade10); + }); - await expectLater( - find.byType(ZetaInPageBanner), - matchesGoldenFile(goldenFile.getFileUri('in_page_banner_default')), + testWidgets('negative background colour is correct', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp(home: ZetaInPageBanner(content: const Text('Test'), onClose: () {}, status: ZetaWidgetStatus.negative)), ); + + 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); }); - }); - testWidgets("ZetaInPageBanner shows 'close icon' correctly", (WidgetTester tester) async { - await tester.pumpWidget( - TestApp(home: ZetaInPageBanner(content: const Text('Test'), onClose: () {}, status: ZetaWidgetStatus.negative)), - ); + testWidgets('positive background colour is correct', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp(home: ZetaInPageBanner(content: Text('Test'), status: ZetaWidgetStatus.positive)), + ); - 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(goldenFile.getFileUri('in_page_banner_negative')), - ); - }); + 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); + }); - 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(goldenFile.getFileUri('in_page_banner_positive')), - ); - }); + testWidgets('neutral background colour is correct', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp(home: ZetaInPageBanner(content: Text('Test'), status: ZetaWidgetStatus.neutral)), + ); - 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); + }); + }); + group('Interaction Tests', () { + testWidgets('button callback works', (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 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( + 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); + }); + }); - await expectLater( - find.byType(ZetaInPageBanner), - matchesGoldenFile(goldenFile.getFileUri('in_page_banner_buttons')), + group('Golden Tests', () { + goldenTest( + goldenFile, + const ZetaInPageBanner(content: Text('Test'), title: 'Title'), + ZetaInPageBanner, + 'in_page_banner_default', ); - }); - 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'))), + goldenTest( + goldenFile, + ZetaInPageBanner(content: const Text('Test'), onClose: () {}, status: ZetaWidgetStatus.negative), + ZetaInPageBanner, + 'in_page_banner_negative', + ); + goldenTest( + goldenFile, + const ZetaInPageBanner(content: Text('Test'), status: ZetaWidgetStatus.positive), + ZetaInPageBanner, + 'in_page_banner_positive', + ); + goldenTest( + goldenFile, + ZetaInPageBanner( + content: const Text('Test'), + status: ZetaWidgetStatus.neutral, + actions: [ + ZetaButton( + label: 'Test button', + onPressed: () {}, + ), + ], + ), + ZetaInPageBanner, + 'in_page_banner_buttons', ); - 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'); }); + group('Performance Tests', () {}); } diff --git a/test/src/components/password/password_input_test.dart b/test/src/components/password/password_input_test.dart index 57dbbd28..c67a7ad6 100644 --- a/test/src/components/password/password_input_test.dart +++ b/test/src/components/password/password_input_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; @@ -8,87 +7,112 @@ import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const goldenFile = GoldenFiles(component: 'password'); + const String parentFolder = 'password'; + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - testWidgets('ZetaPasswordInput initializes correctly', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaPasswordInput(), - ), + group('Accessibility Tests', () {}); + group('Content Tests', () { + final debugFillProperties = { + 'size': 'medium', + 'placeholder': 'null', + 'label': 'null', + 'hintText': 'null', + 'errorText': 'null', + 'semanticLabel': 'null', + 'showSemanticLabel': 'null', + 'obscureSemanticLabel': 'null', + }; + debugFillPropertiesTest( + ZetaPasswordInput(), + debugFillProperties, ); - expect(find.byType(ZetaPasswordInput), findsOneWidget); - await expectLater( - find.byType(ZetaPasswordInput), - matchesGoldenFile(goldenFile.getFileUri('password_default')), - ); - }); + testWidgets('ZetaPasswordInput initializes correctly', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaPasswordInput(), + ), + ); + expect(find.byType(ZetaPasswordInput), findsOneWidget); + }); - testWidgets('Test password visibility', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaPasswordInput(), - ), - ); - final obscureIconOff = find.byIcon(ZetaIcons.visibility_off_round); - expect(obscureIconOff, findsOneWidget); - await tester.tap(obscureIconOff); - await tester.pump(); + testWidgets('obscure icon in rendered correctly', (WidgetTester tester) async { + await tester.pumpWidget( + 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); - }); + 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; - } + 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(); + final controller = TextEditingController()..text = 'password123'; + final formKey = GlobalKey(); - await tester.pumpWidget( - TestApp( - home: Form( - key: formKey, - child: ZetaPasswordInput( - controller: controller, - validator: testValidator, - rounded: false, + 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); + ); + formKey.currentState?.validate(); + await tester.pump(); - await expectLater( - find.byType(ZetaPasswordInput), - matchesGoldenFile(goldenFile.getFileUri('password_error')), - ); + // 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); + }); }); - testWidgets('Test debugFillProperties', (WidgetTester tester) async { - final diagnostics = DiagnosticPropertiesBuilder(); - ZetaPasswordInput().debugFillProperties(diagnostics); - - expect(diagnostics.finder('size'), 'medium'); - expect(diagnostics.finder('placeholder'), 'null'); - expect(diagnostics.finder('label'), 'null'); - expect(diagnostics.finder('hintText'), 'null'); - expect(diagnostics.finder('errorText'), 'null'); - expect(diagnostics.finder('semanticLabel'), 'null'); - expect(diagnostics.finder('showSemanticLabel'), 'null'); - expect(diagnostics.finder('obscureSemanticLabel'), 'null'); + group('Dimensions Tests', () {}); + group('Styling Tests', () {}); + group('Interaction Tests', () {}); + group('Golden Tests', () { + goldenTest(goldenFile, ZetaPasswordInput(), ZetaPasswordInput, 'password_default'); + final formKey = GlobalKey(); + goldenTestWithCallbacks( + goldenFile, + Form( + key: formKey, + child: ZetaPasswordInput( + controller: TextEditingController()..text = 'password123', + validator: (String? value) { + final regExp = RegExp(r'\d'); + if (value != null && regExp.hasMatch(value)) return 'Error'; + return null; + }, + rounded: false, + ), + ), + ZetaPasswordInput, + 'password_error', + after: (tester) async { + formKey.currentState?.validate(); + await tester.pump(); + }, + ); }); + group('Performance Tests', () {}); } diff --git a/test/src/components/search_bar/search_bar_test.dart b/test/src/components/search_bar/search_bar_test.dart index 120b326a..42e1ed7b 100644 --- a/test/src/components/search_bar/search_bar_test.dart +++ b/test/src/components/search_bar/search_bar_test.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -26,8 +25,9 @@ abstract class ISearchBarEvents { ]) void main() { late MockISearchBarEvents callbacks; - const goldenFile = GoldenFiles(component: 'search_bar'); + const String parentFolder = 'search_bar'; + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); @@ -36,8 +36,25 @@ void main() { callbacks = MockISearchBarEvents(); }); - group('ZetaSearchBar', () { - testWidgets('renders with default parameters', (WidgetTester tester) async { + group('Accessibility Tests', () {}); + group('Content Tests', () { + final debugFillProperties = { + 'size': 'medium', + 'shape': 'rounded', + 'placeholder': 'null', + 'textInputAction': 'null', + 'onSpeechToText': 'null', + 'showSpeechToText': 'true', + 'focusNode': 'null', + 'microphoneSemanticLabel': 'null', + 'clearSemanticLabel': 'null', + }; + debugFillPropertiesTest( + ZetaSearchBar(), + debugFillProperties, + ); + + testWidgets('renders text field', (WidgetTester tester) async { await tester.pumpWidget( TestApp( home: Scaffold( @@ -50,82 +67,6 @@ void main() { expect(find.byType(TextField), findsOneWidget); }); - testWidgets('golden: renders initializes correctly', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaSearchBar(), - ), - ); - expect(find.byType(ZetaSearchBar), findsOneWidget); - - await expectLater( - find.byType(ZetaSearchBar), - matchesGoldenFile(goldenFile.getFileUri('search_bar_default')), - ); - }); - - testWidgets('golden: renders size medium correctly', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaSearchBar(), - ), - ); - expect(find.byType(ZetaSearchBar), findsOneWidget); - - await expectLater( - find.byType(ZetaSearchBar), - matchesGoldenFile(goldenFile.getFileUri('search_bar_medium')), - ); - }); - - testWidgets('golden: renders size small correctly', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaSearchBar( - size: ZetaWidgetSize.small, - ), - ), - ); - expect(find.byType(ZetaSearchBar), findsOneWidget); - - await expectLater( - find.byType(ZetaSearchBar), - matchesGoldenFile(goldenFile.getFileUri('search_bar_small')), - ); - }); - - testWidgets('golden: renders shape full correctly', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaSearchBar( - shape: ZetaWidgetBorder.full, - ), - ), - ); - expect(find.byType(ZetaSearchBar), findsOneWidget); - - await expectLater( - find.byType(ZetaSearchBar), - matchesGoldenFile(goldenFile.getFileUri('search_bar_full')), - ); - }); - - testWidgets('golden: renders shape sharp correctly', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: ZetaSearchBar( - shape: ZetaWidgetBorder.sharp, - ), - ), - ); - expect(find.byType(ZetaSearchBar), findsOneWidget); - - await expectLater( - find.byType(ZetaSearchBar), - matchesGoldenFile(goldenFile.getFileUri('search_bar_sharp')), - ); - }); - testWidgets('sets initial value correctly', (WidgetTester tester) async { const initialValue = 'Initial value'; @@ -168,6 +109,25 @@ void main() { expect(find.text(updatedValue), findsOneWidget); }); + testWidgets('speech-to-text button visibility', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Scaffold( + body: ZetaSearchBar( + showSpeechToText: false, + ), + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.byIcon(ZetaIcons.search), findsNothing); + expect(find.byIcon(ZetaIcons.microphone), findsNothing); + }); + }); + group('Dimensions Tests', () {}); + group('Styling Tests', () {}); + group('Interaction Tests', () { testWidgets('triggers onChanged callback when text is entered', (WidgetTester tester) async { await tester.pumpWidget( TestApp( @@ -237,22 +197,6 @@ void main() { expect(find.text('Disabled input'), findsNothing); }); - testWidgets('speech-to-text button visibility', (WidgetTester tester) async { - await tester.pumpWidget( - TestApp( - home: Scaffold( - body: ZetaSearchBar( - showSpeechToText: false, - ), - ), - ), - ); - - await tester.pumpAndSettle(); - expect(find.byIcon(ZetaIcons.search), findsNothing); - expect(find.byIcon(ZetaIcons.microphone), findsNothing); - }); - testWidgets('clear button functionality', (WidgetTester tester) async { await tester.pumpWidget( TestApp( @@ -273,20 +217,34 @@ void main() { verify(callbacks.onChange.call('')).called(1); }); - - test('debugFillProperties', () { - final diagnostics = DiagnosticPropertiesBuilder(); - ZetaSearchBar().debugFillProperties(diagnostics); - - expect(diagnostics.finder('size'), 'medium'); - expect(diagnostics.finder('shape'), 'rounded'); - expect(diagnostics.finder('placeholder'), 'null'); - expect(diagnostics.finder('textInputAction'), 'null'); - expect(diagnostics.finder('onSpeechToText'), 'null'); - expect(diagnostics.finder('showSpeechToText'), 'true'); - expect(diagnostics.finder('focusNode'), 'null'); - expect(diagnostics.finder('microphoneSemanticLabel'), 'null'); - expect(diagnostics.finder('clearSemanticLabel'), 'null'); - }); }); + group('Golden Tests', () { + goldenTest(goldenFile, ZetaSearchBar(), ZetaSearchBar, 'search_bar_default'); + goldenTest(goldenFile, ZetaSearchBar(), ZetaSearchBar, 'search_bar_medium'); + goldenTest( + goldenFile, + ZetaSearchBar( + size: ZetaWidgetSize.small, + ), + ZetaSearchBar, + 'search_bar_small', + ); + goldenTest( + goldenFile, + ZetaSearchBar( + shape: ZetaWidgetBorder.full, + ), + ZetaSearchBar, + 'search_bar_full', + ); + goldenTest( + goldenFile, + ZetaSearchBar( + shape: ZetaWidgetBorder.sharp, + ), + ZetaSearchBar, + 'search_bar_sharp', + ); + }); + group('Performance Tests', () {}); } diff --git a/test/src/components/slider/slider_test.dart b/test/src/components/slider/slider_test.dart index 70522376..5c11e203 100644 --- a/test/src/components/slider/slider_test.dart +++ b/test/src/components/slider/slider_test.dart @@ -3,33 +3,66 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; +import '../../../test_utils/utils.dart'; void main() { - testWidgets('ZetaSlider min/max values', (WidgetTester tester) async { - const double sliderValue = 0.5; - double? changedValue; - - await tester.pumpWidget( - TestApp( - home: ZetaSlider( - value: sliderValue, - onChange: (value) { - changedValue = value; - }, + const String parentFolder = 'slider'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('Accessibility Tests', () {}); + + group('Content Tests', () { + // final debugFillProperties = { + // '': '', + // }; + // debugFillPropertiesTest( + // widget, + // debugFillProperties, + // ); + }); + + group('Dimensions Tests', () {}); + + group('Styling Tests', () {}); + + group('Interaction Tests', () { + testWidgets('ZetaSlider min/max values', (WidgetTester tester) async { + const double sliderValue = 0.5; + double? changedValue; + + await tester.pumpWidget( + TestApp( + home: ZetaSlider( + value: sliderValue, + onChange: (value) { + changedValue = value; + }, + ), ), - ), - ); + ); - final slider = tester.widget(find.byType(Slider)); - expect(slider.min, 0.0); - expect(slider.max, 1.0); + final slider = tester.widget(find.byType(Slider)); + expect(slider.min, 0.0); + expect(slider.max, 1.0); - // Drag the slider to the minimum value - await tester.drag(find.byType(Slider), const Offset(-400, 0)); - expect(changedValue, 0.0); + // Drag the slider to the minimum value + await tester.drag(find.byType(Slider), const Offset(-400, 0)); + expect(changedValue, 0.0); - // Drag the slider to the maximum value - await tester.drag(find.byType(Slider), const Offset(400, 0)); - expect(changedValue, 1.0); + // Drag the slider to the maximum value + await tester.drag(find.byType(Slider), const Offset(400, 0)); + expect(changedValue, 1.0); + }); }); + + group('Golden Tests', () { + // goldenTest(goldenFile, widget, widgetType, 'PNG_FILE_NAME'); + }); + + group('Performance Tests', () {}); } diff --git a/test/src/components/stepper input/stepper_input_test.dart b/test/src/components/stepper input/stepper_input_test.dart deleted file mode 100644 index 53c4b4c1..00000000 --- a/test/src/components/stepper input/stepper_input_test.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/src/components/stepper_input/stepper_input.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import '../../../test_utils/test_app.dart'; - -void main() { - testWidgets('ZetaStepperInput increases value when increment button is pressed', (WidgetTester tester) async { - int value = 0; - await tester.pumpWidget( - TestApp( - home: ZetaStepperInput( - value: value, - onChange: (newValue) { - value = newValue; - }, - ), - ), - ); - - expect(value, 0); - - await tester.tap(find.byIcon(ZetaIcons.add_round)); - await tester.pump(); - - expect(value, 1); - }); - - testWidgets('ZetaStepperInput increases value when programatically changed', (WidgetTester tester) async { - int value = 0; - StateSetter? setState; - await tester.pumpWidget( - StatefulBuilder( - builder: (context, setState2) { - setState = setState2; - - return TestApp( - home: ZetaStepperInput( - value: value, - onChange: (newValue) { - value = newValue; - }, - ), - ); - }, - ), - ); - - final ZetaStepperInputState stepperInputState = tester.state(find.byType(ZetaStepperInput)); - - expect(value, stepperInputState.value); - - setState?.call(() { - value = 1; - }); - - await tester.pump(); - - expect(value, stepperInputState.value); - }); -} diff --git a/test/src/components/stepper_input/stepper_input_test.dart b/test/src/components/stepper_input/stepper_input_test.dart new file mode 100644 index 00000000..95b97b06 --- /dev/null +++ b/test/src/components/stepper_input/stepper_input_test.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:zeta_flutter/src/components/stepper_input/stepper_input.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() { + const String parentFolder = 'stepper_input'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('Accessibility Tests', () {}); + group('Content Tests', () { + // final debugFillProperties = { + // '': '', + // }; + // debugFillPropertiesTest( + // widget, + // debugFillProperties, + // ); + + testWidgets('ZetaStepperInput increases value when programatically changed', (WidgetTester tester) async { + int value = 0; + StateSetter? setState; + await tester.pumpWidget( + StatefulBuilder( + builder: (context, setState2) { + setState = setState2; + + return TestApp( + home: ZetaStepperInput( + value: value, + onChange: (newValue) { + value = newValue; + }, + ), + ); + }, + ), + ); + + final ZetaStepperInputState stepperInputState = tester.state(find.byType(ZetaStepperInput)); + + expect(value, stepperInputState.value); + + setState?.call(() { + value = 1; + }); + + await tester.pump(); + + expect(value, stepperInputState.value); + }); + }); + + group('Dimensions Tests', () {}); + + group('Styling Tests', () {}); + + group('Interaction Tests', () { + testWidgets('ZetaStepperInput increases value when increment button is pressed', (WidgetTester tester) async { + int value = 0; + await tester.pumpWidget( + TestApp( + home: ZetaStepperInput( + value: value, + onChange: (newValue) { + value = newValue; + }, + ), + ), + ); + + expect(value, 0); + + await tester.tap(find.byIcon(ZetaIcons.add_round)); + await tester.pump(); + + expect(value, 1); + }); + }); + + group('Golden Tests', () { + // goldenTest(goldenFile, widget, widgetType, 'PNG_FILE_NAME'); + }); + + group('Performance Tests', () {}); +} diff --git a/test/src/components/tooltip/tooltip_test.dart b/test/src/components/tooltip/tooltip_test.dart index b1c5284f..ef2e6efc 100644 --- a/test/src/components/tooltip/tooltip_test.dart +++ b/test/src/components/tooltip/tooltip_test.dart @@ -15,15 +15,55 @@ import 'tooltip_test.mocks.dart'; MockSpec(), ]) void main() { + const String parentFolder = 'tooltip'; + final mockZeta = MockZeta(); when(mockZeta.radius).thenReturn(const ZetaRadiiAA(primitives: ZetaPrimitivesLight())); - const goldenFile = GoldenFiles(component: 'tooltip'); + const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { goldenFileComparator = TolerantComparator(goldenFile.uri); }); - group('ZetaTooltip Widget Tests', () { + group('Accessibility Tests', () {}); + group('Content Tests', () { + final debugFillProperties = { + 'rounded': 'null', + 'padding': 'EdgeInsets.all(8.0)', + 'color': 'MaterialColor(primary value: Color(0xffffc107))', + 'textStyle': 'TextStyle(inherit: true, size: 9.0)', + 'arrowDirection': 'down', + 'maxWidth': '170.0', + }; + debugFillPropertiesTest( + const ZetaTooltip( + padding: EdgeInsets.all(8), + color: Colors.amber, + textStyle: TextStyle(fontSize: 9), + maxWidth: 170, + child: Text('Rounded tooltip'), + ), + debugFillProperties, + ); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + const ZetaTooltip( + padding: EdgeInsets.all(8), + color: Colors.amber, + textStyle: TextStyle(fontSize: 9), + maxWidth: 170, + child: Text('Rounded tooltip'), + ).debugFillProperties(diagnostics); + + 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'); + }); + testWidgets('renders with default properties', (WidgetTester tester) async { await tester.pumpWidget( const TestApp( @@ -38,13 +78,13 @@ void main() { expect(find.text('Tooltip text'), findsOneWidget); expect(find.byType(ZetaTooltip), findsOneWidget); }); - - testWidgets('renders with custom color and padding', (WidgetTester tester) async { + }); + group('Dimensions Tests', () { + testWidgets('renders with custom padding', (WidgetTester tester) async { await tester.pumpWidget( const TestApp( home: Scaffold( body: ZetaTooltip( - color: Colors.red, padding: EdgeInsets.all(20), child: Text('Tooltip text'), ), @@ -52,23 +92,38 @@ void main() { ), ); - final tooltipBox = tester.widget( + final padding = tester.widget( find.descendant( of: find.byType(ZetaTooltip), - matching: find.byType(DecoratedBox), + matching: find.byType(Padding), ), ); - expect((tooltipBox.decoration as BoxDecoration).color, Colors.red); + expect(padding.padding, const EdgeInsets.all(20)); + }); + }); + group('Styling Tests', () { + testWidgets('renders with custom color', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaTooltip( + color: Colors.red, + padding: EdgeInsets.all(20), + child: Text('Tooltip text'), + ), + ), + ), + ); - final padding = tester.widget( + final tooltipBox = tester.widget( find.descendant( of: find.byType(ZetaTooltip), - matching: find.byType(Padding), + matching: find.byType(DecoratedBox), ), ); - expect(padding.padding, const EdgeInsets.all(20)); + expect((tooltipBox.decoration as BoxDecoration).color, Colors.red); }); testWidgets('renders with custom text style', (WidgetTester tester) async { @@ -99,89 +154,6 @@ void main() { expect(textSpan.style?.color, Colors.blue); }); - testWidgets('renders with arrow correctly in up direction', (WidgetTester tester) async { - await tester.pumpWidget( - const TestApp( - home: Scaffold( - body: ZetaTooltip( - arrowDirection: ZetaTooltipArrowDirection.up, - child: Text('Tooltip up'), - ), - ), - ), - ); - - expect(find.text('Tooltip up'), findsOneWidget); - - // Verifying the CustomPaint with different arrow directions. - await expectLater( - find.byType(ZetaTooltip), - matchesGoldenFile(goldenFile.getFileUri('arrow_up')), - ); - }); - - testWidgets('renders with arrow correctly in down direction', (WidgetTester tester) async { - await tester.pumpWidget( - const TestApp( - home: Scaffold( - body: ZetaTooltip( - child: Text('Tooltip down'), - ), - ), - ), - ); - - expect(find.text('Tooltip down'), findsOneWidget); - - // Verifying the CustomPaint with different arrow directions. - await expectLater( - find.byType(ZetaTooltip), - matchesGoldenFile(goldenFile.getFileUri('arrow_down')), - ); - }); - - testWidgets('renders with arrow correctly in left direction', (WidgetTester tester) async { - await tester.pumpWidget( - const TestApp( - home: Scaffold( - body: ZetaTooltip( - arrowDirection: ZetaTooltipArrowDirection.left, - child: Text('Tooltip left'), - ), - ), - ), - ); - - expect(find.text('Tooltip left'), findsOneWidget); - - // Verifying the CustomPaint with different arrow directions. - await expectLater( - find.byType(ZetaTooltip), - matchesGoldenFile(goldenFile.getFileUri('arrow_left')), - ); - }); - - testWidgets('renders with arrow correctly in right direction', (WidgetTester tester) async { - await tester.pumpWidget( - const TestApp( - home: Scaffold( - body: ZetaTooltip( - arrowDirection: ZetaTooltipArrowDirection.right, - child: Text('Tooltip right'), - ), - ), - ), - ); - - expect(find.text('Tooltip right'), findsOneWidget); - - // Verifying the CustomPaint with different arrow directions. - await expectLater( - find.byType(ZetaTooltip), - matchesGoldenFile(goldenFile.getFileUri('arrow_right')), - ); - }); - testWidgets('renders with rounded and sharp corners', (WidgetTester tester) async { await tester.pumpWidget( const TestApp( @@ -221,23 +193,52 @@ void main() { expect((roundedTooltipBox.decoration as BoxDecoration).borderRadius, mockZeta.radius.minimal); expect((sharpTooltipBox.decoration as BoxDecoration).borderRadius, null); }); - - testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { - final diagnostics = DiagnosticPropertiesBuilder(); - const ZetaTooltip( - padding: EdgeInsets.all(8), - color: Colors.amber, - textStyle: TextStyle(fontSize: 9), - maxWidth: 170, - child: Text('Rounded tooltip'), - ).debugFillProperties(diagnostics); - - 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'); - }); }); + group('Interaction Tests', () {}); + group('Golden Tests', () { + goldenTest( + goldenFile, + const Scaffold( + body: ZetaTooltip( + arrowDirection: ZetaTooltipArrowDirection.up, + child: Text('Tooltip up'), + ), + ), + ZetaTooltip, + 'arrow_up', + ); + goldenTest( + goldenFile, + const Scaffold( + body: ZetaTooltip( + child: Text('Tooltip down'), + ), + ), + ZetaTooltip, + 'arrow_down', + ); + goldenTest( + goldenFile, + const Scaffold( + body: ZetaTooltip( + arrowDirection: ZetaTooltipArrowDirection.left, + child: Text('Tooltip left'), + ), + ), + ZetaTooltip, + 'arrow_left', + ); + goldenTest( + goldenFile, + const Scaffold( + body: ZetaTooltip( + arrowDirection: ZetaTooltipArrowDirection.right, + child: Text('Tooltip right'), + ), + ), + ZetaTooltip, + 'arrow_right', + ); + }); + group('Performance Tests', () {}); } diff --git a/test/test_utils/utils.dart b/test/test_utils/utils.dart index 1bfb59f1..5117d045 100644 --- a/test/test_utils/utils.dart +++ b/test/test_utils/utils.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart'; +import '../test_utils/test_app.dart'; extension Util on DiagnosticPropertiesBuilder { dynamic finder(String finder) { @@ -30,6 +31,77 @@ class GoldenFiles { } } +void goldenTest( + GoldenFiles goldenFile, + Widget widget, + Type widgetType, + String fileName, { + ThemeMode themeMode = ThemeMode.system, + Size? screenSize, + bool? rounded, +}) { + testWidgets('$fileName golden', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + screenSize: screenSize, + themeMode: themeMode, + rounded: rounded, + home: widget, + ), + ); + + await expectLater( + find.byType(widgetType), + matchesGoldenFile(goldenFile.getFileUri(fileName)), + ); + }); +} + +void goldenTestWithCallbacks( + GoldenFiles goldenFile, + Widget widget, + Type widgetType, + String fileName, { + Future Function(WidgetTester)? before, + Future Function(WidgetTester)? after, + bool darkMode = false, + Size? screenSize, +}) { + testWidgets('$fileName golden', (WidgetTester tester) async { + if (before != null) { + await before(tester); + } + + await tester.pumpWidget( + TestApp( + screenSize: screenSize, + themeMode: darkMode ? ThemeMode.dark : ThemeMode.light, + home: widget, + ), + ); + + if (after != null) { + await after(tester); + } + + await expectLater( + find.byType(widgetType), + matchesGoldenFile(goldenFile.getFileUri(fileName)), + ); + }); +} + BuildContext getBuildContext(WidgetTester tester, Type type) { return tester.element(find.byType(type)); } + +void debugFillPropertiesTest(Widget widget, Map properties) { + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnostics = DiagnosticPropertiesBuilder(); + widget.debugFillProperties(diagnostics); + + properties.forEach((key, value) { + expect(diagnostics.finder(key), value); + }); + }); +} From 58fc7f5c3e40888ade19a30a5592e70f5340585a Mon Sep 17 00:00:00 2001 From: mikecoomber <58986130+mikecoomber@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:06:19 +0100 Subject: [PATCH 2/8] fix(UX-1242): Fixed extended app bar alignment (#189) refactor: App bar variants are now constructed through named constructors. fix(UX-1207): The search box on the search app bar now gets closed when the back button is pressed. refactor: Search app bars can no longer be extended app bars. test: Wrote tests for ZetaTopAppbar chore: Merged goldenTest and goldenTestWithCallbacks. Also made the widgetType parameter optional and defaulted it to the type of widget. ci: changed pull request code coverage pass score --- .github/workflows/pull-request.yml | 36 +- .../pages/components/top_app_bar_example.dart | 78 +-- .../components/top_app_bar_widgetbook.dart | 86 +-- .../screen_header_bar/screen_header_bar.dart | 2 +- .../top_app_bar/extended_top_app_bar.dart | 50 +- .../top_app_bar/search_top_app_bar.dart | 10 +- .../components/top_app_bar/top_app_bar.dart | 277 ++++++--- test/src/components/avatar/avatar_test.dart | 6 - test/src/components/badge/indicator_test.dart | 8 +- test/src/components/badge/label_test.dart | 10 +- .../components/badge/priority_pill_test.dart | 5 +- .../components/badge/status_label_test.dart | 3 +- test/src/components/badge/tag_test.dart | 4 +- test/src/components/banner/banner_test.dart | 2 +- test/src/components/button/button_test.dart | 13 +- .../components/chat_item/chat_item_test.dart | 58 +- .../components/checkbox/checkbox_test.dart | 7 +- .../comms_button/comms_button_test.dart | 1 - test/src/components/dialpad/dialpad_test.dart | 3 - test/src/components/fab/fab_test.dart | 14 +- .../in_page_banner/in_page_banner_test.dart | 4 - .../password/password_input_test.dart | 7 +- .../search_bar/search_bar_test.dart | 7 +- test/src/components/tooltip/tooltip_test.dart | 8 +- .../extended_top_app_bar_test.dart | 169 ------ .../golden/extended_app_bar_shrinks.png | Bin 2230 -> 2051 bytes ...tended_app_bar_shrinks_with_no_leading.png | Bin 2206 -> 2043 bytes .../golden/top_app_bar_centered.png | Bin 0 -> 3318 bytes .../golden/top_app_bar_centered_actions.png | Bin 0 -> 3368 bytes .../golden/top_app_bar_default.png | Bin 0 -> 3320 bytes .../golden/top_app_bar_default_actions.png | Bin 0 -> 3368 bytes .../golden/top_app_bar_extended.png | Bin 0 -> 2231 bytes .../golden/top_app_bar_extended_actions.png | Bin 0 -> 2271 bytes .../top_app_bar/golden/top_app_bar_search.png | Bin 0 -> 3320 bytes .../golden/top_app_bar_search_active.png | Bin 0 -> 3512 bytes .../golden/top_app_bar_search_centered.png | Bin 0 -> 3319 bytes .../top_app_bar/top_app_bar_test.dart | 557 ++++++++++++++++++ test/test_utils/utils.dart | 44 +- 38 files changed, 888 insertions(+), 581 deletions(-) delete mode 100644 test/src/components/top_app_bar/extended_top_app_bar_test.dart create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_centered.png create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_centered_actions.png create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_default.png create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_default_actions.png create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_extended.png create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_extended_actions.png create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_search.png create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_search_active.png create mode 100644 test/src/components/top_app_bar/golden/top_app_bar_search_centered.png create mode 100644 test/src/components/top_app_bar/top_app_bar_test.dart diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index a55af954..04b742f7 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,32 +1,30 @@ name: CI - Pull Request on: pull_request_target: - + # Pull Request Runs on the same branch will be cancelled concurrency: group: ${{ github.head_ref }} cancel-in-progress: true - jobs: code-quality: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - repository: ${{github.event.pull_request.head.repo.full_name}} - ref: ${{ github.head_ref }} - fetch-depth: 0 - - uses: subosito/flutter-action@v2 - with: - cache: true - - run: dart run build_runner build --delete-conflicting-outputs - - uses: ZebraDevs/flutter-code-quality@main - with: - token: ${{secrets.GITHUB_TOKEN}} - coverage-pass-score: '90' - - + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: ${{github.event.pull_request.head.repo.full_name}} + ref: ${{ github.head_ref }} + fetch-depth: 0 + - uses: subosito/flutter-action@v2 + with: + cache: true + - run: dart run build_runner build --delete-conflicting-outputs + - uses: ZebraDevs/flutter-code-quality@main + with: + token: ${{secrets.GITHUB_TOKEN}} + coverage-pass-score: "80" + check-secret: runs-on: ubuntu-latest outputs: diff --git a/example/lib/pages/components/top_app_bar_example.dart b/example/lib/pages/components/top_app_bar_example.dart index 6adc0827..5c5d30fe 100644 --- a/example/lib/pages/components/top_app_bar_example.dart +++ b/example/lib/pages/components/top_app_bar_example.dart @@ -15,20 +15,7 @@ class TopAppBarExample extends StatefulWidget { } class _TopAppBarExampleState extends State { - final _searchControllerExtended = ZetaSearchController(); - final _searchControllerRegular = ZetaSearchController(); - - void _showHideSearchExtended() { - _searchControllerExtended.isEnabled - ? _searchControllerExtended.closeSearch() - : _searchControllerExtended.startSearch(); - } - - void _showHideSearchRegular() { - _searchControllerRegular.isEnabled - ? _searchControllerRegular.closeSearch() - : _searchControllerRegular.startSearch(); - } + final _searchController = ZetaSearchController(); @override Widget build(BuildContext context) { @@ -78,8 +65,7 @@ class _TopAppBarExampleState extends State { ], ), Text('Centered', style: ZetaTextStyles.titleLarge), - ZetaTopAppBar( - type: ZetaTopAppBarType.centeredTitle, + ZetaTopAppBar.centered( leading: IconButton( onPressed: () {}, icon: ZetaIcon(Icons.menu), @@ -119,24 +105,20 @@ class _TopAppBarExampleState extends State { ], ), Text('Search', style: ZetaTextStyles.titleLarge), - ZetaTopAppBar( - type: ZetaTopAppBarType.centeredTitle, - leading: BackButton(), + ZetaTopAppBar.search( + leading: IconButton( + onPressed: () {}, + icon: ZetaIcon(Icons.menu), + ), title: Text("Title"), - actions: [ - IconButton( - onPressed: _showHideSearchRegular, - icon: ZetaIcon(ZetaIcons.search), - ) - ], - searchController: _searchControllerRegular, + searchController: _searchController, onSearch: (text) => debugPrint('search text: $text'), onSearchMicrophoneIconPressed: () async { var sampleTexts = ['This is a sample text', 'Another sample', 'Speech recognition text', 'Example']; var generatedText = sampleTexts[Random().nextInt(sampleTexts.length)]; - _searchControllerRegular.text = generatedText; + _searchController.text = generatedText; }, ), Text('Extended', style: ZetaTextStyles.titleLarge), @@ -188,48 +170,6 @@ class _TopAppBarExampleState extends State { ], ), ), - Text('Extended Search', style: ZetaTextStyles.titleLarge), - SizedBox( - width: 800, - height: 200, - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended( - leading: BackButton(), - title: Text("Title"), - actions: [ - IconButton( - onPressed: _showHideSearchExtended, - icon: ZetaIcon(ZetaIcons.search), - ) - ], - searchController: _searchControllerExtended, - onSearch: (text) => debugPrint('search text: $text'), - onSearchMicrophoneIconPressed: () async { - var sampleTexts = [ - 'This is a sample text', - 'Another sample', - 'Speech recognition text', - 'Example' - ]; - var generatedText = sampleTexts[Random().nextInt(sampleTexts.length)]; - _searchControllerExtended.text = generatedText; - }, - ), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 800, - color: Zeta.of(context).colors.surfaceSelectedHover, - child: CustomPaint( - painter: Painter(context: context), - size: Size(800, 800), - ), - ), - ), - ], - ), - ), ].gap(20), ), ), diff --git a/example/widgetbook/pages/components/top_app_bar_widgetbook.dart b/example/widgetbook/pages/components/top_app_bar_widgetbook.dart index 96f80616..ecc9b378 100644 --- a/example/widgetbook/pages/components/top_app_bar_widgetbook.dart +++ b/example/widgetbook/pages/components/top_app_bar_widgetbook.dart @@ -13,7 +13,7 @@ Widget defaultTopAppBarUseCase(BuildContext context) { label: "Title Alignment", options: [ ZetaTopAppBarType.defaultAppBar, - ZetaTopAppBarType.centeredTitle, + ZetaTopAppBarType.centered, ], initialOption: ZetaTopAppBarType.defaultAppBar, labelBuilder: (option) { @@ -53,7 +53,7 @@ Widget defaultTopAppBarUseCase(BuildContext context) { icon: ZetaIcon(ZetaIcons.more_vertical), ) ] - : null, + : [], ), ], )); @@ -84,7 +84,7 @@ class _SearchUseCaseState extends State<_SearchUseCase> { label: "Title Alignment", options: [ ZetaTopAppBarType.defaultAppBar, - ZetaTopAppBarType.centeredTitle, + ZetaTopAppBarType.centered, ], initialOption: ZetaTopAppBarType.defaultAppBar, labelBuilder: (option) { @@ -101,7 +101,7 @@ class _SearchUseCaseState extends State<_SearchUseCase> { initialValue: false, ); - return ZetaTopAppBar( + return ZetaTopAppBar.search( leading: IconButton( onPressed: () {}, icon: ZetaIcon(leadingIcon), @@ -118,43 +118,26 @@ class _SearchUseCaseState extends State<_SearchUseCase> { searchController.text = generatedText; } : null, - actions: [ - IconButton( - onPressed: () { - searchController.isEnabled ? searchController.closeSearch() : searchController.startSearch(); - }, - icon: ZetaIcon(ZetaIcons.search)), - ], ); } } -Widget extendedTopAppBarUseCase(BuildContext context) => ExtendedSearch(); +Widget extendedTopAppBarUseCase(BuildContext context) => ExtendedTopAppBar(); -class ExtendedSearch extends StatefulWidget { - const ExtendedSearch({super.key}); +class ExtendedTopAppBar extends StatefulWidget { + const ExtendedTopAppBar({super.key}); @override - State createState() => _ExtendedSearchState(); + State createState() => _ExtendedTopAppBarState(); } -class _ExtendedSearchState extends State { - final _searchControllerExtended = ZetaSearchController(); - - void _showHideSearchExtended() { - _searchControllerExtended.isEnabled - ? _searchControllerExtended.closeSearch() - : _searchControllerExtended.startSearch(); - } - +class _ExtendedTopAppBarState extends State { @override Widget build(BuildContext context) { final title = context.knobs.string(label: "Title", initialValue: "Title"); final leadingIcon = iconKnob(context, name: 'Leading Icon', initial: ZetaIcons.hamburger_menu); - final showSearch = context.knobs.boolean(label: 'Search variant', initialValue: false); - return WidgetbookScaffold( removeBody: true, builder: (context, constraints) => SafeArea( @@ -166,41 +149,20 @@ class _ExtendedSearchState extends State { ZetaTopAppBar.extended( leading: IconButton(icon: ZetaIcon(leadingIcon), onPressed: () {}), title: Text(title), - actions: showSearch - ? [ - IconButton( - onPressed: _showHideSearchExtended, - icon: ZetaIcon(ZetaIcons.search), - ) - ] - : [ - IconButton( - onPressed: () {}, - icon: ZetaIcon(Icons.language), - ), - IconButton( - onPressed: () {}, - icon: ZetaIcon(Icons.favorite), - ), - IconButton( - onPressed: () {}, - icon: ZetaIcon(ZetaIcons.more_vertical), - ) - ], - searchController: showSearch ? _searchControllerExtended : null, - onSearch: showSearch ? (text) => debugPrint('search text: $text') : null, - onSearchMicrophoneIconPressed: showSearch - ? () async { - var sampleTexts = [ - 'This is a sample text', - 'Another sample', - 'Speech recognition text', - 'Example' - ]; - var generatedText = sampleTexts[Random().nextInt(sampleTexts.length)]; - _searchControllerExtended.text = generatedText; - } - : null, + actions: [ + IconButton( + onPressed: () {}, + icon: ZetaIcon(Icons.language), + ), + IconButton( + onPressed: () {}, + icon: ZetaIcon(Icons.favorite), + ), + IconButton( + onPressed: () {}, + icon: ZetaIcon(ZetaIcons.more_vertical), + ) + ], ), SliverToBoxAdapter( child: Container( @@ -232,7 +194,7 @@ class Painter extends CustomPainter { var p1 = Offset(i, -10); var p2 = Offset(constraints.maxHeight + i, constraints.maxHeight * 4); var paint = Paint() - ..color = Zeta.of(context).colors.primary + ..color = Zeta.of(context).colors.surfacePrimarySubtle ..strokeWidth = Zeta.of(context).spacing.minimum; canvas.drawLine(p1, p2, paint); } diff --git a/lib/src/components/screen_header_bar/screen_header_bar.dart b/lib/src/components/screen_header_bar/screen_header_bar.dart index 544eefe6..4212abf0 100644 --- a/lib/src/components/screen_header_bar/screen_header_bar.dart +++ b/lib/src/components/screen_header_bar/screen_header_bar.dart @@ -50,7 +50,7 @@ class ZetaScreenHeaderBar extends ZetaStatelessWidget { title: title, titleTextStyle: ZetaTextStyles.titleLarge, actions: actionButtonLabel == null - ? null + ? [] : [ ZetaButton( label: actionButtonLabel!, diff --git a/lib/src/components/top_app_bar/extended_top_app_bar.dart b/lib/src/components/top_app_bar/extended_top_app_bar.dart index b2577646..c6a8d289 100644 --- a/lib/src/components/top_app_bar/extended_top_app_bar.dart +++ b/lib/src/components/top_app_bar/extended_top_app_bar.dart @@ -15,7 +15,6 @@ class ZetaExtendedAppBarDelegate extends SliverPersistentHeaderDelegate { required this.shrinks, this.actions, this.leading, - this.searchController, }); /// Title of the app bar. @@ -27,56 +26,49 @@ class ZetaExtendedAppBarDelegate extends SliverPersistentHeaderDelegate { /// Widget displayed first in the app bar row. final Widget? leading; - /// Used to control the search textfield and states. - final ZetaSearchController? searchController; - /// If `ZetaTopAppBarType.extend` shrinks. Does not affect other types of app bar. final bool shrinks; static const double _maxExtent = 104; - static const double _minExtent = 52; + static const double _minExtent = 56; @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { - final searchBarOffsetTop = Zeta.of(context).spacing.minimum * 1.5; - final searchBarOffsetRight = Zeta.of(context).spacing.minimum * 22; - final maxExtent = Zeta.of(context).spacing.minimum * 26; - final leftMin = Zeta.of(context).spacing.large; - final topMin = Zeta.of(context).spacing.xl; - final topMax = Zeta.of(context).spacing.minimum * 15; + final spacing = Zeta.of(context).spacing; + + final maxExtent = spacing.minimum * 26; + final leftMin = spacing.large; + final topMin = spacing.xl; + final topMax = spacing.minimum * 15; /// If there is no leading widget, the left margin should not change /// If there is a leading widget, the left margin should be the same as the leading widget's width plus padding - final leftMax = leading == null ? leftMin : _minExtent + Zeta.of(context).spacing.small; + final leftMax = leading == null ? leftMin : _minExtent + spacing.small; + + final top = shrinks + ? (topMax + (-1 * shrinkOffset)).clamp( + topMin - (spacing.minimum), + topMax - (spacing.minimum), + ) + : topMax; return ConstrainedBox( - constraints: BoxConstraints(minHeight: Zeta.of(context).spacing.xl_9, maxHeight: maxExtent), + constraints: BoxConstraints(minHeight: spacing.xl_9, maxHeight: maxExtent), child: ColoredBox( color: Zeta.of(context).colors.surfacePrimary, child: Stack( children: [ Positioned( - top: shrinks - ? (topMax + (-1 * shrinkOffset)).clamp( - topMin - - (searchController != null && searchController!.isEnabled - ? searchBarOffsetTop - : Zeta.of(context).spacing.none), - topMax, - ) - : topMax, + top: top, left: shrinks ? ((shrinkOffset / maxExtent) * _maxExtent).clamp(leftMin, leftMax) : leftMin, - right: searchController != null && searchController!.isEnabled - ? searchBarOffsetRight - : Zeta.of(context).spacing.none, + right: spacing.none, child: title, ), - if (leading != null) - Positioned(top: Zeta.of(context).spacing.medium, left: Zeta.of(context).spacing.small, child: leading!), + if (leading != null) Positioned(top: spacing.small, left: spacing.small, child: leading!), if (actions != null) Positioned( - top: Zeta.of(context).spacing.medium, - right: Zeta.of(context).spacing.small, + top: spacing.small, + right: spacing.small, child: Row(children: actions!), ), ], diff --git a/lib/src/components/top_app_bar/search_top_app_bar.dart b/lib/src/components/top_app_bar/search_top_app_bar.dart index a05b15f4..7507541f 100644 --- a/lib/src/components/top_app_bar/search_top_app_bar.dart +++ b/lib/src/components/top_app_bar/search_top_app_bar.dart @@ -66,20 +66,12 @@ class _ZetaTopAppBarSearchFieldState extends State wit @override void initState() { - _textFocusNode.addListener(_onFocusChanged); widget.searchController?.addListener(_onSearchControllerChanged); widget.searchController?.textEditingController ??= TextEditingController(); super.initState(); } - void _onFocusChanged() { - final text = widget.searchController?.text ?? ''; - final shouldCloseSearch = _isSearching && text.isEmpty && !_textFocusNode.hasFocus; - - if (shouldCloseSearch) _closeSearch(); - } - void _onSearchControllerChanged() { final controller = widget.searchController; if (controller == null) return; @@ -151,7 +143,7 @@ class _ZetaTopAppBarSearchFieldState extends State wit children: [ Row( mainAxisAlignment: - widget.type == ZetaTopAppBarType.centeredTitle ? MainAxisAlignment.center : MainAxisAlignment.start, + widget.type == ZetaTopAppBarType.centered ? MainAxisAlignment.center : MainAxisAlignment.start, children: [ widget.child ?? const Nothing(), ], diff --git a/lib/src/components/top_app_bar/top_app_bar.dart b/lib/src/components/top_app_bar/top_app_bar.dart index 21f11e5b..07030848 100644 --- a/lib/src/components/top_app_bar/top_app_bar.dart +++ b/lib/src/components/top_app_bar/top_app_bar.dart @@ -8,6 +8,8 @@ import 'search_top_app_bar.dart'; export 'search_top_app_bar.dart' hide ZetaTopAppBarSearchField; /// Top app bars provide content and actions related to the current screen. +/// +/// To create Extended, Centered, or Search app bars, use the respective constructors. /// {@category Components} /// /// Figma: https://www.figma.com/design/JesXQFLaPJLc1BdBM4sisI/%F0%9F%A6%93-ZDS---Components?node-id=229-37&node-type=canvas&m=dev @@ -18,33 +20,76 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { const ZetaTopAppBar({ super.key, super.rounded, - this.actions, + this.actions = const [], this.automaticallyImplyLeading = true, - this.searchController, this.leading, this.title, this.titleTextStyle, this.type = ZetaTopAppBarType.defaultAppBar, - this.onSearch, - this.searchHintText = 'Search', - this.onSearchMicrophoneIconPressed, - }) : shrinks = false; + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : shrinks = false, + onSearch = null, + searchHintText = null, + searchController = null, + clearSemanticLabel = null, + microphoneSemanticLabel = null, + searchBackSemanticLabel = null, + searchSemanticLabel = null, + onSearchMicrophoneIconPressed = null; /// Creates a ZetaTopAppBar with centered title. const ZetaTopAppBar.centered({ super.key, super.rounded, - this.actions, + this.actions = const [], + this.automaticallyImplyLeading = true, + this.leading, + this.title, + this.titleTextStyle, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : type = ZetaTopAppBarType.centered, + onSearch = null, + searchHintText = null, + searchController = null, + onSearchMicrophoneIconPressed = null, + clearSemanticLabel = null, + searchBackSemanticLabel = null, + microphoneSemanticLabel = null, + searchSemanticLabel = null, + shrinks = false; + + /// Creates a ZetaTopAppBar with an expanding search field. + /// This will append a search icon to the right of the app bar. + /// When the search icon is pressed, the search field will expand and replace the title widget. + /// It will replace the leading widget with a back button which closes the search field. + /// The search field can be controlled externally by the [searchController]. + const ZetaTopAppBar.search({ + super.key, + super.rounded, + this.type = ZetaTopAppBarType.defaultAppBar, this.automaticallyImplyLeading = true, this.searchController, this.leading, this.title, this.titleTextStyle, this.onSearch, - this.searchHintText = 'Search', + this.searchHintText, this.onSearchMicrophoneIconPressed, - }) : type = ZetaTopAppBarType.centeredTitle, - shrinks = false; + this.actions = const [], + this.clearSemanticLabel, + this.microphoneSemanticLabel, + this.searchSemanticLabel, + this.searchBackSemanticLabel, + }) : shrinks = false, + assert(type != ZetaTopAppBarType.extended, 'Search app bars cannot be extended'); /// Creates a ZetaTopAppBar with an extended title over 2 lines. /// @@ -52,23 +97,32 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { const ZetaTopAppBar.extended({ super.key, super.rounded, - this.actions, + this.actions = const [], this.automaticallyImplyLeading = true, - this.searchController, this.leading, this.title, this.titleTextStyle, - this.onSearch, - this.searchHintText = 'Search', - this.onSearchMicrophoneIconPressed, this.shrinks = true, - }) : type = ZetaTopAppBarType.extendedTitle; + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : type = ZetaTopAppBarType.extended, + onSearch = null, + searchHintText = null, + onSearchMicrophoneIconPressed = null, + clearSemanticLabel = null, + microphoneSemanticLabel = null, + searchSemanticLabel = null, + searchBackSemanticLabel = null, + searchController = null; /// Called when text in the search field is submitted. - final void Function(String)? onSearch; + final ValueChanged? onSearch; /// A list of Widgets to display in a row after the [title] widget. - final List? actions; + final List actions; /// Configures whether the back button to be displayed. final bool automaticallyImplyLeading; @@ -97,6 +151,18 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { /// If `ZetaTopAppBarType.extend` shrinks. Does not affect other types of app bar. final bool shrinks; + /// The semantic label for the clear icon. + final String? clearSemanticLabel; + + /// The semantic label for the microphone icon. + final String? microphoneSemanticLabel; + + /// The semantic label for the search icon. + final String? searchSemanticLabel; + + /// The semantic label for the back icon when search is open. + final String? searchBackSemanticLabel; + @override State createState() => _ZetaTopAppBarState(); @@ -113,34 +179,28 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { ..add(DiagnosticsProperty('searchController', searchController)) ..add(StringProperty('searchHintText', searchHintText)) ..add(EnumProperty('type', type)) - ..add(DiagnosticsProperty('shrinks', shrinks)); + ..add(DiagnosticsProperty('shrinks', shrinks)) + ..add(StringProperty('clearSemanticLabel', clearSemanticLabel)) + ..add(StringProperty('microphoneSemanticLabel', microphoneSemanticLabel)) + ..add(StringProperty('searchSemanticLabel', searchSemanticLabel)) + ..add(StringProperty('searchBackSemanticLabel', searchBackSemanticLabel)); } } class _ZetaTopAppBarState extends State { - bool _isSearchEnabled = false; + late ZetaSearchController _searchController; + bool get _searchEnabled => widget.searchController != null || widget.onSearch != null; + bool get _searchActive => _searchController.isEnabled; @override void initState() { - widget.searchController?.addListener(_onSearchControllerChanged); + _searchController = widget.searchController ?? ZetaSearchController(); + _searchController.addListener(() => setState(() {})); super.initState(); } - void _onSearchControllerChanged() { - final controller = widget.searchController; - if (controller == null) return; - - setState(() => _isSearchEnabled = controller.isEnabled); - } - - @override - void dispose() { - widget.searchController?.removeListener(_onSearchControllerChanged); - super.dispose(); - } - - Widget _getTitleText(ZetaColors colors) { - var title = widget.title; + Widget _getTitle(ZetaColors colors) { + Widget? title = widget.title; if (widget.title is Row) { final oldRow = widget.title! as Row; title = Row( @@ -169,43 +229,67 @@ class _ZetaTopAppBarState extends State { } List? _getActions(ZetaColors colors) { - return _isSearchEnabled - ? [ - IconButtonTheme( - data: IconButtonThemeData( - style: IconButton.styleFrom(iconSize: Zeta.of(context).spacing.xl), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - color: colors.cool.shade50, - onPressed: () => widget.searchController?.clearText(), - icon: const ZetaIcon(ZetaIcons.cancel), - ), - if (widget.onSearchMicrophoneIconPressed != null) ...[ - SizedBox( - height: Zeta.of(context).spacing.xl_2, - child: VerticalDivider(width: ZetaBorders.medium, color: colors.cool.shade70), - ), - IconButton( - onPressed: widget.onSearchMicrophoneIconPressed, - icon: const ZetaIcon(ZetaIcons.microphone), - ), - ], - ], + if (_searchActive) { + return [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Semantics( + label: widget.clearSemanticLabel, + button: true, + child: IconButton( + color: colors.cool.shade50, + onPressed: () => _searchController.clearText(), + icon: ZetaIcon( + ZetaIcons.cancel, + size: Zeta.of(context).spacing.xl, + ), ), ), - ] - : widget.actions; + if (widget.onSearchMicrophoneIconPressed != null) ...[ + SizedBox( + height: Zeta.of(context).spacing.xl_2, + child: VerticalDivider(width: ZetaBorders.medium, color: colors.cool.shade70), + ), + Semantics( + label: widget.microphoneSemanticLabel, + button: true, + child: IconButton( + onPressed: widget.onSearchMicrophoneIconPressed, + icon: const ZetaIcon(ZetaIcons.microphone), + ), + ), + ], + ], + ), + ]; + } + + if (_searchEnabled) { + return [ + ...widget.actions, + Semantics( + label: widget.searchSemanticLabel, + button: true, + child: IconButton( + onPressed: () => setState(() { + _searchController.startSearch(); + }), + icon: const ZetaIcon(ZetaIcons.search), + ), + ), + ]; + } + return widget.actions; } @override Widget build(BuildContext context) { final colors = Zeta.of(context).colors; + final spacing = Zeta.of(context).spacing; final actions = _getActions(colors); - final titleText = _getTitleText(colors); + final titleText = _getTitle(colors); final title = widget.searchController != null ? ZetaTopAppBarSearchField( @@ -213,48 +297,55 @@ class _ZetaTopAppBarState extends State { hintText: widget.searchHintText ?? 'Search', onSearch: widget.onSearch, type: widget.type, - isExtended: widget.type == ZetaTopAppBarType.extendedTitle, + isExtended: widget.type == ZetaTopAppBarType.extended, child: titleText, ) : titleText; - if (widget.type == ZetaTopAppBarType.extendedTitle) { + if (widget.type == ZetaTopAppBarType.extended) { return SliverPersistentHeader( pinned: true, delegate: ZetaExtendedAppBarDelegate( actions: actions, leading: widget.leading, - searchController: widget.searchController, title: title, shrinks: widget.shrinks, ), ); } + Widget? leading = widget.leading; + + if (_searchActive) { + leading = Semantics( + label: widget.searchBackSemanticLabel, + button: true, + child: IconButton( + onPressed: _searchController.closeSearch, + icon: const ZetaIcon(ZetaIcons.arrow_back), + ), + ); + } + return ZetaRoundedScope( rounded: context.rounded, - child: ColoredBox( - color: colors.surfacePrimary, - child: IconButtonTheme( - data: IconButtonThemeData(style: IconButton.styleFrom(tapTargetSize: MaterialTapTargetSize.shrinkWrap)), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: Zeta.of(context).spacing.minimum), - child: AppBar( - elevation: 0, - scrolledUnderElevation: 0, - iconTheme: IconThemeData(color: colors.cool.shade90), - leadingWidth: Zeta.of(context).spacing.xl_6, - leading: widget.leading, - automaticallyImplyLeading: widget.automaticallyImplyLeading, - surfaceTintColor: Colors.transparent, - centerTitle: widget.type == ZetaTopAppBarType.centeredTitle, - titleTextStyle: widget.titleTextStyle == null - ? ZetaTextStyles.bodyLarge.copyWith(color: colors.textDefault) - : widget.titleTextStyle!.copyWith(color: colors.textDefault), - title: title, - actions: actions, - ), - ), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: spacing.minimum), + child: AppBar( + elevation: 0, + scrolledUnderElevation: 0, + backgroundColor: colors.surfacePrimary, + iconTheme: IconThemeData(color: colors.iconDefault), + leading: leading, + toolbarHeight: spacing.xl_9, + automaticallyImplyLeading: widget.automaticallyImplyLeading, + surfaceTintColor: Colors.transparent, + centerTitle: widget.type == ZetaTopAppBarType.centered, + titleTextStyle: widget.titleTextStyle == null + ? ZetaTextStyles.bodyLarge.copyWith(color: colors.textDefault) + : widget.titleTextStyle!.copyWith(color: colors.textDefault), + title: title, + actions: actions, ), ), ); @@ -267,8 +358,16 @@ enum ZetaTopAppBarType { defaultAppBar, /// Title in the center. + @Deprecated('Use ZetaTopAppBar.centered instead. ' 'Deprecated as of 0.16.0') centeredTitle, + /// Aligns the title to the center of the app bar. + centered, + /// Title below the app bar. + @Deprecated('Use ZetaTopAppBar.extended instead. ' 'Deprecated as of 0.16.0') extendedTitle, + + /// Title extends over 2 lines and collapses when scrolled. + extended, } diff --git a/test/src/components/avatar/avatar_test.dart b/test/src/components/avatar/avatar_test.dart index 7e02d661..10ab2880 100644 --- a/test/src/components/avatar/avatar_test.dart +++ b/test/src/components/avatar/avatar_test.dart @@ -322,7 +322,6 @@ void main() { ZetaAvatar( size: size, ), - ZetaAvatar, 'avatar_default_${size.toString().split('.').last}', ); goldenTest( @@ -331,7 +330,6 @@ void main() { initials: 'AB', size: size, ), - ZetaAvatar, 'avatar_initials_${size.toString().split('.').last}', ); goldenTest( @@ -340,7 +338,6 @@ void main() { image: Image.file(File('/assets/maxresdefault.jpg')), size: size, ), - ZetaAvatar, 'avatar_image_${size.toString().split('.').last}', ); goldenTest( @@ -349,7 +346,6 @@ void main() { name: 'John Doe', size: size, ), - ZetaAvatar, 'avatar_from_name_${size.toString().split('.').last}', ); goldenTest( @@ -358,7 +354,6 @@ void main() { upperBadge: const ZetaAvatarBadge.notification(value: 3), size: size, ), - ZetaAvatar, 'avatar_upper_badge_${size.toString().split('.').last}', ); goldenTest( @@ -367,7 +362,6 @@ void main() { lowerBadge: const ZetaAvatarBadge.icon(icon: Icons.star), size: size, ), - ZetaAvatar, 'avatar_lower_badge_${size.toString().split('.').last}', ); } diff --git a/test/src/components/badge/indicator_test.dart b/test/src/components/badge/indicator_test.dart index 12f768e5..43c683a8 100644 --- a/test/src/components/badge/indicator_test.dart +++ b/test/src/components/badge/indicator_test.dart @@ -157,8 +157,8 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaIndicator(), ZetaIndicator, 'indicator_default'); - goldenTest(goldenFile, const ZetaIndicator.icon(), ZetaIndicator, 'indicator_icon_default'); + goldenTest(goldenFile, const ZetaIndicator(), 'indicator_default'); + goldenTest(goldenFile, const ZetaIndicator.icon(), 'indicator_icon_default'); goldenTest( goldenFile, const ZetaIndicator.icon( @@ -167,10 +167,9 @@ void main() { inverse: true, size: ZetaWidgetSize.medium, ), - ZetaIndicator, 'indicator_icon_values', ); - goldenTest(goldenFile, const ZetaIndicator.notification(), ZetaIndicator, 'indicator_notification_default'); + goldenTest(goldenFile, const ZetaIndicator.notification(), 'indicator_notification_default'); goldenTest( goldenFile, const ZetaIndicator.notification( @@ -180,7 +179,6 @@ void main() { size: ZetaWidgetSize.small, value: 1, ), - ZetaIndicator, 'indicator_notification_values', ); }); diff --git a/test/src/components/badge/label_test.dart b/test/src/components/badge/label_test.dart index ee6fa91e..24d1e99b 100644 --- a/test/src/components/badge/label_test.dart +++ b/test/src/components/badge/label_test.dart @@ -104,33 +104,29 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaLabel(label: 'Test Label'), ZetaLabel, 'label_default'); + goldenTest(goldenFile, const ZetaLabel(label: 'Test Label'), 'label_default'); goldenTest( goldenFile, const ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.positive), - ZetaLabel, 'label_positive', ); goldenTest( goldenFile, const ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.warning), - ZetaLabel, 'label_warning', ); goldenTest( goldenFile, const ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.negative), - ZetaLabel, 'label_negative', ); goldenTest( goldenFile, const ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.neutral), - ZetaLabel, 'label_neutral', ); - goldenTest(goldenFile, const ZetaLabel(label: 'Test Label'), ZetaLabel, 'label_dark', themeMode: ThemeMode.dark); - goldenTest(goldenFile, const ZetaLabel(label: 'Test Label', rounded: false), ZetaLabel, 'label_sharp'); + goldenTest(goldenFile, const ZetaLabel(label: 'Test Label'), 'label_dark', themeMode: ThemeMode.dark); + goldenTest(goldenFile, const ZetaLabel(label: 'Test Label', rounded: false), 'label_sharp'); }); group('Performance Tests', () {}); } diff --git a/test/src/components/badge/priority_pill_test.dart b/test/src/components/badge/priority_pill_test.dart index 0c4b281f..92c120bb 100644 --- a/test/src/components/badge/priority_pill_test.dart +++ b/test/src/components/badge/priority_pill_test.dart @@ -121,7 +121,7 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaPriorityPill(), ZetaPriorityPill, 'priority_pill_default'); + goldenTest(goldenFile, const ZetaPriorityPill(), 'priority_pill_default'); goldenTest( goldenFile, const ZetaPriorityPill( @@ -131,7 +131,6 @@ void main() { rounded: false, size: ZetaPriorityPillSize.small, ), - ZetaPriorityPill, 'priority_pill_high', ); goldenTest( @@ -140,7 +139,6 @@ void main() { type: ZetaPriorityPillType.medium, isBadge: true, ), - ZetaPriorityPill, 'priority_pill_medium', ); goldenTest( @@ -150,7 +148,6 @@ void main() { size: ZetaPriorityPillSize.small, isBadge: true, ), - ZetaPriorityPill, 'priority_pill_low', ); }); diff --git a/test/src/components/badge/status_label_test.dart b/test/src/components/badge/status_label_test.dart index 2198a529..992f6bad 100644 --- a/test/src/components/badge/status_label_test.dart +++ b/test/src/components/badge/status_label_test.dart @@ -57,14 +57,13 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaStatusLabel(label: 'Test Label'), ZetaStatusLabel, 'status_label_default'); + goldenTest(goldenFile, const ZetaStatusLabel(label: 'Test Label'), 'status_label_default'); goldenTest( goldenFile, const ZetaStatusLabel( label: 'Custom Icon', customIcon: Icons.person, ), - ZetaStatusLabel, 'status_label_custom', ); }); diff --git a/test/src/components/badge/tag_test.dart b/test/src/components/badge/tag_test.dart index c7990a11..dc1cdba5 100644 --- a/test/src/components/badge/tag_test.dart +++ b/test/src/components/badge/tag_test.dart @@ -50,8 +50,8 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaTag.right(label: 'Tag', rounded: false), ZetaTag, 'tag_right'); - goldenTest(goldenFile, const ZetaTag.left(label: 'Tag', rounded: true), ZetaTag, 'tag_left'); + goldenTest(goldenFile, const ZetaTag.right(label: 'Tag', rounded: false), 'tag_right'); + goldenTest(goldenFile, const ZetaTag.left(label: 'Tag', rounded: true), 'tag_left'); }); group('Performance Tests', () {}); } diff --git a/test/src/components/banner/banner_test.dart b/test/src/components/banner/banner_test.dart index 44d69c56..a221f5fb 100644 --- a/test/src/components/banner/banner_test.dart +++ b/test/src/components/banner/banner_test.dart @@ -347,8 +347,8 @@ void main() { ); }, ), - ZetaBanner, 'banner_${type.toString().split('.').last}', + widgetType: ZetaBanner, ); } }); diff --git a/test/src/components/button/button_test.dart b/test/src/components/button/button_test.dart index fb72ec0e..140d7537 100644 --- a/test/src/components/button/button_test.dart +++ b/test/src/components/button/button_test.dart @@ -258,47 +258,44 @@ void main() { }); group('Golden Tests', () { - goldenTest(goldenFile, ZetaButton.primary(onPressed: () {}, label: 'Test Button'), ZetaButton, 'button_primary'); + goldenTest( + goldenFile, + ZetaButton.primary(onPressed: () {}, label: 'Test Button'), + 'button_primary', + ); goldenTest( goldenFile, ZetaButton.secondary(onPressed: () {}, label: 'Test Button', leadingIcon: Icons.abc, size: ZetaWidgetSize.small), - ZetaButton, 'button_secondary', ); goldenTest( goldenFile, ZetaButton.positive(onPressed: () {}, label: 'Test Button', trailingIcon: Icons.abc), - ZetaButton, 'button_positive', ); goldenTest( goldenFile, ZetaButton.negative(onPressed: () {}, label: 'Test Button', size: ZetaWidgetSize.small), - ZetaButton, 'button_negative', ); goldenTest( goldenFile, ZetaButton.outline(onPressed: () {}, label: 'Test Button', size: ZetaWidgetSize.large), - ZetaButton, 'button_outline', ); goldenTest( goldenFile, const ZetaButton.outlineSubtle(label: 'Test Button', borderType: ZetaWidgetBorder.sharp), - ZetaButton, 'button_outline_subtle', ); goldenTest( goldenFile, ZetaButton.text(onPressed: () {}, label: 'Test Button', borderType: ZetaWidgetBorder.full), - ZetaButton, 'button_text', ); goldenTest( goldenFile, const ZetaButton.text(label: 'Test Button', borderType: ZetaWidgetBorder.full), - ZetaButton, 'button_disabled', ); }); diff --git a/test/src/components/chat_item/chat_item_test.dart b/test/src/components/chat_item/chat_item_test.dart index e72a00bf..88475d70 100644 --- a/test/src/components/chat_item/chat_item_test.dart +++ b/test/src/components/chat_item/chat_item_test.dart @@ -518,7 +518,7 @@ void main() { const subtitle = Text('Hello, how are you?'); final time = DateTime.now(); - goldenTestWithCallbacks( + goldenTest( goldenFile, Scaffold( body: Column( @@ -544,15 +544,15 @@ void main() { ], ), ), - ZetaChatItem, 'chat_item_default', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -578,15 +578,15 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_highlighted', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -604,20 +604,20 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_slidable_actions', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -644,20 +644,20 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_pale_slidable_buttons', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -689,20 +689,20 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_pale_and_regular_buttons', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -724,15 +724,15 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_custom_leading', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -756,20 +756,20 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_custom_slidable_buttons', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -788,13 +788,13 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_small_screen_slidable_button', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(315, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); diff --git a/test/src/components/checkbox/checkbox_test.dart b/test/src/components/checkbox/checkbox_test.dart index b718cef9..f0b6c598 100644 --- a/test/src/components/checkbox/checkbox_test.dart +++ b/test/src/components/checkbox/checkbox_test.dart @@ -139,14 +139,13 @@ void main() { }); }); group('Golden Tests', () { - goldenTestWithCallbacks( + goldenTest( goldenFile, ZetaCheckbox( onChanged: (value) {}, ), - ZetaCheckbox, 'checkbox_hover', - after: (tester) async { + beforeComparison: (tester) async { final checkboxFinder = find.byType(ZetaCheckbox); // Hover state @@ -165,7 +164,6 @@ void main() { value: true, onChanged: print, ), - ZetaCheckbox, 'checkbox_enabled', ); @@ -174,7 +172,6 @@ void main() { ZetaCheckbox( value: true, ), - ZetaCheckbox, 'checkbox_disabled', ); }); diff --git a/test/src/components/comms_button/comms_button_test.dart b/test/src/components/comms_button/comms_button_test.dart index 2ff9c648..539792c0 100644 --- a/test/src/components/comms_button/comms_button_test.dart +++ b/test/src/components/comms_button/comms_button_test.dart @@ -217,7 +217,6 @@ void main() { icon: ZetaIcons.phone, type: type, ), - ZetaCommsButton, 'CommsButton_${type.name}', ); } diff --git a/test/src/components/dialpad/dialpad_test.dart b/test/src/components/dialpad/dialpad_test.dart index b4f5cc7e..37575292 100644 --- a/test/src/components/dialpad/dialpad_test.dart +++ b/test/src/components/dialpad/dialpad_test.dart @@ -287,21 +287,18 @@ void main() { onNumber: print, onText: print, ), - ZetaDialPad, 'dialpad_enabled', screenSize: const Size(1000, 1000), ); goldenTest( goldenFile, const ZetaDialPad(), - ZetaDialPad, 'dialpad_disabled', screenSize: const Size(1000, 1000), ); goldenTest( goldenFile, const ZetaDialPadButton(primary: '1'), - ZetaDialPadButton, 'dialpadbutton', screenSize: const Size(1000, 1000), ); diff --git a/test/src/components/fab/fab_test.dart b/test/src/components/fab/fab_test.dart index 2820f8ef..636f8b80 100644 --- a/test/src/components/fab/fab_test.dart +++ b/test/src/components/fab/fab_test.dart @@ -211,15 +211,13 @@ void main() { label: 'Label', onPressed: () {}, ), - ZetaFAB, 'FAB_default', ); - goldenTestWithCallbacks( + goldenTest( goldenFile, ZetaFAB(scrollController: ScrollController(), label: 'Label', onPressed: () => {}), - ZetaFAB, 'FAB_pressed', - after: (tester) async { + beforeComparison: (tester) async { await tester.press(find.byType(ZetaFAB)); await tester.pumpAndSettle(); }, @@ -233,15 +231,13 @@ void main() { shape: ZetaWidgetBorder.rounded, size: ZetaFabSize.large, ), - ZetaFAB, 'FAB_inverse', ); - goldenTestWithCallbacks( + goldenTest( goldenFile, ZetaFAB(scrollController: ScrollController(), label: 'Label', onPressed: () => {}), - ZetaFAB, 'FAB_pressed', - after: (tester) async { + beforeComparison: (tester) async { await tester.press(find.byType(ZetaFAB)); await tester.pumpAndSettle(); }, @@ -255,13 +251,11 @@ void main() { type: ZetaFabType.secondary, shape: ZetaWidgetBorder.sharp, ), - ZetaFAB, 'FAB_secondary', ); goldenTest( goldenFile, ZetaFAB(scrollController: ScrollController(), label: 'Disabled'), - ZetaFAB, 'FAB_disabled', ); }); 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 index f29bffed..8d840214 100644 --- a/test/src/components/in_page_banner/in_page_banner_test.dart +++ b/test/src/components/in_page_banner/in_page_banner_test.dart @@ -145,19 +145,16 @@ void main() { goldenTest( goldenFile, const ZetaInPageBanner(content: Text('Test'), title: 'Title'), - ZetaInPageBanner, 'in_page_banner_default', ); goldenTest( goldenFile, ZetaInPageBanner(content: const Text('Test'), onClose: () {}, status: ZetaWidgetStatus.negative), - ZetaInPageBanner, 'in_page_banner_negative', ); goldenTest( goldenFile, const ZetaInPageBanner(content: Text('Test'), status: ZetaWidgetStatus.positive), - ZetaInPageBanner, 'in_page_banner_positive', ); goldenTest( @@ -172,7 +169,6 @@ void main() { ), ], ), - ZetaInPageBanner, 'in_page_banner_buttons', ); }); diff --git a/test/src/components/password/password_input_test.dart b/test/src/components/password/password_input_test.dart index c67a7ad6..d31f0479 100644 --- a/test/src/components/password/password_input_test.dart +++ b/test/src/components/password/password_input_test.dart @@ -90,9 +90,9 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, ZetaPasswordInput(), ZetaPasswordInput, 'password_default'); + goldenTest(goldenFile, ZetaPasswordInput(), 'password_default'); final formKey = GlobalKey(); - goldenTestWithCallbacks( + goldenTest( goldenFile, Form( key: formKey, @@ -106,9 +106,8 @@ void main() { rounded: false, ), ), - ZetaPasswordInput, 'password_error', - after: (tester) async { + beforeComparison: (tester) async { formKey.currentState?.validate(); await tester.pump(); }, diff --git a/test/src/components/search_bar/search_bar_test.dart b/test/src/components/search_bar/search_bar_test.dart index 42e1ed7b..6869471a 100644 --- a/test/src/components/search_bar/search_bar_test.dart +++ b/test/src/components/search_bar/search_bar_test.dart @@ -219,14 +219,13 @@ void main() { }); }); group('Golden Tests', () { - goldenTest(goldenFile, ZetaSearchBar(), ZetaSearchBar, 'search_bar_default'); - goldenTest(goldenFile, ZetaSearchBar(), ZetaSearchBar, 'search_bar_medium'); + goldenTest(goldenFile, ZetaSearchBar(), 'search_bar_default'); + goldenTest(goldenFile, ZetaSearchBar(), 'search_bar_medium'); goldenTest( goldenFile, ZetaSearchBar( size: ZetaWidgetSize.small, ), - ZetaSearchBar, 'search_bar_small', ); goldenTest( @@ -234,7 +233,6 @@ void main() { ZetaSearchBar( shape: ZetaWidgetBorder.full, ), - ZetaSearchBar, 'search_bar_full', ); goldenTest( @@ -242,7 +240,6 @@ void main() { ZetaSearchBar( shape: ZetaWidgetBorder.sharp, ), - ZetaSearchBar, 'search_bar_sharp', ); }); diff --git a/test/src/components/tooltip/tooltip_test.dart b/test/src/components/tooltip/tooltip_test.dart index ef2e6efc..31453509 100644 --- a/test/src/components/tooltip/tooltip_test.dart +++ b/test/src/components/tooltip/tooltip_test.dart @@ -204,8 +204,8 @@ void main() { child: Text('Tooltip up'), ), ), - ZetaTooltip, 'arrow_up', + widgetType: ZetaTooltip, ); goldenTest( goldenFile, @@ -214,8 +214,8 @@ void main() { child: Text('Tooltip down'), ), ), - ZetaTooltip, 'arrow_down', + widgetType: ZetaTooltip, ); goldenTest( goldenFile, @@ -225,8 +225,8 @@ void main() { child: Text('Tooltip left'), ), ), - ZetaTooltip, 'arrow_left', + widgetType: ZetaTooltip, ); goldenTest( goldenFile, @@ -236,8 +236,8 @@ void main() { child: Text('Tooltip right'), ), ), - ZetaTooltip, 'arrow_right', + widgetType: ZetaTooltip, ); }); group('Performance Tests', () {}); diff --git a/test/src/components/top_app_bar/extended_top_app_bar_test.dart b/test/src/components/top_app_bar/extended_top_app_bar_test.dart deleted file mode 100644 index ecc49885..00000000 --- a/test/src/components/top_app_bar/extended_top_app_bar_test.dart +++ /dev/null @@ -1,169 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import '../../../test_utils/test_app.dart'; -import '../../../test_utils/tolerant_comparator.dart'; -import '../../../test_utils/utils.dart'; - -void main() { - const goldenFile = GoldenFiles(component: 'top_app_bar'); - - setUpAll(() { - goldenFileComparator = TolerantComparator(goldenFile.uri); - }); - - testWidgets('ZetaExtendedAppBarDelegate builds correctly', (WidgetTester tester) async { - const title = Text('Title'); - final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; - final leading = IconButton(icon: const Icon(Icons.menu), onPressed: () {}); - const boxKey = Key('box'); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(481, 480); - - await tester.pumpWidget( - TestApp( - home: Builder( - builder: (context) { - return SizedBox( - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended(leading: leading, title: title, actions: actions), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 700, - color: Zeta.of(context).colors.surfaceSelectedHover, - key: boxKey, - ), - ), - ], - ), - ); - }, - ), - ), - ); - - final boxFinder = find.byKey(boxKey); - expect(boxFinder, findsOneWidget); - - await tester.drag(boxFinder.first, const Offset(0, -100)); - await tester.pumpAndSettle(); - - final appBarFinder = find.byType(ZetaTopAppBar); - expect(appBarFinder, findsOneWidget); - - final titleFinder = find.descendant(of: appBarFinder, matching: find.byWidget(title)); - expect(titleFinder, findsOneWidget); - - final actionsFinder = find.descendant(of: appBarFinder, matching: find.byWidget(actions[0])); - expect(actionsFinder, findsOneWidget); - - final leadingFinder = find.descendant(of: appBarFinder, matching: find.byWidget(leading)); - expect(leadingFinder, findsOneWidget); - - await expectLater( - appBarFinder, - matchesGoldenFile(goldenFile.getFileUri('extended_app_bar_shrinks')), - ); - }); - - testWidgets('ZetaExtendedAppBarDelegate shrinks correctly with padding', (WidgetTester tester) async { - const title = Text('Title'); - final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; - final leading = IconButton(icon: const Icon(Icons.menu), onPressed: () {}); - const boxKey = Key('box'); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(481, 480); - - await tester.pumpWidget( - TestApp( - home: Builder( - builder: (context) { - return SizedBox( - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended(leading: leading, title: title, actions: actions), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 700, - color: Zeta.of(context).colors.surfaceSelectedHover, - key: boxKey, - ), - ), - ], - ), - ); - }, - ), - ), - ); - - final boxFinder = find.byKey(boxKey); - expect(boxFinder, findsOneWidget); - - await tester.drag(boxFinder.first, const Offset(0, -100)); - await tester.pumpAndSettle(); - - final appBarFinder = find.byType(ZetaTopAppBar); - expect(appBarFinder, findsOneWidget); - - final positionedFinder = find.descendant(of: appBarFinder, matching: find.byType(Positioned)); - - final positionedWidget = tester.widget(positionedFinder.first); - expect(positionedWidget.left, 60); - }); - testWidgets('ZetaExtendedAppBarDelegate shrinks correctly with padding and no leading', (WidgetTester tester) async { - const title = Text('Title'); - final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; - - const boxKey = Key('box'); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(481, 480); - - await tester.pumpWidget( - TestApp( - home: Builder( - builder: (context) { - return SizedBox( - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended(title: title, actions: actions), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 700, - color: Zeta.of(context).colors.surfaceSelectedHover, - key: boxKey, - ), - ), - ], - ), - ); - }, - ), - ), - ); - - final boxFinder = find.byKey(boxKey); - expect(boxFinder, findsOneWidget); - - await tester.drag(boxFinder.first, const Offset(0, -100)); - await tester.pumpAndSettle(); - - final appBarFinder = find.byType(ZetaTopAppBar); - expect(appBarFinder, findsOneWidget); - - final positionedFinder = find.descendant(of: appBarFinder, matching: find.byType(Positioned)); - - final positionedWidget = tester.widget(positionedFinder.first); - expect(positionedWidget.left, 16); - - await expectLater( - appBarFinder, - matchesGoldenFile(goldenFile.getFileUri('extended_app_bar_shrinks_with_no_leading')), - ); - }); -} diff --git a/test/src/components/top_app_bar/golden/extended_app_bar_shrinks.png b/test/src/components/top_app_bar/golden/extended_app_bar_shrinks.png index 6a0c1237cf93e0ebf43cfba27e66b14b132d73f0..b821a451129da3c390ff8bf6b7a53bba842d0ce6 100644 GIT binary patch delta 214 zcmV;{04e{r5rYtrKsX1jNkl#N0zoz8QV&qO-tP4X{CGVd^#}l)SvA5-1VfVuY2 zAji=F5D{Sjh-`KzJa?UUR|~*W{?JZ~kD*xIbn=k-GL>K@fs~$(s z{dE)oEM*T3n#LHT9s&T4$Z<520RubW Q&;S4c07*qoM6N<$f{Bb(egFUf delta 403 zcmV;E0c`$*5VjGJKv4)_Nkl^?>~H; zW!B?I56ABA?tjm@xVRioo<5&tC+6|)!IgXBA&NA!m z@tbjQa4^d(0Peoq(jean13*L@gaIJ3vL9ys&2j$h>}))H{$lAn0k9d?wLwmkF#{%M ztn6n zz3hg-af~qz0Kn=_^#A~{SCg>6+J zg@xl7W7IpNa;drK74U;IbOWn zop&Qva&&kwHa8#DYXGFKuCB)MhmW(&dj9O`*x1;ZW!7ygorD44e{^|yIrjG6%rfi! z!Mm}&y*9-n^wt%iEl@Zd>U(dTnhn z-zLmr;VS_cTU$@Ym(QQ-HGXL^P195_0RRt?>u8hV0v!rjo_?!W0Dyi1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq`d%r;B4q#hkY{4fC7~MO+;_9_3DG`@6nFEwU}v;rGjn8>iS`whiBI z`>FKY&pk3dhI&9V=5M%GwR`_IT}B25KlcWpfFvUm149Z62Lr{CZ^id*8=LpvKmLxBVfiC=76yhB90Cjs3d#-)3=KUDKzA82QC08^(DycWJ%-=b z0?qz6b?dd;Z|PtwEqpR+CUsNBXn2l>Cryi<(F8V{z(x}ojq3H$%r%<1Ml;u7&RqI8 YoJG68O?jIO>}D`{y85}Sb4q9e0BEq&TL1t6 literal 0 HcmV?d00001 diff --git a/test/src/components/top_app_bar/golden/top_app_bar_centered_actions.png b/test/src/components/top_app_bar/golden/top_app_bar_centered_actions.png new file mode 100644 index 0000000000000000000000000000000000000000..2e6ac8905707721ac0468b183533928959430eaf GIT binary patch literal 3368 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr00er;B4q#hkad4D${-h&VWQ+{kt~{MX-w`S=FzOS42KD&6GQK2uV* z@%5=&%l9A8oHeh#)$kZ2149DuXPf;t^?ToXGcYvtD?0$m9tNPe5fcjo!wC)n1_lM{ z2`<+aw~t@9dd=q@_roID85rzN%AJ3#roh19;MM^2(@fv>3A%qvb2eYw!I6;v=l-{& zrtuGg-#oR+V`5@pNMYe%V3;5XS1rj%gy6G~+>(7UtJea(CI78R&YThMwVW!}*M<9Y zciJ#8eCW2Fcih(ji`R?iEPJ`L#_(9h{p&#Y{Xb=MUjID8y+pYH?6Xmo0P*k7DwfGmP<;~#f>gTe~ HDWM4fECsi1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq`d{r;B4q#hkY{H|8=M3NSeCRos@S{%*gLYLR5hhjT2N-;(7gq;E)(Utb=JRuc2|602D&4Kg@b`%f}jEegM(WG(20_aR258{UHtAme{#>~ zbwJi1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr00er;B4q#hkad4D${-h&VWQd@x<`@Spmk#^f8UnY^x3JiZ<7O1Ij5 z<8IOXe{#nQYvj_}5-ot{=p@csHvRqj{<$m+3^!yMnHU&SSU4CMCI~7pFgUn1098m* zP0(`f&L2OEVpHZG|GVvk6$8Tt_2@nQvk`WL)i#-azrOKo(Bnl*bHBz^KdqEwIPgB_ zsa+lux$b%vl3TJbX7ySIh6eNKooJSQ-Esd~j2r{QkAq_GAFE;Wuw~!lA9Maov^^|d z&d9*akNGltqn(O j!_nGsv^J!mHvG&h@kD)MZ=^>k0}yz+`njxgN@xNAc^vO< literal 0 HcmV?d00001 diff --git a/test/src/components/top_app_bar/golden/top_app_bar_extended.png b/test/src/components/top_app_bar/golden/top_app_bar_extended.png new file mode 100644 index 0000000000000000000000000000000000000000..11817f09010e5788b6376c6fc6f8fe23360e7e14 GIT binary patch literal 2231 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq^5@)5S5QV$R!}8+n-=1y~&A1lcW~$P4nRY;tKhy7K&wRSaQ|w|$#2 zb0bar=14Ba(10w^25fcjo!wC)n1_lM?Q9&#La^~%Q=I_Ug&k9ZZu_u1F zIs?N2e>(*R1_!qW1_lO6MkWS^6c!E!h6#ey73`Zi^R6;5l8QetVsR9`q7LZAQ7{?; lqaiRF0;3@?GD9HZ5%=eF+$T3*YKsR2fTydU%Q~loCIFGPcvAoX literal 0 HcmV?d00001 diff --git a/test/src/components/top_app_bar/golden/top_app_bar_extended_actions.png b/test/src/components/top_app_bar/golden/top_app_bar_extended_actions.png new file mode 100644 index 0000000000000000000000000000000000000000..9677a10eccda3469eb87b6551f832072095b43c0 GIT binary patch literal 2271 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq|pb)5S5QV$R!J2Q!Zth`3&~*uhu*_@DNyvp&jR%)(9`JDPsXVLEa< zZr?V;V?TiUIcnC)0jWFf42%p6MocUW3@11Q7#I|k9T*rIdZ;G&W;Hv|mSUObrcdWy z|7`elZg~vCf_({$3kFZ_dI1{Vqi#N;b35x zaPyc%@>d|^fgF;hH1jGr_%{Fl+W2_SGo8e)>z_AP0bNpanrv?o9}8#9j!CR`2Zq7- z2MFH_DljlOxHSMpB^lvCLR&$gU87($1V%$(Gz3ONU_^$1!BO$Yjbhr{7Ju0d^1r96 KpUXO@geCw%a+cBn literal 0 HcmV?d00001 diff --git a/test/src/components/top_app_bar/golden/top_app_bar_search.png b/test/src/components/top_app_bar/golden/top_app_bar_search.png new file mode 100644 index 0000000000000000000000000000000000000000..00604a3329e24a8be851d99fc2a14ac196194b0c GIT binary patch literal 3320 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq`d{r;B4q#hkY{cjq!23OG1=8r+^B^fi8>@NOQX372;N`EYLj%p6G_ zv)Tux=YHi1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9ji|s42$_5I)B4<9c-4@{nKQ)eRc=Ka~Pb(b zd}|*o14E2_n1j;#`+H{{!%J$x5K{X=dau^Kh9RaK76wO^3vG)zwhQg z{Br&M#mC#t%-f!ypVueH&v2lIQ-FaXLU4WDz8#mIANyBXC(5^wnMhwAYvy2NNO0Bp z#Mm4@mG#88b9tqj3=9uuZDQqSU^sE33FtQS8wda0E7-7q`*mEnyK?XFK@x;!{5W2fzEN=eDlqvY;6V9FrUN7$iN_WOcLne zgiM;n3N2!K)XWA3BGVL=^Vn#}kA^%=3-QqeHk!aj6BvzJNTZo+G;@t+F1lx~XZ#m` XxUFB_b!G{$)yClI>gTe~DWM4fbchsZ literal 0 HcmV?d00001 diff --git a/test/src/components/top_app_bar/golden/top_app_bar_search_centered.png b/test/src/components/top_app_bar/golden/top_app_bar_search_centered.png new file mode 100644 index 0000000000000000000000000000000000000000..e65986b06df85d9e2a14d90ddda07960616ef33a GIT binary patch literal 3319 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq`eCr;B4q#hkY{Hx@b>inux^tTFR>^iNyKFx7+6Ptn}nc>ditn!8>~ z&V4?o|MFzRW1I{O5A4@$jeB=`n<@jtf#ZTe>m1w~fF?^aGBGfuuy8OiOrV+Iv+VB- z4U2#5`}bzv0qMK-`McE_WSZm!7#I|k9T*rIdKefP7>t-$7#L1)P*?D|<@3is6MHu1 z^D;0LXk@RQew&}B9;I7|j#@;AWIGzjqk%kHT+p+k7|n5`Ic_w^jpjJ|G)qTI8=93i b{}}2fi*`RL+OY}P#bEGs^>bP0l+XkKty7{Y literal 0 HcmV?d00001 diff --git a/test/src/components/top_app_bar/top_app_bar_test.dart b/test/src/components/top_app_bar/top_app_bar_test.dart new file mode 100644 index 00000000..8ae60e83 --- /dev/null +++ b/test/src/components/top_app_bar/top_app_bar_test.dart @@ -0,0 +1,557 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; +import '../../../test_utils/utils.dart'; + +void main() { + const String parentFolder = 'top_app_bar'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('ZetaTopAppBar Accessibility Tests', () { + testWidgets('ZetaTopAppBar meets accessibility requirements', (WidgetTester tester) async { + final SemanticsHandle handle = tester.ensureSemantics(); + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar( + title: const Text('Title'), + actions: [ + IconButton( + icon: const Icon(Icons.search), + tooltip: 'Search', + onPressed: () {}, + ), + ], + leading: IconButton( + icon: const Icon(Icons.menu), + tooltip: 'Menu', + onPressed: () {}, + ), + ), + ), + ); + await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); + await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); + await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); + await expectLater(tester, meetsGuideline(textContrastGuideline)); + + handle.dispose(); + }); + + testWidgets('ZetaTopAppBar passes semantic labels to the search actions', (WidgetTester tester) async { + const microphoneSemanticLabel = 'Search with voice'; + const clearSemanticLabel = 'Clear search'; + const searchBackSemanticLabel = 'Back'; + const searchSemanticLabel = 'Search'; + final ZetaSearchController searchController = ZetaSearchController(); + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar.search( + title: const Text('Title'), + microphoneSemanticLabel: microphoneSemanticLabel, + clearSemanticLabel: clearSemanticLabel, + searchController: searchController, + searchSemanticLabel: searchSemanticLabel, + searchBackSemanticLabel: searchBackSemanticLabel, + onSearchMicrophoneIconPressed: () {}, + ), + ), + ); + + expect( + find.bySemanticsLabel(searchSemanticLabel), + findsOneWidget, + ); + + searchController.startSearch(); + await tester.pumpAndSettle(); + + expect( + find.bySemanticsLabel(microphoneSemanticLabel), + findsOneWidget, + ); + expect( + find.bySemanticsLabel(clearSemanticLabel), + findsOneWidget, + ); + expect( + find.bySemanticsLabel(searchBackSemanticLabel), + findsOneWidget, + ); + }); + }); + + group('ZetaTopAppBar Content Tests', () { + final debugFillProperties = { + 'titleTextStyle': 'null', + 'onSearch': 'null', + 'automaticallyImplyLeading': 'true', + 'onSearchMicrophoneIconPressed': 'null', + 'searchController': 'null', + 'searchHintText': 'null', + 'type': 'defaultAppBar', + 'shrinks': 'false', + 'clearSemanticLabel': 'null', + 'microphoneSemanticLabel': 'null', + 'searchSemanticLabel': 'null', + 'searchBackSemanticLabel': 'null', + }; + debugFillPropertiesTest( + const ZetaTopAppBar(), + debugFillProperties, + ); + + test( + 'ZetaTopAppBar Search throws an assertion error if its type is set to extended', + () { + expect( + () => ZetaTopAppBar.search( + title: const Text('Title'), + type: ZetaTopAppBarType.extended, + ), + throwsA(isA()), + ); + }, + ); + + testWidgets('ZetaTopAppBar displays the title correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaTopAppBar( + title: Text('Title'), + ), + ), + ); + + expect(find.text('Title'), findsOneWidget); + }); + + testWidgets('ZetaTopAppBar displays the leading widget correctly', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar( + title: const Text('Title'), + leading: IconButton( + icon: const Icon(Icons.menu), + onPressed: () {}, + ), + ), + ), + ); + + expect(find.byIcon(Icons.menu), findsOneWidget); + }); + + testWidgets('ZetaTopAppBar displays the actions correctly', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar( + title: const Text('Title'), + actions: [ + IconButton( + icon: const Icon(Icons.search), + onPressed: () {}, + ), + ], + ), + ), + ); + + expect(find.byIcon(Icons.search), findsOneWidget); + }); + + testWidgets('ZetaExtendedAppBarDelegate builds correctly', (WidgetTester tester) async { + const title = Text('Title'); + final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; + final leading = IconButton(icon: const Icon(Icons.menu), onPressed: () {}); + const boxKey = Key('box'); + + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return SizedBox( + child: CustomScrollView( + slivers: [ + ZetaTopAppBar.extended(leading: leading, title: title, actions: actions), + SliverToBoxAdapter( + child: Container( + width: 800, + height: 700, + color: Zeta.of(context).colors.surfaceSelectedHover, + key: boxKey, + ), + ), + ], + ), + ); + }, + ), + ), + ); + + final boxFinder = find.byKey(boxKey); + expect(boxFinder, findsOneWidget); + + await tester.drag(boxFinder.first, const Offset(0, -100)); + await tester.pumpAndSettle(); + + final appBarFinder = find.byType(ZetaTopAppBar); + expect(appBarFinder, findsOneWidget); + + final titleFinder = find.descendant(of: appBarFinder, matching: find.byWidget(title)); + expect(titleFinder, findsOneWidget); + + final actionsFinder = find.descendant(of: appBarFinder, matching: find.byWidget(actions[0])); + expect(actionsFinder, findsOneWidget); + + final leadingFinder = find.descendant(of: appBarFinder, matching: find.byWidget(leading)); + expect(leadingFinder, findsOneWidget); + }); + }); + + group('ZetaTopAppBar Dimensions Tests', () { + testWidgets('ZetaTopAppBar has the correct height', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaTopAppBar( + title: Text('Title'), + ), + ), + ); + + final appBar = tester.widget(find.byType(AppBar)); + expect(appBar.preferredSize.height, 64); + }); + }); + + group('ZetaTopAppBar Styling Tests', () { + testWidgets('ZetaTopAppBar has the correct background color', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaTopAppBar( + title: Text('Title'), + ), + ), + ); + + final BuildContext context = tester.element(find.byType(MaterialApp)); + final appBar = tester.widget(find.byType(AppBar)); + expect(appBar.backgroundColor, Zeta.of(context).colors.surfaceDefault); + }); + }); + + group('ZetaTopAppBar Interaction Tests', () { + late ZetaSearchController searchController; + const searchLabel = 'Search'; + const clearLabel = 'Clear'; + const backLabel = 'Back'; + + late Widget subject; + + setUp(() { + searchController = ZetaSearchController(); + subject = TestApp( + home: ZetaTopAppBar.search( + title: const Text('Title'), + searchController: searchController, + searchSemanticLabel: searchLabel, + clearSemanticLabel: clearLabel, + searchBackSemanticLabel: backLabel, + ), + ); + }); + + testWidgets( + 'ZetaTopAppBar Search opens and closes the search bar when the search/back icon is tapped', + (WidgetTester tester) async { + await tester.pumpWidget(subject); + + expect(searchController.isEnabled, isFalse); + await tester.tap(find.bySemanticsLabel(searchLabel)); + await tester.pumpAndSettle(); + + expect(searchController.isEnabled, isTrue); + + await tester.tap(find.bySemanticsLabel(backLabel)); + await tester.pumpAndSettle(); + + expect(searchController.isEnabled, isFalse); + }, + ); + + testWidgets( + 'ZetaTopAppBar Search allows text to be typed in the search field', + (WidgetTester tester) async { + await tester.pumpWidget(subject); + + searchController.startSearch(); + await tester.pumpAndSettle(); + + await tester.enterText(find.byType(TextField), 'Search text'); + expect(searchController.text, 'Search text'); + }, + ); + + testWidgets( + 'ZetaTopAppBar Search gets cleared when the clear button is tapped', + (WidgetTester tester) async { + await tester.pumpWidget(subject); + + searchController.startSearch(); + await tester.pumpAndSettle(); + + await tester.enterText(find.byType(TextField), 'Search text'); + await tester.tap(find.bySemanticsLabel(clearLabel)); + expect(searchController.text, ''); + }, + ); + + testWidgets( + 'ZetaTopAppBar Search submits the correct text when the search input is submitted', + (WidgetTester tester) async { + String inputtedText = ''; + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar.search( + title: const Text('Title'), + searchController: searchController, + onSearch: (String text) { + inputtedText = text; + }, + ), + ), + ); + + searchController.startSearch(); + await tester.pumpAndSettle(); + + await tester.enterText(find.byType(TextField), 'Search text'); + await tester.testTextInput.receiveAction(TextInputAction.done); + expect(inputtedText, 'Search text'); + }, + ); + + testWidgets('ZetaExtendedAppBarDelegate shrinks correctly with padding', (WidgetTester tester) async { + const title = Text('Title'); + final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; + final leading = IconButton(icon: const Icon(Icons.menu), onPressed: () {}); + const boxKey = Key('box'); + + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return SizedBox( + child: CustomScrollView( + slivers: [ + ZetaTopAppBar.extended(leading: leading, title: title, actions: actions), + SliverToBoxAdapter( + child: Container( + width: 800, + height: 700, + color: Zeta.of(context).colors.surfaceSelectedHover, + key: boxKey, + ), + ), + ], + ), + ); + }, + ), + ), + ); + + final boxFinder = find.byKey(boxKey); + expect(boxFinder, findsOneWidget); + + await tester.drag(boxFinder.first, const Offset(0, -100)); + await tester.pumpAndSettle(); + + final appBarFinder = find.byType(ZetaTopAppBar); + expect(appBarFinder, findsOneWidget); + + final positionedFinder = find.descendant(of: appBarFinder, matching: find.byType(Positioned)); + + final positionedWidget = tester.widget(positionedFinder.first); + expect(positionedWidget.left, 64); + }); + + testWidgets('ZetaExtendedAppBarDelegate shrinks correctly with padding and no leading', + (WidgetTester tester) async { + const title = Text('Title'); + final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; + + const boxKey = Key('box'); + + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return SizedBox( + child: CustomScrollView( + slivers: [ + ZetaTopAppBar.extended(title: title, actions: actions), + SliverToBoxAdapter( + child: Container( + width: 800, + height: 700, + color: Zeta.of(context).colors.surfaceSelectedHover, + key: boxKey, + ), + ), + ], + ), + ); + }, + ), + ), + ); + + final boxFinder = find.byKey(boxKey); + expect(boxFinder, findsOneWidget); + + await tester.drag(boxFinder.first, const Offset(0, -100)); + await tester.pumpAndSettle(); + + final appBarFinder = find.byType(ZetaTopAppBar); + expect(appBarFinder, findsOneWidget); + + final positionedFinder = find.descendant(of: appBarFinder, matching: find.byType(Positioned)); + + final positionedWidget = tester.widget(positionedFinder.first); + expect(positionedWidget.left, 16); + }); + }); + + group('ZetaTopAppBar Golden Tests', () { + goldenTest( + goldenFile, + const ZetaTopAppBar( + title: Text('Title'), + ), + 'top_app_bar_default', + ); + + goldenTest( + goldenFile, + ZetaTopAppBar( + title: const Text('Title'), + leading: IconButton(onPressed: () {}, icon: const Icon(Icons.menu)), + actions: [ + IconButton( + icon: const Icon(Icons.search), + tooltip: 'Search', + onPressed: () {}, + ), + ], + ), + 'top_app_bar_default_actions', + ); + + goldenTest( + goldenFile, + const ZetaTopAppBar.centered( + title: Text('Title'), + ), + 'top_app_bar_centered', + ); + + goldenTest( + goldenFile, + ZetaTopAppBar.centered( + leading: IconButton(onPressed: () {}, icon: const Icon(Icons.menu)), + actions: [ + IconButton( + icon: const Icon(Icons.search), + tooltip: 'Search', + onPressed: () {}, + ), + ], + title: const Text('Title'), + ), + 'top_app_bar_centered_actions', + ); + + goldenTest( + goldenFile, + const ZetaTopAppBar.search( + title: Text('Search'), + ), + 'top_app_bar_search', + ); + + goldenTest( + goldenFile, + const ZetaTopAppBar.search( + title: Text('Search'), + type: ZetaTopAppBarType.centered, + ), + 'top_app_bar_search_centered', + ); + + final searchController = ZetaSearchController(); + goldenTest( + goldenFile, + ZetaTopAppBar.search( + title: const Text('Search'), + type: ZetaTopAppBarType.centered, + searchController: searchController, + ), + beforeComparison: (tester) async { + searchController.startSearch(); + await tester.pumpAndSettle(); + }, + 'top_app_bar_search_active', + ); + + goldenTest( + goldenFile, + const CustomScrollView( + slivers: [ + ZetaTopAppBar.extended( + title: Text('Title'), + ), + ], + ), + widgetType: ZetaTopAppBar, + beforeComparison: (tester) async { + searchController.startSearch(); + await tester.pumpAndSettle(); + }, + 'top_app_bar_extended', + ); + + goldenTest( + goldenFile, + CustomScrollView( + slivers: [ + ZetaTopAppBar.extended( + title: const Text('Title'), + actions: [ + IconButton( + icon: const Icon(Icons.search), + tooltip: 'Search', + onPressed: () {}, + ), + ], + ), + ], + ), + widgetType: ZetaTopAppBar, + beforeComparison: (tester) async { + searchController.startSearch(); + await tester.pumpAndSettle(); + }, + 'top_app_bar_extended_actions', + ); + }); + + group('ZetaTopAppBar Performance Tests', () {}); +} diff --git a/test/test_utils/utils.dart b/test/test_utils/utils.dart index 5117d045..ec5c9704 100644 --- a/test/test_utils/utils.dart +++ b/test/test_utils/utils.dart @@ -34,13 +34,19 @@ class GoldenFiles { void goldenTest( GoldenFiles goldenFile, Widget widget, - Type widgetType, String fileName, { + Type? widgetType, ThemeMode themeMode = ThemeMode.system, Size? screenSize, bool? rounded, + Future Function(WidgetTester)? setUp, + Future Function(WidgetTester)? beforeComparison, }) { testWidgets('$fileName golden', (WidgetTester tester) async { + final computedType = widgetType ?? widget.runtimeType; + if (setUp != null) { + await setUp(tester); + } await tester.pumpWidget( TestApp( screenSize: screenSize, @@ -50,42 +56,12 @@ void goldenTest( ), ); - await expectLater( - find.byType(widgetType), - matchesGoldenFile(goldenFile.getFileUri(fileName)), - ); - }); -} - -void goldenTestWithCallbacks( - GoldenFiles goldenFile, - Widget widget, - Type widgetType, - String fileName, { - Future Function(WidgetTester)? before, - Future Function(WidgetTester)? after, - bool darkMode = false, - Size? screenSize, -}) { - testWidgets('$fileName golden', (WidgetTester tester) async { - if (before != null) { - await before(tester); - } - - await tester.pumpWidget( - TestApp( - screenSize: screenSize, - themeMode: darkMode ? ThemeMode.dark : ThemeMode.light, - home: widget, - ), - ); - - if (after != null) { - await after(tester); + if (beforeComparison != null) { + await beforeComparison(tester); } await expectLater( - find.byType(widgetType), + find.byType(computedType), matchesGoldenFile(goldenFile.getFileUri(fileName)), ); }); From 60a137f17dbeb989cf2a9f0b0dc3ee4b78ebb488 Mon Sep 17 00:00:00 2001 From: mikecoomber <58986130+mikecoomber@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:05:38 +0100 Subject: [PATCH 3/8] fix(UX-1241): Fixed inkwell on ZetaStepper (#190) refactor: Optimized ZetaStepper to be made up of sub-components. test: Wrote tests for ZetaStepper --- .../lib/pages/components/stepper_example.dart | 47 +- .../pages/components/stepper_widgetbook.dart | 15 - lib/src/components/components.dart | 2 +- lib/src/components/stepper/stepper.dart | 641 +++++++++++------- test/TESTING_README.md | 6 + .../golden/stepper_horizontal_complete.png | Bin 0 -> 4480 bytes .../golden/stepper_horizontal_incomplete.png | Bin 0 -> 4402 bytes .../stepper_horizontal_step_disabled.png | Bin 0 -> 5115 bytes .../golden/stepper_vertical_complete.png | Bin 0 -> 6646 bytes .../golden/stepper_vertical_incomplete.png | Bin 0 -> 6382 bytes .../golden/stepper_vertical_step_disabled.png | Bin 0 -> 6455 bytes test/src/components/stepper/stepper_test.dart | 532 +++++++++++++++ 12 files changed, 929 insertions(+), 314 deletions(-) create mode 100644 test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_complete.png create mode 100644 test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_incomplete.png create mode 100644 test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_step_disabled.png create mode 100644 test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_complete.png create mode 100644 test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_incomplete.png create mode 100644 test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_step_disabled.png create mode 100644 test/src/components/stepper/stepper_test.dart diff --git a/example/lib/pages/components/stepper_example.dart b/example/lib/pages/components/stepper_example.dart index ac7a2451..b1f8bd84 100644 --- a/example/lib/pages/components/stepper_example.dart +++ b/example/lib/pages/components/stepper_example.dart @@ -12,19 +12,9 @@ class StepperExample extends StatefulWidget { } class _StepperExampleState extends State { - int _sharpHorizontalStep = 0; + int _horistonalStep = 0; int _verticalStep = 0; - ZetaStepType _getForStepIndex({ - required int currentStep, - required int stepIndex, - }) { - if (currentStep == stepIndex) return ZetaStepType.enabled; - if (currentStep > stepIndex) return ZetaStepType.complete; - - return ZetaStepType.disabled; - } - @override Widget build(BuildContext context) { return ExampleScaffold( @@ -35,32 +25,17 @@ class _StepperExampleState extends State { SizedBox( height: 150, child: ZetaStepper( - currentStep: _sharpHorizontalStep, - onStepTapped: (index) => setState(() => _sharpHorizontalStep = index), + currentStep: _horistonalStep, + onStepTapped: (index) => setState(() => _horistonalStep = index), steps: [ ZetaStep( - type: _getForStepIndex( - currentStep: _sharpHorizontalStep, - stepIndex: 0, - ), title: Text("Title"), - content: Text("Content"), ), ZetaStep( - type: _getForStepIndex( - currentStep: _sharpHorizontalStep, - stepIndex: 1, - ), title: Text("Title 2"), - content: Text("Content 2"), ), ZetaStep( - type: _getForStepIndex( - currentStep: _sharpHorizontalStep, - stepIndex: 2, - ), title: Text("Title 3"), - content: Text("Content 3"), ), ], ), @@ -73,31 +48,17 @@ class _StepperExampleState extends State { onStepTapped: (index) => setState(() => _verticalStep = index), steps: [ ZetaStep( - type: _getForStepIndex( - currentStep: _verticalStep, - stepIndex: 0, - ), title: Text("Title"), subtitle: Text("Step Number"), - content: Text("Content"), ), ZetaStep( - type: _getForStepIndex( - currentStep: _verticalStep, - stepIndex: 1, - ), title: Text("Title 2"), subtitle: Text("Step Number"), - content: Text("Content 2"), + disabled: true, ), ZetaStep( - type: _getForStepIndex( - currentStep: _verticalStep, - stepIndex: 2, - ), title: Text("Title 3"), subtitle: Text("Step Number"), - content: Text("Content 3"), ), ], ), diff --git a/example/widgetbook/pages/components/stepper_widgetbook.dart b/example/widgetbook/pages/components/stepper_widgetbook.dart index 6744143f..082e4590 100644 --- a/example/widgetbook/pages/components/stepper_widgetbook.dart +++ b/example/widgetbook/pages/components/stepper_widgetbook.dart @@ -7,13 +7,6 @@ import '../../utils/scaffold.dart'; Widget stepperUseCase(BuildContext context) { int currentStep = 0; - ZetaStepType getForStepIndex(int stepIndex) { - if (currentStep == stepIndex) return ZetaStepType.enabled; - if (currentStep > stepIndex) return ZetaStepType.complete; - - return ZetaStepType.disabled; - } - final type = context.knobs.list( label: "Type", options: [ @@ -24,8 +17,6 @@ Widget stepperUseCase(BuildContext context) { labelBuilder: (type) => type.name, ); - final enabledContent = context.knobs.boolean(label: 'Enabled Content', initialValue: true); - return WidgetbookScaffold( builder: (context, _) => StatefulBuilder( builder: (context, setState) { @@ -40,19 +31,13 @@ Widget stepperUseCase(BuildContext context) { type: type, steps: [ ZetaStep( - type: getForStepIndex(0), title: Text("Title"), - content: enabledContent ? Text("Content") : null, ), ZetaStep( - type: getForStepIndex(1), title: Text("Title 2"), - content: enabledContent ? Text("Content 2") : null, ), ZetaStep( - type: getForStepIndex(2), title: Text("Title 3"), - content: enabledContent ? Text("Content 3") : null, ), ], ), diff --git a/lib/src/components/components.dart b/lib/src/components/components.dart index ab887229..b1e5eb25 100644 --- a/lib/src/components/components.dart +++ b/lib/src/components/components.dart @@ -45,7 +45,7 @@ export 'segmented_control/segmented_control.dart'; export 'select_input/select_input.dart'; export 'slider/slider.dart'; export 'snack_bar/snack_bar.dart'; -export 'stepper/stepper.dart'; +export 'stepper/stepper.dart' hide HorizontalStep, StepDivider, StepIcon, VerticalStep; export 'stepper_input/stepper_input.dart' hide ZetaStepperInputState; export 'switch/zeta_switch.dart'; export 'tabs/tab.dart'; diff --git a/lib/src/components/stepper/stepper.dart b/lib/src/components/stepper/stepper.dart index b118d81f..069f8307 100644 --- a/lib/src/components/stepper/stepper.dart +++ b/lib/src/components/stepper/stepper.dart @@ -6,6 +6,26 @@ import '../../../zeta_flutter.dart'; /// steps. Steppers are particularly useful in the case of forms where one step /// requires the completion of another one, or where multiple steps need to be /// completed in order to submit the whole form. +/// +/// The steppers current step is managed through the [currentStep] property. +/// To change it, store this value in state and change it with the [onStepTapped] callback. +/// The stored value can then be used to update content depending on the selected step. +/// +/// ```dart +/// ZetaStepper( +/// steps: [ +/// ZetaStep(title: Text('Step 1')), +/// ZetaStep(title: Text('Step 2')), +/// ZetaStep(title: Text('Step 3')), +/// ], +/// currentStep: currentStep, +/// onStepTapped: (step) { +/// setState(() { +/// currentStep = step; +/// }); +/// }, +/// ) +/// ``` /// {@category Components} /// /// Figma: https://www.figma.com/design/JesXQFLaPJLc1BdBM4sisI/%F0%9F%A6%93-ZDS---Components?node-id=3420-67488&node-type=canvas&m=dev @@ -52,325 +72,418 @@ class ZetaStepper extends ZetaStatefulWidget { super.debugFillProperties(properties); properties ..add(IterableProperty('steps', steps)) - ..properties.add(IntProperty('currentStep', currentStep)) + ..add(IntProperty('currentStep', currentStep)) ..add(EnumProperty('type', type)) ..add( ObjectFlagProperty?>.has( 'onStepTapped', onStepTapped, ), - ) - ..properties.add(DiagnosticsProperty('rounded', rounded)); + ); } } class _ZetaStepperState extends State with TickerProviderStateMixin { - late List _keys; + bool _isLast(int index) { + return widget.steps.length - 1 == index; + } + + bool _isComplete(int index) { + return widget.currentStep > index; + } @override - void initState() { - super.initState(); - _keys = List.generate( - widget.steps.length, - (_) => GlobalKey(), + Widget build(BuildContext context) { + return ZetaRoundedScope( + rounded: context.rounded, + child: switch (widget.type) { + ZetaStepperType.vertical => IntrinsicWidth( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: widget.steps + .map( + (step) => VerticalStep( + step: step, + index: widget.steps.indexOf(step), + completed: _isComplete(widget.steps.indexOf(step)), + isLast: _isLast(widget.steps.indexOf(step)), + onStepTapped: !step.disabled ? () => widget.onStepTapped?.call(widget.steps.indexOf(step)) : null, + ), + ) + .toList(), + ), + ), + ZetaStepperType.horizontal => Material( + color: Colors.transparent, + child: Container( + margin: EdgeInsets.symmetric(horizontal: Zeta.of(context).spacing.xl_2), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + for (final step in widget.steps) ...[ + HorizontalStep( + step: step, + index: widget.steps.indexOf(step), + completed: _isComplete(widget.steps.indexOf(step)), + onStepTapped: !step.disabled ? () => widget.onStepTapped?.call(widget.steps.indexOf(step)) : null, + ), + if (!_isLast(widget.steps.indexOf(step))) + Expanded( + child: StepDivider( + disabled: step.disabled, + type: ZetaStepperType.horizontal, + completed: _isComplete(widget.steps.indexOf(step)), + ), + ), + ], + ], + ), + ), + ) + }, ); } +} - ZetaColors get _colors => Zeta.of(context).colors; +Color _getElementColor(BuildContext context, bool disabled, bool completed) { + final colors = Zeta.of(context).colors; + Color boxColor = colors.primary; - bool _isFirst(int index) { - return index == 0; + if (disabled) { + boxColor = colors.iconDisabled; + } else if (completed) { + boxColor = colors.surfacePositive; } - bool _isLast(int index) { - return widget.steps.length - 1 == index; - } + return boxColor; +} - bool _isCurrent(int index) { - return widget.currentStep == index; - } +/// The icon that represents a step in the [ZetaStepper] widget. +@visibleForTesting +@protected +class StepIcon extends StatelessWidget { + /// Creates a step icon for the [ZetaStepper] widget. + const StepIcon({ + required this.index, + required this.completed, + required this.disabled, + required this.type, + super.key, + }); + + /// The index of the step in the list of steps. + final int index; - Widget _buildHorizontalIcon(int index) { + /// Whether the step is completed. + final bool completed; + + /// Whether the step is disabled. + final bool disabled; + + /// The size of the icon. + final ZetaStepperType type; + + @override + Widget build(BuildContext context) { final rounded = context.rounded; + final colors = Zeta.of(context).colors; + final spacing = Zeta.of(context).spacing; - return SizedBox( - width: Zeta.of(context).spacing.xl_4, - height: Zeta.of(context).spacing.xl_4, - child: AnimatedContainer( - curve: Curves.fastOutSlowIn, - duration: kThemeAnimationDuration, - decoration: BoxDecoration( - color: _getColorForType(widget.steps[index].type), - shape: rounded ? BoxShape.circle : BoxShape.rectangle, - ), - child: Center( - child: switch (widget.steps[index].type) { - ZetaStepType.complete => ZetaIcon( + final size = switch (type) { + ZetaStepperType.horizontal => spacing.xl_4, + ZetaStepperType.vertical => spacing.xl_6, + }; + + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: _getElementColor(context, disabled, completed), + shape: rounded ? BoxShape.circle : BoxShape.rectangle, + ), + child: Center( + child: completed && !disabled + ? ZetaIcon( ZetaIcons.check_mark, - color: _colors.textInverse, - ), - ZetaStepType.enabled || ZetaStepType.disabled => Text( + color: colors.textInverse, + ) + : Text( (index + 1).toString(), style: ZetaTextStyles.labelLarge.copyWith( - color: _colors.textInverse, + color: colors.textInverse, ), ), - }, - ), ), ); } - Widget _getVerticalIcon(int index) { - return SizedBox( - width: Zeta.of(context).spacing.xl_8, - height: Zeta.of(context).spacing.xl_8, - child: AnimatedContainer( - curve: Curves.fastOutSlowIn, - duration: kThemeAnimationDuration, - decoration: BoxDecoration( - color: _getColorForType(widget.steps[index].type), - shape: context.rounded ? BoxShape.circle : BoxShape.rectangle, - ), - child: Center( - child: switch (widget.steps[index].type) { - ZetaStepType.complete => ZetaIcon( - ZetaIcons.check_mark, - color: _colors.textInverse, - ), - ZetaStepType.enabled || ZetaStepType.disabled => Text( - (index + 1).toString(), - style: ZetaTextStyles.titleLarge.copyWith( - color: _colors.textInverse, - ), - ), - }, - ), + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(IntProperty('index', index)) + ..add(DiagnosticsProperty('completed', completed)) + ..add(DiagnosticsProperty('disabled', disabled)) + ..add(EnumProperty('type', type)); + } +} + +/// A divider that separates steps in the [ZetaStepper] widget. +@visibleForTesting +@protected +class StepDivider extends StatelessWidget { + /// Creates a step divider for the [ZetaStepper] widget. + const StepDivider({ + super.key, + required this.type, + required this.disabled, + required this.completed, + }); + + /// Disables the divider and changes its color. + final bool disabled; + + /// Changes the color of the divider to indicate completion. + final bool completed; + + /// The type of the divider. + final ZetaStepperType type; + + @override + Widget build(BuildContext context) { + final colors = Zeta.of(context).colors; + final spacing = Zeta.of(context).spacing; + + Color color = colors.borderPrimary; + + if (disabled) { + color = colors.borderDefault; + } else if (completed) { + color = colors.borderPositive; + } + + return Container( + margin: switch (type) { + ZetaStepperType.horizontal => EdgeInsets.only( + top: spacing.xl_3, + right: spacing.small, + left: spacing.small, + ), + ZetaStepperType.vertical => EdgeInsets.only( + top: spacing.minimum, + ), + }, + width: switch (type) { + ZetaStepperType.horizontal => double.infinity, + ZetaStepperType.vertical => spacing.minimum, + }, + height: switch (type) { + ZetaStepperType.horizontal => ZetaBorders.medium, + ZetaStepperType.vertical => spacing.xl_8, + }, + decoration: BoxDecoration( + borderRadius: Zeta.of(context).radius.full, + color: color, ), ); } - Widget _getHeaderText(int index) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - AnimatedDefaultTextStyle( - style: switch (widget.steps[index].type) { - ZetaStepType.enabled || ZetaStepType.complete => ZetaTextStyles.bodySmall.copyWith( - color: _colors.textDefault, - ), - ZetaStepType.disabled => ZetaTextStyles.bodySmall.copyWith( - color: _colors.textDisabled, - ), - }, - maxLines: 1, - duration: kThemeAnimationDuration, - curve: Curves.fastOutSlowIn, - child: widget.steps[index].title, - ), - ], - ); + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('disabled', disabled)) + ..add(DiagnosticsProperty('completed', completed)) + ..add(EnumProperty('type', type)); } +} - Widget _getVerticalHeader(int index) { - final subtitle = widget.steps[index].subtitle; +/// A horizontal step in the [ZetaStepper] widget. +@visibleForTesting +@protected +class HorizontalStep extends StatelessWidget { + /// Creates a horizontal step in the [ZetaStepper] widget. + const HorizontalStep({ + required this.step, + required this.index, + required this.completed, + this.onStepTapped, + super.key, + }); - return Container( - margin: EdgeInsets.only(top: _isFirst(index) ? 0.0 : Zeta.of(context).spacing.xl_2), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( + /// The step that this widget represents. + final ZetaStep step; + + /// The index of the step in the list of steps. + final int index; + + /// Whether the step is completed. + final bool completed; + + /// The callback called when the step is tapped. + final VoidCallback? onStepTapped; + + @override + Widget build(BuildContext context) { + final spacing = Zeta.of(context).spacing; + final colors = Zeta.of(context).colors; + final radius = Zeta.of(context).radius; + + return Semantics( + label: step.semanticLabel, + excludeSemantics: step.semanticLabel != null, + child: InkWell( + onTap: onStepTapped, + canRequestFocus: !step.disabled, + borderRadius: radius.minimal, + child: Padding( + padding: EdgeInsets.only(left: spacing.small, right: spacing.small, bottom: spacing.small), + child: Column( + mainAxisSize: MainAxisSize.min, children: [ - _getVerticalIcon(index), - Container( - margin: EdgeInsets.only(top: Zeta.of(context).spacing.minimum), - width: Zeta.of(context).spacing.minimum, - height: Zeta.of(context).spacing.xl_8, - decoration: BoxDecoration( - borderRadius: Zeta.of(context).radius.full, - color: switch (widget.steps[index].type) { - ZetaStepType.complete => _colors.green.shade50, - ZetaStepType.disabled => _colors.borderSubtle, - ZetaStepType.enabled => _colors.blue.shade50, - }, + Center( + child: Padding( + padding: EdgeInsets.symmetric( + vertical: spacing.medium, + ), + child: StepIcon( + index: index, + completed: completed, + disabled: step.disabled, + type: ZetaStepperType.horizontal, + ), ), ), - ], - ), - Expanded( - child: Container( - margin: EdgeInsets.symmetric(horizontal: Zeta.of(context).spacing.xl_2), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - if (subtitle != null) - AnimatedDefaultTextStyle( - style: ZetaTextStyles.bodyMedium.copyWith( - color: _getColorForType(widget.steps[index].type), - ), - maxLines: 1, - duration: kThemeAnimationDuration, - curve: Curves.fastOutSlowIn, - child: subtitle, - ), - AnimatedDefaultTextStyle( - style: switch (widget.steps[index].type) { - ZetaStepType.enabled || ZetaStepType.complete => ZetaTextStyles.titleLarge.copyWith( - color: _colors.textDefault, - ), - ZetaStepType.disabled => ZetaTextStyles.titleLarge.copyWith( - color: _colors.textDisabled, - ), - }, - maxLines: 1, - duration: kThemeAnimationDuration, - curve: Curves.fastOutSlowIn, - child: widget.steps[index].title, - ), - ], + DefaultTextStyle( + style: ZetaTextStyles.bodySmall.copyWith( + color: step.disabled ? colors.textDisabled : colors.textDefault, + ), + child: step.title, ), - ), + ], ), - ], + ), ), ); } - Widget _getVerticalBody(int index) { - return Stack( - children: [ - AnimatedCrossFade( - firstChild: Container(height: 0), - secondChild: widget.steps[index].content ?? const Nothing(), - firstCurve: const Interval(0, 0.6, curve: Curves.fastOutSlowIn), - secondCurve: const Interval(0.4, 1, curve: Curves.fastOutSlowIn), - sizeCurve: Curves.fastOutSlowIn, - crossFadeState: _isCurrent(index) ? CrossFadeState.showSecond : CrossFadeState.showFirst, - duration: kThemeAnimationDuration, - ), - ], - ); + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('step', step)) + ..add(IntProperty('index', index)) + ..add(DiagnosticsProperty('completed', completed)) + ..add(ObjectFlagProperty.has('onStepTapped', onStepTapped)); } +} - Color _getColorForType(ZetaStepType type) { - return switch (type) { - ZetaStepType.complete => _colors.surfacePositive, - ZetaStepType.disabled => _colors.cool.shade50, - ZetaStepType.enabled => _colors.primary, - }; - } +/// A vertical step in the [ZetaStepper] widget. +@visibleForTesting +@protected +class VerticalStep extends StatelessWidget { + /// Creates a vertical step in the [ZetaStepper] widget. + const VerticalStep({ + required this.step, + required this.index, + required this.completed, + required this.isLast, + this.onStepTapped, + super.key, + }); + + /// The step that this widget represents. + final ZetaStep step; + + /// The index of the step in the list of steps. + final int index; + + /// Whether the step is completed. + final bool completed; + + /// Whether the step is the last one in the list. + final bool isLast; + + /// The callback called when the step is tapped. + final VoidCallback? onStepTapped; @override Widget build(BuildContext context) { - return ZetaRoundedScope( - rounded: context.rounded, - child: switch (widget.type) { - ZetaStepperType.vertical => Column( - children: [ - for (int index = 0; index < widget.steps.length; index += 1) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - key: _keys[index], - children: [ - InkResponse( - containedInkWell: true, - borderRadius: Zeta.of(context).radius.minimal, - onTap: widget.onStepTapped != null ? () => widget.onStepTapped?.call(index) : null, - canRequestFocus: widget.steps[index].type != ZetaStepType.disabled, - child: _getVerticalHeader(index), - ), - _getVerticalBody(index), - ], - ), - ], + final spacing = Zeta.of(context).spacing; + final colors = Zeta.of(context).colors; + + return Semantics( + label: step.semanticLabel, + excludeSemantics: step.semanticLabel != null, + child: InkWell( + borderRadius: Zeta.of(context).radius.minimal, + onTap: onStepTapped, + canRequestFocus: !step.disabled, + child: Container( + padding: EdgeInsets.all( + spacing.medium, ), - ZetaStepperType.horizontal => Builder( - builder: (context) { - final children = [ - for (int index = 0; index < widget.steps.length; index += 1) ...[ - InkResponse( - onTap: widget.onStepTapped != null ? () => widget.onStepTapped?.call(index) : null, - canRequestFocus: widget.steps[index].type != ZetaStepType.disabled, - child: Column( - children: [ - Center( - child: Padding( - padding: EdgeInsets.symmetric( - vertical: Zeta.of(context).spacing.medium, - ), - child: _buildHorizontalIcon(index), - ), - ), - _getHeaderText(index), - ], - ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + StepIcon( + index: index, + completed: completed, + disabled: step.disabled, + type: ZetaStepperType.vertical, ), - if (!_isLast(index)) - Expanded( - child: Container( - key: Key('line$index'), - margin: EdgeInsets.only( - top: Zeta.of(context).spacing.xl_3, - right: Zeta.of(context).spacing.large, - left: Zeta.of(context).spacing.large, - ), - height: ZetaBorders.medium, - decoration: BoxDecoration( - borderRadius: Zeta.of(context).radius.full, - color: switch (widget.steps[index].type) { - ZetaStepType.complete => _colors.green.shade50, - ZetaStepType.disabled => _colors.borderSubtle, - ZetaStepType.enabled => _colors.blue.shade50, - }, - ), - ), + if (!isLast) + StepDivider( + type: ZetaStepperType.vertical, + completed: completed, + disabled: step.disabled, ), ], - ]; - - final List stepPanels = []; - for (int i = 0; i < widget.steps.length; i += 1) { - stepPanels.add( - Visibility( - maintainState: true, - visible: i == widget.currentStep, - child: widget.steps[i].content ?? const Nothing(), - ), - ); - } - - return Column( + ), + SizedBox(width: spacing.xl_2), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - Material( - color: Colors.transparent, - child: Container( - margin: EdgeInsets.symmetric(horizontal: Zeta.of(context).spacing.xl_2), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: children, + if (step.subtitle != null) + AnimatedDefaultTextStyle( + style: ZetaTextStyles.bodyMedium.copyWith( + color: _getElementColor(context, step.disabled, completed), ), - ), - ), - Expanded( - child: AnimatedSize( - curve: Curves.fastOutSlowIn, + maxLines: 1, duration: kThemeAnimationDuration, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: stepPanels, - ), + curve: Curves.fastOutSlowIn, + child: step.subtitle!, + ), + DefaultTextStyle( + style: ZetaTextStyles.titleLarge.copyWith( + color: step.disabled ? colors.textDisabled : colors.textDefault, ), + maxLines: 1, + child: step.title, ), ], - ); - }, + ), + ], ), - }, + ), + ), ); } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('step', step)) + ..add(IntProperty('index', index)) + ..add(DiagnosticsProperty('completed', completed)) + ..add(DiagnosticsProperty('isLast', isLast)) + ..add(ObjectFlagProperty.has('onStepTapped', onStepTapped)); + } } /// Zeta step used in [ZetaStepper]. The step can have a title and subtitle, @@ -380,12 +493,18 @@ class ZetaStep { /// Creates a step for a [ZetaStepper]. const ZetaStep({ required this.title, - this.content, + @Deprecated('Steps no longer manage their own content. ' 'Deprecated as of 0.16.1') this.content, this.subtitle, + this.disabled = false, + this.semanticLabel, + @Deprecated( + 'To disable a step, set its disabled prop to true. To complete a step, set the currentStep prop on the stepper greater than the step index. ' + 'Deprecated as of 0.16.1') this.type = ZetaStepType.disabled, }); /// The content of the step that appears below the [title] and [subtitle]. + @Deprecated('Steps no longer manage their own content. ' 'Deprecated as of 0.16.1') final Widget? content; /// The subtitle of the step that appears above the title. @@ -394,12 +513,24 @@ class ZetaStep { /// The title of the step that typically describes it. final Widget title; + /// The semantic label of the step that is read by screen readers. + final String? semanticLabel; + + /// Whether the step is disabled and does not react to taps. + final bool disabled; + /// The type of the step which determines the styling of its components /// and whether steps are interactive. + @Deprecated( + 'To disable a step, set its disabled prop to true. To complete a step, set the activeStep prop on the stepper greater than the step index. ' + 'Deprecated as of 0.16.1') final ZetaStepType type; } /// The type of a [ZetaStep] which is used to control the style of the circle and text. +@Deprecated( + 'To disable a step, set its disabled prop to true. To complete a step, set the activeStep prop on the stepper greater than the step index. ' + 'Deprecated as of 0.16.1') enum ZetaStepType { /// A step that is currently selected with primary color icon enabled, diff --git a/test/TESTING_README.md b/test/TESTING_README.md index a98b8042..0bcb169e 100644 --- a/test/TESTING_README.md +++ b/test/TESTING_README.md @@ -59,6 +59,7 @@ void main() { }); group('Accessibility Tests', () {}); + group('Content Tests', () { final debugFillProperties = { '': '', @@ -68,12 +69,17 @@ void main() { debugFillProperties, ); }); + group('Dimensions Tests', () {}); + group('Styling Tests', () {}); + group('Interaction Tests', () {}); + group('Golden Tests', () { goldenTest(goldenFile, widget, widgetType, 'PNG_FILE_NAME'); }); + group('Performance Tests', () {}); } ``` diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_complete.png b/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_complete.png new file mode 100644 index 0000000000000000000000000000000000000000..23ee538b6848a2eb7fd793b675cf2377658268b7 GIT binary patch literal 4480 zcmeHKX;2eq82$ngC?3c-MPeXmsZFa=JP8a(5VRC4(ppC-9)Q87R3b#m5n&0%YAjdq z0F*1EoqATHL_)Yi=nPpDg;qq4KtNVVIYL;21QJ3T6MyuNqjmbDKl1(A{oZ+>eUI%)M)^0*930cx+l3r-nZ{Ghx#LoQj0drKiv7KC@g=prenuf-+QOCZVY7B)+AqE zlym&o(;p{ani4x-8_Ul*7+Mz>S-pIy1u1fofGQsd4GUXQTNontXz_%3 zN#um_>dCWckurS!E;6m7I?X?*~n>+DNt`mw2}OF0yrGsdir^)FM27arU!oUBph zmP!gp&lsS%aT*gJr3o{8W`DFkNf)YpE~SsvYv9HfZciSye1A1{|IANHaYvz$`SQl? z*+qcAXMh|}vwp&c8wC#n6%6kK?;G6g=sS`stE!|XaGHy>`E4)Ra; z`uz+mH@D*m*S-i zYBYbX%Ar$9kZ+Nym|7)z(JAe4>cHwkg31x&R%CoSjL&2BCHy7oN@?HH&EV9G6rr{L zDyj_QkY#lEBHe2|T&Ut81BYBZ6{175)WH;8eyL^LIzwwoE|`sCfo}2=+5~mVWNFd; zRj`(2GbA%xB(Z#OUS3=VaFkS8E<~YEv1h9dB&eoP(xjV3Xayv4=oXH=m$yOOsZ$Vn zUib?nJ?M-P)H_!zp)%ao#~R~!>*LHkG;iPxb&Kk{eIjyW`lqnX_DI#Tv<*A|;ij`) zg&*XR)f~Fyqxs;=acdY)x`T0wl&51?g%($V1TN|FamysFq+}E2?ner!=E`r4%~M)L zWuwm=sX5^U!I27{&Ta;8n>=?1BDPI8B5Y$$UW(oZC$d1v*Qq)k>*j6*dEg_gU**@I zu|=FLpKarekCP5e4&H0E7=CCt>?=fHu{}4nXFHPZa#bcfDyc}Q8&!OIziolN9{P!d zjQJC+K~nEVwmj#;?1+ZlmyccK|#wl)sqiCc0wy;#Q-qu~6iD;I9CKLWmGk zDID57WYI{)LRTEuTekq2YUl5S4yT_pQl>Z{U9X8a@`8o`N)BHyoVqolzK`L>IK~!rPmt`2Dc-pa8exiZKS9A^YA*mrU#teWctQ7!xwk!1_{n@)1Ux})| zkl{xBTTA-uYj~@PV1oO;#oRP?$<(EH_}ntl#zY$vZT_Q?Y#L{8__;HwjY)0(Pimti YF5_&qyvU5+0rV?y`>t)}ei0de1GE*d8~^|S literal 0 HcmV?d00001 diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_incomplete.png b/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_incomplete.png new file mode 100644 index 0000000000000000000000000000000000000000..11ed680d4a4fa83bc2ca23e1aca1fb0c64b439e5 GIT binary patch literal 4402 zcmeHKYfMvT7(OjXi^?rCVIW!}8m0p}X|!UYMZ6%>fblja-q7Wd~e?Keed&p z&-l)0MMaWDoR%ydKW>sZkB9Z5`-+1ow?}`UDSH0r4Z~3gDIedJ} zr;o1{Z~4IsvK9oK$;sK_^~nAtboi6hl)`JroNrWJ+%Ori_WsuRyWtzMjvgiyKs=T^ zp}NE&fN-1Ytu;W;)J)HqJ8Bl4z9?bIT9lWjRH_7|NY_N__&TI6#eOa_I-$@2K()jI zK)!=30QetzPcYY$jS}8!@7b*`RO01Dq6Q-D%Ziq@Gm|5reoz5I$$ACUPNR|&;cmwK z-2(M2CK#NpJQBH6(ifxW?rLOXVq!TQPSgBBmlS+B1V)mZNE}l|(P$x&(JG5ekgC$@ zEaUJ{S&Le5-$HzdTO!q9)#QO1X1Z^AksN1OhmMxDda&ZX%gDI05& z%Lmw3j0<)$&`+pGdk@akvn9^2M9~j7@@cv*&@H5tQlFK0VN^%^YP8!O4~}G#(1V^0 zD@h~R{2twt9xoXu&D3l`QNrR>hG?ca2a|3qDLK2s)ALz}+SmyC!e4DBenjy)Rzlk* zN8{48aN%|JW{UQE3W=Q*E7EnO%Gy&e`bYT)>_Xl8gdV9w#T0$lbTaNqW>R_rp@PLE zlI#YVE2JcRjiz3R5Ix}GjXhOMkh@4k#7E9qZKz+rf-$Td;PLqB+PO*m%+q}=e*9<+ z-0lMtZDvltB1hefgXb(e!8Bcq;Z(UzZON2{qEq7;!UaK&X>d@C8v5ejQB%ajM_oE1IV?;dQk@Fr+0_IksnA@7Q3t)`cxN|YfF3o+whL}i1D^ZMILFA&6V zm@T$pb8%7qtN0lRe)6m&`!|`f8B;{s7)pExJd&xM^vRKb4f)AuU0f#P~U?Z90VH+q$fMku>SH_4v__iJ%!1?@(~l)XQLAcjaOKn93@dyWddC?(yv$Zlm1trNnelv*U*La6*r)SiZ@bKw|7ipu4QC0I*JJD-uIXJ? zNMs;;!X+MQTv0@(yZ=DOvX9KGi$H;zg^tEznT4d<-rNiC2g{)wi`{q46I}D9ZgPSC)ZjcM00Ki+c263`Z ztUcmtZvFtky|EHSTYh|y7B{|*E@x5d=eN0h3F|b%OJi{Vn4zs(&E0rjUnkc*CY4OJ zikjmqQvcgV|EM!euh*AU;SmE4#i-unyC3e*=1kWQc#oM`GP4AH{O6BZHfGtFW%E&g m4Ca2DW z{_4E@zx4`3LxRkf!3Y@PZ$6|900&D+Qb<2+&p*wG-wRc!h!+-TEERC_+XS4 z5FB9wUO6TwE(3tYo!~vYBQmOHM937f=4jt%`poR?U~pA^FbR9g%59PKYU}=1ob}E9 z4{)Zn<`J(C#ce&ZZqu1Zs~wErtg#4yoM|Zvb|@}7^=GSt)0F*=s?xiA-NWuj9-G;B z@{J$L1jF2LU~EGh=S)gkc`)xLqVc^VbGk|(-FB5I4l1?#urMO=lz2i!So|%pH$!8R zFKB9sCMyBJVQ(II?`miS0GC%e0D%7$zyJV_8<_yW%Ju#LkhjLb5CES1Cba3MZ{AP0 z({gWB@7fhB&b<{5-83;Z+cD?m$_RxZ7{^rp7#r(m4C&(3Bir?==drPFySU0#kT_Nc z7;{!x?6vSh&p#Xz!dD(_Nb(|6C~!<^M3+xk6w)pJ!LX7gJiCZ<+xd~XCE-J>TuRQz zKInR--0g&SY_OV=S65T$ddWdD^T_~%<`dej&p)^LmYK-NO|O;oncU-gk+DAWNT~Ee zfeUYXdhURh6p3z^wJ+8S=MD^^PTb}x5{;r!Y68>Ie`GhFHf66qB#6>sC?O#(vK>@= z+q>PfgVBqlmx7w^T*+UZfX!u9>*c+wf<-80VA2^?TvirI_|!kccyEPmT!-J2x4MVr z5|p=KQ%=5+^{MQ6miEKjjWKfF3|0Fqna`iS{1@ZvbS%EjF8j+oPN^!~7?Uc!&c=!9 zBS)$HPgk3vuT0g17{l>DncI?@pmvqb-}g;iPZtf*)>2aSvJBC=;tj^vYswoq+{98j z6|_#1ol-iSE^e1cp%xME4|F>Bgx!t|xpt9;)$I*~D^LbX1$&U|ikejj+G$)wpIpFIxUO725 zyPWZZY&F)Xr)!&^A2PdoFt#CUrM(|+N?DmTT`1=*JhVEVkaoeCQsF6nTpjoNeRzN8o#(JDtbV>6 zSFcbHoSC3|#Pfyd?u-SD-raXwC{%ZXaI|YL#E@NCJ;{|3--e5db|q;PFrxYwzE11G zQneA$jEmB=nmHBR7K7vqfr?%hJ*NNhGNNjn8|d;lzH-`$TTyfK2R5YrMshV_^ykm7 zH;ZH`K8oSe;|jp}85nJNn-aot;(FAO3pt|2xP}l;yTP&@bwgxofeQ;>ll|#c6K}?u zUFU2tvSpq%@!gLUt!#5E5A*KjZnDBuC5n1z0%7meYD>|ye3uIxHe2voK(whb4OJ6f zo(CE!eXuREirgq|N^LjuoU)izWv2A_mS^erJ3r3Koz<$P!i zSNU16q63NTgn`qGbB;8Wc5Ae8ywA2#tT`c372So+5!ctl3O46?J0KXR*#2MZ4h?=h z`H%X;28|Rc-<)3-&d%8+?_@;ng=BfGiLB-&&UmG%;}ToxMk8hyf9hk=Gy4M&Og)p0 zi~ib9+nMjvQf+p8ZCXuWTR0$8jj^%CY@Jd)kpx-9yj00grKtH^NY=#^ zYuSM|a5x1f!Ud(+^*jYf5nVepqYb=fs>|SXXoZ^XekG~~%@w|Q`iO1ivQHs8f>@^= zEj15J5~*|;RT)fo{DftXcMCZA-VUjs@610#_2HOAuSZa9Gy>TkwG8sI_mZr$&OKf< z-w=lBoR|`3O4QXHgZ4jyy{&Rcbp?mJ)OSZbNuZM5PZ4d6DD}NhWs}ZD46>IrZ*V6j}_6zuP!j8Ax^Y64GGu;lB>0cC{EAFG!$H zL^Z+FaQqKdlTKW*uMa}so?71@KQ?(pV0V`z7SUXWy(iL$JwI^CK1hztFO*}yGp@B& z;xd^EE=kxjGXau3C)NOw8|GOf77p?N$wGh+E~8DSx)5Spb))HhxB@pe9Iz#Bwo3er zSHK_)gf6N}%CuZ66|PR)-UIIESXRk`n1q}CuwGRjUhcMi0PHjbfnWsfMcoG_h;C1h zZ5s)V#bz8f)0XXiw>&{C@cvaBKfAB~kBay^#PiqC7@pE#I9<_J4?7lykRho!7b1eORa5m+LyL|}=)5`iTGO9cKu2+R>nbM36xit|2#f5d>` Mz>qz(fQ0jZ15dEO;s5{u literal 0 HcmV?d00001 diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_complete.png b/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_complete.png new file mode 100644 index 0000000000000000000000000000000000000000..e8b697696531566da3845f0f9ce61d1f300e187a GIT binary patch literal 6646 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 M6@7T2*6oM?4ICQc{Qv*} literal 0 HcmV?d00001 diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_incomplete.png b/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_incomplete.png new file mode 100644 index 0000000000000000000000000000000000000000..de3b2b7052655d5c90bdde8ea7372f1516f940cb GIT binary patch literal 6382 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 literal 0 HcmV?d00001 diff --git a/test/src/components/stepper/stepper_test.dart b/test/src/components/stepper/stepper_test.dart new file mode 100644 index 00000000..f8341f0c --- /dev/null +++ b/test/src/components/stepper/stepper_test.dart @@ -0,0 +1,532 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:zeta_flutter/src/components/stepper/stepper.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() { + const String parentFolder = 'ENTER_PARENT_FOLDER (e.g. button)'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('ZetaStepper Accessibility Tests', () { + testWidgets('Horizontal stepper meets accessibility requirements', (WidgetTester tester) async { + final SemanticsHandle handle = tester.ensureSemantics(); + await tester.pumpWidget( + TestApp( + home: ZetaStepper( + steps: const [ZetaStep(title: Text('Title'))], + currentStep: 0, + onStepTapped: (step) {}, + ), + ), + ); + await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); + await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); + await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); + await expectLater(tester, meetsGuideline(textContrastGuideline)); + + handle.dispose(); + }); + + testWidgets('Vertical stepper meets accessibility requirements', (WidgetTester tester) async { + final SemanticsHandle handle = tester.ensureSemantics(); + await tester.pumpWidget( + TestApp( + home: ZetaStepper( + steps: const [ZetaStep(title: Text('Title'))], + currentStep: 0, + type: ZetaStepperType.vertical, + onStepTapped: (step) {}, + ), + ), + ); + await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); + await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); + await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); + await expectLater(tester, meetsGuideline(textContrastGuideline)); + + handle.dispose(); + }); + + testWidgets('Horizontal steps correctly recieve semantic labels', (WidgetTester tester) async { + final SemanticsHandle handle = tester.ensureSemantics(); + await tester.pumpWidget( + TestApp( + home: ZetaStepper( + steps: const [ZetaStep(title: Text('Title'), semanticLabel: 'semantic label')], + currentStep: 0, + onStepTapped: (step) {}, + ), + ), + ); + + expect(find.bySemanticsLabel('semantic label'), findsOneWidget); + + handle.dispose(); + }); + + testWidgets('Vertical steps correctly recieve semantic labels', (WidgetTester tester) async { + final SemanticsHandle handle = tester.ensureSemantics(); + await tester.pumpWidget( + TestApp( + home: ZetaStepper( + steps: const [ZetaStep(title: Text('Title'), semanticLabel: 'semantic label')], + currentStep: 0, + type: ZetaStepperType.vertical, + onStepTapped: (step) {}, + ), + ), + ); + + expect(find.bySemanticsLabel('semantic label'), findsOneWidget); + + handle.dispose(); + }); + }); + + group('ZetaStepper Content Tests', () { + testWidgets('Horizontal stepper renders the correct steps', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaStepper( + steps: const [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2')), + ZetaStep(title: Text('Title 3')), + ], + currentStep: 0, + onStepTapped: (step) {}, + ), + ), + ); + + expect(find.text('Title 1'), findsOneWidget); + expect(find.text('Title 2'), findsOneWidget); + expect(find.text('Title 3'), findsOneWidget); + }); + + testWidgets('Vertical stepper renders the correct steps', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaStepper( + steps: const [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2')), + ZetaStep(title: Text('Title 3')), + ], + type: ZetaStepperType.vertical, + currentStep: 0, + onStepTapped: (step) {}, + ), + ), + ); + + expect(find.text('Title 1'), findsOneWidget); + expect(find.text('Title 2'), findsOneWidget); + expect(find.text('Title 3'), findsOneWidget); + }); + + testWidgets('StepIcon displays the correct text', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepIcon( + completed: false, + disabled: false, + index: 0, + type: ZetaStepperType.horizontal, + ), + ), + ); + + expect(find.text('1'), findsOneWidget); + }); + testWidgets('StepIcon displays the correct icon when completed', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepIcon( + completed: true, + disabled: false, + index: 0, + type: ZetaStepperType.horizontal, + ), + ), + ); + + expect(find.byIcon(ZetaIcons.check_mark_round), findsOneWidget); + }); + + debugFillPropertiesTest( + const ZetaStepper(steps: [], currentStep: 0), + { + 'steps': '[]', + 'currentStep': '0', + 'type': 'horizontal', + 'onStepTapped': 'null', + }, + ); + debugFillPropertiesTest( + const StepIcon(completed: false, disabled: false, index: 0, type: ZetaStepperType.horizontal), + { + 'index': '0', + 'type': 'horizontal', + 'completed': 'false', + 'disabled': 'false', + }, + ); + const step = ZetaStep(title: Text('Title')); + debugFillPropertiesTest( + const HorizontalStep(step: step, index: 0, completed: false), + { + 'step': "Instance of 'ZetaStep'", + 'index': '0', + 'completed': 'false', + 'onStepTapped': 'null', + }, + ); + debugFillPropertiesTest( + const VerticalStep(step: step, index: 0, completed: false, isLast: false), + { + 'step': "Instance of 'ZetaStep'", + 'index': '0', + 'completed': 'false', + 'isLast': 'false', + 'onStepTapped': 'null', + }, + ); + }); + + group('ZetaStepper Dimensions Tests', () { + testWidgets('StepIcon horiztonal has the correct size', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepIcon( + completed: false, + disabled: false, + index: 0, + type: ZetaStepperType.horizontal, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + final spacing = Zeta.of(getBuildContext(tester, Container)).spacing; + + expect(container.constraints?.maxHeight, spacing.xl_4); + expect(container.constraints?.maxWidth, spacing.xl_4); + }); + + testWidgets('StepIcon vertical has the correct size', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepIcon( + completed: false, + disabled: false, + index: 0, + type: ZetaStepperType.vertical, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + final spacing = Zeta.of(getBuildContext(tester, Container)).spacing; + + expect(container.constraints?.maxHeight, spacing.xl_6); + expect(container.constraints?.maxWidth, spacing.xl_6); + }); + + testWidgets('StepDivider horizontal has the correct size', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepDivider( + completed: false, + disabled: false, + type: ZetaStepperType.horizontal, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + expect(container.constraints?.maxHeight, ZetaBorders.medium); + expect(container.constraints?.maxWidth, double.infinity); + }); + + testWidgets('StepDivider vertical has the correct size', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepDivider( + completed: false, + disabled: false, + type: ZetaStepperType.vertical, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + final spacing = Zeta.of(getBuildContext(tester, Container)).spacing; + expect(container.constraints?.maxHeight, spacing.xl_8); + expect(container.constraints?.maxWidth, spacing.minimum); + }); + }); + + group('ZetaStepper Styling Tests', () { + testWidgets( + 'StepIcon has the correct colour when enabled', + (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepIcon( + completed: false, + disabled: false, + index: 0, + type: ZetaStepperType.horizontal, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + expect( + (container.decoration! as BoxDecoration).color, + Zeta.of(getBuildContext(tester, Container)).colors.primary, + ); + }, + ); + testWidgets( + 'StepIcon has the correct colour when completed', + (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepIcon( + completed: true, + disabled: false, + index: 0, + type: ZetaStepperType.horizontal, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + expect( + (container.decoration! as BoxDecoration).color, + Zeta.of(getBuildContext(tester, Container)).colors.surfacePositive, + ); + }, + ); + testWidgets( + 'StepIcon has the correct colour when disabled', + (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepIcon( + completed: true, + disabled: true, + index: 0, + type: ZetaStepperType.horizontal, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + expect( + (container.decoration! as BoxDecoration).color, + Zeta.of(getBuildContext(tester, Container)).colors.iconDisabled, + ); + }, + ); + testWidgets( + 'StepDivider has the correct colour when enabled', + (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepDivider( + completed: false, + disabled: false, + type: ZetaStepperType.horizontal, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + expect( + (container.decoration! as BoxDecoration).color, + Zeta.of(getBuildContext(tester, Container)).colors.borderPrimary, + ); + }, + ); + testWidgets( + 'StepDivider has the correct colour when completed', + (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepDivider( + completed: true, + disabled: false, + type: ZetaStepperType.horizontal, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + expect( + (container.decoration! as BoxDecoration).color, + Zeta.of(getBuildContext(tester, Container)).colors.borderPositive, + ); + }, + ); + testWidgets( + 'StepDivider has the correct colour when disabled', + (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: StepDivider( + completed: true, + disabled: true, + type: ZetaStepperType.horizontal, + ), + ), + ); + + final container = tester.widget(find.byType(Container)); + expect( + (container.decoration! as BoxDecoration).color, + Zeta.of(getBuildContext(tester, Container)).colors.borderDefault, + ); + }, + ); + }); + + group('ZetaStepper Interaction Tests', () { + testWidgets('Horizontal stepper calls onStepTapped when a step is tapped', (WidgetTester tester) async { + int tappedStep = -1; + await tester.pumpWidget( + TestApp( + home: ZetaStepper( + steps: const [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2')), + ZetaStep(title: Text('Title 3')), + ], + currentStep: 0, + onStepTapped: (step) { + tappedStep = step; + }, + ), + ), + ); + + await tester.tap(find.text('Title 2')); + expect(tappedStep, 1); + }); + + testWidgets('Vertical stepper calls onStepTapped when a step is tapped', (WidgetTester tester) async { + int tappedStep = -1; + await tester.pumpWidget( + TestApp( + home: ZetaStepper( + steps: const [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2')), + ZetaStep(title: Text('Title 3')), + ], + type: ZetaStepperType.vertical, + currentStep: 0, + onStepTapped: (step) { + tappedStep = step; + }, + ), + ), + ); + + await tester.tap(find.text('Title 2')); + expect(tappedStep, 1); + }); + }); + + group('ZetaStepper Golden Tests', () { + goldenTest( + goldenFile, + const ZetaStepper( + steps: [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2')), + ZetaStep(title: Text('Title 3')), + ], + currentStep: 0, + ), + 'stepper_horizontal_incomplete', + ); + + goldenTest( + goldenFile, + const ZetaStepper( + steps: [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2')), + ZetaStep(title: Text('Title 3')), + ], + currentStep: 4, + ), + 'stepper_horizontal_complete', + ); + goldenTest( + goldenFile, + const ZetaStepper( + steps: [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2'), disabled: true), + ZetaStep(title: Text('Title 3')), + ], + currentStep: 0, + ), + 'stepper_horizontal_step_disabled', + ); + goldenTest( + goldenFile, + const ZetaStepper( + type: ZetaStepperType.vertical, + steps: [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2')), + ZetaStep(title: Text('Title 3')), + ], + currentStep: 0, + ), + 'stepper_vertical_incomplete', + ); + + goldenTest( + goldenFile, + const ZetaStepper( + type: ZetaStepperType.vertical, + steps: [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2')), + ZetaStep(title: Text('Title 3')), + ], + currentStep: 4, + ), + 'stepper_vertical_complete', + ); + goldenTest( + goldenFile, + const ZetaStepper( + type: ZetaStepperType.vertical, + steps: [ + ZetaStep(title: Text('Title 1')), + ZetaStep(title: Text('Title 2'), disabled: true), + ZetaStep(title: Text('Title 3')), + ], + currentStep: 0, + ), + 'stepper_vertical_step_disabled', + ); + }); + + group('Performance Tests', () {}); +} From 0c09633b330594cb66457604ff684ab082ab2a4b Mon Sep 17 00:00:00 2001 From: Luke Walton Date: Mon, 21 Oct 2024 14:43:27 +0100 Subject: [PATCH 4/8] deps(automated): Update icons (#191) Co-authored-by: Zeta Tokens Bot Co-authored-by: github-actions --- lib/generated/icons/icons.g.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generated/icons/icons.g.dart b/lib/generated/icons/icons.g.dart index a75631a4..d040c869 100644 --- a/lib/generated/icons/icons.g.dart +++ b/lib/generated/icons/icons.g.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; /// Current version of Zeta Icons // x-release-please-start-version -const zetaIconsVersion = '0.6.2'; +const zetaIconsVersion = '0.8.0'; // x-release-please-end /// Zeta Icons. Full list of icons can be found at [Zeta Icons](https://design.zebra.com/icons/). From 185250206e151ce46e8311fd752bdeb23c82b6b6 Mon Sep 17 00:00:00 2001 From: Luke Walton Date: Tue, 22 Oct 2024 13:36:31 +0100 Subject: [PATCH 5/8] deps: Update widgetbook dep for text scale addon (#193) Co-authored-by: github-actions --- example/widgetbook/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/widgetbook/main.dart b/example/widgetbook/main.dart index 5397a190..96d48344 100644 --- a/example/widgetbook/main.dart +++ b/example/widgetbook/main.dart @@ -246,7 +246,7 @@ class _HotReloadState extends State { ZetaAddon(), InspectorAddon(enabled: false), ZoomAddon(initialZoom: 1.0), - TextScaleAddon(scales: [1.0, 1.2, 1.4, 1.6, 1.8, 2.0], initialScale: 1), + TextScaleAddon(min: 1, max: 2, divisions: 4, initialScale: 1), ], ); } From 65ab6fde3f2307eb6058c12a58d5c20d1feb0601 Mon Sep 17 00:00:00 2001 From: Luke Walton Date: Tue, 22 Oct 2024 13:55:24 +0100 Subject: [PATCH 6/8] chore(main): release 0.16.0 (#162) --- .release-please-manifest.json | 2 +- CHANGELOG.md | 111 +++++++++++++++++++++ example/lib/home.dart | 2 +- example/widgetbook/pages/introduction.dart | 2 +- pubspec.yaml | 2 +- 5 files changed, 115 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2557a107..b4e9013b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.15.2" + ".": "0.16.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b0caab5..347e0d94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,116 @@ # Changelog +## [0.16.0](https://github.com/ZebraDevs/zeta_flutter/compare/v0.15.2...v0.16.0) (2024-10-22) + + +### ✨ New Features + +* added swipe-able actions to notification list item ([057defd](https://github.com/ZebraDevs/zeta_flutter/commit/057defd5a945382826ae8746416473943304da32)) +* comms buttons ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* updated notification list item to match latest design ([057defd](https://github.com/ZebraDevs/zeta_flutter/commit/057defd5a945382826ae8746416473943304da32)) +* **UX-1064:** Comms Buttons ([#182](https://github.com/ZebraDevs/zeta_flutter/issues/182)) ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* **UX-1073:** List Item notification ([#172](https://github.com/ZebraDevs/zeta_flutter/issues/172)) ([057defd](https://github.com/ZebraDevs/zeta_flutter/commit/057defd5a945382826ae8746416473943304da32)) + + +### 🪲 Bug Fixes + +* _getSlidableExtend() now won't return over 1.0 ([057defd](https://github.com/ZebraDevs/zeta_flutter/commit/057defd5a945382826ae8746416473943304da32)) +* A variety of small bug fixes ([#168](https://github.com/ZebraDevs/zeta_flutter/issues/168)) ([cb37705](https://github.com/ZebraDevs/zeta_flutter/commit/cb3770520d21b6e1de4f0bb928d800b12fc49042)) +* Add PlatformIs to not use dart:io ([cb37705](https://github.com/ZebraDevs/zeta_flutter/commit/cb3770520d21b6e1de4f0bb928d800b12fc49042)) +* added different constructors to zetacommsbutton ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* added golden group to testing_conventions.mdx ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* added some commas ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* added styles to banner text ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* Border sizes ([cb37705](https://github.com/ZebraDevs/zeta_flutter/commit/cb3770520d21b6e1de4f0bb928d800b12fc49042)) +* changed avatar xs so it shows two initals ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* changed Colors.transparent to Zeta.of(context).colors.surfaceDefault ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* changed example back ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* changed storybook to widgetbook in name of deploy preview in pull request github action ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* chat item ([#174](https://github.com/ZebraDevs/zeta_flutter/issues/174)) ([3ac64a7](https://github.com/ZebraDevs/zeta_flutter/commit/3ac64a7fa68de77d865c0f95745fdbac760d0984)) +* chat item actions _getSlidableExtend now won't return over 1.0 ([3ac64a7](https://github.com/ZebraDevs/zeta_flutter/commit/3ac64a7fa68de77d865c0f95745fdbac760d0984)) +* Components using the internal text input are now sized correctly ([38b865b](https://github.com/ZebraDevs/zeta_flutter/commit/38b865b8460fe0b8543bab3fa8245750ac2d3d6d)) +* get initials now returns the first and last name initials rather than first and second names. ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* moved analyzer package from dependencies to dev dependencies ([5987c21](https://github.com/ZebraDevs/zeta_flutter/commit/5987c211f79ff6357a47b462a654ca191f64498b)) +* put dev dependencies in alphabetical order ([5987c21](https://github.com/ZebraDevs/zeta_flutter/commit/5987c211f79ff6357a47b462a654ca191f64498b)) +* removed '$componentName' from all test files ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* removed analyzer from dependencies ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* set toggle related properties on non-toggle constructors to null ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* Spacings in example app ([cb37705](https://github.com/ZebraDevs/zeta_flutter/commit/cb3770520d21b6e1de4f0bb928d800b12fc49042)) +* **UX-1105:** Updated search bar to use internal text field ([#186](https://github.com/ZebraDevs/zeta_flutter/issues/186)) ([38b865b](https://github.com/ZebraDevs/zeta_flutter/commit/38b865b8460fe0b8543bab3fa8245750ac2d3d6d)) +* **UX-1141:** Update Spacing tokens ([#159](https://github.com/ZebraDevs/zeta_flutter/issues/159)) ([d22898f](https://github.com/ZebraDevs/zeta_flutter/commit/d22898f7e77704b5a4dd628320662530f1c2a1b4)) +* **UX-1207:** The search box on the search app bar now gets closed when the back button is pressed. ([58fc7f5](https://github.com/ZebraDevs/zeta_flutter/commit/58fc7f5c3e40888ade19a30a5592e70f5340585a)) +* **UX-1241:** Fixed inkwell on ZetaStepper ([#190](https://github.com/ZebraDevs/zeta_flutter/issues/190)) ([60a137f](https://github.com/ZebraDevs/zeta_flutter/commit/60a137f17dbeb989cf2a9f0b0dc3ee4b78ebb488)) +* **UX-1242:** Fixed extended app bar alignment ([#189](https://github.com/ZebraDevs/zeta_flutter/issues/189)) ([58fc7f5](https://github.com/ZebraDevs/zeta_flutter/commit/58fc7f5c3e40888ade19a30a5592e70f5340585a)) +* **UX-922:** FAB-redesign ([#181](https://github.com/ZebraDevs/zeta_flutter/issues/181)) ([11f266b](https://github.com/ZebraDevs/zeta_flutter/commit/11f266bbd955df2ef993f7edee2989bc9be60655)) + + +### 📈 Documentation + +* abstracted functions in test_counter ([5987c21](https://github.com/ZebraDevs/zeta_flutter/commit/5987c211f79ff6357a47b462a654ca191f64498b)) +* added brief docs for fontSize function in avatar ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* added comments to the class about named constructors ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* added doc comments to test_counter script and utils file ([5987c21](https://github.com/ZebraDevs/zeta_flutter/commit/5987c211f79ff6357a47b462a654ca191f64498b)) +* added figma and widgetbook links to all components ([#180](https://github.com/ZebraDevs/zeta_flutter/issues/180)) ([e1d2ba0](https://github.com/ZebraDevs/zeta_flutter/commit/e1d2ba04bfae99f7f383566b483c6558d9a91ec5)) +* added helper function section to TESTING_README ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* added info about how to run the script to testing read me ([5987c21](https://github.com/ZebraDevs/zeta_flutter/commit/5987c211f79ff6357a47b462a654ca191f64498b)) +* added link to excel sheet in TESTING_README ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* added testing_conventions.mdx to keep track how we are testing in flutter ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* changed comms button example to use assorted constructors ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* created test counter script ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* moved helper functions from test_counter to utils file ([5987c21](https://github.com/ZebraDevs/zeta_flutter/commit/5987c211f79ff6357a47b462a654ca191f64498b)) +* set initial values for comms button on widgetbook ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* test counter ([#187](https://github.com/ZebraDevs/zeta_flutter/issues/187)) ([5987c21](https://github.com/ZebraDevs/zeta_flutter/commit/5987c211f79ff6357a47b462a654ca191f64498b)) +* test_counter script improvements ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* testing counter script ([5987c21](https://github.com/ZebraDevs/zeta_flutter/commit/5987c211f79ff6357a47b462a654ca191f64498b)) + + +### ⛓️ Dependencies + +* **automated:** Update icons ([#191](https://github.com/ZebraDevs/zeta_flutter/issues/191)) ([0c09633](https://github.com/ZebraDevs/zeta_flutter/commit/0c09633b330594cb66457604ff684ab082ab2a4b)) +* **automated:** Update icons 2024-10-14 ([18ea9a2](https://github.com/ZebraDevs/zeta_flutter/commit/18ea9a2b123182f6da3382b9ee042297a4d721ae)) +* **automated:** Update tokens - 2024-08-27 ([#163](https://github.com/ZebraDevs/zeta_flutter/issues/163)) ([b499e22](https://github.com/ZebraDevs/zeta_flutter/commit/b499e221c58327ac6e6d18851986d3a503464de6)) +* **automated:** Update tokens ([#170](https://github.com/ZebraDevs/zeta_flutter/issues/170)) ([361b1c7](https://github.com/ZebraDevs/zeta_flutter/commit/361b1c7b7a271a990994ce35ac5d800315e7a753)) +* **automated:** Update tokens ([#176](https://github.com/ZebraDevs/zeta_flutter/issues/176)) ([2bd38de](https://github.com/ZebraDevs/zeta_flutter/commit/2bd38de3454f7544c0e19f9b60f1c2ce9e8e310c)) +* Update widgetbook dep for text scale addon ([#193](https://github.com/ZebraDevs/zeta_flutter/issues/193)) ([1852502](https://github.com/ZebraDevs/zeta_flutter/commit/185250206e151ce46e8311fd752bdeb23c82b6b6)) + + +### 🧪 Tests + +* Add new GoldenFiles class to simplify generating golden file Uris ([a77211a](https://github.com/ZebraDevs/zeta_flutter/commit/a77211a9fd33dfee170605a33098e9df6d634be2)) +* added background color test ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* added debugFillProperties test helper function ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* added loop for zetaavatar with border color for $size ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* added test count script ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* avatar ([#183](https://github.com/ZebraDevs/zeta_flutter/issues/183)) ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* banner ([#184](https://github.com/ZebraDevs/zeta_flutter/issues/184)) ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* banner tests ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* changed extension test to reflect changes ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* changed the iconbutton to a ZetaIcon for golden tests ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* comms goldens ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* Fix bug in golden tests that meant they failed on windows ([#177](https://github.com/ZebraDevs/zeta_flutter/issues/177)) ([a77211a](https://github.com/ZebraDevs/zeta_flutter/commit/a77211a9fd33dfee170605a33098e9df6d634be2)) +* implemented helper function in avatar and banner tests ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* improved banner tests ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* moved script files to different PR ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* organizing tests into groups ([#188](https://github.com/ZebraDevs/zeta_flutter/issues/188)) ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* removed comment that was cause github action to fail ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* removed unused import ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* replaced IconButton with Icon from golden tests ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* standardizing tests for badges ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* started making banner tests ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* Wrote tests for ZetaStepper ([60a137f](https://github.com/ZebraDevs/zeta_flutter/commit/60a137f17dbeb989cf2a9f0b0dc3ee4b78ebb488)) +* Wrote tests for ZetaTopAppbar ([58fc7f5](https://github.com/ZebraDevs/zeta_flutter/commit/58fc7f5c3e40888ade19a30a5592e70f5340585a)) + + +### 🧹 Miscellaneous Chores + +* **automated:** Lint commit and format ([ba4901d](https://github.com/ZebraDevs/zeta_flutter/commit/ba4901d74fff20d6319050f331c5f6d60f99b438)) +* **automated:** Lint commit and format ([ef050af](https://github.com/ZebraDevs/zeta_flutter/commit/ef050af9af8caf9552aa2b7aa1a4465320e4c870)) +* **automated:** Lint commit and format ([7fba9b2](https://github.com/ZebraDevs/zeta_flutter/commit/7fba9b27e399fc3ac5ebc0c09908ae4c3be92e3c)) +* **automated:** Lint commit and format ([af95815](https://github.com/ZebraDevs/zeta_flutter/commit/af958159b3e4f3fad06c24e64983aff5861a9482)) +* **automated:** Lint commit and format ([3ac64a7](https://github.com/ZebraDevs/zeta_flutter/commit/3ac64a7fa68de77d865c0f95745fdbac760d0984)) +* Merged goldenTest and goldenTestWithCallbacks. Also made the widgetType parameter optional and defaulted it to the type of widget. ([58fc7f5](https://github.com/ZebraDevs/zeta_flutter/commit/58fc7f5c3e40888ade19a30a5592e70f5340585a)) +* Update third party licenses ([cb37705](https://github.com/ZebraDevs/zeta_flutter/commit/cb3770520d21b6e1de4f0bb928d800b12fc49042)) + ## [0.15.2](https://github.com/ZebraDevs/zeta_flutter/compare/v0.15.1...v0.15.2) (2024-08-23) diff --git a/example/lib/home.dart b/example/lib/home.dart index 783f36ec..20e08aeb 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -158,7 +158,7 @@ class _HomeState extends State { final _theme = theme..sort((a, b) => a.name.compareTo(b.name)); return ExampleScaffold( // x-release-please-start-version - name: 'zeta_flutter v0.15.2', + name: 'zeta_flutter v0.16.0', // x-release-please-end child: SingleChildScrollView( child: Column( diff --git a/example/widgetbook/pages/introduction.dart b/example/widgetbook/pages/introduction.dart index 24f553f3..2e17ecd8 100644 --- a/example/widgetbook/pages/introduction.dart +++ b/example/widgetbook/pages/introduction.dart @@ -71,7 +71,7 @@ class _IntroductionWidgetbookState extends State { Expanded( child: Text( // x-release-please-start-version - 'zeta_flutter v0.15.2', + 'zeta_flutter v0.16.0', // x-release-please-end style: ZetaTextStyles.displayLarge.copyWith(fontSize: largeScreen ? null : 24), ), diff --git a/pubspec.yaml b/pubspec.yaml index 946dd016..03837121 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: zeta_flutter -version: 0.15.2 +version: 0.16.0 description: Zeta is the new, formal, standardized Zebra Design System based off the successes of ZDS (Zebra Design System). This package is in pre-release, and so many aspects are incomplete. From 17a69951628d17edc635408d1b026abbf3dc92c5 Mon Sep 17 00:00:00 2001 From: Daniel Eshkeri Date: Thu, 24 Oct 2024 14:01:26 +0100 Subject: [PATCH 7/8] test: changed the word 'Dimension' to 'Dimensions' (#194) --- test/scripts/output/test_table.md | 53 +++++++++++---------- test/src/components/banner/banner_test.dart | 2 +- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/test/scripts/output/test_table.md b/test/scripts/output/test_table.md index 06d85966..307b28b7 100644 --- a/test/scripts/output/test_table.md +++ b/test/scripts/output/test_table.md @@ -1,26 +1,27 @@ -| Component | Accessibility | Content | Dimensions | Styling | Interaction | Golden | Performance | Unorganised | Total Tests | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| Accordion | 0 | 2 | 0 | 1 | 2 | 0 | 0 | 0 | 5 | -| Avatar | 1 | 3 | 6 | 5 | 0 | 6 | 0 | 0 | 21 | -| Indicator | 0 | 7 | 0 | 0 | 0 | 5 | 0 | 0 | 12 | -| Label | 0 | 8 | 0 | 0 | 0 | 7 | 0 | 0 | 15 | -| Priority Pill | 0 | 5 | 0 | 0 | 0 | 4 | 0 | 0 | 9 | -| Status Label | 0 | 3 | 0 | 0 | 0 | 2 | 0 | 0 | 5 | -| Tag | 0 | 3 | 0 | 0 | 0 | 2 | 0 | 0 | 5 | -| Banner | 3 | 4 | 0 | 3 | 0 | 1 | 0 | 3 | 14 | -| Button | 0 | 10 | 0 | 2 | 1 | 8 | 0 | 0 | 21 | -| Chat Item | 0 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 10 | -| Checkbox | 0 | 3 | 0 | 0 | 3 | 2 | 0 | 0 | 8 | -| Chip | 0 | 1 | 0 | 0 | 5 | 0 | 0 | 0 | 6 | -| Comms Button | 2 | 7 | 0 | 0 | 0 | 1 | 0 | 0 | 10 | -| Dialpad | 0 | 2 | 0 | 1 | 2 | 3 | 0 | 0 | 8 | -| Fab | 0 | 6 | 0 | 1 | 1 | 4 | 0 | 0 | 12 | -| Icon | 1 | 4 | 1 | 6 | 0 | 0 | 0 | 0 | 12 | -| In Page Banner | 0 | 4 | 0 | 4 | 2 | 4 | 0 | 0 | 14 | -| Password Input | 0 | 4 | 0 | 0 | 0 | 1 | 0 | 0 | 5 | -| Search Bar | 0 | 5 | 0 | 0 | 5 | 5 | 0 | 0 | 15 | -| Slider | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | -| Stepper Input | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 2 | -| Tooltip | 0 | 3 | 1 | 3 | 0 | 4 | 0 | 0 | 11 | -| Extended Top App Bar | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 3 | -| Total Tests | 7 | 95 | 8 | 26 | 23 | 59 | 0 | 6 | 224 | \ No newline at end of file +| Component | Accessibility | Content | Dimensions | Styling | Interaction | Golden | Performance | Unorganised | Total Tests | +| -------------- | ------------- | ------- | ---------- | ------- | ----------- | ------ | ----------- | ----------- | ----------- | +| Accordion | 0 | 2 | 0 | 1 | 2 | 0 | 0 | 0 | 5 | +| Avatar | 1 | 3 | 6 | 5 | 0 | 6 | 0 | 0 | 21 | +| Indicator | 0 | 7 | 0 | 0 | 0 | 5 | 0 | 0 | 12 | +| Label | 0 | 8 | 0 | 0 | 0 | 7 | 0 | 0 | 15 | +| Priority Pill | 0 | 5 | 0 | 0 | 0 | 4 | 0 | 0 | 9 | +| Status Label | 0 | 3 | 0 | 0 | 0 | 2 | 0 | 0 | 5 | +| Tag | 0 | 3 | 0 | 0 | 0 | 2 | 0 | 0 | 5 | +| Banner | 3 | 4 | 3 | 3 | 0 | 1 | 0 | 0 | 14 | +| Button | 0 | 10 | 0 | 2 | 1 | 8 | 0 | 0 | 21 | +| Chat Item | 0 | 10 | 0 | 0 | 0 | 8 | 0 | 0 | 18 | +| Checkbox | 0 | 3 | 0 | 0 | 3 | 3 | 0 | 0 | 9 | +| Chip | 0 | 1 | 0 | 0 | 5 | 0 | 0 | 0 | 6 | +| Comms Button | 2 | 7 | 0 | 0 | 0 | 1 | 0 | 0 | 10 | +| Dialpad | 0 | 2 | 0 | 1 | 2 | 3 | 0 | 0 | 8 | +| Fab | 0 | 6 | 0 | 1 | 1 | 6 | 0 | 0 | 14 | +| Icon | 1 | 4 | 1 | 6 | 0 | 0 | 0 | 0 | 12 | +| In Page Banner | 0 | 4 | 0 | 4 | 2 | 4 | 0 | 0 | 14 | +| Password Input | 0 | 4 | 0 | 0 | 0 | 2 | 0 | 0 | 6 | +| Search Bar | 0 | 5 | 0 | 0 | 5 | 5 | 0 | 0 | 15 | +| Slider | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | +| Stepper | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 30 | 30 | +| Stepper Input | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 2 | +| Tooltip | 0 | 3 | 1 | 3 | 0 | 4 | 0 | 0 | 11 | +| Top App Bar | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 25 | 25 | +| Total Tests | 7 | 95 | 11 | 26 | 23 | 71 | 0 | 55 | 288 | diff --git a/test/src/components/banner/banner_test.dart b/test/src/components/banner/banner_test.dart index a221f5fb..470a0c5e 100644 --- a/test/src/components/banner/banner_test.dart +++ b/test/src/components/banner/banner_test.dart @@ -194,7 +194,7 @@ void main() { }); }); - group('Dimension Tests', () { + group('Dimensions Tests', () { testWidgets('icon is the correct size', (WidgetTester tester) async { await tester.pumpWidget( TestApp( From 823370e161dd753abd22060d8dd862752dc8a34f Mon Sep 17 00:00:00 2001 From: Daniel Eshkeri Date: Mon, 28 Oct 2024 16:58:34 +0000 Subject: [PATCH 8/8] feat(UX-1231): created avatar rail (#196) feat: created avatar rail test: created tests for avatar rail feat: added label to avatar test: fixed parent folder for stepper chore(automated): Lint commit and format fix: added MainAxisSize.min to avatar column to regulate height fix: widgetbook max lines avatar rail --- example/lib/home.dart | 2 + .../pages/components/avatar_rail_example.dart | 96 ++++++ example/widgetbook/main.dart | 9 +- .../components/avatar_rail_widgetbook.dart | 174 ++++++++++ .../pages/components/avatar_widgetbook.dart | 3 + .../components/avatar_rail/avatar_rail.dart | 146 +++++++++ lib/src/components/avatars/avatar.dart | 188 ++++++++--- lib/src/components/components.dart | 1 + test/TESTING_README.md | 2 +- .../components/avatar/avatar_rail_test.dart | 297 ++++++++++++++++++ .../golden/zeta_avatar_rail_default.png | Bin 0 -> 5696 bytes .../golden/stepper_horizontal_complete.png | Bin .../golden/stepper_horizontal_incomplete.png | Bin .../stepper_horizontal_step_disabled.png | Bin .../golden/stepper_vertical_complete.png | Bin .../golden/stepper_vertical_incomplete.png | Bin .../golden/stepper_vertical_step_disabled.png | Bin test/src/components/stepper/stepper_test.dart | 4 +- 18 files changed, 869 insertions(+), 53 deletions(-) create mode 100644 example/lib/pages/components/avatar_rail_example.dart create mode 100644 example/widgetbook/pages/components/avatar_rail_widgetbook.dart create mode 100644 lib/src/components/avatar_rail/avatar_rail.dart create mode 100644 test/src/components/avatar/avatar_rail_test.dart create mode 100644 test/src/components/avatar/golden/zeta_avatar_rail_default.png rename test/src/components/{ENTER_PARENT_FOLDER (e.g. button) => stepper}/golden/stepper_horizontal_complete.png (100%) rename test/src/components/{ENTER_PARENT_FOLDER (e.g. button) => stepper}/golden/stepper_horizontal_incomplete.png (100%) rename test/src/components/{ENTER_PARENT_FOLDER (e.g. button) => stepper}/golden/stepper_horizontal_step_disabled.png (100%) rename test/src/components/{ENTER_PARENT_FOLDER (e.g. button) => stepper}/golden/stepper_vertical_complete.png (100%) rename test/src/components/{ENTER_PARENT_FOLDER (e.g. button) => stepper}/golden/stepper_vertical_incomplete.png (100%) rename test/src/components/{ENTER_PARENT_FOLDER (e.g. button) => stepper}/golden/stepper_vertical_step_disabled.png (100%) diff --git a/example/lib/home.dart b/example/lib/home.dart index 20e08aeb..db398437 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:zeta_example/pages/components/accordion_example.dart'; +import 'package:zeta_example/pages/components/avatar_rail_example.dart'; import 'package:zeta_example/pages/components/avatar_example.dart'; import 'package:zeta_example/pages/components/badges_example.dart'; import 'package:zeta_example/pages/components/banner_example.dart'; @@ -61,6 +62,7 @@ class Component { final List components = [ Component(AccordionExample.name, (context) => const AccordionExample()), Component(TopAppBarExample.name, (context) => const TopAppBarExample()), + Component(AvatarRailExample.name, (context) => const AvatarRailExample()), Component(AvatarExample.name, (context) => const AvatarExample()), Component(BannerExample.name, (context) => const BannerExample()), Component(BadgesExample.name, (context) => const BadgesExample()), diff --git a/example/lib/pages/components/avatar_rail_example.dart b/example/lib/pages/components/avatar_rail_example.dart new file mode 100644 index 00000000..3199a6bb --- /dev/null +++ b/example/lib/pages/components/avatar_rail_example.dart @@ -0,0 +1,96 @@ +// import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:zeta_example/widgets.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class AvatarRailExample extends StatefulWidget { + static const String name = 'AvatarRail'; + + const AvatarRailExample({super.key}); + + @override + State createState() => _AvatarRailExampleState(); +} + +class _AvatarRailExampleState extends State { + int? selected; + @override + Widget build(BuildContext context) { + final avatarList = [ + ZetaAvatar.initials( + initials: 'AZ', + label: 'Archie', + ), + ZetaAvatar.initials( + initials: 'BY', + label: 'Beth', + ), + ZetaAvatar.initials( + initials: 'CX', + label: 'Clara', + ), + ZetaAvatar.initials( + initials: 'DW', + label: 'Dan', + ), + ZetaAvatar.initials( + initials: 'EV', + label: 'Emily', + ), + ZetaAvatar.initials( + initials: 'FU', + label: 'Frank', + ), + ZetaAvatar.initials( + initials: 'GT', + label: 'George', + ), + ZetaAvatar.initials( + initials: 'HS', + label: 'Harith', + ), + ZetaAvatar.initials( + initials: 'IR', + label: 'Irene', + ), + ZetaAvatar.initials( + initials: 'KQ', + label: 'Katie', + ), + ]; + return ExampleScaffold( + name: AvatarRailExample.name, + child: SingleChildScrollView( + child: Column( + children: [ + for (final size in ZetaAvatarSize.values) + Row( + children: [ + Text(size.toString()), + SizedBox( + width: 500, + child: ZetaAvatarRail( + gap: 10, + size: size, + labelMaxLines: 3, + onTap: (key) => { + setState(() { + selected = int.parse(key.toString().replaceAll(RegExp(r'[^0-9]'), '')); + }) + }, + avatars: avatarList, + ), + ), + if (selected != null) + Padding( + padding: const EdgeInsets.all(8.0), + child: avatarList[selected!].copyWith(size: size), + ), + ].gap(50), + ) + ], + ), + ), + ); + } +} diff --git a/example/widgetbook/main.dart b/example/widgetbook/main.dart index 96d48344..6a5202c6 100644 --- a/example/widgetbook/main.dart +++ b/example/widgetbook/main.dart @@ -11,6 +11,7 @@ import 'pages/components/notification_list_item_widgetbook.dart'; import 'pages/components/slider_widgetbook.dart'; import 'pages/components/text_input_widgetbook.dart'; import 'pages/components/top_app_bar_widgetbook.dart'; +import 'pages/components/avatar_rail_widgetbook.dart'; import 'pages/components/avatar_widgetbook.dart'; import 'pages/components/badges_widgetbook.dart'; import 'pages/components/banner_widgetbook.dart'; @@ -121,6 +122,13 @@ class _HotReloadState extends State { name: 'Components', isInitiallyExpanded: false, children: [ + WidgetbookComponent( + name: 'Avatar', + useCases: [ + WidgetbookUseCase(name: 'Avatar', builder: (context) => avatarUseCase(context)), + WidgetbookUseCase(name: 'Avatar Rail', builder: (context) => avatarRailUseCase(context)), + ], + ), WidgetbookComponent( name: 'Top App Bar', useCases: [ @@ -181,7 +189,6 @@ class _HotReloadState extends State { ], ), WidgetbookUseCase(name: 'Accordion', builder: (context) => accordionUseCase(context)), - WidgetbookUseCase(name: 'Avatar', builder: (context) => avatarUseCase(context)), WidgetbookUseCase(name: 'Banners', builder: (context) => bannerUseCase(context)), WidgetbookUseCase(name: 'Bottom Sheet', builder: (context) => bottomSheetContentUseCase(context)), WidgetbookUseCase(name: 'BreadCrumbs', builder: (context) => breadCrumbsUseCase(context)), diff --git a/example/widgetbook/pages/components/avatar_rail_widgetbook.dart b/example/widgetbook/pages/components/avatar_rail_widgetbook.dart new file mode 100644 index 00000000..1b738085 --- /dev/null +++ b/example/widgetbook/pages/components/avatar_rail_widgetbook.dart @@ -0,0 +1,174 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../utils/scaffold.dart'; + +Widget avatarRailUseCase(BuildContext context) { + final Widget image = Image.asset('assets/Omer.jpg', fit: BoxFit.cover); + final colors = Zeta.of(context).colors; + + return WidgetbookScaffold( + builder: (context, _) => ZetaAvatarRail( + labelMaxLines: context.knobs.int.slider(label: 'Label Max Lines', min: 1, max: 3, initialValue: 1), + avatars: [ + ZetaAvatar( + image: context.knobs.boolean(label: 'Image') ? image : null, + size: context.knobs.list( + label: 'Size', + options: ZetaAvatarSize.values, + labelBuilder: (value) => value.name.split('.').last.toUpperCase(), + initialOption: ZetaAvatarSize.m, + ), + upperBadge: context.knobs.boolean(label: 'Status Badge', initialValue: false) + ? ZetaAvatarBadge.icon( + icon: ZetaIcons.close, + color: context.knobs.colorOrNull(label: "Upper Badge Color", initialValue: colors.green) ?? + colors.iconDefault, + ) + : null, + borderColor: context.knobs.colorOrNull(label: 'Outline', initialValue: colors.green), + lowerBadge: context.knobs.boolean(label: 'Notification Badge', initialValue: false) + ? ZetaAvatarBadge.notification( + value: context.knobs.intOrNull.input(label: "Value", initialValue: 1), + ) + : null, + initials: context.knobs.stringOrNull(label: 'Initials', initialValue: 'AZ'), + backgroundColor: context.knobs.colorOrNull(label: 'Background color', initialValue: colors.purple.shade80), + onTap: () => print('Avatar tapped'), + label: context.knobs.stringOrNull(label: 'Label', initialValue: 'ABC'), + ), + ZetaAvatar( + image: context.knobs.boolean(label: 'Image') ? image : null, + size: context.knobs.list( + label: 'Size', + options: ZetaAvatarSize.values, + labelBuilder: (value) => value.name.split('.').last.toUpperCase(), + initialOption: ZetaAvatarSize.m, + ), + upperBadge: context.knobs.boolean(label: 'Status Badge', initialValue: false) + ? ZetaAvatarBadge.icon( + icon: ZetaIcons.close, + color: context.knobs.colorOrNull(label: "Upper Badge Color", initialValue: colors.green) ?? + colors.iconDefault, + ) + : null, + borderColor: context.knobs.colorOrNull(label: 'Outline', initialValue: colors.green), + lowerBadge: context.knobs.boolean(label: 'Notification Badge', initialValue: false) + ? ZetaAvatarBadge.notification( + value: context.knobs.intOrNull.input(label: "Value", initialValue: 1), + ) + : null, + initials: context.knobs.stringOrNull(label: 'Initials', initialValue: 'AZ'), + backgroundColor: context.knobs.colorOrNull(label: 'Background color', initialValue: colors.purple.shade80), + onTap: () => print('Avatar tapped'), + label: context.knobs.stringOrNull(label: 'Label', initialValue: 'ABC'), + ), + ZetaAvatar( + image: context.knobs.boolean(label: 'Image') ? image : null, + size: context.knobs.list( + label: 'Size', + options: ZetaAvatarSize.values, + labelBuilder: (value) => value.name.split('.').last.toUpperCase(), + initialOption: ZetaAvatarSize.m, + ), + upperBadge: context.knobs.boolean(label: 'Status Badge', initialValue: false) + ? ZetaAvatarBadge.icon( + icon: ZetaIcons.close, + color: context.knobs.colorOrNull(label: "Upper Badge Color", initialValue: colors.green) ?? + colors.iconDefault, + ) + : null, + borderColor: context.knobs.colorOrNull(label: 'Outline', initialValue: colors.green), + lowerBadge: context.knobs.boolean(label: 'Notification Badge', initialValue: false) + ? ZetaAvatarBadge.notification( + value: context.knobs.intOrNull.input(label: "Value", initialValue: 1), + ) + : null, + initials: context.knobs.stringOrNull(label: 'Initials', initialValue: 'AZ'), + backgroundColor: context.knobs.colorOrNull(label: 'Background color', initialValue: colors.purple.shade80), + onTap: () => print('Avatar tapped'), + label: context.knobs.stringOrNull(label: 'Label', initialValue: 'ABC'), + ), + ZetaAvatar( + image: context.knobs.boolean(label: 'Image') ? image : null, + size: context.knobs.list( + label: 'Size', + options: ZetaAvatarSize.values, + labelBuilder: (value) => value.name.split('.').last.toUpperCase(), + initialOption: ZetaAvatarSize.m, + ), + upperBadge: context.knobs.boolean(label: 'Status Badge', initialValue: false) + ? ZetaAvatarBadge.icon( + icon: ZetaIcons.close, + color: context.knobs.colorOrNull(label: "Upper Badge Color", initialValue: colors.green) ?? + colors.iconDefault, + ) + : null, + borderColor: context.knobs.colorOrNull(label: 'Outline', initialValue: colors.green), + lowerBadge: context.knobs.boolean(label: 'Notification Badge', initialValue: false) + ? ZetaAvatarBadge.notification( + value: context.knobs.intOrNull.input(label: "Value", initialValue: 1), + ) + : null, + initials: context.knobs.stringOrNull(label: 'Initials', initialValue: 'AZ'), + backgroundColor: context.knobs.colorOrNull(label: 'Background color', initialValue: colors.purple.shade80), + onTap: () => print('Avatar tapped'), + label: context.knobs.stringOrNull(label: 'Label', initialValue: 'ABC'), + ), + ZetaAvatar( + image: context.knobs.boolean(label: 'Image') ? image : null, + size: context.knobs.list( + label: 'Size', + options: ZetaAvatarSize.values, + labelBuilder: (value) => value.name.split('.').last.toUpperCase(), + initialOption: ZetaAvatarSize.m, + ), + upperBadge: context.knobs.boolean(label: 'Status Badge', initialValue: false) + ? ZetaAvatarBadge.icon( + icon: ZetaIcons.close, + color: context.knobs.colorOrNull(label: "Upper Badge Color", initialValue: colors.green) ?? + colors.iconDefault, + ) + : null, + borderColor: context.knobs.colorOrNull(label: 'Outline', initialValue: colors.green), + lowerBadge: context.knobs.boolean(label: 'Notification Badge', initialValue: false) + ? ZetaAvatarBadge.notification( + value: context.knobs.intOrNull.input(label: "Value", initialValue: 1), + ) + : null, + initials: context.knobs.stringOrNull(label: 'Initials', initialValue: 'AZ'), + backgroundColor: context.knobs.colorOrNull(label: 'Background color', initialValue: colors.purple.shade80), + onTap: () => print('Avatar tapped'), + label: context.knobs.stringOrNull(label: 'Label', initialValue: 'ABC'), + ), + ZetaAvatar( + image: context.knobs.boolean(label: 'Image') ? image : null, + size: context.knobs.list( + label: 'Size', + options: ZetaAvatarSize.values, + labelBuilder: (value) => value.name.split('.').last.toUpperCase(), + initialOption: ZetaAvatarSize.m, + ), + upperBadge: context.knobs.boolean(label: 'Status Badge', initialValue: false) + ? ZetaAvatarBadge.icon( + icon: ZetaIcons.close, + color: context.knobs.colorOrNull(label: "Upper Badge Color", initialValue: colors.green) ?? + colors.iconDefault, + ) + : null, + borderColor: context.knobs.colorOrNull(label: 'Outline', initialValue: colors.green), + lowerBadge: context.knobs.boolean(label: 'Notification Badge', initialValue: false) + ? ZetaAvatarBadge.notification( + value: context.knobs.intOrNull.input(label: "Value", initialValue: 1), + ) + : null, + initials: context.knobs.stringOrNull(label: 'Initials', initialValue: 'AZ'), + backgroundColor: context.knobs.colorOrNull(label: 'Background color', initialValue: colors.purple.shade80), + onTap: () => print('Avatar tapped'), + label: context.knobs.stringOrNull(label: 'Label', initialValue: 'ABC'), + ), + ], + ), + ); +} diff --git a/example/widgetbook/pages/components/avatar_widgetbook.dart b/example/widgetbook/pages/components/avatar_widgetbook.dart index 13d1a6cd..8751ee0d 100644 --- a/example/widgetbook/pages/components/avatar_widgetbook.dart +++ b/example/widgetbook/pages/components/avatar_widgetbook.dart @@ -32,6 +32,9 @@ Widget avatarUseCase(BuildContext context) { : null, initials: context.knobs.stringOrNull(label: 'Initials', initialValue: 'AZ'), backgroundColor: context.knobs.colorOrNull(label: 'Background color', initialValue: colors.purple.shade80), + onTap: () => print('Avatar tapped'), + label: context.knobs.stringOrNull(label: 'Label', initialValue: 'ABC'), + labelMaxLines: context.knobs.int.slider(label: 'Label Max Lines', min: 1, max: 3, initialValue: 1), ), ); } diff --git a/lib/src/components/avatar_rail/avatar_rail.dart b/lib/src/components/avatar_rail/avatar_rail.dart new file mode 100644 index 00000000..c57feac4 --- /dev/null +++ b/lib/src/components/avatar_rail/avatar_rail.dart @@ -0,0 +1,146 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import '../../utils/utils.dart'; +import '../avatars/avatar.dart'; + +/// A stateless widget that represents an avatar rail in the Zeta application. +/// +/// The `ZetaAvatarRail` widget is used to display a horizontal rail of avatars, +/// typically used for navigation or selection purposes within the application. +/// +/// This widget does not maintain any state and relies on its parent and children widgets to +/// provide the necessary data and handle interactions. +/// +/// Example usage: +/// +/// ```dart +/// ZetaAvatarRail( +/// avatars: [ +/// ZetaAvatar.initials( +/// key: Key('avatar1'), +/// initials: 'AZ', +/// onTap: () => print('Avatar tapped'), +/// label: 'Archie', +/// ), +/// ZetaAvatar.initials( +/// key: Key('avatar2'), +/// initials: 'BY', +/// onTap: () => print('Avatar tapped'), +/// label: 'Beth', +/// ), +/// ZetaAvatar.initials( +/// key: Key('avatar3'), +/// initials: 'CX', +/// onTap: () => print('Avatar tapped'), +/// label: 'Carla', +/// ), +/// ] +/// ) +/// ``` +/// +/// See also: +/// +/// * [StatelessWidget], which is the superclass of this widget. +/// * [ZetaAvatar], which is used within this rail to represent individual avatars. +/// {@category Components} +/// +/// Figma: https://www.figma.com/file/JesXQFLaPJLc1BdBM4sisI/%F0%9F%A6%93-ZDS---Components?type=design&node-id=20816-388 +/// +/// Widgetbook: https://design.zebra.com/flutter/widgetbook/index.html#/?path=components/avatar/avatar-rail +class ZetaAvatarRail extends StatelessWidget { + /// + const ZetaAvatarRail({ + super.key, + this.size, + required this.avatars, + this.labelTextStyle, + this.labelMaxLines = 1, + this.onTap, + this.gap, + }); + + /// A list of `ZetaAvatar` objects representing the avatars to be displayed. + final List avatars; + + /// The size of the [ZetaAvatar]s + final ZetaAvatarSize? size; + + /// The text style to be applied to the label of the [ZetaAvatar]s. + final TextStyle? labelTextStyle; + + /// The maximum number of lines to be displayed in the label of the [ZetaAvatar]s. + final int labelMaxLines; + + /// A callback function to be executed when an [ZetaAvatar] is tapped. + /// The function receives the key of the tapped [ZetaAvatar] as a parameter. + /// If no key is provided, the index of the [ZetaAvatar] in the list is used. + final void Function(Key)? onTap; + + /// The gap between the avatars. + /// Defaults to 'Zeta.of(context).spacing.small) + final double? gap; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + hitTestBehavior: HitTestBehavior.translucent, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (final avatar in avatars) + avatar.copyWith( + size: size, + labelTextStyle: labelTextStyle, + labelMaxLines: labelMaxLines, + onTap: () => onTap?.call(key ?? Key(avatars.indexOf(avatar).toString())), + key: key ?? Key(avatars.indexOf(avatar).toString()), + ), + ].gap(gap ?? Zeta.of(context).spacing.small), + ), + ), + ], + ); + } + + /// Returns pixel size for [ZetaAvatarSize] + static double pixelSize(BuildContext context, ZetaAvatarSize size) { + switch (size) { + case ZetaAvatarSize.xxxl: + return Zeta.of(context).spacing.minimum * 50; // TODO(UX-1202): ZetaSpacingBase + // return ZetaSpacingBase.x50; + case ZetaAvatarSize.xxl: + return Zeta.of(context).spacing.minimum * 30; // TODO(UX-1202): ZetaSpacingBase + // return ZetaSpacingBase.x30; + case ZetaAvatarSize.xl: + return Zeta.of(context).spacing.xl_10; + case ZetaAvatarSize.l: + return Zeta.of(context).spacing.xl_9; + case ZetaAvatarSize.m: + return Zeta.of(context).spacing.xl_8; + case ZetaAvatarSize.s: + return Zeta.of(context).spacing.xl_6; + case ZetaAvatarSize.xs: + return Zeta.of(context).spacing.xl_5; + case ZetaAvatarSize.xxs: + return Zeta.of(context).spacing.xl_4; + case ZetaAvatarSize.xxxs: + return Zeta.of(context).spacing.xl_2; + } + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('labelTextStyle', labelTextStyle)) + ..add(EnumProperty('size', size)) + ..add(IntProperty('labelMaxLines', labelMaxLines)) + ..add(ObjectFlagProperty.has('onTap', onTap)) + ..add(DoubleProperty('gap', gap)); + } +} diff --git a/lib/src/components/avatars/avatar.dart b/lib/src/components/avatars/avatar.dart index b72e1988..39a1b7eb 100644 --- a/lib/src/components/avatars/avatar.dart +++ b/lib/src/components/avatars/avatar.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -38,7 +39,7 @@ enum ZetaAvatarSize { /// /// Figma: https://www.figma.com/file/JesXQFLaPJLc1BdBM4sisI/%F0%9F%A6%93-ZDS---Components?type=design&node-id=20816-388 /// -/// Widgetbook: https://zeta-ds.web.app/flutter/widgetbook/index.html#/?path=components/avatar +/// Widgetbook: https://zeta-ds.web.app/flutter/widgetbook/index.html#/?path=components/avatar/avatar class ZetaAvatar extends ZetaStatelessWidget { /// Constructor for [ZetaAvatar] const ZetaAvatar({ @@ -54,6 +55,10 @@ class ZetaAvatar extends ZetaStatelessWidget { this.semanticUpperBadgeLabel = 'upperBadge', this.semanticLowerBadgeLabel = 'lowerBadge', this.initialTextStyle, + this.label, + this.labelTextStyle, + this.labelMaxLines = 1, + this.onTap, }); /// Constructor for [ZetaAvatar] with image. @@ -67,6 +72,10 @@ class ZetaAvatar extends ZetaStatelessWidget { this.semanticLabel = 'avatar', this.semanticUpperBadgeLabel = 'upperBadge', this.semanticLowerBadgeLabel = 'lowerBadge', + this.label, + this.labelTextStyle, + this.labelMaxLines = 1, + this.onTap, }) : backgroundColor = null, initials = null, initialTextStyle = null; @@ -84,6 +93,10 @@ class ZetaAvatar extends ZetaStatelessWidget { this.semanticUpperBadgeLabel = 'upperBadge', this.semanticLowerBadgeLabel = 'lowerBadge', this.initialTextStyle, + this.label, + this.labelTextStyle, + this.labelMaxLines = 1, + this.onTap, }) : image = null; /// Constructor for [ZetaAvatar] with initials from a full name. @@ -99,6 +112,10 @@ class ZetaAvatar extends ZetaStatelessWidget { this.semanticUpperBadgeLabel = 'upperBadge', this.semanticLowerBadgeLabel = 'lowerBadge', this.initialTextStyle, + this.label, + this.labelTextStyle, + this.labelMaxLines = 1, + this.onTap, }) : image = null, initials = name.initials; @@ -155,6 +172,18 @@ class ZetaAvatar extends ZetaStatelessWidget { /// ``` final TextStyle? initialTextStyle; + /// Label to display below the avatar. + final String? label; + + /// Text style for label. + final TextStyle? labelTextStyle; + + /// Maximum number of lines for label. + final int labelMaxLines; + + /// Callback when avatar is tapped. + final VoidCallback? onTap; + /// Return copy of avatar with certain changed fields ZetaAvatar copyWith({ ZetaAvatarSize? size, @@ -164,6 +193,11 @@ class ZetaAvatar extends ZetaStatelessWidget { Color? borderColor, ZetaAvatarBadge? lowerBadge, ZetaAvatarBadge? upperBadge, + String? label, + TextStyle? labelTextStyle, + int? labelMaxLines, + VoidCallback? onTap, + Key? key, }) { return ZetaAvatar( size: size ?? this.size, @@ -173,6 +207,11 @@ class ZetaAvatar extends ZetaStatelessWidget { borderColor: borderColor ?? this.borderColor, lowerBadge: lowerBadge ?? this.lowerBadge, upperBadge: upperBadge ?? this.upperBadge, + label: label ?? this.label, + labelTextStyle: labelTextStyle ?? this.labelTextStyle, + labelMaxLines: labelMaxLines ?? this.labelMaxLines, + onTap: onTap ?? this.onTap, + key: key ?? this.key, ); } @@ -212,58 +251,85 @@ class ZetaAvatar extends ZetaStatelessWidget { child: Semantics( value: semanticLabel, child: SelectionContainer.disabled( - child: Stack( + child: Column( + mainAxisSize: MainAxisSize.min, children: [ - Container( - width: pSize, - height: pSize, - decoration: BoxDecoration( - border: borderColor != null ? Border.all(color: borderColor!, width: 0) : null, - borderRadius: Zeta.of(context).radius.full, - color: backgroundColor ?? (_showPlaceholder ? zetaColors.surfacePrimary : zetaColors.cool.shade20), - ), - child: borderColor != null - ? Container( - width: pSize, - height: pSize, - decoration: BoxDecoration( - color: backgroundColor ?? zetaColors.surfaceHover, - border: Border.all(color: borderColor!, width: borderSize(context)), - borderRadius: Zeta.of(context).radius.full, - ), - child: ClipRRect( - borderRadius: Zeta.of(context).radius.full, - child: innerContent, - ), - ) - : DecoratedBox( - decoration: BoxDecoration( - borderRadius: Zeta.of(context).radius.full, - color: backgroundColor ?? zetaColors.surfaceHover, - ), - child: ClipRRect( - borderRadius: Zeta.of(context).radius.full, - child: innerContent, - ), + Stack( + children: [ + GestureDetector( + onTap: onTap, + child: Container( + width: pSize, + height: pSize, + decoration: BoxDecoration( + border: borderColor != null ? Border.all(color: borderColor!, width: 0) : null, + borderRadius: Zeta.of(context).radius.full, + color: + backgroundColor ?? (_showPlaceholder ? zetaColors.surfacePrimary : zetaColors.cool.shade20), ), - ), - if (upperBadge != null) - Positioned( - right: Zeta.of(context).spacing.none, - child: Semantics( - value: semanticLowerBadgeLabel, - child: upperBadge!.copyWith( - size: size, + child: borderColor != null + ? Container( + width: pSize, + height: pSize, + decoration: BoxDecoration( + color: backgroundColor ?? zetaColors.surfaceHover, + border: Border.all(color: borderColor!, width: borderSize(context)), + borderRadius: Zeta.of(context).radius.full, + ), + child: ClipRRect( + borderRadius: Zeta.of(context).radius.full, + child: innerContent, + ), + ) + : DecoratedBox( + decoration: BoxDecoration( + borderRadius: Zeta.of(context).radius.full, + color: backgroundColor ?? zetaColors.surfaceHover, + ), + child: ClipRRect( + borderRadius: Zeta.of(context).radius.full, + child: innerContent, + ), + ), ), ), + if (upperBadge != null) + Positioned( + right: Zeta.of(context).spacing.none, + child: Semantics( + value: semanticLowerBadgeLabel, + child: upperBadge!.copyWith( + size: size, + ), + ), + ), + if (lowerBadge != null) + Positioned( + right: Zeta.of(context).spacing.none, + bottom: Zeta.of(context).spacing.none, + child: Semantics( + value: semanticLowerBadgeLabel, + child: lowerBadge!.copyWith(size: size), + ), + ), + ], + ), + if (label != null) + SizedBox( + height: Zeta.of(context).spacing.minimum, ), - if (lowerBadge != null) - Positioned( - right: Zeta.of(context).spacing.none, - bottom: Zeta.of(context).spacing.none, - child: Semantics( - value: semanticLowerBadgeLabel, - child: lowerBadge!.copyWith(size: size), + if (label != null) + SizedBox( + width: pSize, + child: Text( + label!, + style: labelTextStyle ?? + size.labelStyle(context).copyWith( + color: zetaColors.textSubtle, + ), + maxLines: labelMaxLines, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, ), ), ], @@ -286,7 +352,11 @@ class ZetaAvatar extends ZetaStatelessWidget { ..add(StringProperty('semanticUpperBadgeValue', semanticUpperBadgeLabel)) ..add(StringProperty('semanticValue', semanticLabel)) ..add(StringProperty('semanticLowerBadgeValue', semanticLowerBadgeLabel)) - ..add(DiagnosticsProperty('initialTextStyle', initialTextStyle)); + ..add(DiagnosticsProperty('initialTextStyle', initialTextStyle)) + ..add(DiagnosticsProperty('labelTextStyle', labelTextStyle)) + ..add(StringProperty('label', label)) + ..add(IntProperty('labelMaxLines', labelMaxLines)) + ..add(ObjectFlagProperty.has('onTap', onTap)); } /// Returns pixel size for [ZetaAvatarSize] @@ -347,6 +417,26 @@ extension on ZetaAvatarSize { double fontSize(BuildContext context) { return ZetaAvatar.fontSize(context, this); } + + TextStyle labelStyle(BuildContext context) { + switch (this) { + case ZetaAvatarSize.xxxl: + return ZetaTextStyles.displaySmall; + case ZetaAvatarSize.xxl: + case ZetaAvatarSize.xl: + return ZetaTextStyles.bodyLarge; + case ZetaAvatarSize.l: + return ZetaTextStyles.bodyMedium; + case ZetaAvatarSize.m: + return ZetaTextStyles.bodySmall; + case ZetaAvatarSize.s: + case ZetaAvatarSize.xs: + case ZetaAvatarSize.xxs: + return ZetaTextStyles.bodyXSmall; + case ZetaAvatarSize.xxxs: + return ZetaTextStyles.bodyXSmall; + } + } } /// Enum of types for [ZetaAvatarBadge] diff --git a/lib/src/components/components.dart b/lib/src/components/components.dart index b1e5eb25..63bae3a4 100644 --- a/lib/src/components/components.dart +++ b/lib/src/components/components.dart @@ -1,4 +1,5 @@ export 'accordion/accordion.dart'; +export 'avatar_rail/avatar_rail.dart'; export 'avatars/avatar.dart'; export 'badges/indicator.dart'; export 'badges/label.dart'; diff --git a/test/TESTING_README.md b/test/TESTING_README.md index 0bcb169e..6d31f3b5 100644 --- a/test/TESTING_README.md +++ b/test/TESTING_README.md @@ -77,7 +77,7 @@ void main() { group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, widget, widgetType, 'PNG_FILE_NAME'); + goldenTest(goldenFile, widget, 'PNG_FILE_NAME'); }); group('Performance Tests', () {}); diff --git a/test/src/components/avatar/avatar_rail_test.dart b/test/src/components/avatar/avatar_rail_test.dart new file mode 100644 index 00000000..3906ba51 --- /dev/null +++ b/test/src/components/avatar/avatar_rail_test.dart @@ -0,0 +1,297 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; +import '../../../test_utils/utils.dart'; + +void main() { + final avatarList = [ + const ZetaAvatar.initials( + initials: 'AZ', + label: 'Archie', + ), + const ZetaAvatar.initials( + initials: 'BY', + label: 'Beth', + ), + const ZetaAvatar.initials( + initials: 'CX', + label: 'Clara', + ), + const ZetaAvatar.initials( + initials: 'DW', + label: 'Dan', + ), + const ZetaAvatar.initials( + initials: 'EV', + label: 'Emily', + ), + const ZetaAvatar.initials( + initials: 'FU', + label: 'Frank', + ), + const ZetaAvatar.initials( + initials: 'GT', + label: 'George', + ), + const ZetaAvatar.initials( + initials: 'HS', + label: 'Harith', + ), + const ZetaAvatar.initials( + initials: 'IR', + label: 'Irene', + ), + const ZetaAvatar.initials( + initials: 'KQ', + label: 'Katie', + ), + ]; + + const String parentFolder = 'avatar'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('Accessibility Tests', () { + testWidgets('meets labeled tap target guideline', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaAvatarRail( + avatars: avatarList, + size: ZetaAvatarSize.m, + onTap: (key) {}, + ), + ), + ); + + await expectLater( + tester, + meetsGuideline(labeledTapTargetGuideline), + ); + }); + }); + + group('Content Tests', () { + final debugFillProperties = { + 'labelTextStyle': 'null', + 'size': 'm', + 'labelMaxLines': '2', + 'onTap': 'has onTap', + 'gap': '10.0', + }; + debugFillPropertiesTest( + ZetaAvatarRail( + avatars: avatarList, + size: ZetaAvatarSize.m, + gap: 10, + labelMaxLines: 2, + onTap: (key) {}, + ), + debugFillProperties, + ); + + testWidgets('renders the correct number of avatars', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaAvatarRail( + avatars: avatarList, + size: ZetaAvatarSize.m, + onTap: (key) {}, + ), + ), + ); + + final railFinder = find.byType(ZetaAvatarRail); + final avatarFinder = find.byType(ZetaAvatar); + + expect(railFinder, findsOneWidget); + expect(avatarFinder, findsNWidgets(avatarList.length)); + }); + }); + + group('Dimensions Tests', () { + for (final size in ZetaAvatarSize.values) { + testWidgets('width is correct - avatar size: $size', (WidgetTester tester) async { + const screenWidth = 800.0; + const gap = 10.0; + await tester.pumpWidget( + TestApp( + screenSize: const Size(screenWidth, 600), + home: ZetaAvatarRail( + avatars: avatarList, + size: size, + gap: gap, + onTap: (key) {}, + ), + ), + ); + + final railFinder = find.byType(ZetaAvatarRail); + final avatarFinder = find.byType(ZetaAvatar); + + expect(railFinder, findsOneWidget); + expect(avatarFinder, findsNWidgets(avatarList.length)); + + final railSize = tester.getSize(railFinder); + final avatarSize = tester.getSize(avatarFinder.first); + + if (railSize.width < screenWidth) { + expect(railSize.width, equals(avatarSize.width * avatarList.length + gap * (avatarList.length - 1))); + } else { + expect(railSize.width, equals(screenWidth)); + } + }); + + testWidgets('height is correct - avatar size: $size', (WidgetTester tester) async { + const gap = 10.0; + await tester.pumpWidget( + TestApp( + home: ZetaAvatarRail( + avatars: avatarList, + size: size, + gap: gap, + onTap: (key) {}, + ), + ), + ); + + final railFinder = find.byType(ZetaAvatarRail); + final avatarFinder = find.byType(ZetaAvatar); + + final railSize = tester.getSize(railFinder); + final avatarSize = tester.getSize(avatarFinder.first); + + expect(railSize.height, equals(avatarSize.height)); + }); + } + }); + + group('Styling Tests', () { + testWidgets('applies the correct text style to labels', (WidgetTester tester) async { + const textStyle = TextStyle(color: Colors.red, fontSize: 16); + await tester.pumpWidget( + TestApp( + home: ZetaAvatarRail( + avatars: avatarList, + size: ZetaAvatarSize.m, + labelTextStyle: textStyle, + onTap: (key) {}, + ), + ), + ); + + final labelFinder = find.text('Archie'); + final labelWidget = tester.widget(labelFinder); + + expect(labelWidget.style, equals(textStyle)); + }); + }); + + group('Interaction Tests', () { + final shortList = [ + const ZetaAvatar.initials( + key: Key('avatar1'), + initials: 'AZ', + label: 'Archie', + ), + const ZetaAvatar.initials( + key: Key('avatar2'), + initials: 'BY', + label: 'Beth', + ), + const ZetaAvatar.initials( + key: Key('avatar3'), + initials: 'CX', + label: 'Clara', + ), + ]; + testWidgets('onTap is called when an avatar is tapped', (WidgetTester tester) async { + var tapped = false; + + await tester.pumpWidget( + TestApp( + home: ZetaAvatarRail( + gap: 10, + avatars: shortList, + size: ZetaAvatarSize.m, + onTap: (key) { + tapped = true; + }, + ), + ), + ); + + final avatarFinder = find.byType(ZetaAvatar); + + // Use hitTestable to ensure the widget can receive pointer events + final hitTestableAvatarFinder = avatarFinder.hitTestable(); + + expect(tapped, equals(false)); + + await tester.tap(hitTestableAvatarFinder.first); + await tester.pumpAndSettle(); // Ensure the tap event is processed + + expect(tapped, equals(true)); + }); + + testWidgets('swipe functionality works', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: SizedBox( + height: 70, + width: 300, + child: ZetaAvatarRail( + avatars: avatarList, + size: ZetaAvatarSize.m, + onTap: (key) {}, + ), + ), + ), + ); + + final railFinder = find.byType(ZetaAvatarRail); + expect(railFinder, findsOneWidget); + + await tester.drag(railFinder, const Offset(-300, 0)); + await tester.pumpAndSettle(); + + // Check if the rail has been scrolled + final firstAvatarFinder = find.text('Archie'); + expect(tester.getTopRight(firstAvatarFinder).dx, lessThan(0.0)); + }); + }); + + group('Golden Tests', () { + goldenTest(goldenFile, ZetaAvatarRail(avatars: avatarList), 'zeta_avatar_rail_default'); + }); + + group('Performance Tests', () { + testWidgets( + 'renders within 5 frames', + (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaAvatarRail( + avatars: avatarList, + size: ZetaAvatarSize.m, + onTap: (key) {}, + ), + ), + ); + + final railFinder = find.byType(ZetaAvatarRail); + expect(railFinder, findsOneWidget); + + await tester.pumpAndSettle(); + final frameCount = tester.platformDispatcher.frameData.frameNumber; + expect(frameCount, lessThan(5)); + }, + skip: true, + ); // Skip this test as it is not stable + }); +} diff --git a/test/src/components/avatar/golden/zeta_avatar_rail_default.png b/test/src/components/avatar/golden/zeta_avatar_rail_default.png new file mode 100644 index 0000000000000000000000000000000000000000..1932c51cc3a33612255d4b8abdb033caf9ac0cac GIT binary patch literal 5696 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 literal 0 HcmV?d00001 diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_complete.png b/test/src/components/stepper/golden/stepper_horizontal_complete.png similarity index 100% rename from test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_complete.png rename to test/src/components/stepper/golden/stepper_horizontal_complete.png diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_incomplete.png b/test/src/components/stepper/golden/stepper_horizontal_incomplete.png similarity index 100% rename from test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_incomplete.png rename to test/src/components/stepper/golden/stepper_horizontal_incomplete.png diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_step_disabled.png b/test/src/components/stepper/golden/stepper_horizontal_step_disabled.png similarity index 100% rename from test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_horizontal_step_disabled.png rename to test/src/components/stepper/golden/stepper_horizontal_step_disabled.png diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_complete.png b/test/src/components/stepper/golden/stepper_vertical_complete.png similarity index 100% rename from test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_complete.png rename to test/src/components/stepper/golden/stepper_vertical_complete.png diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_incomplete.png b/test/src/components/stepper/golden/stepper_vertical_incomplete.png similarity index 100% rename from test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_incomplete.png rename to test/src/components/stepper/golden/stepper_vertical_incomplete.png diff --git a/test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_step_disabled.png b/test/src/components/stepper/golden/stepper_vertical_step_disabled.png similarity index 100% rename from test/src/components/ENTER_PARENT_FOLDER (e.g. button)/golden/stepper_vertical_step_disabled.png rename to test/src/components/stepper/golden/stepper_vertical_step_disabled.png diff --git a/test/src/components/stepper/stepper_test.dart b/test/src/components/stepper/stepper_test.dart index f8341f0c..8dd9a0bb 100644 --- a/test/src/components/stepper/stepper_test.dart +++ b/test/src/components/stepper/stepper_test.dart @@ -8,7 +8,7 @@ import '../../../test_utils/tolerant_comparator.dart'; import '../../../test_utils/utils.dart'; void main() { - const String parentFolder = 'ENTER_PARENT_FOLDER (e.g. button)'; + const String parentFolder = 'stepper'; const goldenFile = GoldenFiles(component: parentFolder); setUpAll(() { @@ -16,7 +16,7 @@ void main() { }); group('ZetaStepper Accessibility Tests', () { - testWidgets('Horizontal stepper meets accessibility requirements', (WidgetTester tester) async { + testWidgets('Horizontal stepper meets accessibility requirements', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); await tester.pumpWidget( TestApp(