Protractor: browser.isElementPresent() gives both true and false - reactjs

I am working with a Protractor and Cucumber to test a React project. In the course of this I have some elements that will have visibility:hidden/visible and I would like to use Protractor's isDisplayed or isPresent to test with.
I wrote a little test because I was getting some strange output and it looks like this:
this.Then(/^skal jeg kunne legge inn et nummer$/, function () {
var inputfelt = element(by.css(".mdl-textfield__input"))
expect(browser.isElementPresent(inputfelt)).to.eventually.equal(true)
expect(browser.isElementPresent(inputfelt)).to.eventually.equal(false)
});
This is my last version of this where I am using browser.isElementPresent. I have also used inputfelt.isDisplayed and inputfelt.isPresent and for all three of them this test passes.
I cannot see how it should be able to pass as the field cannot both be present and not be present.
Am I using this wrong?

I think you should use a wrapper, because you're using promises. Therefore any one promise that is resolved passes the test.
Try the following code:
this.Then(/^skal jeg kunne legge inn et nummer$/, function () {
var inputfelt = element(by.css(".mdl-textfield__input"))
return protractor.promise.all([
expect(browser.isElementPresent(inputfelt)).to.eventually.equal(true),
expect(browser.isElementPresent(inputfelt)).to.eventually.equal(false)
]);
});

you can now use protractor-react-selector to identify web elements by react's component, props, and state. This will automatically wait to load REACT in your app and then identify the web elements. It can save you from doing extra ton of workarounds.
You can identify your target element by:
const myElement = element(
by.react('MyComponent', { someBooleanProp: true }, { someBooleanState: true })
);
Let me know if this helps!

Related

Getting Error while waiting for Protractor to sync with the page: "both angul arJS testability and angular testability are undefined

I am trying to run a Protractor Test using PageObjectModel (module.exports mechanism).
My HomePageObjects.js is like below -
//Get the page objects to spec files in the form of key-value pair
/*module.exports.Homepage=
{
Name:element(by.name('name')),
Email:element(by.name('email')),
Password:element(by.id("exampleInputPassword1")),
Checkbox: element(by.css("input[id='exampleCheck1']")),
// element(by.cssContainingText("[id='exampleFormControlSelect1'] option", "Female")).click(); //One way to select a value
Gender:element(by.css("select[id='exampleFormControlSelect1']")).click().element(by.css("select[id='exampleFormControlSelect1']>option:nth-child(2)")), //Another way
// to select a value
EmploymentStatus:element(by.id("inlineRadio1")),
SubmitBtn:element(by.buttonText("Submit")),
Success:element(by.css("div[class*='success']"))
};*/
function HomePage1()
{
this.Name=element(by.name('name'));
this.Email=element(by.name('email'));
this.Password=element(by.id("exampleInputPassword1"));
this.Checkbox= element(by.css("input[id='exampleCheck1']"))
// element(by.cssContainingText("[id='exampleFormControlSelect1'] option", "Female")).click(); //One way to select a value
this.Gender=element(by.css("select[id='exampleFormControlSelect1']")).click().element(by.css("select[id='exampleFormControlSelect1']>option:nth-child(2)")); //Another way
// to select a value
this.EmploymentStatus=element(by.id("inlineRadio1"));
this.SubmitBtn=element(by.buttonText("Submit"));
this.Success=element(by.css("div[class*='success']"));
}
Homepage1 = new HomePage1();
module.exports={
Homepage1
}
When I am trying to execute the spec (which is below), I am getting error.
I have tried with browser.waitForAngularEnabled(false); but of no avail.
describe('Submit Form', function(){
var obj = require("./HomePageObjects.js"); // Access the objects from HomePageObjects.js
it('Test Submit Form', function(){
browser.get("https://qaclickacademy.github.io/protocommerce/");
obj.Homepage1.Name.sendKeys("Prabodh");
});
})
Can anyone please help me?
Error -
Message:
Error: Error while waiting for Protractor to sync with the page: "both angul
arJS testability and angular testability are undefined. This could be either be
cause this is a non-angular page or because your test involves client-side navig
ation, which can interfere with Protractor's bootstrapping. See http://git.io/v
4gXM for details"

Jasmine test to check Parameters passed to Backbone sync

I am writing a jasmine unit test to check if BackBone sync is called with a Parameter (which is an Object).
Here is the code base
In my collection I have sync method which is calling Back Bone sync
sync: function ( method, collection, options ) {
if ( !options ) {
options = {};
}
options.headers = {
“x-yz-webservice-client-id": “abcde"
};
return Backbone.sync( method, collection, options );
}
Here I want to check if options.headers of Backbone sync is set to object {
“x-yz-webservice-client-id": “abcde"
};
What I am trying to do in jasmine test and which is not working is as follows:
var headers = {
“x-yz-webservice-client-id": “abcde"
};
var autoCompleteRecommendationsCollection = new AutoCompleteRecommendationsCollection({},opt);
spyOn( Backbone, 'sync' );
autoCompleteRecommendationsView.initialize( options );
expect(Backbone.sync).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), jasmine.objectContaining(headers));
I want to test this third argument has the property set to headers as above. Is there a better way to check this argument?
Assuming you are using Jasmine v2.x, an alternative would be:
expect(Backbone.sync.calls.mostRecent().args[2].headers)
.toEqual(jasmine.objectContaining({
“x-yz-webservice-client-id": “abcde"
}));
However there appears to be nothing wrong with your code, have you confirmed your spy is actually called? Since you are dealing with a global Backbone variable, you need to make sure the spec and class under test share the same one.

Protractor won't get repeater inside a ng-view

I've a tricky question for you guys out there. I've made a simple exercise webapp using AngularJS and ngRoute.
Inside my index.html I got an ng-view element which provide two pages search_page.html and detail_of_result_page.html. The code works pretty fine, I put something in the first page input field, hit search button and all results magically appears in my page. The troubles comes with protractor that seems to not see my result in results repeater with his:
element.all(by.repeater("result in results"))
I've tried to put in browser.pause() and watch for errors, but everything seems right.
I've forgot the error code from Protractor:
Failed: Index out of bound.
Trying to access element at index: 0, but there are only 0 elements that match locator by.repeater("result in results")
OK, under your searchTest.js line: 27
beforeEach(function () {
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf($('li')),5000);
var resultItem = element.all(by.repeater('result in results')).first(); //BUG
var resultLink = resultItem.element(by.css('a'));
resultLink.click();
resultId = resultLink.getAttribute('href').then(function (attr) {
//var res = attr.match('/\/TDDBook\/Search\/#\/detail\/(\d+)/')[1]; // TODO: fix this shit
return 1;
});
});
Things seem alright until you do resultLink.click(); Assuming that it work fine for the first it ("should set the url to the selected detail view"). But when it come to second it("should see the details in the main page component") at this moment you are no longer on /#/splash route. Therefore your pereater no longer available to be located when your beforeEach() run again.
Solution
Your beforeEach doesn't seem useful and logically not run-able for your second it("should see the details in the main page component"). So just move all the thing like this:
it ("should set the url to the selected detail view",function () {
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf($('li')),5000);
var resultItem = element.all(by.repeater('result in results')).first(); //BUG
var resultLink = resultItem.element(by.css('a'));
resultLink.click();
resultId = resultLink.getAttribute('href').then(function (attr) {
//var res = attr.match('/\/TDDBook\/Search\/#\/detail\/(\d+)/')[1]; // TODO: fix this shit
return 1;
});
resultId.then(function (id) {
var expectedUrl = '/detail/'+id;
browser.getLocationAbsUrl().then(function (url) {
expect(url).toBe(expectedUrl);
})
})
})
P.S. just for your information, there is Page Object in protractor, which will be fit with the thing you attempt to do with your beforeEach() . Plus with a tiny bit of knowledge of commonJS (module.exports & require()) it will perfectly suit your needs ;) Cheer!

How can I grab all the content/text on a page inside a protractor test?

I want to set a variable based on whether some text is present on the current page or not.
And then run a protractor test that depends on that variable. I can't do $(':contains') because there's $ != jQuery in this context and i can't see a simple way to do it with getText() which returns a promise. Is there a matcher like expect.toContain? and a way to run some code after that matcher is run? Or is there some other strategy i can use.
if i think i understand you correctly you could use these following lines :
this will give all the text from the page :
window.document.getElementsByTagName("html")[0].innerText
and then you could use a regex to check for your text like this :
var res = patt.test(str);
this is sort of what you get :
for this example i am searching for the text "status": "ok"
var str = window.document.getElementsByTagName("html")[0].innerText;
var patt = /"status": "ok"/;
var res = patt.test(str);
if(res){console.log(text is present!)}
you want something a bit like this:
// utility to test expection of an element to match a regex:
var expectByCssToMatch = function(css, pattern) {
browser.driver.findElement(
by.css(css)).getText().then(function(text) {
expect(text).toMatch(pattern);
});
};
usage:
describe('Logging in, function() {
it('should work', function() {
var namePattern = new RegExp(param.name, i');
expectByCssToMatch('.messages', /log\s?in successful/i);
expectByCssToMatch('body', namePattern);
});
});
to achieve what you're asking you'd want to replace the expect() call with a callback of your own choice

How to test if an element has class using Protractor?

I'm trying out Protractor to e2e test Angular app and haven't figured out how to detect if an element has a specific class or not.
In my case, the test clicks on submit button and now I want to know if form[name="getoffer"] has class .ngDirty. What may be the solutions?
describe('Contact form', function() {
beforeEach(function(){
browser.get('http://localhost:9000');
element(by.linkText('Contact me')).click();
});
it('should fail form validation, all fields pristine', function() {
element(by.css('.form[name="getoffer"] input[type="submit"]')).click();
expect(element(by.name('getoffer'))).toHaveClass('ngDirty'); // <-- This line
});
});
One gotcha you have to look out for with using toMatch(), as in the accepted answer, is partial matches. For instance, let's say you have an element that might have the classes correct and incorrect, and you want to test that it has the class correct. If you were to use expect(element.getAttribute('class')).toMatch('correct'), that will return true even if the element has the incorrect class.
My suggestion:
If you want to only accept exact matches, you can create a helper method for it:
var hasClass = function (element, cls) {
return element.getAttribute('class').then(function (classes) {
return classes.split(' ').indexOf(cls) !== -1;
});
};
You can use it like this (taking advantage of the fact that expect automatically resolves promises in Protractor):
expect(hasClass(element(by.name('getoffer')), 'ngDirty')).toBe(true);
If you're using Protractor with Jasmine, you could use toMatch to match as a regular expression...
expect(element(by.name('getoffer')).getAttribute('class')).toMatch('ngDirty');
Also, note that toContain will match list items, if you need that.
Simplest is:
expect(element.getAttribute('class')).toContain("active");
Based on the answer from Sergey K, you could also add a custom matcher to do this:
(coffeescript)
beforeEach(()->
this.addMatchers({
toHaveClass: (expected)->
#message = ()->
"Expected #{#actual.locator_.value} to have class '#{expected}'"
#actual.getAttribute('class').then((classes)->
classes.split(' ').indexOf(expected) isnt -1
)
})
)
Then you can use it in tests like this:
expect($('div#ugly')).toHaveClass('beautiful')
And you'll get the following error if it doesn't:
Message:
Expected div#ugly to have class beautiful
Stacktrace:
Error: Expected div#ugly to have class 'beautiful'
Have you tried this?
const el = element(by.name('getoffer'));
expect(el.getAttribute('class')).toBe('ngDirty')
or a variation of the above...
I made this matcher, I had to wrap it in a promise and use 2 returns
this.addMatchers({
toHaveClass: function(a) {
return this.actual.getAttribute('class').then(function(cls){
var patt = new RegExp('(^|\\s)' + a + '(\\s|$)');
return patt.test(cls);
});
}
});
in my test i can now do stuf like this:
var myDivs = element.all(by.css('div.myClass'));
expect(myDivs.count()).toBe(3);
// test for class
expect(myDivs.get(0)).not.toHaveClass('active');
this also works when an element has multiple classes or when an element has no class attribute at all.
function checkHasClass (selector, class_name) {
// custom function returns true/false depending if selector has class name
// split classes for selector into a list
return $(selector).getAttribute('class').then(function(classes){
var classes = classes.split(' ');
if (classes.indexOf(class_name) > -1) return true;
return false;
});
}
This is how I do it at least, without the need to use the expect function. This function simply returns true if the class is inside the element and false if not. This also uses promises so you would use it like:
checkHasClass('#your-element', 'your-class').then(function(class_found){
if (class_found) console.log("Your element has that class");
});
Edit: I just realized this is essentially the same as the top answer
Here a Jasmine 1.3.x custom toHaveClass matcher with negation .not support plus wait up to 5 seconds (or whatever you specify).
Find the full custom matcher to be added on your onPrepare block in this gist
Sample usage:
it('test the class finder custom matcher', function() {
// These guys should pass OK given your user input
// element starts with an ng-invalid class:
expect($('#user_name')).toHaveClass('ng-invalid');
expect($('#user_name')).not.toHaveClass('ZZZ');
expect($('#user_name')).toNotHaveClass('ZZZ');
expect($('#user_name')).not.toNotHaveClass('ng-invalid');
// These guys should each fail:
expect($('#user_name')).toHaveClass('ZZZ');
expect($('#user_name')).not.toHaveClass('ng-invalid');
expect($('#user_name')).toNotHaveClass('ng-invalid');
expect($('#user_name')).not.toNotHaveClass('ZZZ');
});
One way to achieve this would be to use xpath and use contains()
Example:
var expectElementToHaveClass = function (className) {
var path = by.xpath("//div[contains(#class,'"+ className +"')]");
expect(element.all(path).count()).to.eventually.be.eq(1);
};
You can use the CSS parser to handle this by checking if an element with the given class exists:
expect(element(by.css('.form[name="getoffer"].ngDirty')).isPresent()).toBe(true);
Essentially, you're solving a few problems:
how to get class. class is an html attribute and thus can be retrieved by this command (await is the recommended way these days)
let class = await element.getAttribute('class')
Once you got the value of a class, you want to assert it
// for exact value
expect(class).toBe("active");
// for partial match
expect(class).toContain("active");
// or
expect(class.includes("active")).toBe(true);
// BUT, keep in mind
expect('male').toContain('male');
expect('female').toContain('male');
// BOTH pass

Resources