Running multiple tests with Jest and Puppeteer - node.js

I have 2 test examples and when I run them both at once it seems that there is no time to execute them both till end. When I run them separately, they go through till the end.
I've read that Puppeteer-cluster can help out running multiple tests at once but the process stops right after page.goto() function. I'm not sure if this is the right approach to my issue so feel free to offer other solutions that Puppeteer-cluster.
test1:
const { Cluster } = require('puppeteer-cluster');
const timeout = 100000
const { toMatchImageSnapshot } = require('jest-image-snapshot')
expect.extend({ toMatchImageSnapshot })
describe('login', () => {
test('test user login', async () => {
await page.goto(URL + 'login.jsp', { waitUntil: 'domcontentloaded' });
const cluster = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: 2,
});
await cluster.task(async ({ page, data: url }) => {
await page.goto(URL + url, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('input[name=username]')
await page.type('input[name=username]', username)
await page.type('input[name=password]', password)
const loginFormFilled = await page.screenshot();
expect(loginFormFilled).toMatchImageSnapshot({
failureThreshold: '0.01',
failureThresholdType: 'percent'
});
await page.waitForSelector('.roundButton').then(async () =>{
await page.evaluateHandle(() => {
let button = [...document.querySelectorAll('.roundButton')].find(element => element.textContent === 'Prijavi se');
if(button){
button.click();
}
});
})
await page.waitForSelector(".profilePic")
const image = await page.screenshot();
expect(image).toMatchImageSnapshot({
failureThreshold: '0.10',
failureThresholdType: 'percent'
});
});
cluster.queue('login.jsp');
await cluster.idle();
await cluster.close();
}, timeout);
});
The second test is almost the same just instead of login I'm testing registration process.
I've tried the same examples as here https://www.npmjs.com/package/puppeteer-cluster but the test stops right after page.goto and ends as passed test.
In near future ill have 30-40+ tests similar to test1 and I need to run them with one command instead of one by one.

Related

Appium, webdriver.io, Runner in test suite only shows one test despite beeing multiple

I'm trying to create a test suite with multiple test cases. When I add 2 test cases in the suite and run them, in runner I see that the total count is 1 of 1 instead of 2.
Image of the runner.
I'm using appium with webdriver.io and mocha, where is my mistake?
thanks!
describe('News tests', () => {
it('Creation of news', async () => {
var newstab = await $('~NewsTab');
await newstab.waitForDisplayed({timeout: 30000} );
await newstab.click();
var createnewbtn = await $('~create news button');
await createnewbtn.waitForDisplayed({timeout: 30000} );
await createnewbtn.click();
var HeadlineField = await $('~headline input');
await HeadlineField.waitForDisplayed({timeout: 30000} );
await HeadlineField.addValue('Automated Test');
var DeskField = await $('~description input');
await DeskField.waitForDisplayed({timeout: 30000} );
await DeskField.addValue('Automated Test description');
var closebtn = await $('~close button');
await closebtn.waitForDisplayed({timeout: 30000} );
await closebtn.click();
});
it('Create new and cancel', async () => {
var newstab = await $('~NewsTab');
await newstab.waitForDisplayed({timeout: 30000} );
await newstab.click();
var createnewbtn = await $('~create news button');
await createnewbtn.waitForDisplayed({timeout: 30000} );
await createnewbtn.click();
var closebtn = await $('~close button');
await closebtn.waitForDisplayed({timeout: 30000} );
await closebtn.click();
});
});
I figured it out, apparently, I was using the default test runner which didn't show the steps of the tests, new runner fixed the issue

Can not access some url with jest-cucumber

I am using some simple code to implement e2e testes with jest-cucumber.
export const givenOpenUrl = (given) => {
given(/^I open "(.*)"$/, async (arg0) => {
await page.goto(`${arg0}`)
})
}
export const thenMatchPageTitle = (then) => {
then(/^I see "(.*)" in the title$/, async (arg0) => {
await expect(page.title()).resolves.toMatch(arg0)
})
}
Also this code fails as well :
describe('Nozzle AI', () => {
beforeAll(async () => {
await page.goto('https://nozzle.ai', {waitUntil: 'domcontentloaded'});
});
it('should be titled "Nozzle"', async () => {
await expect(page.title()).resolves.toMatch('Nozzle');
});
});
However, I get different behaviors based on the URL that is passed.
For example, the tests are passing if I use https://www.google.com/ and failing when I use https://www.nozzle.ai/ with the following error:
Navigation failed because the browser has disconnected!
It seems that the page.title().resolve has this error:
Expected an Error, but "" was thrown
I noticed that I am able to run this code with success :
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://nozzle.ai', {
waitUntil: 'networkidle2',
});
const title = await page.title()
console.log(title)
await browser.close();
})()
Any suggestions would be highly appreciated!

Async Function returning "undefined" once reaching SetInterval in Node.js

For some reason my script will return once it reaches the "ticker" setinterval loop instead of doing whats inside of the loop and then returning.
Why is it returning early instead of doing the setinerval?
The javascript code
const puppeteer = require('puppeteer');
async function test(url3) {
let counter = 0;
try {
const browser = await puppeteer.launch({
headless: true,
defaultViewport: null
});
const page = await browser.newPage();
const url = 'https://yandex.com/images/';
await page.goto(url);
await page.evaluate(() => {
document.querySelector('.input__cbir-button').firstElementChild.click();
})
await page.focus('input[name="cbir-url"]')
await page.keyboard.type(url3)
await page.keyboard.press('Enter');
page
.on('response', response =>{
//console.log(`${response.url()}`)
if(`${response.url()}` == "https://yandex.com/clck/jclck/"){
counter++;
}
})
SetInterval function where it messes up
var ticker = setInterval(async () => {
if(counter === 2){
clearInterval(ticker)
if(page.$('.cbir-no-results__content' !== null)){
await browser.close();
return('no images found');
} else {
await page.screenshot({path: 'test.png', fullPage: true});
await browser.close();
}
}
})
} catch(err) {
//console.log(err)
}
}
Return
test("animage.jpg").then(ans => {
console.log(ans)
})
setInterval will always return a number: the id of the timer that you can then pass to clearInterval. What you likely want is to wrap the entire thing in a Promise which it then resolved when your condition is met:
// no need for async, we're explicitly returning a Promise
function test() {
return new Promise(resolve => {
const handle = setInterval(() => {
if (conditionMet) {
clearInterval(handle);
resolve();
}
}, 100); // or however often
});
}
This is one of the few times where it is correct to explicitly construct a Promise.

Ensure element is exist in the page, to ensure is logged in or any other action

I'm using puppeteer, node js I'm writing a script to log in it successfully logged, but I need to write code to ensure it successfully logged in by ensure some element is present, I need to write if element is present focused on it or if it not present print "element is not present".
This the script I've written:
const puppeteer = require('puppeteer');
async function log_in() {
const browser = await puppeteer.launch({
headless: false,
args: ['--Window-size=1929,1170', '--Window-position=0,0']
});
const page = await browser.newPage();
await page.setViewport({ 'width': 1366, 'height': 768 });
await page.goto(URL, { waitUntil: 'networkidle2' });
await page.click('.fancybox-item');
await delay(1000);
// fun for waiting
function delay(time) {
return new Promise(function (resolve) {
setTimeout(resolve, time)
});
}
const UserName = 'xxxxxxxxxx';
const Password = '222222';
page.click('selector');
await delay(1000);
//Focus on user name
await page.focus('selector');
await delay(2000);
await page.type('selector', UserName);
await delay(2000);
//Focus on password
await page.focus('selector');
await page.type('selector'', Password);
// Clicking the link will indirectly cause a navigation
page.click('selector');
await delay(5000);
if (await page.waitForSelector('selector')) {
console.log("found")
} else console.log("not found");
await delay(5000);
} log_in();
Note: the code is work successfully but it doesn't print anything in console output.
You can use page.$(selector). It returns an element or null if no matches the selector. And you need to wait for navigation after you click on submit button using page.waitForNavigation([options]) probably.
...
// Clicking the link will indirectly cause a navigation
await page.click('selector')
await page.waitForNavigation()
const element = await page.$('selector')
if (element) {
console.log ('found')
} else {
console.log ('not found')
}
...
First of all, I'm afraid you have lacks experience in working with asynchronous Javascript. It's crucial to understand this concept since most of the functions of Puppeteer is async. The same other libraries or tools like Selenium.
Try to learn more about that https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous
Answering on your problem: I think it'd be to use waitForSelector() instead of document.querySelector (page.$) as #drobnikj suggested. First of all waitForSelector() waits for the element so you can handle elements that might load asynchronously. It might not be the case on your page but in general, it's a better approach I believe. All that you need to do is to handle a potential exception.
My proposition is:
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({
headless: false,
args: ['--Window-size=1929,1170', '--Window-position=0,0']
});
const page = await browser.newPage()[0];
async function logIn(loginPageUrl, userName, password) {
const fancyBoxItemSelector = '.fancybox-item';
const loginInputSelector = '';
const passwordInputSelector = '';
const loginRedirectionLink = '';
const expectedSelectorAfterLogin = '';
await page.goto(URL, { waitUntil: 'networkidle2' });
await page.waitForSelector(fancyBoxItemSelector);
await page.click(fancyBoxItemSelector);
await page.waitForSelector(loginInputSelector);
await page.click(loginInputSelector);
await page.type(loginInputSelector, userName);
await page.waitForSelector(passwordInputSelector);
await page.click(passwordInputSelector);
await page.type(passwordInputSelector, password);
await page.waitForSelector(loginRedirectionLink);
const response = page.waitForNavitation({ waitUntil: 'networkidle2' });
await page.click(loginRedirectionLink);
await response;
try {
await page.waitForSelector(expectedSelectorAfterLogin);
console.log('Selector found!');
} catch (err) {
console.log('Selector not found!');
}
}
(async () => {
await logIn('', '', '')
})()
I allowed myself to polish your code. I hope it's clear what I have done here.

use same browser instance?

Hi I am trying to make a screenshot service
const puppeteer = require('puppeteer');
var resWidth = 1366;
var resHeight = 1000;
var browser;
(async () => {
browser = await puppeteer.launch({ignoreHTTPSErrors: true});
});
and when I receive a work I try to do
data.forEach(function(d){
try {
console.log(d["url"]);
(async () => {
var page = await browser.newPage();
await page.setViewport({width: resWidth, height: resHeight});
await page.goto(d["url"], {timeout: 90000, waitUntil: 'networkidle'});
await page.screenshot({path: './picdata/' + d['id'] + '.png' ,fullPage: true});
await page.close();
})();
} catch(e) {}
});
but I can't... here is the error:
(node:40596) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 7): TypeError: Cannot read property 'newPage' of undefined
I don't want to open a new browser for each screenshot launching browser takes time and requires more memory?
what should I do?
The problem:
(async () => {
browser = await puppeteer.launch({ignoreHTTPSErrors: true});
});
This code never gets executed. Why? because it's not a true closure.
More on closures, here.
That being said, that wont have work for your given scenario, as they are async tasks.
My try with your example:
'use strict';
const puppeteer = require('puppeteer');
const resWidth = 1366;
const resHeight = 1000;
let browser;
let page;
async function launchBrowser() {
browser = await puppeteer.launch({ headless: true }); //this "{ headless: true }" will ensure a browser window is not open a single time.
};
launchBrowser().then((x) => { // wait until browser has launched
data.forEach(async (d) => {
try {
page = await browser.newPage(); // the document is not very clear about this method, so I am still unsure if I could move this in the above launchBrowser() method.
await page.setViewport({ width: resWidth, height: resHeight });
await page.goto(d['url'], { timeout: 90000, waitUntil: 'networkidle' });
await page.screenshot({ path: './picdata/' + d['id'] + '.png', fullPage: true });
}
catch (e) {
console.log(e);
await browser.close(); // close browser if there is an error
}
});
})
.then(() => {
await browser.close(); // close browser finally.
});
Pro Tip: Start using let, const instead of var.
There is a great article on this, here

Resources