From 4a2c9dfec42b423d90f164b36a135317aa9ca259 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:34:53 -0300 Subject: [PATCH 01/17] support multi operators as variant --- lib/src/variants/variant.dart | 20 +++- test/src/variants/variant_operation_test.dart | 111 ++++++++++++++++++ test/src/variants/variant_test.dart | 15 +++ 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 048e76ce3..1da1055c5 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -248,14 +248,30 @@ class MultiVariant extends Variant { /// /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.and`. factory MultiVariant.and(Iterable variants) { - return MultiVariant(variants, type: MultiVariantOperator.and); + return MultiVariant( + _expandVariants(variants), + type: MultiVariantOperator.and, + ); } /// Factory constructor to create a `MultiVariant` where any one of the provided variants needs to be active (`MultiVariantType.or`). /// /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.or`. factory MultiVariant.or(Iterable variants) { - return MultiVariant(variants, type: MultiVariantOperator.or); + return MultiVariant( + _expandVariants(variants), + type: MultiVariantOperator.or, + ); + } + + static List _expandVariants(Iterable variants) { + return variants.expand((element) { + if (element is MultiVariant) { + return element.variants; + } else { + return [element]; + } + }).toList(); } /// Removes specified variants from this `MultiVariant`. diff --git a/test/src/variants/variant_operation_test.dart b/test/src/variants/variant_operation_test.dart index bf3909963..8416322a0 100644 --- a/test/src/variants/variant_operation_test.dart +++ b/test/src/variants/variant_operation_test.dart @@ -1,6 +1,9 @@ +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('VariantOperation', () { test('should add variant with & operator', () { @@ -8,6 +11,7 @@ void main() { const otherVariant = Variant('other'); final result = otherVariant & variant; expect(result.variants, contains(variant)); + expect(result.variants, contains(otherVariant)); }); test('should add variant with | operator', () { @@ -15,6 +19,113 @@ void main() { const otherVariant = Variant('other'); final result = otherVariant | variant; expect(result.variants, contains(variant)); + expect(result.variants, contains(otherVariant)); + }); + + group('Operator `or`', () { + const foo = Variant('foo'); + const bar = Variant('bar'); + const fooBar = Variant('foobar'); + + Widget buildWidgetForTest(Style style, Variant variant) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variant(variant)); + final icon = IconSpec.of(mixData); + + expect(icon.color, Colors.black); + + return const SizedBox( + height: 10, + ); + }, + ); + } + + testWidgets('should set the same icon color for 2 different variants', + (WidgetTester tester) async { + final style = Style( + (foo | bar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + buildWidgetForTest(style, foo), + buildWidgetForTest(style, bar), + ], + ), + ); + }); + + testWidgets('should set the same icon color for 3 different variants', + (WidgetTester tester) async { + final style = Style( + (foo | bar | fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + buildWidgetForTest(style, foo), + buildWidgetForTest(style, bar), + buildWidgetForTest(style, fooBar), + ], + ), + ); + }); + }); + + group('Operator `and`', () { + const foo = Variant('foo'); + const bar = Variant('bar'); + const fooBar = Variant('foobar'); + + Widget buildWidgetForTest(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = + MixData.create(context, style.variantList(variants)); + final icon = IconSpec.of(mixData); + + expect(icon.color, Colors.black); + + return const Placeholder(); + }, + ); + } + + testWidgets( + 'should set the icon color when 2 different variants are needed', + (WidgetTester tester) async { + final style = Style( + (foo & bar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + buildWidgetForTest(style, [foo, bar]), + ); + }); + + testWidgets( + 'should set the icon color when 3 different variants are needed', + (WidgetTester tester) async { + final style = Style( + (foo & bar & fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + buildWidgetForTest(style, [foo, bar, fooBar]), + ); + }); }); }); } diff --git a/test/src/variants/variant_test.dart b/test/src/variants/variant_test.dart index cc5c037fc..205c5b550 100644 --- a/test/src/variants/variant_test.dart +++ b/test/src/variants/variant_test.dart @@ -59,5 +59,20 @@ void main() { expect(multiVariant.variants, containsAll([variant1, variant2])); expect(multiVariant.operatorType, MultiVariantOperator.or); }); + + test( + 'MultiVariant.or should correctly create a MultiVariant when use operator between MultiVariant and a simple Variant', + () { + const variant1 = Variant('variant1'); + const variant2 = Variant('variant2'); + final multiVariant = MultiVariant.or(const [variant1, variant2]); + + const variant = Variant('variant'); + + final sut = MultiVariant.or([multiVariant, variant]); + + expect(sut.variants, containsAll([variant1, variant2, variant])); + expect(sut.operatorType, MultiVariantOperator.or); + }); }); } From 3f4be9ea0cec027ce56150a2e0179739c91f36e8 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:33:03 -0300 Subject: [PATCH 02/17] Add more test cases --- test/src/variants/variant_operation_test.dart | 162 ++++++++++++------ 1 file changed, 114 insertions(+), 48 deletions(-) diff --git a/test/src/variants/variant_operation_test.dart b/test/src/variants/variant_operation_test.dart index 8416322a0..2040f6058 100644 --- a/test/src/variants/variant_operation_test.dart +++ b/test/src/variants/variant_operation_test.dart @@ -23,29 +23,10 @@ void main() { }); group('Operator `or`', () { - const foo = Variant('foo'); - const bar = Variant('bar'); - const fooBar = Variant('foobar'); - - Widget buildWidgetForTest(Style style, Variant variant) { - return Builder( - builder: (context) { - final mixData = MixData.create(context, style.variant(variant)); - final icon = IconSpec.of(mixData); - - expect(icon.color, Colors.black); - - return const SizedBox( - height: 10, - ); - }, - ); - } - testWidgets('should set the same icon color for 2 different variants', (WidgetTester tester) async { final style = Style( - (foo | bar)( + (_foo | _bar)( icon.color.black(), ), ); @@ -53,8 +34,12 @@ void main() { await tester.pumpMaterialApp( Row( children: [ - buildWidgetForTest(style, foo), - buildWidgetForTest(style, bar), + _buildDefaultTestCase(style, [_foo]), + _buildDefaultTestCase(style, [_bar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), ], ), ); @@ -63,7 +48,7 @@ void main() { testWidgets('should set the same icon color for 3 different variants', (WidgetTester tester) async { final style = Style( - (foo | bar | fooBar)( + (_foo | _bar | _fooBar)( icon.color.black(), ), ); @@ -71,9 +56,13 @@ void main() { await tester.pumpMaterialApp( Row( children: [ - buildWidgetForTest(style, foo), - buildWidgetForTest(style, bar), - buildWidgetForTest(style, fooBar), + _buildDefaultTestCase(style, [_foo]), + _buildDefaultTestCase(style, [_bar]), + _buildDefaultTestCase(style, [_fooBar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_bar, _foo, _fooBar]), ], ), ); @@ -81,35 +70,25 @@ void main() { }); group('Operator `and`', () { - const foo = Variant('foo'); - const bar = Variant('bar'); - const fooBar = Variant('foobar'); - - Widget buildWidgetForTest(Style style, List variants) { - return Builder( - builder: (context) { - final mixData = - MixData.create(context, style.variantList(variants)); - final icon = IconSpec.of(mixData); - - expect(icon.color, Colors.black); - - return const Placeholder(); - }, - ); - } - testWidgets( 'should set the icon color when 2 different variants are needed', (WidgetTester tester) async { final style = Style( - (foo & bar)( + (_foo & _bar)( icon.color.black(), ), ); await tester.pumpMaterialApp( - buildWidgetForTest(style, [foo, bar]), + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), + _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), + // _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), + ], + ), ); }); @@ -117,15 +96,102 @@ void main() { 'should set the icon color when 3 different variants are needed', (WidgetTester tester) async { final style = Style( - (foo & bar & fooBar)( + (_foo & _bar & _fooBar)( icon.color.black(), ), ); await tester.pumpMaterialApp( - buildWidgetForTest(style, [foo, bar, fooBar]), + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_foo, _bar]), + // _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), + // _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar]), + _buildTestCaseToVerifyIfNull(style, [_foo]), + // _buildTestCaseToVerifyIfNull(style, [_fooBar]), + ], + ), ); }); }); }); + + group('Operators `and` and `or` in the same expression', () { + testWidgets( + 'should follow the order of operations and set the icon color when all conditions are met, case with | first', + (WidgetTester tester) async { + final style = Style( + (_foo | _bar & _fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + // _buildTestCaseToVerifyIfNull(style, [_foo]), + // _buildTestCaseToVerifyIfNull(style, [_bar]), + // _buildTestCaseToVerifyIfNull(style, [_fooBar]), + ], + ), + ); + }); + + testWidgets( + 'should follow the order of operations and set the icon color when all conditions are met, case with & first', + (WidgetTester tester) async { + final style = Style( + (_foo & _bar | _fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_fooBar]), + // _buildTestCaseToVerifyIfNull(style, [_foo]), + // _buildTestCaseToVerifyIfNull(style, [_bar]), + ], + ), + ); + }); + }); } + +Widget _buildDefaultTestCase(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variantList(variants)); + final icon = IconSpec.of(mixData); + + expect(icon.color, Colors.black); + return const SizedBox(); + }, + ); +} + +Widget _buildTestCaseToVerifyIfNull(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variantList(variants)); + final icon = IconSpec.of(mixData); + + expect(icon.color, null); + return const SizedBox(); + }, + ); +} + +const _foo = Variant('foo'); +const _bar = Variant('bar'); +const _fooBar = Variant('fooBar'); From b6f545cc5edd2be3c79d148507e6b30848212986 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:34:53 -0300 Subject: [PATCH 03/17] Refactoring the operators functionality --- lib/src/variants/variant.dart | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 1da1055c5..2411d54ec 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -249,7 +249,7 @@ class MultiVariant extends Variant { /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.and`. factory MultiVariant.and(Iterable variants) { return MultiVariant( - _expandVariants(variants), + variants, type: MultiVariantOperator.and, ); } @@ -259,7 +259,7 @@ class MultiVariant extends Variant { /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.or`. factory MultiVariant.or(Iterable variants) { return MultiVariant( - _expandVariants(variants), + variants, type: MultiVariantOperator.or, ); } @@ -312,12 +312,24 @@ class MultiVariant extends Variant { /// Here, `isMatched` will be true for `MultiVariantType.and` if both `variantA` and `variantB` are included in the provided list. /// For `MultiVariantType.or`, `isMatched` would be true if either `variantA` or `variantB` is in the list. bool matches(Iterable matchVariants) { - final matchSet = matchVariants.toSet(); - final variantSet = variants.toSet(); + final list = variants.map((variant) { + if (variant is MultiVariant) { + return variant.matches(matchVariants); + } else { + final List x = + variants.map((e) => matchVariants.contains(variant)).toList(); + return operatorType == MultiVariantOperator.and + ? x.every((e) => e == true) + : x.contains(true); + } + }).toList(); - return operatorType == MultiVariantOperator.and - ? variantSet.difference(matchSet).isEmpty - : variantSet.intersection(matchSet).isNotEmpty; + final result = operatorType == MultiVariantOperator.and + ? list.every((e) => e == true) + : list.contains(true); + print( + '${variants.first}(${list.first}) $operatorType ${variants.last}(${list.last}) => $result'); + return result; } /// Evaluates if the `MultiVariant` should be applied based on the build context. From 62204d66802c8932507fb65dfdb1882bcdc04d6b Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:05:41 -0300 Subject: [PATCH 04/17] Create more test case to operators feature --- test/src/factory/style_mix_test.dart | 126 +++++++++++- test/src/variants/variant_operation_test.dart | 194 +++++++++--------- test/src/variants/variant_test.dart | 15 -- 3 files changed, 217 insertions(+), 118 deletions(-) diff --git a/test/src/factory/style_mix_test.dart b/test/src/factory/style_mix_test.dart index 936c883e7..eef71ff5f 100644 --- a/test/src/factory/style_mix_test.dart +++ b/test/src/factory/style_mix_test.dart @@ -167,7 +167,7 @@ void main() { }); }); - group('Style.selectVariant', () { + group('Style.variant', () { const attr1 = MockDoubleScalarAttribute(1.0); const attr2 = MockIntScalarAttribute(2); const attr3 = MockBooleanScalarAttribute(true); @@ -184,15 +184,37 @@ void main() { expect(style.variants.length, 1); }); - test('with matching multi variant', () { + test('with matching multi variant `and` ', () { final multiVariant = MultiVariant.and(const [variantAttr1, variantAttr2]); final style = Style(attr1, attr2, multiVariant(attr3)); + + final a = style.variant(variantAttr1); + final b = a.variant(variantAttr2); + final c = style.variant(variantAttr1, variantAttr2); + + expect(a.styles.length, 2); + expect(a.variants.length, 1); + + expect(b.styles.length, 2); + expect(b.variants.length, 1); + + expect(c.styles.length, 3); + expect(c.variants.length, 0); + + expect(b, a); + expect(c == b, false); + }); + + test('with matching multivariant `or` ', () { + final multiVariant = MultiVariant.or(const [variantAttr1, variantAttr2]); + final style = Style(attr1, attr2, multiVariant(attr3)); + final firstStyle = style.variant(variantAttr1); - final secondStyle = firstStyle.variant(variantAttr2); + final secondStyle = style.variant(variantAttr2); final thirdStyle = style.variant(variantAttr1, variantAttr2); - expect(firstStyle.styles.length, 2); - expect(firstStyle.variants.length, 1); + expect(firstStyle.styles.length, 3); + expect(firstStyle.variants.length, 0); expect(secondStyle.styles.length, 3); expect(secondStyle.variants.length, 0); @@ -200,9 +222,51 @@ void main() { expect(thirdStyle.styles.length, 3); expect(thirdStyle.variants.length, 0); + expect(secondStyle, firstStyle); expect(secondStyle, thirdStyle); }); + test('with matching complex multivariant `or` ', () { + const variantAttr3 = Variant('variantAttr3'); + + final multiVariant = MultiVariant.and([ + variantAttr3, + MultiVariant.or(const [variantAttr1, variantAttr2]) + ]); + + final style = Style(attr1, attr2, multiVariant(attr3)); + + final a = style.variant(variantAttr1); + + expect(a.styles.length, 2); + expect(a.variants.length, 1); + + final b = style.variant(variantAttr2); + + expect(b.styles.length, 2); + expect(b.variants.length, 1); + + final c = style.variant(variantAttr3); + + expect(c.styles.length, 2); + expect(c.variants.length, 1); + + final d = style.variant(variantAttr1, variantAttr2); + + expect(d.styles.length, 2); + expect(d.variants.length, 1); + + final e = style.variant(variantAttr1, variantAttr3); + + expect(e.styles.length, 3); + expect(e.variants.length, 0); + + final f = style.variant(variantAttr2, variantAttr3); + + expect(f.styles.length, 3); + expect(f.variants.length, 0); + }); + test('with an Unmatched Variant', () { final style = Style(attr1, attr2); final updatedStyle = style.variant(variantAttr1); @@ -211,7 +275,7 @@ void main() { }); }); - group('Style.selectVariantList', () { + group('Style.variantList', () { const attr1 = MockDoubleScalarAttribute(1.0); const attr2 = MockIntScalarAttribute(2); const attr3 = MockBooleanScalarAttribute(true); @@ -251,6 +315,56 @@ void main() { expect(updatedMix, mix); }); + + test( + 'should return the same value if the parameter don`t satisfy the logic expression with 3 variants combined with `and` operator', + () { + const variantAttr3 = Variant('variantAttr3'); + + final style = Style( + box.color.red(), + (variantAttr1 & variantAttr2 & variantAttr3)( + icon.color.black(), + ), + ); + + final a = style.variantList([variantAttr3]); + final b = style.variantList([variantAttr2]); + final c = style.variantList([variantAttr1]); + final d = style.variantList([variantAttr2, variantAttr3]); + final e = style.variantList([variantAttr1, variantAttr3]); + final f = style.variantList([variantAttr2, variantAttr1]); + + expect(a, style); + expect(a, b); + expect(a, c); + expect(a, d); + expect(a, e); + expect(a, f); + }); + + test( + 'should return the same value if the parameter don`t satisfy the logic expression with 3 variants combined with `or` operator', + () { + const variantAttr3 = Variant('variantAttr3'); + + const extraVariant1 = Variant('extraVariant1'); + const extraVariant2 = Variant('extraVariant2'); + + final style = Style( + box.color.red(), + (variantAttr1 | variantAttr2 | variantAttr3)( + icon.color.black(), + ), + ); + + final a = style.variantList([extraVariant1]); + final b = style.variantList([extraVariant2]); + final c = style.variantList([extraVariant1, extraVariant2]); + + expect(a, b); + expect(a, c); + }); }); group('Style.pickVariants', () { diff --git a/test/src/variants/variant_operation_test.dart b/test/src/variants/variant_operation_test.dart index 2040f6058..eb9376400 100644 --- a/test/src/variants/variant_operation_test.dart +++ b/test/src/variants/variant_operation_test.dart @@ -21,100 +21,100 @@ void main() { expect(result.variants, contains(variant)); expect(result.variants, contains(otherVariant)); }); + }); + + group('Operator `or`', () { + testWidgets('should set the same icon color for 2 different variants', + (WidgetTester tester) async { + final style = Style( + (_foo | _bar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo]), + _buildDefaultTestCase(style, [_bar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), + ], + ), + ); + }); + + testWidgets('should set the same icon color for 3 different variants', + (WidgetTester tester) async { + final style = Style( + (_foo | _bar | _fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo]), + _buildDefaultTestCase(style, [_bar]), + _buildDefaultTestCase(style, [_fooBar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_bar, _foo, _fooBar]), + ], + ), + ); + }); + }); - group('Operator `or`', () { - testWidgets('should set the same icon color for 2 different variants', - (WidgetTester tester) async { - final style = Style( - (_foo | _bar)( - icon.color.black(), - ), - ); - - await tester.pumpMaterialApp( - Row( - children: [ - _buildDefaultTestCase(style, [_foo]), - _buildDefaultTestCase(style, [_bar]), - _buildDefaultTestCase(style, [_foo, _bar]), - _buildDefaultTestCase(style, [_foo, _fooBar]), - _buildDefaultTestCase(style, [_bar, _fooBar]), - _buildTestCaseToVerifyIfNull(style, [_fooBar]), - ], - ), - ); - }); - - testWidgets('should set the same icon color for 3 different variants', - (WidgetTester tester) async { - final style = Style( - (_foo | _bar | _fooBar)( - icon.color.black(), - ), - ); - - await tester.pumpMaterialApp( - Row( - children: [ - _buildDefaultTestCase(style, [_foo]), - _buildDefaultTestCase(style, [_bar]), - _buildDefaultTestCase(style, [_fooBar]), - _buildDefaultTestCase(style, [_foo, _bar]), - _buildDefaultTestCase(style, [_foo, _fooBar]), - _buildDefaultTestCase(style, [_bar, _fooBar]), - _buildDefaultTestCase(style, [_bar, _foo, _fooBar]), - ], - ), - ); - }); + group('Operator `and`', () { + testWidgets( + 'should set the icon color when 2 different variants are needed', + (WidgetTester tester) async { + final style = Style( + (_foo & _bar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), + _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + ], + ), + ); }); - group('Operator `and`', () { - testWidgets( - 'should set the icon color when 2 different variants are needed', - (WidgetTester tester) async { - final style = Style( - (_foo & _bar)( - icon.color.black(), - ), - ); - - await tester.pumpMaterialApp( - Row( - children: [ - _buildDefaultTestCase(style, [_foo, _bar]), - _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), - _buildTestCaseToVerifyIfNull(style, [_fooBar]), - _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), - // _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), - ], - ), - ); - }); - - testWidgets( - 'should set the icon color when 3 different variants are needed', - (WidgetTester tester) async { - final style = Style( - (_foo & _bar & _fooBar)( - icon.color.black(), - ), - ); - - await tester.pumpMaterialApp( - Row( - children: [ - _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), - _buildTestCaseToVerifyIfNull(style, [_foo, _bar]), - // _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), - // _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), - _buildTestCaseToVerifyIfNull(style, [_bar]), - _buildTestCaseToVerifyIfNull(style, [_foo]), - // _buildTestCaseToVerifyIfNull(style, [_fooBar]), - ], - ), - ); - }); + testWidgets( + 'should set the icon color when 3 different variants are needed', + (WidgetTester tester) async { + final style = Style( + (_foo & _bar & _fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar]), + _buildTestCaseToVerifyIfNull(style, [_foo]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar, _foo]), + _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), + ], + ), + ); }); }); @@ -123,7 +123,7 @@ void main() { 'should follow the order of operations and set the icon color when all conditions are met, case with | first', (WidgetTester tester) async { final style = Style( - (_foo | _bar & _fooBar)( + ((_foo | _bar) & _fooBar)( icon.color.black(), ), ); @@ -134,9 +134,9 @@ void main() { _buildDefaultTestCase(style, [_foo, _fooBar]), _buildDefaultTestCase(style, [_bar, _fooBar]), _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), - // _buildTestCaseToVerifyIfNull(style, [_foo]), - // _buildTestCaseToVerifyIfNull(style, [_bar]), - // _buildTestCaseToVerifyIfNull(style, [_fooBar]), + _buildTestCaseToVerifyIfNull(style, [_foo]), + _buildTestCaseToVerifyIfNull(style, [_bar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), ], ), ); @@ -159,8 +159,8 @@ void main() { _buildDefaultTestCase(style, [_foo, _fooBar]), _buildDefaultTestCase(style, [_bar, _fooBar]), _buildDefaultTestCase(style, [_fooBar]), - // _buildTestCaseToVerifyIfNull(style, [_foo]), - // _buildTestCaseToVerifyIfNull(style, [_bar]), + _buildTestCaseToVerifyIfNull(style, [_foo]), + _buildTestCaseToVerifyIfNull(style, [_bar]), ], ), ); diff --git a/test/src/variants/variant_test.dart b/test/src/variants/variant_test.dart index 205c5b550..cc5c037fc 100644 --- a/test/src/variants/variant_test.dart +++ b/test/src/variants/variant_test.dart @@ -59,20 +59,5 @@ void main() { expect(multiVariant.variants, containsAll([variant1, variant2])); expect(multiVariant.operatorType, MultiVariantOperator.or); }); - - test( - 'MultiVariant.or should correctly create a MultiVariant when use operator between MultiVariant and a simple Variant', - () { - const variant1 = Variant('variant1'); - const variant2 = Variant('variant2'); - final multiVariant = MultiVariant.or(const [variant1, variant2]); - - const variant = Variant('variant'); - - final sut = MultiVariant.or([multiVariant, variant]); - - expect(sut.variants, containsAll([variant1, variant2, variant])); - expect(sut.operatorType, MultiVariantOperator.or); - }); }); } From 9d0a48bc0562276251db58d156a5ebfc8cf6ac6f Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:06:05 -0300 Subject: [PATCH 05/17] fix bug in operators feature --- lib/src/factory/mix_provider_data.dart | 5 +++-- lib/src/factory/style_mix.dart | 6 +----- lib/src/variants/variant.dart | 2 -- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/src/factory/mix_provider_data.dart b/lib/src/factory/mix_provider_data.dart index 27a18b44d..5a5b12abd 100644 --- a/lib/src/factory/mix_provider_data.dart +++ b/lib/src/factory/mix_provider_data.dart @@ -29,11 +29,12 @@ class MixData with Comparable { _tokenResolver = resolver; factory MixData.create(BuildContext context, Style style) { - final styleMix = applyContextToVisualAttributes(context, style); + final attributeList = applyContextToVisualAttributes(context, style); final resolver = MixTokenResolver(context); - return MixData._(resolver: resolver, attributes: AttributeMap(styleMix)); + return MixData._( + resolver: resolver, attributes: AttributeMap(attributeList)); } /// Getter for [MixTokenResolver]. diff --git a/lib/src/factory/style_mix.dart b/lib/src/factory/style_mix.dart index 143e3efbf..ee3132b33 100644 --- a/lib/src/factory/style_mix.dart +++ b/lib/src/factory/style_mix.dart @@ -291,13 +291,9 @@ class Style with Comparable { for (final attr in variants.values) { if (attr is MultiVariantAttribute) { if (attr.matches(selectedVariants)) { - // if all variants match, add it to the matchedVariants matchedVariants.add(attr); } else { - // Remove any matching variants and add it as a new MultiVariantAttribute - // This allows to multiple matching variants to be removed - // For multi pass matching - remainingVariants.add(attr.remove(selectedVariants)); + remainingVariants.add(attr); } } else { if (selectedVariantSet.contains(attr.variant)) { diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 2411d54ec..e19a7e530 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -327,8 +327,6 @@ class MultiVariant extends Variant { final result = operatorType == MultiVariantOperator.and ? list.every((e) => e == true) : list.contains(true); - print( - '${variants.first}(${list.first}) $operatorType ${variants.last}(${list.last}) => $result'); return result; } From fed2a496f73e044aa55ccbabcbb2cdb0d2ebcfa9 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:16:23 -0300 Subject: [PATCH 06/17] create a new test case for variant --- test/src/variants/variant_test.dart | 62 ++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/test/src/variants/variant_test.dart b/test/src/variants/variant_test.dart index cc5c037fc..32da02c31 100644 --- a/test/src/variants/variant_test.dart +++ b/test/src/variants/variant_test.dart @@ -1,9 +1,32 @@ +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/variants/variant.dart'; +import 'package:mix/mix.dart'; import '../../helpers/testing_utils.dart'; void main() { + group('Variant', () { + testWidgets('should set attributes when variant matches, otherwise null', + (WidgetTester tester) async { + final style = Style( + icon.color.black(), + _foo( + box.height(10), + box.width(10), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo]), + _buildTestCaseToVerifyIfNull(style, [_bar]), + ], + ), + ); + }); + }); + group('MultiVariant', () { test('remove should remove the correct variants', () { const variant1 = Variant('variant1'); @@ -61,3 +84,40 @@ void main() { }); }); } + +Widget _buildDefaultTestCase(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variantList(variants)); + + final box = BoxSpec.of(mixData); + final icon = IconSpec.of(mixData); + + expect(box.height, 10); + expect(box.width, 10); + expect(icon.color, Colors.black); + + return const SizedBox(); + }, + ); +} + +Widget _buildTestCaseToVerifyIfNull(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variantList(variants)); + + final box = BoxSpec.of(mixData); + final icon = IconSpec.of(mixData); + + expect(box.height, null); + expect(box.width, null); + expect(icon.color, Colors.black); + + return const SizedBox(); + }, + ); +} + +const _foo = Variant('foo'); +const _bar = Variant('bar'); From 5dab5d4afcde6c34a4200b907e59e7fa4b09e3f7 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:17:04 -0300 Subject: [PATCH 07/17] refactor matches function --- lib/src/attributes/variant_attribute.dart | 4 ++++ lib/src/factory/style_mix.dart | 15 ++++----------- lib/src/variants/variant.dart | 10 ---------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/lib/src/attributes/variant_attribute.dart b/lib/src/attributes/variant_attribute.dart index 91f1c0681..cc6cf7cda 100644 --- a/lib/src/attributes/variant_attribute.dart +++ b/lib/src/attributes/variant_attribute.dart @@ -21,6 +21,9 @@ class VariantAttribute extends Attribute return VariantAttribute(variant, _style.merge(other._style)); } + bool matches(Iterable otherVariants) => + otherVariants.contains(variant); + @override Object get type => ObjectKey(variant); @@ -73,6 +76,7 @@ class MultiVariantAttribute extends VariantAttribute return VariantAttribute(variant, _style); } + @override bool matches(Iterable otherVariants) => variant.matches(otherVariants); diff --git a/lib/src/factory/style_mix.dart b/lib/src/factory/style_mix.dart index ee3132b33..54f03251e 100644 --- a/lib/src/factory/style_mix.dart +++ b/lib/src/factory/style_mix.dart @@ -288,19 +288,12 @@ class Style with Comparable { /// Loop over all VariantAttributes in variants only once instead of a nested loop, /// checking if each one matches with the selected variants. /// If it does, add it to the matchedVariants, else add it to remainingVariants. + for (final attr in variants.values) { - if (attr is MultiVariantAttribute) { - if (attr.matches(selectedVariants)) { - matchedVariants.add(attr); - } else { - remainingVariants.add(attr); - } + if (attr.matches(selectedVariants)) { + matchedVariants.add(attr); } else { - if (selectedVariantSet.contains(attr.variant)) { - matchedVariants.add(attr); - } else { - remainingVariants.add(attr); - } + remainingVariants.add(attr); } } diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index e19a7e530..10463670a 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -264,16 +264,6 @@ class MultiVariant extends Variant { ); } - static List _expandVariants(Iterable variants) { - return variants.expand((element) { - if (element is MultiVariant) { - return element.variants; - } else { - return [element]; - } - }).toList(); - } - /// Removes specified variants from this `MultiVariant`. /// /// This method returns a new variant after removing the specified [variantsToRemove]. From a55e91bcdf951885232402c2676ac43a6bcf707b Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:45:02 -0300 Subject: [PATCH 08/17] refactor matches in Variant class --- lib/src/factory/style_mix.dart | 5 ----- lib/src/variants/variant.dart | 17 ++++++----------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/src/factory/style_mix.dart b/lib/src/factory/style_mix.dart index 54f03251e..3f04902e5 100644 --- a/lib/src/factory/style_mix.dart +++ b/lib/src/factory/style_mix.dart @@ -281,14 +281,9 @@ class Style with Comparable { final matchedVariants = []; final remainingVariants = []; - /// Convert the selected variants list into a set for efficient lookup. - /// A set does not contain duplicate elements and lookup time is O(1), making it faster than list lookup. - final selectedVariantSet = selectedVariants.toSet(); - /// Loop over all VariantAttributes in variants only once instead of a nested loop, /// checking if each one matches with the selected variants. /// If it does, add it to the matchedVariants, else add it to remainingVariants. - for (final attr in variants.values) { if (attr.matches(selectedVariants)) { matchedVariants.add(attr); diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 10463670a..b350a7024 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -103,6 +103,10 @@ class Variant with Comparable { @override get props => [name]; + + bool matches(Iterable matchVariants) { + return matchVariants.contains(this); + } } /// A typedef for a function that determines if a specific context condition is met. @@ -301,18 +305,9 @@ class MultiVariant extends Variant { /// ``` /// Here, `isMatched` will be true for `MultiVariantType.and` if both `variantA` and `variantB` are included in the provided list. /// For `MultiVariantType.or`, `isMatched` would be true if either `variantA` or `variantB` is in the list. + @override bool matches(Iterable matchVariants) { - final list = variants.map((variant) { - if (variant is MultiVariant) { - return variant.matches(matchVariants); - } else { - final List x = - variants.map((e) => matchVariants.contains(variant)).toList(); - return operatorType == MultiVariantOperator.and - ? x.every((e) => e == true) - : x.contains(true); - } - }).toList(); + final list = variants.map((e) => e.matches(matchVariants)).toList(); final result = operatorType == MultiVariantOperator.and ? list.every((e) => e == true) From bb472d77a9d770a64756bb8ebe0e0e3e7226e71f Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:34:53 -0300 Subject: [PATCH 09/17] support multi operators as variant --- lib/src/variants/variant.dart | 20 +++- test/src/variants/variant_operation_test.dart | 111 ++++++++++++++++++ test/src/variants/variant_test.dart | 15 +++ 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 048e76ce3..1da1055c5 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -248,14 +248,30 @@ class MultiVariant extends Variant { /// /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.and`. factory MultiVariant.and(Iterable variants) { - return MultiVariant(variants, type: MultiVariantOperator.and); + return MultiVariant( + _expandVariants(variants), + type: MultiVariantOperator.and, + ); } /// Factory constructor to create a `MultiVariant` where any one of the provided variants needs to be active (`MultiVariantType.or`). /// /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.or`. factory MultiVariant.or(Iterable variants) { - return MultiVariant(variants, type: MultiVariantOperator.or); + return MultiVariant( + _expandVariants(variants), + type: MultiVariantOperator.or, + ); + } + + static List _expandVariants(Iterable variants) { + return variants.expand((element) { + if (element is MultiVariant) { + return element.variants; + } else { + return [element]; + } + }).toList(); } /// Removes specified variants from this `MultiVariant`. diff --git a/test/src/variants/variant_operation_test.dart b/test/src/variants/variant_operation_test.dart index bf3909963..8416322a0 100644 --- a/test/src/variants/variant_operation_test.dart +++ b/test/src/variants/variant_operation_test.dart @@ -1,6 +1,9 @@ +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('VariantOperation', () { test('should add variant with & operator', () { @@ -8,6 +11,7 @@ void main() { const otherVariant = Variant('other'); final result = otherVariant & variant; expect(result.variants, contains(variant)); + expect(result.variants, contains(otherVariant)); }); test('should add variant with | operator', () { @@ -15,6 +19,113 @@ void main() { const otherVariant = Variant('other'); final result = otherVariant | variant; expect(result.variants, contains(variant)); + expect(result.variants, contains(otherVariant)); + }); + + group('Operator `or`', () { + const foo = Variant('foo'); + const bar = Variant('bar'); + const fooBar = Variant('foobar'); + + Widget buildWidgetForTest(Style style, Variant variant) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variant(variant)); + final icon = IconSpec.of(mixData); + + expect(icon.color, Colors.black); + + return const SizedBox( + height: 10, + ); + }, + ); + } + + testWidgets('should set the same icon color for 2 different variants', + (WidgetTester tester) async { + final style = Style( + (foo | bar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + buildWidgetForTest(style, foo), + buildWidgetForTest(style, bar), + ], + ), + ); + }); + + testWidgets('should set the same icon color for 3 different variants', + (WidgetTester tester) async { + final style = Style( + (foo | bar | fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + buildWidgetForTest(style, foo), + buildWidgetForTest(style, bar), + buildWidgetForTest(style, fooBar), + ], + ), + ); + }); + }); + + group('Operator `and`', () { + const foo = Variant('foo'); + const bar = Variant('bar'); + const fooBar = Variant('foobar'); + + Widget buildWidgetForTest(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = + MixData.create(context, style.variantList(variants)); + final icon = IconSpec.of(mixData); + + expect(icon.color, Colors.black); + + return const Placeholder(); + }, + ); + } + + testWidgets( + 'should set the icon color when 2 different variants are needed', + (WidgetTester tester) async { + final style = Style( + (foo & bar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + buildWidgetForTest(style, [foo, bar]), + ); + }); + + testWidgets( + 'should set the icon color when 3 different variants are needed', + (WidgetTester tester) async { + final style = Style( + (foo & bar & fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + buildWidgetForTest(style, [foo, bar, fooBar]), + ); + }); }); }); } diff --git a/test/src/variants/variant_test.dart b/test/src/variants/variant_test.dart index cc5c037fc..205c5b550 100644 --- a/test/src/variants/variant_test.dart +++ b/test/src/variants/variant_test.dart @@ -59,5 +59,20 @@ void main() { expect(multiVariant.variants, containsAll([variant1, variant2])); expect(multiVariant.operatorType, MultiVariantOperator.or); }); + + test( + 'MultiVariant.or should correctly create a MultiVariant when use operator between MultiVariant and a simple Variant', + () { + const variant1 = Variant('variant1'); + const variant2 = Variant('variant2'); + final multiVariant = MultiVariant.or(const [variant1, variant2]); + + const variant = Variant('variant'); + + final sut = MultiVariant.or([multiVariant, variant]); + + expect(sut.variants, containsAll([variant1, variant2, variant])); + expect(sut.operatorType, MultiVariantOperator.or); + }); }); } From 59f0f9ea6ea0e7874677fb0e8f86f2de6a69167a Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:33:03 -0300 Subject: [PATCH 10/17] Add more test cases --- test/src/variants/variant_operation_test.dart | 162 ++++++++++++------ 1 file changed, 114 insertions(+), 48 deletions(-) diff --git a/test/src/variants/variant_operation_test.dart b/test/src/variants/variant_operation_test.dart index 8416322a0..2040f6058 100644 --- a/test/src/variants/variant_operation_test.dart +++ b/test/src/variants/variant_operation_test.dart @@ -23,29 +23,10 @@ void main() { }); group('Operator `or`', () { - const foo = Variant('foo'); - const bar = Variant('bar'); - const fooBar = Variant('foobar'); - - Widget buildWidgetForTest(Style style, Variant variant) { - return Builder( - builder: (context) { - final mixData = MixData.create(context, style.variant(variant)); - final icon = IconSpec.of(mixData); - - expect(icon.color, Colors.black); - - return const SizedBox( - height: 10, - ); - }, - ); - } - testWidgets('should set the same icon color for 2 different variants', (WidgetTester tester) async { final style = Style( - (foo | bar)( + (_foo | _bar)( icon.color.black(), ), ); @@ -53,8 +34,12 @@ void main() { await tester.pumpMaterialApp( Row( children: [ - buildWidgetForTest(style, foo), - buildWidgetForTest(style, bar), + _buildDefaultTestCase(style, [_foo]), + _buildDefaultTestCase(style, [_bar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), ], ), ); @@ -63,7 +48,7 @@ void main() { testWidgets('should set the same icon color for 3 different variants', (WidgetTester tester) async { final style = Style( - (foo | bar | fooBar)( + (_foo | _bar | _fooBar)( icon.color.black(), ), ); @@ -71,9 +56,13 @@ void main() { await tester.pumpMaterialApp( Row( children: [ - buildWidgetForTest(style, foo), - buildWidgetForTest(style, bar), - buildWidgetForTest(style, fooBar), + _buildDefaultTestCase(style, [_foo]), + _buildDefaultTestCase(style, [_bar]), + _buildDefaultTestCase(style, [_fooBar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_bar, _foo, _fooBar]), ], ), ); @@ -81,35 +70,25 @@ void main() { }); group('Operator `and`', () { - const foo = Variant('foo'); - const bar = Variant('bar'); - const fooBar = Variant('foobar'); - - Widget buildWidgetForTest(Style style, List variants) { - return Builder( - builder: (context) { - final mixData = - MixData.create(context, style.variantList(variants)); - final icon = IconSpec.of(mixData); - - expect(icon.color, Colors.black); - - return const Placeholder(); - }, - ); - } - testWidgets( 'should set the icon color when 2 different variants are needed', (WidgetTester tester) async { final style = Style( - (foo & bar)( + (_foo & _bar)( icon.color.black(), ), ); await tester.pumpMaterialApp( - buildWidgetForTest(style, [foo, bar]), + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), + _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), + // _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), + ], + ), ); }); @@ -117,15 +96,102 @@ void main() { 'should set the icon color when 3 different variants are needed', (WidgetTester tester) async { final style = Style( - (foo & bar & fooBar)( + (_foo & _bar & _fooBar)( icon.color.black(), ), ); await tester.pumpMaterialApp( - buildWidgetForTest(style, [foo, bar, fooBar]), + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_foo, _bar]), + // _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), + // _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar]), + _buildTestCaseToVerifyIfNull(style, [_foo]), + // _buildTestCaseToVerifyIfNull(style, [_fooBar]), + ], + ), ); }); }); }); + + group('Operators `and` and `or` in the same expression', () { + testWidgets( + 'should follow the order of operations and set the icon color when all conditions are met, case with | first', + (WidgetTester tester) async { + final style = Style( + (_foo | _bar & _fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + // _buildTestCaseToVerifyIfNull(style, [_foo]), + // _buildTestCaseToVerifyIfNull(style, [_bar]), + // _buildTestCaseToVerifyIfNull(style, [_fooBar]), + ], + ), + ); + }); + + testWidgets( + 'should follow the order of operations and set the icon color when all conditions are met, case with & first', + (WidgetTester tester) async { + final style = Style( + (_foo & _bar | _fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_fooBar]), + // _buildTestCaseToVerifyIfNull(style, [_foo]), + // _buildTestCaseToVerifyIfNull(style, [_bar]), + ], + ), + ); + }); + }); } + +Widget _buildDefaultTestCase(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variantList(variants)); + final icon = IconSpec.of(mixData); + + expect(icon.color, Colors.black); + return const SizedBox(); + }, + ); +} + +Widget _buildTestCaseToVerifyIfNull(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variantList(variants)); + final icon = IconSpec.of(mixData); + + expect(icon.color, null); + return const SizedBox(); + }, + ); +} + +const _foo = Variant('foo'); +const _bar = Variant('bar'); +const _fooBar = Variant('fooBar'); From 98ef1ec2a1cc15d19b5cb069d2d9143ba2b1b432 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:34:53 -0300 Subject: [PATCH 11/17] Refactoring the operators functionality --- lib/src/variants/variant.dart | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 1da1055c5..2411d54ec 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -249,7 +249,7 @@ class MultiVariant extends Variant { /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.and`. factory MultiVariant.and(Iterable variants) { return MultiVariant( - _expandVariants(variants), + variants, type: MultiVariantOperator.and, ); } @@ -259,7 +259,7 @@ class MultiVariant extends Variant { /// It initializes a `MultiVariant` with the given [variants] and sets the type to `MultiVariantType.or`. factory MultiVariant.or(Iterable variants) { return MultiVariant( - _expandVariants(variants), + variants, type: MultiVariantOperator.or, ); } @@ -312,12 +312,24 @@ class MultiVariant extends Variant { /// Here, `isMatched` will be true for `MultiVariantType.and` if both `variantA` and `variantB` are included in the provided list. /// For `MultiVariantType.or`, `isMatched` would be true if either `variantA` or `variantB` is in the list. bool matches(Iterable matchVariants) { - final matchSet = matchVariants.toSet(); - final variantSet = variants.toSet(); + final list = variants.map((variant) { + if (variant is MultiVariant) { + return variant.matches(matchVariants); + } else { + final List x = + variants.map((e) => matchVariants.contains(variant)).toList(); + return operatorType == MultiVariantOperator.and + ? x.every((e) => e == true) + : x.contains(true); + } + }).toList(); - return operatorType == MultiVariantOperator.and - ? variantSet.difference(matchSet).isEmpty - : variantSet.intersection(matchSet).isNotEmpty; + final result = operatorType == MultiVariantOperator.and + ? list.every((e) => e == true) + : list.contains(true); + print( + '${variants.first}(${list.first}) $operatorType ${variants.last}(${list.last}) => $result'); + return result; } /// Evaluates if the `MultiVariant` should be applied based on the build context. From 5c326798fbba9593bca9cfff16de7e03c07d5feb Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:05:41 -0300 Subject: [PATCH 12/17] Create more test case to operators feature --- test/src/factory/style_mix_test.dart | 167 ++++++++++++++- test/src/variants/variant_operation_test.dart | 194 +++++++++--------- test/src/variants/variant_test.dart | 15 -- 3 files changed, 258 insertions(+), 118 deletions(-) diff --git a/test/src/factory/style_mix_test.dart b/test/src/factory/style_mix_test.dart index 5c0bfff49..eef71ff5f 100644 --- a/test/src/factory/style_mix_test.dart +++ b/test/src/factory/style_mix_test.dart @@ -126,7 +126,48 @@ void main() { }); }); - group('Style.selectVariant', () { + group('Style.chooser() ', () { + test('Condition is True', () { + const trueAttribute = MockIntScalarAttribute(1); + const falseAttribute = MockDoubleScalarAttribute(2.0); + + final trueStyle = Style(trueAttribute); + final falseStyle = Style(falseAttribute); + + final mix = Style.chooser(true, trueStyle, falseStyle); + + expect(mix.styles.length, 1); + expect(mix.styles.values[0], trueAttribute); + }); + + test('Condition is False', () { + const trueAttribute = MockIntScalarAttribute(1); + const falseAttribute = MockDoubleScalarAttribute(2.0); + + final trueStyle = Style(trueAttribute); + final falseStyle = Style(falseAttribute); + + final mix = Style.chooser(false, trueStyle, falseStyle); + + expect(mix.styles.length, 1); + expect(mix.styles.values[0], falseAttribute); + }); + + test('Both ifTrue and ifFalse Are Same', () { + const sameAttribute = MockBooleanScalarAttribute(true); + + final sameStyle = Style(sameAttribute); + final otherStyle = Style(const MockBooleanScalarAttribute(false)); + + final style = Style.chooser(true, sameStyle, otherStyle); + + expect(style.styles.length, 1); + expect(style.styles.values[0], sameAttribute); + expect(sameStyle, style); + }); + }); + + group('Style.variant', () { const attr1 = MockDoubleScalarAttribute(1.0); const attr2 = MockIntScalarAttribute(2); const attr3 = MockBooleanScalarAttribute(true); @@ -143,15 +184,37 @@ void main() { expect(style.variants.length, 1); }); - test('with matching multi variant', () { + test('with matching multi variant `and` ', () { final multiVariant = MultiVariant.and(const [variantAttr1, variantAttr2]); final style = Style(attr1, attr2, multiVariant(attr3)); + + final a = style.variant(variantAttr1); + final b = a.variant(variantAttr2); + final c = style.variant(variantAttr1, variantAttr2); + + expect(a.styles.length, 2); + expect(a.variants.length, 1); + + expect(b.styles.length, 2); + expect(b.variants.length, 1); + + expect(c.styles.length, 3); + expect(c.variants.length, 0); + + expect(b, a); + expect(c == b, false); + }); + + test('with matching multivariant `or` ', () { + final multiVariant = MultiVariant.or(const [variantAttr1, variantAttr2]); + final style = Style(attr1, attr2, multiVariant(attr3)); + final firstStyle = style.variant(variantAttr1); - final secondStyle = firstStyle.variant(variantAttr2); + final secondStyle = style.variant(variantAttr2); final thirdStyle = style.variant(variantAttr1, variantAttr2); - expect(firstStyle.styles.length, 2); - expect(firstStyle.variants.length, 1); + expect(firstStyle.styles.length, 3); + expect(firstStyle.variants.length, 0); expect(secondStyle.styles.length, 3); expect(secondStyle.variants.length, 0); @@ -159,9 +222,51 @@ void main() { expect(thirdStyle.styles.length, 3); expect(thirdStyle.variants.length, 0); + expect(secondStyle, firstStyle); expect(secondStyle, thirdStyle); }); + test('with matching complex multivariant `or` ', () { + const variantAttr3 = Variant('variantAttr3'); + + final multiVariant = MultiVariant.and([ + variantAttr3, + MultiVariant.or(const [variantAttr1, variantAttr2]) + ]); + + final style = Style(attr1, attr2, multiVariant(attr3)); + + final a = style.variant(variantAttr1); + + expect(a.styles.length, 2); + expect(a.variants.length, 1); + + final b = style.variant(variantAttr2); + + expect(b.styles.length, 2); + expect(b.variants.length, 1); + + final c = style.variant(variantAttr3); + + expect(c.styles.length, 2); + expect(c.variants.length, 1); + + final d = style.variant(variantAttr1, variantAttr2); + + expect(d.styles.length, 2); + expect(d.variants.length, 1); + + final e = style.variant(variantAttr1, variantAttr3); + + expect(e.styles.length, 3); + expect(e.variants.length, 0); + + final f = style.variant(variantAttr2, variantAttr3); + + expect(f.styles.length, 3); + expect(f.variants.length, 0); + }); + test('with an Unmatched Variant', () { final style = Style(attr1, attr2); final updatedStyle = style.variant(variantAttr1); @@ -170,7 +275,7 @@ void main() { }); }); - group('Style.selectVariantList', () { + group('Style.variantList', () { const attr1 = MockDoubleScalarAttribute(1.0); const attr2 = MockIntScalarAttribute(2); const attr3 = MockBooleanScalarAttribute(true); @@ -210,6 +315,56 @@ void main() { expect(updatedMix, mix); }); + + test( + 'should return the same value if the parameter don`t satisfy the logic expression with 3 variants combined with `and` operator', + () { + const variantAttr3 = Variant('variantAttr3'); + + final style = Style( + box.color.red(), + (variantAttr1 & variantAttr2 & variantAttr3)( + icon.color.black(), + ), + ); + + final a = style.variantList([variantAttr3]); + final b = style.variantList([variantAttr2]); + final c = style.variantList([variantAttr1]); + final d = style.variantList([variantAttr2, variantAttr3]); + final e = style.variantList([variantAttr1, variantAttr3]); + final f = style.variantList([variantAttr2, variantAttr1]); + + expect(a, style); + expect(a, b); + expect(a, c); + expect(a, d); + expect(a, e); + expect(a, f); + }); + + test( + 'should return the same value if the parameter don`t satisfy the logic expression with 3 variants combined with `or` operator', + () { + const variantAttr3 = Variant('variantAttr3'); + + const extraVariant1 = Variant('extraVariant1'); + const extraVariant2 = Variant('extraVariant2'); + + final style = Style( + box.color.red(), + (variantAttr1 | variantAttr2 | variantAttr3)( + icon.color.black(), + ), + ); + + final a = style.variantList([extraVariant1]); + final b = style.variantList([extraVariant2]); + final c = style.variantList([extraVariant1, extraVariant2]); + + expect(a, b); + expect(a, c); + }); }); group('Style.pickVariants', () { diff --git a/test/src/variants/variant_operation_test.dart b/test/src/variants/variant_operation_test.dart index 2040f6058..eb9376400 100644 --- a/test/src/variants/variant_operation_test.dart +++ b/test/src/variants/variant_operation_test.dart @@ -21,100 +21,100 @@ void main() { expect(result.variants, contains(variant)); expect(result.variants, contains(otherVariant)); }); + }); + + group('Operator `or`', () { + testWidgets('should set the same icon color for 2 different variants', + (WidgetTester tester) async { + final style = Style( + (_foo | _bar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo]), + _buildDefaultTestCase(style, [_bar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), + ], + ), + ); + }); + + testWidgets('should set the same icon color for 3 different variants', + (WidgetTester tester) async { + final style = Style( + (_foo | _bar | _fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo]), + _buildDefaultTestCase(style, [_bar]), + _buildDefaultTestCase(style, [_fooBar]), + _buildDefaultTestCase(style, [_foo, _bar]), + _buildDefaultTestCase(style, [_foo, _fooBar]), + _buildDefaultTestCase(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_bar, _foo, _fooBar]), + ], + ), + ); + }); + }); - group('Operator `or`', () { - testWidgets('should set the same icon color for 2 different variants', - (WidgetTester tester) async { - final style = Style( - (_foo | _bar)( - icon.color.black(), - ), - ); - - await tester.pumpMaterialApp( - Row( - children: [ - _buildDefaultTestCase(style, [_foo]), - _buildDefaultTestCase(style, [_bar]), - _buildDefaultTestCase(style, [_foo, _bar]), - _buildDefaultTestCase(style, [_foo, _fooBar]), - _buildDefaultTestCase(style, [_bar, _fooBar]), - _buildTestCaseToVerifyIfNull(style, [_fooBar]), - ], - ), - ); - }); - - testWidgets('should set the same icon color for 3 different variants', - (WidgetTester tester) async { - final style = Style( - (_foo | _bar | _fooBar)( - icon.color.black(), - ), - ); - - await tester.pumpMaterialApp( - Row( - children: [ - _buildDefaultTestCase(style, [_foo]), - _buildDefaultTestCase(style, [_bar]), - _buildDefaultTestCase(style, [_fooBar]), - _buildDefaultTestCase(style, [_foo, _bar]), - _buildDefaultTestCase(style, [_foo, _fooBar]), - _buildDefaultTestCase(style, [_bar, _fooBar]), - _buildDefaultTestCase(style, [_bar, _foo, _fooBar]), - ], - ), - ); - }); + group('Operator `and`', () { + testWidgets( + 'should set the icon color when 2 different variants are needed', + (WidgetTester tester) async { + final style = Style( + (_foo & _bar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), + _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + ], + ), + ); }); - group('Operator `and`', () { - testWidgets( - 'should set the icon color when 2 different variants are needed', - (WidgetTester tester) async { - final style = Style( - (_foo & _bar)( - icon.color.black(), - ), - ); - - await tester.pumpMaterialApp( - Row( - children: [ - _buildDefaultTestCase(style, [_foo, _bar]), - _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), - _buildTestCaseToVerifyIfNull(style, [_fooBar]), - _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), - // _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), - ], - ), - ); - }); - - testWidgets( - 'should set the icon color when 3 different variants are needed', - (WidgetTester tester) async { - final style = Style( - (_foo & _bar & _fooBar)( - icon.color.black(), - ), - ); - - await tester.pumpMaterialApp( - Row( - children: [ - _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), - _buildTestCaseToVerifyIfNull(style, [_foo, _bar]), - // _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), - // _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), - _buildTestCaseToVerifyIfNull(style, [_bar]), - _buildTestCaseToVerifyIfNull(style, [_foo]), - // _buildTestCaseToVerifyIfNull(style, [_fooBar]), - ], - ), - ); - }); + testWidgets( + 'should set the icon color when 3 different variants are needed', + (WidgetTester tester) async { + final style = Style( + (_foo & _bar & _fooBar)( + icon.color.black(), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar]), + _buildTestCaseToVerifyIfNull(style, [_foo]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar, _foo]), + _buildTestCaseToVerifyIfNull(style, [_foo, _fooBar]), + _buildTestCaseToVerifyIfNull(style, [_bar, _fooBar]), + ], + ), + ); }); }); @@ -123,7 +123,7 @@ void main() { 'should follow the order of operations and set the icon color when all conditions are met, case with | first', (WidgetTester tester) async { final style = Style( - (_foo | _bar & _fooBar)( + ((_foo | _bar) & _fooBar)( icon.color.black(), ), ); @@ -134,9 +134,9 @@ void main() { _buildDefaultTestCase(style, [_foo, _fooBar]), _buildDefaultTestCase(style, [_bar, _fooBar]), _buildDefaultTestCase(style, [_foo, _bar, _fooBar]), - // _buildTestCaseToVerifyIfNull(style, [_foo]), - // _buildTestCaseToVerifyIfNull(style, [_bar]), - // _buildTestCaseToVerifyIfNull(style, [_fooBar]), + _buildTestCaseToVerifyIfNull(style, [_foo]), + _buildTestCaseToVerifyIfNull(style, [_bar]), + _buildTestCaseToVerifyIfNull(style, [_fooBar]), ], ), ); @@ -159,8 +159,8 @@ void main() { _buildDefaultTestCase(style, [_foo, _fooBar]), _buildDefaultTestCase(style, [_bar, _fooBar]), _buildDefaultTestCase(style, [_fooBar]), - // _buildTestCaseToVerifyIfNull(style, [_foo]), - // _buildTestCaseToVerifyIfNull(style, [_bar]), + _buildTestCaseToVerifyIfNull(style, [_foo]), + _buildTestCaseToVerifyIfNull(style, [_bar]), ], ), ); diff --git a/test/src/variants/variant_test.dart b/test/src/variants/variant_test.dart index 205c5b550..cc5c037fc 100644 --- a/test/src/variants/variant_test.dart +++ b/test/src/variants/variant_test.dart @@ -59,20 +59,5 @@ void main() { expect(multiVariant.variants, containsAll([variant1, variant2])); expect(multiVariant.operatorType, MultiVariantOperator.or); }); - - test( - 'MultiVariant.or should correctly create a MultiVariant when use operator between MultiVariant and a simple Variant', - () { - const variant1 = Variant('variant1'); - const variant2 = Variant('variant2'); - final multiVariant = MultiVariant.or(const [variant1, variant2]); - - const variant = Variant('variant'); - - final sut = MultiVariant.or([multiVariant, variant]); - - expect(sut.variants, containsAll([variant1, variant2, variant])); - expect(sut.operatorType, MultiVariantOperator.or); - }); }); } From 2c0209c14f1f125b133021fcd4eae17e410b39d9 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:06:05 -0300 Subject: [PATCH 13/17] fix bug in operators feature --- lib/src/factory/mix_provider_data.dart | 5 +++-- lib/src/factory/style_mix.dart | 6 +----- lib/src/variants/variant.dart | 2 -- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/src/factory/mix_provider_data.dart b/lib/src/factory/mix_provider_data.dart index d308b4eee..35ec15af8 100644 --- a/lib/src/factory/mix_provider_data.dart +++ b/lib/src/factory/mix_provider_data.dart @@ -28,11 +28,12 @@ class MixData with Comparable { _tokenResolver = resolver; factory MixData.create(BuildContext context, Style style) { - final styleMix = applyContextToVisualAttributes(context, style); + final attributeList = applyContextToVisualAttributes(context, style); final resolver = MixTokenResolver(context); - return MixData._(resolver: resolver, attributes: AttributeMap(styleMix)); + return MixData._( + resolver: resolver, attributes: AttributeMap(attributeList)); } /// Getter for [MixTokenResolver]. diff --git a/lib/src/factory/style_mix.dart b/lib/src/factory/style_mix.dart index aa58bf575..211d89820 100644 --- a/lib/src/factory/style_mix.dart +++ b/lib/src/factory/style_mix.dart @@ -276,13 +276,9 @@ class Style with Comparable { for (final attr in variants.values) { if (attr is MultiVariantAttribute) { if (attr.matches(selectedVariants)) { - // if all variants match, add it to the matchedVariants matchedVariants.add(attr); } else { - // Remove any matching variants and add it as a new MultiVariantAttribute - // This allows to multiple matching variants to be removed - // For multi pass matching - remainingVariants.add(attr.remove(selectedVariants)); + remainingVariants.add(attr); } } else { if (selectedVariantSet.contains(attr.variant)) { diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 2411d54ec..e19a7e530 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -327,8 +327,6 @@ class MultiVariant extends Variant { final result = operatorType == MultiVariantOperator.and ? list.every((e) => e == true) : list.contains(true); - print( - '${variants.first}(${list.first}) $operatorType ${variants.last}(${list.last}) => $result'); return result; } From 47e9a024965f3a86f80f0e18e8a19d801849a379 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:16:23 -0300 Subject: [PATCH 14/17] create a new test case for variant --- test/src/variants/variant_test.dart | 62 ++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/test/src/variants/variant_test.dart b/test/src/variants/variant_test.dart index cc5c037fc..32da02c31 100644 --- a/test/src/variants/variant_test.dart +++ b/test/src/variants/variant_test.dart @@ -1,9 +1,32 @@ +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/variants/variant.dart'; +import 'package:mix/mix.dart'; import '../../helpers/testing_utils.dart'; void main() { + group('Variant', () { + testWidgets('should set attributes when variant matches, otherwise null', + (WidgetTester tester) async { + final style = Style( + icon.color.black(), + _foo( + box.height(10), + box.width(10), + ), + ); + + await tester.pumpMaterialApp( + Row( + children: [ + _buildDefaultTestCase(style, [_foo]), + _buildTestCaseToVerifyIfNull(style, [_bar]), + ], + ), + ); + }); + }); + group('MultiVariant', () { test('remove should remove the correct variants', () { const variant1 = Variant('variant1'); @@ -61,3 +84,40 @@ void main() { }); }); } + +Widget _buildDefaultTestCase(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variantList(variants)); + + final box = BoxSpec.of(mixData); + final icon = IconSpec.of(mixData); + + expect(box.height, 10); + expect(box.width, 10); + expect(icon.color, Colors.black); + + return const SizedBox(); + }, + ); +} + +Widget _buildTestCaseToVerifyIfNull(Style style, List variants) { + return Builder( + builder: (context) { + final mixData = MixData.create(context, style.variantList(variants)); + + final box = BoxSpec.of(mixData); + final icon = IconSpec.of(mixData); + + expect(box.height, null); + expect(box.width, null); + expect(icon.color, Colors.black); + + return const SizedBox(); + }, + ); +} + +const _foo = Variant('foo'); +const _bar = Variant('bar'); From 7cf1461250dceaab6976841e15d394ce02bfa6d2 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:17:04 -0300 Subject: [PATCH 15/17] refactor matches function --- lib/src/attributes/variant_attribute.dart | 4 ++++ lib/src/factory/style_mix.dart | 15 ++++----------- lib/src/variants/variant.dart | 10 ---------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/lib/src/attributes/variant_attribute.dart b/lib/src/attributes/variant_attribute.dart index 91f1c0681..cc6cf7cda 100644 --- a/lib/src/attributes/variant_attribute.dart +++ b/lib/src/attributes/variant_attribute.dart @@ -21,6 +21,9 @@ class VariantAttribute extends Attribute return VariantAttribute(variant, _style.merge(other._style)); } + bool matches(Iterable otherVariants) => + otherVariants.contains(variant); + @override Object get type => ObjectKey(variant); @@ -73,6 +76,7 @@ class MultiVariantAttribute extends VariantAttribute return VariantAttribute(variant, _style); } + @override bool matches(Iterable otherVariants) => variant.matches(otherVariants); diff --git a/lib/src/factory/style_mix.dart b/lib/src/factory/style_mix.dart index 211d89820..211bf5ee3 100644 --- a/lib/src/factory/style_mix.dart +++ b/lib/src/factory/style_mix.dart @@ -273,19 +273,12 @@ class Style with Comparable { /// Loop over all VariantAttributes in variants only once instead of a nested loop, /// checking if each one matches with the selected variants. /// If it does, add it to the matchedVariants, else add it to remainingVariants. + for (final attr in variants.values) { - if (attr is MultiVariantAttribute) { - if (attr.matches(selectedVariants)) { - matchedVariants.add(attr); - } else { - remainingVariants.add(attr); - } + if (attr.matches(selectedVariants)) { + matchedVariants.add(attr); } else { - if (selectedVariantSet.contains(attr.variant)) { - matchedVariants.add(attr); - } else { - remainingVariants.add(attr); - } + remainingVariants.add(attr); } } diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index e19a7e530..10463670a 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -264,16 +264,6 @@ class MultiVariant extends Variant { ); } - static List _expandVariants(Iterable variants) { - return variants.expand((element) { - if (element is MultiVariant) { - return element.variants; - } else { - return [element]; - } - }).toList(); - } - /// Removes specified variants from this `MultiVariant`. /// /// This method returns a new variant after removing the specified [variantsToRemove]. From f65cd2e07696203038207d5d858857f60a5b3e87 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:45:02 -0300 Subject: [PATCH 16/17] refactor matches in Variant class --- lib/src/factory/style_mix.dart | 5 ----- lib/src/variants/variant.dart | 17 ++++++----------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/src/factory/style_mix.dart b/lib/src/factory/style_mix.dart index 211bf5ee3..a5de25ba1 100644 --- a/lib/src/factory/style_mix.dart +++ b/lib/src/factory/style_mix.dart @@ -266,14 +266,9 @@ class Style with Comparable { final matchedVariants = []; final remainingVariants = []; - /// Convert the selected variants list into a set for efficient lookup. - /// A set does not contain duplicate elements and lookup time is O(1), making it faster than list lookup. - final selectedVariantSet = selectedVariants.toSet(); - /// Loop over all VariantAttributes in variants only once instead of a nested loop, /// checking if each one matches with the selected variants. /// If it does, add it to the matchedVariants, else add it to remainingVariants. - for (final attr in variants.values) { if (attr.matches(selectedVariants)) { matchedVariants.add(attr); diff --git a/lib/src/variants/variant.dart b/lib/src/variants/variant.dart index 10463670a..b350a7024 100644 --- a/lib/src/variants/variant.dart +++ b/lib/src/variants/variant.dart @@ -103,6 +103,10 @@ class Variant with Comparable { @override get props => [name]; + + bool matches(Iterable matchVariants) { + return matchVariants.contains(this); + } } /// A typedef for a function that determines if a specific context condition is met. @@ -301,18 +305,9 @@ class MultiVariant extends Variant { /// ``` /// Here, `isMatched` will be true for `MultiVariantType.and` if both `variantA` and `variantB` are included in the provided list. /// For `MultiVariantType.or`, `isMatched` would be true if either `variantA` or `variantB` is in the list. + @override bool matches(Iterable matchVariants) { - final list = variants.map((variant) { - if (variant is MultiVariant) { - return variant.matches(matchVariants); - } else { - final List x = - variants.map((e) => matchVariants.contains(variant)).toList(); - return operatorType == MultiVariantOperator.and - ? x.every((e) => e == true) - : x.contains(true); - } - }).toList(); + final list = variants.map((e) => e.matches(matchVariants)).toList(); final result = operatorType == MultiVariantOperator.and ? list.every((e) => e == true) From d006c8f5227cecb243cabb4c54931ed4659a7e00 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62367544+tilucasoli@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:26:27 -0300 Subject: [PATCH 17/17] Update style_mix_test.dart --- test/src/factory/style_mix_test.dart | 41 ---------------------------- 1 file changed, 41 deletions(-) diff --git a/test/src/factory/style_mix_test.dart b/test/src/factory/style_mix_test.dart index eef71ff5f..dace08645 100644 --- a/test/src/factory/style_mix_test.dart +++ b/test/src/factory/style_mix_test.dart @@ -126,47 +126,6 @@ void main() { }); }); - group('Style.chooser() ', () { - test('Condition is True', () { - const trueAttribute = MockIntScalarAttribute(1); - const falseAttribute = MockDoubleScalarAttribute(2.0); - - final trueStyle = Style(trueAttribute); - final falseStyle = Style(falseAttribute); - - final mix = Style.chooser(true, trueStyle, falseStyle); - - expect(mix.styles.length, 1); - expect(mix.styles.values[0], trueAttribute); - }); - - test('Condition is False', () { - const trueAttribute = MockIntScalarAttribute(1); - const falseAttribute = MockDoubleScalarAttribute(2.0); - - final trueStyle = Style(trueAttribute); - final falseStyle = Style(falseAttribute); - - final mix = Style.chooser(false, trueStyle, falseStyle); - - expect(mix.styles.length, 1); - expect(mix.styles.values[0], falseAttribute); - }); - - test('Both ifTrue and ifFalse Are Same', () { - const sameAttribute = MockBooleanScalarAttribute(true); - - final sameStyle = Style(sameAttribute); - final otherStyle = Style(const MockBooleanScalarAttribute(false)); - - final style = Style.chooser(true, sameStyle, otherStyle); - - expect(style.styles.length, 1); - expect(style.styles.values[0], sameAttribute); - expect(sameStyle, style); - }); - }); - group('Style.variant', () { const attr1 = MockDoubleScalarAttribute(1.0); const attr2 = MockIntScalarAttribute(2);