// 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, // Same as withFlutterJsEntrypointLoadedEvent, but with nonce required. withFlutterJsNonceOn, // Uses custom serviceWorkerVersion. withFlutterJsCustomServiceWorkerVersion, // Entrypoint generated by `flutter create`. generatedEntrypoint, } // Run a web service worker test as a standalone Dart program. Future 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 runWebServiceWorkerTest(headless: false, testType: ServiceWorkerTestType.withFlutterJsNonceOn); 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); await runWebServiceWorkerTestWithCustomServiceWorkerVersion(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 runWebServiceWorkerTestWithGeneratedEntrypoint({ required bool headless, }) async { await _generateEntrypoint(); await runWebServiceWorkerTestWithCachingResources(headless: headless, testType: ServiceWorkerTestType.generatedEntrypoint); } Future _generateEntrypoint() async { final Directory tempDirectory = Directory.systemTemp.createTempSync('flutter_web_generated_entrypoint.'); await runCommand( _flutter, [ '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 _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) { return switch (type) { ServiceWorkerTestType.blockedServiceWorkers => 'index_with_blocked_service_workers.html', ServiceWorkerTestType.withFlutterJs => 'index_with_flutterjs.html', ServiceWorkerTestType.withoutFlutterJs => 'index_without_flutterjs.html', ServiceWorkerTestType.withFlutterJsShort => 'index_with_flutterjs_short.html', ServiceWorkerTestType.withFlutterJsEntrypointLoadedEvent => 'index_with_flutterjs_entrypoint_loaded.html', ServiceWorkerTestType.withFlutterJsTrustedTypesOn => 'index_with_flutterjs_el_tt_on.html', ServiceWorkerTestType.withFlutterJsNonceOn => 'index_with_flutterjs_el_nonce.html', ServiceWorkerTestType.withFlutterJsCustomServiceWorkerVersion => 'index_with_flutterjs_custom_sw_version.html', ServiceWorkerTestType.generatedEntrypoint => 'generated_entrypoint.html', }; } Future _rebuildApp({ required int version, required ServiceWorkerTestType testType, required String target }) async { await _setAppVersion(version); await runCommand( _flutter, [ 'clean' ], workingDirectory: _testAppDirectory, ); await runCommand( 'cp', [ _testTypeToIndexFile(testType), 'index.html', ], workingDirectory: _testAppWebDirectory, ); await runCommand( _flutter, ['build', 'web', '--web-resources-cdn', '--profile', '-t', target], workingDirectory: _testAppDirectory, environment: { 'FLUTTER_WEB': 'true', }, ); } void _expectRequestCounts( Map expectedCounts, Map requestedPathCounts, ) { expect(requestedPathCounts, expectedCounts); requestedPathCounts.clear(); } Future _waitForAppToLoad( Map waitForCounts, Map requestedPathCounts, AppServer? server ) async { print('Waiting for app to load $waitForCounts'); await Future.any(>[ () async { while (!waitForCounts.entries.every((MapEntry entry) => (requestedPathCounts[entry.key] ?? 0) >= entry.value)) { await Future.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 , see https://github.com/flutter/flutter/issues/99522 final Map matchState = {}; if (matcher.matches(actual, matchState)) { return; } final StringDescription mismatchDescription = StringDescription(); matcher.describeMismatch(actual, mismatchDescription, matchState, true); throw TestFailure(mismatchDescription.toString()); } Future runWebServiceWorkerTest({ required bool headless, required ServiceWorkerTestType testType, }) async { final Map requestedPathCounts = {}; void expectRequestCounts(Map expectedCounts) => _expectRequestCounts(expectedCounts, requestedPathCounts); AppServer? server; Future waitForAppToLoad(Map waitForCounts) async => _waitForAppToLoad(waitForCounts, requestedPathCounts, server); String? reportedVersion; Future 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: [ (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', [ '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( { if (shouldExpectFlutterJs) 'flutter.js': 1, 'CLOSE': 1, }); expect(reportedVersion, '1'); reportedVersion = null; await server!.chrome.reloadPage(ignoreCache: true); await waitForAppToLoad( { 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({ 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({ 'CLOSE': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ // 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.bin.json': 1, 'assets/fonts/MaterialIcons-Regular.otf': 1, 'CLOSE': 1, // In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'. if (!headless) ...{ 'manifest.json': 1, 'favicon.ico': 1, }, }); expect(reportedVersion, '1'); reportedVersion = null; print('With cache: test page reload'); await server!.chrome.reloadPage(); await waitForAppToLoad({ 'CLOSE': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ '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({ 'CLOSE': 1, 'flutter_service_worker.js': 2, }); expectRequestCounts({ 'index.html': 2, if (shouldExpectFlutterJs) 'flutter.js': 1, 'flutter_service_worker.js': 2, 'main.dart.js': 1, 'assets/AssetManifest.bin.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({ 'CLOSE': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ 'index.html': 2, if (shouldExpectFlutterJs) 'flutter.js': 1, 'main.dart.js': 1, 'assets/FontManifest.json': 1, 'flutter_service_worker.js': 1, 'assets/AssetManifest.bin.json': 1, 'assets/fonts/MaterialIcons-Regular.otf': 1, 'CLOSE': 1, // In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'. if (!headless) ...{ 'manifest.json': 1, 'favicon.ico': 1, }, }); expect(reportedVersion, '3'); reportedVersion = null; print('No cache: test page reload'); await server!.chrome.reloadPage(); await waitForAppToLoad({ 'CLOSE': 1, if (shouldExpectFlutterJs) 'flutter.js': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ 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({ 'CLOSE': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ 'index.html': 2, if (shouldExpectFlutterJs) 'flutter.js': 1, 'flutter_service_worker.js': 2, 'main.dart.js': 1, 'assets/AssetManifest.bin.json': 1, 'assets/FontManifest.json': 1, 'CLOSE': 1, if (!headless) ...{ 'manifest.json': 1, 'favicon.ico': 1, }, }); expect(reportedVersion, '4'); reportedVersion = null; } finally { await runCommand( 'mv', [ 'index_og.html', 'index.html', ], workingDirectory: _testAppWebDirectory, ); await _setAppVersion(1); await server?.stop(); } print('END runWebServiceWorkerTest(headless: $headless, testType: $testType)'); } Future runWebServiceWorkerTestWithCachingResources({ required bool headless, required ServiceWorkerTestType testType }) async { final Map requestedPathCounts = {}; void expectRequestCounts(Map expectedCounts) => _expectRequestCounts(expectedCounts, requestedPathCounts); AppServer? server; Future waitForAppToLoad(Map waitForCounts) async => _waitForAppToLoad(waitForCounts, requestedPathCounts, server); Future 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: [ (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', [ '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({ 'assets/fonts/MaterialIcons-Regular.otf': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ // 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.bin.json': 1, 'assets/fonts/MaterialIcons-Regular.otf': 1, // In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'. if (!headless) ...{ 'manifest.json': 1, 'favicon.ico': 1, }, }); print('With cache: test first page reload'); await server!.chrome.reloadPage(); await waitForAppToLoad({ 'assets/fonts/MaterialIcons-Regular.otf': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ 'assets/fonts/MaterialIcons-Regular.otf': 1, 'flutter_service_worker.js': 1, }); print('With cache: test second page reload'); await server!.chrome.reloadPage(); await waitForAppToLoad({ 'assets/fonts/MaterialIcons-Regular.otf': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ 'assets/fonts/MaterialIcons-Regular.otf': 1, 'flutter_service_worker.js': 1, }); print('With cache: test third page reload'); await server!.chrome.reloadPage(); await waitForAppToLoad({ 'assets/fonts/MaterialIcons-Regular.otf': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ '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({ 'assets/fonts/MaterialIcons-Regular.otf': 1, 'flutter_service_worker.js': 1, }); expectRequestCounts({ 'index.html': 2, if (shouldExpectFlutterJs) 'flutter.js': 1, 'main.dart.js': 1, 'flutter_service_worker.js': 2, 'assets/FontManifest.json': 1, 'assets/AssetManifest.bin.json': 1, 'assets/fonts/MaterialIcons-Regular.otf': 1, // In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'. if (!headless) ...{ 'favicon.ico': 1, }, }); } finally { await runCommand( 'mv', [ 'index_og.html', 'index.html', ], workingDirectory: _testAppWebDirectory, ); await server?.stop(); } print('END runWebServiceWorkerTestWithCachingResources(headless: $headless, testType: $testType)'); } Future runWebServiceWorkerTestWithBlockedServiceWorkers({ required bool headless }) async { final Map requestedPathCounts = {}; void expectRequestCounts(Map expectedCounts) => _expectRequestCounts(expectedCounts, requestedPathCounts); AppServer? server; Future waitForAppToLoad(Map waitForCounts) async => _waitForAppToLoad(waitForCounts, requestedPathCounts, server); Future 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: [ (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', [ '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({ 'CLOSE': 1, }); expectRequestCounts({ '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) ...{ 'manifest.json': 1, 'favicon.ico': 1, }, }); } finally { await runCommand( 'mv', [ 'index_og.html', 'index.html', ], workingDirectory: _testAppWebDirectory, ); await server?.stop(); } print('END runWebServiceWorkerTestWithBlockedServiceWorkers(headless: $headless)'); } /// Regression test for https://github.com/flutter/flutter/issues/130212. Future runWebServiceWorkerTestWithCustomServiceWorkerVersion({ required bool headless, }) async { final Map requestedPathCounts = {}; void expectRequestCounts(Map expectedCounts) => _expectRequestCounts(expectedCounts, requestedPathCounts); AppServer? server; Future waitForAppToLoad(Map waitForCounts) async => _waitForAppToLoad(waitForCounts, requestedPathCounts, server); Future 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: [ (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', [ 'index.html', 'index_og.html', ], workingDirectory: _testAppWebDirectory, ); print('BEGIN runWebServiceWorkerTestWithCustomServiceWorkerVersion(headless: $headless)'); try { await _rebuildApp(version: 1, testType: ServiceWorkerTestType.withFlutterJsCustomServiceWorkerVersion, target: _target); print('Test page load'); await startAppServer(cacheControl: 'max-age=0'); await waitForAppToLoad({ 'CLOSE': 1, 'flutter_service_worker.js': 1, 'assets/fonts/MaterialIcons-Regular.otf': 1, }); expectRequestCounts({ 'index.html': 2, 'flutter.js': 1, 'main.dart.js': 1, 'CLOSE': 1, 'flutter_service_worker.js': 1, 'assets/FontManifest.json': 1, 'assets/AssetManifest.bin.json': 1, 'assets/fonts/MaterialIcons-Regular.otf': 1, // In headless mode Chrome does not load 'manifest.json' and 'favicon.ico'. if (!headless) ...{ 'manifest.json': 1, 'favicon.ico': 1, }, }); print('Test page reload, ensure service worker is not reloaded'); await server!.chrome.reloadPage(ignoreCache: true); await waitForAppToLoad({ 'CLOSE': 1, 'flutter.js': 1, }); expectRequestCounts({ '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) ...{ 'manifest.json': 1, 'favicon.ico': 1, }, }); print('Test page reload after rebuild, ensure service worker is not reloaded'); await _rebuildApp(version: 1, testType: ServiceWorkerTestType.withFlutterJsCustomServiceWorkerVersion, target: _target); await server!.chrome.reloadPage(ignoreCache: true); await waitForAppToLoad({ 'CLOSE': 1, 'flutter.js': 1, }); expectRequestCounts({ '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) ...{ 'manifest.json': 1, 'favicon.ico': 1, }, }); } finally { await runCommand( 'mv', [ 'index_og.html', 'index.html', ], workingDirectory: _testAppWebDirectory, ); await server?.stop(); } print('END runWebServiceWorkerTestWithCustomServiceWorkerVersion(headless: $headless)'); }