From e01f70b74acdb1df84a9a799b73fba77a30b8379 Mon Sep 17 00:00:00 2001 From: Dominik Simonik Date: Tue, 9 Sep 2025 18:03:25 +0200 Subject: [PATCH 1/5] feat: add platform option for test commands --- .../commands/dart/commands/dart_test_command.dart | 14 ++++++++++++++ lib/src/commands/test/test.dart | 15 +++++++++++++++ .../commands/dart/commands/dart_test_test.dart | 15 +++++++++++++++ test/src/commands/test/test_test.dart | 15 +++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/lib/src/commands/dart/commands/dart_test_command.dart b/lib/src/commands/dart/commands/dart_test_command.dart index 72b6426ee..5f521f370 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; @@ -190,6 +196,13 @@ 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. ' + 'For Dart tests, this can be "chrome", "vm", etc.', + valueHelp: 'chrome|vm', ); } @@ -245,6 +258,7 @@ 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!], + if (options.platform != null) ...['--platform', options.platform!], ...['-j', options.concurrency], ...options.rest, ], diff --git a/lib/src/commands/test/test.dart b/lib/src/commands/test/test.dart index 30a3e1096..a4a1c6bc3 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; } @@ -227,6 +233,14 @@ 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. ' + 'For Flutter tests, this can be "chrome", "vm", "android", "ios", etc. ' + 'For Dart tests, this can be "chrome", "vm", etc.', + valueHelp: 'chrome|vm|android|ios', ); } @@ -286,6 +300,7 @@ 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) diff --git a/test/src/commands/dart/commands/dart_test_test.dart b/test/src/commands/dart/commands/dart_test_test.dart index 0b7545ed2..33981244b 100644 --- a/test/src/commands/dart/commands/dart_test_test.dart +++ b/test/src/commands/dart/commands/dart_test_test.dart @@ -37,6 +37,7 @@ const expectedTestUsage = [ ' --min-coverage Whether to enforce a minimum coverage percentage.\n' ' --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' + ' --platform The platform to run tests on. For Dart tests, this can be "chrome", "vm", etc.\n' ' --report-on= An optional file path to report coverage information to. This should be a path relative to the current working directory.\n' '\n' 'Run "very_good help" to see global options.', @@ -246,6 +247,20 @@ 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', ...defaultArguments], + 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..66b565701 100644 --- a/test/src/commands/test/test_test.dart +++ b/test/src/commands/test/test_test.dart @@ -38,6 +38,7 @@ const expectedTestUsage = [ ' --test-randomize-ordering-seed The seed to randomize the execution order of test cases within test files.\n' ' --update-goldens Whether "matchesGoldenFile()" calls within your test methods should update the golden files.\n' ' --force-ansi Whether to force ansi output. If not specified, it will maintain the default behavior based on stdout and stderr.\n' + ' --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.\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' '\n' @@ -262,6 +263,20 @@ 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', ...defaultArguments], + 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'], From 517b51642ca21dbde9e08e2bd0e0aef83bbbf6d6 Mon Sep 17 00:00:00 2001 From: Dominik Simonik Date: Tue, 9 Sep 2025 18:03:33 +0200 Subject: [PATCH 2/5] docs: update README and test command documentation to include platform option for running tests --- README.md | 3 +++ site/docs/commands/test.md | 1 + 2 files changed, 4 insertions(+) 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/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. From a4cc2e1e224bf9c59c5cbbc2e1d11c8e475a1956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20=C5=A0imon=C3=ADk?= Date: Wed, 17 Sep 2025 09:08:52 +0200 Subject: [PATCH 3/5] refactor: enhance help messages and adjust concurrency settings for test commands --- .../dart/commands/dart_test_command.dart | 17 ++++++++++------- lib/src/commands/test/test.dart | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/src/commands/dart/commands/dart_test_command.dart b/lib/src/commands/dart/commands/dart_test_command.dart index 5f521f370..4f3b63a21 100644 --- a/lib/src/commands/dart/commands/dart_test_command.dart +++ b/lib/src/commands/dart/commands/dart_test_command.dart @@ -148,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', @@ -199,9 +203,7 @@ class DartTestCommand extends Command { ) ..addOption( 'platform', - help: - 'The platform to run tests on. ' - 'For Dart tests, this can be "chrome", "vm", etc.', + help: 'The platform to run tests on. ', valueHelp: 'chrome|vm', ); } @@ -244,7 +246,8 @@ 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) && + options.platform == null, recursive: recursive, logger: _logger, stdout: _logger.write, @@ -259,7 +262,7 @@ This command should be run from the root of your Dart project.'''); if (options.excludeTags != null) ...['-x', options.excludeTags!], if (options.tags != null) ...['-t', options.tags!], if (options.platform != null) ...['--platform', options.platform!], - ...['-j', options.concurrency], + 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 a4a1c6bc3..639cce657 100644 --- a/lib/src/commands/test/test.dart +++ b/lib/src/commands/test/test.dart @@ -162,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', @@ -236,10 +240,7 @@ class TestCommand extends Command { ) ..addOption( 'platform', - help: - '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.', + help: 'The platform to run tests on. ', valueHelp: 'chrome|vm|android|ios', ); } @@ -285,7 +286,8 @@ This command should be run from the root of your Flutter project.'''); optimizePerformance: options.optimizePerformance && !TestCLIRunner.isTargettingTestFiles(options.rest) && - !options.updateGoldens, + !options.updateGoldens && + options.platform == null, recursive: recursive, logger: _logger, stdout: _logger.write, @@ -306,7 +308,7 @@ This command should be run from the root of your Flutter project.'''); 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, ], From cefeb3d4c9386c3dd46f11001575877e4ad258f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20=C5=A0imon=C3=ADk?= Date: Wed, 17 Sep 2025 09:17:30 +0200 Subject: [PATCH 4/5] test: update help messages and implement platform-specific behavior for test commands --- .../dart/commands/dart_test_test.dart | 37 +++++++++++++++++-- test/src/commands/test/test_test.dart | 37 +++++++++++++++++-- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/test/src/commands/dart/commands/dart_test_test.dart b/test/src/commands/dart/commands/dart_test_test.dart index 33981244b..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' @@ -37,8 +37,8 @@ const expectedTestUsage = [ ' --min-coverage Whether to enforce a minimum coverage percentage.\n' ' --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' - ' --platform The platform to run tests on. For Dart tests, this can be "chrome", "vm", etc.\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.', ]; @@ -107,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([]); }); @@ -253,7 +254,35 @@ void main() { expect(result, equals(ExitCode.success.code)); verify( () => dartTest( - arguments: ['--platform', 'chrome', ...defaultArguments], + 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, diff --git a/test/src/commands/test/test_test.dart b/test/src/commands/test/test_test.dart index 66b565701..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' @@ -38,9 +38,9 @@ const expectedTestUsage = [ ' --test-randomize-ordering-seed The seed to randomize the execution order of test cases within test files.\n' ' --update-goldens Whether "matchesGoldenFile()" calls within your test methods should update the golden files.\n' ' --force-ansi Whether to force ansi output. If not specified, it will maintain the default behavior based on stdout and stderr.\n' - ' --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.\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.', ]; @@ -109,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([]); }); @@ -269,7 +270,35 @@ void main() { expect(result, equals(ExitCode.success.code)); verify( () => flutterTest( - arguments: ['--platform', 'chrome', ...defaultArguments], + 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, From 3efb6188570a0bc0f344b3de4339515159687a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20=C5=A0imon=C3=ADk?= Date: Wed, 17 Sep 2025 13:04:54 +0200 Subject: [PATCH 5/5] doc: add comments about the issue --- lib/src/commands/dart/commands/dart_test_command.dart | 2 ++ lib/src/commands/test/test.dart | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/src/commands/dart/commands/dart_test_command.dart b/lib/src/commands/dart/commands/dart_test_command.dart index 4f3b63a21..c38b471f5 100644 --- a/lib/src/commands/dart/commands/dart_test_command.dart +++ b/lib/src/commands/dart/commands/dart_test_command.dart @@ -247,6 +247,8 @@ This command should be run from the root of your Dart project.'''); optimizePerformance: options.optimizePerformance && !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, diff --git a/lib/src/commands/test/test.dart b/lib/src/commands/test/test.dart index 639cce657..89590046a 100644 --- a/lib/src/commands/test/test.dart +++ b/lib/src/commands/test/test.dart @@ -287,6 +287,8 @@ This command should be run from the root of your Flutter project.'''); options.optimizePerformance && !TestCLIRunner.isTargettingTestFiles(options.rest) && !options.updateGoldens && + // Disabled optimization when platform is specified + // https://github.com/VeryGoodOpenSource/very_good_cli/issues/1363 options.platform == null, recursive: recursive, logger: _logger,