mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
766d42e124
* fix(ios): report an unobserved text commit instead of a partial success awaitSynthesizedFirstResponderCommit returned Void, so its three exits were indistinguishable to the caller: the expected text committed, the app transformed the input, or the 3s deadline expired with a strict prefix still outstanding. The caller returned dispatched-with-no-failure in all three, and `type` answered ok with textEntryRoute synthesized-first-responder over a field holding part of the requested text. The wait now returns a SynthesizedTextCommitOutcome and an expired deadline becomes TEXT_INPUT_COMMIT_NOT_OBSERVED, whose hint points at fill rather than a type retry — type appends, so retrying it concatenates onto whatever committed. The tail is still not re-synthesized: #1676 rejected that because a stalled prefix cannot be told apart from a suffix still queued, so repair double-posts. Reporting is what the runner does instead. typeIntoCurrentTarget loses its `dispatched` flag, which was exactly `failure == nil` and could not express the new state — characters posted, commit unconfirmed, command must refuse. Failure is now the single discriminator. The decision moves behind an injected clock/observer so the deadline branch runs in the macOS host lane on every PR instead of needing a simulator. Refs #1874, #1844 * fix(ios): close false-failure windows in the commit wait Adversarial review found two deterministic false failures in the wait added by the previous commit, plus a message that asserted a field state never read. The deadline was checked before observing, so a commit landing during the final poll sleep was condemned as never observed — under exactly the loaded-host timing the wait exists for. The check now runs after an observation, so the last thing before condemning is a read. `treatingPlaceholderAsEmpty` maps a value equal to the field's placeholder to "", a prefix of every expected value. `type "0.00"` into a field placeheld "0.00" committed instantly, read as pending for the full 3s, and failed. The observation now settles on an exact raw match; the normalized read still drives the prefix walk. The outcome-to-failure mapping moves to textEntryFailure(forCommitOutcome:) so the branch the command refuses on is pinned by a test rather than living only in a ternary. `.unobservable` staying a success is what keeps `type "...\n"` working, and it now has an assertion. Message and hint no longer claim the field holds a partial value: under both fixed windows it may hold all of it. The docs sentence no longer implies every text-entry route verifies its result — the replacement and keyboard-visible routes have no resolvable element to observe and are unchanged. Refs #1874, #1844 * test(ios): pin the placeholder fix at the boundary it actually lives on Review [P1]: testValueEqualToThePlaceholder… injected an observe closure that already returned "0.00", so it never supplied the normalized "" that causes the failure. The raw-value short-circuit lived in the production observe closure, which that test bypassed entirely — reverting the fix left it green. The raw-exact/normalized-prefix choice moves into commitObservation, and the test drives it with (raw: "0.00", normalized: "", expected: "0.00"). Reverting commitObservation to always return the normalized reading now fails the exact-match assertion. normalizedValue is a closure rather than a value so an exact match still costs one accessibility read instead of two, on a path that polls every 20ms for up to three seconds; a second test pins that laziness. The old test is deleted rather than kept: its remaining assertion (an exact match settles without polling) is already covered by testSynthesizedCommitStopsAtTheFirstSettledObservation. Refs #1874 * fix(ios): never treat placeholder equality as commit evidence Review [P1]: an empty text field renders its placeholder AS its accessibility value, which is why editableTextValue(treatingPlaceholderAsEmpty:) classifies that value as empty. The previous revision's raw-exact short-circuit therefore matched BEFORE anything committed whenever the requested text was the placeholder: `type "0.00"` into a field placeheld "0.00" settled on the first read and returned ok with zero characters delivered — reintroducing the success-misdescribes-the-device failure this PR exists to remove. The state is structurally indeterminate. element.value is identical whether the placeholder is rendering or the committed text happens to equal it, and placeholderValue does not disambiguate, so no read resolves it and waiting the deadline out discovers nothing. placeholderMakesCommitUnobservable detects it up front and reports the commit unobserved, which the caller refuses on. commitObservation is deleted rather than narrowed: the raw match was only ever consulted in this exact case, and in this exact case it is not evidence. The failure message drops its deadline reference — this refusal never waits. Refs #1874 * fix(ios): scope the placeholder refusal to an empty baseline Review [P1]: the guard took only the placeholder and the expected text, so it refused any append whose result happened to equal the placeholder. Value "0" + `type ".00"` against placeholder "0.00" was refused before a single read, even though the non-empty pre-dispatch value proves the placeholder is not what is rendering and a later "0.00" is genuine commit evidence. The baseline is what decides it, so it is now an input. placeholderCommitEvidence returns three states rather than a boolean: normalRead expected differs from the placeholder; the placeholder never enters into the observation indistinguishable expected IS the placeholder and the field was empty, so the placeholder was what rendered and no read can resolve it rawValueIsEvidence expected IS the placeholder but the field held content, so a raw match is real Only .indistinguishable refuses, and it still refuses before the wait, since no read resolves it. .rawValueIsEvidence reaches the observation and settles on the raw match, which the normalized read would otherwise hide. commitObservation returns for that third state, now scoped by evidence rather than applied unconditionally as in the revision that made raw equality a false success. Both readings stay closures, so normalRead — the ordinary case — never pays for the raw read. Refs #1874 * fix(ios): keep placeholder-equal commits conservative