Automated browser testing with puppeter and jest - jestjs

I came across one big issue combining puppeteer with jest. Whenever I hit "npm run test" this test fails displaying: "Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.". This warning also appears even if I pass timeout as a third argument to test function or calling jest.setTimeout(timeout) from inside of beforeEach method callback. What the problem is there, could you guys help me with this. P.S. I'm using jest and puppeteer packages separately
const pup = require('puppeteer')
let browser, page
beforeEach(async _ => {
browser = await pup.launch({
headless: false
})
page = await browser.newPage()
await page.goto('localhost:3000')
})
afterEach(async _ => await browser.close())
test('Login function', async _ => {
await page.click('link')
const url = await page.url()
expect(url).toMatch(/accounts\.google\.com/)
})

In Mocha and Jest it mostly looks the same. You manually must override the default timeout when you run async scripts that will execute longer than the default timeout.
test('Login function', async _ => {
//..
}, 60000);
Alternative you override the global timeout for your test via CLI by using:
--testTimeout=<number>

Related

Unable to run mockttp with Cypress

I'm trying to run the mockttp with cypress. I used the example that's listed in project github. I changed the port to run on 3000 but I am getting an error saying Cannot add rules before the server is started.
/*global cy:true,before:true*/
/// <reference path="../../node_modules/cypress/types/index.d.ts" />
const superagent = require("superagent");
const mockServer = require("mockttp").getLocal();
describe('mockttp test' , () => {
beforeEach(() => {
mockServer.start(3000);
});
afterEach(() => {
mockServer.stop();
});
it("lets you mock requests, and assert on the results", async () => {
// Mock your endpoints
const mockedPath = mockServer.forGet("/mocked-path");
// ERROR OCCURS HERE
await mockedPath.thenReply(200, "A mocked response");
// Make a request
const response = await superagent.get("http://localhost:3000/mocked-path");
// Assert on the results
expect(response.text).to.equal("A mocked response");
});
});
You need to wait until the server is actually started before running your test, by waiting for the promises returned by mockServer.start() (and by .stop()).
You can either make your beforeEach & afterEach functions async and then await those lines, or you can just add return to return the promise so that Mocha waits for them automatically.

Firebase-admin nodejs SDK doens't connect

I'm attempt to connect to firebase/firestone using the nodejs SDK,however I it doesn't connect. I've attempted to connect multiple times, using setInterval but nothing works.
First, I initialize the the firebase using the credentials and the databaseURL, after this I get the databaseRef, and in the end I attempt to write to the database.
I've checked the ./info/connected on setInterval with timeout of 1000ms and mocha --timeout flag to 5000ms, and always marks as offline.
I've checked the credentials, when is a wrong credential or config json, they give an JSON parse error message(cause I have several storage instances, each connected according to a flag spawned during the execution time).
I'm using the TDD approach on my application, so, I have to mock the entire database and check against the resulted values of each operation. I've wrote a controller for the task of handling the firebase/firestone work, but I if I can't connected it has no use.
The code goes here:
const analyticsFirebaseMock = admin.initializeApp({
credentials: admin.credential.cert(analyticsCredentials),
databaseURL: process.env.ANALYTICS_FIREBASE_URL
}, 'analyticsMock')
const analyticsDbRef = analyticsFirebaseMock.database()
beforeEach(() => {})
afterEach(() => sinon.restore())
describe('POST - /analytics', () => {
it('should save the analytics data for new year', async (done) => {
const itens = 1
const price = 599.00
setInterval(() => {
clockAnalyticsDbRef.ref(`.info/connected`).once('value', (value) => {
if (value.val() === true) console.log('connected')
else console.error('offline')
})
}, 1000)
await analytics.updateAnalytics(user, itens, price)
await analyticsDbRef.ref(`${user}`).once('value', (value) => {
expect(R.view(userLens, value)).to.be.equals(user)
done()
})
})
})
In the above code, I use async/await on analyticsDbRef cause of the asynchronous characteristic of the js. Call the controller, await the query result, conclude with done. The test fails with timeout, expecting done to be called.
What could I doing wrong?

Issue in Mocking Fetch API with Jest

I am trying to mock fetch API with Jest but facing issue that assertions run before the fetch is resolved so tests always fail. Here is the sandbox : https://codesandbox.io/s/jest-testing-2evvr
I also tried async/await but still same issue :
it("should update result on successful fetch", async () => {
const updateSearchResult = jest.fn();
await handleSearch("api");
expect(updateSearchResult).toHaveBeenCalledTimes(1);
});
How can fix this , so that code runs after finally block of fetch . thanks
handleSearch doesn't return a promise which is an antipattern, so await handleSearch("api") doesn't affect anything. It will also fail if no callback is provided. Native promises allow to provide a non-function to then, in this case it's ignored. Since a callback should receive same parameters as then callback, it should be:
function handleSearch(searchQuery, callback) {
return fetch(searchQuery)
.then(callback)
...
}
well this is my approach to mock a async function :
let resolve , reject;
jest.mock("../module,()=>({
...jest.requireActual("../module"),
handleSearch:jest.fn(()=> new Promise((res,rej)=>{
resolve = res;
reject = rej
}))
}))
it("should update result on successful fetch", async () => {
const updateSearchResult = jest.fn();
await handleSearch();
resolve("api")
expect(handleSearch).toHaveBeenCalledTimes(1);
});

How write simple Jest mock for express-session's req.session.destroy()

I am writing a grapqhl server that has a simple logout mutation. Everything works as expected when I run the server and I can log out by destroying the session and clearing the cookie just fine.
Here is the resolver:
export default async (root, args, context) => {
console.log("THIS WILL LOG")
await new Promise((res, rej) =>
context.req.session.destroy(err => {
if (err) {
return rej(false);
}
context.res.clearCookie("qid");
return res(true);
})
);
console.log("NEVER HERE BEFORE TIMEOUT");
// 4. Return the message
return {
code: "OK",
message: "You have been logged out.",
success: true,
item: null
};
};
I am attempting to write a simple test just to verify that the req.session.destroy and res.clearCookie functions are actually called. At this point I AM NOT attempting to test if a cookie is actually cleared, as I am not actually starting up the server, I am just testing that the graphql resolver was ran correctly and that it called the right functions.
Here is a portion of my test:
describe("confirmLoginResolver", () => {
test("throws error if logged in", async () => {
const user = await createTestUser();
const context = makeTestContext(user.id);
context.req.session.destroy = jest
.fn()
.mockImplementation(() => Promise.resolve(true));
context.res.clearCookie = jest.fn();
// this function is just a helper to process my graphql request.
// it does not actually start up the express server
const res = await graphqlTestCall(
LOGOUT_MUTATION, // the graphql mutation stored in a var
null, // no variables needed for mutation
null // a way for me to pass in a userID to mock auth state,
context // Context override, will use above context
);
console.log(res);
expect(context.req.session.destroy).toHaveBeenCalled();
// expect(res.errors.length).toBe(1);
// expect(res.errors).toMatchSnapshot();
});
});
Again, everything works correctly when actually running the server. The problem is that when I attempt to run the above test, I always get a jest timeout:
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
The reason is that the await section of above resolver will hang because it's promise.resolve() is never being executed. So my console will show "THIS WILL LOG", but will never get to "NEVER HERE BEFORE TIMEOUT".
I suspect I need to write a better jest mock to more accurately simulate the callback inside of context.req.session.destroy, but I can not figure it out.
Any ideas how I can write a better mock implementation here?
context.req.session.destroy = jest
.fn()
.mockImplementation(() => Promise.resolve(true));
Is not cutting it. Thoughts?
Try
context.req.session.destroy = jest
.fn()
.mockImplementation((fn) => fn(false));

How to set a max timeout for Jest test running Puppeteer?

Tried looking through the docs, but didn't find a way to set a max timeout for a test case. Seems like a simple feature.
import puppeteer from 'puppeteer'
test('App loads', async() => {
const browser = await puppeteer.launch({ headless: false, slowMo: 250 });
const page = await browser.newPage();
await page.goto('http://localhost:3000');
await browser.close();
});
Jest's test(name, fn, timeout) function can take a 3rd parameter that specifies a custom timeout.
test('example', async () => {
...
}, 1000); // timeout of 1s (default is 5s)
Source: https://github.com/facebook/jest/issues/5055#issuecomment-350827560
You can also set the timeout globally for a suite using jest.setTimeout(10000); in the beforeAll() function:
beforeAll(async () => {
jest.setTimeout(10000); // change timeout to 10 seconds
});

Resources