To pass values to other test cases in mocha - testing

The following is my index.js file (test -> index.js)
var token;
describe('POST login', () => {
it('should return status 200', (done) => {
chai.request(app)
.post('login')
.send({
"email":"***#mail.com",
"password":"***"
})
.end((err , response) => {
if(err)
done(err);
response.should.have.status(200);
token = response.header.token;
done();
});
});
});
module.exports.token = token
This is my report test file (test -> report.js)
let chai = require('chai');
let chaiHttp = require('chai-http');
let {app} = require('../test');
chai.use(chaiHttp);
chai.should();
const {token} = require('./index');
beforeEach(function (done) {
setTimeout(function(){
done();
}, 5000);
});
describe('POST /report', () => {
it('should return status 200', (done) => {
console.log("MyToken" +token);
chai.request(app)
.post('/report')
.set({'token': token})
.end((err , response) => {
if(err) done(err);
response.should.have.status(200);
response.body.should.be.a('object');
done();
});
});
});
But in the log, I'm getting the MyToken value as undefined. My question is, how can I pass the values across multiple tests? In case if null or undefined, it should stop other tests from executing.

The main problem in your code is that token is a string. String is primitive value which is immutable. You simply export undefined and that's it, this exported value will never change. You could employ an Object instead:
var token;
...and...
token.value = response.header.token;
...and...
.set({'token': token.value})

Related

Why is Jest running the typescript test files and then the compiled JS test files?

When I run Jest, I get 9 failing, 11 passing out of a total of 20, but there are only 10 tests between two different test files, here it is:
const fs = require('fs');
const assert = require('assert');
import * as jwt from 'jsonwebtoken';
import * as auth from '../services/authentication-service';
const JWT_ERROR_INVALID_SIG = 'invalid signature';
describe('MMD Integration', () => {
const SERVICE = "knox";
const SERVICE_ID = "aluna1";
const badPublicKeyFile = "badkey.pub";
describe('Service Config is accessible', () => {
it('should contain data', async (done) => {
let config: {} | null = null;
config = await auth.getServiceConfig().catch(err => {
console.log("caught getServiceConfig error:", err);
return null;
});
if (config != null) {
assert.include(Object.keys(config), SERVICE);
} else {
console.log("Test failed!");
}
});
});
describe('Public Key', () => {
describe('is valid', () => {
it('should decode successfully', async (done) => {
let config: {} | null = null;
config = await auth.getServiceConfig().catch(err => {
console.log("caught getServiceConfig error:", err);
return null;
});
let publicKey: string | null = null;
if (config) {
publicKey = await auth.getServicePublicKey(SERVICE, config).catch(err => {
console.log("caught getServicePublicKey error:", err);
return null;
});
const token = await auth.genJwt(SERVICE);
if (token == null) {
console.log("genJwt returned null: stopping test");
done();
} else if (!publicKey) {
console.log("No public key: stopping test");
done();
} else {
jwt.verify(token, publicKey, (err, decoded) => {
if (err) {
console.log("WARNING: valid public key failed!", err.message);
} else if (decoded && Object.keys(decoded).includes('vendor')) {
assert.include(Object.values(decoded), SERVICE);
} else {
console.log("Test failed!");
}
});
}
}
});
});
describe('is bad', () => {
const badPublicKey = fs.readFileSync(badPublicKeyFile);
it('should fail verify', async (done) => {
const token = await auth.genJwt(SERVICE);
if (token == null) {
console.log("genJwt returned null: stopping test");
done();
} else {
jwt.verify(token, badPublicKey, (err: any, decoded: any) => {
if (err) {
assert.equal(err.message, JWT_ERROR_INVALID_SIG);
} else {
console.log("WARNING: bad public key worked!", decoded);
}
});
}
});
});
});
describe('Verify Service', () => {
describe('with valid public key', () => {
it('should succeed', async (done) => {
try {
const token = await auth.genJwt(SERVICE);
if (token == null) {
console.log("genJwt returned null: stopping test");
done();
} else {
const result = await auth.verifyService(SERVICE, token).catch(err => {
console.log("caught verifyService error: stopping test", err);
throw new Error(err);
});
assert.equal(result, "OK");
}
} catch (err) {
assert.equal(err, "OK");
}
});
});
describe('with mismatch token', () => {
it('should fail', async (done) => {
try {
const result = await auth.verifyService(SERVICE, "xyz").catch(err => {
console.log("caught verifyService error: stopping test", err);
done();
});
} catch (err) {
assert.notEqual(err, "OK");
}
});
});
});
describe('Service as real MMD', () => {
it('should fail', async (done) => {
try {
const token = await auth.genJwt("mmd");
if (token == null) {
console.log("genJwt returned null: stopping test");
throw new Error('null token');
} else {
const result = await auth.verifyService("mmd", token).catch(err => {
console.log("caught verifyService error:", err);
throw new Error(err);
});
}
} catch (err) {
assert.notEqual(err, "OK");
console.log(err);
}
});
});
});
describe('Get Token from Request Header', () => {
const someToken = "fake-jwt";
const headers = {
'Content-Type': 'application/json'
, 'Authorization': 'Bearer ' + someToken
, 'Aluna-Service': 'foobar'
};
const badHeaders2 = {
'Content-Type': 'application/json'
, 'Authorization': someToken
, 'Aluna-Service': 'foobar'
};
describe('Request header has authorization', () => {
it('should return token', () => {
const result = auth.getTokenFromAuth(headers.Authorization);
assert.equal(result, someToken);
});
});
describe('Request header is missing authorization', () => {
it('should return null', () => {
const result = auth.getTokenFromAuth('');
assert.equal(result, null);
});
});
describe('Authorization is missing Bearer', () => {
it('should return null', () => {
const result = auth.getTokenFromAuth(badHeaders2.Authorization);
assert.equal(result, null);
});
});
});
import request from 'supertest';
import { app } from '../app';
it('renders a greeting to screen', () => {
return request(app).get('/').send({ greeting: 'howdy' }).expect(200);
})
This is what I see in the terminal:
Test Suites: 3 failed, 1 passed, 4 totaload:flatten Completed in 1ms
Tests: 9 failed, 11 passed, 20 total
Snapshots: 0 total
Time: 31.358 s
Ran all test suites.
Watch Usage
› Press f to run only failed tests.
› Press o to only run tests related to changed files.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press q to quit watch mode.
› Press Enter to trigger a test run.
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.
at Object.getCodec (node_modules/iconv-lite/lib/index.js:65:27)
at Object.getDecoder (node_modules/iconv-lite/lib/index.js:127:23)
at getDecoder (node_modules/raw-body/index.js:45:18)
at readStream (node_modules/raw-body/index.js:180:15)
at getRawBody (node_modules/raw-body/index.js:108:12)
[2022-03-07T18:40:25.852Z] 1.0.1-dev error: uncaughtException: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "Error: Caught error after test environment was torn down
This API was meant to work with Jest or that was the original testing suite installed, but someone else came behind and started using Mocha that they are using globally on their machine. Would anyone mind also sharing why tests would pass on their global install of Mocha but not on Jest?
Just wanted to post a solution which is not buried in comments.
By default jest will find any test files in your entire project. If you are building or copying files to a build/release directory, you need to do one of the following:
(Recommended) Exclude the test files from your build pipeline. I usually create a separate tsconfig for building which excludes the test files. Your build command should point to this tsconfig: tsc --project tsconfig.build.json. Note: you can extend tsconfigs so that you don't have to manage duplicates. Here's an example of what your tsconfig.build.json might look like:
{
"extends": "./tsconfig.json",
"exclude": ["src/**/*.test.ts"]
}
-- OR --
Exclude your build directories from jest, adding testPathIgnorePatterns: ['dist/'] to your jest.config.js (assuming your compiled JavaScript files are in the dist folder)

jest how can test axios?

No matter how I write, asynchronous problems occur.
test.js:
const auth = require('../methods/auth.js');
describe('test', () => {
test('test', async () => {
expect.assertions(1);
const data = await auth.signin();
return expect(data.success).toBeTruthy();
});
auth.js:
module.exports = {
async signin(data) {
try {
const res = await axios.post('/signin', data);
return res.data;
} catch (error) {
return error.response;
}
},
}
Each execution result is different.
You can test Promises as follows:
test('test awaiting it to resolve', () => {
// Don't await. Note the return; test callback doesn't need to be async
// What I do in my tests as its more readable and the intention is clear
return expect(auth.signin()).resolves.toEqual({success: true});
});
test('test the promises way', () => {
// Not better than above; traditional way; note 'return'
return auth.signin().then(data => { expect(data.success).toBeTruthy() });
});
Detailed notes from jest: https://jestjs.io/docs/asynchronous#promises

Jest / Supertest Error - Right-hand side of 'instanceof' is not callable

When using supertest like so,
import app from "../../src/app";
import request from "supertest";
describe("GET / - a simple api endpoint", () => {
it("Hello API Request", () => {
const result = request(app)
.get("/api/location/5eda6d195dd81b21a056bedb")
.then((res) => {
console.log(res);
})
// expect(result.text).toEqual("hello");
// expect(result.status).toEqual(200);
});
});
Im getting "Right-hand side of 'instanceof' is not callable".
at Response.toError (node_modules/superagent/lib/node/response.js:94:15)
at ResponseBase._setStatusProperties (node_modules/superagent/lib/response-base.js:123:16)
at new Response (node_modules/superagent/lib/node/response.js:41:8)
at Test.Request._emitResponse (node_modules/superagent/lib/node/index.js:752:20)
at node_modules/superagent/lib/node/index.js:916:38
at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/parsers/json.js:19:7)
at processTicksAndRejections (internal/process/task_queues.js:84:21) {
status: 500,
text: `"Right-hand side of 'instanceof' is not callable"`,
method: 'GET',
path: '/api/location/5eda6d195dd81b21a056bedb'
This is just with supertest, the API works when using Postman.
Rest of the code for this call,
router.get(
"/location/:id",
(req, res) => {
locationController.getLocation(req, res);
}
);
const getLocation = async (req: Request, res: Response): Promise<void> => {
const { id } = req.params;
const location = await data.readRecord(id, Location);
res.status(location.code).json(location.data);
};
const readRecord = async (id: string, model: IModel): Promise<Response> => {
try {
const response = await model.findById(id);
if (response == null) return { code: 404, data: `ID ${id} Not Found` };
return { code: 200, data: response };
} catch (error) {
return errorHandler(error);
}
};
Is there a configuration im missing for supertest and typescript?
This approach worked,
import request = require("supertest");
import app from "../../src/app";
describe("GET/ api/location/id", () => {
it("should connect retrieve record and retrieve a code 200 and json response", async () => {
const res = await request(app)
.get(`/api/location/${id}`)
expect(res.status).toBe(200);
expect(res.body._id).toBe(`${id}`);
});
});
If you don't want to use "await" in your code , you can use "done()" in callback function.
like this.
import app from "../../src/app";
import request from "supertest";
describe("GET / - a simple api endpoint", () => {
it("Hello API Request", (done) => {
const result = request(app)
.get("/api/location/5eda6d195dd81b21a056bedb")
.then((res) => {
console.log(res);
expect(res.text).toEqual("hello");
expect(res.status).toEqual(200);
done();
//done() function means this test is done.
})
});
});
Awaiting the expect call (with Jest) worked for me.
await expect(...).rejects.toThrow()

How to test Axios reject condition using Jest

I wrote a unit test for some Axios calls in my component. I verified the success path, where the call resolves successfully, but I am not able to verify the failure path, where the call rejects. How do I use mocks to verify this?
Here's a snippet of my FetchImage.vue component:
methods: {
preparedFetch() {
axios.get(this.imageurl).then(result => {
this.imageInformation.title = result.data.title;
this.imageInformation.copyright = result.data.copyright;
this.imageInformation.detailExplanation = result.data.explanation;
this.imageInformation.date = result.data.date;
this.imageInformation.urlinfo = result.data.url;
this.resultArrived = true;
this.$emit('imagefetched',this.imageInformation);
})
.catch( error => {
this.errorMessage = "Information not found";
this.resultArrived = true;
});
}
}
And my test for when the call rejects (for an invalid URL):
describe('Invalid response',async () => {
beforeEach(() => {
axios.get.mockClear();
axios.get.mockReturnValue(Promise.reject({}));
});
it('Invalid URL verfication', async () => {
// Given
const result = {
errorMessage : "Information not found",
resultArrived : true,
fetchStatus : true
};
// Fetch the error result
axios.get.mockReturnValue(Promise.resolve(result));
const fetchwrapper = mount(FetchImage);
fetchwrapper.vm.imageurl = "https://invalid.request.gov";
fetchwrapper.vm.preparedFetch();
await fetchwrapper.vm.$nextTick();
// Validate the result
expect(axios.get).not.toHaveBeenCalledWith('https://api.nasa.gov/planetary/apod?api_key=vME6LAMD7IhEiy7rDmjfIaG6MhiKbu1MNIqxtqd1');
expect(axios.get).toHaveBeenCalledWith("https://invalid.request.gov");
expect(axios.get).toHaveBeenCalledTimes(1);
expect(fetchwrapper.vm.errorMessage.length).not.toEqual(0);
expect(fetchwrapper.vm.errorMessage).toBe("Information not found");
});
});
Your catch block isn't running because the mock return value is using Promise.resolve() when it actually should be Promise.reject():
describe('Invalid response',async () => {
it('Invalid URL verfication', async () => {
// axios.get.mockReturnValue(Promise.resolve(result)); // DON'T DO THIS
axios.get.mockReturnValue(Promise.reject(result));
});
});
You have to reject the value by using the built-in jest method.
describe('Invalid response', async () => {
it('Invalid URL verfication', async () => {
axios.get.mockRejectedValue(result);
});
});

How to reuse puppeteer tests

I'm currently working with Puppeteer and Jest for end-to-end testing, for my tests to work I always need to run a login tests, but I don't know and haven't been able to find out how to export my tests so I can reuse them.
To conclude: I'm looking for a way to reuse all of my tests inside the describe by exporting them to a different file and reusing them in a beforeAll in the new files.
The complete set of login tests is below:
describe("homepage and login tests", homepageTests = () => {
test("front page loads", async (done) => {
await thePage.goto('http://localhost:3000');
expect(thePage).toBeDefined();
done();
});
test("Login button is present", async (done) => {
theLoginButton = await thePage.$("#login-button");
expect(theLoginButton).toBeDefined();
done();
})
test("Login works", async (done) => {
//the following code runs inside the popup
await theBrowser.on('targetcreated', async (target) => {
const thePopupPage = await target.page();
if (thePopupPage === null) return;
//get the input fields
const usernameField = await thePopupPage.waitFor('input[name=login]');
const passwordField = await thePopupPage.waitFor("input[name=password]");
const submitButton = await thePopupPage.waitFor('input[name=commit]');
//validate input fields
expect(usernameField).not.toBeNull();
expect(passwordField).not.toBeNull();
expect(submitButton).not.toBeNull();
//typing and clicking
await thePopupPage.waitFor(300)
await usernameField.type("USER");
await passwordField.type("PASSWORD");
await submitButton.click();
done();
})
try {
//wait for login button on homepage
theLoginButton = await thePage.waitFor('#login-button');
expect(theLoginButton).toBeDefined();
//click on login
await thePage.waitFor(200);
await theLoginButton.click();
} catch (e) { console.log(e) }
})
test("Arrive on new page after login", async () => {
//resultsButton is only shown for logged in users.
const resultsButton = await thePage.$("#resultsButton");
expect(resultsButton).toBeDefined();
})
Create a separate file name test.js
//test.js
export async function fn1(args){
// your commands
}
//file.test.js
import {fn1} from 'test.js'
describe('test 1 ', () => {
test("test", async () => {
try {
await fn1(args);
} catch (err) {
console.log('There are some unexpected errors: ' + err);
}
},5000);
});
I have the same issue and the above method will work out for you.