Using Protractor to select elements with by.repeater() - protractor

Using the following Protractor element and by.repeater() API methods below:
var targetRowText = 'Sales';
var targetGridName = 'myGrid';
var sel = 'grid-directive[grid-name="' + targetGridName + '"] .col-freeze .grid-wrapper';
var gridRows = element(by.css(sel).all(by.repeater('row in vm.sourceData.data'));
var result = gridRows.all(by.cssContainingText('span', targetRowText)).first();
I am able to select the following row element from a grid which I have labeled, myGrid:
<div id="rowId_21" ng-class-odd="'row-2'" ng-class-even="'row-3'" ng-class="vm.hideRow(row)" class="row-3 height-auto">
<div ng-repeat="column in vm.sourceData.columns" >
<div ng-if="!column.subCols" class="ng-scope">
<div ng-if="row[column.field].length !== 0" class="ng-scope highlight21">
<span ng-bind-html="row[column.field] | changeNegToPrenFormat" vm.highlightedrow="" class="ng-binding">
Sales
</span>
</div>
</div>
</div>
</div>
Please note that I have used by.cssContainingText() to look up the "Sales" span element.
MY PROBLEM:
That that I have located this row in var result, how can I retrieve the id attribute of that outer-most div ?
In other words, I need to selected <div id="rowId_21" so that I can reuse id="rowId_21" in a subsequent Protractor selector.
In jQuery, for example, I could use brute force to get that outer div id as follows :
var el = $('grid-directive[grid-name="Sales"] .col-freeze .grid-wrapper #rowId_21 span')
el.parentElement.parentElement.parentElement.parentElement;
Here's a high-level outlines of what I mean. The grid actually separates the left-most column from the actual data rows, so there are two distinct divs that accomplish this:
<div grid-directive grid-name="myGrid">
<div class="col-freeze" >
<!-- CONTAINS LEFT-MOST "CATEGORIES" COLUMN -->
</div>
<div class="min-width-grid-wrapper">
<!-- CONTAINS THE DATA ROWS-->
</div>
However, I'm struggling to do this in Protractor.
Advice is appreciated...
Bob

A straight-forward option would be to get to the desired parent element using the ancestor axis:
element(by.xpath("./ancestor::div[starts-with(#id, 'rowId')]")).getAttribute("id").then(function (parentId) {
// use parentId here
});
Though, I think that this going down and then up the tree should be considered as a sign that you are not approaching the problem in an easy and correct way.

Related

AEM HTL repeating an element containing "data-sly-resource" using data-sly-repeat

I am trying to loop through an div element containing "data-sly-resource" to be able to include that component as a child.
I have a table element where each cell has an individual authoring interface using a child component. I get two counts that is rowCount as array of "x" and columnCount as array of "y". I'm able to iterate through the class="table-cell" and am able to add "y" number of columns with their respective child components using data-sly-resource . But when I'm trying to iterate through class="table-row" using rowcount it only iterates the content and adds "y" number of rows but doesn't add child components in the rows
thank you
<div class="table">
<sly data-sly-list.card="${rowCount.array}">
<div class="table-row">
<sly data-sly-list.card="${columnCount.array}">
<div class="table-cell" data-sly-test.resourceNode="${['cell', card.intValue] #join='-'}">
<div data-sly-resource="${ #path=resourceNode,
resourceType='example/components/content/tableLongForm/rte'}"
data-sly-unwrap="${!wcmmode.edit && !wcmmode.preview}" style="width:60px;"></div>
</div>
</sly>
</div>
</sly>
</div>
Authoring Interface Image
You are using the same identifier card for the list items in the nested loops, perhaps you meant it as:
<div class="table">
<sly data-sly-list.row="${rowCount.array}">
<div class="table-row">
<sly data-sly-list.card="${columnCount.array}">
<div class="table-cell" data-sly-test.resourceNode="${['cell', row.intValue, card.intValue] # join='-'}">
<div data-sly-resource="${ #path=resourceNode,
resourceType='example/components/content/tableLongForm/rte'}"
data-sly-unwrap="${!wcmmode.edit && !wcmmode.preview}" style="width:60px;"></div>
</div>
</sly>
</div>
</sly>
</div>

How to use result of .length in selector Cypress

I'm trying to perform an action based on the results of an earlier assertion. I have the following situation, I want to determine the number of versions for a document, this is represented in the follwing html:
<ul data-testhook-id="accordion-body">
<li data-testhook-id="accordion-body-item">
<div>
<div data-testhook-id="version-1">
<div data-testhook-id="avatar">
<div data-id="tooltip">John Doe</div>
</div>
</div>
<span data-testhook-id="content">1. 17-08-2018 at 15:26</span>
</div>
<div data-testhook-id="version-2">
<div data-testhook-id="avatar">
<div data-id="tooltip">John Doe</div>
</div>
</div>
<span data-testhook-id="content">2. 20-08-2018 at 13:05</span>
</div>
<div data-testhook-id="version-3">
<div data-testhook-id="avatar">
<div data-id="tooltip">Jane Doe</div>
</div>
</div>
<span data-testhook-id="content">3. 20-08-2018 at 13:11</span>
</div>
<div data-testhook-id="version-4">
<div data-testhook-id="avatar">
<div data-id="tooltip">No Body</div>
</div>
</div>
<span data-testhook-id="content">4. 21-08-2018 at 13:11</span>
</div>
<svg width="12" height="12" data-testhook-id="icon-active-version"><path d="path-here"></path></svg>
</div>
</div>
</li>
Each element with test id data-testhook-id="version-xxx" is a line containing version information and its these div elements I want to count. for this I have set up the following:
cy.get('[data-testhook-id="accordion-body-item"] > div > div').its('length').as('versions')
Now if I add the following assertion, this passes without any problem
cy.get('#versions').should('equal', 4)
But if I try to use the outcome of the number of div's found as a variable in a console.log I no longer get '4' but [object, Object]. Tried this by:
var size = cy.get('[data-testhook-id="asset-versions"] [data-testhook-id="accordion-body-item"] > div > div').its('length')
console.log('foo ' + size)
Which results in foo [object Object] being printed to the console.
What I want to do is use the number of items in a selector to select an element, like:
cy.get('[data-testhook-id="accordion-body-item"] > div > div:nth-child(' + size + ')').find('svg').should('have.attr', 'data-testhook-id', 'icon-active-version')
But as the var is not a number I get the msg
Error: Syntax error, unrecognized expression: :nth-child
[data-testhook-id="asset-versions"] [data-testhook-id="accordion-body-item"] > div > div:nth-child([object Object])
Is it possible to use the number of elements found as a variable for a next selector?
Try this:
cy.get('[data-testhook-id="accordion-body-item"] > div > div').its('length').then((size) => {
cy.get('[data-testhook-id="accordion-body-item"] > div > div:nth-child(' + size + ')').find('svg').should('have.attr', 'data-testhook-id', 'icon-active-version')
});
You cannot assign or work with the return values of any Cypress command. Commands are enqueued and run asynchronously. What commands really return are Chainable<typeOfValueYouWant>, in another words, it's kind of queue object which resolves with desired value.
Read more here
BTW: I think you should consider change your approach for selecting elements. Read more here.
For those who are using Typescript - I wrote plugin for tslint which prevents making this mistake: https://github.com/krzysztof-grzybek/tslint-plugin-cypress

How to obtain the id of an item which the mongo db is currently displaying

I use meteor for my project and I have the following code:
<div class="col-md-4 activities_text">
<center><h2>Learn</h2><br/><br/></center>
{{getContent.description}}
</div>
<div class="col-md-8">
<div class="button_absolute">
<button class="button_ahead"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span></button>
</div>
<img class="activities_image" name="image" src="{{getContent.src}}"/>
</div>
</div>
And I want to obtain the id or (subsequently the whole information in the database) about the item which my HTML currently uses for the "src" of the image subsequently for title and description. So I could swap them when the button is clicked. I tried this:
'click .button_ahead':function(event){
var infoNumber = this.number;
console.log("this is the number of the image"+this._id+" "+ this.number);
infoNumber = infoNumber + 1;
Session.set("number", infoNumber);
}
but both this.number and this._id remain undefined. Maybe the problem is that when I click the button I should obtain the data about the image etc.. but not for the button itself.
The context is the button element, so, this is probably not what you want to use, if you really want to use this try changing the context with .call() or .apply() functions.
PS: I'd like to leave a comment under question but I can't due to the rep points
$(event.currentTarget)[0].id would give you the id of the button in
'click .button_ahead':function(event){
var id = $(event.currentTarget)[0].id
var infoNumber = this.number;
console.log("this is the number of the image"+id+" "+ this.number);
infoNumber = infoNumber + 1;
Session.set("number", infoNumber);
}
so in your template you can simply put
<button class="button_ahead" id="this._id"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span></button>
to get the id of the current resource you're displaying.
you can also use more simply event.currentTarget.id

Selecting a DOM Element when (auto-generated) HTML is not well formed

I'm trying to select a control in order to manipulate it but I'm having a problem: I can't select it. Maybe it's because the xml structure, but I really can't change it because it is externally created. SO I have this:
<span class="xforms-value xforms-control xforms-input xforms-appearance xforms-optional xforms-enabled xforms-readonly xforms-valid " id="pName">
<span class="focus"> </span>
<label class="xforms-label" id="xsltforms-mainform-label-2_2_4_3_">Name:</label>
<span class="value">
<input readonly="" class="xforms-value" type="text">
</span>
<span class="xforms-required-icon">*</span>
<span class="xforms-alert">
<span class="xforms-alert-icon"> </span>
</span>
</span>
And what I need is to get the input (line 5). I tryed a lot, for example:
var elem01 = document.getElementById("pName");
console.log("getElementById: " + elem01);
var elem02 = document.evaluate(".//*[#id='pName']" ,document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null );
console.log("evaluate: " + elem02);
console.log(elem02.singleNodeValue);
var elem03 = document.querySelector("#pName");
console.log("querySelector: " + elem03);
But none of that allows me to get a reference to the control. What's wrong?
With XPath, the problem seems to be the XML is no well formed, so document.getElementById("pName") doesnt return anything.
http://jsfiddle.net/wmzyqqja/7/
The problem with your example is that you are executing your Javascript before the relevant DOM elements are loaded (i.e. your code is in the head element):
This will fix the example:
window.onload = changeControlValue;
JSFiddle: http://jsfiddle.net/TrueBlueAussie/wmzyqqja/8/
Try this
var elem01 = document.getElementById("pName");
var inp = elem01.getElementsByTagName("input")[0];
(in JSFiddle the "onload" setting is required.)

jQuery next() selector when divs are not neighboring

Ive been using the following to change the width of the div.my-div that appears after the one you've clicked:
$(".my-div").click(function () {
$(this).next().css({'width':'500px'});
});
As my divs were neighboring, this worked fine:
<div class="my-div">stuff</div>
<div class="my-div">stuff</div>
<div class="my-div">stuff</div>
However now the structure has changed so they are no longer neighboring:
<div>
<div class="my-div">stuff</div>
</div>
<div>
<div>
<div class="my-div">stuff</div>
</div>
</div>
<div class="my-div">stuff</div>
Whats the simplest way to select the next element of the same class?
Thanks
jQuery will return elements in order of their appearance in the DOM.
As such, you could cache all the .my-div elements, use the index()[docs] method to get the index of the one that received the event, increment it and use the eq()[docs] method to get the next one.
var divs = $(".my-div"); // cache all of them
divs.click(function () {
var idx = divs.index( this ); // get the index in the set of the current one
divs.eq( idx + 1 ).css({'width':'500px'}); // get the one at the next index
});
This saves you from doing a bunch of unnecessary DOM selection and traversing.
Example: http://jsfiddle.net/VrATm/1/
EDIT: Posted wrong example link. Fixed.
You can traverse the tree hierarchy. That is, you can first jump to parent, then to next, then to children, like this:
$(this).parent().next().find(' > div').css({'width':'500px'});