Appium React Native Not Ready for Text Input

Viewed 1417

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.

2 Answers

You should NOT specify accessibilityLabel for ios separate props for ios and android like so try next workaround:

export default function testID(id) {
    return Platform.OS === 'android'
        ? {
            accessible        : true,
            accessibilityLabel: id,
        }
        : {
            testID: id,
        };
}

and then

<TextInput
    {...otherProps}
    {...testID('some-testID')}
/>
Related