Protractor Custom Locator is getting failed to locate the element - javascript

In our project, the automation team will include custom attribute 'lid' to the element where ever we cannot identify the element uniquely.
I created a custom locator method to detect the element using custom attribute 'lid'. It is below:
by.addLocator('lid', function(value, parentElement)
{
parentElement = parentElement || document;
var nodes = parentElement.querySelectorAll('lid');
return Array.prototype.filter.call(nodes, function(node)
{
return (node.getAttribute('lid') === value);
});
});
Here the html code of the page
<div class="col-sm-8 cbx">
<p class="ng-binding"><input type="radio" ng-model="theme"
lid="create-user-them-radio1" ng-value="1" ng-click="user.theme = -1" class="ng-
pristine ng-untouched ng-valid ng-not-empty" name="1194" value="1"> Use the default
for this domain</p>
<p class="litetext ng-binding">Following options override
the domain's default value</p>
<p class="ng-binding"><input type="radio" ng-model="theme"
lid="create-user-them-radio2" ng-value="2" class="ng-pristine ng-untouched ng-
valid ng-not-empty" name="1195" value="2"> Select theme for the user</p>
</div>
I want to use 'lid' attribute and locate the element and wrote the above method to work with any attribute and value.
I call the method from the test file as below:
element(by.lid("create-user-them-radio1")).click();
I am getting 'No element found using locator: by.lid("create-user-theme-radio1")'
Need help to resolve the issue.

why you need to create custom method if you can do the same things by defaults methods
element(by.xpath('.//li[contains(#ng-if, "$select.open")]')).click();
PS: if you will run tests on prod mode, then this locator will not work. attributes like ng-%blabla% embedded only in dev mode.

I am able to find the correct solution for this and the solution is as below:
by.addLocator('lid', function(value, parentElement) {
var nodes;
if(parentElement)
nodes = parentElement.querySelectorAll();
else
nodes = document.querySelectorAll('body *');
return Array.prototype.filter.call(nodes, function(node) {
if( node.hasAttribute("lid") && node.getAttribute('lid') === value)
return node;
});
});
querySelectorAll() needs to give the input as 'tag', if no parentElement is sent then it queries all the tags under 'body' tag otherwise all elements from the parentElement. After that loop through all the elements and check whether attribute 'lid' exists or not. If exists verify the value, if the value is desired value then it return that element.

Related

Javascript: Select all data-qa attributes on HTML Page

Using only JavaScript, without the use of JQuery etc, what is the most efficient way to select all attributes names that have a certain data attribute (let's say data-qa).
<p data-foo="0"></p><br/><h6 data-qa="apple"></h6>
<p data-foo="0"></p><br/><h6 data-qa="book"></h6>
<p data-foo="0"></p><br/><h6 data-qa="car"></h6>
Expected result should be list :
apple
book
car
This question gets the parent elements, I want the attributes themselves. Select all elements with "data-" attribute without using jQuery
Resources:
Selenium find element via Data-Qa attribute
Data-QA Attribute: A better way to select elements for UI test automation
The code below works by getting all of the elements using document.querySelectorAll, mapping the elements to the values of the data attributes, and then filtering out the ones that are falsy or '0'.
let getAllDataAttrs = name => Array.from(
document.querySelectorAll(`[data-${name}]`)
).map(elem => elem.getAttribute(`data-${name}`))
.filter(val => val && val !== '0');
console.log(getAllDataAttrs('foo'));
<p data-foo="0"></p><br/>
<h6 data-foo="apple"></h6>
<p data-foo="0"></p><br/>
<h6 data-foo="book"></h6>
<p data-foo="0"></p><br/>
<h6 data-foo="car"></h6>
For reference, below is essentially the core vanilla javascript functionality needed ...
Using querySelectorAll, querySelector and getElementById should get you started.
// get "data-qa" value by "id"
var data = document.getElementById('id-test2').getAttribute('data-qa');
console.log(data);
// get "id" value by "data-qa"
var data = document.querySelector('[data-qa="data-qa-test2"]').id;
console.log(data);
// get all elements with attribute "data-qa"
var elems = document.querySelectorAll('[data-qa]');
// get all "ids"
var data = [];
elems.forEach(function(elem){
data.push(elem.id);
});
console.log(data);
// get all "data-qa"
var data = [];
elems.forEach(function(elem){
data.push(elem.getAttribute('data-qa'));
});
console.log(data);
<div id="id-test" data-qa="data-qa-test"></div>
<div id="id-test1" data-qa="data-qa-test1"></div>
<div id="id-test2" data-qa="data-qa-test2"></div>
<div id="id-test3" data-qa="data-qa-test3"></div>

Use js if/else with CSS value

I'm not really a programmer, but I have to change some html/css/js code for our needs.. and I'm struggling.
we are using a cloud software, which closed source, but with the possibility to change parts of the HTML frontend.
the software uses the CSS class "dom-admin" to hide some setting for non-admin.
<div class="dom-admin"> hidden for non-admins </div>
if a user logs in, the display property is set by the system to display:none;
.dom-admin {
display: none;
}
these hidden fields are mostly input fields. we dont want to hide this fields for our users, we just want to set the fields readonly.
my idea is to read the display value within a javascript in a if / else query:
if (dom-admin('display') == 'none')
{ document.write('<input type="text" id="XX">') }
else
{ document.write('<input readonly type="text" id="XX">') }
My Problem is to access the display value, i tried x examples from the net, but i never able to read the property of "dom-admin", eg:
document.getElementById('dom-admin').style.display
Chrome Javascript Console:
Uncaught TypeError: Cannot read property 'style' of null
Chrome dev-tools shows me this about the "dom-admin" CSS:
I appreciate any help...
Thanks, Patrick
Phase 1: Reveal All Hidden Field(s)
Multiple Elements with Common ClassName
The class .dom-admin is merely presentational, it is not a security measure. If you want to access multiple elements with the className of dom-admin you must collect them into a NodeList/HTMLCollection:
var DOMObjects = document.querySelectorAll('.dom-admin');
OR
var DOMObjects = document.getElementsByClassName('dom-admin');
With this list you'll have to iterate/loop through each *.dom-admin by a for loop, or array method (if the latter them the list might need to be converted to an array):
for (let i=0; i < DOMObjects.length; i++) {...
OR
DOMObjects.forEach(function(DOMObj, index) {...
To reverse the style of .dom-admin, you can set each *.dom-admin style object to:
DOMObj.style.display = "block"
OR remove .dom-admin class:
DOMObj.classList.remove('dom-admin')
Single Element with ClassName
The most direct method is to target the className via querySelector() or getElementsByClassName()[0]:
var DOMObj = document.querySelector('.dom-admin');
OR
var DOMObj = document.getElementsByClassName('dom-admin')[0];
Phase 2: Set All Revealed Fields to be Non-editable
Set/Get/Change Element Attributes
id, class, readOnly, etc. are called attributes. The attribute needed has a Boolean value (true/false). An attribute such as readonly merely needs to exist on the tag in order for it to be true. Not actually be in the tag of course makes it false.
Set an attribute
More specifically, adding an attribute to an element when it didn't exist before:
DOMObj.setAttribute('readonly', true);
Get an attribute
Finding an attribute and reading the value of said attribute:
DOMObj.getAttribute('readonly');
Change an attribute
If the attribute already exists and the value needs to be changed:
DOMObj.removeAttribute('readonly')
Keep in mind that the attribute you are dealing with has a Boolean value so it is handled differently than an attribute with a String value.
DOMObj.setAttribute('name', 'foo'); /*OR*/ DOMObj.name = "foo"
DOMObj.getAttribute('name');
DOMObj.setAttribute('name', 'bar') /*OR*/ DOMObj.name = 'bar'
Note 1
The syntax for methods querySelector()/querySelectorAll():
document.querySelector('.dom-admin')
CSS Selector ===========^-----^[dot for class]
is different than the syntax of method getElementsByClassName():
document.getElementsByClassName('dom-admin')
DOMString====================^------^[no dot needed]
document.getElementsByClassName('dom-admin')[0]
DOMString====================^------^===^-^[no dot needed]==
[Bracket Notation with index of zero is needed if there is only one target]
Note 2
Do not use document.write unless you intend to destroy everything on your page.
If you have inputs nested within the div.dom-admin, that would mean that those inputs exist already but the user cannot see them. It isn't necessary to create them and even if you do need to create any element, never use document.write. Instead use createElement() or use innerHTML on an element that you intend to use as a container.
Demo
// Collect all .dom-admin into NodeList
var das = document.querySelectorAll('.dom-admin');
// forEach() .dom-admin, remove the class .dom-admin
das.forEach(function(da, idx) {
da.classList.remove('dom-admin');
// Find all inputs in the div formerly known as .dom-admin
var inputs = da.querySelectorAll('input');
/* forEach() input nested within the div formerly known
|| as .dom-admin, set its attribute readonly to true
*/
inputs.forEach(function(input, index) {
input.setAttribute('readonly', true);
});
});
.dom-admin {
display: none;
}
input {
font: inherit;
width: 40%
}
<div class="dom-admin">
<input type='text' value='These inputs exist already'>
<input type='text'>
</div>
<div class="dom-admin">
<input type='text' value='Do not use document.write'>
<input type='text' value='it will overwite EVERYTHING'>
<input type='text' value=''>
</div>
<div class="dom-admin">
<input value='Fun fact:'>
<input value='input by default is type="text"'>
<input>
</div>
Here's what I used to achieve what you want.
As others mentioned, use document.querySelector('.dom-admin') to access the class and when you change display style, you can use the if statement
let admin = document.querySelector('.dom-admin');
if (admin.style.display === 'none') {
document.write('<input type="text" id="XX">');
} else {
document.write('<input readonly type="text" id="XX">');
}
.dom-admin {
width: 200px;
display: none;
}
<div class="dom-admin">
<form action="#">
<input type="text" id="user" />
<input type="number" id="user_number" />
<input type="password" id="user_password" />
<input type="submit" id="submit" />
</form>
</div>

Get all ellements and attributes of an element root using jQuery

I need to find all elements name in tag and all attributes in each element by using JQuery , each element has different attributes name and value i need to know this attributes name and value but don't know how
<Circuit>
<C Type="Bipolar" Name="c1">
<Cathode row="10" col="20"/>
<Anode row="15" col="50"/>
</C>
<AND Name="and1">``
<Input>
<Pin row="10" col="40"></Pin>
<Pin row="20" col="40"></Pin>
</Input>
<Output>
<Pin row="30" col="50"></Pin>
</Output>
</AND>
<Diode Name="d1" Value="30">
<Anode row="30" col="50"></Anode>
<Cathode row="40" col="60"></Cathode>
</Diode>
<R Type="Constant" Name="r1">
<Node row="60" col="80"></Node>
<Node row="70" col="80"></Node>
</R>
</Circuit>
I'll explain what i need, first need to know all elements (tag name) only in tag
o/p like this
elements in circuit tag
C
AND
Diode
R
after this need to know all attributes and elements in each element
like this
first element (C) has 2 attribute
first attribute is "Type" and the value of this attribute ="Bipolar"
second attribute is "Name" and the value of this attribute ="c1"
and has 2 elements
Cathode
Anode
and the same in each element
In jQuery there is the attribute matcher [attr], out of many other selectors
$("[Name]")
will get you all the elements that has the attribute Name
you can then loop through and get the actual name with jQuerys attr() function
$("[Name]").each(function() {
console.log($(this).attr("Name"));
console.log($(this).attr("Value"));
});
Edit
In the comments I found out that the question actually is how to serialize the whole structure into a data set. Therefore this might be a possible duplicate https://stackoverflow.com/a/5287305/576725
The following prior question shows how to get all the attributes of an element using JavaScript. The same, and the elements can be obtained using jQuery:
Get all Attributes from a HTML element with Javascript/jQuery
So that your code works cross-browser define your xml variable as follows:
var xml = $( $.parseXML( 'XML-String' ) );
You can the get all the elements and attributes as follows:
var all = xml.find('*').map(function(v,i) {
var ux = {};
$.each( this.attributes, function(index, node) {
ux[ node.nodeName ] = node.nodeValue;
});
return $.extend({}, { nodeName: this.nodeName }, {attributes:ux});
})
.get();
Which should give you output that looks like:
[{"nodeName":"Circuit","attributes":{}},{"nodeName":"C","attributes":{"Type":"Bipolar","Name":"c1"}},{"nodeName":"Cathode","attributes":{"row":"10","col":"20"}}, ....... .. .. .. ]
JS FIDDLE DEMO

How to check element type in javascript

I have a situation in which i want to convert a <select> tag into a <input type="text"> and <input type="text"> into <select> bu using some condition.
So how can i know that this element a type text or type select using id attribute.
And also a pure javascript solution:
function toggle(a){
if(a.tagName === 'INPUT'){
a.outerHTML = '<select id="toggle"></select>';
}else{
a.outerHTML = '<input type="text" id="toggle"/>'
}
}
http://jsfiddle.net/M6qXZ/1/
2018 ES6
e => e.outerHTML = e.tagName === "INPUT" ? "<select id='toggle'></select>" : "<input id='toggle'/>"
By using
$("#inputID").attr("type");
If you alert above you will get the type of input element, then you can apply checks accordingly.
Ref here : http://api.jquery.com/attr/
UPDATE
check using
if(!$("#inputID").is("select")) {
// the input field is not a select
}
got from a link while searching not tested though.
You can use .is() to test whether the elements is of type x like
Use :text selector to test for text input element
if($("#inputID").is(":text")){
//to test for text type
}
Use element selector to test for select element
if($("#inputID").is("select")){
//to test for select
}
Get the element type using jQuery:
var elementType = $("#myid").prop('tagName');
Get the input type attribute using jQuery:
var inputType = $("#myid").attr('type');
the condicion could be for example:
if($('#whateverid').attr('type') == "text")

get sibling that has id

So I have the following structure:
<div id="some id">
<div>
<input id="some other id" ....>
.....bunch of other inputs with no id field....
<select onclick="findSiblingWithId()">
</div>
<div>
<input id="some other id" ....>
.....bunch of other inputs with no id field....
<select onclick="findSiblingWithId()">
</div>
<div>
<input id="some other id" ....>
.....bunch of other inputs with no id field....
<select onclick="findSiblingWithId()">
</div>
So in my findSiblingWithId i don't know the id I'm looking for, all I know is that for the other components in the same div, only one will have an id. How can I get that ?
Edit:
Since it seems I didnt explain well enough I'll try to add more info. So I want for example when the select component changes (here it could be a button or anything, it doesnt matter) to get the corresponding <input id="some other id" ..> from the same div.
Like I tried to explain above, I have one huge div, the <div id="some id">, now this contains a number of smaller <div> without ids.
Each of these smaller divs will have ONE inputfield with an ID and a bunch of other without an ID. Now on an action, doesn't matter if a button click or whatever, from a component located IN THE SAME div (I thought the correct work would be 'sibling') is there a way I can find the input that has an ID attribute?
Regards,
Bogdan
You could iterate over all children of the parent:
function findSiblingWithId(element) {
var siblings = element.parentNode.children,
sibWithId = null;
for(var i = siblings.length; i--;) {
if(siblings[i].id) {
sibWithId = siblings[i];
break;
}
}
if(sibWithId) [
// found it
}
};
You have to pass the clicked element (I hope you have a proper select field);
<select onclick="findSiblingWithId(this)">
Consider to attach the event handler for the change event, if you want to have it triggered when the user selected a value.
Recursively iterate through the parent div and add logic to separate the inputs that have IDs
function findSiblingWithId(element) {
var sibling = null;
if (element.parentNode != null) {
sibling = element.parentNode.nextSibling;
// Then go through the child elements of your sibling node and get the one with an id
}
}
Using jQuery:
<!--Modify HTML so input is -->
<select onclick="findSiblingWithId(this)">
findSiblingWithId(elem){
return $(elem).siblings("[id]"); //if you need all
//return $(elem).siblings("[id]:eq(0)"); //if you need only first
}
The most direct way to get the value that has this id among the siblings is to use the sibling's filter like this:
let my_id = ...
$(this).siblings(`div[id=${my_id}]`)

Categories

Resources