Simply Put:
Can I match mock-$httpBackend data from $resource service in a jasmine unit test? How would I do it?
What I Want:
When I Jasmine-test my backend services with $httpBackend and the $http service, I can do this:
it("should return movie search data from the title", function() {
var movieData = { title: "The Phantom Menace", description: "A movie.";
var response = [];
$httpBackend.when('GET', 'http://www.omdbapi.com/?v=1&s=the%20phantom%20menace')
.respond(200, movieData);
moviesService.search('the phantom menace')
.then(function onSuccess(data) {
response = data;
});
$httpBackend.flush();
expect(response.data).toEqual(movieData);
}); // end it
I have all of The Phantom Menace's information stored in movieData. This test passes because it compares the data that is received from the mock backend to what it should be (movieData).
What I Am Getting:
When I Jasmine-test my backend services with $httpBackend and the $resource service, things are different.
it("retrieves a bunch objects", function() {
var result = {}, testArray = [];
var dataToCheck = [
{ id: 1, "des": "This is one."},
{ id: 2, "des": "This is two."},
{ id: 3, "des": "This is three."}
];
$httpBackend.whenGET('movies/api')
.respond(dataToCheck);
MovieResource.query().$promise.then(function (data) {
result = data;
});
$httpBackend.flush();
angular.forEach(result, function (resource) {
testArray.push(resource);
})
expect(testArray).toEqual(dataToCheck);
}); // end it
Karma tells me:
Expected [ Resource({ id: 1, des: 'This is one.' }), Resource({ id: 2,
des: 'This is two.' }), Resource({ id: 3, des: 'This is three.' }) ]
to equal [ Object({ id: 1, des: 'This is one.' }), Object({ id: 2,
des: 'This is two.' }), Object({ id: 3, des: 'This is three.' }) ].
So, instead of "Objects" they are "Resources."
I appreciate ANY guidance you can give me.
You will need to do something like this
expect(testArray[0].id).toEqual(dataToCheck[0].id);
expect(testArray[1].id).toEqual(dataToCheck[1].id);
expect(testArray[0].des).toEqual(datatoCheck[0].des);
Reference:
http://accraze.info/testing-angular-services-using-jasmine/
Related
This is my api:
exports.getService = function(req, res) {
var limit = 10; // number of records per page
var offset = 0;
Service.findAndCountAll({
raw: true,
where: {
shop: req.user.shop
}
}).then((data) => {
var page = req.params.page; // page number
var pages = Math.ceil(data.count / limit);
offset = limit * (page - 1);
Service.findAll({
// raw: true,
limit: limit,
offset: offset,
$sort: {
id: 1
},
where: {
shop: req.user.shop
},
include: [{
model: Categoryservice,
attributes: ['id'],
include: [{
model: Category,
attributes: ['id', 'name'],
}]
}],
}).then(function (services) {
var services=JSON.parse(JSON. stringify(services));
console.log('=====stringify==========>>',services);
var arr = services.categoryservices.map(item => item.category.id)
services.cats = arr;
delete services.categoryservices;
console.log('only for the testing========>',services);
res.status(200).json({
'result': services,
'count': data.count,
'pages': pages
});
});
}).catch(function(error) {
res.status(500).send('Internal Server Error');
});
};
I am using map in last then fuction ,
It contains a error map undefined in the server..
I want want a out like below given json using the map fuction.
Actually i need this out put:
{
"id": 2,
"service": "mobile",
"min": "20",
"per": "10",
"tax": "1",
"cats": [
1,
2
]
}
my JSON. stringify(services) out put is:
=====stringify==========>> [ { id: 2,
username: null,
name: null,
image: null,
service: 'mobile',
shop: '$2a$10$NWpbmgtzQAxRZ1ugvdC7LOlorBU36xoGHm1L.k.KmFqDO/7oSmBLu',
min: '20',
per: '10',
tax: '1',
activity: null,
createdAt: '2018-03-14T07:30:57.000Z',
updatedAt: '2018-03-14T07:30:57.000Z',
categoryservices: [ [Object], [Object] ] },
{ id: 1,
username: 'sam',
name: 'New Service',
image: '/images/uploads/22-Feb-2018/f96334384cd78754454c5e4e05e20fc0-dragon_pattern_red_black_9666_1920x1080.jpg',
service: 'battery',
shop: '$2a$10$NWpbmgtzQAxRZ1ugvdC7LOlorBU36xoGHm1L.k.KmFqDO/7oSmBLu',
min: '5',
per: '1',
tax: '1',
activity: '2018-03-14T06:01:36.000Z',
createdAt: '2018-03-14T06:01:36.000Z',
updatedAt: '2018-03-14T06:01:36.000Z',
categoryservices: [] } ]
I was beginner of using map function,
so,I am confused in map ,
so please give any solution to this problem.
You are stringifying your array that comes back. You can't do that if you plan to use .map on it. Remove that code and try again.
.then(function (services) {
var arr = services.categoryservices.map(item => item.category.id)
services.cats = arr;
delete services.categoryservices;
console.log('only for the testing========>',services);
res.status(200).json({
'result': services,
'count': data.count,
'pages': pages
});
});
I think we are missing something because the output you pasted doesn't have the category.id attribute that you are returning from the item passed in to map. Is that what you are trying to target? That's off topic, but this code may not work for what you are trying to achieve but will run the map though.
Looks like services is an array, based on the console.log. If you want the id's of all categories, you can do
let categoryIds = [];
categoryIds = services.reduce((categoryIds, service) => {
let ids = service.categoryservices.map(category => category.id);
for(let id of ids) {
if(categoryIds.indexOf(id) === -1) {
categoryIds.push(id)
}
}
return categoryIds;
}, categoryIds);
If you want to have category ids as cats in each service, you can do,
var services=JSON.parse(JSON. stringify(services));
services.forEach(service) => {
service.cats = service.categoryservices.map(category => category.id);
delete service.categoryservices;
});
res.status(200).json({
'result': services,
'count': data.count,
'pages': pages
});
Hope this helps!
I have the following karma test. But I get the error "EXPECTED: {"data":{"form_field_17":"None Required","title":"Mr","firstName":"John","lastName":"Doe","email":"john#gmail.com","mobile":"1114269874","registration":"123","form_field_15":"Test","pref_date":"2016-08-30T02:13:00.000Z"},"formId":4}
GOT: {"data":"{\"form_field_17\":\"None Required\",\"title\":\"Mr\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"email\":\"john#gmail.com\",\"mobile\":\"1114269874\",\"registration\":\"123\",\"form_field_15\":\"Test\",\"pref_date\":\"30/08/2016 12:13 PM\"}","formId":4}
Why does my JSON object have backslashes and my pref_date has changed format?
describe('when submitting a form', function () {
it('should be successful', function () {
var d = {
form_field_17: "None Required",
title: "Mr",
firstName: "John",
lastName: "Doe",
email: "john#gmail.com",
mobile: "1114269874",
registration: "123",
form_field_15: "Test",
pref_date: "2016-08-30T02:13:00.000Z"
};
$httpBackend.expect('POST', '/DynamicForm/SubmitForm/', { data: d, formId: 4})
.respond(200, "[{ success : 'true', data : 'true' }]");
DynamicFormFactory.SubmitForm(d, 4)
.then(function (data) {
expect(data.success).toBeTruthy();
});
$httpBackend.flush();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
});
In my application I'm having the bellow problem
SERVER RESPONSE DATA
route /users
{ data: [
{ id: 5, name: 'peter' },
{ id: 10, name: 'adan' }
] }
route /users/5
{ data: { id: 5, name: 'peter' } }
case01
Restangular.one('users', 5).get().then(function(user){
$scope.user = user;
});
case02
Restangular.all('users').getList().then(function(users){
$scope.user = users[0];
});
In my case02 I can access $scope.user.id, but in case01 I have that to do $scope.user.data.id (what's not preferible when I render this in my template with {{ user.id }} where I have that use {{ user.data.id }} )
In my case02 I can change my data and use $scope.user.save(), but in my case01 I can't access this function
In my restangular I configure this for handle data from server when I use getList because it come in one object and not in one Array
RestangularProvider.setResponseInterceptor(function(response, operation, what) {
if (operation === "getList") {
// from:
// {data: [{ id: 1, name: 'peter' }, { id: 2, name: 'adan' }]}
// to:
// [{ id: 1, name: 'peter' }, { id: 2, name: 'adan' }]
return response.data;
}
return response;
});
So I would not work, because then I will have a normal JS object and not a Restangular object, the Restangular object has several methods that I still intend to use the same $scope, so we would like to preserve this.
example:
In an item from a case02 consultation (array) I have an object with methods of Restangular:
all (), allUrl (), one (), oneUrl (), ... and the properties of the object I sought (id, name) within $ scope.user.
{
all: function(){...},
allUrl: function(){...},
one: function(){...},
oneUrl: function(){...},
...
id: data_from_user,
name: data_from_user
}
In an item from a case01 query (object) I have an object with methods of Restangular within $ scope.user and object properties that sought (id, name) within $ scope.user.data.
{
all: function(){...},
allUrl: function(){...},
one: function(){...},
oneUrl: function(){...},
...
data: {
id: data_from_user,
name: data_from_user
}
}
For this reason it would not work the solution you presented because it is okay to work with restangular the subject, not an object of normal JS, but thank you for the answer Tim Castelijns.
if (operation === "getList") {
// from:
// {data: [{ id: 1, name: 'peter' }, { id: 2, name: 'adan' }]}
// to:
// [{ id: 1, name: 'peter' }, { id: 2, name: 'adan' }]
return response.data;
}
return response;
If operation is getList, you return response.data, which is
[
{ id: 5, name: 'peter' },
{ id: 10, name: 'adan' }
]
but if operation is not getList, you return response, which is the entire object, including the data sub-object
{
data: {
id: 5,
name: 'peter'
}
}
Your choice to return different results depending on what operation it is, is the reason you need to do user.data.id for case01, because you assign the result to user with then(function(user), when in fact user represents the entire data sub-object containing the user.
You should be able to solve it by changing to this
Restangular.one('users', 5).get().then(function(response){
$scope.user = response['data'];
});
I have a controller that accesses a resource Tag like so:
$scope.tags = Tag.query();
which resolves to something like this:
$scope.tags = [
{ name: "tag1", label: "Tag1" },
{ name: "tag2", label: "Tag2" },
{ name: "tag3", label: "Tag3" },
{ name: "tag4", label: "Tag4" },
];
For this particular controller, the returned tags should have an additional attribute "active": true, like { name: "tag1", label: "Tag1", active: true }.
How can I iterate over the returned promise once it is resolved to add this boolean?
Use the promise.then() function.
Tag.query().$promise.then(function (results) {
angular.forEach(results, function (result) {
result.active = true;
});
$scope.tags = results
});
see the docs on $q
I think what you need is this:
$scope.tags = Tag.query(function() {
$scope.tags['active'] = true;
});
When the server finishes calling the function that adds the property is executed.
I recommend you read https://docs.angularjs.org/api/ngResource/service/$resource
i cant get and ID from my json api, how can i do that? I am doing like $scope.info.id in my delete function but id doesnt work like that.
Here is my delete function
app.controller('InventoryCtrl', function($scope, $http, Inventory, $location) {
//getting the objects from Inventory
$scope.info = Inventory.query();
$scope.deleteInv = function () {
Inventory.drop({id: $scope.info.id}, function() {
$location.path('/');
});
};
});
here is my factory
app.factory('Inventory', function($resource, $http) {
return $resource('http://localhost/api/v1/inventory/:id', {id: "#id"},
{
drop: {
method: 'DELETE',
params: {id: "#id"}
}
}
);
});
And here is my api
{
meta: {
limit: 20,
next: null,
offset: 0,
previous: null,
total_count: 3
},
objects: [
{
category: {},
count: 1,
created: "2014-02-28T11:54:02.831409",
description: "dasdasdasdsa",
id: 20,
location: "asd",
name: "adas11",
resource_uri: "/api/v1/inventory/20",
status: "sad"
},
{
category: {},
count: 1,
created: "2014-02-28T11:54:03.708003",
description: "dasdasdasdsa",
id: 21,
location: "asd",
name: "adas11",
resource_uri: "/api/v1/inventory/21",
status: "sad"
},
]
}
I don't think you should call "drop" function. Instead you should call the 'delete' function which you specified in your factory.
$scope.deleteInv = function () {
Inventory.delete({id: $scope.info.id}, function() {
$location.path('/');
});