| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:async'; |
| |
| import 'package:build/build.dart'; |
| import 'package:glob/glob.dart'; |
| import 'package:html/dom.dart'; |
| import 'package:html/parser.dart'; |
| import 'package:path/path.dart' as p; |
| |
| const _inputExtension = '_test.dart'; |
| const _outputExtension = '_test.debug.html'; |
| |
| /// Returns the (optional) user-provided HTML file to use as an input. |
| /// |
| /// For example, for `test/foo_test.dart`, we look for `test/foo_test.html`. |
| AssetId _customHtmlId(AssetId test) => test.changeExtension('.html'); |
| |
| /// Returns the builder-generated HTML file for browsers to navigate to. |
| AssetId _debugHtmlId(AssetId test) => test.changeExtension('.debug.html'); |
| |
| /// Returns the JS script path for the browser for [dartTest]. |
| String _jsScriptPath(AssetId dartTest) => '${p.url.basename(dartTest.path)}.js'; |
| |
| /// Generates a `*.debug.html` for every file in `test/**/*_test.dart`. |
| /// |
| /// This is normally used in order to use Chrome (or another browser's) |
| /// debugging tools (such as setting breakpoints) while running a test suite. |
| class DebugTestBuilder implements Builder { |
| /// Generates a `.debug.html` for the provided `._test.dart` file. |
| static Future<void> _generateDebugHtml( |
| BuildStep buildStep, |
| AssetId dartTest, |
| ) async { |
| final customHtmlId = _customHtmlId(dartTest); |
| final jsScriptPath = _jsScriptPath(buildStep.inputId); |
| String debugHtml; |
| if (await buildStep.canRead(customHtmlId)) { |
| debugHtml = _replaceCustomHtml( |
| await buildStep.readAsString(customHtmlId), |
| jsScriptPath, |
| ); |
| } else { |
| debugHtml = _createDebugHtml(jsScriptPath); |
| } |
| return buildStep.writeAsString(_debugHtmlId(dartTest), debugHtml); |
| } |
| |
| /// Returns the content of [customHtml] modified to work with this package. |
| static String _replaceCustomHtml(String customHtml, String jsScriptPath) { |
| final document = parse(customHtml); |
| |
| // Replace <link rel="x-dart-test"> with <script src="{jsScriptPath}">. |
| final linkTag = document.querySelector('link[rel="x-dart-test"]'); |
| final scriptTag = Element.tag('script'); |
| scriptTag.attributes['src'] = jsScriptPath; |
| linkTag.replaceWith(scriptTag); |
| |
| // Remove the <script src="packages/test/dart.js"></script> if present. |
| document.querySelector('script[src="packages/test/dart.js"]')?.remove(); |
| |
| return document.outerHtml; |
| } |
| |
| static String _createDebugHtml(String jsScriptPath) => '' |
| '<html>\n' |
| ' <head>\n' |
| ' <script src="$jsScriptPath"></script>\n' |
| ' </head>\n' |
| '</html>\n'; |
| |
| const DebugTestBuilder(); |
| |
| @override |
| final buildExtensions = const { |
| _inputExtension: [_outputExtension], |
| }; |
| |
| @override |
| Future<void> build(BuildStep buildStep) { |
| return _generateDebugHtml(buildStep, buildStep.inputId); |
| } |
| } |
| |
| /// Generates `text/index.html`, useful for navigating and running tests. |
| class DebugIndexBuilder implements Builder { |
| static final _allTests = Glob(p.url.join('test', '**$_inputExtension')); |
| |
| static AssetId _outputFor(BuildStep buildStep) { |
| return AssetId(buildStep.inputId.package, p.url.join('test', 'index.html')); |
| } |
| |
| static String _generateHtml(Iterable<AssetId> tests) { |
| if (tests.isEmpty) { |
| return '<strong>No tests found!</strong>'; |
| } |
| final buffer = StringBuffer(' <ul>'); |
| for (final test in tests) { |
| final path = |
| p.url.joinAll(p.url.split(_debugHtmlId(test).path)..removeAt(0)); |
| buffer.writeln(' <li><a href="/$path">${test.path}</a></li>'); |
| } |
| buffer.writeln(' </ul>'); |
| return buffer.toString(); |
| } |
| |
| const DebugIndexBuilder(); |
| |
| @override |
| final buildExtensions = const { |
| r'$test$': ['index.html'], |
| }; |
| |
| @override |
| Future<void> build(BuildStep buildStep) async { |
| final files = await buildStep.findAssets(_allTests).toList(); |
| final output = _outputFor(buildStep); |
| return buildStep.writeAsString( |
| output, |
| '<html>\n' |
| ' <body>\n' |
| ' ${_generateHtml(files)}\n' |
| ' </body>\n' |
| '</html>'); |
| } |
| } |