diff --git a/.github/workflows/dart_ci.yml b/.github/workflows/dart_ci.yml index 69b79307d..b0f10510a 100644 --- a/.github/workflows/dart_ci.yml +++ b/.github/workflows/dart_ci.yml @@ -17,22 +17,46 @@ permissions: pull-requests: write jobs: + # Run as a separate job outside the Dart SDK matrix below, + # since we can only emit a single SBOM. + create-sbom-release-asset: + name: Create SBOM Release Asset + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + with: + sdk: 2.19.6 # This version doesn't matter so long as it resolves. + - run: dart pub get + - name: Publish SBOM to Release Assets + uses: anchore/sbom-action@v0 + with: + path: ./ + format: cyclonedx-json + build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - # Can't run on `dev` (Dart 3) until we're fully null-safe. - sdk: [ 2.19.6 ] + sdk: [ 2.19.6, stable ] steps: - uses: actions/checkout@v4 - - uses: dart-lang/setup-dart@v1 + - id: setup-dart + uses: dart-lang/setup-dart@v1 with: sdk: ${{ matrix.sdk }} - name: Print Dart SDK version run: dart --version + - name: Delete Dart-2-only files when running on Dart 3 + run: | + DART_VERSION="${{ steps.setup-dart.outputs.dart-version }}" + if [[ "$DART_VERSION" =~ ^3 ]]; then + ./tool/delete_dart_2_only_files.sh + fi + - id: install name: Install dependencies run: dart pub get @@ -44,7 +68,7 @@ jobs: - name: Verify formatting run: dart run dart_dev format --check # Only run on one sdk version in case there are conflicts - if: always() && matrix.sdk != '2.19.6' && 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) @@ -81,42 +105,52 @@ jobs: - name: Run tests (VM) # Can't use build_runner (which dart_dev uses if you depend on it) to run VM tests, since we get the error: # Unable to spawn isolate: /…/build_runner_testRU6M77/.packages: Error: Problem in packages configuration file: Unexpected character - run: dart test -P vm + run: | + DART_VERSION="${{ steps.setup-dart.outputs.dart-version }}" + TEST_ARGS="" + if [[ "$DART_VERSION" =~ ^3 ]]; then + TEST_ARGS="--preset=no-dart-2" + fi + dart test --preset vm $TEST_ARGS if: always() && steps.install.outcome == 'success' && steps.build.outcome == 'success' - name: Run tests (DDC) run: dart test --precompiled ddc_precompiled -P dartdevc if: always() && steps.install.outcome == 'success' && steps.build.outcome == 'success' + timeout-minutes: 5 - name: Run tests (dart2js) run: dart run dart_dev test --build-args="-r" -P dart2js if: always() && steps.install.outcome == 'success' && steps.build.outcome == 'success' - - - uses: anchore/sbom-action@v0 - with: - path: ./ - format: cyclonedx-json + timeout-minutes: 5 validate_analyzer: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - # Can't run on `dev` (Dart 3) until we're fully null-safe. - sdk: [ 2.19.6 ] + sdk: [ 2.19.6, stable ] analyzer: # We only have one version currently, but we'll leave this CI step in place # for the next time we need to support multiple analyzer versions. - ^5.1.0 steps: - uses: actions/checkout@v4 - - uses: dart-lang/setup-dart@v1 + - id: setup-dart + uses: dart-lang/setup-dart@v1 with: sdk: ${{ matrix.sdk }} - name: Print Dart SDK version run: dart --version + - name: Delete Dart-2-only files when running on Dart 3 + run: | + DART_VERSION="${{ steps.setup-dart.outputs.dart-version }}" + if [[ "$DART_VERSION" =~ ^3 ]]; then + ./tool/delete_dart_2_only_files.sh + fi + - name: Update analyzer constraint to ${{ matrix.analyzer }} and validate `dart pub get` can resolve id: resolve run: | @@ -132,7 +166,13 @@ jobs: run: dart run build_runner build --build-filter='**.dart' --delete-conflicting-outputs - name: Run builder tests - run: dart test -p vm -- test/vm_tests/builder + run: | + DART_VERSION="${{ steps.setup-dart.outputs.dart-version }}" + TEST_ARGS="" + if [[ "$DART_VERSION" =~ ^3 ]]; then + TEST_ARGS="--preset=no-dart-2" + fi + dart test --preset vm $TEST_ARGS -- test/vm_tests/builder analyzer_plugin: runs-on: ubuntu-latest @@ -142,17 +182,24 @@ jobs: strategy: fail-fast: false matrix: - # Can't run on `stable` (Dart 3) until we're fully null-safe. - sdk: [ 2.19.6 ] + sdk: [ 2.19.6, stable ] steps: - uses: actions/checkout@v4 - - uses: dart-lang/setup-dart@v1 + - id: setup-dart + uses: dart-lang/setup-dart@v1 with: sdk: ${{ matrix.sdk }} - name: Print Dart SDK version run: dart --version + - name: Delete Dart-2-only files when running on Dart 3 + run: | + DART_VERSION="${{ steps.setup-dart.outputs.dart-version }}" + if [[ "$DART_VERSION" =~ ^3 ]]; then + (cd ../.. && ./tool/delete_dart_2_only_files.sh) + fi + - id: link name: Override over_react dependency with local path run: cd ../.. && dart pub get && dart tool/travis_link_plugin_deps.dart @@ -177,3 +224,4 @@ jobs: - name: Run tests run: dart run dart_dev test if: always() && steps.install.outcome == 'success' + timeout-minutes: 8 diff --git a/analysis_options.yaml b/analysis_options.yaml index 9344e1913..5b3a623f5 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -15,6 +15,9 @@ analyzer: unused_import: warning unnecessary_import: warning unnecessary_null_comparison: warning + comment_references: info + # To work around warning "'can_be_null_after_null_aware' isn't a recognized error code" in Dart 3 + included_file_warning: info # Workaround for https://github.com/dart-lang/sdk/issues/51087 # TODO remove once we're no longer running CI on Dart 2.18 diff --git a/dart_test.yaml b/dart_test.yaml index 7f3b0c835..218c9cc67 100644 --- a/dart_test.yaml +++ b/dart_test.yaml @@ -4,14 +4,18 @@ platforms: - chrome - vm -# Default concurrency of 4 for unit tests, integration preset will override -concurrency: 4 +# Work around process hanging after tests finish: https://github.com/dart-lang/build/issues/3765 +concurrency: 1 presets: vm: + concurrency: 4 paths: - test/vm_tests/ + no-dart-2: + exclude_tags: dart-2-only + dartdevc: exclude_tags: no-ddc paths: @@ -48,3 +52,5 @@ tags: # Tests that represent a case of undesirable JS interop behavior that's # either unavoidable or exists as a tradeoff for improved behavior in other cases. js-interop-tradeoff: {} + # Tests for behavior that's only valid in Dart 2, and does not apply to Dart 3. + dart-2-only: {} diff --git a/lib/src/component_declaration/disposable_manager_proxy.dart b/lib/src/component_declaration/disposable_manager_proxy.dart index 819d6130e..526456815 100644 --- a/lib/src/component_declaration/disposable_manager_proxy.dart +++ b/lib/src/component_declaration/disposable_manager_proxy.dart @@ -63,7 +63,6 @@ mixin DisposableManagerProxy on react.Component implements DisposableManagerV7 { _getDisposableProxy().listenToStream(stream, onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); - @override Disposable manageAndReturnDisposable(Disposable disposable) => _getDisposableProxy().manageAndReturnTypedDisposable(disposable); @@ -71,13 +70,11 @@ mixin DisposableManagerProxy on react.Component implements DisposableManagerV7 { Completer manageCompleter(Completer completer) => _getDisposableProxy().manageCompleter(completer); - @override void manageDisposable(Disposable disposable) => _getDisposableProxy().manageDisposable(disposable); /// DEPRECATED. Use [getManagedDisposer] instead. @Deprecated('w_common 2.0.0') - @override void manageDisposer(Disposer disposer) => _getDisposableProxy().getManagedDisposer(disposer); @@ -87,7 +84,6 @@ mixin DisposableManagerProxy on react.Component implements DisposableManagerV7 { /// DEPRECATED. Use [listenToStream] instead. @Deprecated('w_common 2.0.0') - @override void manageStreamSubscription(StreamSubscription subscription) => _getDisposableProxy().getManagedDisposer(subscription.cancel); diff --git a/lib/src/util/css_value_util.dart b/lib/src/util/css_value_util.dart index e1ff146e8..303f2f958 100644 --- a/lib/src/util/css_value_util.dart +++ b/lib/src/util/css_value_util.dart @@ -132,7 +132,7 @@ class CssValue implements Comparable { /// Returns whether this value's [number] and [unit] are equal to that of [other]. @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other is CssValue && number == other.number && unit == other.unit); } diff --git a/lib/src/util/prop_conversion.dart b/lib/src/util/prop_conversion.dart index 0c8054a57..effe74e17 100644 --- a/lib/src/util/prop_conversion.dart +++ b/lib/src/util/prop_conversion.dart @@ -1,3 +1,5 @@ +// This is only an unnecessary import in Dart 3; in Dart 2, we need it for allowInterop. +// ignore: unnecessary_import import 'package:js/js.dart'; import 'package:js/js_util.dart'; import 'package:meta/meta.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 07c4b28ed..0f3e4a77d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ version: 5.4.0 description: A library for building statically-typed React UI components using Dart. homepage: https://github.com/Workiva/over_react/ environment: - sdk: '>=2.19.0 <3.0.0' + sdk: '>=2.19.0 <4.0.0' dependencies: collection: ^1.15.0 @@ -30,15 +30,13 @@ dev_dependencies: build_resolvers: ^2.0.0 build_runner: ^2.0.0 build_test: ^2.0.0 - build_web_compilers: ^3.0.0 - built_value_generator: ^8.0.0 + build_web_compilers: '>=3.2.6 <5.0.0' dart_dev: ^4.0.1 dependency_validator: ^3.0.0 glob: ^2.0.1 io: ^1.0.0 react_testing_library: ^3.0.1 over_react_test: ^3.0.0 - pedantic: ^1.11.1 test: ^1.20.0 workiva_analysis_options: ^1.4.0 yaml: ^3.1.0 diff --git a/test/over_react/component/lazy_test.dart b/test/over_react/component/lazy_test.dart index 46954047f..21454cbd5 100644 --- a/test/over_react/component/lazy_test.dart +++ b/test/over_react/component/lazy_test.dart @@ -320,8 +320,8 @@ sharedNestedPropConversionTests(UiFactory builder, {bool isJsCompon // DDC error message matches( RegExp(r"Expected a value of type 'Map[^']*', but got one of type '(Native|Legacy)JavaScriptObject'")), - // dart2js error message - matches(RegExp(r"type '(Unknown|Plain)JavaScriptObject' is not a subtype of type 'Map[^']*'")), + // Dart 2 dart2js, Dart 3 DDC error message + matches(RegExp(r"type '(Unknown|Plain|Legacy)JavaScriptObject' is not a subtype of type 'Map[^']*'")), )), ]); if (!isJsComponent) { diff --git a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safe_accessor_integration_test.dart b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safe_accessor_integration_test.dart index 9d885d654..ef60306af 100644 --- a/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safe_accessor_integration_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/backwards_compatible/null_safe_accessor_integration_test.dart @@ -96,13 +96,6 @@ void main() { testValue: 'test value', ); }); - test('nullable dynamic with extraneous ? syntax', () { - testPropWriteAndRead( - readProp: (p) => p.nullableDynamicWithQuestion, - writeProp: (p, value) => p.nullableDynamicWithQuestion = value, - testValue: 'test value', - ); - }); test('nullable typedef, without ? syntax', () { testPropWriteAndRead( readProp: (p) => p.nullableTypedefWithoutQuestion, @@ -171,9 +164,6 @@ void main() { test('nullable dynamic', () { expectReadPropReturnsNull((props) => props.nullableDynamic); }); - test('nullable dynamic with extraneous ? syntax', () { - expectReadPropReturnsNull((props) => props.nullableDynamicWithQuestion); - }); test('nullable typedef, without ? syntax', () { expectReadPropReturnsNull((props) => props.nullableTypedefWithoutQuestion); }); @@ -290,13 +280,6 @@ void main() { testValue: 'test value', ); }); - test('nullable dynamic with extraneous ? syntax', () { - testStateWriteAndRead( - readState: (p) => p.nullableDynamicWithQuestion, - writeState: (p, value) => p.nullableDynamicWithQuestion = value, - testValue: 'test value', - ); - }); test('nullable typedef, without ? syntax', () { testStateWriteAndRead( readState: (p) => p.nullableTypedefWithoutQuestion, @@ -365,9 +348,6 @@ void main() { test('nullable dynamic', () { expectReadStateReturnsNull((state) => state.nullableDynamic); }); - test('nullable dynamic with extraneous ? syntax', () { - expectReadStateReturnsNull((state) => state.nullableDynamicWithQuestion); - }); test('nullable typedef, without ? syntax', () { expectReadStateReturnsNull((state) => state.nullableTypedefWithoutQuestion); }); @@ -396,8 +376,6 @@ class _$NullSafeTestProps extends UiProps { String? nullable; dynamic nullableDynamic; - // ignore: unnecessary_question_mark - dynamic? nullableDynamicWithQuestion; NullableTypedef nullableTypedefWithoutQuestion; } @@ -412,8 +390,6 @@ class _$NullSafeTestState extends UiState { String? nullable; dynamic nullableDynamic; - // ignore: unnecessary_question_mark - dynamic? nullableDynamicWithQuestion; NullableTypedef nullableTypedefWithoutQuestion; } diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safe_accessor_integration_test.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safe_accessor_integration_test.dart index bf78a2deb..5a0e9a13d 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safe_accessor_integration_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safe_accessor_integration_test.dart @@ -97,13 +97,6 @@ void main() { testValue: 'test value', ); }); - test('nullable dynamic with extraneous ? syntax', () { - testPropWriteAndRead( - readProp: (p) => p.nullableDynamicWithQuestion, - writeProp: (p, value) => p.nullableDynamicWithQuestion = value, - testValue: 'test value', - ); - }); test('nullable typedef, without ? syntax', () { testPropWriteAndRead( readProp: (p) => p.nullableTypedefWithoutQuestion, @@ -172,9 +165,6 @@ void main() { test('nullable dynamic', () { expectReadPropReturnsNull((props) => props.nullableDynamic); }); - test('nullable dynamic with extraneous ? syntax', () { - expectReadPropReturnsNull((props) => props.nullableDynamicWithQuestion); - }); test('nullable typedef, without ? syntax', () { expectReadPropReturnsNull((props) => props.nullableTypedefWithoutQuestion); }); @@ -291,13 +281,6 @@ void main() { testValue: 'test value', ); }); - test('nullable dynamic with extraneous ? syntax', () { - testStateWriteAndRead( - readState: (p) => p.nullableDynamicWithQuestion, - writeState: (p, value) => p.nullableDynamicWithQuestion = value, - testValue: 'test value', - ); - }); test('nullable typedef, without ? syntax', () { testStateWriteAndRead( readState: (p) => p.nullableTypedefWithoutQuestion, @@ -366,9 +349,6 @@ void main() { test('nullable dynamic', () { expectReadStateReturnsNull((state) => state.nullableDynamic); }); - test('nullable dynamic with extraneous ? syntax', () { - expectReadStateReturnsNull((state) => state.nullableDynamicWithQuestion); - }); test('nullable typedef, without ? syntax', () { expectReadStateReturnsNull((state) => state.nullableTypedefWithoutQuestion); }); @@ -397,7 +377,6 @@ class _$NullSafeTestProps extends UiProps { String? nullable; dynamic nullableDynamic; - dynamic? nullableDynamicWithQuestion; NullableTypedef nullableTypedefWithoutQuestion; } @@ -412,7 +391,6 @@ class _$NullSafeTestState extends UiState { String? nullable; dynamic nullableDynamic; - dynamic? nullableDynamicWithQuestion; NullableTypedef nullableTypedefWithoutQuestion; } diff --git a/test/over_react/component_declaration/builder_integration_tests/component2/null_safe_accessor_integration_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/component2/null_safe_accessor_integration_test.over_react.g.dart index c5d843ac8..3a0eddcbc 100644 --- a/test/over_react/component_declaration/builder_integration_tests/component2/null_safe_accessor_integration_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/component2/null_safe_accessor_integration_test.over_react.g.dart @@ -111,17 +111,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin set nullableDynamic(dynamic value) => props[_$key__nullableDynamic___$NullSafeTestProps] = value; - /// - @override - dynamic? get nullableDynamicWithQuestion => - (props[_$key__nullableDynamicWithQuestion___$NullSafeTestProps] ?? null) - as dynamic?; - - /// - @override - set nullableDynamicWithQuestion(dynamic? value) => - props[_$key__nullableDynamicWithQuestion___$NullSafeTestProps] = value; - /// @override NullableTypedef get nullableTypedefWithoutQuestion => @@ -166,9 +155,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin PropDescriptor(_$key__nullable___$NullSafeTestProps); static const PropDescriptor _$prop__nullableDynamic___$NullSafeTestProps = PropDescriptor(_$key__nullableDynamic___$NullSafeTestProps); - static const PropDescriptor - _$prop__nullableDynamicWithQuestion___$NullSafeTestProps = - PropDescriptor(_$key__nullableDynamicWithQuestion___$NullSafeTestProps); static const PropDescriptor _$prop__nullableTypedefWithoutQuestion___$NullSafeTestProps = PropDescriptor( @@ -191,8 +177,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin 'NullSafeTestProps.nullable'; static const String _$key__nullableDynamic___$NullSafeTestProps = 'NullSafeTestProps.nullableDynamic'; - static const String _$key__nullableDynamicWithQuestion___$NullSafeTestProps = - 'NullSafeTestProps.nullableDynamicWithQuestion'; static const String _$key__nullableTypedefWithoutQuestion___$NullSafeTestProps = 'NullSafeTestProps.nullableTypedefWithoutQuestion'; @@ -206,7 +190,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin _$prop__requiredWithAccessorAndCustomKey___$NullSafeTestProps, _$prop__nullable___$NullSafeTestProps, _$prop__nullableDynamic___$NullSafeTestProps, - _$prop__nullableDynamicWithQuestion___$NullSafeTestProps, _$prop__nullableTypedefWithoutQuestion___$NullSafeTestProps ]; static const List $propKeys = [ @@ -218,7 +201,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin _$key__requiredWithAccessorAndCustomKey___$NullSafeTestProps, _$key__nullable___$NullSafeTestProps, _$key__nullableDynamic___$NullSafeTestProps, - _$key__nullableDynamicWithQuestion___$NullSafeTestProps, _$key__nullableTypedefWithoutQuestion___$NullSafeTestProps ]; } @@ -388,17 +370,6 @@ abstract class _$NullSafeTestStateAccessorsMixin set nullableDynamic(dynamic value) => state[_$key__nullableDynamic___$NullSafeTestState] = value; - /// - @override - dynamic? get nullableDynamicWithQuestion => - (state[_$key__nullableDynamicWithQuestion___$NullSafeTestState] ?? null) - as dynamic?; - - /// - @override - set nullableDynamicWithQuestion(dynamic? value) => - state[_$key__nullableDynamicWithQuestion___$NullSafeTestState] = value; - /// @override NullableTypedef get nullableTypedefWithoutQuestion => @@ -439,9 +410,6 @@ abstract class _$NullSafeTestStateAccessorsMixin StateDescriptor(_$key__nullable___$NullSafeTestState); static const StateDescriptor _$prop__nullableDynamic___$NullSafeTestState = StateDescriptor(_$key__nullableDynamic___$NullSafeTestState); - static const StateDescriptor - _$prop__nullableDynamicWithQuestion___$NullSafeTestState = - StateDescriptor(_$key__nullableDynamicWithQuestion___$NullSafeTestState); static const StateDescriptor _$prop__nullableTypedefWithoutQuestion___$NullSafeTestState = StateDescriptor( @@ -461,8 +429,6 @@ abstract class _$NullSafeTestStateAccessorsMixin 'NullSafeTestState.nullable'; static const String _$key__nullableDynamic___$NullSafeTestState = 'NullSafeTestState.nullableDynamic'; - static const String _$key__nullableDynamicWithQuestion___$NullSafeTestState = - 'NullSafeTestState.nullableDynamicWithQuestion'; static const String _$key__nullableTypedefWithoutQuestion___$NullSafeTestState = 'NullSafeTestState.nullableTypedefWithoutQuestion'; @@ -475,7 +441,6 @@ abstract class _$NullSafeTestStateAccessorsMixin _$prop__requiredDynamic___$NullSafeTestState, _$prop__nullable___$NullSafeTestState, _$prop__nullableDynamic___$NullSafeTestState, - _$prop__nullableDynamicWithQuestion___$NullSafeTestState, _$prop__nullableTypedefWithoutQuestion___$NullSafeTestState ]; static const List $stateKeys = [ @@ -486,7 +451,6 @@ abstract class _$NullSafeTestStateAccessorsMixin _$key__requiredDynamic___$NullSafeTestState, _$key__nullable___$NullSafeTestState, _$key__nullableDynamic___$NullSafeTestState, - _$key__nullableDynamicWithQuestion___$NullSafeTestState, _$key__nullableTypedefWithoutQuestion___$NullSafeTestState ]; } diff --git a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safe_accessor_integration_test.dart b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safe_accessor_integration_test.dart index 67813be50..af1639758 100644 --- a/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safe_accessor_integration_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/new_boilerplate/null_safe_accessor_integration_test.dart @@ -96,13 +96,6 @@ void main() { testValue: 'test value', ); }); - test('nullable dynamic with extraneous ? syntax', () { - testPropWriteAndRead( - readProp: (p) => p.nullableDynamicWithQuestion, - writeProp: (p, value) => p.nullableDynamicWithQuestion = value, - testValue: 'test value', - ); - }); test('nullable typedef, without ? syntax', () { testPropWriteAndRead( readProp: (p) => p.nullableTypedefWithoutQuestion, @@ -171,9 +164,6 @@ void main() { test('nullable dynamic', () { expectReadPropReturnsNull((props) => props.nullableDynamic); }); - test('nullable dynamic with extraneous ? syntax', () { - expectReadPropReturnsNull((props) => props.nullableDynamicWithQuestion); - }); test('nullable typedef, without ? syntax', () { expectReadPropReturnsNull((props) => props.nullableTypedefWithoutQuestion); }); @@ -290,13 +280,6 @@ void main() { testValue: 'test value', ); }); - test('nullable dynamic with extraneous ? syntax', () { - testStateWriteAndRead( - readState: (p) => p.nullableDynamicWithQuestion, - writeState: (p, value) => p.nullableDynamicWithQuestion = value, - testValue: 'test value', - ); - }); test('nullable typedef, without ? syntax', () { testStateWriteAndRead( readState: (p) => p.nullableTypedefWithoutQuestion, @@ -365,9 +348,6 @@ void main() { test('nullable dynamic', () { expectReadStateReturnsNull((state) => state.nullableDynamic); }); - test('nullable dynamic with extraneous ? syntax', () { - expectReadStateReturnsNull((state) => state.nullableDynamicWithQuestion); - }); test('nullable typedef, without ? syntax', () { expectReadStateReturnsNull((state) => state.nullableTypedefWithoutQuestion); }); @@ -393,8 +373,6 @@ mixin NullSafeTestProps on UiProps { String? nullable; dynamic nullableDynamic; - // ignore: unnecessary_question_mark - dynamic? nullableDynamicWithQuestion; NullableTypedef nullableTypedefWithoutQuestion; } @@ -408,8 +386,6 @@ mixin NullSafeTestState on UiState { String? nullable; dynamic nullableDynamic; - // ignore: unnecessary_question_mark - dynamic? nullableDynamicWithQuestion; NullableTypedef nullableTypedefWithoutQuestion; } diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safe_accessor_integration_test.dart b/test/over_react/component_declaration/builder_integration_tests/null_safe_accessor_integration_test.dart index 91caf6071..8bcca1f84 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safe_accessor_integration_test.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safe_accessor_integration_test.dart @@ -98,13 +98,6 @@ void main() { testValue: 'test value', ); }); - test('nullable dynamic with extraneous ? syntax', () { - testPropWriteAndRead( - readProp: (p) => p.nullableDynamicWithQuestion, - writeProp: (p, value) => p.nullableDynamicWithQuestion = value, - testValue: 'test value', - ); - }); test('nullable typedef, without ? syntax', () { testPropWriteAndRead( readProp: (p) => p.nullableTypedefWithoutQuestion, @@ -173,9 +166,6 @@ void main() { test('nullable dynamic', () { expectReadPropReturnsNull((props) => props.nullableDynamic); }); - test('nullable dynamic with extraneous ? syntax', () { - expectReadPropReturnsNull((props) => props.nullableDynamicWithQuestion); - }); test('nullable typedef, without ? syntax', () { expectReadPropReturnsNull((props) => props.nullableTypedefWithoutQuestion); }); @@ -292,13 +282,6 @@ void main() { testValue: 'test value', ); }); - test('nullable dynamic with extraneous ? syntax', () { - testStateWriteAndRead( - readState: (p) => p.nullableDynamicWithQuestion, - writeState: (p, value) => p.nullableDynamicWithQuestion = value, - testValue: 'test value', - ); - }); test('nullable typedef, without ? syntax', () { testStateWriteAndRead( readState: (p) => p.nullableTypedefWithoutQuestion, @@ -367,9 +350,6 @@ void main() { test('nullable dynamic', () { expectReadStateReturnsNull((state) => state.nullableDynamic); }); - test('nullable dynamic with extraneous ? syntax', () { - expectReadStateReturnsNull((state) => state.nullableDynamicWithQuestion); - }); test('nullable typedef, without ? syntax', () { expectReadStateReturnsNull((state) => state.nullableTypedefWithoutQuestion); }); @@ -398,8 +378,6 @@ class _$NullSafeTestProps extends UiProps { String? nullable; dynamic nullableDynamic; - // ignore: unnecessary_question_mark - dynamic? nullableDynamicWithQuestion; NullableTypedef nullableTypedefWithoutQuestion; } @@ -414,8 +392,6 @@ class _$NullSafeTestState extends UiState { String? nullable; dynamic nullableDynamic; - // ignore: unnecessary_question_mark - dynamic? nullableDynamicWithQuestion; NullableTypedef nullableTypedefWithoutQuestion; } diff --git a/test/over_react/component_declaration/builder_integration_tests/null_safe_accessor_integration_test.over_react.g.dart b/test/over_react/component_declaration/builder_integration_tests/null_safe_accessor_integration_test.over_react.g.dart index 8c994e01c..bb27e880c 100644 --- a/test/over_react/component_declaration/builder_integration_tests/null_safe_accessor_integration_test.over_react.g.dart +++ b/test/over_react/component_declaration/builder_integration_tests/null_safe_accessor_integration_test.over_react.g.dart @@ -111,17 +111,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin set nullableDynamic(dynamic value) => props[_$key__nullableDynamic___$NullSafeTestProps] = value; - /// - @override - dynamic? get nullableDynamicWithQuestion => - (props[_$key__nullableDynamicWithQuestion___$NullSafeTestProps] ?? null) - as dynamic?; - - /// - @override - set nullableDynamicWithQuestion(dynamic? value) => - props[_$key__nullableDynamicWithQuestion___$NullSafeTestProps] = value; - /// @override NullableTypedef get nullableTypedefWithoutQuestion => @@ -166,9 +155,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin PropDescriptor(_$key__nullable___$NullSafeTestProps); static const PropDescriptor _$prop__nullableDynamic___$NullSafeTestProps = PropDescriptor(_$key__nullableDynamic___$NullSafeTestProps); - static const PropDescriptor - _$prop__nullableDynamicWithQuestion___$NullSafeTestProps = - PropDescriptor(_$key__nullableDynamicWithQuestion___$NullSafeTestProps); static const PropDescriptor _$prop__nullableTypedefWithoutQuestion___$NullSafeTestProps = PropDescriptor( @@ -191,8 +177,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin 'NullSafeTestProps.nullable'; static const String _$key__nullableDynamic___$NullSafeTestProps = 'NullSafeTestProps.nullableDynamic'; - static const String _$key__nullableDynamicWithQuestion___$NullSafeTestProps = - 'NullSafeTestProps.nullableDynamicWithQuestion'; static const String _$key__nullableTypedefWithoutQuestion___$NullSafeTestProps = 'NullSafeTestProps.nullableTypedefWithoutQuestion'; @@ -206,7 +190,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin _$prop__requiredWithAccessorAndCustomKey___$NullSafeTestProps, _$prop__nullable___$NullSafeTestProps, _$prop__nullableDynamic___$NullSafeTestProps, - _$prop__nullableDynamicWithQuestion___$NullSafeTestProps, _$prop__nullableTypedefWithoutQuestion___$NullSafeTestProps ]; static const List $propKeys = [ @@ -218,7 +201,6 @@ abstract class _$NullSafeTestPropsAccessorsMixin _$key__requiredWithAccessorAndCustomKey___$NullSafeTestProps, _$key__nullable___$NullSafeTestProps, _$key__nullableDynamic___$NullSafeTestProps, - _$key__nullableDynamicWithQuestion___$NullSafeTestProps, _$key__nullableTypedefWithoutQuestion___$NullSafeTestProps ]; } @@ -355,17 +337,6 @@ abstract class _$NullSafeTestStateAccessorsMixin set nullableDynamic(dynamic value) => state[_$key__nullableDynamic___$NullSafeTestState] = value; - /// - @override - dynamic? get nullableDynamicWithQuestion => - (state[_$key__nullableDynamicWithQuestion___$NullSafeTestState] ?? null) - as dynamic?; - - /// - @override - set nullableDynamicWithQuestion(dynamic? value) => - state[_$key__nullableDynamicWithQuestion___$NullSafeTestState] = value; - /// @override NullableTypedef get nullableTypedefWithoutQuestion => @@ -406,9 +377,6 @@ abstract class _$NullSafeTestStateAccessorsMixin StateDescriptor(_$key__nullable___$NullSafeTestState); static const StateDescriptor _$prop__nullableDynamic___$NullSafeTestState = StateDescriptor(_$key__nullableDynamic___$NullSafeTestState); - static const StateDescriptor - _$prop__nullableDynamicWithQuestion___$NullSafeTestState = - StateDescriptor(_$key__nullableDynamicWithQuestion___$NullSafeTestState); static const StateDescriptor _$prop__nullableTypedefWithoutQuestion___$NullSafeTestState = StateDescriptor( @@ -428,8 +396,6 @@ abstract class _$NullSafeTestStateAccessorsMixin 'NullSafeTestState.nullable'; static const String _$key__nullableDynamic___$NullSafeTestState = 'NullSafeTestState.nullableDynamic'; - static const String _$key__nullableDynamicWithQuestion___$NullSafeTestState = - 'NullSafeTestState.nullableDynamicWithQuestion'; static const String _$key__nullableTypedefWithoutQuestion___$NullSafeTestState = 'NullSafeTestState.nullableTypedefWithoutQuestion'; @@ -442,7 +408,6 @@ abstract class _$NullSafeTestStateAccessorsMixin _$prop__requiredDynamic___$NullSafeTestState, _$prop__nullable___$NullSafeTestState, _$prop__nullableDynamic___$NullSafeTestState, - _$prop__nullableDynamicWithQuestion___$NullSafeTestState, _$prop__nullableTypedefWithoutQuestion___$NullSafeTestState ]; static const List $stateKeys = [ @@ -453,7 +418,6 @@ abstract class _$NullSafeTestStateAccessorsMixin _$key__requiredDynamic___$NullSafeTestState, _$key__nullable___$NullSafeTestState, _$key__nullableDynamic___$NullSafeTestState, - _$key__nullableDynamicWithQuestion___$NullSafeTestState, _$key__nullableTypedefWithoutQuestion___$NullSafeTestState ]; } diff --git a/test/over_react/component_declaration/component_base_test.dart b/test/over_react/component_declaration/component_base_test.dart index f02b4eef1..235389b65 100644 --- a/test/over_react/component_declaration/component_base_test.dart +++ b/test/over_react/component_declaration/component_base_test.dart @@ -24,7 +24,6 @@ import 'package:over_react/over_react.dart' show Dom, DummyComponent, DummyCompo import 'package:over_react/over_react.dart' as over_react; import 'package:over_react/src/component_declaration/component_base.dart'; import 'package:over_react/src/component_declaration/component_type_checking.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:react/react_client.dart'; import 'package:test/test.dart'; import 'package:w_common/disposable.dart'; @@ -534,8 +533,7 @@ main() { test('a Map of the wrong type throws a type error', () { final style = {'color': 'blue'}; expect(() => getTypedView({styleKey: style}).style, - // ignore: deprecated_member_use - throwsA(anyOf(isA(), isA()))); + throwsA(isATypeCastError())); }); test('null: returns null', () { diff --git a/test/over_react/component_declaration/flux_component_test/component2/flux_component_test.dart b/test/over_react/component_declaration/flux_component_test/component2/flux_component_test.dart index 50401ebc8..cb9c5407f 100644 --- a/test/over_react/component_declaration/flux_component_test/component2/flux_component_test.dart +++ b/test/over_react/component_declaration/flux_component_test/component2/flux_component_test.dart @@ -20,7 +20,6 @@ import 'dart:async'; import 'dart:html'; import 'package:logging/logging.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; import 'package:w_flux/w_flux.dart'; import 'package:over_react/over_react.dart'; diff --git a/test/over_react/util/cast_ui_factory_test.dart b/test/over_react/util/cast_ui_factory_test.dart index e3f1be72c..614ca25e8 100644 --- a/test/over_react/util/cast_ui_factory_test.dart +++ b/test/over_react/util/cast_ui_factory_test.dart @@ -18,6 +18,8 @@ library cast_ui_factory_test; import 'package:over_react/over_react.dart'; import 'package:test/test.dart'; +import '../../test_util/test_util.dart'; + part 'cast_ui_factory_test.over_react.g.dart'; main() { @@ -44,7 +46,7 @@ main() { }); test('throws an error if provided something other than a UiFactory', () { - expect(() => castUiFactory('test'), throwsA(isA())); + expect(() => castUiFactory('test'), throwsA(isATypeCastError())); }); }); } diff --git a/test/over_react/util/prop_conversion_test.dart b/test/over_react/util/prop_conversion_test.dart index 13ca46d72..b53a46a07 100644 --- a/test/over_react/util/prop_conversion_test.dart +++ b/test/over_react/util/prop_conversion_test.dart @@ -1083,9 +1083,9 @@ main() { // DDC error message matches(RegExp( r"Expected a value of type 'Map[^']*', but got one of type '(Native|Legacy)JavaScriptObject'")), - // dart2js error message + // Dart 2 dart2js, Dart 3 DDC error message matches(RegExp( - r"type '(Unknown|Plain)JavaScriptObject' is not a subtype of type 'Map[^']*'")), + r"type '(Unknown|Plain|Legacy)JavaScriptObject' is not a subtype of type 'Map[^']*'")), )), ]); diff --git a/test/over_react/util/rem_util_test.dart b/test/over_react/util/rem_util_test.dart index 6216984ff..121eb37ab 100644 --- a/test/over_react/util/rem_util_test.dart +++ b/test/over_react/util/rem_util_test.dart @@ -21,7 +21,6 @@ import 'dart:html'; import 'package:over_react/src/util/css_value_util.dart'; import 'package:over_react/src/util/rem_util.dart'; import 'package:over_react/src/util/test_mode.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:test/test.dart'; import '../../test_util/test_util.dart'; diff --git a/test/test_util/test_util.dart b/test/test_util/test_util.dart index 48b525ca0..661015157 100644 --- a/test/test_util/test_util.dart +++ b/test/test_util/test_util.dart @@ -17,6 +17,7 @@ library test_util; import 'dart:js_util'; import 'package:over_react/over_react.dart'; +import 'package:test/test.dart'; export 'package:over_react_test/over_react_test.dart' hide testJsComponentFactory; @@ -37,3 +38,14 @@ bool isDDC() { assert(assertsEnabled = true); return assertsEnabled; } + +/// A matcher that matches an error thrown when a runtime cast fails. +/// +/// This will be either +/// - a `CastError` in Dart 2 (removed in Dart 3) +/// - a [TypeError] in Dart 3 +Matcher isATypeCastError() => anyOf( + isA(), + // Use toString since CastError isn't available in Dart 3 + isA().having((o) => o.toString(), 'toString() value', contains('CastError')), + ); diff --git a/test/vm_tests/builder/conditionally_null_safe_builder_test.dart b/test/vm_tests/builder/conditionally_null_safe_builder_test.dart index 6d719b306..e2d43bbd5 100644 --- a/test/vm_tests/builder/conditionally_null_safe_builder_test.dart +++ b/test/vm_tests/builder/conditionally_null_safe_builder_test.dart @@ -19,7 +19,7 @@ import 'package:test/test.dart'; main() { group('conditionally null safe builder', () { - group('on package-level Dart version that is not null safe', () { + group('on package-level Dart language version that is not null safe', () { setUpAll(() { const nonNullSafePackagePath = 'test_fixtures/test_packages/non_null_safe'; @@ -81,9 +81,9 @@ main() { 'Using nullSafety: false. {languageVersion: 2.11, source: libraryVersionComment}')); }); }); - }); + }, tags: 'dart-2-only'); - group('on package-level Dart version that is null safe', () { + group('on package-level Dart language version that is null safe', () { setUpAll(() { const nonNullSafePackagePath = 'test_fixtures/test_packages/null_safe'; Process.runSync('dart', ['pub', 'get'], diff --git a/test/vm_tests/builder/parsing/members_test.dart b/test/vm_tests/builder/parsing/members_test.dart index dd3f705fd..24357463e 100644 --- a/test/vm_tests/builder/parsing/members_test.dart +++ b/test/vm_tests/builder/parsing/members_test.dart @@ -195,34 +195,31 @@ main() { }; // Loop over the deprecated lifecycle methods - legacyLifecycleMethodsMap.keys.forEach((lifecycle) => { - // Loop through every version of the boilerplate - for (final version in BoilerplateVersions.values) - { - test('$lifecycle with ${versionDescriptions[version]}', () { - // Grab the boilerplate with the deprecated lifecycle method - final componentString = getBoilerplateString( - deprecatedLifecycleMethod: lifecycle, version: version); - final members = - BoilerplateMemberHelper.parseAndReturnMembers(componentString); - final component = members.whereType().first; - final componentVersion = resolveVersion(members).version; - final file = SourceFile.fromString(componentString); - final collector = ErrorCollector.callback(file, onError: validateCallback); - - component.validate(componentVersion, collector); - - // Warnings should only be logged if the component is a Component2 - if (component.isComponent2(componentVersion)) { - expect(validateResults, [ - contains(legacyLifecycleMethodsMap[lifecycle]), - ]); - } else { - expect(validateResults, []); - } - }) - } + for (final lifecycle in legacyLifecycleMethodsMap.keys) { + // Loop through every version of the boilerplate + for (final version in BoilerplateVersions.values) { + test('$lifecycle with ${versionDescriptions[version]}', () { + // Grab the boilerplate with the deprecated lifecycle method + final componentString = getBoilerplateString(deprecatedLifecycleMethod: lifecycle, version: version); + final members = BoilerplateMemberHelper.parseAndReturnMembers(componentString); + final component = members.whereType().first; + final componentVersion = resolveVersion(members).version; + final file = SourceFile.fromString(componentString); + final collector = ErrorCollector.callback(file, onError: validateCallback); + + component.validate(componentVersion, collector); + + // Warnings should only be logged if the component is a Component2 + if (component.isComponent2(componentVersion)) { + expect(validateResults, [ + contains(legacyLifecycleMethodsMap[lifecycle]), + ]); + } else { + expect(validateResults, []); + } }); + } + } }); }); }); diff --git a/tool/delete_dart_2_only_files.sh b/tool/delete_dart_2_only_files.sh new file mode 100755 index 000000000..af7122bdd --- /dev/null +++ b/tool/delete_dart_2_only_files.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# +# This script deletes files that can only be run in Dart 2 (ones not using null-safety), +# and need to be deleted for analysis and compilation to work in Dart 3. +# + +set -e + +rm -rf test/over_react/component_declaration/non_null_safe_builder_integration_tests +rm test/over_react/component_declaration/flux_component_test/component2/unsound_flux_component_test.dart +rm test/over_react/component_declaration/flux_component_test/unsound_flux_component_test.dart +rm test/over_react_component_declaration_non_null_safe_test.dart + +# Remove the reference to the test file. Use a temporary file since we can't +# redirect to/from the same file in the same command. See https://github.com/koalaman/shellcheck/wiki/SC2094 +mv dart_test.yaml dart_test.yaml.old +grep over_react_component_declaration_non_null_safe_test.dart \ + --fixed-strings \ + --invert-match \ + dart_test.yaml.old > dart_test.yaml +rm dart_test.yaml.old + +rm -rf tools/analyzer_plugin/test/unit/util/non_null_safe +rm -rf tools/analyzer_plugin/test/integration/assists/non_null_safe +rm -rf tools/analyzer_plugin/test/integration/diagnostics/non_null_safe \ No newline at end of file diff --git a/tools/analyzer_plugin/analysis_options.yaml b/tools/analyzer_plugin/analysis_options.yaml index f11db5869..76e1b662d 100644 --- a/tools/analyzer_plugin/analysis_options.yaml +++ b/tools/analyzer_plugin/analysis_options.yaml @@ -15,8 +15,9 @@ analyzer: missing_return: warning # Override workiva_analysis_options upgrade to warning unused_import: info - import_of_legacy_library_into_null_safe: warning implementation_imports: warning + # To work around warning "'can_be_null_after_null_aware' isn't a recognized error code" in Dart 3 + included_file_warning: info # ignores from v1.recommended prefer_interpolation_to_compose_strings: ignore