high priority medium complexity testing pending testing specialist Tier 8

Acceptance Criteria

Integration test suite executes end-to-end without flakiness across 3 consecutive runs on both iOS and Android targets
Test opens CoordinatorStatsDashboardScreen and verifies it renders within 2 seconds with all stat cards visible
Changing the period filter (e.g., week → month → year) triggers a data reload and all chart widgets reflect updated values deterministically
Toggling to PersonalStatsView correctly renders the personal bar chart with labelled axes and at least one data series visible
Switching org-admin scope updates all dashboard cards and charts to reflect the org-level aggregated data, not personal data
All test assertions use seeded Supabase data or golden mocks so chart output is fully deterministic and reproducible in CI
Tests are registered under the integration_test package and run via `flutter test integration_test/` with no manual setup required
Each test step includes descriptive comments and meaningful finder keys to aid debugging on failure
Test suite passes on TestFlight build variant without modification

Technical Requirements

frameworks
Flutter
flutter_test
integration_test
apis
Supabase REST API (seeded test project or local emulator)
Supabase Auth (test credentials)
data models
CoordinatorStats
PersonalStats
OrgStats
PeriodFilter
performance requirements
Dashboard must fully render within 2 seconds of navigation in integration test environment
Period filter change must trigger visible UI update within 1 second
security requirements
Test credentials must be stored in environment variables or Flutter test secrets, never hardcoded
Seeded test data must not contain real personal identifiers
ui components
CoordinatorStatsDashboardScreen
PeriodFilterSelector
PersonalStatsView
StatsBarChart
StatSummaryCard

Execution Context

Execution Tier
Tier 8

Tier 8 - 48 tasks

Can start after Tier 7 completes

Implementation Notes

Use `IntegrationTestWidgetsFlutterBinding.ensureInitialized()` at test entry. Provide a dedicated Supabase test project URL and anon key via `--dart-define` to isolate test data. Seed the database with a fixed dataset before each test using a `setUp` helper that calls Supabase RPC or direct REST inserts, then tears down in `tearDown`. For chart determinism, prefer golden mock providers (Riverpod overrides or BLoC test stubs) that return fixed data rather than relying on network.

Use `find.byKey(ValueKey('period-filter'))` style finders — ensure all interactive widgets have `Key` values set in the production widget tree. Avoid `pump(Duration)` with arbitrary durations; always use `pumpAndSettle` after async operations. Tag tests with `@Tags(['integration'])` so they can be excluded from fast unit test runs.

Testing Requirements

Integration tests only (flutter_test integration_test package). All tests run against a seeded Supabase test environment or deterministic golden mocks — no live production data. Cover: (1) initial render, (2) period filter change → data reload, (3) PersonalStatsView toggle → bar chart render, (4) org-admin scope switch → aggregated data. Each scenario asserts UI state after async completion using `pumpAndSettle`.

CI must execute the suite on every PR targeting the dashboard feature branch. Minimum: 1 happy-path test per major interaction; 1 error-state test (e.g., empty data period).

Epic Risks (4)
medium impact medium prob technical

PeerMentorStatsList must handle rosters of 40+ mentors efficiently. A naive ListView implementation that re-renders all rows on filter change may cause frame drops on mid-range devices, degrading the experience for coordinators managing large chapters.

Mitigation & Contingency

Mitigation: Use ListView.builder with const constructors for row widgets. Profile the list with Flutter DevTools on a release build against a 60-mentor dataset before submitting for review. Implement sort in the BLoC layer, not in the widget.

Contingency: If frame drops persist, introduce pagination (load 20 mentors, scroll to load more) and add a search filter to reduce visible row count in practice.

medium impact low prob integration

The ActivityTypeDonutChart must render org-configured activity type labels from the org-labels system. If the terminology provider is not available or returns stale labels, chart segments will display raw key strings instead of human-readable organisation-specific names, confusing coordinators.

Mitigation & Contingency

Mitigation: Always resolve labels through the OrganizationLabelsProvider before passing data to the donut chart widget. Implement a fallback that formats the raw key as a readable string (e.g., 'peer_support' → 'Peer Support') if the provider returns null.

Contingency: If the org-labels system is unavailable, display the formatted fallback label and log a warning. Do not block chart rendering on label resolution — render with fallbacks immediately.

high impact high prob technical

fl_chart widgets do not natively expose semantic labels for individual bars and donut segments. Without explicit Semantics wrappers, VoiceOver and TalkBack users will receive no meaningful chart information, failing accessibility requirements critical for Blindeforbundet and NHF deployments.

Mitigation & Contingency

Mitigation: Wrap each fl_chart widget in a Semantics widget with a descriptive label summarising the chart data. Implement the data table fallback toggle from the start, not as an afterthought. Validate with VoiceOver on iOS and TalkBack on Android during development.

Contingency: If fl_chart's rendering pipeline prevents semantic overlay from working reliably, replace chart widgets with a custom Canvas-based implementation that has full semantic control, or use a different charting library with better accessibility support.

medium impact medium prob technical

The period filter state must be preserved when navigating away from the dashboard (e.g., drilling into a mentor's detail screen and pressing back). If the BLoC is re-created on navigation, the filter resets to default, forcing coordinators to re-select their period every time they drill down, degrading operational workflow efficiency.

Mitigation & Contingency

Mitigation: Provide the StatsBloc at a route level above the dashboard screen using a BlocProvider scoped to the statistics navigation shell, not inside the screen widget itself. Verify persistence with a widget integration test that navigates away and back.

Contingency: If route-level scoping is not achievable within the current navigation architecture, persist the last-used filter to a local session store and restore it on screen re-entry.