Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/src/cli/flutter_cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ class Flutter {
void Function(String)? stdout,
void Function(String)? stderr,
GeneratorBuilder buildGenerator = MasonGenerator.fromBundle,
String? reportOn,
}) async {
return TestCLIRunner.test(
logger: logger,
Expand All @@ -225,6 +226,7 @@ class Flutter {
stdout: stdout,
stderr: stderr,
buildGenerator: buildGenerator,
reportOn: reportOn,
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/cli/test_cli_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ class TestCLIRunner {
await _enhanceLcovWithUntestedFiles(
lcovPath: lcovPath,
cwd: cwd,
reportOn: 'lib',
reportOn: reportOn ?? 'lib',
excludeFromCoverage: excludeFromCoverage,
);
}
Expand Down
27 changes: 27 additions & 0 deletions lib/src/commands/test/test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class FlutterTestOptions {
required this.dartDefine,
required this.dartDefineFromFile,
required this.platform,
required this.reportOn,
required this.runSkipped,
required this.rest,
});

Expand Down Expand Up @@ -59,6 +61,8 @@ class FlutterTestOptions {
final dartDefineFromFile =
argResults['dart-define-from-file'] as List<String>?;
final platform = argResults['platform'] as String?;
final reportOn = argResults['report-on'] as String?;
final runSkipped = argResults['run-skipped'] as bool;
final rest = argResults.rest;

return FlutterTestOptions._(
Expand All @@ -78,6 +82,8 @@ class FlutterTestOptions {
dartDefine: dartDefine,
dartDefineFromFile: dartDefineFromFile,
platform: platform,
reportOn: reportOn,
runSkipped: runSkipped,
rest: rest,
);
}
Expand Down Expand Up @@ -132,6 +138,12 @@ class FlutterTestOptions {
/// The platform to run tests on (e.g., 'chrome', 'vm', 'android', 'ios').
final String? platform;

/// An optional file path to report coverage information to.
final String? reportOn;

/// Whether to run skipped tests instead of skipping them.
final bool runSkipped;

/// The remaining arguments passed to the test command.
final List<String> rest;
}
Expand All @@ -157,6 +169,7 @@ typedef FlutterTestCommand =
List<String>? arguments,
void Function(String)? stdout,
void Function(String)? stderr,
String? reportOn,
});

/// {@template test_command}
Expand Down Expand Up @@ -289,6 +302,18 @@ class TestCommand extends Command<int> {
'platform',
help: 'The platform to run tests on. ',
valueHelp: 'chrome|vm|android|ios',
)
..addOption(
'report-on',
help:
'An optional file path to report coverage information to. '
'This should be a path relative to the current working directory.',
valueHelp: 'lib/',
)
..addFlag(
'run-skipped',
help: 'Run skipped tests instead of skipping them.',
negatable: false,
);
}

Expand Down Expand Up @@ -351,11 +376,13 @@ This command should be run from the root of your Flutter project.''');
collectCoverageFrom: options.collectCoverageFrom,
randomSeed: options.randomSeed,
forceAnsi: options.forceAnsi,
reportOn: options.reportOn,
arguments: [
if (options.excludeTags != null) ...['-x', options.excludeTags!],
if (options.tags != null) ...['-t', options.tags!],
if (options.updateGoldens) '--update-goldens',
if (options.failFast) '--fail-fast',
if (options.runSkipped) '--run-skipped',
if (options.platform != null) ...['--platform', options.platform!],
if (options.dartDefine != null)
for (final value in options.dartDefine!) '--dart-define=$value',
Expand Down
43 changes: 43 additions & 0 deletions test/src/commands/test/test_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const expectedTestUsage = [
' --dart-define=<foo=bar> 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=<use-define-config.json|.env> 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=<chrome|vm|android|ios> The platform to run tests on. \n'
' --report-on=<lib/> An optional file path to report coverage information to. This should be a path relative to the current working directory.\n'
' --run-skipped Run skipped tests instead of skipping them.\n'
'\n'
'Run "very_good help" to see global options.',
];
Expand All @@ -70,6 +72,7 @@ abstract class FlutterTestCommand {
void Function(String)? stdout,
void Function(String)? stderr,
bool? forceAnsi,
String? reportOn,
});
}

Expand Down Expand Up @@ -115,6 +118,7 @@ void main() {
stdout: any(named: 'stdout'),
stderr: any(named: 'stderr'),
forceAnsi: any(named: 'forceAnsi'),
reportOn: any(named: 'reportOn'),
),
).thenAnswer((_) async => [0]);
when<dynamic>(() => argResults['concurrency']).thenReturn(concurrency);
Expand All @@ -125,6 +129,8 @@ void main() {
when<dynamic>(() => argResults['fail-fast']).thenReturn(false);
when<dynamic>(() => argResults['optimization']).thenReturn(true);
when<dynamic>(() => argResults['platform']).thenReturn(null);
when<dynamic>(() => argResults['report-on']).thenReturn(null);
when<dynamic>(() => argResults['run-skipped']).thenReturn(false);
when<dynamic>(
() => argResults['collect-coverage-from'],
).thenReturn('imports');
Expand Down Expand Up @@ -766,5 +772,42 @@ void main() {
).called(1);
},
);

test(
'reports on a different directory when --report-on is supplied',
() async {
when<dynamic>(() => argResults['min-coverage']).thenReturn('0');
when<dynamic>(() => argResults['report-on']).thenReturn('routes');
final result = await testCommand.run();
expect(result, equals(ExitCode.success.code));
verify(
() => flutterTest(
optimizePerformance: true,
collectCoverage: true,
arguments: defaultArguments,
minCoverage: 0,
logger: logger,
stdout: logger.write,
stderr: logger.err,
reportOn: 'routes',
),
).called(1);
},
);

test('completes normally --run-skipped', () async {
when<dynamic>(() => argResults['run-skipped']).thenReturn(true);
final result = await testCommand.run();
expect(result, equals(ExitCode.success.code));
verify(
() => flutterTest(
optimizePerformance: true,
arguments: ['--run-skipped', ...defaultArguments],
logger: logger,
stdout: logger.write,
stderr: logger.err,
),
).called(1);
});
});
}