diff --git a/.github/workflows/flutter-linter.yml b/.github/workflows/flutter-linter.yml index 5d5e8d5..44dba99 100644 --- a/.github/workflows/flutter-linter.yml +++ b/.github/workflows/flutter-linter.yml @@ -4,6 +4,8 @@ on: pull_request: branches: - main + paths: + - "flutter/**" jobs: build: diff --git a/.github/workflows/flutter-snapshot-tests.yml b/.github/workflows/flutter-snapshot-tests.yml new file mode 100644 index 0000000..5313b46 --- /dev/null +++ b/.github/workflows/flutter-snapshot-tests.yml @@ -0,0 +1,23 @@ +name: Flutter - Snapshot Tests + +on: + pull_request: + branches: + - main + paths: + - "flutter/craftd_widget/lib/presentation/ui/**" + +jobs: + build: + runs-on: macos-latest # Use macOS because I generated the golden images in my macoOS + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Flutter + uses: ./.github/actions/setup-flutter + + - name: Test - CraftD Widget + working-directory: flutter/craftd_widget + run: flutter test diff --git a/flutter/craftd_widget/fonts/Roboto-Regular.ttf b/flutter/craftd_widget/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000..7e3bb2f Binary files /dev/null and b/flutter/craftd_widget/fonts/Roboto-Regular.ttf differ diff --git a/flutter/craftd_widget/fonts/Roboto-SemiBold.ttf b/flutter/craftd_widget/fonts/Roboto-SemiBold.ttf new file mode 100644 index 0000000..3f34834 Binary files /dev/null and b/flutter/craftd_widget/fonts/Roboto-SemiBold.ttf differ diff --git a/flutter/craftd_widget/fonts/Roboto-SemiBoldItalic.ttf b/flutter/craftd_widget/fonts/Roboto-SemiBoldItalic.ttf new file mode 100644 index 0000000..132cca1 Binary files /dev/null and b/flutter/craftd_widget/fonts/Roboto-SemiBoldItalic.ttf differ diff --git a/flutter/craftd_widget/fonts/Roboto-Thin.ttf b/flutter/craftd_widget/fonts/Roboto-Thin.ttf new file mode 100644 index 0000000..6ee97b8 Binary files /dev/null and b/flutter/craftd_widget/fonts/Roboto-Thin.ttf differ diff --git a/flutter/craftd_widget/fonts/Roboto-ThinItalic.ttf b/flutter/craftd_widget/fonts/Roboto-ThinItalic.ttf new file mode 100644 index 0000000..0381198 Binary files /dev/null and b/flutter/craftd_widget/fonts/Roboto-ThinItalic.ttf differ diff --git a/flutter/craftd_widget/lib/presentation/ui/button/craftd_button.dart b/flutter/craftd_widget/lib/presentation/ui/button/craftd_button.dart index be330a9..78ef5bf 100644 --- a/flutter/craftd_widget/lib/presentation/ui/button/craftd_button.dart +++ b/flutter/craftd_widget/lib/presentation/ui/button/craftd_button.dart @@ -12,6 +12,17 @@ class CraftDButton extends StatelessWidget { @override Widget build(BuildContext context) { + if (buttonProperties.fillMaxSize == true) { + return SizedBox( + child: _buildButton(), + width: double.infinity, + ); + } else { + return _buildButton(); + } + } + + _buildButton() { return ElevatedButton( onPressed: () { callback(); diff --git a/flutter/craftd_widget/lib/presentation/ui/text/craftd_text.dart b/flutter/craftd_widget/lib/presentation/ui/text/craftd_text.dart index 8ccf856..279dc31 100644 --- a/flutter/craftd_widget/lib/presentation/ui/text/craftd_text.dart +++ b/flutter/craftd_widget/lib/presentation/ui/text/craftd_text.dart @@ -7,19 +7,25 @@ class CraftDText extends StatelessWidget { final TextProperties textProperties; final VoidCallback? callback; - const CraftDText( - {super.key, required this.textProperties, this.callback}); + const CraftDText({super.key, required this.textProperties, this.callback}); @override Widget build(BuildContext context) { + final String text = textProperties.textAllCaps == true + ? textProperties.text.toUpperCase() + : textProperties.text; return GestureDetector( - onTap: callback, - child: Text(textProperties.text, - style: TextStyle( - fontSize: textProperties.textSize != null - ? double.tryParse(textProperties.textSize!) - : 16.0, - color: CraftDColor.hexToColor(textProperties.textColorHex), - ))); + onTap: callback, + child: Text( + text, + style: TextStyle( + fontSize: textProperties.textSize != null + ? double.tryParse(textProperties.textSize!) + : 16.0, + color: CraftDColor.hexToColor(textProperties.textColorHex), + backgroundColor: CraftDColor.hexToColor(textProperties.backgroundHex), + ), + ), + ); } } diff --git a/flutter/craftd_widget/pubspec.yaml b/flutter/craftd_widget/pubspec.yaml index a37a37e..b22cd18 100644 --- a/flutter/craftd_widget/pubspec.yaml +++ b/flutter/craftd_widget/pubspec.yaml @@ -20,3 +20,11 @@ dev_dependencies: flutter_lints: ^4.0.0 flutter: + fonts: + - family: Roboto + fonts: + - asset: fonts/Roboto-Regular.ttf + - asset: fonts/Roboto-SemiBold.ttf + - asset: fonts/Roboto-SemiBoldItalic.ttf + - asset: fonts/Roboto-Thin.ttf + - asset: fonts/Roboto-ThinItalic.ttf diff --git a/flutter/craftd_widget/test/snapshot/craftd_button_snapshot_test.dart b/flutter/craftd_widget/test/snapshot/craftd_button_snapshot_test.dart new file mode 100644 index 0000000..c93de16 --- /dev/null +++ b/flutter/craftd_widget/test/snapshot/craftd_button_snapshot_test.dart @@ -0,0 +1,69 @@ +import 'package:craftd_widget/data/model/button/button_properties.dart'; +import 'package:craftd_widget/presentation/ui/button/craftd_button.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../util/widget_util.dart'; + +const defaultBackgroundHexColor = '#FFFFFF'; +const defaultTextHexColor = '#000000'; +const defaultTextSize = '32.0'; +const defaultFillMaxSize = false; +const defaultText = 'CodandoTV'; + +void main() { + testWidgets( + 'CraftD - Button with a fillMaxSize false', + (tester) async { + const buttonProperties = ButtonProperties( + text: defaultText, + textColorHex: defaultTextHexColor, + backgroundHex: defaultBackgroundHexColor, + textSize: defaultTextSize, + fillMaxSize: false, + ); + final widget = await WidgetsUtil.buildMaterialAppWidgetTest( + child: CraftDButton( + buttonProperties: buttonProperties, + callback: () => {}, + ), + tester: tester, + ); + + await tester.pumpWidget(widget); + + await expectLater( + find.byType(CraftDButton), + matchesGoldenFile( + 'goldens/craftd_button_fill_max_size_false_snapshot.png'), + ); + }, + ); + + testWidgets( + 'CraftD - Button with a fillMaxSize true', + (tester) async { + const buttonProperties = ButtonProperties( + text: defaultText, + textColorHex: defaultTextHexColor, + backgroundHex: defaultBackgroundHexColor, + textSize: defaultTextSize, + fillMaxSize: true, + ); + final widget = await WidgetsUtil.buildMaterialAppWidgetTest( + child: CraftDButton( + buttonProperties: buttonProperties, + callback: () => {}, + ), + tester: tester, + ); + + await tester.pumpWidget(widget); + + await expectLater( + find.byType(CraftDButton), + matchesGoldenFile( + 'goldens/craftd_button_fill_max_size_true_snapshot.png'), + ); + }, + ); +} diff --git a/flutter/craftd_widget/test/snapshot/craftd_empty_snapshot_test.dart b/flutter/craftd_widget/test/snapshot/craftd_empty_snapshot_test.dart new file mode 100644 index 0000000..d7d0962 --- /dev/null +++ b/flutter/craftd_widget/test/snapshot/craftd_empty_snapshot_test.dart @@ -0,0 +1,23 @@ +import 'package:craftd_widget/presentation/ui/empty/craftd_empty.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../util/widget_util.dart'; + +void main() { + testWidgets( + 'CraftD - Empty', + (tester) async { + final widget = await WidgetsUtil.buildMaterialAppWidgetTest( + child: const CraftDEmpty(), + tester: tester, + ); + + await tester.pumpWidget(widget); + + await expectLater( + find.byType(CraftDEmpty), + matchesGoldenFile('goldens/craftd_empty_snapshot.png'), + ); + }, + ); +} diff --git a/flutter/craftd_widget/test/snapshot/craftd_text_snapshot_test.dart b/flutter/craftd_widget/test/snapshot/craftd_text_snapshot_test.dart new file mode 100644 index 0000000..01cb875 --- /dev/null +++ b/flutter/craftd_widget/test/snapshot/craftd_text_snapshot_test.dart @@ -0,0 +1,146 @@ +import 'package:craftd_widget/data/model/text/text_properties.dart'; +import 'package:craftd_widget/presentation/ui/text/craftd_text.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../util/widget_util.dart'; + +const defaultBackgroundHexColor = '#FFFFFF'; +const defaultTextHexColor = '#000000'; +const defaultTextSize = '32.0'; +const defaultTextAllCaps = false; +const defaultText = 'CodandoTV'; + +void main() { + testWidgets( + 'CraftD - Text with allcaps false', + (tester) async { + const textProperties = TextProperties( + text: defaultText, + textColorHex: defaultTextHexColor, + backgroundHex: defaultBackgroundHexColor, + textSize: defaultTextSize, + textAllCaps: false, + ); + final widget = await WidgetsUtil.buildMaterialAppWidgetTest( + child: CraftDText( + textProperties: textProperties, + callback: () => {}, + ), + tester: tester, + ); + + await tester.pumpWidget(widget); + + await expectLater( + find.byType(CraftDText), + matchesGoldenFile('goldens/craftd_text_allcaps_false_snapshot.png'), + ); + }, + ); + + testWidgets( + 'CraftD - Text with allcaps true', + (tester) async { + const textProperties = TextProperties( + text: defaultText, + textColorHex: defaultTextHexColor, + backgroundHex: defaultBackgroundHexColor, + textSize: defaultTextSize, + textAllCaps: true, + ); + final widget = await WidgetsUtil.buildMaterialAppWidgetTest( + child: CraftDText( + textProperties: textProperties, + callback: () => {}, + ), + tester: tester, + ); + + await tester.pumpWidget(widget); + + await expectLater( + find.byType(CraftDText), + matchesGoldenFile('goldens/craftd_text_allcaps_true_snapshot.png'), + ); + }, + ); + + testWidgets( + 'CraftD - Text with orange text color', + (tester) async { + const textProperties = TextProperties( + text: defaultText, + backgroundHex: defaultBackgroundHexColor, + textSize: defaultTextSize, + textAllCaps: defaultTextAllCaps, + textColorHex: '#FFA500', + ); + final widget = await WidgetsUtil.buildMaterialAppWidgetTest( + child: CraftDText( + textProperties: textProperties, + callback: () => {}, + ), + tester: tester, + ); + + await tester.pumpWidget(widget); + + await expectLater( + find.byType(CraftDText), + matchesGoldenFile('goldens/craftd_text_with_orange_text_color_snapshot.png'), + ); + }, + ); + + testWidgets( + 'CraftD - Text with orange background color', + (tester) async { + const textProperties = TextProperties( + text: defaultText, + textColorHex: defaultTextHexColor, + textSize: defaultTextSize, + backgroundHex: '#FFA500', + ); + final widget = await WidgetsUtil.buildMaterialAppWidgetTest( + child: CraftDText( + textProperties: textProperties, + callback: () => {}, + ), + tester: tester, + ); + + await tester.pumpWidget(widget); + + await expectLater( + find.byType(CraftDText), + matchesGoldenFile('goldens/craftd_text_with_orange_background_color_snapshot.png'), + ); + }, + ); + + testWidgets( + 'CraftD - Text with a big font size', + (tester) async { + const textProperties = TextProperties( + textSize: '80.0', + text: defaultText, + textColorHex: defaultTextHexColor, + backgroundHex: defaultBackgroundHexColor, + ); + final widget = await WidgetsUtil.buildMaterialAppWidgetTest( + child: CraftDText( + textProperties: textProperties, + callback: () => {}, + ), + tester: tester, + ); + + await tester.pumpWidget(widget); + + await expectLater( + find.byType(CraftDText), + matchesGoldenFile('goldens/craftd_text_with_big_font_size_snapshot.png'), + ); + }, + ); +} diff --git a/flutter/craftd_widget/test/snapshot/goldens/craftd_button_fill_max_size_false_snapshot.png b/flutter/craftd_widget/test/snapshot/goldens/craftd_button_fill_max_size_false_snapshot.png new file mode 100644 index 0000000..61684a5 Binary files /dev/null and b/flutter/craftd_widget/test/snapshot/goldens/craftd_button_fill_max_size_false_snapshot.png differ diff --git a/flutter/craftd_widget/test/snapshot/goldens/craftd_button_fill_max_size_true_snapshot.png b/flutter/craftd_widget/test/snapshot/goldens/craftd_button_fill_max_size_true_snapshot.png new file mode 100644 index 0000000..133dfa5 Binary files /dev/null and b/flutter/craftd_widget/test/snapshot/goldens/craftd_button_fill_max_size_true_snapshot.png differ diff --git a/flutter/craftd_widget/test/snapshot/goldens/craftd_empty_snapshot.png b/flutter/craftd_widget/test/snapshot/goldens/craftd_empty_snapshot.png new file mode 100644 index 0000000..7ba9fee Binary files /dev/null and b/flutter/craftd_widget/test/snapshot/goldens/craftd_empty_snapshot.png differ diff --git a/flutter/craftd_widget/test/snapshot/goldens/craftd_text_allcaps_false_snapshot.png b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_allcaps_false_snapshot.png new file mode 100644 index 0000000..ec27a65 Binary files /dev/null and b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_allcaps_false_snapshot.png differ diff --git a/flutter/craftd_widget/test/snapshot/goldens/craftd_text_allcaps_true_snapshot.png b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_allcaps_true_snapshot.png new file mode 100644 index 0000000..1be00c3 Binary files /dev/null and b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_allcaps_true_snapshot.png differ diff --git a/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_big_font_size_snapshot.png b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_big_font_size_snapshot.png new file mode 100644 index 0000000..3e976d2 Binary files /dev/null and b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_big_font_size_snapshot.png differ diff --git a/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_orange_background_color_snapshot.png b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_orange_background_color_snapshot.png new file mode 100644 index 0000000..309513d Binary files /dev/null and b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_orange_background_color_snapshot.png differ diff --git a/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_orange_text_color_snapshot.png b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_orange_text_color_snapshot.png new file mode 100644 index 0000000..79218b2 Binary files /dev/null and b/flutter/craftd_widget/test/snapshot/goldens/craftd_text_with_orange_text_color_snapshot.png differ diff --git a/flutter/craftd_widget/test/util/widget_util.dart b/flutter/craftd_widget/test/util/widget_util.dart new file mode 100644 index 0000000..a744953 --- /dev/null +++ b/flutter/craftd_widget/test/util/widget_util.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class WidgetsUtil { + static buildMaterialAppWidgetTest({ + required Widget child, + required WidgetTester tester, + }) async { + await _loadFont(); + + TestWidgetsFlutterBinding.ensureInitialized(); + + const baseColor = Color.fromARGB(255, 60, 6, 194); + _setupDeviceConstraintsForSnapshotTests(tester); + + return MaterialApp( + home: Scaffold( + body: Center( + child: child, + ), + ), + themeMode: ThemeMode.light, + darkTheme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: baseColor, + brightness: Brightness.light, + ), + ), + ); + } + + static _loadFont() async { + final fonts = [ + 'fonts/Roboto-Regular.ttf', + 'fonts/Roboto-SemiBold.ttf', + 'fonts/Roboto-SemiBoldItalic.ttf', + 'fonts/Roboto-Thin.ttf', + 'fonts/Roboto-ThinItalic.ttf', + ]; + final fontLoader = FontLoader('Roboto'); + for (final font in fonts) { + final fontData = await rootBundle.load(font); + fontLoader.addFont(Future.value(fontData)); + } + await fontLoader.load(); + } + + static _setupDeviceConstraintsForSnapshotTests(WidgetTester tester) { + tester.view.physicalSize = const Size(360, 780); + tester.view.devicePixelRatio = 1.0; + + addTearDown(() { + tester.view.resetPhysicalSize(); + tester.view.resetDevicePixelRatio(); + tester.binding.platformDispatcher.clearTextScaleFactorTestValue(); + }); + } +}