
Fixes #117659 It turns out this was due to the output-suppression timer introduced recently as part of cleaning up the output (#109206); on success, the script would wait 10 minutes for the timeout to expire. This didn't affect CI because this feature doesn't apply in CI (as detected by lack of color on stdout.) Fix the issue by cleaning up the timer on success in the same way as on failure. While here, clean up the final summary messages slightly, and also cut the trailing space that printProgress was leaving on each line.
713 lines
24 KiB
Dart
713 lines
24 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:core' hide print;
|
|
import 'dart:io' hide exit;
|
|
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:shelf/shelf.dart';
|
|
|
|
import 'browser.dart';
|
|
import 'run_command.dart';
|
|
import 'test/common.dart';
|
|
import 'utils.dart';
|
|
|
|
final String _bat = Platform.isWindows ? '.bat' : '';
|
|
final String _flutterRoot = path.dirname(path.dirname(path.dirname(path.fromUri(Platform.script))));
|
|
final String _flutter = path.join(_flutterRoot, 'bin', 'flutter$_bat');
|
|
final String _testAppDirectory = path.join(_flutterRoot, 'dev', 'integration_tests', 'web');
|
|
final String _testAppWebDirectory = path.join(_testAppDirectory, 'web');
|
|
final String _appBuildDirectory = path.join(_testAppDirectory, 'build', 'web');
|
|
final String _target = path.join('lib', 'service_worker_test.dart');
|
|
final String _targetWithCachedResources = path.join('lib', 'service_worker_test_cached_resources.dart');
|
|
final String _targetWithBlockedServiceWorkers = path.join('lib', 'service_worker_test_blocked_service_workers.dart');
|
|
final String _targetPath = path.join(_testAppDirectory, _target);
|
|
|
|
enum ServiceWorkerTestType {
|
|
// Mocks how FF disables service workers.
|
|
blockedServiceWorkers,
|
|
// Drops the main.dart.js directly on the page.
|
|
withoutFlutterJs,
|
|
// Uses the standard, promise-based, flutterJS initialization.
|
|
withFlutterJs,
|
|
// Uses the shorthand engineInitializer.autoStart();
|
|
withFlutterJsShort,
|
|
// Uses onEntrypointLoaded callback instead of returned promise.
|
|
withFlutterJsEntrypointLoadedEvent,
|
|
// Same as withFlutterJsEntrypointLoadedEvent, but with TrustedTypes enabled.
|
|
withFlutterJsTrustedTypesOn,
|
|
// Entrypoint generated by `flutter create`.
|
|
generatedEntrypoint,
|
|
}
|
|
|
|
// Run a web service worker test as a standalone Dart program.
|
|
Future<void> main() async {
|
|
// When updating this list, also update `dev/bots/test.dart`. This `main()`
|
|
// function is only here for convenience. Adding tests here will not add them
|
|
// to LUCI.
|
|
await runWebServiceWorkerTest(headless: false, testType: ServiceWorkerTestType.withoutFlutterJs);
|
|
await runWebServiceWorkerTest(headless: false, testType: ServiceWorkerTestType.withFlutterJs);
|
|
await runWebServiceWorkerTest(headless: false, testType: ServiceWorkerTestType.withFlutterJsShort);
|
|
await runWebServiceWorkerTest(headless: false, testType: ServiceWorkerTestType.withFlutterJsEntrypointLoadedEvent);
|
|
await runWebServiceWorkerTest(headless: false, testType: ServiceWorkerTestType.withFlutterJsTrustedTypesOn);
|
|
await runWebServiceWorkerTestWithCachingResources(headless: false, testType: ServiceWorkerTestType.withoutFlutterJs);
|
|
await runWebServiceWorkerTestWithCachingResources(headless: false, testType: ServiceWorkerTestType.withFlutterJs);
|
|
await runWebServiceWorkerTestWithCachingResources(headless: false, testType: ServiceWorkerTestType.withFlutterJsShort);
|
|
await runWebServiceWorkerTestWithCachingResources(headless: false, testType: ServiceWorkerTestType.withFlutterJsEntrypointLoadedEvent);
|
|
await runWebServiceWorkerTestWithCachingResources(headless: false, testType: ServiceWorkerTestType.withFlutterJsTrustedTypesOn);
|
|
await runWebServiceWorkerTestWithGeneratedEntrypoint(headless: false);
|
|
await runWebServiceWorkerTestWithBlockedServiceWorkers(headless: false);
|
|
|
|
if (hasError) {
|
|
reportErrorsAndExit('${bold}One or more tests failed.$reset');
|
|
}
|
|
reportSuccessAndExit('${bold}Tests successful.$reset');
|
|
}
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/109093.
|
|
//
|
|
// Tests the entrypoint that's generated by `flutter create`.
|
|
Future<void> runWebServiceWorkerTestWithGeneratedEntrypoint({
|
|
required bool headless,
|
|
}) async {
|
|
await _generateEntrypoint();
|
|
await runWebServiceWorkerTestWithCachingResources(headless: headless, testType: ServiceWorkerTestType.generatedEntrypoint);
|
|
}
|
|
|
|
Future<void> _generateEntrypoint() async {
|
|
final Directory tempDirectory = Directory.systemTemp.createTempSync('flutter_web_generated_entrypoint.');
|
|
await runCommand(
|
|
_flutter,
|
|
<String>[ 'create', 'generated_entrypoint_test' ],
|
|
workingDirectory: tempDirectory.path,
|
|
);
|
|
final File generatedEntrypoint = File(path.join(tempDirectory.path, 'generated_entrypoint_test', 'web', 'index.html'));
|
|
final String generatedEntrypointCode = generatedEntrypoint.readAsStringSync();
|
|
final File testEntrypoint = File(path.join(
|
|
_testAppWebDirectory,
|
|
_testTypeToIndexFile(ServiceWorkerTestType.generatedEntrypoint),
|
|
));
|
|
testEntrypoint.writeAsStringSync(generatedEntrypointCode);
|
|
tempDirectory.deleteSync(recursive: true);
|
|
}
|
|
|
|
Future<void> _setAppVersion(int version) async {
|
|
final File targetFile = File(_targetPath);
|
|
await targetFile.writeAsString(
|
|
(await targetFile.readAsString()).replaceFirst(
|
|
RegExp(r'CLOSE\?version=\d+'),
|
|
'CLOSE?version=$version',
|
|
)
|
|
);
|
|
}
|
|
|
|
String _testTypeToIndexFile(ServiceWorkerTestType type) {
|
|
late String indexFile;
|
|
switch (type) {
|
|
case ServiceWorkerTestType.blockedServiceWorkers:
|
|
indexFile = 'index_with_blocked_service_workers.html';
|
|
break;
|
|
case ServiceWorkerTestType.withFlutterJs:
|
|
indexFile = 'index_with_flutterjs.html';
|
|
break;
|
|
case ServiceWorkerTestType.withoutFlutterJs:
|
|
indexFile = 'index_without_flutterjs.html';
|
|
break;
|
|
case ServiceWorkerTestType.withFlutterJsShort:
|
|
indexFile = 'index_with_flutterjs_short.html';
|
|
break;
|
|
case ServiceWorkerTestType.withFlutterJsEntrypointLoadedEvent:
|
|
indexFile = 'index_with_flutterjs_entrypoint_loaded.html';
|
|
break;
|
|
case ServiceWorkerTestType.withFlutterJsTrustedTypesOn:
|
|
indexFile = 'index_with_flutterjs_el_tt_on.html';
|
|
break;
|
|
case ServiceWorkerTestType.generatedEntrypoint:
|
|
indexFile = 'generated_entrypoint.html';
|
|
break;
|
|
}
|
|
return indexFile;
|
|
}
|
|
|
|
Future<void> _rebuildApp({ required int version, required ServiceWorkerTestType testType, required String target }) async {
|
|
await _setAppVersion(version);
|
|
await runCommand(
|
|
_flutter,
|
|
<String>[ 'clean' ],
|
|
workingDirectory: _testAppDirectory,
|
|
);
|
|
await runCommand(
|
|
'cp',
|
|
<String>[
|
|
_testTypeToIndexFile(testType),
|
|
'index.html',
|
|
],
|
|
workingDirectory: _testAppWebDirectory,
|
|
);
|
|
await runCommand(
|
|
_flutter,
|
|
<String>['build', 'web', '--profile', '-t', target],
|
|
workingDirectory: _testAppDirectory,
|
|
environment: <String, String>{
|
|
'FLUTTER_WEB': 'true',
|
|
},
|
|
);
|
|
}
|
|
|
|
void _expectRequestCounts(
|
|
Map<String, int> expectedCounts,
|
|
Map<String, int> requestedPathCounts,
|
|
) {
|
|
expect(requestedPathCounts, expectedCounts);
|
|
requestedPathCounts.clear();
|
|
}
|
|
|
|
Future<void> _waitForAppToLoad(
|
|
Map<String, int> waitForCounts,
|
|
Map<String, int> requestedPathCounts,
|
|
AppServer? server
|
|
) async {
|
|
print('Waiting for app to load $waitForCounts');
|
|
await Future.any(<Future<Object?>>[
|
|
() async {
|
|
while (!waitForCounts.entries.every((MapEntry<String, int> entry) => (requestedPathCounts[entry.key] ?? 0) >= entry.value)) {
|
|
await Future<void>.delayed(const Duration(milliseconds: 100));
|
|
}
|
|
}(),
|
|
server!.onChromeError.then((String error) {
|
|
throw Exception('Chrome error: $error');
|
|
}),
|
|
]);
|
|
}
|
|
|
|
/// A drop-in replacement for `package:test` expect that can run outside the
|
|
/// test zone.
|
|
void expect(Object? actual, Object? expected) {
|
|
final Matcher matcher = wrapMatcher(expected);
|
|
// matchState needs to be of type <Object?, Object?>, see https://github.com/flutter/flutter/issues/99522
|
|
final Map<Object?, Object?> matchState = <Object?, Object?>{};
|
|
if (matcher.matches(actual, matchState)) {
|
|
return;
|
|
}
|
|
final StringDescription mismatchDescription = StringDescription();
|
|
matcher.describeMismatch(actual, mismatchDescription, matchState, true);
|
|
throw TestFailure(mismatchDescription.toString());
|
|
}
|
|
|
|
Future<void> runWebServiceWorkerTest({
|
|
required bool headless,
|
|
required ServiceWorkerTestType testType,
|
|
}) async {
|
|
final Map<String, int> requestedPathCounts = <String, int>{};
|
|
void expectRequestCounts(Map<String, int> expectedCounts) =>
|
|
_expectRequestCounts(expectedCounts, requestedPathCounts);
|
|
|
|
AppServer? server;
|
|
Future<void> waitForAppToLoad(Map<String, int> waitForCounts) async =>
|
|
_waitForAppToLoad(waitForCounts, requestedPathCounts, server);
|
|
|
|
String? reportedVersion;
|
|
|
|
Future<void> startAppServer({
|
|
required String cacheControl,
|
|
}) async {
|
|
final int serverPort = await findAvailablePortAndPossiblyCauseFlakyTests();
|
|
final int browserDebugPort = await findAvailablePortAndPossiblyCauseFlakyTests();
|
|
server = await AppServer.start(
|
|
headless: headless,
|
|
cacheControl: cacheControl,
|
|
// TODO(yjbanov): use a better port disambiguation strategy than trying
|
|
// to guess what ports other tests use.
|
|
appUrl: 'http://localhost:$serverPort/index.html',
|
|
serverPort: serverPort,
|
|
browserDebugPort: browserDebugPort,
|
|
appDirectory: _appBuildDirectory,
|
|
additionalRequestHandlers: <Handler>[
|
|
(Request request) {
|
|
final String requestedPath = request.url.path;
|
|
requestedPathCounts.putIfAbsent(requestedPath, () => 0);
|
|
requestedPathCounts[requestedPath] = requestedPathCounts[requestedPath]! + 1;
|
|
if (requestedPath == 'CLOSE') {
|
|
reportedVersion = request.url.queryParameters['version'];
|
|
return Response.ok('OK');
|
|
}
|
|
return Response.notFound('');
|
|
},
|
|
],
|
|
);
|
|
}
|
|
|
|
// Preserve old index.html as index_og.html so we can restore it later for other tests
|
|
await runCommand(
|
|
'mv',
|
|
<String>[
|
|
'index.html',
|
|
'index_og.html',
|
|
],
|
|
workingDirectory: _testAppWebDirectory,
|
|
);
|
|
|
|
final bool shouldExpectFlutterJs = testType != ServiceWorkerTestType.withoutFlutterJs;
|
|
|
|
print('BEGIN runWebServiceWorkerTest(headless: $headless, testType: $testType)');
|
|
|
|
try {
|
|
/////
|
|
// Attempt to load a different version of the service worker!
|
|
/////
|
|
await _rebuildApp(version: 1, testType: testType, target: _target);
|
|
|
|
print('Call update() on the current web worker');
|
|
await startAppServer(cacheControl: 'max-age=0');
|
|
await waitForAppToLoad(<String, int> {
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'CLOSE': 1,
|
|
});
|
|
expect(reportedVersion, '1');
|
|
reportedVersion = null;
|
|
|
|
await server!.chrome.reloadPage(ignoreCache: true);
|
|
await waitForAppToLoad(<String, int> {
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 2,
|
|
'CLOSE': 2,
|
|
});
|
|
expect(reportedVersion, '1');
|
|
reportedVersion = null;
|
|
|
|
await _rebuildApp(version: 2, testType: testType, target: _target);
|
|
|
|
await server!.chrome.reloadPage(ignoreCache: true);
|
|
await waitForAppToLoad(<String, int>{
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 3,
|
|
'CLOSE': 3,
|
|
});
|
|
expect(reportedVersion, '2');
|
|
|
|
reportedVersion = null;
|
|
requestedPathCounts.clear();
|
|
await server!.stop();
|
|
|
|
//////////////////////////////////////////////////////
|
|
// Caching server
|
|
//////////////////////////////////////////////////////
|
|
await _rebuildApp(version: 1, testType: testType, target: _target);
|
|
|
|
print('With cache: test first page load');
|
|
await startAppServer(cacheControl: 'max-age=3600');
|
|
await waitForAppToLoad(<String, int>{
|
|
'CLOSE': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
|
|
expectRequestCounts(<String, int>{
|
|
// Even though the server is caching index.html is downloaded twice,
|
|
// once by the initial page load, and once by the service worker.
|
|
// Other resources are loaded once only by the service worker.
|
|
'index.html': 2,
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'main.dart.js': 1,
|
|
'flutter_service_worker.js': 1,
|
|
'assets/FontManifest.json': 1,
|
|
'assets/AssetManifest.json': 1,
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'CLOSE': 1,
|
|
// In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'.
|
|
if (!headless)
|
|
...<String, int>{
|
|
'manifest.json': 1,
|
|
'favicon.ico': 1,
|
|
},
|
|
});
|
|
expect(reportedVersion, '1');
|
|
reportedVersion = null;
|
|
|
|
print('With cache: test page reload');
|
|
await server!.chrome.reloadPage();
|
|
await waitForAppToLoad(<String, int>{
|
|
'CLOSE': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
|
|
expectRequestCounts(<String, int>{
|
|
'flutter_service_worker.js': 1,
|
|
'CLOSE': 1,
|
|
});
|
|
expect(reportedVersion, '1');
|
|
reportedVersion = null;
|
|
|
|
print('With cache: test page reload after rebuild');
|
|
await _rebuildApp(version: 2, testType: testType, target: _target);
|
|
|
|
// Since we're caching, we need to ignore cache when reloading the page.
|
|
await server!.chrome.reloadPage(ignoreCache: true);
|
|
await waitForAppToLoad(<String, int>{
|
|
'CLOSE': 1,
|
|
'flutter_service_worker.js': 2,
|
|
});
|
|
expectRequestCounts(<String, int>{
|
|
'index.html': 2,
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'flutter_service_worker.js': 2,
|
|
'main.dart.js': 1,
|
|
'assets/AssetManifest.json': 1,
|
|
'assets/FontManifest.json': 1,
|
|
'CLOSE': 1,
|
|
if (!headless)
|
|
'favicon.ico': 1,
|
|
});
|
|
|
|
expect(reportedVersion, '2');
|
|
reportedVersion = null;
|
|
await server!.stop();
|
|
|
|
|
|
//////////////////////////////////////////////////////
|
|
// Non-caching server
|
|
//////////////////////////////////////////////////////
|
|
print('No cache: test first page load');
|
|
await _rebuildApp(version: 3, testType: testType, target: _target);
|
|
await startAppServer(cacheControl: 'max-age=0');
|
|
await waitForAppToLoad(<String, int>{
|
|
'CLOSE': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
|
|
expectRequestCounts(<String, int>{
|
|
'index.html': 2,
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'main.dart.js': 1,
|
|
'assets/FontManifest.json': 1,
|
|
'flutter_service_worker.js': 1,
|
|
'assets/AssetManifest.json': 1,
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'CLOSE': 1,
|
|
// In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'.
|
|
if (!headless)
|
|
...<String, int>{
|
|
'manifest.json': 1,
|
|
'favicon.ico': 1,
|
|
},
|
|
});
|
|
|
|
expect(reportedVersion, '3');
|
|
reportedVersion = null;
|
|
|
|
print('No cache: test page reload');
|
|
await server!.chrome.reloadPage();
|
|
await waitForAppToLoad(<String, int>{
|
|
'CLOSE': 1,
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
|
|
expectRequestCounts(<String, int>{
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'flutter_service_worker.js': 1,
|
|
'CLOSE': 1,
|
|
if (!headless)
|
|
'manifest.json': 1,
|
|
});
|
|
expect(reportedVersion, '3');
|
|
reportedVersion = null;
|
|
|
|
print('No cache: test page reload after rebuild');
|
|
await _rebuildApp(version: 4, testType: testType, target: _target);
|
|
|
|
// TODO(yjbanov): when running Chrome with DevTools protocol, for some
|
|
// reason a hard refresh is still required. This works without a hard
|
|
// refresh when running Chrome manually as normal. At the time of writing
|
|
// this test I wasn't able to figure out what's wrong with the way we run
|
|
// Chrome from tests.
|
|
await server!.chrome.reloadPage(ignoreCache: true);
|
|
await waitForAppToLoad(<String, int>{
|
|
'CLOSE': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
expectRequestCounts(<String, int>{
|
|
'index.html': 2,
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'flutter_service_worker.js': 2,
|
|
'main.dart.js': 1,
|
|
'assets/AssetManifest.json': 1,
|
|
'assets/FontManifest.json': 1,
|
|
'CLOSE': 1,
|
|
if (!headless)
|
|
...<String, int>{
|
|
'manifest.json': 1,
|
|
'favicon.ico': 1,
|
|
},
|
|
});
|
|
|
|
expect(reportedVersion, '4');
|
|
reportedVersion = null;
|
|
} finally {
|
|
await runCommand(
|
|
'mv',
|
|
<String>[
|
|
'index_og.html',
|
|
'index.html',
|
|
],
|
|
workingDirectory: _testAppWebDirectory,
|
|
);
|
|
await _setAppVersion(1);
|
|
await server?.stop();
|
|
}
|
|
|
|
print('END runWebServiceWorkerTest(headless: $headless, testType: $testType)');
|
|
}
|
|
|
|
Future<void> runWebServiceWorkerTestWithCachingResources({
|
|
required bool headless,
|
|
required ServiceWorkerTestType testType
|
|
}) async {
|
|
final Map<String, int> requestedPathCounts = <String, int>{};
|
|
void expectRequestCounts(Map<String, int> expectedCounts) =>
|
|
_expectRequestCounts(expectedCounts, requestedPathCounts);
|
|
|
|
AppServer? server;
|
|
Future<void> waitForAppToLoad(Map<String, int> waitForCounts) async =>
|
|
_waitForAppToLoad(waitForCounts, requestedPathCounts, server);
|
|
|
|
Future<void> startAppServer({
|
|
required String cacheControl,
|
|
}) async {
|
|
final int serverPort = await findAvailablePortAndPossiblyCauseFlakyTests();
|
|
final int browserDebugPort = await findAvailablePortAndPossiblyCauseFlakyTests();
|
|
server = await AppServer.start(
|
|
headless: headless,
|
|
cacheControl: cacheControl,
|
|
// TODO(yjbanov): use a better port disambiguation strategy than trying
|
|
// to guess what ports other tests use.
|
|
appUrl: 'http://localhost:$serverPort/index.html',
|
|
serverPort: serverPort,
|
|
browserDebugPort: browserDebugPort,
|
|
appDirectory: _appBuildDirectory,
|
|
additionalRequestHandlers: <Handler>[
|
|
(Request request) {
|
|
final String requestedPath = request.url.path;
|
|
requestedPathCounts.putIfAbsent(requestedPath, () => 0);
|
|
requestedPathCounts[requestedPath] = requestedPathCounts[requestedPath]! + 1;
|
|
if (requestedPath == 'assets/fonts/MaterialIcons-Regular.otf') {
|
|
return Response.internalServerError();
|
|
}
|
|
return Response.notFound('');
|
|
},
|
|
],
|
|
);
|
|
}
|
|
|
|
// Preserve old index.html as index_og.html so we can restore it later for other tests
|
|
await runCommand(
|
|
'mv',
|
|
<String>[
|
|
'index.html',
|
|
'index_og.html',
|
|
],
|
|
workingDirectory: _testAppWebDirectory,
|
|
);
|
|
|
|
final bool shouldExpectFlutterJs = testType != ServiceWorkerTestType.withoutFlutterJs;
|
|
|
|
print('BEGIN runWebServiceWorkerTestWithCachingResources(headless: $headless, testType: $testType)');
|
|
|
|
try {
|
|
//////////////////////////////////////////////////////
|
|
// Caching server
|
|
//////////////////////////////////////////////////////
|
|
await _rebuildApp(version: 1, testType: testType, target: _targetWithCachedResources);
|
|
|
|
print('With cache: test first page load');
|
|
await startAppServer(cacheControl: 'max-age=3600');
|
|
await waitForAppToLoad(<String, int>{
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
|
|
expectRequestCounts(<String, int>{
|
|
// Even though the server is caching index.html is downloaded twice,
|
|
// once by the initial page load, and once by the service worker.
|
|
// Other resources are loaded once only by the service worker.
|
|
'index.html': 2,
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'main.dart.js': 1,
|
|
'flutter_service_worker.js': 1,
|
|
'assets/FontManifest.json': 1,
|
|
'assets/AssetManifest.json': 1,
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
// In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'.
|
|
if (!headless)
|
|
...<String, int>{
|
|
'manifest.json': 1,
|
|
'favicon.ico': 1,
|
|
},
|
|
});
|
|
|
|
print('With cache: test first page reload');
|
|
await server!.chrome.reloadPage();
|
|
await waitForAppToLoad(<String, int>{
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
expectRequestCounts(<String, int>{
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
|
|
print('With cache: test second page reload');
|
|
await server!.chrome.reloadPage();
|
|
await waitForAppToLoad(<String, int>{
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
expectRequestCounts(<String, int>{
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
|
|
print('With cache: test third page reload');
|
|
await server!.chrome.reloadPage();
|
|
await waitForAppToLoad(<String, int>{
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
expectRequestCounts(<String, int>{
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
|
|
print('With cache: test page reload after rebuild');
|
|
await _rebuildApp(version: 1, testType: testType, target: _targetWithCachedResources);
|
|
|
|
// Since we're caching, we need to ignore cache when reloading the page.
|
|
await server!.chrome.reloadPage(ignoreCache: true);
|
|
await waitForAppToLoad(<String, int>{
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'flutter_service_worker.js': 1,
|
|
});
|
|
expectRequestCounts(<String, int>{
|
|
'index.html': 2,
|
|
if (shouldExpectFlutterJs)
|
|
'flutter.js': 1,
|
|
'main.dart.js': 1,
|
|
'flutter_service_worker.js': 2,
|
|
'assets/FontManifest.json': 1,
|
|
'assets/AssetManifest.json': 1,
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
// In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'.
|
|
if (!headless)
|
|
...<String, int>{
|
|
'favicon.ico': 1,
|
|
},
|
|
});
|
|
} finally {
|
|
await runCommand(
|
|
'mv',
|
|
<String>[
|
|
'index_og.html',
|
|
'index.html',
|
|
],
|
|
workingDirectory: _testAppWebDirectory,
|
|
);
|
|
await server?.stop();
|
|
}
|
|
|
|
print('END runWebServiceWorkerTestWithCachingResources(headless: $headless, testType: $testType)');
|
|
}
|
|
|
|
Future<void> runWebServiceWorkerTestWithBlockedServiceWorkers({
|
|
required bool headless
|
|
}) async {
|
|
final Map<String, int> requestedPathCounts = <String, int>{};
|
|
void expectRequestCounts(Map<String, int> expectedCounts) =>
|
|
_expectRequestCounts(expectedCounts, requestedPathCounts);
|
|
|
|
AppServer? server;
|
|
Future<void> waitForAppToLoad(Map<String, int> waitForCounts) async =>
|
|
_waitForAppToLoad(waitForCounts, requestedPathCounts, server);
|
|
|
|
Future<void> startAppServer({
|
|
required String cacheControl,
|
|
}) async {
|
|
final int serverPort = await findAvailablePortAndPossiblyCauseFlakyTests();
|
|
final int browserDebugPort = await findAvailablePortAndPossiblyCauseFlakyTests();
|
|
server = await AppServer.start(
|
|
headless: headless,
|
|
cacheControl: cacheControl,
|
|
// TODO(yjbanov): use a better port disambiguation strategy than trying
|
|
// to guess what ports other tests use.
|
|
appUrl: 'http://localhost:$serverPort/index.html',
|
|
serverPort: serverPort,
|
|
browserDebugPort: browserDebugPort,
|
|
appDirectory: _appBuildDirectory,
|
|
additionalRequestHandlers: <Handler>[
|
|
(Request request) {
|
|
final String requestedPath = request.url.path;
|
|
requestedPathCounts.putIfAbsent(requestedPath, () => 0);
|
|
requestedPathCounts[requestedPath] = requestedPathCounts[requestedPath]! + 1;
|
|
if (requestedPath == 'CLOSE') {
|
|
return Response.ok('OK');
|
|
}
|
|
return Response.notFound('');
|
|
},
|
|
],
|
|
);
|
|
}
|
|
|
|
// Preserve old index.html as index_og.html so we can restore it later for other tests
|
|
await runCommand(
|
|
'mv',
|
|
<String>[
|
|
'index.html',
|
|
'index_og.html',
|
|
],
|
|
workingDirectory: _testAppWebDirectory,
|
|
);
|
|
|
|
print('BEGIN runWebServiceWorkerTestWithBlockedServiceWorkers(headless: $headless)');
|
|
try {
|
|
await _rebuildApp(version: 1, testType: ServiceWorkerTestType.blockedServiceWorkers, target: _targetWithBlockedServiceWorkers);
|
|
|
|
print('Ensure app starts (when service workers are blocked)');
|
|
await startAppServer(cacheControl: 'max-age=3600');
|
|
await waitForAppToLoad(<String, int>{
|
|
'CLOSE': 1,
|
|
});
|
|
expectRequestCounts(<String, int>{
|
|
'index.html': 1,
|
|
'flutter.js': 1,
|
|
'main.dart.js': 1,
|
|
'assets/FontManifest.json': 1,
|
|
'assets/fonts/MaterialIcons-Regular.otf': 1,
|
|
'CLOSE': 1,
|
|
// In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'.
|
|
if (!headless)
|
|
...<String, int>{
|
|
'manifest.json': 1,
|
|
'favicon.ico': 1,
|
|
},
|
|
});
|
|
} finally {
|
|
await runCommand(
|
|
'mv',
|
|
<String>[
|
|
'index_og.html',
|
|
'index.html',
|
|
],
|
|
workingDirectory: _testAppWebDirectory,
|
|
);
|
|
await server?.stop();
|
|
}
|
|
print('END runWebServiceWorkerTestWithBlockedServiceWorkers(headless: $headless)');
|
|
}
|