Puppeteer - Login falied only at automation - authentication

I'm trying to do login at a web page but when I try to do it through Puppeteer it fails.
The code below should redirect with success to https://prenotami.esteri.it/UserArea but it fails and redirect to https://prenotami.esteri.it/Home/Login
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({headless:false, ignoreDefaultArgs: ['--enable-automation']});
const page = await browser.newPage();
await page.goto('https://prenotami.esteri.it/UserArea');
await page.evaluate(() => {
document.querySelector('#login-email').value = "****#gmail.com"
document.querySelector('#login-password').value = "pass***"
document.querySelector('button[type="submit"]').click()
})
})();
As it can be seen, I've tried to disable automation.I also tried to put some delay but I couldn't realize where exactly
SOLVED:
I've changed the code and it works now
const extra = require('puppeteer-extra');
const stealth = require('puppeteer-extra-plugin-stealth')
extra.use(stealth())
extra
.launch({
headless: false,
args: ['--no-sandbox']
})
.then(async browser => {
const page = await browser.newPage();
await page.goto('https://prenotami.esteri.it/');
page.setDefaultNavigationTimeout(30000)
await page.type("#login-email", "*****#gmail.com")
await page.type("#login-password", "*****pass")
await page.click('button[type="submit"]')
await page.waitForNavigation({ waitUntil: 'domcontentloaded' })
await page.goto("https://prenotami.esteri.it/Services/Booking/751")
await page.waitForNavigation({ waitUntil: 'domcontentloaded' })

Related

TypeScript Playwright: Saving Storagestate via Sign in via API request and then using inside BowserContext does not work - takes me back to login page

I need to decide whether to go with Playwright as an E2E Automation tool or not.
The following piece of code using backend API for sign in is working fine.
But when I use the saved Storagestate file inside the BrowserContext, then it fails to sign me in, hence taking me back to the Login page.
test.describe('User login', async () => {
var browser: any;
test.beforeEach(async () => {
browser = await chromium.launch({
channel: 'chrome',
headless: false
});
});
const requestContext = await request.newContext();
const response = await requestContext.post(`${testConfig.envConfig.apiURL}/login/authorize`, {
form: {
'email': 'user.tech+admin#xxxx.com',
'password': 'xxxxxx'
}
});
const responseJson = await response.json();
console.log(responseJson);
expect(response.ok()).toBeTruthy();
// Save signed-in state.
await requestContext.storageState({ path: './.storagestates/adminUserStorageState.json' });
await requestContext.dispose();
// Using the Storagesate in the BrowserContext
// page should sign in as a admin user.
test.use({ storageState: './.storagestates/adminUserStorageState.json' });
test('Version 1: Cookies stored via Auth. by backend API', async ({ page }) => {
let url = `/brand/f99f06fb1f121234aaf/gallery`;
await page.goto(url, {timeout: 60000});
});
test('Version 2: Cookies stored via Auth. by backend API', async () => {
// page should sign in as a buyer user.
const userContext = await browser.newContext({ storageState: './.storagestates/buyerUserStorageState.json' });
const page = await userContext.newPage();
await page.goto(`/`, {timeout: 60000});
});
});

Express router's mock post handler doesn't work as expected

I have a documents router which has router.post('/mine', [auth, uploadFile], async (req, res) => { ... }) route handler. The actual implementation of this route handler is below.
documents.js router
const createError = require('./../helpers/createError');
const auth = require('./../middlewares/auth');
const uploadFile = require('./../middlewares/uploadFile');
const express = require('express');
const router = express.Router();
router.post('/mine', [auth, uploadFile], async (req, res) => {
try {
let user = await User.findById(req.user._id);
let leftDiskSpace = await user.leftDiskSpace();
if(leftDiskSpace < 0) {
await accessAndRemoveFile(req.file.path);
res.status(403).send(createError('Your plan\'s disk space is exceeded.', 403));
} else {
let document = new Document({
filename: req.file.filename,
path: `/uploads/${req.user.username}/${req.file.filename}`,
size: req.file.size
});
document = await document.save();
user.documents.push(document._id);
user = await user.save();
res.send(document);
}
} catch(ex) {
res.status(500).send(createError(ex.message, 500));
}
});
module.exports = router;
I'm currently writing integration tests using Jest and Supertest. My current documents.test.js test file is below:
documents.test.js test file
const request = require('supertest');
const { Document } = require('../../../models/document');
const { User } = require('../../../models/user');
const fs = require('fs');
const path = require('path');
let server;
describe('/api/documents', () => {
beforeEach(() => { server = require('../../../bin/www'); });
afterEach(async () => {
let pathToTestFolder = path.join(process.cwd(), config.get('diskStorage.destination'), 'user');
// Remove test uploads folder for next tests
await fs.promises.access(pathToTestFolder)
.then(() => fs.promises.rm(pathToTestFolder, { recursive: true }))
.catch((err) => { return; });
// Remove all users and documents written in test database
await User.deleteMany({});
await Document.deleteMany({});
server.close();
});
describe('POST /mine', () => {
it('should call user.leftDiskSpace method once', async () => {
let user = new User({
username: 'user',
password: '1234'
});
user = await user.save();
let token = user.generateAuthToken();
let file = path.join(process.cwd(), 'tests', 'integration', 'files', 'test.json');
let documentsRouter = require('../../../routes/documents');
let errorToThrow = new Error('An error occured...');
user.leftDiskSpace = jest.fn().mockRejectedValue(errorToThrow);
let mockReq = { user: user };
let mockRes = {};
documentsRouter.post = jest.fn();
documentsRouter.post.mockImplementation((path, callback) => {
if(path === '/mine') {
console.warn('called');
callback(mockReq, mockRes);
}
});
const res = await request(server)
.post('/api/documents/mine')
.set('x-auth-token', token)
.attach('document', file);
expect(documentsRouter.post).toHaveBeenCalled();
expect(user.leftDiskSpace).toHaveBeenCalled();
});
});
});
I create mock post router handler for documents.js router. As you can see from mockImplementation for this route handler, it checks if the path is equal to '/mine' (which is my supertest endpoint), then calls console.warn('called'); and callback. When I run this test file, I can not see any yellow warning message with body 'called'. And also when POST request endpoint /api/documents/mine the server doesn't trigger my mock function documentsRouter.post. It has never been called. So I think the server's documents router is not getting replaced with my mock post route handler. It still uses original post route handler to respond my POST request. What should I do to test if my mock documentsRouter.post function have been called?
Note that my User model has a custom method for checking left disk space of user. I also tried to mock that mongoose custom method but It also doesn't work.

page.goto (puppeteer) does not stop calling itself when opening the same url it is navigating to

I am trying to convert an html web page into a pdf file by using puppeteer. It is converting the html web page into pdf file but the problem is that, page.goto is not stopping after its job is done. When this happens, the terminal I run this localhost server from becomes a little unresponsive. I also deployed it on heroku but when I visit the url, the server responds me with error code 503 and it does not convert to pdf there.
let printPDF = async () => {
try {
const browser = await puppeteer.launch({ headless: true});
const page = await browser.newPage();
await page.goto('http://localhost:3000/preview')
await page.pdf({ format: 'A4' , path: `${__dirname}/contact.pdf`, printBackground: true});
await page.close();
await browser.close();
console.log("repeat") //logging to see its repetition
}
catch (error) {
await page.close();
await browser.close();
}
}
getReviewPage = ((req, res) => {
printPDF()
res.sendFile(`${__dirname}/form.html`)
})
app.route('/preview').get(getReviewPage);
Since printPDF is an async function returning Promise, you need to wait for it to finish, with then for example:
const getReviewPage = (req, res) => {
printPDF().then(() => res.sendFile(`${__dirname}/form.html`)
}

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 get text content with puppeteer and test it

I am trying to scrape some text and than compare it in jest except.
My code:
expect(received).toBe(expected) // Object.is equality
Returns:
Expected: Forgot password
Received: {}
describe("login to Synthrone tests", () => {
test("no existed user try", async () => {
browser = await puppeteer.launch(chromeOptions);
const page = await browser.newPage();
await page.goto("https://admin.manager.test-synthrone.com/");
const a = page.evaluate(() =>
document.querySelector(".additional-user-actions").textContent.trim()
);
expect(a).toBe("Forgot password");
});
});