How to test an alert displaying using Jest + Puppeteer? - javascript

I need to verify that after a button click an alert is displayed.
I tried the following:
it('should display an alert when the user tries to add empty value', () => {
jest.setTimeout(100000);
page.alert = jest.fn(text => text);
const addButtonSelector = '#root > div > div > div.ToDoInput > button';
expect(page).toClick(addButtonSelector);
expect(page.alert).toHaveBeenCalledWith('Please enter a todo!');
})
But the test Fails with zero calls to the page.alert (in reality the alert is shown after the click on this button in this conditions).
Also I tried window.alert - it is not recognized.
Please help.
Update: I tried like that, and the alert function is not replaced with the mock function (???)
it('should display an alert when the user tries to add empty value', async() => {
jest.setTimeout(50000);
const dialogHandler = jest.fn();
page.on('dialog', dialogHandler);
const addButtonSelector = '#root > div > div > div.ToDoInput > button';
await expect(page).toClick(addButtonSelector);
await expect(dialogHandler).toHaveBeenCalled();
const [firstCall] = dialogHandler.mock.calls;
const [dialog] = firstCall;
expect(dialog.message()).toEqual('Please enter a todo!');
})

Take a look at Pupeteer's Dialog. You can set event handler once a Dialog is displayed and if you pass jest.fn() you can have reference to the dialog that it has been called with
e.g.:
describe('page with dialog', () => {
const dialogHandler = jest.fn(dialog => dialog.dismiss());
beforeAll(() => {
page.on('dialog', dialogHandler);
});
describe('when the ToDoInput button is clicked', () => {
beforeAll(async () => {
await page.click('#root > div > div > div.ToDoInput > button');
});
it('should display a dialog', () => {
expect(dialogHandler).toHaveBeenCalled();
});
it('should have message "Please enter a todo!"', () => {
const [firstCall] = dialogHandler.mock.calls;
const [dialog] = firstCall;
expect(dialog.message()).toEqual('Please enter a todo!');
});
});
});
Edit: after testing it the only thing that was not working is that the test script was becoming idle until the dialog is either accepted or dismissed and after changing the dialogHandler to dismiss the dialog made it work flawlessly:
const dialogHandler = jest.fn(dialog => dialog.dismiss());
here is a working example

Related

Close button popup doesn't work (JAVASCRIPT)

i'm trying to create a custom pupop in javascript, this is my first time with this.
I have a problem with the close button, the "x" target correctly the div to close, but doesn't remove the "active" class at click.
https://demomadeingenesi.it/demo-cedolino/
HTML CODE
<div class="spot spot-2">
<div class="pin"></div>
<div class="contenuto-spot flex flex-col gap-3">
<img class="chiudi-popup" src="img/chiudi.svg" />
[---CONTENT---]
</div>
</div>
</div>
JAVASCRIPT CODE
const tooltips = function () {
const spots = document.querySelectorAll(".spot");
spots.forEach((spot) => {
const contenuto = spot.querySelector(".contenuto-spot");
const pin = spot.querySelector(".pin");
spot.addEventListener("click", () => {
let curActive = document.querySelector(".spot.active");
let contActive = document.querySelector(".contenuto-spot.show");
const chiudiPopup = document.querySelector(".chiudi-popup");
spot.classList.add("active");
contenuto.classList.add("show");
if (curActive && curActive !== spot) {
curActive.classList.toggle("active");
contActive.classList.toggle("show");
}
chiudiPopup.addEventListener("click", () => {
spot.classList.remove("active");
contenuto.classList.remove("show");
});
});
});
const chiudiPopup = document.querySelector(".chiudi-popup");
chiudiPopup.addEventListener("click", () => {
spot.classList.remove("active");
contenuto.classList.remove("show");
});
What the code above does is adding an click listener, but it's inside another click listener, so all it's doing is adding an click listener on the first .chiudi-popup that removes .active and .show from the last spot element.
It's hard to see if this is correct, because you haven't given us enough to reproduce the problem, but I moved the code above outside the spot.addEventListener("click", () => { and instead of searching the whole document with const chiudiPopup = document.querySelector(".chiudi-popup"); the code nows only targets the .chuidi-popup element within the spot: const chiudiPopup = spot.querySelector(".chiudi-popup");
const tooltips = function() {
const spots = document.querySelectorAll(".spot");
spots.forEach((spot) => {
const contenuto = spot.querySelector(".contenuto-spot");
const pin = spot.querySelector(".pin");
spot.addEventListener("click", () => {
let curActive = document.querySelector(".spot.active");
let contActive = document.querySelector(".contenuto-spot.show");
spot.classList.add("active");
contenuto.classList.add("show");
if (curActive && curActive !== spot) {
curActive.classList.toggle("active");
contActive.classList.toggle("show");
}
});
// MOVED FROM THE CLICK LISTENER
const chiudiPopup = spot.querySelector(".chiudi-popup");
chiudiPopup.addEventListener("click", () => {
spot.classList.remove("active");
contenuto.classList.remove("show");
});
});
EDIT: I missed that you have the img.chiudi-popup inside your container, which will trigger both event listeners. I would honestly just simplify the code and always hide the container when clicking on it again. You can still have the img.chiudi-popup (close image) to make it easier for the users to understand that they can click on it.
const tooltips = function() {
const spots = document.querySelectorAll(".spot");
spots.forEach((spot) => {
const contenuto = spot.querySelector(".contenuto-spot");
const pin = spot.querySelector(".pin");
spot.addEventListener("click", () => {
let curActive = document.querySelector(".spot.active");
let contActive = document.querySelector(".contenuto-spot.show");
if (curActive !== spot) {
spot.classList.add("active");
contenuto.classList.add("show");
}
if (curActive) {
curActive.classList.remove("active");
contActive.classList.remove("show");
}
});

Puppeteer - Click a button only if exists

I'm trying to create a webscraper in Node, using Puppeteer.
My first challange (which I thought would be easy), it's pass by a pagination "Load more" button.
But, when I run the following code, Puppeteer click on all "Load more" and after click on a content, when I need to stop clicking.
Why this happens?
let loadMore = true;
while (loadMore) {
selector = 'ul.pager > li > a.button';
await page.waitForSelector(selector, { timeout: 600 }).then(() => {
page.click(selector);
}).catch(() => {
loadMore = false;
});
}
Thx all!
I would do it like this.
I think timeout doesn't make a difference here, if the await inside the if's condition is not enough then there could be other problem with the script.
let loadMore = true;
while (loadMore) {
const selector = 'ul.pager > li > a.button';
if ((await page.$(selector)) !== null) {
await page.click(selector);
} else {
loadMore = false;
}
}

Event listener on multiple elements with the same class

On the site I have a navbar. When I click on the a in navbar I want a new div (with content) appears and everything else "hides". I did it withdisplay: none. In the corner there should be an "X" which closes the div and makes "first screen" appear again.
I did the first part, but have some problems with closing. Here is my code:
const about = document.getElementById('li-about');
const work = document.getElementById('li-work');
const contact = document.getElementById('li-contact');
^These are links in a navbar.
const openSlide = (id) => {
document.querySelector('main article#' + id).style.display = 'block';
document.querySelector('header').style.display = 'none';
};
about.addEventListener('click', () => openSlide('about'));
work.addEventListener('click', () => openSlide('work'));
contact.addEventListener('click', () => openSlide('contact'));
And here is the code. It works. But when I want to do similar thing to a closing div function, it only works with about, not work and contact.
const closeSlide = (id) => {
document.querySelector('main article#' + id).style.display = 'none';
document.querySelector('header').style.display = 'block';
};
const close = document.querySelector('.close');
close.addEventListener('click', () => closeSlide('about'));
close.addEventListener('click', () => closeSlide('work'));
close.addEventListener('click', () => closeSlide('contact'));
Can you tell me why? There is no error in console, I tried to replace closeSlide function with a simple console.log and it doesn't work as well. Is it possible that JavaScript detects only one (first in HTML code) .close div?
Is it possible that JavaScript detects only one (first in HTML code) .close div?
Yes, document.querySelector returns the first matching element it finds in the DOM. If you want to add your listeners to every .close on the page, either loop through the NodeList returned by document.querySelectorAll:
const closeList = document.querySelectorAll('.close');
closeList.forEach(element => {
element.addEventListener('click', () => closeSlide('about'));
element.addEventListener('click', () => closeSlide('work'));
element.addEventListener('click', () => closeSlide('contact'));
};
or add a listener to an element containing all of your .close elements that only takes action if a .close was clicked:
document.querySelector('#some-container').addEventListener('click', evt => {
if (!evt.target.closest('.close')) return;
closeSlide('about');
closeSlide('work');
closeSlide('contact');
});

How to catch ngToast with Protractor?

I need somehow to catch ngToast message after some action. I tried different solutions from this site, but they didn't help me and I don't know why. Does anyone has a working solution ? My last attempt was like:
var HomePage = require('./home.pageObject');
describe('Home Page tests', function () {
var homePage = new HomePage();
var EC = protractor.ExpectedConditions;
beforeEach(function () {
browser.get('/#/');
});
it('should fail login', function () {
var toast = $('.ng-toast');
homePage.signinBtn.click();
homePage.login('admin', 'Password')
.then(function () {
var toast = $('.alert');
browser.wait(EC.visibilityOf(toast), 3000);
});
});
});
Thanks.
Inspect the toast element once it is shown and then try to grab the message using css.
In one of our projects where we use angular-toastr, this does the trick:
element(by.css('#toast-container .toast-message')
You can use this function and call it in the test:
First I'm waiting presence in the dom, then visibility and then I return the text:
this.returnToastText= function(){
browser.wait(EC.presenceOf(element(by.css(".alert"))), 3000).then(function () {
browser.wait(EC.visibilityOf(element(by.css(".alert"))), 3000).then(function () {
return toastText= element(by.css(".alert")).getText();
})
})
};

event for file upload cancel in javascript

We can use file.onchange if we gonna set an event callback for file reading using javascript, but how to set event for when user cancel the upload (close the browse panel)?
There is no API for the file input modal. Besides, if the user closes the browser your code won't be running anymore, will it?
Of course there is the window.onunload method which allows you to detect the example you give.
Per the comments, the best thing I can come up with that would be helpful is that if nothing is selected, file.value.length will be 0.
That’s a great solution:
const createUpload = () => {
let lock = false
return new Promise((resolve, reject) => {
const el = document.createElement('input');
el.id = +new Date();
el.style.display = 'none';
el.setAttribute('type', 'file');
document.body.appendChild(el)
el.addEventListener('change', () => {
lock = true;
const file = el.files[0];
resolve(file)
document.body.removeChild(document.getElementById(el.id));
}, { once: true })
window.addEventListener('focus', () => { // file blur
setTimeout(() => {
if (!lock && document.getElementById(el.id)) {
reject(new Error('onblur'))
document.body.removeChild(document.getElementById(el.id))
}
}, 300)
}, { once: true })
el.click() // open file select box
})
}
Ussage:
// $label.classList.add('loading');
createUpload()
.then(function (file) {
// Sent file
// $label.classList.remove('loading');
})
.catch(function (err) {
// Your Cancel Event
// $label.classList.remove('loading');
});
It is very simple with jQuery :
$("#fileInputId").change(function () {
//implement your code here
});

Categories

Resources