mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
When using $resource you must setup your actions carefully based on what the server returns. If the server responds to a request with an array then you must configure the action with `isArray:true` and vice versa. The built-in `get` action defaults to `isArray:false` and the `query` action defaults to `isArray:true`, which is must be changed if the server does not do this. Before the error message was an exception inside angular.copy, which didn't explain what the real problem was. Rather than changing the way that angular.copy works, this change ensures that a better error message is provided to the programmer if they do not set up their resource actions correctly. Closes #2255, #1044
1083 lines
No EOL
36 KiB
JavaScript
1083 lines
No EOL
36 KiB
JavaScript
'use strict';
|
|
|
|
describe("resource", function() {
|
|
var $resource, CreditCard, callback, $httpBackend;
|
|
|
|
beforeEach(module('ngResource'));
|
|
beforeEach(inject(function($injector) {
|
|
$httpBackend = $injector.get('$httpBackend');
|
|
$resource = $injector.get('$resource');
|
|
CreditCard = $resource('/CreditCard/:id:verb', {id:'@id.key'}, {
|
|
charge:{
|
|
method:'post',
|
|
params:{verb:'!charge'}
|
|
},
|
|
patch: {
|
|
method: 'PATCH'
|
|
},
|
|
conditionalPut: {
|
|
method: 'PUT',
|
|
headers: {
|
|
'If-None-Match': '*'
|
|
}
|
|
}
|
|
|
|
});
|
|
callback = jasmine.createSpy();
|
|
}));
|
|
|
|
|
|
afterEach(function() {
|
|
$httpBackend.verifyNoOutstandingExpectation();
|
|
});
|
|
|
|
|
|
it("should build resource", function() {
|
|
expect(typeof CreditCard).toBe('function');
|
|
expect(typeof CreditCard.get).toBe('function');
|
|
expect(typeof CreditCard.save).toBe('function');
|
|
expect(typeof CreditCard.remove).toBe('function');
|
|
expect(typeof CreditCard['delete']).toBe('function');
|
|
expect(typeof CreditCard.query).toBe('function');
|
|
});
|
|
|
|
|
|
it('should default to empty parameters', function() {
|
|
$httpBackend.expect('GET', 'URL').respond({});
|
|
$resource('URL').query();
|
|
});
|
|
|
|
|
|
it('should ignore slashes of undefinend parameters', function() {
|
|
var R = $resource('/Path/:a/:b/:c');
|
|
|
|
$httpBackend.when('GET', '/Path').respond('{}');
|
|
$httpBackend.when('GET', '/Path/0').respond('{}');
|
|
$httpBackend.when('GET', '/Path/false').respond('{}');
|
|
$httpBackend.when('GET', '/Path').respond('{}');
|
|
$httpBackend.when('GET', '/Path/').respond('{}');
|
|
$httpBackend.when('GET', '/Path/1').respond('{}');
|
|
$httpBackend.when('GET', '/Path/2/3').respond('{}');
|
|
$httpBackend.when('GET', '/Path/4/5').respond('{}');
|
|
$httpBackend.when('GET', '/Path/6/7/8').respond('{}');
|
|
|
|
R.get({});
|
|
R.get({a:0});
|
|
R.get({a:false});
|
|
R.get({a:null});
|
|
R.get({a:undefined});
|
|
R.get({a:''});
|
|
R.get({a:1});
|
|
R.get({a:2, b:3});
|
|
R.get({a:4, c:5});
|
|
R.get({a:6, b:7, c:8});
|
|
});
|
|
|
|
it('should not ignore leading slashes of undefinend parameters that have non-slash trailing sequence', function() {
|
|
var R = $resource('/Path/:a.foo/:b.bar/:c.baz');
|
|
|
|
$httpBackend.when('GET', '/Path/.foo/.bar.baz').respond('{}');
|
|
$httpBackend.when('GET', '/Path/0.foo/.bar.baz').respond('{}');
|
|
$httpBackend.when('GET', '/Path/false.foo/.bar.baz').respond('{}');
|
|
$httpBackend.when('GET', '/Path/.foo/.bar.baz').respond('{}');
|
|
$httpBackend.when('GET', '/Path/.foo/.bar.baz').respond('{}');
|
|
$httpBackend.when('GET', '/Path/1.foo/.bar.baz').respond('{}');
|
|
$httpBackend.when('GET', '/Path/2.foo/3.bar.baz').respond('{}');
|
|
$httpBackend.when('GET', '/Path/4.foo/.bar/5.baz').respond('{}');
|
|
$httpBackend.when('GET', '/Path/6.foo/7.bar/8.baz').respond('{}');
|
|
|
|
R.get({});
|
|
R.get({a:0});
|
|
R.get({a:false});
|
|
R.get({a:null});
|
|
R.get({a:undefined});
|
|
R.get({a:''});
|
|
R.get({a:1});
|
|
R.get({a:2, b:3});
|
|
R.get({a:4, c:5});
|
|
R.get({a:6, b:7, c:8});
|
|
});
|
|
|
|
|
|
it('should support escaping colons in url template', function() {
|
|
var R = $resource('http://localhost\\:8080/Path/:a/\\:stillPath/:b');
|
|
|
|
$httpBackend.expect('GET', 'http://localhost:8080/Path/foo/:stillPath/bar').respond();
|
|
R.get({a: 'foo', b: 'bar'});
|
|
});
|
|
|
|
it('should support an unescaped url', function() {
|
|
var R = $resource('http://localhost:8080/Path/:a');
|
|
|
|
$httpBackend.expect('GET', 'http://localhost:8080/Path/foo').respond();
|
|
R.get({a: 'foo'});
|
|
});
|
|
|
|
|
|
it('should correctly encode url params', function() {
|
|
var R = $resource('/Path/:a');
|
|
|
|
$httpBackend.expect('GET', '/Path/foo%231').respond('{}');
|
|
$httpBackend.expect('GET', '/Path/doh!@foo?bar=baz%231').respond('{}');
|
|
|
|
R.get({a: 'foo#1'});
|
|
R.get({a: 'doh!@foo', bar: 'baz#1'});
|
|
});
|
|
|
|
|
|
it('should not encode @ in url params', function() {
|
|
//encodeURIComponent is too agressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt
|
|
//with regards to the character set (pchar) allowed in path segments
|
|
//so we need this test to make sure that we don't over-encode the params and break stuff like
|
|
//buzz api which uses @self
|
|
|
|
var R = $resource('/Path/:a');
|
|
$httpBackend.expect('GET', '/Path/doh@fo%20o?!do%26h=g%3Da+h&:bar=$baz@1').respond('{}');
|
|
R.get({a: 'doh@fo o', ':bar': '$baz@1', '!do&h': 'g=a h'});
|
|
});
|
|
|
|
|
|
it('should encode array params', function() {
|
|
var R = $resource('/Path/:a');
|
|
$httpBackend.expect('GET', '/Path/doh&foo?bar=baz1&bar=baz2').respond('{}');
|
|
R.get({a: 'doh&foo', bar: ['baz1', 'baz2']});
|
|
});
|
|
|
|
it('should not encode string "null" to "+" in url params', function() {
|
|
var R = $resource('/Path/:a');
|
|
$httpBackend.expect('GET', '/Path/null').respond('{}');
|
|
R.get({a: 'null'});
|
|
});
|
|
|
|
it('should allow relative paths in resource url', function () {
|
|
var R = $resource(':relativePath');
|
|
$httpBackend.expect('GET', 'data.json').respond('{}');
|
|
R.get({ relativePath: 'data.json' });
|
|
});
|
|
|
|
it('should handle + in url params', function () {
|
|
var R = $resource('/api/myapp/:myresource?from=:from&to=:to&histlen=:histlen');
|
|
$httpBackend.expect('GET', '/api/myapp/pear+apple?from=2012-04-01&to=2012-04-29&histlen=3').respond('{}');
|
|
R.get({ myresource: 'pear+apple', from : '2012-04-01', to : '2012-04-29', histlen : 3 });
|
|
});
|
|
|
|
|
|
it('should encode & in url params', function() {
|
|
var R = $resource('/Path/:a');
|
|
$httpBackend.expect('GET', '/Path/doh&foo?bar=baz%261').respond('{}');
|
|
R.get({a: 'doh&foo', bar: 'baz&1'});
|
|
});
|
|
|
|
|
|
it('should build resource with default param', function() {
|
|
$httpBackend.expect('GET', '/Order/123/Line/456.visa?minimum=0.05').respond({id: 'abc'});
|
|
var LineItem = $resource('/Order/:orderId/Line/:id:verb',
|
|
{orderId: '123', id: '@id.key', verb:'.visa', minimum: 0.05});
|
|
var item = LineItem.get({id: 456});
|
|
$httpBackend.flush();
|
|
expect(item).toEqualData({id:'abc'});
|
|
});
|
|
|
|
|
|
it('should not pass default params between actions', function() {
|
|
var R = $resource('/Path', {}, {get: {method: 'GET', params: {objId: '1'}}, perform: {method: 'GET'}});
|
|
|
|
$httpBackend.expect('GET', '/Path?objId=1').respond('{}');
|
|
$httpBackend.expect('GET', '/Path').respond('{}');
|
|
|
|
R.get({});
|
|
R.perform({});
|
|
});
|
|
|
|
|
|
it("should build resource with action default param overriding default param", function() {
|
|
$httpBackend.expect('GET', '/Customer/123').respond({id: 'abc'});
|
|
var TypeItem = $resource('/:type/:typeId', {type: 'Order'},
|
|
{get: {method: 'GET', params: {type: 'Customer'}}});
|
|
var item = TypeItem.get({typeId: 123});
|
|
|
|
$httpBackend.flush();
|
|
expect(item).toEqualData({id: 'abc'});
|
|
});
|
|
|
|
|
|
it('should build resource with action default param reading the value from instance', function() {
|
|
$httpBackend.expect('POST', '/Customer/123').respond();
|
|
var R = $resource('/Customer/:id', {}, {post: {method: 'POST', params: {id: '@id'}}});
|
|
|
|
var inst = new R({id:123});
|
|
expect(inst.id).toBe(123);
|
|
|
|
inst.$post();
|
|
});
|
|
|
|
|
|
it('should not throw TypeError on null default params', function() {
|
|
$httpBackend.expect('GET', '/Path?').respond('{}');
|
|
var R = $resource('/Path', {param: null}, {get: {method: 'GET'}});
|
|
|
|
expect(function() {
|
|
R.get({});
|
|
}).not.toThrow();
|
|
});
|
|
|
|
|
|
it('should handle multiple params with same name', function() {
|
|
var R = $resource('/:id/:id');
|
|
|
|
$httpBackend.when('GET').respond('{}');
|
|
$httpBackend.expect('GET', '/1/1');
|
|
|
|
R.get({id:1});
|
|
});
|
|
|
|
|
|
it("should create resource", function() {
|
|
$httpBackend.expect('POST', '/CreditCard', '{"name":"misko"}').respond({id: 123, name: 'misko'});
|
|
|
|
var cc = CreditCard.save({name: 'misko'}, callback);
|
|
expect(cc).toEqualData({name: 'misko'});
|
|
expect(callback).not.toHaveBeenCalled();
|
|
|
|
$httpBackend.flush();
|
|
expect(cc).toEqualData({id: 123, name: 'misko'});
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(callback.mostRecentCall.args[0]).toEqual(cc);
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
});
|
|
|
|
|
|
it("should read resource", function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
var cc = CreditCard.get({id: 123}, callback);
|
|
|
|
expect(cc instanceof CreditCard).toBeTruthy();
|
|
expect(cc).toEqualData({});
|
|
expect(callback).not.toHaveBeenCalled();
|
|
|
|
$httpBackend.flush();
|
|
expect(cc).toEqualData({id: 123, number: '9876'});
|
|
expect(callback.mostRecentCall.args[0]).toEqual(cc);
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
});
|
|
|
|
|
|
it('should send correct headers', function() {
|
|
$httpBackend.expectPUT('/CreditCard/123', undefined, function(headers) {
|
|
return headers['If-None-Match'] == "*";
|
|
}).respond({id:123});
|
|
|
|
CreditCard.conditionalPut({id: {key:123}});
|
|
});
|
|
|
|
|
|
it("should read partial resource", function() {
|
|
$httpBackend.expect('GET', '/CreditCard').respond([{id:{key:123}}]);
|
|
var ccs = CreditCard.query();
|
|
|
|
$httpBackend.flush();
|
|
expect(ccs.length).toEqual(1);
|
|
|
|
var cc = ccs[0];
|
|
expect(cc instanceof CreditCard).toBe(true);
|
|
expect(cc.number).toBeUndefined();
|
|
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: {key: 123}, number: '9876'});
|
|
cc.$get(callback);
|
|
$httpBackend.flush();
|
|
expect(callback.mostRecentCall.args[0]).toEqual(cc);
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
expect(cc.number).toEqual('9876');
|
|
});
|
|
|
|
|
|
it("should update resource", function() {
|
|
$httpBackend.expect('POST', '/CreditCard/123', '{"id":{"key":123},"name":"misko"}').
|
|
respond({id: {key: 123}, name: 'rama'});
|
|
|
|
var cc = CreditCard.save({id: {key: 123}, name: 'misko'}, callback);
|
|
expect(cc).toEqualData({id:{key:123}, name:'misko'});
|
|
expect(callback).not.toHaveBeenCalled();
|
|
$httpBackend.flush();
|
|
});
|
|
|
|
|
|
it("should query resource", function() {
|
|
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
|
|
|
var ccs = CreditCard.query({key: 'value'}, callback);
|
|
expect(ccs).toEqualData([]);
|
|
expect(callback).not.toHaveBeenCalled();
|
|
|
|
$httpBackend.flush();
|
|
expect(ccs).toEqualData([{id:1}, {id:2}]);
|
|
expect(callback.mostRecentCall.args[0]).toEqual(ccs);
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
});
|
|
|
|
|
|
it("should have all arguments optional", function() {
|
|
$httpBackend.expect('GET', '/CreditCard').respond([{id:1}]);
|
|
|
|
var log = '';
|
|
var ccs = CreditCard.query(function() { log += 'cb;'; });
|
|
|
|
$httpBackend.flush();
|
|
expect(ccs).toEqualData([{id:1}]);
|
|
expect(log).toEqual('cb;');
|
|
});
|
|
|
|
|
|
it('should delete resource and call callback', function() {
|
|
$httpBackend.expect('DELETE', '/CreditCard/123').respond({});
|
|
CreditCard.remove({id:123}, callback);
|
|
expect(callback).not.toHaveBeenCalled();
|
|
|
|
$httpBackend.flush();
|
|
expect(callback.mostRecentCall.args[0]).toEqualData({});
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
|
|
callback.reset();
|
|
$httpBackend.expect('DELETE', '/CreditCard/333').respond(204, null);
|
|
CreditCard.remove({id:333}, callback);
|
|
expect(callback).not.toHaveBeenCalled();
|
|
|
|
$httpBackend.flush();
|
|
expect(callback.mostRecentCall.args[0]).toEqualData({});
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
});
|
|
|
|
|
|
it('should post charge verb', function() {
|
|
$httpBackend.expect('POST', '/CreditCard/123!charge?amount=10', '{"auth":"abc"}').respond({success: 'ok'});
|
|
CreditCard.charge({id:123, amount:10}, {auth:'abc'}, callback);
|
|
});
|
|
|
|
|
|
it('should post charge verb on instance', function() {
|
|
$httpBackend.expect('POST', '/CreditCard/123!charge?amount=10',
|
|
'{"id":{"key":123},"name":"misko"}').respond({success: 'ok'});
|
|
|
|
var card = new CreditCard({id:{key:123}, name:'misko'});
|
|
card.$charge({amount:10}, callback);
|
|
});
|
|
|
|
|
|
it("should patch a resource", function() {
|
|
$httpBackend.expectPATCH('/CreditCard/123', '{"name":"igor"}').
|
|
respond({id: 123, name: 'rama'});
|
|
|
|
var card = CreditCard.patch({id: 123}, {name: 'igor'}, callback);
|
|
|
|
expect(card).toEqualData({name: 'igor'});
|
|
expect(callback).not.toHaveBeenCalled();
|
|
$httpBackend.flush();
|
|
expect(callback).toHaveBeenCalled();
|
|
expect(card).toEqualData({id: 123, name: 'rama'});
|
|
});
|
|
|
|
|
|
it('should create on save', function() {
|
|
$httpBackend.expect('POST', '/CreditCard', '{"name":"misko"}').respond({id: 123}, {header1: 'a'});
|
|
|
|
var cc = new CreditCard();
|
|
expect(cc.$get).toBeDefined();
|
|
expect(cc.$query).toBeDefined();
|
|
expect(cc.$remove).toBeDefined();
|
|
expect(cc.$save).toBeDefined();
|
|
|
|
cc.name = 'misko';
|
|
cc.$save(callback);
|
|
expect(cc).toEqualData({name:'misko'});
|
|
|
|
$httpBackend.flush();
|
|
expect(cc).toEqualData({id:123});
|
|
expect(callback.mostRecentCall.args[0]).toEqual(cc);
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({header1: 'a'});
|
|
});
|
|
|
|
|
|
it('should not mutate the resource object if response contains no body', function() {
|
|
var data = {id:{key:123}, number:'9876'};
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond(data);
|
|
|
|
var cc = CreditCard.get({id:123});
|
|
$httpBackend.flush();
|
|
expect(cc instanceof CreditCard).toBe(true);
|
|
|
|
$httpBackend.expect('POST', '/CreditCard/123', angular.toJson(data)).respond('');
|
|
var idBefore = cc.id;
|
|
|
|
cc.$save();
|
|
$httpBackend.flush();
|
|
expect(idBefore).toEqual(cc.id);
|
|
});
|
|
|
|
|
|
it('should bind default parameters', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123.visa?minimum=0.05').respond({id: 123});
|
|
var Visa = CreditCard.bind({verb:'.visa', minimum:0.05});
|
|
var visa = Visa.get({id:123});
|
|
$httpBackend.flush();
|
|
expect(visa).toEqualData({id:123});
|
|
});
|
|
|
|
|
|
it('should support dynamic default parameters (global)', function() {
|
|
var currentGroup = 'students',
|
|
Person = $resource('/Person/:group/:id', { group: function() { return currentGroup; }});
|
|
|
|
|
|
$httpBackend.expect('GET', '/Person/students/fedor').respond({id: 'fedor', email: 'f@f.com'});
|
|
|
|
var fedor = Person.get({id: 'fedor'});
|
|
$httpBackend.flush();
|
|
|
|
expect(fedor).toEqualData({id: 'fedor', email: 'f@f.com'});
|
|
});
|
|
|
|
|
|
it('should support dynamic default parameters (action specific)', function() {
|
|
var currentGroup = 'students',
|
|
Person = $resource('/Person/:group/:id', {}, {
|
|
fetch: {method: 'GET', params: {group: function() { return currentGroup; }}}
|
|
});
|
|
|
|
$httpBackend.expect('GET', '/Person/students/fedor').respond({id: 'fedor', email: 'f@f.com'});
|
|
|
|
var fedor = Person.fetch({id: 'fedor'});
|
|
$httpBackend.flush();
|
|
|
|
expect(fedor).toEqualData({id: 'fedor', email: 'f@f.com'});
|
|
});
|
|
|
|
|
|
it('should exercise full stack', function() {
|
|
var Person = $resource('/Person/:id');
|
|
|
|
$httpBackend.expect('GET', '/Person/123').respond('\n{\n"name":\n"misko"\n}\n');
|
|
var person = Person.get({id:123});
|
|
$httpBackend.flush();
|
|
expect(person.name).toEqual('misko');
|
|
});
|
|
|
|
|
|
describe('promise api', function() {
|
|
|
|
var $rootScope;
|
|
|
|
|
|
beforeEach(inject(function(_$rootScope_) {
|
|
$rootScope = _$rootScope_;
|
|
}));
|
|
|
|
|
|
describe('single resource', function() {
|
|
|
|
it('should add $promise to the result object', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
var cc = CreditCard.get({id: 123});
|
|
|
|
cc.$promise.then(callback);
|
|
expect(callback).not.toHaveBeenCalled();
|
|
|
|
$httpBackend.flush();
|
|
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(callback.mostRecentCall.args[0]).toBe(cc);
|
|
});
|
|
|
|
|
|
it('should keep $promise around after resolution', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
var cc = CreditCard.get({id: 123});
|
|
|
|
cc.$promise.then(callback);
|
|
$httpBackend.flush();
|
|
|
|
callback.reset();
|
|
|
|
cc.$promise.then(callback);
|
|
$rootScope.$apply(); //flush async queue
|
|
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
|
|
it('should keep the original promise after instance action', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
$httpBackend.expect('POST', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
|
|
var cc = CreditCard.get({id: 123});
|
|
var originalPromise = cc.$promise;
|
|
|
|
cc.number = '666';
|
|
cc.$save({id: 123});
|
|
|
|
expect(cc.$promise).toBe(originalPromise);
|
|
});
|
|
|
|
|
|
it('should allow promise chaining', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
var cc = CreditCard.get({id: 123});
|
|
|
|
cc.$promise.then(function(value) { return 'new value'; }).then(callback);
|
|
$httpBackend.flush();
|
|
|
|
expect(callback).toHaveBeenCalledOnceWith('new value');
|
|
});
|
|
|
|
|
|
it('should allow $promise error callback registration', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond(404, 'resource not found');
|
|
var cc = CreditCard.get({id: 123});
|
|
|
|
cc.$promise.then(null, callback);
|
|
$httpBackend.flush();
|
|
|
|
var response = callback.mostRecentCall.args[0];
|
|
|
|
expect(response.data).toEqual('resource not found');
|
|
expect(response.status).toEqual(404);
|
|
});
|
|
|
|
|
|
it('should add $resolved boolean field to the result object', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
var cc = CreditCard.get({id: 123});
|
|
|
|
expect(cc.$resolved).toBe(false);
|
|
|
|
cc.$promise.then(callback);
|
|
expect(cc.$resolved).toBe(false);
|
|
|
|
$httpBackend.flush();
|
|
|
|
expect(cc.$resolved).toBe(true);
|
|
});
|
|
|
|
|
|
it('should set $resolved field to true when an error occurs', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond(404, 'resource not found');
|
|
var cc = CreditCard.get({id: 123});
|
|
|
|
cc.$promise.then(null, callback);
|
|
$httpBackend.flush();
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(cc.$resolved).toBe(true);
|
|
});
|
|
|
|
|
|
it('should keep $resolved true in all subsequent interactions', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
var cc = CreditCard.get({id: 123});
|
|
$httpBackend.flush();
|
|
expect(cc.$resolved).toBe(true);
|
|
|
|
$httpBackend.expect('POST', '/CreditCard/123').respond();
|
|
cc.$save({id: 123});
|
|
expect(cc.$resolved).toBe(true);
|
|
$httpBackend.flush();
|
|
expect(cc.$resolved).toBe(true);
|
|
});
|
|
|
|
|
|
it('should return promise from action method calls', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
|
var cc = new CreditCard({name: 'Mojo'});
|
|
|
|
expect(cc).toEqualData({name: 'Mojo'});
|
|
|
|
cc.$get({id:123}).then(callback);
|
|
|
|
$httpBackend.flush();
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(cc).toEqualData({id: 123, number: '9876'});
|
|
callback.reset();
|
|
|
|
$httpBackend.expect('POST', '/CreditCard').respond({id: 1, number: '9'});
|
|
|
|
cc.$save().then(callback);
|
|
|
|
$httpBackend.flush();
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(cc).toEqualData({id: 1, number: '9'});
|
|
});
|
|
|
|
|
|
it('should allow parsing a value from headers', function() {
|
|
// https://github.com/angular/angular.js/pull/2607#issuecomment-17759933
|
|
$httpBackend.expect('POST', '/CreditCard').respond(201, '', {'Location': '/new-id'});
|
|
|
|
var parseUrlFromHeaders = function(response) {
|
|
var resource = response.resource;
|
|
resource.url = response.headers('Location');
|
|
return resource;
|
|
};
|
|
|
|
var CreditCard = $resource('/CreditCard', {}, {
|
|
save: {
|
|
method: 'post',
|
|
interceptor: {response: parseUrlFromHeaders}
|
|
}
|
|
});
|
|
|
|
var cc = new CreditCard({name: 'Me'});
|
|
|
|
cc.$save();
|
|
$httpBackend.flush();
|
|
|
|
expect(cc.url).toBe('/new-id');
|
|
});
|
|
});
|
|
|
|
|
|
describe('resource collection', function() {
|
|
|
|
it('should add $promise to the result object', function() {
|
|
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
|
var ccs = CreditCard.query({key: 'value'});
|
|
|
|
ccs.$promise.then(callback);
|
|
expect(callback).not.toHaveBeenCalled();
|
|
|
|
$httpBackend.flush();
|
|
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(callback.mostRecentCall.args[0]).toBe(ccs);
|
|
});
|
|
|
|
|
|
it('should keep $promise around after resolution', function() {
|
|
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
|
var ccs = CreditCard.query({key: 'value'});
|
|
|
|
ccs.$promise.then(callback);
|
|
$httpBackend.flush();
|
|
|
|
callback.reset();
|
|
|
|
ccs.$promise.then(callback);
|
|
$rootScope.$apply(); //flush async queue
|
|
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
|
|
it('should allow promise chaining', function() {
|
|
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
|
var ccs = CreditCard.query({key: 'value'});
|
|
|
|
ccs.$promise.then(function(value) { return 'new value'; }).then(callback);
|
|
$httpBackend.flush();
|
|
|
|
expect(callback).toHaveBeenCalledOnceWith('new value');
|
|
});
|
|
|
|
|
|
it('should allow $promise error callback registration', function() {
|
|
$httpBackend.expect('GET', '/CreditCard?key=value').respond(404, 'resource not found');
|
|
var ccs = CreditCard.query({key: 'value'});
|
|
|
|
ccs.$promise.then(null, callback);
|
|
$httpBackend.flush();
|
|
|
|
var response = callback.mostRecentCall.args[0];
|
|
|
|
expect(response.data).toEqual('resource not found');
|
|
expect(response.status).toEqual(404);
|
|
});
|
|
|
|
|
|
it('should add $resolved boolean field to the result object', function() {
|
|
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
|
var ccs = CreditCard.query({key: 'value'}, callback);
|
|
|
|
expect(ccs.$resolved).toBe(false);
|
|
|
|
ccs.$promise.then(callback);
|
|
expect(ccs.$resolved).toBe(false);
|
|
|
|
$httpBackend.flush();
|
|
|
|
expect(ccs.$resolved).toBe(true);
|
|
});
|
|
|
|
|
|
it('should set $resolved field to true when an error occurs', function() {
|
|
$httpBackend.expect('GET', '/CreditCard?key=value').respond(404, 'resource not found');
|
|
var ccs = CreditCard.query({key: 'value'});
|
|
|
|
ccs.$promise.then(null, callback);
|
|
$httpBackend.flush();
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(ccs.$resolved).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('should allow per action response interceptor that gets full response', function() {
|
|
CreditCard = $resource('/CreditCard', {}, {
|
|
query: {
|
|
method: 'get',
|
|
isArray: true,
|
|
interceptor: {
|
|
response: function(response) {
|
|
return response;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
$httpBackend.expect('GET', '/CreditCard').respond([{id: 1}]);
|
|
|
|
var ccs = CreditCard.query();
|
|
|
|
ccs.$promise.then(callback);
|
|
|
|
$httpBackend.flush();
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
|
|
var response = callback.mostRecentCall.args[0];
|
|
expect(response.resource).toBe(ccs);
|
|
expect(response.status).toBe(200);
|
|
expect(response.config).toBeDefined();
|
|
});
|
|
|
|
|
|
it('should allow per action responseError interceptor that gets full response', function() {
|
|
CreditCard = $resource('/CreditCard', {}, {
|
|
query: {
|
|
method: 'get',
|
|
isArray: true,
|
|
interceptor: {
|
|
responseError: function(response) {
|
|
return response;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
$httpBackend.expect('GET', '/CreditCard').respond(404);
|
|
|
|
var ccs = CreditCard.query();
|
|
|
|
ccs.$promise.then(callback);
|
|
|
|
$httpBackend.flush();
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
|
|
var response = callback.mostRecentCall.args[0];
|
|
expect(response.status).toBe(404);
|
|
expect(response.config).toBeDefined();
|
|
});
|
|
});
|
|
|
|
|
|
describe('failure mode', function() {
|
|
var ERROR_CODE = 500,
|
|
ERROR_RESPONSE = 'Server Error',
|
|
errorCB;
|
|
|
|
beforeEach(function() {
|
|
errorCB = jasmine.createSpy('error').andCallFake(function(response) {
|
|
expect(response.data).toBe(ERROR_RESPONSE);
|
|
expect(response.status).toBe(ERROR_CODE);
|
|
});
|
|
});
|
|
|
|
|
|
it('should call the error callback if provided on non 2xx response', function() {
|
|
$httpBackend.expect('GET', '/CreditCard/123').respond(ERROR_CODE, ERROR_RESPONSE);
|
|
|
|
CreditCard.get({id:123}, callback, errorCB);
|
|
$httpBackend.flush();
|
|
expect(errorCB).toHaveBeenCalledOnce();
|
|
expect(callback).not.toHaveBeenCalled();
|
|
});
|
|
|
|
|
|
it('should call the error callback if provided on non 2xx response', function() {
|
|
$httpBackend.expect('GET', '/CreditCard').respond(ERROR_CODE, ERROR_RESPONSE);
|
|
|
|
CreditCard.get(callback, errorCB);
|
|
$httpBackend.flush();
|
|
expect(errorCB).toHaveBeenCalledOnce();
|
|
expect(callback).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it('should transform request/response', function() {
|
|
var Person = $resource('/Person/:id', {}, {
|
|
save: {
|
|
method: 'POST',
|
|
params: {id: '@id'},
|
|
transformRequest: function(data) {
|
|
return angular.toJson({ __id: data.id });
|
|
},
|
|
transformResponse: function(data) {
|
|
return { id: data.__id };
|
|
}
|
|
}
|
|
});
|
|
|
|
$httpBackend.expect('POST', '/Person/123', { __id: 123 }).respond({ __id: 456 });
|
|
var person = new Person({id:123});
|
|
person.$save();
|
|
$httpBackend.flush();
|
|
expect(person.id).toEqual(456);
|
|
});
|
|
|
|
describe('suffix parameter', function() {
|
|
|
|
describe('query', function() {
|
|
it('should add a suffix', function() {
|
|
$httpBackend.expect('GET', '/users.json').respond([{id: 1, name: 'user1'}]);
|
|
var UserService = $resource('/users/:id.json', {id: '@id'});
|
|
var user = UserService.query();
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData([{id: 1, name: 'user1'}]);
|
|
});
|
|
|
|
it('should not require it if not provided', function(){
|
|
$httpBackend.expect('GET', '/users.json').respond([{id: 1, name: 'user1'}]);
|
|
var UserService = $resource('/users.json');
|
|
var user = UserService.query();
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData([{id: 1, name: 'user1'}]);
|
|
});
|
|
|
|
it('should work when query parameters are supplied', function() {
|
|
$httpBackend.expect('GET', '/users.json?red=blue').respond([{id: 1, name: 'user1'}]);
|
|
var UserService = $resource('/users/:user_id.json', {user_id: '@id'});
|
|
var user = UserService.query({red: 'blue'});
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData([{id: 1, name: 'user1'}]);
|
|
});
|
|
|
|
it('should work when query parameters are supplied and the format is a resource parameter', function() {
|
|
$httpBackend.expect('GET', '/users.json?red=blue').respond([{id: 1, name: 'user1'}]);
|
|
var UserService = $resource('/users/:user_id.:format', {user_id: '@id', format: 'json'});
|
|
var user = UserService.query({red: 'blue'});
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData([{id: 1, name: 'user1'}]);
|
|
});
|
|
|
|
it('should work with the action is overriden', function(){
|
|
$httpBackend.expect('GET', '/users.json').respond([{id: 1, name: 'user1'}]);
|
|
var UserService = $resource('/users/:user_id', {user_id: '@id'}, {
|
|
query: {
|
|
method: 'GET',
|
|
url: '/users/:user_id.json',
|
|
isArray: true
|
|
}
|
|
});
|
|
var user = UserService.query();
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData([ {id: 1, name: 'user1'} ]);
|
|
});
|
|
});
|
|
|
|
describe('get', function(){
|
|
it('should add them to the id', function() {
|
|
$httpBackend.expect('GET', '/users/1.json').respond({id: 1, name: 'user1'});
|
|
var UserService = $resource('/users/:user_id.json', {user_id: '@id'});
|
|
var user = UserService.get({user_id: 1});
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData({id: 1, name: 'user1'});
|
|
});
|
|
|
|
it('should work when an id and query parameters are supplied', function() {
|
|
$httpBackend.expect('GET', '/users/1.json?red=blue').respond({id: 1, name: 'user1'});
|
|
var UserService = $resource('/users/:user_id.json', {user_id: '@id'});
|
|
var user = UserService.get({user_id: 1, red: 'blue'});
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData({id: 1, name: 'user1'});
|
|
});
|
|
|
|
it('should work when the format is a parameter', function() {
|
|
$httpBackend.expect('GET', '/users/1.json?red=blue').respond({id: 1, name: 'user1'});
|
|
var UserService = $resource('/users/:user_id.:format', {user_id: '@id', format: 'json'});
|
|
var user = UserService.get({user_id: 1, red: 'blue'});
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData({id: 1, name: 'user1'});
|
|
});
|
|
|
|
it('should work with the action is overriden', function(){
|
|
$httpBackend.expect('GET', '/users/1.json').respond({id: 1, name: 'user1'});
|
|
var UserService = $resource('/users/:user_id', {user_id: '@id'}, {
|
|
get: {
|
|
method: 'GET',
|
|
url: '/users/:user_id.json'
|
|
}
|
|
});
|
|
var user = UserService.get({user_id: 1});
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData({id: 1, name: 'user1'});
|
|
});
|
|
});
|
|
|
|
describe("save", function() {
|
|
it('should append the suffix', function() {
|
|
$httpBackend.expect('POST', '/users.json', '{"name":"user1"}').respond({id: 123, name: 'user1'});
|
|
var UserService = $resource('/users/:user_id.json', {user_id: '@id'});
|
|
var user = UserService.save({name: 'user1'}, callback);
|
|
expect(user).toEqualData({name: 'user1'});
|
|
expect(callback).not.toHaveBeenCalled();
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData({id: 123, name: 'user1'});
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(callback.mostRecentCall.args[0]).toEqual(user);
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
});
|
|
|
|
it('should append when an id is supplied', function() {
|
|
$httpBackend.expect('POST', '/users/123.json', '{"id":123,"name":"newName"}').respond({id: 123, name: 'newName'});
|
|
var UserService = $resource('/users/:user_id.json', {user_id: '@id'});
|
|
var user = UserService.save({id: 123, name: 'newName'}, callback);
|
|
expect(callback).not.toHaveBeenCalled();
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData({id: 123, name: 'newName'});
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(callback.mostRecentCall.args[0]).toEqual(user);
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
});
|
|
|
|
it('should append when an id is supplied and the format is a parameter', function() {
|
|
$httpBackend.expect('POST', '/users/123.json', '{"id":123,"name":"newName"}').respond({id: 123, name: 'newName'});
|
|
var UserService = $resource('/users/:user_id.:format', {user_id: '@id', format: 'json'});
|
|
var user = UserService.save({id: 123, name: 'newName'}, callback);
|
|
expect(callback).not.toHaveBeenCalled();
|
|
$httpBackend.flush();
|
|
expect(user).toEqualData({id: 123, name: 'newName'});
|
|
expect(callback).toHaveBeenCalledOnce();
|
|
expect(callback.mostRecentCall.args[0]).toEqual(user);
|
|
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
|
});
|
|
});
|
|
|
|
describe('escaping /. with /\\.', function() {
|
|
it('should work with query()', function() {
|
|
$httpBackend.expect('GET', '/users/.json').respond();
|
|
$resource('/users/\\.json').query();
|
|
});
|
|
it('should work with get()', function() {
|
|
$httpBackend.expect('GET', '/users/.json').respond();
|
|
$resource('/users/\\.json').get();
|
|
});
|
|
it('should work with save()', function() {
|
|
$httpBackend.expect('POST', '/users/.json').respond();
|
|
$resource('/users/\\.json').save({});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('action-level url override', function() {
|
|
|
|
it('should support overriding url template with static url', function() {
|
|
$httpBackend.expect('GET', '/override-url?type=Customer&typeId=123').respond({id: 'abc'});
|
|
var TypeItem = $resource('/:type/:typeId', {type: 'Order'}, {
|
|
get: {
|
|
method: 'GET',
|
|
params: {type: 'Customer'},
|
|
url: '/override-url'
|
|
}
|
|
});
|
|
var item = TypeItem.get({typeId: 123});
|
|
$httpBackend.flush();
|
|
expect(item).toEqualData({id: 'abc'});
|
|
});
|
|
|
|
|
|
it('should support overriding url template with a new template ending in param', function() {
|
|
// url parameter in action, parameter ending the string
|
|
$httpBackend.expect('GET', '/Customer/123').respond({id: 'abc'});
|
|
var TypeItem = $resource('/foo/:type', {type: 'Order'}, {
|
|
get: {
|
|
method: 'GET',
|
|
params: {type: 'Customer'},
|
|
url: '/:type/:typeId'
|
|
}
|
|
});
|
|
var item = TypeItem.get({typeId: 123});
|
|
$httpBackend.flush();
|
|
expect(item).toEqualData({id: 'abc'});
|
|
|
|
// url parameter in action, parameter not ending the string
|
|
$httpBackend.expect('GET', '/Customer/123/pay').respond({id: 'abc'});
|
|
var TypeItem = $resource('/foo/:type', {type: 'Order'}, {
|
|
get: {
|
|
method: 'GET',
|
|
params: {type: 'Customer'},
|
|
url: '/:type/:typeId/pay'
|
|
}
|
|
});
|
|
var item = TypeItem.get({typeId: 123});
|
|
$httpBackend.flush();
|
|
expect(item).toEqualData({id: 'abc'});
|
|
});
|
|
|
|
|
|
it('should support overriding url template with a new template ending in string', function() {
|
|
$httpBackend.expect('GET', '/Customer/123/pay').respond({id: 'abc'});
|
|
var TypeItem = $resource('/foo/:type', {type: 'Order'}, {
|
|
get: {
|
|
method: 'GET',
|
|
params: {type: 'Customer'},
|
|
url: '/:type/:typeId/pay'
|
|
}
|
|
});
|
|
var item = TypeItem.get({typeId: 123});
|
|
$httpBackend.flush();
|
|
expect(item).toEqualData({id: 'abc'});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('resource', function() {
|
|
var $httpBackend, $resource;
|
|
|
|
beforeEach(module(function($exceptionHandlerProvider) {
|
|
$exceptionHandlerProvider.mode('log');
|
|
}));
|
|
|
|
beforeEach(module('ngResource'));
|
|
|
|
beforeEach(inject(function($injector) {
|
|
$httpBackend = $injector.get('$httpBackend');
|
|
$resource = $injector.get('$resource');
|
|
}));
|
|
|
|
|
|
it('should fail if action expects an object but response is an array', function() {
|
|
var successSpy = jasmine.createSpy('successSpy');
|
|
var failureSpy = jasmine.createSpy('failureSpy');
|
|
|
|
$httpBackend.expect('GET', '/Customer/123').respond({id: 'abc'});
|
|
|
|
$resource('/Customer/123').query()
|
|
.$promise.then(successSpy, function(e) { failureSpy(e.message); });
|
|
$httpBackend.flush();
|
|
|
|
expect(successSpy).not.toHaveBeenCalled();
|
|
expect(failureSpy).toHaveBeenCalledWith(
|
|
'[ngResource:badcfg] Error in resource configuration. Expected response to contain an array but got an object');
|
|
});
|
|
|
|
it('should fail if action expects an array but response is an object', function() {
|
|
var successSpy = jasmine.createSpy('successSpy');
|
|
var failureSpy = jasmine.createSpy('failureSpy');
|
|
|
|
$httpBackend.expect('GET', '/Customer/123').respond([1,2,3]);
|
|
|
|
$resource('/Customer/123').get()
|
|
.$promise.then(successSpy, function(e) { failureSpy(e.message); });
|
|
$httpBackend.flush();
|
|
|
|
expect(successSpy).not.toHaveBeenCalled();
|
|
expect(failureSpy).toHaveBeenCalledWith(
|
|
'[ngResource:badcfg] Error in resource configuration. Expected response to contain an object but got an array');
|
|
});
|
|
|
|
|
|
}); |