Skip to content

Commit a579abf

Browse files
Create theming system for Golden Scenes (Resolves #16)
1 parent 1f597c4 commit a579abf

19 files changed

+625
-216
lines changed

doc/website/source/_includes/components/navMain.jinja

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,4 @@
11
<nav class="main">
2-
<ol class="organization-links">
3-
<!-- Hard-coded, specific top-level menu items -->
4-
{% if package.github != null %}
5-
<a href="{{ package.github.url }}" target="_blank" role="button" class="btn"><span class="fa fa-brands fa-github"></span>&nbsp;&nbsp;GitHub</a>
6-
{% endif %}
7-
8-
{% if package.discord != null %}
9-
<a href="{{ package.discord }}" target="_blank" role="button" class="btn"><span class="fa fa-brands fa-discord"></span>&nbsp;&nbsp;Discord</a>
10-
{% endif %}
11-
12-
{% if package.sponsorship != null %}
13-
<a href="{{ package.sponsorship }}" target="_blank" role="button" class="btn"><span class="fa fa-dollar-sign"></span>&nbsp;&nbsp;Sponsor</a>
14-
{% endif %}
15-
</ol>
16-
17-
<div class="organization-nav-divider">&nbsp;</div>
18-
192
{% if navigation != null %}
203
<ol>
214
{% for item in navigation.items %}

doc/website/source/_includes/layouts/docs_page.jinja

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!doctype html>
22

3-
<html lang="en" data-bs-theme="light">
3+
<html lang="en" data-bs-theme="dark">
44
<head>
55
<meta charset="utf-8">
66
<title>{{ title }}</title>
@@ -67,10 +67,6 @@
6767
<a class="nav-link" href="{{ package.sponsorship }}" target="_blank"><span class="fa fa-dollar-sign"></span></a>
6868
</li>
6969
{% endif %}
70-
71-
<li class="nav-item">
72-
<span id="brightness-button" onclick="toggleBrightness()" class="fa fa-moon"></span>
73-
</li>
7470
</ul>
7571
</div>
7672

lib/src/scenes/film_strip.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import 'dart:typed_data';
55
import 'dart:ui' as ui;
66

77
import 'package:flutter/material.dart' hide Image;
8-
import 'package:flutter/material.dart' as m;
98
import 'package:flutter_test/flutter_test.dart';
109
import 'package:flutter_test_goldens/src/flutter/flutter_test_extensions.dart';
1110
import 'package:flutter_test_goldens/src/goldens/golden_camera.dart';
@@ -35,7 +34,7 @@ class FilmStrip {
3534

3635
final String goldenName;
3736
final SceneLayout layout;
38-
final Widget? goldenBackground;
37+
final GoldenSceneBackground? goldenBackground;
3938

4039
_FilmStripSetup? _setup;
4140
final _steps = <Object>[];
@@ -234,7 +233,7 @@ class FilmStrip {
234233
List<GoldenPhoto> photos,
235234
Map<GoldenPhoto, (Uint8List, GlobalKey)> renderablePhotos,
236235
SceneLayout layout, {
237-
Widget? goldenBackground,
236+
GoldenSceneBackground? goldenBackground,
238237
}) async {
239238
// Layout the final strip within an OverflowBox to let it be whatever
240239
// size it wants. Then check the content render object for final dimensions.
@@ -308,7 +307,7 @@ class FilmStrip {
308307
GlobalKey contentKey,
309308
Map<GoldenPhoto, (Uint8List, GlobalKey)> renderablePhotos, {
310309
Key? galleryKey,
311-
Widget? goldenBackground,
310+
GoldenSceneBackground? goldenBackground,
312311
}) {
313312
return GoldenSceneBounds(
314313
child: IntrinsicWidth(

lib/src/scenes/gallery.dart

Lines changed: 50 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'dart:ui' as ui;
66

77
import 'package:flutter/material.dart' hide Image;
88
import 'package:flutter_test/flutter_test.dart';
9-
import 'package:flutter_test_goldens/golden_bricks.dart';
109
import 'package:flutter_test_goldens/src/flutter/flutter_test_extensions.dart';
1110
import 'package:flutter_test_goldens/src/goldens/golden_camera.dart';
1211
import 'package:flutter_test_goldens/src/goldens/golden_collections.dart';
@@ -16,7 +15,6 @@ import 'package:flutter_test_goldens/src/goldens/golden_scenes.dart';
1615
import 'package:flutter_test_goldens/src/goldens/pixel_comparisons.dart';
1716
import 'package:flutter_test_goldens/src/logging.dart';
1817
import 'package:flutter_test_goldens/src/png/png_metadata.dart';
19-
import 'package:flutter_test_goldens/src/scenes/golden_files.dart';
2018
import 'package:flutter_test_goldens/src/scenes/golden_scene.dart';
2119
import 'package:flutter_test_goldens/src/scenes/scene_layout.dart';
2220
import 'package:image/image.dart';
@@ -26,33 +24,30 @@ import 'package:path/path.dart';
2624
/// renders those into a single scene file, or compares them against an existing
2725
/// scene file.
2826
class Gallery {
29-
Gallery(
30-
this._tester, {
27+
Gallery({
3128
Directory? directory,
3229
required String fileName,
3330
required String sceneDescription,
3431
required SceneLayout layout,
35-
GalleryItemScaffold itemScaffold = defaultGalleryItemScaffold,
36-
GalleryItemDecorator? itemDecorator = defaultGalleryItemDecorator,
37-
Widget? goldenBackground,
32+
GoldenSceneItemScaffold? itemScaffold,
33+
GoldenSceneItemDecorator? itemDecorator,
34+
GoldenSceneBackground? goldenBackground,
3835
}) : _fileName = fileName,
3936
_sceneDescription = sceneDescription,
4037
_layout = layout,
4138
_itemScaffold = itemScaffold,
4239
_itemDecorator = itemDecorator,
4340
_goldenBackground = goldenBackground {
44-
_directory = directory ?? defaultGoldenDirectory;
41+
_directory = directory ?? GoldenSceneTheme.current.directory;
4542
}
4643

47-
final WidgetTester _tester;
48-
4944
/// A scaffold built around each item in this scene.
5045
///
51-
/// Defaults to [defaultGalleryItemScaffold].
52-
final GalleryItemScaffold _itemScaffold;
46+
/// Defaults to [defaultGoldenSceneItemScaffold].
47+
final GoldenSceneItemScaffold? _itemScaffold;
5348

5449
/// A decoration applied to each item in this scene.
55-
final GalleryItemDecorator? _itemDecorator;
50+
final GoldenSceneItemDecorator? _itemDecorator;
5651

5752
/// All screenshots within this scene.
5853
final _items = <GalleryItem>[];
@@ -70,7 +65,7 @@ class Gallery {
7065
final SceneLayout _layout;
7166

7267
/// The background behind the items in this scene.
73-
final Widget? _goldenBackground;
68+
final GoldenSceneBackground? _goldenBackground;
7469

7570
/// Adds a screenshot item to the scene, based on a given [widget].
7671
Gallery itemFromWidget({
@@ -147,41 +142,43 @@ class Gallery {
147142

148143
/// Either renders a new golden to a scene file, or compares new screenshots against an existing
149144
/// golden scene file.
150-
Future<void> renderOrCompareGolden() async {
145+
Future<void> renderOrCompareGolden(WidgetTester tester) async {
151146
FtgLog.pipeline.info("Rendering or comparing golden - $_sceneDescription");
152147

153148
// Build each gallery item and screenshot it.
149+
final itemScaffold = _itemScaffold ?? GoldenSceneTheme.current.itemScaffold;
150+
print("Rendering gallery: Given scaffold: $_itemScaffold, chosen scaffold: $itemScaffold");
151+
final itemDecorator = _itemDecorator ?? GoldenSceneTheme.current.itemDecorator;
154152
final camera = GoldenCamera();
153+
155154
for (final item in _items) {
156155
FtgLog.pipeline.info("Building gallery item: ${item.description}, item decorated: $_itemDecorator");
157156

158157
if (item.pumper != null) {
159158
// Defer to the `pumper` to pump the entire widget tree for this gallery item.
160-
await item.pumper!.call(_tester, _itemScaffold, _itemDecorator);
159+
await item.pumper!.call(tester, itemScaffold, item.description, itemDecorator);
161160
} else if (item.builder != null) {
162161
// Pump this gallery item, deferring to a `WidgetBuilder` for the content.
163-
await _tester.pumpWidget(
164-
_itemScaffold(
165-
_tester,
166-
_itemDecorator != null
167-
? _itemDecorator.call(
168-
_tester,
169-
Builder(builder: item.builder!),
170-
)
171-
: Builder(builder: item.builder!),
162+
await tester.pumpWidget(
163+
itemScaffold(
164+
tester,
165+
itemDecorator.call(
166+
tester,
167+
item.description,
168+
Builder(builder: item.builder!),
169+
),
172170
),
173171
);
174172
} else {
175173
// Pump this gallery item, deferring to a `Widget` for the content.
176-
await _tester.pumpWidget(
177-
_itemScaffold(
178-
_tester,
179-
_itemDecorator != null
180-
? _itemDecorator.call(
181-
_tester,
182-
item.child!,
183-
)
184-
: item.child!,
174+
await tester.pumpWidget(
175+
itemScaffold(
176+
tester,
177+
itemDecorator.call(
178+
tester,
179+
item.description,
180+
item.child!,
181+
),
185182
),
186183
);
187184
}
@@ -201,7 +198,7 @@ class Gallery {
201198
final photos = camera.photos;
202199
// TODO: cleanup the modeling of these photos vs renderable photos once things are working
203200
final renderablePhotos = <GoldenPhoto, (Uint8List, GlobalKey)>{};
204-
await _tester.runAsync(() async {
201+
await tester.runAsync(() async {
205202
for (final photo in photos) {
206203
final byteData = await photo.pixels.toByteData(format: ui.ImageByteFormat.png);
207204
renderablePhotos[photo] = (byteData!.buffer.asUint8List(), GlobalKey());
@@ -210,34 +207,35 @@ class Gallery {
210207

211208
// Layout photos in the gallery so we can lookup their final offsets and sizes.
212209
var sceneMetadata = await _layoutPhotos(
210+
tester,
213211
photos,
214212
renderablePhotos,
215213
_layout,
216214
goldenBackground: _goldenBackground,
217215
);
218216

219217
FtgLog.pipeline.finer("Running momentary delay for render flakiness");
220-
await _tester.runAsync(() async {
218+
await tester.runAsync(() async {
221219
// Without this delay, the screenshot loading is spotty. However, with
222220
// this delay, we seem to always get screenshots displayed in the widget tree.
223221
// FIXME: Root cause this render flakiness and see if we can fix it.
224222
await Future.delayed(const Duration(milliseconds: 1));
225223
});
226224

227-
await _tester.pumpAndSettle();
225+
await tester.pumpAndSettle();
228226

229227
if (autoUpdateGoldenFiles) {
230228
// Generate new goldens.
231229
await _updateGoldenScene(
232-
_tester,
230+
tester,
233231
_fileName,
234232
sceneMetadata,
235233
);
236234
} else {
237235
// Compare to existing goldens.
238236
FtgLog.pipeline.finer("Comparing existing goldens...");
239237
await _compareGoldens(
240-
_tester,
238+
tester,
241239
sceneMetadata,
242240
_fileName,
243241
find.byType(GoldenSceneBounds),
@@ -250,10 +248,11 @@ class Gallery {
250248

251249
// TODO: de-dup this with FilmStrip
252250
Future<GoldenSceneMetadata> _layoutPhotos(
251+
WidgetTester tester,
253252
List<GoldenPhoto> photos,
254253
Map<GoldenPhoto, (Uint8List, GlobalKey)> renderablePhotos,
255254
SceneLayout layout, {
256-
Widget? goldenBackground,
255+
GoldenSceneBackground? goldenBackground,
257256
}) async {
258257
// Layout the final strip within an OverflowBox to let it be whatever
259258
// size it wants. Then check the content render object for final dimensions.
@@ -282,13 +281,13 @@ class Gallery {
282281
goldenBackground: goldenBackground,
283282
);
284283

285-
await _tester.pumpWidgetAndAdjustWindow(gallery);
284+
await tester.pumpWidgetAndAdjustWindow(gallery);
286285

287-
await _tester.runAsync(() async {
286+
await tester.runAsync(() async {
288287
for (final entry in renderablePhotos.entries) {
289288
await precacheImage(
290289
MemoryImage(entry.value.$1),
291-
_tester.element(find.byKey(entry.value.$2)),
290+
tester.element(find.byKey(entry.value.$2)),
292291
);
293292
}
294293
});
@@ -313,7 +312,7 @@ class Gallery {
313312
GlobalKey contentKey,
314313
Map<GoldenPhoto, (Uint8List, GlobalKey)> renderablePhotos, {
315314
Key? galleryKey,
316-
Widget? goldenBackground,
315+
GoldenSceneBackground? goldenBackground,
317316
}) {
318317
return GoldenSceneBounds(
319318
child: IntrinsicWidth(
@@ -334,8 +333,8 @@ class Gallery {
334333
String goldenFileName,
335334
GoldenSceneMetadata sceneMetadata,
336335
) async {
337-
FtgLog.pipeline.finer("Doing golden generation - window height: ${_tester.view.physicalSize.height}");
338-
await expectLater(find.byType(GoldenSceneBounds), matchesGoldenFile("$goldenFileName.png"));
336+
FtgLog.pipeline.finer("Doing golden generation - window height: ${tester.view.physicalSize.height}");
337+
await expectLater(find.byType(GoldenSceneBounds), matchesGoldenFile(_goldenFilePath()));
339338

340339
final goldenFile = File(_goldenFilePath());
341340
var pngData = goldenFile.readAsBytesSync();
@@ -489,8 +488,9 @@ class Gallery {
489488
/// {@macro gallery_item_pumper_requirements}
490489
typedef GalleryItemPumper = Future<void> Function(
491490
WidgetTester tester,
492-
GalleryItemScaffold scaffold,
493-
GalleryItemDecorator? decorator,
491+
GoldenSceneItemScaffold scaffold,
492+
String description,
493+
GoldenSceneItemDecorator? decorator,
494494
);
495495

496496
/// Scaffolds a gallery item, such as building a `MaterialApp` with a `Scaffold`.
@@ -503,12 +503,12 @@ typedef GalleryItemPumper = Future<void> Function(
503503
/// Gallery item decorator
504504
/// Gallery item (the content)
505505
/// {@endtemplate}
506-
typedef GalleryItemScaffold = Widget Function(WidgetTester tester, Widget content);
506+
typedef GoldenSceneItemScaffold = Widget Function(WidgetTester tester, Widget content);
507507

508508
/// Decorates a golden screenshot by wrapping the given [content] in a new widget tree.
509509
///
510510
/// {@macro gallery_item_structure}
511-
typedef GalleryItemDecorator = Widget Function(WidgetTester tester, Widget content);
511+
typedef GoldenSceneItemDecorator = Widget Function(WidgetTester tester, String description, Widget content);
512512

513513
/// A single UI screenshot within a gallery of gallery items.
514514
class GalleryItem {
@@ -564,30 +564,3 @@ class GalleryItem {
564564
/// item is created with a [pumper] or a [builder].
565565
final Widget? child;
566566
}
567-
568-
/// The ancestor widget tree for every item in a gallery, unless overridden by
569-
/// the gallery configuration.
570-
Widget defaultGalleryItemScaffold(WidgetTester tester, Widget content) {
571-
return MaterialApp(
572-
home: Scaffold(
573-
body: Builder(builder: (context) {
574-
return DefaultTextStyle(
575-
style: DefaultTextStyle.of(context).style.copyWith(
576-
fontFamily: goldenBricks,
577-
),
578-
child: Center(
579-
child: GoldenImageBounds(child: content),
580-
),
581-
);
582-
}),
583-
),
584-
debugShowCheckedModeBanner: false,
585-
);
586-
}
587-
588-
Widget defaultGalleryItemDecorator(WidgetTester tester, Widget content) {
589-
return Padding(
590-
padding: const EdgeInsets.all(24),
591-
child: content,
592-
);
593-
}

0 commit comments

Comments
 (0)