Protractor Tests Assign Window Objects - angularjs

I'm using CefSharp to run an AngularJS application. CefSharp has a method to RegisterJsObject, which assigns objects or properties onto the window before any of the client scripts execute.
I would love to be able to test this functionality with Protractor, but I'm not sure how I can access the window and assign objects before any of my tests run. Since my application depends on those objects being registered to the window I need a way to either hard-code this or mock it, without the objects registered, my application continues to throw undefined errors.
Is it even possible to access the window from Protractor? Something ideal would look something like:
describe('my test', function () {
beforeEach(function () {
window.foo = "bar";
});
it('should do stuff', function () {
browser.get('index.html');
// do stuff
});
});
EDIT: I have tried
describe('my test', function () {
it('should do stuff', function () {
browser.executeScript('return window.foo = { bar: {a: "b"}}').then(function () {
browser.get('#/');
browser.driver.manage().timeouts().implicitlyWait(10000);
});
});
But when the window actually loads I'm still seeing cannot read property 'bar' of undefined.
EDIT 2: Even tried executeAsyncScript with no luck.
describe('my test', function () {
it('should do stuff', function () {
browser.executeAsyncScript(function(cb) {
window.foo = { bar: { a: "b" } };
cb(true);
}).then(function (cb) {
browser.get('#/');
browser.driver.manage().timeouts().implicitlyWait(10000);
browser.wait(function () {
element(by.id('some-element')).isPresent();
});
expect(element(by.id('some-element')).isPresent()).toBe(true);
});
});
});

The problem is that the objects on window are being cleared when a new URL is loaded. You'll have to revert to using plain old webdriver for navigating to the URL:
describe('my test', function () {
it('should do stuff', function () {
browser.driver.get('youraddress');
browser.executeScript(function() {
window.foo = { bar: { a: "b" } };
});
browser.driver.manage().timeouts().implicitlyWait(10000);
browser.wait(function () {
element(by.id('some-element')).isPresent();
});
expect(element(by.id('some-element')).isPresent()).toBe(true);
});

This worked for me:
var originalDriverGet = browser.driver.get;
browser.driver.get = function() {
return originalDriverGet.apply(this, arguments).then(runScript);
};
function runScript() {
setTimeout(function() {
browser.executeScript("script to execute...");
});
}
Do this in protractor.conf.js in onPrepare block.

Related

$uibModal.open is not a function in karma (AngularJS Factory)

I am trying to test a function that opens a $uibmodal. This is my factory function.
confirmationMessage: function (message) {
var modalInstance = $uibModal.open({
templateUrl: 'views/templates/utilsTemplates/confirmationMessage.html',
backdrop: 'static',
controller: function () {
var messageCtrlVM = this;
// message to show
messageCtrlVM.message = message;
// when yes_button is pressed
messageCtrlVM.yesPress = function () {
modalInstance.close(true);
};
// when no_button is pressed
messageCtrlVM.noPress = function () {
modalInstance.close();
};
},
controllerAs: "messageCtrlVM"
});
return modalInstance.result;
},
In its unit testing file, I am first adding provider for it like so.
beforeEach(angular.mock.module('ui.bootstrap'));
beforeEach(function () {
module(function ($provide) {
$provide.value('$uibModal', function (value) {
return value;
});
});
});
After that I am injecting open, dismiss and close functions with beforeEach like so.
beforeEach(inject(function (_utilsFactory_, _$httpBackend_, _$filter_) {
utilsService = _utilsFactory_;
$httpBackend = _$httpBackend_;
filter = _$filter_;
uibModal = {
open: function () {},
dismiss: function () {},
close: function () {}
};
}));
Finally I am trying to run my unit test by calling factory function.
it('should show a confirmation message', function () {
var spy = spyOn(uibModal, "open").and.callFake(function () {
return {
result: {
then: function () {}
}
};
});
utilsService.confirmationMessage("Are you Sure?");
expect(spy).toHaveBeenCalled();
});
It gives me error that $uibModal.open is not a function.
Your beforeEach should be like this:
beforeEach(module('myApp', function ($provide) {
mockModal = {
result: {
then: function(confirmCallback, cancelCallback) {
this.confirmCallback = confirmCallback;
this.cancelCallback = cancelCallback;
return this;
}
},
opened: {
then: function (confirmCallback, cancelCallback) {
this.confirmCallback = confirmCallback;
this.cancelCallback = cancelCallback;
return this;
}
},
close: function() {
this.opened.confirmCallback(); // covers opened.then success
this.result.confirmCallback(); // covers result.then success
this.result.cancelCallback(); // covers result.then error
},
open: function (object1) {
return this;
}
};
$provide.value('$uibModal', mockModal);
}));
Note that, here, the object which we are providing as $uibModal has the open function. Having this passed in $provide, you would need to callThrough (not callFake after spying)
Feel free to remove result / opened / close if you are not using them here. They are useful when you have corresponding code.

Protractor override "beforeAll" http mock

I'm using Protractor in order to get some End-to-End testing in my application. In a test, I mock the backend-calls with a MockModule as following:
describe('Lets test a feature' , function(){
beforeAll(function () {
var customerE2E = function () {
angular.module('customerE2E', ['customer', 'ngMockE2E'])
.run(function ($httpBackend) {
$httpBackend.whenPOST('/api/data').respond(200);
$httpBackend.whenGET(/^.*/).passThrough();
});
};
browser.addMockModule('customerE2E', customerE2E);
});
it('correctly demonstrates my problem', function () {
expect(element(by.css('h4')).getText()).toMatch('Hello world');
}
})
This works really good but my problem is that I also want to test my application when the post responds with a 404, 500 etc. I have solved this by having a new "describe"-function for each case but it would be nice to just be able to override this call from inside the "it"-functions.
it('correctly demonstrates my problem', function () {
expect(element(by.css('h4')).getText()).toMatch('Hello world');
}
it('show error due to bad request', function () {
/*
Replace $httpBackend.whenPOST('/api/data').respond(200);
with $httpBackend.whenPOST('/api/data').respond(404);
*/
expect(element(by.css('h4')).getText()).toMatch('Failed api call');
}
I'm really new to this so I am wondering if there is a nice way to achieve an easy way to override the earlier MockModuled that was set in the beforeAll-function.
You can use beforeEach() to achieve it.
describe('Test Mock Module',function () {
var statusCodes = [200,404,500];
var currentTestNumber = 0;
beforeAll(function () {
var customerE2E = function () {
angular.module('customerE2E', ['customer', 'ngMockE2E'])
.run(function ($httpBackend) {
$httpBackend.whenPOST('/api/data').respond(200);
$httpBackend.whenGET(/^.*/).passThrough();
});
};
browser.addMockModule('customerE2E', customerE2E);
});
/*Below method will be executed before each test and set the required response respectively*/
beforeEach(function () {
$httpBackend.whenPOST('/api/data').respond(statusCodes[currentTestNumber++]);
});
it('test 200 status code',function () {
expect(element(by.css('h4')).getText()).toMatch('Message for 200 status code');
});
it('test 404 status code',function () {
expect(element(by.css('h4')).getText()).toMatch('Message for 404 status code');
});
it('test 500 status code',function () {
expect(element(by.css('h4')).getText()).toMatch('Message for 500 status code');
});
});

Trying to mock an angular provider. unsuccessfully

I've been trying to mock a provider (for use in another provider) and couldn't manage to get it mocked.
In the test, I see the actual provider function and not the mocked one.
Here is the (simplified) code:
First, the setup: (I removed anything that isn't needed):
function ServiceToMock() {
this.functionMock = function () {
// do something...
};
this.$get = function () {
// doesn't matter
};
}
module('theModule').provider('serviceToMock', ServiceToMock);
function ServiceThatUsesMock(serviceToMock) {
serviceToMock.functionMock(); // I want the mocked function to run here but the actual one does
this.$get = function () {
// doesn't matter...
};
}
module('theModule').provider('serviceThatUsesMock', ServiceThatUsesMock);
As you can see, two services defined with .provider(), the second is dependent on the first.
And the unit test:
let functionMock = jasmine.createSpy();
module('theModule');
module(function ($provide) {
$provide.provider('serviceToMock', function () {
this.functionMock = functionMock;
this.$get = () => ({});
this.test = 'aaa';
});
});
var serviceThatUsesMock;
beforeEach(inject(function (_serviceThatUsesMock_) {
serviceThatUsesMock = _serviceThatUsesMock_;
}));
it('should register for update failure', function () {
expect(functionMock).toHaveBeenCalled();
});
The test fails because the real function gets called and not the mocked one.
What Am I Doing wrong?
Thanks!

Unknown Provider when unit testing filter

I am unit testing my first filter, which seems easy enough but I keep getting
Unknown provider: activeProfileProvider <- activeProfile <- pbRoles <- rolesFilter
The activeProfile is a consent that is a dependency of pbRoles. My understanding of constants is limited and I cant seem to find a way to include it. I have tried adding the config file where the constants are declared as well as mocking the constant in the test to no avail.
does anyone know if there is a specific way to do this? or does my problem lay else where?
My filter:
angular.module('pb.roles.filters')
.filter('roles', ['pbRoles', function (pbRoles) {
return function (input) {
if (!input) {
return "None";
} else {
return pbRoles.roleDisplayName(input);
}
};
}]);
My test:
describe('RolesController', function () {
beforeEach(module('pb.roles'));
beforeEach(module('pb.roles.filters'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var rolesFilter;
var mockActiveProfile = {};
beforeEach(inject(function (_rolesFilter_) {
activeProfile = mockActiveProfile;
rolesFilter = _rolesFilter_;
}));
var pbRoles = {
roleDisplayName: function (input) {
return input
}
};
describe('role: filter', function () {
it('should return None if called with no input', function () {
expect(rolesFilter()).toBe('None');
});
it('should call roleDisplayName with input', function () {
expect(roles('Hello')).toBe('Hello');
});
});
});
have also tried mocking the constent like so:
module(function ($provide) {
$provide.constant('activeProfile', function () {
pbGlobal.activeProfile = {
profile: ""
}
});
});
mocking the providers at the top of the page like so worked:
beforeEach(module(function ($provide) {
$provide.constant('organizationService', function () {
});
$provide.service('activeProfile', function () {
activeProfile = {
profile: ""
}
});
}));
http://www.sitepoint.com/mocking-dependencies-angularjs-tests/ was a huge help.
My full working test if anyone is curious:
describe('RolesController', function () {
var mockPbRoles = {
roleDisplayName: function (input) {
return "it worked!"
}
};
beforeEach(module(function ($provide) {
$provide.value('pbRoles', mockPbRoles);
$provide.constant('organizationService', function () {});
$provide.service('activeProfile', function () { });
}));
beforeEach(module('pb.roles'));
beforeEach(module('pb.roles.filters'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var rolesFilter;
beforeEach(inject(function (_rolesFilter_) {
rolesFilter = _rolesFilter_;
}));
describe('role: filter', function () {
it('should return None if called with no input', function () {
expect(rolesFilter(false)).toBe('None');
});
it('should call roleDisplayName with input', function () {
result = rolesFilter(true);
expect(result).toEqual("it worked!");
});
});
});

How to mock an object within an AngularJS factory method

I have created an Angular factory that has methods which handle saving code to a server. One of the factory methods contains a third party object which has a method which does the actual callout. I would like to test this code, but I can't work out how to mock out the third party object.
I have set up a plunker with a Jasmine test.
My aim for this test is just to successfully get the code to use my mock object rather than the ThirdPartySavingUtils object. Is that possible?
var app = angular.module("MyApp", []);
app.factory("SavingUtils", function() {
return {
saveStuff: function() {
if(typeof ThirdPartySavingUtils !== "undefined") {
return ThirdPartySavingUtils.value;
}
}
};
});
this is my jasmine tests
describe("Mocking Test", function() {
var ThirdPartySavingUtilsMock;
var SavingUtils;
beforeEach(function() {
angular.mock.module("MyApp", function($provide) {
ThirdPartySavingUtilsMock = {
value: "I am the mock object"
};
$provide.value("ThirdPartySavingUtils", ThirdPartySavingUtilsMock);
});
inject(function(_SavingUtils_) {
SavingUtils = _SavingUtils_;
});
});
it("should run without throwing an exception", function() {
expect(true).toBe(true);
});
it("should mock out ThirdPartySavingUtils with ThirdPartySavingUtilsMock", function() {
var result = SavingUtils.saveStuff();
expect(result).toEqual("I am the mock object");
});
});
You have a few options really but more than likely you would need to do both.
1) You could create an angular service which wraps this third party object - this way you get a nice abstraction incase you ever need to change the third party object.
2) You could use a mocking framework like http://sinonjs.org/ which enable you to mock methods out and do asserts like calledOnce etc.
Here is a link to a mocked test using sinon test.
You can bascially see sinon is used as a sandbox to mock out an object methods. Sinon provides extra propeties to those mocked methods so you can assert if they were called, the parameters they were called with even the order of the calls. It is a really, really essential testing tool.
describe('validationManager', function () {
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$q = $injector.get('$q');
defer = $q.defer();
validator = $injector.get('validator');
validationManager = $injector.get('validationManager');
sandbox.stub(validator, 'makeValid');
sandbox.stub(validator, 'makeInvalid');
sandbox.stub(validator, 'getErrorMessage').returns(defer.promise);
setModelCtrl();
}));
afterEach(function () {
sandbox.restore();
setModelCtrl();
});
it('should be defined', function () {
expect(validationManager).to.exist;
});
describe('validateElement', function () {
it('should return if no $parsers or $formatters on the controller', function () {
validationManager.validateElement(modelCtrl);
expect(validator.makeValid.called).to.equal(false);
expect(validator.makeInvalid.called).to.equal(false);
});
});
EDIT -----------------------
Here this put into practice for your code (I haven't run this but it give the general idea).
(function (angular, ThirdPartyApi) {
'use strict';
var app = angular.module('MyApp', []);
app.factory('thirdPartApi', [
function () {
return {
save: ThirdPartyApi.save,
value: ThirdPartyApi.value
};
}
]);
app.factory('SavingUtils', [
'thirdPartApi',
function (thirdPartApi) {
var getValue = function () {
return thirdPartApi.value;
},
save = function (item) {
return thirdPartApi.save(item);
};
return {
save: save,
getValue: getValue
};
}
]);
}(angular, window.ThirdPartyApi));
The tests.....
(function (angular, sinon) {
'use strict';
describe('MyApp.SavingUtils', function () {
var sandbox, thirdPartyApi, SavingUtils, thirdPartyApiValue = 2;
beforeEach(inject(function ($injector) {
sandbox = sinon.sandbox.create();
thirdPartyApi = $injector.get('thirdPartyApi');
SavingUtils = $injector.get('SavingUtils');
// stub the method and when called return a simple object or whatever you want
sandbox.stub(thirdPartyApi, 'save').returns({ id: 1});
sandbox.stub(thirdPartyApi, 'value', function () {
return thirdPartyApiValue;
});
}));
afterEach(function () {
// This removes those stubs and replace the original methods/values
sandbox.restore();
});
describe('save', function () {
it('should return call the save method on thirdPartyApi', function () {
var item = {};
SavingUtils.save(item);
expect(thirdPartyApi.save.calledOnce).to.equal(true);
});
});
describe('getValue', function () {
it('should return value of value property on thirdPartyApi', function () {
var result = SavingUtils.getValue();
expect(result).to.equal(thirdPartyApiValue);
});
});
});
}(angular, sinon));

Resources