blob: 5c5b30a00ff08ef8b8dab709c8f3d29348c1ac3d [file] [log] [blame]
// 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';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/browser.dart';
import 'package:meta/meta.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_static/shelf_static.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
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');
// Run a web service worker test. The expectations are currently stored here
// instead of in the application. This is not run on CI due to the requirement
// of having a headful chrome instance.
Future<void> main() async {
await _runWebServiceWorkerTest('lib/service_worker_test.dart');
}
Future<void> _runWebServiceWorkerTest(String target, {
List<String> additionalArguments = const<String>[],
}) async {
final String testAppDirectory = path.join(flutterRoot, 'dev', 'integration_tests', 'web');
final String appBuildDirectory = path.join(testAppDirectory, 'build', 'web');
// Build the app.
await Process.run(
flutter,
<String>[ 'clean' ],
workingDirectory: testAppDirectory,
);
await Process.run(
flutter,
<String>[
'build',
'web',
'--release',
...additionalArguments,
'-t',
target,
],
workingDirectory: testAppDirectory,
environment: <String, String>{
'FLUTTER_WEB': 'true',
},
);
final List<Uri> requests = <Uri>[];
final List<Map<String, String>> headers = <Map<String, String>>[];
await runRecordingServer(
appUrl: 'http://localhost:8080/',
appDirectory: appBuildDirectory,
requests: requests,
headers: headers,
browserDebugPort: null,
);
final List<String> requestedPaths = requests.map((Uri uri) => uri.toString()).toList();
final List<String> expectedPaths = <String>[
// Initial page load
'',
'main.dart.js',
'assets/FontManifest.json',
'flutter_service_worker.js',
'manifest.json',
'favicon.ico',
// Service worker install.
'main.dart.js',
'index.html',
'assets/LICENSE',
'assets/AssetManifest.json',
'assets/FontManifest.json',
'',
// Second page load all cached.
];
print('requests: $requestedPaths');
// The exact order isn't important or deterministic.
for (final String path in requestedPaths) {
if (!expectedPaths.remove(path)) {
print('unexpected service worker request: $path');
exit(1);
}
}
if (expectedPaths.isNotEmpty) {
print('Missing service worker requests from expected paths: $expectedPaths');
exit(1);
}
}
/// This server runs a release web application and verifies that the service worker
/// caches files correctly, by checking the request resources over HTTP.
///
/// When it receives a request for `CLOSE` the server will be torn down.
///
/// Expects a path to the `build/web` directory produced from `flutter build web`.
Future<void> runRecordingServer({
@required String appUrl,
@required String appDirectory,
@required List<Uri> requests,
@required List<Map<String, String>> headers,
int serverPort = 8080,
int browserDebugPort = 8081,
}) async {
Chrome chrome;
HttpServer server;
final Completer<void> completer = Completer<void>();
Directory userDataDirectory;
try {
server = await HttpServer.bind('localhost', serverPort);
final Cascade cascade = Cascade()
.add((Request request) async {
if (request.url.toString().contains('CLOSE')) {
completer.complete();
return Response.notFound('');
}
requests.add(request.url);
headers.add(request.headers);
return Response.notFound('');
})
.add(createStaticHandler(appDirectory, defaultDocument: 'index.html'));
shelf_io.serveRequests(server, cascade.handler);
userDataDirectory = Directory.systemTemp.createTempSync('chrome_user_data_');
chrome = await Chrome.launch(ChromeOptions(
headless: false,
debugPort: browserDebugPort,
url: appUrl,
userDataDirectory: userDataDirectory.path,
windowHeight: 500,
windowWidth: 500,
), onError: completer.completeError);
await completer.future;
} finally {
chrome?.stop();
await server?.close();
userDataDirectory.deleteSync(recursive: true);
}
}