Repository navigation
Description
Summary
On an iOS simulator, fill() into a React Native TextInput with secureTextEntry always fails with ENGINE_FAILURE: perform fill failed: text entry verification failed. The runner reads the masked field value back ("••••••••") and compares it with the typed text, so the check can never pass. This happens with a credential password and with a plain string, so it is not specific to secrets. After the failure, the show/hide toggle that overlays the field's right edge has been flipped, which suggests the runner's repair path tapped the field again.
This blocks every password sign-in on iOS devices: fill(credentials.user(...).password) and type_secret cannot succeed.
Environment
- e2e 0.17.0, @e2e-dev/mobile 0.9.2, agent-device 0.21.20 (also seen on e2e 0.15.1, @e2e-dev/mobile 0.8.1, agent-device 0.21.18)
- iOS 27.0 simulator (iPhone 18 Pro), Xcode 27.0 (27A266a), macOS 27.0.1
- React Native 0.86.3, Expo SDK 57, Release build for the simulator
Reproduction
The field is a standard React Native TextInput with secureTextEntry, wrapped together with a show/hide Pressable that sits over the input's right edge:
<View style={{ position: 'relative' }}>
<TextInput accessibilityLabel="Password" secureTextEntry={!visible} textContentType="none" autoComplete="off" style={{ paddingRight: 48 }} />
<Pressable accessibilityRole="button" accessibilityLabel={visible ? 'Hide password' : 'Show password'}
onPress={() => setVisible((v) => !v)} style={{ position: 'absolute', right: 0, top: 0, bottom: 0, width: 48 }} />
</View>Test:
await expect(screen.getByRole('button', 'Show password')).toBeVisible(); // passes
await screen.getByLabel('Password').fill('not-a-secret-123'); // failsResult (2 of 2 runs, same for a credential password in 2 of 2 runs):
ENGINE_FAILURE: perform fill failed: text entry verification failed: expected "not-a-secret-123", observed "••••••••"
With a credential password the message reads expected "<secret:...>", which is only the redacted form of the same comparison.
Screen at failure (from failure/screen.txt):
other "Password"
textbox "Password" [focused]
button "Hide password"
Observations
- e2e's tree reports the field as a plain
textbox, withoutsecureorinputPurpose: 'password', so XCTest does not seem to classify it assecureTextFieldhere. - In agent-device's runner,
editableTextValuereturnsnilfor.secureTextField, which skips verification. A masked field that XCTest reports as.textFieldfalls through to theobservedText == expectedTextcomparison inverifyTextEntry(RunnerTests+TextTyping.swift), which a masked value can never satisfy. 16 characters typed were read back as 8 bullets, so even a length check would not help. - The toggle reads "Show password" before the fill and "Hide password" after it. Something in the fill (the repair or retry after the failed verification, as far as we can tell) tapped inside the field's frame where the toggle overlays it, which also leaves the field showing its text.
Expected
A fill into a masked field either skips the value check (as already done for .secureTextField) or treats a value made only of bullet characters as unverifiable, and never taps outside the field's own text area while repairing.
Possible direction
Treat an observed value that consists only of • (U+2022) as unverifiable (verified: nil) in verifyTextEntry, and/or use the element's secure trait rather than only elementType == .secureTextField.
Activity
nyiramisigaroleocadie85-dot commentedon Oct 6, 2026
This command is a good how to create in web in have raliationship
fix(ios): treat a bullet-only field value as unreadable during fill
fix(ios): treat a bullet-only field value as unreadable during fill (#…
okwasniewski commentedon Oct 22, 2026
hey, this should be already fixed in latest release, thanks for reporting!