Skip to content

Commit b9256b6

Browse files
authored
Disable toggle suppression until update call completes (#4981)
Closes flutter/flutter#182386.
1 parent 74f83a1 commit b9256b6

3 files changed

Lines changed: 71 additions & 2 deletions

File tree

dashboard/lib/widgets/test_details_popover.dart

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class _TestDetailsPopoverState extends State<TestDetailsPopover> {
3636
);
3737
}
3838

39+
bool _isPendingUpdate = false;
40+
3941
@override
4042
Widget build(BuildContext context) {
4143
final suppressed = suppressedTest;
@@ -61,7 +63,9 @@ class _TestDetailsPopoverState extends State<TestDetailsPopover> {
6163
style: TextStyle(fontStyle: FontStyle.italic),
6264
),
6365
(true, null) => ElevatedButton.icon(
64-
onPressed: () => _showSuppressDialog(context),
66+
onPressed: _isPendingUpdate
67+
? null
68+
: () => _showSuppressDialog(context),
6569
icon: const Icon(Icons.do_not_disturb_on_outlined),
6670
label: const Text('Unblock Tree'),
6771
style: ElevatedButton.styleFrom(
@@ -70,7 +74,9 @@ class _TestDetailsPopoverState extends State<TestDetailsPopover> {
7074
),
7175
),
7276
(true, _) => ElevatedButton.icon(
73-
onPressed: () => _toggleSuppression(false),
77+
onPressed: _isPendingUpdate
78+
? null
79+
: () => _toggleSuppression(false),
7480
icon: const Icon(Icons.check_circle_outline),
7581
label: const Text('Remove Suppression'),
7682
style: ElevatedButton.styleFrom(
@@ -236,12 +242,18 @@ class _TestDetailsPopoverState extends State<TestDetailsPopover> {
236242
String? issueLink,
237243
String? note,
238244
}) async {
245+
setState(() {
246+
_isPendingUpdate = true;
247+
});
239248
final success = await widget.buildState.updateTestSuppression(
240249
testName: widget.qualifiedTask.task,
241250
suppress: suppress,
242251
issueLink: issueLink,
243252
note: note,
244253
);
254+
setState(() {
255+
_isPendingUpdate = false;
256+
});
245257
if (!success) {
246258
widget.showSnackBarCallback(
247259
const SnackBar(content: Text('Failed to update test suppression')),

dashboard/test/utils/fake_build.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ class FakeBuildState extends ChangeNotifier implements BuildState {
122122

123123
bool updateTestSuppressionResult = true;
124124

125+
/// If non-null, [updateTestSuppression] waits until this future completes.
126+
Future<void>? pauseUpdateUntilCompleted;
127+
125128
@override
126129
Future<bool> updateTestSuppression({
127130
required String testName,
@@ -135,6 +138,9 @@ class FakeBuildState extends ChangeNotifier implements BuildState {
135138
issueLink: issueLink,
136139
note: note,
137140
));
141+
if (pauseUpdateUntilCompleted case final shouldPause?) {
142+
await shouldPause;
143+
}
138144
return updateTestSuppressionResult;
139145
}
140146
}

dashboard/test/widgets/test_details_popover_test.dart

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:async';
6+
57
import 'package:cocoon_common/rpc_model.dart';
68
import 'package:cocoon_common/task_status.dart';
79
import 'package:flutter/material.dart';
@@ -212,6 +214,55 @@ void main() {
212214
expect(call.issueLink, 'https://github.com/flutter/flutter/issues/1234');
213215
expect(call.note, 'Flaky test');
214216
});
217+
218+
testWidgets('disables toggle suppression while pending', (
219+
WidgetTester tester,
220+
) async {
221+
final waitUntil = Completer<void>();
222+
buildState.pauseUpdateUntilCompleted = waitUntil.future;
223+
buildState.updateTestSuppressionResult = true;
224+
buildState.suppressedTests = [suppressedTest];
225+
226+
await tester.pumpWidget(
227+
MaterialApp(
228+
home: Scaffold(
229+
body: TestDetailsPopover(
230+
qualifiedTask: task,
231+
buildState: buildState,
232+
showSnackBarCallback: (SnackBar snackBar) {
233+
ScaffoldMessenger.of(
234+
tester.element(find.byType(Scaffold)),
235+
).showSnackBar(snackBar);
236+
},
237+
closeCallback: () {},
238+
),
239+
),
240+
),
241+
);
242+
243+
// Tap Remove Suppression
244+
final findButton = find.text('Remove Suppression');
245+
246+
await tester.tap(findButton);
247+
await tester.pump(); // Start async call
248+
await tester.pump(); // Resolve async call and show snackbar
249+
expect(
250+
find.byWidgetPredicate(
251+
(w) => w is ElevatedButton && w.onPressed == null,
252+
),
253+
findsOneWidget,
254+
);
255+
256+
waitUntil.complete();
257+
await tester.pump();
258+
expect(
259+
find.byWidgetPredicate(
260+
(w) => w is ElevatedButton && w.onPressed != null,
261+
),
262+
findsOneWidget,
263+
);
264+
});
265+
215266
testWidgets('shows error snackbar when update fails', (
216267
WidgetTester tester,
217268
) async {

0 commit comments

Comments
 (0)