Protractor - select next sibling of the current element - angularjs

In my code there a list of elements (tr) selected with "protractor.By.repeater".
This list is looped using "forEach".
Within this loop, the element is clicked, and this click should trigger the inclusion of a new "tr" just after the clicked element.
I want to select that new line.
I used :
var nextRow = tr.$(protractor.By.xpath('following-sibling::tr'));
but then with :
nextRow.isDisplayed(function(row){
console.log('row', row);
});
it generates errors such as : " UnknownError: java.util.HashMap cannot be cast to java.lang.String"
Is there another way to achieve what I want, i.e to select the next sibling of the current element ?
Or did I wrote something incorrect there ?
Thanks for any help!

Looks like you are missing a .then there and misinterpreted what isDisplayed does.
Code should be more like:
nextRow.isDisplayed().then(function(visible) {
console.log('is displayed? ', visible);
});
Also your ElementFinder nextRow doesn't look quite alright, what is variable tr below? and Protractor $ is an alias for element(by.css which takes a string argument, not a webdriver.Locator like you interpreted below, in your code:
var nextRow = tr.$(protractor.By.xpath('following-sibling::tr'));
So instead maybe you meant:
var tr = $('table tr.first'); // just guessing here since you didn't provide that code
var nextRow = tr.element(By.xpath('following-sibling::tr'));

Using Xpath selectors is not a better choice as it slows down the element finding mechanism.
I have designed a plugin to address this specific issues: protractor-css-booster
The plugin provides some handly locators to find out siblings in a better way and most importantly with CSS selector.
using this plugin, you can directly use:
var nextRow = element(by.css('table tr.first')).element(By.followingSibling('tr'));
Hope, it will help you...

Related

Can't find id of Angular ui-grid element with Selenium/Protractor

I have some Protractor tests that are running on an Angular application that uses ui-grid. I have some working smoke tests for the application, but I am just starting to implement tests using https://github.com/angular-ui/ui-grid/blob/master/test/e2e/gridTestUtils.spec.js to test the ui-grid components. The problem I'm having is that I need the actual id of the grid element in order to use the getGrid function.
I'm able to successfully locate the element using element(by.css("[id$='-grid-container']")), but for some reasons my attempts to get the full id out of the element have failed. Here is what I am trying:
var grid = element(by.css("[id$='-grid-container']"));
grid.getAttribute('id').then(function(result) {
console.log(result);
var myGrid = gridTestUtils.getGrid(result.toString());
gridTestUtils.expectCellValueMatch(myGrid, 0, 0, 'Cox');
});
The console.log(result); is not logging anything. It doesn't SEEM necessarily related to ui-grid, it's just Selenium isn't finding the id for some reason. As far as I can tell I'm using getAttribute correctly; it works with this syntax in other tests, but maybe I'm missing something. Any ideas why this isn't working?
Edit because my comment is unreadable:
Thanks for the suggestions. However, I'm still just as confused because
var grid = element(by.css("[id$='-grid-container']"));
console.log(grid.toString());
grid.getAttribute('id').then(function(result) {
console.log('======'+result);
var myGrid = gridTestUtils.getGrid(result.toString());
gridTestUtils.expectCellValueMatch(myGrid, 0, 0, 'Cox');
});
gives a console output of
[object Object]
======
so it seems like the element is being found, which I had already checked, and the console.log inside the promise is being executed.
It's like it can't find the 'id', which according to the API documentation means there is no id on the element. But that is definitely not true.
Not sure on the semantics, but you could try this, just to make sure that you are getting the first element, if multiple:
element.all(by.css('[id$="-grid-container"]')).first().getAttribute('id').then(function(result) {
console.log(result);
var myGrid = gridTestUtils.getGrid(result.toString());
gridTestUtils.expectCellValueMatch(myGrid, 0, 0, 'Cox');
});
Your code looks correct.
However, if your console.log(result) doesn't log anything, this means you either didn't find the element or the moment you execute the getAttribute() the element is no longer present.
See in the API description, that getAttribute() always returns a value, if the element is present.
Maybe try to extend console.log('======='+result); to figure out, if that line of code gets executed (I'm pretty sure it's not executed). Also try console.log(grid.toString());, which should output [object Object], if the element is really found.
As for the ElementFinder, I'm used to use the ' and " just the other way around, so element(by.css('[id$="-grid-container"]')); or shorter $('[id$="-grid-container"]').
Let me know, if this helped and you could further determine the cause.
Round 2
Let's exclude a few things
change getAttribute('id') to getAttribute('outerHTML') to see,
if there is anything logged.
change (result) to (resultattr) to exclude, that result is else used by a plugin, who put result as a global variable.
change grid.getAttribute() to be element(by.css("[id$='-grid-container']")).getAttribute
What are the results of those actions?

Protractor retrieve single element from ElementArrayFinder

I want to retrieve the first element from a ElementArrayFinder matching a condition. The following code provides me with the element that I'm expecting but it goes through all elements which takes time.
Is there any way to this?
return content.all(by.css('.class')).filter(function(label){
return label.getText().then(function(text){
return text === searchName;
});
}).first();
CONCLUSION:
Answer provided by Sudharsan Selvaraj worked as a charm
var ele = content.all(by.xpath(".//*[contains(#class,'class')][normalize-space(text())='some-value']")).first()
Reduced drastically time searching the element
The answer provided by Florent B. also solves the problem and looks like a much simpler approach.
return content.element(by.cssContainingText('.class', searchName));
You can use xpath to find the element with specific text.Look at below sample xpath.
var ele = content.all(by.xpath(".//*[contains(#class,'class')][normalize-space(text())='some-value']")).first()
You should use the locator by.cssContainingText to find an element by CSS and text:
return content.element(by.cssContainingText('.class', searchName));
Why don't you directly access the first element of the label?
return content.all(by.css('.class label')).first().getText().then(function (text) {
return text === searchName;
});
P.S. I didn't test the code but it should probably work

Cursor (caret ) moves to beginning when call on change event on contenteditable div using angular js

The cursor gets moved to the beginning instead of being set to last type position when the change event is fired. I am using content editable div using angular js.
plunker
http://plnkr.co/edit/FFaWNZmgk00Etubtjgg8?p=preview
Why does the cursor position move to beginning?
I know this is an old question but, as I've been searching my whole working day to find a solution to this issue, I'd like to share this solution to people who might still looking for a working answer :
var el, el2, range, sel;
el = element[0];
range = document.createRange();
sel = window.getSelection();
if (el.childNodes.length > 0) {
el2 = el.childNodes[el.childNodes.length - 1];
range.setStartAfter(el2);
} else {
range.setStartAfter(el);
}
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
I got this code originally from akatov/angular-contenteditable directive, "moveCaretToEndOnChange" method.
and this worked for me.
I would assume Angular is rewriting the innerHTML property of the editable element, thus destroying and recreating all of the DOM tree within it, which results in the caret resetting to the start. See these answers for potential solutions:
https://stackoverflow.com/a/13950376/96100
https://stackoverflow.com/a/8240055/96100
In my case I was using the same reference of array for changing the innerHTML as was used on the DOM (two way binding). Hence the dom re-rendered and thus the cursor moved to the beginning.
The Solution was to make a deep copy of the array using JSON.parse(JSON.stringify(a)); where a is the array that stores the words that are contenteditable and then do the change
let filetexts =JSON.parse(JSON.stringify(this.filetexts));
// updating the current text
filetexts[alternativesIndex].Alternatives[0].Words[wordIndex].Word = html;
let data = { Text: JSON.stringify(filetexts) };
this.fileService.changeFileText(data, this.file.fileId).subscribe(res => {
});

Error appending Pagedown in AngularJS Directive

Based on this question, though I felt this warranted its own question: Google pagedown AngularJS directive
Following the example from this question, it seems to work, however I am running into issues when I try to append a directive to the page.
Here is the code I have in the linking function:
scope.editor_id = null;
if(attrs.id == null) {
scope.editor_id = nextID++;
} else {
scope.editor_id = attrs.id;
}
//append editor HTML
//has to be done here so that it is available to the editor when it is run below
var editor_html = $compile(
'<div id="wmd-button-bar-' + scope.editor_id + '"></div>' +
'<textarea class="wmd-input" id="wmd-input-' + scope.editor_id + '" ng-model="content"></textarea>'
)(scope);
element.find('.wmd-panel').append(editor_html);
var editor = new Markdown.Editor(editor_converter, "-" + scope.editor_id);
editor.run();
However, when I append one of these to the document, I get the following error:
TypeError: Cannot read property 'attachEvent' of null
This error tends to crop up when the wmd-input is not present in the HTML. however, I am adding it with the $compile function, and it works on page load, but not when it is appended. What am I doing wrong here?
UPDATE
I was able to reproduce your problem: http://plnkr.co/edit/R2eDKBreHmYBjPtU0JxD?p=preview
Why typeError: Cannot read property 'attachEvent' of null?
I was wrong with my previous assumption ( the composite linking function do returns the element)
The problem is with the way you use angular.element#find.
angular.element#find only search for child elements not on the whole document.
the DOM element with a .wmd-panel class is not a child of the current element.
This should work fine:
angular.element('.wmd-panel').append(editor_html);
Try doing compile like this:
$compile('template stuff goes here')(scope, function(cloned, scope){
element.append(cloned);
});
You may also have to define your editor inside the callback function because I'm not sure if it's asynchronous or not. You may also want to re-consider having your directive compile and append to itself like this. Why not just add more instances of the entire directive using something like ng-repeat?
Also, if you have multiple instances inside this one directive, you will lose reference to editor. Not sure what's going on outside this code so I can't really tell.

How can I select elements in ExtJS?

I have a ExtJS page that is outputting the following DOM.
I want to select a specific element so that I can respond to a click on it.
However, the following code never selected the elements that are built with the extJS library. JQuery can't seem to select them either.
ExtJS can't even find an element that is defined in the <body> element in the HTML file itself. However, JQuery can indeed select an element defined in the <body> element.
I have been reading this guide which seems to indicate it should be no problem to just use Ext.get() and Ext.select() to get elements, however, as the following code shows, the elements are never selected.
How do I select an element in ExtJS?
var members = Ext.select('x-panel');
for (var i = members.length - 1; i >= 0; i--){
element = members[i];
element.on('click', function() { alert('test');})
};
Try Ext.select('.x-panel') with a period in the selector, which is required for selecting by class name.
bmoeskau is right, you can use Ext.select('.x-panel') which returns a collection of divs with class x-panel.
Note that Ext.get() just returns a single element, so if you want to get multiple matches you should use Ext.select() or Ext.query().
And what's more, Ext.select() returns Ext.CompositeElement, though the document does not list but it supports all the methods of Ext.Element so your example can be simply written as:
Ext.select('.x-panel').on('click', function(){ alert('test') });
Code above will add event handler for click operation to each x-panel.
Use:
Ext.get(el);
if you need to get the flyweight element, use:
Ext.fly(el);
See the API documentation for the Static Ext object for details

Resources