Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions lib/web_ui/dev/browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,50 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math' as math;

import 'package:image/image.dart';
import 'package:pedantic/pedantic.dart';
import 'package:stack_trace/stack_trace.dart';
import 'package:test_api/src/backend/runtime.dart';
import 'package:typed_data/typed_buffers.dart';

/// Provides the environment for a specific web browser.
abstract class BrowserEnvironment {
/// The [Runtime] used by `package:test` to identify this browser type.
Runtime get packageTestRuntime;

/// The name of the configuration YAML file used to configure `package:test`.
///
/// The configuration file is expected to be a direct child of the `web_ui`
/// directory.
String get packageTestConfigurationYamlFile;

/// Prepares the OS environment to run tests for this browser.
///
/// This may include things like staring web drivers, iOS Simulators, and/or
/// Android emulators.
///
/// Typically the browser environment is prepared once and supports multiple
/// browser instances.
Future<void> prepareEnvironment();

/// Launches a browser instance.
///
/// The browser will be directed to open the provided [url].
///
/// If [debug] is true and the browser supports debugging, launches the
/// browser in debug mode by pausing test execution after the code is loaded
/// but before calling the `main()` function of the test, giving the
/// developer a chance to set breakpoints.
Browser launchBrowserInstance(Uri url, {bool debug = false});

/// Returns the screenshot manager used by this browser.
///
/// If the browser does not support screenshots, returns null.
ScreenshotManager? getScreenshotManager();
}

/// An interface for running browser instances.
///
/// This is intentionally coarse-grained: browsers are controlled primary from
Expand Down Expand Up @@ -147,3 +186,18 @@ abstract class Browser {
return onExit.catchError((dynamic _) {});
}
}

/// Interface for capturing screenshots from a browser.
abstract class ScreenshotManager {
/// Capture a screenshot.
///
/// Please read the details for the implementing classes.
Future<Image> capture(math.Rectangle region);

/// Suffix to be added to the end of the filename.
///
/// Example file names:
/// - Chrome, no-suffix: backdrop_filter_clip_moved.actual.png
/// - iOS Safari: backdrop_filter_clip_moved.iOS_Safari.actual.png
String get filenameSuffix;
}
92 changes: 91 additions & 1 deletion lib/web_ui/dev/chrome.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,44 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math' as math;

import 'package:image/image.dart';
import 'package:pedantic/pedantic.dart';
import 'package:test_api/src/backend/runtime.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'
as wip;

import 'browser.dart';
import 'chrome_installer.dart';
import 'common.dart';
import 'environment.dart';

/// A class for running an instance of Chrome.
/// Provides an environment for desktop Chrome.
class ChromeEnvironment implements BrowserEnvironment {
@override
Browser launchBrowserInstance(Uri url, {bool debug = false}) {
return Chrome(url, debug: debug);
}

@override
Runtime get packageTestRuntime => Runtime.chrome;

@override
Future<void> prepareEnvironment() async {
// Chrome doesn't need any special prep.
}

@override
ScreenshotManager? getScreenshotManager() {
return ChromeScreenshotManager();
}

@override
String get packageTestConfigurationYamlFile => 'dart_test_chrome.yaml';
}

/// Runs desktop Chrome.
///
/// Most of the communication with the browser is expected to happen via HTTP,
/// so this exposes a bare-bones API. The browser starts as soon as the class is
Expand Down Expand Up @@ -174,3 +203,64 @@ Future<Uri> getRemoteDebuggerUrl(Uri base) async {
return base;
}
}

/// [ScreenshotManager] implementation for Chrome.
///
/// This manager can be used for both macOS and Linux.
// TODO: https://github.com/flutter/flutter/issues/65673
class ChromeScreenshotManager extends ScreenshotManager {
String get filenameSuffix => '';

/// Capture a screenshot of the web content.
///
/// Uses Webkit Inspection Protocol server's `captureScreenshot` API.
///
/// [region] is used to decide which part of the web content will be used in
/// test image. It includes starting coordinate x,y as well as height and
/// width of the area to capture.
Future<Image> capture(math.Rectangle? region) async {
final wip.ChromeConnection chromeConnection =
wip.ChromeConnection('localhost', kDevtoolsPort);
final wip.ChromeTab? chromeTab = await chromeConnection.getTab(
(wip.ChromeTab chromeTab) => chromeTab.url.contains('localhost'));
if (chromeTab == null) {
throw StateError(
'Failed locate Chrome tab with the test page',
);
}
final wip.WipConnection wipConnection = await chromeTab.connect();

Map<String, dynamic>? captureScreenshotParameters = null;
if (region != null) {
captureScreenshotParameters = <String, dynamic>{
'format': 'png',
'clip': <String, dynamic>{
'x': region.left,
'y': region.top,
'width': region.width,
'height': region.height,
'scale':
// This is NOT the DPI of the page, instead it's the "zoom level".
1,
},
};
}

// Setting hardware-independent screen parameters:
// https://chromedevtools.github.io/devtools-protocol/tot/Emulation
await wipConnection
.sendCommand('Emulation.setDeviceMetricsOverride', <String, dynamic>{
'width': kMaxScreenshotWidth,
'height': kMaxScreenshotHeight,
'deviceScaleFactor': 1,
'mobile': false,
});
final wip.WipResponse response = await wipConnection.sendCommand(
'Page.captureScreenshot', captureScreenshotParameters);

final Image screenshot =
decodePng(base64.decode(response.result!['data'] as String))!;

return screenshot;
}
}
26 changes: 25 additions & 1 deletion lib/web_ui/dev/edge.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,35 @@
import 'dart:async';
import 'dart:io';

import 'package:test_api/src/backend/runtime.dart';

import 'browser.dart';
import 'common.dart';
import 'edge_installation.dart';

/// A class for running an instance of Edge.
/// Provides an environment for the desktop Microsoft Edge (Chromium-based).
class EdgeEnvironment implements BrowserEnvironment {
@override
Browser launchBrowserInstance(Uri url, {bool debug = false}) {
return Edge(url, debug: debug);
}

@override
Runtime get packageTestRuntime => Runtime.internetExplorer;

@override
Future<void> prepareEnvironment() async {
// Edge doesn't need any special prep.
}

@override
ScreenshotManager? getScreenshotManager() => null;

@override
String get packageTestConfigurationYamlFile => 'dart_test_edge.yaml';
}

/// Runs desktop Edge.
///
/// Most of the communication with the browser is expected to happen via HTTP,
/// so this exposes a bare-bones API. The browser starts as soon as the class is
Expand Down
25 changes: 24 additions & 1 deletion lib/web_ui/dev/firefox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,37 @@ import 'dart:io';
import 'package:pedantic/pedantic.dart';

import 'package:path/path.dart' as path;
import 'package:test_api/src/backend/runtime.dart';
import 'package:test_core/src/util/io.dart';

import 'browser.dart';
import 'common.dart';
import 'environment.dart';
import 'firefox_installer.dart';

/// A class for running an instance of Firefox.
/// Provides an environment for the desktop Firefox.
class FirefoxEnvironment implements BrowserEnvironment {
@override
Browser launchBrowserInstance(Uri url, {bool debug = false}) {
return Firefox(url, debug: debug);
}

@override
Runtime get packageTestRuntime => Runtime.firefox;

@override
Future<void> prepareEnvironment() async {
// Firefox doesn't need any special prep.
}

@override
String get packageTestConfigurationYamlFile => 'dart_test_firefox.yaml';

@override
ScreenshotManager? getScreenshotManager() => null;
}

/// Runs desktop Firefox.
///
/// Most of the communication with the browser is expected to happen via HTTP,
/// so this exposes a bare-bones API. The browser starts as soon as the class is
Expand Down
74 changes: 0 additions & 74 deletions lib/web_ui/dev/safari.dart

This file was deleted.

Loading