From 2e99bfc92bf15f28144e46d69dbf48eef43a0b83 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Wed, 17 Jul 2024 18:45:40 -0400 Subject: [PATCH 1/4] Improved tests --- .../attributes/border/border_radius_dto.dart | 19 +-- .../border/border_radius_dto_test.dart | 120 +++++++++++++--- .../border/shape_border_dto_test.dart | 132 ++++++++++++++++++ 3 files changed, 240 insertions(+), 31 deletions(-) diff --git a/packages/mix/lib/src/attributes/border/border_radius_dto.dart b/packages/mix/lib/src/attributes/border/border_radius_dto.dart index 20f42482f..1a84bb4e9 100644 --- a/packages/mix/lib/src/attributes/border/border_radius_dto.dart +++ b/packages/mix/lib/src/attributes/border/border_radius_dto.dart @@ -24,7 +24,8 @@ sealed class BorderRadiusGeometryDto extends Dto with Diagnosticable { const BorderRadiusGeometryDto(); - Radius _getRadiusValue(MixData mix, Radius? radius) { + @visibleForTesting + Radius getRadiusValue(MixData mix, Radius? radius) { if (radius == null) return Radius.zero; return radius is RadiusRef ? mix.tokens.radiiRef(radius) : radius; @@ -81,10 +82,10 @@ final class BorderRadiusDto extends BorderRadiusGeometryDto @override BorderRadius resolve(MixData mix) { return BorderRadius.only( - topLeft: _getRadiusValue(mix, topLeft), - topRight: _getRadiusValue(mix, topRight), - bottomLeft: _getRadiusValue(mix, bottomLeft), - bottomRight: _getRadiusValue(mix, bottomRight), + topLeft: getRadiusValue(mix, topLeft), + topRight: getRadiusValue(mix, topRight), + bottomLeft: getRadiusValue(mix, bottomLeft), + bottomRight: getRadiusValue(mix, bottomRight), ); } @@ -124,10 +125,10 @@ final class BorderRadiusDirectionalDto @override BorderRadiusDirectional resolve(MixData mix) { return BorderRadiusDirectional.only( - topStart: _getRadiusValue(mix, topStart), - topEnd: _getRadiusValue(mix, topEnd), - bottomStart: _getRadiusValue(mix, bottomStart), - bottomEnd: _getRadiusValue(mix, bottomEnd), + topStart: getRadiusValue(mix, topStart), + topEnd: getRadiusValue(mix, topEnd), + bottomStart: getRadiusValue(mix, bottomStart), + bottomEnd: getRadiusValue(mix, bottomEnd), ); } diff --git a/packages/mix/test/src/attributes/border/border_radius_dto_test.dart b/packages/mix/test/src/attributes/border/border_radius_dto_test.dart index a016dab2c..a550188e6 100644 --- a/packages/mix/test/src/attributes/border/border_radius_dto_test.dart +++ b/packages/mix/test/src/attributes/border/border_radius_dto_test.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; @@ -15,10 +16,10 @@ void main() { ); const attr2 = BorderRadiusDto( - topLeft: Radius.circular(5.0), - topRight: Radius.circular(10.0), - bottomLeft: Radius.circular(15.0), - bottomRight: Radius.circular(20.0), + topLeft: Radius.circular(25.0), + topRight: Radius.circular(30.0), + bottomLeft: Radius.circular(35.0), + bottomRight: Radius.circular(40.0), ); final merged = attr1.merge(attr2); @@ -38,6 +39,7 @@ void main() { ); const borderRadius2 = BorderRadiusDto( topLeft: Radius.circular(20), + bottomRight: Radius.circular(50), ); final mergedBorderRadius = borderRadius1.merge(borderRadius2); @@ -45,7 +47,7 @@ void main() { expect(mergedBorderRadius.topLeft, const Radius.circular(20)); expect(mergedBorderRadius.topRight, const Radius.circular(40)); expect(mergedBorderRadius.bottomLeft, const Radius.circular(10)); - expect(mergedBorderRadius.bottomRight, const Radius.circular(20)); + expect(mergedBorderRadius.bottomRight, const Radius.circular(50)); }); test('resolve should create a BorderRadius with the correct values', () { @@ -58,10 +60,14 @@ void main() { final resolvedBorderRadius = borderRadius.resolve(EmptyMixData); - expect(resolvedBorderRadius.topLeft, const Radius.circular(10)); - expect(resolvedBorderRadius.topRight, const Radius.circular(20)); - expect(resolvedBorderRadius.bottomLeft, const Radius.circular(30)); - expect(resolvedBorderRadius.bottomRight, const Radius.circular(40)); + expect( + resolvedBorderRadius, + const BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(20), + bottomLeft: Radius.circular(30), + bottomRight: Radius.circular(40), + )); }); test('Equality holds when properties are the same', () { @@ -78,6 +84,7 @@ void main() { bottomRight: Radius.circular(20.0), ); expect(attr1, attr2); + expect(attr1.hashCode, attr2.hashCode); }); test('Equality fails when properties are different', () { @@ -95,6 +102,7 @@ void main() { bottomRight: Radius.circular(25.0), ); expect(attr1, isNot(attr2)); + expect(attr1.hashCode, isNot(attr2.hashCode)); }); }); @@ -108,10 +116,10 @@ void main() { ); const attr2 = BorderRadiusDirectionalDto( - topStart: Radius.circular(5.0), - topEnd: Radius.circular(10.0), - bottomStart: Radius.circular(15.0), - bottomEnd: Radius.circular(20.0), + topStart: Radius.circular(25.0), + topEnd: Radius.circular(30.0), + bottomStart: Radius.circular(35.0), + bottomEnd: Radius.circular(40.0), ); final merged = attr1.merge(attr2); @@ -131,6 +139,7 @@ void main() { ); const borderRadius2 = BorderRadiusDirectionalDto( topStart: Radius.circular(20), + bottomEnd: Radius.circular(30), ); final mergedBorderRadius = borderRadius1.merge(borderRadius2); @@ -138,10 +147,12 @@ void main() { expect(mergedBorderRadius.topStart, const Radius.circular(20)); expect(mergedBorderRadius.topEnd, const Radius.circular(10)); expect(mergedBorderRadius.bottomStart, const Radius.circular(10)); - expect(mergedBorderRadius.bottomEnd, const Radius.circular(10)); + expect(mergedBorderRadius.bottomEnd, const Radius.circular(30)); }); - test('resolve should create a BorderRadius with the correct values', () { + test( + 'resolve should create a BorderRadiusDirectional with the correct values', + () { const borderRadius = BorderRadiusDirectionalDto( topStart: Radius.circular(10), topEnd: Radius.circular(20), @@ -151,10 +162,14 @@ void main() { final resolvedBorderRadius = borderRadius.resolve(EmptyMixData); - expect(resolvedBorderRadius.topStart, const Radius.circular(10)); - expect(resolvedBorderRadius.topEnd, const Radius.circular(20)); - expect(resolvedBorderRadius.bottomStart, const Radius.circular(30)); - expect(resolvedBorderRadius.bottomEnd, const Radius.circular(40)); + expect( + resolvedBorderRadius, + const BorderRadiusDirectional.only( + topStart: Radius.circular(10), + topEnd: Radius.circular(20), + bottomStart: Radius.circular(30), + bottomEnd: Radius.circular(40), + )); }); test('Equality holds when properties are the same', () { @@ -171,6 +186,7 @@ void main() { bottomEnd: Radius.circular(20.0), ); expect(attr1, attr2); + expect(attr1.hashCode, attr2.hashCode); }); test('Equality fails when properties are different', () { @@ -188,6 +204,7 @@ void main() { bottomEnd: Radius.circular(25.0), ); expect(attr1, isNot(attr2)); + expect(attr1.hashCode, isNot(attr2.hashCode)); }); }); @@ -209,9 +226,13 @@ void main() { width: 1.0, ); final borderSide = attr.resolve(EmptyMixData); - expect(borderSide.color, Colors.red); - expect(borderSide.width, 1.0); - expect(borderSide.style, BorderStyle.solid); + expect( + borderSide, + const BorderSide( + color: Colors.red, + width: 1.0, + style: BorderStyle.solid, + )); }); test('Equality holds when all attributes are the same', () { final attr1 = BorderSideDto( @@ -225,6 +246,7 @@ void main() { width: 1.0, ); expect(attr1, attr2); + expect(attr1.hashCode, attr2.hashCode); }); test('Equality fails when attributes are different', () { final attr1 = BorderSideDto( @@ -238,6 +260,60 @@ void main() { width: 1.0, ); expect(attr1, isNot(attr2)); + expect(attr1.hashCode, isNot(attr2.hashCode)); + }); + }); + group('BorderRadiusGeometryDto', () { + test('getRadiusValue returns Radius.zero when radius is null', () { + const dto = BorderRadiusDto(); + final radius = dto.getRadiusValue(EmptyMixData, null); + expect(radius, Radius.zero); + }); + + test('debugFillProperties adds all properties', () { + const dto = BorderRadiusDto( + topLeft: Radius.circular(1), + topRight: Radius.circular(2), + bottomLeft: Radius.circular(3), + bottomRight: Radius.circular(4), + ); + final properties = DiagnosticPropertiesBuilder(); + dto.debugFillProperties(properties); + expect(properties.properties.length, 8); + expect(properties.properties[0].name, 'topLeft'); + expect(properties.properties[1].name, 'topRight'); + expect(properties.properties[2].name, 'bottomLeft'); + expect(properties.properties[3].name, 'bottomRight'); + expect(properties.properties[4].name, 'topStart'); + expect(properties.properties[5].name, 'topEnd'); + expect(properties.properties[6].name, 'bottomStart'); + expect(properties.properties[7].name, 'bottomEnd'); + }); + }); + + group('BorderRadiusDirectionalDto', () { + test('resolve returns BorderRadiusDirectional with null values', () { + const dto = BorderRadiusDirectionalDto(); + final resolved = dto.resolve(EmptyMixData); + expect(resolved, BorderRadiusDirectional.zero); + }); + + test('defaultValue returns BorderRadiusDirectional.zero', () { + const dto = BorderRadiusDirectionalDto(); + expect(dto.defaultValue, BorderRadiusDirectional.zero); + }); + + test('topLeft, topRight, bottomLeft, and bottomRight are always null', () { + const dto = BorderRadiusDirectionalDto( + topStart: Radius.circular(1), + topEnd: Radius.circular(2), + bottomStart: Radius.circular(3), + bottomEnd: Radius.circular(4), + ); + expect(dto.topLeft, isNull); + expect(dto.topRight, isNull); + expect(dto.bottomLeft, isNull); + expect(dto.bottomRight, isNull); }); }); } diff --git a/packages/mix/test/src/attributes/border/shape_border_dto_test.dart b/packages/mix/test/src/attributes/border/shape_border_dto_test.dart index e394e7772..eeed06870 100644 --- a/packages/mix/test/src/attributes/border/shape_border_dto_test.dart +++ b/packages/mix/test/src/attributes/border/shape_border_dto_test.dart @@ -448,4 +448,136 @@ void main() { expect(mergedDto, equals(stadiumBorderDto)); }); }); + + // Additional tests for StarBorderDto + group('StarBorderDto', () { + test('from factory method should create StarBorderDto from StarBorder', () { + const starBorder = StarBorder( + side: BorderSide(color: Colors.teal), + points: 5, + innerRadiusRatio: 0.5, + pointRounding: 0.2, + valleyRounding: 0.1, + rotation: 0.3, + squash: 0.4, + ); + + final starBorderDto = starBorder.toDto(); + + expect(starBorderDto.side, + equals(const BorderSide(color: Colors.teal).toDto())); + expect(starBorderDto.points, equals(5)); + expect(starBorderDto.innerRadiusRatio, equals(0.5)); + expect(starBorderDto.pointRounding, equals(0.2)); + expect(starBorderDto.valleyRounding, equals(0.1)); + expect(starBorderDto.rotation, equals(0.3)); + expect(starBorderDto.squash, equals(0.4)); + }); + + test('merge method should handle null values correctly', () { + final starBorderDto = StarBorderDto( + side: const BorderSide(color: Colors.teal).toDto(), + points: 5, + innerRadiusRatio: 0.5, + pointRounding: 0.2, + valleyRounding: 0.1, + rotation: 0.3, + squash: 0.4, + ); + + final mergedDto = starBorderDto.merge(null); + + expect(mergedDto, equals(starBorderDto)); + }); + }); + + // Additional tests for LinearBorderDto + group('LinearBorderDto', () { + test('from factory method should create LinearBorderDto from LinearBorder', + () { + const linearBorder = LinearBorder( + side: BorderSide(color: Colors.brown), + start: LinearBorderEdge(size: 0.1, alignment: 0.1), + end: LinearBorderEdge(size: 0.2, alignment: 0.2), + top: LinearBorderEdge(size: 0.3, alignment: 0.3), + bottom: LinearBorderEdge(size: 0.4, alignment: 0.4), + ); + + final linearBorderDto = linearBorder.toDto(); + + expect(linearBorderDto.side, + equals(const BorderSide(color: Colors.brown).toDto())); + expect(linearBorderDto.start, + equals(const LinearBorderEdge(size: 0.1, alignment: 0.1).toDto())); + expect(linearBorderDto.end, + equals(const LinearBorderEdge(size: 0.2, alignment: 0.2).toDto())); + expect(linearBorderDto.top, + equals(const LinearBorderEdge(size: 0.3, alignment: 0.3).toDto())); + expect(linearBorderDto.bottom, + equals(const LinearBorderEdge(size: 0.4, alignment: 0.4).toDto())); + }); + + test('merge method should handle null values correctly', () { + final linearBorderDto = LinearBorderDto( + side: const BorderSide(color: Colors.brown).toDto(), + start: const LinearBorderEdge(size: 0.1, alignment: 0.1).toDto(), + end: const LinearBorderEdge(size: 0.2, alignment: 0.2).toDto(), + top: const LinearBorderEdge(size: 0.3, alignment: 0.3).toDto(), + bottom: const LinearBorderEdge(size: 0.4, alignment: 0.4).toDto(), + ); + + final mergedDto = linearBorderDto.merge(null); + + expect(mergedDto, equals(linearBorderDto)); + }); + + // Additional tests for LinearBorderEdgeDto + group('LinearBorderEdgeDto', () { + test( + 'from factory method should create LinearBorderEdgeDto from LinearBorderEdge', + () { + const linearBorderEdge = LinearBorderEdge(size: 1.0, alignment: 0.1); + + final linearBorderEdgeDto = linearBorderEdge.toDto(); + + expect(linearBorderEdgeDto.size, equals(1.0)); + expect(linearBorderEdgeDto.alignment, equals(0.1)); + }); + + test('merge method should handle null values correctly', () { + const linearBorderEdgeDto = + LinearBorderEdgeDto(size: 1.0, alignment: 0.1); + + final mergedDto = linearBorderEdgeDto.merge(null); + + expect(mergedDto, equals(linearBorderEdgeDto)); + }); + + // test equality + test('== should return true if two LinearBorderEdgeDto are equal', () { + const linearBorderEdgeDto1 = + LinearBorderEdgeDto(size: 1.0, alignment: 0.1); + const linearBorderEdgeDto2 = + LinearBorderEdgeDto(size: 1.0, alignment: 0.1); + + expect(linearBorderEdgeDto1, equals(linearBorderEdgeDto2)); + }); + }); + + // Additional tests for ShapeBorderUtility + group('ShapeBorderUtility', () { + test('should create utility instances for each shape border type', () { + final shapeBorderUtility = ShapeBorderUtility(UtilityTestAttribute.new); + + expect(shapeBorderUtility.roundedRectangle, + isA()); + expect(shapeBorderUtility.circle, isA()); + expect(shapeBorderUtility.beveledRectangle, + isA()); + expect(shapeBorderUtility.stadium, isA()); + expect(shapeBorderUtility.continuousRectangle, + isA()); + }); + }); + }); } From a1940d438e73be0b45603099e699c951531db5c4 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Wed, 17 Jul 2024 18:46:41 -0400 Subject: [PATCH 2/4] Linting --- .../src/attributes/border/border_radius_dto.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/mix/lib/src/attributes/border/border_radius_dto.dart b/packages/mix/lib/src/attributes/border/border_radius_dto.dart index 1a84bb4e9..480952253 100644 --- a/packages/mix/lib/src/attributes/border/border_radius_dto.dart +++ b/packages/mix/lib/src/attributes/border/border_radius_dto.dart @@ -24,13 +24,6 @@ sealed class BorderRadiusGeometryDto extends Dto with Diagnosticable { const BorderRadiusGeometryDto(); - @visibleForTesting - Radius getRadiusValue(MixData mix, Radius? radius) { - if (radius == null) return Radius.zero; - - return radius is RadiusRef ? mix.tokens.radiiRef(radius) : radius; - } - Radius? get topLeft; Radius? get topRight; Radius? get bottomLeft; @@ -39,6 +32,13 @@ sealed class BorderRadiusGeometryDto Radius? get topEnd; Radius? get bottomStart; Radius? get bottomEnd; + @visibleForTesting + Radius getRadiusValue(MixData mix, Radius? radius) { + if (radius == null) return Radius.zero; + + return radius is RadiusRef ? mix.tokens.radiiRef(radius) : radius; + } + @override BorderRadiusGeometryDto merge(covariant BorderRadiusGeometryDto? other); From 589d7bcb12d7fc4a76e2b784da74c5caa9ed3c11 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Thu, 18 Jul 2024 09:37:41 -0400 Subject: [PATCH 3/4] Scalar test --- .../attributes/scalars/scalar_util_test.dart | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/packages/mix/test/src/attributes/scalars/scalar_util_test.dart b/packages/mix/test/src/attributes/scalars/scalar_util_test.dart index bd2ddba3f..3fcc7c848 100644 --- a/packages/mix/test/src/attributes/scalars/scalar_util_test.dart +++ b/packages/mix/test/src/attributes/scalars/scalar_util_test.dart @@ -1,3 +1,6 @@ +import 'dart:io'; +import 'dart:typed_data'; + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; @@ -490,4 +493,70 @@ void main() { expect(utility.wavy().value, TextDecorationStyle.wavy); }); }); + + group('ImageProviderUtility Tests', () { + const utility = ImageProviderUtility(UtilityTestAttribute.new); + + test('network() returns correct instance', () { + final imageProvider = utility.network('https://example.com/image.png'); + + expect(imageProvider.value, isA()); + expect((imageProvider.value as NetworkImage).url, + 'https://example.com/image.png'); + }); + + test('file() returns correct instance', () { + final file = File('path/to/image.png'); + final imageProvider = utility.file(file); + + expect(imageProvider.value, isA()); + expect((imageProvider.value as FileImage).file, file); + }); + + test('asset() returns correct instance', () { + final imageProvider = utility.asset('assets/image.png'); + + expect(imageProvider.value, isA()); + expect((imageProvider.value as AssetImage).assetName, 'assets/image.png'); + }); + + test('memory() returns correct instance', () { + final bytes = Uint8List.fromList([0, 1, 2, 3]); + final imageProvider = utility.memory(bytes); + + expect(imageProvider.value, isA()); + expect((imageProvider.value as MemoryImage).bytes, bytes); + }); + }); + + group('TextHeightBehaviorUtility Tests', () { + const utility = TextHeightBehaviorUtility(UtilityTestAttribute.new); + + test('call() returns correct instance', () { + final textHeightBehavior = utility( + const TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: false, + ), + ); + + expect(textHeightBehavior.value, isA()); + expect(textHeightBehavior.value.applyHeightToFirstAscent, isTrue); + expect(textHeightBehavior.value.applyHeightToLastDescent, isFalse); + }); + group('TextScalerUtility Tests', () { + const utility = TextScalerUtility(UtilityTestAttribute.new); + + test('call() returns correct instance', () { + final textScaler = utility( + const TextScaler.linear(2), + ); + + expect(textScaler.value, isA()); + expect(textScaler.value.maxLines, equals(3)); + expect(textScaler.value.overflow, equals(TextOverflow.ellipsis)); + expect(textScaler.value.semanticsLabel, equals('Test Label')); + }); + }); + }); } From f0d87dcd1aa30e177b36bb8a4992c613635f3f3e Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Thu, 18 Jul 2024 15:41:29 -0400 Subject: [PATCH 4/4] Improved tests --- packages/mix/lib/src/core/helpers.dart | 18 + .../mix/lib/src/internal/iterable_ext.dart | 16 - .../mix/lib/src/internal/lerp_helpers.dart | 91 --- .../src/modifiers/clip_widget_modifier.dart | 28 +- .../modifiers/flexible_widget_modifier.dart | 6 +- .../rotated_box_widget_modifier.dart | 4 +- .../modifiers/transform_widget_modifier.dart | 4 +- .../modifiers/visibility_widget_modifier.dart | 4 +- .../spacing/edge_insets_dto_test.dart | 151 +++++ packages/mix/test/helpers/testing_utils.dart | 4 +- .../border/shape_border_dto_test.dart | 60 ++ .../attributes/scalars/scalar_util_test.dart | 563 +++++++++++++++++- .../text_style/text_style_dto_test.dart | 31 + .../test/src/helpers/iterable_ext_test.dart | 24 + .../test/src/helpers/lerp_helpers_test.dart | 20 - 15 files changed, 850 insertions(+), 174 deletions(-) delete mode 100644 packages/mix/lib/src/internal/lerp_helpers.dart create mode 100644 packages/mix/test/attributes/spacing/edge_insets_dto_test.dart delete mode 100644 packages/mix/test/src/helpers/lerp_helpers_test.dart diff --git a/packages/mix/lib/src/core/helpers.dart b/packages/mix/lib/src/core/helpers.dart index 99d80f3c1..270579605 100644 --- a/packages/mix/lib/src/core/helpers.dart +++ b/packages/mix/lib/src/core/helpers.dart @@ -23,9 +23,20 @@ class MixHelpers { static const lerpTextStyle = _lerpTextStyle; + static const lerpInt = _lerpInt; + + static const lerpSnap = _lerpSnap; + const MixHelpers._(); } +P? _lerpSnap

(P? from, P? to, double t) { + if (from == null) return to; + if (to == null) return from; + + return t < 0.5 ? from : to; +} + w.TextStyle? _lerpTextStyle(w.TextStyle? a, w.TextStyle? b, double t) { return w.TextStyle.lerp(a, b, t)?.copyWith( shadows: w.Shadow.lerpList(a?.shadows, b?.shadows, t), @@ -37,6 +48,13 @@ w.TextStyle? _lerpTextStyle(w.TextStyle? a, w.TextStyle? b, double t) { ); } +int _lerpInt(int? a, int? b, double t) { + a ??= 0; + b ??= 0; + + return (a + (b - a) * t).round(); +} + List? _mergeDtoList(List? a, List? b) { if (b == null) return a; if (a == null) return b; diff --git a/packages/mix/lib/src/internal/iterable_ext.dart b/packages/mix/lib/src/internal/iterable_ext.dart index 6b0dba50b..350867812 100644 --- a/packages/mix/lib/src/internal/iterable_ext.dart +++ b/packages/mix/lib/src/internal/iterable_ext.dart @@ -45,19 +45,3 @@ extension ListExt on List { }); } } - -List? merge(List? a, List? b) { - if (b == null) return a; - if (a == null) return b; - - return List.generate(max(a.length, b.length), (index) { - if (index < a.length) { - final currentValue = a[index]; - final otherValue = index < b.length ? b[index] : null; - - return otherValue ?? currentValue; - } - - return b[index]; - }); -} diff --git a/packages/mix/lib/src/internal/lerp_helpers.dart b/packages/mix/lib/src/internal/lerp_helpers.dart deleted file mode 100644 index a07edca9d..000000000 --- a/packages/mix/lib/src/internal/lerp_helpers.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter/widgets.dart'; - -/// Linearly interpolates between two integers. -/// -/// The [lerpInt] function takes two integers, [a] and [b], and a value [t] -/// between 0.0 and 1.0. It returns a new integer that is linearly interpolated -/// between [a] and [b] based on the value of [t]. -/// -/// Example usage: -/// ```dart -/// int a = 10; -/// int b = 20; -/// double t = 0.3; -/// int result = lerpInt(a, b, t); -/// print(result); // Output: 13 -/// ``` -/// -int lerpInt(int? a, int? b, double t) { - a ??= 0; - b ??= 0; - - return (a + (b - a) * t).round(); -} - -/// Snaps between two values based on a threshold. -/// -/// The [lerpSnap] function takes two values, [from] and [to], and a threshold -/// value [t] between 0.0 and 1.0. If [t] is less than 0.5, it returns [from], -/// otherwise it returns [to]. -/// -/// Example usage: -/// ```dart -/// String from = 'Hello'; -/// String to = 'World'; -/// double t = 0.8; -/// String result = lerpSnap(from, to, t); -/// print(result); // Output: 'World' -/// ``` -P? lerpSnap

(P? from, P? to, double t) { - if (from == null) return to; - if (to == null) return from; - - return t < 0.5 ? from : to; -} - -double? lerpDouble(num? a, num? b, double t) { - if (a == b || (a?.isNaN ?? false) && (b?.isNaN ?? false)) { - return a?.toDouble(); - } - a ??= 0.0; - b ??= 0.0; - - return a * (1.0 - t) + b * t; -} - -TextStyle? lerpTextStyle(TextStyle? from, TextStyle? other, double t) { - return TextStyle.lerp(from, other, t) - ?.copyWith(shadows: Shadow.lerpList(from?.shadows, other?.shadows, t)); -} - -Matrix4? lerpMatrix4(Matrix4? from, Matrix4? other, double t) { - return from != null && other != null - ? Matrix4Tween(begin: from, end: other).lerp(t) - : lerpSnap(from, other, t); -} - -/// Linearly interpolates between two [StrutStyle] objects. -/// -/// The [lerpStrutStyle] function takes two [StrutStyle] objects, [a] and [b], -/// and a value [t] between 0.0 and 1.0. It returns a new [StrutStyle] object -/// that is linearly interpolated between [a] and [b] based on the value of [t]. -/// ``` -StrutStyle? lerpStrutStyle(StrutStyle? a, StrutStyle? b, double t) { - if (a == null && b == null) return null; - if (a == null) return b; - if (b == null) return a; - - return StrutStyle( - fontFamily: t < 0.5 ? a.fontFamily : b.fontFamily, - fontFamilyFallback: t < 0.5 ? a.fontFamilyFallback : b.fontFamilyFallback, - fontSize: lerpDouble(a.fontSize, b.fontSize, t), - height: lerpDouble(a.height, b.height, t), - leadingDistribution: - t < 0.5 ? a.leadingDistribution : b.leadingDistribution, - leading: lerpDouble(a.leading, b.leading, t), - fontWeight: FontWeight.lerp(a.fontWeight, b.fontWeight, t), - fontStyle: t < 0.5 ? a.fontStyle : b.fontStyle, - forceStrutHeight: t < 0.5 ? a.forceStrutHeight : b.forceStrutHeight, - debugLabel: a.debugLabel ?? b.debugLabel, - ); -} diff --git a/packages/mix/lib/src/modifiers/clip_widget_modifier.dart b/packages/mix/lib/src/modifiers/clip_widget_modifier.dart index f4b6986c0..e746e15ac 100644 --- a/packages/mix/lib/src/modifiers/clip_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/clip_widget_modifier.dart @@ -5,10 +5,10 @@ import 'package:flutter/widgets.dart'; import '../core/attribute.dart'; import '../core/factory/mix_data.dart'; +import '../core/helpers.dart'; import '../core/modifier.dart'; import '../core/utility.dart'; import '../internal/diagnostic_properties_builder_ext.dart'; -import '../internal/lerp_helpers.dart'; final class ClipOvalModifierSpec extends WidgetModifierSpec { @@ -20,8 +20,8 @@ final class ClipOvalModifierSpec @override ClipOvalModifierSpec lerp(ClipOvalModifierSpec? other, double t) { return ClipOvalModifierSpec( - clipper: lerpSnap(clipper, other?.clipper, t), - clipBehavior: lerpSnap(clipBehavior, other?.clipBehavior, t), + clipper: MixHelpers.lerpSnap(clipper, other?.clipper, t), + clipBehavior: MixHelpers.lerpSnap(clipBehavior, other?.clipBehavior, t), ); } @@ -90,9 +90,9 @@ final class ClipRectModifierSpec @override ClipRectModifierSpec lerp(ClipRectModifierSpec? other, double t) { return ClipRectModifierSpec( - clipper: lerpSnap(clipper, other?.clipper, t), - clipBehavior: - lerpSnap(clipBehavior, other?.clipBehavior, t) ?? clipBehavior, + clipper: MixHelpers.lerpSnap(clipper, other?.clipper, t), + clipBehavior: MixHelpers.lerpSnap(clipBehavior, other?.clipBehavior, t) ?? + clipBehavior, ); } @@ -164,9 +164,9 @@ final class ClipRRectModifierSpec return ClipRRectModifierSpec( borderRadius: BorderRadiusGeometry.lerp(borderRadius, other?.borderRadius, t), - clipper: lerpSnap(clipper, other?.clipper, t), - clipBehavior: - lerpSnap(clipBehavior, other?.clipBehavior, t) ?? clipBehavior, + clipper: MixHelpers.lerpSnap(clipper, other?.clipper, t), + clipBehavior: MixHelpers.lerpSnap(clipBehavior, other?.clipBehavior, t) ?? + clipBehavior, ); } @@ -242,9 +242,9 @@ final class ClipPathModifierSpec @override ClipPathModifierSpec lerp(ClipPathModifierSpec? other, double t) { return ClipPathModifierSpec( - clipper: lerpSnap(clipper, other?.clipper, t), - clipBehavior: - lerpSnap(clipBehavior, other?.clipBehavior, t) ?? clipBehavior, + clipper: MixHelpers.lerpSnap(clipper, other?.clipper, t), + clipBehavior: MixHelpers.lerpSnap(clipBehavior, other?.clipBehavior, t) ?? + clipBehavior, ); } @@ -312,8 +312,8 @@ final class ClipTriangleModifierSpec @override ClipTriangleModifierSpec lerp(ClipTriangleModifierSpec? other, double t) { return ClipTriangleModifierSpec( - clipBehavior: - lerpSnap(clipBehavior, other?.clipBehavior, t) ?? clipBehavior, + clipBehavior: MixHelpers.lerpSnap(clipBehavior, other?.clipBehavior, t) ?? + clipBehavior, ); } diff --git a/packages/mix/lib/src/modifiers/flexible_widget_modifier.dart b/packages/mix/lib/src/modifiers/flexible_widget_modifier.dart index a344ed4ac..81eb171ba 100644 --- a/packages/mix/lib/src/modifiers/flexible_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/flexible_widget_modifier.dart @@ -6,10 +6,10 @@ import 'package:flutter/widgets.dart'; import '../attributes/enum/enum_util.dart'; import '../core/attribute.dart'; import '../core/factory/mix_data.dart'; +import '../core/helpers.dart'; import '../core/modifier.dart'; import '../core/utility.dart'; import '../internal/diagnostic_properties_builder_ext.dart'; -import '../internal/lerp_helpers.dart'; final class FlexibleModifierSpec extends WidgetModifierSpec { @@ -20,8 +20,8 @@ final class FlexibleModifierSpec @override FlexibleModifierSpec lerp(FlexibleModifierSpec? other, double t) { return FlexibleModifierSpec( - flex: lerpInt(flex, other?.flex, t), - fit: lerpSnap(fit, other?.fit, t), + flex: MixHelpers.lerpInt(flex, other?.flex, t), + fit: MixHelpers.lerpSnap(fit, other?.fit, t), ); } diff --git a/packages/mix/lib/src/modifiers/rotated_box_widget_modifier.dart b/packages/mix/lib/src/modifiers/rotated_box_widget_modifier.dart index 0141edb13..5efbeb3f7 100644 --- a/packages/mix/lib/src/modifiers/rotated_box_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/rotated_box_widget_modifier.dart @@ -5,10 +5,10 @@ import 'package:flutter/widgets.dart'; import '../core/attribute.dart'; import '../core/factory/mix_data.dart'; +import '../core/helpers.dart'; import '../core/modifier.dart'; import '../core/utility.dart'; import '../internal/diagnostic_properties_builder_ext.dart'; -import '../internal/lerp_helpers.dart'; final class RotatedBoxModifierSpec extends WidgetModifierSpec { @@ -19,7 +19,7 @@ final class RotatedBoxModifierSpec RotatedBoxModifierSpec lerp(RotatedBoxModifierSpec? other, double t) { // Use lerpInt for interpolating between integers return RotatedBoxModifierSpec( - lerpInt(quarterTurns, other?.quarterTurns, t), + MixHelpers.lerpInt(quarterTurns, other?.quarterTurns, t), ); } diff --git a/packages/mix/lib/src/modifiers/transform_widget_modifier.dart b/packages/mix/lib/src/modifiers/transform_widget_modifier.dart index 0d91e2565..bb130b100 100644 --- a/packages/mix/lib/src/modifiers/transform_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/transform_widget_modifier.dart @@ -5,10 +5,10 @@ import 'package:flutter/widgets.dart'; import '../core/attribute.dart'; import '../core/factory/mix_data.dart'; +import '../core/helpers.dart'; import '../core/modifier.dart'; import '../core/utility.dart'; import '../internal/diagnostic_properties_builder_ext.dart'; -import '../internal/lerp_helpers.dart'; final class TransformModifierSpec extends WidgetModifierSpec { @@ -20,7 +20,7 @@ final class TransformModifierSpec @override TransformModifierSpec lerp(TransformModifierSpec? other, double t) { return TransformModifierSpec( - transform: lerpMatrix4(transform, other?.transform, t), + transform: MixHelpers.lerpMatrix4(transform, other?.transform, t), ); } diff --git a/packages/mix/lib/src/modifiers/visibility_widget_modifier.dart b/packages/mix/lib/src/modifiers/visibility_widget_modifier.dart index d6d163f1b..0e9645f58 100644 --- a/packages/mix/lib/src/modifiers/visibility_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/visibility_widget_modifier.dart @@ -5,10 +5,10 @@ import 'package:flutter/widgets.dart'; import '../core/attribute.dart'; import '../core/factory/mix_data.dart'; +import '../core/helpers.dart'; import '../core/modifier.dart'; import '../core/utility.dart'; import '../internal/diagnostic_properties_builder_ext.dart'; -import '../internal/lerp_helpers.dart'; final class VisibilityModifierSpec extends WidgetModifierSpec { @@ -18,7 +18,7 @@ final class VisibilityModifierSpec @override VisibilityModifierSpec lerp(VisibilityModifierSpec? other, double t) { return VisibilityModifierSpec( - lerpSnap(visible, other?.visible, t) ?? visible, + MixHelpers.lerpSnap(visible, other?.visible, t) ?? visible, ); } diff --git a/packages/mix/test/attributes/spacing/edge_insets_dto_test.dart b/packages/mix/test/attributes/spacing/edge_insets_dto_test.dart new file mode 100644 index 000000000..07eafe636 --- /dev/null +++ b/packages/mix/test/attributes/spacing/edge_insets_dto_test.dart @@ -0,0 +1,151 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +import '../../helpers/testing_utils.dart'; + +void main() { + group('EdgeInsetsGeometryDto', () { + test('only creates EdgeInsetsDto when directional values are not provided', + () { + final dto = + EdgeInsetsGeometryDto.only(top: 10, bottom: 20, left: 30, right: 40); + expect(dto, isA()); + expect(dto.top, equals(10)); + expect(dto.bottom, equals(20)); + expect((dto as EdgeInsetsDto).left, equals(30)); + expect((dto).right, equals(40)); + }); + + test( + 'only creates EdgeInsetsDirectionalDto when directional values are provided', + () { + final dto = + EdgeInsetsGeometryDto.only(top: 10, bottom: 20, start: 30, end: 40); + expect(dto, isA()); + expect(dto.top, equals(10)); + expect(dto.bottom, equals(20)); + expect((dto as EdgeInsetsDirectionalDto).start, equals(30)); + expect((dto).end, equals(40)); + }); + + test('tryToMerge returns first dto if second is null', () { + const dto1 = EdgeInsetsDto(top: 10, bottom: 20, left: 30, right: 40); + final merged = EdgeInsetsGeometryDto.tryToMerge(dto1, null); + expect(merged, equals(dto1)); + }); + + test('tryToMerge returns second dto if first is null', () { + const dto2 = + EdgeInsetsDirectionalDto(top: 10, bottom: 20, start: 30, end: 40); + final merged = EdgeInsetsGeometryDto.tryToMerge(null, dto2); + expect(merged, equals(dto2)); + }); + + test('tryToMerge merges dtos of the same type', () { + const dto1 = EdgeInsetsDto(top: 10, bottom: 20); + const dto2 = EdgeInsetsDto(left: 30, right: 40); + final merged = EdgeInsetsGeometryDto.tryToMerge(dto1, dto2); + expect(merged, isA()); + expect(merged!.top, equals(10)); + expect(merged.bottom, equals(20)); + expect((merged as EdgeInsetsDto).left, equals(30)); + expect((merged).right, equals(40)); + }); + + test('tryToMerge merges dtos of different types', () { + const dto1 = EdgeInsetsDto(top: 10, bottom: 20); + const dto2 = EdgeInsetsDirectionalDto(start: 30, end: 40); + final merged = EdgeInsetsGeometryDto.tryToMerge(dto1, dto2); + expect(merged, isA()); + expect(merged!.top, equals(10)); + expect(merged.bottom, equals(20)); + expect((merged as EdgeInsetsDirectionalDto).start, equals(30)); + expect((merged).end, equals(40)); + }); + }); + + group('EdgeInsetsDto', () { + test('all constructor sets all values', () { + const dto = EdgeInsetsDto.all(10); + expect(dto.top, equals(10)); + expect(dto.bottom, equals(10)); + expect(dto.left, equals(10)); + expect(dto.right, equals(10)); + }); + + test('none constructor sets all values to 0', () { + const dto = EdgeInsetsDto.none(); + expect(dto.top, equals(0)); + expect(dto.bottom, equals(0)); + expect(dto.left, equals(0)); + expect(dto.right, equals(0)); + }); + + test('resolve returns EdgeInsets with token values', () { + const dto = EdgeInsetsDto(top: 10, bottom: 20, left: 30, right: 40); + final mix = EmptyMixData; + final resolved = dto.resolve(mix); + expect(resolved, isA()); + expect(resolved.top, equals(10)); + expect(resolved.bottom, equals(20)); + expect(resolved.left, equals(30)); + expect(resolved.right, equals(40)); + }); + }); + + group('EdgeInsetsDirectionalDto', () { + test('all constructor sets all values', () { + const dto = EdgeInsetsDirectionalDto.all(10); + expect(dto.top, equals(10)); + expect(dto.bottom, equals(10)); + expect(dto.start, equals(10)); + expect(dto.end, equals(10)); + }); + + test('none constructor sets all values to 0', () { + const dto = EdgeInsetsDirectionalDto.none(); + expect(dto.top, equals(0)); + expect(dto.bottom, equals(0)); + expect(dto.start, equals(0)); + expect(dto.end, equals(0)); + }); + + test('resolve returns EdgeInsetsDirectional with token values', () { + const dto = + EdgeInsetsDirectionalDto(top: 10, bottom: 20, start: 30, end: 40); + final mix = EmptyMixData; + final resolved = dto.resolve(mix); + expect(resolved, isA()); + expect(resolved.top, equals(10)); + expect(resolved.bottom, equals(20)); + expect(resolved.start, equals(30)); + expect(resolved.end, equals(40)); + }); + }); + + group('EdgeInsetsGeometryExt', () { + test('toDto converts EdgeInsets to EdgeInsetsDto', () { + const edgeInsets = + EdgeInsets.only(top: 10, bottom: 20, left: 30, right: 40); + final dto = edgeInsets.toDto(); + expect(dto, isA()); + expect(dto.top, equals(10)); + expect(dto.bottom, equals(20)); + expect((dto).left, equals(30)); + expect((dto).right, equals(40)); + }); + + test('toDto converts EdgeInsetsDirectional to EdgeInsetsDirectionalDto', + () { + const edgeInsetsDirectional = + EdgeInsetsDirectional.only(top: 10, bottom: 20, start: 30, end: 40); + final dto = edgeInsetsDirectional.toDto(); + expect(dto, isA()); + expect(dto.top, equals(10)); + expect(dto.bottom, equals(20)); + expect((dto).start, equals(30)); + expect((dto).end, equals(40)); + }); + }); +} diff --git a/packages/mix/test/helpers/testing_utils.dart b/packages/mix/test/helpers/testing_utils.dart index bc7c392b3..9f3b3902b 100644 --- a/packages/mix/test/helpers/testing_utils.dart +++ b/packages/mix/test/helpers/testing_utils.dart @@ -5,7 +5,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; import 'package:mix/src/core/internal/widget_state/gesturable_builder.dart'; import 'package:mix/src/core/internal/widget_state/interactive_widget.dart'; -import 'package:mix/src/internal/lerp_helpers.dart'; export 'package:mix/src/internal/values_ext.dart'; @@ -327,7 +326,8 @@ final class CustomWidgetModifierSpec CustomWidgetModifierSpec lerp(CustomWidgetModifierSpec? other, double t) { if (other == null) return this; - return CustomWidgetModifierSpec(lerpSnap(value, other.value, t) ?? value); + return CustomWidgetModifierSpec( + MixHelpers.lerpSnap(value, other.value, t) ?? value); } @override diff --git a/packages/mix/test/src/attributes/border/shape_border_dto_test.dart b/packages/mix/test/src/attributes/border/shape_border_dto_test.dart index eeed06870..e2b2b6e32 100644 --- a/packages/mix/test/src/attributes/border/shape_border_dto_test.dart +++ b/packages/mix/test/src/attributes/border/shape_border_dto_test.dart @@ -580,4 +580,64 @@ void main() { }); }); }); + + group('ShapeBorderDto.tryToMerge', () { + test('should return null when both inputs are null', () { + final result = ShapeBorderDto.tryToMerge(null, null); + expect(result, isNull); + }); + + test('should return the non-null input when one input is null', () { + const dto = RoundedRectangleBorderDto(); + expect(ShapeBorderDto.tryToMerge(dto, null), equals(dto)); + expect(ShapeBorderDto.tryToMerge(null, dto), equals(dto)); + }); + + test( + 'should return the second input when both inputs are not OutlinedBorderDto', + () { + const dto1 = CircleBorderDto(); + const dto2 = StarBorderDto(); + expect(ShapeBorderDto.tryToMerge(dto1, dto2), equals(dto2)); + }); + + test( + 'should call OutlinedBorderDto.tryToMerge when both inputs are OutlinedBorderDto', + () { + const dto1 = RoundedRectangleBorderDto( + borderRadius: BorderRadiusDto(topLeft: Radius.circular(10))); + final dto2 = RoundedRectangleBorderDto( + side: BorderSideDto(color: Colors.red.toDto())); + final expectedResult = RoundedRectangleBorderDto( + borderRadius: const BorderRadiusDto(topLeft: Radius.circular(10)), + side: BorderSideDto(color: Colors.red.toDto()), + ); + expect(ShapeBorderDto.tryToMerge(dto1, dto2), equals(expectedResult)); + }); + }); + + group('OutlinedBorderDto.tryToMerge', () { + test('should return null when both inputs are null', () { + final result = OutlinedBorderDto.tryToMerge(null, null); + expect(result, isNull); + }); + + test('should return the non-null input when one input is null', () { + const dto = RoundedRectangleBorderDto(); + expect(OutlinedBorderDto.tryToMerge(dto, null), equals(dto)); + expect(OutlinedBorderDto.tryToMerge(null, dto), equals(dto)); + }); + + test('should call _exhaustiveMerge when both inputs are not null', () { + const dto1 = RoundedRectangleBorderDto( + borderRadius: BorderRadiusDto(topLeft: Radius.circular(10))); + final dto2 = RoundedRectangleBorderDto( + side: BorderSideDto(color: Colors.red.toDto())); + final expectedResult = RoundedRectangleBorderDto( + borderRadius: const BorderRadiusDto(topLeft: Radius.circular(10)), + side: BorderSideDto(color: Colors.red.toDto()), + ); + expect(OutlinedBorderDto.tryToMerge(dto1, dto2), equals(expectedResult)); + }); + }); } diff --git a/packages/mix/test/src/attributes/scalars/scalar_util_test.dart b/packages/mix/test/src/attributes/scalars/scalar_util_test.dart index 3fcc7c848..da029b2e9 100644 --- a/packages/mix/test/src/attributes/scalars/scalar_util_test.dart +++ b/packages/mix/test/src/attributes/scalars/scalar_util_test.dart @@ -175,11 +175,9 @@ void main() { }); }); - // AlignmentUtility group('AlignmentUtility Tests', () { const utility = AlignmentUtility(UtilityTestAttribute.new); - // only test('only() returns correct instance', () { final alignment = utility.only(x: 10, y: 8); final alignmnetDirectional = utility.only(y: 8, start: 10); @@ -214,7 +212,6 @@ void main() { }); }); -// BorderStyleUtility group('BorderStyleUtility Tests', () { const utility = BorderStyleUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -225,26 +222,21 @@ void main() { }); }); - // ShapeBorderUtility - group('ShapeBorderUtility', () { final utility = ShapeBorderUtility(UtilityTestAttribute.new); - // circle() test('circle() returns correct instance', () { final shapeBorder = utility.circle(); expect(shapeBorder.value, isA()); }); - // stadium() test('stadium() returns correct instance', () { final shapeBorder = utility.stadium(); expect(shapeBorder.value, isA()); }); - // rounded() test('rounded() returns correct instance', () { final shapeBorder = utility.roundedRectangle.borderRadius(20); @@ -255,7 +247,6 @@ void main() { ); }); - // beveled() test('beveled() returns correct instance', () { final shapeBorder = utility.beveledRectangle(); @@ -292,7 +283,6 @@ void main() { }); }); - // TextDirectionUtility group('TextDirectionUtility Tests', () { const utility = TextDirectionUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -303,7 +293,6 @@ void main() { }); }); - // TileModeUtility group('TileModeUtility Tests', () { const utility = TileModeUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -318,7 +307,6 @@ void main() { }); }); - // GradientTransformUtility group('GradientTransformUtility Tests', () { const utility = GradientTransformUtility(UtilityTestAttribute.new); test('rotate', () { @@ -327,7 +315,6 @@ void main() { }); }); - // Matrix4Utility group('Matrix4Utility Tests', () { const utility = Matrix4Utility(UtilityTestAttribute.new); test('identity', () { @@ -357,6 +344,80 @@ void main() { Matrix4.translationValues(20, 20, 20), ); }); + //fromList + test('fromList', () { + final list = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; + expect( + utility.fromList(list).value, + isA(), + ); + expect( + utility.fromList(list).value, + Matrix4.fromList(list), + ); + }); + + // diagonal3Values + test('diagonal3Values', () { + expect( + utility.diagonal3Values(1, 2, 3).value, + isA(), + ); + expect( + utility.diagonal3Values(1, 2, 3).value, + Matrix4.diagonal3Values(1, 2, 3), + ); + }); + + // skewX + test('skewX', () { + expect( + utility.skewX(20).value, + isA(), + ); + expect( + utility.skewX(20).value, + Matrix4.skewX(20), + ); + }); + + // skewY + test('skewY', () { + expect( + utility.skewY(20).value, + isA(), + ); + expect( + utility.skewY(20).value, + Matrix4.skewY(20), + ); + }); + + // skew + test('skew', () { + expect( + utility.skew(20, 20).value, + isA(), + ); + expect( + utility.skew(20, 20).value, + Matrix4.skew(20, 20), + ); + }); + test('fromBuffer', () { + final list = List.generate(130, (index) => index * 1.0); + final float32List = Float32List.fromList(list); + + expect( + utility.fromBuffer(float32List.buffer, 0).value, + isA(), + ); + + expect( + utility.fromBuffer(float32List.buffer, 0).value, + Matrix4.fromBuffer(float32List.buffer, 0), + ); + }); }); group('BlendModeUtility Tests', () { @@ -378,7 +439,6 @@ void main() { }); }); - // BlendModeUtility group('BlendModeUtility Tests', () { const utility = BlendModeUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -411,7 +471,6 @@ void main() { }); }); - // FontWeightUtility group('FontWeightUtility Tests', () { const utility = FontWeightUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -440,7 +499,6 @@ void main() { }); }); - // TextDecorationUtility group('TextDecorationUtility Tests', () { const utility = TextDecorationUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -452,10 +510,14 @@ void main() { expect(utility.underline().value, TextDecoration.underline); expect(utility.overline().value, TextDecoration.overline); expect(utility.lineThrough().value, TextDecoration.lineThrough); + + expect(utility.combine([TextDecoration.underline]).value, + isA()); + + expect(utility(TextDecoration.underline).value, isA()); }); }); - // FontStyleUtility group('FontStyleUtility Tests', () { const utility = FontStyleUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -466,7 +528,6 @@ void main() { }); }); - // RadiusUtility group('RadiusUtility Tests', () { const utility = RadiusUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -477,7 +538,6 @@ void main() { }); }); - // TextDecorationStyleUtility group('TextDecorationStyleUtility Tests', () { const utility = TextDecorationStyleUtility(UtilityTestAttribute.new); test('Properties are initialized correctly', () { @@ -552,11 +612,470 @@ void main() { const TextScaler.linear(2), ); + final linearTextScaler = utility.linear(3); + + final noScalingTextScaler = utility.noScaling(); + expect(textScaler.value, isA()); - expect(textScaler.value.maxLines, equals(3)); - expect(textScaler.value.overflow, equals(TextOverflow.ellipsis)); - expect(textScaler.value.semanticsLabel, equals('Test Label')); + expect(textScaler.value, const TextScaler.linear(2)); + expect(linearTextScaler.value, const TextScaler.linear(3)); + expect(noScalingTextScaler.value, TextScaler.noScaling); + }); + }); + group('DurationUtility Tests', () { + const utility = DurationUtility(UtilityTestAttribute.new); + test('microseconds() returns correct instance', () { + final duration = utility.microseconds(500); + expect(duration.value, isA()); + expect(duration.value.inMicroseconds, 500); + }); + + test('milliseconds() returns correct instance', () { + final duration = utility.milliseconds(1500); + expect(duration.value, isA()); + expect(duration.value.inMilliseconds, 1500); + }); + + test('seconds() returns correct instance', () { + final duration = utility.seconds(30); + expect(duration.value, isA()); + expect(duration.value.inSeconds, 30); + }); + + test('minutes() returns correct instance', () { + final duration = utility.minutes(2); + expect(duration.value, isA()); + expect(duration.value.inMinutes, 2); + }); + + test('zero() returns correct instance', () { + final duration = utility.zero(); + expect(duration.value, isA()); + expect(duration.value.inMicroseconds, 0); + }); + + test('call() returns correct instance', () { + final duration = utility(const Duration(seconds: 5)); + expect(duration.value, isA()); + expect(duration.value.inSeconds, 5); + }); + }); + group('FontFeatureUtility Tests', () { + const utility = FontFeatureUtility(UtilityTestAttribute.new); + + test('call() returns correct instance', () { + final fontFeature = utility( + const FontFeature('liga', 1), + ); + + expect(fontFeature.value, isA()); + expect(fontFeature.value.feature, 'liga'); + expect(fontFeature.value.value, 1); + }); + + test('enable() returns correct instance', () { + final fontFeature = utility.enable('liga'); + + expect(fontFeature.value, isA()); + expect(fontFeature.value.feature, 'liga'); + expect(fontFeature.value.value, 1); + }); + + test('disable() returns correct instance', () { + final fontFeature = utility.disable('liga'); + + expect(fontFeature.value, isA()); + expect(fontFeature.value.feature, 'liga'); + expect(fontFeature.value.value, 0); + }); + + test('alternative() returns correct instance', () { + final fontFeature = utility.alternative(2); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.alternative(2)); + }); + + test('randomize() returns correct instance', () { + final fontFeature = utility.randomize(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.randomize()); + }); + + test('swash() returns correct instance', () { + final fontFeature = utility.swash(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.swash()); + }); + + test('alternativeFractions() returns correct instance', () { + final fontFeature = utility.alternativeFractions(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.alternativeFractions()); + }); + + test('contextualAlternates() returns correct instance', () { + final fontFeature = utility.contextualAlternates(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.contextualAlternates()); + }); + + test('caseSensitiveForms() returns correct instance', () { + final fontFeature = utility.caseSensitiveForms(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.caseSensitiveForms()); + }); + + test('characterVariant() returns correct instance', () { + final fontFeature = utility.characterVariant(5); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, FontFeature.characterVariant(5)); + }); + + test('historicalForms() returns correct instance', () { + final fontFeature = utility.historicalForms(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.historicalForms()); + }); + + test('historicalLigatures() returns correct instance', () { + final fontFeature = utility.historicalLigatures(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.historicalLigatures()); + }); + + test('liningFigures() returns correct instance', () { + final fontFeature = utility.liningFigures(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.liningFigures()); + }); + + test('localeAware() returns correct instance', () { + final fontFeature = utility.localeAware(); + final fontFeatureEnabled = utility.localeAware(enable: true); + final fontFeatureDisabled = utility.localeAware(enable: false); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.localeAware()); + expect(fontFeatureEnabled.value, + const FontFeature.localeAware(enable: true)); + expect(fontFeatureDisabled.value, + const FontFeature.localeAware(enable: false)); + }); + + test('notationalForms() returns correct instance', () { + final fontFeature = utility.notationalForms(2); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.notationalForms(2)); + }); + + test('numerators() returns correct instance', () { + final fontFeature = utility.numerators(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.numerators()); + }); + + test('ordinalForms() returns correct instance', () { + final fontFeature = utility.ordinalForms(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.ordinalForms()); + }); + + test('proportionalFigures() returns correct instance', () { + final fontFeature = utility.proportionalFigures(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.proportionalFigures()); + }); + + test('stylisticAlternates() returns correct instance', () { + final fontFeature = utility.stylisticAlternates(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.stylisticAlternates()); + }); + + test('scientificInferiors() returns correct instance', () { + final fontFeature = utility.scientificInferiors(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.scientificInferiors()); + }); + + test('stylisticSet() returns correct instance', () { + final fontFeature = utility.stylisticSet(2); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, FontFeature.stylisticSet(2)); + }); + + test('subscripts() returns correct instance', () { + final fontFeature = utility.subscripts(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.subscripts()); + }); + + test('superscripts() returns correct instance', () { + final fontFeature = utility.superscripts(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.superscripts()); + }); + + test('tabularFigures() returns correct instance', () { + final fontFeature = utility.tabularFigures(); + + expect(fontFeature.value, isA()); + expect(fontFeature.value, const FontFeature.tabularFigures()); + }); + }); + + group('AlignmentDirectionalUtility Tests', () { + const utility = AlignmentDirectionalUtility(UtilityTestAttribute.new); + + test('only() returns correct instance with y and start values', () { + final alignmentDirectional = utility.only(y: 5, start: 10); + + expect(alignmentDirectional.value, isA()); + expect(alignmentDirectional.value, + equals(const AlignmentDirectional(10, 5))); + }); + + test('only() returns correct instance with only y value', () { + final alignmentDirectional = utility.only(y: 5); + + expect(alignmentDirectional.value, isA()); + expect(alignmentDirectional.value, + equals(const AlignmentDirectional(0, 5))); + }); + + test('only() returns correct instance with only start value', () { + final alignmentDirectional = utility.only(start: 10); + + expect(alignmentDirectional.value, isA()); + expect(alignmentDirectional.value, + equals(const AlignmentDirectional(10, 0))); + }); + + test('only() returns correct instance with no values', () { + final alignmentDirectional = utility.only(y: -1, start: -1); + + expect(alignmentDirectional.value, isA()); + expect( + alignmentDirectional.value, equals(AlignmentDirectional.topStart)); + }); + + test('topStart() returns correct instance', () { + final alignmentDirectional = utility.topStart(); + + expect(alignmentDirectional.value, isA()); + expect( + alignmentDirectional.value, equals(AlignmentDirectional.topStart)); + }); + + test('topCenter() returns correct instance', () { + final alignmentDirectional = utility.topCenter(); + + expect(alignmentDirectional.value, isA()); + expect( + alignmentDirectional.value, equals(AlignmentDirectional.topCenter)); + }); + + test('topEnd() returns correct instance', () { + final alignmentDirectional = utility.topEnd(); + + expect(alignmentDirectional.value, isA()); + expect(alignmentDirectional.value, equals(AlignmentDirectional.topEnd)); + }); + + test('centerStart() returns correct instance', () { + final alignmentDirectional = utility.centerStart(); + + expect(alignmentDirectional.value, isA()); + expect(alignmentDirectional.value, + equals(AlignmentDirectional.centerStart)); + }); + + test('center() returns correct instance', () { + final alignmentDirectional = utility.center(); + + expect(alignmentDirectional.value, isA()); + expect(alignmentDirectional.value, equals(AlignmentDirectional.center)); + }); + + test('centerEnd() returns correct instance', () { + final alignmentDirectional = utility.centerEnd(); + + expect(alignmentDirectional.value, isA()); + expect( + alignmentDirectional.value, equals(AlignmentDirectional.centerEnd)); + }); + + test('bottomStart() returns correct instance', () { + final alignmentDirectional = utility.bottomStart(); + + expect(alignmentDirectional.value, isA()); + expect(alignmentDirectional.value, + equals(AlignmentDirectional.bottomStart)); + }); + + test('bottomCenter() returns correct instance', () { + final alignmentDirectional = utility.bottomCenter(); + + expect(alignmentDirectional.value, isA()); + expect(alignmentDirectional.value, + equals(AlignmentDirectional.bottomCenter)); + }); + + test('bottomEnd() returns correct instance', () { + final alignmentDirectional = utility.bottomEnd(); + + expect(alignmentDirectional.value, isA()); + expect( + alignmentDirectional.value, equals(AlignmentDirectional.bottomEnd)); + }); + + test('call() returns correct instance', () { + final alignmentDirectional = utility(AlignmentDirectional.topStart); + + expect(alignmentDirectional.value, isA()); + expect( + alignmentDirectional.value, equals(AlignmentDirectional.topStart)); + }); + }); + group('PaintUtility Tests', () { + const utility = PaintUtility(UtilityTestAttribute.new); + test('PaintUtility returns correct instance', () { + final paint = utility(Paint()); + expect(paint.value, isA()); }); }); + + group('LocaleUtility Tests', () { + const utility = LocaleUtility(UtilityTestAttribute.new); + test('LocaleUtility returns correct instance', () { + final locale = utility(const Locale('en', 'US')); + expect(locale.value, isA()); + expect(locale.value.languageCode, 'en'); + expect(locale.value.countryCode, 'US'); + }); + + test('LocaleUtility handles locale without country code', () { + final locale = utility(const Locale('fr')); + expect(locale.value, isA()); + expect(locale.value.languageCode, 'fr'); + expect(locale.value.countryCode, null); + }); + }); + }); + + group('CurveUtility Tests', () { + const utility = CurveUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.linear().value, isA()); + expect(utility.decelerate().value, isA()); + expect(utility.fastOutSlowIn().value, isA()); + expect(utility.bounceIn().value, isA()); + expect(utility.bounceOut().value, isA()); + expect(utility.bounceInOut().value, isA()); + expect(utility.elasticIn().value, isA()); + expect(utility.elasticOut().value, isA()); + expect(utility.elasticInOut().value, isA()); + expect(utility.linear().value, Curves.linear); + expect(utility.decelerate().value, Curves.decelerate); + expect(utility.fastOutSlowIn().value, Curves.fastOutSlowIn); + expect(utility.bounceIn().value, Curves.bounceIn); + expect(utility.bounceOut().value, Curves.bounceOut); + expect(utility.bounceInOut().value, Curves.bounceInOut); + expect(utility.elasticIn().value, Curves.elasticIn); + expect(utility.elasticOut().value, Curves.elasticOut); + expect(utility.elasticInOut().value, Curves.elasticInOut); + expect(utility.ease().value, isA()); + }); + }); + + group('OffsetUtility Tests', () { + const utility = OffsetUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.zero().value, isA()); + expect(utility.infinite().value, isA()); + expect(utility.zero().value, Offset.zero); + expect(utility.infinite().value, Offset.infinite); + + expect(utility.fromDirection(10, 20).value, isA()); + expect(utility.fromDirection(10, 20).value, Offset.fromDirection(10, 20)); + }); + }); + + group('RectUtility Tests', () { + const utility = RectUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility.zero().value, isA()); + expect(utility.fromLTRB(10, 20, 30, 40).value, isA()); + expect(utility.fromCircle(center: const Offset(10, 20), radius: 30).value, + isA()); + expect( + utility + .fromCenter(center: const Offset(10, 20), width: 30, height: 40) + .value, + isA()); + expect(utility.fromLTWH(10, 20, 30, 40).value, isA()); + expect(utility.fromCircle(center: const Offset(10, 20), radius: 30).value, + Rect.fromCircle(center: const Offset(10, 20), radius: 30)); + expect( + utility + .fromCenter(center: const Offset(10, 20), width: 30, height: 40) + .value, + Rect.fromCenter(center: const Offset(10, 20), width: 30, height: 40), + ); + expect(utility.fromLTRB(10, 20, 30, 40).value, + const Rect.fromLTRB(10, 20, 30, 40)); + expect(utility.fromLTWH(10, 20, 30, 40).value, + const Rect.fromLTWH(10, 20, 30, 40)); + // fromPoints + expect( + utility + .fromPoints( + const Offset(10, 20), + const Offset(30, 40), + ) + .value, + Rect.fromPoints( + const Offset(10, 20), + const Offset(30, 40), + ), + ); + + // largest + expect( + utility.largest().value, + Rect.largest, + ); + }); + }); + + group('FontFamilyUtility Tests', () { + const utility = FontFamilyUtility(UtilityTestAttribute.new); + test('Properties are initialized correctly', () { + expect(utility('Roboto').value, 'Roboto'); + expect( + utility.fromCharCodes([82, 111, 98, 111, 116, 111]).value, 'Roboto'); + expect(utility.fromCharCode(82).value, 'R'); + expect(utility.fromEnvironment('name').value, ''); + expect(utility('Roboto').value, 'Roboto'); + }); }); } diff --git a/packages/mix/test/src/attributes/text_style/text_style_dto_test.dart b/packages/mix/test/src/attributes/text_style/text_style_dto_test.dart index cc07c83ea..06c0b3f84 100644 --- a/packages/mix/test/src/attributes/text_style/text_style_dto_test.dart +++ b/packages/mix/test/src/attributes/text_style/text_style_dto_test.dart @@ -115,4 +115,35 @@ void main() { expect(attr1, isNot(attr2)); }); }); + test('TextStyleDto.ref creates a TextStyleDto with a TextStyleDataRef', () { + const token = TextStyleToken('test_token'); + final attr = TextStyleDto.ref(token); + expect(attr.value.length, 1); + expect(attr.value.first, isA()); + expect((attr.value.first as TextStyleDataRef).ref.token, token); + }); + + test('TextStyleExt toDto method converts TextStyle to TextStyleDto correctly', + () { + const style = TextStyle( + color: Colors.blue, + fontSize: 18.0, + fontWeight: FontWeight.bold, + ); + final attr = style.toDto(); + expect(attr.value.length, 1); + expect(attr.value.first.color?.value, Colors.blue); + expect(attr.value.first.fontSize, 18.0); + expect(attr.value.first.fontWeight, FontWeight.bold); + }); + + test('TextStyleExt toDto method handles TextStyleRef correctly', () { + const token = TextStyleToken('test_token'); + const style = TextStyleRef(token); + final attr = style.toDto(); + expect(attr, isA()); + expect(attr.value.length, 1); + expect(attr.value.first, isA()); + expect((attr.value.first as TextStyleDataRef).ref.token, token); + }); } diff --git a/packages/mix/test/src/helpers/iterable_ext_test.dart b/packages/mix/test/src/helpers/iterable_ext_test.dart index 376b3f255..177866966 100644 --- a/packages/mix/test/src/helpers/iterable_ext_test.dart +++ b/packages/mix/test/src/helpers/iterable_ext_test.dart @@ -19,4 +19,28 @@ void main() { expect(list.sorted(), []); expect(list.sorted((a, b) => b.compareTo(a)), []); }); + test('IterableExt elementAtOrNull returns correct element or null', () { + final list = [1, 2, 3, 4, 5]; + expect(list.elementAtOrNull(2), 3); + expect(list.elementAtOrNull(-1), null); + expect(list.elementAtOrNull(5), null); + }); + + test('ListExt merge returns correct merged list', () { + final list1 = [1, 2, 3, 4, 5]; + final list2 = [4, 5, 6, 7]; + final list3 = []; + + expect(list1.merge(list2), [4, 5, 6, 7, 5]); + expect(list1.merge(list3), [1, 2, 3, 4, 5]); + expect(list3.merge(list2), [4, 5, 6, 7]); + expect(list3.merge(null), []); + }); + + test('IterableExt sorted returns correct sorted list', () { + final list = [5, 2, 4, 1, 3]; + expect(list.sorted(), [1, 2, 3, 4, 5]); + expect(list.sorted((a, b) => a.compareTo(b)), [1, 2, 3, 4, 5]); + expect(list.sorted((a, b) => b.compareTo(a)), [5, 4, 3, 2, 1]); + }); } diff --git a/packages/mix/test/src/helpers/lerp_helpers_test.dart b/packages/mix/test/src/helpers/lerp_helpers_test.dart deleted file mode 100644 index 88cb65f5d..000000000 --- a/packages/mix/test/src/helpers/lerp_helpers_test.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/internal/lerp_helpers.dart'; - -void main() { - test('Linearly interpolates between two integers', () { - int a = 10; - int b = 20; - double t = 0.3; - int? result = lerpInt(a, b, t); - expect(result, 13); - }); - - test('Snaps between two values based on a threshold', () { - String from = 'Hello'; - String to = 'World'; - double t = 0.8; - String? result = lerpSnap(from, to, t); - expect(result, 'World'); - }); -}