From 85a78410f43def32bc6dea34603562f017fb30e3 Mon Sep 17 00:00:00 2001 From: Dara Adedeji Date: Tue, 27 Oct 2026 21:07:26 -0400 Subject: [PATCH 1/3] Look for the cancelled-screenshots toast as it shows "only Stop ends a capture" failed on and off on Windows: the toast is shown while the test waits in real time for the dialog to close, so its 3-second timer runs on real time, and a slow run let it close before the check. The test now notes the toast while it waits, as the user would see it. --- test/page_screenshots_test.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/page_screenshots_test.dart b/test/page_screenshots_test.dart index 4a2613b2..0d60f0b1 100644 --- a/test/page_screenshots_test.dart +++ b/test/page_screenshots_test.dart @@ -712,7 +712,11 @@ void main() { await tester.pump(); // It stays up, saying so, until the capture has stopped. expect(find.text('Stopping…'), findsOneWidget); - // The clock moves too, so the dialog's closing animation plays out. + // The clock moves too, so the dialog's closing animation plays out. The + // toast is shown while real time passes here, so its timer runs on real + // time: it is looked for as it shows, not after a slow machine has let + // it close. + var toldCancelled = false; for (var i = 0; i < 400 && find.text('Saving screenshots').evaluate().isNotEmpty; i++) { @@ -720,6 +724,8 @@ void main() { () => Future.delayed(const Duration(milliseconds: 16)), ); await tester.pump(const Duration(milliseconds: 16)); + toldCancelled |= + find.text('Screenshots cancelled.').evaluate().isNotEmpty; } for (var i = 0; i < 10; i++) { await tester.pump(const Duration(milliseconds: 100)); @@ -729,7 +735,7 @@ void main() { expect(find.byType(EditorToolbar), findsOneWidget); expect(find.text('Library'), findsNothing); expect(folder.listSync(), isEmpty); - expect(find.text('Screenshots cancelled.'), findsOneWidget); + expect(toldCancelled, isTrue); ignoreTestFontOverflow(tester); // Let the toast leave. await tester.pump(const Duration(seconds: 10)); From a0e69886f08e388c689a3080233d95210a34c8ce Mon Sep 17 00:00:00 2001 From: Dara Adedeji Date: Tue, 27 Oct 2026 21:23:15 -0400 Subject: [PATCH 2/3] Take the cancelled-screenshots toast away before the test ends Its timer runs on real time, so on CI it fired after the test and removed the toast from a torn-down tree (AnimationController disposed twice). --- test/page_screenshots_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/page_screenshots_test.dart b/test/page_screenshots_test.dart index 0d60f0b1..1f9cf848 100644 --- a/test/page_screenshots_test.dart +++ b/test/page_screenshots_test.dart @@ -737,8 +737,10 @@ void main() { expect(folder.listSync(), isEmpty); expect(toldCancelled, isTrue); ignoreTestFontOverflow(tester); - // Let the toast leave. - await tester.pump(const Duration(seconds: 10)); + // Take the toast away now: its timer runs on real time and would close + // it after the test, on a torn-down tree. + toastification.dismissAll(delayForAnimation: false); + await tester.pump(const Duration(seconds: 4)); }); testWidgets('a finished capture closes only its own dialog', (tester) async { From d30a0409ed34dc40c0d960b34654d2771e6db4e5 Mon Sep 17 00:00:00 2001 From: Dara Adedeji Date: Tue, 27 Oct 2026 21:39:27 -0400 Subject: [PATCH 3/3] Take the toast away as soon as the test sees it Dismissing it at the end was too late: its closing timer runs on real time and could already have started removing it, which finished after the test on a torn-down tree. Taken away when it shows, its removal runs on the test's clock and is done before the test ends. --- test/page_screenshots_test.dart | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/page_screenshots_test.dart b/test/page_screenshots_test.dart index 1f9cf848..985f282e 100644 --- a/test/page_screenshots_test.dart +++ b/test/page_screenshots_test.dart @@ -713,9 +713,10 @@ void main() { // It stays up, saying so, until the capture has stopped. expect(find.text('Stopping…'), findsOneWidget); // The clock moves too, so the dialog's closing animation plays out. The - // toast is shown while real time passes here, so its timer runs on real - // time: it is looked for as it shows, not after a slow machine has let - // it close. + // toast is shown while real time passes here, so its closing timer runs + // on real time: it is looked for as it shows, and taken away at once, + // so it neither closes before the check on a slow machine nor closes + // itself after the test, on a torn-down tree. var toldCancelled = false; for (var i = 0; i < 400 && find.text('Saving screenshots').evaluate().isNotEmpty; @@ -724,8 +725,11 @@ void main() { () => Future.delayed(const Duration(milliseconds: 16)), ); await tester.pump(const Duration(milliseconds: 16)); - toldCancelled |= - find.text('Screenshots cancelled.').evaluate().isNotEmpty; + if (!toldCancelled && + find.text('Screenshots cancelled.').evaluate().isNotEmpty) { + toldCancelled = true; + toastification.dismissAll(delayForAnimation: false); + } } for (var i = 0; i < 10; i++) { await tester.pump(const Duration(milliseconds: 100)); @@ -737,9 +741,7 @@ void main() { expect(folder.listSync(), isEmpty); expect(toldCancelled, isTrue); ignoreTestFontOverflow(tester); - // Take the toast away now: its timer runs on real time and would close - // it after the test, on a torn-down tree. - toastification.dismissAll(delayForAnimation: false); + // Let the toast's removal play out. await tester.pump(const Duration(seconds: 4)); });