I've recently switched over to Appium + webdriverIO for E2E testing. Everything is working pretty well except for one test case relating to text input.
Basically, the component under test is a login screen that uses redux-form for form management. I'm constantly getting the error "'"login-field" Other' is not ready for a text input. Neither the accessibility element itself nor its accessible descendants have the input focus". The components are as follow:
SignInScreen.tsx
export class SignInScreen extends React.Component<any> {
render() {
const { handleSubmit, submitting, style } = this.props;
return (
<View style={style}>
<View>
<View>
<Field
name="login"
component={Input}
accessibilityLabel="login-field"
testID="login-field"
/>
<Field
secureTextEntry
name="password"
component={Input}
accessibilityLabel="password-field"
testID="password-field"
/>
</View>
</View>
</View>
);
}
}
Input.tsx
export class Input extends React.Component {
render() {
const {
input,
meta: { error, active, focused },
accessibilityLabel,
testID
} = this.props;
const showError = !active && !!error && !focused;
const errorText = "ERROR!"
return (
<View style={[style, styles.container]}>
<TextInput
autoCapitalize="none"
value={input.value}
onChangeText={input.onChange}
onFocus={input.onFocus}
onBlur={input.onBlur}
accessibilityLabel={accessibilityLabel},
testID={testID}
/>
<View style={{height: 30}}>
{showError && (
<Text>{errorText}</Text>
)}
</View>
</View>
);
}
}
SignInScreen.test.ts
describe('Sign In Screen Test', () => {
let client;
beforeAll(async () => {
// set up code
});
afterAll(async () => {
// tear down code
});
it('Can login', async () => {
const loginField = await client.$('~login-field');
await loginField.setValue('test@gmail.com'); // error here
const passwordField = await client.$('~password-field');
await passwordField.set('password' + '\n');
});
});
I do realize that the test cases work when I either add an additional <TextInput /> on top of the existing <TextInput /> component in the Input.tsx component as follows:
Input.tsx
export class Input extends React.Component {
render() {
const {
input,
meta: { error, active, focused },
accessibilityLabel,
testID
} = this.props;
const showError = !active && !!error && !focused;
const errorText = "ERROR!"
return (
<View style={[style, styles.container]}>
<TextInput />
<TextInput
autoCapitalize="none"
value={input.value}
onChangeText={input.onChange}
onFocus={input.onFocus}
onBlur={input.onBlur}
accessibilityLabel={accessibilityLabel},
testID={testID}
/>
<View style={{height: 30}}>
{showError && (
<Text>{errorText}</Text>
)}
</View>
</View>
);
}
}
or I remove the fixed height in the View component that nests the error message as follows:
Input.tsx
export class Input extends React.Component {
render() {
const {
input,
meta: { error, active, focused },
accessibilityLabel,
testID
} = this.props;
const showError = !active && !!error && !focused;
const errorText = "ERROR!"
return (
<View style={[style, styles.container]}>
<TextInput
autoCapitalize="none"
value={input.value}
onChangeText={input.onChange}
onFocus={input.onFocus}
onBlur={input.onBlur}
accessibilityLabel={accessibilityLabel},
testID={testID}
/>
<View>
{showError && (
<Text>{errorText}</Text>
)}
</View>
</View>
);
}
}
So what gives? I'm really lost as to what's causing Appium to not pick up the input focus without making the above adjustments.