restangular
Version:
Restful Resources service for AngularJS apps
1,485 lines (1,199 loc) • 49.2 kB
JavaScript
/* global describe, beforeEach, inject, afterEach, it, expect, spyOn, jasmine */
/* jshint unused: false */
describe('Restangular', function() {
// API
var Restangular, $httpBackend,
accountsModel, restangularAccounts, restangularAccount0, restangularAccount1,
infoModel, accountsDoSomethingModel, customers, publications, newCustomer,
accountsHalModel,
messages, newAccount, nextAccountId;
// Load required modules
beforeEach(angular.mock.module('restangular'));
// Init HTTP mock backend and Restangular resources
beforeEach(inject(function($injector) {
// 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
};
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('HEAD', '/accounts').respond();
$httpBackend.when('TRACE', '/accounts').respond();
$httpBackend.when('OPTIONS', '/accounts').respond();
$httpBackend.whenGET('/accounts').respond(accountsModel);
$httpBackend.whenGET('/accounts/do-something').respond(accountsDoSomethingModel);
$httpBackend.whenJSONP('/accounts').respond(accountsModel);
$httpBackend.whenGET('/accounts/0,1').respond(accountsModel);
$httpBackend.whenGET('/accounts/messages').respond(messages);
$httpBackend.whenGET('/accounts/1/message').respond(messages[0]);
$httpBackend.whenGET('/accounts/1/messages').respond(messages);
$httpBackend.whenGET('/accounts/0').respond(accountsModel[0]);
$httpBackend.whenGET('/accounts/1').respond(accountsModel[1]);
$httpBackend.whenJSONP('/accounts/1').respond(accountsModel[1]);
$httpBackend.whenGET('/accounts/1/transactions').respond(accountsModel[1].transactions);
$httpBackend.whenGET('/accounts/1/transactions/1').respond(accountsModel[1].transactions[1]);
$httpBackend.whenGET('/accounts/search/byOwner').respond(accountsModel);
$httpBackend.whenGET('/info').respond(infoModel);
$httpBackend.whenGET('/accounts/1/info').respond(infoModel);
$httpBackend.whenPUT('/info').respond(function(method, url, data) {
return [200, data, ''];
});
$httpBackend.whenGET('/accountsHAL').respond(accountsHalModel);
$httpBackend.whenPUT('/accountsHAL/martin').respond(function(method, url, data) {
accountsHalModel[0] = angular.fromJson(data);
return [200, data, ''];
});
// Full URL
$httpBackend.whenGET('http://accounts.com/all').respond(accountsModel);
$httpBackend.whenPOST('/accounts').respond(function(method, url, data, headers) {
var newData = angular.fromJson(data);
newData.fromServer = true;
newData.id = nextAccountId;
return [201, JSON.stringify(newData), ''];
});
$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) {
accountsModel[1] = angular.fromJson(data);
return [201, data, ''];
});
$httpBackend.whenGET('/error').respond(function() {
return [500, {}, ''];
});
$httpBackend.whenGET('/misc/zero').respond(function() {
return [200, 0, ''];
});
$httpBackend.whenPOST('/customs').respond(function(method, url, data, headers) {
if (JSON.parse(data).one) {
return [201, '', ''];
} else {
return [400, '', ''];
}
});
// 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], {}, ''];
});
Restangular = $injector.get('Restangular');
restangularAccounts = Restangular.all('accounts');
restangularAccount0 = Restangular.one('accounts', 0);
restangularAccount1 = Restangular.one('accounts', 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
};
$httpBackend.whenGET('/customers/').respond(customers);
$httpBackend.whenGET('http://localhost:8080/customers/').respond(customers);
$httpBackend.whenGET('api.new.domain/customers/').respond(customers);
$httpBackend.whenGET('/customers/?active=true').respond(customers);
$httpBackend.whenGET('/customers/publications/?tags=chemistry').respond(publications);
$httpBackend.whenPUT('/customers/0').respond(function(method, url, data) {
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), ''];
});
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
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(accountsModel);
Restangular.restangularizeCollection(null, collection, 'accounts');
expect(_.has(collection, 'get')).toBe(true);
expect(_.has(collection[0], '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(_.has(collection, '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(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(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(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(messages));
});
$httpBackend.flush();
});
it('post() should add a new item', function() {
restangularAccounts.post({
id: 2,
user: 'Someone'
}).then(function() {
expect(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(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(newAccount).then(function(added) {
expect(added.fromServer).toEqual(true);
expect(added.id).toEqual(nextAccountId);
expect(added.user).toEqual(newAccount.user);
});
$httpBackend.expectPOST('/accounts');
$httpBackend.flush();
});
it('Doing a post and then other operation (delete) should call right URLs', function() {
restangularAccounts.post(newAccount).then(function(added) {
added.remove();
$httpBackend.expectDELETE('/accounts/' + 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(accountsModel);
restangularAccounts.customPUT({
key: 'value'
}, 'hey');
$httpBackend.flush();
});
it('customPATCH should work', function() {
var data = {
foo: 'bar'
};
$httpBackend.expectPATCH('/accounts/hey', data).respond(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(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(accountsModel);
return restangularAccounts.getList({
foo: 1
});
}).then(function(accounts) {
expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(accountsModel));
});
$httpBackend.flush();
});
});
describe('Scoped Service', function() {
it('should correctly work', function() {
var Accounts = Restangular.service('accounts');
Accounts.post(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(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(_.isFunction(Accounts.doSomething)).toBeTruthy();
Accounts.post(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(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(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(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(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(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(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(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(accountsModel[0]);
var that;
copiedAccount.user = 'Copied string';
expect(copiedAccount).not.toBe(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 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('getRestangularUrl 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('accountsHAL').getList().$object;
$httpBackend.flush();
var account = arr[0];
$httpBackend.expectPUT('/accountsHAL/martin');
account.name = 'Updated';
account.put();
$httpBackend.flush();
});
});
describe('Singe one (endpoint not expecting an id)', function() {
it('does not use the id for single resource GET', function() {
Restangular.one('info', 0, true).get();
$httpBackend.expectGET('/info');
$httpBackend.flush();
});
it('getRestangularUrl() returns still the url without id after GET', function() {
var record = Restangular.one('info', 0, true);
record.get().then(function(data) {
expect(data.getRestangularUrl()).toEqual('/info');
});
$httpBackend.expectGET('/info');
$httpBackend.flush();
});
it('does not use the id for single nested resource GET', function() {
Restangular.one('accounts', 1).one('info', 0, true).get();
$httpBackend.expectGET('/accounts/1/info');
$httpBackend.flush();
});
it('does not use the id for single resource PUT', function() {
Restangular.one('info', 0, true).put();
$httpBackend.expectPUT('/info');
$httpBackend.flush();
});
});
describe('setSelfLinkAbsoluteUrl', function() {
it('works', function() {
var childRestangular = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setSelfLinkAbsoluteUrl(false);
});
expect(Restangular.configuration.absoluteUrl).toEqual(true);
expect(childRestangular.configuration.absoluteUrl).toEqual(false);
});
});
describe('Misc', function() {
it('should not strip [one] or [all] key from plain object', function() {
Restangular.all('customs').customPOST({
one: 'I am here',
two: 'I am also here'
}).then(function() {
expect(1).toBe(1);
}, function() {
expect('Promise').toBe('correctly fulfilled');
});
$httpBackend.flush();
});
it('should not stip non-restangularized elements', function() {
expect(Restangular.stripRestangular(['test', 'test2'])).toEqual(['test', 'test2']);
});
it('should accept 0 as response', function() {
Restangular.one('misc', 'zero').get().then(function(res) {
expect(res).toEqual(0);
});
$httpBackend.flush();
});
it('Should accept 0 as a proper id in custom requests', function() {
$httpBackend.expectDELETE('/accounts/0').respond(202);
Restangular.all('accounts').customDELETE(0);
$httpBackend.flush();
});
});
describe('testing normalize url', function() {
it('should get a list of objects', function() {
Restangular.all('customers/').getList().then(function(res) {
res.getList({
active: true
});
$httpBackend.expectGET('/customers/?active=true');
//res.getList('publications/', {tags: 'chemistry'});
//$httpBackend.expectGET('/customers/publications/?tags=chemistry');
});
$httpBackend.expectGET('/customers/');
$httpBackend.flush();
});
it('should get a list of objects even if the path has extra slashes', function() {
Restangular.all('customers///').getList().then(function(res) {
res.getList({
active: true
});
$httpBackend.expectGET('/customers/?active=true');
});
$httpBackend.expectGET('/customers/');
$httpBackend.flush();
});
it('should post with slash at the end', function() {
Restangular.all('customers/').getList().then(function(res) {
res.post(newCustomer);
$httpBackend.expectPOST('/customers/');
});
$httpBackend.expectGET('/customers/');
$httpBackend.flush();
});
it('should put with slash at the end', function() {
Restangular.all('customers/').getList().then(function(customers) {
customers[0].put();
$httpBackend.expectPUT('/customers/0');
});
$httpBackend.flush();
});
it('should return a normalized URL even it has extra slashes', function() {
var restangularSpaces = Restangular.one('accounts//', 123).one('buildings//', 456).all('spaces///');
expect(restangularSpaces.getRestangularUrl()).toEqual('/accounts/123/buildings/456/spaces/');
});
it('should create a new service and still working normalized URL', function() {
var newRes = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('http://localhost:8080');
});
expect(newRes.configuration.baseUrl).toEqual('http://localhost:8080');
newRes.all('customers////').getList();
$httpBackend.expectGET('http://localhost:8080/customers/');
var newApi = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('api.new.domain');
});
expect(newApi.configuration.baseUrl).toEqual('api.new.domain');
newApi.all('customers////').getList();
$httpBackend.expectGET('api.new.domain/customers/');
$httpBackend.flush();
});
it('Should work with absolute URL with //authority', function() {
var newRes = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('//localhost:8080');
});
expect(newRes.configuration.baseUrl).toEqual('//localhost:8080');
newRes.all('customers////').getList();
$httpBackend.expectGET('//localhost:8080/customers/').respond([]);
$httpBackend.flush();
});
});
describe('setPlainByDefault', function() {
it('should not add restangularized methods to response object', function() {
var newRes = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setPlainByDefault(true);
});
expect(newRes.configuration.plainByDefault).toEqual(true);
newRes.one('accounts', 0).get().then(function(account) {
expect(account).toEqual(accountsModel[0]);
});
$httpBackend.flush();
});
it('shoud not add restangularized methods to response collection', function() {
var newRes = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setPlainByDefault(true);
});
newRes.all('accounts').getList().then(function(accounts) {
expect(accounts).toEqual(accountsModel);
});
$httpBackend.flush();
});
});
});