I am working on a code that has some unit test failing which I am trying to run. The unit test is
it('opens a confirmation dialog when deleting a layer and calls the delete wizard', function () {
var numLayers = scope.service.layers.length;
scope.deleteLayer({
serviceName: 'service1',
layerName: 'testLayer'
});
//Make sure confirmation dialog has been called
expect(bootbox.confirm).toHaveBeenCalled();
//the layer is NOT removed from the list
expect(scope.service.layers.length).toBe(numLayers);
});
I keep getting the error:
Unexpected request Get /api/configuration/utilities/userpreferences/f=json
I am trying to create a spyon for taking care of this api call. I am using this
spyOn(resourceFactory.configuration.utilities.userpreferences, 'get').and.callFake(function () { });
I have also defined this in the describe scope and injected in beforeeach
var resourceFactory = {
configuration: {
utilities: {
userpreferences: {
get: function () { }
}
}
}
};
The thing is I dont care about this call to API since I am not testing it.
When I use $httpBackend and do the following
/$httpBackend.whenGET("/api/configuration/utilities/userpreferences?f=json").respond({});
it works but I am not sure if this is the right way to do it. Especially since its not used anywhere else in the project.
Can you help with this.
I have a Typescript/Angular app that runs in an iframed window. In the app, I'm using the intl-tel-input module to format & validate our phone number inputs. Intl-tel-input puts 'intlTelInputUtils' functions on the $window object. When testing the app locally without the iframe, I was able to successfully call the methods like (note the surrounding window is just to satisfy the Typescript compiler:
(<any>window).intlTelInputUtils.getExtension(phoneNumber.userInput, phoneNumber.countryCode);
and get tests to pass by mocking the window.intlTelInputUtils object:
$window.intlTelInputUtils = {
'getExtension': function () { return ''; },
'formatNumber': function () { return ''; },
'isValidNumber': function () { return true; }
};
But of course this doesn't work once we're in the embedded iframe window. IntlTelInputUtils now appears at parent.window.intl....
I tried changing the method call to:
(<any>parent.window).intlTelInputUtils.getExtension(phoneNumber.userInput, phoneNumber.countryCode);
but I'm unable to push it to the server to see if that'll work (will it??) until I can get the Jasmine tests to pass. I tried changing the mock to:
$parent.$window = {
'intlTelInputUtils': {
'getExtension': function () {
return '';
},
'formatNumber': function () {
return '';
},
'isValidNumber': function () {
return true;
}
}
};
But I realize this isn't correct.
How do I correctly mock parent.window.intlTelInputUtils in my tests??
I'm not sure this will solve your problem but have you tried to add a spy to that specific call? For instance:
it('my test name', inject(function ($window) {
spyOn($window.intlTelInputUtils, 'getExtension');
...
}));
And ofc you need to ensure that you and use $window on your code and not window directly.
I'm trying to use Protractor's addMockModule to insert mock data in my end-2-end test.
My test is supposed to go to a web site, find a button by css-class and click it. The button click calls the function dostuff() in MyService, which fetches data from the backend.
My code so far:
describe('should display validation error', function () {
it('should work', function () {
browser.get('http://my.url');
browser.addMockModule('MyService', function () {
// Fake Service Implementation returning a promise
angular.module('MyService', [])
.value({
dostuff: function () {
return {
then: function (callback) {
var data = { "foo": "bar" };
return callback(data);
}
};
}
});
});
var button = element(by.css('.btn-primary'));
button.click();
browser.sleep(5000);
});
});
The test is accessing the web site and clicking the button. The problem is that real data from the database is displayed, not the mock data.
I followed several threads, like this one: How to mock angular.module('myModule', []).value() in Jasmine/Protractor
However, it seems like the function protractor.getInstance() is deprecated.
Anyone got this working?
Take a look at the unit test for addMockModule(). Try to add the addMockModule statement before you call browser.get()
https://github.com/angular/protractor/blob/673d416b7ef5abd0953da940cfa8cf2a59650df4/spec/basic/mockmodule_spec.js
Trying to clear localStorage at the start of each test, but it's not working with Internet Explorer 11 (where it works without issue in Chrome/Firefox).
Code we have tried:
browser.executeScript('window.localStorage.clear();');
browser.executeScript('localStorage.clear();');
And:
browser.driver.get('javascript:window.localStorage.clear();');
browser.driver.get('javascript:localStorage.clear();');
Versions:
IEDriverServer_2.45.0
selenium-server-standalone-2.45.0
Searched Google and cannot locate any solution that works.
Any ideas?
[Edit:] Note: further things that we have tried -- when we do something like this, localStorage is NOT cleared, however the alert DOES appear:
browser.executeScript('javascript:localStorage.clear(); alert("HelloWorld");');
So browser.executeScript seems to work.
After several hours of debugging, we're unsure why it started working all of a sudden, but looks like it may have needed the following line:
browser.get('/');
So altogether, the reset should look like:
browser.get('/');
browser.executeScript('localStorage.clear();');
Here is what we've been successfully using:
"use strict";
var LocalStorage = function () {
this.getValue = function (key) {
return browser.executeScript("return window.localStorage.getItem('" + key + "');");
};
this.get = function () {
browser.executeScript("return window.localStorage;");
};
this.clear = function () {
browser.executeScript("return window.localStorage.clear();");
};
};
module.exports = new LocalStorage();
Usage:
var localStorage = require("localStorage.js");
localStorage.get(); // this is basically used for testing if local storage is supported
localStorage.getValue("key_in_storage");
localStorage.clear();
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.