Can't access elements in detox after react-native firstly load - react-native

I'm running detox on app that started from native app and now working as a combination of react native app and native code.
I'm using iOS simulator to check the test.
My app is starting in the iOS native side, and the detox tests running on it and get the elements(all works fine), but as we go with the flow of the app, when we firstly load the bundle(index.js- for running the react-native side), we can't access the elements(in the native side) with detox, and the tests failed.
In the native side(iOS) i entered the buttons with - accessibilityIdentifier
describe('Previews', () => {
beforeAll(async () => {
await device.launchApp({ permissions: {notifications: 'YES',location:'always'}});
});
it('should tap on acceptButton', async () => {
await element(by.id('acceptButton')).tap();
});
it('should tap on enable Notifications button', async () => {
await element(by.id('enableNotificationsButton')).tap();
});
// Here the bundle of the react-native loading, and the detox can't find 'editButton'
// 'editButton' it's still in the native side
it('should click on editButton', async() => {
await element(by.id('editButton')).toBeVisible();
});
});
Getting this warning:
detox[73960] WARN: [Client.js/PENDING_REQUESTS] App has not responded to the network requests below:
(id = 6) invoke: {"type":"action","action":"tap","predicate":{"type":"id","value":"continueFaceIdButton"}}
Unresponded network requests might result in timeout errors in Detox tests.

Related

How to properly test if a Toast has been shown in react native using native base?

I am trying to write a test that checks if the screen is showing a Toast with an error message. The test passes, but there is a warning:
console.error
Warning: You called act(async () => ...) without await.
This could lead to unexpected testing behaviour, interleaving multiple act calls
and mixing their scopes. You should - await act(async () => ...);
The screen is working fine, I am just learning how to write tests. This is my test:
it('shows error correctly', async () => {
mockAxios.get.mockRejectedValueOnce(new Error('Async error'))
const { queryByText } = renderWithRedux(<DiscoverScreen />)
await waitFor(() => {
expect(queryByText(ErrorMessages.GeneralErrorToast)).not.toBeNull()
})
await waitForElementToBeRemoved(() => queryByText(ErrorMessages.GeneralErrorToast), { timeout: 5000 })
})
What am I not doing right? Definitely there is an issue with react native testing, because there are problems for certain async querying, especially when you have several of them. I found that here: https://github.com/callstack/react-native-testing-library/issues/379#issuecomment-720734366
I am using native base for showing the Toast, which is using Animated I think. Should I use jest.useFakeTimers() and how?
After researching how the Toast in native base works (this could be done when you open the source code in github - https://github.com/GeekyAnts/NativeBase/blob/master/src/basic/ToastContainer.js), I found that it uses Animated.timing.
So I had to find out how to deal with react native animations in tests. That article had a solution that worked for me: https://medium.com/#joncardasis/react-native-how-to-test-components-implementing-animated-with-jest-8cabb5fc2730
After I added the code in my jest setup file, this is how my test looks:
global.withAnimatedTimeTravelEnabled(async () => {
const { queryByText } = renderedComponent
await waitFor(() => {
expect(queryByText(ErrorMessages.GeneralErrorToast)).not.toBeNull()
})
global.timeTravel(Constants.ErrorToastDelay)
expect(queryByText(ErrorMessages.GeneralErrorToast)).toBeNull()
})
It works and now the test passes with no warnings!
One little adjustment in my jest configuration was also missing. I had to add this:
"testEnvironment": "jsdom"
I hope this could help someone else, too!

Launch React Native App from Background on Notification Received

I am working on react native application and my requirement is to launch specific screen on Notification received.
Notification is working fine in all state i.e. Foreground or background.
I am using below library:
react-navigation
#react-native-firebase/messaging
react-native-push-notification
Below is the code for handling notification :
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
let text = {
title: remoteMessage.data.title,
body: remoteMessage.data.body,
}
notif.cancelAll();
notif.localNotif(text);
NavigationService.navigate('Contact', { userName: 'Lucy' })
});
Also wrote a navigation code on notification received method referring
https://reactnavigation.org/docs/4.x/navigating-without-navigation-prop
I can see app navigate to particular screen but its not launching app.
Can anyone help me with this?

Admob rewarded video is showing but not clickable on React native app

I'm trying to use Admob Rewarded Video in my React Native app.
Here is the code I am testing:
const advert = firebase.admob().rewarded('ca-app-pub-3940256099942544/5224354917');
const AdRequest = firebase.admob.AdRequest;
const request = new AdRequest();
// Load the advert with our AdRequest
advert.loadAd(request.build());
advert.on('onAdLoaded', () => {
console.log('Advert ready to show.');
});
advert.on('onRewarded', (event) => {
console.log('The user watched the entire video and will now be rewarded!', event);
});
if (advert.isLoaded()) {
advert.show();
}
else {
advert.on('onAdLoaded', () => {
console.log('Advert ready to show.');
advert.show();
});
}
This is perfectly working when I execute it from the main screen of the app (root of the navigator), but not anymore after I move to an other screen by calling this.props.navigation.navigate('ScreenB');:
The ad is showing normally but I cannot skip, open or close it... The app gets stuck in this state.
I am currently testing on iOS simulator and I use the following config:
"react": "16.6.0-alpha.8af6728", "react-native": "^0.57.2",
"react-native-firebase": "^5.1.0"
Am I missing something or is it a bug?
Evertime when you want to show a banner you need to rebuild it again.
A single rewarded video instance can only be shown once. If you want
to display another, create a new one.
advert.loadAd(request.build());

Detox tests are breaking

This is regarding detox e2e tests.
I am running my tests, each under an it('xx', async => { await...});
The tests are scripted in such a way that 1st test would log in, 2nd test would do something on homepage, 3 rd test would navigate from homepage to other pages and so on.
The issue here is, as soon as my first test executes, the app is getting logged out and all the consecutive tests are failing.
But when I include all steps(right from login to the desired functionality) in every test, the suite is working properly.
I would like to know why is this happening. Is there any connection with async function?
One of the gotchas for using Detox is that the sample test specification uses a beforeEach and there is a tendency to copy verbatim examples that we are given, some times missing out things that either need to be removed or should be added.
In this particular case in the beforeEach there is the call await device.reloadReactNative(); this command reloads the device as if you had pressed CMD+R (on iOS) or RR (on Android). This means that items that have been saved to state are lost and the application is pretty much returned to its initial state.
Check your code for the offending line, you can see it in example provided below. If you remove this line then it will stop reloading React Native on your device before each test.
example.spec.js
https://github.com/wix/Detox/blob/master/examples/demo-react-native/e2e/example.spec.js
describe('Example', () => {
beforeEach(async () => {
await device.reloadReactNative(); // <- this is the problem
});
it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});
it('should show hello screen after tap', async () => {
await element(by.id('hello_button')).tap();
await expect(element(by.text('Hello!!!'))).toBeVisible();
});
it('should show world screen after tap', async () => {
await element(by.id('world_button')).tap();
await expect(element(by.text('World!!!'))).toBeVisible();
});
});

How can I clear redux state after finishing test

I just tried Detox for e2e testing UI Automation. This library is really good. I just want to ask if there is any way for me to clear redux state after finishing a test suite. In my case, I want to clear my shopping cart
Here's my code:
import CartActions, {reducer, INITIAL_STATE} from '../../App/Redux/CartRedux'
describe('Add menu from home screen and do checkout', () => {
it ('should navigate to meal detail screen after tapping first meal', async () => {
await waitFor(element(by.id('MealListSlider'))).toBeVisible().withTimeout(7500)
await element(by.id('homeMenu_0')).tap()
await expect(element(by.id('MealDetailScreen'))).toBeVisible()
})
it ('should show shopping cart button after add item to cart', async () => {
await element(by.id('addToCart_')).tap()
await expect(element(by.id('shoppingCartButtonOnMealDetail'))).toBeVisible()
})
it ('should navigate to checkout screen', async () => {
await element(by.id('shoppingCartButtonOnMealDetail')).tap()
await expect(element(by.id('SingleOrderCheckoutScreen'))).toBeVisible()
})
afterAll(() => {
reducer(INITIAL_STATE, CartActions.userEmptyingCart())
})
})
I'm using Jest as my test runner. Please help me if I'm doing wrong. Thanks
Detox: 8.0.0
React Native: 0.47.2
Device: iOS
Xcode: 9
macOS: High Sierra
Probably you got confused regarding in which context Detox is running. The code of your Detox tests is running in the context of your computer and OS (like any Node.js app you run), but your application code is running on device/emulator/simulator.
The bottom line is that you cannot simply access your application code from the detox.
Normally, you would use device API methods like launchApp or reloadReactNative.
P. S. Random suggestion. If you indeed want to mess with Redux reducers from Detox, consider shipping a sort of "developer mode screen" with the development build of your app, access it from Detox and tap on a button you create for a specific use case. Turn on your imagination and creativity - maybe a specific keyboard listener will suffice (instead of an extra screen). But anyway, maybe stick to reloadReactNative - usually, it is enough.