Consider pubspec_overrides.yaml when publishing (#3782)
diff --git a/lib/src/command.dart b/lib/src/command.dart index 41f8343..a0e4ebf 100644 --- a/lib/src/command.dart +++ b/lib/src/command.dart
@@ -75,12 +75,7 @@ /// /// This will load the pubspec and fail with an error if the current directory /// is not a package. - late final Entrypoint entrypoint = - Entrypoint(directory, cache, withPubspecOverrides: withPubspecOverrides); - - /// Whether `pubspec_overrides.yaml` is taken into account, when creating - /// [entrypoint]. - bool get withPubspecOverrides => true; + late final Entrypoint entrypoint = Entrypoint(directory, cache); /// The URL for web documentation for this command. String? get docUrl => null;
diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart index 53cec88..bc080db 100644 --- a/lib/src/command/lish.dart +++ b/lib/src/command/lish.dart
@@ -34,8 +34,6 @@ String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-lish'; @override bool get takesArguments => false; - @override - bool get withPubspecOverrides => false; /// The URL of the server to which to upload the package. late final Uri host = () {
diff --git a/lib/src/validator/dependency_override.dart b/lib/src/validator/dependency_override.dart index 96a0b83..2fda8d5 100644 --- a/lib/src/validator/dependency_override.dart +++ b/lib/src/validator/dependency_override.dart
@@ -16,8 +16,13 @@ var overridden = MapKeySet(entrypoint.root.dependencyOverrides); var dev = MapKeySet(entrypoint.root.devDependencies); if (overridden.difference(dev).isNotEmpty) { - warnings.add(''' -Your pubspec.yaml is overriding non-dev dependencies. + final overridesFile = + entrypoint.root.pubspec.dependencyOverridesFromOverridesFile + ? entrypoint.pubspecOverridesPath + : entrypoint.pubspecPath; + + hints.add(''' +Non-dev dependencies are overridden in $overridesFile. This indicates you are not testing your package against the same versions of its dependencies that users will have when they use it.
diff --git a/test/descriptor.dart b/test/descriptor.dart index 6a2ec60..be962c1 100644 --- a/test/descriptor.dart +++ b/test/descriptor.dart
@@ -31,9 +31,12 @@ TarFileDescriptor tar(String name, [List<Descriptor>? contents]) => TarFileDescriptor(name, contents ?? <Descriptor>[]); +FileDescriptor validPubspec({Map<String, Object?>? extras}) => + libPubspec('test_pkg', '1.0.0', sdk: '>=3.1.2 <=3.2.0', extras: extras); + /// Describes a package that passes all validation. DirectoryDescriptor get validPackage => dir(appPath, [ - libPubspec('test_pkg', '1.0.0', sdk: '>=3.1.2 <=3.2.0'), + validPubspec(), file('LICENSE', 'Eh, do what you want.'), file('README.md', "This package isn't real."), file('CHANGELOG.md', '# 1.0.0\nFirst version\n'), @@ -102,13 +105,13 @@ Map? deps, Map? devDeps, String? sdk, - Map<String, Object> extras = const {}, + Map<String, Object?>? extras, }) { var map = packageMap(name, version, deps, devDeps); if (sdk != null) { map['environment'] = {'sdk': sdk}; } - return pubspec({...map, ...extras}); + return pubspec({...map, ...extras ?? {}}); } /// Describes a file named `pubspec_overrides.yaml` by default, with the given
diff --git a/test/validator/changelog_test.dart b/test/validator/changelog_test.dart index 397723d..bf274d2 100644 --- a/test/validator/changelog_test.dart +++ b/test/validator/changelog_test.dart
@@ -26,7 +26,7 @@ * Passes Turing test. '''), ]).create(); - await expectValidation(changelog); + await expectValidationDeprecated(changelog); }); }); @@ -35,7 +35,7 @@ await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0'), ]).create(); - await expectValidation(changelog, warnings: isNotEmpty); + await expectValidationDeprecated(changelog, warnings: isNotEmpty); }); test('has has a CHANGELOG not named CHANGELOG.md', () async { @@ -48,7 +48,7 @@ * Passes Turing test. '''), ]).create(); - await expectValidation(changelog, warnings: isNotEmpty); + await expectValidationDeprecated(changelog, warnings: isNotEmpty); }); test('has a CHANGELOG that doesn\'t include the current package version', @@ -62,7 +62,7 @@ * Passes Turing test. '''), ]).create(); - await expectValidation(changelog, warnings: isNotEmpty); + await expectValidationDeprecated(changelog, warnings: isNotEmpty); }); test('has a CHANGELOG with invalid utf-8', () async { @@ -70,7 +70,7 @@ d.libPubspec('test_pkg', '1.0.0'), d.file('CHANGELOG.md', [192]), ]).create(); - await expectValidation(changelog, warnings: isNotEmpty); + await expectValidationDeprecated(changelog, warnings: isNotEmpty); }); }); }
diff --git a/test/validator/compiled_dartdoc_test.dart b/test/validator/compiled_dartdoc_test.dart index da02005..1958d91 100644 --- a/test/validator/compiled_dartdoc_test.dart +++ b/test/validator/compiled_dartdoc_test.dart
@@ -16,7 +16,7 @@ setUp(d.validPackage.create); group('should consider a package valid if it', () { - test('looks normal', () => expectValidation(compiledDartdoc)); + test('looks normal', () => expectValidationDeprecated(compiledDartdoc)); test('has most but not all files from compiling dartdoc', () async { await d.dir(appPath, [ @@ -27,7 +27,7 @@ d.file('dart-logo-small.png', '') ]) ]).create(); - await expectValidation(compiledDartdoc); + await expectValidationDeprecated(compiledDartdoc); }); test('contains compiled dartdoc in a hidden directory', () async { @@ -42,7 +42,7 @@ d.file('client-live-nav.js', '') ]) ]).create(); - await expectValidation(compiledDartdoc); + await expectValidationDeprecated(compiledDartdoc); }); test('contains compiled dartdoc in a gitignored directory', () async { @@ -58,7 +58,7 @@ ]), d.file('.gitignore', '/doc-out') ]).create(); - await expectValidation(compiledDartdoc); + await expectValidationDeprecated(compiledDartdoc); }); }); @@ -74,7 +74,7 @@ ]) ]).create(); - await expectValidation(compiledDartdoc, warnings: isNotEmpty); + await expectValidationDeprecated(compiledDartdoc, warnings: isNotEmpty); }); }); }
diff --git a/test/validator/dependency_override_test.dart b/test/validator/dependency_override_test.dart index 60af87d..7145b3f 100644 --- a/test/validator/dependency_override_test.dart +++ b/test/validator/dependency_override_test.dart
@@ -2,56 +2,93 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:pub/src/validator.dart'; -import 'package:pub/src/validator/dependency_override.dart'; import 'package:test/test.dart'; import '../descriptor.dart' as d; import '../test_pub.dart'; import 'utils.dart'; -Validator dependencyOverride() => DependencyOverrideValidator(); - void main() { test( 'should consider a package valid if it has dev dependency ' 'overrides', () async { + final server = await servePackages(); + server.serve('foo', '3.0.0'); + await d.validPackage.create(); + await d.dir(appPath, [ - d.pubspec({ - 'name': 'myapp', - 'dev_dependencies': {'foo': '1.0.0'}, - 'dependency_overrides': {'foo': '<3.0.0'} - }) + d.validPubspec( + extras: { + 'dev_dependencies': {'foo': '^1.0.0'}, + 'dependency_overrides': {'foo': '^3.0.0'} + }, + ) ]).create(); - await expectValidation(dependencyOverride); + await expectValidation(); }); group('should consider a package invalid if', () { test('it has only non-dev dependency overrides', () async { + final server = await servePackages(); + server.serve('foo', '3.0.0'); + await d.validPackage.create(); + await d.dir(appPath, [ - d.pubspec({ - 'name': 'myapp', - 'dependency_overrides': {'foo': '<3.0.0'} - }) + d.validPubspec( + extras: { + 'dependencies': {'foo': '^1.0.0'}, + 'dependency_overrides': {'foo': '^3.0.0'} + }, + ) ]).create(); - await expectValidation(dependencyOverride, warnings: isNotEmpty); + await expectValidationHint( + contains('Non-dev dependencies are overridden in pubspec.yaml.'), + ); + }); + test('it has a pubspec_overrides.yaml', () async { + final server = await servePackages(); + server.serve('foo', '3.0.0'); + await d.validPackage.create(); + + await d.dir(appPath, [ + d.validPubspec( + extras: { + 'dependencies': {'foo': '^1.0.0'}, + }, + ), + d.pubspecOverrides({ + 'dependency_overrides': {'foo': '3.0.0'} + }), + ]).create(); + + await expectValidationHint( + 'Non-dev dependencies are overridden in pubspec_overrides.yaml.', + ); }); test('it has any non-dev dependency overrides', () async { + final server = await servePackages(); + server.serve('foo', '3.0.0'); + server.serve('bar', '3.0.0'); + + await d.validPackage.create(); await d.dir(appPath, [ - d.pubspec({ - 'name': 'myapp', - 'dev_dependencies': {'foo': '1.0.0'}, - 'dependency_overrides': { - 'foo': '<3.0.0', - 'bar': '>3.0.0', - } - }) + d.validPubspec( + extras: { + 'dev_dependencies': {'foo': '^1.0.0'}, + 'dependency_overrides': { + 'foo': '^3.0.0', + 'bar': '^3.0.0', + } + }, + ) ]).create(); - await expectValidation(dependencyOverride, warnings: isNotEmpty); + await expectValidationHint( + 'Non-dev dependencies are overridden in pubspec.yaml.', + ); }); }); }
diff --git a/test/validator/deprecated_fields_test.dart b/test/validator/deprecated_fields_test.dart index 8fa6820..0206701 100644 --- a/test/validator/deprecated_fields_test.dart +++ b/test/validator/deprecated_fields_test.dart
@@ -17,7 +17,7 @@ test( 'should not warn if neither transformers or web is included', - () => expectValidation(deprecatedFields), + () => expectValidationDeprecated(deprecatedFields), ); test('should warn if pubspec has a transformers section', () async { @@ -27,7 +27,7 @@ }) ]).create(); - await expectValidation(deprecatedFields, warnings: isNotEmpty); + await expectValidationDeprecated(deprecatedFields, warnings: isNotEmpty); }); test('should warn if pubspec has a web section', () async { @@ -37,7 +37,7 @@ }) ]).create(); - await expectValidation(deprecatedFields, warnings: isNotEmpty); + await expectValidationDeprecated(deprecatedFields, warnings: isNotEmpty); }); test('should warn if pubspec has an author', () async { @@ -45,7 +45,7 @@ d.pubspec({'author': 'Ronald <ronald@example.com>'}) ]).create(); - await expectValidation(deprecatedFields, warnings: isNotEmpty); + await expectValidationDeprecated(deprecatedFields, warnings: isNotEmpty); }); test('should warn if pubspec has a list of authors', () async { @@ -55,6 +55,6 @@ }) ]).create(); - await expectValidation(deprecatedFields, warnings: isNotEmpty); + await expectValidationDeprecated(deprecatedFields, warnings: isNotEmpty); }); }
diff --git a/test/validator/directory_test.dart b/test/validator/directory_test.dart index 1ccebde..d1eedf8 100644 --- a/test/validator/directory_test.dart +++ b/test/validator/directory_test.dart
@@ -16,7 +16,7 @@ group('should consider a package valid if it', () { setUp(d.validPackage.create); - test('looks normal', () => expectValidation(directory)); + test('looks normal', () => expectValidationDeprecated(directory)); test('has a nested directory named "tools"', () async { await d.dir(appPath, [ @@ -24,7 +24,7 @@ d.dir('tools', [d.file('empty')]) ]) ]).create(); - await expectValidation(directory); + await expectValidationDeprecated(directory); }); test('is pubignoring the folder', () async { @@ -34,7 +34,7 @@ d.dir('tools', [d.file('empty')]) ]) ]).create(); - await expectValidation(directory); + await expectValidationDeprecated(directory); }); }); @@ -58,7 +58,7 @@ await d.dir(appPath, [ d.dir(name, [d.file('empty')]) ]).create(); - await expectValidation(directory, warnings: isNotEmpty); + await expectValidationDeprecated(directory, warnings: isNotEmpty); }); } });
diff --git a/test/validator/executable_test.dart b/test/validator/executable_test.dart index c1f2a50..497c16b 100644 --- a/test/validator/executable_test.dart +++ b/test/validator/executable_test.dart
@@ -28,7 +28,7 @@ d.file('two.dart', "main() => print('ok');") ]) ]).create(); - await expectValidation(executable); + await expectValidationDeprecated(executable); }); }); @@ -41,7 +41,7 @@ 'executables': {'nope': 'not_there', 'nada': null} }) ]).create(); - await expectValidation(executable, warnings: isNotEmpty); + await expectValidationDeprecated(executable, warnings: isNotEmpty); }); test('has .gitignored one or more listed executables', () async { @@ -57,7 +57,7 @@ ]), d.file('.gitignore', 'bin') ]).create(); - await expectValidation(executable, warnings: isNotEmpty); + await expectValidationDeprecated(executable, warnings: isNotEmpty); }); }); }
diff --git a/test/validator/flutter_plugin_format_test.dart b/test/validator/flutter_plugin_format_test.dart index 526944b..de06aa3 100644 --- a/test/validator/flutter_plugin_format_test.dart +++ b/test/validator/flutter_plugin_format_test.dart
@@ -16,7 +16,7 @@ group('should consider a package valid if it', () { test('is not a plugin', () async { await d.validPackage.create(); - return expectValidation(flutterPluginFormat); + return expectValidationDeprecated(flutterPluginFormat); }); test('is a Flutter 1.9.0 package', () async { @@ -27,7 +27,7 @@ 'flutter': '>=1.9.0 <2.0.0', }); await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation(flutterPluginFormat); + await expectValidationDeprecated(flutterPluginFormat); }); test('is a Flutter 1.10.0 package', () async { @@ -38,7 +38,7 @@ 'flutter': '>=1.10.0 <2.0.0', }); await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation(flutterPluginFormat); + await expectValidationDeprecated(flutterPluginFormat); }); test('is a Flutter 1.10.0-0 package', () async { @@ -49,7 +49,7 @@ 'flutter': '>=1.10.0-0 <2.0.0', }); await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation(flutterPluginFormat); + await expectValidationDeprecated(flutterPluginFormat); }); test('is a flutter 1.10.0 plugin with the new format', () async { @@ -70,7 +70,7 @@ }, }; await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation(flutterPluginFormat); + await expectValidationDeprecated(flutterPluginFormat); }); }); @@ -96,7 +96,7 @@ }, }; await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation( + await expectValidationDeprecated( flutterPluginFormat, errors: contains( contains( @@ -121,7 +121,7 @@ }, }; await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation( + await expectValidationDeprecated( flutterPluginFormat, errors: contains( contains('Instead use the flutter.plugin.platforms key'), @@ -147,7 +147,7 @@ }, }; await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation( + await expectValidationDeprecated( flutterPluginFormat, errors: contains( contains( @@ -176,7 +176,7 @@ }, }; await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation( + await expectValidationDeprecated( flutterPluginFormat, errors: contains( contains( @@ -201,7 +201,7 @@ }, }; await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation( + await expectValidationDeprecated( flutterPluginFormat, errors: contains( contains( @@ -229,7 +229,7 @@ }, }; await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation( + await expectValidationDeprecated( flutterPluginFormat, errors: contains( contains( @@ -257,7 +257,7 @@ }, }; await d.dir(appPath, [d.pubspec(pkg), d.dir('ios')]).create(); - await expectValidation( + await expectValidationDeprecated( flutterPluginFormat, errors: contains( contains(
diff --git a/test/validator/language_version_test.dart b/test/validator/language_version_test.dart index dea8444..cfcb0d8 100644 --- a/test/validator/language_version_test.dart +++ b/test/validator/language_version_test.dart
@@ -35,7 +35,7 @@ group('should consider a package valid if it', () { test('has no library-level language version annotations', () async { await setup(sdkConstraint: '>=2.4.0 <3.0.0'); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test('opts in to older language versions', () async { @@ -44,7 +44,7 @@ libraryLanguageVersion: '2.0', ); await d.dir(appPath, []).create(); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test('opts in to same language versions', () async { await setup( @@ -52,14 +52,14 @@ libraryLanguageVersion: '2.4', ); await d.dir(appPath, []).create(); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test('opts in to older language version, with non-range constraint', () async { await setup(sdkConstraint: '2.7.0', libraryLanguageVersion: '2.3'); await d.dir(appPath, []).create(); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); }); @@ -69,11 +69,11 @@ sdkConstraint: '>=2.4.1 <3.0.0', libraryLanguageVersion: '2.5', ); - await expectValidation(validator, errors: isNotEmpty); + await expectValidationDeprecated(validator, errors: isNotEmpty); }); test('opts in to a newer version, with non-range constraint.', () async { await setup(sdkConstraint: '2.7.0', libraryLanguageVersion: '2.8'); - await expectValidation(validator, errors: isNotEmpty); + await expectValidationDeprecated(validator, errors: isNotEmpty); }); }); }
diff --git a/test/validator/leak_detection_test.dart b/test/validator/leak_detection_test.dart index f16e145..a890658 100644 --- a/test/validator/leak_detection_test.dart +++ b/test/validator/leak_detection_test.dart
@@ -25,7 +25,7 @@ '''), ]) ]).create(); - await expectValidation(leakDetection); + await expectValidationDeprecated(leakDetection); }); test('contains a source file listed in false_secrets', () async { @@ -43,7 +43,7 @@ '''), ]) ]).create(); - await expectValidation(leakDetection, errors: isEmpty); + await expectValidationDeprecated(leakDetection, errors: isEmpty); }); }); @@ -57,7 +57,7 @@ '''), ]) ]).create(); - await expectValidation(leakDetection, errors: isNotEmpty); + await expectValidationDeprecated(leakDetection, errors: isNotEmpty); }); }); @@ -82,7 +82,7 @@ '''), ]) ]).create(); - await expectValidation( + await expectValidationDeprecated( leakDetection, errors: allOf( hasLength(lessThanOrEqualTo(3)), @@ -117,7 +117,7 @@ '''), ]) ]).create(); - await expectValidation( + await expectValidationDeprecated( leakDetection, errors: allOf( hasLength(lessThanOrEqualTo(3)),
diff --git a/test/validator/license_test.dart b/test/validator/license_test.dart index 17aeec5..73ab202 100644 --- a/test/validator/license_test.dart +++ b/test/validator/license_test.dart
@@ -18,13 +18,13 @@ group('should consider a package valid if it', () { test('looks normal', () async { await d.validPackage.create(); - await expectValidation(license); + await expectValidationDeprecated(license); }); test('has both LICENSE and UNLICENSE file', () async { await d.validPackage.create(); await d.file(path.join(appPath, 'UNLICENSE'), '').create(); - await expectValidation(license); + await expectValidationDeprecated(license); }); }); @@ -33,28 +33,28 @@ await d.validPackage.create(); deleteEntry(path.join(d.sandbox, appPath, 'LICENSE')); await d.file(path.join(appPath, 'COPYING'), '').create(); - await expectValidation(license, warnings: isNotEmpty); + await expectValidationDeprecated(license, warnings: isNotEmpty); }); test('has only an UNLICENSE file', () async { await d.validPackage.create(); deleteEntry(path.join(d.sandbox, appPath, 'LICENSE')); await d.file(path.join(appPath, 'UNLICENSE'), '').create(); - await expectValidation(license, warnings: isNotEmpty); + await expectValidationDeprecated(license, warnings: isNotEmpty); }); test('has only a prefixed LICENSE file', () async { await d.validPackage.create(); deleteEntry(path.join(d.sandbox, appPath, 'LICENSE')); await d.file(path.join(appPath, 'MIT_LICENSE'), '').create(); - await expectValidation(license, warnings: isNotEmpty); + await expectValidationDeprecated(license, warnings: isNotEmpty); }); test('has only a suffixed LICENSE file', () async { await d.validPackage.create(); deleteEntry(path.join(d.sandbox, appPath, 'LICENSE')); await d.file(path.join(appPath, 'LICENSE.md'), '').create(); - await expectValidation(license, warnings: isNotEmpty); + await expectValidationDeprecated(license, warnings: isNotEmpty); }); }); @@ -62,21 +62,21 @@ test('has no LICENSE file', () async { await d.validPackage.create(); deleteEntry(path.join(d.sandbox, appPath, 'LICENSE')); - await expectValidation(license, errors: isNotEmpty); + await expectValidationDeprecated(license, errors: isNotEmpty); }); test('has a prefixed UNLICENSE file', () async { await d.validPackage.create(); deleteEntry(path.join(d.sandbox, appPath, 'LICENSE')); await d.file(path.join(appPath, 'MIT_UNLICENSE'), '').create(); - await expectValidation(license, errors: isNotEmpty); + await expectValidationDeprecated(license, errors: isNotEmpty); }); test('has a .gitignored LICENSE file', () async { var repo = d.git(appPath, [d.file('.gitignore', 'LICENSE')]); await d.validPackage.create(); await repo.create(); - await expectValidation(license, errors: isNotEmpty); + await expectValidationDeprecated(license, errors: isNotEmpty); }); }); }
diff --git a/test/validator/name_test.dart b/test/validator/name_test.dart index 2e8f8af..267cdd6 100644 --- a/test/validator/name_test.dart +++ b/test/validator/name_test.dart
@@ -18,7 +18,7 @@ group('should consider a package valid if it', () { setUp(d.validPackage.create); - test('looks normal', () => expectValidation(name)); + test('looks normal', () => expectValidationDeprecated(name)); test('has dots in potential library names', () async { await d.dir(appPath, [ @@ -28,7 +28,7 @@ d.file('test_pkg.g.dart', 'int j = 2;') ]) ]).create(); - await expectValidation(name); + await expectValidationDeprecated(name); }); test('has a name that starts with an underscore', () async { @@ -36,7 +36,7 @@ d.libPubspec('_test_pkg', '1.0.0'), d.dir('lib', [d.file('_test_pkg.dart', 'int i = 1;')]) ]).create(); - await expectValidation(name); + await expectValidationDeprecated(name); }); }); @@ -45,7 +45,7 @@ test('has a package name that contains upper-case letters', () async { await d.dir(appPath, [d.libPubspec('TestPkg', '1.0.0')]).create(); - await expectValidation(name, warnings: isNotEmpty); + await expectValidationDeprecated(name, warnings: isNotEmpty); }); test('has a single library named differently than the package', () async { @@ -53,7 +53,7 @@ await d.dir(appPath, [ d.dir('lib', [d.file('best_pkg.dart', 'int i = 0;')]) ]).create(); - await expectValidation(name, warnings: isNotEmpty); + await expectValidationDeprecated(name, warnings: isNotEmpty); }); }); }
diff --git a/test/validator/pubspec_field_test.dart b/test/validator/pubspec_field_test.dart index cd205ef..1637115 100644 --- a/test/validator/pubspec_field_test.dart +++ b/test/validator/pubspec_field_test.dart
@@ -16,14 +16,14 @@ group('should consider a package valid if it', () { setUp(d.validPackage.create); - test('looks normal', () => expectValidation(pubspecField)); + test('looks normal', () => expectValidationDeprecated(pubspecField)); test('has an HTTPS homepage URL', () async { var pkg = packageMap('test_pkg', '1.0.0'); pkg['homepage'] = 'https://pub.dev'; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField); + await expectValidationDeprecated(pubspecField); }); test('has an HTTPS repository URL instead of homepage', () async { @@ -32,7 +32,7 @@ pkg['repository'] = 'https://pub.dev'; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField); + await expectValidationDeprecated(pubspecField); }); test('has an HTTPS documentation URL', () async { @@ -40,7 +40,7 @@ pkg['documentation'] = 'https://pub.dev'; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField); + await expectValidationDeprecated(pubspecField); }); test('has empty executables', () async { @@ -48,7 +48,7 @@ pkg['executables'] = <String, String>{}; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField); + await expectValidationDeprecated(pubspecField); }); test('has executables', () async { @@ -59,7 +59,7 @@ }; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField); + await expectValidationDeprecated(pubspecField); }); }); @@ -70,7 +70,7 @@ pkg.remove('homepage'); await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, warnings: isNotEmpty); + await expectValidationDeprecated(pubspecField, warnings: isNotEmpty); }); }); @@ -82,7 +82,7 @@ pkg.remove('description'); await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); test('has a non-string "homepage" field', () async { @@ -90,7 +90,7 @@ pkg['homepage'] = 12; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); test('has a non-string "repository" field', () async { @@ -98,7 +98,7 @@ pkg['repository'] = 12; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); test('has a non-string "description" field', () async { @@ -106,7 +106,7 @@ pkg['description'] = 12; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); test('has a non-HTTP homepage URL', () async { @@ -114,7 +114,7 @@ pkg['homepage'] = 'file:///foo/bar'; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); test('has a non-HTTP documentation URL', () async { @@ -122,7 +122,7 @@ pkg['documentation'] = 'file:///foo/bar'; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); test('has a non-HTTP repository URL', () async { @@ -130,7 +130,7 @@ pkg['repository'] = 'file:///foo/bar'; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); test('has invalid executables', () async { @@ -138,7 +138,7 @@ pkg['executables'] = <String>['wrong-thing']; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); test('has invalid executables mapping to a number', () async { @@ -148,7 +148,7 @@ }; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await expectValidation(pubspecField, errors: isNotEmpty); + await expectValidationDeprecated(pubspecField, errors: isNotEmpty); }); }); }
diff --git a/test/validator/pubspec_test.dart b/test/validator/pubspec_test.dart index c7e1272..fd1a255 100644 --- a/test/validator/pubspec_test.dart +++ b/test/validator/pubspec_test.dart
@@ -13,7 +13,7 @@ test('should consider a package valid if it has a pubspec', () async { await d.validPackage.create(); - await expectValidation(PubspecValidator.new); + await expectValidationDeprecated(PubspecValidator.new); }); test('should consider a package invalid if it has a .gitignored pubspec', @@ -22,6 +22,6 @@ await d.validPackage.create(); await repo.create(); - await expectValidation(PubspecValidator.new, errors: isNotEmpty); + await expectValidationDeprecated(PubspecValidator.new, errors: isNotEmpty); }); }
diff --git a/test/validator/pubspec_typo_test.dart b/test/validator/pubspec_typo_test.dart index ebfacd9..bc784d4 100644 --- a/test/validator/pubspec_typo_test.dart +++ b/test/validator/pubspec_typo_test.dart
@@ -16,7 +16,7 @@ group('should consider a package valid if it', () { setUp(d.validPackage.create); - test('looks normal', () => expectValidation(pubspecTypo)); + test('looks normal', () => expectValidationDeprecated(pubspecTypo)); test('has no typos', () async { await d.dir(appPath, [ @@ -38,7 +38,7 @@ }) ]).create(); - await expectValidation(pubspecTypo); + await expectValidationDeprecated(pubspecTypo); }); test('has different keys which are likely not typos', () async { @@ -52,7 +52,7 @@ }) ]).create(); - await expectValidation(pubspecTypo); + await expectValidationDeprecated(pubspecTypo); }); }); @@ -67,7 +67,7 @@ }) ]).create(); - await expectValidation(pubspecTypo, warnings: isNotEmpty); + await expectValidationDeprecated(pubspecTypo, warnings: isNotEmpty); }); test('contains typos but does not issue too many warnings', () async { @@ -82,7 +82,7 @@ }) ]).create(); - await expectValidation( + await expectValidationDeprecated( pubspecTypo, warnings: hasLength(lessThanOrEqualTo(3)), );
diff --git a/test/validator/readme_test.dart b/test/validator/readme_test.dart index 934feae..67fb79e 100644 --- a/test/validator/readme_test.dart +++ b/test/validator/readme_test.dart
@@ -18,7 +18,7 @@ group('should consider a package valid if it', () { test('looks normal', () async { await d.validPackage.create(); - await expectValidation(readme); + await expectValidationDeprecated(readme); }); test('has a non-primary readme with invalid utf-8', () async { @@ -26,7 +26,7 @@ await d.dir(appPath, [ d.file('README.x.y.z', [192]) ]).create(); - await expectValidation(readme); + await expectValidationDeprecated(readme); }); test('has a gitignored README with invalid utf-8', () async { @@ -36,7 +36,7 @@ d.file('.gitignore', 'README') ]); await repo.create(); - await expectValidation(readme); + await expectValidationDeprecated(readme); }); }); @@ -45,13 +45,13 @@ await d.validPackage.create(); deleteEntry(p.join(d.sandbox, 'myapp/README.md')); - await expectValidation(readme, warnings: isNotEmpty); + await expectValidationDeprecated(readme, warnings: isNotEmpty); }); test('has only a .gitignored README', () async { await d.validPackage.create(); await d.git(appPath, [d.file('.gitignore', 'README.md')]).create(); - await expectValidation(readme, warnings: isNotEmpty); + await expectValidationDeprecated(readme, warnings: isNotEmpty); }); test('has a primary README with invalid utf-8', () async { @@ -59,28 +59,28 @@ await d.dir(appPath, [ d.file('README', [192]) ]).create(); - await expectValidation(readme, warnings: isNotEmpty); + await expectValidationDeprecated(readme, warnings: isNotEmpty); }); test('has only a non-primary readme', () async { await d.validPackage.create(); deleteEntry(p.join(d.sandbox, 'myapp/README.md')); await d.dir(appPath, [d.file('README.whatever')]).create(); - await expectValidation(readme, warnings: isNotEmpty); + await expectValidationDeprecated(readme, warnings: isNotEmpty); }); test('Uses only deprecated readme name .markdown', () async { await d.validPackage.create(); deleteEntry(p.join(d.sandbox, 'myapp/README.md')); await d.dir(appPath, [d.file('README.markdown')]).create(); - await expectValidation(readme, warnings: isNotEmpty); + await expectValidationDeprecated(readme, warnings: isNotEmpty); }); test('Uses only deprecated readme name .mdown', () async { await d.validPackage.create(); deleteEntry(p.join(d.sandbox, 'myapp/README.md')); await d.dir(appPath, [d.file('README.mdown')]).create(); - await expectValidation(readme, warnings: isNotEmpty); + await expectValidationDeprecated(readme, warnings: isNotEmpty); }); }); }
diff --git a/test/validator/relative_version_numbering_test.dart b/test/validator/relative_version_numbering_test.dart index 9a8bca0..34dc23d 100644 --- a/test/validator/relative_version_numbering_test.dart +++ b/test/validator/relative_version_numbering_test.dart
@@ -39,7 +39,7 @@ ); await setup(sdkConstraint: '>=2.9.0 <3.0.0'); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test( @@ -62,7 +62,7 @@ ); await setup(sdkConstraint: '>=2.9.0 <3.0.0'); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test('is opting in to null-safety with previous null-safe version', @@ -77,7 +77,7 @@ ); await setup(sdkConstraint: '>=2.12.0 <3.0.0'); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test( @@ -93,7 +93,7 @@ ); await setup(sdkConstraint: '>=2.12.0-dev <3.0.0'); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test( @@ -116,20 +116,20 @@ ); await setup(sdkConstraint: '>=2.12.0 <3.0.0'); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test('is opting in to null-safety with no existing versions', () async { await setup(sdkConstraint: '>=2.12.0 <3.0.0'); await servePackages(); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test('is not opting in to null-safety with no existing versions', () async { await setup(sdkConstraint: '>=2.9.0 <3.0.0'); await servePackages(); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test( @@ -152,7 +152,7 @@ ); await setup(sdkConstraint: '>=2.9.0 <3.0.0'); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); test( @@ -175,7 +175,7 @@ ); await setup(sdkConstraint: '>=2.12.0 <3.0.0'); - await expectValidation(validator); + await expectValidationDeprecated(validator); }); }); @@ -192,7 +192,7 @@ ); await setup(sdkConstraint: '>=2.12.0 <3.0.0'); - await expectValidation(validator, hints: isNotEmpty); + await expectValidationDeprecated(validator, hints: isNotEmpty); }); test( @@ -208,7 +208,7 @@ }, ); - await expectValidation(validator, hints: isNotEmpty); + await expectValidationDeprecated(validator, hints: isNotEmpty); }); test( 'is not opting in to null-safety with previous non-null-safe stable version. ' @@ -230,7 +230,7 @@ ); await setup(sdkConstraint: '>=2.9.0 <3.0.0'); - await expectValidation(validator, hints: isNotEmpty); + await expectValidationDeprecated(validator, hints: isNotEmpty); }); test( @@ -253,7 +253,7 @@ ); await setup(sdkConstraint: '>=2.12.0 <3.0.0'); - await expectValidation(validator, hints: isNotEmpty); + await expectValidationDeprecated(validator, hints: isNotEmpty); }); test('is not opting in to null-safety with previous null-safe version', @@ -268,7 +268,7 @@ ); await setup(sdkConstraint: '>=2.9.0 <3.0.0'); - await expectValidation(validator, hints: isNotEmpty); + await expectValidationDeprecated(validator, hints: isNotEmpty); }); test( @@ -291,7 +291,7 @@ ); await setup(sdkConstraint: '>=2.9.0 <3.0.0'); - await expectValidation(validator, hints: isNotEmpty); + await expectValidationDeprecated(validator, hints: isNotEmpty); }); test( @@ -314,7 +314,7 @@ ); await setup(sdkConstraint: '>=2.12.0 <3.0.0'); - await expectValidation(validator, hints: isNotEmpty); + await expectValidationDeprecated(validator, hints: isNotEmpty); }); test( @@ -329,7 +329,7 @@ 'environment': {'sdk': '>=2.9.0<3.0.0'} }, ); - await expectValidation(validator, hints: isNotEmpty); + await expectValidationDeprecated(validator, hints: isNotEmpty); }); }); }
diff --git a/test/validator/sdk_constraint_test.dart b/test/validator/sdk_constraint_test.dart index 4fb8f35..4f0e2db 100644 --- a/test/validator/sdk_constraint_test.dart +++ b/test/validator/sdk_constraint_test.dart
@@ -16,7 +16,7 @@ group('should consider a package valid if it', () { test('has no SDK constraint', () async { await d.validPackage.create(); - await expectValidation(sdkConstraint); + await expectValidationDeprecated(sdkConstraint); }); test('has an SDK constraint without ^', () async { @@ -24,7 +24,7 @@ appPath, [d.libPubspec('test_pkg', '1.0.0', sdk: '>=1.8.0 <2.0.0')], ).create(); - await expectValidation(sdkConstraint); + await expectValidationDeprecated(sdkConstraint); }); test('has an SDK constraint with ^', () async { @@ -32,14 +32,14 @@ appPath, [d.libPubspec('test_pkg', '1.0.0', sdk: '^1.8.0')], ).create(); - await expectValidation(sdkConstraint); + await expectValidationDeprecated(sdkConstraint); }); test('depends on a pre-release Dart SDK from a pre-release', () async { await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0-dev.1', sdk: '>=1.8.0-dev.1 <2.0.0') ]).create(); - await expectValidation(sdkConstraint); + await expectValidationDeprecated(sdkConstraint); }); test( @@ -52,7 +52,7 @@ 'environment': {'sdk': '>=1.19.0 <2.0.0', 'flutter': '^1.2.3'} }) ]).create(); - await expectValidation(sdkConstraint); + await expectValidationDeprecated(sdkConstraint); }); test( @@ -65,7 +65,7 @@ 'environment': {'sdk': '>=2.0.0-dev.51.0 <2.0.0', 'fuchsia': '^1.2.3'} }) ]).create(); - await expectValidation(sdkConstraint); + await expectValidationDeprecated(sdkConstraint); }); }); @@ -75,7 +75,7 @@ appPath, [d.libPubspec('test_pkg', '1.0.0', sdk: '>=1.8.0')], ).create(); - await expectValidation( + await expectValidationDeprecated( sdkConstraint, errors: anyElement(contains('should have an upper bound constraint')), ); @@ -88,7 +88,7 @@ 'version': '1.0.0', }), ]).create(); - await expectValidation( + await expectValidationDeprecated( sdkConstraint, errors: anyElement(contains('should have an upper bound constraint')), ); @@ -104,7 +104,7 @@ 'environment': {'sdk': '>=1.18.0 <1.50.0', 'flutter': '^1.2.3'} }) ]).create(); - await expectValidation( + await expectValidationDeprecated( sdkConstraint, errors: anyElement(contains('">=1.19.0 <1.50.0"')), ); @@ -118,7 +118,7 @@ 'environment': {'flutter': '^1.2.3'} }) ]).create(); - await expectValidation( + await expectValidationDeprecated( sdkConstraint, errors: anyElement(contains('"^1.19.0"')), ); @@ -134,7 +134,7 @@ 'environment': {'sdk': '>=2.0.0-dev.50.0 <2.0.0', 'fuchsia': '^1.2.3'} }) ]).create(); - await expectValidation( + await expectValidationDeprecated( sdkConstraint, errors: anyElement(contains('"^2.0.0"')), ); @@ -148,7 +148,7 @@ 'environment': {'fuchsia': '^1.2.3'} }) ]).create(); - await expectValidation( + await expectValidationDeprecated( sdkConstraint, errors: anyElement(contains('"^2.0.0"')), ); @@ -158,7 +158,7 @@ await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0', sdk: '>=1.8.0-dev.1 <2.0.0') ]).create(); - await expectValidation( + await expectValidationDeprecated( sdkConstraint, warnings: anyElement( contains( @@ -178,7 +178,7 @@ 'environment': {'sdk': '^2.19.0'} }) ]).create(); - await expectValidation( + await expectValidationDeprecated( sdkConstraint, hints: anyElement( '''
diff --git a/test/validator/size_test.dart b/test/validator/size_test.dart index 9ee5013..4f179c8 100644 --- a/test/validator/size_test.dart +++ b/test/validator/size_test.dart
@@ -12,7 +12,7 @@ import 'utils.dart'; Future<void> expectSizeValidationError(Matcher matcher) async { - await expectValidation( + await expectValidationDeprecated( SizeValidator.new, size: 100 * (1 << 20) + 1, errors: contains(matcher), @@ -23,8 +23,8 @@ test('considers a package valid if it is <= 100 MB', () async { await d.validPackage.create(); - await expectValidation(SizeValidator.new, size: 100); - await expectValidation(SizeValidator.new, size: 100 * (1 << 20)); + await expectValidationDeprecated(SizeValidator.new, size: 100); + await expectValidationDeprecated(SizeValidator.new, size: 100 * (1 << 20)); }); group('considers a package invalid if it is more than 100 MB', () {
diff --git a/test/validator/strict_dependencies_test.dart b/test/validator/strict_dependencies_test.dart index 436c0d8..26e8d7e 100644 --- a/test/validator/strict_dependencies_test.dart +++ b/test/validator/strict_dependencies_test.dart
@@ -17,7 +17,7 @@ group('should consider a package valid if it', () { setUp(d.validPackage.create); - test('looks normal', () => expectValidation(strictDeps)); + test('looks normal', () => expectValidationDeprecated(strictDeps)); test('declares an "import" as a dependency in lib/', () async { await d.dir(appPath, [ @@ -34,7 +34,7 @@ ]), ]).create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('declares an "export" as a dependency in lib/', () async { @@ -52,7 +52,7 @@ ]), ]).create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('declares an "import" as a dependency in bin/', () async { @@ -70,7 +70,7 @@ ]), ]).create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); for (var port in ['import', 'export']) { @@ -102,7 +102,7 @@ ]), ]).create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); } } @@ -115,7 +115,7 @@ import 'dart:typed_data'; ''').create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('imports itself', () async { @@ -123,7 +123,7 @@ import 'package:test_pkg/test_pkg.dart'; ''').create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('has a relative import', () async { @@ -131,7 +131,7 @@ import 'some/relative/path.dart'; ''').create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('has an absolute import', () async { @@ -139,7 +139,7 @@ import 'file://shared/some/library.dart'; ''').create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('has a parse error preventing reading directives', () async { @@ -147,7 +147,7 @@ import not_supported_keyword 'dart:async'; ''').create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('has a top-level Dart file with an invalid dependency', () async { @@ -155,7 +155,7 @@ import 'package:'; ''').create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('has a Dart-like file with an invalid dependency', () async { @@ -163,7 +163,7 @@ import 'package:'; ''').create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('has analysis_options.yaml that excludes files', () async { @@ -197,7 +197,7 @@ '''), ]).create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); test('has lib/analysis_options.yaml that excludes files', () async { @@ -232,7 +232,7 @@ ]), ]).create(); - await expectValidation(strictDeps); + await expectValidationDeprecated(strictDeps); }); }); @@ -244,7 +244,10 @@ import 'package:$bad'; ''').create(); - await expectValidation(strictDeps, errors: [matches('Invalid URL.')]); + await expectValidationDeprecated( + strictDeps, + errors: [matches('Invalid URL.')], + ); }); test('does not declare an "import" as a dependency', () async { @@ -252,7 +255,7 @@ import 'package:silly_monkey/silly_monkey.dart'; ''').create(); - await expectValidation( + await expectValidationDeprecated( strictDeps, errors: [ matches('does not have silly_monkey in the `dependencies` section') @@ -265,7 +268,7 @@ export 'package:silly_monkey/silly_monkey.dart'; ''').create(); - await expectValidation( + await expectValidationDeprecated( strictDeps, errors: [ matches('does not have silly_monkey in the `dependencies` section') @@ -278,7 +281,7 @@ import 'package:/'; ''').create(); - await expectValidation(strictDeps, errors: isNotEmpty); + await expectValidationDeprecated(strictDeps, errors: isNotEmpty); }); for (var port in ['import', 'export']) { @@ -298,7 +301,7 @@ ]), ]).create(); - await expectValidation(strictDeps, errors: isNotEmpty); + await expectValidationDeprecated(strictDeps, errors: isNotEmpty); }); } } @@ -316,7 +319,7 @@ ]), ]).create(); - await expectValidation( + await expectValidationDeprecated( strictDeps, warnings: [ matches( @@ -338,7 +341,7 @@ ]), ]).create(); - await expectValidation(strictDeps, errors: isNotEmpty); + await expectValidationDeprecated(strictDeps, errors: isNotEmpty); }); test('"package:silly_monkey"', () async { @@ -356,7 +359,7 @@ ]), ]).create(); - await expectValidation(strictDeps, errors: isNotEmpty); + await expectValidationDeprecated(strictDeps, errors: isNotEmpty); }); test('"package:/"', () async { @@ -368,7 +371,7 @@ ]), ]).create(); - await expectValidation(strictDeps, errors: isNotEmpty); + await expectValidationDeprecated(strictDeps, errors: isNotEmpty); }); test('"package:/]"', () async { @@ -380,7 +383,7 @@ ]), ]).create(); - await expectValidation(strictDeps, errors: isNotEmpty); + await expectValidationDeprecated(strictDeps, errors: isNotEmpty); }); }); });
diff --git a/test/validator/utils.dart b/test/validator/utils.dart index 92fbeff..ffbe892 100644 --- a/test/validator/utils.dart +++ b/test/validator/utils.dart
@@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:pub/src/exit_codes.dart'; import 'package:test/test.dart'; import '../test_pub.dart'; @@ -9,7 +10,8 @@ // TODO(sigurdm) consider rewriting all validator tests as integration tests. // That would make them more robust, and test actual end2end behaviour. -Future<void> expectValidation( +// Prefer using expectValidation. +Future<void> expectValidationDeprecated( ValidatorCreator fn, { hints, warnings, @@ -21,3 +23,58 @@ expect(validator.warnings, warnings ?? isEmpty); expect(validator.hints, hints ?? isEmpty); } + +Future<void> expectValidation({ + error, + int exitCode = 0, + Map<String, String> environment = const {}, +}) async { + await runPub( + error: error ?? contains('Package has 0 warnings.'), + args: ['publish', '--dry-run'], + // workingDirectory: d.path(appPath), + exitCode: exitCode, + environment: environment, + ); +} + +Future<void> expectValidationWarning( + error, { + int count = 1, + Map<String, String> environment = const {}, +}) async { + if (error is String) error = contains(error); + final s = count == 1 ? '' : 's'; + await expectValidation( + error: allOf([error, contains('Package has $count warning$s')]), + exitCode: DATA, + environment: environment, + ); +} + +Future<void> expectValidationHint( + hint, { + int count = 1, + Map<String, String> environment = const {}, +}) async { + if (hint is String) hint = contains(hint); + final s = count == 1 ? '' : 's'; + await expectValidation( + error: allOf([hint, contains('and $count hint$s')]), + environment: environment, + ); +} + +Future<void> expectValidationError( + String text, { + Map<String, String> environment = const {}, +}) async { + await expectValidation( + error: allOf([ + contains(text), + contains('Package validation found the following error:') + ]), + exitCode: DATA, + environment: environment, + ); +}