Skip to content

testWidgets: spec every shared widget under lib/widgets/ (8 testable + 1 dead code) #545

@TaprootFreak

Description

@TaprootFreak

Context

lib/widgets/ holds 31 Dart files. Two are not strictly widgets (iban_input_formatter.dart is a TextInputFormatter, iban_text_formatter.dart is a utility class) — but both are in scope for tests because they sit in lib/widgets/.

Mechanical accounting against existing tests:

  • Files with a basename-matched or combined-spec test: 20
  • Files with no dedicated widget-level spec: 11 (of which 1 is dead code, 3 are part-of files attached to mnemonic_field.dart)

Form / mnemonic widgets are the primary entry point for untrusted user input (KYC, restore-wallet seed entry, contact form). A silent regression in any of them ripples into every consuming screen.

Already tested — do not re-add

Widget file Test location
lib/widgets/action_button.dart test/widgets/action_button_test.dart
lib/widgets/buttons/app_filled_button.dart test/widgets/buttons/app_buttons_test.dart (combined)
lib/widgets/buttons/app_text_button.dart test/widgets/buttons/app_buttons_test.dart (combined)
lib/widgets/date_picker_field.dart test/widgets/date_picker_field_test.dart
lib/widgets/form/country_field.dart test/widgets/form/country_field_test.dart
lib/widgets/form/dropdown_field.dart test/widgets/form/form_fields_test.dart (combined)
lib/widgets/form/labeled_text_field.dart test/widgets/form/form_fields_test.dart (combined)
lib/widgets/handlebars.dart test/widgets/outlined_tile_handlebars_test.dart (combined)
lib/widgets/hide_amount_text.dart test/widgets/hide_amount_text_test.dart
lib/widgets/iban_input_formatter.dart test/widgets/iban_formatters_test.dart (combined; not strictly a widget)
lib/widgets/iban_text_formatter.dart test/widgets/iban_formatters_test.dart (combined; not strictly a widget)
lib/widgets/info_row.dart test/widgets/info_row_test.dart
lib/widgets/mnemonic_field.dart MnemonicReadOnlyField part-class test/widgets/mnemonic_read_only_field_test.dart
Mnemonic extensions test/widgets/mnemonic_extensions_test.dart, test/widgets/mnemonic_field_extensions_test.dart
lib/widgets/number_pad.dart test/widgets/number_pad_test.dart
lib/widgets/outlined_tile.dart test/widgets/outlined_tile_handlebars_test.dart (combined)
lib/widgets/standard_slide_button.dart test/widgets/standard_slide_button_test.dart
lib/widgets/tab_selector.dart test/widgets/tag_tab_selectors_test.dart (combined)
lib/widgets/tag_selection.dart test/widgets/tag_tab_selectors_test.dart (combined)
lib/widgets/text_link_span.dart test/widgets/text_link_span_test.dart
lib/widgets/text_substring_highlighting.dart test/widgets/text_substring_highlighting_test.dart

Dead code — delete, do not test

  • lib/widgets/chain_asset_icon.dart — verified by grep -rn "ChainAssetIcon" across lib/: zero consumers in the production tree. Pure StatelessWidget, no callers. Tracked as its own cleanup ticket (separate "chore: delete dead widget" issue).

Scope — 8 testable files (the true gap after removing dead code)

Form / input widgets (user-input boundary)

  • lib/widgets/form/birthday_field.dart — date picker round-trip, locale handling
  • lib/widgets/form/file_picker_field.dart — file selection, validation, error display
  • lib/widgets/form/phone_number_field.dart — test the actual contract: hard-coded prefix list ['+41', '+49'] at phone_number_field.dart:16, digit-only number input, minimum length 6 (:91-99), prefix/number split on initial value, recombine on emit. Do not assert E.164 round-trip — the widget does not implement E.164.

Mnemonic widgets (security-critical)

  • lib/widgets/mnemonic_field.dartMnemonicInputField interactive class. The four files mnemonic_input_field.dart, mnemonic_input_field_controller.dart, mnemonic_field_base.dart, mnemonic_read_only_field.dart are all part of 'mnemonic_field.dart'; (line 1 of each) and cannot be imported independently. One test file covers MnemonicInputField paste handling, focus traversal, BIP-39 validation. MnemonicReadOnlyField is already tested.
  • lib/widgets/seed_blur_card.dart — verifies the wrapped mnemonic is hidden by default (ExcludeSemantics at seed_blur_card.dart:23) and revealed only on explicit tap-to-show.

KYC-document upload (testable today, no refactor needed)

  • lib/widgets/image_picker_sheet.dart — consumers are settings_edit_name_page.dart and settings_edit_address_page.dart (KYC proof-of-identity / proof-of-address). A bug here blocks KYC completion. Sheet selection logic (the ListTile taps → ImageSource mapping → null-on-dismiss) is fully testable today via Navigator-pop assertions; ImagePicker().pickImage(...) itself can be stubbed via TestDefaultBinaryMessenger.setMockMethodCallHandler on the plugins.flutter.io/image_picker channel — no source refactor needed.

    Earlier versions of this issue said this file was "blocked on refactor / Port abstraction" — verified false. The sheet's ListTile taps each return distinct ImageSource values; swapping camera ↔ gallery is a plausible regression worth catching.

Picker (low-value but cheap)

  • lib/widgets/date_picker.dart — pure Flutter (Cupertino branch via DeviceInfo.instance.isIOS at date_picker.dart:13, Material branch via showDatePicker). Both branches testable in flutter_test. Specific bug-pattern worth catching: the Cupertino branch's selectedDate defaults to initialDate and is only updated via onDateTimeChanged — if the popup is dismissed by tap-outside, it returns initialDate instead of null, diverging from Material's showDatePicker which returns null on cancel. Consumers are settings_tax_report_page.dart and transaction_history_page.dart (non-funds-loss but UX-incorrect).

Allow-list candidates

None from the current lib/widgets/ tree. chain_asset_icon.dart would have been one, but it's dead code (delete instead).

Acceptance criteria

  • Each widget gets a *_test.dart covering: builds with required + minimal props, builds with optional props, handles user interaction (tap / fill / select) when applicable, asserts callback wire-up
  • Form widgets additionally use table-driven tests over equivalence classes (valid / invalid / boundary). Not arbitrary "fuzz" counts.
  • mnemonic_field.dart test additionally asserts that the entered text is excluded from the Semantics tree (verifiable via seed_blur_card.dart:23 ExcludeSemantics).
  • image_picker_sheet.dart test uses TestDefaultBinaryMessenger.setMockMethodCallHandler for the image-picker channel; does NOT require a port-abstraction refactor.
  • Use the existing widget-test convention: test/widgets/form/form_fields_test.dart and test/widgets/buttons/app_buttons_test.dart both define a private _host(Widget child) => MaterialApp(...) helper and call tester.pumpWidget(_host(...)). This is the established pattern for widget-level tests. (pumpApp from test/helper/helper.dart is the screen-test convention used by *_page_test.dart — not the widget-test convention.)

Cross-issue coordination

Estimated effort

Sub-task Days
Form widgets (birthday, file_picker, phone_number) — table-driven 1.5
Mnemonic widgets (MnemonicInputField + seed_blur_card) — Semantics + paste/focus 1.0
image_picker_sheet (channel-mock + sheet logic) 0.5
date_picker (Cupertino + Material branches, cancel-semantics bug) 0.5
Cleanup PR: delete chain_asset_icon.dart (#560) 0.1
Total ~3 engineer-days

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions