test(e2e): Phase 2 verte — S2/S5/S6/S7 débloqués, fixes #136 couverts, CI E2E fiabilisée - #138
Conversation
|
| Filename | Overview |
|---|---|
| purchasely/example/integration_test/tools/ci_run_e2e.sh | Rewritten to all-hard-gate model with portable run_with_timeout watchdog, --reporter expanded, ANR-clear preamble, and 4 new suites (inline_paywall, re_display, flow_dismiss, purchase_restore_android). Logic is sound. |
| purchasely/example/integration_test/tools/ci_run_e2e_ios.sh | iOS runner extended to 14 suites with same watchdog/hard-gate model; StoreKit Apple-bug exception logic (storekit_apple_sig vs storekit_other_failure) is correctly scoped to all-Apple-only failure runs. |
| purchasely/example/integration_test/tools/run_storekit_suite_ios.sh | Dart-marker-authoritative gate correctly requires both xcodebuild exit 0 AND S7-IOS-RESULT: PASS in the simulator unified log; log show fallback handles log stream buffering races. |
| purchasely/example/integration_test/helpers/e2e_start.dart | startWithRetry correctly gates retries on network-ish error substrings only; 180 s backstop timeout deliberately not classified as retryable to surface genuine hangs. |
| purchasely/example/integration_test/purchase_restore_ios_test.dart | S7-IOS-RESULT marker mechanism correctly uses _completedTests++ as last line of test body; manual run instructions in header still reference tap_purchase_ios.sh alongside xcodebuild, which silently fails now that RunnerIntegrationTests.m owns the CTA tap. |
| purchasely/example/ios/RunnerIntegrationTests/RunnerIntegrationTests.m | SKTestSession setup, CTA tap via XCUITest, and 420 s poll with XCTFail are correctly wired; disableDialogs eliminates SpringBoard confirmation sheet dependency. |
| purchasely/example/integration_test/interceptor_actions_ios_test.dart | S5/S6 correctly distinguish failed vs notHandled via AppLifecycleState transitions; load-proof assertion on presentation.type prevents silent fallback to bundled screen. |
| purchasely/example/integration_test/purchase_restore_android_test.dart | Honest-degradation suite: proves interceptor fires, then asserts restore fast-fails (≤20 s) without a real Play Store; all three outcome paths (resolved/timeout/PlatformException) are caught and asserted. |
| .github/workflows/e2e-android.yml | Flutter 3.44.0 pin fixes the AGP9/compileGroovy hang; step-level 50-min ceiling preserves log upload even on worst-case retry exhaustion. |
| .github/workflows/e2e-ios.yml | Mirrors Android workflow with 50-min step ceiling; branch trigger extended to feat/sdk-v6-migration to gate the current migration branch. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant CI as GitHub Actions
participant Script as ci_run_e2e_ios.sh
participant RWT as run_with_timeout
participant FT as flutter test
participant Driver as Driver script
participant Sim as iOS Simulator
CI->>Script: bash ci_run_e2e_ios.sh udid
loop Each suite (1-13)
Script->>Driver: "bash driver.sh udid &"
Script->>RWT: run_with_timeout flutter test
RWT->>FT: flutter test (set -m own PGID)
FT->>Sim: install + launch app
Driver->>Sim: idb tap / xcrun simctl launch
Sim-->>FT: Dart callbacks / events
FT-->>RWT: exit 0 or non-zero
alt timeout (600 s)
RWT->>FT: kill -TERM PGID then kill -KILL PGID
RWT-->>Script: exit 124
else pass
RWT-->>Script: exit 0
end
Script->>Driver: kill driver PID
end
Note over Script: Suite 14 StoreKit special path
Script->>RWT: run_with_timeout bash run_storekit_suite_ios.sh udid
RWT->>Script: xcodebuild test RunnerIntegrationTests
Script->>Sim: xcrun simctl spawn log stream flutter
Sim-->>Script: S7-IOS-RESULT PASS or FAIL
alt xcodebuild exit 0 AND PASS marker
Script-->>CI: exit 0
else Apple FB22237318 signature only
Script-->>CI: warning S7-iOS BLOCKED non-gating
else other failure
Script-->>CI: exit 1 gate
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant CI as GitHub Actions
participant Script as ci_run_e2e_ios.sh
participant RWT as run_with_timeout
participant FT as flutter test
participant Driver as Driver script
participant Sim as iOS Simulator
CI->>Script: bash ci_run_e2e_ios.sh udid
loop Each suite (1-13)
Script->>Driver: "bash driver.sh udid &"
Script->>RWT: run_with_timeout flutter test
RWT->>FT: flutter test (set -m own PGID)
FT->>Sim: install + launch app
Driver->>Sim: idb tap / xcrun simctl launch
Sim-->>FT: Dart callbacks / events
FT-->>RWT: exit 0 or non-zero
alt timeout (600 s)
RWT->>FT: kill -TERM PGID then kill -KILL PGID
RWT-->>Script: exit 124
else pass
RWT-->>Script: exit 0
end
Script->>Driver: kill driver PID
end
Note over Script: Suite 14 StoreKit special path
Script->>RWT: run_with_timeout bash run_storekit_suite_ios.sh udid
RWT->>Script: xcodebuild test RunnerIntegrationTests
Script->>Sim: xcrun simctl spawn log stream flutter
Sim-->>Script: S7-IOS-RESULT PASS or FAIL
alt xcodebuild exit 0 AND PASS marker
Script-->>CI: exit 0
else Apple FB22237318 signature only
Script-->>CI: warning S7-iOS BLOCKED non-gating
else other failure
Script-->>CI: exit 1 gate
end
Reviews (2): Last reviewed commit: "fix(e2e): make iOS review gates reliable" | Re-trigger Greptile
There was a problem hiding this comment.
Pull request overview
This PR expands and hardens the Flutter SDK v6 E2E test coverage (new Phase 2 suites for S2/S5/S6/S7 and regression guards for fixes from #136) while making CI E2E runs more reliable and diagnosable (portable per-attempt watchdog, retries, and stricter hard-gating).
Changes:
- Added new Android+iOS E2E suites for flow dismiss (S2), action interceptor outcomes (S5/S6), re-display source preservation (M2), modal swipe dismiss behavior (M1), and purchase/restore coverage (S7).
- Introduced shared
startWithRetry()to reduce CI flakes from transient network/TLS failures duringPurchasely.start(). - Reworked CI E2E runners/scripts to be hard-gated with per-attempt watchdogs and improved diagnostics; updated workflows (notably Flutter 3.44.0 for AGP9 compatibility on Android).
Reviewed changes
Copilot reviewed 43 out of 43 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| purchasely/example/ios/RunnerIntegrationTests/RunnerIntegrationTests.m | Adds a host UI test bundle to launch the app under SKTestSession for StoreKit E2E. |
| purchasely/example/ios/RunnerIntegrationTests/Info.plist | Adds Info.plist for the new RunnerIntegrationTests target. |
| purchasely/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme | Wires RunnerIntegrationTests into the scheme and attaches the StoreKit configuration reference. |
| purchasely/example/ios/Runner.xcodeproj/project.pbxproj | Adds the RunnerIntegrationTests target and includes Configuration.storekit as a resource. |
| purchasely/example/ios/Configuration.storekit | Defines local StoreKit products for S7 iOS purchase/restore testing. |
| purchasely/example/integration_test/helpers/e2e_start.dart | Adds startWithRetry() helper used by multiple suites. |
| purchasely/example/integration_test/user_attribute_listener_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/local_dismiss_handler_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/local_dismiss_handler_ios_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/interceptor_trigger_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/interceptor_trigger_ios_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/inline_paywall_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/inline_events_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/default_dismiss_handler_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/default_dismiss_handler_ios_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/default_dismiss_via_display_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/default_dismiss_via_display_ios_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/deeplink_cold_start_test.dart | Strengthens event assertions and switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/dart_android_bridge_test.dart | Switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/dart_ios_bridge_test.dart | Updates comments + switches SDK start to startWithRetry(). |
| purchasely/example/integration_test/re_display_test.dart | New Android suite guarding M2 (re-display preserves original source). |
| purchasely/example/integration_test/re_display_ios_test.dart | New iOS mirror suite guarding M2 (re-display preserves original source). |
| purchasely/example/integration_test/flow_dismiss_test.dart | New Android suite for S2 (flow display + dismiss). |
| purchasely/example/integration_test/flow_dismiss_ios_test.dart | New iOS mirror suite for S2 (flow display + dismiss). |
| purchasely/example/integration_test/modal_dismissible_ios_test.dart | New iOS suite guarding M1 and exercising swipe-dismiss for dismissible modals. |
| purchasely/example/integration_test/interceptor_actions_ios_test.dart | New iOS suite for S5/S6 (navigate action interceptor failed vs notHandled). |
| purchasely/example/integration_test/purchase_restore_ios_test.dart | New iOS suite for S7 (local StoreKit purchase + restore). |
| purchasely/example/integration_test/purchase_restore_android_test.dart | New Android “honest degradation” S7 suite (no Play Store). |
| purchasely/example/integration_test/tools/ci_run_e2e.sh | Makes all Android suites hard-gated; adds watchdog and improved diagnostics. |
| purchasely/example/integration_test/tools/ci_run_e2e_ios.sh | Makes all iOS suites hard-gated with StoreKit Apple-bug exception logic + watchdog. |
| purchasely/example/integration_test/tools/run_storekit_suite_ios.sh | New StoreKit iOS runner (xcodebuild + simulator log capture). |
| purchasely/example/integration_test/tools/tap_purchase.sh | Adds ANR-dialog recovery to reduce CI flakiness. |
| purchasely/example/integration_test/tools/press_back.sh | Adds ANR-dialog recovery to reduce CI flakiness. |
| purchasely/example/integration_test/tools/tap_content_desc.sh | New generalized Android UI driver for content-desc substring taps. |
| purchasely/example/integration_test/tools/flow_close_all.sh | New wrapper driver to tap action:close_all for flow dismiss on Android. |
| purchasely/example/integration_test/tools/re_display_driver.sh | New Android driver wrapper to close paywall twice for re-display suite. |
| purchasely/example/integration_test/tools/re_display_driver_ios.sh | New iOS driver wrapper to close paywall twice for re-display suite. |
| purchasely/example/integration_test/tools/tap_label_ios.sh | New generalized iOS idb driver to tap elements by AXLabel. |
| purchasely/example/integration_test/tools/swipe_dismiss_ios.sh | New iOS driver to send a fixed number of interactive swipe-down gestures. |
| purchasely/example/integration_test/tools/modal_dismissible_driver_ios.sh | New iOS driver wrapper to run swipe-dismiss twice for modal dismissible suite. |
| purchasely/example/integration_test/tools/interceptor_actions_driver_ios.sh | New iOS driver coordinating two “Login” taps with log-driven synchronization + re-foreground. |
| .github/workflows/e2e-android.yml | Updates Flutter version to 3.44.0 and adds step-level timeout. |
| .github/workflows/e2e-ios.yml | Adds step-level timeout to preserve diagnostics on failures/hangs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…start backstop timeout Three cheap Greptile P2 findings from PR #138: - swipe_dismiss_ios.sh: the no-frame fallback geometry (390x844, iPhone-SE proportions) undershoots the swipe endpoints by ~1% on an iPhone 15 Pro simulator (393x852), making dismissal less reliable. Bump to 390 852. - ci_run_e2e.sh (Android): add --reporter expanded to match the iOS runner (ci_run_e2e_ios.sh already has it), so a failing suite's log artifact shows per-test detail instead of a single "Some tests failed" line. - e2e_start.dart: add a 180s backstop `.timeout(...)` around each startWithRetry attempt so callers without their own Dart-level timeout (e.g. flow_dismiss_test.dart) can't hang for the full ~600s CI watchdog budget. A caller's own tighter timeout still wins. Left as the default TimeoutException (no "timed out" in its message) so it does NOT match _kNetworkNeedles and get silently retried like a network hiccup — a real hang should surface as a failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@greptileai review |
…start backstop timeout Three cheap Greptile P2 findings from PR #138: - swipe_dismiss_ios.sh: the no-frame fallback geometry (390x844, iPhone-SE proportions) undershoots the swipe endpoints by ~1% on an iPhone 15 Pro simulator (393x852), making dismissal less reliable. Bump to 390 852. - ci_run_e2e.sh (Android): add --reporter expanded to match the iOS runner (ci_run_e2e_ios.sh already has it), so a failing suite's log artifact shows per-test detail instead of a single "Some tests failed" line. - e2e_start.dart: add a 180s backstop `.timeout(...)` around each startWithRetry attempt so callers without their own Dart-level timeout (e.g. flow_dismiss_test.dart) can't hang for the full ~600s CI watchdog budget. A caller's own tighter timeout still wins. Left as the default TimeoutException (no "timed out" in its message) so it does NOT match _kNetworkNeedles and get silently retried like a network hiccup — a real hang should surface as a failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2d1e807 to
951362d
Compare
Review avant merge — à merger, avec une réserveContenu vérifié et toujours pertinent malgré les 6 semaines : 0 commit de retard sur
|
…ent assertion Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…5/S6) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uites as hard gates Root fix: e2e-android.yml pinned flutter-version 3.24.x post-AGP9 (#130), breaking compileGroovy under Gradle 9 and surfacing only via flutter_tools' 12-minute per-test timeout, x3 retries, burning the whole 60-minute job on the first 1-2 suites. Bumped to 3.44.0 (e2e-ios.yml's 3.41.4 was already correct, left untouched). ci_run_e2e.sh / ci_run_e2e_ios.sh: - Portable per-attempt watchdog (run_with_timeout, no GNU coreutils dependency) wrapping every flutter test/xcodebuild invocation; two real bugs found and fixed via local testing (not just static review): an orphaned watchdog child holding a piped tee open (stalling every attempt for the full timeout), and an empty marker file defeating the 124 timeout-exit-code detection. - ::group::SUITE <name> attempt <n> / ::endgroup:: markers with exit code + duration on every attempt. - All suites promoted from best-effort/::warning:: to HARD gates (no || true, no continue-on-error) except one explicit, narrowly-scoped exception: the iOS StoreKit suite prints a loud "S7-iOS BLOCKED (Apple FB22237318)" marker and does not gate only when its failure matches that known, currently-open Apple/Xcode platform bug signature; any other failure of that suite gates normally. - Wired inline_paywall_test.dart (previously orphaned) plus the new re_display, flow_dismiss, purchase_restore_android/ios, modal_dismissible_ios and interceptor_actions_ios suites, adding 5 new driver-wrapper scripts (re_display_driver[_ios], modal_dismissible_driver_ios, flow_close_all, and a log-driven two-tap + re-foreground driver for interceptor_actions_ios). Local proof on emulator-5554: 2 suites passed end-to-end through the fixed script (user_attribute_listener, re_display — the latter also proving the new chained press_back.sh driver), plus a TIMEOUT=5 run showing 3 clean kill+retry cycles ending in a correctly gated failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…exception Fix 1: run_with_timeout backgrounds the suite command with job control (set -m) so it gets its own process group, then kills the group on timeout instead of just $cmd_pid — reaches gradle/dart/xcodebuild children that a single-PID kill orphaned. Portable: process-group creation via job control is core POSIX shell behavior, not a coreutils extra like setsid/timeout, so no per-runner branching is needed. Verified on the real emulator: 3 timed- out attempts leave zero leaked flutter/dart/gradle-client processes behind (only the pre-existing shared gradle daemon remains, as expected), and the normal/green path still completes correctly afterward. Fix 2: the StoreKit (FB22237318) non-gating exception now requires every failed attempt to match the Apple bug signature, not just any one of them — a mixed run (one attempt hits the known bug, another fails for a real regression) now gates instead of silently passing. Banner text states exactly how many attempts matched instead of a hardcoded "all 3 attempts". Fix 3: removed the newly-added || true instances (interceptor re-foreground, storekit cleanup) plus run_suite()'s pre-existing ones in both scripts, replaced with explicit non-fatal logging on failure. grep -c '|| true' on all three touched files is now 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…set -u Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ocus Task-9 CI run 29778283517: dumpsys window showed the IDENTICAL mCurrentFocus window IDs (ImmersiveModeConfirmation + "App Not Responding: com.google.android.apps.nexuslauncher") pinned across 5 different failing suites — one launcher ANR, under CI's resource-starved cold AVD, holds input focus for the rest of the job, so every driver BACK/tap lands on that dialog instead of the paywall. press_back.sh/tap_purchase.sh/ tap_content_desc.sh now force-stop the ANR'd package (never our own app) every poll iteration; ci_run_e2e.sh also pre-confirms the immersive-mode dialog to remove one contender. Bridge suite was never affected — it has no uiautomator driver at all, the T8/T9 comments only point at the separate interceptor/dismiss suites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… P1) CI run 29778281515 was a confirmed false green: xcodebuild exited 0 while the Dart suite had actually failed `expect(capturedPayload, isA<PLYPurchasePayload>())` (interceptor never fired). RunnerIntegrationTests is a hostless XCTest bundle that only proves the app launched and exited/timed out, and run_storekit_suite_ios.sh's `exit $STATUS` never looked at the captured Dart log at all. purchase_restore_ios_test.dart now bumps a per-test completion counter as the last line of each test body and prints exactly one `S7-IOS-RESULT: PASS`/`FAIL (completed=N/M)` marker from tearDownAll. run_storekit_suite_ios.sh gates its exit code on BOTH xcodebuild exit 0 AND that PASS marker, hardens the log capture (longer drain + a `log show` fallback appended after the live stream is killed), and RunnerIntegrationTests.m now XCTFails if the app is still running after the 180s poll window instead of silently falling through to a pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…start backstop timeout Three cheap Greptile P2 findings from PR #138: - swipe_dismiss_ios.sh: the no-frame fallback geometry (390x844, iPhone-SE proportions) undershoots the swipe endpoints by ~1% on an iPhone 15 Pro simulator (393x852), making dismissal less reliable. Bump to 390 852. - ci_run_e2e.sh (Android): add --reporter expanded to match the iOS runner (ci_run_e2e_ios.sh already has it), so a failing suite's log artifact shows per-test detail instead of a single "Some tests failed" line. - e2e_start.dart: add a 180s backstop `.timeout(...)` around each startWithRetry attempt so callers without their own Dart-level timeout (e.g. flow_dismiss_test.dart) can't hang for the full ~600s CI watchdog budget. A caller's own tighter timeout still wins. Left as the default TimeoutException (no "timed out" in its message) so it does NOT match _kNetworkNeedles and get silently retried like a network hiccup — a real hang should surface as a failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The CI autopsy showed the tap driver legitimately needs ~44s to find the CTA on a cold runner simulator while the interceptor-fire budget was 40s. Present 20s->60s, fire 40s->120s, outcome 90s->180s, host poll 180s->420s (each still bounded well under the 600s per-attempt watchdog). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…K path Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reconciliation after rebasing onto 6.1.0 (#153). Two things, both deliberate. ## The pull_request trigger stays This branch dropped `on: pull_request` from e2e-android.yml and e2e-ios.yml, leaving workflow_dispatch + nightly. The original reasoning holds for its time ("while the v6 migration is in flight"), but experience since says keep it. On #153 the PR-triggered E2E caught a deterministic 3/3 failure no unit test and no local run saw: a listener that never fired because `appHandlesRedemptionAlert` defaults to false, so the SDK waits for a popin nobody acknowledges in CI. It also produced the only proof that `proxy(null)` and `anonymousUserId` work against the real backend. Without the trigger, that class of bug surfaces on the nightly — i.e. after merge. Everything else from this branch is kept, cron at 05:00 UTC included. ## #153's two suites, renumbered per runner The rebase kept this branch's version of both runner scripts, so #153's suites were dropped from them while their test files stayed (they came from main). Added back, respecting each runner's own structure: - Android: `Suite 13/14` and `Suite 14/14`, after this branch's 12. - iOS: `Batch 8/9` and `Batch 9/9`. This branch restructured the iOS runner from 12 individual suites into 7 consolidated batches plus an xcodebuild-driven StoreKit batch, so a naive renumber would have been wrong — these two stay standalone rather than folded into a batch, because each starts the SDK with a distinct proxy/identity state and `start()` only runs once per process. Verified: 360 Dart tests, analyze clean, `dart format` clean under Dart 3.5.4, both workflow files parse, both runner scripts pass `bash -n`, and #153's `minSdk = 23` pin survived the rebase. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
040f0b7 to
abb0008
Compare
The iOS E2E job failed on 'Batch 8/9: 6.1.0 identity + cleared proxy + redemption listener', but the suite log holds only 'loading' and the build warnings — the app never launched. That is the known simulator launch flake, which the retry loop is designed to absorb; Batch 1 hit the same 600s timeout on this run and passed on attempt 2. What actually failed is the wall clock. Batches 1-7 took 23 minutes including one retry, Batch 8's first attempt burned its full 600s, and the job's 60-minute cap cut attempt 2 before it finished. Batch 9 never ran. So a passing suite looked like a failing one. This branch consolidated 12 individual iOS suites into 7 batches precisely to fit 60 minutes; #153 then added two more. 90 gives the retry loop room to do its job instead of being truncated by the cap. The same cap is raised on Android for symmetry: it now runs 14 suites. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diagnostic du check rouge — ce n'était pas une suite qui échoue
Puis rien pendant 600 s. L'app n'a jamais démarré — pas un Ce qui a réellement échoué, c'est le mur de temps :
Donc une suite qui passe ailleurs a été rapportée comme rouge parce qu'on ne l'a jamais laissée finir. Preuve indépendante : ces deux suites sont vertes 9/9 sur Correctif :
|
Objectif
Rendre la Phase 2 E2E du SDK Flutter v6 verte et fiable : couverture des fixes Go/No-Go (PR #136), déblocage des scénarios S2/S5/S6/S7, et fiabilisation des jobs CI E2E (hang Android résolu à la racine).
Nouvelles suites (toutes en hard gate CI)
modal_dismissible_ios_test.dartmodal(dismissible:false)ignore le swipe + couverture du swipe interactif (dismissible:true)re_display_test.dart/re_display_ios_test.dartflow_dismiss_test.dart/flow_dismiss_ios_test.dartintegration_test_flow, flowintegration_test_v_1, preuve preload +PLYPresentation.flowId)interceptor_actions_ios_test.dartPLYInterceptResult.failedvs.notHandled, outcomes distincts (AppLifecycleState), ordre des callbacks assertépurchase_restore_ios_test.dart+Configuration.storekit+ hostRunnerIntegrationTests.purchased) + restore — voir « Limites »purchase_restore_android_test.dartDurcissement des suites existantes
helpers/e2e_start.dart: retry 3× + backoff sur erreurs réseau/TLS uniquement (flake TLS destart()en CI), adopté par les 22 suites.deeplink_cold_start_test.dart: assertion explicitePRESENTATION_OPENEDABSENT ; ordreDEEPLINK_OPENED/PRESENTATION_LOADEDvolontairement non contraint (divergence iOS/Android).inline_paywall_test.dart: suite orpheline désormais câblée en CI.CI (
e2e-android.yml,e2e-ios.yml,ci_run_e2e*.sh)flutter-version3.24.x → 3.44.0 (post-AGP 9/chore(android): validate Flutter 3.44 with AGP 9 #130,compileGroovyéchouait en 7 s maisintegration_testattendait son timeout de 12 min ×3 → job tué à 60 min).::group::SUITE <name> attempt <n>+ code retour + durée pour rendre tout futur hang diagnosticable.|| true/continue-on-errorajouté (et purge de ceux des scripts touchés).interceptor_actions).Preuves locales (sweep complet post-rebase)
fail=0(5 m 33, tentative 1 partout).storekit→ marqueur expliciteS7-iOS BLOCKED (Apple FB22237318)non-gating (comportement conçu) ;modal_dismissible(testdismissible:true) FAIL 3/3 local → arbitrage CI (sim iOS 26.5 local suspect, couche Flutter innocentée par revue de la sérialisation) ;interceptor_actions→ bug driver corrigé dans cette PR (ellipse Unicode +$SUITE_LOGnon bracé sousset -u).Limites connues (dites honnêtement)
.purchasedn'a jamais été observé sur la machine locale — bug Apple ouvert (SKTestSessionSKInternalErrorDomain Code=3, FB22237318, Xcode 26.5+, reproduit dans [Only on 26.3/26.4][in_app_purchase_storekit] iOS native tests failing with timeouts, SKInternalErrorDomain, and crashes flutter/flutter#184678). La suite est câblée et stricte (aucun échappatoire fake-green) ; le runner CI (Xcode plus ancien) peut ne pas être affecté.onPresentedréassigné sur handle post-preload ignoré ;PLYEventPropertiessans champs flow ; fallback_outcomeFromMapsur l'objet caché.Résultats CI sur le HEAD (
6cd504d)storekit-iosPASS tentative 1 : le Xcode du runner n'est pas affecté par FB22237318, S7 iOS est donc réellement prouvé en CI (.purchased+ restore).modal-dismissiblePASS (arbitrage : le FAIL local était la flakiness sim 26.5).dismiss-iosvert en tentative 2 (retry nominal).fail=0— après correction d'une cause CI-only : un dialogue ANR du launcher restait au premier plan de l'AVD froide et volait tous les inputs uiautomator (prouvé par IDs de fenêtres identiques inter-suites) ; les drivers force-stoppent désormais le package ANR à chaque poll.🤖 Generated with Claude Code