Jest Puppeteer E2E tests pass locally but time out Heroku - testing

Writing some e2e tests using Jest and Puppeteer. These tests pass locally but they fail on Heroku CI (the beforeAll hook times out after 15 seconds).
I added some console.log statements and figured out that the beforeAll hook is timing out in the waitForSelector line. I'm wondering if there's an issue with navigating to localhost on Heroku?
const port = process.env.PORT || 3001;
beforeAll(async () => {
// Set a definite size for the page viewport so view is consistent across browsers
await page.setViewport({
width: 1366,
height: 768,
deviceScaleFactor: 1,
});
await page.goto("http://localhost:" + port);
await page.waitForSelector("div > div > button");
await page.click("div > div > button");
await page.waitForSelector("input#username");
await page.type("input#username", process.env.TEST_USERNAME);
await page.type("input#password", process.env.TEST_PASSWORD);
await page.keyboard.press("Enter");
});
describe("Basic authentication e2e tests", () => {
it("should load the business table and display total number of accounts", async () => {
await page.waitForSelector(".business-table");
await page.waitForSelector(".paginator-counter");
let element = await page.$(".paginator-counter");
let value = await page.evaluate((el) => el.textContent, element);
let firstWord = value.split(" ")[0];
expect(firstWord).toBe("1-25");
}, 10000);
});

Related

Upload a local image file via puppeteer connect on Chrome extension

Task: I'm building a chrome extension with a popup (panel_chatwindow.js) window (standalone window). I want to take a local image file (e.g., /1.png) and upload it to a website that has a <input type='file'/> element (e.g., any services that require to upload a user photo).
Note
Other than upload file, the code can execute other web macro just fine.
My current code:
let browserWSEndpoint = "";
const puppeteer = require("puppeteer");
async function initiatePuppeteer() {
await fetch("http://localhost:9222/json/version")
.then((response) => response.json())
.then(function (data) {
browserWSEndpoint = data.webSocketDebuggerUrl;
})
.catch((error) => console.log(error));
}
initiatePuppeteer();
$("#puppeteer-button").on("action", doPuppeteerThings);
// Assign button to puppeteer function
async function doPuppeteerThings(event, actionArray) {
const browser = await puppeteer.connect({
browserWSEndpoint: browserWSEndpoint,
});
const page = await browser.newPage();
await page.setViewport({
width: 1600,
height: 1000,
});
await page.goto("website", {
waitUntil: "networkidle2",
});
await page.waitForSelector("input[type=file]");
await page.waitFor(2000);
const input = await page.$('input[type="file"]');
await input.uploadFile("/1.PNG");
})
Error message on popup window console (panel_chatwindow.js)
panel_chatwindow.js:160 is the await input.uploadFile("/1.PNG"); line
Using puppeteer.launch works perfectly fine with the code, i.e., can upload a local file to a website once launching the node server.
I don't have to use puppeteer to upload image files, any other way on chrome extension would be just fine.
I'm aware of this post: https://github.com/puppeteer/puppeteer/issues/4405 but i am not able to address my issue with any answers in the thread.

Testing stripe.redirectToCheckout using selenium

The problem that I am experiencing is that when I click the checkout button on Stripe domain page (the selenium is losing the session ). I am using JWT tokens, so the question is how should I access localStorage to get the token and later on to pass it to the browser ( I am not sure that this will actually work, so if there is some other way please give me a hand ). I got this code before the execution of the test:
const { Before, After } = require('cucumber');
const { Builder } = require('selenium-webdriver');
const firefox = require('selenium-webdriver/firefox');
const config = require('../config');
let driver;
Before(async () => {
driver = await new Builder().forBrowser('firefox');
if (config.headlessBrowser === 'true') {
const screen = {
width: 1920,
height: 1200,
};
driver = driver
.setFirefoxOptions(new firefox.Options().headless().windowSize(screen));
}
driver = driver
.build();
driver.manage().window().maximize();
exports.driver = driver;
});
After(() => {
driver.quit();
});
And the tests for clicking the submit button and expected URL when redirected:
Then('I press stripe checkout button', {timeout: waitTime}, async () => {
await d.driver.findElement(By.className('SubmitButton')).click();
})
Then(/I should be redirected to "(.*)"/, {timeout: waitTime}, async (path) => {
await d.driver.wait(until.urlIs(config.url + path), waitTime);
});
I can see that it is trying to redirect me to the correct success URL, but unfortunately, it logs me out( I assume that is happening because the session is lost ).

logging in with cypress/puppeteer

I have an application that uses Okta to log in. I am trying to set up an automation test using Cypress with Puppeteer. The problem I am having is that when I launch puppeteer with {headless: true}, it doesn't fill in the login form. {headless: false} opens another browser window outside of Cypress and works as expected. It fills in the login form, logs in correctly, but I can't run any cypress tests in that window.
on('task', {
login({username, password}) {
return (async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto(config.baseUrl);
await page.waitForNavigation();
await page.waitFor('#okta-signin-username');
await page.type('#okta-signin-username', username, {delay: 100});
await page.waitFor('#okta-signin-password');
await page.type('#okta-signin-password', password, {delay: 100});
await page.click('#okta-signin-submit');
await page.waitForNavigation();
return true;
};
});
Here's how it's being used:
it('my test', () => {
cy.visit('/');
cy.viewport(1280, 1024);
cy.login('username', 'password');
//tests go here
});
The above code opens Chromium and correctly logs in but then the tests fail. It doesn't log in with {headless: true}. Am I missing something here?
Any help would be appreciated.
Thanks

How do I split my Jest + Puppeteer tests in multiple files?

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. :)

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.