mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

* Migrate packages/flutter_tools/test/general.shard, part 2/2 * Fix analysis * Fix tests * Fix analysis * Apply suggestions from code review Co-authored-by: Christopher Fujino <fujino@google.com> Co-authored-by: Christopher Fujino <fujino@google.com>
1238 lines
48 KiB
Dart
1238 lines
48 KiB
Dart
// Copyright 2014 The Flutter Authors. 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:io' hide Directory, File;
|
|
|
|
import 'package:dwds/dwds.dart';
|
|
import 'package:fake_async/fake_async.dart';
|
|
import 'package:flutter_tools/src/artifacts.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/build_system/targets/shader_compiler.dart';
|
|
import 'package:flutter_tools/src/build_system/targets/web.dart';
|
|
import 'package:flutter_tools/src/compile.dart';
|
|
import 'package:flutter_tools/src/convert.dart';
|
|
import 'package:flutter_tools/src/devfs.dart';
|
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
|
import 'package:flutter_tools/src/isolated/devfs_web.dart';
|
|
import 'package:flutter_tools/src/web/compile.dart';
|
|
import 'package:logging/logging.dart' as logging;
|
|
import 'package:package_config/package_config.dart';
|
|
import 'package:shelf/shelf.dart';
|
|
import 'package:test/fake.dart';
|
|
import 'package:vm_service/vm_service.dart' as vm_service;
|
|
|
|
import '../../src/common.dart';
|
|
import '../../src/testbed.dart';
|
|
|
|
const List<int> kTransparentImage = <int>[
|
|
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49,
|
|
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06,
|
|
0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44,
|
|
0x41, 0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0D,
|
|
0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE,
|
|
];
|
|
|
|
void main() {
|
|
late Testbed testbed;
|
|
late WebAssetServer webAssetServer;
|
|
late ReleaseAssetServer releaseAssetServer;
|
|
late Platform linux;
|
|
late PackageConfig packages;
|
|
late Platform windows;
|
|
late FakeHttpServer httpServer;
|
|
late BufferLogger logger;
|
|
|
|
setUpAll(() async {
|
|
packages = PackageConfig(<Package>[
|
|
Package('flutter_tools', Uri.file('/flutter_tools/lib/').normalizePath()),
|
|
]);
|
|
});
|
|
|
|
setUp(() {
|
|
httpServer = FakeHttpServer();
|
|
linux = FakePlatform(environment: <String, String>{});
|
|
windows = FakePlatform(operatingSystem: 'windows', environment: <String, String>{});
|
|
logger = BufferLogger.test();
|
|
testbed = Testbed(setup: () {
|
|
webAssetServer = WebAssetServer(
|
|
httpServer,
|
|
packages,
|
|
InternetAddress.loopbackIPv4,
|
|
<String, String>{},
|
|
<String, String>{},
|
|
NullSafetyMode.unsound,
|
|
);
|
|
releaseAssetServer = ReleaseAssetServer(
|
|
globals.fs.file('main.dart').uri,
|
|
fileSystem: globals.fs,
|
|
flutterRoot: null, // ignore: avoid_redundant_argument_values
|
|
platform: FakePlatform(),
|
|
webBuildDirectory: null, // ignore: avoid_redundant_argument_values
|
|
basePath: null,
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
Logger: () => logger,
|
|
});
|
|
});
|
|
|
|
test('.log() reports warnings', () => testbed.run(() {
|
|
const String unresolvedUriMessage = 'Unresolved uri:';
|
|
const String otherMessage = 'Something bad happened';
|
|
|
|
final List<logging.LogRecord> events = <logging.LogRecord>[
|
|
logging.LogRecord(
|
|
logging.Level.WARNING,
|
|
unresolvedUriMessage,
|
|
'DartUri',
|
|
),
|
|
logging.LogRecord(
|
|
logging.Level.WARNING,
|
|
otherMessage,
|
|
'DartUri',
|
|
),
|
|
];
|
|
|
|
events.forEach(log);
|
|
expect(logger.warningText, contains(unresolvedUriMessage));
|
|
expect(logger.warningText, contains(otherMessage));
|
|
}));
|
|
|
|
test('Handles against malformed manifest', () => testbed.run(() async {
|
|
final File source = globals.fs.file('source')
|
|
..writeAsStringSync('main() {}');
|
|
final File sourcemap = globals.fs.file('sourcemap')
|
|
..writeAsStringSync('{}');
|
|
final File metadata = globals.fs.file('metadata')
|
|
..writeAsStringSync('{}');
|
|
|
|
// Missing ending offset.
|
|
final File manifestMissingOffset = globals.fs.file('manifestA')
|
|
..writeAsStringSync(json.encode(<String, Object>{
|
|
'/foo.js': <String, Object>{
|
|
'code': <int>[0],
|
|
'sourcemap': <int>[0],
|
|
'metadata': <int>[0],
|
|
},
|
|
}));
|
|
final File manifestOutOfBounds = globals.fs.file('manifest')
|
|
..writeAsStringSync(json.encode(<String, Object>{
|
|
'/foo.js': <String, Object>{
|
|
'code': <int>[0, 100],
|
|
'sourcemap': <int>[0],
|
|
'metadata': <int>[0],
|
|
},
|
|
}));
|
|
|
|
expect(webAssetServer.write(source, manifestMissingOffset, sourcemap, metadata), isEmpty);
|
|
expect(webAssetServer.write(source, manifestOutOfBounds, sourcemap, metadata), isEmpty);
|
|
}));
|
|
|
|
test('serves JavaScript files from in memory cache', () => testbed.run(() async {
|
|
final File source = globals.fs.file('source')
|
|
..writeAsStringSync('main() {}');
|
|
final File sourcemap = globals.fs.file('sourcemap')
|
|
..writeAsStringSync('{}');
|
|
final File metadata = globals.fs.file('metadata')
|
|
..writeAsStringSync('{}');
|
|
final File manifest = globals.fs.file('manifest')
|
|
..writeAsStringSync(json.encode(<String, Object>{
|
|
'/foo.js': <String, Object>{
|
|
'code': <int>[0, source.lengthSync()],
|
|
'sourcemap': <int>[0, 2],
|
|
'metadata': <int>[0, 2],
|
|
},
|
|
}));
|
|
webAssetServer.write(source, manifest, sourcemap, metadata);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.js')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'application/javascript'),
|
|
containsPair(HttpHeaders.etagHeader, isNotNull),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => linux,
|
|
}));
|
|
|
|
test('serves metadata files from in memory cache', () => testbed.run(() async {
|
|
const String metadataContents = '{"name":"foo"}';
|
|
final File source = globals.fs.file('source')
|
|
..writeAsStringSync('main() {}');
|
|
final File sourcemap = globals.fs.file('sourcemap')
|
|
..writeAsStringSync('{}');
|
|
final File metadata = globals.fs.file('metadata')
|
|
..writeAsStringSync(metadataContents);
|
|
final File manifest = globals.fs.file('manifest')
|
|
..writeAsStringSync(json.encode(<String, Object>{
|
|
'/foo.js': <String, Object>{
|
|
'code': <int>[0, source.lengthSync()],
|
|
'sourcemap': <int>[0, sourcemap.lengthSync()],
|
|
'metadata': <int>[0, metadata.lengthSync()],
|
|
},
|
|
}));
|
|
webAssetServer.write(source, manifest, sourcemap, metadata);
|
|
|
|
final String? merged = await webAssetServer.metadataContents('main_module.ddc_merged_metadata');
|
|
expect(merged, equals(metadataContents));
|
|
|
|
final String? single = await webAssetServer.metadataContents('foo.js.metadata');
|
|
expect(single, equals(metadataContents));
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => linux,
|
|
}));
|
|
|
|
test('Removes leading slashes for valid requests to avoid requesting outside'
|
|
' of served directory', () => testbed.run(() async {
|
|
globals.fs.file('foo.png').createSync();
|
|
globals.fs.currentDirectory = globals.fs.directory('project_directory')
|
|
..createSync();
|
|
|
|
final File source = globals.fs.file(globals.fs.path.join('web', 'foo.png'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(kTransparentImage);
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar////foo.png')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'image/png'),
|
|
containsPair(HttpHeaders.etagHeader, isNotNull),
|
|
containsPair(HttpHeaders.cacheControlHeader, 'max-age=0, must-revalidate'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}));
|
|
|
|
test('takes base path into account when serving', () => testbed.run(() async {
|
|
webAssetServer.basePath = 'base/path';
|
|
|
|
globals.fs.file('foo.png').createSync();
|
|
globals.fs.currentDirectory = globals.fs.directory('project_directory')
|
|
..createSync();
|
|
|
|
final File source = globals.fs.file(globals.fs.path.join('web', 'foo.png'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(kTransparentImage);
|
|
final Response response =
|
|
await webAssetServer.handleRequest(
|
|
Request('GET', Uri.parse('http://foobar/base/path/foo.png')),
|
|
);
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'image/png'),
|
|
containsPair(HttpHeaders.etagHeader, isNotNull),
|
|
containsPair(HttpHeaders.cacheControlHeader, 'max-age=0, must-revalidate'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}));
|
|
|
|
test('serves index.html at the base path', () => testbed.run(() async {
|
|
webAssetServer.basePath = 'base/path';
|
|
|
|
const String htmlContent = '<html><head></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/base/path/')));
|
|
|
|
expect(response.statusCode, HttpStatus.ok);
|
|
expect(await response.readAsString(), htmlContent);
|
|
}));
|
|
|
|
test('serves index.html at / if href attribute is $kBaseHrefPlaceholder', () => testbed.run(() async {
|
|
const String htmlContent = '<html><head><base href ="$kBaseHrefPlaceholder"></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/')));
|
|
|
|
expect(response.statusCode, HttpStatus.ok);
|
|
expect(await response.readAsString(), htmlContent.replaceAll(kBaseHrefPlaceholder, '/'));
|
|
}));
|
|
|
|
test('does not serve outside the base path', () => testbed.run(() async {
|
|
webAssetServer.basePath = 'base/path';
|
|
|
|
const String htmlContent = '<html><head></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/')));
|
|
|
|
expect(response.statusCode, HttpStatus.notFound);
|
|
}));
|
|
|
|
test('parses base path from index.html', () => testbed.run(() async {
|
|
const String htmlContent = '<html><head><base href="/foo/bar/"></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
final WebAssetServer webAssetServer = WebAssetServer(
|
|
httpServer,
|
|
packages,
|
|
InternetAddress.loopbackIPv4,
|
|
<String, String>{},
|
|
<String, String>{},
|
|
NullSafetyMode.unsound,
|
|
);
|
|
|
|
expect(webAssetServer.basePath, 'foo/bar');
|
|
}));
|
|
|
|
test('handles lack of base path in index.html', () => testbed.run(() async {
|
|
const String htmlContent = '<html><head></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
final WebAssetServer webAssetServer = WebAssetServer(
|
|
httpServer,
|
|
packages,
|
|
InternetAddress.loopbackIPv4,
|
|
<String, String>{},
|
|
<String, String>{},
|
|
NullSafetyMode.unsound,
|
|
);
|
|
|
|
// Defaults to "/" when there's no base element.
|
|
expect(webAssetServer.basePath, '');
|
|
}));
|
|
|
|
test('throws if base path is relative', () => testbed.run(() async {
|
|
const String htmlContent = '<html><head><base href="foo/bar/"></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
expect(
|
|
() => WebAssetServer(
|
|
httpServer,
|
|
packages,
|
|
InternetAddress.loopbackIPv4,
|
|
<String, String>{},
|
|
<String, String>{},
|
|
NullSafetyMode.unsound,
|
|
),
|
|
throwsToolExit(),
|
|
);
|
|
}));
|
|
|
|
test('throws if base path does not end with slash', () => testbed.run(() async {
|
|
const String htmlContent = '<html><head><base href="/foo/bar"></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
expect(
|
|
() => WebAssetServer(
|
|
httpServer,
|
|
packages,
|
|
InternetAddress.loopbackIPv4,
|
|
<String, String>{},
|
|
<String, String>{},
|
|
NullSafetyMode.unsound,
|
|
),
|
|
throwsToolExit(),
|
|
);
|
|
}));
|
|
|
|
test('serves JavaScript files from in memory cache not from manifest', () => testbed.run(() async {
|
|
webAssetServer.writeFile('foo.js', 'main() {}');
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.js')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, '9'),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'application/javascript'),
|
|
containsPair(HttpHeaders.etagHeader, isNotNull),
|
|
containsPair(HttpHeaders.cacheControlHeader, 'max-age=0, must-revalidate'),
|
|
]));
|
|
expect((await response.read().toList()).first, utf8.encode('main() {}'));
|
|
}));
|
|
|
|
test('Returns notModified when the ifNoneMatch header matches the etag', () => testbed.run(() async {
|
|
webAssetServer.writeFile('foo.js', 'main() {}');
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.js')));
|
|
final String etag = response.headers[HttpHeaders.etagHeader]!;
|
|
|
|
final Response cachedResponse = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.js'), headers: <String, String>{
|
|
HttpHeaders.ifNoneMatchHeader: etag,
|
|
}));
|
|
|
|
expect(cachedResponse.statusCode, HttpStatus.notModified);
|
|
expect(await cachedResponse.read().toList(), isEmpty);
|
|
}));
|
|
|
|
test('serves index.html when path is unknown', () => testbed.run(() async {
|
|
const String htmlContent = '<html><head></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/bar/baz')));
|
|
|
|
expect(response.statusCode, HttpStatus.ok);
|
|
expect(await response.readAsString(), htmlContent);
|
|
}));
|
|
|
|
test('does not serve outside the base path', () => testbed.run(() async {
|
|
webAssetServer.basePath = 'base/path';
|
|
|
|
const String htmlContent = '<html><head></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/')));
|
|
|
|
expect(response.statusCode, HttpStatus.notFound);
|
|
}));
|
|
|
|
test('does not serve index.html when path is inside assets or packages', () => testbed.run(() async {
|
|
const String htmlContent = '<html><head></head><body id="test"></body></html>';
|
|
final Directory webDir = globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
..createSync();
|
|
webDir.childFile('index.html').writeAsStringSync(htmlContent);
|
|
|
|
Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo/bar.png')));
|
|
expect(response.statusCode, HttpStatus.notFound);
|
|
|
|
response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/packages/foo/bar.dart.js')));
|
|
expect(response.statusCode, HttpStatus.notFound);
|
|
|
|
webAssetServer.basePath = 'base/path';
|
|
|
|
response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/base/path/assets/foo/bar.png')));
|
|
expect(response.statusCode, HttpStatus.notFound);
|
|
|
|
response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/base/path/packages/foo/bar.dart.js')));
|
|
expect(response.statusCode, HttpStatus.notFound);
|
|
}));
|
|
|
|
test('serves default index.html', () => testbed.run(() async {
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/')));
|
|
|
|
expect(response.statusCode, HttpStatus.ok);
|
|
expect((await response.read().toList()).first,
|
|
containsAllInOrder(utf8.encode('<html>')));
|
|
}));
|
|
|
|
test('handles web server paths without .lib extension', () => testbed.run(() async {
|
|
final File source = globals.fs.file('source')
|
|
..writeAsStringSync('main() {}');
|
|
final File sourcemap = globals.fs.file('sourcemap')
|
|
..writeAsStringSync('{}');
|
|
final File metadata = globals.fs.file('metadata')
|
|
..writeAsStringSync('{}');
|
|
final File manifest = globals.fs.file('manifest')
|
|
..writeAsStringSync(json.encode(<String, Object>{
|
|
'/foo.dart.lib.js': <String, Object>{
|
|
'code': <int>[0, source.lengthSync()],
|
|
'sourcemap': <int>[0, 2],
|
|
'metadata': <int>[0, 2],
|
|
},
|
|
}));
|
|
webAssetServer.write(source, manifest, sourcemap, metadata);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.dart.js')));
|
|
|
|
expect(response.statusCode, HttpStatus.ok);
|
|
}));
|
|
|
|
test('serves JavaScript files from in memory cache on Windows', () => testbed.run(() async {
|
|
final File source = globals.fs.file('source')
|
|
..writeAsStringSync('main() {}');
|
|
final File sourcemap = globals.fs.file('sourcemap')
|
|
..writeAsStringSync('{}');
|
|
final File metadata = globals.fs.file('metadata')
|
|
..writeAsStringSync('{}');
|
|
final File manifest = globals.fs.file('manifest')
|
|
..writeAsStringSync(json.encode(<String, Object>{
|
|
'/foo.js': <String, Object>{
|
|
'code': <int>[0, source.lengthSync()],
|
|
'sourcemap': <int>[0, 2],
|
|
'metadata': <int>[0, 2],
|
|
},
|
|
}));
|
|
webAssetServer.write(source, manifest, sourcemap, metadata);
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://localhost/foo.js')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'application/javascript'),
|
|
containsPair(HttpHeaders.etagHeader, isNotNull),
|
|
containsPair(HttpHeaders.cacheControlHeader, 'max-age=0, must-revalidate'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => windows,
|
|
}));
|
|
|
|
test('serves asset files from in filesystem with url-encoded paths', () => testbed.run(() async {
|
|
final File source = globals.fs.file(globals.fs.path.join('build', 'flutter_assets', Uri.encodeFull('abcd象形字.png')))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(kTransparentImage);
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/abcd%25E8%25B1%25A1%25E5%25BD%25A2%25E5%25AD%2597.png')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'image/png'),
|
|
containsPair(HttpHeaders.etagHeader, isNotNull),
|
|
containsPair(HttpHeaders.cacheControlHeader, 'max-age=0, must-revalidate'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}));
|
|
test('serves files from web directory', () => testbed.run(() async {
|
|
final File source = globals.fs.file(globals.fs.path.join('web', 'foo.png'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(kTransparentImage);
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.png')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'image/png'),
|
|
containsPair(HttpHeaders.etagHeader, isNotNull),
|
|
containsPair(HttpHeaders.cacheControlHeader, 'max-age=0, must-revalidate'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}));
|
|
|
|
test('serves asset files from in filesystem with known mime type on Windows', () => testbed.run(() async {
|
|
final File source = globals.fs.file(globals.fs.path.join('build', 'flutter_assets', 'foo.png'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(kTransparentImage);
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo.png')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'image/png'),
|
|
containsPair(HttpHeaders.etagHeader, isNotNull),
|
|
containsPair(HttpHeaders.cacheControlHeader, 'max-age=0, must-revalidate'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => windows,
|
|
}));
|
|
|
|
test('serves Dart files from in filesystem on Linux/macOS', () => testbed.run(() async {
|
|
final File source = globals.fs.file('foo.dart').absolute
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('void main() {}');
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.dart')));
|
|
|
|
expect(response.headers, containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => linux,
|
|
}));
|
|
|
|
test('serves asset files from in filesystem with known mime type', () => testbed.run(() async {
|
|
final File source = globals.fs.file(globals.fs.path.join('build', 'flutter_assets', 'foo.png'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(kTransparentImage);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo.png')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, source.lengthSync().toString()),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'image/png'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}));
|
|
|
|
test('serves asset files from in filesystem with known mime type and empty content', () => testbed.run(() async {
|
|
final File source = globals.fs.file(globals.fs.path.join('web', 'foo.js'))
|
|
..createSync(recursive: true);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.js')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, '0'),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'application/javascript'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}));
|
|
|
|
test('serves asset files files from in filesystem with unknown mime type', () => testbed.run(() async {
|
|
final File source = globals.fs.file(globals.fs.path.join('build', 'flutter_assets', 'foo'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(List<int>.filled(100, 0));
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, '100'),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'application/octet-stream'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}));
|
|
|
|
test('serves valid etag header for asset files with non-ascii characters', () => testbed.run(() async {
|
|
globals.fs.file(globals.fs.path.join('build', 'flutter_assets', 'fooπ'))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(<int>[1, 2, 3]);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/fooπ')));
|
|
final String etag = response.headers[HttpHeaders.etagHeader]!;
|
|
|
|
expect(etag.runes, everyElement(predicate((int char) => char < 255)));
|
|
}));
|
|
|
|
test('serves /packages/<package>/<path> files as if they were '
|
|
'package:<package>/<path> uris', () => testbed.run(() async {
|
|
final Uri? expectedUri = packages.resolve(
|
|
Uri.parse('package:flutter_tools/foo.dart'));
|
|
final File source = globals.fs.file(globals.fs.path.fromUri(expectedUri))
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(<int>[1, 2, 3]);
|
|
|
|
final Response response = await webAssetServer
|
|
.handleRequest(Request('GET', Uri.parse('http:///packages/flutter_tools/foo.dart')));
|
|
|
|
expect(response.headers, allOf(<Matcher>[
|
|
containsPair(HttpHeaders.contentLengthHeader, '3'),
|
|
containsPair(HttpHeaders.contentTypeHeader, 'text/x-dart'),
|
|
]));
|
|
expect((await response.read().toList()).first, source.readAsBytesSync());
|
|
}));
|
|
|
|
test('calling dispose closes the http server', () => testbed.run(() async {
|
|
await webAssetServer.dispose();
|
|
|
|
expect(httpServer.closed, true);
|
|
}));
|
|
|
|
test('Can start web server with specified assets', () => testbed.run(() async {
|
|
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
|
..createSync(recursive: true);
|
|
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
|
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.metadata').writeAsStringSync('{}');
|
|
|
|
final ResidentCompiler residentCompiler = FakeResidentCompiler()
|
|
..output = const CompilerOutput('a', 0, <Uri>[]);
|
|
|
|
final WebDevFS webDevFS = WebDevFS(
|
|
hostname: 'localhost',
|
|
port: 0,
|
|
packagesFilePath: '.packages',
|
|
urlTunneller: null, // ignore: avoid_redundant_argument_values
|
|
useSseForDebugProxy: true,
|
|
useSseForDebugBackend: true,
|
|
useSseForInjectedClient: true,
|
|
nullAssertions: true,
|
|
nativeNullAssertions: true,
|
|
buildInfo: const BuildInfo(
|
|
BuildMode.debug,
|
|
'',
|
|
treeShakeIcons: false,
|
|
nullSafetyMode: NullSafetyMode.unsound,
|
|
),
|
|
enableDwds: false,
|
|
enableDds: false,
|
|
entrypoint: Uri.base,
|
|
testMode: true,
|
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
|
nullSafetyMode: NullSafetyMode.unsound,
|
|
);
|
|
webDevFS.requireJS.createSync(recursive: true);
|
|
webDevFS.stackTraceMapper.createSync(recursive: true);
|
|
|
|
final Uri uri = await webDevFS.create();
|
|
webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory;
|
|
final String webPrecompiledSdk = globals.artifacts!
|
|
.getHostArtifact(HostArtifact.webPrecompiledSdk).path;
|
|
final String webPrecompiledSdkSourcemaps = globals.artifacts!
|
|
.getHostArtifact(HostArtifact.webPrecompiledSdkSourcemaps).path;
|
|
final String webPrecompiledCanvaskitSdk = globals.artifacts!
|
|
.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdk).path;
|
|
final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts!
|
|
.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSdkSourcemaps).path;
|
|
globals.fs.currentDirectory
|
|
.childDirectory('lib')
|
|
.childFile('web_entrypoint.dart')
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('GENERATED');
|
|
globals.fs.file(webPrecompiledSdk)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('HELLO');
|
|
globals.fs.file(webPrecompiledSdkSourcemaps)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('THERE');
|
|
globals.fs.file(webPrecompiledCanvaskitSdk)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('OL');
|
|
globals.fs.file(webPrecompiledCanvaskitSdkSourcemaps)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('CHUM');
|
|
|
|
await webDevFS.update(
|
|
mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri,
|
|
generator: residentCompiler,
|
|
trackWidgetCreation: true,
|
|
bundleFirstUpload: true,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
pathToReload: '',
|
|
dillOutputPath: 'out.dill',
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
|
|
expect(webDevFS.webAssetServer.getFile('require.js'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('stack_trace_mapper.js'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('main.dart'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('manifest.json'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('flutter.js'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('flutter_service_worker.js'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('version.json'),isNotNull);
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'HELLO');
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'THERE');
|
|
|
|
// Update to the SDK.
|
|
globals.fs.file(webPrecompiledSdk).writeAsStringSync('BELLOW');
|
|
|
|
// New SDK should be visible..
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW');
|
|
|
|
// Toggle CanvasKit
|
|
expect(webDevFS.webAssetServer.webRenderer, WebRendererMode.html);
|
|
webDevFS.webAssetServer.webRenderer = WebRendererMode.canvaskit;
|
|
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'OL');
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'CHUM');
|
|
|
|
// Generated entrypoint.
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('web_entrypoint.dart'),
|
|
contains('GENERATED'));
|
|
|
|
// served on localhost
|
|
expect(uri.host, 'localhost');
|
|
|
|
await webDevFS.destroy();
|
|
}, overrides: <Type, Generator>{
|
|
Artifacts: () => Artifacts.test(),
|
|
}));
|
|
|
|
test('Can start web server with specified assets in sound null safety mode', () => testbed.run(() async {
|
|
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
|
..createSync(recursive: true);
|
|
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
|
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.metadata').writeAsStringSync('{}');
|
|
|
|
final ResidentCompiler residentCompiler = FakeResidentCompiler()
|
|
..output = const CompilerOutput('a', 0, <Uri>[]);
|
|
|
|
final WebDevFS webDevFS = WebDevFS(
|
|
hostname: 'localhost',
|
|
port: 0,
|
|
packagesFilePath: '.packages',
|
|
urlTunneller: null, // ignore: avoid_redundant_argument_values
|
|
useSseForDebugProxy: true,
|
|
useSseForDebugBackend: true,
|
|
useSseForInjectedClient: true,
|
|
nullAssertions: true,
|
|
nativeNullAssertions: true,
|
|
buildInfo: const BuildInfo(
|
|
BuildMode.debug,
|
|
'',
|
|
treeShakeIcons: false,
|
|
),
|
|
enableDwds: false,
|
|
enableDds: false,
|
|
entrypoint: Uri.base,
|
|
testMode: true,
|
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
|
nullSafetyMode: NullSafetyMode.sound,
|
|
);
|
|
webDevFS.requireJS.createSync(recursive: true);
|
|
webDevFS.stackTraceMapper.createSync(recursive: true);
|
|
|
|
final Uri uri = await webDevFS.create();
|
|
webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory;
|
|
globals.fs.currentDirectory
|
|
.childDirectory('lib')
|
|
.childFile('web_entrypoint.dart')
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('GENERATED');
|
|
final String webPrecompiledSdk = globals.artifacts!
|
|
.getHostArtifact(HostArtifact.webPrecompiledSoundSdk).path;
|
|
final String webPrecompiledSdkSourcemaps = globals.artifacts!
|
|
.getHostArtifact(HostArtifact.webPrecompiledSoundSdkSourcemaps).path;
|
|
final String webPrecompiledCanvaskitSdk = globals.artifacts!
|
|
.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSoundSdk).path;
|
|
final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts!
|
|
.getHostArtifact(HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps).path;
|
|
globals.fs.file(webPrecompiledSdk)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('HELLO');
|
|
globals.fs.file(webPrecompiledSdkSourcemaps)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('THERE');
|
|
globals.fs.file(webPrecompiledCanvaskitSdk)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('OL');
|
|
globals.fs.file(webPrecompiledCanvaskitSdkSourcemaps)
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('CHUM');
|
|
|
|
await webDevFS.update(
|
|
mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri,
|
|
generator: residentCompiler,
|
|
trackWidgetCreation: true,
|
|
bundleFirstUpload: true,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
pathToReload: '',
|
|
dillOutputPath: '',
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
|
|
expect(webDevFS.webAssetServer.getFile('require.js'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('stack_trace_mapper.js'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('main.dart'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('manifest.json'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('flutter.js'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('flutter_service_worker.js'), isNotNull);
|
|
expect(webDevFS.webAssetServer.getFile('version.json'), isNotNull);
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'HELLO');
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'THERE');
|
|
|
|
// Update to the SDK.
|
|
globals.fs.file(webPrecompiledSdk).writeAsStringSync('BELLOW');
|
|
|
|
// New SDK should be visible..
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW');
|
|
|
|
// Toggle CanvasKit
|
|
webDevFS.webAssetServer.webRenderer = WebRendererMode.canvaskit;
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'OL');
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'CHUM');
|
|
|
|
// Generated entrypoint.
|
|
expect(await webDevFS.webAssetServer.dartSourceContents('web_entrypoint.dart'),
|
|
contains('GENERATED'));
|
|
|
|
// served on localhost
|
|
expect(uri.host, 'localhost');
|
|
|
|
await webDevFS.destroy();
|
|
}, overrides: <Type, Generator>{
|
|
Artifacts: () => Artifacts.test(),
|
|
}));
|
|
|
|
test('.connect() will never call vmServiceFactory twice', () => testbed.run(() async {
|
|
await FakeAsync().run<Future<void>>((FakeAsync time) {
|
|
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
|
..createSync(recursive: true);
|
|
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
|
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.metadata').writeAsStringSync('{}');
|
|
|
|
final WebDevFS webDevFS = WebDevFS(
|
|
// if this is any other value, we will do a real ip lookup
|
|
hostname: 'any',
|
|
port: 0,
|
|
packagesFilePath: '.packages',
|
|
urlTunneller: null,
|
|
useSseForDebugProxy: true,
|
|
useSseForDebugBackend: true,
|
|
useSseForInjectedClient: true,
|
|
nullAssertions: true,
|
|
nativeNullAssertions: true,
|
|
buildInfo: const BuildInfo(
|
|
BuildMode.debug,
|
|
'',
|
|
treeShakeIcons: false,
|
|
),
|
|
enableDwds: true,
|
|
enableDds: false,
|
|
entrypoint: Uri.base,
|
|
testMode: true,
|
|
expressionCompiler: null,
|
|
chromiumLauncher: null,
|
|
nullSafetyMode: NullSafetyMode.sound,
|
|
);
|
|
webDevFS.requireJS.createSync(recursive: true);
|
|
webDevFS.stackTraceMapper.createSync(recursive: true);
|
|
final FakeAppConnection firstConnection = FakeAppConnection();
|
|
final FakeAppConnection secondConnection = FakeAppConnection();
|
|
|
|
final Future<void> done = webDevFS.create().then<void>((Uri _) {
|
|
// In non-test mode, webDevFS.create() would have initialized DWDS
|
|
webDevFS.webAssetServer.dwds = FakeDwds(<AppConnection>[firstConnection, secondConnection]);
|
|
|
|
int vmServiceFactoryInvocationCount = 0;
|
|
Future<vm_service.VmService> vmServiceFactory(Uri uri, {CompressionOptions? compression, required Logger logger}) {
|
|
if (vmServiceFactoryInvocationCount > 0) {
|
|
fail('Called vmServiceFactory twice!');
|
|
}
|
|
vmServiceFactoryInvocationCount += 1;
|
|
return Future<vm_service.VmService>.delayed(
|
|
const Duration(seconds: 2),
|
|
() => FakeVmService(),
|
|
);
|
|
}
|
|
return webDevFS.connect(false, vmServiceFactory: vmServiceFactory).then<void>((ConnectionResult? firstConnectionResult) {
|
|
return webDevFS.destroy();
|
|
});
|
|
});
|
|
time.elapse(const Duration(seconds: 1));
|
|
time.elapse(const Duration(seconds: 2));
|
|
return done;
|
|
});
|
|
}, overrides: <Type, Generator>{
|
|
Artifacts: () => Artifacts.test(),
|
|
}));
|
|
|
|
test('Can start web server with hostname any', () => testbed.run(() async {
|
|
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
|
..createSync(recursive: true);
|
|
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
|
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
|
|
|
final WebDevFS webDevFS = WebDevFS(
|
|
hostname: 'any',
|
|
port: 0,
|
|
packagesFilePath: '.packages',
|
|
urlTunneller: null, // ignore: avoid_redundant_argument_values
|
|
useSseForDebugProxy: true,
|
|
useSseForDebugBackend: true,
|
|
useSseForInjectedClient: true,
|
|
buildInfo: BuildInfo.debug,
|
|
enableDwds: false,
|
|
enableDds: false,
|
|
entrypoint: Uri.base,
|
|
testMode: true,
|
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
|
nullAssertions: true,
|
|
nativeNullAssertions: true,
|
|
nullSafetyMode: NullSafetyMode.sound,
|
|
);
|
|
webDevFS.requireJS.createSync(recursive: true);
|
|
webDevFS.stackTraceMapper.createSync(recursive: true);
|
|
|
|
final Uri uri = await webDevFS.create();
|
|
|
|
expect(uri.host, 'localhost');
|
|
await webDevFS.destroy();
|
|
}));
|
|
|
|
test('Can start web server with canvaskit enabled', () => testbed.run(() async {
|
|
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
|
..createSync(recursive: true);
|
|
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
|
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
|
|
|
final WebDevFS webDevFS = WebDevFS(
|
|
hostname: 'localhost',
|
|
port: 0,
|
|
packagesFilePath: '.packages',
|
|
urlTunneller: null, // ignore: avoid_redundant_argument_values
|
|
useSseForDebugProxy: true,
|
|
useSseForDebugBackend: true,
|
|
useSseForInjectedClient: true,
|
|
nullAssertions: true,
|
|
nativeNullAssertions: true,
|
|
buildInfo: const BuildInfo(
|
|
BuildMode.debug,
|
|
'',
|
|
treeShakeIcons: false,
|
|
dartDefines: <String>[
|
|
'FLUTTER_WEB_USE_SKIA=true',
|
|
]
|
|
),
|
|
enableDwds: false,
|
|
enableDds: false,
|
|
entrypoint: Uri.base,
|
|
testMode: true,
|
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
|
nullSafetyMode: NullSafetyMode.sound,
|
|
);
|
|
webDevFS.requireJS.createSync(recursive: true);
|
|
webDevFS.stackTraceMapper.createSync(recursive: true);
|
|
|
|
await webDevFS.create();
|
|
|
|
expect(webDevFS.webAssetServer.webRenderer, WebRendererMode.canvaskit);
|
|
|
|
await webDevFS.destroy();
|
|
}));
|
|
|
|
test('Can start web server with auto detect enabled', () => testbed.run(() async {
|
|
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
|
..createSync(recursive: true);
|
|
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
|
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
|
|
|
final WebDevFS webDevFS = WebDevFS(
|
|
hostname: 'localhost',
|
|
port: 0,
|
|
packagesFilePath: '.packages',
|
|
urlTunneller: null, // ignore: avoid_redundant_argument_values
|
|
useSseForDebugProxy: true,
|
|
useSseForDebugBackend: true,
|
|
useSseForInjectedClient: true,
|
|
nullAssertions: true,
|
|
nativeNullAssertions: true,
|
|
buildInfo: const BuildInfo(
|
|
BuildMode.debug,
|
|
'',
|
|
treeShakeIcons: false,
|
|
dartDefines: <String>[
|
|
'FLUTTER_WEB_AUTO_DETECT=true',
|
|
]
|
|
),
|
|
enableDwds: false,
|
|
enableDds: false,
|
|
entrypoint: Uri.base,
|
|
testMode: true,
|
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
|
nullSafetyMode: NullSafetyMode.sound,
|
|
);
|
|
webDevFS.requireJS.createSync(recursive: true);
|
|
webDevFS.stackTraceMapper.createSync(recursive: true);
|
|
|
|
await webDevFS.create();
|
|
|
|
expect(webDevFS.webAssetServer.webRenderer, WebRendererMode.autoDetect);
|
|
|
|
await webDevFS.destroy();
|
|
}));
|
|
|
|
test('allows frame embedding', () async {
|
|
final WebAssetServer webAssetServer = await WebAssetServer.start(
|
|
null,
|
|
'localhost',
|
|
0,
|
|
null,
|
|
true,
|
|
true,
|
|
true,
|
|
const BuildInfo(
|
|
BuildMode.debug,
|
|
'',
|
|
treeShakeIcons: false,
|
|
),
|
|
false,
|
|
false,
|
|
Uri.base,
|
|
null,
|
|
NullSafetyMode.unsound,
|
|
testMode: true);
|
|
|
|
expect(webAssetServer.defaultResponseHeaders['x-frame-options'], null);
|
|
await webAssetServer.dispose();
|
|
});
|
|
|
|
test('WebAssetServer responds to POST requests with 404 not found', () => testbed.run(() async {
|
|
final Response response = await webAssetServer.handleRequest(
|
|
Request('POST', Uri.parse('http://foobar/something')),
|
|
);
|
|
expect(response.statusCode, 404);
|
|
}));
|
|
|
|
test('ReleaseAssetServer responds to POST requests with 404 not found', () => testbed.run(() async {
|
|
final Response response = await releaseAssetServer.handle(
|
|
Request('POST', Uri.parse('http://foobar/something')),
|
|
);
|
|
expect(response.statusCode, 404);
|
|
}));
|
|
|
|
test('WebAssetServer strips leading base href off of asset requests', () => testbed.run(() async {
|
|
const String htmlContent = '<html><head><base href="/foo/"></head><body id="test"></body></html>';
|
|
globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
.childFile('index.html')
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync(htmlContent);
|
|
final WebAssetServer webAssetServer = WebAssetServer(
|
|
FakeHttpServer(),
|
|
PackageConfig.empty,
|
|
InternetAddress.anyIPv4,
|
|
<String, String>{},
|
|
<String, String>{},
|
|
NullSafetyMode.sound,
|
|
);
|
|
|
|
expect(await webAssetServer.metadataContents('foo/main_module.ddc_merged_metadata'), null);
|
|
// Not base href.
|
|
expect(() async => webAssetServer.metadataContents('bar/main_module.ddc_merged_metadata'), throwsException);
|
|
}));
|
|
|
|
test('DevFS URI includes any specified base path.', () => testbed.run(() async {
|
|
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
|
..createSync(recursive: true);
|
|
const String htmlContent = '<html><head><base href="/foo/"></head><body id="test"></body></html>';
|
|
globals.fs.currentDirectory
|
|
.childDirectory('web')
|
|
.childFile('index.html')
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync(htmlContent);
|
|
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
|
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
|
outputFile.parent.childFile('a.metadata').writeAsStringSync('{}');
|
|
|
|
final WebDevFS webDevFS = WebDevFS(
|
|
hostname: 'localhost',
|
|
port: 0,
|
|
packagesFilePath: '.packages',
|
|
urlTunneller: null, // ignore: avoid_redundant_argument_values
|
|
useSseForDebugProxy: true,
|
|
useSseForDebugBackend: true,
|
|
useSseForInjectedClient: true,
|
|
nullAssertions: true,
|
|
nativeNullAssertions: true,
|
|
buildInfo: BuildInfo.debug,
|
|
enableDwds: false,
|
|
enableDds: false,
|
|
entrypoint: Uri.base,
|
|
testMode: true,
|
|
expressionCompiler: null, // ignore: avoid_redundant_argument_values
|
|
chromiumLauncher: null, // ignore: avoid_redundant_argument_values
|
|
nullSafetyMode: NullSafetyMode.unsound,
|
|
);
|
|
webDevFS.requireJS.createSync(recursive: true);
|
|
webDevFS.stackTraceMapper.createSync(recursive: true);
|
|
|
|
final Uri uri = await webDevFS.create();
|
|
|
|
// served on localhost
|
|
expect(uri.host, 'localhost');
|
|
// Matches base URI specified in html.
|
|
expect(uri.path, '/foo');
|
|
|
|
await webDevFS.destroy();
|
|
}, overrides: <Type, Generator>{
|
|
Artifacts: () => Artifacts.test(),
|
|
}));
|
|
}
|
|
|
|
class FakeHttpServer extends Fake implements HttpServer {
|
|
bool closed = false;
|
|
|
|
@override
|
|
Future<void> close({bool force = false}) async {
|
|
closed = true;
|
|
}
|
|
}
|
|
|
|
class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|
CompilerOutput? output;
|
|
|
|
@override
|
|
void addFileSystemRoot(String root) { }
|
|
|
|
@override
|
|
Future<CompilerOutput?> recompile(Uri mainUri, List<Uri>? invalidatedFiles, {
|
|
String? outputPath,
|
|
PackageConfig? packageConfig,
|
|
String? projectRootPath,
|
|
FileSystem? fs,
|
|
bool suppressErrors = false,
|
|
bool checkDartPluginRegistry = false,
|
|
File? dartPluginRegistrant,
|
|
}) async {
|
|
return output;
|
|
}
|
|
}
|
|
|
|
class FakeShaderCompiler implements DevelopmentShaderCompiler {
|
|
const FakeShaderCompiler();
|
|
|
|
@override
|
|
void configureCompiler(TargetPlatform? platform, { required bool enableImpeller }) { }
|
|
|
|
@override
|
|
Future<DevFSContent> recompileShader(DevFSContent inputShader) {
|
|
throw UnimplementedError();
|
|
}
|
|
}
|
|
|
|
class FakeDwds extends Fake implements Dwds {
|
|
FakeDwds(this.connectedAppsIterable) :
|
|
connectedApps = Stream<AppConnection>.fromIterable(connectedAppsIterable);
|
|
|
|
final Iterable<AppConnection> connectedAppsIterable;
|
|
|
|
@override
|
|
final Stream<AppConnection> connectedApps;
|
|
|
|
@override
|
|
Future<DebugConnection> debugConnection(AppConnection appConnection) => Future<DebugConnection>.value(FakeDebugConnection());
|
|
}
|
|
|
|
class FakeAppConnection extends Fake implements AppConnection {
|
|
@override
|
|
void runMain() {}
|
|
}
|
|
|
|
class FakeDebugConnection extends Fake implements DebugConnection {
|
|
FakeDebugConnection({
|
|
this.uri = 'http://foo',
|
|
});
|
|
|
|
@override
|
|
final String uri;
|
|
}
|
|
|
|
class FakeVmService extends Fake implements vm_service.VmService {}
|