| // Copyright (c) 2023, 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 'dart:convert'; | |
| import 'dart:io'; | |
| import 'package:path/path.dart' as p; | |
| import 'package:shelf/shelf.dart' as shelf; | |
| import 'package:test_api/backend.dart' show StackTraceMapper, SuitePlatform; | |
| import 'package:test_core/src/runner/configuration.dart'; // ignore: implementation_imports | |
| import 'package:test_core/src/runner/suite.dart'; // ignore: implementation_imports | |
| import 'package:web_socket_channel/web_socket_channel.dart'; | |
| /// The shared interface for all compiler support libraries. | |
| abstract class CompilerSupport { | |
| /// The global test runner configuration. | |
| final Configuration config; | |
| /// The default template path. | |
| final String defaultTemplatePath; | |
| CompilerSupport(this.config, this.defaultTemplatePath); | |
| /// The URL at which this compiler serves its tests. | |
| /// | |
| /// Each compiler serves its tests under a different directory. | |
| Uri get serverUrl; | |
| /// Compiles [dartPath] using [suiteConfig] for [platform]. | |
| /// | |
| /// [dartPath] is the path to the original `.dart` test suite, relative to the | |
| /// package root. | |
| Future<void> compileSuite( | |
| String dartPath, | |
| SuiteConfiguration suiteConfig, | |
| SuitePlatform platform, | |
| ); | |
| /// Retrieves a stack trace mapper for [dartPath] if available. | |
| /// | |
| /// [dartPath] is the path to the original `.dart` test suite, relative to the | |
| /// package root. | |
| StackTraceMapper? stackTraceMapperForPath(String dartPath); | |
| /// Returns the eventual URI for the web socket, as well as the channel itself | |
| /// once the connection is established. | |
| (Uri uri, Future<WebSocketChannel> socket) get webSocket; | |
| /// Closes down anything necessary for this implementation. | |
| Future<void> close(); | |
| /// A handler that serves html wrapper files used to bootstrap tests. | |
| shelf.Response htmlWrapperHandler(shelf.Request request); | |
| } | |
| mixin JsHtmlWrapper on CompilerSupport { | |
| @override | |
| shelf.Response htmlWrapperHandler(shelf.Request request) { | |
| var path = p.fromUri(request.url); | |
| if (path.endsWith('.html')) { | |
| var test = p.setExtension(path, '.dart'); | |
| var scriptBase = htmlEscape.convert(p.basename(test)); | |
| var link = '<link rel="x-dart-test" href="$scriptBase">'; | |
| var testName = htmlEscape.convert(test); | |
| var template = config.customHtmlTemplatePath ?? defaultTemplatePath; | |
| var contents = File(template).readAsStringSync(); | |
| var processedContents = contents | |
| // Checked during loading phase that there is only one {{testScript}} placeholder. | |
| .replaceFirst('{{testScript}}', link) | |
| .replaceAll('{{testName}}', testName); | |
| return shelf.Response.ok( | |
| processedContents, | |
| headers: {'Content-Type': 'text/html'}, | |
| ); | |
| } | |
| return shelf.Response.notFound('Not found.'); | |
| } | |
| } | |
| mixin WasmHtmlWrapper on CompilerSupport { | |
| @override | |
| shelf.Response htmlWrapperHandler(shelf.Request request) { | |
| var path = p.fromUri(request.url); | |
| if (path.endsWith('.html')) { | |
| var test = '${p.withoutExtension(path)}.dart'; | |
| var scriptBase = htmlEscape.convert(p.basename(test)); | |
| var link = '<link rel="x-dart-test" href="$scriptBase">'; | |
| var testName = htmlEscape.convert(test); | |
| var template = config.customHtmlTemplatePath ?? defaultTemplatePath; | |
| var contents = File(template).readAsStringSync(); | |
| var jsRuntime = p.basename('$test.browser_test.dart.mjs'); | |
| var wasmData = | |
| '<data id="WasmBootstrapInfo" ' | |
| 'data-wasmurl="${p.basename('$test.browser_test.dart.wasm')}" ' | |
| 'data-jsruntimeurl="$jsRuntime"></data>'; | |
| var processedContents = contents | |
| // Checked during loading phase that there is only one {{testScript}} placeholder. | |
| .replaceFirst('{{testScript}}', '$link\n$wasmData') | |
| .replaceAll('{{testName}}', testName); | |
| return shelf.Response.ok( | |
| processedContents, | |
| headers: {'Content-Type': 'text/html'}, | |
| ); | |
| } | |
| return shelf.Response.notFound('Not found.'); | |
| } | |
| } |