@priotas/restangular
Version:
Restful Resources service for AngularJS apps
1,419 lines (1,156 loc) • 60.4 kB
JavaScript
/* global describe, beforeEach, afterEach, it, expect, spyOn, jasmine */
/* jshint unused: false */
describe('Restangular', function () {
// API
var Restangular, $httpBackend, testData,
restangularAccounts, restangularAccount0, restangularAccount1;
// Load required modules
beforeEach(function () {
// Load restangular module
angular.mock.module('restangular');
// Get references to modules from the injector
angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
Restangular = $injector.get('Restangular');
});
// Restangularize a few demo accounts
restangularAccounts = Restangular.all('accounts');
restangularAccount0 = Restangular.one('accounts', 0);
restangularAccount1 = Restangular.one('accounts', 1);
// Create testdata for our tests
testData = {
// Model
accountsModel: [{
id: 0,
user: 'Martin ',
amount: 42,
transactions: []
}, {
id: 1,
user: 'Paul',
amount: 3.1416,
transactions: [{
from: 'Martin',
amount: 3,
id: 0
}, {
from: 'Anonymous',
amount: 0.1416,
id: 1
}]
}],
nextAccountId: 22,
// HAL model (http://stateless.co/hal_specification.html)
accountsHalModel: [{
id: 0,
user: 'Martin',
amount: 42,
transaction: [],
_links: {
self: '/accountsHAL/martin'
}
}, {
id: 1,
user: 'Paul',
amount: 3.1416,
transaction: [{
from: 'Martin',
amount: 3,
id: 0,
_links: {
self: '/accountsHAL/paul/transactions/0'
}
}, {
from: 'Anonymous',
amount: 0.1416,
id: 1,
_links: {
self: '/accountsHAL/paul/transactions/1'
}
}],
_links: {
self: '/accountsHAL/paul'
}
}],
infoModel: {
id: 0,
text: 'Some additional account information'
},
newAccount: {
user: 'First User',
amount: 45,
transactions: []
},
messages: [{
id: 23,
name: 'Gonto'
}, {
id: 45,
name: 'John'
}],
accountsDoSomethingModel: {
result: 1
},
// Another API for testing
customers: [{
id: 0,
name: 'Alice',
status: 'active',
credit: 4000.0
}, {
id: 1,
name: 'Bob',
status: 'active',
credit: 4000.0
}, {
id: 2,
name: 'Carl',
status: 'active',
credit: 4000.0
}],
publications: [{
id: 1,
title: 'Sample',
content: 'Rich data',
tags: [
'science',
'chemistry'
]
}],
newCustomer: {
id: 3,
name: 'New',
status: 'active',
credit: 4000.0
}
};
// Set up backend responses
$httpBackend.when('HEAD', '/accounts').respond();
$httpBackend.when('TRACE', '/accounts').respond();
$httpBackend.when('OPTIONS', '/accounts').respond();
// CRUD /accounts
$httpBackend.whenGET('/accounts').respond(testData.accountsModel);
$httpBackend.whenJSONP('/accounts').respond(testData.accountsModel);
$httpBackend.whenPOST('/accounts').respond(function (method, url, data, headers) {
var newData = angular.fromJson(data);
newData.fromServer = true;
newData.id = testData.nextAccountId;
return [201, JSON.stringify(newData), ''];
});
$httpBackend.whenGET('/accounts/do-something').respond(testData.accountsDoSomethingModel);
$httpBackend.whenGET('/accounts/search/byOwner').respond(testData.accountsModel);
// CRUD /accounts/{id}
$httpBackend.whenGET('/accounts/0,1').respond(testData.accountsModel);
$httpBackend.whenGET('/accounts/messages').respond(testData.messages);
$httpBackend.whenGET('/accounts/1/message').respond(testData.messages[0]);
$httpBackend.whenGET('/accounts/1/messages').respond(testData.messages);
$httpBackend.whenGET('/accounts/0').respond(testData.accountsModel[0]);
$httpBackend.whenGET('/accounts/1').respond(testData.accountsModel[1]);
$httpBackend.whenJSONP('/accounts/1').respond(testData.accountsModel[1]);
$httpBackend.whenGET('/accounts/1/transactions').respond(testData.accountsModel[1].transactions);
$httpBackend.whenGET('/accounts/1/transactions/1').respond(testData.accountsModel[1].transactions[1]);
$httpBackend.whenPOST('/accounts/1/transactions').respond(function (method, url, data, headers) {
return [201, '', ''];
});
$httpBackend.whenDELETE('/accounts/1/transactions/1').respond(function (method, url, data, headers) {
return [200, '', ''];
});
$httpBackend.whenDELETE('/accounts/1').respond(function (method, url, data, headers) {
return [200, '', ''];
});
$httpBackend.whenPOST('/accounts/1').respond(function (method, url, data, headers) {
return [200, '', ''];
});
$httpBackend.whenPUT('/accounts/1').respond(function (method, url, data, headers) {
testData.accountsModel[1] = angular.fromJson(data);
return [201, data, ''];
});
$httpBackend.whenGET('/info').respond(testData.infoModel);
$httpBackend.whenGET('/accounts/1/info').respond(testData.infoModel);
$httpBackend.whenPUT('/info').respond(function (method, url, data) {
return [200, data, ''];
});
$httpBackend.whenGET('/accountsHAL').respond(testData.accountsHalModel);
$httpBackend.whenPUT('/accountsHAL/martin').respond(function (method, url, data) {
testData.accountsHalModel[0] = angular.fromJson(data);
return [200, data, ''];
});
// Full URL
$httpBackend.whenGET('http://accounts.com/all').respond(testData.accountsModel);
$httpBackend.whenGET('/error').respond(function () {
return [500, {}, ''];
});
$httpBackend.whenGET('/misc/zero').respond(function () {
return [200, 0, ''];
});
// return the status code given
// e.g.: /error/404 returns 404 Not Found
var urlRegex = /\/error\/(\d{3})/;
$httpBackend.whenGET(urlRegex).respond(function (method, url, data, headers) {
return [url.match(urlRegex)[1], {}, ''];
});
$httpBackend.whenGET('/customers/').respond(testData.customers);
$httpBackend.whenGET('http://localhost:8080/customers/').respond(testData.customers);
$httpBackend.whenGET('api.new.domain/customers/').respond(testData.customers);
$httpBackend.whenGET('/customers/?active=true').respond(testData.customers);
$httpBackend.whenGET('/customers/publications/?tags=chemistry').respond(testData.publications);
$httpBackend.whenPUT('/customers/0').respond(function (method, url, data) {
testData.customers[0] = angular.fromJson(data);
return [200, data, ''];
});
$httpBackend.whenPOST('/customers/').respond(function (method, url, data, headers) {
var newData = angular.fromJson(data);
newData.fromServer = true;
return [201, JSON.stringify(newData), ''];
});
}); // END OF BEFOREEACH
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
describe('stripRestangular', function () {
// We test stripRestangular by saving objects and checking
// the data received by the backend in the POST request.
// StripRestangular is used to remove Restangular's methods
// from the restangularized POST data (element), but not from raw data.
it('should not strip Restangular properties from raw POST data', function () {
// https://github.com/mgonto/restangular/issues/374
var restangularFields = Restangular.configuration.restangularFields;
// create an object whose keys are the values of the restangularFields
var postData = {};
angular.forEach(restangularFields, function (value, key) {
postData.value = value;
});
// we don't want our post data to be treated as a restangularized object
postData.restangularized = false;
// when posting, restangular shouldn't remove any of our properties
var expectedData = angular.copy(postData);
$httpBackend.expectPOST('/accounts/1/merge', expectedData).respond(200);
var parent = Restangular.restangularizeElement(null, {id: 1}, 'accounts', true);
parent.post('merge', postData);
$httpBackend.flush();
});
it('should not strip "original" Restangular properties in restangularized POST data when overriding restangularFields', function () {
// https://github.com/mgonto/restangular/issues/374
// Here, we want to post a data object with fields
// that normally are used by Restangular, such that save, ids, options etc.
// We do that by taking each field in restangularFields and overriding it
// with something else, making restangular use different properties
// for its internal properties and functions, freeing the original ones
// for use in our data object
// these are the original field names used by restangular
var restangularFields = Restangular.configuration.restangularFields;
// create an object whose keys are the values of the restangularFields
// i.e. {save: "save", clone: "clone", doPOST: "doPOST", ...}
var postData = {};
angular.forEach(restangularFields, function (value, key) {
postData.value = value;
});
// we expect the http service to get all of these "original" properties in the data object
var expectedData = angular.copy(postData);
// Override the field names used internally by Restangular,
// the new config will be something like
// {id: '_id', save: '_save', clone: '_clone', ...}
var newFieldConfig = {};
angular.forEach(restangularFields, function (value, key) {
newFieldConfig[key] = '_' + key;
});
Restangular.setRestangularFields(newFieldConfig);
// Restangularize the data as an element to save
var parent = Restangular.restangularizeElement(null, postData, 'accounts', false);
// make the POST and check the posted data
$httpBackend.expectPOST('/accounts', expectedData).respond(200);
parent._save(); // we've overriden the save method as _save
$httpBackend.flush();
});
});
describe('Interceptors', function () {
it('Should add multiple request and response interceptors', function () {
Restangular.addRequestInterceptor(function (elem) {
var elemCopy = angular.copy(elem);
elemCopy.firstRequestInterceptor = true;
return elemCopy;
});
Restangular.addRequestInterceptor(function (elem) {
expect(elem.firstRequestInterceptor).toBeDefined();
var elemCopy = angular.copy(elem);
elemCopy.secondRequestInterceptor = true;
return elemCopy;
});
Restangular.addFullRequestInterceptor(function (elem) {
expect(elem.firstRequestInterceptor).toBeDefined();
expect(elem.secondRequestInterceptor).toBeDefined();
var elemCopy = angular.copy(elem);
elemCopy.thirdRequestInterceptor = true;
return {
element: elemCopy
};
});
Restangular.addResponseInterceptor(function (elem) {
var elemCopy = angular.copy(elem);
elemCopy.firstResponseInterceptor = true;
return elemCopy;
});
Restangular.addResponseInterceptor(function (elem) {
expect(elem.firstResponseInterceptor).toBeDefined();
var elemCopy = angular.copy(elem);
elemCopy.secondResponseInterceptor = true;
return elemCopy;
});
$httpBackend.whenPOST('/list').respond(function (method, url, data, headers) {
var elem = angular.fromJson(data);
expect(elem.firstRequestInterceptor).toBeDefined();
expect(elem.secondRequestInterceptor).toBeDefined();
expect(elem.thirdRequestInterceptor).toBeDefined();
return [200, elem, ''];
});
$httpBackend.expectPOST('/list');
Restangular.all('list').post({
name: 'Gonto'
}).then(function (elem) {
expect(elem.firstResponseInterceptor).toBeDefined();
expect(elem.secondResponseInterceptor).toBeDefined();
});
$httpBackend.flush();
});
it('Should add multiple error interceptors', function () {
$httpBackend.expectGET('/error');
var CallbackManager = function () {};
CallbackManager.successCallback = function () {
expect(CallbackManager.successCallback).not.toHaveBeenCalled();
};
CallbackManager.errorCallback = function () {
expect(CallbackManager.firstErrorInterceptor).toHaveBeenCalled();
expect(CallbackManager.secondErrorInterceptor).toHaveBeenCalled();
};
CallbackManager.firstErrorInterceptor = function () {};
CallbackManager.secondErrorInterceptor = function () {};
spyOn(CallbackManager, 'successCallback').and.callThrough();
spyOn(CallbackManager, 'firstErrorInterceptor').and.callThrough();
spyOn(CallbackManager, 'secondErrorInterceptor').and.callThrough();
Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor);
Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor);
Restangular.all('error').getList()
.then(CallbackManager.successCallback)
.catch(CallbackManager.errorCallback);
$httpBackend.flush();
});
it('Should add multiple error interceptors but don\'t reject the promise if one of them returns false', function () {
$httpBackend.expectGET('/error');
var CallbackManager = function () {};
CallbackManager.successCallback = function () {
expect(CallbackManager.successCallback).not.toHaveBeenCalled();
};
CallbackManager.errorCallback = function () {
expect(CallbackManager.errorCallback).not.toHaveBeenCalled();
};
CallbackManager.firstErrorInterceptor = function () {
return true;
};
CallbackManager.secondErrorInterceptor = function () {
return false; // prevent promise to be rejected
};
spyOn(CallbackManager, 'successCallback').and.callThrough();
spyOn(CallbackManager, 'errorCallback').and.callThrough();
Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor);
Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor);
Restangular.all('error').getList()
.then(CallbackManager.successCallback)
.catch(CallbackManager.errorCallback);
$httpBackend.flush();
});
it('Should add multiple error interceptors for a single get too', function () {
$httpBackend.expectGET('/error/404');
var CallbackManager = function () {};
CallbackManager.successCallback = function () {
expect(CallbackManager.successCallback).not.toHaveBeenCalled();
};
CallbackManager.errorCallback = function () {
expect(CallbackManager.firstErrorInterceptor).toHaveBeenCalled();
expect(CallbackManager.secondErrorInterceptor).toHaveBeenCalled();
};
CallbackManager.firstErrorInterceptor = function (response) {
expect(Number(response.status)).toEqual(404);
};
CallbackManager.secondErrorInterceptor = function () {};
spyOn(CallbackManager, 'successCallback').and.callThrough();
spyOn(CallbackManager, 'firstErrorInterceptor').and.callThrough();
spyOn(CallbackManager, 'secondErrorInterceptor').and.callThrough();
Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor);
Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor);
Restangular.one('error', 404).get()
.then(CallbackManager.successCallback)
.catch(CallbackManager.errorCallback);
$httpBackend.flush();
});
});
describe('Transformers', function () {
it('Should decorate element both on server and local by default', function () {
Restangular.extendModel('accounts', function (account) {
account.extended = function () {
return true;
};
return account;
});
Restangular.one('accounts', 1).get().then(function (account) {
expect(account.extended).toBeDefined();
});
var local = {};
Restangular.restangularizeElement(null, local, 'accounts');
expect(local.extended).toBeDefined();
$httpBackend.flush();
});
});
describe('With Suffix', function () {
it('shouldn\'t add suffix to getRestangularUrl', function () {
var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
var collection = suffixRestangular.all('accounts');
expect(collection.getRestangularUrl()).toBe('/accounts');
expect(collection.one('1').getRestangularUrl()).toBe('/accounts/1');
});
it('should add suffix to getRequestedUrl', function () {
var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
var collection = suffixRestangular.all('accounts');
expect(collection.getRequestedUrl()).toBe('/accounts.json');
expect(collection.one('1').getRequestedUrl()).toBe('/accounts/1.json');
});
it('should add suffix to request', function () {
var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
var collection = suffixRestangular.all('accounts');
$httpBackend.expectGET('/accounts.json').respond(200);
$httpBackend.expectGET('/accounts/1.json').respond(200);
collection.getList();
collection.get('1');
$httpBackend.flush();
});
it('shouldn\'t add suffix to allUrl', function () {
var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
$httpBackend.expectGET('http://accounts.com/all');
suffixRestangular.allUrl('accounts', 'http://accounts.com/all').getList();
$httpBackend.flush();
});
});
describe('JSONp', function () {
it('should work for get', function () {
Restangular.setJsonp(true);
Restangular.one('accounts', 1).get();
$httpBackend.expectJSONP('/accounts/1');
$httpBackend.flush();
});
it('should work for getList', function () {
Restangular.setJsonp(true);
Restangular.all('accounts').getList();
$httpBackend.expectJSONP('/accounts');
$httpBackend.flush();
});
it('shouldn\'t override post', function () {
Restangular.setJsonp(true);
restangularAccounts.post({
id: 2,
user: 'Someone'
});
$httpBackend.expectPOST('/accounts').respond(201, '');
$httpBackend.flush();
});
});
describe('Local data', function () {
it('Should restangularize a collection OK', function () {
var collection = angular.copy(testData.accountsModel);
Restangular.restangularizeCollection(null, collection, 'accounts');
expect(collection.hasOwnProperty('get')).toBe(true);
expect(collection[0].hasOwnProperty('get')).toBe(true);
expect(collection.getRestangularUrl()).toBe('/accounts');
expect(collection[0].getRestangularUrl()).toBe('/accounts/0');
});
it('Should restangularize a function with arguments OK', function () {
var collection = function (a, b) {};
Restangular.restangularizeCollection(null, collection, 'accounts');
expect(collection.hasOwnProperty('get')).toBe(true);
expect(collection.getRestangularUrl()).toBe('/accounts');
});
it('should have fromServer set when restangularizeElement is called with that param', function () {
var element = Restangular.restangularizeElement(null, {}, 'accounts', true);
expect(element.fromServer).toEqual(true);
element = Restangular.restangularizeElement(null, {}, 'accounts', false);
expect(element.fromServer).toEqual(false);
element = Restangular.restangularizeElement(null, {}, 'accounts');
expect(element.fromServer).toEqual(false);
});
it('should have fromServer set when restangularizeCollection is called with that param', function () {
var collection = Restangular.restangularizeCollection(null, [{}], 'accounts', true);
expect(collection[0].fromServer).toEqual(true);
collection = Restangular.restangularizeCollection(null, [{}], 'accounts', false);
expect(collection[0].fromServer).toEqual(false);
collection = Restangular.restangularizeCollection(null, [{}], 'accounts');
expect(collection[0].fromServer).toEqual(false);
});
});
describe('restangularizePromiseIntercept', function () {
it('should be invoked by restangularizePromise', function () {
var calledWithPromise;
Restangular.setRestangularizePromiseInterceptor(function (promise) {
calledWithPromise = promise;
promise.$object.$custom = true;
});
var promise = Restangular.one('accounts', 1).get();
expect(calledWithPromise).toBeDefined();
expect(promise.$object.$custom).toBeDefined();
$httpBackend.flush();
});
});
describe('$object', function () {
it('Should work for single get', function () {
var promise = Restangular.one('accounts', 1).get();
var obj = promise.$object;
expect(obj).toBeDefined();
expect(obj.amount).toBeUndefined();
$httpBackend.flush();
expect(obj.amount).toEqual(3.1416);
});
it('Shouldn\'t be restangularized by default', function () {
Restangular.extendModel('accounts', function (account) {
account.extended = function () {
return true;
};
return account;
});
var promise = Restangular.one('accounts', 1).get();
var obj = promise.$object;
expect(obj).toBeDefined();
expect(obj.extended).toBeUndefined();
$httpBackend.flush();
});
it('Should work for single get', function () {
var promise = Restangular.all('accounts').getList();
var list = promise.$object;
expect(list).toBeDefined();
expect(list.length).toEqual(0);
$httpBackend.flush();
expect(list.length).toEqual(2);
expect(list[1].amount).toEqual(3.1416);
});
});
describe('ALL', function () {
it('getList() should return an array of items', function () {
restangularAccounts.getList().then(function (accounts) {
expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(testData.accountsModel));
});
$httpBackend.flush();
});
it('several getList() should return an array of items', function () {
$httpBackend.expectGET('/accounts/0,1');
Restangular.several('accounts', 0, 1).getList().then(function (accounts) {
expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(testData.accountsModel));
});
$httpBackend.flush();
});
it('several remove() should work', function () {
$httpBackend.expectDELETE('/accounts/0,1').respond([200, '', '']);
Restangular.several('accounts', 0, 1).remove();
$httpBackend.flush();
});
it('get(id) should return the item with given id', function () {
restangularAccounts.get(0).then(function (account) {
expect(Restangular.stripRestangular(account)).toEqual(Restangular.stripRestangular(testData.accountsModel[0]));
});
$httpBackend.flush();
});
it('uses all to get the list without parameters', function () {
Restangular.one('accounts', 1).all('messages').getList();
$httpBackend.expectGET('/accounts/1/messages');
$httpBackend.flush();
});
it('Custom GET methods should work', function () {
restangularAccounts.customGETLIST('messages').then(function (msgs) {
expect(Restangular.stripRestangular(msgs)).toEqual(Restangular.stripRestangular(testData.messages));
});
$httpBackend.flush();
});
it('post() should add a new item', function () {
restangularAccounts.post({
id: 2,
user: 'Someone'
}).then(function () {
expect(testData.accountsModel.length).toEqual(2);
});
$httpBackend.expectPOST('/accounts').respond(201, '');
$httpBackend.flush();
});
it('customPOST() should add a new item', function () {
restangularAccounts.customPOST({
id: 2,
user: 'Someone'
}).then(function () {
expect(testData.accountsModel.length).toEqual(2);
});
$httpBackend.expectPOST('/accounts').respond(201, '');
$httpBackend.flush();
});
it('post() should work with arrays', function () {
Restangular.all('places').post([{
name: 'Gonto'
}, {
name: 'John'
}]).then(function (value) {
expect(value.length).toEqual(2);
});
$httpBackend.expectPOST('/places').respond(function (method, url, data, headers) {
return [201, angular.fromJson(data), ''];
});
$httpBackend.flush();
});
it('post() should add a new item with data and return the data from the server', function () {
restangularAccounts.post(testData.newAccount).then(function (added) {
expect(added.fromServer).toEqual(true);
expect(added.id).toEqual(testData.nextAccountId);
expect(added.user).toEqual(testData.newAccount.user);
});
$httpBackend.expectPOST('/accounts');
$httpBackend.flush();
});
it('Doing a post and then other operation (delete) should call right URLs', function () {
restangularAccounts.post(testData.newAccount).then(function (added) {
added.remove();
$httpBackend.expectDELETE('/accounts/' + testData.nextAccountId).respond(201, '');
});
$httpBackend.flush();
});
it('Doing a post to a server that returns no element will return undefined', function () {
restangularAccounts.getList().then(function (accounts) {
var newTransaction = {
id: 1,
name: 'Gonto'
};
accounts[1].post('transactions', newTransaction).then(function (transaction) {
expect(transaction).toBeUndefined();
});
});
$httpBackend.flush();
});
it('head() should safely return', function () {
restangularAccounts.head().then(function () {
expect(true).toBe(true);
});
$httpBackend.flush();
});
it('trace() should safely return', function () {
restangularAccounts.trace().then(function () {
expect(true).toBe(true);
});
$httpBackend.flush();
});
it('customPUT should work', function () {
$httpBackend.expectPUT('/accounts/hey').respond(testData.accountsModel);
restangularAccounts.customPUT({
key: 'value'
}, 'hey');
$httpBackend.flush();
});
it('customPATCH should work', function () {
var data = {
foo: 'bar'
};
$httpBackend.expectPATCH('/accounts/hey', data).respond(testData.accountsModel);
restangularAccounts.customPATCH(data, 'hey');
$httpBackend.flush();
});
it('options() should safely return', function () {
restangularAccounts.options().then(function () {
expect(true).toBe(true);
});
$httpBackend.flush();
});
it('getList() should correctly handle params after customDELETE', function () {
$httpBackend.expectGET('/accounts?foo=1').respond(testData.accountsModel);
restangularAccounts.getList({
foo: 1
}).then(function () {
$httpBackend.expectDELETE('/accounts?id=1').respond(201, '');
return restangularAccounts.customDELETE('', {
id: 1
});
}).then(function () {
$httpBackend.expectGET('/accounts?foo=1').respond(testData.accountsModel);
return restangularAccounts.getList({
foo: 1
});
}).then(function (accounts) {
expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(testData.accountsModel));
});
$httpBackend.flush();
});
});
describe('Scoped Service', function () {
it('should correctly work', function () {
var Accounts = Restangular.service('accounts');
Accounts.post(testData.newAccount);
Accounts.one(0).get();
Accounts.getList();
$httpBackend.expectPOST('/accounts');
$httpBackend.expectGET('/accounts/0');
$httpBackend.expectGET('/accounts');
$httpBackend.flush();
});
it('should correctly work with children', function () {
var Transactions = Restangular.service('transactions', restangularAccount1);
Transactions.post(testData.newAccount);
Transactions.one(1).get();
Transactions.getList();
Transactions.get(1);
$httpBackend.expectPOST('/accounts/1/transactions');
$httpBackend.expectGET('/accounts/1/transactions/1');
$httpBackend.expectGET('/accounts/1/transactions');
$httpBackend.expectGET('/accounts/1/transactions/1');
$httpBackend.flush();
});
it('should add custom collection method added with withConfig', function () {
var Accounts = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.addElementTransformer('accounts', true, function (worker) {
worker.addRestangularMethod('doSomething', 'get', 'do-something');
return worker;
});
}).service('accounts');
expect(Accounts.doSomething).toBeDefined();
expect(angular.isFunction(Accounts.doSomething)).toBeTruthy();
Accounts.post(testData.newAccount);
Accounts.one(0).get();
Accounts.getList();
Accounts.doSomething();
Accounts.get(0);
$httpBackend.expectPOST('/accounts');
$httpBackend.expectGET('/accounts/0');
$httpBackend.expectGET('/accounts');
$httpBackend.expectGET('/accounts/do-something');
$httpBackend.expectGET('/accounts/0');
$httpBackend.flush();
});
it('should provide a one-off $http configuration method', function () {
var Accounts = Restangular.service('accounts');
Accounts.withHttpConfig({
transformRequest: angular.identity
});
Accounts.post(testData.newAccount);
$httpBackend.expectPOST('/accounts');
$httpBackend.flush();
});
});
describe('ONE', function () {
it('get() should return a JSON item', function () {
restangularAccount1.get().then(function (account) {
expect(Restangular.stripRestangular(account))
.toEqual(Restangular.stripRestangular(testData.accountsModel[1]));
});
$httpBackend.flush();
});
it('Should save as put correctly', function () {
restangularAccount1.get().then(function (account) {
$httpBackend.expectPUT('/accounts/1');
account.put();
});
$httpBackend.flush();
});
it('Should save as post correctly', function () {
var account1 = angular.copy(restangularAccount1);
$httpBackend.expectPOST('/accounts/1');
account1.name = 'Hey';
account1.save();
$httpBackend.flush();
});
it('Should keep route property when element is created', function () {
var account1 = Restangular.restangularizeElement(null, {}, 'accounts');
$httpBackend.expectPOST('/accounts');
$httpBackend.expectPUT('/accounts/1');
account1.name = 'Hey';
account1.save().then(function (accountFromServer) {
accountFromServer.id = 1;
return accountFromServer.save();
}).then(function (accountFromServer2) {
expect(accountFromServer2.route).toBe(account1.route);
});
$httpBackend.flush();
});
it('Should make RequestLess connections with one', function () {
restangularAccount1.one('transactions', 1).get().then(function (transaction) {
expect(Restangular.stripRestangular(transaction))
.toEqual(Restangular.stripRestangular(testData.accountsModel[1].transactions[1]));
});
$httpBackend.flush();
});
it('Should make RequestLess connections with all', function () {
restangularAccount1.all('transactions').getList().then(function (transactions) {
expect(Restangular.stripRestangular(transactions))
.toEqual(Restangular.stripRestangular(testData.accountsModel[1].transactions));
});
$httpBackend.flush();
});
it('Custom GET methods should work', function () {
restangularAccount1.customGET('message').then(function (msg) {
expect(Restangular.stripRestangular(msg)).toEqual(Restangular.stripRestangular(testData.messages[0]));
});
$httpBackend.flush();
});
it('put() should update the value', function () {
restangularAccount1.get().then(function (account) {
account.amount = 1.618;
account.put().then(function (newAc) {
expect(testData.accountsModel[1].amount).toEqual(1.618);
newAc.remove();
$httpBackend.expectDELETE('/accounts/1');
});
$httpBackend.expectPUT('/accounts/1');
});
$httpBackend.flush();
});
it('should return an array when accessing a subvalue', function () {
restangularAccount1.get().then(function (account) {
account.getList('transactions').then(function (transactions) {
expect(Restangular.stripRestangular(transactions))
.toEqual(Restangular.stripRestangular(testData.accountsModel[1].transactions));
});
});
$httpBackend.flush();
});
});
describe('COPY', function () {
it('should copy an object and "this" should reference the copied object', function () {
var copiedAccount = Restangular.copy(testData.accountsModel[0]);
var that;
copiedAccount.user = 'Copied string';
expect(copiedAccount).not.toBe(testData.accountsModel[0]);
// create a spy for one of the methods to capture the value of 'this'
spyOn(copiedAccount, 'getRestangularUrl').and.callFake(function () {
that = this;
});
copiedAccount.getRestangularUrl(); // invoke the method we are spying on
expect(that).toBe(copiedAccount);
});
it('should copy an object and "fromServer" param should be the same with the copied object', function () {
var responseHandler = jasmine.createSpy();
// with fromServer=true
restangularAccount1.get().then(responseHandler);
$httpBackend.flush();
var account = responseHandler.calls.argsFor(0)[0],
copiedAccount = Restangular.copy(account);
expect(account.fromServer).toEqual(true);
expect(copiedAccount.fromServer).toEqual(true);
// with fromServer=false
account = Restangular.one('accounts', 123),
copiedAccount = Restangular.copy(account);
expect(account.fromServer).toEqual(false);
expect(copiedAccount.fromServer).toEqual(false);
});
it('should copy a collection and "fromServer" param should stay the same', function () {
var responseHandler = jasmine.createSpy();
// with collections, fromServer=false
var accounts = Restangular.all('accounts'),
copiedAccounts = Restangular.copy(accounts);
expect(accounts.fromServer).toEqual(false);
expect(copiedAccounts.fromServer).toEqual(false);
// with collections, fromServer = true;
restangularAccounts.getList().then(responseHandler);
$httpBackend.flush();
accounts = responseHandler.calls.argsFor(0)[0],
copiedAccounts = Restangular.copy(accounts);
expect(accounts.fromServer).toEqual(true);
expect(copiedAccounts.fromServer).toEqual(true);
});
it('should copy an object and "route" param should be the same in the copied object', function () {
// for element
var account = Restangular.one('accounts', 123),
copiedAccount = Restangular.copy(account);
expect(account.route).toEqual(copiedAccount.route);
// for collection
var accounts = Restangular.all('accounts'),
copiedAccounts = Restangular.copy(accounts);
expect(accounts.route).toEqual(copiedAccounts.route);
});
it('should copy an object and the parent property should stay the same', function () {
// element
var user = Restangular.one('account', 12).one('user', 14),
userCopy = Restangular.copy(user);
expect(user.parentResource.route).toEqual('account');
expect(userCopy.parentResource.route).toEqual('account');
// collection
var users = Restangular.one('account', 12).all('users'),
usersCopy = Restangular.copy(users);
expect(user.parentResource.route).toEqual('account');
expect(usersCopy.parentResource.route).toEqual('account');
});
});
describe('getRestangularUrl', function () {
it('should get the URL for the current object', function () {
var element = Restangular.one('accounts', 123);
expect(element.getRestangularUrl()).toEqual('/accounts/123');
});
it('should not include query parameters', function () {
var responseHandler = jasmine.createSpy(), element;
$httpBackend.expectGET('/accounts/123?query=params').respond({id: 123, name: 'account123'});
Restangular.one('accounts', 123).get({query: 'params'}).then(responseHandler);
$httpBackend.flush();
element = responseHandler.calls.argsFor(0)[0];
expect(element.getRestangularUrl()).toEqual('/accounts/123');
});
it('should be the same for the built resource as for the fetched resource', function () {
var responseHandler = jasmine.createSpy(), element, resource;
$httpBackend.expectGET('/accounts/123').respond({id: 123, name: 'Account 123'});
resource = Restangular.one('accounts', 123);
resource.get().then(responseHandler);
$httpBackend.flush();
element = responseHandler.calls.argsFor(0)[0];
expect(resource.getRestangularUrl()).toEqual('/accounts/123');
expect(element.getRestangularUrl()).toEqual('/accounts/123');
});
it('should use the id from the response, not the request', function () {
var responseHandler = jasmine.createSpy(), element, resource;
$httpBackend.expectGET('/accounts/123').respond({id: 444, name: 'Account 444'});
resource = Restangular.one('accounts', 123);
resource.get().then(responseHandler);
$httpBackend.flush();
element = responseHandler.calls.argsFor(0)[0];
expect(resource.getRestangularUrl()).toEqual('/accounts/123');
expect(element.getRestangularUrl()).toEqual('/accounts/444');
});
it('should have an empty id in the URL if the response id is empty', function () {
// https://github.com/mgonto/restangular/issues/1421
var responseHandler = jasmine.createSpy(), element, resource;
$httpBackend.expectGET('/accounts/123').respond({name: 'Account 444'});
resource = Restangular.one('accounts', 123);
resource.get().then(responseHandler);
$httpBackend.flush();
element = responseHandler.calls.argsFor(0)[0];
expect(resource.getRestangularUrl()).toEqual('/accounts/123');
expect(element.getRestangularUrl()).toEqual('/accounts');
});
it('should return the generated URL for PUTed elements', function () {
var responseHandler = jasmine.createSpy(), element;
$httpBackend.expectPUT('/accounts/123').respond({id: 123, name: 'Account 123'});
Restangular.one('accounts', 123).put().then(responseHandler);
$httpBackend.flush();
element = responseHandler.calls.argsFor(0)[0];
expect(element.getRestangularUrl()).toEqual('/accounts/123');
});
it('should return the generated URL for POSTed elements', function () {
var responseHandler = jasmine.createSpy(), element;
$httpBackend.expectPOST('/accounts').respond({id: 123, name: 'Account 123'});
Restangular.restangularizeElement(null, {name: 'Account 123'}, 'accounts', false, false).save().then(responseHandler);
$httpBackend.flush();
element = responseHandler.calls.argsFor(0)[0];
expect(element.getRestangularUrl()).toEqual('/accounts/123');
});
it('should return the generated URL when you chain Restangular methods together', function () {
var restangularSpaces = Restangular.one('accounts', 123).one('buildings', 456).all('spaces');
expect(restangularSpaces.getRestangularUrl()).toEqual('/accounts/123/buildings/456/spaces');
});
describe('with useCannonicalId set to true', function () {
it('should return the generated URL when you chain Restangular methods together', function () {
var R = Restangular.withConfig(function (config) {
config.setUseCannonicalId(true);
});
var restangularSpaces = R.one('accounts', 123).one('buildings', 456).all('spaces');
expect(restangularSpaces.getRestangularUrl()).toEqual('/accounts/123/buildings/456/spaces');
});
});
});
describe('addElementTransformer', function () {
it('should allow for a custom method to be placed at the collection level', function () {
var accountsPromise;
Restangular.addElementTransformer('accounts', true, function (collection) {
collection.totalAmount = function () {};
return collection;
});
accountsPromise = Restangular.all('accounts').getList();
accountsPromise.then(function (accounts) {
expect(typeof accounts.totalAmount).toEqual('function');
});
$httpBackend.flush();
});
it('should allow for a custom method to be placed at the model level when one model is requested', function () {
var accountPromise;
Restangular.addElementTransformer('accounts', false, function (model) {
model.prettifyAmount = function () {};
return model;
});
accountPromise = Restangular.one('accounts', 1).get();
accountPromise.then(function (account) {
expect(typeof account.prettifyAmount).toEqual('function');
});
$httpBackend.flush();
});
it('should allow for a custom method to be placed at the model level when several models are requested', function () {
var accountsPromise;
Restangular.addElementTransformer('accounts', false, function (model) {
model.prettifyAmount = function () {};
return model;
});
accountsPromise = Restangular.all('accounts', 1).getList();
accountsPromise.then(function (accounts) {
accounts.forEach(function (account) {
expect(typeof account.prettifyAmount).toEqual('function');
});
});
$httpBackend.flush();
});
it('should allow for a custom method to be placed at the collection level using a regexp matching the route', function () {
var accountsPromise;
Restangular.addElementTransformer(/^accounts/, false, function (model) {
model.prettifyAmount = function () {};
return model;
});
accountsPromise = Restangular.all('accounts/search/byOwner', 1).getList();
accountsPromise.then(function (accounts) {
accounts.forEach(function (account) {
expect(typeof account.prettifyAmount).toEqual('function');
});
});
$httpBackend.flush();
});
it('should work with cloned collections', function () {
var responseHandler = jasmine.createSpy();
Restangular.addElementTransformer(/^accounts/, true, function (collection) {
collection.customThing = 'customValue';
return collection;
});
Restangular.all('accounts').getList().then(responseHandler);
$httpBackend.flush();
var accounts = responseHandler.calls.argsFor(0)[0];
var accountsCopy = accounts.clone();
expect(accounts.customThing).toEqual('customValue');
expect(accountsCopy.customThing).toEqual('customValue');
});
it('should allow for a custom method to be placed at the model level using regexp route when one model is requested', function () {
var accountPromise;
Restangular.addElementTransformer(/^accounts/, false, function (model) {
model.prettifyAmount = function () {};
return model;
});
accountPromise = Restangular.one('accounts', 1).get();
accountPromise.then(function (account) {
expect(typeof account.prettifyAmount).toEqual('function');
});
$httpBackend.flush();
});
it('should allow for a custom method to be placed at the model level using regexp when several models are requested', function () {
var accountsPromise;
Restangular.addElementTransformer(/^accounts/, false, function (model) {
model.prettifyAmount = function () {};
return model;
});
accountsPromise = Restangular.all('accounts', 1).getList();
accountsPromise.then(function (accounts) {
accounts.forEach(function (account) {
expect(typeof account.prettifyAmount).toEqual('function');
});
});
$httpBackend.flush();
});
});
describe('extendCollection', function () {
it('should be an alias for a specific invocation of addElementTransformer', function () {
var spy = spyOn(Restangular, 'addElementTransformer');
var fn = function (collection) {
collection.totalAmount = function () {};
return collection;
};
Restangular.extendCollection('accounts', fn);
expect(spy).toHaveBeenCalledWith('accounts', true, fn);
});
});
describe('extendModel', function () {
it('should be an alias for a specific invocation of addElementTransformer', function () {
var spy = spyOn(Restangular, 'addElementTransformer');
var fn = function (model) {
model.prettifyAmount = function () {};
return model;
};
Restangular.extendModel('accounts', fn);
expect(spy).toHaveBeenCalledWith('accounts', false, fn);
});
});
describe('headers', function () {
it('should return defaultHeaders', function () {
var defaultHeaders = {
testheader: 'header value'
};
Restangular.setDefaultHeaders(defaultHeaders);
expect(Restangular.defaultHeaders).toEqual(defaultHeaders);
});
it('should pass uppercase methods in X-HTTP-Method-Override', function () {
Restangular.setMethodOverriders(['put']);
$httpBackend.expectPOST('/overriders/1').respond(function (method, url, data, headers) {
expect(headers['X-HTTP-Method-Override']).toBe('PUT');
return {};
});
Restangular.one('overriders', 1).put();
$httpBackend.flush();
});
});
describe('defaultRequestParams', function () {
it('should return defaultRequestParams', function () {
var defaultRequestParams = {
param: 'value'
};
Restangular.setDefaultRequestParams(defaultRequestParams);
expect(Restangular.requestParams.common).toEqual(defaultRequestParams);
});
it('should be able to set default params for get, post, put.. methods separately', function () {
var postParams = {
post: 'value'
},
putParams = {
put: 'value'
};
Restangular.setDefaultRequestParams('post', postParams);
expect(Restangular.requestParams.post).toEqual(postParams);
Restangular.setDefaultRequestParams('put', putParams);
expect(Restangular.requestParams.put).toEqual(putParams);
expect(Restangular.requestParams.common).not.toEqual(putParams);
});
it('should be able to set default params for multiple methods with array', function () {
var defaultParams = {
param: 'value'
};
Restangular.setDefaultRequestParams(['post', 'put'], defaultParams);
expect(Restangular.requestParams.post).toEqual(defaultParams);
expect(Restangular.requestParams.put).toEqual(defaultParams);
expect(Restangular.requestParams.common).not.toEqual(defaultParams);
});
});
describe('withConfig', function () {
it('should create new service with scoped configuration', function () {
var childRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api/v1');
});
expect(Restangular.configuration.baseUrl).toEqual('');
expect(childRestangular.configuration.baseUrl).toEqual('/api/v1');
});
it('should allow nested configurations', function () {
var childRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api/v1');
});
var grandchildRestangular = childRestangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
expect(Restangular.configuration.baseUrl).toEqual('');
expect(Restangular.configuration.suffix).toEqual(null);
expect(childRestangular.configuration.baseUrl).toEqual('/api/v1');
expect(childRestangular.configuration.suffix).toEqual(null);
expect(grandchildRestangular.configuration.baseUrl).toEqual('/api/v1');
expect(grandchildRestangular.configuration.suffix).toEqual('.json');
});
});
describe('Self linking', function () {
it('Should request the link in HAL format', function () {
var linkRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRestangularFields({
selfLink: '_links.self'
});
});
var arr = linkRestangular.all('account