Use flutter.js in the actual test harness. (#47670)

Run the JS unit tests with flutter.js.

I am going to leave the dart2wasm tests doing the old `window.flutterConfiguration` setup for now, until I actually get dart2wasm/dart2js switching logic built into flutter.js itself.
diff --git a/lib/web_ui/dev/steps/copy_artifacts_step.dart b/lib/web_ui/dev/steps/copy_artifacts_step.dart
index 4e359b4..fa63d91 100644
--- a/lib/web_ui/dev/steps/copy_artifacts_step.dart
+++ b/lib/web_ui/dev/steps/copy_artifacts_step.dart
@@ -33,10 +33,11 @@
   @override
   Future<void> run() async {
     await environment.webTestsArtifactsDir.create(recursive: true);
-    await copyDart2WasmTestScript();
+    await copyTestBootstrapScripts();
     await buildHostPage();
     await copyTestFonts();
     await copySkiaTestImages();
+    await copyFlutterJsFiles();
     if (artifactDeps.canvasKit) {
       print('Copying CanvasKit...');
       await copyCanvasKitFiles('canvaskit', 'canvaskit');
@@ -51,16 +52,21 @@
     }
   }
 
-  Future<void> copyDart2WasmTestScript() async {
-    final io.File sourceFile = io.File(pathlib.join(
-      environment.webUiDevDir.path,
+  Future<void> copyTestBootstrapScripts() async {
+    for (final String filename in <String>[
+      'test_dart2js.js',
       'test_dart2wasm.js',
-    ));
-    final io.File targetFile = io.File(pathlib.join(
-      environment.webTestsArtifactsDir.path,
-      'test_dart2wasm.js',
-    ));
-    await sourceFile.copy(targetFile.path);
+    ]) {
+      final io.File sourceFile = io.File(pathlib.join(
+        environment.webUiDevDir.path,
+        filename,
+      ));
+      final io.File targetFile = io.File(pathlib.join(
+        environment.webTestsArtifactsDir.path,
+        filename,
+      ));
+      await sourceFile.copy(targetFile.path);
+    }
   }
 
   Future<void> copyTestFonts() async {
@@ -158,6 +164,37 @@
     }
   }
 
+  Future<void> copyFlutterJsFiles() async {
+    final io.Directory flutterJsInputDirectory = io.Directory(pathlib.join(
+      outBuildPath,
+      'flutter_web_sdk',
+      'flutter_js',
+    ));
+    final String targetDirectoryPath = pathlib.join(
+      environment.webTestsArtifactsDir.path,
+      'flutter_js',
+    );
+
+    for (final io.File sourceFile in flutterJsInputDirectory
+      .listSync(recursive: true)
+      .whereType<io.File>()
+    ) {
+      final String relativePath = pathlib.relative(
+        sourceFile.path,
+        from: flutterJsInputDirectory.path
+      );
+      final String targetPath = pathlib.join(
+        targetDirectoryPath,
+        relativePath,
+      );
+      final io.File targetFile = io.File(targetPath);
+      if (!targetFile.parent.existsSync()) {
+        targetFile.parent.createSync(recursive: true);
+      }
+      sourceFile.copySync(targetPath);
+    }
+  }
+
   Future<void> copyCanvasKitFiles(String sourcePath, String destinationPath) async {
     final String sourceDirectoryPath = pathlib.join(
       outBuildPath,
diff --git a/lib/web_ui/dev/test_dart2js.js b/lib/web_ui/dev/test_dart2js.js
new file mode 100644
index 0000000..22f55b5
--- /dev/null
+++ b/lib/web_ui/dev/test_dart2js.js
@@ -0,0 +1,67 @@
+// Copyright 2013 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.
+
+// This script runs in HTML files and loads and instantiates unit tests
+// for the flutter web engine that are compiled to dart2js. It is based
+// off of the `test/dart.js` script from the `test` dart package.
+
+export const runTest = (configuration) => {
+  // Sends an error message to the server indicating that the script failed to
+  // load.
+  //
+  // This mimics a MultiChannel-formatted message.
+  var sendLoadException = function (message) {
+    window.parent.postMessage({
+      "href": window.location.href,
+      "data": [0, { "type": "loadException", "message": message }],
+      "exception": true,
+    }, window.location.origin);
+  }
+
+  // Listen for dartLoadException events and forward to the server.
+  window.addEventListener('dartLoadException', function (e) {
+    sendLoadException(e.detail);
+  });
+
+  // The basename of the current page.
+  var name = window.location.href.replace(/.*\//, '').replace(/#.*/, '');
+
+  // Find <link rel="x-dart-test">.
+  var links = document.getElementsByTagName("link");
+  var testLinks = [];
+  var length = links.length;
+  for (var i = 0; i < length; ++i) {
+    if (links[i].rel == "x-dart-test") testLinks.push(links[i]);
+  }
+
+  if (testLinks.length != 1) {
+    sendLoadException(
+      'Expected exactly 1 <link rel="x-dart-test"> in ' + name + ', found ' +
+      testLinks.length + '.');
+    return;
+  }
+
+  var link = testLinks[0];
+
+  if (link.href == '') {
+    sendLoadException(
+      'Expected <link rel="x-dart-test"> in ' + name + ' to have an "href" ' +
+      'attribute.');
+    return;
+  }
+
+  try {
+    window._flutter.loader.loadEntrypoint({
+      entrypointUrl: link.href + '.browser_test.dart.js',
+      onEntrypointLoaded: function(engineInitializer) {
+        engineInitializer.initializeEngine(configuration).then(function(appRunner) {
+          appRunner.runApp();
+        });
+      }
+    });
+  } catch (exception) {
+    const message = `Failed to bootstrap unit test: ${exception}`;
+    sendLoadException(message);
+  }
+};
diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart
index da32db9..90d3172 100644
--- a/lib/web_ui/dev/test_platform.dart
+++ b/lib/web_ui/dev/test_platform.dart
@@ -538,23 +538,36 @@
       final String scriptBase = htmlEscape.convert(p.basename(test));
       final String link = '<link rel="x-dart-test" href="$scriptBase"${linkSkwasm ? " skwasm" : ""}>';
 
-      final String testRunner = isWasm ? '/test_dart2wasm.js' : 'packages/test/dart.js';
+      final String bootstrapScript = isWasm ? '''
+<script>
+  window.flutterConfiguration = {
+    canvasKitBaseUrl: "/canvaskit/",
+    // Some of our tests rely on color emoji
+    useColorEmoji: true,
+    canvasKitVariant: "${getCanvasKitVariant()}",
+  };
+</script>
+<script src="/test_dart2wasm.js"></script>
+      ''' : '''
+<script src="/flutter_js/flutter.js"></script>
+<script type="module">
+  import { runTest } from "/test_dart2js.js";
 
+  runTest({
+    canvasKitBaseUrl: "/canvaskit/",
+    // Some of our tests rely on color emoji
+    useColorEmoji: true,
+    canvasKitVariant: "${getCanvasKitVariant()}",
+  });
+</script>
+''';
       return shelf.Response.ok('''
         <!DOCTYPE html>
         <html>
         <head>
           <meta name="assetBase" content="/">
-          <script>
-            window.flutterConfiguration = {
-              canvasKitBaseUrl: "/canvaskit/",
-              // Some of our tests rely on color emoji
-              useColorEmoji: true,
-              canvasKitVariant: "${getCanvasKitVariant()}",
-            };
-          </script>
           $link
-          <script src="$testRunner"></script>
+          $bootstrapScript
         </head>
         </html>
       ''', headers: <String, String>{
diff --git a/lib/web_ui/lib/initialization.dart b/lib/web_ui/lib/initialization.dart
index 9e16411..43dc09e 100644
--- a/lib/web_ui/lib/initialization.dart
+++ b/lib/web_ui/lib/initialization.dart
@@ -23,24 +23,6 @@
 
 part of ui;
 
-/// Performs one-time initialization of the web environment that supports the
-/// Flutter framework.
-///
-/// This is only available on the Web, as native Flutter configures the
-/// environment in the native embedder.
-// TODO(mdebbar): Deprecate this and remove it.
-// https://github.com/flutter/flutter/issues/127395
-Future<void> webOnlyInitializePlatform() async {
-  assert(() {
-    engine.printWarning(
-      'The webOnlyInitializePlatform API is deprecated and will be removed in a '
-      'future release. Please use `bootstrapEngine` from `dart:ui_web` instead.',
-    );
-    return true;
-  }());
-  await engine.initializeEngine();
-}
-
 // TODO(mdebbar): Deprecate this and remove it.
 // https://github.com/flutter/flutter/issues/127395
 Future<void> webOnlyWarmupEngine({
diff --git a/lib/web_ui/lib/src/engine/configuration.dart b/lib/web_ui/lib/src/engine/configuration.dart
index 1a5dff3..d875686 100644
--- a/lib/web_ui/lib/src/engine/configuration.dart
+++ b/lib/web_ui/lib/src/engine/configuration.dart
@@ -51,10 +51,16 @@
 import 'dom.dart';
 
 /// The Web Engine configuration for the current application.
-FlutterConfiguration get configuration =>
-  _configuration ??= FlutterConfiguration.legacy(_jsConfiguration);
+FlutterConfiguration get configuration {
+  if (_debugConfiguration != null) {
+    return _debugConfiguration!;
+  }
+  return _configuration ??= FlutterConfiguration.legacy(_jsConfiguration);
+}
 FlutterConfiguration? _configuration;
 
+FlutterConfiguration? _debugConfiguration;
+
 /// Overrides the initial test configuration with new values coming from `newConfig`.
 ///
 /// The initial test configuration (AKA `_jsConfiguration`) is set in the
@@ -76,16 +82,9 @@
 @visibleForTesting
 void debugOverrideJsConfiguration(JsFlutterConfiguration? newConfig) {
   if (newConfig != null) {
-    final JSObject newJsConfig = objectConstructor.assign(
-      <String, Object>{}.jsify(),
-      _jsConfiguration.jsify(),
-      newConfig.jsify(),
-    );
-    _configuration = FlutterConfiguration()
-        ..setUserConfiguration(newJsConfig as JsFlutterConfiguration);
-    print('Overridden engine JS config to: ${newJsConfig.dartify()}');
+    _debugConfiguration = configuration.withOverrides(newConfig);
   } else {
-    _configuration = null;
+    _debugConfiguration = null;
   }
 }
 
@@ -120,6 +119,17 @@
     }());
   }
 
+  FlutterConfiguration withOverrides(JsFlutterConfiguration? overrides) {
+    final JsFlutterConfiguration newJsConfig = objectConstructor.assign(
+      <String, Object>{}.jsify(),
+      _configuration.jsify(),
+      overrides.jsify(),
+    ) as JsFlutterConfiguration;
+    final FlutterConfiguration newConfig = FlutterConfiguration();
+    newConfig._configuration = newJsConfig;
+    return newConfig;
+  }
+
   bool _usedLegacyConfigStyle = false;
   JsFlutterConfiguration? _configuration;
 
@@ -314,8 +324,11 @@
 
 /// The JS bindings for the object that's set as `window.flutterConfiguration`.
 @JS()
+@anonymous
 @staticInterop
-class JsFlutterConfiguration {}
+class JsFlutterConfiguration {
+  external factory JsFlutterConfiguration();
+}
 
 extension JsFlutterConfigurationExtension on JsFlutterConfiguration {
   @JS('assetBase')
diff --git a/lib/web_ui/lib/src/engine/initialization.dart b/lib/web_ui/lib/src/engine/initialization.dart
index 17b0909..16faf28 100644
--- a/lib/web_ui/lib/src/engine/initialization.dart
+++ b/lib/web_ui/lib/src/engine/initialization.dart
@@ -55,14 +55,6 @@
   }
 }
 
-/// Fully initializes the engine, including services and UI.
-Future<void> initializeEngine({
-  ui_web.AssetManager? assetManager,
-}) async {
-  await initializeEngineServices(assetManager: assetManager);
-  await initializeEngineUi();
-}
-
 /// How far along the initialization process the engine is currently is.
 ///
 /// The initialization process starts with [none] and proceeds in increasing
@@ -231,8 +223,11 @@
   _initializationState = DebugEngineInitializationState.initialized;
 }
 
-ui_web.AssetManager get engineAssetManager => _assetManager!;
+ui_web.AssetManager get engineAssetManager => _debugAssetManager ?? _assetManager!;
 ui_web.AssetManager? _assetManager;
+ui_web.AssetManager? _debugAssetManager;
+
+set debugOnlyAssetManager(ui_web.AssetManager? manager) => _debugAssetManager = manager;
 
 void _setAssetManager(ui_web.AssetManager assetManager) {
   if (assetManager == _assetManager) {
@@ -254,7 +249,7 @@
     );
   }
 
-  if (_assetManager != null) {
+  if (_debugAssetManager != null || _assetManager != null) {
     await renderer.fontCollection.loadAssetFonts(await fetchFontManifest(ui_web.assetManager));
   }
 }
diff --git a/lib/web_ui/test/canvaskit/fragment_program_test.dart b/lib/web_ui/test/canvaskit/fragment_program_test.dart
index e7eaf22..d6de8dd 100644
--- a/lib/web_ui/test/canvaskit/fragment_program_test.dart
+++ b/lib/web_ui/test/canvaskit/fragment_program_test.dart
@@ -10,7 +10,8 @@
 
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
-import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
+
+import '../common/test_initialization.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
@@ -183,7 +184,7 @@
 
 void testMain() {
   setUpAll(() async {
-    await ui_web.bootstrapEngine();
+    await bootstrapAndRunApp();
   });
 
   test('FragmentProgram can be created from JSON IPLR bundle', () {
diff --git a/lib/web_ui/test/canvaskit/initialization/does_not_mock_module_exports_test.dart b/lib/web_ui/test/canvaskit/initialization/does_not_mock_module_exports_test.dart
index c06e387..b6676f2 100644
--- a/lib/web_ui/test/canvaskit/initialization/does_not_mock_module_exports_test.dart
+++ b/lib/web_ui/test/canvaskit/initialization/does_not_mock_module_exports_test.dart
@@ -6,6 +6,8 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 
+import '../../common/test_initialization.dart';
+
 void main() {
   internalBootstrapBrowserTest(() => testMain);
 }
@@ -14,7 +16,7 @@
   group('initializeEngineServices', () {
     test('does not mock module loaders', () async {
       // Initialize CanvasKit...
-      await initializeEngineServices();
+      await bootstrapAndRunApp();
 
       // CanvasKitInit should be defined...
       expect(
diff --git a/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart b/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart
index f4adbc5..49236ea 100644
--- a/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart
+++ b/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:async';
+
 import 'package:test/bootstrap/browser.dart';
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
@@ -12,6 +14,7 @@
 
 void testMain() {
   test('services are initalized separately from UI', () async {
+    final JsFlutterConfiguration? config = await bootstrapAndExtractConfig();
     expect(scheduleFrameCallback, isNull);
     expect(windowFlutterCanvasKit, isNull);
 
@@ -21,7 +24,7 @@
     expect(PointerBinding.instance, isNull);
 
     // After initializing services the UI should remain intact.
-    await initializeEngineServices();
+    await initializeEngineServices(jsConfiguration: config);
     expect(scheduleFrameCallback, isNotNull);
     expect(windowFlutterCanvasKit, isNotNull);
 
@@ -42,3 +45,27 @@
 DomElement? findGlassPane() {
   return domDocument.querySelector('flt-glass-pane');
 }
+
+Future<JsFlutterConfiguration?> bootstrapAndExtractConfig() {
+  // Since this test is explicitly checking each part of the bootstrapping process,
+  // we can't use the standard bootstrapper here. However, we do need the flutter
+  // configuration object that is passed into flutter.js to actually initialize the
+  // engine with, so here we do a little no-op bootstrap that just retrieves the
+  // configuration that is passed into the `initializeEngine` callback.
+  final Completer<JsFlutterConfiguration?> configCompleter = Completer<JsFlutterConfiguration?>();
+  final AppBootstrap bootstrap = AppBootstrap(
+    initializeEngine: ([JsFlutterConfiguration? config]) async => configCompleter.complete(config),
+    runApp: () {}
+  );
+  final FlutterLoader? loader = flutter?.loader;
+  if (loader == null || loader.isAutoStart) {
+    // TODO(jacksongardner): Unit tests under dart2wasm still use the old way which
+    // doesn't invoke flutter.js directly, so we autostart here. Once dart2wasm tests
+    // work with flutter.js, we can remove this code path.
+    bootstrap.autoStart();
+  } else {
+    loader.didCreateEngineInitializer(bootstrap.prepareEngineInitializer());
+  }
+
+  return configCompleter.future;
+}
diff --git a/lib/web_ui/test/canvaskit/initialization/stores_config_test.dart b/lib/web_ui/test/canvaskit/initialization/stores_config_test.dart
index faf49af..b7869d9 100644
--- a/lib/web_ui/test/canvaskit/initialization/stores_config_test.dart
+++ b/lib/web_ui/test/canvaskit/initialization/stores_config_test.dart
@@ -14,13 +14,10 @@
 void testMain() {
   group('initializeEngineServices', () {
     test('stores user configuration', () async {
-      // dev/test_platform.dart injects a global configuration object. Let's
-      // fetch that, override one of its properties (under test), then delete it
-      // from window (so our configuration asserts don't fire!)
-      final JsFlutterConfiguration config = js_util.getProperty(domWindow, 'flutterConfiguration');
+      final JsFlutterConfiguration config = JsFlutterConfiguration();
       js_util.setProperty(config, 'canvasKitMaximumSurfaces', 32.0);
+      js_util.setProperty(config, 'canvasKitBaseUrl', '/canvaskit/');
       js_util.setProperty(domWindow, 'flutterConfiguration', null);
-
       await initializeEngineServices(jsConfiguration: config);
 
       expect(configuration.canvasKitMaximumSurfaces, 32);
diff --git a/lib/web_ui/test/canvaskit/scene_test.dart b/lib/web_ui/test/canvaskit/scene_test.dart
index 4bad3c7..9526636 100644
--- a/lib/web_ui/test/canvaskit/scene_test.dart
+++ b/lib/web_ui/test/canvaskit/scene_test.dart
@@ -7,7 +7,8 @@
 
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
-import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
+
+import '../common/test_initialization.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
@@ -16,7 +17,7 @@
 void testMain() {
   group('$LayerScene', () {
     setUpAll(() async {
-      await ui_web.bootstrapEngine();
+      await bootstrapAndRunApp();
     });
 
     test('toImage returns an image', () async {
diff --git a/lib/web_ui/test/common/test_initialization.dart b/lib/web_ui/test/common/test_initialization.dart
index 5849ea9..b635987 100644
--- a/lib/web_ui/test/common/test_initialization.dart
+++ b/lib/web_ui/test/common/test_initialization.dart
@@ -2,8 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:async';
+
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart' as engine;
+import 'package:ui/src/engine/initialization.dart';
 import 'package:ui/ui.dart' as ui;
 import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
 
@@ -24,7 +27,8 @@
     }
 
     debugFontsScope = configureDebugFontsAssetScope(fakeAssetManager);
-    await engine.initializeEngine(assetManager: fakeAssetManager);
+    debugOnlyAssetManager = fakeAssetManager;
+    await bootstrapAndRunApp();
     engine.renderer.fontCollection.fontFallbackManager?.downloadQueue.fallbackFontUrlPrefixOverride = 'assets/fallback_fonts/';
 
     if (setUpTestViewDimensions) {
@@ -46,3 +50,9 @@
     fakeAssetManager.popAssetScope(debugFontsScope);
   });
 }
+
+Future<void> bootstrapAndRunApp() async {
+  final Completer<void> completer = Completer<void>();
+  await ui_web.bootstrapEngine(runApp: () => completer.complete());
+  await completer.future;
+}
diff --git a/lib/web_ui/test/engine/canvas_test.dart b/lib/web_ui/test/engine/canvas_test.dart
index 324dae7..0f19e12 100644
--- a/lib/web_ui/test/engine/canvas_test.dart
+++ b/lib/web_ui/test/engine/canvas_test.dart
@@ -9,13 +9,14 @@
 import 'package:ui/ui.dart' as ui;
 
 import '../common/mock_engine_canvas.dart';
+import '../common/test_initialization.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
 }
 
 Future<void> testMain() async {
-  await initializeEngine();
+  await bootstrapAndRunApp();
 
   group('EngineCanvas', () {
     late MockEngineCanvas mockCanvas;
diff --git a/lib/web_ui/test/engine/composition_test.dart b/lib/web_ui/test/engine/composition_test.dart
index dcfc100..cde257d 100644
--- a/lib/web_ui/test/engine/composition_test.dart
+++ b/lib/web_ui/test/engine/composition_test.dart
@@ -9,10 +9,11 @@
 import 'package:ui/src/engine/browser_detection.dart';
 
 import 'package:ui/src/engine/dom.dart';
-import 'package:ui/src/engine/initialization.dart';
 import 'package:ui/src/engine/text_editing/composition_aware_mixin.dart';
 import 'package:ui/src/engine/text_editing/text_editing.dart';
 
+import '../common/test_initialization.dart';
+
 void main() {
   internalBootstrapBrowserTest(() => testMain);
 }
@@ -47,7 +48,7 @@
 }
 
 Future<void> testMain() async {
-  await initializeEngine();
+  await bootstrapAndRunApp();
 
   const String fakeComposingText = 'ImComposingText';
 
diff --git a/lib/web_ui/test/engine/dom_http_fetch_test.dart b/lib/web_ui/test/engine/dom_http_fetch_test.dart
index 48ed06a..fbe4896 100644
--- a/lib/web_ui/test/engine/dom_http_fetch_test.dart
+++ b/lib/web_ui/test/engine/dom_http_fetch_test.dart
@@ -8,14 +8,15 @@
 import 'package:test/bootstrap/browser.dart';
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
-import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
+
+import '../common/test_initialization.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
 }
 
 Future<void> testMain() async {
-  await ui_web.bootstrapEngine();
+  await bootstrapAndRunApp();
 
   // Test successful HTTP roundtrips where the server returns a happy status
   // code and a payload.
diff --git a/lib/web_ui/test/engine/platform_views/content_manager_test.dart b/lib/web_ui/test/engine/platform_views/content_manager_test.dart
index 445094c..6c21441 100644
--- a/lib/web_ui/test/engine/platform_views/content_manager_test.dart
+++ b/lib/web_ui/test/engine/platform_views/content_manager_test.dart
@@ -7,13 +7,15 @@
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
 
+import '../../common/test_initialization.dart';
+
 void main() {
   internalBootstrapBrowserTest(() => testMain);
 }
 
 void testMain() {
   setUpAll(() async {
-    await initializeEngine();
+    await bootstrapAndRunApp();
   });
 
   group('PlatformViewManager', () {
diff --git a/lib/web_ui/test/engine/routing_test.dart b/lib/web_ui/test/engine/routing_test.dart
index e637cd7..f587575 100644
--- a/lib/web_ui/test/engine/routing_test.dart
+++ b/lib/web_ui/test/engine/routing_test.dart
@@ -11,6 +11,7 @@
 import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
 
 import '../common/matchers.dart';
+import '../common/test_initialization.dart';
 import 'history_test.dart';
 
 const MethodCodec codec = JSONMethodCodec();
@@ -31,7 +32,7 @@
   late EngineFlutterWindow myWindow;
 
   setUpAll(() async {
-    await initializeEngine();
+    await bootstrapAndRunApp();
   });
 
   setUp(() {
diff --git a/lib/web_ui/test/engine/semantics/semantics_test.dart b/lib/web_ui/test/engine/semantics/semantics_test.dart
index 47b11e6..0156372 100644
--- a/lib/web_ui/test/engine/semantics/semantics_test.dart
+++ b/lib/web_ui/test/engine/semantics/semantics_test.dart
@@ -16,6 +16,7 @@
 import 'package:ui/ui.dart' as ui;
 import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
 
+import '../../common/test_initialization.dart';
 import 'semantics_tester.dart';
 
 DateTime _testTime = DateTime(2018, 12, 17);
@@ -34,7 +35,7 @@
 }
 
 Future<void> testMain() async {
-  await ui_web.bootstrapEngine();
+  await bootstrapAndRunApp();
   runSemanticsTests();
 }
 
diff --git a/lib/web_ui/test/engine/surface/frame_timings_test.dart b/lib/web_ui/test/engine/surface/frame_timings_test.dart
index 901f1ff..0dcf882 100644
--- a/lib/web_ui/test/engine/surface/frame_timings_test.dart
+++ b/lib/web_ui/test/engine/surface/frame_timings_test.dart
@@ -4,9 +4,9 @@
 
 import 'package:test/bootstrap/browser.dart';
 import 'package:test/test.dart';
-import 'package:ui/src/engine.dart';
 
 import '../../common/frame_timings_common.dart';
+import '../../common/test_initialization.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
@@ -14,7 +14,7 @@
 
 void testMain() {
   setUp(() async {
-    await initializeEngine();
+    await bootstrapAndRunApp();
   });
 
   test('collects frame timings', () async {
diff --git a/lib/web_ui/test/engine/surface/platform_view_test.dart b/lib/web_ui/test/engine/surface/platform_view_test.dart
index 76e2ce4..5ca231e 100644
--- a/lib/web_ui/test/engine/surface/platform_view_test.dart
+++ b/lib/web_ui/test/engine/surface/platform_view_test.dart
@@ -11,6 +11,7 @@
 import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
 
 import '../../common/matchers.dart';
+import '../../common/test_initialization.dart';
 
 const MethodCodec codec = StandardMethodCodec();
 final EngineFlutterWindow window = EngineFlutterWindow(0, EnginePlatformDispatcher.instance);
@@ -20,7 +21,7 @@
 }
 
 Future<void> testMain() async {
-  await ui_web.bootstrapEngine();
+  await bootstrapAndRunApp();
 
   late PersistedPlatformView view;
 
diff --git a/lib/web_ui/test/engine/surface/scene_builder_test.dart b/lib/web_ui/test/engine/surface/scene_builder_test.dart
index b173c76..2a653c0 100644
--- a/lib/web_ui/test/engine/surface/scene_builder_test.dart
+++ b/lib/web_ui/test/engine/surface/scene_builder_test.dart
@@ -13,9 +13,9 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
-import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
 
 import '../../common/matchers.dart';
+import '../../common/test_initialization.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
@@ -23,7 +23,7 @@
 
 void testMain() {
   setUpAll(() async {
-    await ui_web.bootstrapEngine();
+    await bootstrapAndRunApp();
   });
 
   group('SceneBuilder', () {
diff --git a/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart b/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart
index 02ddbfa..f20467b 100644
--- a/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart
+++ b/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart
@@ -5,7 +5,8 @@
 import 'package:test/bootstrap/browser.dart';
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
-import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
+
+import '../../../common/test_initialization.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
@@ -24,7 +25,7 @@
       ')';
 
   setUpAll(() async {
-    await ui_web.bootstrapEngine();
+    await bootstrapAndRunApp();
   });
 
   group('Shader Declarations', () {
diff --git a/lib/web_ui/test/engine/surface/surface_test.dart b/lib/web_ui/test/engine/surface/surface_test.dart
index f5e9be6..adaa212 100644
--- a/lib/web_ui/test/engine/surface/surface_test.dart
+++ b/lib/web_ui/test/engine/surface/surface_test.dart
@@ -6,7 +6,8 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart';
-import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
+
+import '../../common/test_initialization.dart';
 
 void main() {
   internalBootstrapBrowserTest(() => testMain);
@@ -15,7 +16,7 @@
 void testMain() {
   group('Surface', () {
     setUpAll(() async {
-      await ui_web.bootstrapEngine();
+      await bootstrapAndRunApp();
     });
 
     setUp(() {
diff --git a/lib/web_ui/test/engine/window_test.dart b/lib/web_ui/test/engine/window_test.dart
index 73e823b..3c8847c 100644
--- a/lib/web_ui/test/engine/window_test.dart
+++ b/lib/web_ui/test/engine/window_test.dart
@@ -11,7 +11,8 @@
 import 'package:test/test.dart';
 import 'package:ui/src/engine.dart';
 import 'package:ui/ui.dart' as ui;
-import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
+
+import '../common/test_initialization.dart';
 
 const int kPhysicalKeyA = 0x00070004;
 const int kLogicalKeyA = 0x00000000061;
@@ -21,7 +22,7 @@
 }
 
 Future<void> testMain() async {
-  await ui_web.bootstrapEngine();
+  await bootstrapAndRunApp();
 
   late EngineFlutterWindow myWindow;