Detox - How to get a previous spec to run before a new spec runs to avoid duplicate test steps? - react-native

So I've wrote a test that logs in a user:
describe('Login', () => {
beforeEach(async () => {
await device.reloadReactNative()
})
it('Should grant access to a user with valid credentials', async () => {
test code
})
})
And now I'm writing a new spec to log out a user, so instead of writing the same test code again, I want the login spec to run within the log out spec. I would imagine it would look something like:
describe('Log Out', () => {
beforeEach(async () => {
await device.reloadReactNative()
it ('Should grant access to a user with valid credentials')
})
it('A User Logs Out', async () => {
test code
})
How do I get Detox to run the first login test before continuing with the new steps?
The beforeEach it ('Should grant access to a user with valid credentials') doesn't work unfortunately, so I'm missing something in the syntax.

This has no relation to Detox, this describe/it API is related to the test runner you are using. Anyway, use functions:
describe('Login', () => {
beforeEach(async () => {
await device.reloadReactNative();
await grantAccessToUserWithValidCredentials();
});
it('A User Logs Out', async () => {
// here the app is ready for you specific log out use case
});
async function grantAccessToUserWithValidCredentials() {
//grant it
}
});

Best practice is to use Drivers in your tests.
You can check out these slides:
http://slides.com/shlomitoussiacohen/testing-react-components#/7

Related

I keep getting Error: ResizeObserver loop limit exceeded in cypress

sorry I am new to cpyress
enter image description here
Cypress.on('uncaught:exception', (err, runnable) => {
return false;
}
I added this inside my code block
describe('TBoss Account Creation Credentials', () => {
Cypress.on('uncaught:exception', (err, runnable) => {
return false;
});
it('Identification Validation', () => {
cy.visit('our website')
cy.get('.mat-focus-indicator').click()
cy.wait(5000)
cy.origin('https://accounts.google.com/', () => {
cy.get('.Xb9hP').eq(0).type('1234')
cy.get('.VfPpkd-vQzf8d').eq(1).invoke('show').click()
cy.wait(5000)
cy.get('.whsOnd.zHQkBf').eq(0).type('1234', {force: true})
})
})
})
this is the code block, when I access to our website, it redirects me to google sign in page, I couldn't get() the google sign in input box for typing ID, so I used origin, and it works
however, after adding origin, it gives me that error.
on the google sign in page, I can successfully type the ID, and then it shows the password input form, thats where this error occurs
I want to get() the password input and type() the password and log in through google sign in form
please help me
The origin is sandboxed from the main page.
First thing to try is to move or copy the error catcher inside the cy.origion(). It may not work, there are some things that can't be run in there.
describe('TBoss Account Creation Credentials', () => {
it('Identification Validation', () => {
cy.visit('our website')
cy.get('.mat-focus-indicator').click()
cy.wait(5000)
cy.origin('https://accounts.google.com/', () => {
Cypress.on('uncaught:exception', (err, runnable) => {
return false;
});
cy.get('.Xb9hP').eq(0).type('1234')
cy.get('.VfPpkd-vQzf8d').eq(1).invoke('show').click()
cy.wait(5000)
cy.get('.whsOnd.zHQkBf').eq(0).type('1234', {force: true})
})
})
})

cypress multiple commands writing on localstorage doesn't work properly

My use case is when a user logs in he has to select a store to navigate.
So I wanted to create commands to reuse them.
commands.js
Cypress.Commands.add('login', () => {
localStorage.setItem('username', 'bob')
})
Cypress.Commands.add('selectStore', () => {
localStorage.setItem('storeId', 100001)
})
And my tests:
describe('Welcome', () => {
it('guest visits website, must login first', () => {
cy.visit('/')
cy.location('pathname').should('eq', '/login')
})
it('logged in user without store selected must select a store first', () => {
cy.login()
cy.visit('/')
cy.location('pathname').should('eq', '/stores')
})
it('logged in user with store selected should visit dashboard', () => {
cy.login()
cy.selectStore()
cy.visit('/')
cy.location('pathname').should('eq', '/dashboard')
})
})
This suite is passing first and second test but failing third.
The problem is:
cy.login() # localstorage has username key
cy.selectStore() # localstorage has storeId key but lost username key!
cy.visit('/')
cy.location('pathname').should('eq', '/dashboard') # cy.location('pathname') is '/login'
Why is happening this? Is this a bug? Is there something I'm doing wrong?
I tried agoff way but it does the same:
commands.js
Cypress.Commands.add('login', () => {
cy.window().then((window) => {
window.localStorage.setItem('username', 'bob')
})
})
Cypress.Commands.add('selectStore', () => {
cy.window().then((window) => {
window.localStorage.setItem('storeId', 100001)
})
})
Did you mean this?

Is it possible to mock responses during a detox end-to-end test

I'm currently making a react-native mobile application. I try to test my login button and fields and want to test the logic of moving to the account screen upon login.
Currently I got this as my testcase:
import {clickAccountButton} from "./Navigation";
//API
import nock from "nock";
import {API_URL} from "#env";
import {axiosPost} from "../app/config/api";
jest.useFakeTimers();
describe('LoginScreen tests', function () {
beforeAll(async () => {
await device.launchApp();
});
it('should show the login screen', async () => {
await clickAccountButton();
await expect(element(by.id('loginScreen'))).toBeVisible();
});
it('should have the header', async () => {
await expect(element(by.id('header'))).toBeVisible();
});
it('should contain email and password form field', async () => {
await expect(element(by.id('email'))).toBeVisible();
await expect(element(by.id('password'))).toBeVisible();
});
it('should contain the login and forgotten password buttons', async () => {
await expect(element(by.id('loginSubmit'))).toBeVisible();
await expect(element(by.id('forgotPassword'))).toBeVisible();
});
it('should navigate to ForgotPassword onPress', async () => {
await element(by.id('forgotPassword')).tap();
await expect(element(by.id('forgotPasswordScreen'))).toBeVisible();
//Click the account button again to return to the login screen
await clickAccountButton();
});
it('should login successfully', async () => {
//Give the following response to the next httpRequest
nock(`${API_URL}`)
.post('/api/v1/auth/login')
.reply(200, {
loggedIn: true,
user: {
id: 1,
}
}).persist();
await element(by.id('email')).replaceText('hello#email.com');
await element(by.id('password')).replaceText('foobar');
await element(by.id('loginSubmit')).tap();
//Double check: Check if the view with testID 'welcomeScreen' is showing
//and the input field with testID 'email' is gone
await expect(element(by.id('accountScreen'))).toBeVisible();
await expect(element(by.id('email'))).not.toBeVisible();
});
});
I expect the 'should login successfully' case to succeed because i'm intercepting the request and nock sends a response. This is not the case though. Instead it just does the actual request to my local API server which I don't want to use. Cause I don't want actual login details in my test.
Does anyone know how to handle this? Thanks in advance!
Jest and the app under test run in separate processes so normal Jest mocking techniques such as Nock won't work. Have a look at the mocking guide for Detox.
If you have a module such as apiClient.js in your app then you can mock that with something like apiClient.mock.js.

How to write business logic in a service as sinon in ExpressJS

I want to use sinon to write test code for the business logic of a service using ExpressJS and Mongoose.
I wrote the following test code, but findOneService takes only id as an argument and returns a document with that id.
//service_test.js
const sinon = require('sinon');
// Service
const { findOneService } = require('../services/service');
// Schema
const Post = require('../models/mongoose/schemas/post');
describe('findOneService', () => {
let find;
beforeEach(() => {
find = sinon.stub(Post, 'findOne');
});
afterEach(() => {
find.restore();
});
it('should findOne', async () => {
const id = ???;
...?
});
})
//service.js
exports.findOneDocument = async (id) => {
const result = await Post.findOne({_id: id});
if (!result) {
throw new Error('404');
}
return result;
};
How can I define the result of this to pass the test code?
To test this kind of behaviour, I strongly suggest an integration test (with an embedded/dockerized MongoDB, for example). This would allow to test-drive more things than just the service, such as schema, migration, db config.
However, if you just want to test-drive the if (!result)... logic, you can stick with sinon. What you're missing is stubbing the return value:
it('returns the document if found', async () => {
find.returns('a post');
expect(await findOneService.findOneDocument('id')).toReturn('a post');
});
it('throw error when document does not exist', async () => {
find.returns(null);
expect(() => await findOneService.findOneDocument('non-existent id')).toThrow(Error);
});

Testcafe: How to update the store of userRole

Using userRole, I try to avoid having to log in before each test, but my test suite is running long, so the app goes fetching a new accessToken based on the current refershToken a number of times. This causes tests to fail sometimes, and after some investigation, I discovered that the localStorage seems only to be stored at login time, not at test exit time. So when a refresh happened, the next test does get the wrong accessToken and an expired refreshToken, so the app fails. (Which probably should not happen and simply present a login screen, but that is beside the point, as that is a scenario that should not happen in these tests.)
How can I make sure that the Role is updated with the latest version of localStorage upon exiting the test?
This is the test code to isolate the problem:
test('xxx', async t => {
await t.useRole(anAdminUser)
await ClientFunction(() => window.localStorage.setItem('xxx', 'xxx'))()
await t.expect(await ClientFunction(() => window.localStorage.getItem('xxx'))()).eql('xxx')
}
test('yyy', async t => {
await t.useRole(anAdminUser)
await t.expect(await ClientFunction(() => window.localStorage.getItem('xxx'))()).eql('xxx') /* => fail */
}
I finally worked around the problem using:
fixture('aaa')
.beforeEach(async t => {
await t.useRole(anAdminUser)
const { sessionCache } = anAdminUser
if (!anAdminUser.sessionCache) return
await ClientFunction(sessionCache => window.localStorage.setItem('xxx', sessionCache))(sessionCache)
})
.afterEach(async () => {
const sessionCache = await ClientFunction(() => window.localStorage.getItem('xxx'))()
anAdminUser.sessionCache = sessionCache
})
but it seems like a waste to duplicate some of the efforts of useRole itself.