Determine encoding of source files used for coverage (#1691)
Restore behavior that used to come from `package:http`,
check the headers to choose an encoding.
diff --git a/pkgs/test/CHANGELOG.md b/pkgs/test/CHANGELOG.md
index 170b6bb..5c15d42 100644
--- a/pkgs/test/CHANGELOG.md
+++ b/pkgs/test/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 1.21.1
+
+* Fix a bug loading JS sources with non-utf8 content while parsing coverage
+ information from chrome.
+
## 1.21.0
* Allow analyzer version `4.x`.
diff --git a/pkgs/test/lib/src/runner/browser/chrome.dart b/pkgs/test/lib/src/runner/browser/chrome.dart
index d68fc11..0feb397 100644
--- a/pkgs/test/lib/src/runner/browser/chrome.dart
+++ b/pkgs/test/lib/src/runner/browser/chrome.dart
@@ -194,11 +194,23 @@
}
extension on HttpClient {
+ Encoding determineEncoding(HttpHeaders headers) {
+ final contentType = headers.contentType?.charset;
+
+ /// Using the `charset` property of the `contentType` if available.
+ /// If it's unavailable or if the encoding name is unknown, [latin1] is used by default,
+ /// as per [RFC 2616][].
+ ///
+ /// [RFC 2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
+ return Encoding.getByName(contentType) ?? latin1;
+ }
+
Future<String?> getString(String url) async {
final request = await getUrl(Uri.parse(url));
final response = await request.close();
if (response.statusCode != HttpStatus.ok) return null;
var bytes = [await for (var chunk in response) ...chunk];
- return utf8.decode(bytes);
+ final encoding = determineEncoding(response.headers);
+ return encoding.decode(bytes);
}
}
diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml
index b837eb3..42053e8 100644
--- a/pkgs/test/pubspec.yaml
+++ b/pkgs/test/pubspec.yaml
@@ -1,5 +1,5 @@
name: test
-version: 1.21.0
+version: 1.21.1
description: >-
A full featured library for writing and running Dart tests across platforms.
repository: https://github.com/dart-lang/test/tree/master/pkgs/test
diff --git a/pkgs/test/test/runner/coverage_test.dart b/pkgs/test/test/runner/coverage_test.dart
index a9005db..26eea45 100644
--- a/pkgs/test/test/runner/coverage_test.dart
+++ b/pkgs/test/test/runner/coverage_test.dart
@@ -62,5 +62,70 @@
['--coverage', coverageDirectory.path, 'test.dart', '-p', 'chrome']);
await _validateCoverage(test, 'test.dart.chrome.json');
});
+
+ test(
+ 'gathers coverage for Chrome tests when JS files contain unicode characters',
+ () async {
+ final sourceMapFileContent =
+ '{"version":3,"file":"","sources":[],"names":[],"mappings":""}';
+ final jsContent = '''
+ (function() {
+ '© '
+ window.foo = function foo() {
+ return 'foo';
+ };
+ })({
+
+ '© ': ''
+ });
+ ''';
+ await d.file('file_with_unicode.js', jsContent).create();
+ await d.file('file_with_unicode.js.map', sourceMapFileContent).create();
+
+ await d.file('js_with_unicode_test.dart', '''
+ import 'dart:html';
+ import 'dart:js';
+
+ import 'package:test/test.dart';
+
+ Future<void> loadScript(String src) async {
+ final script = ScriptElement()..src = src;
+ final scriptLoaded = script.onLoad.first;
+ document.body!.append(script);
+ await scriptLoaded.timeout(Duration(seconds: 1));
+ }
+
+ void main() {
+ test("test 1", () async {
+ await loadScript('file_with_unicode.js');
+ expect(context['foo'], isNotNull);
+ context.callMethod('foo', []);
+ expect(true, isTrue);
+ });
+ }
+ ''').create();
+
+ final jsBytes = utf8.encode(jsContent);
+ final jsLatin1 = latin1.decode(jsBytes);
+ final jsUtf8 = utf8.decode(jsBytes);
+ expect(jsLatin1, isNot(jsUtf8),
+ reason: 'test setup: should have decoded differently');
+
+ const functionPattern = 'function foo';
+ expect([jsLatin1, jsUtf8], everyElement(contains(functionPattern)));
+ expect(jsLatin1.indexOf(functionPattern),
+ isNot(jsUtf8.indexOf(functionPattern)),
+ reason:
+ 'test setup: decoding should have shifted the position of the function');
+
+ var test = await runTest([
+ '--coverage',
+ coverageDirectory.path,
+ 'js_with_unicode_test.dart',
+ '-p',
+ 'chrome'
+ ]);
+ await _validateCoverage(test, 'js_with_unicode_test.dart.chrome.json');
+ });
});
}