Skip to content

Commit 112e8b5

Browse files
authored
Add tests for actions.0.dart API example. (#148678)
This PR contributes to flutter/flutter#130459 ### Description - Updates `examples/api/lib/widgets/actions/actions.0.dart` to meet the latest API examples structure - Adds tests for `examples/api/lib/widgets/actions/actions.0.dart`
1 parent ea7cf54 commit 112e8b5

File tree

3 files changed

+174
-20
lines changed

3 files changed

+174
-20
lines changed

dev/bots/check_code_samples.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,6 @@ final Set<String> _knownMissingTests = <String>{
426426
'examples/api/test/widgets/single_child_scroll_view/single_child_scroll_view.1_test.dart',
427427
'examples/api/test/widgets/single_child_scroll_view/single_child_scroll_view.0_test.dart',
428428
'examples/api/test/widgets/restoration/restoration_mixin.0_test.dart',
429-
'examples/api/test/widgets/actions/actions.0_test.dart',
430429
'examples/api/test/widgets/actions/action_listener.0_test.dart',
431430
'examples/api/test/widgets/actions/focusable_action_detector.0_test.dart',
432431
'examples/api/test/widgets/color_filter/color_filtered.0_test.dart',

examples/api/lib/widgets/actions/actions.0.dart

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class ActionsExampleApp extends StatelessWidget {
2626

2727
// A simple model class that notifies listeners when it changes.
2828
class Model {
29-
ValueNotifier<bool> isDirty = ValueNotifier<bool>(false);
30-
ValueNotifier<int> data = ValueNotifier<int>(0);
29+
final ValueNotifier<bool> isDirty = ValueNotifier<bool>(false);
30+
final ValueNotifier<int> data = ValueNotifier<int>(0);
3131

3232
int save() {
3333
if (isDirty.value) {
@@ -41,6 +41,11 @@ class Model {
4141
isDirty.value = data.value != newValue;
4242
data.value = newValue;
4343
}
44+
45+
void dispose() {
46+
isDirty.dispose();
47+
data.dispose();
48+
}
4449
}
4550

4651
class ModifyIntent extends Intent {
@@ -87,7 +92,7 @@ class SaveButton extends StatefulWidget {
8792
}
8893

8994
class _SaveButtonState extends State<SaveButton> {
90-
int savedValue = 0;
95+
int _savedValue = 0;
9196

9297
@override
9398
Widget build(BuildContext context) {
@@ -96,15 +101,15 @@ class _SaveButtonState extends State<SaveButton> {
96101
builder: (BuildContext context, Widget? child) {
97102
return TextButton.icon(
98103
icon: const Icon(Icons.save),
99-
label: Text('$savedValue'),
104+
label: Text('$_savedValue'),
100105
style: ButtonStyle(
101106
foregroundColor: MaterialStatePropertyAll<Color>(
102107
widget.valueNotifier.value ? Colors.red : Colors.green,
103108
),
104109
),
105110
onPressed: () {
106111
setState(() {
107-
savedValue = Actions.invoke(context, const SaveIntent())! as int;
112+
_savedValue = Actions.invoke(context, const SaveIntent())! as int;
108113
});
109114
},
110115
);
@@ -121,15 +126,21 @@ class ActionsExample extends StatefulWidget {
121126
}
122127

123128
class _ActionsExampleState extends State<ActionsExample> {
124-
Model model = Model();
125-
int count = 0;
129+
final Model _model = Model();
130+
int _count = 0;
131+
132+
@override
133+
void dispose() {
134+
_model.dispose();
135+
super.dispose();
136+
}
126137

127138
@override
128139
Widget build(BuildContext context) {
129140
return Actions(
130141
actions: <Type, Action<Intent>>{
131-
ModifyIntent: ModifyAction(model),
132-
SaveIntent: SaveAction(model),
142+
ModifyIntent: ModifyAction(_model),
143+
SaveIntent: SaveAction(_model),
133144
},
134145
child: Builder(
135146
builder: (BuildContext context) {
@@ -143,26 +154,30 @@ class _ActionsExampleState extends State<ActionsExample> {
143154
IconButton(
144155
icon: const Icon(Icons.exposure_plus_1),
145156
onPressed: () {
146-
Actions.invoke(context, ModifyIntent(++count));
157+
Actions.invoke(context, ModifyIntent(++_count));
147158
},
148159
),
149160
ListenableBuilder(
150-
listenable: model.data,
151-
builder: (BuildContext context, Widget? child) {
152-
return Padding(
153-
padding: const EdgeInsets.all(8.0),
154-
child: Text('${model.data.value}', style: Theme.of(context).textTheme.headlineMedium),
155-
);
156-
}),
161+
listenable: _model.data,
162+
builder: (BuildContext context, Widget? child) {
163+
return Padding(
164+
padding: const EdgeInsets.all(8.0),
165+
child: Text(
166+
'Value: ${_model.data.value}',
167+
style: Theme.of(context).textTheme.headlineMedium,
168+
),
169+
);
170+
},
171+
),
157172
IconButton(
158173
icon: const Icon(Icons.exposure_minus_1),
159174
onPressed: () {
160-
Actions.invoke(context, ModifyIntent(--count));
175+
Actions.invoke(context, ModifyIntent(--_count));
161176
},
162177
),
163178
],
164179
),
165-
SaveButton(model.isDirty),
180+
SaveButton(_model.isDirty),
166181
const Spacer(),
167182
],
168183
);
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/foundation.dart';
6+
import 'package:flutter/material.dart';
7+
import 'package:flutter_api_samples/widgets/actions/actions.0.dart' as example;
8+
import 'package:flutter_test/flutter_test.dart';
9+
10+
void main() {
11+
Color? getSaveButtonColor(WidgetTester tester) {
12+
final ButtonStyleButton button = tester.widget<ButtonStyleButton>(
13+
find.descendant(
14+
of: find.byType(example.SaveButton),
15+
matching: find.byWidgetPredicate(
16+
(Widget widget) => widget is TextButton,
17+
),
18+
),
19+
);
20+
21+
return button.style?.foregroundColor?.resolve(<WidgetState>{});
22+
}
23+
24+
testWidgets('increments and decrements value', (WidgetTester tester) async {
25+
await tester.pumpWidget(
26+
const example.ActionsExampleApp(),
27+
);
28+
29+
int value = 0;
30+
31+
while (value < 10) {
32+
expect(find.text('Value: $value'), findsOneWidget);
33+
34+
// Increment the value.
35+
await tester.tap(find.byIcon(Icons.exposure_plus_1));
36+
await tester.pump();
37+
38+
value++;
39+
}
40+
41+
while (value >= 0) {
42+
expect(find.text('Value: $value'), findsOneWidget);
43+
44+
// Decrement the value.
45+
await tester.tap(find.byIcon(Icons.exposure_minus_1));
46+
await tester.pump();
47+
48+
value--;
49+
}
50+
});
51+
52+
testWidgets('SaveButton indicates dirty status', (WidgetTester tester) async {
53+
await tester.pumpWidget(
54+
const example.ActionsExampleApp(),
55+
);
56+
57+
// Verify that initial color is green, as the value is not marked as dirty.
58+
Color? saveButtonColor = getSaveButtonColor(tester);
59+
expect(saveButtonColor, equals(Colors.green));
60+
61+
// Decrement the value, which marks it as dirty.
62+
await tester.tap(find.byIcon(Icons.exposure_minus_1));
63+
await tester.pump();
64+
expect(find.text('Value: -1'), findsOneWidget);
65+
66+
// Verify that the color is red, as the value is marked as dirty.
67+
saveButtonColor = getSaveButtonColor(tester);
68+
expect(saveButtonColor, equals(Colors.red));
69+
70+
// Increment the value.
71+
await tester.tap(find.byIcon(Icons.exposure_plus_1));
72+
await tester.pump();
73+
expect(find.text('Value: 0'), findsOneWidget);
74+
75+
// Verify that the color is red, as the value is still marked as dirty.
76+
saveButtonColor = getSaveButtonColor(tester);
77+
expect(saveButtonColor, equals(Colors.red));
78+
});
79+
80+
testWidgets('SaveButton tap resets dirty status and adds log', (WidgetTester tester) async {
81+
final List<String?> log = <String?>[];
82+
83+
final DebugPrintCallback originalDebugPrint = debugPrint;
84+
debugPrint = (String? message, {int? wrapWidth}) {
85+
log.add(message);
86+
};
87+
88+
await tester.pumpWidget(
89+
const example.ActionsExampleApp(),
90+
);
91+
92+
// Verify that value is not marked as dirty.
93+
Color? saveButtonColor = getSaveButtonColor(tester);
94+
expect(saveButtonColor, equals(Colors.green));
95+
expect(
96+
find.descendant(
97+
of: find.byType(example.SaveButton),
98+
matching: find.text('0'),
99+
),
100+
findsOneWidget,
101+
);
102+
103+
// Decrement the value, which marks it as dirty.
104+
await tester.tap(find.byIcon(Icons.exposure_minus_1));
105+
await tester.pump();
106+
expect(find.text('Value: -1'), findsOneWidget);
107+
108+
// Verify that value is marked as dirty.
109+
saveButtonColor = getSaveButtonColor(tester);
110+
expect(saveButtonColor, equals(Colors.red));
111+
expect(
112+
find.descendant(
113+
of: find.byType(example.SaveButton),
114+
matching: find.text('0'),
115+
),
116+
findsOneWidget,
117+
);
118+
119+
// Tap SaveButton to reset dirty status.
120+
await tester.tap(find.byType(example.SaveButton));
121+
await tester.pump();
122+
123+
// Verify log record.
124+
expect(log.length, equals(1));
125+
expect(log.last, equals('Saved Data: -1'));
126+
127+
// Verify that value is no more marked as dirty.
128+
saveButtonColor = getSaveButtonColor(tester);
129+
expect(saveButtonColor, equals(Colors.green));
130+
expect(
131+
find.descendant(
132+
of: find.byType(example.SaveButton),
133+
matching: find.text('-1'),
134+
),
135+
findsOneWidget,
136+
);
137+
138+
debugPrint = originalDebugPrint;
139+
});
140+
}

0 commit comments

Comments
 (0)