Skip to content

Commit 084995e

Browse files
WIP: Change theme class name to config to better represent its role
1 parent 1d83588 commit 084995e

34 files changed

+305
-79
lines changed

doc/marketing_goldens/flutter_test_config.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'package:super_text_layout/super_text_layout.dart';
66

77
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
88
// Adjust the theme that's applied to all golden tests in this suite.
9-
GoldenSceneTheme.push(GoldenSceneTheme.standard.copyWith(
9+
GoldenTestConfig.push(GoldenTestConfig.standard.copyWith(
1010
directory: Directory("."),
1111
));
1212

lib/flutter_test_goldens.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ export 'src/scenes/layouts/magazine_layout.dart';
1717
export 'src/scenes/layouts/row_and_column_layout.dart';
1818
export 'src/scenes/scene_layout.dart';
1919
export 'src/scenes/single_shot.dart';
20+
export 'src/test_config.dart';
2021
export 'src/logging.dart';
2122
export 'src/test_runners.dart';

lib/src/scenes/gallery.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'dart:ui' as ui;
55
import 'package:flutter/foundation.dart';
66
import 'package:flutter/material.dart' hide Image;
77
import 'package:flutter_test/flutter_test.dart';
8+
import 'package:flutter_test_goldens/src/test_config.dart';
89
import 'package:flutter_test_goldens/src/flutter/flutter_camera.dart';
910
import 'package:flutter_test_goldens/src/flutter/flutter_test_extensions.dart';
1011
import 'package:flutter_test_goldens/src/goldens/golden_collections.dart';
@@ -40,7 +41,7 @@ class Gallery {
4041
_itemBoundsFinder = itemBoundsFinder,
4142
_layout = layout,
4243
_itemSetup = itemSetup {
43-
_directory = directory ?? GoldenSceneTheme.current.directory;
44+
_directory = directory ?? GoldenTestConfig.current.directory;
4445
}
4546

4647
/// A scaffold built around each item widget tree in this scene when new screenshots are

lib/src/scenes/golden_files.dart

Lines changed: 0 additions & 7 deletions
This file was deleted.

lib/src/scenes/golden_scene.dart

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'dart:async';
2-
import 'dart:io';
32

43
import 'package:flutter/foundation.dart';
54
import 'package:flutter/material.dart' show Colors, MaterialApp, Scaffold, ThemeData;
@@ -11,19 +10,9 @@ import 'package:flutter_test_goldens/src/goldens/golden_collections.dart';
1110
import 'package:flutter_test_goldens/src/goldens/golden_comparisons.dart';
1211
import 'package:flutter_test_goldens/src/goldens/golden_rendering.dart';
1312
import 'package:flutter_test_goldens/src/goldens/golden_scenes.dart';
14-
import 'package:flutter_test_goldens/src/scenes/golden_files.dart';
1513
import 'package:golden_bricks/golden_bricks.dart';
1614

17-
/// A theme, which is applied to various [GoldenScene]s.
18-
///
19-
/// The purpose of [GoldenSceneTheme] is to make it easy to configure similar visual styles
20-
/// for all [GoldenScene]s in a project, file, group, or within a test.
21-
///
22-
/// A [GoldenSceneTheme] captures various details that are visually common among
23-
/// [GoldenScene]s. For example, a theme includes an [itemScaffold] and [itemDecorator] that are
24-
/// built around every golden in a scene. It includes a [background] that renders behind the
25-
/// golden images. For logistics, it includes a relative [directory] path, which says where
26-
/// to store [GoldenScene]s in relation to each golden test file.
15+
/// A theme, which contains visual aspects that are common to most types of Golden Scenes.
2716
class GoldenSceneTheme {
2817
/// The [GoldenSceneTheme] that should be used for the currently executing test.
2918
///
@@ -50,16 +39,16 @@ class GoldenSceneTheme {
5039
addTearDown(() => GoldenSceneTheme.pop());
5140
}
5241

53-
/// Pushes the given [theme] on to the global theme stack, which will make it
42+
/// Pushes the given [theme] on to the global config stack, which will make it
5443
/// the global theme until there's a call to [pop].
5544
///
5645
/// Pushing and popping themes is useful within group and test setups and teardowns
5746
/// to configure a [GoldenSceneTheme] for that group or test.
5847
static void push(GoldenSceneTheme theme) => _themeStack.add(theme);
5948

60-
/// Removes to the top theme on the global stack, which was added with [push].
49+
/// Removes to the top config on the global stack, which was added with [push].
6150
///
62-
/// If there is no corresponding theme that was added by an earlier [push], then
51+
/// If there is no corresponding config that was added by an earlier [push], then
6352
/// this method does nothing.
6453
static void pop() {
6554
if (_themeStack.length > 1) {
@@ -69,7 +58,6 @@ class GoldenSceneTheme {
6958

7059
/// The default [GoldenSceneTheme] for all tests.
7160
static final standard = GoldenSceneTheme(
72-
directory: defaultGoldenDirectory,
7361
background: defaultGoldenSceneBackground,
7462
defaultTextStyle: TextStyle(
7563
color: Colors.black,
@@ -84,7 +72,6 @@ class GoldenSceneTheme {
8472
/// This theme isn't used anywhere by default, but it's a convenient theme if
8573
/// you want a dark theme and you don't care about all the specifics.
8674
static final standardDark = GoldenSceneTheme(
87-
directory: defaultGoldenDirectory,
8875
background: defaultDarkGoldenSceneBackground,
8976
defaultTextStyle: TextStyle(
9077
color: Colors.white,
@@ -96,20 +83,12 @@ class GoldenSceneTheme {
9683
);
9784

9885
const GoldenSceneTheme({
99-
required this.directory,
10086
required this.background,
10187
required this.defaultTextStyle,
10288
required this.itemScaffold,
10389
required this.itemDecorator,
10490
});
10591

106-
/// The relative path from a running test to where that test's goldens are
107-
/// stored.
108-
///
109-
/// The [standard] directory is `Directory("./goldens/")`. To store goldens in the same
110-
/// directory as the running tests, use `Directory(".")`.
111-
final Directory directory;
112-
11392
/// The background that's painted full-bleed across the scene, behind the goldens.
11493
///
11594
/// The [standard] background is a color.
@@ -133,14 +112,12 @@ class GoldenSceneTheme {
133112
final GoldenSceneItemDecorator itemDecorator;
134113

135114
GoldenSceneTheme copyWith({
136-
Directory? directory,
137115
GoldenSceneBackground? background,
138116
TextStyle? defaultTextStyle,
139117
GoldenSceneItemScaffold? itemScaffold,
140118
GoldenSceneItemDecorator? itemDecorator,
141119
}) {
142120
return GoldenSceneTheme(
143-
directory: directory ?? this.directory,
144121
background: background ?? this.background,
145122
defaultTextStyle: defaultTextStyle ?? this.defaultTextStyle,
146123
itemScaffold: itemScaffold ?? this.itemScaffold,

lib/src/scenes/layouts/animation_timeline_layout.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:math';
22

33
import 'package:flutter/material.dart';
44
import 'package:flutter_test/flutter_test.dart';
5+
import 'package:flutter_test_goldens/src/test_config.dart';
56
import 'package:flutter_test_goldens/src/flutter/flutter_pixel_alignment.dart';
67
import 'package:flutter_test_goldens/src/fonts/fonts.dart';
78
import 'package:flutter_test_goldens/src/goldens/golden_collections.dart';
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import 'package:flutter/widgets.dart';
2+
3+
class GoldenImageShowcase extends SlottedMultiChildRenderObjectWidget {
4+
static const _slotGolden = "golden";
5+
static const _slotLabel = "label";
6+
7+
const GoldenImageShowcase({
8+
super.key,
9+
required this.golden,
10+
required this.label,
11+
this.description,
12+
});
13+
14+
final Widget golden;
15+
final Widget label;
16+
17+
final String? description;
18+
19+
@override
20+
Iterable get slots => [_slotGolden, _slotLabel];
21+
22+
@override
23+
Widget? childForSlot(slot) {
24+
switch (slot) {
25+
case _slotGolden:
26+
return golden;
27+
case _slotLabel:
28+
return label;
29+
default:
30+
return null;
31+
}
32+
}
33+
34+
@override
35+
RenderGoldenShowcase createRenderObject(BuildContext context) {
36+
return RenderGoldenShowcase()..description = description;
37+
}
38+
39+
@override
40+
void updateRenderObject(BuildContext context, RenderGoldenShowcase renderObject) {
41+
renderObject.description = description;
42+
}
43+
}
44+
45+
class RenderGoldenShowcase extends RenderBox with SlottedContainerRenderObjectMixin {
46+
String? description;
47+
48+
@override
49+
void performLayout() {
50+
print("------- performLayout -------");
51+
print("Laying out $description - constraints: $constraints");
52+
final renderGolden = childForSlot(GoldenImageShowcase._slotGolden)! as RenderBox;
53+
renderGolden.layout(constraints.copyWith(minHeight: 0), parentUsesSize: true);
54+
print(
55+
" - golden, intrinsic width: ${renderGolden.computeMinIntrinsicWidth(double.infinity)}, wants to be: ${renderGolden.size} ($renderGolden)");
56+
57+
final renderLabel = childForSlot(GoldenImageShowcase._slotLabel)! as RenderBox;
58+
renderLabel.layout(constraints.copyWith(minHeight: 0), parentUsesSize: true);
59+
print(
60+
" - label intrinsic width: ${renderLabel.computeMaxIntrinsicWidth(double.infinity)}, wants to be: ${renderLabel.size} ($renderLabel)");
61+
62+
late final double width;
63+
if (renderGolden.size.width >= renderLabel.size.width) {
64+
print("Golden is setting the reference width");
65+
width = renderGolden.size.width;
66+
print(" - golden width: $width");
67+
68+
final goldenHeight = renderGolden.computeMinIntrinsicHeight(width);
69+
print(" - golden min intrinsic height: $goldenHeight");
70+
renderGolden.layout(BoxConstraints.tightFor(width: width, height: goldenHeight), parentUsesSize: true);
71+
print(" - final golden size: ${renderGolden.size}");
72+
73+
print(" - label min intrinsic height: ${renderLabel.computeMinIntrinsicHeight(width)}");
74+
renderLabel.layout(BoxConstraints.tightFor(width: width), parentUsesSize: true);
75+
print(" - final label size: ${renderLabel.size}");
76+
} else {
77+
print("Label is setting the reference width");
78+
width = renderLabel.size.width;
79+
print(" - label width: $width");
80+
print(" - label min intrinsic height: ${renderLabel.computeMinIntrinsicHeight(width)}");
81+
82+
final goldenHeight = renderGolden.computeMinIntrinsicHeight(width);
83+
print(" - golden min intrinsic height: $goldenHeight");
84+
renderGolden.layout(BoxConstraints.tightFor(width: width, height: goldenHeight), parentUsesSize: true);
85+
print(" - final golden size: ${renderGolden.size}");
86+
}
87+
88+
final desiredSize = Size(width, renderGolden.size.height + renderLabel.size.height);
89+
print("Description: $description, desired size: $desiredSize, max allowable size: ${constraints.biggest}");
90+
91+
size = Size(width, renderGolden.size.height + renderLabel.size.height);
92+
}
93+
94+
@override
95+
void paint(PaintingContext context, Offset offset) {
96+
final renderGolden = childForSlot(GoldenImageShowcase._slotGolden)! as RenderBox;
97+
final renderLabel = childForSlot(GoldenImageShowcase._slotLabel)! as RenderBox;
98+
99+
context.paintChild(renderGolden, offset + Offset.zero);
100+
context.paintChild(renderLabel, offset + Offset(0, renderGolden.size.height.ceilToDouble()));
101+
}
102+
}

lib/src/scenes/timeline.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'dart:ui' as ui;
55

66
import 'package:flutter/material.dart' hide Image;
77
import 'package:flutter_test/flutter_test.dart';
8+
import 'package:flutter_test_goldens/src/test_config.dart';
89
import 'package:flutter_test_goldens/src/flutter/flutter_camera.dart';
910
import 'package:flutter_test_goldens/src/flutter/flutter_test_extensions.dart';
1011
import 'package:flutter_test_goldens/src/goldens/golden_collections.dart';
@@ -548,7 +549,7 @@ class Timeline {
548549

549550
String get _goldenDirectory => "$_testFileDirectory$_relativeGoldenDirectory$separator";
550551

551-
String get _relativeGoldenDirectory => _directory?.path ?? GoldenSceneTheme.current.directory.path;
552+
String get _relativeGoldenDirectory => _directory?.path ?? GoldenTestConfig.current.directory.path;
552553

553554
/// Calculates and returns a complete file path to the golden file specified by
554555
/// this gallery, which consists of the current test file directory + an optional

lib/src/test_config.dart

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter_test/flutter_test.dart';
4+
import 'package:flutter_test_goldens/src/scenes/golden_scene.dart';
5+
6+
/// A configuration for golden tests.
7+
///
8+
/// The purpose of [GoldenTestConfig] is to make it easy to configure multiple golden
9+
/// tests in a project, file, group, or within a test.
10+
///
11+
/// A [GoldenTestConfig] captures various details that are common among [GoldenScene]s. For
12+
/// example, a relative [directory] path, which says where to store [GoldenScene]s in
13+
/// relation to each golden test file.
14+
class GoldenTestConfig {
15+
/// The [GoldenTestConfig] that should be used for the currently executing test.
16+
///
17+
/// By default, this value is [standard]. The theme can be customized
18+
/// by [push]ing a new theme on the stack. Any theme that is [push]ed on the stack
19+
/// will be reported as the [current] theme until it is [pop]ed.
20+
static GoldenTestConfig get current => _themeStack.last;
21+
22+
static final _themeStack = [standard];
23+
24+
/// Configures a [setUp] that makes the given [config] the [current] global
25+
/// [GoldenTestConfig] within the current test group, and configures a
26+
/// [tearDown] that returns the previous global theme when the group exits.
27+
static void useForGroup(GoldenTestConfig config) {
28+
setUp(() => GoldenTestConfig.push(config));
29+
tearDown(() => GoldenTestConfig.pop());
30+
}
31+
32+
/// Configures a [setUp] that makes the given [config] the [current] global
33+
/// [GoldenTestConfig] within the current test, and configures a [tearDown]
34+
/// that returns the previous global theme when the group exits.
35+
static void useForTest(GoldenTestConfig config) {
36+
GoldenTestConfig.push(config);
37+
addTearDown(() => GoldenTestConfig.pop());
38+
}
39+
40+
/// Pushes the given [config] on to the global config stack, which will make it
41+
/// the global theme until there's a call to [pop].
42+
///
43+
/// Pushing and popping themes is useful within group and test setups and teardowns
44+
/// to configure a [GoldenTestConfig] for that group or test.
45+
static void push(GoldenTestConfig config) => _themeStack.add(config);
46+
47+
/// Removes to the top config on the global stack, which was added with [push].
48+
///
49+
/// If there is no corresponding config that was added by an earlier [push], then
50+
/// this method does nothing.
51+
static void pop() {
52+
if (_themeStack.length > 1) {
53+
_themeStack.removeLast();
54+
}
55+
}
56+
57+
/// The default [GoldenTestConfig] for all tests.
58+
static final standard = GoldenTestConfig(
59+
directory: defaultGoldenDirectory,
60+
);
61+
62+
/// The default dark [GoldenTestConfig].
63+
///
64+
/// This theme isn't used anywhere by default, but it's a convenient theme if
65+
/// you want a dark theme and you don't care about all the specifics.
66+
static final standardDark = GoldenTestConfig(
67+
directory: defaultGoldenDirectory,
68+
);
69+
70+
const GoldenTestConfig({
71+
required this.directory,
72+
});
73+
74+
/// The relative path from a running test to where that test's goldens are stored.
75+
///
76+
/// The [standard] directory is `Directory("./goldens/")`. To store goldens in the same
77+
/// directory as the running tests, use `Directory(".")`.
78+
final Directory directory;
79+
80+
GoldenTestConfig copyWith({
81+
Directory? directory,
82+
GoldenSceneTheme? theme,
83+
}) {
84+
return GoldenTestConfig(
85+
directory: directory ?? this.directory,
86+
);
87+
}
88+
}
89+
90+
/// The standard path to where goldens are saved.
91+
///
92+
/// The default path is a `/goldens/` directory, which sits in the same parent directory as
93+
/// the test file that's running the test.
94+
final defaultGoldenDirectory = Directory("./goldens/");

test_goldens/flutter_test_config.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import 'package:flutter_test_goldens/flutter_test_goldens.dart';
55

66
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
77
// Adjust the theme that's applied to all golden tests in this suite.
8-
GoldenSceneTheme.push(GoldenSceneTheme.standard.copyWith(
8+
GoldenTestConfig.push(GoldenTestConfig.standard.copyWith(
99
directory: Directory("."),
1010
));
1111

0 commit comments

Comments
 (0)