fix(iOS): integration test flakiness#152
Merged
Merged
Conversation
…listener attachment
The text-locator and reader-status channels buffer their latest event so the first emission isn't dropped when the EPUB platform view fires before the EventChannel onListen handshake completes. Clear those buffers in closePublication() so a stale locator/status from a closed publication is never replayed to the next one, and reset the lazy Dart stream fields so the next getter access re-establishes the native onListen handshake before the next book's widget mounts. Also document the per-channel buffering contract on EventStreamHandler (buffer state-like streams, never event-like/sentinel streams) and make onReaderStatusChanged a broadcast stream so multiple subscribers are supported. Android and Web are structurally immune to this race and need no change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ddfreiling
added a commit
that referenced
this pull request
Jun 10, 2026
Brings the Spotlight decoration feature (#142), iOS event buffering (#152), and the page_width-120 reformat from main into the web-TS refactor branch. Conflict resolutions: - Spotlight ported into the refactored web modules: body-dimming + CSS Custom Highlight restore lives in decorations/decorationOverrides.ts; group activation lives in decorations/DecorationController.ts. The thin ReadiumReader facade delegates to DecorationController (no direct spotlight imports). - FlutterTTSNavigator / FlutterEpubNavigator keep the shared navigators/locatorEnrich.ts (flattenToc / enrichWithTocHref) instead of main's per-module duplicates. - Navigators drop the focus-stealing window.focus() in positionChanged; FlutterWebPubNavigator gains the same 200ms trailing-edge text-locator debounce as EPUB. - Dart conflicts (reader_decoration isActive, method-channel formatting, example spotlight segment, integration-test list-stable waits) take main. - Web bundle regenerated from web/src and copied into example/web. Stale web/_scripts cleanup: - bin/install: install web deps from the plugin root where package.json lives. - Removed the orphaned readiumReader.js.LICENSE.txt (a Terser artifact the dev build no longer emits; all its module paths were stale). - Updated stale path/name references in a few source comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The iOS integration tests were intermittently failing on CI (e.g.
goToLocator round-trips back to a saved position— "No initial textLocator emitted", and the TTS test with aTimeoutException).Root cause (iOS-specific): the
FlutterEventChannelonListenhandshake is asynchronous and can complete after the freshly-mounted EPUB platform view has already fired its firstlocationDidChange. With a nileventSinkat that moment, the firsttext-locator/reader-statusevent was silently dropped — so a test (or app) subscribing around mount time never saw the initial locator.Android and Web are structurally immune: Android only emits the first locator after
onPageLoaded(well after the Dart handshake) and dispatches via a coroutine, and Web uses in-processStreamControllers with no channel handshake.Fix
EventStreamHandlerbuffers the latest event when no Dart subscriber is attached and flushes it ononListen. Onlytext-locatorandreader-statusopt in;timebased-stateanderrormust not buffer (they carry one-shot / sentinel values such as the.nonestate emitted on close).closePublication()clears the buffered locator/status so a stale value from a closed publication is never replayed to the next one.closePublication(), so the next getter access re-establishes the nativeonListenhandshake before the next book's widget mounts.onReaderStatusChangedis now a broadcast stream — supports multiple subscribers (previously single-subscription).EventStreamHandler: buffer state-like streams, never event-like / sentinel streams; clear buffered streams on close.Testing
All 22 integration tests pass on iOS, including the previously-flaky
goToLocator round-trips,initialLocator restores, andopens EPUB and enables TTS.No Android or Web changes.