React-native-navigation: BackButton testID not set when passed to setDefaultOptions

Created on 13 Oct 2020  ·  11Comments  ·  Source: wix/react-native-navigation

Issue Description

Having issues selecting the back button when setting a testID in setDefaultOptions.

Have also tried setting the testID in the component itself and the element still isn't being found.

Noticed there was meant to be a fix in 6.9.0

Steps to Reproduce / Code Snippets / Screenshots

Give backButton a testID like this:

  Navigation.setDefaultOptions({

    layout: {
      orientation:              ['portrait'],
      backgroundColor:          Colors.background,
      componentBackgroundColor: Colors.background
    },

    modalPresentationStyle: 'pageSheet',

    bottomTabs: {
      titleDisplayMode: 'alwaysShow',
      backgroundColor:  '#1C2432',
      hideShadow:       false,
      elevation:        15
    },

    bottomTab: {
      textColor:         '#4b747c',
      selectedTextColor: Colors.accentBright,
      fontSize:          8,
      fontFamily:        'Nunito-Regular'
    },

    topBar: {
      title: {
        fontFamily: 'Nunito-Bold',
        fontSize:   18,
        color:      '#fff'
      },
      backButton: {
        icon:      require('../assets/images/arrowBack.png'),
        color:     '#fff',
        showTitle: false,
        testID:    'navigation-back'
      },
      buttonColor: {
        color: '#fff'
      },
      background: {
        color: Colors.background
      }
    },

    statusBar: {
      backgroundColor: Colors.background2,
      style:           'light'
    }

  })

Then trying to select the testID: 'navigation-back' in my test like:

Using Appium 1.18.2 with webdriverio 6.5.2

  it('should be able to navigate back', async () => {
    const back = await $('~navigation-back')
    await back.waitForDisplayed({ timeout: 5000 })
    await back.click()
    const screen = await $('intro-choice-screen')
    const visible = await screen.waitForDisplayed({ timeout: 5000 })
    expect(visible).toEqual(true)
  })

Results in ~navigation-back not being found.

Applying a testID to bottomTab works as expected, so it doesn't seem to be an issue with Appium.


Environment

  • React Native Navigation version: 7.0.0
  • React Native version: 0.63.2
  • Platform(s) (iOS, Android, or both?): iOS (not tried on Android)
  • Device info (Simulator/Device? OS version? Debug/Release?): iOS Simulator - Debug & Release
requires reproduction 🏚 stale

All 11 comments

Hi @calumgould can you see if not using a custom icon successfully registers the back button test id?

Hi @calumgould can you see if not using a custom icon successfully registers the back button test id?

Hi @jinshin1013, have just tried this and unfortunately it still has the same outcome.

Hi @calumgould, I've just tried your config and it seems like it works with the default button: https://www.dropbox.com/s/1rbzxo4w1dwzezs/Screenshot%202020-10-25%20at%2022.57.53.png?dl=0. Accessibility identifier is there.

Also, this is a custom back button which works: https://www.dropbox.com/s/tg9s7z0o8qwdhet/Screenshot%202020-10-25%20at%2023.07.21.png?dl=0

I've also ran a test with detox with your test case and everything works fine. Can you upgrade to 7.1.0 or even 7.2.0 ?

Hi @mateioprea, I appreciate you taking the time to look into this further. I have upgraded to 7.2.0 and booted up the Appium inspector to get a better look at what is going on.

I've now identified a consistent behaviour for how testIDs are being assigned to the back button. The testID passed to setDefaultOptions above has no effect, and the testID that is being read by Appium can be one of two things:

Outcome 1:

  • TestID is whatever the name of the title prop is from the screen that it would navigate back to, e.g.

Options passed to previous screen with topBar title 'Wallet':

{
      topBar: {
        title: {
          text: 'Wallet'
        },
        visible: false,
        height:  0
      },

      bottomTab: {
        text:              'WALLET',
        icon:              require('../../../assets/images/walletsUnactive.png'),
        selectedIcon:      require('../../../assets/images/wallets.png'),
        selectedIconColor: (Platform.OS === 'android') ? Colors.accent : undefined,
        testID:            'navigation-wallet'
      }
}

Now pressing on an item in this screen, brings up the backButton in the next component. This is what the Appium inspector shows for this: (notice the correct assignment of the bottom navigation)

Screenshot 2020-10-27 at 10 33 35

Outcome 2:

  • TestID is set to default value of 'Back' when no title prop was provided in the previous screen. e.g.

Options passed to previous screen without a title prop:

{
      topBar: {
        visible: false,
        noBorder:  true,
        elevation: 0
      },

      bottomTab: {
        text:              'SETTINGS',
        icon:              require('../../../assets/images/settingsUnactive.png'),
        selectedIcon:      require('../../../assets/images/settings.png'),
        selectedIconColor: (Platform.OS === 'android') ? Colors.accent : undefined,
        testID:            'navigation-settings'
      }
}

Following the same steps as above, the Appium inspector now shows:

Screenshot 2020-10-27 at 10 50 45

As you can see here, the testID provided to backButton isn't being applied at any stage, even passing a testID directly to backButton options in the component has no effect.

The testIDs for the bottom nav are applied correctly and recognised by the inspector.

Running the previous test, replacing the id selector with whatever testID was automatically assigned results in passing tests:

  it('should be able to navigate back', async () => {
    const back = await $('~Wallet')
    await back.waitForDisplayed({ timeout: 5000 })
    await back.click()
    const screen = await $('wallet-screen')
    const visible = await screen.waitForDisplayed({ timeout: 5000 })
    expect(visible).toEqual(true)
  })

However, this obviously isn't an ideal solution and one would expect the testID to be consistent across all screens.

@calumgould can you fork this repo and provide a failing playground e2e test? Either with Detox or Appium. This way we can investigate further and fix the actual issue, if any.

@mateioprea forked the repo and here you can see from the Appium inspector, the exact same issue to what I am having.

https://github.com/calumgould/react-native-navigation

Didn't have time to go through the whole Appium setup, but all that's needed to view what accessibility ids are being provided is to use the appium desktop inspector, make a build and provide config like this:

{
  "platformName": "iOS",
  "deviceName": "iPhone 12 Pro",
  "orientation": "PORTRAIT",
  "automationName": "XCUITest",
  "app": "PATH_TO_APP",
  "autoAcceptAlerts": true
}

The only code added to the playground was the backButton options passed to your existing Navigation.setDefaultOptions in Options.ts. I also added the custom back button image from my app.

const setDefaultOptions = () =>
  Navigation.setDefaultOptions({
    window: {
      backgroundColor: Colors.primary,
    },
    layout: {
      componentBackgroundColor: Colors.background,
      orientation: ['portrait'],
    },
    topBar: {
      backButton: {
        icon: require('../../img/arrowBack.png'),
        color: 'black',
        showTitle: false,
        testID: 'navigation-back'
      }
    },
    bottomTabs: {
      titleDisplayMode: 'alwaysShow',
    },
    bottomTab: {
      selectedIconColor: Colors.primary,
      selectedTextColor: Colors.primary,
    },
    animations: {
      ...(useSlideAnimation ? slideAnimations : useCustomAnimations ? customAnimations : {}),
    },
    modalPresentationStyle: OptionsModalPresentationStyle.fullScreen,
  });

The accessibility id is not set by Navigation.setDefaultOptions and is instead just whatever the topBar -> title prop was of the previous screen.

(Previous screen title was Stack -> press button labelled push -> accessibility id of backButton is Stack)

Screenshot 2020-11-03 at 16 58 30

Another screenshot to show the issue repeating

(Now on screen with title Pushed Screen -> press button labelled push -> again accessibility id is title of previous screen instead of the testID assigned earlier)

Screenshot 2020-11-03 at 17 00 19

Hope this helps you investigate further.

An e2e test in the playground app would have been preferable.

@guyca Here's some tests showing the issue https://github.com/calumgould/react-native-navigation

Make a build with yarn appium-build
Test with yarn appium-test

I used the Stack Position: X in the footer to verify these tests

Failing test

describe('failing back button', () => {
  beforeAll(async () => {
    await browser.pause(2000);
  });

  it('try to navigate back', async () => {
    const stackButton = await $('~STACK_BUTTON');
    await stackButton.click();
    const pushButton = await $('~PUSH_BUTTON');
    // Press push button twice
    await pushButton.click();
    await browser.pause(1000);
    // Now on 'Pushed Screen'
    await pushButton.click();
    await browser.pause(1000);
    // Now on 'Pushed 2'
    // Stack position is 2
    const backButton = await $('~navigation-back');
    await backButton.click();
    // Should now be back on 'Pushed Screen'
    const pushFooter = await $('~footer');
    await pushFooter.waitForDisplayed({ timeout: 5000 });
    // Stack position should be 1
    const footerText = await pushFooter.getText();

    expect(footerText).toEqual('Stack Position: 1');
  });
});

Screenshot 2020-11-04 at 11 37 56

Passing test

Now by changing the backButton selector to the topBar -> title of the previous screen, in this case Pushed Screen the test passes.

describe('passing back button', () => {
  beforeAll(async () => {
    await browser.pause(2000);
  });

  it('try to navigate back', async () => {
    const stackButton = await $('~STACK_BUTTON');
    await stackButton.click();
    const pushButton = await $('~PUSH_BUTTON');
    // Press push button twice
    await pushButton.click();
    await browser.pause(1000);
    // Now on 'Pushed Screen'
    await pushButton.click();
    await browser.pause(1000);
    // Now on 'Pushed 2'
    // Stack position is 2
    const backButton = await $('~Pushed Screen');
    await backButton.click();
    // Should now be back on 'Pushed Screen'
    const pushFooter = await $('~footer');
    await pushFooter.waitForDisplayed({ timeout: 5000 });
    // Stack position should be 1
    const footerText = await pushFooter.getText();

    expect(footerText).toEqual('Stack Position: 1');
  });
});

Screenshot 2020-11-04 at 11 37 46

@calumgould We don't officially support Appium. If you believe this is a bug with RNN, please submit a failing Detox e2e test.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you believe the issue is still relevant, please test on the latest version and report back. Thank you for your contributions.

The issue has been closed for inactivity.

Was this page helpful?
0 / 5 - 0 ratings