From 4d44b7e7d9273e3e23f5fdbd4553c2765b1a1175 Mon Sep 17 00:00:00 2001 From: nturgut Date: Wed, 11 Nov 2020 17:18:15 -0800 Subject: [PATCH] Upgrades to felt (running on multiple modes, multiple backends, single test target option) (#22260) * testing running the tests on all build modes * don't run debug mode on other browsers * fix platform message test failures * some cleanup. change dispose platform channel message * adding flags to control the integration tests better with felt * running tests by target name, selecting web rendering backend * fix conditions * carrying some conditions to helper methods. Adding comments * create a blocked list for failing canvaskit test * parse parameters before all integration tests * Give better warning to developers for tests that are blocked for CI * address some reviwer comments (more remains) * remove named parameters * also run with auto mode * add verbose option * reduce the number of tests running. skip url_test for now --- .../lib/profile_diagnostics_main.dart | 1 - .../regular_integration_tests/pubspec.yaml | 2 +- .../platform_messages_integration.dart | 11 +- .../test_driver/text_editing_integration.dart | 28 +- lib/web_ui/dev/integration_tests_manager.dart | 294 +++++++++++++++--- lib/web_ui/dev/test_runner.dart | 9 +- lib/web_ui/dev/utils.dart | 32 ++ web_sdk/web_test_utils/pubspec.yaml | 2 +- 8 files changed, 315 insertions(+), 64 deletions(-) diff --git a/e2etests/web/regular_integration_tests/lib/profile_diagnostics_main.dart b/e2etests/web/regular_integration_tests/lib/profile_diagnostics_main.dart index 26dba545b..fe9c4ddf5 100644 --- a/e2etests/web/regular_integration_tests/lib/profile_diagnostics_main.dart +++ b/e2etests/web/regular_integration_tests/lib/profile_diagnostics_main.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; void main() { runApp(MyApp()); diff --git a/e2etests/web/regular_integration_tests/pubspec.yaml b/e2etests/web/regular_integration_tests/pubspec.yaml index 9c32394c9..edff2da9d 100644 --- a/e2etests/web/regular_integration_tests/pubspec.yaml +++ b/e2etests/web/regular_integration_tests/pubspec.yaml @@ -2,7 +2,7 @@ name: regular_integration_tests publish_to: none environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.11.0-0 <3.0.0" dependencies: flutter: diff --git a/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration.dart b/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration.dart index af77ea1a1..c20e91163 100644 --- a/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration.dart +++ b/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration.dart @@ -28,7 +28,7 @@ void main() async { await tester.tap(find.byKey(const Key('input'))); // Focus in input, otherwise clipboard will fail with // 'document is not focused' platform exception. - html.document.querySelector('input').focus(); + html.document.querySelector('input')?.focus(); await Clipboard.setData(const ClipboardData(text: 'sample text')); }, skip: true); // https://github.com/flutter/flutter/issues/54296 @@ -36,7 +36,7 @@ void main() async { (WidgetTester tester) async { int viewInstanceCount = 0; - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + platformViewsRegistry.getNextPlatformViewId(); // ignore: undefined_prefixed_name ui.platformViewRegistry.registerViewFactory('MyView', (int viewId) { ++viewInstanceCount; @@ -46,14 +46,11 @@ void main() async { app.main(); await tester.pumpAndSettle(); final Map createArgs = { - 'id': '567', + 'id': 567, 'viewType': 'MyView', }; await SystemChannels.platform_views.invokeMethod('create', createArgs); - final Map disposeArgs = { - 'id': '567', - }; - await SystemChannels.platform_views.invokeMethod('dispose', disposeArgs); + await SystemChannels.platform_views.invokeMethod('dispose', 567); expect(viewInstanceCount, 1); }); } diff --git a/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart b/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart index 49247d7d7..1fe774d85 100644 --- a/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart +++ b/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart @@ -13,7 +13,9 @@ import 'package:flutter/material.dart'; import 'package:integration_test/integration_test.dart'; void main() { - final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding; + final IntegrationTestWidgetsFlutterBinding binding = + IntegrationTestWidgetsFlutterBinding.ensureInitialized() + as IntegrationTestWidgetsFlutterBinding; testWidgets('Focused text field creates a native input element', (WidgetTester tester) async { @@ -38,7 +40,7 @@ void main() { // Change the value of the TextFormField. final TextFormField textFormField = tester.widget(finder); - textFormField.controller.text = 'New Value'; + textFormField.controller?.text = 'New Value'; // DOM element's value also changes. expect(input.value, 'New Value'); @@ -68,7 +70,7 @@ void main() { // Change the value of the TextFormField. final TextFormField textFormField = tester.widget(finder); - textFormField.controller.text = 'New Value'; + textFormField.controller?.text = 'New Value'; // DOM element's value also changes. expect(input.value, 'New Value'); }); @@ -145,9 +147,9 @@ void main() { expect(input2.value, 'Text2'); }); - testWidgets('Jump between TextFormFields with tab key after CapsLock is' - 'activated', - (WidgetTester tester) async { + testWidgets( + 'Jump between TextFormFields with tab key after CapsLock is' + 'activated', (WidgetTester tester) async { app.main(); await tester.pumpAndSettle(); @@ -163,7 +165,7 @@ void main() { final List nodeList = document.getElementsByTagName('input'); expect(nodeList.length, equals(1)); final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; + document.getElementsByTagName('input')[0] as InputElement; // Press and release CapsLock. dispatchKeyboardEvent(input, 'keydown', { @@ -207,7 +209,7 @@ void main() { // A native input element for the next TextField should be attached to the // DOM. final InputElement input2 = - document.getElementsByTagName('input')[0] as InputElement; + document.getElementsByTagName('input')[0] as InputElement; expect(input2.value, 'Text2'); }); @@ -243,8 +245,8 @@ void main() { expect(input.hasAttribute('readonly'), isTrue); // Make sure the entire text is selected. - TextRange range = - TextRange(start: input.selectionStart, end: input.selectionEnd); + TextRange range = TextRange( + start: input.selectionStart ?? 0, end: input.selectionEnd ?? 0); expect(range.textInside(text), text); // Double tap to select the first word. @@ -257,7 +259,8 @@ void main() { await gesture.up(); await gesture.down(firstWordOffset); await gesture.up(); - range = TextRange(start: input.selectionStart, end: input.selectionEnd); + range = TextRange( + start: input.selectionStart ?? 0, end: input.selectionEnd ?? 0); expect(range.textInside(text), 'Lorem'); // Double tap to select the last word. @@ -270,7 +273,8 @@ void main() { await gesture.up(); await gesture.down(lastWordOffset); await gesture.up(); - range = TextRange(start: input.selectionStart, end: input.selectionEnd); + range = TextRange( + start: input.selectionStart ?? 0, end: input.selectionEnd ?? 0); expect(range.textInside(text), 'amet'); }); } diff --git a/lib/web_ui/dev/integration_tests_manager.dart b/lib/web_ui/dev/integration_tests_manager.dart index ac6c2b50a..5ce70b0f4 100644 --- a/lib/web_ui/dev/integration_tests_manager.dart +++ b/lib/web_ui/dev/integration_tests_manager.dart @@ -5,6 +5,7 @@ // @dart = 2.6 import 'dart:io' as io; +import 'package:args/args.dart'; import 'package:path/path.dart' as pathlib; import 'chrome_installer.dart'; @@ -109,6 +110,9 @@ class IntegrationTestsManager { return testResults; } + int _numberOfPassedTests = 0; + int _numberOfFailedTests = 0; + Future _runTestsInDirectory(io.Directory directory) async { final io.Directory testDirectory = io.Directory(pathlib.join(directory.path, 'test_driver')); @@ -121,47 +125,83 @@ class IntegrationTestsManager { final List blockedTests = blockedTestsListsMap[getBlockedTestsListMapKey(_browser)] ?? []; - // The following loops over the contents of the directory and saves an - // expected driver file name for each e2e test assuming any dart file - // not ending with `_test.dart` is an e2e test. - // Other files are not considered since developers can add files such as - // README. - for (io.File f in entities) { - final String basename = pathlib.basename(f.path); - if (!basename.contains('_test.dart') && basename.endsWith('.dart')) { - // Do not add the basename if it is in the `blockedTests`. - if (!blockedTests.contains(basename)) { - e2eTestsToRun.add(basename); - } else { - print('INFO: Test $basename is skipped since it is blocked for ' - '${getBlockedTestsListMapKey(_browser)}'); + // If no target is specified run all the tests. + if (_runAllTestTargets) { + // The following loops over the contents of the directory and saves an + // expected driver file name for each e2e test assuming any dart file + // not ending with `_test.dart` is an e2e test. + // Other files are not considered since developers can add files such as + // README. + for (io.File f in entities) { + final String basename = pathlib.basename(f.path); + if (!basename.contains('_test.dart') && basename.endsWith('.dart')) { + // Do not add the basename if it is in the `blockedTests`. + if (!blockedTests.contains(basename)) { + e2eTestsToRun.add(basename); + } else { + print('INFO: Test $basename is skipped since it is blocked for ' + '${getBlockedTestsListMapKey(_browser)}'); + } } } + if (isVerboseLoggingEnabled) { + print( + 'INFO: In project ${directory} ${e2eTestsToRun.length} tests to run.'); + } + } else { + // If a target is specified it will run regardless of if it's blocked or + // not. There will be an info note to warn the developer. + final String targetTest = + IntegrationTestsArgumentParser.instance.testTarget; + final io.File file = + entities.singleWhere((f) => pathlib.basename(f.path) == targetTest); + final String basename = pathlib.basename(file.path); + if (blockedTests.contains(basename) && isVerboseLoggingEnabled) { + print('INFO: Test $basename do not run on CI environments. Please ' + 'remove it from the blocked tests list if you want to enable this ' + 'test on CI.'); + } + e2eTestsToRun.add(basename); } - print( - 'INFO: In project ${directory} ${e2eTestsToRun.length} tests to run.'); - int numberOfPassedTests = 0; int numberOfFailedTests = 0; + + final Set buildModes = _getBuildModes(); + for (String fileName in e2eTestsToRun) { - final bool testResults = - await _runTestsInProfileMode(directory, fileName); - if (testResults) { - numberOfPassedTests++; - } else { - numberOfFailedTests++; - } + await _runTestsTarget(directory, fileName, buildModes); } - final int numberOfTestsRun = numberOfPassedTests + numberOfFailedTests; + + final int numberOfTestsRun = _numberOfPassedTests + _numberOfFailedTests; print('INFO: ${numberOfTestsRun} tests run. ${numberOfPassedTests} passed ' 'and ${numberOfFailedTests} failed.'); return numberOfFailedTests == 0; } - Future _runTestsInProfileMode( - io.Directory directory, String testName) async { + Future _runTestsTarget( + io.Directory directory, String target, Set buildModes) async { + final Set renderingBackends = _getRenderingBackends(); + for (String renderingBackend in renderingBackends) { + for (String mode in buildModes) { + if (!blockedTestsListsMapForModes[mode].contains(target) && + !blockedTestsListsMapForRenderBackends[renderingBackend] + .contains(target)) { + final bool result = await _runTestsInMode(directory, target, + mode: mode, webRenderer: renderingBackend); + if (result) { + _numberOfPassedTests++; + } else { + _numberOfFailedTests++; + } + } + } + } + } + + Future _runTestsInMode(io.Directory directory, String testName, + {String mode = 'profile', String webRenderer = 'html'}) async { String executable = _useSystemFlutter ? 'flutter' : environment.flutterCommand.path; Map enviroment = Map(); @@ -172,22 +212,54 @@ class IntegrationTestsManager { IntegrationArguments.fromBrowser(_browser); final int exitCode = await runProcess( executable, - arguments.getTestArguments(testName, 'profile'), + arguments.getTestArguments(testName, mode, webRenderer), workingDirectory: directory.path, environment: enviroment, ); if (exitCode != 0) { + final String command = + arguments.getCommandToRun(testName, mode, webRenderer); io.stderr .writeln('ERROR: Failed to run test. Exited with exit code $exitCode' '. To run $testName locally use the following command:' - '\n\n${arguments.getCommandToRun(testName, 'profile')}'); + '\n\n$command'); return false; } else { return true; } } + Set _getRenderingBackends() { + Set renderingBackends; + if (_renderingBackendSelected) { + final String mode = IntegrationTestsArgumentParser.instance.webRenderer; + renderingBackends = {mode}; + } else { + // TODO(nurhan): Enable `auto` when recipe is sharded. + renderingBackends = {'html', 'canvaskit'}; + } + return renderingBackends; + } + + Set _getBuildModes() { + Set buildModes; + if (_buildModeSelected) { + final String mode = IntegrationTestsArgumentParser.instance.buildMode; + if (mode == 'debug' && _browser != 'chrome') { + throw ToolException('Debug mode is only supported for Chrome.'); + } else { + buildModes = {mode}; + } + } else { + // TODO(nurhan): Enable `release` when recipe is sharded. + buildModes = _browser == 'chrome' + ? {'debug', 'profile'} + : {'profile'}; + } + return buildModes; + } + /// Validate the directory has a `pubspec.yaml` file and a `test_driver` /// directory. /// @@ -290,9 +362,25 @@ class IntegrationTestsManager { } } + bool get _buildModeSelected => + !IntegrationTestsArgumentParser.instance.buildMode.isEmpty; + + bool get _renderingBackendSelected => + !IntegrationTestsArgumentParser.instance.webRenderer.isEmpty; + + bool get _runAllTestTargets => + IntegrationTestsArgumentParser.instance.testTarget.isEmpty; + /// Validate the given `browser`, `platform` combination is suitable for /// integration tests to run. bool validateIfTestsShouldRun() { + if (_buildModeSelected) { + final String mode = IntegrationTestsArgumentParser.instance.buildMode; + if (mode == 'debug' && _browser != 'chrome') { + throw ToolException('Debug mode is only supported for Chrome.'); + } + } + // Chrome tests should run at all Platforms (Linux, macOS, Windows). // They can also run successfully on CI and local. if (_browser == 'chrome') { @@ -326,14 +414,16 @@ abstract class IntegrationArguments { } } - List getTestArguments(String testName, String mode); + List getTestArguments( + String testName, String mode, String webRenderer); - String getCommandToRun(String testName, String mode); + String getCommandToRun(String testName, String mode, String webRenderer); } /// Arguments to give `flutter drive` to run the integration tests on Chrome. class ChromeIntegrationArguments extends IntegrationArguments { - List getTestArguments(String testName, String mode) { + List getTestArguments( + String testName, String mode, String webRenderer) { return [ 'drive', '--target=test_driver/${testName}', @@ -344,13 +434,15 @@ class ChromeIntegrationArguments extends IntegrationArguments { if (isLuci) '--chrome-binary=${preinstalledChromeExecutable()}', '--headless', '--local-engine=host_debug_unopt', + '--web-renderer=$webRenderer', ]; } - String getCommandToRun(String testName, String mode) { + String getCommandToRun(String testName, String mode, String webRenderer) { String statementToRun = 'flutter drive ' - '--target=test_driver/${testName} -d web-server --profile ' - '--browser-name=chrome --local-engine=host_debug_unopt'; + '--target=test_driver/${testName} -d web-server --$mode ' + '--browser-name=chrome --local-engine=host_debug_unopt ' + '--web-renderer=$webRenderer'; if (isLuci) { statementToRun = '$statementToRun --chrome-binary=' '${preinstalledChromeExecutable()}'; @@ -361,7 +453,8 @@ class ChromeIntegrationArguments extends IntegrationArguments { /// Arguments to give `flutter drive` to run the integration tests on Firefox. class FirefoxIntegrationArguments extends IntegrationArguments { - List getTestArguments(String testName, String mode) { + List getTestArguments( + String testName, String mode, String webRenderer) { return [ 'drive', '--target=test_driver/${testName}', @@ -371,18 +464,23 @@ class FirefoxIntegrationArguments extends IntegrationArguments { '--browser-name=firefox', '--headless', '--local-engine=host_debug_unopt', + '--web-renderer=$webRenderer', ]; } - String getCommandToRun(String testName, String mode) => - 'flutter ${getTestArguments(testName, mode).join(' ')}'; + String getCommandToRun(String testName, String mode, String webRenderer) { + final String arguments = + getTestArguments(testName, mode, webRenderer).join(' '); + return 'flutter $arguments'; + } } /// Arguments to give `flutter drive` to run the integration tests on Safari. class SafariIntegrationArguments extends IntegrationArguments { SafariIntegrationArguments(); - List getTestArguments(String testName, String mode) { + List getTestArguments( + String testName, String mode, String webRenderer) { return [ 'drive', '--target=test_driver/${testName}', @@ -391,11 +489,96 @@ class SafariIntegrationArguments extends IntegrationArguments { '--$mode', '--browser-name=safari', '--local-engine=host_debug_unopt', + '--web-renderer=$webRenderer', ]; } - String getCommandToRun(String testName, String mode) => - 'flutter ${getTestArguments(testName, mode).join(' ')}'; + String getCommandToRun(String testName, String mode, String webRenderer) { + final String arguments = + getTestArguments(testName, mode, webRenderer).join(' '); + return 'flutter $arguments'; + } +} + +/// Parses additional options that can be used when running integration tests. +class IntegrationTestsArgumentParser { + static final IntegrationTestsArgumentParser _singletonInstance = + IntegrationTestsArgumentParser._(); + + /// The [IntegrationTestsArgumentParser] singleton. + static IntegrationTestsArgumentParser get instance => _singletonInstance; + + IntegrationTestsArgumentParser._(); + + /// If target name is provided integration tests can run that one test + /// instead of running all the tests. + String testTarget; + + /// The build mode to run the integration tests. + /// + /// If not specified, these tests will run using 'debug, profile, release' + /// modes on Chrome and will run using 'profile, release' on other browsers. + /// + /// In order to skip a test for one of the modes, add the test to the + /// `blockedTestsListsMapForModes` list for the relevant compile mode. + String buildMode; + + /// Whether to use html, canvaskit or auto for web renderer. + /// + /// If not set all backends will be used one after another for integration + /// tests. If set only the provided option will be used. + String webRenderer; + + void populateOptions(ArgParser argParser) { + argParser + ..addOption( + 'target', + defaultsTo: '', + help: 'By default integration tests are run for all the tests under' + 'flutter/e2etests/web directory. If a test name is specified, that ' + 'only that test will run. The test name will be the name of the ' + 'integration test (e2e test) file. For example: ' + 'text_editing_integration.dart or ' + 'profile_diagnostics_integration.dart', + ) + ..addOption('build-mode', + defaultsTo: '', + help: 'Flutter supports three modes when building your app. This ' + 'option sets the build mode for the integration tests. ' + 'By default an integration test will sequentially run on ' + 'multiple modes. All three modes (debug, release, profile) are ' + 'used for Chrome. Only profile, release modes will be used for ' + 'other browsers. In other words, if a build mode is selected ' + 'tests will only be run using that mode. ' + 'See https://flutter.dev/docs/testing/build-modes for more ' + 'details on the build modes.') + ..addOption('web-renderer', + defaultsTo: '', + help: 'By default all three options (`html`, `canvaskit`, `auto`) ' + ' for rendering backends are tested when running integration ' + ' tests. If this option is set only the backend provided by this ' + ' option will be used. `auto`, `canvaskit` and `html`' + ' are the available options. '); + } + + /// Populate results of the arguments passed. + void parseOptions(ArgResults argResults) { + testTarget = argResults['target'] as String; + buildMode = argResults['build-mode'] as String; + if (!buildMode.isEmpty && + buildMode != 'debug' && + buildMode != 'profile' && + buildMode != 'release') { + throw ArgumentError('Unexpected build mode: $buildMode'); + } + webRenderer = argResults['web-renderer'] as String; + if (!webRenderer.isEmpty && + webRenderer != 'html' && + webRenderer != 'canvaskit' && + webRenderer != 'auto') { + throw ArgumentError('Unexpected rendering backend: $webRenderer'); + } + } } /// Prepares a key for the [blackList] map. @@ -440,3 +623,32 @@ const Map> blockedTestsListsMap = >{ 'target_platform_ios_integration.dart', ], }; + +/// Tests blocked for one of the build modes. +/// +/// If a test is not supposed to run for one of the modes also add that test +/// to the corresponding list. +// TODO(nurhan): Remove the failing test after fixing. +const Map> blockedTestsListsMapForModes = + >{ + 'debug': [ + 'treeshaking_integration.dart', + 'text_editing_integration.dart', + 'url_strategy_integration.dart', + ], + 'profile': [], + 'release': [], +}; + +/// Tests blocked for one of the rendering backends. +/// +/// If a test is not suppose to run for one of the backends also add that test +/// to the corresponding list. +// TODO(nurhan): Remove the failing test after fixing. +const Map> blockedTestsListsMapForRenderBackends = + >{ + 'auto': [], + 'html': [], + // This test failed on canvaskit on all three build modes. + 'canvaskit': ['image_loading_integration.dart'], +}; diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index c9dfacdb3..872698d20 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -90,6 +90,8 @@ class TestCommand extends Command with ArgUtils { SupportedBrowsers.instance.argParsers .forEach((t) => t.populateOptions(argParser)); + GeneralTestsArgumentParser.instance.populateOptions(argParser); + IntegrationTestsArgumentParser.instance.populateOptions(argParser); } @override @@ -133,6 +135,7 @@ class TestCommand extends Command with ArgUtils { Future run() async { SupportedBrowsers.instance ..argParsers.forEach((t) => t.parseOptions(argResults)); + GeneralTestsArgumentParser.instance.parseOptions(argResults); // Check the flags to see what type of integration tests are requested. testTypesRequested = findTestType(); @@ -165,6 +168,8 @@ class TestCommand extends Command with ArgUtils { } Future runIntegrationTests() async { + // Parse additional arguments specific for integration testing. + IntegrationTestsArgumentParser.instance.parseOptions(argResults); if(!_testPreparationReady) { await _prepare(); } @@ -204,7 +209,9 @@ class TestCommand extends Command with ArgUtils { // If screenshot tests are available, fetch the screenshot goldens. if (isScreenshotTestsAvailable) { - print('screenshot tests available'); + if (isVerboseLoggingEnabled) { + print('INFO: Screenshot tests available'); + } final GoldensRepoFetcher goldensRepoFetcher = GoldensRepoFetcher( environment.webUiGoldensRepositoryDirectory, path.join(environment.webUiDevDir.path, 'goldens_lock.yaml')); diff --git a/lib/web_ui/dev/utils.dart b/lib/web_ui/dev/utils.dart index 72a1b295a..662550ad0 100644 --- a/lib/web_ui/dev/utils.dart +++ b/lib/web_ui/dev/utils.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:io' as io; +import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; @@ -185,6 +186,37 @@ mixin ArgUtils on Command { } } +/// Parses additional options that can be used for all tests. +class GeneralTestsArgumentParser { + static final GeneralTestsArgumentParser _singletonInstance = + GeneralTestsArgumentParser._(); + + /// The [GeneralTestsArgumentParser] singleton. + static GeneralTestsArgumentParser get instance => _singletonInstance; + + GeneralTestsArgumentParser._(); + + /// If target name is provided integration tests can run that one test + /// instead of running all the tests. + bool verbose = false; + + void populateOptions(ArgParser argParser) { + argParser + ..addFlag( + 'verbose', + defaultsTo: false, + help: 'Flag to indicate extra logs should also be printed.', + ); + } + + /// Populate results of the arguments passed. + void parseOptions(ArgResults argResults) { + verbose = argResults['verbose'] as bool; + } +} + +bool get isVerboseLoggingEnabled => GeneralTestsArgumentParser.instance.verbose; + /// There might be proccesses started during the tests. /// /// Use this list to store those Processes, for cleaning up before shutdown. diff --git a/web_sdk/web_test_utils/pubspec.yaml b/web_sdk/web_test_utils/pubspec.yaml index 7f92e2348..15b2cacc1 100644 --- a/web_sdk/web_test_utils/pubspec.yaml +++ b/web_sdk/web_test_utils/pubspec.yaml @@ -1,7 +1,7 @@ name: web_test_utils environment: - sdk: ">=2.2.0 <3.0.0" + sdk: ">=2.11.0-0 <3.0.0" dependencies: path: 1.8.0-nullsafety.3 -- GitLab