How can I clear redux state after finishing test - react-native

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.

Related

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

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.

[expo-ads-admob][IOS [Android works]] - Rewarded add closes immediately

So I have ejected my Expo app a while ago and now would like to use the expo-ads-admob package for the Admob integration. I followed the installation instructions and I can successfully show Banners ads in the Android and IOS app. The Android app also worked without a problem using the Reward app. However the IOS app closes the Rewarded app immediately.
The idea is that users view a Rewarded Ad and afterwards a upload with selected information starts.
The problem is that on IOS the ad is loaded, viewable for a second and I can hear the audio start, however it instantly closes and the app continues uploading. The Android app waits until users close the app and after closing starts the submitHandler.
Is anyone familiar with this problem and knows a fix? Below is some code and explanation to hopefully understand it better;
Users click on a upload button and get the choice to pay or do it for free. After selecting free the addUsingAdHandler is started. SetisAddMode closes the modal, after that I get the console log the ad is started and the video is shown.
const addUsingAdHandler = async () => {
setIsAddMode(false)
console.log('Rewared video is shown')
await AdMobRewarded.showAdAsync();
}
On opening the page I add the eventListeners as instructed. The rewardedVideoDidClose listener is used to eventually start the upload.
useEffect(() => {
AdMobRewarded.setTestDeviceID("EMULATOR");
AdMobRewarded.setAdUnitID('ca-app-pub-3940256099942544/5224354917');
AdMobRewarded.addEventListener("rewardedVideoDidRewardUser", () =>
console.log("Reward is binnen gekomen. ")
);
AdMobRewarded.requestAdAsync();
AdMobRewarded.addEventListener("rewardedVideoDidLoad", () =>
console.log("Video did load")
);
AdMobRewarded.addEventListener("rewardedVideoDidFailToLoad", () =>
console.log("Failed to load")
);
AdMobRewarded.addEventListener("rewardedVideoDidOpen", () =>
console.log("Video did open")
);
AdMobRewarded.addEventListener("rewardedVideoDidClose", () => {
console.log("video did close")
submitHandler()
}
);
AdMobRewarded.addEventListener("rewardedVideoWillLeaveApplication", () =>
console.log("Video did leave application")
);
AdMobRewarded.addEventListener("rewardedVideoDidStart", () =>
console.log("Video did start")
);
}, [])
So When pushing the upload for free button the ad is shown for a second however it inmediatly is closed (logs show this using the event listeners) and the upload starts as expected. On my Android emulator the ad is shown until I press the close button as I would expect to have also on my IOS emulator.
Has anyone come across this as well before and/or could guide me in the right direction in fixen this?
React-Native version; 0.59.10
Expo Version; 35.0.0
Expo-ads-admob version: 8.0.0

How to force users to update the app using react native

I have updated my app on app and play store and I want to force my app users to update the new version of app in App store and playstore.
You can check for the App Store / Play Store version of your app by using this library
react-native-appstore-version-checker.
In expo app you can get the current bundle version using Constants.nativeAppVersion. docs.
Now in your root react native component, you can add an event listener to detect app state change. Every time the app transitions from background to foreground, you can run your logic to determine the current version and the latest version and prompt the user to update the app.
import { AppState } from 'react-native';
class Root extends Component {
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (nextState) => {
if (nextState === 'active') {
/**
Add code to check for the remote app version.
Compare it with the local version. If they differ, i.e.,
(remote version) !== (local version), then you can show a screen,
with some UI asking for the user to update. (You can probably show
a button, which on press takes the user directly to the store)
*/
}
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
}
import VersionCheck from 'react-native-version-check';
i have used version check lib for this purpose and approach i used is below. if version is lower i'm opening a modal on which an update button appears, and that button redirects to app store/google play
componentDidMount() {
this.checkAppUpdate();
}
checkAppUpdate() {
VersionCheck.needUpdate().then(res => {
if (res.isNeeded) {
setTimeout(() => {
this.setState({openModal: true});
});
}
});
}
updateApp = () => {
VersionCheck.getStoreUrl({
appID: 'com.showassist.showassist',
appName,
})
.then(url => {
Linking.canOpenURL(url)
.then(supported => {
if (!supported) {
} else {
return Linking.openURL(url);
}
})
.catch(err => console.error('An error occurred', err));
})
.catch(err => {
console.log(`error is: ${err}`);
});
};
For future readers.
If you are using Expo managed workflow, install this package react-native-version-check-expo using yarn add react-native-version-check-expo or npm install react-native-version-check-expo.
Consult the package documentation on Github for usage guidelines.
I'm using react-native-version-check-expo library to achieve this. Working fine for me.
if you are looking for an easy to integrate built in solution. You can use App Upgrade https://appupgrade.dev/ service to force update your mobile apps.
Create new version entry for your app version that you want to update in the app upgrade service and select whether you want to force it or just want to let users know that new version is available.
Integrate your app with App Upgrade using available SDK. Official SDK are available for React Native, Flutter, Expo, Android and iOS(Swift).
The SDK will take care of the rest.
Whenever you want to force upgrade a version just create a version entry in app upgrade dashboard.
You can also integrate using API. Just call the appupgrade api from your app with the required details such as your app version, platform, environment and app name.
The API will return you the details.. that this app needs to be updated or not.
Based on the response you can show popup in your app.You can call this API when app starts or periodically to check for the update. You can even provide a custom message.
API response:
See the response has force update true. So handle in the app by showing popup.
You can find the complete user documentation here. https://appupgrade.dev/docs
Thanks.

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();
});
});

Detox test failed on typeText if text is "long"

Have some interesting problem with detox testing!
All configs are clearly right (cause in another case it wasn't even work, yeah?)
Work with Mocha tests, and yes, it works.
I use redux to work with state and custom components in my main component.
<OpacityInput
idTest="LoginEmailInput"
icon="account"
placeholder="Login..."
value={this.props.email}
onChangeText={this.emailChanged}
/>
So i pass all data by props to component (testID and value for inputs i pass too)...and here is my test (i make two restricted test to catch an error)
it('should find input login', async () => {
await expect(element(by.id('LoginEmailInput'))).toBeVisible()
})
it('should type text login', async () => {
await element(by.id('LoginEmailInput')).typeText('test#test.com')
})
And it is really interesting...if i want to type 'a'(two-three symbol max) - it is ok! Text passing with no problem! But when i change my 'a' to real e-mail...it crash!
I have a strange feeling that detox has only few seconds for typeText, and if it can't so...it crash :)
Here is the error after crash. I used
detox test —loglevel trace
for details. Test on IOS 6