diff --git a/.gitignore b/.gitignore index 79787e35..e8a68eb0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ results lib node_modules components +coverage bower_components npm-debug.log .idea diff --git a/.travis.yml b/.travis.yml index ad563755..20b0f596 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,11 @@ language: node_js node_js: - - "0.10" + - "7.0" before_script: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - 'npm install -g bower grunt-cli' - 'bower install --config.interactive=false' + +script: grunt travis diff --git a/CHANGELOG.md b/CHANGELOG.md index ef816d33..b768c53e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,36 @@ -# Unreleased + +## 1.6.1 (2017-01-06) +### Bug fixes -# 1.6.0 (2016-12-25) +* fix(copy) Collections are now copied/cloned properly ([c92b138](https://github.com/mgonto/restangular/commit/c92b138)) +* fix(copy) Copying collections now correctly sets route, fromServer and parent on the copy ([7fd668b](https://github.com/mgonto/restangular/commit/7fd668b)) +* fix(elementTransformer) matchTransformer now doesn't throw if route is undefined ([fb242ae](https://github.com/mgonto/restangular/commit/fb242ae)) + +### Docs + +* chore(docs): update contribution guidelines ([c49ca45](https://github.com/mgonto/restangular/commit/c49ca45)) +* fix(docs): add link to david-dm.org from badge ([2bfb745](https://github.com/mgonto/restangular/commit/2bfb745)) +* chore(docs) Add new example production site ([2596035](https://github.com/mgonto/restangular/commit/2596035)) +* chore(docs) Add note about pull requests and github preview tab ([6883075](https://github.com/mgonto/restangular/commit/6883075)) +* chore(docs) Apply automatic formatting to code and spec ([bc16122](https://github.com/mgonto/restangular/commit/bc16122)) +* chore(docs) Reformat changelog, add unreleased section ([8bfa685](https://github.com/mgonto/restangular/commit/8bfa685)) +* chore(docs) Update issue guidelines to include StackOverflow as source for solutions to problems ([34b0e9a](https://github.com/mgonto/restangular/commit/34b0e9a)) +* chore(docs) Update link to demo Plunker, rephrase ([7c30615](https://github.com/mgonto/restangular/commit/7c30615)) +* chore(test) fix jshint errors in spec file ([1a988cb](https://github.com/mgonto/restangular/commit/1a988cb)) +* feat(docs) Add FAQ about cancelling request ([8552c51](https://github.com/mgonto/restangular/commit/8552c51)), closes [#926](https://github.com/mgonto/restangular/issues/926) [#1145](https://github.com/mgonto/restangular/issues/1145) [#1377](https://github.com/mgonto/restangular/issues/1377) [#1391](https://github.com/mgonto/restangular/issues/1391) + +### Other + +* chore(changelog): upgrade package and config ([58caacd](https://github.com/mgonto/restangular/commit/58caacd)) +* chore(dependencies): Update lodash version to ~4.17.0 as in unit tests ([e0b68a0](https://github.com/mgonto/restangular/commit/e0b68a0)) +* chore(deps): upgrade dev dependencies, fix tests (#1450) ([b583197](https://github.com/mgonto/restangular/commit/b583197)), closes [#1450](https://github.com/mgonto/restangular/issues/1450) +* chore(travis): change travis script and include coveralls ([ca9856a](https://github.com/mgonto/restangular/commit/ca9856a)) +* test(coverage): add coverage and coveralls.io integration ([fdd5de6](https://github.com/mgonto/restangular/commit/fdd5de6)) +* Update dist files ([7c245a2](https://github.com/mgonto/restangular/commit/7c245a2)) + + +## 1.6.0 (2016-12-25) * Url now supports unescaped suffix (0350bcd) * Added Restangular Plunkr example (c4ef002) @@ -23,11 +52,13 @@ * Added context/explanation of when to use JSONP. (fec9b27) * Add regexp matching for route to element transformers (#1430) (de8f561) -# 1.5.2 (2016-02-15) + +## 1.5.2 (2016-02-15) * Change \_.contains to \_.includes for compatability with lodash >= 4.0 -# 1.5.1 (2015-04-03) + +## 1.5.1 (2015-04-03) * Release 1.5.0 * Updated zip @@ -39,7 +70,8 @@ * Update README.md * Update README.md -# 1.5.0 (2015-04-03) + +## 1.5.0 (2015-04-03) * Tons of bug fixes * Upgraded Lodash to 1.3.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9722aac4..59451f29 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,42 +1,67 @@ -#Issues +# Issues -## Opening an Issue +Read the [issue guidlines](.github/ISSUE_TEMPLATE.md) before opening an issue! -To open an issue, please keep in mind a few important things. First, take a look at the README docs and make sure that your question isn't already answered in the documentation. In particular, review [the configuration methods](https://github.com/mgonto/restangular#configuring-restangular) and [methods description](https://github.com/mgonto/restangular#methods-description). Then make sure you search the issues list to see if there's already an issue open that solves your problem. Then, once you've determined that your issue isn't a duplicate, here a couple guidelines to opening an issue that will be addressed easily and quickly: +# How to submit PRs -- Please make sure your issue is written in a clear and succint manner, so that all the details of your issue are easy to understand and fully explained. Also, please make sure enclose your code in [fenced blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/) for readability. -- Make sure your issue includes a live Plunker (fork [this Plunker](http://plnkr.co/edit/26Heuv5F6hUgxpNWNTee?p=info)), and all relevant code samples (as well as information about server responses, if relevant) +## Install env -# PRs - -##Install env - -In order to contribute just git clone the repository and then run: +In order to contribute just create your own [fork](https://help.github.com/articles/fork-a-repo/) +of the repository and then run: ``` -git clone git@github.com:mgonto/restangular.git +git clone git@github.com:/restangular.git cd restangular npm install grunt-cli --global npm install ``` -Be sure to have PhantomJS installed as Karma tests use it. Otherwise, in Mac just run +## Create a branch -``` -brew install phantomjs -``` +Create a branch for your code changes -All changes must be done in src/restangular.js +`git checkout -b my_awesome_fix` -##Branching +## Write tests -Please submit a Pull Request or create issues for anything you want :). If your code is super small, nobody will blame -you for doing it from master to master. Otherwise, please try to create a branch `features/[name of your awesome feature]`. +When making changes to the code, please always **write a test case** for +your topic before making the change. Watch the test fail, then +implement the change and watch the test succeed. -##Testing and styling +Tests are run with `grunt test`. -Before submitting any PR, you should run `grunt test` to validate your didn't break anything. If you just added a new -feature, please also add tests for it. And when you're done with your code, run `grunt jshint` to check +## Keep the style + +When you're done with your code, run `grunt jshint` to check if you code follow the same simple coding design as the rest of the project. +Consider integrating `jshint` in your editor to get real time feedback on your +style as you're coding. + +## Commit message format + +Please write your commit messages in the [angular conventional changelog](https://github.com/conventional-changelog/conventional-changelog-angular/blob/master/convention.md) format. This will help +us to keep a decent [CHANGELOG](CHANGELOG.md) with minimum effort. Check previous commit +messages for examples. + +## Update docs + +If your code change includes new features, please include an update to the [README.md](README.md) +explaining your feature. + +**Don't**, however, generate distribution files (the files in [dist](dist)). This will be done +on a regular basis by maintainers as PRs are merged. + +## Squash commits + +Please consider squasing the commits in your topic branch into a single commit including +all changes needed. This will make the PR cleaner and the change history more easy to follow +after the PR has been merged. Also, the CHANGELOG will make more sense. + +Look [here](https://ariejan.net/2011/07/05/git-squash-your-latests-commits-into-one/) and +[here](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits) for how to. + +## Sumbit the PR + +Now you're ready to [open a PR](https://help.github.com/articles/creating-a-pull-request-from-a-fork/). Thanks! diff --git a/Gruntfile.js b/Gruntfile.js index d06566eb..13420589 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -85,9 +85,45 @@ module.exports = function(grunt) { autoWatch: true } }, - changelog: { + coveralls: { + // Options relevant to all targets options: { - dest: 'CHANGELOG.md' + // When true, grunt-coveralls will only print a warning rather than + // an error, to prevent CI builds from failing unnecessarily (e.g. if + // coveralls.io is down). Optional, defaults to false. + force: false + }, + + restangular: { + // LCOV coverage file (can be string, glob or array) + src: 'coverage/**/lcov.info', + options: { + // Any options for just this target + } + }, + }, + conventionalChangelog: { + options: { + changelogOpts: { + // conventional-changelog options go here + outputUnreleased: true, + // preset: 'angular' + }, + context: { + // context goes here + }, + gitRawCommitsOpts: { + // git-raw-commits options go here + }, + parserOpts: { + // conventional-commits-parser options go here + }, + writerOpts: { + // conventional-changelog-writer options go here + } + }, + release: { + src: 'CHANGELOG.md' } } }); @@ -111,6 +147,8 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-zip'); + grunt.loadNpmTasks('grunt-coveralls'); + // Default task. grunt.registerTask('default', ['build']); @@ -122,7 +160,9 @@ module.exports = function(grunt) { grunt.registerTask('test-debug', ['karma:debug']); - grunt.registerTask('travis', ['karma:travis', 'karma:travisUnderscore']); + grunt.registerTask('travis', ['karma:travis', 'karma:travisUnderscore', 'coveralls']); + + grunt.registerTask('changelog', ['conventionalChangelog']); // Provides the "bump" task. grunt.registerTask('bump', 'Increment version number', function() { diff --git a/MAINTENANCE.md b/MAINTENANCE.md new file mode 100644 index 00000000..6d219897 --- /dev/null +++ b/MAINTENANCE.md @@ -0,0 +1,45 @@ +# Restangular maintenance policy + +Restangular follows the [Semantic Versioning](http://semver.org/) for its releases: +`(Major).(Minor).(Patch)`. + +- **Patch number**: When backwards compatible bug fixes are introduced that fix + incorrect behavior. +- **Minor version**: When new, backwards compatible functionality is introduced + to the public API or a minor feature is introduced, or when a set of smaller + features is rolled out. +- **Major version**: Whenever there is something significant or any backwards + incompatible changes are introduced to the public API. + +The current stable release will receive security patches and bug fixes +(eg. `1.6.0` -> `1.6.1`). Feature releases will mark the next supported stable +release where the minor version is increased numerically by increments of one +(eg. `1.6.3 -> 1.7.0`). + +We encourage everyone to run the latest stable release. + +# Notes for maintainers + +Following are a set of guidelines and checklists for maintaining the +[main repository](https://github.com/mgonto/restangular). + +## Keeping master usable + +In order for developers to use the bleeding edge master version in their projects, +we'll need to keep the dist files up to date. After merging PRs that include mentionable +changes, please update the dist files `grunt build` and the CHANGELOG `grunt changelog`. +The CHANGELOG will have a section called `Unreleased` for changes not yet included in +any release. This is the place for + +## Releasing + +Follow this checklist for publishing a new release. + +- [ ] Bump version `grunt bump --type=patch/minor/major` +- [ ] Create changelog `grunt changelog` (open it in editor and make sure its sensible) +- [ ] Make dist files `grunt build` +- [ ] Commit changes `git add dist package.json CHANGELOG.md` and `git commit -m "chore(release): release 1.6.1"` +- [ ] Tag release `git tag -a -m "Version 1.6.1" 1.6.1` +- [ ] Push everything `git push` and `git push --tags` +- [ ] Publish to NPM `npm publish` +- [ ] Create a [new release](https://github.com/mgonto/restangular/releases) on GitHub, entering the version changelog as body diff --git a/README.md b/README.md index 359bf272..aea15823 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,23 @@ -#Restangular +# Restangular [![Build Status](https://travis-ci.org/mgonto/restangular.svg?branch=master)](https://travis-ci.org/mgonto/restangular) +[![Coverage Status](https://coveralls.io/repos/github/mgonto/restangular/badge.svg?branch=master)](https://coveralls.io/github/mgonto/restangular?branch=master) +[![David](https://img.shields.io/david/dev/mgonto/restangular.svg)](https://david-dm.org/mgonto/restangular/?type=dev) +[![Known Vulnerabilities](https://snyk.io/test/github/mgonto/restangular/badge.svg)](https://snyk.io/test/github/mgonto/restangular) [![PayPayl donate button](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=martin%40gon%2eto&lc=US&item_name=Martin%20Gontovnikas¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted "Donate once-off to this project using Paypal") [![Donate on Gittip](http://img.shields.io/gittip/mgonto.svg)](https://www.gittip.com/mgonto/) -Restangular is an AngularJS service that simplifies common GET, POST, DELETE, and UPDATE requests with a minimum of client code. -It's a perfect fit for any WebApp that consumes data from a RESTful API. +Restangular is an AngularJS service that simplifies common GET, POST, DELETE, and UPDATE requests with a minimum of client code. It's a perfect fit for any WebApp that consumes data from a RESTful API. -Try the [live demo on plunkr](http://plnkr.co/edit/8qrGeE?p=preview). It uses the same example as the official [Angular Javascript Project](http://angularjs.org/#wire-up-a-backend), but with Restangular! +**Note This version of Restangular [only supports Angular 1](#supported-angular-versions). For an Angular 2+ version of Restangular, check out [ngx-restangular](https://github.com/2muchcoffeecom/ngx-restangular).** It's a separate project with different maintainers, so issues regarding ngx-restangular should be reported [over there](https://github.com/2muchcoffeecom/ngx-restangular/issues) :wink: -Watch [a video introduction of a talk I gave at Devoxx France](http://www.parleys.com/play/535a189ee4b0c5ba17d43455/chapter1/about) about Restangular. +Learn Restangular! Try the [live demo on plunkr](http://plnkr.co/edit/8qrGeE?p=preview). It uses the same example as the official [Angular Javascript Project](http://angularjs.org/#wire-up-a-backend), but with Restangular! Or watch [a video introduction of a talk I gave at Devoxx France](http://www.parleys.com/play/535a189ee4b0c5ba17d43455/chapter1/about) about Restangular. -#Table of contents + +# Table of contents - [Restangular](#restangular) - [Differences with $resource](#differences-with-resource) @@ -135,7 +138,7 @@ $scope.user.one('messages', 123).one('from', 123).getList('unread'); **[Back to top](#table-of-contents)** -#How do I add this to my project? +## How do I add this to my project? You can download this by: @@ -152,7 +155,7 @@ You can download this by: **[Back to top](#table-of-contents)** -#Dependencies +## Dependencies Restangular depends on Angular and Lodash (or Underscore). @@ -165,11 +168,12 @@ Each time, there're more Production WebApps using `Restangular`. If your webapp * **Life360** is using Restangular to build the WebApp version of their platform * **Thomson Reuters** is using Restangular for the new Webapp they've built * **Quran.com** is using Restangular for their alpha/beta app and soon to be main site +* **[Worldcampus.co](http://www.worldcampus.co)** is using Restangular for their beta international students social network. * **[ENTSO-E Transparency Platform](https://transparency.entsoe.eu)** **[Back to top](#table-of-contents)** -#Starter Guide +# Starter Guide ## Quick Configuration (For Lazy Readers) This is all you need to start using all the basic Restangular features. diff --git a/dist/restangular.js b/dist/restangular.js index f014fd39..b59b25f9 100644 --- a/dist/restangular.js +++ b/dist/restangular.js @@ -1,6 +1,6 @@ /** * Restful Resources service for AngularJS apps - * @version v1.6.0 - 2017-01-05 * @link https://github.com/mgonto/restangular + * @version v1.6.1 - 2017-01-06 * @link https://github.com/mgonto/restangular * @author Martin Gontovnikas * @license MIT License, http://www.opensource.org/licenses/MIT */(function(root, factory) { diff --git a/dist/restangular.min.js b/dist/restangular.min.js index 67d3cfc0..729f8aad 100644 --- a/dist/restangular.min.js +++ b/dist/restangular.min.js @@ -1,6 +1,6 @@ /** * Restful Resources service for AngularJS apps - * @version v1.6.0 - 2017-01-05 * @link https://github.com/mgonto/restangular + * @version v1.6.1 - 2017-01-06 * @link https://github.com/mgonto/restangular * @author Martin Gontovnikas * @license MIT License, http://www.opensource.org/licenses/MIT */!function(a,b){"function"==typeof define&&define.amd?define(["lodash","angular"],b):"object"==typeof module&&module.exports?module.exports=b(require("lodash"),require("angular")):b(a._,a.angular)}(this,function(a,b){var c=b.module("restangular",[]);return c.provider("Restangular",function(){var c={};c.init=function(c,d){function e(b,c,d,e){var f={};return a.each(a.keys(e),function(g){var h=e[g];h.params=a.extend({},h.params,b.defaultRequestParams[h.method.toLowerCase()]),a.isEmpty(h.params)&&delete h.params,b.isSafe(h.method)?f[g]=function(){return c(a.extend(h,{url:d}))}:f[g]=function(b){return c(a.extend(h,{url:d,data:b}))}}),f}c.configuration=d;var f=["get","head","options","trace","getlist"];d.isSafe=function(b){return a.includes(f,b.toLowerCase())};var g=/^https?:\/\//i;d.isAbsoluteUrl=function(b){return a.isUndefined(d.absoluteUrl)||a.isNull(d.absoluteUrl)?b&&g.test(b):d.absoluteUrl},d.absoluteUrl=!!a.isUndefined(d.absoluteUrl)||d.absoluteUrl,c.setSelfLinkAbsoluteUrl=function(a){d.absoluteUrl=a},d.baseUrl=a.isUndefined(d.baseUrl)?"":d.baseUrl,c.setBaseUrl=function(a){return d.baseUrl=/\/$/.test(a)?a.substring(0,a.length-1):a,this},d.extraFields=d.extraFields||[],c.setExtraFields=function(a){return d.extraFields=a,this},d.defaultHttpFields=d.defaultHttpFields||{},c.setDefaultHttpFields=function(a){return d.defaultHttpFields=a,this},d.plainByDefault=d.plainByDefault||!1,c.setPlainByDefault=function(a){return d.plainByDefault=a===!0,this},d.withHttpValues=function(b,c){return a.defaults(c,b,d.defaultHttpFields)},d.encodeIds=!!a.isUndefined(d.encodeIds)||d.encodeIds,c.setEncodeIds=function(a){d.encodeIds=a},d.defaultRequestParams=d.defaultRequestParams||{get:{},post:{},put:{},remove:{},common:{}},c.setDefaultRequestParams=function(b,c){var e=[],f=c||b;return a.isUndefined(c)?e.push("common"):a.isArray(b)?e=b:e.push(b),a.each(e,function(a){d.defaultRequestParams[a]=f}),this},c.requestParams=d.defaultRequestParams,d.defaultHeaders=d.defaultHeaders||{},c.setDefaultHeaders=function(a){return d.defaultHeaders=a,c.defaultHeaders=d.defaultHeaders,this},c.defaultHeaders=d.defaultHeaders,d.methodOverriders=d.methodOverriders||[],c.setMethodOverriders=function(b){var c=a.extend([],b);return d.isOverridenMethod("delete",c)&&c.push("remove"),d.methodOverriders=c,this},d.jsonp=!a.isUndefined(d.jsonp)&&d.jsonp,c.setJsonp=function(a){d.jsonp=a},d.isOverridenMethod=function(b,c){var e=c||d.methodOverriders;return!a.isUndefined(a.find(e,function(a){return a.toLowerCase()===b.toLowerCase()}))},d.urlCreator=d.urlCreator||"path",c.setUrlCreator=function(b){if(!a.has(d.urlCreatorFactory,b))throw new Error("URL Path selected isn't valid");return d.urlCreator=b,this},d.restangularFields=d.restangularFields||{id:"id",route:"route",parentResource:"parentResource",restangularCollection:"restangularCollection",cannonicalId:"__cannonicalId",etag:"restangularEtag",selfLink:"href",get:"get",getList:"getList",put:"put",post:"post",remove:"remove",head:"head",trace:"trace",options:"options",patch:"patch",getRestangularUrl:"getRestangularUrl",getRequestedUrl:"getRequestedUrl",putElement:"putElement",addRestangularMethod:"addRestangularMethod",getParentList:"getParentList",clone:"clone",ids:"ids",httpConfig:"_$httpConfig",reqParams:"reqParams",one:"one",all:"all",several:"several",oneUrl:"oneUrl",allUrl:"allUrl",customPUT:"customPUT",customPATCH:"customPATCH",customPOST:"customPOST",customDELETE:"customDELETE",customGET:"customGET",customGETLIST:"customGETLIST",customOperation:"customOperation",doPUT:"doPUT",doPATCH:"doPATCH",doPOST:"doPOST",doDELETE:"doDELETE",doGET:"doGET",doGETLIST:"doGETLIST",fromServer:"fromServer",withConfig:"withConfig",withHttpConfig:"withHttpConfig",singleOne:"singleOne",plain:"plain",save:"save",restangularized:"restangularized"},c.setRestangularFields=function(b){return d.restangularFields=a.extend(d.restangularFields,b),this},d.isRestangularized=function(a){return!!a[d.restangularFields.restangularized]},d.setFieldToElem=function(b,c,d){var e=b.split("."),f=c;return a.each(a.initial(e),function(a){f[a]={},f=f[a]}),f[a.last(e)]=d,this},d.getFieldFromElem=function(c,d){var e=c.split("."),f=d;return a.each(e,function(a){f&&(f=f[a])}),b.copy(f)},d.setIdToElem=function(a,b){return d.setFieldToElem(d.restangularFields.id,a,b),this},d.getIdFromElem=function(a){return d.getFieldFromElem(d.restangularFields.id,a)},d.isValidId=function(b){return""!==b&&!a.isUndefined(b)&&!a.isNull(b)},d.setUrlToElem=function(a,b){return d.setFieldToElem(d.restangularFields.selfLink,a,b),this},d.getUrlFromElem=function(a){return d.getFieldFromElem(d.restangularFields.selfLink,a)},d.useCannonicalId=!a.isUndefined(d.useCannonicalId)&&d.useCannonicalId,c.setUseCannonicalId=function(a){return d.useCannonicalId=a,this},d.getCannonicalIdFromElem=function(a){var b=a[d.restangularFields.cannonicalId],c=d.isValidId(b)?b:d.getIdFromElem(a);return c},d.responseInterceptors=d.responseInterceptors||[],d.defaultResponseInterceptor=function(a){return a},d.responseExtractor=function(c,e,f,g,h,i){var j=b.copy(d.responseInterceptors);j.push(d.defaultResponseInterceptor);var k=c;return a.each(j,function(a){k=a(k,e,f,g,h,i)}),k},c.addResponseInterceptor=function(a){return d.responseInterceptors.push(a),this},d.errorInterceptors=d.errorInterceptors||[],c.addErrorInterceptor=function(a){return d.errorInterceptors.push(a),this},c.setResponseInterceptor=c.addResponseInterceptor,c.setResponseExtractor=c.addResponseInterceptor,c.setErrorInterceptor=c.addErrorInterceptor,d.requestInterceptors=d.requestInterceptors||[],d.defaultInterceptor=function(a,b,c,d,e,f,g){return{element:a,headers:e,params:f,httpConfig:g}},d.fullRequestInterceptor=function(c,e,f,g,h,i,j){var k=b.copy(d.requestInterceptors),l=d.defaultInterceptor(c,e,f,g,h,i,j);return a.reduce(k,function(b,c){return a.extend(b,c(b.element,e,f,g,b.headers,b.params,b.httpConfig))},l)},c.addRequestInterceptor=function(a){return d.requestInterceptors.push(function(b,c,d,e,f,g,h){return{headers:f,params:g,element:a(b,c,d,e),httpConfig:h}}),this},c.setRequestInterceptor=c.addRequestInterceptor,c.addFullRequestInterceptor=function(a){return d.requestInterceptors.push(a),this},c.setFullRequestInterceptor=c.addFullRequestInterceptor,d.onBeforeElemRestangularized=d.onBeforeElemRestangularized||function(a){return a},c.setOnBeforeElemRestangularized=function(a){return d.onBeforeElemRestangularized=a,this},c.setRestangularizePromiseInterceptor=function(a){return d.restangularizePromiseInterceptor=a,this},d.onElemRestangularized=d.onElemRestangularized||function(a){return a},c.setOnElemRestangularized=function(a){return d.onElemRestangularized=a,this},d.shouldSaveParent=d.shouldSaveParent||function(){return!0},c.setParentless=function(b){return a.isArray(b)?d.shouldSaveParent=function(c){return!a.includes(b,c)}:a.isBoolean(b)&&(d.shouldSaveParent=function(){return!b}),this},d.suffix=a.isUndefined(d.suffix)?null:d.suffix,c.setRequestSuffix=function(a){return d.suffix=a,this},d.transformers=d.transformers||{},d.matchTransformers=d.matchTransformers||[],c.addElementTransformer=function(b,e,f){var g=null,h=null;2===arguments.length?h=e:(h=f,g=e);var i=function(b,c){return a.isNull(g)||b===g?h(c):c};return a.isRegExp(b)?d.matchTransformers.push({regexp:b,transformer:i}):(d.transformers[b]||(d.transformers[b]=[]),d.transformers[b].push(i)),c},c.extendCollection=function(a,b){return c.addElementTransformer(a,!0,b)},c.extendModel=function(a,b){return c.addElementTransformer(a,!1,b)},d.transformElem=function(b,c,e,f,g){if(!g&&!d.transformLocalElements&&!b[d.restangularFields.fromServer])return b;var h=b,i=d.matchTransformers;i&&a.each(i,function(a){a.regexp.test(e)&&(h=a.transformer(c,h))});var j=d.transformers[e];return j&&a.each(j,function(a){h=a(c,h)}),d.onElemRestangularized(h,c,e,f)},d.transformLocalElements=!a.isUndefined(d.transformLocalElements)&&d.transformLocalElements,c.setTransformOnlyServerElements=function(a){d.transformLocalElements=!a},d.fullResponse=!a.isUndefined(d.fullResponse)&&d.fullResponse,c.setFullResponse=function(a){return d.fullResponse=a,this},d.urlCreatorFactory={};var h=function(){};h.prototype.setConfig=function(a){return this.config=a,this},h.prototype.parentsArray=function(a){for(var b=[];a;)b.push(a),a=a[this.config.restangularFields.parentResource];return b.reverse()},h.prototype.resource=function(b,c,f,g,h,i,j,k){var l=a.defaults(h||{},this.config.defaultRequestParams.common),m=a.defaults(g||{},this.config.defaultHeaders);j&&(d.isSafe(k)?m["If-None-Match"]=j:m["If-Match"]=j);var n=this.base(b);if(i||0===i){var o="";/\/$/.test(n)||(o+="/"),o+=i,n+=o}return this.config.suffix&&n.indexOf(this.config.suffix,n.length-this.config.suffix.length)===-1&&!this.config.getUrlFromElem(b)&&(n+=this.config.suffix),b[this.config.restangularFields.httpConfig]=void 0,e(this.config,c,n,{getList:this.config.withHttpValues(f,{method:"GET",params:l,headers:m}),get:this.config.withHttpValues(f,{method:"GET",params:l,headers:m}),jsonp:this.config.withHttpValues(f,{method:"jsonp",params:l,headers:m}),put:this.config.withHttpValues(f,{method:"PUT",params:l,headers:m}),post:this.config.withHttpValues(f,{method:"POST",params:l,headers:m}),remove:this.config.withHttpValues(f,{method:"DELETE",params:l,headers:m}),head:this.config.withHttpValues(f,{method:"HEAD",params:l,headers:m}),trace:this.config.withHttpValues(f,{method:"TRACE",params:l,headers:m}),options:this.config.withHttpValues(f,{method:"OPTIONS",params:l,headers:m}),patch:this.config.withHttpValues(f,{method:"PATCH",params:l,headers:m})})};var i=function(){};i.prototype=new h,i.prototype.normalizeUrl=function(a){var b=/((?:http[s]?:)?\/\/)?(.*)?/.exec(a);return b[2]=b[2].replace(/[\\\/]+/g,"/"),"undefined"!=typeof b[1]?b[1]+b[2]:b[2]},i.prototype.base=function(b){var c=this;return a.reduce(this.parentsArray(b),function(a,b){var e,f=c.config.getUrlFromElem(b);if(f){if(c.config.isAbsoluteUrl(f))return f;e=f}else if(e=b[c.config.restangularFields.route],b[c.config.restangularFields.restangularCollection]){var g=b[c.config.restangularFields.ids];g&&(e+="/"+g.join(","))}else{var h;h=c.config.useCannonicalId?c.config.getCannonicalIdFromElem(b):c.config.getIdFromElem(b),d.isValidId(h)&&!b.singleOne&&(e+="/"+(c.config.encodeIds?encodeURIComponent(h):h))}return a=a.replace(/\/$/,"")+"/"+e,c.normalizeUrl(a)},this.config.baseUrl)},i.prototype.fetchUrl=function(a,b){var c=this.base(a);return b&&(c+="/"+b),c},i.prototype.fetchRequestedUrl=function(a,c){function e(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b.sort()}function f(a,b,c){for(var d=e(a),f=0;f= 0.1.7", + "grunt": "^1.0.0", + "grunt-bower": "*", + "grunt-bower-task": "*", + "grunt-cli": "^1.2.0", "grunt-contrib-concat": "*", "grunt-contrib-jshint": "*", "grunt-contrib-uglify": "*", - "grunt-bower": "*", - "grunt-bower-task": "*", + "grunt-conventional-changelog": "latest", + "grunt-coveralls": "^1.0.1", "grunt-karma": "latest", - "grunt-conventional-changelog": "0.0.12", "grunt-zip": "*", - "karma": "^0.13.19", - "karma-chrome-launcher": "~v2.0.0", + "jasmine-core": "^2.5.2", + "karma": "^1.3.0", + "karma-chrome-launcher": "~2.0.0", + "karma-coverage": "^1.1.1", "karma-firefox-launcher": "~v1.0.0", - "karma-jasmine": "~0.1.5", - "karma-mocha-reporter": "0.2.8", + "karma-jasmine": "^1.1.0", + "karma-mocha-reporter": "^2.2.0", "karma-phantomjs-launcher": "~v1.0.2" }, "scripts": { "test": "grunt test --verbose" }, "license": "MIT" -} +} \ No newline at end of file diff --git a/src/restangular.js b/src/restangular.js index 644a2b8b..ff7d5e24 100644 --- a/src/restangular.js +++ b/src/restangular.js @@ -989,7 +989,7 @@ _.each(requestMethods, function(requestFunc, name) { var callOperation = name === 'delete' ? 'remove' : name; _.each(['do', 'custom'], function(alias) { - elem[alias + name.toUpperCase()] = _.bind(requestFunc, elem, callOperation); + elem[config.restangularFields[alias + name.toUpperCase()]] = _.bind(requestFunc, elem, callOperation); }); }); elem[config.restangularFields.customGETLIST] = _.bind(fetchFunction, elem); diff --git a/test/restangularSpec.js b/test/restangularSpec.js index a6a08221..fd989819 100644 --- a/test/restangularSpec.js +++ b/test/restangularSpec.js @@ -1,257 +1,317 @@ -/* global describe, beforeEach, inject, afterEach, it, expect, spyOn, jasmine */ +/* global describe, beforeEach, afterEach, it, expect, spyOn, jasmine */ /* jshint unused: false */ -describe('Restangular', function() { +describe('Restangular', function () { // API - var Restangular, $httpBackend, - accountsModel, restangularAccounts, restangularAccount0, restangularAccount1, - infoModel, accountsDoSomethingModel, customers, publications, newCustomer, - accountsHalModel, - messages, newAccount, nextAccountId; + var Restangular, $httpBackend, testData, + restangularAccounts, restangularAccount0, restangularAccount1; // 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 + 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: [] }, { - 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: 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/paul/transactions/0' + self: '/accountsHAL/martin' } }, { - from: 'Anonymous', - amount: 0.1416, 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/transactions/1' + self: '/accountsHAL/paul' } }], - _links: { - self: '/accountsHAL/paul' - } - }]; - infoModel = { - id: 0, - text: 'Some additional account information' - }; + infoModel: { + id: 0, + text: 'Some additional account information' + }, - newAccount = { - user: 'First User', - amount: 45, - transactions: [] - }; + newAccount: { + user: 'First User', + amount: 45, + transactions: [] + }, - messages = [{ - id: 23, - name: 'Gonto' - }, { - id: 45, - name: 'John' - }]; + messages: [{ + id: 23, + name: 'Gonto' + }, { + id: 45, + name: 'John' + }], - accountsDoSomethingModel = { - result: 1 + 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 + } }; - $httpBackend = $injector.get('$httpBackend'); + // Set up backend responses $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) { + // 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 = nextAccountId; + newData.id = testData.nextAccountId; return [201, JSON.stringify(newData), '']; }); - - $httpBackend.whenPOST('/accounts/1/transactions').respond(function(method, url, data, headers) { + $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) { + $httpBackend.whenDELETE('/accounts/1/transactions/1').respond(function (method, url, data, headers) { return [200, '', '']; }); - - $httpBackend.whenDELETE('/accounts/1').respond(function(method, url, data, headers) { + $httpBackend.whenDELETE('/accounts/1').respond(function (method, url, data, headers) { return [200, '', '']; }); - - $httpBackend.whenPOST('/accounts/1').respond(function(method, url, data, headers) { + $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); + $httpBackend.whenPUT('/accounts/1').respond(function (method, url, data, headers) { + testData.accountsModel[1] = angular.fromJson(data); return [201, data, '']; }); - $httpBackend.whenGET('/error').respond(function() { - return [500, {}, '']; + + $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('/misc/zero').respond(function() { - return [200, 0, '']; + $httpBackend.whenGET('/accountsHAL').respond(testData.accountsHalModel); + $httpBackend.whenPUT('/accountsHAL/martin').respond(function (method, url, data) { + testData.accountsHalModel[0] = angular.fromJson(data); + return [200, data, '']; }); - $httpBackend.whenPOST('/customs').respond(function(method, url, data, headers) { - if (JSON.parse(data).one) { - return [201, '', '']; - } else { - return [400, '', '']; - } + // 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) { + $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); + $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) { + $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() { + afterEach(function () { $httpBackend.verifyNoOutstandingExpectation(); $httpBackend.verifyNoOutstandingRequest(); }); - describe('Interceptors', function() { - it('Should add multiple request and response interceptors', function() { - Restangular.addRequestInterceptor(function(elem) { + 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 = {}; + _.each(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 = {}; + _.each(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 = {}; + _.each(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) { + Restangular.addRequestInterceptor(function (elem) { expect(elem.firstRequestInterceptor).toBeDefined(); var elemCopy = angular.copy(elem); elemCopy.secondRequestInterceptor = true; return elemCopy; }); - Restangular.addFullRequestInterceptor(function(elem) { + Restangular.addFullRequestInterceptor(function (elem) { expect(elem.firstRequestInterceptor).toBeDefined(); expect(elem.secondRequestInterceptor).toBeDefined(); var elemCopy = angular.copy(elem); @@ -261,20 +321,20 @@ describe('Restangular', function() { }; }); - Restangular.addResponseInterceptor(function(elem) { + Restangular.addResponseInterceptor(function (elem) { var elemCopy = angular.copy(elem); elemCopy.firstResponseInterceptor = true; return elemCopy; }); - Restangular.addResponseInterceptor(function(elem) { + 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) { + $httpBackend.whenPOST('/list').respond(function (method, url, data, headers) { var elem = angular.fromJson(data); expect(elem.firstRequestInterceptor).toBeDefined(); expect(elem.secondRequestInterceptor).toBeDefined(); @@ -286,7 +346,7 @@ describe('Restangular', function() { Restangular.all('list').post({ name: 'Gonto' - }).then(function(elem) { + }).then(function (elem) { expect(elem.firstResponseInterceptor).toBeDefined(); expect(elem.secondResponseInterceptor).toBeDefined(); }); @@ -294,24 +354,24 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('Should add multiple error interceptors', function() { + it('Should add multiple error interceptors', function () { $httpBackend.expectGET('/error'); - var CallbackManager = function() {}; - CallbackManager.successCallback = function() { + var CallbackManager = function () {}; + CallbackManager.successCallback = function () { expect(CallbackManager.successCallback).not.toHaveBeenCalled(); }; - CallbackManager.errorCallback = function() { + CallbackManager.errorCallback = function () { expect(CallbackManager.firstErrorInterceptor).toHaveBeenCalled(); expect(CallbackManager.secondErrorInterceptor).toHaveBeenCalled(); }; - CallbackManager.firstErrorInterceptor = function() {}; - CallbackManager.secondErrorInterceptor = function() {}; + CallbackManager.firstErrorInterceptor = function () {}; + CallbackManager.secondErrorInterceptor = function () {}; - spyOn(CallbackManager, 'successCallback').andCallThrough(); - spyOn(CallbackManager, 'firstErrorInterceptor').andCallThrough(); - spyOn(CallbackManager, 'secondErrorInterceptor').andCallThrough(); + spyOn(CallbackManager, 'successCallback').and.callThrough(); + spyOn(CallbackManager, 'firstErrorInterceptor').and.callThrough(); + spyOn(CallbackManager, 'secondErrorInterceptor').and.callThrough(); Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor); Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor); @@ -323,26 +383,26 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('Should add multiple error interceptors but don\'t reject the promise if one of them returns false', function() { + 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() { + var CallbackManager = function () {}; + CallbackManager.successCallback = function () { expect(CallbackManager.successCallback).not.toHaveBeenCalled(); }; - CallbackManager.errorCallback = function() { + CallbackManager.errorCallback = function () { expect(CallbackManager.errorCallback).not.toHaveBeenCalled(); }; - CallbackManager.firstErrorInterceptor = function() { + CallbackManager.firstErrorInterceptor = function () { return true; }; - CallbackManager.secondErrorInterceptor = function() { + CallbackManager.secondErrorInterceptor = function () { return false; // prevent promise to be rejected }; - spyOn(CallbackManager, 'successCallback').andCallThrough(); - spyOn(CallbackManager, 'errorCallback').andCallThrough(); + spyOn(CallbackManager, 'successCallback').and.callThrough(); + spyOn(CallbackManager, 'errorCallback').and.callThrough(); Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor); Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor); @@ -354,26 +414,26 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('Should add multiple error interceptors for a single get too', function() { + it('Should add multiple error interceptors for a single get too', function () { $httpBackend.expectGET('/error/404'); - var CallbackManager = function() {}; - CallbackManager.successCallback = function() { + var CallbackManager = function () {}; + CallbackManager.successCallback = function () { expect(CallbackManager.successCallback).not.toHaveBeenCalled(); }; - CallbackManager.errorCallback = function() { + CallbackManager.errorCallback = function () { expect(CallbackManager.firstErrorInterceptor).toHaveBeenCalled(); expect(CallbackManager.secondErrorInterceptor).toHaveBeenCalled(); }; - CallbackManager.firstErrorInterceptor = function(response) { + CallbackManager.firstErrorInterceptor = function (response) { expect(Number(response.status)).toEqual(404); }; - CallbackManager.secondErrorInterceptor = function() {}; + CallbackManager.secondErrorInterceptor = function () {}; - spyOn(CallbackManager, 'successCallback').andCallThrough(); - spyOn(CallbackManager, 'firstErrorInterceptor').andCallThrough(); - spyOn(CallbackManager, 'secondErrorInterceptor').andCallThrough(); + spyOn(CallbackManager, 'successCallback').and.callThrough(); + spyOn(CallbackManager, 'firstErrorInterceptor').and.callThrough(); + spyOn(CallbackManager, 'secondErrorInterceptor').and.callThrough(); Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor); Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor); @@ -386,17 +446,17 @@ describe('Restangular', function() { }); }); - describe('Transformers', function() { - it('Should decorate element both on server and local by default', function() { + describe('Transformers', function () { + it('Should decorate element both on server and local by default', function () { - Restangular.extendModel('accounts', function(account) { - account.extended = function() { + Restangular.extendModel('accounts', function (account) { + account.extended = function () { return true; }; return account; }); - Restangular.one('accounts', 1).get().then(function(account) { + Restangular.one('accounts', 1).get().then(function (account) { expect(account.extended).toBeDefined(); }); @@ -409,9 +469,9 @@ describe('Restangular', function() { }); - describe('With Suffix', function() { - it('shouldn\'t add suffix to getRestangularUrl', function() { - var suffixRestangular = Restangular.withConfig(function(RestangularConfigurer) { + 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'); @@ -419,8 +479,8 @@ describe('Restangular', function() { expect(collection.one('1').getRestangularUrl()).toBe('/accounts/1'); }); - it('should add suffix to getRequestedUrl', function() { - var suffixRestangular = Restangular.withConfig(function(RestangularConfigurer) { + it('should add suffix to getRequestedUrl', function () { + var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setRequestSuffix('.json'); }); var collection = suffixRestangular.all('accounts'); @@ -428,8 +488,8 @@ describe('Restangular', function() { expect(collection.one('1').getRequestedUrl()).toBe('/accounts/1.json'); }); - it('should add suffix to request', function() { - var suffixRestangular = Restangular.withConfig(function(RestangularConfigurer) { + it('should add suffix to request', function () { + var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setRequestSuffix('.json'); }); var collection = suffixRestangular.all('accounts'); @@ -440,8 +500,8 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('shouldn\'t add suffix to allUrl', function() { - var suffixRestangular = Restangular.withConfig(function(RestangularConfigurer) { + it('shouldn\'t add suffix to allUrl', function () { + var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setRequestSuffix('.json'); }); $httpBackend.expectGET('http://accounts.com/all'); @@ -450,8 +510,8 @@ describe('Restangular', function() { }); }); - describe('JSONp', function() { - it('should work for get', function() { + describe('JSONp', function () { + it('should work for get', function () { Restangular.setJsonp(true); Restangular.one('accounts', 1).get(); @@ -459,7 +519,7 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('should work for getList', function() { + it('should work for getList', function () { Restangular.setJsonp(true); Restangular.all('accounts').getList(); @@ -467,7 +527,7 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('shouldn\'t override post', function() { + it('shouldn\'t override post', function () { Restangular.setJsonp(true); restangularAccounts.post({ id: 2, @@ -481,9 +541,9 @@ describe('Restangular', function() { }); - describe('Local data', function() { - it('Should restangularize a collection OK', function() { - var collection = angular.copy(accountsModel); + describe('Local data', function () { + it('Should restangularize a collection OK', function () { + var collection = angular.copy(testData.accountsModel); Restangular.restangularizeCollection(null, collection, 'accounts'); @@ -495,8 +555,8 @@ describe('Restangular', function() { }); - it('Should restangularize a function with arguments OK', function() { - var collection = function(a, b) {}; + it('Should restangularize a function with arguments OK', function () { + var collection = function (a, b) {}; Restangular.restangularizeCollection(null, collection, 'accounts'); @@ -505,7 +565,7 @@ describe('Restangular', function() { expect(collection.getRestangularUrl()).toBe('/accounts'); }); - it('should have fromServer set when restangularizeElement is called with that param', function() { + 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); @@ -516,7 +576,7 @@ describe('Restangular', function() { expect(element.fromServer).toEqual(false); }); - it('should have fromServer set when restangularizeCollection is called with that param', function() { + 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); @@ -528,11 +588,11 @@ describe('Restangular', function() { }); }); - describe('restangularizePromiseIntercept', function() { - it('should be invoked by restangularizePromise', function() { + describe('restangularizePromiseIntercept', function () { + it('should be invoked by restangularizePromise', function () { var calledWithPromise; - Restangular.setRestangularizePromiseInterceptor(function(promise) { + Restangular.setRestangularizePromiseInterceptor(function (promise) { calledWithPromise = promise; promise.$object.$custom = true; @@ -547,8 +607,8 @@ describe('Restangular', function() { }); }); - describe('$object', function() { - it('Should work for single get', function() { + describe('$object', function () { + it('Should work for single get', function () { var promise = Restangular.one('accounts', 1).get(); var obj = promise.$object; expect(obj).toBeDefined(); @@ -559,9 +619,9 @@ describe('Restangular', function() { expect(obj.amount).toEqual(3.1416); }); - it('Shouldn\'t be restangularized by default', function() { - Restangular.extendModel('accounts', function(account) { - account.extended = function() { + it('Shouldn\'t be restangularized by default', function () { + Restangular.extendModel('accounts', function (account) { + account.extended = function () { return true; }; return account; @@ -575,7 +635,7 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('Should work for single get', function() { + it('Should work for single get', function () { var promise = Restangular.all('accounts').getList(); var list = promise.$object; expect(list).toBeDefined(); @@ -588,120 +648,120 @@ describe('Restangular', function() { }); }); - 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)); + 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() { + 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)); + 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() { + 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])); + 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() { + 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)); + 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() { + it('post() should add a new item', function () { restangularAccounts.post({ id: 2, user: 'Someone' - }).then(function() { - expect(accountsModel.length).toEqual(2); + }).then(function () { + expect(testData.accountsModel.length).toEqual(2); }); $httpBackend.expectPOST('/accounts').respond(201, ''); $httpBackend.flush(); }); - it('customPOST() should add a new item', function() { + it('customPOST() should add a new item', function () { restangularAccounts.customPOST({ id: 2, user: 'Someone' - }).then(function() { - expect(accountsModel.length).toEqual(2); + }).then(function () { + expect(testData.accountsModel.length).toEqual(2); }); $httpBackend.expectPOST('/accounts').respond(201, ''); $httpBackend.flush(); }); - it('post() should work with arrays', function() { + it('post() should work with arrays', function () { Restangular.all('places').post([{ name: 'Gonto' }, { name: 'John' - }]).then(function(value) { + }]).then(function (value) { expect(value.length).toEqual(2); }); - $httpBackend.expectPOST('/places').respond(function(method, url, data, headers) { + $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) { + 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(nextAccountId); - expect(added.user).toEqual(newAccount.user); + 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(newAccount).then(function(added) { + 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/' + nextAccountId).respond(201, ''); + $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) { + 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) { + accounts[1].post('transactions', newTransaction).then(function (transaction) { expect(transaction).toBeUndefined(); }); }); @@ -709,23 +769,23 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('head() should safely return', function() { - restangularAccounts.head().then(function() { + 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() { + 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); + it('customPUT should work', function () { + $httpBackend.expectPUT('/accounts/hey').respond(testData.accountsModel); restangularAccounts.customPUT({ key: 'value' }, 'hey'); @@ -733,50 +793,50 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('customPATCH should work', function() { + it('customPATCH should work', function () { var data = { foo: 'bar' }; - $httpBackend.expectPATCH('/accounts/hey', data).respond(accountsModel); + $httpBackend.expectPATCH('/accounts/hey', data).respond(testData.accountsModel); restangularAccounts.customPATCH(data, 'hey'); $httpBackend.flush(); }); - it('options() should safely return', function() { - restangularAccounts.options().then(function() { + 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); + it('getList() should correctly handle params after customDELETE', function () { + $httpBackend.expectGET('/accounts?foo=1').respond(testData.accountsModel); restangularAccounts.getList({ foo: 1 - }).then(function() { + }).then(function () { $httpBackend.expectDELETE('/accounts?id=1').respond(201, ''); return restangularAccounts.customDELETE('', { id: 1 }); - }).then(function() { - $httpBackend.expectGET('/accounts?foo=1').respond(accountsModel); + }).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(accountsModel)); + }).then(function (accounts) { + expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(testData.accountsModel)); }); $httpBackend.flush(); }); }); - describe('Scoped Service', function() { + describe('Scoped Service', function () { - it('should correctly work', function() { + it('should correctly work', function () { var Accounts = Restangular.service('accounts'); - Accounts.post(newAccount); + Accounts.post(testData.newAccount); Accounts.one(0).get(); Accounts.getList(); @@ -786,9 +846,9 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('should correctly work with children', function() { + it('should correctly work with children', function () { var Transactions = Restangular.service('transactions', restangularAccount1); - Transactions.post(newAccount); + Transactions.post(testData.newAccount); Transactions.one(1).get(); Transactions.getList(); Transactions.get(1); @@ -800,9 +860,9 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('should add custom collection method added with withConfig', function() { - var Accounts = Restangular.withConfig(function(RestangularConfigurer) { - RestangularConfigurer.addElementTransformer('accounts', true, function(worker) { + 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; }); @@ -811,7 +871,7 @@ describe('Restangular', function() { expect(Accounts.doSomething).toBeDefined(); expect(_.isFunction(Accounts.doSomething)).toBeTruthy(); - Accounts.post(newAccount); + Accounts.post(testData.newAccount); Accounts.one(0).get(); Accounts.getList(); Accounts.doSomething(); @@ -825,30 +885,30 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('should provide a one-off $http configuration method', function() { + it('should provide a one-off $http configuration method', function () { var Accounts = Restangular.service('accounts'); Accounts.withHttpConfig({ transformRequest: angular.identity }); - Accounts.post(newAccount); + 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) { + 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])); + .toEqual(Restangular.stripRestangular(testData.accountsModel[1])); }); $httpBackend.flush(); }); - it('Should save as put correctly', function() { - restangularAccount1.get().then(function(account) { + it('Should save as put correctly', function () { + restangularAccount1.get().then(function (account) { $httpBackend.expectPUT('/accounts/1'); account.put(); }); @@ -856,7 +916,7 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('Should save as post correctly', function() { + it('Should save as post correctly', function () { var account1 = angular.copy(restangularAccount1); $httpBackend.expectPOST('/accounts/1'); account1.name = 'Hey'; @@ -865,52 +925,52 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('Should keep route property when element is created', function() { + 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) { + account1.save().then(function (accountFromServer) { accountFromServer.id = 1; return accountFromServer.save(); - }).then(function(accountFromServer2) { + }).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) { + 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])); + .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) { + 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)); + .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(messages[0])); + 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) { + 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); + account.put().then(function (newAc) { + expect(testData.accountsModel[1].amount).toEqual(1.618); newAc.remove(); $httpBackend.expectDELETE('/accounts/1'); }); @@ -922,11 +982,11 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('should return an array when accessing a subvalue', function() { - restangularAccount1.get().then(function(account) { - account.getList('transactions').then(function(transactions) { + 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)); + .toEqual(Restangular.stripRestangular(testData.accountsModel[1].transactions)); }); }); @@ -934,16 +994,16 @@ describe('Restangular', function() { }); }); - describe('COPY', function() { - it('should copy an object and "this" should reference the copied object', function() { - var copiedAccount = Restangular.copy(accountsModel[0]); + 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(accountsModel[0]); + expect(copiedAccount).not.toBe(testData.accountsModel[0]); // create a spy for one of the methods to capture the value of 'this' - spyOn(copiedAccount, 'getRestangularUrl').andCallFake(function() { + spyOn(copiedAccount, 'getRestangularUrl').and.callFake(function () { that = this; }); @@ -951,13 +1011,13 @@ describe('Restangular', function() { expect(that).toBe(copiedAccount); }); - it('should copy an object and "fromServer" param should be the same with the copied object', function() { + 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[0].args[0], + var account = responseHandler.calls.argsFor(0)[0], copiedAccount = Restangular.copy(account); expect(account.fromServer).toEqual(true); expect(copiedAccount.fromServer).toEqual(true); @@ -981,7 +1041,7 @@ describe('Restangular', function() { // with collections, fromServer = true; restangularAccounts.getList().then(responseHandler); $httpBackend.flush(); - accounts = responseHandler.calls[0].args[0], + accounts = responseHandler.calls.argsFor(0)[0], copiedAccounts = Restangular.copy(accounts); expect(accounts.fromServer).toEqual(true); expect(copiedAccounts.fromServer).toEqual(true); @@ -1014,71 +1074,135 @@ describe('Restangular', function() { }); }); - describe('getRestangularUrl', function() { - it('should return the generated URL when you chain Restangular methods together', function() { + 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('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); + 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'); }); - 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() { + 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() {}; + Restangular.addElementTransformer('accounts', true, function (collection) { + collection.totalAmount = function () {}; return collection; }); accountsPromise = Restangular.all('accounts').getList(); - accountsPromise.then(function(accounts) { + 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() { + 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() {}; + Restangular.addElementTransformer('accounts', false, function (model) { + model.prettifyAmount = function () {}; return model; }); accountPromise = Restangular.one('accounts', 1).get(); - accountPromise.then(function(account) { + 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() { + 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() {}; + 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) { + accountsPromise.then(function (accounts) { + accounts.forEach(function (account) { expect(typeof account.prettifyAmount).toEqual('function'); }); }); @@ -1086,18 +1210,18 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('should allow for a custom method to be placed at the collection level using a regexp matching the route', function() { + 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() {}; + 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) { + accountsPromise.then(function (accounts) { + accounts.forEach(function (account) { expect(typeof account.prettifyAmount).toEqual('function'); }); }); @@ -1108,7 +1232,7 @@ describe('Restangular', function() { it('should work with cloned collections', function () { var responseHandler = jasmine.createSpy(); - Restangular.addElementTransformer(/^accounts/, true, function(collection) { + Restangular.addElementTransformer(/^accounts/, true, function (collection) { collection.customThing = 'customValue'; return collection; }); @@ -1116,42 +1240,42 @@ describe('Restangular', function() { Restangular.all('accounts').getList().then(responseHandler); $httpBackend.flush(); - var accounts = responseHandler.calls[0].args[0]; + 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() { + 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() {}; + Restangular.addElementTransformer(/^accounts/, false, function (model) { + model.prettifyAmount = function () {}; return model; }); accountPromise = Restangular.one('accounts', 1).get(); - accountPromise.then(function(account) { + 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() { + 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() {}; + 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) { + accountsPromise.then(function (accounts) { + accounts.forEach(function (account) { expect(typeof account.prettifyAmount).toEqual('function'); }); }); @@ -1161,12 +1285,12 @@ describe('Restangular', function() { }); - describe('extendCollection', function() { - it('should be an alias for a specific invocation of addElementTransformer', function() { + 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() {}; + var fn = function (collection) { + collection.totalAmount = function () {}; return collection; }; @@ -1176,12 +1300,12 @@ describe('Restangular', function() { }); }); - describe('extendModel', function() { - it('should be an alias for a specific invocation of addElementTransformer', function() { + 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() {}; + var fn = function (model) { + model.prettifyAmount = function () {}; return model; }; @@ -1191,8 +1315,8 @@ describe('Restangular', function() { }); }); - describe('headers', function() { - it('should return defaultHeaders', function() { + describe('headers', function () { + it('should return defaultHeaders', function () { var defaultHeaders = { testheader: 'header value' }; @@ -1200,9 +1324,9 @@ describe('Restangular', function() { expect(Restangular.defaultHeaders).toEqual(defaultHeaders); }); - it('should pass uppercase methods in X-HTTP-Method-Override', function() { + it('should pass uppercase methods in X-HTTP-Method-Override', function () { Restangular.setMethodOverriders(['put']); - $httpBackend.expectPOST('/overriders/1').respond(function(method, url, data, headers) { + $httpBackend.expectPOST('/overriders/1').respond(function (method, url, data, headers) { expect(headers['X-HTTP-Method-Override']).toBe('PUT'); return {}; }); @@ -1211,8 +1335,8 @@ describe('Restangular', function() { }); }); - describe('defaultRequestParams', function() { - it('should return defaultRequestParams', function() { + describe('defaultRequestParams', function () { + it('should return defaultRequestParams', function () { var defaultRequestParams = { param: 'value' }; @@ -1222,7 +1346,7 @@ describe('Restangular', function() { expect(Restangular.requestParams.common).toEqual(defaultRequestParams); }); - it('should be able to set default params for get, post, put.. methods separately', function() { + it('should be able to set default params for get, post, put.. methods separately', function () { var postParams = { post: 'value' }, @@ -1239,7 +1363,7 @@ describe('Restangular', function() { expect(Restangular.requestParams.common).not.toEqual(putParams); }); - it('should be able to set default params for multiple methods with array', function() { + it('should be able to set default params for multiple methods with array', function () { var defaultParams = { param: 'value' }; @@ -1253,9 +1377,9 @@ describe('Restangular', function() { }); }); - describe('withConfig', function() { - it('should create new service with scoped configuration', function() { - var childRestangular = Restangular.withConfig(function(RestangularConfigurer) { + describe('withConfig', function () { + it('should create new service with scoped configuration', function () { + var childRestangular = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setBaseUrl('/api/v1'); }); @@ -1264,12 +1388,12 @@ describe('Restangular', function() { }); - it('should allow nested configurations', function() { - var childRestangular = Restangular.withConfig(function(RestangularConfigurer) { + it('should allow nested configurations', function () { + var childRestangular = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setBaseUrl('/api/v1'); }); - var grandchildRestangular = childRestangular.withConfig(function(RestangularConfigurer) { + var grandchildRestangular = childRestangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setRequestSuffix('.json'); }); @@ -1284,9 +1408,9 @@ describe('Restangular', function() { }); }); - describe('Self linking', function() { - it('Should request the link in HAL format', function() { - var linkRestangular = Restangular.withConfig(function(RestangularConfigurer) { + describe('Self linking', function () { + it('Should request the link in HAL format', function () { + var linkRestangular = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setRestangularFields({ selfLink: '_links.self' }); @@ -1304,38 +1428,38 @@ describe('Restangular', function() { }); }); - describe('Singe one (endpoint not expecting an id)', function() { - it('does not use the id for single resource GET', function() { + 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() { + it('getRestangularUrl() returns still the url without id after GET', function () { var record = Restangular.one('info', 0, true); - record.get().then(function(data) { + 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() { + 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() { + 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) { + describe('setSelfLinkAbsoluteUrl', function () { + it('works', function () { + var childRestangular = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setSelfLinkAbsoluteUrl(false); }); @@ -1344,41 +1468,27 @@ describe('Restangular', function() { }); }); - 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(); - }); + describe('Misc', function () { - 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) { + 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() { + 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() { + describe('testing normalize url', function () { - it('should get a list of objects', function() { - Restangular.all('customers/').getList().then(function(res) { + it('should get a list of objects', function () { + Restangular.all('customers/').getList().then(function (res) { res.getList({ active: true }); @@ -1390,8 +1500,8 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('should get a list of objects even if the path has extra slashes', function() { - Restangular.all('customers///').getList().then(function(res) { + 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 }); @@ -1401,37 +1511,37 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('should post with slash at the end', function() { - Restangular.all('customers/').getList().then(function(res) { - res.post(newCustomer); + it('should post with slash at the end', function () { + Restangular.all('customers/').getList().then(function (res) { + res.post(testData.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) { + 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() { + 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) { + 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) { + var newApi = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setBaseUrl('api.new.domain'); }); @@ -1442,8 +1552,8 @@ describe('Restangular', function() { $httpBackend.flush(); }); - it('Should work with absolute URL with //authority', function() { - var newRes = Restangular.withConfig(function(RestangularConfigurer) { + 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'); @@ -1454,31 +1564,153 @@ describe('Restangular', function() { }); }); - describe('setPlainByDefault', function() { + describe('setPlainByDefault', function () { + var plainByDefaultRestangular; - it('should not add restangularized methods to response object', function() { - var newRes = Restangular.withConfig(function(RestangularConfigurer) { + beforeEach(function () { + plainByDefaultRestangular = Restangular.withConfig(function (RestangularConfigurer) { RestangularConfigurer.setPlainByDefault(true); }); + }); - expect(newRes.configuration.plainByDefault).toEqual(true); + it('should set the property on the configuration', function () { + expect(plainByDefaultRestangular.configuration.plainByDefault).toEqual(true); + }); - newRes.one('accounts', 0).get().then(function(account) { - expect(account).toEqual(accountsModel[0]); + it('should not add restangularized methods to response object', function () { + plainByDefaultRestangular.one('accounts', 0).get().then(function (account) { + expect(account).toEqual(testData.accountsModel[0]); }); + $httpBackend.flush(); + }); + it('shoud not add restangularized methods to response collection', function () { + plainByDefaultRestangular.all('accounts').getList().then(function (accounts) { + expect(accounts).toEqual(testData.accountsModel); + }); $httpBackend.flush(); }); - it('shoud not add restangularized methods to response collection', function() { - var newRes = Restangular.withConfig(function(RestangularConfigurer) { - RestangularConfigurer.setPlainByDefault(true); + describe('with ETag', function () { + beforeEach(function () { + $httpBackend.whenGET('/accounts').respond( + testData.accountsModel, + {'ETag': 'c11ea3f8-3bfd-4be8-a6a6-501dd831b8a4'} + ); + $httpBackend.whenGET('/accounts/1').respond( + testData.accountsModel[1], + {'ETag': 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2'} + ); }); - newRes.all('accounts').getList().then(function(accounts) { - expect(accounts).toEqual(accountsModel); + it('should not add restangularized ETag to response object', function () { + plainByDefaultRestangular.one('accounts', 0).get().then(function (account) { + expect(account).toEqual(testData.accountsModel[0]); + }); + $httpBackend.flush(); + }); + + it('shoud not add restangularized ETag to response collection', function () { + plainByDefaultRestangular.all('accounts').getList().then(function (accounts) { + expect(accounts).toEqual(testData.accountsModel); + }); + $httpBackend.flush(); + }); + }); + }); + + describe('ETags', function () { + beforeEach(function () { + $httpBackend.whenGET('/etagAccounts').respond( + testData.accountsModel, + {'ETag': 'c11ea3f8-3bfd-4be8-a6a6-501dd831b8a4'} + ); + $httpBackend.whenGET('/etagAccounts/1').respond( + testData.accountsModel[1], + {'ETag': 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2'} + ); + }); + + it('should include the ETag in the restangularized element', function () { + Restangular.one('etagAccounts', 1).get().then(function (account) { + expect(account.restangularEtag).toEqual('bf79b780-f132-4f44-a9eb-7e6eb4f902b2'); }); $httpBackend.flush(); }); + it('should include the ETag in the restangularized collection', function () { + Restangular.all('etagAccounts').getList().then(function (accounts) { + expect(accounts.restangularEtag).toEqual('c11ea3f8-3bfd-4be8-a6a6-501dd831b8a4'); + }); + $httpBackend.flush(); + }); + it('should add the If-Match header on PUT requests', function () { + var responseHandler = jasmine.createSpy(); + Restangular.one('etagAccounts', 1).get().then(responseHandler); + $httpBackend.flush(); + + var account = responseHandler.calls.argsFor(0)[0]; + $httpBackend.expect( + 'PUT', + '/etagAccounts/1', + testData.accountsModel[1], + function (headers) { + return headers['If-Match'] === 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2'; + } + ).respond(200); + account.save(); + $httpBackend.flush(); + }); + it('should add the If-Match header on DELETE requests', function () { + var responseHandler = jasmine.createSpy(); + Restangular.one('etagAccounts', 1).get().then(responseHandler); + $httpBackend.flush(); + + var account = responseHandler.calls.argsFor(0)[0]; + $httpBackend.expect( + 'DELETE', + '/etagAccounts/1', + testData.accountsModel[1], + function (headers) { + return headers['If-Match'] === 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2'; + } + ).respond(200); + account.remove(); + $httpBackend.flush(); + }); + it('should add the If-None-Match header on GET requests for elements', function () { + var responseHandler = jasmine.createSpy(); + Restangular.one('etagAccounts', 1).get().then(responseHandler); + $httpBackend.flush(); + + var account = responseHandler.calls.argsFor(0)[0]; + $httpBackend.expect( + 'GET', + '/etagAccounts/1', + undefined, + function (headers) { + return headers['If-None-Match'] === 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2'; + } + ).respond(200); + account.get(); + $httpBackend.flush(); + }); + it('should add the If-None-Match header on GET requests for collections', function () { + var responseHandler = jasmine.createSpy(); + Restangular.all('etagAccounts').getList().then(responseHandler); + $httpBackend.flush(); + + var accounts = responseHandler.calls.argsFor(0)[0]; + $httpBackend.expect( + 'GET', + '/etagAccounts', + undefined, + function (headers) { + return headers['If-None-Match'] === 'c11ea3f8-3bfd-4be8-a6a6-501dd831b8a4'; + } + ).respond(200); + accounts.getList(); + $httpBackend.flush(); + }); + }); });