I'm trying to automate some tasks using casperJS, and I need to open multiple popups. However, all popups have the exact same url (http://.../printit.aspx/...), so that whenever I use
this.withPopup(/printit/, function() {...});
it always opens the first popup. I can't access the other ones.
I suppose there are two possibilities :
close each popup after visiting it, but I can't find how to do this
accessing popups using another way than the URL regex /printit/. Maybe using casper.popups, but the documentation is very vague about this.
There is no easy and documented way of disambiguating two popups. The documentation says that casper.popups is an array-like property. So you could iterate over it. Judging by the code, the popups property itself is a pagestack. One can easily modify the pagestack.findByRegExp() function to do this kind of thing.
It seems that the casper.popups property contains duplicate entries, so one can filter them out.
casper.findAllPopupsByRegExp = function(regexp){
var popups = this.popups.filter(function(popupPage) {
return regexp.test(popupPage.url);
});
if (!popups) {
throw new CasperError(f("Couldn't find popup with url matching pattern %s", regexp));
}
// remove duplicates
var uniquePopups = [];
popups.forEach(function(p){
if (uniquePopups.indexOf(p) === -1) {
uniquePopups.push(p);
}
});
return uniquePopups;
}
casper.withPopup() accepts three types of inputs to identify a popup page. The third one is a the page object itself. So you can retrieve the matching popup page objects with findAllPopupsByRegExp(), select the one that you want and pass that to withPopup() to change into its context:
casper.then(function(){
var popups = this.findAllPopupsByRegExp(/printit/);
this.withPopup(popups[1], function(){
...
});
});
In my case i have a list of links. Every link calls some javascript that opens a new tab(=popup in casperjs), always with the same url (...\View.aspx).
Inside the tab i have to click a button that changes the url in the tab (...\List.aspx).
on("popup.loaded"...) is called twice, pushing every new page in the casper.popups array. They usually alternate, but for some reason (i guess asyncrony) not always: sometimes casper.popups[/*LAST*/].url matches /View\.aspx/, sometimes it matches /List\.aspx/.
I always had to use casper.withPopup( /*LAST VIEW.ASPX LOADED*/, ...); that was not always the last popup loaded and neither the one matching /View.aspx/ (it could be one of the oldes), so i had to find the latest loaded matching /View\.aspx/.
Here's my solution:
var is_view_loaded=false;
casper.on('popup.loaded', function(page) {
if(page.url.match(/View\.aspx/)) {
is_view_loaded=true;
}
}
// return last popup which url matches the required regexp
casper.getLastRegExPopup=function(regex) {
var l=casper.popups.length;
var i=l-1;
while(!regex.test(casper.popups[i].url)) {
i--;
if(i<0) return null;
}
return casper.popups[i];
}
Then in my core steps:
.
.
// clicked the link, must wait for popup (but which?!)
casper.waitFor(
function test() {
return is_view_loaded;
},
function then() {
var popup=casper.getLastRegExPopup(/View\.aspx/);
casper.withPopup(popup, function() {
// do things with "this"
});
is_view_loaded=false;
}
//, timeout
);
.
.
Related
My requirement is to click on each and every payment method (pay-safe, visa, bit-pay etc.)
and then validate using assert method by comparing URL.
Problem : Unable to click on element. I'm getting null value in variable. Tried using val() as well as html() method.
I tried below code.
//cy.get('.real-money--providers-list') = allPaymentMethods
depositFiat.allPaymentMethods().find('[src*="providers/logo"]').each(($element, index, $list) => {
var namePaymentProvider = $element.find('[alt*="safe"]').text()
cy.log(namePaymentProvider)
cy.wait(1000)
if(namePaymentProvider.includes('class')){
$element.find('.provider-content--choice').click()
//cy.get('.provider-content').invoke('removeAttr','src').click()
//depositFiat.secureCheckout().click()
//cy.back()
}
})
As cypress unable to handle child windows I tried to use invoke method but no luck.
Find HTML here
<div class="provider-img"><img alt="safecharge_paysafecard" class="style__Logo-a3ugi5-2 fAwRoV visible" src="https://static.xyz.com/1234123463/img/providers/logo_safecharge_paysafecard.svg"></div>
As per your HTML fiddle, I could see that for every payment provider you can use the css selector img[class*="style__Logo"]
For one payment method you can use:
cy.get('img[class*="style__Logo"]').eq(0).invoke('attr', 'src').should('contain', 'https: //static.xyz.com/')
You are finding an image, then trying to click on it.
Most likely the click-event sits on the button
Instead try to click on the button:
cy.get('.provider-content').each($element => {
cy.wrap($element).click()
// Assert something here
})
If the click action opens up a new tab/window, and you want to assert that it moved you do this new link, then Cypress does not support this directly.
Instead, you would either get the url that should be opened by the click and verify that.
Or
Stub the browser window so that the new tab opens up in the same tab you are currently in.
You can use Recursion and Jquery .removeAttr :
cy.get('[src*="providers/logo"]') //You need to make sure here is the correct selector that covers all methods here
.then(methods => {
checkPaymentMethod
function checkPaymentMethod(methodNumber = 0) {
if(methodNumber < methods.length) {
Cypress.$(methods[methodNumber]).removeAttr("target");
cy.get(methods[methodNumber])
.click()
.should('not.exist')
cy.url().should('eq', 'targetUrl')
cy.visit('yourPageUrl')
cy.url().should('eq', 'yourPageUrl')
methodNumber ++
checkPaymentMethod(methodNumber)
}
}
})
In the case of 1 tab, the newest tab will always be the last tab.
Same applies if it is the last tab that is used to open a new tab.
The following code will switch to last tab.
EDIT : Thanks to #JimEvans
In most answers on SO, you will find hardcoded indexes for windows. Like below:
return await driver.wait(async function () {
return await driver.getAllWindowHandles().then(async function (handles) {
// var isHandleCount2 = (handles.length == 2);
if (handles.length > 1) {
return driver.switchTo().window(handles[handles.length - 1]);
}
return false;
});
}).then(function () {
// Now do some stuff in new tab
});
What about the case where we have 5 tabs, and active tab 3 opens a new tab and the new tab is launched between tab 3 and tab 4.
How do we get the index of the newly created tab dynamically, without hardcoding its index, as in most examples on SO?
Yeah people do things all the time that they don't completely understand and don't realize that the code doesn't always work as they expect.
As Jim pointed out, the order of the window handles (tabs) in the returned array from .getAllWindowHandles() is not guaranteed. You can call .getAllWindowHandles(), store that list as before, create your new tab, call .getAllWindowHandles() again and store that new list as after, and then compare the two and find the handle in the new list that doesn't exist in the old list... that's the handle of your newly created tab.
I don't do JS but you can use the .filter() function to do this comparison.
function array_diff(a, b) {
return a.filter(function(value) {
return (b.indexOf(value) === -1);
});
}
console.log(array_diff(["a", "b", "c"], ["a", "b"]));
This returns ["c"].
I can imagine you doing something like
before = driver.getAllWindowHandles();
// do something that creates a new window/tab
// might need to wait for the new window to be created
after = driver.getAllWindowHandles();
handle = array_diff(after, before);
driver.switchTo().window(handle);
is there a way to check if the user has more than one tab open (not globally, only on my website) or how many tabs the user has open?
In the best case i need the amount but a boolean value would be enough.
I need a solution in vanilla JavaScript (ES6) without jQuery or something else.
Thank you in advance.
You could create an ID for every opened tab and save it in an array in the localStorage (document.cookie would also work, but requires a bit more effort with getting and setting):
var tabId = Math.random();
var tabs = JSON.parse(localStorage.getItem('tabs')) || [];
tabs.push(tabId);
localStorage.setItem('tabs', JSON.stringify(tabs));
Now, to check how many tabs we have open on the current domain, we check the length of the tabs array in the localStorage:
function getTabCount()
{
return JSON.parse(localStorage.getItem('tabs')).length;
}
Now, you can use getTabCount() to get the amount of tabs that are currently open.
Finally, we need to make sure our tabId is removed from the array when we close it:
window.addEventListener('beforeunload', function(e)
{
tabs = JSON.parse(localStorage.getItem('tabs'));
var index = tabs.indexOf(tabId);
if (index !== -1)
tabs.splice(index, 1);
localStorage.setItem('tabs', JSON.stringify(tabs));
});
Do keep in mind that you need to keep using getTabCount() to check if any tabs have been opened or closed since you last used it.
I'm creating a simple extension to show an icon on different URLs.
My background script includes:
chrome.tabs.onUpdated.addListener(function(o,r,m){for(var c=[
"URL1",
"URL2",
],a=0;a<c.length;a++)if(~m.url.indexOf(c[a])){chrome.pageAction.show(o);break}});
I would like to display a different page action icon for each of the two URLs. How should I proceed?
You appear to be looking for chrome.pageAction.setIcon().
I have re-written the code to be a bit more readable. I changed your use of Bitwise NOT, ~, to a test for !== -1. I used .some() to both loop through the urlList (a new Object containing both URLs and the icons) and to indicate if a match was found so that the page action icon could be hidden if the URL does not match. I assumed you wanted it hidden on a non-match, given that you were .show()ing it when there was a match.
var urlList=[{
url:"URL1",
icon:"/URL1icon.png"
},
{
url:"URL2",
icon:"/URL2icon.png"
}];
chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){
if(!urlList.some(function(urlInfo){
if(tab.url.indexOf(urlInfo.url) !== -1) {
//The urlInfo.url must match the beginning of the tab's current URL.
chrome.pageAction.show(tabId);
chrome.pageAction.setIcon(tabId,{
path:urlInfo.icon
});
return true;
}
return false;
})) {
//Hide the icon if the URL does not match.
chrome.pageAction.hide(tabId);
}
});
I have a home.html and faq.html.
The faq page has many questions and their answers listed consecutively down the page. I want the answers hidden until a question is clicked that opens that answer. When another question is clicked, it will close the previous answer and open the new one. I'd like to use slideUp/slideDown or fadeIn/fadeOut for that, but not essential.
The home page has a link that when clicked, I want taken to the faq page and also have 'open' a specific answer, both from the one click; slideDown or fadeIn again not essential.
Is there a JavaScript that can be used to perform the task?
I have seen some posts with setvisibility type code somewhat acceptable for the faq page, but not that I can activate from my home page.
I am a novice, so I ask for the html code too that I can insert on both pages. A bigger chore than usual I know!
Many thanks.
NOTE
I now see a problem with my idea because of the following. I didn't mention it before but it would be neccessary.
Let me try to explain it.
I now find that to have a link on one page open another page at a particular position down that page will
correctly position the page to that point IF previous content on that page is not hidden.
If previous content is visibility: hidden;, that point where the page will open at is innacurate. In other words, it opens at a point below intended, a point at which is correct if previous content is not hidden.
I hope that makes some sense.
I will soon look at whether display:none; is better suited for this before I look at my original questions.
Thanks to those who have helped me to date, you have spent a lot of time for me.
Try this one
Reference
$(function() {
$('div.answer').hide();
$('a.question').before('<span class="faqplusminus dark nounderline">[+]</span>');
$('a.question').click(function() {
$('div.answer').slideUp('slow', 'easeInOutExpo');
$('span.faqplusminus').html('[+]');
var slidedownelement = $(this).closest('div.faq').find('div.answer').eq(0);
if(!slidedownelement.is(':visible')) {
slidedownelement.slideDown('slow', 'easeInOutExpo');
slidedownelement.parent().find('span.faqplusminus').html('[-]');
}
});
});
You can add querystring parameters to the request for the faq page and read the querystring using javascript on the faq page. Depending on the parameters you pass, you could do your desired animations.
In home.html: You would create the links with the query string param like so:
Link to FAQ
In faq.html: you would need to read the querystring parameter and depending on the value, call the javascript function that actually handles the show/hide of questions.
To read querystring parameters in javascript you need to parse window.location.search. Here is a javascript helper that you can use:
var QueryString = function () {
// This function is anonymous, is executed immediately and
// the return value is assigned to QueryString!
var query_string = {};
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
// If first entry with this name
if (typeof query_string[pair[0]] === "undefined") {
query_string[pair[0]] = pair[1];
// If second entry with this name
} else if (typeof query_string[pair[0]] === "string") {
var arr = [ query_string[pair[0]], pair[1] ];
query_string[pair[0]] = arr;
// If third or later entry with this name
} else {
query_string[pair[0]].push(pair[1]);
}
}
return query_string;
} ();
You can then access QueryString.question