blob: 9fb9c93c5e084d112e03b2ce7581a48c1bf7dc6b [file] [log] [blame]
// 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.
import 'dart:async';
import 'dart:html' as html;
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;
import 'package:test/test.dart';
import 'package:web_engine_tester/golden_tester.dart';
/// Class that controls some details of how screenshotting is made.
///
/// (For Googlers: Not really related with internal Scuba anymore)
class EngineScubaTester {
/// The size of the browser window used in this scuba test.
final ui.Size viewportSize;
EngineScubaTester(this.viewportSize);
static Future<EngineScubaTester> initialize(
{ui.Size viewportSize = const ui.Size(2400, 1800)}) async {
assert(() {
if (viewportSize.width.ceil() != viewportSize.width ||
viewportSize.height.ceil() != viewportSize.height) {
throw Exception(
'Scuba only supports integer screen sizes, but found: $viewportSize');
}
if (viewportSize.width < 472) {
throw Exception('Scuba does not support screen width smaller than 472');
}
return true;
}());
return EngineScubaTester(viewportSize);
}
ui.Rect get viewportRegion =>
ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height);
Future<void> diffScreenshot(
String fileName, {
ui.Rect? region,
double? maxDiffRatePercent,
bool write = false,
}) async {
await matchGoldenFile(
'$fileName.png',
region: region ?? viewportRegion,
maxDiffRatePercent: maxDiffRatePercent,
write: write,
);
}
/// Prepares the DOM and inserts all the necessary nodes, then invokes scuba's
/// screenshot diffing.
///
/// It also cleans up the DOM after itself.
Future<void> diffCanvasScreenshot(
EngineCanvas canvas,
String fileName, {
ui.Rect? region,
double? maxDiffRatePercent,
bool write = false,
}) async {
// Wrap in <flt-scene> so that our CSS selectors kick in.
final html.Element sceneElement = html.Element.tag('flt-scene');
try {
sceneElement.append(canvas.rootElement);
html.document.body!.append(sceneElement);
String screenshotName = '${fileName}_${canvas.runtimeType}';
if (WebExperiments.instance!.useCanvasText) {
screenshotName += '+canvas_measurement';
}
await diffScreenshot(
screenshotName,
region: region,
maxDiffRatePercent: maxDiffRatePercent,
write: write,
);
} finally {
// The page is reused across tests, so remove the element after taking the
// Scuba screenshot.
sceneElement.remove();
}
}
}
typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);
/// Runs the given test [body] with each type of canvas.
void testEachCanvas(String description, CanvasTest body,
{double? maxDiffRate}) {
const ui.Rect bounds = ui.Rect.fromLTWH(0, 0, 600, 800);
test('$description (bitmap)', () {
try {
TextMeasurementService.initialize(rulerCacheCapacity: 2);
WebExperiments.instance!.useCanvasText = false;
WebExperiments.instance!.useCanvasRichText = false;
return body(BitmapCanvas(bounds, RenderStrategy()));
} finally {
WebExperiments.instance!.useCanvasText = null;
WebExperiments.instance!.useCanvasRichText = null;
TextMeasurementService.clearCache();
}
});
test('$description (bitmap + canvas measurement)', () async {
try {
TextMeasurementService.initialize(rulerCacheCapacity: 2);
WebExperiments.instance!.useCanvasText = true;
WebExperiments.instance!.useCanvasRichText = false;
await body(BitmapCanvas(bounds, RenderStrategy()));
} finally {
WebExperiments.instance!.useCanvasText = null;
WebExperiments.instance!.useCanvasRichText = null;
TextMeasurementService.clearCache();
}
});
test('$description (dom)', () {
try {
TextMeasurementService.initialize(rulerCacheCapacity: 2);
WebExperiments.instance!.useCanvasText = false;
WebExperiments.instance!.useCanvasRichText = false;
return body(DomCanvas(domRenderer.createElement('flt-picture')));
} finally {
WebExperiments.instance!.useCanvasText = null;
WebExperiments.instance!.useCanvasRichText = null;
TextMeasurementService.clearCache();
}
});
}
final ui.TextStyle _defaultTextStyle = ui.TextStyle(
color: const ui.Color(0xFF000000),
fontFamily: 'Roboto',
fontSize: 14,
);
ui.Paragraph paragraph(
String text, {
ui.ParagraphStyle? paragraphStyle,
ui.TextStyle? textStyle,
double maxWidth = double.infinity,
}) {
final ui.ParagraphBuilder builder =
ui.ParagraphBuilder(paragraphStyle ?? ui.ParagraphStyle());
builder.pushStyle(textStyle ?? _defaultTextStyle);
builder.addText(text);
builder.pop();
return builder.build()..layout(ui.ParagraphConstraints(width: maxWidth));
}
/// Configures the test to use bundled Roboto and Ahem fonts to avoid golden
/// screenshot differences due to differences in the preinstalled system fonts.
void setUpStableTestFonts() {
setUp(() async {
await ui.webOnlyInitializePlatform();
ui.webOnlyFontCollection.debugRegisterTestFonts();
await ui.webOnlyFontCollection.ensureFontsLoaded();
});
}