I use Protractor 4.0.14 on an Angular 1.6.3 website.
I execute two identical tests. The first one will succeed but not the second one. Any idea ?
it('Should return a toast error on inferior amount', function () {
$$('#purposeList input.amount').first().clear().sendKeys('4999');
$$('button[type="submit"]').get(0).click();
expect($('div.toast.toast-error').isPresent()).toBe(true);
element.all(by.model('purpose.amount')).first().clear();
$$('button.toast-close-button').each(function (item) {
item.click();
});
expect($('button.toast-close-button').isPresent()).toBe(false);
});
it('Should return a toast error on inferior amount BIS', function () {
$$('#purposeList input.amount').first().clear().sendKeys('4999');
$$('button[type="submit"]').get(0).click();
expect($('div.toast.toast-error').isPresent()).toBe(true);
element.all(by.model('purpose.amount')).first().clear();
$$('button.toast-close-button').each(function (item) {
item.click();
});
expect($('button.toast-close-button').isPresent()).toBe(false);
});
The field is provided with a wrong amount so it throws a toast-error when I submit. When I do it manually, I get the toast-error. When doing it with protractor, only the first of the identical tests will pass. On the other, it looks like the toast is never launch or closed really quickly.
I tried sleeps, ignoreSynchronisation with browser wait. I tried to replace the $timeout by $interval in the service dealing with error toasts (although other $timeout could be present). It didn't change anything.
It is actually the first expect of the second it that is failing and there is no actual help from the error, it's just an expect at false instead of true :
Message:
Expected false to be true.
Edit :
So #LostJon, by handling every promises, do you mean like this :
it('Should return a toast error on inferior than authorized purpose amount on step 1 BIS', function () {
$$('#purposeList input.amount').first().clear().then(function() {
$$('#purposeList input.amount').first().sendKeys('4999').then(function () {
$$('button[type="submit"]').get(0).click().then(function () {
expect($('div.toast.toast-error').isPresent()).toBe(true);
element.all(by.model('purpose.amount')).first().clear().then(function () {
$$('button.toast-close-button').each(function (item) {
item.click();
}).then(function () {
expect($('button.toast-close-button').isPresent()).toBe(false);
});
});
});
});
});
});
I tried it, same result.
Edit :
Apparently it was an ignoreSynchronization = true that was inside the wait instead of before. I didn't figure it out before because I also set browser.manage().timeouts().implicitlyWait(2000), so without synchronization, we were still waiting for the elements but it was not accurate.
so, the below line returns a promise.
$$('button[type="submit"]').get(0).click();
Your code is running synchronously, when you should be handling the promise resolve from click(). In fact, your line that calls clear() also returns a promise, so that needs to be handled as well.
It was an ignoreSynchronization = true that was inside the wait instead of before. I didn't figure it out before because I also set browser.manage().timeouts().implicitlyWait(2000), so without synchronization, we were still waiting for the elements but it was not accurate.
Related
Whilst attempting to run integration tests with Angular 2 and Karma test runner the following issue became clear. A test was always passing even when it should have been failing.
The issue occurs when the expect() method is placed inside the subscribe() method of an Observable.
The need to do this arose as the test would subscribe to the Observable and then continue processing the rest of the test before the Observable has finished executing.
However, placing the expect within the subscribe() method automatically causes the test to pass even when there are very obvious syntax errors:
it('should pass or fail', inject([Service], (_service : Service) => {
let result = _service.returnObservable();
result.subscribe((sfasdasdaa23231gr) => {
expect(r.isAfhahzdzd vailable).not.35q6w623tyrg /.0824568sfn toBe(truDDIDIDIDDIe);
});
}));
the previous code passes, but how? there are syntax errors everywhere. Does anyone know where this issue lies? In the testing or in the subscribe() method?
Because it's asynchronous processing, you should add the async method:
it('should pass or fail', async(inject([Service], (_service : Service) => {
let result = _service.returnObservable();
result.subscribe((sfasdasdaa23231gr) => {
expect(r.isAfhahzdzd vailable).not.35q6w623tyrg /.0824568sfn toBe(truDDIDIDIDDIe);
});
})));
I'm running karma test cases through gulp as below:
gulp.task('unit-test-karma', function () {
return gulp.src(filePaths.libraryPaths.concat(filePaths.codePathsVerbose.concat(filePaths.testPaths).concat(filePaths.htmlPaths).concat(filePaths.jadePaths)))
//.pipe(plumber({ errorHandler: notify.onError(function(error) { console.log(error.message); return "Karma Error"; }) }))
.pipe(karma({
configFile: './karma.conf.js',
action: 'run', // watch
singleRun: true,
reporters: [ 'dots' ]
}));
});
When I run with action as run, IE 11 throws below error.
IE 11.0.0 (Windows 10 0.0.0) ERROR
'expect' was used when there was no current spec, this could be because an asynchronous test timed out
at C:/BbCAT-WebDI/BbCAT-Web/BbCAT-Angular/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:938
But if run the same with action as watch then all test case executing successfully in chrome, IE and firefox.
After reading some post, It seems there is some issue with $http service call but not able to find from where exactly the problem is!
Could you have nested a couple of tests that should be separated, or have multiple async calls resolving in the same test case?
I produced this same error, but it was of my own doing. I had two async tests inside of one it(). As soon as either of the promises resolved, the test ended. The other promise resolution was orphaned.
Consider these snippets. Assume that the function under test responds correctly as called.
Note: I have left out the error paths from the then() for purposes of illustrating the issue more clearly.
This construction fails. When either of the promises return and done() is fired, the second one now fails with the "'expect' was used when there was no current spec..." error.
describe( "delay", function(){
var calculator = new Calculator();
it( "delays execution - add and subtract", function(done){
delay( 1000, calculator, 'add', [ 10, 5 ] )
.then(function(result){
expect(result).toEqual( 15 );
done(); // <---- as soon as this runs, test is over
});
delay( 500, calculator, 'subtract', [ 9, 5 ] )
.then(function(result){
expect(result).toEqual( 4 );
done(); // <---- as soon as this runs, test is over
});
});
} );
This is the correct way to write the tests. Each promise is encapsulated in its own test.
describe( "delay", function(){
var calculator = new Calculator();
it( "delays execution - add", function(done){
delay( 1000, calculator, 'add', [ 10, 5 ] )
.then(function(result){
expect(result).toEqual( 15 );
done(); // <--- this is now the only resolution for this test
});
});
it( "delays execution - subtract", function(done){
delay( 500, calculator, 'subtract', [ 9, 5 ] )
.then(function(result){
expect(result).toEqual( 4 );
done(); // <--- this is now the only resolution for this test
});
});
} );
As I don't yet have enough reputation to comment, I'm putting my plea here. :-)
Could you mark this answer as correct if this turns out to be your problem?
I experienced this error, but it was because I had a describe function without an it function inside.
Incorrect
describe('helpDocsDirective', function () {
expect(true).toBe(true);
});
Correct
describe('helpDocsDirective', function () {
it("should return true", function () {
expect(true).toBe(true);
});
});
This is a very real problem, I am currently experiencing it too. I think there is a core bug here. I have very well encapsulated tests. they are small (at most 3 lines each)
I have a main describe section with 2 nested describes
first describe has 8 it() functions
second has 3it() functions.
i.e
describe("main", ()=>{
describe("1st", ()=>{
//here are 8 it() definitions
})
describe("2nd", ()=>{
//here are 3 it() definitions
})
})
Now when I remove a single it() definition from either describe, the issue disappears. Alternatively, if I add a 3rd describe(), the issue disappears.
This is an issue in jasmine - either they are not reporting an error correctly, or there is something horribly wrong. Alternatively it may be karma trying to be smart by running multiple tests simultaneously.. Either way, this problem is real and it's got nothing to do with messy code.
Perhaps it has to do with the underlying unit being tested - my function is recursive (though my test cases don't dive deep).
karma-jasmine#0.3.8
jasmine-core#2.4.1
karma#0.13.22
phantomjs-prebuilt#2.1.7
karma-phantomjs-launcher#1.0.0
Had the same issue here, it turn out that I had a test with setTimeout. Cleared that and all good!
You should try adding a done inside your catch() or try() just after your expect() call;
like this
it('should return a Promise that resolves', (done) => {
let promise = promiseFunction();
promise.then(response => {
expect(true).toBe(true);
done();
});
});
Also had this error message in Jasmine 3.5 - it threw me more than it should have, as it was talking about async and I have some jquery in project from someone else.
It was just a syntax issue in setting up the test ... my original
it("should ...")
expect(thing).toBe(whatever);
})
Versus the working ...
it("should ...", function(){
expect(thing).toBe(whatever);
})
Error: Wait timed out after 3005ms
I used window.scrollTo(0,200) for scrolling the web page down. The code is as follows:
browser.wait(function() {
browser.executeScript('window.scrollTo(0,200);').then(function () {
})
}, 3000);
REQUIREMENT: NFAR after scroll down. It should scroll up after waiting for 3000ms.
To scroll up I wrote the code as follows
browser.wait(function() {
browser.executeScript('window.scrollTo(0,-200);').then(function () {
})
}, 10000);
Is multiple browser.wait causing errors? Please suggest some best way to do the same.
browser.wait does not 'wait' after performing some action. It is a function to keep retrying the function inside until it returns true OR reaches the specified timeout (if it reaches the timeout it will throw an error).
Since you are not returning anything inside your function, it will always return 'undefined' and thus never be true, and will throw the error that it timed out in 3000 ms.
The code you probably want uses browser.sleep
browser.executeScript('window.scrollTo(0,200);').then(function() {
browser.sleep(3000);
}).then(function() {
browser.executeScript('window.scrollTo(0,0);');
});
Finally I found my solution!!
The following code I found helpful to remove the error.
var elm = element.all(by.css('.your-css-class')).get(9);
browser.executeScript("arguments[0].scrollIntoView();", elm.getWebElement());
elm.click();
Basically this allows you scroll into your view..
There is an open issue on protractor relating to this.
Perhaps this will help you:
browser.executeScript('window.scrollTo(0,200);').then(function () {
browser.executeScript('window.scrollTo(0,0);').then(function () {
// ...
})
})
Before that day I always made isolated small tests. But now I want run them in one scenario. And I have the strange error. Some tests can't work together.
For example.
First one:
beforeEach(function(){
browser.get('ng-components/examples/ps-grid-column-filter-range.html');
});
it('балун содержит текст', function () {
filter_field.click();
browser.actions().click(filter_field).perform();
browser.wait(function () {
return balloon_info.isPresent();
},5000).then(function () {
expect(balloon_text.getText()).toContain(balloon_contain_text);
expect(balloon_text.isDisplayed()).toBe(true);
}).thenCatch(function () {
expect(true).toBe(false);
});
console.log("ps-grid-column-filter-range_spec_1.1.с");
});
Second one:
beforeEach(function(){
browser.get('ng-components/examples/ps-grid-column-filter-range.html');
});
it('балун демонстрируется', function () {
filter_field.click();
browser.actions().click(filter_field).perform();
browser.wait(function () {
return balloon_info.isPresent();
},5000).then(function () {
expect(balloon_info.isDisplayed()).toBe(true);
}
,function (error) {
expect(false).toBe(true);
});
console.log("ps-grid-column-filter-range_spec_1.1.a");
});
When my tests isolated they working fine. But in group - they failing. What is my mistake? It is a problem with asynchronous?
Also, what is interesting is that some broken test hasn't method wait() in it.
browser.wait is non blocking so your second test probably runs while your first is still in progress. And since it is the same functionality they probably influence each others outcome.
Try putting them both in the same test and chain them with
.then()
You can make protractor blocking wait by following this example
Blocking wait
I'm trying to test the login page on my site using protractor.
If you log in incorrectly, the site displays a "toast" message that pops up for 5 seconds, then disappears (using $timeout).
I'm using the following test:
describe('[login]', ()->
it('should show a toast with an error if the password is wrong', ()->
username = element(select.model("user.username"))
password = element(select.model("user.password"))
loginButton = $('button[type=\'submit\']')
toast = $('.toaster')
# Verify that the toast isn't visible yet
expect(toast.isDisplayed()).toBe(false)
username.sendKeys("admin")
password.sendKeys("wrongpassword")
loginButton.click().then(()->
# Verify that toast appears and contains an error
toastMessage = $('.toast-message')
expect(toast.isDisplayed()).toBe(true)
expect(toastMessage.getText()).toBe("Invalid password")
)
)
)
The relevant markup (jade) is below:
.toaster(ng-show="messages.length")
.toast-message(ng-repeat="message in messages") {{message.body}}
The problem is the toastMessage test is failing (it can't find the element). It seems to be waiting for the toast to disappear and then running the test.
I've also tried putting the toastMessage test outside the then() callback (I think this is pretty much redundant anyway), but I get the exact same behaviour.
My best guess is that protractor sees that there's a $timeout running, and waits for it to finish before running the next test (ref protractor control flow). How would I get around this and make sure the test runs during the timeout?
Update:
Following the suggestion below, I used browser.wait() to wait for the toast to be visible, then tried to run the test when the promise resolved. It didn't work.
console.log "clicking button"
loginButton.click()
browser.wait((()-> toast.isDisplayed()),20000, "never visible").then(()->
console.log "looking for message"
toastMessage = $('.toaster')
expect(toastMessage.getText()).toBe("Invalid password")
)
The console.log statements let me see what's going on. This is the series of events, the [] are what I see happening in the browser.
clicking button
[toast appears]
[5 sec pass]
[toast disappears]
looking for message
[test fails]
For added clarity on what is going on with the toaster: I have a service which essentially holds an array of messages. The toast directive is always on the page (template is the jade above), and watches the messages in the toast service. If there is a new message, it runs the following code:
scope.messages.push(newMessage)
# set a timeout to remove it afterwards.
$timeout(
()->
scope.messages.splice(0,1)
,
5000
)
This pushes the message into the messages array on the scope for 5 seconds, which is what makes the toast appear (via ng-show="messages.length").
Why is protractor waiting for the toast's $timeout to expire before moving on to the tests?
I hacked around this using the below code block. I had a notification bar from a 3rd party node package (ng-notifications-bar) that used $timeout instead of $interval, but needed to expect that the error text was a certain value. I put used a short sleep() to allow the notification bar animation to appear, switched ignoreSynchronization to true so Protractor wouldn't wait for the $timeout to end, set my expect(), and switched the ignoreSynchronization back to false so Protractor can continue the test within regular AngularJS cadence. I know the sleeps aren't ideal, but they are very short.
browser.sleep(500);
browser.ignoreSynchronization = true;
expect(page.notification.getText()).toContain('The card was declined.');
browser.sleep(500);
browser.ignoreSynchronization = false;
It turns out that this is known behaviour for protractor. I think it should be a bug, but at the moment the issue is closed.
The workaround is to use $interval instead of $timeout, setting the third argument to 1 so it only gets called once.
you should wait for your toast displayed then do other steps
browser.wait(function() {
return $('.toaster').isDisplayed();
}, 20000);
In case anyone is still interested, this code works for me with no hacks to $timeout or $interval or Toast. The idea is to use the promises of click() and wait() to turn on and off synchronization. Click whatever to get to the page with the toast message, and immediately turn off sync, wait for the toast message, then dismiss it and then turn back on sync (INSIDE the promise).
element(by.id('createFoo')).click().then(function () {
browser.wait(EC.stalenessOf(element(by.id('createFoo'))), TIMEOUT);
browser.ignoreSynchronization = true;
browser.wait(EC.visibilityOf(element(by.id('toastClose'))), TIMEOUT).then(function () {
element(by.id('toastClose')).click();
browser.ignoreSynchronization = false;
})
});
I hope this can help who has some trouble with protractor, jasmine, angular and ngToast.
I create a CommonPage to handle Toast in every pages without duplicate code.
For example:
var CommonPage = require('./pages/common-page');
var commonPage = new CommonPage();
decribe('Test toast', function(){
it('should add new product', function () {
browser.setLocation("/products/new").then(function () {
element(by.model("product.name")).sendKeys("Some name");
var btnSave = element(by.css("div.head a.btn-save"));
browser.wait(EC.elementToBeClickable(btnSave, 5000));
btnSave.click().then(function () {
// this function use a callback to notify
// me when Toast appears
commonPage.successAlert(function (toast) {
expect(toast.isDisplayed()).toBe(true);
});
});
});
})
});
And this is my CommonPage:
var _toastAlert = function (type, cb) {
var toast = null;
switch (type) {
case "success":
toast = $('ul.ng-toast__list div.alert-success');
break;
case "danger":
toast = $('ul.ng-toast__list div.alert-danger');
break;
}
if (!toast) {
throw new Error("Unable to determine the correct toast's type");
}
browser.ignoreSynchronization = true;
browser.sleep(500);
browser.wait(EC.presenceOf(toast), 10000).then(function () {
cb(toast);
toast.click();
browser.ignoreSynchronization = false;
})
}
var CommonPage = function () {
this.successAlert = function (cb) {
_toastAlert("success", cb);
};
this.dangerAlert = function(cb) {
_toastAlert("danger", cb);
}
}
module.exports = CommonPage;
Chris-Traynor's answer worked for me but i've got an update.
ignoreSynchronization is now deprecated.
For those using angular and protractor to test this, the below works nicely for me.
$(locators.button).click();
await browser.waitForAngularEnabled(false);
const isDisplayed = await $(locators.notification).isPresent();
await browser.waitForAngularEnabled(true);
expect(isDisplayed).toEqual(true);
I've simplified this to make it easier to see, I would normally place this inside a method to make the locators dynamic.