From 8b44e52ea1be6f6aba7f60d80c87e11224d0a731 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 12 Nov 2015 09:13:37 -0700 Subject: [PATCH] Add option to run browser tests using `pub serve` in coverage/test tasks --- .gitignore | 1 + README.md | 12 ++ lib/src/reporter.dart | 31 ++-- lib/src/tasks/coverage/api.dart | 141 +++++++++++++----- lib/src/tasks/coverage/cli.dart | 7 + lib/src/tasks/coverage/config.dart | 2 + lib/src/tasks/serve/api.dart | 114 ++++++++++++++ lib/src/tasks/test/api.dart | 2 + lib/src/tasks/test/cli.dart | 55 ++++++- lib/src/tasks/test/config.dart | 2 + .../lib/coverage_browser.dart | 11 ++ .../lib/transformer.dart | 20 +++ .../browser_needs_pub_serve/pubspec.yaml | 15 ++ .../test/browser_custom_test.dart | 14 ++ .../test/browser_custom_test.html | 5 + .../test/browser_test.dart | 11 ++ .../browser_needs_pub_serve/tool/dev.dart | 9 ++ .../test/needs_pub_serve/lib/transformer.dart | 20 +++ .../test/needs_pub_serve/pubspec.yaml | 15 ++ .../test/needs_pub_serve/test/unit_test.dart | 11 ++ .../test/needs_pub_serve/tool/dev.dart | 12 ++ test/integration/coverage_test.dart | 11 ++ test/integration/test_test.dart | 5 + 23 files changed, 474 insertions(+), 52 deletions(-) create mode 100644 lib/src/tasks/serve/api.dart create mode 100644 test/fixtures/coverage/browser_needs_pub_serve/lib/coverage_browser.dart create mode 100644 test/fixtures/coverage/browser_needs_pub_serve/lib/transformer.dart create mode 100644 test/fixtures/coverage/browser_needs_pub_serve/pubspec.yaml create mode 100644 test/fixtures/coverage/browser_needs_pub_serve/test/browser_custom_test.dart create mode 100644 test/fixtures/coverage/browser_needs_pub_serve/test/browser_custom_test.html create mode 100644 test/fixtures/coverage/browser_needs_pub_serve/test/browser_test.dart create mode 100644 test/fixtures/coverage/browser_needs_pub_serve/tool/dev.dart create mode 100644 test/fixtures/test/needs_pub_serve/lib/transformer.dart create mode 100644 test/fixtures/test/needs_pub_serve/pubspec.yaml create mode 100644 test/fixtures/test/needs_pub_serve/test/unit_test.dart create mode 100644 test/fixtures/test/needs_pub_serve/tool/dev.dart diff --git a/.gitignore b/.gitignore index 1deacf77..b4b87dba 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ pubspec.lock # generated assets in test fixtures /test/fixtures/coverage/browser/coverage/ +/test/fixtures/coverage/browser_needs_pub_serve/coverage/ /test/fixtures/coverage/non_test_file/coverage/ /test/fixtures/coverage/vm/coverage/ /test/fixtures/docs/docs/doc/api/ diff --git a/README.md b/README.md index 15974ee3..f49d5b9b 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,12 @@ configuration from the `config.test` object. ['lib/'] List of paths to include in the generated coverage report (LCOV and HTML). + + pubServe + bool + false + Whether or not to serve browser tests using a Pub server.
If true, make sure to follow the test package's setup instructions and include the test/pub_serve transformer. + @@ -413,6 +419,12 @@ object. ['test/'] Unit test locations. Items in this list can be directories and/or files. + + pubServe + bool + false + Whether or not to serve browser tests using a Pub server.
If true, make sure to follow the test package's setup instructions and include the test/pub_serve transformer. + * Individual test files can be executed by appending their path to the end of the command. diff --git a/lib/src/reporter.dart b/lib/src/reporter.dart index 6df9e12d..760da9cc 100644 --- a/lib/src/reporter.dart +++ b/lib/src/reporter.dart @@ -44,24 +44,31 @@ class Reporter { _log(stdout, message, shout: shout); } + static String _indent(String lines) { + return ' ' + lines.replaceAll('\n', '\n '); + } + void logGroup(String title, {String output, + String error, Stream outputStream, Stream errorStream}) { - log(colorBlue('\n::: $title')); - if (output != null) { - log('${output.split('\n').join('\n ')}'); - return; - } + var formattedTitle = colorBlue('\n::: $title'); - if (outputStream != null) { - outputStream.listen((line) { - log(' $line'); + if (output != null) { + log(formattedTitle); + log(_indent(output)); + } else if (error != null) { + warning(formattedTitle); + warning(_indent(error)); + } else { + log(formattedTitle); + + outputStream?.listen((line) { + log(_indent(line)); }); - } - if (errorStream != null) { - errorStream.listen((line) { - warning(' $line'); + errorStream?.listen((line) { + warning(_indent(line)); }); } } diff --git a/lib/src/tasks/coverage/api.dart b/lib/src/tasks/coverage/api.dart index 300c14d9..57f9b763 100644 --- a/lib/src/tasks/coverage/api.dart +++ b/lib/src/tasks/coverage/api.dart @@ -24,7 +24,9 @@ import 'package:path/path.dart' as path; import 'package:dart_dev/src/platform_util/api.dart' as platform_util; import 'package:dart_dev/src/tasks/coverage/config.dart'; import 'package:dart_dev/src/tasks/coverage/exceptions.dart'; +import 'package:dart_dev/src/tasks/serve/api.dart'; import 'package:dart_dev/src/tasks/task.dart'; +import 'package:dart_dev/src/tasks/test/config.dart'; const String _dartFilePattern = '.dart'; const String _testFilePattern = '_test.dart'; @@ -85,14 +87,18 @@ class CoverageTask extends Task { /// [tests] will be searched (recursively) for all files ending in /// "_test.dart" and all matching files will be run as tests. /// + /// If [pubServe] is true, a Pub server will be automatically started and + /// used to run any browser tests. + /// /// If [html] is true, `genhtml` will be used to generate an HTML report of /// the collected coverage and the report will be opened. static CoverageTask start(List tests, {bool html: defaultHtml, + bool pubServe: defaultPubServe, String output: defaultOutput, List reportOn: defaultReportOn}) { - CoverageTask coverage = - new CoverageTask._(tests, reportOn, html: html, output: output); + CoverageTask coverage = new CoverageTask._(tests, reportOn, + pubServe: pubServe, html: html, output: output); coverage._run(); return coverage; } @@ -120,6 +126,10 @@ class CoverageTask extends Task { /// Whether or not to generate the HTML report. bool _html = defaultHtml; + /// Whether to automatically start and use a Pub server when running + /// browser tests. + final bool pubServe; + /// File created to run the test in a browser. Need to store it so it can be /// cleaned up after the test finishes. File _lastHtmlFile; @@ -135,7 +145,9 @@ class CoverageTask extends Task { List _reportOn; CoverageTask._(List tests, List reportOn, - {bool html: defaultHtml, String output: defaultOutput}) + {bool html: defaultHtml, + bool this.pubServe: defaultPubServe, + String output: defaultOutput}) : _html = html, _outputDirectory = new Directory(output), _reportOn = reportOn { @@ -407,45 +419,98 @@ class CoverageTask extends Task { String _testsPassedPattern = 'All tests passed!'; if (isBrowserTest) { - // Run the test in content-shell. - String executable = 'content_shell'; - List args = [htmlFile.path]; - _coverageOutput.add(''); - _coverageOutput.add('Running test suite ${file.path}'); - _coverageOutput.add('$executable ${args.join(' ')}\n'); - TaskProcess process = - _lastTestProcess = new TaskProcess('content_shell', args); + PubServeTask pubServeTask; - // Content-shell dumps render tree to stderr, which is where the test - // results will be. The observatory port should be output to stderr as - // well, but it is sometimes malformed. In those cases, the correct - // observatory port is output to stdout. So we listen to both. - int observatoryPort; - process.stdout.listen((line) { - _coverageOutput.add(' $line'); - if (line.contains(_observatoryPortPattern)) { - Match m = _observatoryPortPattern.firstMatch(line); - observatoryPort = int.parse(m.group(2)); - } - }); - await for (String line in process.stderr) { - _coverageOutput.add(' $line'); - if (line.contains(_observatoryFailPattern)) { - throw new CoverageTestSuiteException(file.path); - } - if (line.contains(_observatoryPortPattern)) { - Match m = _observatoryPortPattern.firstMatch(line); - observatoryPort = int.parse(m.group(2)); - } - if (line.contains(_testsFailedPattern)) { - throw new CoverageTestSuiteException(file.path); + try { + String testPath; + if (pubServe) { + _coverageOutput.add('Starting Pub server...'); + + // Start `pub serve` on the `test` directory. + pubServeTask = startPubServe(additionalArgs: ['test']); + + _coverageOutput.add('::: ${pubServeTask.command}'); + String indentLine(String line) => ' $line'; + + var startupLogFinished = new Completer(); + pubServeTask.stdOut + .transform(until(startupLogFinished.future)) + .map(indentLine) + .listen(_coverageOutput.add); + pubServeTask.stdErr + .transform(until(startupLogFinished.future)) + .map(indentLine) + .listen(_coverageErrorOutput.add); + + PubServeInfo serveInfo = await pubServeTask.serveInfos.first; + if (!path.isWithin(serveInfo.directory, htmlFile.path)) { + throw '`pub serve` directory does not contain test file: ${htmlFile.path}'; + } + + var relativeHtmlPath = + path.relative(htmlFile.path, from: serveInfo.directory); + testPath = 'http://localhost:${serveInfo.port}/$relativeHtmlPath'; + + startupLogFinished.complete(); + pubServeTask.stdOut.map(indentLine).join('\n').then((stdOut) { + if (stdOut.isNotEmpty) { + _coverageOutput + .add('`${pubServeTask.command}` (buffered stdout)'); + _coverageOutput.add(stdOut); + } + }); + pubServeTask.stdErr.map(indentLine).join('\n').then((stdErr) { + if (stdErr.isNotEmpty) { + _coverageOutput + .add('`${pubServeTask.command}` (buffered stdout)'); + _coverageOutput.add(stdErr); + } + }); + } else { + testPath = htmlFile.path; } - if (line.contains(_testsPassedPattern)) { - break; + + // Run the test in content-shell. + String executable = 'content_shell'; + List args = [testPath]; + _coverageOutput.add(''); + _coverageOutput.add('Running test suite ${file.path}'); + _coverageOutput.add('$executable ${args.join(' ')}\n'); + TaskProcess process = + _lastTestProcess = new TaskProcess('content_shell', args); + + // Content-shell dumps render tree to stderr, which is where the test + // results will be. The observatory port should be output to stderr as + // well, but it is sometimes malformed. In those cases, the correct + // observatory port is output to stdout. So we listen to both. + int observatoryPort; + process.stdout.listen((line) { + _coverageOutput.add(' $line'); + if (line.contains(_observatoryPortPattern)) { + Match m = _observatoryPortPattern.firstMatch(line); + observatoryPort = int.parse(m.group(2)); + } + }); + await for (String line in process.stderr) { + _coverageOutput.add(' $line'); + if (line.contains(_observatoryFailPattern)) { + throw new CoverageTestSuiteException(file.path); + } + if (line.contains(_observatoryPortPattern)) { + Match m = _observatoryPortPattern.firstMatch(line); + observatoryPort = int.parse(m.group(2)); + } + if (line.contains(_testsFailedPattern)) { + throw new CoverageTestSuiteException(file.path); + } + if (line.contains(_testsPassedPattern)) { + break; + } } + return observatoryPort; + } finally { + pubServeTask?.stop(); } - - return observatoryPort; } else { // Find an open port to observe the Dart VM on. int port = await getOpenPort(); diff --git a/lib/src/tasks/coverage/cli.dart b/lib/src/tasks/coverage/cli.dart index 1584c2a2..92d8d0a7 100644 --- a/lib/src/tasks/coverage/cli.dart +++ b/lib/src/tasks/coverage/cli.dart @@ -40,6 +40,10 @@ class CoverageCli extends TaskCli { negatable: true, defaultsTo: defaultHtml, help: 'Generate and open an HTML report.') + ..addFlag('pub-serve', + negatable: true, + defaultsTo: defaultPubServe, + help: 'Serves browser tests using a Pub server.') ..addFlag('open', negatable: true, defaultsTo: true, @@ -61,6 +65,8 @@ class CoverageCli extends TaskCli { } bool html = TaskCli.valueOf('html', parsedArgs, config.coverage.html); + bool pubServe = + TaskCli.valueOf('pub-serve', parsedArgs, config.coverage.pubServe); bool open = TaskCli.valueOf('open', parsedArgs, true); List tests = []; @@ -85,6 +91,7 @@ class CoverageCli extends TaskCli { try { CoverageTask task = CoverageTask.start(tests, html: html, + pubServe: pubServe, output: config.coverage.output, reportOn: config.coverage.reportOn); reporter.logGroup('Collecting coverage', diff --git a/lib/src/tasks/coverage/config.dart b/lib/src/tasks/coverage/config.dart index 5b882f76..b67b6786 100644 --- a/lib/src/tasks/coverage/config.dart +++ b/lib/src/tasks/coverage/config.dart @@ -15,6 +15,7 @@ library dart_dev.src.tasks.coverage.config; import 'package:dart_dev/src/tasks/config.dart'; +import 'package:dart_dev/src/tasks/test/config.dart'; const bool defaultHtml = true; const String defaultOutput = 'coverage/'; @@ -22,6 +23,7 @@ const List defaultReportOn = const ['lib/']; class CoverageConfig extends TaskConfig { bool html = defaultHtml; + bool pubServe = defaultPubServe; String output = defaultOutput; List reportOn = defaultReportOn; } diff --git a/lib/src/tasks/serve/api.dart b/lib/src/tasks/serve/api.dart new file mode 100644 index 00000000..f3e14bd0 --- /dev/null +++ b/lib/src/tasks/serve/api.dart @@ -0,0 +1,114 @@ +// Copyright 2015 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +library dart_dev.src.tasks.serve.api; + +import 'dart:async'; + +import 'package:dart_dev/util.dart' show TaskProcess; + +/// A regular expression that matches the output of "pub serve". +final RegExp _servingRegExp = + new RegExp(r'^Serving [^ ]+ +([^ ]+) +on http://localhost:(\d+)$'); + +/// Starts a Pub server on the specified [port] and returns the associated +/// [PubServeTask]. +/// +/// If [port] is 0, `pub serve` will pick its own port automatically. +PubServeTask startPubServe({int port: 0, List additionalArgs}) { + var pubServeExecutable = 'pub'; + var pubServeArgs = ['serve', '--port=$port']; + if (additionalArgs != null) { + pubServeArgs.addAll(additionalArgs); + } + + TaskProcess pubServeProcess = + new TaskProcess(pubServeExecutable, pubServeArgs); + + var pubServeInfos = new StreamController(); + + var task = new PubServeTask('$pubServeExecutable ${pubServeArgs.join(' ')}', + pubServeInfos.stream, pubServeProcess.done, pubServeProcess.kill); + + pubServeProcess.stdout.listen((String line) { + task._pubServeStdOut.add(line); + + var match = _servingRegExp.firstMatch(line); + if (match != null) { + var directory = match[1]; + var port = int.parse(match[2]); + pubServeInfos.add(new PubServeInfo(directory, port)); + } + }); + + pubServeProcess.stderr.listen(task._pubServeStdErr.add); + + return task; +} + +class PubServeInfo { + final int port; + final String directory; + + PubServeInfo(String this.directory, int this.port); +} + +class PubServeTask { + final String command; + final Stream serveInfos; + final Future done; + final Function _stop; + + StreamController _pubServeStdOut = new StreamController.broadcast(); + StreamController _pubServeStdErr = new StreamController.broadcast(); + + PubServeTask(String this.command, Stream this.serveInfos, + Future this.done, void this._stop()) { + done.then((_) { + _pubServeStdOut.close(); + _pubServeStdErr.close(); + }); + } + + void stop() { + _stop(); + } + + Stream get stdOut => _pubServeStdOut.stream; + Stream get stdErr => _pubServeStdErr.stream; +} + +/// Returns a Stream that proxies [stream] until [cancelled] completes or [stream] closes. +StreamTransformer until(Future cancelled) { + return new StreamTransformer((Stream input, bool cancelOnError) { + StreamController controller; + StreamSubscription subscription; + controller = new StreamController(onListen: () { + subscription = input.listen(controller.add, + onError: controller.addError, + onDone: controller.close, + cancelOnError: cancelOnError); + }, + onPause: () => subscription.pause(), + onResume: () => subscription.resume(), + onCancel: () => subscription.cancel(), + sync: true); + + cancelled.then((_) { + controller.close(); + }); + + return controller.stream.listen(null); + }); +} diff --git a/lib/src/tasks/test/api.dart b/lib/src/tasks/test/api.dart index b7df3916..735ef207 100644 --- a/lib/src/tasks/test/api.dart +++ b/lib/src/tasks/test/api.dart @@ -22,6 +22,7 @@ import 'package:dart_dev/src/tasks/task.dart'; TestTask test( {int concurrency, + List additionalArgs: const [], List platforms: const [], List tests: const []}) { var executable = 'pub'; @@ -33,6 +34,7 @@ TestTask test( args.addAll(['-p', p]); }); args.addAll(['--reporter=expanded']); + args.addAll(additionalArgs); args.addAll(tests); TaskProcess process = new TaskProcess(executable, args); diff --git a/lib/src/tasks/test/cli.dart b/lib/src/tasks/test/cli.dart index 8f7bbf9f..42e43e94 100644 --- a/lib/src/tasks/test/cli.dart +++ b/lib/src/tasks/test/cli.dart @@ -24,6 +24,7 @@ import 'package:dart_dev/util.dart' show reporter; import 'package:dart_dev/src/platform_util/api.dart' as platform_util; import 'package:dart_dev/src/tasks/cli.dart'; import 'package:dart_dev/src/tasks/config.dart'; +import 'package:dart_dev/src/tasks/serve/api.dart'; import 'package:dart_dev/src/tasks/test/api.dart'; import 'package:dart_dev/src/tasks/test/config.dart'; @@ -37,6 +38,10 @@ class TestCli extends TaskCli { abbr: 'j', defaultsTo: '$defaultConcurrency', help: 'The number of concurrent test suites run.') + ..addFlag('pub-serve', + negatable: true, + defaultsTo: defaultPubServe, + help: 'Serves browser tests using a Pub server.') ..addOption('platform', abbr: 'p', allowMultiple: true, @@ -76,11 +81,16 @@ class TestCli extends TaskCli { .hasImmediateDependency('test')) return new CliResult.fail( 'Package "test" must be an immediate dependency in order to run its executables.'); + List additionalArgs = []; + bool unit = parsedArgs['unit']; bool integration = parsedArgs['integration']; List tests = []; int individualTests = 0; + bool pubServe = + TaskCli.valueOf('pub-serve', parsedArgs, config.test.pubServe); + var concurrency = TaskCli.valueOf('concurrency', parsedArgs, config.test.concurrency); if (concurrency is String) { @@ -118,10 +128,51 @@ class TestCli extends TaskCli { } } - TestTask task = - test(tests: tests, concurrency: concurrency, platforms: platforms); + PubServeTask pubServeTask; + if (pubServe) { + // Start `pub serve` on the `test` directory + pubServeTask = startPubServe(additionalArgs: ['test']); + + var startupLogFinished = new Completer(); + reporter.logGroup(pubServeTask.command, + outputStream: + pubServeTask.stdOut.transform(until(startupLogFinished.future)), + errorStream: + pubServeTask.stdErr.transform(until(startupLogFinished.future))); + + var serveInfo = await pubServeTask.serveInfos.first; + additionalArgs.add('--pub-serve=${serveInfo.port}'); + + startupLogFinished.complete(); + pubServeTask.stdOut.join('\n').then((stdOut) { + if (stdOut.isNotEmpty) { + reporter.logGroup('`${pubServeTask.command}` (buffered stdout)', + output: stdOut); + } + }); + pubServeTask.stdErr.join('\n').then((stdErr) { + if (stdErr.isNotEmpty) { + reporter.logGroup('`${pubServeTask.command}` (buffered stderr)', + error: stdErr); + } + }); + } + + TestTask task = test( + tests: tests, + concurrency: concurrency, + platforms: platforms, + additionalArgs: additionalArgs); reporter.logGroup(task.testCommand, outputStream: task.testOutput); + await task.done; + + if (pubServeTask != null) { + pubServeTask.stop(); + // Wait for the task to finish to flush its output. + await pubServeTask.done; + } + return task.successful ? new CliResult.success(task.testSummary) : new CliResult.fail(task.testSummary); diff --git a/lib/src/tasks/test/config.dart b/lib/src/tasks/test/config.dart index 9044d98f..bad69dd2 100644 --- a/lib/src/tasks/test/config.dart +++ b/lib/src/tasks/test/config.dart @@ -17,6 +17,7 @@ library dart_dev.src.tasks.test.config; import 'package:dart_dev/src/tasks/config.dart'; const int defaultConcurrency = 4; +const bool defaultPubServe = false; const bool defaultIntegration = false; const List defaultIntegrationTests = const []; const bool defaultUnit = true; @@ -25,6 +26,7 @@ const List defaultPlatforms = const []; class TestConfig extends TaskConfig { int concurrency = defaultConcurrency; + bool pubServe = defaultPubServe; List integrationTests = defaultIntegrationTests; List platforms = defaultPlatforms; List unitTests = defaultUnitTests; diff --git a/test/fixtures/coverage/browser_needs_pub_serve/lib/coverage_browser.dart b/test/fixtures/coverage/browser_needs_pub_serve/lib/coverage_browser.dart new file mode 100644 index 00000000..5d77493e --- /dev/null +++ b/test/fixtures/coverage/browser_needs_pub_serve/lib/coverage_browser.dart @@ -0,0 +1,11 @@ +library coverage_browser; + +import 'dart:html'; + +void notCovered() { + print('nope'); +} + +final bool wasTransformed = false; + +bool works() => document is Document && wasTransformed; diff --git a/test/fixtures/coverage/browser_needs_pub_serve/lib/transformer.dart b/test/fixtures/coverage/browser_needs_pub_serve/lib/transformer.dart new file mode 100644 index 00000000..3ea35d29 --- /dev/null +++ b/test/fixtures/coverage/browser_needs_pub_serve/lib/transformer.dart @@ -0,0 +1,20 @@ +library needs_pub_serve.transformer; + +import 'dart:async'; + +import 'package:barback/barback.dart'; + +class FalseToTrueTransformer extends Transformer { + FalseToTrueTransformer.asPlugin(); + + String get allowedExtensions => '.dart'; + + @override + Future apply(Transform transform) async { + var contents = await transform.primaryInput.readAsString(); + var transformedContents = contents.replaceAll( + 'bool wasTransformed = false', 'bool wasTransformed = true'); + transform.addOutput( + new Asset.fromString(transform.primaryInput.id, transformedContents)); + } +} diff --git a/test/fixtures/coverage/browser_needs_pub_serve/pubspec.yaml b/test/fixtures/coverage/browser_needs_pub_serve/pubspec.yaml new file mode 100644 index 00000000..b48499d5 --- /dev/null +++ b/test/fixtures/coverage/browser_needs_pub_serve/pubspec.yaml @@ -0,0 +1,15 @@ +name: coverage_browser_needs_pub_serve +version: 0.0.0 +dev_dependencies: + coverage: "^0.7.2" + dart_dev: + path: ../../../.. + test: "^0.12.0" +transformers: + # Add the necessary `test` package transformer + # See https://github.com/dart-lang/test#testing-with-barback + - test/pub_serve: + $include: test/**_test{.*,}.dart + # Add a custom transformer that makes the test pass. + - coverage_browser_needs_pub_serve: + $include: lib/coverage_browser.dart diff --git a/test/fixtures/coverage/browser_needs_pub_serve/test/browser_custom_test.dart b/test/fixtures/coverage/browser_needs_pub_serve/test/browser_custom_test.dart new file mode 100644 index 00000000..05d27bd2 --- /dev/null +++ b/test/fixtures/coverage/browser_needs_pub_serve/test/browser_custom_test.dart @@ -0,0 +1,14 @@ +@TestOn('browser') +library coverage.browser_needs_pub_serve.test.browser_custom_test; + +import 'dart:js' show context; + +import 'package:coverage_browser_needs_pub_serve/coverage_browser.dart' as lib; +import 'package:test/test.dart'; + +main() { + test('browser test', () { + expect(lib.works(), isTrue); + expect(context['customScript'], isTrue); + }); +} diff --git a/test/fixtures/coverage/browser_needs_pub_serve/test/browser_custom_test.html b/test/fixtures/coverage/browser_needs_pub_serve/test/browser_custom_test.html new file mode 100644 index 00000000..0c0a5718 --- /dev/null +++ b/test/fixtures/coverage/browser_needs_pub_serve/test/browser_custom_test.html @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/test/fixtures/coverage/browser_needs_pub_serve/test/browser_test.dart b/test/fixtures/coverage/browser_needs_pub_serve/test/browser_test.dart new file mode 100644 index 00000000..d5f3b2d7 --- /dev/null +++ b/test/fixtures/coverage/browser_needs_pub_serve/test/browser_test.dart @@ -0,0 +1,11 @@ +@TestOn('browser') +library coverage.browser.test.browser_test; + +import 'package:coverage_browser_needs_pub_serve/coverage_browser.dart' as lib; +import 'package:test/test.dart'; + +main() { + test('browser test', () { + expect(lib.works(), isTrue); + }); +} diff --git a/test/fixtures/coverage/browser_needs_pub_serve/tool/dev.dart b/test/fixtures/coverage/browser_needs_pub_serve/tool/dev.dart new file mode 100644 index 00000000..fa198e34 --- /dev/null +++ b/test/fixtures/coverage/browser_needs_pub_serve/tool/dev.dart @@ -0,0 +1,9 @@ +library tool.dev; + +import 'package:dart_dev/dart_dev.dart' show dev, config; + +main(List args) async { + config.coverage..pubServe = true; + + await dev(args); +} diff --git a/test/fixtures/test/needs_pub_serve/lib/transformer.dart b/test/fixtures/test/needs_pub_serve/lib/transformer.dart new file mode 100644 index 00000000..3ea35d29 --- /dev/null +++ b/test/fixtures/test/needs_pub_serve/lib/transformer.dart @@ -0,0 +1,20 @@ +library needs_pub_serve.transformer; + +import 'dart:async'; + +import 'package:barback/barback.dart'; + +class FalseToTrueTransformer extends Transformer { + FalseToTrueTransformer.asPlugin(); + + String get allowedExtensions => '.dart'; + + @override + Future apply(Transform transform) async { + var contents = await transform.primaryInput.readAsString(); + var transformedContents = contents.replaceAll( + 'bool wasTransformed = false', 'bool wasTransformed = true'); + transform.addOutput( + new Asset.fromString(transform.primaryInput.id, transformedContents)); + } +} diff --git a/test/fixtures/test/needs_pub_serve/pubspec.yaml b/test/fixtures/test/needs_pub_serve/pubspec.yaml new file mode 100644 index 00000000..c847115e --- /dev/null +++ b/test/fixtures/test/needs_pub_serve/pubspec.yaml @@ -0,0 +1,15 @@ +name: test_needs_pub_serve +version: 0.0.0 +dev_dependencies: + dart_dev: + path: ../../../.. + test: "^0.12.0" + barback: "^0.15.2" +transformers: + # Add the necessary `test` package transformer + # See https://github.com/dart-lang/test#testing-with-barback + - test/pub_serve: + $include: test/**_test{.*,}.dart + # Add a custom transformer that makes the test pass. + - test_needs_pub_serve: + $include: test/unit_test.dart diff --git a/test/fixtures/test/needs_pub_serve/test/unit_test.dart b/test/fixtures/test/needs_pub_serve/test/unit_test.dart new file mode 100644 index 00000000..8e939aa1 --- /dev/null +++ b/test/fixtures/test/needs_pub_serve/test/unit_test.dart @@ -0,0 +1,11 @@ +library test_failing.test.passing_unit_test; + +import 'package:test/test.dart'; + +final bool wasTransformed = false; + +void main() { + test('passes', () { + expect(wasTransformed, isTrue); + }); +} diff --git a/test/fixtures/test/needs_pub_serve/tool/dev.dart b/test/fixtures/test/needs_pub_serve/tool/dev.dart new file mode 100644 index 00000000..3f5e096b --- /dev/null +++ b/test/fixtures/test/needs_pub_serve/tool/dev.dart @@ -0,0 +1,12 @@ +library tool.dev; + +import 'package:dart_dev/dart_dev.dart' show dev, config; + +main(List args) async { + config.test + ..pubServe = true + ..unitTests = ['test/unit_test.dart'] + ..platforms = ['content-shell']; + + await dev(args); +} diff --git a/test/integration/coverage_test.dart b/test/integration/coverage_test.dart index 0c1287b0..46d9e9a7 100644 --- a/test/integration/coverage_test.dart +++ b/test/integration/coverage_test.dart @@ -24,6 +24,8 @@ import 'package:test/test.dart'; const String projectWithDartFile = 'test/fixtures/coverage/non_test_file'; const String projectWithBrowserTests = 'test/fixtures/coverage/browser'; const String projectWithVmTests = 'test/fixtures/coverage/vm'; +const String projectWithBrowserTestsThatNeedsPubServe = + 'test/fixtures/coverage/browser_needs_pub_serve'; const String projectWithoutCoveragePackage = 'test/fixtures/coverage/no_coverage_package'; @@ -51,6 +53,15 @@ void main() { expect(lcov.existsSync(), isTrue); }, timeout: new Timeout(new Duration(seconds: 60))); + test('should generate coverage for Browser tests that require a Pub server', + () async { + expect( + await runCoverage(projectWithBrowserTestsThatNeedsPubServe), isTrue); + File lcov = new File( + '$projectWithBrowserTestsThatNeedsPubServe/coverage/coverage.lcov'); + expect(lcov.existsSync(), isTrue); + }, timeout: new Timeout(new Duration(seconds: 60))); + test('should generate coverage for VM tests', () async { expect(await runCoverage(projectWithVmTests), isTrue); File lcov = new File('$projectWithVmTests/coverage/coverage.lcov'); diff --git a/test/integration/test_test.dart b/test/integration/test_test.dart index 4cb9f340..418f938b 100644 --- a/test/integration/test_test.dart +++ b/test/integration/test_test.dart @@ -24,6 +24,7 @@ import 'package:test/test.dart'; const String projectWithoutTestPackage = 'test/fixtures/test/no_test_package'; const String projectWithFailingTests = 'test/fixtures/test/failing'; const String projectWithPassingTests = 'test/fixtures/test/passing'; +const String projectThatNeedsPubServe = 'test/fixtures/test/needs_pub_serve'; Future runTests(String projectPath, {bool unit: true, bool integration: false, List files}) async { @@ -114,5 +115,9 @@ void main() { test('should warn if "test" package is not immediate dependency', () async { expect(await runTests(projectWithoutTestPackage), isFalse); }); + + test('should run tests that require a Pub server', () async { + expect(await runTests(projectThatNeedsPubServe), isTrue); + }); }); }