From e5be48d819d87253fb012124b82e26e4d236cb08 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Tue, 28 Nov 2023 11:37:55 -0700 Subject: [PATCH 01/24] Add initial validateRequiredProps implementation in generated files --- .../builder/codegen/accessors_generator.dart | 26 ++++ .../component_declaration/annotations.dart | 6 +- .../builder_helpers.dart | 112 +++++++++--------- 3 files changed, 86 insertions(+), 58 deletions(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 9e509961e..5f4f7a131 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -99,6 +99,10 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato } generatedClass.write(_generateAccessors()); + + // todo add validate required props here + generatedClass.write('\n// sydney\n'); + generatedClass.writeln('}'); generatedClass.writeln(); return generatedClass.toString(); @@ -149,6 +153,8 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato StringBuffer output = StringBuffer(); + final requiredPropKeys = []; + node.members.whereType().where((field) => !field.isStatic).forEach((field) { T? getConstantAnnotation(AnnotatedNode member, String name, T value) { return member.metadata.any((annotation) => annotation.name.name == name) ? value : null; @@ -237,6 +243,11 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato if (requiredErrorMessage.isNotEmpty) { constantValue += ', errorMessage: ${stringLiteral(requiredErrorMessage)}'; } + + // todo sydney add key namespace test + requiredPropKeys.add(' if(!props.containsKey($keyValue)) {\n' + ' throw MissingRequiredPropsError(\'Required prop `$accessorName` is missing.\');\n' + '}\n'); } constantValue += ')'; @@ -350,8 +361,23 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato String staticVariablesImpl = ' /* GENERATED CONSTANTS */\n$constantsImpl$keyConstantsImpl\n$listImpl$keyListImpl'; + // todo add it here - collect late props from above output.write(staticVariablesImpl); + + if(requiredPropKeys.isNotEmpty) { + // todo sydney only include late props + final validateRequiredPropsMethod = ' @override\n' + ' @mustCallSuper\n' + ' void validateRequiredProps() {\n' + // todo sydney why is this failing? + // ' super.validateRequiredProps();\n' + // ' debugger();\n' + ' ${requiredPropKeys.join('\n')}\n' + ' }\n'; + output.write(validateRequiredPropsMethod); + } + return output.toString(); } } diff --git a/lib/src/component_declaration/annotations.dart b/lib/src/component_declaration/annotations.dart index c854df485..887b5268d 100644 --- a/lib/src/component_declaration/annotations.dart +++ b/lib/src/component_declaration/annotations.dart @@ -15,6 +15,8 @@ // Dummy annotations that would be used by Pub code generator library over_react.component_declaration.annotations; +export 'package:meta/meta.dart' show mustCallSuper; + /// Annotation used with the `over_react` builder to declare a `UiFactory` for a component. /// /// @Factory() @@ -274,7 +276,7 @@ class AbstractComponent2 implements AbstractComponent { // ignore: deprecated_me /// /// Classes using this annotation must include the abstract `props` getter. /// -/// __Deprecated.__ Use the `@Props()` annotation instead if you need to make use of an annotation argument. +/// __Deprecated.__ Use the `@Props()` annotation instead if you need to make use of an annotation argument. /// Otherwise, this can be removed completely. Will be removed in the 4.0.0 release of over_react. @Deprecated('Use the @Props() annotation if you need to make use of an annotation argument. Otherwise, this can be removed completely. Will be removed in the 4.0.0 release of over_react.') class PropsMixin implements TypedMap { @@ -298,7 +300,7 @@ class PropsMixin implements TypedMap { /// /// Classes using this annotation must include the abstract `state` getter. /// -/// __Deprecated.__ Use the `@State()` annotation instead if you need to make use of an annotation argument. +/// __Deprecated.__ Use the `@State()` annotation instead if you need to make use of an annotation argument. /// Otherwise, this can be removed completely. Will be removed in the 4.0.0 release of over_react. @Deprecated('Use the @State() annotation if you need to make use of an annotation argument. Otherwise, this can be removed completely. Will be removed in the 4.0.0 release of over_react.') class StateMixin implements TypedMap { diff --git a/lib/src/component_declaration/builder_helpers.dart b/lib/src/component_declaration/builder_helpers.dart index 105766ea4..3d809e8b9 100644 --- a/lib/src/component_declaration/builder_helpers.dart +++ b/lib/src/component_declaration/builder_helpers.dart @@ -132,62 +132,62 @@ abstract class UiProps extends component_base.UiProps with GeneratedClass { /// and [addUnconsumedDomProps]. @toBeGenerated PropsMetaCollection get staticMeta => throw UngeneratedError(member: #meta); - @override - @visibleForOverriding - @mustCallSuper - void validateRequiredProps() { - super.validateRequiredProps(); - // This fails when staticMeta isn't generated, so return early for now so tests don't fail. - // FIXME(null-safety) generate a static implementation of this instead in FED-1886, and remove this - return; - - // ignore: dead_code - List? missingRequiredProps; - List? nullNonNullableRequiredProps; - - for (final meta in staticMeta.all) { - for (final prop in meta.props) { - if (prop.isRequired) { - if (prop.isNullable) { - if (!props.containsKey(prop.key)) { - (missingRequiredProps ??= []).add(prop); - } - } else { - // Avoid looking up the key twice. - if (props[prop.key] == null) { - if (props.containsKey(prop.key)) { - (nullNonNullableRequiredProps ??= []).add(prop); - } else { - (missingRequiredProps ??= []).add(prop); - } - } - } - } - } - } - - if (missingRequiredProps == null && nullNonNullableRequiredProps == null) { - return; - } - - String formatPropKey(String propKey) => '`$propKey`'; - - final messageSegments = []; - if (missingRequiredProps != null) { - messageSegments.add('Required props are missing: ${missingRequiredProps.map((prop) { - var propMessage = formatPropKey(prop.key); - if (prop.isNullable) propMessage += ' (can be null, but must be specified)'; - return propMessage; - }).join(' ,')}.'); - } - if (nullNonNullableRequiredProps != null) { - messageSegments - .add('Required non-nullable props are null: ${nullNonNullableRequiredProps.map((prop) { - return formatPropKey(prop.key); - }).join(' ,')}.'); - } - throw MissingRequiredPropsError(messageSegments.join(' ')); - } + // @override + // @visibleForOverriding + // @mustCallSuper + // void validateRequiredProps() { + // super.validateRequiredProps(); + // // This fails when staticMeta isn't generated, so return early for now so tests don't fail. + // // FIXME(null-safety) generate a static implementation of this instead in FED-1886, and remove this + // return; + // + // // ignore: dead_code + // List? missingRequiredProps; + // List? nullNonNullableRequiredProps; + // + // for (final meta in staticMeta.all) { + // for (final prop in meta.props) { + // if (prop.isRequired) { + // if (prop.isNullable) { + // if (!props.containsKey(prop.key)) { + // (missingRequiredProps ??= []).add(prop); + // } + // } else { + // // Avoid looking up the key twice. + // if (props[prop.key] == null) { + // if (props.containsKey(prop.key)) { + // (nullNonNullableRequiredProps ??= []).add(prop); + // } else { + // (missingRequiredProps ??= []).add(prop); + // } + // } + // } + // } + // } + // } + // + // if (missingRequiredProps == null && nullNonNullableRequiredProps == null) { + // return; + // } + // + // String formatPropKey(String propKey) => '`$propKey`'; + // + // final messageSegments = []; + // if (missingRequiredProps != null) { + // messageSegments.add('Required props are missing: ${missingRequiredProps.map((prop) { + // var propMessage = formatPropKey(prop.key); + // if (prop.isNullable) propMessage += ' (can be null, but must be specified)'; + // return propMessage; + // }).join(' ,')}.'); + // } + // if (nullNonNullableRequiredProps != null) { + // messageSegments + // .add('Required non-nullable props are null: ${nullNonNullableRequiredProps.map((prop) { + // return formatPropKey(prop.key); + // }).join(' ,')}.'); + // } + // throw MissingRequiredPropsError(messageSegments.join(' ')); + // } } class MissingRequiredPropsError extends Error { From 9b2f6fa4da3de86555c2532e3db86bfa6e15d319 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Tue, 28 Nov 2023 14:24:22 -0700 Subject: [PATCH 02/24] Limit validation to late props --- lib/src/builder/codegen/accessors_generator.dart | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 5f4f7a131..0ada90b7c 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -100,9 +100,6 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato generatedClass.write(_generateAccessors()); - // todo add validate required props here - generatedClass.write('\n// sydney\n'); - generatedClass.writeln('}'); generatedClass.writeln(); return generatedClass.toString(); @@ -204,6 +201,11 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato annotationCount++; isRequired = true; isPotentiallyNullable = true; + + // todo sydney add key namespace test + requiredPropKeys.add(' if(!props.containsKey($keyValue)) {\n' + ' throw MissingRequiredPropsError(\'Required prop `$accessorName` is missing.\');\n' + '}\n'); } if (accessorMeta != null) { @@ -243,11 +245,6 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato if (requiredErrorMessage.isNotEmpty) { constantValue += ', errorMessage: ${stringLiteral(requiredErrorMessage)}'; } - - // todo sydney add key namespace test - requiredPropKeys.add(' if(!props.containsKey($keyValue)) {\n' - ' throw MissingRequiredPropsError(\'Required prop `$accessorName` is missing.\');\n' - '}\n'); } constantValue += ')'; @@ -366,8 +363,7 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato if(requiredPropKeys.isNotEmpty) { - // todo sydney only include late props - final validateRequiredPropsMethod = ' @override\n' + final validateRequiredPropsMethod = '\n @override\n' ' @mustCallSuper\n' ' void validateRequiredProps() {\n' // todo sydney why is this failing? From 7a904ea3941e9cfaf4caca6e3d6db075fd8288ed Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Wed, 29 Nov 2023 16:39:54 -0700 Subject: [PATCH 03/24] Add base test file --- ...l_safety_validate_required_props_test.dart | 230 ++++++++++++++++++ ...over_react_component_declaration_test.dart | 4 + 2 files changed, 234 insertions(+) create mode 100644 test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart new file mode 100644 index 000000000..958814335 --- /dev/null +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart @@ -0,0 +1,230 @@ +// Copyright 2023 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:developer'; + +import 'package:over_react/over_react.dart'; +import 'package:react_testing_library/react_testing_library.dart' as rtl; +import 'package:test/test.dart'; + +import '../../../../test_util/test_util.dart' hide mount; + +part 'null_safety_validate_required_props_test.over_react.g.dart'; + +void main() { + group('(New boilerplate) validates required props:', () { + group('non-nullable required prop - throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render( + (ComponentTest() + ..requiredDynamic = true + ..requiredNullable = true + + // ..requiredAndLengthLimited = [1,2] + )(), + ); + }, throwsA(isA().having((e) => e.toString(), 'toString value', + contains('Required prop `requiredNonNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render( + (ComponentTest() + ..required = true + ..nullable = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsNoPropTypeWarnings); + + expect(() { + view.rerender((ComponentTest() + ..required = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsPropRequiredError('ComponentTestProps.nullable')); + }); + }); + + group('throwing when a prop is required and set to null', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + // ..required = null + ..nullable = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsPropRequiredError('ComponentTestProps.required')); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render( + (ComponentTest() + ..required = true + ..nullable = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsNoPropTypeWarnings); + + expect(() { + view.rerender((ComponentTest() + // ..required = null + ..nullable = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsPropRequiredError('ComponentTestProps.required')); + }); + }); + + group('throwing when a prop is nullable and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..required = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsPropRequiredError('ComponentTestProps.nullable')); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render( + (ComponentTest() + ..required = true + ..nullable = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsNoPropTypeWarnings); + + expect(() { + view.rerender((ComponentTest() + ..required = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsPropRequiredError('ComponentTestProps.nullable')); + }); + }); + + group('not throwing when a prop is required and set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..nullable = true + ..required = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsNoPropTypeWarnings); + }); + + test('on re-render', () { + var view = rtl.render((ComponentTest() + ..required = true + ..nullable = true + // ..requiredAndLengthLimited = [1,2] + )() + ); + + expect(() { + view.rerender((ComponentTest() + ..required = true + ..nullable = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsNoPropTypeWarnings); + }); + }); + + group('not throwing when a prop is nullable and set to null', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..nullable = null + // ..requiredAndLengthLimited = [1,2] + ..required = true + )()); + }, logsNoPropTypeWarnings); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render( + (ComponentTest() + ..required = true + ..nullable = true + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsNoPropTypeWarnings); + + expect(() { + view.rerender((ComponentTest() + ..required = true + ..nullable = null + // ..requiredAndLengthLimited = [1,2] + )()); + }, logsNoPropTypeWarnings); + }); + }); + + group('when a consumer propType function is also provided', () { + test('required fires', () { + expect(() { + rtl.render((ComponentTest() + ..nullable = null + ..required = true + )()); + }, logsPropValueError('null', 'ComponentTestProps.requiredAndLengthLimited')); + }); + + test('consumer check fires', () { + expect(() { + rtl.render((ComponentTest() + ..required = true + ..nullable = true + // ..requiredAndLengthLimited = [1] + )()); + }, logsPropValueError('1', 'ComponentTestProps.requiredAndLengthLimited')); + }); + }); + }); +} + +// ignore: undefined_identifier, invalid_assignment +UiFactory ComponentTest = _$ComponentTest; + +mixin ComponentTestProps on UiProps { + late bool required; + + late bool? requiredNullable; + + late dynamic requiredDynamic; + + bool? nullable; +} + +class ComponentTestComponent extends UiComponent2 { + // todo test default props + // todo test other boilerplates + @override + render() => Dom.div()(); +} diff --git a/test/over_react_component_declaration_test.dart b/test/over_react_component_declaration_test.dart index d2c8924a6..46fee88a9 100644 --- a/test/over_react_component_declaration_test.dart +++ b/test/over_react_component_declaration_test.dart @@ -65,6 +65,7 @@ import 'over_react/component_declaration/builder_integration_tests/new_boilerpla import 'over_react/component_declaration/builder_integration_tests/new_boilerplate/component_integration_test.dart' as new_boilerplate_component_integration_test; import 'over_react/component_declaration/builder_integration_tests/new_boilerplate/component_integration_verbose_syntax_test.dart' as new_boilerplate_component_integration_verbose_syntax_test; import 'over_react/component_declaration/builder_integration_tests/new_boilerplate/constant_required_accessor_integration_test.dart' as new_boilerplate_constant_required_accessor_integration_test; +import 'over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart' as new_boilerplate_null_safety_validate_required_props_test; import 'over_react/component_declaration/builder_integration_tests/new_boilerplate/covariant_accessor_override_integration_test.dart' as new_boilerplate_covariant_accessor_override_integration_test; import 'over_react/component_declaration/builder_integration_tests/new_boilerplate/do_not_generate_accessor_integration_test.dart' as new_boilerplate_do_not_generate_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/new_boilerplate/function_component_test.dart' as new_boilerplate_function_component_integration_test; @@ -136,4 +137,7 @@ main() { new_boilerplate_required_accessor_integration_test.main(); new_boilerplate_stateful_component_integration_test.main(); new_boilerplate_unassigned_prop_integration_test.main(); + + // FIXME: Move these tests to only be run opted in to null safety. + new_boilerplate_null_safety_validate_required_props_test.main(); } From 8d9c6aec2619b8933edee5a970f00adc8f539376 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Wed, 29 Nov 2023 17:30:00 -0700 Subject: [PATCH 04/24] Add basic tests --- ...l_safety_validate_required_props_test.dart | 250 +++++++----------- ...over_react_component_declaration_test.html | 1 + 2 files changed, 90 insertions(+), 161 deletions(-) diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart index 958814335..fffddf859 100644 --- a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart @@ -18,192 +18,119 @@ import 'package:over_react/over_react.dart'; import 'package:react_testing_library/react_testing_library.dart' as rtl; import 'package:test/test.dart'; -import '../../../../test_util/test_util.dart' hide mount; - part 'null_safety_validate_required_props_test.over_react.g.dart'; void main() { group('(New boilerplate) validates required props:', () { - group('non-nullable required prop - throws when a prop is required and not set', () { - test('on mount', () { - expect(() { - rtl.render( - (ComponentTest() + group('non-nullable required prop', () { + group('throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() ..requiredDynamic = true ..requiredNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); - // ..requiredAndLengthLimited = [1,2] - )(), - ); - }, throwsA(isA().having((e) => e.toString(), 'toString value', - contains('Required prop `requiredNonNullable` is missing.')))); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render( - (ComponentTest() - ..required = true - ..nullable = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsNoPropTypeWarnings); - - expect(() { - view.rerender((ComponentTest() - ..required = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsPropRequiredError('ComponentTestProps.nullable')); - }); - }); - - group('throwing when a prop is required and set to null', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - // ..required = null - ..nullable = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsPropRequiredError('ComponentTestProps.required')); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render( - (ComponentTest() - ..required = true - ..nullable = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsNoPropTypeWarnings); - - expect(() { - view.rerender((ComponentTest() - // ..required = null - ..nullable = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsPropRequiredError('ComponentTestProps.required')); - }); - }); - - group('throwing when a prop is nullable and not set', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - ..required = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsPropRequiredError('ComponentTestProps.nullable')); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render( - (ComponentTest() - ..required = true - ..nullable = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsNoPropTypeWarnings); - - expect(() { - view.rerender((ComponentTest() - ..required = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsPropRequiredError('ComponentTestProps.nullable')); + expect(() { + view.rerender((ComponentTest() + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); }); - }); - group('not throwing when a prop is required and set', () { - test('on mount', () { + test('does not throw when the prop is set', () { expect(() { rtl.render((ComponentTest() - ..nullable = true - ..required = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsNoPropTypeWarnings); - }); - - test('on re-render', () { - var view = rtl.render((ComponentTest() - ..required = true - ..nullable = true - // ..requiredAndLengthLimited = [1,2] - )() - ); - - expect(() { - view.rerender((ComponentTest() - ..required = true - ..nullable = true - // ..requiredAndLengthLimited = [1,2] + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true )()); - }, logsNoPropTypeWarnings); + }, returnsNormally); }); }); - group('not throwing when a prop is nullable and set to null', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - ..nullable = null - // ..requiredAndLengthLimited = [1,2] - ..required = true - )()); - }, logsNoPropTypeWarnings); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render( - (ComponentTest() - ..required = true - ..nullable = true - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsNoPropTypeWarnings); + group('nullable required prop', () { + group('throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..requiredDynamic = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); - expect(() { - view.rerender((ComponentTest() - ..required = true - ..nullable = null - // ..requiredAndLengthLimited = [1,2] - )()); - }, logsNoPropTypeWarnings); + expect(() { + view.rerender((ComponentTest() + ..requiredDynamic = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); }); - }); - group('when a consumer propType function is also provided', () { - test('required fires', () { + test('does not throw when the prop is set to null', () { expect(() { rtl.render((ComponentTest() - ..nullable = null - ..required = true + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = null )()); - }, logsPropValueError('null', 'ComponentTestProps.requiredAndLengthLimited')); + }, returnsNormally); }); - test('consumer check fires', () { + test('does not throw when the prop is set', () { expect(() { rtl.render((ComponentTest() - ..required = true - ..nullable = true - // ..requiredAndLengthLimited = [1] + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true )()); - }, logsPropValueError('1', 'ComponentTestProps.requiredAndLengthLimited')); + }, returnsNormally); }); }); }); @@ -213,10 +140,11 @@ void main() { UiFactory ComponentTest = _$ComponentTest; mixin ComponentTestProps on UiProps { - late bool required; + late bool requiredNonNullable; late bool? requiredNullable; + // todo is this necessary? late dynamic requiredDynamic; bool? nullable; diff --git a/test/over_react_component_declaration_test.html b/test/over_react_component_declaration_test.html index 14154933b..3cdbb0e12 100644 --- a/test/over_react_component_declaration_test.html +++ b/test/over_react_component_declaration_test.html @@ -21,6 +21,7 @@ + From 6fed86b7cf0e3775a0b1fe486abd22997259d19c Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Fri, 1 Dec 2023 10:47:43 -0700 Subject: [PATCH 05/24] Add validation escape hatches and tests --- .../builder/codegen/accessors_generator.dart | 15 ++++--- .../component_declaration/annotations.dart | 7 +++ .../component_declaration/component_base.dart | 24 +++++++++-- ...l_safety_validate_required_props_test.dart | 43 ++++++++++++++++++- 4 files changed, 77 insertions(+), 12 deletions(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 0ada90b7c..d56ab1b38 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -150,7 +150,7 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato StringBuffer output = StringBuffer(); - final requiredPropKeys = []; + final requiredPropKeys = []; node.members.whereType().where((field) => !field.isStatic).forEach((field) { T? getConstantAnnotation(AnnotatedNode member, String name, T value) { @@ -161,6 +161,8 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato final requiredProp = getConstantAnnotation(field, 'requiredProp', annotations.requiredProp); final nullableRequiredProp = getConstantAnnotation(field, 'nullableRequiredProp', annotations.nullableRequiredProp); + final disableRequiredPropValidation = getConstantAnnotation( + field, 'disableRequiredPropValidation', annotations.disableRequiredPropValidation); if (accessorMeta?.doNotGenerate == true) { return; @@ -203,9 +205,11 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato isPotentiallyNullable = true; // todo sydney add key namespace test - requiredPropKeys.add(' if(!props.containsKey($keyValue)) {\n' - ' throw MissingRequiredPropsError(\'Required prop `$accessorName` is missing.\');\n' - '}\n'); + if (disableRequiredPropValidation == null) { + requiredPropKeys.add(' if(!props.containsKey($keyValue)) {\n' + ' throw MissingRequiredPropsError(\'Required prop `$accessorName` is missing.\');\n' + '}\n'); + } } if (accessorMeta != null) { @@ -361,8 +365,7 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato // todo add it here - collect late props from above output.write(staticVariablesImpl); - - if(requiredPropKeys.isNotEmpty) { + if (requiredPropKeys.isNotEmpty) { final validateRequiredPropsMethod = '\n @override\n' ' @mustCallSuper\n' ' void validateRequiredProps() {\n' diff --git a/lib/src/component_declaration/annotations.dart b/lib/src/component_declaration/annotations.dart index 887b5268d..e2431eacc 100644 --- a/lib/src/component_declaration/annotations.dart +++ b/lib/src/component_declaration/annotations.dart @@ -383,3 +383,10 @@ class Accessor { abstract class TypedMap { String? get keyNamespace; } + +// todo add doc comment +const _DisableRequiredPropValidation disableRequiredPropValidation = _DisableRequiredPropValidation(); + +class _DisableRequiredPropValidation { + const _DisableRequiredPropValidation(); +} diff --git a/lib/src/component_declaration/component_base.dart b/lib/src/component_declaration/component_base.dart index 70c9131b8..58b807865 100644 --- a/lib/src/component_declaration/component_base.dart +++ b/lib/src/component_declaration/component_base.dart @@ -622,10 +622,12 @@ abstract class UiProps extends MapBase assert(_validateChildren(childArguments.length == 1 ? childArguments.single : childArguments)); // FIXME(null-safety) finalize this implementation and add escape-hatch to opt out in FED-1886 - assert(() { - validateRequiredProps(); - return true; - }()); + if(_shouldValidateRequiredProps) { + assert(() { + validateRequiredProps(); + return true; + }()); + } // Use `build` instead of using emulated function behavior to work around DDC issue // https://github.com/dart-lang/sdk/issues/29904 @@ -677,9 +679,23 @@ abstract class UiProps extends MapBase } // FIXME(null-safety) document and generate overrides in FED-1886 + // todo add doc comment @visibleForOverriding @mustCallSuper void validateRequiredProps() {} + + // todo doc comment + var _shouldValidateRequiredProps = true; + + /// Prevents [validateRequiredProps] from being called. + /// + /// Allows for an element to have multiple test IDs to prevent overwriting when cloning elements or components. + /// + /// > For use in a testing environment (when [testMode] is true). + void disableRequiredPropValidation() { + // todo should this also override proptype warnings?? + _shouldValidateRequiredProps = false; + } } /// A class that declares the `_map` getter shared by [PropsMapViewMixin]/[StateMapViewMixin] and [MapViewMixin]. diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart index fffddf859..9165f0e34 100644 --- a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:developer'; - import 'package:over_react/over_react.dart'; import 'package:react_testing_library/react_testing_library.dart' as rtl; import 'package:test/test.dart'; +import '../../../../test_util/test_util.dart'; + part 'null_safety_validate_required_props_test.over_react.g.dart'; void main() { @@ -133,6 +133,35 @@ void main() { }, returnsNormally); }); }); + + test('@disableRequiredPropValidation annotation turns off validation for specific props', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, allOf(returnsNormally, + logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), + logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), + logsPropRequiredError('ComponentTestProps.ref'), + )); + }); + + test('disableRequiredPropValidation method turns off validation for component usage', () { + expect(() { + rtl.render((ComponentTest() + ..disableRequiredPropValidation() + )()); + }, allOf(returnsNormally, + logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), + logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), + logsPropRequiredError('ComponentTestProps.ref'), + logsPropRequiredError('ComponentTestProps.requiredNonNullable'), + logsPropRequiredError('ComponentTestProps.requiredNullable'), + logsPropRequiredError('ComponentTestProps.requiredDynamic'), + )); + }); }); } @@ -147,6 +176,16 @@ mixin ComponentTestProps on UiProps { // todo is this necessary? late dynamic requiredDynamic; + @disableRequiredPropValidation + late bool disabledRequiredProp; + + @disableRequiredPropValidation + late bool? disabledNullableRequiredProp; + + @disableRequiredPropValidation + @override + late dynamic ref; + bool? nullable; } From 111826da830eaccebb62839393027ad81fe2729b Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Fri, 1 Dec 2023 10:59:43 -0700 Subject: [PATCH 06/24] Add other boilerplate tests --- lib/src/builder/codegen.dart | 9 +- .../builder/codegen/accessors_generator.dart | 36 +- .../builder/codegen/component_generator.dart | 16 +- .../codegen/typed_map_impl_generator.dart | 33 +- lib/src/builder/codegen/util.dart | 3 +- .../builder/parsing/member_association.dart | 4 +- .../builder/parsing/members/component.dart | 4 +- lib/src/builder/parsing/members_from_ast.dart | 14 +- lib/src/builder/parsing/util.dart | 6 +- lib/src/util/react_util.dart | 4 + .../dummy_component.over_react.g.dart | 9 + .../prop_typedef_fixtures.over_react.g.dart | 31 ++ ...l_safety_validate_required_props_test.dart | 205 ++++++++++++ ...l_safety_validate_required_props_test.dart | 200 ++++++++++++ ...date_required_props_test.over_react.g.dart | 308 ++++++++++++++++++ ...l_safety_validate_required_props_test.dart | 199 +++++++++++ ...date_required_props_test.over_react.g.dart | 251 ++++++++++++++ ...over_react_component_declaration_test.dart | 6 + test/vm_tests/builder/codegen_test.dart | 1 + 19 files changed, 1284 insertions(+), 55 deletions(-) create mode 100644 test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart create mode 100644 test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart create mode 100644 test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart create mode 100644 test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart create mode 100644 test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart diff --git a/lib/src/builder/codegen.dart b/lib/src/builder/codegen.dart index e780d3ae0..68dfa440d 100644 --- a/lib/src/builder/codegen.dart +++ b/lib/src/builder/codegen.dart @@ -89,11 +89,13 @@ class ImplGenerator { } void _generateLegacyAbstractProps(LegacyAbstractPropsDeclaration declaration) { - _generateUsing(TypedMapAccessorsGenerator.legacyAbstractProps(declaration, nullSafety: nullSafety)); + _generateUsing( + TypedMapAccessorsGenerator.legacyAbstractProps(declaration, nullSafety: nullSafety)); } void _generateLegacyAbstractState(LegacyAbstractStateDeclaration declaration) { - _generateUsing(TypedMapAccessorsGenerator.legacyAbstractState(declaration, nullSafety: nullSafety)); + _generateUsing( + TypedMapAccessorsGenerator.legacyAbstractState(declaration, nullSafety: nullSafety)); } void _generatePropsMixin(PropsMixinDeclaration declaration) { @@ -115,6 +117,7 @@ class ImplGenerator { void _generatePropsMapViewOrFunctionComponent( PropsMapViewOrFunctionComponentDeclaration declaration) { - _generateUsing(TypedMapImplGenerator.propsMapViewOrFunctionComponent(declaration, nullSafety: nullSafety)); + _generateUsing( + TypedMapImplGenerator.propsMapViewOrFunctionComponent(declaration, nullSafety: nullSafety)); } } diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index d56ab1b38..7ff2405c0 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -30,23 +30,23 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato // Provide factory constructors since they make invocations easier to read and tell apart // than all of the different subclasses. - factory TypedMapAccessorsGenerator.propsMixin(PropsMixinDeclaration decl, {required bool nullSafety}) = - _TypedMapMixinAccessorsGenerator.props; + factory TypedMapAccessorsGenerator.propsMixin(PropsMixinDeclaration decl, + {required bool nullSafety}) = _TypedMapMixinAccessorsGenerator.props; - factory TypedMapAccessorsGenerator.stateMixin(StateMixinDeclaration decl, {required bool nullSafety}) = - _TypedMapMixinAccessorsGenerator.state; + factory TypedMapAccessorsGenerator.stateMixin(StateMixinDeclaration decl, + {required bool nullSafety}) = _TypedMapMixinAccessorsGenerator.state; - factory TypedMapAccessorsGenerator.legacyProps(LegacyClassComponentDeclaration decl, {required bool nullSafety}) = - _LegacyTypedMapAccessorsGenerator.props; + factory TypedMapAccessorsGenerator.legacyProps(LegacyClassComponentDeclaration decl, + {required bool nullSafety}) = _LegacyTypedMapAccessorsGenerator.props; - factory TypedMapAccessorsGenerator.legacyState(LegacyClassComponentDeclaration decl, {required bool nullSafety}) = - _LegacyTypedMapAccessorsGenerator.state; + factory TypedMapAccessorsGenerator.legacyState(LegacyClassComponentDeclaration decl, + {required bool nullSafety}) = _LegacyTypedMapAccessorsGenerator.state; - factory TypedMapAccessorsGenerator.legacyAbstractProps(LegacyAbstractPropsDeclaration decl, {required bool nullSafety}) = - _LegacyTypedMapAccessorsGenerator.abstractProps; + factory TypedMapAccessorsGenerator.legacyAbstractProps(LegacyAbstractPropsDeclaration decl, + {required bool nullSafety}) = _LegacyTypedMapAccessorsGenerator.abstractProps; - factory TypedMapAccessorsGenerator.legacyAbstractState(LegacyAbstractStateDeclaration decl, {required bool nullSafety}) = - _LegacyTypedMapAccessorsGenerator.abstractState; + factory TypedMapAccessorsGenerator.legacyAbstractState(LegacyAbstractStateDeclaration decl, + {required bool nullSafety}) = _LegacyTypedMapAccessorsGenerator.abstractState; TypedMapType get type; @@ -440,25 +440,29 @@ class _LegacyTypedMapAccessorsGenerator extends TypedMapAccessorsGenerator { @override final bool nullSafety; - _LegacyTypedMapAccessorsGenerator.props(LegacyClassComponentDeclaration decl, {required this.nullSafety}) + _LegacyTypedMapAccessorsGenerator.props(LegacyClassComponentDeclaration decl, + {required this.nullSafety}) : member = decl.props, names = TypedMapNames(decl.props.name.name), version = decl.version, type = TypedMapType.props; - _LegacyTypedMapAccessorsGenerator.state(LegacyClassComponentDeclaration decl, {required this.nullSafety}) + _LegacyTypedMapAccessorsGenerator.state(LegacyClassComponentDeclaration decl, + {required this.nullSafety}) : member = decl.state!, names = TypedMapNames(decl.state!.name.name), version = decl.version, type = TypedMapType.state; - _LegacyTypedMapAccessorsGenerator.abstractProps(LegacyAbstractPropsDeclaration decl, {required this.nullSafety}) + _LegacyTypedMapAccessorsGenerator.abstractProps(LegacyAbstractPropsDeclaration decl, + {required this.nullSafety}) : member = decl.props, names = TypedMapNames(decl.props.name.name), version = decl.version, type = TypedMapType.abstractProps; - _LegacyTypedMapAccessorsGenerator.abstractState(LegacyAbstractStateDeclaration decl, {required this.nullSafety}) + _LegacyTypedMapAccessorsGenerator.abstractState(LegacyAbstractStateDeclaration decl, + {required this.nullSafety}) : member = decl.state, names = TypedMapNames(decl.state.name.name), version = decl.version, diff --git a/lib/src/builder/codegen/component_generator.dart b/lib/src/builder/codegen/component_generator.dart index ecc5e72a6..729947189 100644 --- a/lib/src/builder/codegen/component_generator.dart +++ b/lib/src/builder/codegen/component_generator.dart @@ -23,10 +23,11 @@ abstract class ComponentGenerator extends BoilerplateDeclarationGenerator { // Provide factory constructors since they make invocations easier to read and tell apart // than all of the different subclasses. - factory ComponentGenerator(ClassComponentDeclaration declaration, {required bool nullSafety}) = _ComponentGenerator; + factory ComponentGenerator(ClassComponentDeclaration declaration, {required bool nullSafety}) = + _ComponentGenerator; - factory ComponentGenerator.legacy(LegacyClassComponentDeclaration declaration, {required bool nullSafety}) = - _LegacyComponentGenerator; + factory ComponentGenerator.legacy(LegacyClassComponentDeclaration declaration, + {required bool nullSafety}) = _LegacyComponentGenerator; TypedMapNames get propsNames; TypedMapNames? get stateNames; @@ -79,7 +80,8 @@ abstract class ComponentGenerator extends BoilerplateDeclarationGenerator { ..writeln(' }') ..writeln() ..writeln(' @override ') - ..writeln(' ${propsNames.jsMapImplName} typedPropsFactoryJs(JsBackedMap${nullSafety ? '?' : ''} backingMap)' + ..writeln( + ' ${propsNames.jsMapImplName} typedPropsFactoryJs(JsBackedMap${nullSafety ? '?' : ''} backingMap)' ' => ${propsNames.jsMapImplName}(backingMap);') ..writeln(); } @@ -108,13 +110,15 @@ abstract class ComponentGenerator extends BoilerplateDeclarationGenerator { ..writeln(' }') ..writeln() ..writeln(' @override ') - ..writeln(' ${stateNames.jsMapImplName} typedStateFactoryJs(JsBackedMap${nullSafety ? '?' : ''} backingMap)' + ..writeln( + ' ${stateNames.jsMapImplName} typedStateFactoryJs(JsBackedMap${nullSafety ? '?' : ''} backingMap)' ' => ${stateNames.jsMapImplName}(backingMap);') ..writeln(); } outputContentsBuffer ..writeln(' @override') - ..writeln(' ${stateNames.implName} typedStateFactory(Map${nullSafety ? '?' : ''} backingMap)' + ..writeln( + ' ${stateNames.implName} typedStateFactory(Map${nullSafety ? '?' : ''} backingMap)' ' => ${stateNames.implName}(backingMap);') ..writeln(); } diff --git a/lib/src/builder/codegen/typed_map_impl_generator.dart b/lib/src/builder/codegen/typed_map_impl_generator.dart index 8b9ba35e3..46588340b 100644 --- a/lib/src/builder/codegen/typed_map_impl_generator.dart +++ b/lib/src/builder/codegen/typed_map_impl_generator.dart @@ -27,21 +27,21 @@ abstract class TypedMapImplGenerator extends BoilerplateDeclarationGenerator { // Provide factory constructors since they make invocations easier to read and tell apart // than all of the different subclasses. - factory TypedMapImplGenerator.legacyProps(LegacyClassComponentDeclaration declaration, {required bool nullSafety}) = - _LegacyTypedMapImplGenerator.props; + factory TypedMapImplGenerator.legacyProps(LegacyClassComponentDeclaration declaration, + {required bool nullSafety}) = _LegacyTypedMapImplGenerator.props; - factory TypedMapImplGenerator.legacyState(LegacyClassComponentDeclaration declaration, {required bool nullSafety}) = - _LegacyTypedMapImplGenerator.state; + factory TypedMapImplGenerator.legacyState(LegacyClassComponentDeclaration declaration, + {required bool nullSafety}) = _LegacyTypedMapImplGenerator.state; - factory TypedMapImplGenerator.props(ClassComponentDeclaration declaration, {required bool nullSafety}) = - _TypedMapImplGenerator.props; + factory TypedMapImplGenerator.props(ClassComponentDeclaration declaration, + {required bool nullSafety}) = _TypedMapImplGenerator.props; - factory TypedMapImplGenerator.state(ClassComponentDeclaration declaration, {required bool nullSafety}) = - _TypedMapImplGenerator.state; + factory TypedMapImplGenerator.state(ClassComponentDeclaration declaration, + {required bool nullSafety}) = _TypedMapImplGenerator.state; factory TypedMapImplGenerator.propsMapViewOrFunctionComponent( - PropsMapViewOrFunctionComponentDeclaration declaration, {required bool nullSafety}) = - _TypedMapImplGenerator.propsMapViewOrFunctionComponent; + PropsMapViewOrFunctionComponentDeclaration declaration, + {required bool nullSafety}) = _TypedMapImplGenerator.propsMapViewOrFunctionComponent; TypedMapNames get names; bool get isComponent2; @@ -72,8 +72,8 @@ abstract class TypedMapImplGenerator extends BoilerplateDeclarationGenerator { void _generateFactory() { assert(factoryNames.length == 1, 'factoryNames must have a length of 1'); - outputContentsBuffer - .write('${names.implName} ${factoryNames.first.implName}([Map${nullSafety ? '?' : ''} backingProps]) => '); + outputContentsBuffer.write( + '${names.implName} ${factoryNames.first.implName}([Map${nullSafety ? '?' : ''} backingProps]) => '); if (!isComponent2) { /// _$$FooProps _$Foo([Map backingProps]) => _$$FooProps(backingProps); @@ -175,7 +175,8 @@ abstract class TypedMapImplGenerator extends BoilerplateDeclarationGenerator { ..writeln() ..writeln(' factory ${names.implName}(Map${nullSafety ? '?' : ''} backingMap) {') ..writeln(' if (backingMap == null || backingMap is JsBackedMap) {') - ..writeln(' return ${names.jsMapImplName}(backingMap as JsBackedMap${nullSafety ? '?' : ''});') + ..writeln( + ' return ${names.jsMapImplName}(backingMap as JsBackedMap${nullSafety ? '?' : ''});') ..writeln(' } else {') ..writeln(' return ${names.plainMapImplName}(backingMap);') ..writeln(' }') @@ -187,7 +188,8 @@ abstract class TypedMapImplGenerator extends BoilerplateDeclarationGenerator { ..writeln( ' // of `_$propsOrState` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217') // TODO need to remove this workaround once https://github.com/dart-lang/sdk/issues/36217 is fixed get nice dart2js output - ..writeln(' ${names.implName}(Map${nullSafety ? '?' : ''} backingMap) : this._$propsOrState = {} {') + ..writeln( + ' ${names.implName}(Map${nullSafety ? '?' : ''} backingMap) : this._$propsOrState = {} {') ..writeln(' this._$propsOrState = backingMap ?? {};') ..writeln(' }'); } @@ -383,7 +385,8 @@ class _TypedMapImplGenerator extends TypedMapImplGenerator { version = declaration.version; _TypedMapImplGenerator.propsMapViewOrFunctionComponent( - PropsMapViewOrFunctionComponentDeclaration declaration, {required this.nullSafety}) + PropsMapViewOrFunctionComponentDeclaration declaration, + {required this.nullSafety}) : names = TypedMapNames(declaration.props.either.name.name), factoryNames = declaration.factories.map((factory) => FactoryNames(factory.name.name)).toList(), diff --git a/lib/src/builder/codegen/util.dart b/lib/src/builder/codegen/util.dart index 05ccfcb46..6398a8724 100644 --- a/lib/src/builder/codegen/util.dart +++ b/lib/src/builder/codegen/util.dart @@ -58,7 +58,8 @@ mixin TemporaryGenerationContext { StateError('$name is null. setGenerationContext must be called first to initialize it.'); SourceFile get sourceFile => _sourceFile ?? (throw _uninitializedError('sourceFile')); - StringBuffer get outputContentsBuffer => _outputContentsBuffer ?? (throw _uninitializedError('outputContentsBuffer')); + StringBuffer get outputContentsBuffer => + _outputContentsBuffer ?? (throw _uninitializedError('outputContentsBuffer')); Logger get logger => _logger ?? (throw _uninitializedError('logger')); /// Populates fields needed temporarily for generation. diff --git a/lib/src/builder/parsing/member_association.dart b/lib/src/builder/parsing/member_association.dart index 596b7d163..feb348ec4 100644 --- a/lib/src/builder/parsing/member_association.dart +++ b/lib/src/builder/parsing/member_association.dart @@ -62,8 +62,8 @@ String _normalizeBoilerplateStateName(String name) => // String _normalizeBoilerplateStateMixinName(String name) => // name.replaceFirst(RegExp(r'State(?:Mixin)?$'), ''); -T? _getNameMatch(Iterable members, String name) => members - .firstWhereOrNull((member) => normalizeNameAndRemoveSuffix(member) == name); +T? _getNameMatch(Iterable members, String name) => + members.firstWhereOrNull((member) => normalizeNameAndRemoveSuffix(member) == name); Union? _getNameMatchUnion( Iterable membersA, Iterable membersB, String name) { diff --git a/lib/src/builder/parsing/members/component.dart b/lib/src/builder/parsing/members/component.dart index 9c5d84ceb..42ca04827 100644 --- a/lib/src/builder/parsing/members/component.dart +++ b/lib/src/builder/parsing/members/component.dart @@ -49,8 +49,8 @@ class BoilerplateComponent extends BoilerplateMember { /// The [TypeAnnotation] for the component's prop class. TypeAnnotation? get propsGenericArg { - return nodeHelper.superclass!.typeArguments?.arguments.firstWhereOrNull( - (type) => propsOrMixinNamePattern.hasMatch(type.typeNameWithoutPrefix!)); + return nodeHelper.superclass!.typeArguments?.arguments + .firstWhereOrNull((type) => propsOrMixinNamePattern.hasMatch(type.typeNameWithoutPrefix!)); } /// Whether or not the component has any annotation, ignoring component version diff --git a/lib/src/builder/parsing/members_from_ast.dart b/lib/src/builder/parsing/members_from_ast.dart index f16515615..05fbd0b2b 100644 --- a/lib/src/builder/parsing/members_from_ast.dart +++ b/lib/src/builder/parsing/members_from_ast.dart @@ -133,7 +133,8 @@ class _BoilerplateMemberDetector { // /// For `FooProps`, returns `_$FooProps` - NamedCompilationUnitMember? _getSourceClassForPotentialCompanion(NamedCompilationUnitMember node) { + NamedCompilationUnitMember? _getSourceClassForPotentialCompanion( + NamedCompilationUnitMember node) { final name = node.name.name; if (name.startsWith(privateSourcePrefix)) { return null; @@ -225,7 +226,8 @@ class _BoilerplateMemberDetector { // _processClassishDeclaration helpers // - bool _detectClassBasedOnAnnotations(ClassishDeclaration classish, ClassishDeclaration? companion) { + bool _detectClassBasedOnAnnotations( + ClassishDeclaration classish, ClassishDeclaration? companion) { final node = classish.node; for (final annotation in classish.metadata) { switch (annotation.name.nameWithoutPrefix) { @@ -264,13 +266,13 @@ class _BoilerplateMemberDetector { return true; case 'PropsMixin': - onPropsMixin(BoilerplatePropsMixin(classish, companion, - _annotatedPropsOrStateMixinConfidence(classish, companion))); + onPropsMixin(BoilerplatePropsMixin( + classish, companion, _annotatedPropsOrStateMixinConfidence(classish, companion))); return true; case 'StateMixin': - onStateMixin(BoilerplateStateMixin(classish, companion, - _annotatedPropsOrStateMixinConfidence(classish, companion))); + onStateMixin(BoilerplateStateMixin( + classish, companion, _annotatedPropsOrStateMixinConfidence(classish, companion))); return true; case 'Component': diff --git a/lib/src/builder/parsing/util.dart b/lib/src/builder/parsing/util.dart index 441b918aa..d9240f996 100644 --- a/lib/src/builder/parsing/util.dart +++ b/lib/src/builder/parsing/util.dart @@ -55,10 +55,8 @@ class Union { final A? a; final B? b; - Union.a(A this.a) - : b = null; - Union.b(B this.b) - : a = null; + Union.a(A this.a) : b = null; + Union.b(B this.b) : a = null; /// Executes a callback based upon which field is set. T switchCase(T Function(A) onA, T Function(B) onB) { diff --git a/lib/src/util/react_util.dart b/lib/src/util/react_util.dart index 43b39f483..f60c98402 100644 --- a/lib/src/util/react_util.dart +++ b/lib/src/util/react_util.dart @@ -101,4 +101,8 @@ class UiPropsMapView extends MapView @override void validateRequiredProps() => throw UnimplementedError('@PropsMixin instances do not implement validateRequiredProps'); + + @override + void disableRequiredPropValidation() => + throw UnimplementedError('@PropsMixin instances do not implement disableRequiredPropValidation'); } diff --git a/test/over_react/component/fixtures/dummy_component.over_react.g.dart b/test/over_react/component/fixtures/dummy_component.over_react.g.dart index 7658d16ed..c02968a3d 100644 --- a/test/over_react/component/fixtures/dummy_component.over_react.g.dart +++ b/test/over_react/component/fixtures/dummy_component.over_react.g.dart @@ -44,6 +44,15 @@ abstract class _$DummyPropsAccessorsMixin implements _$DummyProps { static const List $propKeys = [ _$key__onComponentDidMount___$DummyProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey('DummyProps.onComponentDidMount')) { + throw MissingRequiredPropsError( + 'Required prop `onComponentDidMount` is missing.'); + } + } } const PropsMeta _$metaForDummyProps = PropsMeta( diff --git a/test/over_react/component/fixtures/prop_typedef_fixtures.over_react.g.dart b/test/over_react/component/fixtures/prop_typedef_fixtures.over_react.g.dart index 6fc6602c5..63f2ee8cc 100644 --- a/test/over_react/component/fixtures/prop_typedef_fixtures.over_react.g.dart +++ b/test/over_react/component/fixtures/prop_typedef_fixtures.over_react.g.dart @@ -97,6 +97,22 @@ abstract class _$TestAbstractCustomRendererComponentPropsAccessorsMixin _$key__parameterizedCustomRenderer___$TestAbstractCustomRendererComponentProps, _$key__somePropKey___$TestAbstractCustomRendererComponentProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey( + 'TestAbstractCustomRendererComponentProps.customRenderer')) { + throw MissingRequiredPropsError( + 'Required prop `customRenderer` is missing.'); + } + + if (!props.containsKey( + 'TestAbstractCustomRendererComponentProps.parameterizedCustomRenderer')) { + throw MissingRequiredPropsError( + 'Required prop `parameterizedCustomRenderer` is missing.'); + } + } } const PropsMeta _$metaForTestAbstractCustomRendererComponentProps = PropsMeta( @@ -983,6 +999,21 @@ abstract class _$TestCustomRendererComponentPropsAccessorsMixin _$key__somePropKey___$TestCustomRendererComponentProps, _$key__someInitialStateKeyValue___$TestCustomRendererComponentProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey('TestCustomRendererComponentProps.customRenderer')) { + throw MissingRequiredPropsError( + 'Required prop `customRenderer` is missing.'); + } + + if (!props.containsKey( + 'TestCustomRendererComponentProps.parameterizedCustomRenderer')) { + throw MissingRequiredPropsError( + 'Required prop `parameterizedCustomRenderer` is missing.'); + } + } } const PropsMeta _$metaForTestCustomRendererComponentProps = PropsMeta( diff --git a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart new file mode 100644 index 000000000..93092d53a --- /dev/null +++ b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart @@ -0,0 +1,205 @@ +// Copyright 2023 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:over_react/over_react.dart'; +import 'package:react_testing_library/react_testing_library.dart' as rtl; +import 'package:test/test.dart'; + +import '../../../../test_util/test_util.dart'; + +part 'null_safety_validate_required_props_test.over_react.g.dart'; + +void main() { + group('(New boilerplate) validates required props:', () { + group('non-nullable required prop', () { + group('throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + + expect(() { + view.rerender((ComponentTest() + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + }); + + test('does not throw when the prop is set', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + }); + }); + + group('nullable required prop', () { + group('throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..requiredDynamic = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + + expect(() { + view.rerender((ComponentTest() + ..requiredDynamic = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); + }); + + test('does not throw when the prop is set to null', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = null + )()); + }, returnsNormally); + }); + + test('does not throw when the prop is set', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + }); + }); + + test('@disableRequiredPropValidation annotation turns off validation for specific props', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, allOf(returnsNormally, + logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), + logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), + logsPropRequiredError('ComponentTestProps.ref'), + )); + }); + + test('disableRequiredPropValidation method turns off validation for component usage', () { + expect(() { + rtl.render((ComponentTest() + ..disableRequiredPropValidation() + )()); + }, allOf(returnsNormally, + logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), + logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), + logsPropRequiredError('ComponentTestProps.ref'), + logsPropRequiredError('ComponentTestProps.requiredNonNullable'), + logsPropRequiredError('ComponentTestProps.requiredNullable'), + logsPropRequiredError('ComponentTestProps.requiredDynamic'), + )); + }); + }); +} + +@Factory() +// ignore: undefined_identifier, invalid_assignment +UiFactory ComponentTest = _$ComponentTest; + +@Props() +class _$ComponentTestProps extends UiProps { + late bool requiredNonNullable; + + late bool? requiredNullable; + + // todo is this necessary? + late dynamic requiredDynamic; + + @disableRequiredPropValidation + late bool disabledRequiredProp; + + @disableRequiredPropValidation + late bool? disabledNullableRequiredProp; + + @disableRequiredPropValidation + @override + late dynamic ref; + + bool? nullable; +} + +@Component() +class ComponentTestComponent extends UiComponent { + @override + render() => Dom.div()(); +} + +// AF-3369 This will be removed once the transition to Dart 2 is complete. +// ignore: mixin_of_non_class, undefined_class +class ComponentTestProps extends _$ComponentTestProps with _$ComponentTestPropsAccessorsMixin { + // ignore: undefined_identifier, undefined_class, const_initialized_with_non_constant_value, invalid_assignment + static const PropsMeta meta = _$metaForComponentTestProps; +} diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart new file mode 100644 index 000000000..11f0cbea3 --- /dev/null +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart @@ -0,0 +1,200 @@ +// Copyright 2023 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:over_react/over_react.dart'; +import 'package:react_testing_library/react_testing_library.dart' as rtl; +import 'package:test/test.dart'; + +import '../../../../test_util/test_util.dart'; + +part 'null_safety_validate_required_props_test.over_react.g.dart'; + +void main() { + group('(New boilerplate) validates required props:', () { + group('non-nullable required prop', () { + group('throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + + expect(() { + view.rerender((ComponentTest() + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + }); + + test('does not throw when the prop is set', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + }); + }); + + group('nullable required prop', () { + group('throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..requiredDynamic = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + + expect(() { + view.rerender((ComponentTest() + ..requiredDynamic = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); + }); + + test('does not throw when the prop is set to null', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = null + )()); + }, returnsNormally); + }); + + test('does not throw when the prop is set', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + }); + }); + + test('@disableRequiredPropValidation annotation turns off validation for specific props', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, allOf(returnsNormally, + logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), + logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), + logsPropRequiredError('ComponentTestProps.ref'), + )); + }); + + test('disableRequiredPropValidation method turns off validation for component usage', () { + expect(() { + rtl.render((ComponentTest() + ..disableRequiredPropValidation() + )()); + }, allOf(returnsNormally, + logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), + logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), + logsPropRequiredError('ComponentTestProps.ref'), + logsPropRequiredError('ComponentTestProps.requiredNonNullable'), + logsPropRequiredError('ComponentTestProps.requiredNullable'), + logsPropRequiredError('ComponentTestProps.requiredDynamic'), + )); + }); + }); +} + +@Factory() +// ignore: undefined_identifier, invalid_assignment +UiFactory ComponentTest = _$ComponentTest; + +@Props() +class _$ComponentTestProps extends UiProps { + late bool requiredNonNullable; + + late bool? requiredNullable; + + // todo is this necessary? + late dynamic requiredDynamic; + + @disableRequiredPropValidation + late bool disabledRequiredProp; + + @disableRequiredPropValidation + late bool? disabledNullableRequiredProp; + + @disableRequiredPropValidation + @override + late dynamic ref; + + bool? nullable; +} + +@Component2() +class ComponentTestComponent extends UiComponent2 { + // todo test default props + // todo test other boilerplates + @override + render() => Dom.div()(); +} diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart new file mode 100644 index 000000000..b3e57a2b9 --- /dev/null +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart @@ -0,0 +1,308 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: deprecated_member_use_from_same_package, unnecessary_null_in_if_null_operators, prefer_null_aware_operators +part of 'null_safety_validate_required_props_test.dart'; + +// ************************************************************************** +// OverReactBuilder (package:over_react/src/builder.dart) +// ************************************************************************** + +// React component factory implementation. +// +// Registers component implementation and links type meta to builder factory. +final $ComponentTestComponentFactory = registerComponent2( + () => _$ComponentTestComponent(), + builderFactory: _$ComponentTest, + componentClass: ComponentTestComponent, + isWrapper: false, + parentType: null, +); + +abstract class _$ComponentTestPropsAccessorsMixin + implements _$ComponentTestProps { + @override + Map get props; + + /// + @override + bool get requiredNonNullable => + (props[_$key__requiredNonNullable___$ComponentTestProps] ?? null) as bool; + + /// + @override + set requiredNonNullable(bool value) => + props[_$key__requiredNonNullable___$ComponentTestProps] = value; + + /// + @override + bool? get requiredNullable => + (props[_$key__requiredNullable___$ComponentTestProps] ?? null) as bool?; + + /// + @override + set requiredNullable(bool? value) => + props[_$key__requiredNullable___$ComponentTestProps] = value; + + /// + @override + dynamic get requiredDynamic => + (props[_$key__requiredDynamic___$ComponentTestProps] ?? null) as dynamic; + + /// + @override + set requiredDynamic(dynamic value) => + props[_$key__requiredDynamic___$ComponentTestProps] = value; + + /// + @override + @disableRequiredPropValidation + bool get disabledRequiredProp => + (props[_$key__disabledRequiredProp___$ComponentTestProps] ?? null) + as bool; + + /// + @override + @disableRequiredPropValidation + set disabledRequiredProp(bool value) => + props[_$key__disabledRequiredProp___$ComponentTestProps] = value; + + /// + @override + @disableRequiredPropValidation + bool? get disabledNullableRequiredProp => + (props[_$key__disabledNullableRequiredProp___$ComponentTestProps] ?? null) + as bool?; + + /// + @override + @disableRequiredPropValidation + set disabledNullableRequiredProp(bool? value) => + props[_$key__disabledNullableRequiredProp___$ComponentTestProps] = value; + + /// + @override + @disableRequiredPropValidation + @override + dynamic get ref => + (props[_$key__ref___$ComponentTestProps] ?? null) as dynamic; + + /// + @override + @disableRequiredPropValidation + @override + set ref(dynamic value) => props[_$key__ref___$ComponentTestProps] = value; + + /// + @override + bool? get nullable => + (props[_$key__nullable___$ComponentTestProps] ?? null) as bool?; + + /// + @override + set nullable(bool? value) => + props[_$key__nullable___$ComponentTestProps] = value; + /* GENERATED CONSTANTS */ + static const PropDescriptor + _$prop__requiredNonNullable___$ComponentTestProps = PropDescriptor( + _$key__requiredNonNullable___$ComponentTestProps, + isRequired: true, + isNullable: true); + static const PropDescriptor _$prop__requiredNullable___$ComponentTestProps = + PropDescriptor(_$key__requiredNullable___$ComponentTestProps, + isRequired: true, isNullable: true); + static const PropDescriptor _$prop__requiredDynamic___$ComponentTestProps = + PropDescriptor(_$key__requiredDynamic___$ComponentTestProps, + isRequired: true, isNullable: true); + static const PropDescriptor + _$prop__disabledRequiredProp___$ComponentTestProps = PropDescriptor( + _$key__disabledRequiredProp___$ComponentTestProps, + isRequired: true, + isNullable: true); + static const PropDescriptor + _$prop__disabledNullableRequiredProp___$ComponentTestProps = + PropDescriptor(_$key__disabledNullableRequiredProp___$ComponentTestProps, + isRequired: true, isNullable: true); + static const PropDescriptor _$prop__ref___$ComponentTestProps = + PropDescriptor(_$key__ref___$ComponentTestProps, + isRequired: true, isNullable: true); + static const PropDescriptor _$prop__nullable___$ComponentTestProps = + PropDescriptor(_$key__nullable___$ComponentTestProps); + static const String _$key__requiredNonNullable___$ComponentTestProps = + 'ComponentTestProps.requiredNonNullable'; + static const String _$key__requiredNullable___$ComponentTestProps = + 'ComponentTestProps.requiredNullable'; + static const String _$key__requiredDynamic___$ComponentTestProps = + 'ComponentTestProps.requiredDynamic'; + static const String _$key__disabledRequiredProp___$ComponentTestProps = + 'ComponentTestProps.disabledRequiredProp'; + static const String + _$key__disabledNullableRequiredProp___$ComponentTestProps = + 'ComponentTestProps.disabledNullableRequiredProp'; + static const String _$key__ref___$ComponentTestProps = + 'ComponentTestProps.ref'; + static const String _$key__nullable___$ComponentTestProps = + 'ComponentTestProps.nullable'; + + static const List $props = [ + _$prop__requiredNonNullable___$ComponentTestProps, + _$prop__requiredNullable___$ComponentTestProps, + _$prop__requiredDynamic___$ComponentTestProps, + _$prop__disabledRequiredProp___$ComponentTestProps, + _$prop__disabledNullableRequiredProp___$ComponentTestProps, + _$prop__ref___$ComponentTestProps, + _$prop__nullable___$ComponentTestProps + ]; + static const List $propKeys = [ + _$key__requiredNonNullable___$ComponentTestProps, + _$key__requiredNullable___$ComponentTestProps, + _$key__requiredDynamic___$ComponentTestProps, + _$key__disabledRequiredProp___$ComponentTestProps, + _$key__disabledNullableRequiredProp___$ComponentTestProps, + _$key__ref___$ComponentTestProps, + _$key__nullable___$ComponentTestProps + ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey('ComponentTestProps.requiredNonNullable')) { + throw MissingRequiredPropsError( + 'Required prop `requiredNonNullable` is missing.'); + } + + if (!props.containsKey('ComponentTestProps.requiredNullable')) { + throw MissingRequiredPropsError( + 'Required prop `requiredNullable` is missing.'); + } + + if (!props.containsKey('ComponentTestProps.requiredDynamic')) { + throw MissingRequiredPropsError( + 'Required prop `requiredDynamic` is missing.'); + } + } +} + +const PropsMeta _$metaForComponentTestProps = PropsMeta( + fields: _$ComponentTestPropsAccessorsMixin.$props, + keys: _$ComponentTestPropsAccessorsMixin.$propKeys, +); + +class ComponentTestProps extends _$ComponentTestProps + with _$ComponentTestPropsAccessorsMixin { + static const PropsMeta meta = _$metaForComponentTestProps; +} + +_$$ComponentTestProps _$ComponentTest([Map? backingProps]) => + backingProps == null + ? _$$ComponentTestProps$JsMap(JsBackedMap()) + : _$$ComponentTestProps(backingProps); + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +abstract class _$$ComponentTestProps extends _$ComponentTestProps + with _$ComponentTestPropsAccessorsMixin + implements ComponentTestProps { + _$$ComponentTestProps._(); + + factory _$$ComponentTestProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$ComponentTestProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$ComponentTestProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The `ReactComponentFactory` associated with the component built by this class. + @override + ReactComponentFactoryProxy get componentFactory => + super.componentFactory ?? $ComponentTestComponentFactory; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => 'ComponentTestProps.'; +} + +// Concrete props implementation that can be backed by any [Map]. +class _$$ComponentTestProps$PlainMap extends _$$ComponentTestProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ComponentTestProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +class _$$ComponentTestProps$JsMap extends _$$ComponentTestProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ComponentTestProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +// Concrete component implementation mixin. +// +// Implements typed props/state factories, defaults `consumedPropKeys` to the keys +// generated for the associated props class. +class _$ComponentTestComponent extends ComponentTestComponent { + late _$$ComponentTestProps$JsMap _cachedTypedProps; + + @override + _$$ComponentTestProps$JsMap get props => _cachedTypedProps; + + @override + set props(Map value) { + assert( + getBackingMap(value) is JsBackedMap, + 'Component2.props should never be set directly in ' + 'production. If this is required for testing, the ' + 'component should be rendered within the test. If ' + 'that does not have the necessary result, the last ' + 'resort is to use typedPropsFactoryJs.'); + super.props = value; + _cachedTypedProps = + typedPropsFactoryJs(getBackingMap(value) as JsBackedMap); + } + + @override + _$$ComponentTestProps$JsMap typedPropsFactoryJs(JsBackedMap? backingMap) => + _$$ComponentTestProps$JsMap(backingMap); + + @override + _$$ComponentTestProps typedPropsFactory(Map? backingMap) => + _$$ComponentTestProps(backingMap); + + /// Let `UiComponent` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + @override + String get displayName => 'ComponentTest'; + + /// The default consumed props, taken from _$ComponentTestProps. + /// Used in `*ConsumedProps` methods if [consumedProps] is not overridden. + @override + final List $defaultConsumedProps = const [ + _$metaForComponentTestProps + ]; +} diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart new file mode 100644 index 000000000..334ef14bc --- /dev/null +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart @@ -0,0 +1,199 @@ +// Copyright 2023 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:over_react/over_react.dart'; +import 'package:react_testing_library/react_testing_library.dart' as rtl; +import 'package:test/test.dart'; + +import '../../../test_util/test_util.dart'; + +part 'null_safety_validate_required_props_test.over_react.g.dart'; + +void main() { + group('(New boilerplate) validates required props:', () { + group('non-nullable required prop', () { + group('throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + + expect(() { + view.rerender((ComponentTest() + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + }); + + test('does not throw when the prop is set', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + }); + }); + + group('nullable required prop', () { + group('throws when a prop is required and not set', () { + test('on mount', () { + expect(() { + rtl.render((ComponentTest() + ..requiredDynamic = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); + + test('on re-render', () { + late rtl.RenderResult view; + + expect(() { + view = rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + + expect(() { + view.rerender((ComponentTest() + ..requiredDynamic = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); + }); + + test('does not throw when the prop is set to null', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = null + )()); + }, returnsNormally); + }); + + test('does not throw when the prop is set', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, returnsNormally); + }); + }); + + test('@disableRequiredPropValidation annotation turns off validation for specific props', () { + expect(() { + rtl.render((ComponentTest() + ..requiredNonNullable = true + ..requiredDynamic = true + ..requiredNullable = true + )()); + }, allOf(returnsNormally, + logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), + logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), + logsPropRequiredError('ComponentTestProps.ref'), + )); + }); + + test('disableRequiredPropValidation method turns off validation for component usage', () { + expect(() { + rtl.render((ComponentTest() + ..disableRequiredPropValidation() + )()); + }, allOf(returnsNormally, + logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), + logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), + logsPropRequiredError('ComponentTestProps.ref'), + logsPropRequiredError('ComponentTestProps.requiredNonNullable'), + logsPropRequiredError('ComponentTestProps.requiredNullable'), + logsPropRequiredError('ComponentTestProps.requiredDynamic'), + )); + }); + }); +} + +@Factory() +// ignore: undefined_identifier, invalid_assignment +UiFactory ComponentTest = _$ComponentTest; + +@Props() +class _$ComponentTestProps extends UiProps { + late bool requiredNonNullable; + + late bool? requiredNullable; + + // todo is this necessary? + late dynamic requiredDynamic; + + @disableRequiredPropValidation + late bool disabledRequiredProp; + + @disableRequiredPropValidation + late bool? disabledNullableRequiredProp; + + @disableRequiredPropValidation + @override + late dynamic ref; + + bool? nullable; +} + +@Component() +class ComponentTestComponent extends UiComponent { + @override + render() => Dom.div()(); +} + diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart new file mode 100644 index 000000000..e1d79586a --- /dev/null +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart @@ -0,0 +1,251 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: deprecated_member_use_from_same_package, unnecessary_null_in_if_null_operators, prefer_null_aware_operators +part of 'null_safety_validate_required_props_test.dart'; + +// ************************************************************************** +// OverReactBuilder (package:over_react/src/builder.dart) +// ************************************************************************** + +// React component factory implementation. +// +// Registers component implementation and links type meta to builder factory. +final $ComponentTestComponentFactory = registerComponent( + () => _$ComponentTestComponent(), + builderFactory: _$ComponentTest, + componentClass: ComponentTestComponent, + isWrapper: false, + parentType: null, +); + +abstract class _$ComponentTestPropsAccessorsMixin + implements _$ComponentTestProps { + @override + Map get props; + + /// + @override + bool get requiredNonNullable => + (props[_$key__requiredNonNullable___$ComponentTestProps] ?? null) as bool; + + /// + @override + set requiredNonNullable(bool value) => + props[_$key__requiredNonNullable___$ComponentTestProps] = value; + + /// + @override + bool? get requiredNullable => + (props[_$key__requiredNullable___$ComponentTestProps] ?? null) as bool?; + + /// + @override + set requiredNullable(bool? value) => + props[_$key__requiredNullable___$ComponentTestProps] = value; + + /// + @override + dynamic get requiredDynamic => + (props[_$key__requiredDynamic___$ComponentTestProps] ?? null) as dynamic; + + /// + @override + set requiredDynamic(dynamic value) => + props[_$key__requiredDynamic___$ComponentTestProps] = value; + + /// + @override + @disableRequiredPropValidation + bool get disabledRequiredProp => + (props[_$key__disabledRequiredProp___$ComponentTestProps] ?? null) + as bool; + + /// + @override + @disableRequiredPropValidation + set disabledRequiredProp(bool value) => + props[_$key__disabledRequiredProp___$ComponentTestProps] = value; + + /// + @override + @disableRequiredPropValidation + bool? get disabledNullableRequiredProp => + (props[_$key__disabledNullableRequiredProp___$ComponentTestProps] ?? null) + as bool?; + + /// + @override + @disableRequiredPropValidation + set disabledNullableRequiredProp(bool? value) => + props[_$key__disabledNullableRequiredProp___$ComponentTestProps] = value; + + /// + @override + @disableRequiredPropValidation + @override + dynamic get ref => + (props[_$key__ref___$ComponentTestProps] ?? null) as dynamic; + + /// + @override + @disableRequiredPropValidation + @override + set ref(dynamic value) => props[_$key__ref___$ComponentTestProps] = value; + + /// + @override + bool? get nullable => + (props[_$key__nullable___$ComponentTestProps] ?? null) as bool?; + + /// + @override + set nullable(bool? value) => + props[_$key__nullable___$ComponentTestProps] = value; + /* GENERATED CONSTANTS */ + static const PropDescriptor + _$prop__requiredNonNullable___$ComponentTestProps = PropDescriptor( + _$key__requiredNonNullable___$ComponentTestProps, + isRequired: true, + isNullable: true); + static const PropDescriptor _$prop__requiredNullable___$ComponentTestProps = + PropDescriptor(_$key__requiredNullable___$ComponentTestProps, + isRequired: true, isNullable: true); + static const PropDescriptor _$prop__requiredDynamic___$ComponentTestProps = + PropDescriptor(_$key__requiredDynamic___$ComponentTestProps, + isRequired: true, isNullable: true); + static const PropDescriptor + _$prop__disabledRequiredProp___$ComponentTestProps = PropDescriptor( + _$key__disabledRequiredProp___$ComponentTestProps, + isRequired: true, + isNullable: true); + static const PropDescriptor + _$prop__disabledNullableRequiredProp___$ComponentTestProps = + PropDescriptor(_$key__disabledNullableRequiredProp___$ComponentTestProps, + isRequired: true, isNullable: true); + static const PropDescriptor _$prop__ref___$ComponentTestProps = + PropDescriptor(_$key__ref___$ComponentTestProps, + isRequired: true, isNullable: true); + static const PropDescriptor _$prop__nullable___$ComponentTestProps = + PropDescriptor(_$key__nullable___$ComponentTestProps); + static const String _$key__requiredNonNullable___$ComponentTestProps = + 'ComponentTestProps.requiredNonNullable'; + static const String _$key__requiredNullable___$ComponentTestProps = + 'ComponentTestProps.requiredNullable'; + static const String _$key__requiredDynamic___$ComponentTestProps = + 'ComponentTestProps.requiredDynamic'; + static const String _$key__disabledRequiredProp___$ComponentTestProps = + 'ComponentTestProps.disabledRequiredProp'; + static const String + _$key__disabledNullableRequiredProp___$ComponentTestProps = + 'ComponentTestProps.disabledNullableRequiredProp'; + static const String _$key__ref___$ComponentTestProps = + 'ComponentTestProps.ref'; + static const String _$key__nullable___$ComponentTestProps = + 'ComponentTestProps.nullable'; + + static const List $props = [ + _$prop__requiredNonNullable___$ComponentTestProps, + _$prop__requiredNullable___$ComponentTestProps, + _$prop__requiredDynamic___$ComponentTestProps, + _$prop__disabledRequiredProp___$ComponentTestProps, + _$prop__disabledNullableRequiredProp___$ComponentTestProps, + _$prop__ref___$ComponentTestProps, + _$prop__nullable___$ComponentTestProps + ]; + static const List $propKeys = [ + _$key__requiredNonNullable___$ComponentTestProps, + _$key__requiredNullable___$ComponentTestProps, + _$key__requiredDynamic___$ComponentTestProps, + _$key__disabledRequiredProp___$ComponentTestProps, + _$key__disabledNullableRequiredProp___$ComponentTestProps, + _$key__ref___$ComponentTestProps, + _$key__nullable___$ComponentTestProps + ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey('ComponentTestProps.requiredNonNullable')) { + throw MissingRequiredPropsError( + 'Required prop `requiredNonNullable` is missing.'); + } + + if (!props.containsKey('ComponentTestProps.requiredNullable')) { + throw MissingRequiredPropsError( + 'Required prop `requiredNullable` is missing.'); + } + + if (!props.containsKey('ComponentTestProps.requiredDynamic')) { + throw MissingRequiredPropsError( + 'Required prop `requiredDynamic` is missing.'); + } + } +} + +const PropsMeta _$metaForComponentTestProps = PropsMeta( + fields: _$ComponentTestPropsAccessorsMixin.$props, + keys: _$ComponentTestPropsAccessorsMixin.$propKeys, +); + +class ComponentTestProps extends _$ComponentTestProps + with _$ComponentTestPropsAccessorsMixin { + static const PropsMeta meta = _$metaForComponentTestProps; +} + +_$$ComponentTestProps _$ComponentTest([Map? backingProps]) => + _$$ComponentTestProps(backingProps); + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +class _$$ComponentTestProps extends _$ComponentTestProps + with _$ComponentTestPropsAccessorsMixin + implements ComponentTestProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ComponentTestProps(Map? backingMap) : this._props = {} { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The `ReactComponentFactory` associated with the component built by this class. + @override + ReactComponentFactoryProxy get componentFactory => + super.componentFactory ?? $ComponentTestComponentFactory; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => 'ComponentTestProps.'; +} + +// Concrete component implementation mixin. +// +// Implements typed props/state factories, defaults `consumedPropKeys` to the keys +// generated for the associated props class. +class _$ComponentTestComponent extends ComponentTestComponent { + @override + _$$ComponentTestProps typedPropsFactory(Map? backingMap) => + _$$ComponentTestProps(backingMap); + + /// Let `UiComponent` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + @override + String get displayName => 'ComponentTest'; + + /// The default consumed props, taken from _$ComponentTestProps. + /// Used in `*ConsumedProps` methods if [consumedProps] is not overridden. + @override + final List $defaultConsumedProps = const [ + _$metaForComponentTestProps + ]; +} diff --git a/test/over_react_component_declaration_test.dart b/test/over_react_component_declaration_test.dart index 46fee88a9..972e18751 100644 --- a/test/over_react_component_declaration_test.dart +++ b/test/over_react_component_declaration_test.dart @@ -33,6 +33,7 @@ import 'over_react/component_declaration/builder_integration_tests/abstract_acce import 'over_react/component_declaration/builder_integration_tests/accessor_mixin_integration_test.dart' as accessor_mixin_integration_test; import 'over_react/component_declaration/builder_integration_tests/component_integration_test.dart' as component_integration_test; import 'over_react/component_declaration/builder_integration_tests/constant_required_accessor_integration_test.dart' as constant_required_accessor_integration_test; +import 'over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart' as null_safety_validate_required_props_test; import 'over_react/component_declaration/builder_integration_tests/do_not_generate_accessor_integration_test.dart' as do_not_generate_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/namespaced_accessor_integration_test.dart' as namespaced_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/private_props_ddc_bug.dart' as private_props_ddc_bug; @@ -43,6 +44,7 @@ import 'over_react/component_declaration/builder_integration_tests/backwards_com import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/accessor_mixin_integration_test.dart' as backwards_compat_accessor_mixin_integration_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/component_integration_test.dart' as backwards_compat_component_integration_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/constant_required_accessor_integration_test.dart' as backwards_compat_constant_required_accessor_integration_test; +import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart' as backwards_compat_null_safety_validate_required_props_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/do_not_generate_accessor_integration_test.dart' as backwards_compat_do_not_generate_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/namespaced_accessor_integration_test.dart' as backwards_compat_namespaced_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/private_props_ddc_bug.dart' as backwards_compat_private_props_ddc_bug; @@ -54,6 +56,7 @@ import 'over_react/component_declaration/builder_integration_tests/component2/ac import 'over_react/component_declaration/builder_integration_tests/component2/annotation_error_integration_test.dart' as annotation_error_integration_test; import 'over_react/component_declaration/builder_integration_tests/component2/component_integration_test.dart' as component2_component_integration_test; import 'over_react/component_declaration/builder_integration_tests/component2/constant_required_accessor_integration_test.dart' as component2_constant_required_accessor_integration_test; +import 'over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart' as component2_null_safety_validate_required_props_test; import 'over_react/component_declaration/builder_integration_tests/component2/do_not_generate_accessor_integration_test.dart' as component2_do_not_generate_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/component2/namespaced_accessor_integration_test.dart' as component2_namespaced_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/component2/private_props_ddc_bug.dart' as component2_private_props_ddc_bug; @@ -139,5 +142,8 @@ main() { new_boilerplate_unassigned_prop_integration_test.main(); // FIXME: Move these tests to only be run opted in to null safety. + null_safety_validate_required_props_test.main(); + backwards_compat_null_safety_validate_required_props_test.main(); + component2_null_safety_validate_required_props_test.main(); new_boilerplate_null_safety_validate_required_props_test.main(); } diff --git a/test/vm_tests/builder/codegen_test.dart b/test/vm_tests/builder/codegen_test.dart index ae31d6325..ec90cde0b 100644 --- a/test/vm_tests/builder/codegen_test.dart +++ b/test/vm_tests/builder/codegen_test.dart @@ -27,6 +27,7 @@ import 'package:test/test.dart'; import '../../mockito.mocks.dart'; import './util.dart'; +// todo add tests here as well for generated code?? main() { group('ImplGenerator', () { ImplGenerator? implGenerator; From b1ca526a22383d19f3472225f9ab95c1b551c450 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Fri, 1 Dec 2023 11:01:38 -0700 Subject: [PATCH 07/24] Update RTL dep --- pubspec.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 1bdcc135e..1fe2bd799 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,17 +35,13 @@ dev_dependencies: glob: ^2.0.1 io: '>=0.3.2+1 <2.0.0' mockito: ^5.3.1 - react_testing_library: ^2.1.0 + react_testing_library: ^3.0.1 over_react_test: ^2.10.2 pedantic: ^1.11.1 test: ^1.20.0 yaml: ^3.1.0 dependency_overrides: - react_testing_library: - git: - url: https://github.com/Workiva/react_testing_library - ref: null-safety over_react_test: git: url: https://github.com/Workiva/over_react_test From 880819fbade893f912a82686b441d99e952ac075 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Fri, 1 Dec 2023 11:12:57 -0700 Subject: [PATCH 08/24] Fix warning --- lib/src/builder/codegen.dart | 9 +++------ lib/src/component_declaration/builder_helpers.dart | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/src/builder/codegen.dart b/lib/src/builder/codegen.dart index 68dfa440d..e780d3ae0 100644 --- a/lib/src/builder/codegen.dart +++ b/lib/src/builder/codegen.dart @@ -89,13 +89,11 @@ class ImplGenerator { } void _generateLegacyAbstractProps(LegacyAbstractPropsDeclaration declaration) { - _generateUsing( - TypedMapAccessorsGenerator.legacyAbstractProps(declaration, nullSafety: nullSafety)); + _generateUsing(TypedMapAccessorsGenerator.legacyAbstractProps(declaration, nullSafety: nullSafety)); } void _generateLegacyAbstractState(LegacyAbstractStateDeclaration declaration) { - _generateUsing( - TypedMapAccessorsGenerator.legacyAbstractState(declaration, nullSafety: nullSafety)); + _generateUsing(TypedMapAccessorsGenerator.legacyAbstractState(declaration, nullSafety: nullSafety)); } void _generatePropsMixin(PropsMixinDeclaration declaration) { @@ -117,7 +115,6 @@ class ImplGenerator { void _generatePropsMapViewOrFunctionComponent( PropsMapViewOrFunctionComponentDeclaration declaration) { - _generateUsing( - TypedMapImplGenerator.propsMapViewOrFunctionComponent(declaration, nullSafety: nullSafety)); + _generateUsing(TypedMapImplGenerator.propsMapViewOrFunctionComponent(declaration, nullSafety: nullSafety)); } } diff --git a/lib/src/component_declaration/builder_helpers.dart b/lib/src/component_declaration/builder_helpers.dart index 3d809e8b9..593e40d43 100644 --- a/lib/src/component_declaration/builder_helpers.dart +++ b/lib/src/component_declaration/builder_helpers.dart @@ -14,7 +14,6 @@ library over_react.component_declaration.builder_helpers; -import 'package:meta/meta.dart'; import '../../over_react.dart'; import './component_base.dart' as component_base; import './annotations.dart' as annotations; From 7cdb8aaffea32f56b5ef4a8c0d8133f6a1798def Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Fri, 1 Dec 2023 11:13:26 -0700 Subject: [PATCH 09/24] Validate formatting on 2.19.6 --- .github/workflows/dart_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dart_ci.yml b/.github/workflows/dart_ci.yml index 161bdf8e6..994efbe77 100644 --- a/.github/workflows/dart_ci.yml +++ b/.github/workflows/dart_ci.yml @@ -36,7 +36,7 @@ jobs: - name: Verify formatting run: dart run dart_dev format --check - if: always() && matrix.sdk == '2.7.2' && steps.install.outcome == 'success' + if: always() && matrix.sdk == '2.19.6' && steps.install.outcome == 'success' # Analyze before generated files are created to verify that component boilerplate analysis is "clean" without the need for building - name: Analyze example source (pre-build) From 10a82ce1b8babc769920c2ea431bde6ad538dfd7 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Fri, 1 Dec 2023 12:14:15 -0700 Subject: [PATCH 10/24] Remove disable validation tests for backwards compatible and older boilerplates --- .github/workflows/dart_ci.yml | 3 +- lib/src/builder/codegen.dart | 9 ++- ...l_safety_validate_required_props_test.dart | 41 +----------- ...l_safety_validate_required_props_test.dart | 2 +- ...l_safety_validate_required_props_test.dart | 41 +----------- ...date_required_props_test.over_react.g.dart | 64 ------------------- 6 files changed, 13 insertions(+), 147 deletions(-) diff --git a/.github/workflows/dart_ci.yml b/.github/workflows/dart_ci.yml index 994efbe77..f825feeea 100644 --- a/.github/workflows/dart_ci.yml +++ b/.github/workflows/dart_ci.yml @@ -36,7 +36,8 @@ jobs: - name: Verify formatting run: dart run dart_dev format --check - if: always() && matrix.sdk == '2.19.6' && steps.install.outcome == 'success' + # Only run on one sdk version in case there are conflicts + if: always() && matrix.sdk != '2.18.7' && steps.install.outcome == 'success' # Analyze before generated files are created to verify that component boilerplate analysis is "clean" without the need for building - name: Analyze example source (pre-build) diff --git a/lib/src/builder/codegen.dart b/lib/src/builder/codegen.dart index e780d3ae0..68dfa440d 100644 --- a/lib/src/builder/codegen.dart +++ b/lib/src/builder/codegen.dart @@ -89,11 +89,13 @@ class ImplGenerator { } void _generateLegacyAbstractProps(LegacyAbstractPropsDeclaration declaration) { - _generateUsing(TypedMapAccessorsGenerator.legacyAbstractProps(declaration, nullSafety: nullSafety)); + _generateUsing( + TypedMapAccessorsGenerator.legacyAbstractProps(declaration, nullSafety: nullSafety)); } void _generateLegacyAbstractState(LegacyAbstractStateDeclaration declaration) { - _generateUsing(TypedMapAccessorsGenerator.legacyAbstractState(declaration, nullSafety: nullSafety)); + _generateUsing( + TypedMapAccessorsGenerator.legacyAbstractState(declaration, nullSafety: nullSafety)); } void _generatePropsMixin(PropsMixinDeclaration declaration) { @@ -115,6 +117,7 @@ class ImplGenerator { void _generatePropsMapViewOrFunctionComponent( PropsMapViewOrFunctionComponentDeclaration declaration) { - _generateUsing(TypedMapImplGenerator.propsMapViewOrFunctionComponent(declaration, nullSafety: nullSafety)); + _generateUsing( + TypedMapImplGenerator.propsMapViewOrFunctionComponent(declaration, nullSafety: nullSafety)); } } diff --git a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart index 93092d53a..457604e0a 100644 --- a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart @@ -21,7 +21,7 @@ import '../../../../test_util/test_util.dart'; part 'null_safety_validate_required_props_test.over_react.g.dart'; void main() { - group('(New boilerplate) validates required props:', () { + group('(backwards compatible with Dart 1) validates required props:', () { group('non-nullable required prop', () { group('throws when a prop is required and not set', () { test('on mount', () { @@ -134,34 +134,7 @@ void main() { }); }); - test('@disableRequiredPropValidation annotation turns off validation for specific props', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredDynamic = true - ..requiredNullable = true - )()); - }, allOf(returnsNormally, - logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), - logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), - logsPropRequiredError('ComponentTestProps.ref'), - )); - }); - - test('disableRequiredPropValidation method turns off validation for component usage', () { - expect(() { - rtl.render((ComponentTest() - ..disableRequiredPropValidation() - )()); - }, allOf(returnsNormally, - logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), - logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), - logsPropRequiredError('ComponentTestProps.ref'), - logsPropRequiredError('ComponentTestProps.requiredNonNullable'), - logsPropRequiredError('ComponentTestProps.requiredNullable'), - logsPropRequiredError('ComponentTestProps.requiredDynamic'), - )); - }); + // Do not test the validation escape hatches because this boilerplate version has existing error throwing for required props. }); } @@ -178,16 +151,6 @@ class _$ComponentTestProps extends UiProps { // todo is this necessary? late dynamic requiredDynamic; - @disableRequiredPropValidation - late bool disabledRequiredProp; - - @disableRequiredPropValidation - late bool? disabledNullableRequiredProp; - - @disableRequiredPropValidation - @override - late dynamic ref; - bool? nullable; } diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart index 11f0cbea3..22c030b30 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart @@ -21,7 +21,7 @@ import '../../../../test_util/test_util.dart'; part 'null_safety_validate_required_props_test.over_react.g.dart'; void main() { - group('(New boilerplate) validates required props:', () { + group('(Component2) validates required props:', () { group('non-nullable required prop', () { group('throws when a prop is required and not set', () { test('on mount', () { diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart index 334ef14bc..6e7a16db7 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart @@ -21,7 +21,7 @@ import '../../../test_util/test_util.dart'; part 'null_safety_validate_required_props_test.over_react.g.dart'; void main() { - group('(New boilerplate) validates required props:', () { + group('validates required props:', () { group('non-nullable required prop', () { group('throws when a prop is required and not set', () { test('on mount', () { @@ -134,34 +134,7 @@ void main() { }); }); - test('@disableRequiredPropValidation annotation turns off validation for specific props', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredDynamic = true - ..requiredNullable = true - )()); - }, allOf(returnsNormally, - logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), - logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), - logsPropRequiredError('ComponentTestProps.ref'), - )); - }); - - test('disableRequiredPropValidation method turns off validation for component usage', () { - expect(() { - rtl.render((ComponentTest() - ..disableRequiredPropValidation() - )()); - }, allOf(returnsNormally, - logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), - logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), - logsPropRequiredError('ComponentTestProps.ref'), - logsPropRequiredError('ComponentTestProps.requiredNonNullable'), - logsPropRequiredError('ComponentTestProps.requiredNullable'), - logsPropRequiredError('ComponentTestProps.requiredDynamic'), - )); - }); + // Do not test the validation escape hatches because this boilerplate version has existing error throwing for required props. }); } @@ -178,16 +151,6 @@ class _$ComponentTestProps extends UiProps { // todo is this necessary? late dynamic requiredDynamic; - @disableRequiredPropValidation - late bool disabledRequiredProp; - - @disableRequiredPropValidation - late bool? disabledNullableRequiredProp; - - @disableRequiredPropValidation - @override - late dynamic ref; - bool? nullable; } diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart index e1d79586a..cc2635b49 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart @@ -53,45 +53,6 @@ abstract class _$ComponentTestPropsAccessorsMixin set requiredDynamic(dynamic value) => props[_$key__requiredDynamic___$ComponentTestProps] = value; - /// - @override - @disableRequiredPropValidation - bool get disabledRequiredProp => - (props[_$key__disabledRequiredProp___$ComponentTestProps] ?? null) - as bool; - - /// - @override - @disableRequiredPropValidation - set disabledRequiredProp(bool value) => - props[_$key__disabledRequiredProp___$ComponentTestProps] = value; - - /// - @override - @disableRequiredPropValidation - bool? get disabledNullableRequiredProp => - (props[_$key__disabledNullableRequiredProp___$ComponentTestProps] ?? null) - as bool?; - - /// - @override - @disableRequiredPropValidation - set disabledNullableRequiredProp(bool? value) => - props[_$key__disabledNullableRequiredProp___$ComponentTestProps] = value; - - /// - @override - @disableRequiredPropValidation - @override - dynamic get ref => - (props[_$key__ref___$ComponentTestProps] ?? null) as dynamic; - - /// - @override - @disableRequiredPropValidation - @override - set ref(dynamic value) => props[_$key__ref___$ComponentTestProps] = value; - /// @override bool? get nullable => @@ -113,18 +74,6 @@ abstract class _$ComponentTestPropsAccessorsMixin static const PropDescriptor _$prop__requiredDynamic___$ComponentTestProps = PropDescriptor(_$key__requiredDynamic___$ComponentTestProps, isRequired: true, isNullable: true); - static const PropDescriptor - _$prop__disabledRequiredProp___$ComponentTestProps = PropDescriptor( - _$key__disabledRequiredProp___$ComponentTestProps, - isRequired: true, - isNullable: true); - static const PropDescriptor - _$prop__disabledNullableRequiredProp___$ComponentTestProps = - PropDescriptor(_$key__disabledNullableRequiredProp___$ComponentTestProps, - isRequired: true, isNullable: true); - static const PropDescriptor _$prop__ref___$ComponentTestProps = - PropDescriptor(_$key__ref___$ComponentTestProps, - isRequired: true, isNullable: true); static const PropDescriptor _$prop__nullable___$ComponentTestProps = PropDescriptor(_$key__nullable___$ComponentTestProps); static const String _$key__requiredNonNullable___$ComponentTestProps = @@ -133,13 +82,6 @@ abstract class _$ComponentTestPropsAccessorsMixin 'ComponentTestProps.requiredNullable'; static const String _$key__requiredDynamic___$ComponentTestProps = 'ComponentTestProps.requiredDynamic'; - static const String _$key__disabledRequiredProp___$ComponentTestProps = - 'ComponentTestProps.disabledRequiredProp'; - static const String - _$key__disabledNullableRequiredProp___$ComponentTestProps = - 'ComponentTestProps.disabledNullableRequiredProp'; - static const String _$key__ref___$ComponentTestProps = - 'ComponentTestProps.ref'; static const String _$key__nullable___$ComponentTestProps = 'ComponentTestProps.nullable'; @@ -147,18 +89,12 @@ abstract class _$ComponentTestPropsAccessorsMixin _$prop__requiredNonNullable___$ComponentTestProps, _$prop__requiredNullable___$ComponentTestProps, _$prop__requiredDynamic___$ComponentTestProps, - _$prop__disabledRequiredProp___$ComponentTestProps, - _$prop__disabledNullableRequiredProp___$ComponentTestProps, - _$prop__ref___$ComponentTestProps, _$prop__nullable___$ComponentTestProps ]; static const List $propKeys = [ _$key__requiredNonNullable___$ComponentTestProps, _$key__requiredNullable___$ComponentTestProps, _$key__requiredDynamic___$ComponentTestProps, - _$key__disabledRequiredProp___$ComponentTestProps, - _$key__disabledNullableRequiredProp___$ComponentTestProps, - _$key__ref___$ComponentTestProps, _$key__nullable___$ComponentTestProps ]; From a17cee051703d918c1e868954c4f1dab9338326b Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Fri, 1 Dec 2023 12:21:10 -0700 Subject: [PATCH 11/24] Fix warnings --- lib/src/component_declaration/component_base.dart | 8 +++----- .../null_safety_validate_required_props_test.dart | 2 -- .../null_safety_validate_required_props_test.dart | 2 -- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/src/component_declaration/component_base.dart b/lib/src/component_declaration/component_base.dart index 58b807865..8e6fbd29b 100644 --- a/lib/src/component_declaration/component_base.dart +++ b/lib/src/component_declaration/component_base.dart @@ -622,6 +622,7 @@ abstract class UiProps extends MapBase assert(_validateChildren(childArguments.length == 1 ? childArguments.single : childArguments)); // FIXME(null-safety) finalize this implementation and add escape-hatch to opt out in FED-1886 + // todo find out about assert vs devMode if(_shouldValidateRequiredProps) { assert(() { validateRequiredProps(); @@ -684,16 +685,13 @@ abstract class UiProps extends MapBase @mustCallSuper void validateRequiredProps() {} - // todo doc comment + /// Whether [validateRequiredProps] should be run. var _shouldValidateRequiredProps = true; /// Prevents [validateRequiredProps] from being called. /// - /// Allows for an element to have multiple test IDs to prevent overwriting when cloning elements or components. - /// - /// > For use in a testing environment (when [testMode] is true). + /// Allows validation to be skipped to support cases where required props are cloned onto an element. void disableRequiredPropValidation() { - // todo should this also override proptype warnings?? _shouldValidateRequiredProps = false; } } diff --git a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart index 457604e0a..91bed3c30 100644 --- a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart @@ -16,8 +16,6 @@ import 'package:over_react/over_react.dart'; import 'package:react_testing_library/react_testing_library.dart' as rtl; import 'package:test/test.dart'; -import '../../../../test_util/test_util.dart'; - part 'null_safety_validate_required_props_test.over_react.g.dart'; void main() { diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart index 6e7a16db7..975503de1 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart @@ -16,8 +16,6 @@ import 'package:over_react/over_react.dart'; import 'package:react_testing_library/react_testing_library.dart' as rtl; import 'package:test/test.dart'; -import '../../../test_util/test_util.dart'; - part 'null_safety_validate_required_props_test.over_react.g.dart'; void main() { From 1c4a634025860e41eb42f8129c5116e4e8cf2adf Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Wed, 6 Dec 2023 10:50:05 -0700 Subject: [PATCH 12/24] Update for props mixins only --- lib/src/builder/codegen/accessors_generator.dart | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index d52d972c1..129676993 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -150,7 +150,7 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato StringBuffer output = StringBuffer(); - final requiredPropKeys = []; + final requiredPropChecks = []; node.members.whereType().where((field) => !field.isStatic).forEach((field) { T? getConstantAnnotation(AnnotatedNode member, String name, T value) { @@ -205,8 +205,8 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato isPotentiallyNullable = true; // todo sydney add key namespace test - if (disableRequiredPropValidation == null) { - requiredPropKeys.add(' if(!props.containsKey($keyValue)) {\n' + if (type.isProps && disableRequiredPropValidation == null) { + requiredPropChecks.add(' if(!props.containsKey($keyValue)) {\n' ' throw MissingRequiredPropsError(\'Required prop `$accessorName` is missing.\');\n' '}\n'); } @@ -362,17 +362,13 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato String staticVariablesImpl = ' /* GENERATED CONSTANTS */\n$constantsImpl$keyConstantsImpl\n$listImpl$keyListImpl'; - // todo add it here - collect late props from above output.write(staticVariablesImpl); - if (requiredPropKeys.isNotEmpty) { + if (requiredPropChecks.isNotEmpty) { final validateRequiredPropsMethod = '\n @override\n' ' @mustCallSuper\n' ' void validateRequiredProps() {\n' - // todo sydney why is this failing? - // ' super.validateRequiredProps();\n' - // ' debugger();\n' - ' ${requiredPropKeys.join('\n')}\n' + ' ${requiredPropChecks.join('\n')}\n' ' }\n'; output.write(validateRequiredPropsMethod); } From c72755de01ce55105dd660e3adb2a6a64fd20ca6 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Wed, 6 Dec 2023 11:37:38 -0700 Subject: [PATCH 13/24] Fix broken tests --- .../builder_helpers.dart | 57 --------------- .../component_declaration/component_base.dart | 4 +- .../flux_component.over_react.g.dart | 12 ++++ .../over_react_flux.over_react.g.dart | 8 +++ .../over_react_redux/over_react_redux.dart | 4 +- .../over_react_redux.over_react.g.dart | 19 +++-- .../redux_multi_provider.over_react.g.dart | 9 +++ test/mockito.mocks.dart | 70 ++++++++++++++----- .../fixtures/connect_flux_counter.dart | 4 +- .../connect_flux_counter.over_react.g.dart | 10 +-- test/over_react_redux/fixtures/counter.dart | 14 ++-- .../dev_tools/components/counter.dart | 8 +-- .../multiple_stores/components/counter.dart | 8 +-- .../examples/simple/components/counter.dart | 8 +-- 14 files changed, 126 insertions(+), 109 deletions(-) diff --git a/lib/src/component_declaration/builder_helpers.dart b/lib/src/component_declaration/builder_helpers.dart index 593e40d43..a461110d5 100644 --- a/lib/src/component_declaration/builder_helpers.dart +++ b/lib/src/component_declaration/builder_helpers.dart @@ -130,63 +130,6 @@ abstract class UiProps extends component_base.UiProps with GeneratedClass { /// This can be used to derive consumed props by usage in conjunction with [addUnconsumedProps] /// and [addUnconsumedDomProps]. @toBeGenerated PropsMetaCollection get staticMeta => throw UngeneratedError(member: #meta); - - // @override - // @visibleForOverriding - // @mustCallSuper - // void validateRequiredProps() { - // super.validateRequiredProps(); - // // This fails when staticMeta isn't generated, so return early for now so tests don't fail. - // // FIXME(null-safety) generate a static implementation of this instead in FED-1886, and remove this - // return; - // - // // ignore: dead_code - // List? missingRequiredProps; - // List? nullNonNullableRequiredProps; - // - // for (final meta in staticMeta.all) { - // for (final prop in meta.props) { - // if (prop.isRequired) { - // if (prop.isNullable) { - // if (!props.containsKey(prop.key)) { - // (missingRequiredProps ??= []).add(prop); - // } - // } else { - // // Avoid looking up the key twice. - // if (props[prop.key] == null) { - // if (props.containsKey(prop.key)) { - // (nullNonNullableRequiredProps ??= []).add(prop); - // } else { - // (missingRequiredProps ??= []).add(prop); - // } - // } - // } - // } - // } - // } - // - // if (missingRequiredProps == null && nullNonNullableRequiredProps == null) { - // return; - // } - // - // String formatPropKey(String propKey) => '`$propKey`'; - // - // final messageSegments = []; - // if (missingRequiredProps != null) { - // messageSegments.add('Required props are missing: ${missingRequiredProps.map((prop) { - // var propMessage = formatPropKey(prop.key); - // if (prop.isNullable) propMessage += ' (can be null, but must be specified)'; - // return propMessage; - // }).join(' ,')}.'); - // } - // if (nullNonNullableRequiredProps != null) { - // messageSegments - // .add('Required non-nullable props are null: ${nullNonNullableRequiredProps.map((prop) { - // return formatPropKey(prop.key); - // }).join(' ,')}.'); - // } - // throw MissingRequiredPropsError(messageSegments.join(' ')); - // } } class MissingRequiredPropsError extends Error { diff --git a/lib/src/component_declaration/component_base.dart b/lib/src/component_declaration/component_base.dart index 8e6fbd29b..0a3ba3eb5 100644 --- a/lib/src/component_declaration/component_base.dart +++ b/lib/src/component_declaration/component_base.dart @@ -623,12 +623,12 @@ abstract class UiProps extends MapBase // FIXME(null-safety) finalize this implementation and add escape-hatch to opt out in FED-1886 // todo find out about assert vs devMode - if(_shouldValidateRequiredProps) { assert(() { + if(_shouldValidateRequiredProps) { validateRequiredProps(); + } return true; }()); - } // Use `build` instead of using emulated function behavior to work around DDC issue // https://github.com/dart-lang/sdk/issues/29904 diff --git a/lib/src/component_declaration/flux_component.over_react.g.dart b/lib/src/component_declaration/flux_component.over_react.g.dart index 30ed6c60f..14ae68190 100644 --- a/lib/src/component_declaration/flux_component.over_react.g.dart +++ b/lib/src/component_declaration/flux_component.over_react.g.dart @@ -49,6 +49,18 @@ mixin $FluxUiPropsMixin _$key__actions__FluxUiPropsMixin, _$key__store__FluxUiPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey('FluxUiPropsMixin.actions')) { + throw MissingRequiredPropsError('Required prop `actions` is missing.'); + } + + if (!props.containsKey('FluxUiPropsMixin.store')) { + throw MissingRequiredPropsError('Required prop `store` is missing.'); + } + } } @Deprecated('This API is for use only within generated code.' diff --git a/lib/src/over_react_redux/over_react_flux.over_react.g.dart b/lib/src/over_react_redux/over_react_flux.over_react.g.dart index 52cb90f90..597f0a0d5 100644 --- a/lib/src/over_react_redux/over_react_flux.over_react.g.dart +++ b/lib/src/over_react_redux/over_react_flux.over_react.g.dart @@ -35,6 +35,14 @@ abstract class ConnectFluxPropsMixin static const List $propKeys = [ _$key__actions___$ConnectFluxPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey('actions')) { + throw MissingRequiredPropsError('Required prop `actions` is missing.'); + } + } } const PropsMeta _$metaForConnectFluxPropsMixin = PropsMeta( diff --git a/lib/src/over_react_redux/over_react_redux.dart b/lib/src/over_react_redux/over_react_redux.dart index 73171b180..e5541a79d 100644 --- a/lib/src/over_react_redux/over_react_redux.dart +++ b/lib/src/over_react_redux/over_react_redux.dart @@ -21,6 +21,7 @@ import 'package:meta/meta.dart'; import 'package:over_react/component_base.dart'; import 'package:over_react/src/component_declaration/annotations.dart'; import 'package:over_react/src/component_declaration/builder_helpers.dart' as builder_helpers; +import 'package:over_react/src/component_declaration/builder_helpers.dart' show MissingRequiredPropsError; import 'package:over_react/src/component_declaration/component_type_checking.dart'; import 'package:over_react/src/component_declaration/function_component.dart'; import 'package:over_react/src/util/context.dart'; @@ -51,7 +52,8 @@ abstract class _$ConnectPropsMixin implements UiProps { @override Map get props; - dynamic Function(dynamic action)? dispatch; + @disableRequiredPropValidation + late dynamic Function(dynamic action) dispatch; } // ignore: prefer_generic_function_type_aliases diff --git a/lib/src/over_react_redux/over_react_redux.over_react.g.dart b/lib/src/over_react_redux/over_react_redux.over_react.g.dart index 6f7b6c092..2b369585c 100644 --- a/lib/src/over_react_redux/over_react_redux.over_react.g.dart +++ b/lib/src/over_react_redux/over_react_redux.over_react.g.dart @@ -15,17 +15,20 @@ abstract class ConnectPropsMixin implements _$ConnectPropsMixin { /// @override - dynamic Function(dynamic action)? get dispatch => + @disableRequiredPropValidation + dynamic Function(dynamic action) get dispatch => (props[_$key__dispatch___$ConnectPropsMixin] ?? null) as dynamic Function( - dynamic action)?; + dynamic action); /// @override - set dispatch(dynamic Function(dynamic action)? value) => + @disableRequiredPropValidation + set dispatch(dynamic Function(dynamic action) value) => props[_$key__dispatch___$ConnectPropsMixin] = value; /* GENERATED CONSTANTS */ static const PropDescriptor _$prop__dispatch___$ConnectPropsMixin = - PropDescriptor(_$key__dispatch___$ConnectPropsMixin); + PropDescriptor(_$key__dispatch___$ConnectPropsMixin, + isRequired: true, isNullable: true); static const String _$key__dispatch___$ConnectPropsMixin = 'dispatch'; static const List $props = [ @@ -74,6 +77,14 @@ mixin $ReduxProviderPropsMixin on ReduxProviderPropsMixin { _$key__store__ReduxProviderPropsMixin, _$key__context__ReduxProviderPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey('store')) { + throw MissingRequiredPropsError('Required prop `store` is missing.'); + } + } } @Deprecated('This API is for use only within generated code.' diff --git a/lib/src/over_react_redux/redux_multi_provider.over_react.g.dart b/lib/src/over_react_redux/redux_multi_provider.over_react.g.dart index 1625e24b6..5263a95e0 100644 --- a/lib/src/over_react_redux/redux_multi_provider.over_react.g.dart +++ b/lib/src/over_react_redux/redux_multi_provider.over_react.g.dart @@ -62,6 +62,15 @@ abstract class _$ReduxMultiProviderPropsAccessorsMixin static const List $propKeys = [ _$key__storesByContext___$ReduxMultiProviderProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + if (!props.containsKey('ReduxMultiProviderProps.storesByContext')) { + throw MissingRequiredPropsError( + 'Required prop `storesByContext` is missing.'); + } + } } const PropsMeta _$metaForReduxMultiProviderProps = PropsMeta( diff --git a/test/mockito.mocks.dart b/test/mockito.mocks.dart index abff00e9d..d839bdb20 100644 --- a/test/mockito.mocks.dart +++ b/test/mockito.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.0 from annotations +// Mocks generated by Mockito 5.4.2 from annotations // in over_react/test/mockito.dart. // Do not manually edit this file. @@ -42,16 +42,19 @@ class MockLogger extends _i1.Mock implements _i3.Logger { Invocation.getter(#name), returnValue: '', ) as String); + @override Map get children => (super.noSuchMethod( Invocation.getter(#children), returnValue: {}, ) as Map); + @override String get fullName => (super.noSuchMethod( Invocation.getter(#fullName), returnValue: '', ) as String); + @override _i2.Level get level => (super.noSuchMethod( Invocation.getter(#level), @@ -60,6 +63,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { Invocation.getter(#level), ), ) as _i2.Level); + @override set level(_i2.Level? value) => super.noSuchMethod( Invocation.setter( @@ -68,11 +72,19 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + + @override + _i4.Stream<_i2.Level?> get onLevelChanged => (super.noSuchMethod( + Invocation.getter(#onLevelChanged), + returnValue: _i4.Stream<_i2.Level?>.empty(), + ) as _i4.Stream<_i2.Level?>); + @override _i4.Stream<_i5.LogRecord> get onRecord => (super.noSuchMethod( Invocation.getter(#onRecord), returnValue: _i4.Stream<_i5.LogRecord>.empty(), ) as _i4.Stream<_i5.LogRecord>); + @override void clearListeners() => super.noSuchMethod( Invocation.method( @@ -81,6 +93,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override bool isLoggable(_i2.Level? value) => (super.noSuchMethod( Invocation.method( @@ -89,6 +102,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValue: false, ) as bool); + @override void log( _i2.Level? logLevel, @@ -110,6 +124,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override void finest( Object? message, [ @@ -127,6 +142,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override void finer( Object? message, [ @@ -144,6 +160,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override void fine( Object? message, [ @@ -161,6 +178,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override void config( Object? message, [ @@ -178,6 +196,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override void info( Object? message, [ @@ -195,6 +214,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override void warning( Object? message, [ @@ -212,6 +232,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override void severe( Object? message, [ @@ -229,6 +250,7 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); + @override void shout( Object? message, [ @@ -257,31 +279,37 @@ class MockMap extends _i1.Mock implements Map { Invocation.getter(#entries), returnValue: >[], ) as Iterable>); + @override Iterable get keys => (super.noSuchMethod( Invocation.getter(#keys), returnValue: [], ) as Iterable); + @override Iterable get values => (super.noSuchMethod( Invocation.getter(#values), returnValue: [], ) as Iterable); + @override int get length => (super.noSuchMethod( Invocation.getter(#length), returnValue: 0, ) as int); + @override bool get isEmpty => (super.noSuchMethod( Invocation.getter(#isEmpty), returnValue: false, ) as bool); + @override bool get isNotEmpty => (super.noSuchMethod( Invocation.getter(#isNotEmpty), returnValue: false, ) as bool); + @override Map cast() => (super.noSuchMethod( Invocation.method( @@ -290,6 +318,7 @@ class MockMap extends _i1.Mock implements Map { ), returnValue: {}, ) as Map); + @override bool containsValue(Object? value) => (super.noSuchMethod( Invocation.method( @@ -298,6 +327,7 @@ class MockMap extends _i1.Mock implements Map { ), returnValue: false, ) as bool); + @override bool containsKey(Object? key) => (super.noSuchMethod( Invocation.method( @@ -306,6 +336,7 @@ class MockMap extends _i1.Mock implements Map { ), returnValue: false, ) as bool); + @override void operator []=( K? key, @@ -321,13 +352,13 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); + @override Map map( MapEntry Function( - K, - V, - )? - convert) => + K, + V, + )? convert) => (super.noSuchMethod( Invocation.method( #map, @@ -335,6 +366,7 @@ class MockMap extends _i1.Mock implements Map { ), returnValue: {}, ) as Map); + @override void addEntries(Iterable>? newEntries) => super.noSuchMethod( Invocation.method( @@ -343,6 +375,7 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); + @override V update( K? key, @@ -364,13 +397,13 @@ class MockMap extends _i1.Mock implements Map { ifAbsent: ifAbsent, ), ) as V); + @override void updateAll( V Function( - K, - V, - )? - update) => + K, + V, + )? update) => super.noSuchMethod( Invocation.method( #updateAll, @@ -378,13 +411,13 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); + @override void removeWhere( bool Function( - K, - V, - )? - test) => + K, + V, + )? test) => super.noSuchMethod( Invocation.method( #removeWhere, @@ -392,6 +425,7 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); + @override V putIfAbsent( K? key, @@ -410,6 +444,7 @@ class MockMap extends _i1.Mock implements Map { ifAbsent, ), ) as V); + @override void addAll(Map? other) => super.noSuchMethod( Invocation.method( @@ -418,6 +453,7 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); + @override void clear() => super.noSuchMethod( Invocation.method( @@ -426,13 +462,13 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); + @override void forEach( void Function( - K, - V, - )? - action) => + K, + V, + )? action) => super.noSuchMethod( Invocation.method( #forEach, diff --git a/test/over_react_redux/fixtures/connect_flux_counter.dart b/test/over_react_redux/fixtures/connect_flux_counter.dart index a7467e137..92d2c3fa9 100644 --- a/test/over_react_redux/fixtures/connect_flux_counter.dart +++ b/test/over_react_redux/fixtures/connect_flux_counter.dart @@ -36,7 +36,7 @@ class _$ConnectFluxCounterProps extends UiProps { void Function()? mutateStoreDirectly; - late FluxActions actions; + FluxActions? actions; } @Component2() @@ -57,7 +57,7 @@ class ConnectFluxCounterComponent } else if (props.increment != null) { props.increment!(); } else { - props.actions.incrementAction(); + props.actions?.incrementAction(); } } )('+'), diff --git a/test/over_react_redux/fixtures/connect_flux_counter.over_react.g.dart b/test/over_react_redux/fixtures/connect_flux_counter.over_react.g.dart index 3d1c923d4..16df6e0e5 100644 --- a/test/over_react_redux/fixtures/connect_flux_counter.over_react.g.dart +++ b/test/over_react_redux/fixtures/connect_flux_counter.over_react.g.dart @@ -90,12 +90,13 @@ abstract class _$ConnectFluxCounterPropsAccessorsMixin /// @override - FluxActions get actions => - (props[_$key__actions___$ConnectFluxCounterProps] ?? null) as FluxActions; + FluxActions? get actions => + (props[_$key__actions___$ConnectFluxCounterProps] ?? null) + as FluxActions?; /// @override - set actions(FluxActions value) => + set actions(FluxActions? value) => props[_$key__actions___$ConnectFluxCounterProps] = value; /* GENERATED CONSTANTS */ static const PropDescriptor _$prop__currentCount___$ConnectFluxCounterProps = @@ -112,8 +113,7 @@ abstract class _$ConnectFluxCounterPropsAccessorsMixin _$prop__mutateStoreDirectly___$ConnectFluxCounterProps = PropDescriptor(_$key__mutateStoreDirectly___$ConnectFluxCounterProps); static const PropDescriptor _$prop__actions___$ConnectFluxCounterProps = - PropDescriptor(_$key__actions___$ConnectFluxCounterProps, - isRequired: true, isNullable: true); + PropDescriptor(_$key__actions___$ConnectFluxCounterProps); static const String _$key__currentCount___$ConnectFluxCounterProps = 'ConnectFluxCounterProps.currentCount'; static const String _$key__wrapperStyles___$ConnectFluxCounterProps = diff --git a/test/over_react_redux/fixtures/counter.dart b/test/over_react_redux/fixtures/counter.dart index 70fbfe694..860618d1b 100644 --- a/test/over_react_redux/fixtures/counter.dart +++ b/test/over_react_redux/fixtures/counter.dart @@ -50,7 +50,7 @@ class CounterComponent extends UiComponent2 { if (props.increment != null) { props.increment!(); } else if (props.dispatch != null) { - props.dispatch!(IncrementAction()); + props.dispatch(IncrementAction()); } } )('+'), @@ -59,25 +59,21 @@ class CounterComponent extends UiComponent2 { ..onClick = (_) { if (props.decrement != null) { props.decrement!(); - } else if (props.dispatch != null) { - props.dispatch!(DecrementAction()); + } else { + props.dispatch(DecrementAction()); } } )('-'), (Dom.button() ..addTestId('button-model-increment') ..onClick = (_) { - if (props.dispatch != null) { - props.dispatch!(IncrementModelCountAction()); - } + props.dispatch(IncrementModelCountAction()); } )('+'), (Dom.button() ..addTestId('button-model-decrement') ..onClick = (_) { - if (props.dispatch != null) { - props.dispatch!(DecrementModelCountAction()); - } + props.dispatch(DecrementModelCountAction()); } )('-'), props.children, diff --git a/web/over_react_redux/examples/dev_tools/components/counter.dart b/web/over_react_redux/examples/dev_tools/components/counter.dart index 1b6aa6cd1..e3954656f 100644 --- a/web/over_react_redux/examples/dev_tools/components/counter.dart +++ b/web/over_react_redux/examples/dev_tools/components/counter.dart @@ -71,15 +71,15 @@ class CounterComponent extends UiComponent2 { (Dom.button()..onClick = (_) { if (props.increment != null) { props.increment!(); - } else if (props.dispatch != null) { - props.dispatch!(SmallIncrementAction()); + } else { + props.dispatch(SmallIncrementAction()); } })('+'), (Dom.button()..onClick = (_) { if (props.decrement != null) { props.decrement!(); - } else if (props.dispatch != null) { - props.dispatch!(SmallDecrementAction()); + } else { + props.dispatch(SmallDecrementAction()); } })('-'), props.children diff --git a/web/over_react_redux/examples/multiple_stores/components/counter.dart b/web/over_react_redux/examples/multiple_stores/components/counter.dart index 5f2ba6269..adf07e08f 100644 --- a/web/over_react_redux/examples/multiple_stores/components/counter.dart +++ b/web/over_react_redux/examples/multiple_stores/components/counter.dart @@ -49,15 +49,15 @@ class CounterComponent extends UiComponent2 { (Dom.button()..onClick = (_) { if (props.increment != null) { props.increment!(); - } else if (props.dispatch != null) { - props.dispatch!(IncrementAction()); + } else { + props.dispatch(IncrementAction()); } })('+'), (Dom.button()..onClick = (_) { if (props.decrement != null) { props.decrement!(); - } else if (props.dispatch != null) { - props.dispatch!(DecrementAction()); + } else { + props.dispatch(DecrementAction()); } })('-'), props.children diff --git a/web/over_react_redux/examples/simple/components/counter.dart b/web/over_react_redux/examples/simple/components/counter.dart index 58da8d337..fffae0f80 100644 --- a/web/over_react_redux/examples/simple/components/counter.dart +++ b/web/over_react_redux/examples/simple/components/counter.dart @@ -78,15 +78,15 @@ class CounterComponent extends UiComponent2 { // this will be set via mapDispatchToProps, otherwise it will be null. if (props.increment != null) { props.increment!(); - } else if (props.dispatch != null) { - props.dispatch!(SmallIncrementAction()); + } else { + props.dispatch(SmallIncrementAction()); } })('+'), (Dom.button()..onClick = (_) { if (props.decrement != null) { props.decrement!(); - } else if (props.dispatch != null) { - props.dispatch!(SmallDecrementAction()); + } else { + props.dispatch(SmallDecrementAction()); } })('-'), props.children From a3ad695a1df16c0374afc23573abbcf26c0b8930 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Wed, 6 Dec 2023 11:47:05 -0700 Subject: [PATCH 14/24] Clean up --- .../component_declaration/annotations.dart | 2 +- .../component_declaration/component_base.dart | 13 ++-- test/mockito.mocks.dart | 70 +++++-------------- 3 files changed, 24 insertions(+), 61 deletions(-) diff --git a/lib/src/component_declaration/annotations.dart b/lib/src/component_declaration/annotations.dart index e2431eacc..200f459ae 100644 --- a/lib/src/component_declaration/annotations.dart +++ b/lib/src/component_declaration/annotations.dart @@ -384,7 +384,7 @@ abstract class TypedMap { String? get keyNamespace; } -// todo add doc comment +/// Prevents required prop validation from being performed on a prop. const _DisableRequiredPropValidation disableRequiredPropValidation = _DisableRequiredPropValidation(); class _DisableRequiredPropValidation { diff --git a/lib/src/component_declaration/component_base.dart b/lib/src/component_declaration/component_base.dart index 0a3ba3eb5..e7dc7978d 100644 --- a/lib/src/component_declaration/component_base.dart +++ b/lib/src/component_declaration/component_base.dart @@ -621,12 +621,10 @@ abstract class UiProps extends MapBase assert(_validateChildren(childArguments.length == 1 ? childArguments.single : childArguments)); - // FIXME(null-safety) finalize this implementation and add escape-hatch to opt out in FED-1886 - // todo find out about assert vs devMode assert(() { - if(_shouldValidateRequiredProps) { - validateRequiredProps(); - } + if(_shouldValidateRequiredProps) { + validateRequiredProps(); + } return true; }()); @@ -679,8 +677,9 @@ abstract class UiProps extends MapBase : const {}; } - // FIXME(null-safety) document and generate overrides in FED-1886 - // todo add doc comment + /// Validate at run-time that all required props are set. + /// + /// This method is overridden in generated files. @visibleForOverriding @mustCallSuper void validateRequiredProps() {} diff --git a/test/mockito.mocks.dart b/test/mockito.mocks.dart index d839bdb20..abff00e9d 100644 --- a/test/mockito.mocks.dart +++ b/test/mockito.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.2 from annotations +// Mocks generated by Mockito 5.4.0 from annotations // in over_react/test/mockito.dart. // Do not manually edit this file. @@ -42,19 +42,16 @@ class MockLogger extends _i1.Mock implements _i3.Logger { Invocation.getter(#name), returnValue: '', ) as String); - @override Map get children => (super.noSuchMethod( Invocation.getter(#children), returnValue: {}, ) as Map); - @override String get fullName => (super.noSuchMethod( Invocation.getter(#fullName), returnValue: '', ) as String); - @override _i2.Level get level => (super.noSuchMethod( Invocation.getter(#level), @@ -63,7 +60,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { Invocation.getter(#level), ), ) as _i2.Level); - @override set level(_i2.Level? value) => super.noSuchMethod( Invocation.setter( @@ -72,19 +68,11 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - - @override - _i4.Stream<_i2.Level?> get onLevelChanged => (super.noSuchMethod( - Invocation.getter(#onLevelChanged), - returnValue: _i4.Stream<_i2.Level?>.empty(), - ) as _i4.Stream<_i2.Level?>); - @override _i4.Stream<_i5.LogRecord> get onRecord => (super.noSuchMethod( Invocation.getter(#onRecord), returnValue: _i4.Stream<_i5.LogRecord>.empty(), ) as _i4.Stream<_i5.LogRecord>); - @override void clearListeners() => super.noSuchMethod( Invocation.method( @@ -93,7 +81,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override bool isLoggable(_i2.Level? value) => (super.noSuchMethod( Invocation.method( @@ -102,7 +89,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValue: false, ) as bool); - @override void log( _i2.Level? logLevel, @@ -124,7 +110,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override void finest( Object? message, [ @@ -142,7 +127,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override void finer( Object? message, [ @@ -160,7 +144,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override void fine( Object? message, [ @@ -178,7 +161,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override void config( Object? message, [ @@ -196,7 +178,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override void info( Object? message, [ @@ -214,7 +195,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override void warning( Object? message, [ @@ -232,7 +212,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override void severe( Object? message, [ @@ -250,7 +229,6 @@ class MockLogger extends _i1.Mock implements _i3.Logger { ), returnValueForMissingStub: null, ); - @override void shout( Object? message, [ @@ -279,37 +257,31 @@ class MockMap extends _i1.Mock implements Map { Invocation.getter(#entries), returnValue: >[], ) as Iterable>); - @override Iterable get keys => (super.noSuchMethod( Invocation.getter(#keys), returnValue: [], ) as Iterable); - @override Iterable get values => (super.noSuchMethod( Invocation.getter(#values), returnValue: [], ) as Iterable); - @override int get length => (super.noSuchMethod( Invocation.getter(#length), returnValue: 0, ) as int); - @override bool get isEmpty => (super.noSuchMethod( Invocation.getter(#isEmpty), returnValue: false, ) as bool); - @override bool get isNotEmpty => (super.noSuchMethod( Invocation.getter(#isNotEmpty), returnValue: false, ) as bool); - @override Map cast() => (super.noSuchMethod( Invocation.method( @@ -318,7 +290,6 @@ class MockMap extends _i1.Mock implements Map { ), returnValue: {}, ) as Map); - @override bool containsValue(Object? value) => (super.noSuchMethod( Invocation.method( @@ -327,7 +298,6 @@ class MockMap extends _i1.Mock implements Map { ), returnValue: false, ) as bool); - @override bool containsKey(Object? key) => (super.noSuchMethod( Invocation.method( @@ -336,7 +306,6 @@ class MockMap extends _i1.Mock implements Map { ), returnValue: false, ) as bool); - @override void operator []=( K? key, @@ -352,13 +321,13 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); - @override Map map( MapEntry Function( - K, - V, - )? convert) => + K, + V, + )? + convert) => (super.noSuchMethod( Invocation.method( #map, @@ -366,7 +335,6 @@ class MockMap extends _i1.Mock implements Map { ), returnValue: {}, ) as Map); - @override void addEntries(Iterable>? newEntries) => super.noSuchMethod( Invocation.method( @@ -375,7 +343,6 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); - @override V update( K? key, @@ -397,13 +364,13 @@ class MockMap extends _i1.Mock implements Map { ifAbsent: ifAbsent, ), ) as V); - @override void updateAll( V Function( - K, - V, - )? update) => + K, + V, + )? + update) => super.noSuchMethod( Invocation.method( #updateAll, @@ -411,13 +378,13 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); - @override void removeWhere( bool Function( - K, - V, - )? test) => + K, + V, + )? + test) => super.noSuchMethod( Invocation.method( #removeWhere, @@ -425,7 +392,6 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); - @override V putIfAbsent( K? key, @@ -444,7 +410,6 @@ class MockMap extends _i1.Mock implements Map { ifAbsent, ), ) as V); - @override void addAll(Map? other) => super.noSuchMethod( Invocation.method( @@ -453,7 +418,6 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); - @override void clear() => super.noSuchMethod( Invocation.method( @@ -462,13 +426,13 @@ class MockMap extends _i1.Mock implements Map { ), returnValueForMissingStub: null, ); - @override void forEach( void Function( - K, - V, - )? action) => + K, + V, + )? + action) => super.noSuchMethod( Invocation.method( #forEach, From f1c606f3129beb707470837b863a427ec19ec2a5 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Wed, 6 Dec 2023 11:59:19 -0700 Subject: [PATCH 15/24] Only run tests on ddc --- .../null_safety_validate_required_props_test.dart | 2 +- .../component2/null_safety_validate_required_props_test.dart | 2 +- .../null_safety_validate_required_props_test.dart | 2 +- .../null_safety_validate_required_props_test.dart | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart index 91bed3c30..4a2ea076e 100644 --- a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart @@ -133,7 +133,7 @@ void main() { }); // Do not test the validation escape hatches because this boilerplate version has existing error throwing for required props. - }); + }, tags: 'ddc'); } @Factory() diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart index 22c030b30..07e4dc13c 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart @@ -162,7 +162,7 @@ void main() { logsPropRequiredError('ComponentTestProps.requiredDynamic'), )); }); - }); + }, tags: 'ddc'); } @Factory() diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart index 9165f0e34..405921ac7 100644 --- a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart @@ -162,7 +162,7 @@ void main() { logsPropRequiredError('ComponentTestProps.requiredDynamic'), )); }); - }); + }, tags: 'ddc'); } // ignore: undefined_identifier, invalid_assignment diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart index 975503de1..671072281 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart @@ -133,7 +133,7 @@ void main() { }); // Do not test the validation escape hatches because this boilerplate version has existing error throwing for required props. - }); + }, tags: 'ddc'); } @Factory() From bfcd331824ee066a7172b534faaae6ecd3881630 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Wed, 6 Dec 2023 12:19:09 -0700 Subject: [PATCH 16/24] Remove todos --- .../builder/codegen/accessors_generator.dart | 1 - ...l_safety_validate_required_props_test.dart | 12 ---------- ...l_safety_validate_required_props_test.dart | 15 ------------- ...date_required_props_test.over_react.g.dart | 22 ------------------- ...l_safety_validate_required_props_test.dart | 19 ++++------------ ...l_safety_validate_required_props_test.dart | 12 ---------- ...date_required_props_test.over_react.g.dart | 22 ------------------- test/vm_tests/builder/codegen_test.dart | 1 - 8 files changed, 4 insertions(+), 100 deletions(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 129676993..1003b2ed5 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -204,7 +204,6 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato isRequired = true; isPotentiallyNullable = true; - // todo sydney add key namespace test if (type.isProps && disableRequiredPropValidation == null) { requiredPropChecks.add(' if(!props.containsKey($keyValue)) {\n' ' throw MissingRequiredPropsError(\'Required prop `$accessorName` is missing.\');\n' diff --git a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart index 4a2ea076e..600498a6a 100644 --- a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart @@ -25,7 +25,6 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredDynamic = true ..requiredNullable = true )()); }, @@ -42,14 +41,12 @@ void main() { expect(() { view = rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); expect(() { view.rerender((ComponentTest() - ..requiredDynamic = true ..requiredNullable = true )()); }, @@ -65,7 +62,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); @@ -77,7 +73,6 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredDynamic = true ..requiredNonNullable = true )()); }, @@ -93,14 +88,12 @@ void main() { expect(() { view = rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); expect(() { view.rerender((ComponentTest() - ..requiredDynamic = true ..requiredNonNullable = true )()); }, @@ -115,7 +108,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = null )()); }, returnsNormally); @@ -125,7 +117,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); @@ -146,9 +137,6 @@ class _$ComponentTestProps extends UiProps { late bool? requiredNullable; - // todo is this necessary? - late dynamic requiredDynamic; - bool? nullable; } diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart index 07e4dc13c..9d4c9de43 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart @@ -27,7 +27,6 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredDynamic = true ..requiredNullable = true )()); }, @@ -44,14 +43,12 @@ void main() { expect(() { view = rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); expect(() { view.rerender((ComponentTest() - ..requiredDynamic = true ..requiredNullable = true )()); }, @@ -67,7 +64,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); @@ -79,7 +75,6 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredDynamic = true ..requiredNonNullable = true )()); }, @@ -95,14 +90,12 @@ void main() { expect(() { view = rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); expect(() { view.rerender((ComponentTest() - ..requiredDynamic = true ..requiredNonNullable = true )()); }, @@ -117,7 +110,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = null )()); }, returnsNormally); @@ -127,7 +119,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); @@ -138,7 +129,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, allOf(returnsNormally, @@ -175,9 +165,6 @@ class _$ComponentTestProps extends UiProps { late bool? requiredNullable; - // todo is this necessary? - late dynamic requiredDynamic; - @disableRequiredPropValidation late bool disabledRequiredProp; @@ -193,8 +180,6 @@ class _$ComponentTestProps extends UiProps { @Component2() class ComponentTestComponent extends UiComponent2 { - // todo test default props - // todo test other boilerplates @override render() => Dom.div()(); } diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart index b3e57a2b9..4783cdc41 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart @@ -43,16 +43,6 @@ abstract class _$ComponentTestPropsAccessorsMixin set requiredNullable(bool? value) => props[_$key__requiredNullable___$ComponentTestProps] = value; - /// - @override - dynamic get requiredDynamic => - (props[_$key__requiredDynamic___$ComponentTestProps] ?? null) as dynamic; - - /// - @override - set requiredDynamic(dynamic value) => - props[_$key__requiredDynamic___$ComponentTestProps] = value; - /// @override @disableRequiredPropValidation @@ -110,9 +100,6 @@ abstract class _$ComponentTestPropsAccessorsMixin static const PropDescriptor _$prop__requiredNullable___$ComponentTestProps = PropDescriptor(_$key__requiredNullable___$ComponentTestProps, isRequired: true, isNullable: true); - static const PropDescriptor _$prop__requiredDynamic___$ComponentTestProps = - PropDescriptor(_$key__requiredDynamic___$ComponentTestProps, - isRequired: true, isNullable: true); static const PropDescriptor _$prop__disabledRequiredProp___$ComponentTestProps = PropDescriptor( _$key__disabledRequiredProp___$ComponentTestProps, @@ -131,8 +118,6 @@ abstract class _$ComponentTestPropsAccessorsMixin 'ComponentTestProps.requiredNonNullable'; static const String _$key__requiredNullable___$ComponentTestProps = 'ComponentTestProps.requiredNullable'; - static const String _$key__requiredDynamic___$ComponentTestProps = - 'ComponentTestProps.requiredDynamic'; static const String _$key__disabledRequiredProp___$ComponentTestProps = 'ComponentTestProps.disabledRequiredProp'; static const String @@ -146,7 +131,6 @@ abstract class _$ComponentTestPropsAccessorsMixin static const List $props = [ _$prop__requiredNonNullable___$ComponentTestProps, _$prop__requiredNullable___$ComponentTestProps, - _$prop__requiredDynamic___$ComponentTestProps, _$prop__disabledRequiredProp___$ComponentTestProps, _$prop__disabledNullableRequiredProp___$ComponentTestProps, _$prop__ref___$ComponentTestProps, @@ -155,7 +139,6 @@ abstract class _$ComponentTestPropsAccessorsMixin static const List $propKeys = [ _$key__requiredNonNullable___$ComponentTestProps, _$key__requiredNullable___$ComponentTestProps, - _$key__requiredDynamic___$ComponentTestProps, _$key__disabledRequiredProp___$ComponentTestProps, _$key__disabledNullableRequiredProp___$ComponentTestProps, _$key__ref___$ComponentTestProps, @@ -174,11 +157,6 @@ abstract class _$ComponentTestPropsAccessorsMixin throw MissingRequiredPropsError( 'Required prop `requiredNullable` is missing.'); } - - if (!props.containsKey('ComponentTestProps.requiredDynamic')) { - throw MissingRequiredPropsError( - 'Required prop `requiredDynamic` is missing.'); - } } } diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart index 405921ac7..2028d216b 100644 --- a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart @@ -27,7 +27,7 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredDynamic = true + ..requiredNullable = true )()); }, @@ -44,14 +44,13 @@ void main() { expect(() { view = rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); expect(() { view.rerender((ComponentTest() - ..requiredDynamic = true + ..requiredNullable = true )()); }, @@ -67,7 +66,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); @@ -79,7 +77,7 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredDynamic = true + ..requiredNonNullable = true )()); }, @@ -95,14 +93,13 @@ void main() { expect(() { view = rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); expect(() { view.rerender((ComponentTest() - ..requiredDynamic = true + ..requiredNonNullable = true )()); }, @@ -117,7 +114,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = null )()); }, returnsNormally); @@ -127,7 +123,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); @@ -138,7 +133,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, allOf(returnsNormally, @@ -173,9 +167,6 @@ mixin ComponentTestProps on UiProps { late bool? requiredNullable; - // todo is this necessary? - late dynamic requiredDynamic; - @disableRequiredPropValidation late bool disabledRequiredProp; @@ -190,8 +181,6 @@ mixin ComponentTestProps on UiProps { } class ComponentTestComponent extends UiComponent2 { - // todo test default props - // todo test other boilerplates @override render() => Dom.div()(); } diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart index 671072281..4cf189953 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart @@ -25,7 +25,6 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredDynamic = true ..requiredNullable = true )()); }, @@ -42,14 +41,12 @@ void main() { expect(() { view = rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); expect(() { view.rerender((ComponentTest() - ..requiredDynamic = true ..requiredNullable = true )()); }, @@ -65,7 +62,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); @@ -77,7 +73,6 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredDynamic = true ..requiredNonNullable = true )()); }, @@ -93,14 +88,12 @@ void main() { expect(() { view = rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); expect(() { view.rerender((ComponentTest() - ..requiredDynamic = true ..requiredNonNullable = true )()); }, @@ -115,7 +108,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = null )()); }, returnsNormally); @@ -125,7 +117,6 @@ void main() { expect(() { rtl.render((ComponentTest() ..requiredNonNullable = true - ..requiredDynamic = true ..requiredNullable = true )()); }, returnsNormally); @@ -146,9 +137,6 @@ class _$ComponentTestProps extends UiProps { late bool? requiredNullable; - // todo is this necessary? - late dynamic requiredDynamic; - bool? nullable; } diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart index cc2635b49..5c6513d43 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart @@ -43,16 +43,6 @@ abstract class _$ComponentTestPropsAccessorsMixin set requiredNullable(bool? value) => props[_$key__requiredNullable___$ComponentTestProps] = value; - /// - @override - dynamic get requiredDynamic => - (props[_$key__requiredDynamic___$ComponentTestProps] ?? null) as dynamic; - - /// - @override - set requiredDynamic(dynamic value) => - props[_$key__requiredDynamic___$ComponentTestProps] = value; - /// @override bool? get nullable => @@ -71,30 +61,23 @@ abstract class _$ComponentTestPropsAccessorsMixin static const PropDescriptor _$prop__requiredNullable___$ComponentTestProps = PropDescriptor(_$key__requiredNullable___$ComponentTestProps, isRequired: true, isNullable: true); - static const PropDescriptor _$prop__requiredDynamic___$ComponentTestProps = - PropDescriptor(_$key__requiredDynamic___$ComponentTestProps, - isRequired: true, isNullable: true); static const PropDescriptor _$prop__nullable___$ComponentTestProps = PropDescriptor(_$key__nullable___$ComponentTestProps); static const String _$key__requiredNonNullable___$ComponentTestProps = 'ComponentTestProps.requiredNonNullable'; static const String _$key__requiredNullable___$ComponentTestProps = 'ComponentTestProps.requiredNullable'; - static const String _$key__requiredDynamic___$ComponentTestProps = - 'ComponentTestProps.requiredDynamic'; static const String _$key__nullable___$ComponentTestProps = 'ComponentTestProps.nullable'; static const List $props = [ _$prop__requiredNonNullable___$ComponentTestProps, _$prop__requiredNullable___$ComponentTestProps, - _$prop__requiredDynamic___$ComponentTestProps, _$prop__nullable___$ComponentTestProps ]; static const List $propKeys = [ _$key__requiredNonNullable___$ComponentTestProps, _$key__requiredNullable___$ComponentTestProps, - _$key__requiredDynamic___$ComponentTestProps, _$key__nullable___$ComponentTestProps ]; @@ -110,11 +93,6 @@ abstract class _$ComponentTestPropsAccessorsMixin throw MissingRequiredPropsError( 'Required prop `requiredNullable` is missing.'); } - - if (!props.containsKey('ComponentTestProps.requiredDynamic')) { - throw MissingRequiredPropsError( - 'Required prop `requiredDynamic` is missing.'); - } } } diff --git a/test/vm_tests/builder/codegen_test.dart b/test/vm_tests/builder/codegen_test.dart index ec90cde0b..ae31d6325 100644 --- a/test/vm_tests/builder/codegen_test.dart +++ b/test/vm_tests/builder/codegen_test.dart @@ -27,7 +27,6 @@ import 'package:test/test.dart'; import '../../mockito.mocks.dart'; import './util.dart'; -// todo add tests here as well for generated code?? main() { group('ImplGenerator', () { ImplGenerator? implGenerator; From 8715d67c59b9a6b00a206115fbf0941555060447 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Wed, 6 Dec 2023 12:19:41 -0700 Subject: [PATCH 17/24] More cleanup --- .../component2/null_safety_validate_required_props_test.dart | 1 - .../null_safety_validate_required_props_test.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart index 9d4c9de43..db2b54b38 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart @@ -149,7 +149,6 @@ void main() { logsPropRequiredError('ComponentTestProps.ref'), logsPropRequiredError('ComponentTestProps.requiredNonNullable'), logsPropRequiredError('ComponentTestProps.requiredNullable'), - logsPropRequiredError('ComponentTestProps.requiredDynamic'), )); }); }, tags: 'ddc'); diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart index 2028d216b..b65729d8e 100644 --- a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart @@ -153,7 +153,6 @@ void main() { logsPropRequiredError('ComponentTestProps.ref'), logsPropRequiredError('ComponentTestProps.requiredNonNullable'), logsPropRequiredError('ComponentTestProps.requiredNullable'), - logsPropRequiredError('ComponentTestProps.requiredDynamic'), )); }); }, tags: 'ddc'); From 8890aad7c2104f68200e310a12ac52be49c07501 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Mon, 11 Dec 2023 10:23:46 -0700 Subject: [PATCH 18/24] Add multiple mixin test --- ...l_safety_validate_required_props_test.dart | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart index b65729d8e..e90436bb4 100644 --- a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart @@ -27,7 +27,6 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredNullable = true )()); }, @@ -50,7 +49,6 @@ void main() { expect(() { view.rerender((ComponentTest() - ..requiredNullable = true )()); }, @@ -77,7 +75,6 @@ void main() { test('on mount', () { expect(() { rtl.render((ComponentTest() - ..requiredNonNullable = true )()); }, @@ -99,7 +96,6 @@ void main() { expect(() { view.rerender((ComponentTest() - ..requiredNonNullable = true )()); }, @@ -155,6 +151,47 @@ void main() { logsPropRequiredError('ComponentTestProps.requiredNullable'), )); }); + + group('required props in multiple mixins', () { + test('throw an error when a prop in the first mixin is missing', () { + expect(() { + rtl.render((MultipleMixinsTest() + ..requiredNullable = true + ..secondRequiredProp = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + + test('throw an error when a prop in the second mixin is missing', () { + expect(() { + rtl.render((MultipleMixinsTest() + ..requiredNullable = true + ..requiredNonNullable = true + )()); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `secondRequiredProp` is missing.')))); + }); + + test('does not throw when all required props are set', () { + expect(() { + rtl.render((MultipleMixinsTest() + ..requiredNullable = true + ..requiredNonNullable = true + ..secondRequiredProp = true + )()); + }, + returnsNormally); + }); + }); }, tags: 'ddc'); } @@ -183,3 +220,14 @@ class ComponentTestComponent extends UiComponent2 { @override render() => Dom.div()(); } + +UiFactory MultipleMixinsTest = uiFunction( + (props) {}, + _$MultipleMixinsTestConfig, // ignore: undefined_identifier +); + +mixin MultipleMixinsTestPropsMixin on UiProps { + late bool secondRequiredProp; +} + +class MultipleMixinsTestProps = UiProps with MultipleMixinsTestPropsMixin, ComponentTestProps; From c346c0c371957eceb59fefafb0750c9017c1cb3d Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Mon, 11 Dec 2023 10:25:10 -0700 Subject: [PATCH 19/24] Add back super call --- .../builder/codegen/accessors_generator.dart | 5 +- ...bstract_transition_props.over_react.g.dart | 6 ++ .../error_boundary.over_react.g.dart | 6 ++ .../component/resize_sensor.over_react.g.dart | 6 ++ .../suspense_component.over_react.g.dart | 6 ++ .../with_transition.over_react.g.dart | 6 ++ .../flux_component.over_react.g.dart | 1 + .../over_react_flux.over_react.g.dart | 8 --- .../over_react_redux.over_react.g.dart | 1 + .../redux_multi_provider.over_react.g.dart | 9 --- ...fe_render_manager_helper.over_react.g.dart | 6 ++ ...abstract_transition_test.over_react.g.dart | 6 ++ .../element_type_test.over_react.g.dart | 12 ++++ .../shared_stack_tests.over_react.g.dart | 18 ++++++ .../dummy_component.over_react.g.dart | 1 + .../flawed_component.over_react.g.dart | 6 ++ ...lawed_component_on_mount.over_react.g.dart | 6 ++ ...nt_that_renders_a_string.over_react.g.dart | 6 ++ ...ent_that_renders_nothing.over_react.g.dart | 6 ++ .../lazy_load_me_props.over_react.g.dart | 6 ++ .../prop_typedef_fixtures.over_react.g.dart | 31 ---------- .../pure_test_components.over_react.g.dart | 12 ++++ .../component/memo_test.over_react.g.dart | 6 ++ .../component/ref_util_test.over_react.g.dart | 12 ++++ ...date_required_props_test.over_react.g.dart | 14 ----- ...date_required_props_test.over_react.g.dart | 14 ----- .../components.over_react.g.dart | 60 +++++++++++++++++++ .../cast_ui_factory_test.over_react.g.dart | 6 ++ ...omponent_debug_name_test.over_react.g.dart | 12 ++++ .../util/js_component_test.over_react.g.dart | 12 ++++ .../prop_conversion_test.over_react.g.dart | 42 +++++++++++++ .../fixtures/counter_fn.over_react.g.dart | 18 ++++++ .../hooks/use_dispatch_test.over_react.g.dart | 12 ++++ .../hooks/use_store_test.over_react.g.dart | 12 ++++ .../store_bindings_tests.over_react.g.dart | 12 ++++ 35 files changed, 324 insertions(+), 78 deletions(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 1003b2ed5..3d72fb35e 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -150,7 +150,7 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato StringBuffer output = StringBuffer(); - final requiredPropChecks = []; + final requiredPropChecks = []; node.members.whereType().where((field) => !field.isStatic).forEach((field) { T? getConstantAnnotation(AnnotatedNode member, String name, T value) { @@ -363,10 +363,11 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato output.write(staticVariablesImpl); - if (requiredPropChecks.isNotEmpty) { + if (type.isProps && version != Version.v3_legacyDart2Only) { final validateRequiredPropsMethod = '\n @override\n' ' @mustCallSuper\n' ' void validateRequiredProps() {\n' + ' super.validateRequiredProps();\n' ' ${requiredPropChecks.join('\n')}\n' ' }\n'; output.write(validateRequiredPropsMethod); diff --git a/lib/src/component/abstract_transition_props.over_react.g.dart b/lib/src/component/abstract_transition_props.over_react.g.dart index 574a83397..79c3b82c3 100644 --- a/lib/src/component/abstract_transition_props.over_react.g.dart +++ b/lib/src/component/abstract_transition_props.over_react.g.dart @@ -103,6 +103,12 @@ mixin $TransitionPropsMixin on TransitionPropsMixin { _$key__onWillShow__TransitionPropsMixin, _$key__onDidShow__TransitionPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/lib/src/component/error_boundary.over_react.g.dart b/lib/src/component/error_boundary.over_react.g.dart index 4e812a885..87a668f92 100644 --- a/lib/src/component/error_boundary.over_react.g.dart +++ b/lib/src/component/error_boundary.over_react.g.dart @@ -349,6 +349,12 @@ mixin $ErrorBoundaryProps on ErrorBoundaryProps { _$key__shouldLogErrors__ErrorBoundaryProps, _$key__logger__ErrorBoundaryProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/lib/src/component/resize_sensor.over_react.g.dart b/lib/src/component/resize_sensor.over_react.g.dart index 1365e936f..9989cfe76 100644 --- a/lib/src/component/resize_sensor.over_react.g.dart +++ b/lib/src/component/resize_sensor.over_react.g.dart @@ -266,6 +266,12 @@ mixin $ResizeSensorProps on ResizeSensorProps { _$key__onDetachedMountCheck__ResizeSensorProps, _$key__onDidReset__ResizeSensorProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/lib/src/component/suspense_component.over_react.g.dart b/lib/src/component/suspense_component.over_react.g.dart index 2f50f22c0..41d57775f 100644 --- a/lib/src/component/suspense_component.over_react.g.dart +++ b/lib/src/component/suspense_component.over_react.g.dart @@ -28,6 +28,12 @@ mixin $SuspensePropsMixin on SuspensePropsMixin { _$prop__fallback__SuspensePropsMixin ]; static const List $propKeys = [_$key__fallback__SuspensePropsMixin]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/lib/src/component/with_transition.over_react.g.dart b/lib/src/component/with_transition.over_react.g.dart index f9fa2b7b3..c1f416f13 100644 --- a/lib/src/component/with_transition.over_react.g.dart +++ b/lib/src/component/with_transition.over_react.g.dart @@ -300,6 +300,12 @@ mixin $WithTransitionPropsMixin on WithTransitionPropsMixin { _$key__childPropsByPhase__WithTransitionPropsMixin, _$key__transitionTimeout__WithTransitionPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/lib/src/component_declaration/flux_component.over_react.g.dart b/lib/src/component_declaration/flux_component.over_react.g.dart index 14ae68190..2335d1ed3 100644 --- a/lib/src/component_declaration/flux_component.over_react.g.dart +++ b/lib/src/component_declaration/flux_component.over_react.g.dart @@ -53,6 +53,7 @@ mixin $FluxUiPropsMixin @override @mustCallSuper void validateRequiredProps() { + super.validateRequiredProps(); if (!props.containsKey('FluxUiPropsMixin.actions')) { throw MissingRequiredPropsError('Required prop `actions` is missing.'); } diff --git a/lib/src/over_react_redux/over_react_flux.over_react.g.dart b/lib/src/over_react_redux/over_react_flux.over_react.g.dart index 597f0a0d5..52cb90f90 100644 --- a/lib/src/over_react_redux/over_react_flux.over_react.g.dart +++ b/lib/src/over_react_redux/over_react_flux.over_react.g.dart @@ -35,14 +35,6 @@ abstract class ConnectFluxPropsMixin static const List $propKeys = [ _$key__actions___$ConnectFluxPropsMixin ]; - - @override - @mustCallSuper - void validateRequiredProps() { - if (!props.containsKey('actions')) { - throw MissingRequiredPropsError('Required prop `actions` is missing.'); - } - } } const PropsMeta _$metaForConnectFluxPropsMixin = PropsMeta( diff --git a/lib/src/over_react_redux/over_react_redux.over_react.g.dart b/lib/src/over_react_redux/over_react_redux.over_react.g.dart index 2b369585c..176e50943 100644 --- a/lib/src/over_react_redux/over_react_redux.over_react.g.dart +++ b/lib/src/over_react_redux/over_react_redux.over_react.g.dart @@ -81,6 +81,7 @@ mixin $ReduxProviderPropsMixin on ReduxProviderPropsMixin { @override @mustCallSuper void validateRequiredProps() { + super.validateRequiredProps(); if (!props.containsKey('store')) { throw MissingRequiredPropsError('Required prop `store` is missing.'); } diff --git a/lib/src/over_react_redux/redux_multi_provider.over_react.g.dart b/lib/src/over_react_redux/redux_multi_provider.over_react.g.dart index 5263a95e0..1625e24b6 100644 --- a/lib/src/over_react_redux/redux_multi_provider.over_react.g.dart +++ b/lib/src/over_react_redux/redux_multi_provider.over_react.g.dart @@ -62,15 +62,6 @@ abstract class _$ReduxMultiProviderPropsAccessorsMixin static const List $propKeys = [ _$key__storesByContext___$ReduxMultiProviderProps ]; - - @override - @mustCallSuper - void validateRequiredProps() { - if (!props.containsKey('ReduxMultiProviderProps.storesByContext')) { - throw MissingRequiredPropsError( - 'Required prop `storesByContext` is missing.'); - } - } } const PropsMeta _$metaForReduxMultiProviderProps = PropsMeta( diff --git a/lib/src/util/safe_render_manager/safe_render_manager_helper.over_react.g.dart b/lib/src/util/safe_render_manager/safe_render_manager_helper.over_react.g.dart index 39a13b1bf..9e624a134 100644 --- a/lib/src/util/safe_render_manager/safe_render_manager_helper.over_react.g.dart +++ b/lib/src/util/safe_render_manager/safe_render_manager_helper.over_react.g.dart @@ -289,6 +289,12 @@ mixin $SafeRenderManagerHelperProps on SafeRenderManagerHelperProps { _$key__getInitialContent__SafeRenderManagerHelperProps, _$key__contentRef__SafeRenderManagerHelperProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/component/abstract_transition_test.over_react.g.dart b/test/over_react/component/abstract_transition_test.over_react.g.dart index 44e0968cd..7869444cf 100644 --- a/test/over_react/component/abstract_transition_test.over_react.g.dart +++ b/test/over_react/component/abstract_transition_test.over_react.g.dart @@ -391,6 +391,12 @@ mixin $TransitionerPropsMixin on TransitionerPropsMixin { _$key__initiallyShown__TransitionerPropsMixin, _$key__transitionTimeout__TransitionerPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/component/element_type_test.over_react.g.dart b/test/over_react/component/element_type_test.over_react.g.dart index 9b83c3272..a6a86f0d1 100644 --- a/test/over_react/component/element_type_test.over_react.g.dart +++ b/test/over_react/component/element_type_test.over_react.g.dart @@ -164,6 +164,12 @@ mixin $CustomTestProps on CustomTestProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -183,6 +189,12 @@ mixin $CustomFnTestProps on CustomFnTestProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/component/error_boundary/shared_stack_tests.over_react.g.dart b/test/over_react/component/error_boundary/shared_stack_tests.over_react.g.dart index 9bdc30900..3e534e8f5 100644 --- a/test/over_react/component/error_boundary/shared_stack_tests.over_react.g.dart +++ b/test/over_react/component/error_boundary/shared_stack_tests.over_react.g.dart @@ -256,6 +256,12 @@ mixin $ThrowingComponent2Props on ThrowingComponent2Props { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -275,6 +281,12 @@ mixin $ThrowingFunctionComponentProps on ThrowingFunctionComponentProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -294,6 +306,12 @@ mixin $ThrowingForwardRefComponentProps on ThrowingForwardRefComponentProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/component/fixtures/dummy_component.over_react.g.dart b/test/over_react/component/fixtures/dummy_component.over_react.g.dart index c02968a3d..ca62d27f4 100644 --- a/test/over_react/component/fixtures/dummy_component.over_react.g.dart +++ b/test/over_react/component/fixtures/dummy_component.over_react.g.dart @@ -48,6 +48,7 @@ abstract class _$DummyPropsAccessorsMixin implements _$DummyProps { @override @mustCallSuper void validateRequiredProps() { + super.validateRequiredProps(); if (!props.containsKey('DummyProps.onComponentDidMount')) { throw MissingRequiredPropsError( 'Required prop `onComponentDidMount` is missing.'); diff --git a/test/over_react/component/fixtures/flawed_component.over_react.g.dart b/test/over_react/component/fixtures/flawed_component.over_react.g.dart index d73cf51ab..c72470981 100644 --- a/test/over_react/component/fixtures/flawed_component.over_react.g.dart +++ b/test/over_react/component/fixtures/flawed_component.over_react.g.dart @@ -43,6 +43,12 @@ abstract class _$FlawedPropsAccessorsMixin implements _$FlawedProps { static const List $propKeys = [ _$key__buttonTestIdPrefix___$FlawedProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } const PropsMeta _$metaForFlawedProps = PropsMeta( diff --git a/test/over_react/component/fixtures/flawed_component_on_mount.over_react.g.dart b/test/over_react/component/fixtures/flawed_component_on_mount.over_react.g.dart index 37ddfd71d..91135f362 100644 --- a/test/over_react/component/fixtures/flawed_component_on_mount.over_react.g.dart +++ b/test/over_react/component/fixtures/flawed_component_on_mount.over_react.g.dart @@ -27,6 +27,12 @@ abstract class _$FlawedOnMountPropsAccessorsMixin static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } const PropsMeta _$metaForFlawedOnMountProps = PropsMeta( diff --git a/test/over_react/component/fixtures/flawed_component_that_renders_a_string.over_react.g.dart b/test/over_react/component/fixtures/flawed_component_that_renders_a_string.over_react.g.dart index 0d2427712..688bba879 100644 --- a/test/over_react/component/fixtures/flawed_component_that_renders_a_string.over_react.g.dart +++ b/test/over_react/component/fixtures/flawed_component_that_renders_a_string.over_react.g.dart @@ -27,6 +27,12 @@ abstract class _$FlawedWithStringChildPropsAccessorsMixin static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } const PropsMeta _$metaForFlawedWithStringChildProps = PropsMeta( diff --git a/test/over_react/component/fixtures/flawed_component_that_renders_nothing.over_react.g.dart b/test/over_react/component/fixtures/flawed_component_that_renders_nothing.over_react.g.dart index 5b7c744ec..93a2b2084 100644 --- a/test/over_react/component/fixtures/flawed_component_that_renders_nothing.over_react.g.dart +++ b/test/over_react/component/fixtures/flawed_component_that_renders_nothing.over_react.g.dart @@ -27,6 +27,12 @@ abstract class _$FlawedWithNoChildPropsAccessorsMixin static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } const PropsMeta _$metaForFlawedWithNoChildProps = PropsMeta( diff --git a/test/over_react/component/fixtures/lazy_load_me_props.over_react.g.dart b/test/over_react/component/fixtures/lazy_load_me_props.over_react.g.dart index c5453e13a..1d0b7caca 100644 --- a/test/over_react/component/fixtures/lazy_load_me_props.over_react.g.dart +++ b/test/over_react/component/fixtures/lazy_load_me_props.over_react.g.dart @@ -31,6 +31,12 @@ mixin $LazyLoadMePropsMixin on LazyLoadMePropsMixin { static const List $propKeys = [ _$key__initialCount__LazyLoadMePropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/component/fixtures/prop_typedef_fixtures.over_react.g.dart b/test/over_react/component/fixtures/prop_typedef_fixtures.over_react.g.dart index 63f2ee8cc..6fc6602c5 100644 --- a/test/over_react/component/fixtures/prop_typedef_fixtures.over_react.g.dart +++ b/test/over_react/component/fixtures/prop_typedef_fixtures.over_react.g.dart @@ -97,22 +97,6 @@ abstract class _$TestAbstractCustomRendererComponentPropsAccessorsMixin _$key__parameterizedCustomRenderer___$TestAbstractCustomRendererComponentProps, _$key__somePropKey___$TestAbstractCustomRendererComponentProps ]; - - @override - @mustCallSuper - void validateRequiredProps() { - if (!props.containsKey( - 'TestAbstractCustomRendererComponentProps.customRenderer')) { - throw MissingRequiredPropsError( - 'Required prop `customRenderer` is missing.'); - } - - if (!props.containsKey( - 'TestAbstractCustomRendererComponentProps.parameterizedCustomRenderer')) { - throw MissingRequiredPropsError( - 'Required prop `parameterizedCustomRenderer` is missing.'); - } - } } const PropsMeta _$metaForTestAbstractCustomRendererComponentProps = PropsMeta( @@ -999,21 +983,6 @@ abstract class _$TestCustomRendererComponentPropsAccessorsMixin _$key__somePropKey___$TestCustomRendererComponentProps, _$key__someInitialStateKeyValue___$TestCustomRendererComponentProps ]; - - @override - @mustCallSuper - void validateRequiredProps() { - if (!props.containsKey('TestCustomRendererComponentProps.customRenderer')) { - throw MissingRequiredPropsError( - 'Required prop `customRenderer` is missing.'); - } - - if (!props.containsKey( - 'TestCustomRendererComponentProps.parameterizedCustomRenderer')) { - throw MissingRequiredPropsError( - 'Required prop `parameterizedCustomRenderer` is missing.'); - } - } } const PropsMeta _$metaForTestCustomRendererComponentProps = PropsMeta( diff --git a/test/over_react/component/fixtures/pure_test_components.over_react.g.dart b/test/over_react/component/fixtures/pure_test_components.over_react.g.dart index 01d99c285..220bdafa5 100644 --- a/test/over_react/component/fixtures/pure_test_components.over_react.g.dart +++ b/test/over_react/component/fixtures/pure_test_components.over_react.g.dart @@ -434,6 +434,12 @@ mixin $PureTestPropsMixin on PureTestPropsMixin { _$key__childBoolProp__PureTestPropsMixin, _$key__childFuncProp__PureTestPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -480,6 +486,12 @@ mixin $SharedPureTestPropsMixin on SharedPureTestPropsMixin { _$key__sharedBoolProp__SharedPureTestPropsMixin, _$key__someVDomEl__SharedPureTestPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/component/memo_test.over_react.g.dart b/test/over_react/component/memo_test.over_react.g.dart index 3000a8dff..5b66b3fa2 100644 --- a/test/over_react/component/memo_test.over_react.g.dart +++ b/test/over_react/component/memo_test.over_react.g.dart @@ -209,6 +209,12 @@ mixin $FunctionCustomPropsProps on FunctionCustomPropsProps { _$key__testProp__FunctionCustomPropsProps, _$key__testFuncProp__FunctionCustomPropsProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/component/ref_util_test.over_react.g.dart b/test/over_react/component/ref_util_test.over_react.g.dart index a28607f1f..b96ac8066 100644 --- a/test/over_react/component/ref_util_test.over_react.g.dart +++ b/test/over_react/component/ref_util_test.over_react.g.dart @@ -170,6 +170,12 @@ mixin $BasicProps on BasicProps { static const List $props = [_$prop__childId__BasicProps]; static const List $propKeys = [_$key__childId__BasicProps]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -189,6 +195,12 @@ mixin $BasicUiFunctionProps on BasicUiFunctionProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart index 4783cdc41..1f7a8c809 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart @@ -144,20 +144,6 @@ abstract class _$ComponentTestPropsAccessorsMixin _$key__ref___$ComponentTestProps, _$key__nullable___$ComponentTestProps ]; - - @override - @mustCallSuper - void validateRequiredProps() { - if (!props.containsKey('ComponentTestProps.requiredNonNullable')) { - throw MissingRequiredPropsError( - 'Required prop `requiredNonNullable` is missing.'); - } - - if (!props.containsKey('ComponentTestProps.requiredNullable')) { - throw MissingRequiredPropsError( - 'Required prop `requiredNullable` is missing.'); - } - } } const PropsMeta _$metaForComponentTestProps = PropsMeta( diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart index 5c6513d43..dfd3c910a 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart @@ -80,20 +80,6 @@ abstract class _$ComponentTestPropsAccessorsMixin _$key__requiredNullable___$ComponentTestProps, _$key__nullable___$ComponentTestProps ]; - - @override - @mustCallSuper - void validateRequiredProps() { - if (!props.containsKey('ComponentTestProps.requiredNonNullable')) { - throw MissingRequiredPropsError( - 'Required prop `requiredNonNullable` is missing.'); - } - - if (!props.containsKey('ComponentTestProps.requiredNullable')) { - throw MissingRequiredPropsError( - 'Required prop `requiredNullable` is missing.'); - } - } } const PropsMeta _$metaForComponentTestProps = PropsMeta( diff --git a/test/over_react/component_declaration/function_type_checking_test/components.over_react.g.dart b/test/over_react/component_declaration/function_type_checking_test/components.over_react.g.dart index 98809424f..24a9bc772 100644 --- a/test/over_react/component_declaration/function_type_checking_test/components.over_react.g.dart +++ b/test/over_react/component_declaration/function_type_checking_test/components.over_react.g.dart @@ -187,6 +187,12 @@ mixin $TestAProps on TestAProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -206,6 +212,12 @@ mixin $TestBProps on TestBProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -225,6 +237,12 @@ mixin $TestParentProps on TestParentProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -244,6 +262,12 @@ mixin $TestSubtypeProps on TestSubtypeProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -263,6 +287,12 @@ mixin $TestSubsubtypeProps on TestSubsubtypeProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -282,6 +312,12 @@ mixin $TestExtendtypeProps on TestExtendtypeProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -301,6 +337,12 @@ mixin $OneLevelWrapperProps on OneLevelWrapperProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -320,6 +362,12 @@ mixin $TwoLevelWrapperProps on TwoLevelWrapperProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -341,6 +389,12 @@ mixin $DoNotReferenceThisFactoryExceptForInASingleTestProps static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -361,6 +415,12 @@ mixin $TestUninitializedParentProps on TestUninitializedParentProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/util/cast_ui_factory_test.over_react.g.dart b/test/over_react/util/cast_ui_factory_test.over_react.g.dart index 4bf0ee2e9..93b63579f 100644 --- a/test/over_react/util/cast_ui_factory_test.over_react.g.dart +++ b/test/over_react/util/cast_ui_factory_test.over_react.g.dart @@ -163,6 +163,12 @@ mixin $BasicProps on BasicProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/util/component_debug_name_test.over_react.g.dart b/test/over_react/util/component_debug_name_test.over_react.g.dart index bc42e9e92..6c5c27320 100644 --- a/test/over_react/util/component_debug_name_test.over_react.g.dart +++ b/test/over_react/util/component_debug_name_test.over_react.g.dart @@ -175,6 +175,12 @@ abstract class _$TestComponentPropsAccessorsMixin static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } const PropsMeta _$metaForTestComponentProps = PropsMeta( @@ -250,6 +256,12 @@ mixin $TestComponent2Props on TestComponent2Props { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/util/js_component_test.over_react.g.dart b/test/over_react/util/js_component_test.over_react.g.dart index 3d3584f3e..885c8894f 100644 --- a/test/over_react/util/js_component_test.over_react.g.dart +++ b/test/over_react/util/js_component_test.over_react.g.dart @@ -70,6 +70,12 @@ mixin $TestPropsMixin on TestPropsMixin { _$key__dynamicProp__TestPropsMixin, _$key__untypedProp__TestPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -113,6 +119,12 @@ mixin $ASecondPropsMixin on ASecondPropsMixin { _$key__disabled__ASecondPropsMixin, _$key__anotherProp__ASecondPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react/util/prop_conversion_test.over_react.g.dart b/test/over_react/util/prop_conversion_test.over_react.g.dart index 7a41a05f3..36ba43c65 100644 --- a/test/over_react/util/prop_conversion_test.over_react.g.dart +++ b/test/over_react/util/prop_conversion_test.over_react.g.dart @@ -179,6 +179,12 @@ mixin $ExpectsDartMapPropProps on ExpectsDartMapPropProps { static const List $propKeys = [ _$key__dartMapProp__ExpectsDartMapPropProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -198,6 +204,12 @@ mixin $ExpectsDartStylePropProps on ExpectsDartStylePropProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -217,6 +229,12 @@ mixin $ExpectsListChildrenPropProps on ExpectsListChildrenPropProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -236,6 +254,12 @@ mixin $ClassComponentProps on ClassComponentProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -255,6 +279,12 @@ mixin $BasicForwardRefProps on BasicForwardRefProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -289,6 +319,12 @@ mixin $DartTestJsWrapperPropsMixin on DartTestJsWrapperPropsMixin { static const List $propKeys = [ _$key__onRender__DartTestJsWrapperPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -396,6 +432,12 @@ mixin $TestJsProps on TestJsProps { _$key__inputComponent__TestJsProps, _$key__buttonComponent__TestJsProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react_redux/fixtures/counter_fn.over_react.g.dart b/test/over_react_redux/fixtures/counter_fn.over_react.g.dart index b162d752e..ea58ce538 100644 --- a/test/over_react_redux/fixtures/counter_fn.over_react.g.dart +++ b/test/over_react_redux/fixtures/counter_fn.over_react.g.dart @@ -32,6 +32,12 @@ mixin $CounterFnProps on CounterFnProps { static const List $propKeys = [ _$key__countEqualityFn__CounterFnProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -71,6 +77,12 @@ mixin $ModelCounterFnPropsMixin on ModelCounterFnPropsMixin { static const List $propKeys = [ _$key__modelCountEqualityFn__ModelCounterFnPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -110,6 +122,12 @@ mixin $CustomContextCounterFnPropsMixin on CustomContextCounterFnPropsMixin { static const List $propKeys = [ _$key__bigCountEqualityFn__CustomContextCounterFnPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react_redux/hooks/use_dispatch_test.over_react.g.dart b/test/over_react_redux/hooks/use_dispatch_test.over_react.g.dart index 64bf7d62a..5105c7b45 100644 --- a/test/over_react_redux/hooks/use_dispatch_test.over_react.g.dart +++ b/test/over_react_redux/hooks/use_dispatch_test.over_react.g.dart @@ -17,6 +17,12 @@ mixin $UseDispatchCounterFnProps on UseDispatchCounterFnProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -37,6 +43,12 @@ mixin $CustomContextUseDispatchCounterFnProps static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react_redux/hooks/use_store_test.over_react.g.dart b/test/over_react_redux/hooks/use_store_test.over_react.g.dart index 3724633b6..698e88447 100644 --- a/test/over_react_redux/hooks/use_store_test.over_react.g.dart +++ b/test/over_react_redux/hooks/use_store_test.over_react.g.dart @@ -17,6 +17,12 @@ mixin $UseStoreCounterFnProps on UseStoreCounterFnProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -37,6 +43,12 @@ mixin $CustomContextUseStoreCounterFnProps static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test/over_react_redux/store_bindings_tests.over_react.g.dart b/test/over_react_redux/store_bindings_tests.over_react.g.dart index 35cdefac8..fdf7966f1 100644 --- a/test/over_react_redux/store_bindings_tests.over_react.g.dart +++ b/test/over_react_redux/store_bindings_tests.over_react.g.dart @@ -45,6 +45,12 @@ mixin $TestSelectorProps on TestSelectorProps { _$key__onRender__TestSelectorProps, _$key__equality__TestSelectorProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -79,6 +85,12 @@ mixin $TestConnectPropsMixin on TestConnectPropsMixin { static const List $propKeys = [ _$key__interestingValue__TestConnectPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' From 1e9a5bf18d8ec6326af1104107545b2c43f02d14 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Mon, 11 Dec 2023 14:09:21 -0700 Subject: [PATCH 20/24] Remove tests for legacy boilerplates --- .../builder/codegen/accessors_generator.dart | 4 +- .../dummy_component.over_react.g.dart | 10 - .../flawed_component.over_react.g.dart | 6 - ...lawed_component_on_mount.over_react.g.dart | 6 - ...nt_that_renders_a_string.over_react.g.dart | 6 - ...ent_that_renders_nothing.over_react.g.dart | 6 - ...l_safety_validate_required_props_test.dart | 154 ---------- ...l_safety_validate_required_props_test.dart | 184 ------------ ...date_required_props_test.over_react.g.dart | 272 ------------------ ...l_safety_validate_required_props_test.dart | 148 ---------- ...date_required_props_test.over_react.g.dart | 151 ---------- ...omponent_debug_name_test.over_react.g.dart | 6 - ...over_react_component_declaration_test.dart | 6 - 13 files changed, 3 insertions(+), 956 deletions(-) delete mode 100644 test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart delete mode 100644 test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart delete mode 100644 test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart delete mode 100644 test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart delete mode 100644 test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 3d72fb35e..e63104160 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -363,7 +363,9 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato output.write(staticVariablesImpl); - if (type.isProps && version != Version.v3_legacyDart2Only) { + if (type.isProps && + version != Version.v3_legacyDart2Only && + version != Version.v2_legacyBackwardsCompat) { final validateRequiredPropsMethod = '\n @override\n' ' @mustCallSuper\n' ' void validateRequiredProps() {\n' diff --git a/test/over_react/component/fixtures/dummy_component.over_react.g.dart b/test/over_react/component/fixtures/dummy_component.over_react.g.dart index ca62d27f4..7658d16ed 100644 --- a/test/over_react/component/fixtures/dummy_component.over_react.g.dart +++ b/test/over_react/component/fixtures/dummy_component.over_react.g.dart @@ -44,16 +44,6 @@ abstract class _$DummyPropsAccessorsMixin implements _$DummyProps { static const List $propKeys = [ _$key__onComponentDidMount___$DummyProps ]; - - @override - @mustCallSuper - void validateRequiredProps() { - super.validateRequiredProps(); - if (!props.containsKey('DummyProps.onComponentDidMount')) { - throw MissingRequiredPropsError( - 'Required prop `onComponentDidMount` is missing.'); - } - } } const PropsMeta _$metaForDummyProps = PropsMeta( diff --git a/test/over_react/component/fixtures/flawed_component.over_react.g.dart b/test/over_react/component/fixtures/flawed_component.over_react.g.dart index c72470981..d73cf51ab 100644 --- a/test/over_react/component/fixtures/flawed_component.over_react.g.dart +++ b/test/over_react/component/fixtures/flawed_component.over_react.g.dart @@ -43,12 +43,6 @@ abstract class _$FlawedPropsAccessorsMixin implements _$FlawedProps { static const List $propKeys = [ _$key__buttonTestIdPrefix___$FlawedProps ]; - - @override - @mustCallSuper - void validateRequiredProps() { - super.validateRequiredProps(); - } } const PropsMeta _$metaForFlawedProps = PropsMeta( diff --git a/test/over_react/component/fixtures/flawed_component_on_mount.over_react.g.dart b/test/over_react/component/fixtures/flawed_component_on_mount.over_react.g.dart index 91135f362..37ddfd71d 100644 --- a/test/over_react/component/fixtures/flawed_component_on_mount.over_react.g.dart +++ b/test/over_react/component/fixtures/flawed_component_on_mount.over_react.g.dart @@ -27,12 +27,6 @@ abstract class _$FlawedOnMountPropsAccessorsMixin static const List $props = []; static const List $propKeys = []; - - @override - @mustCallSuper - void validateRequiredProps() { - super.validateRequiredProps(); - } } const PropsMeta _$metaForFlawedOnMountProps = PropsMeta( diff --git a/test/over_react/component/fixtures/flawed_component_that_renders_a_string.over_react.g.dart b/test/over_react/component/fixtures/flawed_component_that_renders_a_string.over_react.g.dart index 688bba879..0d2427712 100644 --- a/test/over_react/component/fixtures/flawed_component_that_renders_a_string.over_react.g.dart +++ b/test/over_react/component/fixtures/flawed_component_that_renders_a_string.over_react.g.dart @@ -27,12 +27,6 @@ abstract class _$FlawedWithStringChildPropsAccessorsMixin static const List $props = []; static const List $propKeys = []; - - @override - @mustCallSuper - void validateRequiredProps() { - super.validateRequiredProps(); - } } const PropsMeta _$metaForFlawedWithStringChildProps = PropsMeta( diff --git a/test/over_react/component/fixtures/flawed_component_that_renders_nothing.over_react.g.dart b/test/over_react/component/fixtures/flawed_component_that_renders_nothing.over_react.g.dart index 93a2b2084..5b7c744ec 100644 --- a/test/over_react/component/fixtures/flawed_component_that_renders_nothing.over_react.g.dart +++ b/test/over_react/component/fixtures/flawed_component_that_renders_nothing.over_react.g.dart @@ -27,12 +27,6 @@ abstract class _$FlawedWithNoChildPropsAccessorsMixin static const List $props = []; static const List $propKeys = []; - - @override - @mustCallSuper - void validateRequiredProps() { - super.validateRequiredProps(); - } } const PropsMeta _$metaForFlawedWithNoChildProps = PropsMeta( diff --git a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart deleted file mode 100644 index 600498a6a..000000000 --- a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2023 Workiva Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:over_react/over_react.dart'; -import 'package:react_testing_library/react_testing_library.dart' as rtl; -import 'package:test/test.dart'; - -part 'null_safety_validate_required_props_test.over_react.g.dart'; - -void main() { - group('(backwards compatible with Dart 1) validates required props:', () { - group('non-nullable required prop', () { - group('throws when a prop is required and not set', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains( - 'Required prop `requiredNonNullable` is missing.')))); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - - expect(() { - view.rerender((ComponentTest() - ..requiredNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains( - 'Required prop `requiredNonNullable` is missing.')))); - }); - }); - - test('does not throw when the prop is set', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - }); - }); - - group('nullable required prop', () { - group('throws when a prop is required and not set', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains('Required prop `requiredNullable` is missing.')))); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - - expect(() { - view.rerender((ComponentTest() - ..requiredNonNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains('Required prop `requiredNullable` is missing.')))); - }); - }); - - test('does not throw when the prop is set to null', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = null - )()); - }, returnsNormally); - }); - - test('does not throw when the prop is set', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - }); - }); - - // Do not test the validation escape hatches because this boilerplate version has existing error throwing for required props. - }, tags: 'ddc'); -} - -@Factory() -// ignore: undefined_identifier, invalid_assignment -UiFactory ComponentTest = _$ComponentTest; - -@Props() -class _$ComponentTestProps extends UiProps { - late bool requiredNonNullable; - - late bool? requiredNullable; - - bool? nullable; -} - -@Component() -class ComponentTestComponent extends UiComponent { - @override - render() => Dom.div()(); -} - -// AF-3369 This will be removed once the transition to Dart 2 is complete. -// ignore: mixin_of_non_class, undefined_class -class ComponentTestProps extends _$ComponentTestProps with _$ComponentTestPropsAccessorsMixin { - // ignore: undefined_identifier, undefined_class, const_initialized_with_non_constant_value, invalid_assignment - static const PropsMeta meta = _$metaForComponentTestProps; -} diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart deleted file mode 100644 index db2b54b38..000000000 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2023 Workiva Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:over_react/over_react.dart'; -import 'package:react_testing_library/react_testing_library.dart' as rtl; -import 'package:test/test.dart'; - -import '../../../../test_util/test_util.dart'; - -part 'null_safety_validate_required_props_test.over_react.g.dart'; - -void main() { - group('(Component2) validates required props:', () { - group('non-nullable required prop', () { - group('throws when a prop is required and not set', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains( - 'Required prop `requiredNonNullable` is missing.')))); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - - expect(() { - view.rerender((ComponentTest() - ..requiredNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains( - 'Required prop `requiredNonNullable` is missing.')))); - }); - }); - - test('does not throw when the prop is set', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - }); - }); - - group('nullable required prop', () { - group('throws when a prop is required and not set', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains('Required prop `requiredNullable` is missing.')))); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - - expect(() { - view.rerender((ComponentTest() - ..requiredNonNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains('Required prop `requiredNullable` is missing.')))); - }); - }); - - test('does not throw when the prop is set to null', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = null - )()); - }, returnsNormally); - }); - - test('does not throw when the prop is set', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - }); - }); - - test('@disableRequiredPropValidation annotation turns off validation for specific props', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, allOf(returnsNormally, - logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), - logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), - logsPropRequiredError('ComponentTestProps.ref'), - )); - }); - - test('disableRequiredPropValidation method turns off validation for component usage', () { - expect(() { - rtl.render((ComponentTest() - ..disableRequiredPropValidation() - )()); - }, allOf(returnsNormally, - logsPropRequiredError('ComponentTestProps.disabledRequiredProp'), - logsPropRequiredError('ComponentTestProps.disabledNullableRequiredProp'), - logsPropRequiredError('ComponentTestProps.ref'), - logsPropRequiredError('ComponentTestProps.requiredNonNullable'), - logsPropRequiredError('ComponentTestProps.requiredNullable'), - )); - }); - }, tags: 'ddc'); -} - -@Factory() -// ignore: undefined_identifier, invalid_assignment -UiFactory ComponentTest = _$ComponentTest; - -@Props() -class _$ComponentTestProps extends UiProps { - late bool requiredNonNullable; - - late bool? requiredNullable; - - @disableRequiredPropValidation - late bool disabledRequiredProp; - - @disableRequiredPropValidation - late bool? disabledNullableRequiredProp; - - @disableRequiredPropValidation - @override - late dynamic ref; - - bool? nullable; -} - -@Component2() -class ComponentTestComponent extends UiComponent2 { - @override - render() => Dom.div()(); -} diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart deleted file mode 100644 index 1f7a8c809..000000000 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.over_react.g.dart +++ /dev/null @@ -1,272 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ignore_for_file: deprecated_member_use_from_same_package, unnecessary_null_in_if_null_operators, prefer_null_aware_operators -part of 'null_safety_validate_required_props_test.dart'; - -// ************************************************************************** -// OverReactBuilder (package:over_react/src/builder.dart) -// ************************************************************************** - -// React component factory implementation. -// -// Registers component implementation and links type meta to builder factory. -final $ComponentTestComponentFactory = registerComponent2( - () => _$ComponentTestComponent(), - builderFactory: _$ComponentTest, - componentClass: ComponentTestComponent, - isWrapper: false, - parentType: null, -); - -abstract class _$ComponentTestPropsAccessorsMixin - implements _$ComponentTestProps { - @override - Map get props; - - /// - @override - bool get requiredNonNullable => - (props[_$key__requiredNonNullable___$ComponentTestProps] ?? null) as bool; - - /// - @override - set requiredNonNullable(bool value) => - props[_$key__requiredNonNullable___$ComponentTestProps] = value; - - /// - @override - bool? get requiredNullable => - (props[_$key__requiredNullable___$ComponentTestProps] ?? null) as bool?; - - /// - @override - set requiredNullable(bool? value) => - props[_$key__requiredNullable___$ComponentTestProps] = value; - - /// - @override - @disableRequiredPropValidation - bool get disabledRequiredProp => - (props[_$key__disabledRequiredProp___$ComponentTestProps] ?? null) - as bool; - - /// - @override - @disableRequiredPropValidation - set disabledRequiredProp(bool value) => - props[_$key__disabledRequiredProp___$ComponentTestProps] = value; - - /// - @override - @disableRequiredPropValidation - bool? get disabledNullableRequiredProp => - (props[_$key__disabledNullableRequiredProp___$ComponentTestProps] ?? null) - as bool?; - - /// - @override - @disableRequiredPropValidation - set disabledNullableRequiredProp(bool? value) => - props[_$key__disabledNullableRequiredProp___$ComponentTestProps] = value; - - /// - @override - @disableRequiredPropValidation - @override - dynamic get ref => - (props[_$key__ref___$ComponentTestProps] ?? null) as dynamic; - - /// - @override - @disableRequiredPropValidation - @override - set ref(dynamic value) => props[_$key__ref___$ComponentTestProps] = value; - - /// - @override - bool? get nullable => - (props[_$key__nullable___$ComponentTestProps] ?? null) as bool?; - - /// - @override - set nullable(bool? value) => - props[_$key__nullable___$ComponentTestProps] = value; - /* GENERATED CONSTANTS */ - static const PropDescriptor - _$prop__requiredNonNullable___$ComponentTestProps = PropDescriptor( - _$key__requiredNonNullable___$ComponentTestProps, - isRequired: true, - isNullable: true); - static const PropDescriptor _$prop__requiredNullable___$ComponentTestProps = - PropDescriptor(_$key__requiredNullable___$ComponentTestProps, - isRequired: true, isNullable: true); - static const PropDescriptor - _$prop__disabledRequiredProp___$ComponentTestProps = PropDescriptor( - _$key__disabledRequiredProp___$ComponentTestProps, - isRequired: true, - isNullable: true); - static const PropDescriptor - _$prop__disabledNullableRequiredProp___$ComponentTestProps = - PropDescriptor(_$key__disabledNullableRequiredProp___$ComponentTestProps, - isRequired: true, isNullable: true); - static const PropDescriptor _$prop__ref___$ComponentTestProps = - PropDescriptor(_$key__ref___$ComponentTestProps, - isRequired: true, isNullable: true); - static const PropDescriptor _$prop__nullable___$ComponentTestProps = - PropDescriptor(_$key__nullable___$ComponentTestProps); - static const String _$key__requiredNonNullable___$ComponentTestProps = - 'ComponentTestProps.requiredNonNullable'; - static const String _$key__requiredNullable___$ComponentTestProps = - 'ComponentTestProps.requiredNullable'; - static const String _$key__disabledRequiredProp___$ComponentTestProps = - 'ComponentTestProps.disabledRequiredProp'; - static const String - _$key__disabledNullableRequiredProp___$ComponentTestProps = - 'ComponentTestProps.disabledNullableRequiredProp'; - static const String _$key__ref___$ComponentTestProps = - 'ComponentTestProps.ref'; - static const String _$key__nullable___$ComponentTestProps = - 'ComponentTestProps.nullable'; - - static const List $props = [ - _$prop__requiredNonNullable___$ComponentTestProps, - _$prop__requiredNullable___$ComponentTestProps, - _$prop__disabledRequiredProp___$ComponentTestProps, - _$prop__disabledNullableRequiredProp___$ComponentTestProps, - _$prop__ref___$ComponentTestProps, - _$prop__nullable___$ComponentTestProps - ]; - static const List $propKeys = [ - _$key__requiredNonNullable___$ComponentTestProps, - _$key__requiredNullable___$ComponentTestProps, - _$key__disabledRequiredProp___$ComponentTestProps, - _$key__disabledNullableRequiredProp___$ComponentTestProps, - _$key__ref___$ComponentTestProps, - _$key__nullable___$ComponentTestProps - ]; -} - -const PropsMeta _$metaForComponentTestProps = PropsMeta( - fields: _$ComponentTestPropsAccessorsMixin.$props, - keys: _$ComponentTestPropsAccessorsMixin.$propKeys, -); - -class ComponentTestProps extends _$ComponentTestProps - with _$ComponentTestPropsAccessorsMixin { - static const PropsMeta meta = _$metaForComponentTestProps; -} - -_$$ComponentTestProps _$ComponentTest([Map? backingProps]) => - backingProps == null - ? _$$ComponentTestProps$JsMap(JsBackedMap()) - : _$$ComponentTestProps(backingProps); - -// Concrete props implementation. -// -// Implements constructor and backing map, and links up to generated component factory. -abstract class _$$ComponentTestProps extends _$ComponentTestProps - with _$ComponentTestPropsAccessorsMixin - implements ComponentTestProps { - _$$ComponentTestProps._(); - - factory _$$ComponentTestProps(Map? backingMap) { - if (backingMap == null || backingMap is JsBackedMap) { - return _$$ComponentTestProps$JsMap(backingMap as JsBackedMap?); - } else { - return _$$ComponentTestProps$PlainMap(backingMap); - } - } - - /// Let `UiProps` internals know that this class has been generated. - @override - bool get $isClassGenerated => true; - - /// The `ReactComponentFactory` associated with the component built by this class. - @override - ReactComponentFactoryProxy get componentFactory => - super.componentFactory ?? $ComponentTestComponentFactory; - - /// The default namespace for the prop getters/setters generated for this class. - @override - String get propKeyNamespace => 'ComponentTestProps.'; -} - -// Concrete props implementation that can be backed by any [Map]. -class _$$ComponentTestProps$PlainMap extends _$$ComponentTestProps { - // This initializer of `_props` to an empty map, as well as the reassignment - // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 - _$$ComponentTestProps$PlainMap(Map? backingMap) - : this._props = {}, - super._() { - this._props = backingMap ?? {}; - } - - /// The backing props map proxied by this class. - @override - Map get props => _props; - Map _props; -} - -// Concrete props implementation that can only be backed by [JsMap], -// allowing dart2js to compile more optimal code for key-value pair reads/writes. -class _$$ComponentTestProps$JsMap extends _$$ComponentTestProps { - // This initializer of `_props` to an empty map, as well as the reassignment - // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 - _$$ComponentTestProps$JsMap(JsBackedMap? backingMap) - : this._props = JsBackedMap(), - super._() { - this._props = backingMap ?? JsBackedMap(); - } - - /// The backing props map proxied by this class. - @override - JsBackedMap get props => _props; - JsBackedMap _props; -} - -// Concrete component implementation mixin. -// -// Implements typed props/state factories, defaults `consumedPropKeys` to the keys -// generated for the associated props class. -class _$ComponentTestComponent extends ComponentTestComponent { - late _$$ComponentTestProps$JsMap _cachedTypedProps; - - @override - _$$ComponentTestProps$JsMap get props => _cachedTypedProps; - - @override - set props(Map value) { - assert( - getBackingMap(value) is JsBackedMap, - 'Component2.props should never be set directly in ' - 'production. If this is required for testing, the ' - 'component should be rendered within the test. If ' - 'that does not have the necessary result, the last ' - 'resort is to use typedPropsFactoryJs.'); - super.props = value; - _cachedTypedProps = - typedPropsFactoryJs(getBackingMap(value) as JsBackedMap); - } - - @override - _$$ComponentTestProps$JsMap typedPropsFactoryJs(JsBackedMap? backingMap) => - _$$ComponentTestProps$JsMap(backingMap); - - @override - _$$ComponentTestProps typedPropsFactory(Map? backingMap) => - _$$ComponentTestProps(backingMap); - - /// Let `UiComponent` internals know that this class has been generated. - @override - bool get $isClassGenerated => true; - - @override - String get displayName => 'ComponentTest'; - - /// The default consumed props, taken from _$ComponentTestProps. - /// Used in `*ConsumedProps` methods if [consumedProps] is not overridden. - @override - final List $defaultConsumedProps = const [ - _$metaForComponentTestProps - ]; -} diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart deleted file mode 100644 index 4cf189953..000000000 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2023 Workiva Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:over_react/over_react.dart'; -import 'package:react_testing_library/react_testing_library.dart' as rtl; -import 'package:test/test.dart'; - -part 'null_safety_validate_required_props_test.over_react.g.dart'; - -void main() { - group('validates required props:', () { - group('non-nullable required prop', () { - group('throws when a prop is required and not set', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains( - 'Required prop `requiredNonNullable` is missing.')))); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - - expect(() { - view.rerender((ComponentTest() - ..requiredNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains( - 'Required prop `requiredNonNullable` is missing.')))); - }); - }); - - test('does not throw when the prop is set', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - }); - }); - - group('nullable required prop', () { - group('throws when a prop is required and not set', () { - test('on mount', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains('Required prop `requiredNullable` is missing.')))); - }); - - test('on re-render', () { - late rtl.RenderResult view; - - expect(() { - view = rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - - expect(() { - view.rerender((ComponentTest() - ..requiredNonNullable = true - )()); - }, - throwsA(isA().having( - (e) => e.toString(), - 'toString value', - contains('Required prop `requiredNullable` is missing.')))); - }); - }); - - test('does not throw when the prop is set to null', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = null - )()); - }, returnsNormally); - }); - - test('does not throw when the prop is set', () { - expect(() { - rtl.render((ComponentTest() - ..requiredNonNullable = true - ..requiredNullable = true - )()); - }, returnsNormally); - }); - }); - - // Do not test the validation escape hatches because this boilerplate version has existing error throwing for required props. - }, tags: 'ddc'); -} - -@Factory() -// ignore: undefined_identifier, invalid_assignment -UiFactory ComponentTest = _$ComponentTest; - -@Props() -class _$ComponentTestProps extends UiProps { - late bool requiredNonNullable; - - late bool? requiredNullable; - - bool? nullable; -} - -@Component() -class ComponentTestComponent extends UiComponent { - @override - render() => Dom.div()(); -} - diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart deleted file mode 100644 index dfd3c910a..000000000 --- a/test/over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.over_react.g.dart +++ /dev/null @@ -1,151 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ignore_for_file: deprecated_member_use_from_same_package, unnecessary_null_in_if_null_operators, prefer_null_aware_operators -part of 'null_safety_validate_required_props_test.dart'; - -// ************************************************************************** -// OverReactBuilder (package:over_react/src/builder.dart) -// ************************************************************************** - -// React component factory implementation. -// -// Registers component implementation and links type meta to builder factory. -final $ComponentTestComponentFactory = registerComponent( - () => _$ComponentTestComponent(), - builderFactory: _$ComponentTest, - componentClass: ComponentTestComponent, - isWrapper: false, - parentType: null, -); - -abstract class _$ComponentTestPropsAccessorsMixin - implements _$ComponentTestProps { - @override - Map get props; - - /// - @override - bool get requiredNonNullable => - (props[_$key__requiredNonNullable___$ComponentTestProps] ?? null) as bool; - - /// - @override - set requiredNonNullable(bool value) => - props[_$key__requiredNonNullable___$ComponentTestProps] = value; - - /// - @override - bool? get requiredNullable => - (props[_$key__requiredNullable___$ComponentTestProps] ?? null) as bool?; - - /// - @override - set requiredNullable(bool? value) => - props[_$key__requiredNullable___$ComponentTestProps] = value; - - /// - @override - bool? get nullable => - (props[_$key__nullable___$ComponentTestProps] ?? null) as bool?; - - /// - @override - set nullable(bool? value) => - props[_$key__nullable___$ComponentTestProps] = value; - /* GENERATED CONSTANTS */ - static const PropDescriptor - _$prop__requiredNonNullable___$ComponentTestProps = PropDescriptor( - _$key__requiredNonNullable___$ComponentTestProps, - isRequired: true, - isNullable: true); - static const PropDescriptor _$prop__requiredNullable___$ComponentTestProps = - PropDescriptor(_$key__requiredNullable___$ComponentTestProps, - isRequired: true, isNullable: true); - static const PropDescriptor _$prop__nullable___$ComponentTestProps = - PropDescriptor(_$key__nullable___$ComponentTestProps); - static const String _$key__requiredNonNullable___$ComponentTestProps = - 'ComponentTestProps.requiredNonNullable'; - static const String _$key__requiredNullable___$ComponentTestProps = - 'ComponentTestProps.requiredNullable'; - static const String _$key__nullable___$ComponentTestProps = - 'ComponentTestProps.nullable'; - - static const List $props = [ - _$prop__requiredNonNullable___$ComponentTestProps, - _$prop__requiredNullable___$ComponentTestProps, - _$prop__nullable___$ComponentTestProps - ]; - static const List $propKeys = [ - _$key__requiredNonNullable___$ComponentTestProps, - _$key__requiredNullable___$ComponentTestProps, - _$key__nullable___$ComponentTestProps - ]; -} - -const PropsMeta _$metaForComponentTestProps = PropsMeta( - fields: _$ComponentTestPropsAccessorsMixin.$props, - keys: _$ComponentTestPropsAccessorsMixin.$propKeys, -); - -class ComponentTestProps extends _$ComponentTestProps - with _$ComponentTestPropsAccessorsMixin { - static const PropsMeta meta = _$metaForComponentTestProps; -} - -_$$ComponentTestProps _$ComponentTest([Map? backingProps]) => - _$$ComponentTestProps(backingProps); - -// Concrete props implementation. -// -// Implements constructor and backing map, and links up to generated component factory. -class _$$ComponentTestProps extends _$ComponentTestProps - with _$ComponentTestPropsAccessorsMixin - implements ComponentTestProps { - // This initializer of `_props` to an empty map, as well as the reassignment - // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 - _$$ComponentTestProps(Map? backingMap) : this._props = {} { - this._props = backingMap ?? {}; - } - - /// The backing props map proxied by this class. - @override - Map get props => _props; - Map _props; - - /// Let `UiProps` internals know that this class has been generated. - @override - bool get $isClassGenerated => true; - - /// The `ReactComponentFactory` associated with the component built by this class. - @override - ReactComponentFactoryProxy get componentFactory => - super.componentFactory ?? $ComponentTestComponentFactory; - - /// The default namespace for the prop getters/setters generated for this class. - @override - String get propKeyNamespace => 'ComponentTestProps.'; -} - -// Concrete component implementation mixin. -// -// Implements typed props/state factories, defaults `consumedPropKeys` to the keys -// generated for the associated props class. -class _$ComponentTestComponent extends ComponentTestComponent { - @override - _$$ComponentTestProps typedPropsFactory(Map? backingMap) => - _$$ComponentTestProps(backingMap); - - /// Let `UiComponent` internals know that this class has been generated. - @override - bool get $isClassGenerated => true; - - @override - String get displayName => 'ComponentTest'; - - /// The default consumed props, taken from _$ComponentTestProps. - /// Used in `*ConsumedProps` methods if [consumedProps] is not overridden. - @override - final List $defaultConsumedProps = const [ - _$metaForComponentTestProps - ]; -} diff --git a/test/over_react/util/component_debug_name_test.over_react.g.dart b/test/over_react/util/component_debug_name_test.over_react.g.dart index 6c5c27320..693b724c2 100644 --- a/test/over_react/util/component_debug_name_test.over_react.g.dart +++ b/test/over_react/util/component_debug_name_test.over_react.g.dart @@ -175,12 +175,6 @@ abstract class _$TestComponentPropsAccessorsMixin static const List $props = []; static const List $propKeys = []; - - @override - @mustCallSuper - void validateRequiredProps() { - super.validateRequiredProps(); - } } const PropsMeta _$metaForTestComponentProps = PropsMeta( diff --git a/test/over_react_component_declaration_test.dart b/test/over_react_component_declaration_test.dart index 972e18751..46fee88a9 100644 --- a/test/over_react_component_declaration_test.dart +++ b/test/over_react_component_declaration_test.dart @@ -33,7 +33,6 @@ import 'over_react/component_declaration/builder_integration_tests/abstract_acce import 'over_react/component_declaration/builder_integration_tests/accessor_mixin_integration_test.dart' as accessor_mixin_integration_test; import 'over_react/component_declaration/builder_integration_tests/component_integration_test.dart' as component_integration_test; import 'over_react/component_declaration/builder_integration_tests/constant_required_accessor_integration_test.dart' as constant_required_accessor_integration_test; -import 'over_react/component_declaration/builder_integration_tests/null_safety_validate_required_props_test.dart' as null_safety_validate_required_props_test; import 'over_react/component_declaration/builder_integration_tests/do_not_generate_accessor_integration_test.dart' as do_not_generate_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/namespaced_accessor_integration_test.dart' as namespaced_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/private_props_ddc_bug.dart' as private_props_ddc_bug; @@ -44,7 +43,6 @@ import 'over_react/component_declaration/builder_integration_tests/backwards_com import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/accessor_mixin_integration_test.dart' as backwards_compat_accessor_mixin_integration_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/component_integration_test.dart' as backwards_compat_component_integration_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/constant_required_accessor_integration_test.dart' as backwards_compat_constant_required_accessor_integration_test; -import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safety_validate_required_props_test.dart' as backwards_compat_null_safety_validate_required_props_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/do_not_generate_accessor_integration_test.dart' as backwards_compat_do_not_generate_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/namespaced_accessor_integration_test.dart' as backwards_compat_namespaced_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/backwards_compatible/private_props_ddc_bug.dart' as backwards_compat_private_props_ddc_bug; @@ -56,7 +54,6 @@ import 'over_react/component_declaration/builder_integration_tests/component2/ac import 'over_react/component_declaration/builder_integration_tests/component2/annotation_error_integration_test.dart' as annotation_error_integration_test; import 'over_react/component_declaration/builder_integration_tests/component2/component_integration_test.dart' as component2_component_integration_test; import 'over_react/component_declaration/builder_integration_tests/component2/constant_required_accessor_integration_test.dart' as component2_constant_required_accessor_integration_test; -import 'over_react/component_declaration/builder_integration_tests/component2/null_safety_validate_required_props_test.dart' as component2_null_safety_validate_required_props_test; import 'over_react/component_declaration/builder_integration_tests/component2/do_not_generate_accessor_integration_test.dart' as component2_do_not_generate_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/component2/namespaced_accessor_integration_test.dart' as component2_namespaced_accessor_integration_test; import 'over_react/component_declaration/builder_integration_tests/component2/private_props_ddc_bug.dart' as component2_private_props_ddc_bug; @@ -142,8 +139,5 @@ main() { new_boilerplate_unassigned_prop_integration_test.main(); // FIXME: Move these tests to only be run opted in to null safety. - null_safety_validate_required_props_test.main(); - backwards_compat_null_safety_validate_required_props_test.main(); - component2_null_safety_validate_required_props_test.main(); new_boilerplate_null_safety_validate_required_props_test.main(); } From e413815f574fd53c03200c34889e18601fdc2f26 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Mon, 11 Dec 2023 15:19:20 -0700 Subject: [PATCH 21/24] Update golds --- .../basic.over_react.g.dart.goldFile | 6 ++++ .../basic_library.over_react.g.dart.goldFile | 18 +++++++++++ .../basic_two_nine.over_react.g.dart.goldFile | 6 ++++ .../props_mixin.over_react.g.dart.goldFile | 12 ++++++++ ...tiple_factories.over_react.g.dart.goldfile | 8 ++++- ...type_parameters.over_react.g.dart.goldFile | 30 +++++++++++++++++++ 6 files changed, 79 insertions(+), 1 deletion(-) diff --git a/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile index e27f18e3d..4389680b1 100644 --- a/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile @@ -225,6 +225,12 @@ mixin $BasicProps on BasicProps { _$key__basic4__BasicProps, _$key__basic5__BasicProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile index aaa148e5e..c054cf510 100644 --- a/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile @@ -338,6 +338,12 @@ mixin $BasicPartOfLibPropsMixin on BasicPartOfLibPropsMixin { _$key__basic4__BasicPartOfLibPropsMixin, _$key__basic5__BasicPartOfLibPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -560,6 +566,12 @@ mixin $SuperPartOfLibPropsMixin on SuperPartOfLibPropsMixin { static const List $propKeys = [ _$key__superProp__SuperPartOfLibPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -593,6 +605,12 @@ mixin $SubPartOfLibPropsMixin on SubPartOfLibPropsMixin { static const List $propKeys = [ _$key__subProp__SubPartOfLibPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile index d40664a09..baf2699e0 100644 --- a/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile @@ -225,6 +225,12 @@ mixin $BasicProps on BasicProps { _$key__basic4__BasicProps, _$key__basic5__BasicProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test_fixtures/gold_output_files/mixin_based/props_mixin.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/props_mixin.over_react.g.dart.goldFile index 6e579b5dd..79bc04ed2 100644 --- a/test_fixtures/gold_output_files/mixin_based/props_mixin.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/props_mixin.over_react.g.dart.goldFile @@ -30,6 +30,12 @@ mixin $ExamplePropsMixin on ExamplePropsMixin { _$prop__propMixin1__ExamplePropsMixin ]; static const List $propKeys = [_$key__propMixin1__ExamplePropsMixin]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -66,6 +72,12 @@ mixin $RequiresOtherMixinPropsMixin static const List $propKeys = [ _$key__otherPropMixin__RequiresOtherMixinPropsMixin ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test_fixtures/gold_output_files/mixin_based/two_nine_with_multiple_factories.over_react.g.dart.goldfile b/test_fixtures/gold_output_files/mixin_based/two_nine_with_multiple_factories.over_react.g.dart.goldfile index 3ca3afaab..3510f1f04 100644 --- a/test_fixtures/gold_output_files/mixin_based/two_nine_with_multiple_factories.over_react.g.dart.goldfile +++ b/test_fixtures/gold_output_files/mixin_based/two_nine_with_multiple_factories.over_react.g.dart.goldfile @@ -148,7 +148,7 @@ class _$CounterComponent extends CounterComponent { bool get $isClassGenerated => true; @override - String get displayName => 'Counter'; + String get displayName => '_Counter'; /// The default consumed props, comprising all props mixins used by CounterProps. /// Used in `*ConsumedProps` methods if [consumedProps] is not overridden. @@ -174,6 +174,12 @@ mixin $CounterPropsMixin on CounterPropsMixin { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' diff --git a/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile index 3d570d1bb..1e60851a6 100644 --- a/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile @@ -18,6 +18,12 @@ mixin $NoneProps on NoneProps { static const List $props = []; static const List $propKeys = []; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -44,6 +50,12 @@ mixin $SingleProps on SingleProps { static const List $props = [_$prop__single__SingleProps]; static const List $propKeys = [_$key__single__SingleProps]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -82,6 +94,12 @@ mixin $SingleThatWontBeSpecifiedProps on SingleThatWontBeSpecifiedProps { static const List $propKeys = [ _$key__singleThatWontBeSpecified__SingleThatWontBeSpecifiedProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -115,6 +133,12 @@ mixin $SingleWithBoundProps on SingleWithBoundProps { static const List $propKeys = [ _$key__singleWithBound__SingleWithBoundProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' @@ -154,6 +178,12 @@ mixin $DoubleProps on DoubleProps { _$key__doubleA__DoubleProps, _$key__doubleB__DoubleProps ]; + + @override + @mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } } @Deprecated('This API is for use only within generated code.' From c1c1cf2043ed854b6bf41b289fd81294d568cea4 Mon Sep 17 00:00:00 2001 From: sydneyjodon-wk <51122966+sydneyjodon-wk@users.noreply.github.com> Date: Fri, 15 Dec 2023 12:09:45 -0700 Subject: [PATCH 22/24] Update lib/src/builder/codegen/accessors_generator.dart Co-authored-by: Greg Littlefield --- lib/src/builder/codegen/accessors_generator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index e63104160..dd477e526 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -206,7 +206,7 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato if (type.isProps && disableRequiredPropValidation == null) { requiredPropChecks.add(' if(!props.containsKey($keyValue)) {\n' - ' throw MissingRequiredPropsError(\'Required prop `$accessorName` is missing.\');\n' + ' throw MissingRequiredPropsError(${stringLiteral('Required prop `$accessorName` is missing.')});\n' '}\n'); } } From 8b9bbaa7399283aa0adde72054278bf84e9c821d Mon Sep 17 00:00:00 2001 From: sydneyjodon-wk <51122966+sydneyjodon-wk@users.noreply.github.com> Date: Fri, 15 Dec 2023 12:10:08 -0700 Subject: [PATCH 23/24] Update lib/src/component_declaration/component_base.dart Co-authored-by: Greg Littlefield --- lib/src/component_declaration/component_base.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/component_declaration/component_base.dart b/lib/src/component_declaration/component_base.dart index e7dc7978d..ca795ed7c 100644 --- a/lib/src/component_declaration/component_base.dart +++ b/lib/src/component_declaration/component_base.dart @@ -621,12 +621,12 @@ abstract class UiProps extends MapBase assert(_validateChildren(childArguments.length == 1 ? childArguments.single : childArguments)); - assert(() { - if(_shouldValidateRequiredProps) { - validateRequiredProps(); - } - return true; - }()); + assert(() { + if (_shouldValidateRequiredProps) { + validateRequiredProps(); + } + return true; + }()); // Use `build` instead of using emulated function behavior to work around DDC issue // https://github.com/dart-lang/sdk/issues/29904 From 1f0a030c82531ab886cd4f440d95a3bceb34c0d2 Mon Sep 17 00:00:00 2001 From: Sydney Jodon Date: Fri, 15 Dec 2023 14:48:07 -0700 Subject: [PATCH 24/24] Address feedback --- .../component_declaration/annotations.dart | 1 + .../over_react_redux/over_react_redux.dart | 1 + .../required_accessor_integration_test.dart | 42 +++++++++++++++++- .../required_accessor_integration_test.dart | 44 ++++++++++++++++++- ...ccessor_integration_test.over_react.g.dart | 21 ++++++++- ...l_safety_validate_required_props_test.dart | 44 ++++++++++++++++--- .../required_accessor_integration_test.dart | 42 +++++++++++++++++- ...ccessor_integration_test.over_react.g.dart | 21 ++++++++- ...over_react_component_declaration_test.dart | 2 - 9 files changed, 201 insertions(+), 17 deletions(-) diff --git a/lib/src/component_declaration/annotations.dart b/lib/src/component_declaration/annotations.dart index 200f459ae..0bf139f90 100644 --- a/lib/src/component_declaration/annotations.dart +++ b/lib/src/component_declaration/annotations.dart @@ -15,6 +15,7 @@ // Dummy annotations that would be used by Pub code generator library over_react.component_declaration.annotations; +// Exported for use in generated code. export 'package:meta/meta.dart' show mustCallSuper; /// Annotation used with the `over_react` builder to declare a `UiFactory` for a component. diff --git a/lib/src/over_react_redux/over_react_redux.dart b/lib/src/over_react_redux/over_react_redux.dart index e5541a79d..a37dc79a0 100644 --- a/lib/src/over_react_redux/over_react_redux.dart +++ b/lib/src/over_react_redux/over_react_redux.dart @@ -52,6 +52,7 @@ abstract class _$ConnectPropsMixin implements UiProps { @override Map get props; + // Disable validation since this prop is set by the `connect` HOC, and does not need to be set by consumers. @disableRequiredPropValidation late dynamic Function(dynamic action) dispatch; } diff --git a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/required_accessor_integration_test.dart b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/required_accessor_integration_test.dart index b1174c015..5ab2dbe99 100644 --- a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/required_accessor_integration_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/required_accessor_integration_test.dart @@ -29,7 +29,10 @@ void main() { group('(backwards compatible with Dart 1) properly identifies required props by', () { group('throwing when a prop is required and not set', () { test('on mount', () { - expect(() => render(ComponentTest()..nullable = true), + expect(() => render(ComponentTest() + ..nullable = true + ..lateProp = true + )(), throwsPropError_Required('ComponentTestProps.required', 'This Prop is Required for testing purposes.') ); }); @@ -39,6 +42,7 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect(() => react_dom.render((ComponentTest()..nullable = true)(), mountNode), @@ -52,6 +56,7 @@ void main() { expect(() => render(ComponentTest() ..required = null ..nullable = true + ..lateProp = true ), throwsPropError_Required('ComponentTestProps.required')); }); @@ -60,12 +65,14 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect( () => react_dom.render((ComponentTest() ..required = null ..nullable = true + ..lateProp = true )(), mountNode), throwsPropError_Required('ComponentTestProps.required', 'This Prop is Required for testing purposes.') ); @@ -74,7 +81,10 @@ void main() { group('throwing when a prop is nullable and not set', () { test('on mount', () { - expect(() => render(ComponentTest()..required = true), + expect(() => render(ComponentTest() + ..required = true + ..lateProp = true + )(), throwsPropError_Required('ComponentTestProps.nullable')); }); @@ -83,6 +93,7 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect(() => react_dom.render((ComponentTest()..required = true)(), mountNode), @@ -96,6 +107,7 @@ void main() { expect(() => render(ComponentTest() ..nullable = true ..required = true + ..lateProp = true ), returnsNormally); }); @@ -104,11 +116,13 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect(() => react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode), returnsNormally); }); }); @@ -118,6 +132,7 @@ void main() { expect(() => render(ComponentTest() ..nullable = null ..required = true + ..lateProp = true ), returnsNormally); }); @@ -126,14 +141,35 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect(() => react_dom.render((ComponentTest() ..required = true ..nullable = null + ..lateProp = true )(), mountNode), returnsNormally); }); }); + + group('for late props', () { + test('does not throw on invocation', () { + expect(() { + (ComponentTest() + ..required = true + ..nullable = true + )(); + }, + returnsNormally); + }); + + test('on mount', () { + expect(() => render(ComponentTest() + ..nullable = null + ..required = true + ), throwsPropError_Required('ComponentTestProps.lateProp')); + }); + }); }); } @@ -148,6 +184,8 @@ class _$ComponentTestProps extends UiProps { @Accessor(isRequired: true, isNullable: true, requiredErrorMessage: 'This prop can be set to null!') dynamic nullable; + + late bool lateProp; } @Component() diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/required_accessor_integration_test.dart b/test/over_react/component_declaration/builder_integration_tests/component2/required_accessor_integration_test.dart index 6b98d57f8..6d6eb8b0b 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/required_accessor_integration_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/required_accessor_integration_test.dart @@ -26,7 +26,11 @@ void main() { test('on mount', () { expect(() { mount( - (ComponentTest()..nullable = true..requiredAndLengthLimited = [1,2])(), + (ComponentTest() + ..nullable = true + ..requiredAndLengthLimited = [1,2] + ..lateProp = true + )(), attachedToDocument: true, ); }, logsPropRequiredError('ComponentTestProps.required', 'This Prop is Required for testing purposes.')); @@ -41,6 +45,7 @@ void main() { ..required = true ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )(), attachedToDocument: true ); @@ -50,6 +55,7 @@ void main() { jacket.rerender((ComponentTest() ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )() ); }, logsPropRequiredError('ComponentTestProps.required', 'This Prop is Required for testing purposes.')); @@ -64,6 +70,7 @@ void main() { ..required = null ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )()); }, logsPropRequiredError('ComponentTestProps.required', 'This Prop is Required for testing purposes.')); }); @@ -77,6 +84,7 @@ void main() { ..required = true ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )(), attachedToDocument: true); }, logsNoPropTypeWarnings); @@ -86,6 +94,7 @@ void main() { ..required = null ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )()); }, logsPropRequiredError('ComponentTestProps.required', 'This Prop is Required for testing purposes.')); }); @@ -97,6 +106,7 @@ void main() { render((ComponentTest() ..required = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )()); }, logsPropRequiredError('ComponentTestProps.nullable', 'This prop can be set to null!')); }); @@ -110,6 +120,7 @@ void main() { ..required = true ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )(), attachedToDocument: true); }, logsNoPropTypeWarnings); @@ -118,6 +129,7 @@ void main() { jacket.rerender((ComponentTest() ..required = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )()); }, logsPropRequiredError('ComponentTestProps.nullable', 'This prop can be set to null!')); }); @@ -130,6 +142,7 @@ void main() { ..nullable = true ..required = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )()); }, logsNoPropTypeWarnings); }); @@ -139,6 +152,7 @@ void main() { ..required = true ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )(), attachedToDocument: true, ); @@ -148,6 +162,7 @@ void main() { ..required = true ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )()); }, logsNoPropTypeWarnings); }); @@ -160,6 +175,7 @@ void main() { ..nullable = null ..requiredAndLengthLimited = [1,2] ..required = true + ..lateProp = true )()); }, logsNoPropTypeWarnings); }); @@ -173,6 +189,7 @@ void main() { ..required = true ..nullable = true ..requiredAndLengthLimited = [1,2] + ..lateProp = true )(), attachedToDocument: true); }, logsNoPropTypeWarnings); @@ -182,6 +199,7 @@ void main() { ..required = true ..nullable = null ..requiredAndLengthLimited = [1,2] + ..lateProp = true )()); }, logsNoPropTypeWarnings); }); @@ -193,6 +211,7 @@ void main() { mount((ComponentTest() ..nullable = null ..required = true + ..lateProp = true )()); }, logsPropValueError('null', 'ComponentTestProps.requiredAndLengthLimited')); }); @@ -203,10 +222,32 @@ void main() { ..required = true ..nullable = true ..requiredAndLengthLimited = [1] + ..lateProp = true )()); }, logsPropValueError('1', 'ComponentTestProps.requiredAndLengthLimited')); }); }); + + group('for late props', () { + test('does not throw on invocation', () { + expect(() { + (ComponentTest() + ..required = true + ..nullable = true + ..requiredAndLengthLimited = [1,2] + )(); + }, + returnsNormally); + }); + + test('on mount', () { + expect(() => render(ComponentTest() + ..nullable = null + ..required = true + ..requiredAndLengthLimited = [1,2] + ), logsPropRequiredError('ComponentTestProps.lateProp')); + }); + }); }); } @@ -225,6 +266,7 @@ class _$ComponentTestProps extends UiProps { @Accessor(isRequired: true, isNullable: false, requiredErrorMessage: 'This Prop Array is Required for testing purposes.') List? requiredAndLengthLimited; + late bool lateProp; } @Component2() diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/required_accessor_integration_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/component2/required_accessor_integration_test.over_react.g.dart index 70e4d2f18..4cd5cab76 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/required_accessor_integration_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/required_accessor_integration_test.over_react.g.dart @@ -75,6 +75,16 @@ abstract class _$ComponentTestPropsAccessorsMixin requiredErrorMessage: 'This Prop Array is Required for testing purposes.') set requiredAndLengthLimited(List? value) => props[_$key__requiredAndLengthLimited___$ComponentTestProps] = value; + + /// + @override + bool get lateProp => + (props[_$key__lateProp___$ComponentTestProps] ?? null) as bool; + + /// + @override + set lateProp(bool value) => + props[_$key__lateProp___$ComponentTestProps] = value; /* GENERATED CONSTANTS */ static const PropDescriptor _$prop__required___$ComponentTestProps = PropDescriptor(_$key__required___$ComponentTestProps, @@ -90,22 +100,29 @@ abstract class _$ComponentTestPropsAccessorsMixin _$key__requiredAndLengthLimited___$ComponentTestProps, isRequired: true, errorMessage: 'This Prop Array is Required for testing purposes.'); + static const PropDescriptor _$prop__lateProp___$ComponentTestProps = + PropDescriptor(_$key__lateProp___$ComponentTestProps, + isRequired: true, isNullable: true); static const String _$key__required___$ComponentTestProps = 'ComponentTestProps.required'; static const String _$key__nullable___$ComponentTestProps = 'ComponentTestProps.nullable'; static const String _$key__requiredAndLengthLimited___$ComponentTestProps = 'ComponentTestProps.requiredAndLengthLimited'; + static const String _$key__lateProp___$ComponentTestProps = + 'ComponentTestProps.lateProp'; static const List $props = [ _$prop__required___$ComponentTestProps, _$prop__nullable___$ComponentTestProps, - _$prop__requiredAndLengthLimited___$ComponentTestProps + _$prop__requiredAndLengthLimited___$ComponentTestProps, + _$prop__lateProp___$ComponentTestProps ]; static const List $propKeys = [ _$key__required___$ComponentTestProps, _$key__nullable___$ComponentTestProps, - _$key__requiredAndLengthLimited___$ComponentTestProps + _$key__requiredAndLengthLimited___$ComponentTestProps, + _$key__lateProp___$ComponentTestProps ]; } diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart index e90436bb4..178db25b5 100644 --- a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safety_validate_required_props_test.dart @@ -24,6 +24,19 @@ void main() { group('(New boilerplate) validates required props:', () { group('non-nullable required prop', () { group('throws when a prop is required and not set', () { + test('on invocation', () { + expect(() { + (ComponentTest() + ..requiredNullable = true + )(); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains( + 'Required prop `requiredNonNullable` is missing.')))); + }); + test('on mount', () { expect(() { rtl.render((ComponentTest() @@ -72,6 +85,18 @@ void main() { group('nullable required prop', () { group('throws when a prop is required and not set', () { + test('on invocation', () { + expect(() { + (ComponentTest() + ..requiredNonNullable = true + )(); + }, + throwsA(isA().having( + (e) => e.toString(), + 'toString value', + contains('Required prop `requiredNullable` is missing.')))); + }); + test('on mount', () { expect(() { rtl.render((ComponentTest() @@ -155,10 +180,10 @@ void main() { group('required props in multiple mixins', () { test('throw an error when a prop in the first mixin is missing', () { expect(() { - rtl.render((MultipleMixinsTest() + (MultipleMixinsTest() ..requiredNullable = true ..secondRequiredProp = true - )()); + )(); }, throwsA(isA().having( (e) => e.toString(), @@ -169,10 +194,10 @@ void main() { test('throw an error when a prop in the second mixin is missing', () { expect(() { - rtl.render((MultipleMixinsTest() + (MultipleMixinsTest() ..requiredNullable = true ..requiredNonNullable = true - )()); + )(); }, throwsA(isA().having( (e) => e.toString(), @@ -183,16 +208,23 @@ void main() { test('does not throw when all required props are set', () { expect(() { - rtl.render((MultipleMixinsTest() + (MultipleMixinsTest() ..requiredNullable = true ..requiredNonNullable = true ..secondRequiredProp = true - )()); + )(); }, returnsNormally); }); }); }, tags: 'ddc'); + + test('(New boilerplate) validates required props: does not throw in dart2js', () { + expect(() { + rtl.render((ComponentTest())()); + }, + returnsNormally); + }, tags: 'no-ddc'); } // ignore: undefined_identifier, invalid_assignment diff --git a/test/over_react/component_declaration/builder_integration_tests/required_accessor_integration_test.dart b/test/over_react/component_declaration/builder_integration_tests/required_accessor_integration_test.dart index 604042443..7be292874 100644 --- a/test/over_react/component_declaration/builder_integration_tests/required_accessor_integration_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/required_accessor_integration_test.dart @@ -29,7 +29,10 @@ void main() { group('properly identifies required props by', () { group('throwing when a prop is required and not set', () { test('on mount', () { - expect(() => render(ComponentTest()..nullable = true), + expect(() => render(ComponentTest() + ..nullable = true + ..lateProp = true + )(), throwsPropError_Required('ComponentTestProps.required', 'This Prop is Required for testing purposes.') ); }); @@ -39,6 +42,7 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect(() => react_dom.render((ComponentTest()..nullable = true)(), mountNode), @@ -52,6 +56,7 @@ void main() { expect(() => render(ComponentTest() ..required = null ..nullable = true + ..lateProp = true ), throwsPropError_Required('ComponentTestProps.required')); }); @@ -60,12 +65,14 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect( () => react_dom.render((ComponentTest() ..required = null ..nullable = true + ..lateProp = true )(), mountNode), throwsPropError_Required('ComponentTestProps.required', 'This Prop is Required for testing purposes.') ); @@ -74,7 +81,10 @@ void main() { group('throwing when a prop is nullable and not set', () { test('on mount', () { - expect(() => render(ComponentTest()..required = true), + expect(() => render(ComponentTest() + ..required = true + ..lateProp = true + )(), throwsPropError_Required('ComponentTestProps.nullable')); }); @@ -83,6 +93,7 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect(() => react_dom.render((ComponentTest()..required = true)(), mountNode), @@ -96,6 +107,7 @@ void main() { expect(() => render(ComponentTest() ..nullable = true ..required = true + ..lateProp = true ), returnsNormally); }); @@ -104,11 +116,13 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect(() => react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode), returnsNormally); }); }); @@ -118,6 +132,7 @@ void main() { expect(() => render(ComponentTest() ..nullable = null ..required = true + ..lateProp = true ), returnsNormally); }); @@ -126,14 +141,35 @@ void main() { react_dom.render((ComponentTest() ..required = true ..nullable = true + ..lateProp = true )(), mountNode); expect(() => react_dom.render((ComponentTest() ..required = true ..nullable = null + ..lateProp = true )(), mountNode), returnsNormally); }); }); + + group('for late props', () { + test('does not throw on invocation', () { + expect(() { + (ComponentTest() + ..required = true + ..nullable = true + )(); + }, + returnsNormally); + }); + + test('on mount', () { + expect(() => render(ComponentTest() + ..nullable = null + ..required = true + ), throwsPropError_Required('ComponentTestProps.lateProp')); + }); + }); }); } @@ -148,6 +184,8 @@ class _$ComponentTestProps extends UiProps { @Accessor(isRequired: true, isNullable: true, requiredErrorMessage: 'This prop can be set to null!') dynamic nullable; + + late bool lateProp; } @Component() diff --git a/test/over_react/component_declaration/builder_integration_tests/required_accessor_integration_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/required_accessor_integration_test.over_react.g.dart index 122354f87..4db52d13b 100644 --- a/test/over_react/component_declaration/builder_integration_tests/required_accessor_integration_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/required_accessor_integration_test.over_react.g.dart @@ -56,6 +56,16 @@ abstract class _$ComponentTestPropsAccessorsMixin requiredErrorMessage: 'This prop can be set to null!') set nullable(dynamic value) => props[_$key__nullable___$ComponentTestProps] = value; + + /// + @override + bool get lateProp => + (props[_$key__lateProp___$ComponentTestProps] ?? null) as bool; + + /// + @override + set lateProp(bool value) => + props[_$key__lateProp___$ComponentTestProps] = value; /* GENERATED CONSTANTS */ static const PropDescriptor _$prop__required___$ComponentTestProps = PropDescriptor(_$key__required___$ComponentTestProps, @@ -66,18 +76,25 @@ abstract class _$ComponentTestPropsAccessorsMixin isRequired: true, isNullable: true, errorMessage: 'This prop can be set to null!'); + static const PropDescriptor _$prop__lateProp___$ComponentTestProps = + PropDescriptor(_$key__lateProp___$ComponentTestProps, + isRequired: true, isNullable: true); static const String _$key__required___$ComponentTestProps = 'ComponentTestProps.required'; static const String _$key__nullable___$ComponentTestProps = 'ComponentTestProps.nullable'; + static const String _$key__lateProp___$ComponentTestProps = + 'ComponentTestProps.lateProp'; static const List $props = [ _$prop__required___$ComponentTestProps, - _$prop__nullable___$ComponentTestProps + _$prop__nullable___$ComponentTestProps, + _$prop__lateProp___$ComponentTestProps ]; static const List $propKeys = [ _$key__required___$ComponentTestProps, - _$key__nullable___$ComponentTestProps + _$key__nullable___$ComponentTestProps, + _$key__lateProp___$ComponentTestProps ]; } diff --git a/test/over_react_component_declaration_test.dart b/test/over_react_component_declaration_test.dart index 46fee88a9..60860dcb9 100644 --- a/test/over_react_component_declaration_test.dart +++ b/test/over_react_component_declaration_test.dart @@ -137,7 +137,5 @@ main() { new_boilerplate_required_accessor_integration_test.main(); new_boilerplate_stateful_component_integration_test.main(); new_boilerplate_unassigned_prop_integration_test.main(); - - // FIXME: Move these tests to only be run opted in to null safety. new_boilerplate_null_safety_validate_required_props_test.main(); }