I'm testing my Angular controller using mock data pulled in from a JSON file. The problem I'm running into is that my controller expects a promise, whereas my mock data is simply a JSON object.
Relevant controller code:
var vm = this;
var playerId = $routeParams.playerId;
playersService.getInfo({
playerId: playerId
}).$promise.then(function(info) {
vm.info = info;
});
Relevant test code:
$httpBackend = $injector.get('$httpBackend');
jasmine.getJSONFixtures().fixturesPath = 'base/test/fixtures';
$httpBackend.whenGET('/api/players/5/info').respond(
getJSONFixture('info.json')
);
I'm trying to work out how to setup my test to work with the controller's promise structure.
Related
I have been trying to inject $log in to a component created by a require statement for some client Angular.
var App = require('./app/containers/App');
var Header = require('./app/components/Header');
require('angular-ui-router');
var routesConfig = require('./routes');
import './index.css';
angular
.module('app', ['ui.router'])
.config(routesConfig)
.controller(App.App, ['$log'])
.service('todoService', todos.TodoService)
.component('app', App)
.component('headerComponent', Header);
The code for header is
module.exports = {
template: require('./Header.html'),
controller: Header,
bindings: {
todos: '='
}
};
/** #ngInject */
function Header(todoService) {
this.todoService = todoService;
}
Header.prototype = {
handleSave: function (text) {
if (text.length !== 0) {
this.todos = this.todoService.addTodo(text, this.todos);
}
}
};
~
The code for App is
module.exports = {
template: require('./App.html'),
controller: App
};
function App($log) {
this.log = $log;
$log.error('Hello from App');
}
I can inject $log as dependency for App as I have access to the controller. But attempting the same task for Header is difficult,because Header is created by require which does not seem to allow access to the controller function.
What I like to know is there a way round this?
I am trying to find a way of logging information from any possible javascript function in header.js.
I have seen any alternatives other than using $log to log information in a client side application
My solution so far has been to say in code written in the require block.
var ing = angular.injector(['ng']);
this.$log = ing.get('$log');
this.$log.error('I am a message');
I think this is the wrong way of doing things, it gives me what I want, but I expect it will break at some point. I find having access to $log is useful for debugging only. Its not sort of thing I need for any production code.
All I was trying to do was to get access to the $log angular wrapper. Turns out all I had to do was add $log to the argument list.
function Header(todoService,$log) {
$log.log('I am a log message');
this.todoService = todoService;
}
I am bit of a Angular 1.5 newbie and I had assume that you had to inject the $log to get the right response. Just declare it seems to be a bit of a kop out.
I am currently new to Sinon, Mocha, Supertest and in the process to writes tests. In my current scenario, i have authentication library which verifies my "OTP" and after verifying it proceeds to do operation within the callback function.
I am unable to mock the callback to return null and carry on to test rest of the code. Following is my code snippet:
Controller.js
var authy = require('authy')(sails.config.authy.token);
authy.verify(req.param('aid'), req.param('oid'), function(err, response) {
console.log(err);
if (err) {
return res.badRequest('verification failed.');
}
....
My test is :
var authy = require('authy')('token');
describe('Controller', function() {
before(function() {
var authyStub = sinon.stub(authy, 'verify');
authyStub.callsArgWith(2, null, true);
});
it('creates a test user', function(done) {
// This function will create a user again and again.
this.timeout(15000);
api.post('my_endpoint')
.send({
aid: 1,
oid: 1
})
.expect(201, done);
});
});
I essentially want to call authy verify get a null as "err" in callback, so i can test the rest of the code.
Any help would be highly appreciated.
Thanks
The trouble is that you're using different instances of the authy object in your tests and your code. See here authy github repo.
In your code you do
var authy = require('authy')(sails.config.authy.token);
and in your test
var authy = require('authy')('token');
So your stub is generally fine, but it will never work like this because your code does not use your stub.
A way out is to allow for the authy instance in your controller to be injected from the outside. Something like this:
function Controller(authy) {
// instantiate authy if no argument passed
in your tests you can then do
describe('Controller', function() {
before(function() {
var authyStub = sinon.stub(authy, 'verify');
authyStub.callsArgWith(2, null, true);
// get a controller instance, however you do it
// but pass in your stub explicitly
ctrl = Controller(authyStub);
});
});
i've been searching around for the last days but i cannot get closer to the solution. I try to mock the http response requested by the angular controller.
angular controller:
myController = function ($http, appConfig) {
$http.post(appConfig.restPath+"/administration/imports", {
}).then(function(data){
$scope.pagination = data;
$scope.totalItems = data.data.content.length;
$scope.totalPages = data.data.totalPages;
$scope.pages = [];
$scope.imports = data.data;
for (var i = 0; i < $scope.totalPages; i++){
$scope.pages.push(i);
}
});
}
and the test:
describe('Controller: myController', function() {
// load the controller's module
beforeEach(module("myModule"));
var controller,
scope, httpBackend, myPost;
// Initialize the controller and a mock scope
beforeEach(inject(function ($injector) {
httpBackend = $injector.get('$httpBackend');
myPost = httpBackend.whenPOST('http://localhost:9000/api/v1/administration/imports').respond({data: {content: ["a", "b"], totalPages: "1"}}, "");
scope = $injector.get('$rootScope');
var $controller = $injector.get('$controller');
createController = function() {
return $controller(myController, {'$scope' : scope });
};
}));
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should browse the list of imported files', function() {
httpBackend.expectPOST('http://localhost:9000/api/v1/administration/imports');
var controller = createController();
httpBackend.flush();
});
});
But it seems that he wants to ask the server for the real data when i inspect the test in the chrome console (network traffic -> HTTP requests shows me, that he is requesting the server instead of loading the mocked data...), but he receives 403 (forbidden).
the error i receive by karma is the following:
TypeError: Cannot read property 'length' of undefined at myController.js:35:40
line 35 is:
$scope.totalItems = data.data.content.length;
that makes me think that he tries to load the data from the REST service, receives 403, empty result (means data == {}) and then he tries to access on data.data.content.length which is undefined....
as you can see i did it exactly like google it recommends...
https://docs.angularjs.org/api/ngMock/service/$httpBackend
Other examples on SO or anywhere else look quite similar. What am i doing wrong?
yes you have to provide the real data or at least you can provide a prototype pf your data because you are testing that unit and it requires length.
and remove this part because you are mocking it twice
httpBackend.expectPOST('http://localhost:9000/api/v1/administration/imports');
use
myPost = httpBackend.expectPOST('http://localhost:9000/api/v1/administration/imports').respond({data: {content: ["a", "b"], totalPages: "1"}}, "");
this way you make sure that post is being called but if you still get same error then you have to check respond data.
In my app, for one basic model i get data so:
var baseAccounts = Restangular.all('accounts');
baseAccounts.getList().then(function(accounts) {
$scope.allAccounts = accounts;
});
var newAccount = {name: "Gonto's account"};
and then i send post request:
baseAccounts.post(newAccount);
But in my app also i have such route:
/accounts/batchimport
how can i send my newAccount object to model-url: accounts/batchimport?
is it possible?
and how?
Restangular has a set of custom methods.
which could be used to make a custom requests, like:
var baseAccounts = Restangular.all('accounts');
baseAccounts.getList().then(function(accounts) {
$scope.allAccounts = accounts;
});
var newAccount = {name: "Gonto's account"};
baseAccounts.customPost(newAccount, "batchimport"); // it will call the accounts/batchimport
Hope it will help.
The angular project I am working on is adding a configuration file to it. The configuration file is loaded as a JSON, it contains strings that will be replacing the static strings that are currently used in the current version of the project. There is multiple modules where the JSON's data needs to be used, what would be the best way to make the JSON file global throughout the project? I was thinking about loading it separately in each module using a HTTP GET, but I need the JSON to be loaded before everything else.
Thanks.
You probably can use a service. define a object on the scope and on that define the JSON. create a function which returns the JSON and inject it wherever the need arises. Implementation maybe like:
app.service("commonService", ["$log", function($log){
this.myConfiguration = {
id:"0",
name:"abc"
};
this.mystatic = function(){
return myConfiguration;
}
}
Now you can inject it and use in a controller as:
app.controller("mycontroller", function($scope, commonService){
$scope.static = commonService.myStatic();
//other code here
});
you can use value:
// create the module as usual
angular.module("myapp",[/*...*/]);
// on any other app point, set your json:
angular.module("myapp").value("myjson",{/*...*/});
If you don't need is before you do any routing you could use a service. This is what a config service could look like:
(untested)
app.service('ConfigService', function ($q, $http) {
// loads the config file
this.loadConfig: function() {
var deferred = $q.defer();
if(angular.isDefined(this.config) && this.config.length){
deferred.resolve(this.config);
}
$http.get('config.json')
.success(function(response) {
this.config = response;
deferred.resolve(this.config);
})
.error(function(){
deferred.reject('Could not load "config.json" file');
});
return deferred.promise;
};
// returns a config setting
this.get: function(key){
// if the config is not loaded yet, load it and call self
if(!angular.isDefined(this.config) || !this.config.length){
this.loadConfig().then(function(){
return this.get(key);
})
}
// config is loaded. Return requested key
if(angular.isDefined(config[key])){
return config[key];
}else{
console.error( 'Could not load config value: ' + key );
return false;
}
};
});