diff --git a/README.md b/README.md index e823c92e0..1e8c8edc5 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,9 @@ very_good test --recursive # Run tests recursively (shorthand) very_good test -r + +# Run tests on a specific platform +very_good test --platform chrome ``` ### [`very_good packages get`](https://cli.vgv.dev/docs/commands/get_pkgs) diff --git a/lib/src/commands/dart/commands/dart_test_command.dart b/lib/src/commands/dart/commands/dart_test_command.dart index 72b6426ee..c38b471f5 100644 --- a/lib/src/commands/dart/commands/dart_test_command.dart +++ b/lib/src/commands/dart/commands/dart_test_command.dart @@ -20,6 +20,7 @@ class DartTestOptions { required this.randomSeed, required this.optimizePerformance, required this.forceAnsi, + required this.platform, required this.rest, required this.reportOn, }); @@ -41,6 +42,7 @@ class DartTestOptions { : randomOrderingSeed; final optimizePerformance = argResults['optimization'] as bool; final forceAnsi = argResults['force-ansi'] as bool?; + final platform = argResults['platform'] as String?; final reportOn = argResults['report-on'] as String?; final rest = argResults.rest; @@ -54,6 +56,7 @@ class DartTestOptions { randomSeed: randomSeed, optimizePerformance: optimizePerformance, forceAnsi: forceAnsi, + platform: platform, reportOn: reportOn, rest: rest, ); @@ -87,6 +90,9 @@ class DartTestOptions { /// default behavior based on stdout and stderr. final bool? forceAnsi; + /// The platform to run tests on (e.g., 'chrome', 'vm'). + final String? platform; + /// An optional file path to report coverage information to. final String? reportOn; @@ -142,13 +148,17 @@ class DartTestCommand extends Command { ..addFlag( 'optimization', defaultsTo: true, - help: 'Whether to apply optimizations for test performance.', + help: + 'Whether to apply optimizations for test performance. ' + 'Automatically disabled when --platform is specified.', ) ..addOption( 'concurrency', abbr: 'j', defaultsTo: '4', - help: 'The number of concurrent test suites run.', + help: + 'The number of concurrent test suites run. ' + 'Automatically set to 1 when --platform is specified.', ) ..addOption( 'tags', @@ -190,6 +200,11 @@ class DartTestCommand extends Command { 'An optional file path to report coverage information to. ' 'This should be a path relative to the current working directory.', valueHelp: 'lib/', + ) + ..addOption( + 'platform', + help: 'The platform to run tests on. ', + valueHelp: 'chrome|vm', ); } @@ -231,7 +246,10 @@ This command should be run from the root of your Dart project.'''); final results = await _dartTest( optimizePerformance: options.optimizePerformance && - !TestCLIRunner.isTargettingTestFiles(options.rest), + !TestCLIRunner.isTargettingTestFiles(options.rest) && + // Disabled optimization when platform is specified + // https://github.com/VeryGoodOpenSource/very_good_cli/issues/1363 + options.platform == null, recursive: recursive, logger: _logger, stdout: _logger.write, @@ -245,7 +263,8 @@ This command should be run from the root of your Dart project.'''); arguments: [ if (options.excludeTags != null) ...['-x', options.excludeTags!], if (options.tags != null) ...['-t', options.tags!], - ...['-j', options.concurrency], + if (options.platform != null) ...['--platform', options.platform!], + if (options.platform == null) ...['-j', options.concurrency], ...options.rest, ], reportOn: options.reportOn, diff --git a/lib/src/commands/test/test.dart b/lib/src/commands/test/test.dart index 30a3e1096..89590046a 100644 --- a/lib/src/commands/test/test.dart +++ b/lib/src/commands/test/test.dart @@ -23,6 +23,7 @@ class FlutterTestOptions { required this.forceAnsi, required this.dartDefine, required this.dartDefineFromFile, + required this.platform, required this.rest, }); @@ -47,6 +48,7 @@ class FlutterTestOptions { final dartDefine = argResults['dart-define'] as List?; final dartDefineFromFile = argResults['dart-define-from-file'] as List?; + final platform = argResults['platform'] as String?; final rest = argResults.rest; return FlutterTestOptions._( @@ -62,6 +64,7 @@ class FlutterTestOptions { forceAnsi: forceAnsi, dartDefine: dartDefine, dartDefineFromFile: dartDefineFromFile, + platform: platform, rest: rest, ); } @@ -104,6 +107,9 @@ class FlutterTestOptions { /// Optional list of dart define from files final List? dartDefineFromFile; + /// The platform to run tests on (e.g., 'chrome', 'vm', 'android', 'ios'). + final String? platform; + /// The remaining arguments passed to the test command. final List rest; } @@ -156,13 +162,17 @@ class TestCommand extends Command { ..addFlag( 'optimization', defaultsTo: true, - help: 'Whether to apply optimizations for test performance.', + help: + 'Whether to apply optimizations for test performance. ' + 'Automatically disabled when --platform is specified.', ) ..addOption( 'concurrency', abbr: 'j', defaultsTo: '4', - help: 'The number of concurrent test suites run.', + help: + 'The number of concurrent test suites run. ' + 'Automatically set to 1 when --platform is specified.', ) ..addOption( 'tags', @@ -227,6 +237,11 @@ class TestCommand extends Command { 'Entries from "--dart-define" with identical keys take ' 'precedence over entries from these files.', valueHelp: 'use-define-config.json|.env', + ) + ..addOption( + 'platform', + help: 'The platform to run tests on. ', + valueHelp: 'chrome|vm|android|ios', ); } @@ -271,7 +286,10 @@ This command should be run from the root of your Flutter project.'''); optimizePerformance: options.optimizePerformance && !TestCLIRunner.isTargettingTestFiles(options.rest) && - !options.updateGoldens, + !options.updateGoldens && + // Disabled optimization when platform is specified + // https://github.com/VeryGoodOpenSource/very_good_cli/issues/1363 + options.platform == null, recursive: recursive, logger: _logger, stdout: _logger.write, @@ -286,12 +304,13 @@ This command should be run from the root of your Flutter project.'''); if (options.excludeTags != null) ...['-x', options.excludeTags!], if (options.tags != null) ...['-t', options.tags!], if (options.updateGoldens) '--update-goldens', + if (options.platform != null) ...['--platform', options.platform!], if (options.dartDefine != null) for (final value in options.dartDefine!) '--dart-define=$value', if (options.dartDefineFromFile != null) for (final value in options.dartDefineFromFile!) '--dart-define-from-file=$value', - ...['-j', options.concurrency], + if (options.platform == null) ...['-j', options.concurrency], '--no-pub', ...options.rest, ], diff --git a/site/docs/commands/test.md b/site/docs/commands/test.md index 5a203484f..74160cb77 100644 --- a/site/docs/commands/test.md +++ b/site/docs/commands/test.md @@ -26,6 +26,7 @@ very_good test [arguments] --test-randomize-ordering-seed The seed to randomize the execution order of test cases within test files. --update-goldens Whether "matchesGoldenFile()" calls within your test methods should update the golden files. --force-ansi Whether to force ansi output. If not specified, it will maintain the default behavior based on stdout and stderr. + --platform The platform to run tests on. For Flutter tests, this can be "chrome", "vm", "android", "ios", etc. For Dart tests, this can be "chrome", "vm", etc. --dart-define= Additional key-value pairs that will be available as constants from the String.fromEnvironment, bool.fromEnvironment, int.fromEnvironment, and double.fromEnvironment constructors. Multiple defines can be passed by repeating "--dart-define" multiple times. Run "very_good help" to see global options. diff --git a/test/src/commands/dart/commands/dart_test_test.dart b/test/src/commands/dart/commands/dart_test_test.dart index 0b7545ed2..34af20a01 100644 --- a/test/src/commands/dart/commands/dart_test_test.dart +++ b/test/src/commands/dart/commands/dart_test_test.dart @@ -27,9 +27,9 @@ const expectedTestUsage = [ '-h, --help Print this usage information.\n' ' --coverage Whether to collect coverage information.\n' '-r, --recursive Run tests recursively for all nested packages.\n' - ' --[no-]optimization Whether to apply optimizations for test performance.\n' + ' --[no-]optimization Whether to apply optimizations for test performance. Automatically disabled when --platform is specified.\n' ' (defaults to on)\n' - '-j, --concurrency The number of concurrent test suites run.\n' + '-j, --concurrency The number of concurrent test suites run. Automatically set to 1 when --platform is specified.\n' ' (defaults to "4")\n' '-t, --tags Run only tests associated with the specified tags.\n' ' --exclude-coverage A glob which will be used to exclude files that match from the coverage.\n' @@ -38,6 +38,7 @@ const expectedTestUsage = [ ' --test-randomize-ordering-seed The seed to randomize the execution order of test cases within test files.\n' ' --force-ansi Whether to force ansi output. If not specified, it will maintain the default behavior based on stdout and stderr.\n' ' --report-on= An optional file path to report coverage information to. This should be a path relative to the current working directory.\n' + ' --platform= The platform to run tests on. \n' '\n' 'Run "very_good help" to see global options.', ]; @@ -106,6 +107,7 @@ void main() { when(() => argResults['recursive']).thenReturn(false); when(() => argResults['coverage']).thenReturn(false); when(() => argResults['optimization']).thenReturn(true); + when(() => argResults['platform']).thenReturn(null); when(() => argResults.rest).thenReturn([]); }); @@ -246,6 +248,48 @@ void main() { ).called(1); }); + test('completes normally --platform chrome', () async { + when(() => argResults['platform']).thenReturn('chrome'); + final result = await testCommand.run(); + expect(result, equals(ExitCode.success.code)); + verify( + () => dartTest( + arguments: ['--platform', 'chrome'], + logger: logger, + stdout: logger.write, + stderr: logger.err, + ), + ).called(1); + }); + + test('disables optimization when --platform is specified', () async { + when(() => argResults['platform']).thenReturn('chrome'); + final result = await testCommand.run(); + expect(result, equals(ExitCode.success.code)); + verify( + () => dartTest( + arguments: ['--platform', 'chrome'], + logger: logger, + stdout: logger.write, + stderr: logger.err, + ), + ).called(1); + }); + + test('disables concurrency when --platform is specified', () async { + when(() => argResults['platform']).thenReturn('chrome'); + final result = await testCommand.run(); + expect(result, equals(ExitCode.success.code)); + verify( + () => dartTest( + arguments: ['--platform', 'chrome'], + logger: logger, + stdout: logger.write, + stderr: logger.err, + ), + ).called(1); + }); + test('completes normally --test-randomize-ordering-seed random', () async { when( () => argResults['test-randomize-ordering-seed'], diff --git a/test/src/commands/test/test_test.dart b/test/src/commands/test/test_test.dart index 826cc0fca..510cbcae3 100644 --- a/test/src/commands/test/test_test.dart +++ b/test/src/commands/test/test_test.dart @@ -27,9 +27,9 @@ const expectedTestUsage = [ '-h, --help Print this usage information.\n' ' --coverage Whether to collect coverage information.\n' '-r, --recursive Run tests recursively for all nested packages.\n' - ' --[no-]optimization Whether to apply optimizations for test performance.\n' + ' --[no-]optimization Whether to apply optimizations for test performance. Automatically disabled when --platform is specified.\n' ' (defaults to on)\n' - '-j, --concurrency The number of concurrent test suites run.\n' + '-j, --concurrency The number of concurrent test suites run. Automatically set to 1 when --platform is specified.\n' ' (defaults to "4")\n' '-t, --tags Run only tests associated with the specified tags.\n' ' --exclude-coverage A glob which will be used to exclude files that match from the coverage.\n' @@ -40,6 +40,7 @@ const expectedTestUsage = [ ' --force-ansi Whether to force ansi output. If not specified, it will maintain the default behavior based on stdout and stderr.\n' ' --dart-define= Additional key-value pairs that will be available as constants from the String.fromEnvironment, bool.fromEnvironment, int.fromEnvironment, and double.fromEnvironment constructors. Multiple defines can be passed by repeating "--dart-define" multiple times.\n' ' --dart-define-from-file= The path of a .json or .env file containing key-value pairs that will be available as environment variables. These can be accessed using the String.fromEnvironment, bool.fromEnvironment, and int.fromEnvironment constructors. Multiple defines can be passed by repeating "--dart-define-from-file" multiple times. Entries from "--dart-define" with identical keys take precedence over entries from these files.\n' + ' --platform= The platform to run tests on. \n' '\n' 'Run "very_good help" to see global options.', ]; @@ -108,6 +109,7 @@ void main() { when(() => argResults['coverage']).thenReturn(false); when(() => argResults['update-goldens']).thenReturn(false); when(() => argResults['optimization']).thenReturn(true); + when(() => argResults['platform']).thenReturn(null); when(() => argResults.rest).thenReturn([]); }); @@ -262,6 +264,48 @@ void main() { ).called(1); }); + test('completes normally --platform chrome', () async { + when(() => argResults['platform']).thenReturn('chrome'); + final result = await testCommand.run(); + expect(result, equals(ExitCode.success.code)); + verify( + () => flutterTest( + arguments: ['--platform', 'chrome', '--no-pub'], + logger: logger, + stdout: logger.write, + stderr: logger.err, + ), + ).called(1); + }); + + test('disables optimization when --platform is specified', () async { + when(() => argResults['platform']).thenReturn('chrome'); + final result = await testCommand.run(); + expect(result, equals(ExitCode.success.code)); + verify( + () => flutterTest( + arguments: ['--platform', 'chrome', '--no-pub'], + logger: logger, + stdout: logger.write, + stderr: logger.err, + ), + ).called(1); + }); + + test('disables concurrency when --platform is specified', () async { + when(() => argResults['platform']).thenReturn('chrome'); + final result = await testCommand.run(); + expect(result, equals(ExitCode.success.code)); + verify( + () => flutterTest( + arguments: ['--platform', 'chrome', '--no-pub'], + logger: logger, + stdout: logger.write, + stderr: logger.err, + ), + ).called(1); + }); + test('completes normally --test-randomize-ordering-seed random', () async { when( () => argResults['test-randomize-ordering-seed'],