My first cypress e2e test fails to do much of anything; an obvious failure is not reported - authorization

I am new to Cypress trying to run an e2e test. My modest goal is to create an account and login with that account. My expected result is that I get some indication that the line I highlight with a comment // should fail! reports a failure. My actual result is that I don't get much of anything!
Error message: none! kind of.
I get some messages about failed post requests, does this matter? See the red "POST 400". Does that interrupt my tests?
Here's my test:
it("registers, logs in, and waits for the refresh token to refresh", async () => {
cy.visit(baseFrontendUrl);
const loginResponse = await axios.post(baseBackendUrl + "/auth/email/login", { email: "rm" });
const accessToken = loginResponse.data.accessToken;
console.log(accessToken, "access token");
const allUsersResponse = await axios.get(baseBackendUrl + "/auth/all", {
headers: { Authorization: "Bearer " + accessToken },
});
const allUsers: Member[] = allUsersResponse.data;
console.log(allUsers, "24rm");
const allTestAccounts = allUsers.map((m: Member) => m.googleId.slice(0, 4) === "TEST");
const nextTestAccountFieldValue = "TEST" + (allTestAccounts.length + 1).toString();
const nextTestAccount = {
email: nextTestAccountFieldValue,
firstName: nextTestAccountFieldValue,
lastName: nextTestAccountFieldValue,
fakeGoogleId: nextTestAccountFieldValue,
};
cy.get("#devPortal").should("have.text", "SHOULD OBVIOUSLY FAIL"); // doesn't fail!
cy.get("#signUpEmail").type(nextTestAccount.email);
cy.get("#signUpFirstName").type(nextTestAccount.firstName);
cy.get("#signUpLastName").type(nextTestAccount.lastName);
cy.get("#signUpGoogleId").type(nextTestAccount.fakeGoogleId);
cy.get("#signUpButton").click();
// assert
cy.get("#msg").should("have.text", "Successful registration").should("be.visible"); // doesn't seem to run at all
}
Neither of the requests in the above code fail; they work fine and I know that because the accessToken value logs something.
Putting expect(true).to.equal(false) does show assertexpected true to equal **false** AssertionError and "true to be true" does give assertexpected true to equal **true** so perhaps I don't understand should?
And in case its not clear, the text for #devPortal# is not "should obviously fail"! :-) Sign up with email: `
I don't know what to look for because I am newb at Cypress. It's something obvious, I'm sure.
edit: So I disabled the bunch of axios requests at the top. The tests worked after that:
edit2: This solution isn't perfect because I want to (a) get the list of users => (b) get the # of test accounts => (c) increment the # at the end of "testAccount" by 1 so the e2e test runs with a new account. I need to login, get an access token, and hit an endpoint, for this to work. Hence the question becomes, "why does running an axios request at the start of the test make it malfunction?"

The axios calls are succeeding, but they aren't part of the test they just set up some "fixture" data for the sign-in test itself.
I would separate that block of code out to make things more understandable.
Usually you will make API calls using cy.request() rather than axios, and
then you don't need async/await which are in fact "illegal" in Cypress (the commands don't return promises so you can't await them).
beforeEach(() => {
cy.request('POST', baseBackendUrl + "/auth/email/login", { email: "rm" })
.then(response => {
const accessToken = response.data.accessToken;
cy.request(baseBackendUrl + "/auth/all", {
headers: { Authorization: "Bearer " + accessToken },
})
.then(response => {
const allUsers: Member[] = response.data;
const allTestAccounts = allUsers.map((m: Member) => m.googleId.slice(0, 4) === "TEST");
const nextTestAccountFieldValue = "TEST" + (allTestAccounts.length + 1).toString();
const nextTestAccount = {
email: nextTestAccountFieldValue,
firstName: nextTestAccountFieldValue,
lastName: nextTestAccountFieldValue,
fakeGoogleId: nextTestAccountFieldValue,
};
cy.wrap(nextTestAccount).as('nextAccount') // use alias to save value
})
})
})
})
// no need for async when using cy.request()
it("registers, logs in, and waits for the refresh token to refresh", () => {
cy.get('#nextAccount').then(nextTestAccount => {
cy.visit(baseFrontendUrl);
...
I think your problem with the .should() not failing the test may be due to the async/await around the test. Obviously the assert is failing as it's logging red, but if the test is still passing then it looks like a timing issue - the test is completing too soon.

Related

Axios Post React Native Return Undefined Sometimes

Why my axios post return undefined sometimes ?
Check this code :
export const ApiConfigAxios = axios.create({
// * Begin =========================== Configuration API
baseURL: 'https://website.id/index.php',
withCredentials: true,
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": `Basic ${base64data}`,
},
// * End =========================== Configuration API
});
export const ApiRegisterOrLoginUser = async (email, name, picture, givenname, familyname) => {
try {
// * Begin =========================== API
const response = await ApiConfigAxios.post('/AuthenticationAPI', {
email: email,
name: name,
picture: picture,
givenname: givenname,
familyname: familyname,
});
// * End =========================== API
if (response.data.Data.length === 2) {
let results = Object.assign(response.data.Data[0], response.data.Data[1]);
return results;
} else {
return response.data;
}
} catch (err) {
console.error(err);
}
};
The Problem Is :
When I clear data app from android setting and open the app then quickly click the sign in button I will got undefined
But when I clear data app from android setting and open the app then wait at least 3 second before click the sign in button it is working
what is the problem with the axios ?
Found the problem.
The problem is in server side. I have a post value (TOKENS) that cannot be null.
That is a Notification Tokens. It is inside useEffect.
So in my app I check if TOKENS != null (delay until the token generate) then the button login show.
Also to make a better user experience when got undefined I check in my API Axios :
if (typeof response == 'undefined') {
return "FAILED";
}
When user login and receive undefined I will prompt a Message Box rather than loading without end. So user can try login again.

How to test `verify` of an express middleware in Jest

I have a function which returns a middleware as such:
const jsonParser = () => {
return express.json({
limit: '5mb',
verify: (req, res, buf) => {
// If the incoming request is a stripe event,
if (req.headers['some-header']) {
httpContext.set('raw-body', buf.toString());
}
},
});
};
I would like to test that the httpContext.setis indeed called when the some-header header is present.
My test:
describe('jsonParser middleware', () => {
it('sets the http context', async () => {
const req = {
headers: {
'some-header': 'some-sig',
'content-type': 'application/json',
},
body: JSON.stringify({
some: 'thing',
}),
};
const res = {};
const middleware = jsonParser();
middleware(req, res, () => {});
expect(httpContext.set).toHaveBeenCalled();
});
});
I have no idea how to make the test run the function passed to verify. Express docs state that the content type should be json, but nothing more. Anyone that can point me in the right direction is highly appreciated.
Thank you.
as mentioned in the comments i want to give you an example of an integration test which tests the header and jsonwebtoken. i am also using the express framework but i wrote my code in JS.
this is a test for creating a forumpost in a forum i built. a middleware is checking for the token of the user so this case could be similiar to yours.
const request = require('supertest');
test('create authorized 201', async () => {
const forumCountBefore = await ForumPost.countDocuments();
const response = await request(app)
.post('/api/forumPosts')
.set({
Authorization: `Bearer ${forumUserOne.tokens[0].token}`,
userData: {
userId: forumUserOneId,
email: 'forum#controller.com',
username: 'forum',
},
})
.send(forumPost)
.expect(201);
expect(response.body.message).toBe('created forumPost');
const forumCountAfter = await ForumPost.countDocuments();
expect(forumCountBefore + 1).toBe(forumCountAfter);
});
i am using mongoDB thats why i use ForumPost.countDocuments to count the amount of entries in the DB.
as you can see in the test i use supertest (imported as request) to send an http call. in the set block i set the authorization token. this causes the middleware to be executed in the integration test.
the test can only pass when the code of the middleware gets executed correctly so it should cover the code of your middleware.

'return res.json' isn't stopping my request as expected?

I created some sample code to demonstrate my issue on a smaller scale. I would like a solution that doesn't involve adding 'unique: true' to my model, if possible, because I seem to run into similar problems in many different scenarios:
const express = require('express')
const mongoose = require('mongoose')
const app = express()
const PORT = 6000
app.use(express.json())
// Initializing mongoose model and connection
const SampleSchema = mongoose.Schema({
username: String,
password: String
})
const Sample = mongoose.model('sample', SampleSchema)
mongoose.connect('mongodb://localhost/testdatabase', {
useNewUrlParser: true,
useUnifiedTopology: true
})
// Running my post request
app.post('/api', async (req, res) => {
await Sample.findOne({
username: req.body.username
}).then(data => {
if(data) {
console.log(data)
return res.json('This user already exists in my database')
}
})
await Sample.create({
username: req.body.username,
password: req.body.password
})
return res.json('User created')
})
app.listen(PORT, () => {
console.log('Server running on 6000')
})
Here is my request and database the first time I send a request:
This is as intended. However, if I send the same request a second time:
I want the code to stop on the first 'res.json' if that line of code is executed - basically, in this example I don't want to create a new Sample if one with the same username already exists. I do realize that in this case I can approach the issue differently to solve the problem, but I want to know why my 'Sample.create' line runs, and if there's a way to prevent it from running aside from the aforementioned method.
This is because the .then callback executes after the enclosing function has already finished. In this code here:
await Sample.findOne({
username: req.body.username
}).then(data => {
if(data) {
console.log(data)
return res.json('This user already exists in my database')
}
})
The function being returned from is the data => ... arrow function passed to .then, not the enclosing request handler, so it doesn't prevent subsequent code from executing.
You want to rewrite that bit to use async/await syntax as well:
const data = await Sample.findOne({
username: req.body.username
})
if(data) {
console.log(data)
return res.json('This user already exists in my database')
}
You might want to read up a bit on async/await and Promises in general-- asynchronous code can be quite confusing at first! https://developers.google.com/web/fundamentals/primers/async-functions

I don't want to clear cashe and cookies in cypress

I have tried many but have not been successful, please help me with the example. I have put all code here you can also try it on your system.
I am testing registration flow using cypress. and I don't want to clear cache/cookies before each test. can anyone help me?
This is my test file, and the first describe block is to send OTP for entered email. the second one is to create a temporary email and save OTP into a JSON file for letter use. and the third one is for verifying OTP using API. but when I use the same URL and enter an email which OTP verified by API it shows 500 Internal server error
const faker = require("faker");
const firstName = faker.Name.firstName();
const lastName = faker.Name.lastName();
const email = firstName + "#mailinator.com";
describe('My Test Suite', function () {
it('Otp Test', function () {
cy.visit('https://outsized.site/')
cy.get('.css-jqdzg6-commonButtonStyle > canvas', { timeout: 30000 }).click()
cy.get('#email').type(email.toLocaleLowerCase())
cy.get('.ant-btn').click()
cy.fixture('data1').then((profile) => {
profile.FreelancerName = firstName.toLocaleLowerCase()
profile.FreelancerEmail = email.toLocaleLowerCase()
cy.writeFile("cypress/fixtures/data1.json", profile)
cy.wait(2000)
})
})
})
context('My Test Suite', function () {
it('Otp Test', function () {
cy.visit('https://www.mailinator.com/')
cy.fixture("data1.json").then(profile => {
cy.get("#addOverlay").type(profile.FreelancerName)
})
cy.get("#go-to-public").click()
cy.wait(2000)
cy.contains('table tbody tr', 'OTP').click() // find the right email
cy.get('#html_msg_body') // iframe
.its('0.contentDocument.body').should('not.be.empty') // wait for loading
.then(console.log) // works with this but errors without - totally weird
.wait(0)
.find("table > tbody > tr:nth-child(3) > td > h2")
.then($h2 => {
const OTP = $h2.text()
cy.fixture("data1.json").then(profile => {
profile.OTP = OTP
cy.writeFile("cypress/fixtures/data1.json", profile);
})
})
})
})
context('My Test Suite', function () {
it('Otp Test', function () {
cy.fixture('data1').then((profile) => {
cy.request({
method: 'POST',
url: 'https://api.outsized.site/graphql',
headers: {
'Content-Type': 'text/plain'
},
body:
'mutation { verifyEmailOtp(email: "' + profile.FreelancerName + '#mailinator.com", otp: ' + profile.OTP + '){ message } }'
})
})
cy.wait(5000)
cy.fixture("data1.json").then(profile => {
cy.visit("https://outsized.site")
cy.wait(5000)
//cy.visit(profile.url+profile.FreelancerName+"%40"+"mailinator.com")
cy.get('.css-jqdzg6-commonButtonStyle > canvas', { timeout: 30000 }).click()
cy.get('#email').type(profile.FreelancerEmail)
cy.get('.ant-btn').click()
cy.request({
method: 'POST',
url: 'https://api.outsized.site/graphql',
headers: {
'Content-Type': 'text/plain'
},
body:
'mutation { addNewEmail(email: "' + profile.FreelancerName + '#mailinator.com"){ message } }'
})
cy.get('.ant-btn').click()
})
})
})
500 Internal server error get because cypress has clear cache and cookies before each test.
There's a relatively new command cy.session() (docs) that preserves cookies, localStorage, and sessionStorage. Not sure if that includes "cache", in fact I don't know what you refer to there.
The way it works is you can add it into a beforeEach() so it gets called by each test, but it only calls the code inside once (first test), then for subsequent calls it retains and restores the values from the above stores that were set during the first test.
There's an example here Unable to access modal-dialogue in cypress which is simpler than the examples in the official documents.
The basic pattern is worth repeating
Cypress.config('experimentalSessionSupport', true) // set this flag
beforeEach(() => {
cy.session('mySession', () => {
// code that sets cookies, only called once
// thereafter same cookies, localstorage, sessionStorage
// are preserved for future test
})
})
I can't really tell what code you need from your sample above, but I'm sure you know already.

Nuxtjs - Authentication

I wrote a sign up functionality in nuxtjs. It saves a new user in my database. However, there seems to be a problem with generating a token afterwards, to log in the user.
The register action gets called by a method in the register component. It returns the error response in the catch block. It seems to fail after the token is generated on the server.
Action in the store
async register ({ commit }, { name, slug, email, password }) {
try {
const { data } = await this.$axios.post('/users', { name, slug, email, password })
commit('SET_USER', data)
} catch (err) {
commit('base/SET_ERROR', err.response, { root: true })
throw err
}
}
Post function on the nodejs server
router.post('/users', async (req, res) => {
try {
const body = _.pick(req.body, ['name', 'slug', 'email', 'password']);
const user = new User(body);
await user.save();
const token = await user.generateAuthToken(); // execution seems to fail on this line
console.log(token); // never gets called
req.session['token'] = 'Bearer ' + token;
req.session['user'] = user;
res.header('Authorization', 'Bearer ' + token).send(user);
} catch (err) {
res.status(400).json({ message: "Der Account konnte leider nicht erstellt werden" });
}
});
GenerateAuthToken function in mongo model User
UserSchema.methods.generateAuthToken = function () {
var user = this;
var access = 'auth';
var token = jwt.sign({_id: user._id.toHexString(), access}, process.env.JWT_SECRET).toString();
user.tokens.push({access, token});
return user.save().then(() => {
return token;
});
};
Error message
I would be tremendously thankful for any kind of help!
Maybe it doesn't help too much, but I would try to create a dummy token and try to make everything works with it. One of my debugging techniques is to isolate every single part of my code and be sure that everything works piece for piece, maybe that technique is slow but most of the time it works.
If everything works, I would continue debugging the generateAuthToken function.
If your console log never gets called, then the error could be in the function.
I hope it helps a little and sorry I don't know too much about MongoDB but everything seems to be ok.