Selenium - cannot click on an element inside a modal - javascript

I am using Selenium and java and I cannot click on an element inside a modal.
The scenario is this:
after clicking on an item inside a frame, it opens up a modal and I need to click on an element inside this modal but I cannot get it.
I already tried with:
js.executeScript("document.getElementById('saveexit').scrollIntoView(true);");
I also tried with switchTo() this way:
while (itr.hasNext()) {
String popup = itr.next();
System.out.println("itr: " + popup);
driver.switchTo().window(popup);
}
Here is the html of my modal:
<div class="modal-dialog">
<div class="modal-content modal-custom-content">
<div class="modal-header">
...
</div>
<div class="modal-body">
<form id="formTo" class="form-container">
<div class="row">
...
</div>
<div class="small-space"></div>
<input ...>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
...
</div>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
...
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
...
</div>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
...
</div>
</div>
<div class="small-space"></div>
<div class="row">
...
</div>
</form>
</div>
<div class="small-space"></div>
<div class="modal-footer">
<div class="row text-center">
<div class="col-md-6 col-sm-6 col-xs-12">
<button class="btn modal-button full-btn" id="saveexit" type="button">SAVE AND EXIT</button>
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
...
</div>
</div>
</div>
</div>
</div>
this is the CSS Path as taken from firefox dev tool:
html.no-touch body div.remodal-wrapper.remodal-is-opened div.modaliAdesione.remodal.remodal-is-initialized.remodal-is‌​-opened div.modal-dialog div.modal-content.modal-custom-content div.modal-footer div.row.text-center div.col-md-6.col-sm-6.col-xs-12 button#saveexit.btn.modal-button.full-btn
the object is never found.
Question 1: if an element is inside a modal has to be managed
differently?
Question 2: How to finally have the click on the button
saveexit working?
here is shared a code snippet of the html: https://codeshare.io/arLW9q
Here is the java code:
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[#id=\"saveexit\"]")))
I have also tried with:
cssSelector: #saveexit
cssPath: html.no-touch body div.remodal-wrapper.remodal-is-opened div.modaliAdesione.remodal.remodal-is-initialized.remodal-is-opened
div.modal-dialog div.modal-content.modal-custom-content div.modal-footer div.row.text-center div.col-md-6.col-sm-6.col-xs-12
button#saveexit.btn.modal-button.full-btn
xpath: //*[#id="saveexit"]
Please note: if I run document.getElementById('saveexit').click(); from browser's console it works out

As you are using the Selenium-Java clients as per best practices the first and foremost trial must be with invoking the highly efficient and proven click() method. Depending on the errors resulting out of click() method we can work on other alternate solutions.
As I can see from your code trials with JavascriptExecutor and switchTo().window(), you havn't identified the WebElement representing the SAVE AND EXIT button well.
To click on the SAVE AND EXIT button you can use the following code block :
new WebDriverWait(driver, 5).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#class='modal-dialog']//div[#class='modal-footer']//button[#class='btn modal-button full-btn' and #id='saveexit']"))).click();

I fixed it using jquery inside my script;
here is the linecode:
js.executeScript("$('#saveexit').trigger('click');");
I hope it can help someone in future.
I dont know why plain javascript was not working...

Related

How do i change my HTML page with a form?

I will explain my problem, I have an HTML page "index" with many buttons (all Href linked to other sites), I would like to create a form which writes alone the basic code.
I think an example would explain better, this form is for non-developer, 3 input (name, href, picture) and when you use the form, it updates the index page with a new button who is already linked to the other website.
I was thinking about creating many buttons with display: none, and update these buttons. Or having a script already written who could be filled.
I want these new buttons to stay even if you refresh the page.
My index looks like this :
#{
ViewData["Title"] = "Index";
}
<div style="background-image: url('img/LOGOFIVES.jpg');">
<div class="text-center">
<h5 class="my-4">Bienvenue sur le portail Fives.</h5>
<div class="row m-4">
<div class="col-12 col-md-6 col-lg-4 col-xl-3 my-2">
<a asp-controller="Order" asp-action="Index" class="btn btn-lg btn-primary btn-block">Ordres</a>
</div>
<div class="col-12 col-md-6 col-lg-4 col-xl-3 my-2">
<a asp-controller="Article" asp-action="Index" class="btn btn-lg btn-primary btn-block">Articles</a>
</div>
<div class="col-12 col-md-6 col-lg-4 col-xl-3 my-2">
Portail Qualios
</div>
<div class="col-12 col-md-6 col-lg-4 col-xl-3 my-2">
Saphir
</div>
<div class="col-12 col-md-6 col-lg-4 col-xl-3 my-2">
Base Qualité
</div>
<div class="col-12 col-md-6 col-lg-4 col-xl-3 my-2" onclick="alert([message?: veuillez utiliser Internet Explorer pour utiliser Chronogestor sans problème])">
Chronogestor
</div>
<div class="col-12 col-md-6 col-lg-4 col-xl-3 my-2">
Intranet Fives ECL
</div>
<div class="col-12 col-md-6 col-lg-4 col-xl-3 my-2">
Portail Fives
</div>
</div>
</div>
</div>
If you want other people to see these newly added links/buttons, you have to use some back end language (PHP for example) and a database to store these links. To my knowledge, you can't update the HTML code without any back-end language or server-side apps but if you can, it's neither practical nor commonly used.
What you want is not too complicated so some basic knowledge of a back-end language should be enough.
Good luck!
P.S. If what you mean is to just update those buttons for the current user until they refresh the page, that's a whole different story and you can just accomplish that with JavaScript.
if you want too change the view just for the user
you can do this with java script using this function
function add_button(text, href){
let b = document.createElement("a");
let t = document.createTextNode(text);
b.appendChild(t);
b.setAttribute("href", href)
let element = document.getElementById("div");
element.appendChild(b);
}
and just call it from the submit button in the form

Collapse Panel Using Header and Header Text

I have a collapsible panel working currently using the following HTML:
<div class="panel panel-entity panel-default>
<div class="panel-heading" onclick="{ $(event.target).siblings('.panel-body').slideToggle('slow'); }">
<span class="panel-heading-text">#Model.RoleName</span>
</div>
<div class="panel-body">
<div class="form-horizontal">
<div class="entity">
#*A bunch of form groups*#
</div>
</div>
</div>
</div>
However, this only allows the user to click on the panel header to collapse the panel, meaning that when they click on the text within the header, it will not collapse the panel.
Is there another approach I can take to allow the user to click anywhere within the header, including the text, to collapse the panel?
For any potential future readers, the way I solved this problem was by adding style="pointer-events: none;" to the span where the header text was stored.
If you are using a span element to add a class to the text within a collapsible panel header, this is the correct way to make the text click-through.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="panel panel-entity panel-default">
<div class="panel-heading" onclick="{ $(event.target).siblings('.panel-body').slideToggle('slow'); }">
<span class="panel-heading-text" style="pointer-events: none;">#Model.RoleName</span>
</div>
<div class="panel-body">
<div class="form-horizontal">
<div class="entity">
#*A bunch of form groups*#
</div>
</div>
</div>
</div>
You need to remove the <span class="panel-heading-text"> tag. Since that tag is there, when you click on the text, you are actually clicking on that element and not the .panel-heading element.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="panel panel-entity panel-default">
<div class="panel-heading" onclick="{ $(event.target).siblings('.panel-body').slideToggle('slow'); }">
#Model.RoleName
</div>
<div class="panel-body">
<div class="form-horizontal">
<div class="entity">
#*A bunch of form groups*#
</div>
</div>
</div>
</div>

Get Portion of Class Name, in order, on Web Page Using JavaScript?

Is there a way using Javascript, to pull a portion of a class name in order (top to bottom) on a web app? I have an issue where I need to get information on a 3rd party web app we are integrating into our site. I can't change their code. What I want to do is take the class name (e.g. class="index solid-shape-large-rating-fair AnalysisCategory" or class="index solid-shape-large-rating-poor AnalysisCategory" etc.) and grab the value after "rating-" in the class name (class="index solid-shape-large-rating-[want this value] AnalysisCategory"). So in the example I have, I would get back, in this order, top to bottom, "fair", "poor" and "excellent". Is this possible? If so how? Please be aware there will be more mark-up between elements in my example, if that impacts the answer? I just cleaned up the non-related code for presentation purposes. They aren't stacked directly on top of each other as in my example.
///////Top Element//////////
<div class="stack-item">
<div class="row">
<div class="col-md-3 col-sm-6 col-xs-12 text-center index-area">
<label tabindex="0">YOUR INDUSTRY BORROWING HISTORY</label>
<div class="index solid-shape-large-rating-fair AnalysisCategory">
MODERATE RISK
</div>
</div>
</div>
</div>
//////////////Middle Element////////////////////
<div class="stack-item">
<div class="row">
<div class="col-md-3 col-sm-6 col-xs-12 text-center index-area">
<label tabindex="0">TENURE OF YOUR BUSINESS</label>
<div class="index solid-shape-large-rating-poor AnalysisCategory">
POTENTIAL CONCERN
</div>
</div>
</div>
</div>
////////////Bottom Element/////////////
<div class="stack-item" style="">
<div class="row">
<div class="col-md-3 col-sm-6 col-xs-12 text-center index-area">
<label tabindex="0">YOUR BUSINESS PROFITABILITY</label>
<div class="index solid-shape-large-rating-excellent AnalysisCategory">
EXCELLENT
</div>
</div>
</div>
</div>
Thank you!
I found this answer on another site.
$(function() {
$('[class*="solid-shape-large-rating-"]').each(function(i, e) {
var result = this.className.match(/solid-shape-large-rating-(.*?)\s+/);
console.log(result[1]);
});
});

Cannot set .color property in JS

<button id="change_button" class="btn btn-primary" onclick="ColorMe()">CLICK ME</button>
<div class="container">
<div class="row">
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
</div>
Clicking a button is supposed to color all the elements of class "grid_element" into red but in never happens.
function ColorMe() {
document.getElementsByClassName("grid_element").style.color = ("red");
}
The problem is said to be Cannot set property 'color' of undefined
at ColorMe (js.js:2) but I know it worked in the same way many times before.
The problem is that you are attempting to use the .style property on the collection of elements found by .getElementsByClassName() instead of on each of the elements within the collection.
Also (FYI), .getElementsByClassName() returns a "live" node list, which causes the entire DOM to be re-scanned every time you access the node list variable and that can impact performance quite a bit. There are limited use cases for that, so you probably want a "static" node list more often than not. For that, use .querySelectorAll().
function ColorMe() {
// Get all the matching elements into a JavaScript Array
var elements = Array.prototype.slice.call(document.querySelectorAll(".grid_element"));
// Loop over each element....
elements.forEach(function(el){
el.style.color = "red"; // Adjust the element's style
});
}
<button id="change_button" class="btn btn-primary" onclick="ColorMe()">CLICK ME</button>
<div class="container">
<div class="row">
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
</div>

AngularJS ng-repeat, $scope variable issue and preventing infinite $digest loop

I need to find a better solution regarding ng-repeat, $scope variable and infinite $digest loop. I'm sure it's not $watch issue and I'm sure it's in the way I'm updating my $scope variable.
The issue doesn't happen in Chrome. It only happens in Safari and Firefox. This is how I'm doing it. I have a $scope.chartCollection which is dynamically populated with Google Chart objects. The $scope.chartCollection can have 1 or more google chart objects depending on my selection.
In my html template, I have this code,
<div ng-repeat="device in chartCollection">
<div class="col-md-6 padding10px">
<div class="widget enable-top-radius chartProperties">
<div class="widget-title enable-top-radius dark-gray-title">
<h4>
<span class="alignleft">
<i class="fa fa-circle" ng-style="{ color : device.dotColor }"></i><span class="padding5px">{{printerTypeName}}</span><span class="padding5px">|</span><span class="padding5px" popover="{{device.modelOrLocation }}" popover-trigger="mouseenter">{{ device.modelOrLocation }}</span>
</span>
</h4>
<span class="alignright">{{device.location | uppercase }}</span>
<div style="clear: both;" class="padding5px"></div>
</div>
<div class="widget-body">
<div google-chart chart="device.chart" on-select="seriesSelected(selectedItem, $index)"></div>
</div>
</div>
<div class="col-md-12 col-sm-12 col-xs-12 no-padding chartInfoBox">{{dateForGraph}}</div>
<div class="col-md-12 col-sm-12 col-xs-12 no-padding">
<div class="col-md-4 col-sm-4 col-xs-4 odometerChartFooter">
<div class="odometerFooterFieldValue" ng-bind="device.leftCellNumber | number"></div>
<div class="odometerFooterFieldKey" ng-bind="device.leftCellText"></div>
</div>
<div class="col-md-4 col-sm-4 col-xs-4 odometerChartFooter">
<div class="odometerFooterFieldValue" ng-bind="device.middleCellNumber | number"></div>
<div class="odometerFooterFieldKey" ng-bind="device.middleCellText"></div>
</div>
<div class="col-md-4 col-sm-4 col-xs-4 odometerChartFooter">
<div class="odometerFooterFieldValue" ng-bind="device.rightCellNumber | number"></div>
<div class="odometerFooterFieldKey" ng-bind="device.rightCellText"></div>
</div>
</div>
</div>
</div>
so that I can display each chart in separate divs. It works really well in Chrome but not in Firefox and Safari.
I'm getting this error in Firefox and Safari
https://docs.angularjs.org/error/$rootScope/infdig?p0=10&p1=%5B%5B%5D,%5B%5D,%5B%5D,%5B%5D,%5B%5D%5D
And my situation is similar to the second example they mentioned which generates a new array. However, in my situation, it will always be a new array.
I was thinking of not using ng-repeat and use $scope.chart1, $scope.chart2 etc but the problem with this approach is that I cannot tell if there will be 1, 10 or 20 charts since everything is generated dynamically. Code will be very messy!
Update:
Here is the code that generates the chart objects
http://pastebin.com/DmQkDHjW

Categories