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);
+ });
});
}