Detox test failed on typeText if text is "long" - react-native

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

Related

Mock or spyOn location.reload in angular unit testing

I have the function with location.reload(). How to write the angular unit test cases for test that reload functionality.
searchregion(){
location.reload();
}
I tried to written the test cases like below. But it shows error "Error: : could not find an object to spy upon reload()":
it('should test the reload functions', () =>{
spyOn(location, 'reload');
component.searchregion();
expect(location.reload).toHaveBeenCalled();
});
Can anyone please help me on this?

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!

Appium element.text() returns element accessibilityLabel instead of text

I am starting to test my React Native app with Appium. I have a simple login scenario where I expect the app status to be 'Logged in' after some username and password are entered and submitted. I am running the test on an iPhone X 12.2 simulator.
However the test fails with this error:
Expected: "Logged in"
Received: "status"
Somehow the text value is not received correctly. So how to get the inner text of my element?
App.js:
<Text accessibilityLabel="status">{this.state.status}</Text>
appium.test.js:
test('Login success', async() => {
expect(await driver.hasElementByAccessibilityId('username input')).toBe(true)
expect(await driver.hasElementByAccessibilityId('password input')).toBe(true)
expect(await driver.hasElementByAccessibilityId('submit button')).toBe(true)
expect(await driver.hasElementByAccessibilityId('status')).toBe(true)
await driver.elementByAccessibilityId('username input').sendKeys('some_username')
await driver.elementByAccessibilityId('password input').sendKeys('some_password')
await driver.elementByAccessibilityId('submit button').click()
const result = await driver.elementByAccessibilityId('status').text()
console.log(result) // 'status' WHY???
// the test runs fine until here:
expect(result).toBe('Logged in')
})
All I can think of is that text() is not the right function to get the inner text of my element, but this is all I see in the docs or tutorials I've been following...
I got in the same issue.
It seems that RN0.50 introduced this issue.
It's not a bug on Appium, but a problem with WDA implementation and how it is returning the text value.
https://github.com/appium/appium/issues/10349 there's a discussion about this problem.
You can solve it on IOS using testID instead of accessibilityLabel.
let textProps={}
if(Platform.OS==='android')
textProps.accessibilityLabel = "welcomeLabel"
...
...
...
<Text
style={styles.welcome}
{...textProps}
testID="welcomeLabel">
...
...
You can still use elementByAccessibilityId function.

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.