I am testing my express server in Jest. All test pass but Jest doesn't close or exit. I have no idea why but I believe my server is closing after the test runs. I tried running yarn jest --detectOpenHandles and the response came back saying "Jest has detected the following 1 open handle potentially keeping Jest from exiting:
Promise"
Here is my code...
const request = require('supertest');
const app = require('../server');
const server = app.listen(8000, '127.0.0.1');
afterAll(() => {
server.close();
});
describe('GET /', () => {
it('should respond with JSON (list of all users)', async () => {
try {
const response = await request(server).get('/')
.expect('Content-Type', /json/);
expect(response.statusCode).toBe(200);
} catch (e) {
console.log(e);
}
});
});
I can't seem to understand why the promise isn't resolving if that's actually the issue. My express app is pulling data from Firebase and returning the data once a get request is made. Any ideas on what's going on?
In Server.js file I connect to Firebase maybe this is the issue?
const db = admin.database();
const ref = db.ref('users');
app.get('/', (req, res) => {
// Attach an asynchronous callback to read the data at our posts reference
ref.on('value', function(snapshot) {
res.send(parseUsers(snapshot.val()));
res.status(200);
}, function (errorObject) {
res.send([],'The read failed: ' + errorObject.code);
res.status(500);
});
} );
Related
I had a simple endpoint using Express to allow user to download a csv file.
How should I make a test with just Jest for a file download endpoint
I'm not sure which function or mock should I use to test out this scenario, as it returns with Number of calls: 0 for below test
controller.js
const getFile = async (req, res, next) => {
try {
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', 'attachment; filename=sample.csv');
const csv = 'ID\n1\n2\n3';
res.send(csv);
} catch (e) {
next(
new HttpException(
'internal error',
'file download error',
e.message,
),
);
}
}
controller.test.js
test('should successfully download the csv', async () => {
const mockReq = {};
const mockRes = {
send: jest.fn(),
};
await controller.getFile(mockReq, mockRes, jest.fn());
expect(mockRes.send).toHaveBeenCalledWith({ 'Content-Type': 'text/csv' });
});
In case anyone face similar problem like me, I think the easiest way is using supertest library. This library support HTTP assertions, so I can test at route level:
const request = require('supertest');
...
const response = await request(app).get(
'/api/.../download-file',
);
expect(response.status).toEqual(200);
expect(response.headers['content-type']).toMatch('text/csv; charset=utf-8');
expect(response.headers['content-disposition']).toMatch(
'attachment; filename=' + 'sample.csv',
);
This error is really driving me crazy for the last 2 days. Please help.
So when I try to login with google the 1st time on my website, it doesn't cause any problem but when I try to do it the second time, with any account, it shows this error in the console:
The FetchEvent for "http://localhost:3000/auth/google/callback?code=4%2F0AX4somethingsomethingsomethingsomething&scope=profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile" resulted in a network error response: an object that was not a Response was passed to respondWith().
and the webpage shows this error:
This site can’t be reached The web page at http://localhost:3000/auth/google/callback?code=4%2F0AX4somethingsomethingsomethingsomething&scope=profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile might be temporarily down or it may have moved permanently to a new web address.
I am quite new to pwa and don't understand some of the code in the service worker file (I have copy pasted the 'fetch' part of the code from this webiste: blog.bitsrc.io) so that might be the reason I am not able to identify the error in the code. But you might identify it, this is my service worker code:
const staticCacheName = "site-static-v2";
const dynamicCacheName = "site-dynamic-v2";
const assets = ["/", "/stories", "/groups", "offline.html"];
// cache size limit function
const limitCacheSize = (name, size) => {
caches.open(name).then((cache) => {
cache.keys().then((keys) => {
if (keys.length > size) {
cache.delete(keys[0]).then(limitCacheSize(name, size));
}
});
});
};
// install event
self.addEventListener("install", (evt) => {
//console.log('service worker installed');
evt.waitUntil(
caches.open(staticCacheName).then((cache) => {
console.log("caching shell assets");
cache.addAll(assets);
})
);
});
// activate event
self.addEventListener("activate", (evt) => {
//console.log('service worker activated');
evt.waitUntil(
caches.keys().then((keys) => {
//console.log(keys);
return Promise.all(
keys
.filter((key) => key !== staticCacheName && key !== dynamicCacheName)
.map((key) => caches.delete(key))
);
})
);
});
// fetch events
self.addEventListener("fetch", function (event) {
event.respondWith(
fetch(event.request)
.catch(function () {
return caches.match(event.request);
})
.catch("offline.html")
);
});
This is my script in main.hbs (just like index.html).
if('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/serviceworker.js', { scope: '/' })
.then((reg) => console.log('Success: ', reg.scope))
.catch((err) => console.log('Failure: ', err));
})
}
I am making my website using express by the way.
I have tried pretty much every solution on stackoverflow but none seem to work.
Just for Information, I have also tried this for the 'fetch' part:
self.addEventListener('fetch', evt => {
evt.respondWith(
caches.match(evt.request).then(cacheRes => {
return cacheRes || fetch(evt.request).then(fetchRes => {
return caches.open(dynamicCacheName).then(cache => {
cache.put(evt.request.url, fetchRes.clone());
// check cached items size
limitCacheSize(dynamicCacheName, 15);
return fetchRes;
})
});
}).catch(() => {
return caches.match('offline.html');
})
);
}
);
(The above code also lets me login only once but doesn't let me logout unlike the previous code)
I have copy pasted almost every 'fetch' code on the internet but all of them have a problem with google auth (I am using passport for google auth).
This is my auth.js code:
const express = require("express");
const router = express.Router();
const passport = require("passport");
//Authenticate with google
//GET /auth/google
router.get("/google", passport.authenticate("google", { scope: ["profile"] }));
//Google auth callback
//GET /auth/google/callback
router.get(
"/google/callback",
passport.authenticate("google", { failureRedirect: "/" }),
function (req, res) {
// Successful authentication, redirect home.
res.redirect("/stories");
}
);
router.get("/logout", (req, res) => {
req.logout();
res.redirect("/");
});
module.exports = router;
You can also suggest a workaround with workbox
I'am currently studying React native and Express JS for my server side. So now I do practicing calling api from express js to react native and I use axios to call http request. My problem is in my cmd it shows that [Error: Network Error]. To be more specific I will show you guys my sample work both server.js and components and sample screenshot Error on my cmd.
[Error: Network Error]
Server JS:
const express = require('express');
var cors = require('cors')
const bodyParser = require('body-parser');
const mysql = require('mysql');
const connection = mysql.createPool({
host : '192.168.61.1',
user : 'root',
password : '',
database : 'sample_db'
});
if(connection) {
console.log(connection);
}
// Starting our app.
const app = express();
var cors = require('cors');
app.use(cors());
// Creating a GET route that returns data from the 'mobile' table.
app.get('/api/readings', function (req, res) {
// Connecting to the database.
connection.getConnection(function (err, connection) {
// Executing the MySQL query (select all data from the 'mobile' table).
connection.query('SELECT * FROM tbl_mobile', function (error, results, fields) {
// If some error occurs, we throw an error.
if (error) throw error;
// Getting the 'response' from the database and sending it to our route. This is were the data is.
res.send(results)
});
});
});
// get the specific meter
app.get('/api/readings/:id', function (req, res) {
// Connecting to the database.
connection.getConnection(function (err, connection) {
// Executing the MySQL query (select all data from the 'mobile' table).
connection.query('SELECT * FROM tbl_mobile WHERE id = ?',req.params.id, function (error, results, fields) {
// If some error occurs, we throw an error.
if (error) throw error;
// Getting the 'response' from the database and sending it to our route. This is were the data is.
res.send(results)
});
});
});
// Starting our server.
app.listen(3000, () => {
console.log('http://192.168.61.1/api/readings');
});
Axios:
axios.get('http://192.168.61.1:3000/api/readings')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
Response:
I've implemented axios interceptor in my VueJs project, which looks like this:
axios.interceptors.response.use(
async (response) => {
return response;
},
async (error) => {
const originalRequest = error.config;
const isPublicUrl = [
'/account/login',
'/account/register'
].some(url=> originalRequest.url.includes(url));
if(isPublicUrl){
return Promise.reject(error);
}
if(error.response.status!==401){
return Promise.reject(error);
}
const token:TokenOpts = store.getters['auth/getToken'];
try{
const res = await AuthService.refreshToken(token);
const newToken = res.data;
store.dispatch('auth/refreshToken',newToken);
originalRequest.headers.Authorization = `Bearer ${newToken.accessToken}`;
const originalResponce = await axios.request(originalRequest);
return Promise.resolve(originalResponce);
}
catch(error){
return Promise.reject(error);
}
}
)
I can refresh my token and then send original request, here no problems.
My problem is that when I intercept an error, and end up inside error handler: async (error) => { HERE ... I already have an error in Chrome console. How can I prevent logging this error into the console?
This has nothing to do with VueJs. All errors will be logged in the console as that is the default option enabled in your browser. Follow this link to disable your network error messages. Suppress Chrome 'Failed to load resource' messages in console
I am writing automated tests using Jest & Puppeteer for a Front-end application written in Vue.js
So far I managed to write a set of tests, but they all reside in the same file:
import puppeteer from 'puppeteer';
import faker from 'faker';
let page;
let browser;
const width = 860;
const height = 1080;
const homepage = 'http://localhost:8001/brt/';
const timeout = 1000 * 16;
beforeAll(async () => {
browser = await puppeteer.launch({
headless: false, // set to false if you want to see tests running live
slowMo: 30, // ms amount Puppeteer operations are slowed down by
args: [`--window-size=${width},${height}`],
});
page = await browser.newPage();
await page.setViewport({ width, height });
});
afterAll(() => {
browser.close();
});
describe('Homepage buttons', () => {
test('Gallery Button', async () => {
// navigate to the login view
await page.goto(homepage);
await page.waitFor(1000 * 0.5); // without this, the test gets stuck :(
await page.waitForSelector('[data-testid="navBarLoginBtn"]');
await page.click('[data-testid="navBarLoginBtn"]'),
await page.waitForSelector('[data-testid="navBarGalleryBtn"]');
await page.click('[data-testid="navBarGalleryBtn"]'),
// test: check if we got to the gallery view (by checking nr of tutorials)
await page.waitForSelector('.card-header');
const srcResultNumber = await page.$$eval('.card-header', (headers) => headers.length);
expect(srcResultNumber).toBeGreaterThan(1);
}, timeout);
});
describe('Register', () => {
const btnLoginToRegister = '#btn-login-to-register';
const btnRegister = '#btn-register';
const btnToLogin = '#btn-goto-login';
test('Register failed attempt: empty fields', async () => {
// navigate to the register form page via the login button
await page.goto(homepage);
await page.waitForSelector(navLoginBtn);
await page.click(navLoginBtn);
await page.waitForSelector(btnLoginToRegister);
await page.click(btnLoginToRegister);
// test; checking for error messages
await page.waitForSelector(btnRegister);
await page.click(btnRegister);
const errNumber = await page.$$eval('#errMessage', (err) => err.length);
expect(errNumber).toEqual(3);
}, timeout);
test('Register failed: invalid char count, email format', async () => {
// fill inputs
await page.waitForSelector('#userInput');
await page.type('#userInput', 'a');
await page.waitForSelector('#emailInput');
await page.type('#emailInput', 'a');
await page.waitForSelector('#emailInput');
await page.type('#passInput', 'a');
await page.waitForSelector(btnRegister);
await page.click(btnRegister);
// test: check if we 3 errors (one for each row), from the front end validations
const err = await page.$$eval('#errMessage', (errors) => errors.length);
expect(err).toEqual(3);
}, timeout);
test('Register: success', async () => {
await page.click('#userInput', { clickCount: 3 });
await page.type('#userInput', name1);
await page.click('#emailInput', { clickCount: 3 });
await page.type('#emailInput', email1);
await page.click('#passInput', { clickCount: 3 });
await page.type('#passInput', password1);
await page.waitForSelector(btnRegister);
await page.click(btnRegister);
// test: check if go to login link appeared
await page.waitForSelector(btnToLogin);
await page.click(btnToLogin);
// await Promise.all([
// page.click(btnToLogin),
// page.waitForNavigation(),
// ]);
}, timeout);
test('Register failed: email already taken', async () => {
// navigate back to the register form
await page.waitForSelector(btnLoginToRegister);
await page.click(btnLoginToRegister);
await page.click('#userInput');
await page.type('#userInput', name2);
await page.click('#emailInput');
await page.type('#emailInput', email1); // <- existing email
await page.click('#passInput');
await page.type('#passInput', password2);
await page.click(btnRegister);
const err = await page.$eval('#errMessage', (e) => e.innerHTML);
expect(err).toEqual('Email already taken');
}, timeout);
});
I would like to be able to have a single test file that does the beforeAll and afterAll stuff, and each test suite: HomepageButtons, Register, etc. to reside in it's own test file. How would I be able to achieve this?
I've tried splitting tets into:
testsUtils.js that would contain the beforeAll and afterAll hooks and code but it doesn't guarantee that it runs when it needs: the beforeAll code to fire before all other test files and the afterAll code to fire after all the test files finished.
Sorry, I'd rather comment on your question, but I don't have reputation for that. Anyway, I think that you are looking for something like a "global beforeAll" and "global afterAll" hooks, right? Jest has it alread. It's called "globalSetup" and "globalTeardown".
Take a look at globalSetup. Excerpt:
This option allows the use of a custom global setup module which
exports an async function that is triggered once before all test
suites.
The Global Teardown one goes the same.
I think you'll have a headache trying to get a reference to the page or browser in globalSetup/globalTeardown and I confess that I never try this. Maybe the answer for that problem (if you have it) is on this page, under "Custom example without jest-puppeteer preset section.
Also there is a repo that tries to facilitate Jest + Puppeteer integration. Maybe you find it util: repo.
Good luck. :)