diff --git a/.github/workflows/dart_ci.yml b/.github/workflows/dart_ci.yml index 63e61bfa9..531ef4bb3 100644 --- a/.github/workflows/dart_ci.yml +++ b/.github/workflows/dart_ci.yml @@ -16,8 +16,9 @@ jobs: fail-fast: false matrix: # Can't run on `dev` (Dart 3) until we're fully null-safe. - # TODO re-enable stable once we get the analysis/test parts of the build passing - sdk: [ 2.13.4 ] + # We can't run on Dart >=2.19 until https://github.com/dart-lang/sdk/issues/51128 + # is fixed, or we work around it, which requires upgrading to analyzer ^5.1.0. + sdk: [ 2.13.4, 2.18.7 ] steps: - uses: actions/checkout@v2 - uses: dart-lang/setup-dart@v0.2 @@ -44,9 +45,8 @@ jobs: run: | # Analyze lib to ensure public APIs don't depend on build-to-cache files, # which could cause analysis issues for consumers who haven't run a build yet. - dartanalyzer lib - cd example/boilerplate_versions - dart analyze + dart analyze lib + dart analyze example/boilerplate_versions if: always() && steps.install.outcome == 'success' - id: build @@ -54,11 +54,12 @@ jobs: name: Build generated files / precompile DDC assets run: | dart pub run build_runner build --delete-conflicting-outputs -o ddc_precompiled - if [ ${{ matrix.sdk }} = '2.7.2' ]; then + if [ ${{ matrix.sdk }} = '2.18.7' ]; then git diff --exit-code else - # Exclude built_redux generated files since they get generated differently in Dart 2.7 vs other versions - git diff --exit-code -- ":(exclude)test/over_react/component_declaration/redux_component_test/test_reducer.g.dart" + # Don't check these generated files for other SDKs, since they may generate differently + # due to different resolved dependencies. + git diff --exit-code -- ":(exclude)test/mockito.mocks.dart" ":(exclude)test/over_react/component_declaration/redux_component_test/test_reducer.g.dart" fi if: always() && steps.install.outcome == 'success' @@ -68,7 +69,9 @@ jobs: if: always() && steps.install.outcome == 'success' && steps.build.outcome == 'success' - name: Run tests (VM) - run: dart pub run dart_dev test -P 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 if: always() && steps.install.outcome == 'success' && steps.build.outcome == 'success' - name: Run tests (DDC) @@ -79,49 +82,6 @@ jobs: run: dart pub run dart_dev test --build-args="-r" -P dart2js if: always() && steps.install.outcome == 'success' && steps.build.outcome == 'success' - validate_analyzer: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - sdk: [ 2.13.4, stable ] - analyzer: - - ^0.40.0 - - ^0.41.0 - # No ^0.42.0 since that line only contains 0.42.0-nullsafety.0, which is the 1.0.0 prerelease - - ^1.0.0 - # For stable, only validate analyzer ^1.0.0. Otherwise, validate all analyzer versions. - exclude: - - sdk: stable - include: - - sdk: stable - analyzer: ^1.0.0 - steps: - - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v0.2 - with: - sdk: ${{ matrix.sdk }} - - - name: Print Dart SDK version - run: dart --version - - - name: Update analyzer constraint to ${{ matrix.analyzer }} and validate `pub get` can resolve - id: resolve - run: | - dart tool/set_analyzer_constraint.dart "${{ matrix.analyzer }}" - # Show the updated version constraint - git diff pubspec.yaml - dart pub get - - - name: Analyze package source - run: dart analyze . - - - name: Verify builder runs without errors - run: dart run build_runner build --build-filter='**.dart' --delete-conflicting-outputs - - - name: Run builder tests - run: dart run test -p vm -- test/vm_tests/builder - analyzer_plugin: runs-on: ubuntu-latest defaults: @@ -131,7 +91,7 @@ jobs: fail-fast: false matrix: # Can't run on `dev` (Dart 3) until we're fully null-safe. - sdk: [ 2.13.4, stable ] + sdk: [ 2.13.4, 2.18.7, stable ] steps: - uses: actions/checkout@v2 - uses: dart-lang/setup-dart@v0.2 diff --git a/analysis_options.yaml b/analysis_options.yaml index 581cdd299..9c68ce005 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -16,6 +16,9 @@ analyzer: missing_required_param: error must_call_super: error uri_has_not_been_generated: ignore + # Workaround for https://github.com/dart-lang/sdk/issues/51087 + # TODO remove once we can run on Dart 2.19 (blocked by https://github.com/dart-lang/sdk/issues/51128 or working around it by upgrading to analyzer ^5.1.0) + part_of_different_library: ignore linter: rules: # ------------------- diff --git a/app/over_react_redux/todo_client/pubspec.yaml b/app/over_react_redux/todo_client/pubspec.yaml index 153883664..8dfdf60ae 100644 --- a/app/over_react_redux/todo_client/pubspec.yaml +++ b/app/over_react_redux/todo_client/pubspec.yaml @@ -7,7 +7,7 @@ environment: dependencies: color: any memoize: ^2.0.0 - meta: ^1.6.0 # Workaround to avoid https://github.com/dart-lang/sdk/issues/46142 + meta: ^1.6.0 over_react: ">=3.1.5 <5.0.0" json_annotation: ^3.0.0 redux: ">=3.0.0 <5.0.0" diff --git a/build.yaml b/build.yaml index 74b3af2af..37626b8fa 100644 --- a/build.yaml +++ b/build.yaml @@ -17,11 +17,10 @@ builders: targets: $default: builders: - # mockito's builder is expensive and is not needed until this package is - # migrated to null-safety. At that point, it should be scoped only to - # relevant files. mockito:mockBuilder: - enabled: false + generate_for: + include: + - "test/mockito.dart" over_react|_over_react_local_builder: enabled: true generate_for: diff --git a/pubspec.yaml b/pubspec.yaml index 7c20f83b6..ef58f88c6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: dart_style: '>=1.2.5 <3.0.0' js: ^0.6.1+1 logging: '>=0.11.3+2 <2.0.0' - meta: ^1.6.0 # Workaround to avoid https://github.com/dart-lang/sdk/issues/46142 + meta: ^1.6.0 path: ^1.5.1 react: ^6.1.8 redux: ">=3.0.0 <5.0.0" @@ -36,11 +36,13 @@ dev_dependencies: dependency_validator: '>=2.0.0 <4.0.0' glob: '>=1.2.0<3.0.0' io: '>=0.3.2+1 <2.0.0' - mockito: ^4.1.1 + mockito: ^5.0.0 react_testing_library: ^2.1.0 over_react_test: ^2.10.2 pedantic: ^1.8.0 - test: ^1.15.7 + # Pin as a workaround for https://github.com/dart-lang/test/issues/1594 + # TODO Unpin and remove dependency_validator ignore once we can upgrade past 1.18.1, which is dependent on upgrading to at least analyzer 2.0. + test: '>=1.15.7 <1.18.0' yaml: '>=2.2.1 <4.0.0' workiva: @@ -53,4 +55,4 @@ dependency_validator: - app/** - tools/** ignore: - - meta + - test diff --git a/test/mockito.dart b/test/mockito.dart new file mode 100644 index 000000000..0c14453b5 --- /dev/null +++ b/test/mockito.dart @@ -0,0 +1,25 @@ +import 'package:logging/logging.dart'; +import 'package:mockito/annotations.dart'; + +@GenerateMocks([], customMocks: [ + MockSpec(returnNullOnMissingStub: true), + MockSpec(fallbackGenerators: { + #[]: listIndexOperatorShim, + #removeAt: listRemoveAtShim, + #removeLast: listRemoveLastShim, + }, returnNullOnMissingStub: true), + MockSpec(fallbackGenerators: { + #update: mapUpdateShim, + #putIfAbsent: mapPutIfAbsentShim, + }, returnNullOnMissingStub: true) +]) +void main() {} + +dynamic listIndexOperatorShim(int index) => 1; +dynamic listRemoveAtShim(int index) => 1; +dynamic listRemoveLastShim() => 1; + +String mapUpdateShim(K key, V Function(V value) update, + {V Function() ifAbsent}) => + 'value'; +String mapPutIfAbsentShim(K key, V Function() ifAbsent) => 'value'; diff --git a/test/mockito.mocks.dart b/test/mockito.mocks.dart new file mode 100644 index 000000000..054b2ee46 --- /dev/null +++ b/test/mockito.mocks.dart @@ -0,0 +1,30 @@ +// Mocks generated by Mockito 5.0.15 from annotations +// in over_react/test/mockito.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:logging/src/logger.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis + +/// A class which mocks [Logger]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockLogger extends _i1.Mock implements _i2.Logger {} + +/// A class which mocks [List]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockList extends _i1.Mock implements List {} + +/// A class which mocks [Map]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMap extends _i1.Mock implements Map {} diff --git a/test/over_react/component_declaration/redux_component_test/test_reducer.g.dart b/test/over_react/component_declaration/redux_component_test/test_reducer.g.dart index 8afa9e060..5b0d5ea03 100644 --- a/test/over_react/component_declaration/redux_component_test/test_reducer.g.dart +++ b/test/over_react/component_declaration/redux_component_test/test_reducer.g.dart @@ -7,7 +7,6 @@ part of over_react.component_declaration.redux_component.reducer; // ************************************************************************** // ignore_for_file: avoid_classes_with_only_static_members -// ignore_for_file: annotate_overrides // ignore_for_file: overridden_fields // ignore_for_file: type_annotate_public_apis @@ -124,4 +123,4 @@ class BaseStateBuilder implements Builder { } } -// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new +// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,deprecated_member_use_from_same_package,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new diff --git a/test/over_react/shared/map_proxy_tests.dart b/test/over_react/shared/map_proxy_tests.dart index f92d95c84..42f310286 100644 --- a/test/over_react/shared/map_proxy_tests.dart +++ b/test/over_react/shared/map_proxy_tests.dart @@ -18,6 +18,8 @@ library over_react_tests.shared.map_proxy_tests; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; +import '../../mockito.mocks.dart'; + void mapProxyTests(Map Function(Map proxiedMap) mapProxyFactory) { group('proxies the Map member:', () { Map proxy; @@ -173,5 +175,3 @@ void mapProxyTests(Map Function(Map proxiedMap) mapProxyFactory) { }); }); } - -class MockMap extends Mock implements Map {} diff --git a/test/over_react/util/prop_conversion_test.dart b/test/over_react/util/prop_conversion_test.dart index e4a2473a7..cc886682a 100644 --- a/test/over_react/util/prop_conversion_test.dart +++ b/test/over_react/util/prop_conversion_test.dart @@ -1072,7 +1072,7 @@ main() { isA().havingToStringValue(anyOf( // DDC error message matches(RegExp( - r"Expected a value of type 'Map[^']*', but got one of type 'NativeJavaScriptObject'")), + 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[^']*'")), diff --git a/test/over_react/util/react_wrappers_test.dart b/test/over_react/util/react_wrappers_test.dart index ebba5991b..12298b021 100644 --- a/test/over_react/util/react_wrappers_test.dart +++ b/test/over_react/util/react_wrappers_test.dart @@ -1063,19 +1063,25 @@ main() { expect(result2, same(result1), reason: 'should have returned the same object'); }, tags: 'no-ddc'); - test('unless the runtime is the DDC', () { + // A previous version of this test asserted that caching didn't take place, but that's + // no longer true in newer SDKs since Expando started using WeakMap in Dart 2.14.0: https://github.com/dart-lang/sdk/commit/460887d8149748d3feaad857f1b13721faadeffa, + // so we can't test that behavior having different tests for different SDKs. + // Once we raise our minimums, we can update the test above to run on DDC as well. + // Until then, just ensure the implementation isn't broken in DDC, as a regression test for older SDKs. + test('and works without throwing in DDC', () { final element = factory({ 'dartProp': 'dart' }) as ReactElement; - var result1 = getProps(element); - var result2 = getProps(element); - - expect(result1, containsPair('dartProp', 'dart'), reason: 'test setup sanity check'); - expect(result2, isNot(same(result1)), - reason: 'if this test fails, then it\'s possible that the bug was fixed in' - ' a newer version of the Dart SDK, and this test can be removed!'); - }, tags: 'ddc'); + dynamic result1; + dynamic result2; + expect(() { + result1 = getProps(element); + result2 = getProps(element); + }, returnsNormally); + expect(result1, containsPair('dartProp', 'dart')); + expect(result2, containsPair('dartProp', 'dart')); + }); }); } diff --git a/test/over_react_redux/value_mutation_checker_test.dart b/test/over_react_redux/value_mutation_checker_test.dart index cf041324d..7dd9ce263 100644 --- a/test/over_react_redux/value_mutation_checker_test.dart +++ b/test/over_react_redux/value_mutation_checker_test.dart @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:mockito/mockito.dart'; import 'package:over_react/over_react.dart'; import 'package:over_react/src/over_react_redux/value_mutation_checker.dart'; import 'package:react/react.dart' as react; import 'package:test/test.dart'; +import '../mockito.mocks.dart'; + // ignore_for_file: invalid_use_of_protected_member main() { group('Value Mutation Checker:', () { @@ -150,5 +151,3 @@ void sharedHashTests(InstanceHasher Function() getHasher) { }); }); } - -class MockList extends Mock implements List {} diff --git a/test/test_util/mock_classes.dart b/test/test_util/mock_classes.dart deleted file mode 100644 index 26fdbadda..000000000 --- a/test/test_util/mock_classes.dart +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 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. -library mock_classes; - -import 'dart:async'; -import 'dart:html'; - -import 'package:mockito/mockito.dart'; -import 'package:react/react.dart' as react; - -class MockTimer extends Mock implements Timer { - bool _isInstantiated = false; - void Function() _callback; - Duration _duration; - - /// Whether the timer has been initialized via [getTimerFactory]. - bool get isInstantiated => _isInstantiated; - - /// The callback the timer was initialized with. - void Function() get callback => _callback; - - /// The duration the timer was initialized with. - Duration get duration => _duration; - - /// Returns a function that initializes [mock] with the given duration and callback. - /// - /// This function will raise an [AssertionError] if called more than once. - static Function getTimerFactory(MockTimer mock) { - Timer timerFactory(Duration duration, void Function() callback) { - assert(!mock._isInstantiated); - - mock._isInstantiated = true; - mock._callback = callback; - mock._duration = duration; - - return mock; - } - - return timerFactory; - } -} - -// ignore: avoid_implementing_value_types -class MockFileList extends Mock implements FileList {} - -// ignore: avoid_implementing_value_types -class MockFile extends Mock implements File {} - -class MockFileUploadInputElement extends Mock implements FileUploadInputElement {} - -class MockSyntheticEvent extends Mock implements react.SyntheticEvent {} - -class MockSyntheticMouseEvent extends Mock implements react.SyntheticMouseEvent {} - -// ignore: avoid_implementing_value_types -class MockMouseEvent extends Mock implements MouseEvent {} diff --git a/test/vm_tests/builder/codegen_test.dart b/test/vm_tests/builder/codegen_test.dart index 89e5c654f..28eaaba47 100644 --- a/test/vm_tests/builder/codegen_test.dart +++ b/test/vm_tests/builder/codegen_test.dart @@ -24,6 +24,7 @@ import 'package:over_react/src/builder/codegen.dart'; import 'package:source_span/source_span.dart'; import 'package:test/test.dart'; +import '../../mockito.mocks.dart'; import './util.dart'; main() { diff --git a/test/vm_tests/builder/declaration_parsing_test.dart b/test/vm_tests/builder/declaration_parsing_test.dart index 804efcc05..7a638df07 100644 --- a/test/vm_tests/builder/declaration_parsing_test.dart +++ b/test/vm_tests/builder/declaration_parsing_test.dart @@ -28,6 +28,7 @@ import 'package:over_react/src/component_declaration/annotations.dart' as annota import 'package:source_span/source_span.dart'; import 'package:test/test.dart'; +import '../../mockito.mocks.dart'; import './util.dart'; import 'parsing/parsing_helpers.dart'; diff --git a/test/vm_tests/builder/parsing/error_collection.dart b/test/vm_tests/builder/parsing/error_collection.dart index aa598818c..2426293db 100644 --- a/test/vm_tests/builder/parsing/error_collection.dart +++ b/test/vm_tests/builder/parsing/error_collection.dart @@ -20,7 +20,7 @@ import 'package:over_react/src/builder/parsing.dart'; import 'package:source_span/source_span.dart'; import 'package:test/test.dart'; -import '../util.dart'; +import '../../../mockito.mocks.dart'; main() { group('error collection -', () { diff --git a/test/vm_tests/builder/util.dart b/test/vm_tests/builder/util.dart index 54b37d217..c0fe474e5 100644 --- a/test/vm_tests/builder/util.dart +++ b/test/vm_tests/builder/util.dart @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:logging/logging.dart'; -import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; const String factorySrc = '\n@Factory()\nUiFactory Foo = _\$Foo;\n'; @@ -585,9 +583,6 @@ class OverReactSrc { } } -class MockLogger extends Mock implements Logger {} - - Iterable expectLengthAndAllOfType(Iterable items, int count) { expect(items, List.generate(count, (i) => isA())); return items.cast();