| name | flutter-adaptive-cards-testing |
| description | > Use when this capability is needed. |
Flutter Adaptive Cards Testing Skill
Overview
All library tests live under:
packages/flutter_adaptive_cards_fs/test/
Tests are run from that package directory, not the monorepo root:
cd packages/flutter_adaptive_cards_fs
fvm flutter test
fvm flutter test test/golden_sample_test.dart
fvm flutter test --tags golden
fvm flutter test --update-goldens
Key-First Testing (Mandatory)
To ensure tests are resilient to UI refactoring, always locate widgets via
generateWidgetKey() / generateAdaptiveWidgetKey() rather than hardcoded
ValueKey strings or text/type finders.
import 'package:flutter_adaptive_cards_fs/src/utils/utils.dart';
// Extract the element map from the card body before assertions
final elementMap = map['body'][0] as Map<String, dynamic>;
// [GOOD] Derived from the same map used to build the widget
expect(find.byKey(generateWidgetKey(elementMap)), findsOneWidget);
// [GOOD] Outer StatefulWidget wrapper
expect(find.byKey(generateAdaptiveWidgetKey(elementMap)), findsOneWidget);
// [GOOD] ChoiceSet item (suffix form)
expect(
find.byKey(generateWidgetKey(elementMap, suffix: 'Choice 1')),
findsOneWidget,
);
// [NEVER] Hard-coded string — breaks silently if the id format changes
// find.byKey(const ValueKey('submitButton')) ← do NOT do this
// [AVOID] Brittle and broken by text changes
// find.text('Submit')
// [AVOID] Ambiguous in large cards
// find.byType(ElevatedButton)
[!IMPORTANT]
Never write find.byKey(const ValueKey('someId')) in tests.
If the key format ever changes, string literals silently break whereas
generateWidgetKey() calls continue to match the live implementation.
Core Test Utilities — test/utils/test_utils.dart
Import this in every test file:
import 'utils/test_utils.dart';
getTestWidgetFromPath & getTestWidgetFromMap — Primary Test Helpers
These helpers load an Adaptive Card (from a file or a Map) and return a fully-wrapped MaterialApp.
[!IMPORTANT]
Mandatory Usage: Always use these helpers instead of RawAdaptiveCard.fromMap or AdaptiveCardsCanvas directly. They ensure that:
- ID Injection: Missing IDs are recursively injected into the JSON map.
- Context: Necessary
ProviderScope and InheritedAdaptiveCardHandlers are provided.
- UI Context: The card is wrapped in a
MaterialApp, Scaffold, and RepaintBoundary.
Architecture Note: These helpers automatically wrap the card in:
- MaterialApp & Scaffold: Providing necessary theme and layout context.
- RepaintBoundary: With an optional
key, used to target specific regions for golden images.
- InheritedAdaptiveCardHandlers: Injects mock handlers for
onSubmit, onExecute, onChange, etc., if provided as arguments.
Widget getTestWidgetFromPath({
required String path, // relative to test/samples/
Key? key, // targets the RepaintBoundary for Goldens
// ... handlers
})
Widget getTestWidgetFromMap({
required Map<String, dynamic> map,
required String title,
Key? key,
// ... handlers
})
Widget Key Generation Patterns
All widgets use deterministic ValueKeys generated by two functions from
package:flutter_adaptive_cards_fs/src/utils/utils.dart.
| Widget Type | Generator call | Produced key |
|---|
| Card Wrapper | generateAdaptiveWidgetKey(elementMap) | ValueKey('{id}_adaptive') |
| Input Content | generateWidgetKey(elementMap) | ValueKey('{id}') |
| ChoiceSet Item | generateWidgetKey(elementMap, suffix: 'Choice 1') | ValueKey('{id}_Choice 1') |
| Modal Search | generateWidgetKey(elementMap) | Same id as input field |
Canonical test pattern
import 'package:flutter_adaptive_cards_fs/src/utils/utils.dart';
// 1. Define the card map
final Map<String, dynamic> map = {
'type': 'AdaptiveCard',
'body': [
{'type': 'Input.Text', 'id': 'myField', 'label': 'Name'},
],
};
// 2. Pump the widget
await tester.pumpWidget(getTestWidgetFromMap(map: map, title: 'Test'));
await tester.pumpAndSettle();
// 3. Extract element map — the single source of truth for keys
final fieldMap = map['body'][0] as Map<String, dynamic>;
// 4. Find widgets
expect(find.byKey(generateAdaptiveWidgetKey(fieldMap)), findsOneWidget); // wrapper
expect(find.byKey(generateWidgetKey(fieldMap)), findsOneWidget); // input
// 5. Interact
await tester.enterText(find.byKey(generateWidgetKey(fieldMap)), 'hello');
Reference: See AdaptiveWidget-Key-Generation.md
for the full key contract and automatic ID injection rules.
Golden Image Tests
Canonical Environment (Linux)
[!WARNING]
Golden image pixels are platform-specific. This project organizes golden images into platform-specific subdirectories:
test/gold_files/linux/: Project-wide source of truth (CI generated).
test/gold_files/macos/: Local verification images.
Updating Goldens: Should primarily be done via CI (for Linux results). Use getGoldenPath(filename) to dynamically resolve the path.
Standard Golden Pattern
testWidgets('My Card Golden', (tester) async {
// 1. Fixed viewport
RendererBinding.instance.renderViews.first.configuration =
TestViewConfiguration.fromView(
size: const Size(500, 700),
view: PlatformDispatcher.instance.implicitView!,
);
const key = ValueKey('paint');
// 2. Load and Pump
await tester.pumpWidget(getTestWidgetFromPath(path: 'my_card.json', key: key));
await tester.pumpAndSettle();
// 3. Compare (Note: targets the key, uses dynamic platform path)
await expectLater(
find.byKey(key),
matchesGoldenFile(getGoldenPath('my_card-base.png')),
);
}, tags: ['golden']);
Local Golden Generation for Visual Verifications
You can generate goldens on your local machine for visual verification purposes, but they will not be used for CI testing and they should not be comitted to the repository.
cd packages/flutter_adaptive_cards_fs
flutter test --update-goldens --tags golden
Warning: Golden image pixels are platform-specific. macOS-generated
goldens may not match Linux CI exactly. The project uses dart_test.yaml
to manage this. Check test/analysis_options.yaml for any tag restrictions.
Running Only Non-Golden Tests (Faster Iteration)
The local AI agents should always run the tests with the --exclude-tags golden flag to speed up the test execution and because local execution of golden tests will fail due to the platform aliasing issues.
flutter test --exclude-tags golden
Test Sample Files
Sample JSON cards live in test/samples/.
Always add a new sample JSON when implementing a feature or fixing a bug to enable regression testing and designer validation.
- Create
test/samples/feature_name.json.
- Reference via
getTestWidgetFromPath(path: 'feature_name.json').
Source: freemansoft/Flutter-AdaptiveCards — distributed by TomeVault.