From 112e8b55095cb75c5061d89ba4cb64cc060f102c Mon Sep 17 00:00:00 2001 From: Kostia Sokolovskyi Date: Wed, 22 May 2024 09:25:21 +0200 Subject: [PATCH] Add tests for actions.0.dart API example. (#148678) This PR contributes to https://github.com/flutter/flutter/issues/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` --- dev/bots/check_code_samples.dart | 1 - .../api/lib/widgets/actions/actions.0.dart | 53 ++++--- .../test/widgets/actions/actions.0_test.dart | 140 ++++++++++++++++++ 3 files changed, 174 insertions(+), 20 deletions(-) create mode 100644 examples/api/test/widgets/actions/actions.0_test.dart diff --git a/dev/bots/check_code_samples.dart b/dev/bots/check_code_samples.dart index e31a20a8a1164..263a7ff242460 100644 --- a/dev/bots/check_code_samples.dart +++ b/dev/bots/check_code_samples.dart @@ -426,7 +426,6 @@ final Set _knownMissingTests = { 'examples/api/test/widgets/single_child_scroll_view/single_child_scroll_view.1_test.dart', 'examples/api/test/widgets/single_child_scroll_view/single_child_scroll_view.0_test.dart', 'examples/api/test/widgets/restoration/restoration_mixin.0_test.dart', - 'examples/api/test/widgets/actions/actions.0_test.dart', 'examples/api/test/widgets/actions/action_listener.0_test.dart', 'examples/api/test/widgets/actions/focusable_action_detector.0_test.dart', 'examples/api/test/widgets/color_filter/color_filtered.0_test.dart', diff --git a/examples/api/lib/widgets/actions/actions.0.dart b/examples/api/lib/widgets/actions/actions.0.dart index 9752b07cfa6b3..bef8f6c2d39a6 100644 --- a/examples/api/lib/widgets/actions/actions.0.dart +++ b/examples/api/lib/widgets/actions/actions.0.dart @@ -26,8 +26,8 @@ class ActionsExampleApp extends StatelessWidget { // A simple model class that notifies listeners when it changes. class Model { - ValueNotifier isDirty = ValueNotifier(false); - ValueNotifier data = ValueNotifier(0); + final ValueNotifier isDirty = ValueNotifier(false); + final ValueNotifier data = ValueNotifier(0); int save() { if (isDirty.value) { @@ -41,6 +41,11 @@ class Model { isDirty.value = data.value != newValue; data.value = newValue; } + + void dispose() { + isDirty.dispose(); + data.dispose(); + } } class ModifyIntent extends Intent { @@ -87,7 +92,7 @@ class SaveButton extends StatefulWidget { } class _SaveButtonState extends State { - int savedValue = 0; + int _savedValue = 0; @override Widget build(BuildContext context) { @@ -96,7 +101,7 @@ class _SaveButtonState extends State { builder: (BuildContext context, Widget? child) { return TextButton.icon( icon: const Icon(Icons.save), - label: Text('$savedValue'), + label: Text('$_savedValue'), style: ButtonStyle( foregroundColor: MaterialStatePropertyAll( widget.valueNotifier.value ? Colors.red : Colors.green, @@ -104,7 +109,7 @@ class _SaveButtonState extends State { ), onPressed: () { setState(() { - savedValue = Actions.invoke(context, const SaveIntent())! as int; + _savedValue = Actions.invoke(context, const SaveIntent())! as int; }); }, ); @@ -121,15 +126,21 @@ class ActionsExample extends StatefulWidget { } class _ActionsExampleState extends State { - Model model = Model(); - int count = 0; + final Model _model = Model(); + int _count = 0; + + @override + void dispose() { + _model.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return Actions( actions: >{ - ModifyIntent: ModifyAction(model), - SaveIntent: SaveAction(model), + ModifyIntent: ModifyAction(_model), + SaveIntent: SaveAction(_model), }, child: Builder( builder: (BuildContext context) { @@ -143,26 +154,30 @@ class _ActionsExampleState extends State { IconButton( icon: const Icon(Icons.exposure_plus_1), onPressed: () { - Actions.invoke(context, ModifyIntent(++count)); + Actions.invoke(context, ModifyIntent(++_count)); }, ), ListenableBuilder( - listenable: model.data, - builder: (BuildContext context, Widget? child) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Text('${model.data.value}', style: Theme.of(context).textTheme.headlineMedium), - ); - }), + listenable: _model.data, + builder: (BuildContext context, Widget? child) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Value: ${_model.data.value}', + style: Theme.of(context).textTheme.headlineMedium, + ), + ); + }, + ), IconButton( icon: const Icon(Icons.exposure_minus_1), onPressed: () { - Actions.invoke(context, ModifyIntent(--count)); + Actions.invoke(context, ModifyIntent(--_count)); }, ), ], ), - SaveButton(model.isDirty), + SaveButton(_model.isDirty), const Spacer(), ], ); diff --git a/examples/api/test/widgets/actions/actions.0_test.dart b/examples/api/test/widgets/actions/actions.0_test.dart new file mode 100644 index 0000000000000..e2e5f03ec51e1 --- /dev/null +++ b/examples/api/test/widgets/actions/actions.0_test.dart @@ -0,0 +1,140 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/widgets/actions/actions.0.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + Color? getSaveButtonColor(WidgetTester tester) { + final ButtonStyleButton button = tester.widget( + find.descendant( + of: find.byType(example.SaveButton), + matching: find.byWidgetPredicate( + (Widget widget) => widget is TextButton, + ), + ), + ); + + return button.style?.foregroundColor?.resolve({}); + } + + testWidgets('increments and decrements value', (WidgetTester tester) async { + await tester.pumpWidget( + const example.ActionsExampleApp(), + ); + + int value = 0; + + while (value < 10) { + expect(find.text('Value: $value'), findsOneWidget); + + // Increment the value. + await tester.tap(find.byIcon(Icons.exposure_plus_1)); + await tester.pump(); + + value++; + } + + while (value >= 0) { + expect(find.text('Value: $value'), findsOneWidget); + + // Decrement the value. + await tester.tap(find.byIcon(Icons.exposure_minus_1)); + await tester.pump(); + + value--; + } + }); + + testWidgets('SaveButton indicates dirty status', (WidgetTester tester) async { + await tester.pumpWidget( + const example.ActionsExampleApp(), + ); + + // Verify that initial color is green, as the value is not marked as dirty. + Color? saveButtonColor = getSaveButtonColor(tester); + expect(saveButtonColor, equals(Colors.green)); + + // Decrement the value, which marks it as dirty. + await tester.tap(find.byIcon(Icons.exposure_minus_1)); + await tester.pump(); + expect(find.text('Value: -1'), findsOneWidget); + + // Verify that the color is red, as the value is marked as dirty. + saveButtonColor = getSaveButtonColor(tester); + expect(saveButtonColor, equals(Colors.red)); + + // Increment the value. + await tester.tap(find.byIcon(Icons.exposure_plus_1)); + await tester.pump(); + expect(find.text('Value: 0'), findsOneWidget); + + // Verify that the color is red, as the value is still marked as dirty. + saveButtonColor = getSaveButtonColor(tester); + expect(saveButtonColor, equals(Colors.red)); + }); + + testWidgets('SaveButton tap resets dirty status and adds log', (WidgetTester tester) async { + final List log = []; + + final DebugPrintCallback originalDebugPrint = debugPrint; + debugPrint = (String? message, {int? wrapWidth}) { + log.add(message); + }; + + await tester.pumpWidget( + const example.ActionsExampleApp(), + ); + + // Verify that value is not marked as dirty. + Color? saveButtonColor = getSaveButtonColor(tester); + expect(saveButtonColor, equals(Colors.green)); + expect( + find.descendant( + of: find.byType(example.SaveButton), + matching: find.text('0'), + ), + findsOneWidget, + ); + + // Decrement the value, which marks it as dirty. + await tester.tap(find.byIcon(Icons.exposure_minus_1)); + await tester.pump(); + expect(find.text('Value: -1'), findsOneWidget); + + // Verify that value is marked as dirty. + saveButtonColor = getSaveButtonColor(tester); + expect(saveButtonColor, equals(Colors.red)); + expect( + find.descendant( + of: find.byType(example.SaveButton), + matching: find.text('0'), + ), + findsOneWidget, + ); + + // Tap SaveButton to reset dirty status. + await tester.tap(find.byType(example.SaveButton)); + await tester.pump(); + + // Verify log record. + expect(log.length, equals(1)); + expect(log.last, equals('Saved Data: -1')); + + // Verify that value is no more marked as dirty. + saveButtonColor = getSaveButtonColor(tester); + expect(saveButtonColor, equals(Colors.green)); + expect( + find.descendant( + of: find.byType(example.SaveButton), + matching: find.text('-1'), + ), + findsOneWidget, + ); + + debugPrint = originalDebugPrint; + }); +}