Skip to content

Commit

Permalink
added widget and unit tests (#93)
Browse files Browse the repository at this point in the history
* - initial tests for utils and theme

* - 100% test coverage for theme, utils, Zeta and ZetaProvider

* - updated ios project

* - removed unused file
- updated github actions to run tests

* - added MockSpec

* - added testing guide on golden and unit tests

* - added TolerantComparator

* [automated commit] lint format and import sort

* - tests for Tooltip debugFillProperties

---------

Co-authored-by: github-actions <[email protected]>
  • Loading branch information
ps9310 and github-actions authored Jun 14, 2024
1 parent 868b26c commit 66a0037
Show file tree
Hide file tree
Showing 46 changed files with 4,383 additions and 362 deletions.
1 change: 1 addition & 0 deletions .github/workflows/on-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
dart format . -l 120
dart fix --apply
flutter analyze
flutter test
cd example && flutter test
- name: Check for modified files
id: git-check
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ jobs:
dart format . -l 120
dart fix --apply
flutter analyze
flutter test
cd example && flutter test
- name: Check for modified files
id: git-check
Expand Down
121 changes: 121 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,127 @@ For more information on widgetbook, [read the docs](https://docs.widgetbook.io/)

We should also create a test for each widget created.

### Contributing Guide for Writing Tests

#### Folder Structure

To maintain consistency and ease of navigation, follow the same folder structure in the `test` directory as in the `lib` directory. For example, if you have a file located at `lib/src/components/tooltip/tooltip.dart`, your test file should be located at `test/src/components/tooltip/tooltip_test.dart`.

##### Example Folder Structure

```
lib/
└── src/
└── components/
└── tooltip/
└── tooltip.dart
test/
└── src/
└── components/
└── tooltip/
└── tooltip_test.dart
```

#### Writing Tests

1. **Unit Tests**: Test individual functions and classes.
2. **Widget Tests**: Test individual widgets and their interactions.
3. **Integration Tests**: Test the complete app or large parts of it.

##### Guidelines

- Use descriptive test names.
- Test one thing per test.
- Mock dependencies using `mockito`.
- Write tests for edge cases.

#### Measuring Code Coverage

To ensure high code coverage (at least > 96%), use the following steps:

##### With 'lcov'
1. **Run the script coverage.sh from the project root, which make use of `lcov` to generate the coverage report**:
```sh
sh coverage.sh
```
##### Alternatively, with 'genhtml'
1. **Install dependencies**:
```sh
flutter pub add --dev test coverage
```

2. **Run tests with coverage**:
```sh
flutter test --coverage
```

3. **Generate coverage report**:
```sh
genhtml coverage/lcov.info -o coverage/html
```

4. **View coverage report**:
Open `coverage/html/index.html` in a web browser.


##### Maximizing Coverage

- Write tests for all public methods and classes.
- Include tests for edge cases and error handling.
- Avoid excluding files from coverage unless necessary.

#### Golden Tests

Golden tests are used to ensure that the visual output of your widgets remains consistent over time. They are particularly useful for complex UI components.

##### Why Golden Tests?

In the `tooltip` example, the direction of the arrows in the tooltip is crucial. Golden tests help to ensure that any changes to the tooltip do not unintentionally alter the direction of the arrows or other visual aspects.

##### Adding Golden Tests

1. **Set up golden tests**:
```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:zeta_flutter/zeta_flutter.dart';
import '../../../test_utils/test_app.dart';
void main() {
testWidgets('renders with arrow correctly in up direction', (WidgetTester tester) async {
await tester.pumpWidget(
const TestApp(
home: Scaffold(
body: ZetaTooltip(
arrowDirection: ZetaTooltipArrowDirection.up,
child: Text('Tooltip up'),
),
),
),
);
expect(find.text('Tooltip up'), findsOneWidget);
// Verifying the CustomPaint with different arrow directions.
await expectLater(
find.byType(ZetaTooltip),
matchesGoldenFile(p.join('golden', 'arrow_up.png')),
);
});
}
```

2. **Run golden tests**:
```sh
flutter test --update-goldens
```

3. **Verify golden tests**:
Ensure that the generated golden files are correct and commit them to the repository.



## Code reviews

All submissions, including submissions by project members, require review. We use GitHub pull requests (PRs) for this purpose. Consult [GitHub Help](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) for more information on using pull requests.
Expand Down
31 changes: 31 additions & 0 deletions coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

# Check if lcov is installed
if ! command -v lcov &> /dev/null
then
echo "lcov could not be found, please install it first."
exit
fi

# Run the tests with coverage
flutter test --coverage

# Generate the LCOV report
lcov --capture --directory coverage --output-file coverage/lcov.info

# Remove unnecessary files from the report
lcov --remove coverage/lcov.info 'lib/*/*.g.dart' 'lib/*/*.freezed.dart' -o coverage/lcov.info

# Generate the HTML report
genhtml coverage/lcov.info --output-directory coverage/html

# Open the coverage report in the default browser
if [ "$(uname)" == "Darwin" ]; then
open coverage/html/index.html
elif [ "$(uname)" == "Linux" ]; then
xdg-open coverage/html/index.html
elif [ "$(uname)" == "CYGWIN" ] || [ "$(uname)" == "MINGW32" ] || [ "$(uname)" == "MINGW64" ]; then
start coverage/html/index.html
fi

echo "Coverage report generated and opened in the default browser."
2 changes: 1 addition & 1 deletion example/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
23 changes: 18 additions & 5 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite (0.0.3):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter

DEPENDENCIES:
- Flutter (from `Flutter`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)

EXTERNAL SOURCES:
Flutter:
Expand All @@ -19,12 +26,18 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite:
:path: ".symlinks/plugins/sqflite/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"

SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe

PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011

COCOAPODS: 1.14.3
COCOAPODS: 1.15.2
8 changes: 4 additions & 4 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
Expand Down Expand Up @@ -343,7 +343,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -421,7 +421,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -470,7 +470,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
6 changes: 3 additions & 3 deletions example/lib/utils/theme_color_switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ class ZetaThemeColorSwitch extends StatelessWidget {
return DropdownMenuItem<String>(
value: e.value.identifier,
alignment: Alignment.center,
child: CircleAvatar(
child: ZetaAvatar(
size: ZetaAvatarSize.xxs,
backgroundColor: color.surface,
foregroundColor: color,
child: Icon(Icons.color_lens, color: color),
image: Icon(Icons.color_lens, color: color),
),
);
}).toList(),
Expand Down
12 changes: 7 additions & 5 deletions example/lib/utils/theme_constrast_switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ class ZetaThemeContrastSwitch extends StatelessWidget {
return DropdownMenuItem<ZetaContrast>(
value: e,
alignment: Alignment.center,
child: CircleAvatar(
child: ZetaAvatar(
size: ZetaAvatarSize.xxs,
backgroundColor: colors.primary.surface,
foregroundColor: colors.primary,
child: Text(
e == ZetaContrast.aa ? 'AA' : 'AAA',
style: ZetaTextStyles.bodyMedium.copyWith(color: colors.primary, fontWeight: FontWeight.w700),
image: Center(
child: Text(
e == ZetaContrast.aa ? 'AA' : 'AAA',
style: ZetaTextStyles.bodyMedium.copyWith(color: colors.primary, fontWeight: FontWeight.w700),
),
),
),
);
Expand Down
6 changes: 3 additions & 3 deletions example/lib/utils/theme_mode_switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ class ZetaThemeModeSwitch extends StatelessWidget {
return DropdownMenuItem<ThemeMode>(
value: e,
alignment: Alignment.center,
child: CircleAvatar(
child: ZetaAvatar(
size: ZetaAvatarSize.xxs,
backgroundColor: colors.primary.surface,
foregroundColor: colors.primary,
child: Icon(
image: Icon(
e == ThemeMode.system
? Icons.system_security_update_good
: e == ThemeMode.light
Expand Down
Binary file added golden/arrow_down.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added golden/arrow_left.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added golden/arrow_right.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added golden/arrow_up.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 19 additions & 12 deletions lib/src/theme/color_swatch.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';

import 'color_extensions.dart';
Expand All @@ -6,7 +7,7 @@ import 'contrast.dart';

/// A swatch of colors with values from 10 (light) to 100 (dark).
@immutable
class ZetaColorSwatch extends ColorSwatch<int> {
class ZetaColorSwatch extends ColorSwatch<int> with EquatableMixin {
/// Constructs a [ZetaColorSwatch].
///
/// See also:
Expand Down Expand Up @@ -142,7 +143,8 @@ class ZetaColorSwatch extends ColorSwatch<int> {

/// Returns the color shade for a subtle visual element depending on the ZetaContrast value.
///
/// For both [ZetaContrast.aa] and [ZetaContrast.aaa], it returns 40.
/// For [ZetaContrast.aa], it returns 40.
/// For [ZetaContrast.aaa], it returns 60.
Color get subtle => shade(contrast.subtle);

/// Returns the color shade for a surface depending on the ZetaContrast value.
Expand Down Expand Up @@ -185,14 +187,19 @@ class ZetaColorSwatch extends ColorSwatch<int> {
}

@override
bool operator ==(Object other) =>
identical(this, other) ||
super == other &&
other is ZetaColorSwatch &&
runtimeType == other.runtimeType &&
brightness == other.brightness &&
contrast == other.contrast;

@override
int get hashCode => super.hashCode ^ brightness.hashCode ^ contrast.hashCode;
List<Object?> get props => [
super.value,
brightness,
contrast,
shade10,
shade20,
shade30,
shade40,
shade50,
shade60,
shade70,
shade80,
shade90,
shade100,
];
}
Loading

0 comments on commit 66a0037

Please sign in to comment.