browser.dart 3.79 KB
Newer Older
1 2 3 4 5 6 7
// 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' as io;

8
import 'package:flutter_devicelab/framework/browser.dart';
9 10 11 12 13 14 15 16 17 18 19 20 21 22
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_static/shelf_static.dart';

/// Runs Chrome, opens the given `appUrl`, and returns the result reported by the
/// app.
///
/// The app is served from the `appDirectory`. Typically, the app is built
/// using `flutter build web` and served from `build/web`.
///
/// The launched app is expected to report the result by sending an HTTP POST
/// request to "/test-result" containing result data as plain text body of the
/// request. This function has no opinion about what that string contains.
Future<String> evalTestAppInChrome({
23 24
  required String appUrl,
  required String appDirectory,
25 26 27
  int serverPort = 8080,
  int browserDebugPort = 8081,
}) async {
28 29
  io.HttpServer? server;
  Chrome? chrome;
30 31 32 33 34 35 36 37 38 39 40 41 42
  try {
    final Completer<String> resultCompleter = Completer<String>();
    server = await io.HttpServer.bind('localhost', serverPort);
    final Cascade cascade = Cascade()
      .add((Request request) async {
        if (request.requestedUri.path.endsWith('/test-result')) {
          resultCompleter.complete(await request.readAsString());
          return Response.ok('Test results received');
        }
        return Response.notFound('');
      })
      .add(createStaticHandler(appDirectory));
    shelf_io.serveRequests(server, cascade.handler);
43
    final io.Directory userDataDirectory = io.Directory.systemTemp.createTempSync('flutter_chrome_user_data.');
44 45 46 47 48 49 50 51 52 53 54 55 56 57
    chrome = await Chrome.launch(ChromeOptions(
      headless: true,
      debugPort: browserDebugPort,
      url: appUrl,
      userDataDirectory: userDataDirectory.path,
      windowHeight: 500,
      windowWidth: 500,
    ), onError: resultCompleter.completeError);
    return await resultCompleter.future;
  } finally {
    chrome?.stop();
    await server?.close();
  }
}
58 59 60 61 62 63 64

typedef ServerRequestListener = void Function(Request);

class AppServer {
  AppServer._(this._server, this.chrome, this.onChromeError);

  static Future<AppServer> start({
65 66 67
    required String appUrl,
    required String appDirectory,
    required String cacheControl,
68 69 70
    int serverPort = 8080,
    int browserDebugPort = 8081,
    bool headless = true,
71
    List<Handler>? additionalRequestHandlers,
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
  }) async {
    io.HttpServer server;
    Chrome chrome;
    server = await io.HttpServer.bind('localhost', serverPort);
    final Handler staticHandler = createStaticHandler(appDirectory, defaultDocument: 'index.html');
    Cascade cascade = Cascade();
    if (additionalRequestHandlers != null) {
      for (final Handler handler in additionalRequestHandlers) {
        cascade = cascade.add(handler);
      }
    }
    cascade = cascade.add((Request request) async {
      final Response response = await staticHandler(request);
      return response.change(headers: <String, Object>{
        'cache-control': cacheControl,
      });
    });
    shelf_io.serveRequests(server, cascade.handler);
90
    final io.Directory userDataDirectory = io.Directory.systemTemp.createTempSync('flutter_chrome_user_data.');
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
    final Completer<String> chromeErrorCompleter = Completer<String>();
    chrome = await Chrome.launch(ChromeOptions(
      headless: headless,
      debugPort: browserDebugPort,
      url: appUrl,
      userDataDirectory: userDataDirectory.path,
    ), onError: chromeErrorCompleter.complete);
    return AppServer._(server, chrome, chromeErrorCompleter.future);
  }

  final Future<String> onChromeError;
  final io.HttpServer _server;
  final Chrome chrome;

  Future<void> stop() async {
106 107
    chrome.stop();
    await _server.close();
108 109
  }
}