diff --git a/docs/verification/issue-33/analyze.log b/docs/verification/issue-33/analyze.log new file mode 100644 index 0000000..c6ab005 --- /dev/null +++ b/docs/verification/issue-33/analyze.log @@ -0,0 +1,6 @@ +$ flutter analyze + +Upgrading analysis_options.yaml to exclude build and platform directories. +Analyzing flutter-starter... +No issues found! (ran in 4.1s) +exit: 0 diff --git a/docs/verification/issue-33/audit_template.log b/docs/verification/issue-33/audit_template.log new file mode 100644 index 0000000..8f99c99 --- /dev/null +++ b/docs/verification/issue-33/audit_template.log @@ -0,0 +1,7 @@ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:14 +2229 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: HomeScreen should create HomeScreen widget +01:14 +2230 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: HomeScreen should display welcome message +01:14 +2231 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: HomeScreen should have app bar with title +01:14 +2232 ~1: All tests passed! +exit: 0 diff --git a/docs/verification/issue-33/criteria-1-2-green-run.txt b/docs/verification/issue-33/criteria-1-2-green-run.txt new file mode 100644 index 0000000..f6f7622 --- /dev/null +++ b/docs/verification/issue-33/criteria-1-2-green-run.txt @@ -0,0 +1,24 @@ +# Criteria 1 and 2: green with no backend, and the gated test is visibly skipped + +run 32978638487 (workflow_dispatch, fix/e2e-green-without-backend) -> SUCCESS + + 📝 Total: 2 + ✅ Successful: 1 + ❌ Failed: 0 + ⏩ Skipped: 1 + + Patrol reported test count: 2 + OK: 2 test(s) collected and executed, 0 failed. + +Criterion 1: Total > 0, Failed 0, job SUCCESS, no backend configured. +Criterion 2: Skipped: 1 - the authenticated flow is visible as skipped, + not silently absent. + +-- the three attempts it took, and what each proved -- + 32970921492 FAIL Total 2 / Failed 1 smoke found 0 widgets: + app.main() was called without await, and could not be + awaited at all because it was declared 'void main() async'. + 32975097312 FAIL Total 2 / Failed 1 'pumpAndSettle timed out' after 131s: + awaiting main() worked, but the tree never reaches an + idle frame on a real device. + 32978638487 PASS Total 2 / Successful 1 / Skipped 1 / Failed 0 diff --git a/docs/verification/issue-33/criteria-4-5-consistency.txt b/docs/verification/issue-33/criteria-4-5-consistency.txt new file mode 100644 index 0000000..5196470 --- /dev/null +++ b/docs/verification/issue-33/criteria-4-5-consistency.txt @@ -0,0 +1,16 @@ +# Criteria 4 and 5: auth_flow_test consistent, all six goldens updated + +$ grep -c hasBackend integration_test/*.dart tool/golden/*/integration_test/*.dart +integration_test/app_e2e_test.dart:2 +tool/golden/no_tasks/integration_test/app_e2e_test.dart:2 +integration_test/auth_flow_test.dart:2 +tool/golden/no_feature_flags/integration_test/auth_flow_test.dart:2 +tool/golden/stripped/integration_test/app_e2e_test.dart:2 +tool/golden/no_feature_flags/integration_test/app_e2e_test.dart:2 +tool/golden/no_tasks/integration_test/auth_flow_test.dart:2 +tool/golden/stripped/integration_test/auth_flow_test.dart:2 + +$ grep -rn pumpAndSettle integration_test/ tool/golden/*/integration_test/ | grep -v '///' +(none outside doc comments) + +Strip + analyze + test on the PR: pass (goldens consistent with the root tree). diff --git a/docs/verification/issue-33/criterion-3-smoke-is-real.txt b/docs/verification/issue-33/criterion-3-smoke-is-real.txt new file mode 100644 index 0000000..e7b8505 --- /dev/null +++ b/docs/verification/issue-33/criterion-3-smoke-is-real.txt @@ -0,0 +1,20 @@ +# Criterion 3: the smoke test asserts something real + + // Runs everywhere, including with no backend. Not a token test: reaching a + // rendered login screen exercises the native Patrol harness, app bootstrap, + // the Riverpod scope, the router, and localization. + patrolTest('E2E: app boots to a usable login screen', ($) async { + await _boot($); + + await $(#e2e_login_submit).waitUntilVisible(); + expect($(#e2e_login_submit), findsOneWidget); + + await $(TextField).at(0).enterText('test@example.com'); + await $(TextField).at(1).enterText('password123'); + expect($('test@example.com'), findsOneWidget); + }); + +It waits for the login control, asserts it exists, types into both fields, +and asserts the typed text renders. Reaching a rendered login screen +exercises the native harness, app bootstrap, the Riverpod scope, the +router and localization. It is not a bare app.main() call. diff --git a/docs/verification/issue-33/criterion-6-readme.txt b/docs/verification/issue-33/criterion-6-readme.txt new file mode 100644 index 0000000..96d7088 --- /dev/null +++ b/docs/verification/issue-33/criterion-6-readme.txt @@ -0,0 +1,26 @@ +# Criterion 6: integration_test/README.md documents the gate + +## Stable selectors + +Do not rely on translated button labels for critical steps. Use `ValueKey`s from [`lib/core/constants/ui_keys.dart`](../lib/core/constants/ui_keys.dart) (e.g. `e2e_login_submit`, `e2e_home_content`). + +Each file here holds **two** tests: + +1. **A smoke test that always runs** — the app boots to a usable login screen. No backend needed. It is not a token test: reaching a rendered login screen exercises the native Patrol harness, app bootstrap, the Riverpod scope, the router, and localization. +2. **The authenticated flow, skipped by default** — everything past login. The sample auth flow POSTs to `BASE_URL`; with no server the login call fails, the app stays on the login screen, and those assertions are unreachable. Rather than ship a suite that is red by construction, they are gated: + +```bash +# skipped (default) — the suite is green on a fresh clone +patrol test --target integration_test/app_e2e_test.dart + +# run the authenticated flow, once you have an API reachable from the device +patrol test --target integration_test/app_e2e_test.dart --dart-define=E2E_BACKEND=true +``` + +A skipped test still appears in the summary (`⏩ Skipped: 1`), so it cannot be quietly forgotten. + +**There is no tasks coverage, on purpose.** `UiKeys.openTasks`, `UiKeys.tasksFab`, and `UiKeys.addTaskSubmit` are declared but attached to no widget in `lib/`: `HomeScreen` is a deliberately minimal shell with no entry point into the sample `tasks` feature. Patrol matches on the widget tree, so a selector written against an unattached key finds nothing — attach the key first if your fork adds that entry point. `tool/golden/no_feature_flags/` shows the wiring, and its own `app_e2e_test.dart` does drive the full tasks flow. + +**After** `dart run tool/strip_sample_features.dart --apply`, golden files replace these E2E files outright, so the post-strip variant is whatever `tool/golden//integration_test/` contains — editing the copies here does not affect it. + +## CI diff --git a/docs/verification/issue-33/format.log b/docs/verification/issue-33/format.log new file mode 100644 index 0000000..3843b1a --- /dev/null +++ b/docs/verification/issue-33/format.log @@ -0,0 +1,4 @@ +$ dart format --set-exit-if-changed lib test integration_test tool examples + +Formatted 333 files (0 changed) in 1.05 seconds. +exit: 0 diff --git a/docs/verification/issue-33/tests.log b/docs/verification/issue-33/tests.log new file mode 100644 index 0000000..20bef52 --- /dev/null +++ b/docs/verification/issue-33/tests.log @@ -0,0 +1,124 @@ +$ flutter test --timeout=5m --reporter compact + +00:03 +54: /Users/nguyenanhkiet/Playground/flutter-starter/test/core/config/app_config_test.dart: AppConfig Debug utilities should have printConfig method +═══════════════════════════════════════════════════════════ +📱 App Configuration +═══════════════════════════════════════════════════════════ +Environment: development +Debug Mode: true +Release Mode: false + +🌐 API Configuration: + Base URL: http://localhost:3000 + Timeout: 30s + Connect Timeout: 10s + Receive Timeout: 30s + Send Timeout: 30s + SSL Pinning Enabled: false + SSL Fingerprints Configured: 0 + +🚩 Feature Flags: + Logging: true + Analytics: false + Crash Reporting: false + Performance Monitoring: false + Debug Features: true + HTTP Logging: true + +📦 App Info: + Version: 1.0.0 + Build Number: 1 +═══════════════════════════════════════════════════════════ +00:03 +78: /Users/nguyenanhkiet/Playground/flutter-starter/test/core/config/app_config_test.dart: AppConfig Edge Cases printConfig should not throw in debug mode +═══════════════════════════════════════════════════════════ +📱 App Configuration +═══════════════════════════════════════════════════════════ +Environment: development +Debug Mode: true +Release Mode: false + +🌐 API Configuration: + Base URL: http://localhost:3000 + Timeout: 30s + Connect Timeout: 10s + Receive Timeout: 30s + Send Timeout: 30s + SSL Pinning Enabled: false + SSL Fingerprints Configured: 0 + +🚩 Feature Flags: + Logging: true + Analytics: false + Crash Reporting: false + Performance Monitoring: false + Debug Features: true + HTTP Logging: true + +📦 App Info: + Version: 1.0.0 + Build Number: 1 +═══════════════════════════════════════════════════════════ + +[... 683 lines elided by scripts/test/run_acceptance.sh. + Full output is reproducible by re-running the command above. ...] + +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:16 +2221 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: MyApp should use light theme by default +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +│ #0 LoggingService.info (package:flutter_starter/core/logging/logging_service.dart:123:13) +│ #1 NavigationLoggingObserver._logNavigation (package:flutter_starter/core/routing/navigation_logging.dart:73:21) +├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:17 +2222 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: MyApp should have dark theme configured +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +│ #0 LoggingService.info (package:flutter_starter/core/logging/logging_service.dart:123:13) +│ #1 NavigationLoggingObserver._logNavigation (package:flutter_starter/core/routing/navigation_logging.dart:73:21) +├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:17 +2223 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: MyApp should configure router correctly +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +│ #0 LoggingService.info (package:flutter_starter/core/logging/logging_service.dart:123:13) +│ #1 NavigationLoggingObserver._logNavigation (package:flutter_starter/core/routing/navigation_logging.dart:73:21) +├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:17 +2224 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: MyApp should configure localization delegates +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +│ #0 LoggingService.info (package:flutter_starter/core/logging/logging_service.dart:123:13) +│ #1 NavigationLoggingObserver._logNavigation (package:flutter_starter/core/routing/navigation_logging.dart:73:21) +├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:17 +2225 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: MyApp should configure supported locales +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +│ #0 LoggingService.info (package:flutter_starter/core/logging/logging_service.dart:123:13) +│ #1 NavigationLoggingObserver._logNavigation (package:flutter_starter/core/routing/navigation_logging.dart:73:21) +├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:17 +2226 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: MyApp should use locale from provider +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +│ #0 LoggingService.info (package:flutter_starter/core/logging/logging_service.dart:123:13) +│ #1 NavigationLoggingObserver._logNavigation (package:flutter_starter/core/routing/navigation_logging.dart:73:21) +├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:17 +2227 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: MyApp should configure text direction from provider +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +│ #0 LoggingService.info (package:flutter_starter/core/logging/logging_service.dart:123:13) +│ #1 NavigationLoggingObserver._logNavigation (package:flutter_starter/core/routing/navigation_logging.dart:73:21) +├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:17 +2228 ~1: /Users/nguyenanhkiet/Playground/flutter-starter/test/main_test.dart: MyApp should wrap content in RepaintBoundary +┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +│ #0 LoggingService.info (package:flutter_starter/core/logging/logging_service.dart:123:13) +│ #1 NavigationLoggingObserver._logNavigation (package:flutter_starter/core/routing/navigation_logging.dart:73:21) +├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +│ 💡 Navigation: Route Pushed | Context: {"routeName":"login","routePath":"login"} +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── +01:17 +2232 ~1: 1 skipped test. +01:17 +2232 ~1: All other tests passed! +exit: 0