Testing Angular-Resource: Expected an Object, got a Resource - angularjs

When trying to test some simple angular code using $resource, I end up with a Resource object which contains a $promise key and hence a failure of the form: Failure/Error: Expected Resource(...) to equal Object(...)
I was expecting to get back the object I passed to the respond method as part of httpBackend.expectGET('/books/5.json').respond(my_book). Am I using $resource wrong or is something wrong in my test?
Code
var bookApp = angular.module('bookApp',
[
'ngResource',
]
);
function BookController(scope, $resource) {
var ctrl = this;
var Book = $resource('/books/:selected.json', {selected:'#id'});
ctrl.fetch_book = function(id){
console.log('Selecting options ' + id);
Book.get({selected:id}, function(data){
console.log('Received: ' + JSON.stringify(data));
ctrl.current_book = data;
});
};
}
BookController.$inject = ['$scope', '$resource'];
bookApp.component('book', {
controller: BookController
});
Test
describe('component: tree', function() {
var component_controller, $componentController, httpBackend, my_book;
beforeEach(module('bookApp'));
beforeEach(inject(function($httpBackend, _$componentController_) {
httpBackend = $httpBackend;
$componentController = _$componentController_;
}));
describe('$ctrl.fetch_book(book_id)', function(){
beforeEach(function() {
component_controller = $componentController('book');
my_book = {title: 'Sanctuary', id: '5'};
});
it('fetches the book with id=book_id', function(){
httpBackend.expectGET('/books/5.json').respond(my_book);
component_controller.fetch_book(5);
httpBackend.flush();
console.log('Options: ' + JSON.stringify(component_controller.current_book));
console.log('constructor: ' + JSON.stringify(component_controller.current_book.constructor.name));
expect(component_controller.current_book).toEqual(my_book);
});
});
});
Result
$ bundle exec teaspoon -f documentation
component: tree
$ctrl.fetch_book(book_id)
fetches the book with id=book_id (FAILED - 1)
# Selecting options 5
# Received: {"title":"Sanctuary","id":"5"}
# Options: {"title":"Sanctuary","id":"5"}
# constructor: "Resource"
Failures:
1) component: tree $ctrl.fetch_book(book_id) fetches the book with id=book_id
Failure/Error: Expected Resource({ title: 'Sanctuary', id: '5',
$promise: Promise({ $$state: Object({ status: 1, value:
<circular reference: Object> }) }), $resolved: true }) to equal
Object({ title: 'Sanctuary', id: '5' }).
Finished in 0.02600 seconds
1 example, 1 failure

Try this in your tester:
expect(component_controller.current_book).toEqual(angular.toJSON(my_book));
It'll strip the object's properties and you'll have a match.
You can also try angular.equals but I haven't tested that.

Try adding the following to your spec file and see if it works. I saw it in the PhoneCat example and it worked for me.
beforeEach(function() {
jasmine.addCustomEqualityTester(angular.equals);
});

You can try doing something like this:
expect(component_controller.current_book.toJSON()).toEqual(my_book);
I had the same issue where I got an error of
Expected object to be a kind of Object, but was Resource(
This is what I had before:
expect(self.project).toEqual(mockProject);
And after I added .toJSON() it was all good:
expect(self.project.toJSON()).toEqual(mockProject);
Hope this helps!

Related

Can’t get ‘comment’ value from camForm

I want to get ‘refusalComment’ from my form (when i write some value into it ) but i can’t make this operation every time i deploy project i got this error:
ReferenceError: variableValue is not defined
at Array.eval (eval at (camunda-tasklist-ui.js?bust=7.8.0:5), :27:84)
here is my code example:
<form role=“form”>
var selectedDocuments=$scope.selectedDocuments=[];
var variableManager=camForm.variableManager;
var json=$scope.json={
id:1,
cardNumber:“12345678”,
organizationNameGE:“ptp”,
organizationNameEN:“psp”
};
$scope.selectedDocuments.push($scope.json);
camForm.on('form-loaded', function() {
// declare a 'json' variable 'customer'
camForm.variableManager.createVariable({
name: 'selectedDocuments',
type: 'String',
value: $scope.selectedDocuments
});
});
var comments = $scope.comments=[];
comments.length =$scope.selectedDocuments.length ;
camForm.on('variables-fetched', function() {
// value has been fetched, bind to $scope.user
$scope.refusalComment = $( '#refusalComment', camForm.formElement).textContent(variableValue);
console.log( $scope.refusalComment+"see comment");
$scope.selectedDocuments=EnrichValue(variableManager.variable('selectedDocuments').value,$scope.refusalComment);
});
camForm.on('submit', function(evt) {
// set value in variable manager so that it can be sent to backend
variableManager.variableValue('selectedDocuments', $scope.selectedDocuments);
});
function EnrichValue(data,comment){
for(var i=0;i<Object.keys(json).length;i++){
json[i].comment=comment;
comments[i]=comment;
}
return json;
}
what should i change to make to get rid of this error?

Issue with backbonejs model objects json representation

I have a question related to contained backbonejs model objects.
Using yeoman backbone generator described at https://github.com/yeoman/generator-backbone, I created a backbone application to test how a model object that contains another model object is being sent as json to the backend server.
I added two models contact and address to this test application.
yo backbone:model contact
yo backbone:model address
Contact object contains one address object. Contact and address backbone model objects are shown below. The init function in the generated main.js is also shown below. The console log is shown below. Ignore the POST error since there is no endpoint contacts.
My question is: in the Chrome browser Developer Tools window, network tab the request payload is:
{"name":"John Doe"}
What changes are needed to the backbone model objects so that the request payload will have the contained address object also? Thanks. I want the payload to look like this:
{
"name": "John Doe",
"address": {
"addressLine1": "Somewhere"
}
}
From console in the Developer Tools window, I can confirm addressLine1 is 'Somewhere' in the contained address object :
Inside Contact.initialize
address.js:14 Inside Address.initialize
contact.js:24 Inside Contact.getAddress
main.js:12 Hello from Backbone! name = John Doe addressLine1 = Somewhere
contact.js:21 Inside Contact.validate
main.js:22 return value from save [object Object]
jquery.js:8630 POST http://localhost:9001/contacts 404 (Not Found)
...
main.js:19 Error [object Object]
From main.js
init: function () {
'use strict';
var myContact = new Test.Models.Contact();
console.log('Hello from Backbone! name = ' + myContact.get('name') + ' addressLine1 = ' + myContact.getAddress().get('addressLine1'));
var rv = myContact.save(null,{
success: function(response) {
console.log('Success ' + response);
},
error: function(response) {
console.log('Error ' + response);
}
});
console.log ('return value from save ' + rv);
}
};
From contact.js
/*global Test, Backbone*/
Test.Models = Test.Models || {};
(function () {
'use strict';
Test.Models.Contact = Backbone.Model.extend({
url: '/contacts',
initialize: function() {
console.log ('Inside Contact.initialize');
this.address=new Test.Models.Address();
},
defaults: {
name: 'John Doe'
},
validate: function(attrs, options) {
console.log ('Inside Contact.validate');
},
getAddress: function() {
console.log ('Inside Contact.getAddress');
return this.address;
},
parse: function(response, options) {
console.log ('Inside Contact.parse');
return response;
}
});
})();
From address.js
/*global Test, Backbone*/
Test = {}; // a global object
Test.Models = Test.Models || {};
(function () {
'use strict';
Test.Models.Address = Backbone.Model.extend({
url: '',
initialize: function() {
console.log ('Inside Address.initialize');
},
defaults: {
addressLine1: 'Somewhere'
},
validate: function(attrs, options) {
console.log ('Inside Address.validate');
},
parse: function(response, options) {
console.log ('Inside Address.parse');
return response;
}
});
})();
Call stack when I set a breakpoint at contact validate function:
validate (contact.js:21)
_validate (backbone.js:568)
save (backbone.js:465)
init (main.js:14)
(anonymous) (main.js:29)
fire (jquery.js:3099)
fireWith (jquery.js:3211)
ready (jquery.js:3417)
completed (jquery.js:3433)
When the above break point is triggered, I can verify this object has address information:
this
child {cid: "c1", attributes: {…}, _changing: false, _previousAttributes: {…}, changed: {…}, …}
address:child
attributes:{addressLine1: "Somewhere"}
changed:{}
cid:"c2"
_changing:false
_pending:false
_previousAttributes:{}
__proto__:Backbone.Model
attributes:{name: "John Doe"}
changed:{}
cid:"c1"
_changing:false
_pending:false
_previousAttributes:{}
__proto__:Backbone.Model
Thanks a lot.
I saw Nesting collection or models within another model
I added the following line of code just before I save the model in main.js:
myContact.set('address', myContact.getAddress().toJSON());
then I can verify the request payload is what I was expecting:
{"name":"John Doe","address":{"addressLine1":"Somewhere"}}
Thanks to Emile Bergeron for pointing out toJSON() method must be called.
The take away for me is backbone is bare bones. It is not smart enough to figure out an object may contain other objects and do the needful. The javascript programmer must call toJSON() at appropriate time.

Unit testing promises in js-data-angular models

We use js-data and js-data-angular in our project.
I have the following model:
(function () {
'use strict';
angular.module('dash.models')
.factory('Diagnosis', ['DS', function (DS) {
function transform(resourcename, attrs, cb) {
attrs.icd9codes.forEach(function (el) {
delete el.add;
});
cb(null, attrs);
}
this.transform = transform;
return DS.defineResource({
name: 'diagnosis',
idAttribute: 'id',
endpoint: '/diagnosis',
baseUrl: '/api',
beforeCreate: transform,
beforeUpdate: transform
});
}]);
}());
And the following call to said model:
var startEditing = self.startEditing = function(parentScope, diagnosis) {
Diagnosis.findAll({
deep:true
}, {
endpoint: '/diagnosis/' + diagnosis.id
}).then(function(d) {
$scope.diagnosis = d;
$scope.inScope = true;
});
};
In my unit test, I mock the call like this:
var diagDeferred = _$q_.defer();
diagDeferred.resolve({
'name': 'Breast',
'categories': null,
'id': '026c7cd0-14ef-4312-a8f1-2092107b0e50',
'icd9codes': [{id: '1', code: '001', description: 'ICD9 Code'}]
});
spyOn(Diagnosis, 'findAll').and.returnValue(diagDeferred.promise);
And the actual call is mocked, what doesn't get executed (and I can't find any reliable information on how to get this done) is the function inside the .then of the Diagnosis.findAll
I know the code works, but I need to cover it with unit tests and I'm coming up dry.
Thanks.
I think you forgot to call $scope.digest() in your test. Here is a working fiddle.
After you call startEditing(), you should call $scope.$digest() so that your mock promise is executed and you can get your data in then block. Hope it helps.

Object #<Resource> has no method 'save'

I have a fairly simple AngularJS app that is consuming a Spring MVC Rest API.
I am able to query the API. But I am having trouble Creating objects. I get the following error and have not been able to figure out why. I have looked at similar questions but my API is returning the created object and from what I can tell my injections are correct.
Here is the stack from the javascript console:
TypeError: Object #<Resource> has no method 'save'
at Object.TeamListCtrl.$scope.addPlayer(http://localhost:8000/app/js/controllers.js:17:13)
at elementFns (http://localhost:8000/app/lib/angular/angular.js:6365:19)
at Object.$get.Scope.$eval (http://localhost:8000/app/lib/angular/angular.js:8057:28)
at Object.$get.Scope.$apply (http://localhost:8000/app/lib/angular/angular.js:8137:23)
at Object.ng.config.$provide.decorator.$delegate.__proto__.$apply (http://localhost:8000/app/index.html:855:30)
at HTMLFormElement.ngIncludeDirective.restrict (http://localhost:8000/app/lib/angular/angular.js:13159:11)
at event.preventDefault (http://localhost:8000/app/lib/angular/angular.js:1992:10)
at Array.forEach (native)
at forEach (http://localhost:8000/app/lib/angular/angular.js:130:11)
at HTMLFormElement.eventHandler (http://localhost:8000/app/lib/angular/angular.js:1991:5)
Here is my controller:
function TeamListCtrl($scope, Shared, Teams, Players) {
$scope.teams = Teams.query();
$scope.addPlayer = function() {
var newPlayer = new Players({
position: $scope.newPlayer.position,
lastName: $scope.newPlayer.lastName,
firstName: $scope.newPlayer.firstName
});
newPlayer.save({teamId: $scope.newPlayer.teamId});
};
}
TeamListCtrl.$inject = ['$scope', 'Shared', 'Teams', 'Players'];
Here is my service:
var teamsService = angular.module('teamsService', ['ngResource']);
teamsService.factory('Players', function($resource){
return $resource('http://localhost\\:8080/api/teams/players/:teamId', {teamId:'#teamId'}, {
save: {method:'POST'}
});
});
Thank you for any help you can provide.
Can you try to post the resource, like this
var newPlayer = {
teamId: $scope.newPlayer.teamId,
position: $scope.newPlayer.position,
lastName: $scope.newPlayer.lastName,
firstName: $scope.newPlayer.firstName
};
Players.save(newPlayer);

Missing injection for test (unkown provider)

I am trying to write unit tests for my Angular.js application but I cannot manage to inject what I need (it is not able to find a suitable provider).
Does anyone see what I missed?
Firefox 21.0 (Linux) filter staticList should convert static list object into its display value FAILED
Error: Unknown provider: staticListProvider <- staticList in /path/to/my-app/public/third-party/angular/angular.js (line 2734)
createInjector/providerInjector<#/path/to/my-app/public/third-party/angular/angular.js:2734
getService#/path/to/my-app/public/third-party/angular/angular.js:2862
createInjector/instanceCache.$injector<#/path/to/my-app/public/third-party/angular/angular.js:2739
getService#/path/to/my-app/public/third-party/angular/angular.js:2862
invoke#/path/to/my-app/public/third-party/angular/angular.js:2880
workFn#/path/to/my-app/test/lib/angular/angular-mocks.js:1778
angular.mock.inject#/path/to/my-app/test/lib/angular/angular-mocks.js:1764
#/path/to/my-app/test/unit/filtersSpec.js:19
#/path/to/my-app/test/unit/filtersSpec.js:16
#/path/to/my-app/test/unit/filtersSpec.js:3
The application:
angular.module('myApp', ['myAppFilters', 'ui.bootstrap', '$strap.directives']).
// Some other stuff
The filters:
"use strict";
angular.module('myAppFilters', []).
filter('staticList', function () {
return function (listItem) {
if (!listItem) {
return '';
}
return listItem.value;
};
});
The test:
'use strict';
describe('filter', function () {
beforeEach(angular.module('myAppFilters'));
describe('staticList', function () {
it('should convert static list object into its display value',
inject(function (staticList) {
expect(undefined).toBe('');
expect({key: 'A', value: 'B'}).toBe('B');
}));
});
});
The Karma configuration:
basePath = '../';
files = [
JASMINE,
JASMINE_ADAPTER,
'public/third-party/jquery/*.js',
'public/third-party/angular/angular.js',
'public/third-party/angular/i18n/angular-*.js',
'public/third-party/moment/moment.min.js',
'public/third-party/moment/moment-*.js',
'public/js/**/*.js',
'test/lib/**/*.js',
'test/unit/**/*.js'
];
colors = true;
autoWatch = true;
browsers = ['Firefox'];
junitReporter = {
outputFile: 'test_out/unit.xml',
suite: 'unit'
};
If anybody wants to see the full code, the application repository is here: https://github.com/adericbourg/GestionCourrier
Thanks a lot,
Alban
In your inject code
it('should convert static list object into its display value',
inject(function (staticList) {
expect(undefined).toBe('');
expect({key: 'A', value: 'B'}).toBe('B');
}));
replace "inject(function (staticList)" with "inject(function (staticListFilter)". This is some random convention angular is following. You can check comments on this page for more info http://docs.angularjs.org/tutorial/step_09
I faced similar problem, but in my case my filter name contained suffix 'Filter' which caused the same injection problem.
.filter('assetLabelFilter', function(){
return function(assets, selectedLabels){
// Implementation here
};
});
I was finally able to solve the problem by manually injecting the filter to my test
'use strict';
describe('assetLabelFilter', function() {
beforeEach(module('filters.labels'));
var asset1 = {labels: ['A']};
var asset2 = {labels: ['B']};
var asset3 = {labels: []};
var asset4 = {labels: ['A', 'B']};
var assets = [asset1, asset2, asset3, asset4];
var assetLabelFilter;
beforeEach(inject(function($filter) {
assetLabelFilter = $filter('assetLabelFilter');
}));
it('should return only assets with selected label', function() {
var selectedLabels = ['B'];
expect(assetLabelFilter(assets, selectedLabels)).toEqual([asset2, asset4]);
});
});
The nice answer above made me realize that in order to use the angular tutorial way:
it('should ', inject(function(assetLabelFilter) {
var selectedLabels = ['B'];
expect(assetLabelFilter(assets, selectedLabels)).toEqual([asset2, asset4]);
}));
The filter name can't have the suffix 'Filter' because it is magic word as mentioned in the answer above.
To demonstrate the scenario I also created a Plunker

Resources