Context
#544 (page tests), #545 (widget tests), and #547 (visual regression) all reference a "CI check that lists *_page.dart / lib/widgets/**/*.dart without a matching *_test.dart / *_golden_test.dart" as an enforcement deliverable. None of those three issues own that check. This issue owns it.
Without an enforcement guard, every new page or widget added after the test backlog is closed will silently reopen the gap.
This guard is a complement to #544/#545/#547, not a prerequisite — those issues can ship without it; the guard's value is forward-looking regression prevention.
Constraint: combined and parent-test coverage
A naive basename-match (find lib/screens -name '*_page.dart' | while read p; do test -f "test/${p#lib/}" || echo MISSING; done) produces false positives in three ways:
Combined test files (one test, multiple source files)
test/screens/kyc/steps/financial_data/subpages/kyc_financial_data_loading_failure_pages_test.dart covers kyc_financial_data_loading_page.dart + kyc_financial_data_failure_page.dart
test/widgets/buttons/app_buttons_test.dart covers app_filled_button.dart + app_text_button.dart
test/widgets/iban_formatters_test.dart covers iban_input_formatter.dart + iban_text_formatter.dart
test/widgets/tag_tab_selectors_test.dart covers tab_selector.dart + tag_selection.dart
test/widgets/outlined_tile_handlebars_test.dart covers outlined_tile.dart + handlebars.dart
test/widgets/form/form_fields_test.dart covers dropdown_field.dart + labeled_text_field.dart
Parent-test coverage (page rendered + asserted in another page's test)
kyc_financial_data_questions_page.dart — asserted in test/screens/kyc/steps/kyc_financial_data_page_test.dart:130,154,178,202 via find.byType
settings_edit_pending_page.dart, settings_edit_failure_page.dart — asserted in test/screens/settings_user_data/subpages/settings_edit_name_page_test.dart and settings_edit_address_page_test.dart
part of files (not independently testable)
lib/widgets/mnemonic_input_field.dart, mnemonic_input_field_controller.dart, mnemonic_field_base.dart, mnemonic_read_only_field.dart — all part of 'mnemonic_field.dart'; (line 1 of each). They cannot be imported by a test directly; the test imports the parent.
The guard must combine import-detection with these three exemptions.
Bottom-sheet glob exception
lib/screens/pin/widgets/enable_biometric_bottom_sheet.dart, lib/screens/pin/widgets/forgot_pin_bottom_sheet.dart, and lib/screens/hardware_connect_bitbox/show_bitbox_reconnect_sheet.dart should be in scope but don't match *_page.dart. Need either a separate glob or an explicit include-list.
Scope
Guard implementation
Variants (later, not load-bearing for #544/#545)
CI wiring
Documentation
Acceptance criteria
Estimated effort
| Sub-task |
Days |
| Detection script (Dart preferred; Bash workable) — import-grep + part-of exclusion + allow-list reader |
0.5 |
.test-coverage-allowlist initial entries for current coverage:ignore-file candidates (web_view, debug_auth, legal_document, image_picker_sheet) |
0.25 |
New Test Coverage Pairs CI job + parallel wiring |
0.25 |
Documentation in docs/testing.md + README roadmap row |
0.25 |
| Testing the guard against the existing tree (zero false positives target) |
0.5 |
| Total |
~1.5-2 engineer-days |
Why this is its own issue, not embedded in #544/#545/#547
The script is a one-time deliverable that all three issues reference. Embedding it in any one of them creates an ownership conflict; embedding it in all three duplicates the work. Note: this is a forward-looking complement — #544/#545/#547 can ship before this lands.
Related
Context
#544 (page tests), #545 (widget tests), and #547 (visual regression) all reference a "CI check that lists
*_page.dart/lib/widgets/**/*.dartwithout a matching*_test.dart/*_golden_test.dart" as an enforcement deliverable. None of those three issues own that check. This issue owns it.Without an enforcement guard, every new page or widget added after the test backlog is closed will silently reopen the gap.
This guard is a complement to #544/#545/#547, not a prerequisite — those issues can ship without it; the guard's value is forward-looking regression prevention.
Constraint: combined and parent-test coverage
A naive basename-match (
find lib/screens -name '*_page.dart' | while read p; do test -f "test/${p#lib/}" || echo MISSING; done) produces false positives in three ways:Combined test files (one test, multiple source files)
test/screens/kyc/steps/financial_data/subpages/kyc_financial_data_loading_failure_pages_test.dartcoverskyc_financial_data_loading_page.dart+kyc_financial_data_failure_page.darttest/widgets/buttons/app_buttons_test.dartcoversapp_filled_button.dart+app_text_button.darttest/widgets/iban_formatters_test.dartcoversiban_input_formatter.dart+iban_text_formatter.darttest/widgets/tag_tab_selectors_test.dartcoverstab_selector.dart+tag_selection.darttest/widgets/outlined_tile_handlebars_test.dartcoversoutlined_tile.dart+handlebars.darttest/widgets/form/form_fields_test.dartcoversdropdown_field.dart+labeled_text_field.dartParent-test coverage (page rendered + asserted in another page's test)
kyc_financial_data_questions_page.dart— asserted intest/screens/kyc/steps/kyc_financial_data_page_test.dart:130,154,178,202viafind.byTypesettings_edit_pending_page.dart,settings_edit_failure_page.dart— asserted intest/screens/settings_user_data/subpages/settings_edit_name_page_test.dartandsettings_edit_address_page_test.dartpart offiles (not independently testable)lib/widgets/mnemonic_input_field.dart,mnemonic_input_field_controller.dart,mnemonic_field_base.dart,mnemonic_read_only_field.dart— allpart of 'mnemonic_field.dart';(line 1 of each). They cannot be imported by a test directly; the test imports the parent.The guard must combine import-detection with these three exemptions.
Bottom-sheet glob exception
lib/screens/pin/widgets/enable_biometric_bottom_sheet.dart,lib/screens/pin/widgets/forgot_pin_bottom_sheet.dart, andlib/screens/hardware_connect_bitbox/show_bitbox_reconnect_sheet.dartshould be in scope but don't match*_page.dart. Need either a separate glob or an explicit include-list.Scope
Guard implementation
tool/check_test_coverage.sh(ortool/check_test_coverage.dart) that:lib/screens/**/*_page.dart+lib/screens/**/*_bottom_sheet.dart+lib/screens/hardware_connect_bitbox/show_bitbox_reconnect_sheet.dart+lib/widgets/**/*.dart*.g.dart(Drift codegen — onlydatabase.g.darttoday)lib/generated/**(i18n + release_info codegen — 3 files)part of '...';(part-of pattern, e.g. all 4 mnemonic part files)*.freezed.dartand*.mocks.dartdo not exist in this tree today (verified viafind); they're listed in the existinglcov --removestep at.github/workflows/pull-request.yaml:113-116as preemptive, harmless to mirror but unnecessarytest/**/*_test.dartthat imports the source file bypackage:realunit_wallet/...path (covers basename and combined cases), or.test-coverage-allowlist.test-coverage-allowlistflat-file in repo root with one path per line + one-line rationale (e.g.lib/screens/web_view/web_view_page.dart # InAppWebView wrapper, see #544)Variants (later, not load-bearing for #544/#545)
*_page.dart→test/goldens/**/*_golden_test.dart. Companion to Visual regression: scale from 5-screen pilot to every*_page.dart#547.CI wiring
Test Coverage Pairsin.github/workflows/pull-request.yaml, runs in parallel withAnalyze & Testpull_requestand on push todevelop*_page.dart#547 closes the gapDocumentation
docs/testing.mdAcceptance criteria
coverage:ignorecases (e.g.web_view_page.dart)*_page.dartorlib/widgets/**/*.dartlands without a test and without an allow-list entry*_page.dart(12 untested + 5 special-handling) #544 / testWidgets: spec every shared widget underlib/widgets/(8 testable + 1 dead code) #545 state is green when this lands (all current allow-list entries land in the same PR as the guard)Estimated effort
.test-coverage-allowlistinitial entries for currentcoverage:ignore-filecandidates (web_view, debug_auth, legal_document, image_picker_sheet)Test Coverage PairsCI job + parallel wiringdocs/testing.md+ README roadmap rowWhy this is its own issue, not embedded in #544/#545/#547
The script is a one-time deliverable that all three issues reference. Embedding it in any one of them creates an ownership conflict; embedding it in all three duplicates the work. Note: this is a forward-looking complement — #544/#545/#547 can ship before this lands.
Related
*_page.dart(12 untested + 5 special-handling) #544 — page tests (downstream consumer)lib/widgets/(8 testable + 1 dead code) #545 — widget tests (downstream consumer)*_page.dart#547 — visual regression (downstream consumer)main.dart, router config, DI wiring, deep links #546)docs/testing.md— testing conventions