How to navigate a dropdown triggered by JavaScript using Python Selenium - javascript

I am trying to navigate a dropdown button that operates via JavaScript. However, no matter what I try, the HTML list items it should have never seem to show up in selenium.
An image of the dropdown button:
Inspector page source:
<div class="dropdown" style="border-bottom: 1px solid #ebebeb; padding-bottom: 2px;">
<a class="btn btn-default btn-sm" onclick="$(this).parent().toggleClass('open')" title="Select an Event" style="width: 220px">
<span id="event-selection-span-id">No event selected.</span>
<span class="fa fa-caret-down routing-toolbar-menu"></span>
</a>
<ul class="dropdown-menu user-dropdown dropdown-menu-left" id="call-events-list-id">
<li title="ADRC Archived Call" event_definition_id="2f617fc5-c0b0-492a-92e2-561c39c239fc" form_code="AACOG_ADRC_CCC_ARCH" onclick="CallCenter.SetEvent(this)" class="list-group-item event-group-list-item">ADRC Archived Call</li>
<li title="ADRC Information Call" event_definition_id="0a22deba-4788-4647-bee6-47305e182eca" form_code="AACOG_ADRC_CCC" onclick="CallCenter.SetEvent(this)" class="list-group-item event-group-list-item">ADRC Information Call</li>
</ul>
</div>
Selenium page source:
<div class="dropdown open" style="border-bottom: 1px solid #ebebeb; padding-bottom: 2px;">
<a class="btn btn-default btn-sm" onclick="$(this).parent().toggleClass('open')" title="Select an Event" style="width: 220px">
<span id="event-selection-span-id">No event selected.</span>
<span class="fa fa-caret-down routing-toolbar-menu"></span>
</a>
<ul class="dropdown-menu user-dropdown dropdown-menu-left" id="call-events-list-id"></ul>
</div>
Here is what I've tried so far, all unsuccessful:
Originally just tried finding the elements and using the .click() method to click in them. (e.g. driver.find_element_by_xpath('//*[#id="step-3"]/div[2]/a').click() then driver.find_element_by_xpath('//*[#id="call-events-list-id"]/li[2]').click(). Selenium could not find the list element in the second line.
Then I tried a method that's worked for me before when the previous one didn't: finding the elements then using driver.execute_script("arguments[0].click()", btn) for each one. Like before, it worked for the main dropdown button but not the list item that should show up afterwords.
So I figured I could just execute the javascript manually with the elements' JS paths using driver.execute_script("$(document.querySelector('#step-3 > div.dropdown > a')).parent().toggleClass('open');") then driver.execute_script("CallCenter.SetEvent(document.querySelector('#call-events-list-id > li:nth-child(2)'));"). This still didn't work and the list elements still did not show up in selenium's page source.
The strangest thing is the list elements show up in the inspector before you even click the main dropdown button. Therefore I should just be able to execute the second JS line manually with no problems, and that works fine when I do it in the browser. I have also tried just waiting for the list elements to show up when the source is loaded but they never show up no matter how long I set the delay for.

First to click on the element with text as Select an Event and then from the dropdown to click on the element with title as ADRC Information you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following locator strategies:
Using CSS_SELECTOR:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a.btn.btn-default.btn-sm[title='Select an Event']"))).click()
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a.btn.btn-default.btn-sm[title='Select an Event'] +ul#call-events-list-id li[title='ADRC Information Call']"))).click()
Using XPATH:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='btn btn-default btn-sm' and #title='Select an Event']"))).click()
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='btn btn-default btn-sm' and #title='Select an Event']//following::ul[#id='call-events-list-id']//li[#title='ADRC Information Call']"))).click()
Note: You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

Related

Function and its selector to choose how to export CSV

Here is the source code in a page for a user to choose to export a CSV file.
<div class="btn-group pull-right" style="margin-right: 10px">
<a class="btn btn-sm btn-twitter"><i class="fa fa-download"></i> 导出</a>
<button type="button" class="btn btn-sm btn-twitter dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu" role="menu">
<li>全部</li>
<li>当前页</li>
<li><a href="/admin/order?_export_=selected%3A__rows__" target="_blank" class='export-selected'>选择的行</a></li>
</ul>
</div>
The purpose of the code is to choose 当前页 out of 3 options and fire up the export event. Here is the snippet of the code for exporting CSV:
await page._client.send('Page.setDownloadBehavior', {behavior: 'allow',
downloadPath: '/tmp'})
res = await Promise.all([
page.waitForNavigation({waitUntil: 'load', timeout: 60000}),
page.click('a[href$="/admin/order?_export_=page%3A1"]'),
]);
The error is:
Error: No node found for selector: a[href$="/admin/order?_export_=page%3A1"]
at assert (C:\node_modules\puppeteer\lib\helper.js:259:11)
at Frame.click (C:\node_modules\puppeteer\lib\FrameManager.js:704:5)
at <anonymous>
There are 2 questions here. The first is: is page.click() right for the purpose? There is page.select() which does the similar thing. Second is: what is the correct selector for the 2nd options I would like to choose?
Make sure you are clicking the dropdown button before clicking the link:
await page.click('.btn-twitter.dropdown-toggle');
Then, once the link is visible, you can click it using the following selector:
await page.click('a[href$="/admin/order?_export_=page%3A1"]');
You may need to use page.waitForSelector() to wait for the element to be added to the DOM if the element is dynamically generated:
await page.waitForSelector('a[href$="/admin/order?_export_=page%3A1"]');
page.click() is correct if you want to follow the link. page.select() is for selecting a value in a select box.
Regarding your query selector, you are missing a closing bracket and quotation marks. It should work like this:
page.click('a[href$="/admin/order?_export_=page%3A1"]')

Clicking a button/href with JavaScript/Jquery by class name

I'm trying to click a button that brings up an edit screen on the same page.
Eg. Click "edit user", edit screen pops up on the same page without redirecting, change name, save. This button does not redirect to a new page.
The code for this button is the following:
<i class="fa fa-pencil"></i> Edit
For some reason I can click every other button on this website that's in the same exact form but I can't click this one. There are no IDs at all so calling by the class name is the only other method I know.
This is what I've tried:
setTimeout(function() {
document.getElementsByClassName(" btn btn-xs btn-inverse btn-modify")[0].click();
}, 3000);
I tried using some Jquery instead as well but no luck. Am I doing something wrong or is this all that I can really do? The other buttons I clicked either redirected me to a different site or just brought up some information on the same screen.
Thanks in advance.
Edit:
It seems that when I try iterating through the different buttons, who all have similar starting class names, I can iterate and click every button except for the edit button. So it's safe to assume that this isn't an issue with the code, so thank you everyone for the help and suggestions.
Edit #2:
Here is the code for all three buttons:
<div class="btn-group"><i class="stm stm-goog"></i> &nbsp
<i class="fa fa-pencil"></i> Edit
<i class="fa fa-bar-chart"></i><button type="submit" class="btn btn-xs btn-danger btn-submit"><i class="fa fa-trash-o"></i></button></div>
When searching for elements by class, it's better to use:
document.querySelector(); // Finds first matching element only
and
document.querySelectorAll(); // Finds all matching elements
instead of document.getElementsByClassName() as this returns a "live node list" and hinder performance.
When searching for classes with these methods, remember to include the dot (.) to signify classes and when multiple classes are used, do not include spaces. The spaces are only used when setting multiple classes.
Also, if there is only one class that is unique to the element you wish to find, you only need to search on that one class.
Lastly, only use a elements for navigation. If you simply need something to click, just about any object can have a click event handler.
var edit = document.querySelector(".btn-modify");
edit.addEventListener("click", function(){ console.log("clicked")});
setTimeout(function() {
edit.click();
}, 3000);
// Addtional test
var edit = document.querySelectorAll(".btn.btn-xs.btn-inverse")[1];
edit.addEventListener("mouseover", function(){
console.log("moused over");
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<div class="btn-group">
<a href="https://google.com" target="_blank" class="btn btn-xs btn-inverse">
<i class="stm stm-goog">X</i> <!-- <-- You missed a semi-colon here. -->
</a>
<a href="#" data-id="29508"
class="btn btn-xs btn-inverse btn-modify">
<i class="fa fa-pencil"></i> Edit
</a>
<a href="#" data-page-modal="https://*randomwebsite*.com/manage/stats/301&q=11"
class="btn btn-xs btn-inverse">
<i class="fa fa-bar-chart">X</i>
</a>
<button type="button" class="btn btn-xs btn-danger btn-submit"><i class="fa fa-trash-o"></i>TEST</button></div>
Because you are trying to select the element by its class you need to loop through the elements.
So if you wanna use jQuery you would do like so:
$(".btn-modify").each(function(){
$(this).click(function(){
your code here
});
});
If you want to fetch the element having all the class, try this:
JQuery
$(".btn.btn-xs.btn-inverse.btn-modify")
Plain JavaScript
document.querySelectorAll(".btn.btn-xs.btn-inverse.btn-modify")
Giving a space in between a class names in a css like selector meant that (next one) is a nested element in any depth. use . to indicate its a class, and write them together (without a space) you are searching for elements having all the lasses. And getElementsByClassName is not a css like selector, its can take only a class name and all those element having that class.

Drop-down not showing on Bootstrap input-group

I can't seem to make the drop-down work. When you click on 'action' nothing happens.
I double-checked the CSS and JS links and all seems ok, tried with Bootstrapv4 and 3.3.7 and none works. Any thoughts?
Check my app https://codepen.io/thomasfaller/pen/NpGpPP
<iframe height='265' scrolling='no' title='XE.com WebApp' src='//codepen.io/thomasfaller/embed/NpGpPP/?height=265&theme-id=0&default-tab=html,result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/thomasfaller/pen/NpGpPP/'>XE.com WebApp</a> by Thomas Faller (<a href='http://codepen.io/thomasfaller'>#thomasfaller</a>) on <a href='http://codepen.io'>CodePen</a>.
You are trying to use bootstrap dropdown as a select input. Dropdowns are supposed to just call some actions, not behave like "select" elements. So there is no such functionality out of the box as you want. You have to have some JS to make it work.
So my very quick and dirty solution just to make it work:
1) Assign id="selected" to currently selected value:
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-eur" aria-hidden="true" id="selected"></i>
</button>
2) Assign onclick handler on dropdown items:
<a class="dropdown-item" onclick="select('eur')"><i class="fa fa-eur" aria-hidden="true"></i></a>
3) And this handler could be like this:
function select(currency) {
let selected = document.getElementById('selected');
selected.className = `fa fa-${currency}`
}
Codepen: https://codepen.io/anon/pen/KWdOxa
But you still have to save somehow a selected value to make your calculations proper in JS code.
So you had better use a special bootstrap element for select inputs - https://silviomoreto.github.io/bootstrap-select/

selenium explicitly wait, wait until a list of buttons are clickable

In a page there are a list of buttons. Source code of each button is the same except for the link text.
I need to confirm the all the buttons in one page are clickable using WebDriverWait.until. I can the following
WebDriverWait(driver,10).until(ec.element_to_be_clickable(By.XPATH,'//a[#class="ng-scope"]'))
to confirm the first button to be clickable, but how can I confirm the second one without using the text feature(abc|efg)?
Any suggestion to extract the index within xpath? Thank you.
<li class="ng-scope" ng-report="one in typelist">
<a class = "btn ng-binding" ng-class="{aabbcc}"> abc</a>
</li>
<li class="ng-scope" ng-report="one in typelist">
<a class = "btn ng-binding" ng-class="{aabbcc}"> efg</a>
</li>
Try Capturing all the element to a list and iterate through each for checking if they are clickable
You can search with xpath by index
WebDriverWait(driver, 10).until(ec.element_to_be_clickable(By.XPATH, "//a[#class='ng-scope'][2]"))
This will give you the second button.

Selenium IDE select window without href or url

I am new in Selenium IDE. I try to select a PDF that opens during the test, but it doesn't work. I'am testing a Webapplication that is written on JSF and so includes some Java-Methodes/Classes.
I want to test if the PDF opens correctly or not. The problem is, there is no direct href in the HTML-Code. Only a Java(?)-Methode who opens the PDF. Is there any solution for this problem in Selenium IDE?
The Element you can click on is
<h:commandLink id="createDokument" style="background-image: none;" styleClass="Button Blue"
action="#{myBean.checkDocument()}" value="#{buttons['BTN_Document']}">
</h:commandLink>
Then a javaScript function should test if a pdf can be created
IF yes the hidden button is clicked:
<h:commandLink id="createDocumentPdf" style="background-image: none; display: none;"
styleClass="Button Blue"action="#{myBean.createDocument()}" value="something" target="_blank">
</h:commandLink>
What Firebug knows from this two buttons is:
<a id="mainForm:createDocumentPdf" class="Button Blue" onclick="if(typeof jsfcljs == 'function'){jsfcljs
(document.getElementById('mainForm'),{'mainForm:createDocumentPdf':'mainForm:createDocumentPdf'},'_blank');}return false"
style="background-image: none; display: none;" href="#">Something </a>
<a id="mainForm:createDocument" class="Button Blue" onclick="if(typeof jsfcljs =='function'){jsfcljs
(document.getElementById('mainForm') {'mainForm:createDocument':'mainForm:createDocument'},'');return false
"style="background-image:none;" href="#" tabindex="0">Document</a>
I tried a few things already but nothing works....
I tried: select window | titleOfThePDFWindow
but it does not work
so all I have now is:
clickAndWait | id=mainForm:createDocument

Categories