504 |
505 | */
506 | angular.module('ngSanitize').filter('linky', function() {
507 | var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
508 | MAILTO_REGEXP = /^mailto:/;
509 |
510 | return function(text) {
511 | if (!text) return text;
512 | var match;
513 | var raw = text;
514 | var html = [];
515 | // TODO(vojta): use $sanitize instead
516 | var writer = htmlSanitizeWriter(html);
517 | var url;
518 | var i;
519 | while ((match = raw.match(LINKY_URL_REGEXP))) {
520 | // We can not end in these as they are sometimes found at the end of the sentence
521 | url = match[0];
522 | // if we did not match ftp/http/mailto then assume mailto
523 | if (match[2] == match[3]) url = 'mailto:' + url;
524 | i = match.index;
525 | writer.chars(raw.substr(0, i));
526 | writer.start('a', {href:url});
527 | writer.chars(match[0].replace(MAILTO_REGEXP, ''));
528 | writer.end('a');
529 | raw = raw.substring(i + match[0].length);
530 | }
531 | writer.chars(raw);
532 | return html.join('');
533 | };
534 | });
535 |
536 |
537 | })(window, window.angular);
538 |
--------------------------------------------------------------------------------
/app/lib/angular/angular-sanitize.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.0.7
3 | (c) 2010-2012 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(I,g){'use strict';function i(a){var d={},a=a.split(","),b;for(b=0;b
=0;e--)if(f[e]==b)break;if(e>=0){for(c=f.length-1;c>=e;c--)d.end&&d.end(f[c]);f.length=
7 | e}}var c,h,f=[],j=a;for(f.last=function(){return f[f.length-1]};a;){h=!0;if(!f.last()||!q[f.last()]){if(a.indexOf("<\!--")===0)c=a.indexOf("--\>"),c>=0&&(d.comment&&d.comment(a.substring(4,c)),a=a.substring(c+3),h=!1);else if(B.test(a)){if(c=a.match(r))a=a.substring(c[0].length),c[0].replace(r,e),h=!1}else if(C.test(a)&&(c=a.match(s)))a=a.substring(c[0].length),c[0].replace(s,b),h=!1;h&&(c=a.indexOf("<"),h=c<0?a:a.substring(0,c),a=c<0?"":a.substring(c),d.chars&&d.chars(k(h)))}else a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+
8 | f.last()+"[^>]*>","i"),function(b,a){a=a.replace(D,"$1").replace(E,"$1");d.chars&&d.chars(k(a));return""}),e("",f.last());if(a==j)throw"Parse Error: "+a;j=a}e()}function k(a){l.innerHTML=a.replace(//g,">")}function u(a){var d=!1,b=g.bind(a,a.push);return{start:function(a,c,h){a=g.lowercase(a);!d&&q[a]&&(d=a);!d&&v[a]==
9 | !0&&(b("<"),b(a),g.forEach(c,function(a,c){var e=g.lowercase(c);if(G[e]==!0&&(w[e]!==!0||a.match(H)))b(" "),b(c),b('="'),b(t(a)),b('"')}),b(h?"/>":">"))},end:function(a){a=g.lowercase(a);!d&&v[a]==!0&&(b(""),b(a),b(">"));a==d&&(d=!1)},chars:function(a){d||b(t(a))}}}var s=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,r=/^<\s*\/\s*([\w:-]+)[^>]*>/,A=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,C=/^,B=/^<\s*\//,D=/<\!--(.*?)--\>/g,
10 | E=//g,H=/^((ftp|https?):\/\/|mailto:|#)/,F=/([^\#-~| |!])/g,p=i("area,br,col,hr,img,wbr"),x=i("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),y=i("rp,rt"),o=g.extend({},y,x),m=g.extend({},x,i("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),n=g.extend({},y,i("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),
11 | q=i("script,style"),v=g.extend({},p,m,n,o),w=i("background,cite,href,longdesc,src,usemap"),G=g.extend({},w,i("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),l=document.createElement("pre");g.module("ngSanitize",[]).value("$sanitize",function(a){var d=[];
12 | z(a,u(d));return d.join("")});g.module("ngSanitize").directive("ngBindHtml",["$sanitize",function(a){return function(d,b,e){b.addClass("ng-binding").data("$binding",e.ngBindHtml);d.$watch(e.ngBindHtml,function(c){c=a(c);b.html(c||"")})}}]);g.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,d=/^mailto:/;return function(b){if(!b)return b;for(var e=b,c=[],h=u(c),f,g;b=e.match(a);)f=b[0],b[2]==b[3]&&(f="mailto:"+f),g=b.index,
13 | h.chars(e.substr(0,g)),h.start("a",{href:f}),h.chars(b[0].replace(d,"")),h.end("a"),e=e.substring(g+b[0].length);h.chars(e);return c.join("")}})})(window,window.angular);
14 |
--------------------------------------------------------------------------------
/app/lib/angular/version.txt:
--------------------------------------------------------------------------------
1 | 1.0.7
2 |
--------------------------------------------------------------------------------
/app/partials/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikemilano/angular-seed-rest/6fcad6c2d86bc688ed2f2821c55b282b2513cc45/app/partials/.gitkeep
--------------------------------------------------------------------------------
/app/partials/partial1.html:
--------------------------------------------------------------------------------
1 | AngularJS GitHub Issues
2 |
3 |
4 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 | Number |
21 | Created |
22 | Updated |
23 | Comments |
24 | State |
25 | Reporter |
26 | Title |
27 |
28 |
29 |
30 |
31 | {{issue.number}} |
32 | {{issue.created_at | date:'short'}} |
33 | {{issue.updated_at | date:'short'}} |
34 | {{issue.comments}} |
35 | {{issue.state}} |
36 | {{issue.user.login}} |
37 | {{issue.title}} |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
« Back
45 |
{{myData.issue.title}}
46 |
47 | - Number
48 | - {{myData.currentIssue.number}}
49 | - Date
50 | - {{myData.currentIssue.created_at | date:'short'}}
51 | - Last Update
52 | - {{myData.currentIssue.updated_at | date:'short'}}
53 | - State
54 | - {{myData.currentIssue.state}}
55 | - URL
56 | - {{myData.currentIssue.url}}
57 | - Asignee
58 | - {{myData.currentIssue.asignee}}
59 | - Body
60 | - {{myData.currentIssue.body}}
61 |
62 |
--------------------------------------------------------------------------------
/app/partials/partial2.html:
--------------------------------------------------------------------------------
1 | This is the partial for view 2.
2 |
3 | Showing of 'interpolate' filter:
4 | {{ 'Current version is v%VERSION%.' | interpolate }}
5 |
6 |
--------------------------------------------------------------------------------
/config/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | basePath = '../';
2 |
3 | files = [
4 | ANGULAR_SCENARIO,
5 | ANGULAR_SCENARIO_ADAPTER,
6 | 'test/e2e/**/*.js'
7 | ];
8 |
9 | autoWatch = false;
10 |
11 | browsers = ['Chrome'];
12 |
13 | singleRun = true;
14 |
15 | proxies = {
16 | '/': 'http://localhost:8000/'
17 | };
18 |
19 | junitReporter = {
20 | outputFile: 'test_out/e2e.xml',
21 | suite: 'e2e'
22 | };
23 |
--------------------------------------------------------------------------------
/config/karma.conf.js:
--------------------------------------------------------------------------------
1 | basePath = '../';
2 |
3 | files = [
4 | JASMINE,
5 | JASMINE_ADAPTER,
6 | 'app/lib/angular/angular.js',
7 | 'app/lib/angular/angular-*.js',
8 | 'test/lib/angular/angular-mocks.js',
9 | 'app/js/**/*.js',
10 | 'test/unit/**/*.js'
11 | ];
12 |
13 | autoWatch = true;
14 |
15 | browsers = ['Chrome'];
16 |
17 | junitReporter = {
18 | outputFile: 'test_out/unit.xml',
19 | suite: 'unit'
20 | };
21 |
--------------------------------------------------------------------------------
/logs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikemilano/angular-seed-rest/6fcad6c2d86bc688ed2f2821c55b282b2513cc45/logs/.gitkeep
--------------------------------------------------------------------------------
/scripts/e2e-test.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | REM Windows script for running e2e tests
4 | REM You have to run server and capture some browser first
5 | REM
6 | REM Requirements:
7 | REM - NodeJS (http://nodejs.org/)
8 | REM - Karma (npm install -g karma)
9 |
10 | set BASE_DIR=%~dp0
11 | karma start "%BASE_DIR%\..\config\karma-e2e.conf.js" %*
12 |
--------------------------------------------------------------------------------
/scripts/e2e-test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BASE_DIR=`dirname $0`
4 |
5 | echo ""
6 | echo "Starting Karma Server (http://karma-runner.github.io)"
7 | echo "-------------------------------------------------------------------"
8 |
9 | karma start $BASE_DIR/../config/karma-e2e.conf.js $*
10 |
--------------------------------------------------------------------------------
/scripts/test.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | REM Windows script for running unit tests
4 | REM You have to run server and capture some browser first
5 | REM
6 | REM Requirements:
7 | REM - NodeJS (http://nodejs.org/)
8 | REM - Karma (npm install -g karma)
9 |
10 | set BASE_DIR=%~dp0
11 | karma start "%BASE_DIR%\..\config\karma.conf.js" %*
12 |
--------------------------------------------------------------------------------
/scripts/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BASE_DIR=`dirname $0`
4 |
5 | echo ""
6 | echo "Starting Karma Server (http://karma-runner.github.io)"
7 | echo "-------------------------------------------------------------------"
8 |
9 | karma start $BASE_DIR/../config/karma.conf.js $*
10 |
--------------------------------------------------------------------------------
/scripts/watchr.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env watchr
2 |
3 | # config file for watchr http://github.com/mynyml/watchr
4 | # install: gem install watchr
5 | # run: watch watchr.rb
6 | # note: make sure that you have jstd server running (server.sh) and a browser captured
7 |
8 | log_file = File.expand_path(File.dirname(__FILE__) + '/../logs/jstd.log')
9 |
10 | `cd ..`
11 | `touch #{log_file}`
12 |
13 | puts "String watchr... log file: #{log_file}"
14 |
15 | watch( '(app/js|test/unit)' ) do
16 | `echo "\n\ntest run started @ \`date\`" > #{log_file}`
17 | `scripts/test.sh &> #{log_file}`
18 | end
19 |
20 |
--------------------------------------------------------------------------------
/scripts/web-server.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var util = require('util'),
4 | http = require('http'),
5 | fs = require('fs'),
6 | url = require('url'),
7 | events = require('events');
8 |
9 | var DEFAULT_PORT = 8000;
10 |
11 | function main(argv) {
12 | new HttpServer({
13 | 'GET': createServlet(StaticServlet),
14 | 'HEAD': createServlet(StaticServlet)
15 | }).start(Number(argv[2]) || DEFAULT_PORT);
16 | }
17 |
18 | function escapeHtml(value) {
19 | return value.toString().
20 | replace('<', '<').
21 | replace('>', '>').
22 | replace('"', '"');
23 | }
24 |
25 | function createServlet(Class) {
26 | var servlet = new Class();
27 | return servlet.handleRequest.bind(servlet);
28 | }
29 |
30 | /**
31 | * An Http server implementation that uses a map of methods to decide
32 | * action routing.
33 | *
34 | * @param {Object} Map of method => Handler function
35 | */
36 | function HttpServer(handlers) {
37 | this.handlers = handlers;
38 | this.server = http.createServer(this.handleRequest_.bind(this));
39 | }
40 |
41 | HttpServer.prototype.start = function(port) {
42 | this.port = port;
43 | this.server.listen(port);
44 | util.puts('Http Server running at http://localhost:' + port + '/');
45 | };
46 |
47 | HttpServer.prototype.parseUrl_ = function(urlString) {
48 | var parsed = url.parse(urlString);
49 | parsed.pathname = url.resolve('/', parsed.pathname);
50 | return url.parse(url.format(parsed), true);
51 | };
52 |
53 | HttpServer.prototype.handleRequest_ = function(req, res) {
54 | var logEntry = req.method + ' ' + req.url;
55 | if (req.headers['user-agent']) {
56 | logEntry += ' ' + req.headers['user-agent'];
57 | }
58 | util.puts(logEntry);
59 | req.url = this.parseUrl_(req.url);
60 | var handler = this.handlers[req.method];
61 | if (!handler) {
62 | res.writeHead(501);
63 | res.end();
64 | } else {
65 | handler.call(this, req, res);
66 | }
67 | };
68 |
69 | /**
70 | * Handles static content.
71 | */
72 | function StaticServlet() {}
73 |
74 | StaticServlet.MimeMap = {
75 | 'txt': 'text/plain',
76 | 'html': 'text/html',
77 | 'css': 'text/css',
78 | 'xml': 'application/xml',
79 | 'json': 'application/json',
80 | 'js': 'application/javascript',
81 | 'jpg': 'image/jpeg',
82 | 'jpeg': 'image/jpeg',
83 | 'gif': 'image/gif',
84 | 'png': 'image/png',
85 | 'svg': 'image/svg+xml'
86 | };
87 |
88 | StaticServlet.prototype.handleRequest = function(req, res) {
89 | var self = this;
90 | var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
91 | return String.fromCharCode(parseInt(hex, 16));
92 | });
93 | var parts = path.split('/');
94 | if (parts[parts.length-1].charAt(0) === '.')
95 | return self.sendForbidden_(req, res, path);
96 | fs.stat(path, function(err, stat) {
97 | if (err)
98 | return self.sendMissing_(req, res, path);
99 | if (stat.isDirectory())
100 | return self.sendDirectory_(req, res, path);
101 | return self.sendFile_(req, res, path);
102 | });
103 | }
104 |
105 | StaticServlet.prototype.sendError_ = function(req, res, error) {
106 | res.writeHead(500, {
107 | 'Content-Type': 'text/html'
108 | });
109 | res.write('\n');
110 | res.write('Internal Server Error\n');
111 | res.write('Internal Server Error
');
112 | res.write('' + escapeHtml(util.inspect(error)) + '
');
113 | util.puts('500 Internal Server Error');
114 | util.puts(util.inspect(error));
115 | };
116 |
117 | StaticServlet.prototype.sendMissing_ = function(req, res, path) {
118 | path = path.substring(1);
119 | res.writeHead(404, {
120 | 'Content-Type': 'text/html'
121 | });
122 | res.write('\n');
123 | res.write('404 Not Found\n');
124 | res.write('Not Found
');
125 | res.write(
126 | 'The requested URL ' +
127 | escapeHtml(path) +
128 | ' was not found on this server.
'
129 | );
130 | res.end();
131 | util.puts('404 Not Found: ' + path);
132 | };
133 |
134 | StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
135 | path = path.substring(1);
136 | res.writeHead(403, {
137 | 'Content-Type': 'text/html'
138 | });
139 | res.write('\n');
140 | res.write('403 Forbidden\n');
141 | res.write('Forbidden
');
142 | res.write(
143 | 'You do not have permission to access ' +
144 | escapeHtml(path) + ' on this server.
'
145 | );
146 | res.end();
147 | util.puts('403 Forbidden: ' + path);
148 | };
149 |
150 | StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
151 | res.writeHead(301, {
152 | 'Content-Type': 'text/html',
153 | 'Location': redirectUrl
154 | });
155 | res.write('\n');
156 | res.write('301 Moved Permanently\n');
157 | res.write('Moved Permanently
');
158 | res.write(
159 | 'The document has moved here.
'
162 | );
163 | res.end();
164 | util.puts('301 Moved Permanently: ' + redirectUrl);
165 | };
166 |
167 | StaticServlet.prototype.sendFile_ = function(req, res, path) {
168 | var self = this;
169 | var file = fs.createReadStream(path);
170 | res.writeHead(200, {
171 | 'Content-Type': StaticServlet.
172 | MimeMap[path.split('.').pop()] || 'text/plain'
173 | });
174 | if (req.method === 'HEAD') {
175 | res.end();
176 | } else {
177 | file.on('data', res.write.bind(res));
178 | file.on('close', function() {
179 | res.end();
180 | });
181 | file.on('error', function(error) {
182 | self.sendError_(req, res, error);
183 | });
184 | }
185 | };
186 |
187 | StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
188 | var self = this;
189 | if (path.match(/[^\/]$/)) {
190 | req.url.pathname += '/';
191 | var redirectUrl = url.format(url.parse(url.format(req.url)));
192 | return self.sendRedirect_(req, res, redirectUrl);
193 | }
194 | fs.readdir(path, function(err, files) {
195 | if (err)
196 | return self.sendError_(req, res, error);
197 |
198 | if (!files.length)
199 | return self.writeDirectoryIndex_(req, res, path, []);
200 |
201 | var remaining = files.length;
202 | files.forEach(function(fileName, index) {
203 | fs.stat(path + '/' + fileName, function(err, stat) {
204 | if (err)
205 | return self.sendError_(req, res, err);
206 | if (stat.isDirectory()) {
207 | files[index] = fileName + '/';
208 | }
209 | if (!(--remaining))
210 | return self.writeDirectoryIndex_(req, res, path, files);
211 | });
212 | });
213 | });
214 | };
215 |
216 | StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
217 | path = path.substring(1);
218 | res.writeHead(200, {
219 | 'Content-Type': 'text/html'
220 | });
221 | if (req.method === 'HEAD') {
222 | res.end();
223 | return;
224 | }
225 | res.write('\n');
226 | res.write('' + escapeHtml(path) + '\n');
227 | res.write('\n');
230 | res.write('Directory: ' + escapeHtml(path) + '
');
231 | res.write('');
232 | files.forEach(function(fileName) {
233 | if (fileName.charAt(0) !== '.') {
234 | res.write('- ' +
236 | escapeHtml(fileName) + '
');
237 | }
238 | });
239 | res.write('
');
240 | res.end();
241 | };
242 |
243 | // Must be last,
244 | main(process.argv);
245 |
--------------------------------------------------------------------------------
/test/e2e/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | End2end Test Runner
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/e2e/scenarios.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* http://docs.angularjs.org/guide/dev_guide.e2e-testing */
4 |
5 | describe('my app', function() {
6 |
7 | beforeEach(function() {
8 | browser().navigateTo('../../app/index.html');
9 | });
10 |
11 |
12 | it('should automatically redirect to /view1 when location hash/fragment is empty', function() {
13 | expect(browser().location().url()).toBe("/view1");
14 | });
15 |
16 |
17 | describe('view1', function() {
18 |
19 | beforeEach(function() {
20 | browser().navigateTo('#/view1');
21 | });
22 |
23 |
24 | it('should render view1 when user navigates to /view1', function() {
25 | expect(element('[ng-view] p:first').text()).
26 | toMatch(/partial for view 1/);
27 | });
28 |
29 | });
30 |
31 |
32 | describe('view2', function() {
33 |
34 | beforeEach(function() {
35 | browser().navigateTo('#/view2');
36 | });
37 |
38 |
39 | it('should render view2 when user navigates to /view2', function() {
40 | expect(element('[ng-view] p:first').text()).
41 | toMatch(/partial for view 2/);
42 | });
43 |
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/lib/angular/angular-mocks.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.0.7
3 | * (c) 2010-2012 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | *
6 | * TODO(vojta): wrap whole file into closure during build
7 | */
8 |
9 | /**
10 | * @ngdoc overview
11 | * @name angular.mock
12 | * @description
13 | *
14 | * Namespace from 'angular-mocks.js' which contains testing related code.
15 | */
16 | angular.mock = {};
17 |
18 | /**
19 | * ! This is a private undocumented service !
20 | *
21 | * @name ngMock.$browser
22 | *
23 | * @description
24 | * This service is a mock implementation of {@link ng.$browser}. It provides fake
25 | * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
26 | * cookies, etc...
27 | *
28 | * The api of this service is the same as that of the real {@link ng.$browser $browser}, except
29 | * that there are several helper methods available which can be used in tests.
30 | */
31 | angular.mock.$BrowserProvider = function() {
32 | this.$get = function(){
33 | return new angular.mock.$Browser();
34 | };
35 | };
36 |
37 | angular.mock.$Browser = function() {
38 | var self = this;
39 |
40 | this.isMock = true;
41 | self.$$url = "http://server/";
42 | self.$$lastUrl = self.$$url; // used by url polling fn
43 | self.pollFns = [];
44 |
45 | // TODO(vojta): remove this temporary api
46 | self.$$completeOutstandingRequest = angular.noop;
47 | self.$$incOutstandingRequestCount = angular.noop;
48 |
49 |
50 | // register url polling fn
51 |
52 | self.onUrlChange = function(listener) {
53 | self.pollFns.push(
54 | function() {
55 | if (self.$$lastUrl != self.$$url) {
56 | self.$$lastUrl = self.$$url;
57 | listener(self.$$url);
58 | }
59 | }
60 | );
61 |
62 | return listener;
63 | };
64 |
65 | self.cookieHash = {};
66 | self.lastCookieHash = {};
67 | self.deferredFns = [];
68 | self.deferredNextId = 0;
69 |
70 | self.defer = function(fn, delay) {
71 | delay = delay || 0;
72 | self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
73 | self.deferredFns.sort(function(a,b){ return a.time - b.time;});
74 | return self.deferredNextId++;
75 | };
76 |
77 |
78 | self.defer.now = 0;
79 |
80 |
81 | self.defer.cancel = function(deferId) {
82 | var fnIndex;
83 |
84 | angular.forEach(self.deferredFns, function(fn, index) {
85 | if (fn.id === deferId) fnIndex = index;
86 | });
87 |
88 | if (fnIndex !== undefined) {
89 | self.deferredFns.splice(fnIndex, 1);
90 | return true;
91 | }
92 |
93 | return false;
94 | };
95 |
96 |
97 | /**
98 | * @name ngMock.$browser#defer.flush
99 | * @methodOf ngMock.$browser
100 | *
101 | * @description
102 | * Flushes all pending requests and executes the defer callbacks.
103 | *
104 | * @param {number=} number of milliseconds to flush. See {@link #defer.now}
105 | */
106 | self.defer.flush = function(delay) {
107 | if (angular.isDefined(delay)) {
108 | self.defer.now += delay;
109 | } else {
110 | if (self.deferredFns.length) {
111 | self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
112 | } else {
113 | throw Error('No deferred tasks to be flushed');
114 | }
115 | }
116 |
117 | while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
118 | self.deferredFns.shift().fn();
119 | }
120 | };
121 | /**
122 | * @name ngMock.$browser#defer.now
123 | * @propertyOf ngMock.$browser
124 | *
125 | * @description
126 | * Current milliseconds mock time.
127 | */
128 |
129 | self.$$baseHref = '';
130 | self.baseHref = function() {
131 | return this.$$baseHref;
132 | };
133 | };
134 | angular.mock.$Browser.prototype = {
135 |
136 | /**
137 | * @name ngMock.$browser#poll
138 | * @methodOf ngMock.$browser
139 | *
140 | * @description
141 | * run all fns in pollFns
142 | */
143 | poll: function poll() {
144 | angular.forEach(this.pollFns, function(pollFn){
145 | pollFn();
146 | });
147 | },
148 |
149 | addPollFn: function(pollFn) {
150 | this.pollFns.push(pollFn);
151 | return pollFn;
152 | },
153 |
154 | url: function(url, replace) {
155 | if (url) {
156 | this.$$url = url;
157 | return this;
158 | }
159 |
160 | return this.$$url;
161 | },
162 |
163 | cookies: function(name, value) {
164 | if (name) {
165 | if (value == undefined) {
166 | delete this.cookieHash[name];
167 | } else {
168 | if (angular.isString(value) && //strings only
169 | value.length <= 4096) { //strict cookie storage limits
170 | this.cookieHash[name] = value;
171 | }
172 | }
173 | } else {
174 | if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
175 | this.lastCookieHash = angular.copy(this.cookieHash);
176 | this.cookieHash = angular.copy(this.cookieHash);
177 | }
178 | return this.cookieHash;
179 | }
180 | },
181 |
182 | notifyWhenNoOutstandingRequests: function(fn) {
183 | fn();
184 | }
185 | };
186 |
187 |
188 | /**
189 | * @ngdoc object
190 | * @name ngMock.$exceptionHandlerProvider
191 | *
192 | * @description
193 | * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors passed
194 | * into the `$exceptionHandler`.
195 | */
196 |
197 | /**
198 | * @ngdoc object
199 | * @name ngMock.$exceptionHandler
200 | *
201 | * @description
202 | * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
203 | * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
204 | * information.
205 | *
206 | *
207 | *
208 | * describe('$exceptionHandlerProvider', function() {
209 | *
210 | * it('should capture log messages and exceptions', function() {
211 | *
212 | * module(function($exceptionHandlerProvider) {
213 | * $exceptionHandlerProvider.mode('log');
214 | * });
215 | *
216 | * inject(function($log, $exceptionHandler, $timeout) {
217 | * $timeout(function() { $log.log(1); });
218 | * $timeout(function() { $log.log(2); throw 'banana peel'; });
219 | * $timeout(function() { $log.log(3); });
220 | * expect($exceptionHandler.errors).toEqual([]);
221 | * expect($log.assertEmpty());
222 | * $timeout.flush();
223 | * expect($exceptionHandler.errors).toEqual(['banana peel']);
224 | * expect($log.log.logs).toEqual([[1], [2], [3]]);
225 | * });
226 | * });
227 | * });
228 | *
229 | */
230 |
231 | angular.mock.$ExceptionHandlerProvider = function() {
232 | var handler;
233 |
234 | /**
235 | * @ngdoc method
236 | * @name ngMock.$exceptionHandlerProvider#mode
237 | * @methodOf ngMock.$exceptionHandlerProvider
238 | *
239 | * @description
240 | * Sets the logging mode.
241 | *
242 | * @param {string} mode Mode of operation, defaults to `rethrow`.
243 | *
244 | * - `rethrow`: If any errors are passed into the handler in tests, it typically
245 | * means that there is a bug in the application or test, so this mock will
246 | * make these tests fail.
247 | * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` mode stores an
248 | * array of errors in `$exceptionHandler.errors`, to allow later assertion of them.
249 | * See {@link ngMock.$log#assertEmpty assertEmpty()} and
250 | * {@link ngMock.$log#reset reset()}
251 | */
252 | this.mode = function(mode) {
253 | switch(mode) {
254 | case 'rethrow':
255 | handler = function(e) {
256 | throw e;
257 | };
258 | break;
259 | case 'log':
260 | var errors = [];
261 |
262 | handler = function(e) {
263 | if (arguments.length == 1) {
264 | errors.push(e);
265 | } else {
266 | errors.push([].slice.call(arguments, 0));
267 | }
268 | };
269 |
270 | handler.errors = errors;
271 | break;
272 | default:
273 | throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
274 | }
275 | };
276 |
277 | this.$get = function() {
278 | return handler;
279 | };
280 |
281 | this.mode('rethrow');
282 | };
283 |
284 |
285 | /**
286 | * @ngdoc service
287 | * @name ngMock.$log
288 | *
289 | * @description
290 | * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
291 | * (one array per logging level). These arrays are exposed as `logs` property of each of the
292 | * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
293 | *
294 | */
295 | angular.mock.$LogProvider = function() {
296 |
297 | function concat(array1, array2, index) {
298 | return array1.concat(Array.prototype.slice.call(array2, index));
299 | }
300 |
301 |
302 | this.$get = function () {
303 | var $log = {
304 | log: function() { $log.log.logs.push(concat([], arguments, 0)); },
305 | warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
306 | info: function() { $log.info.logs.push(concat([], arguments, 0)); },
307 | error: function() { $log.error.logs.push(concat([], arguments, 0)); }
308 | };
309 |
310 | /**
311 | * @ngdoc method
312 | * @name ngMock.$log#reset
313 | * @methodOf ngMock.$log
314 | *
315 | * @description
316 | * Reset all of the logging arrays to empty.
317 | */
318 | $log.reset = function () {
319 | /**
320 | * @ngdoc property
321 | * @name ngMock.$log#log.logs
322 | * @propertyOf ngMock.$log
323 | *
324 | * @description
325 | * Array of messages logged using {@link ngMock.$log#log}.
326 | *
327 | * @example
328 | *
329 | * $log.log('Some Log');
330 | * var first = $log.log.logs.unshift();
331 | *
332 | */
333 | $log.log.logs = [];
334 | /**
335 | * @ngdoc property
336 | * @name ngMock.$log#warn.logs
337 | * @propertyOf ngMock.$log
338 | *
339 | * @description
340 | * Array of messages logged using {@link ngMock.$log#warn}.
341 | *
342 | * @example
343 | *
344 | * $log.warn('Some Warning');
345 | * var first = $log.warn.logs.unshift();
346 | *
347 | */
348 | $log.warn.logs = [];
349 | /**
350 | * @ngdoc property
351 | * @name ngMock.$log#info.logs
352 | * @propertyOf ngMock.$log
353 | *
354 | * @description
355 | * Array of messages logged using {@link ngMock.$log#info}.
356 | *
357 | * @example
358 | *
359 | * $log.info('Some Info');
360 | * var first = $log.info.logs.unshift();
361 | *
362 | */
363 | $log.info.logs = [];
364 | /**
365 | * @ngdoc property
366 | * @name ngMock.$log#error.logs
367 | * @propertyOf ngMock.$log
368 | *
369 | * @description
370 | * Array of messages logged using {@link ngMock.$log#error}.
371 | *
372 | * @example
373 | *
374 | * $log.log('Some Error');
375 | * var first = $log.error.logs.unshift();
376 | *
377 | */
378 | $log.error.logs = [];
379 | };
380 |
381 | /**
382 | * @ngdoc method
383 | * @name ngMock.$log#assertEmpty
384 | * @methodOf ngMock.$log
385 | *
386 | * @description
387 | * Assert that the all of the logging methods have no logged messages. If messages present, an exception is thrown.
388 | */
389 | $log.assertEmpty = function() {
390 | var errors = [];
391 | angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) {
392 | angular.forEach($log[logLevel].logs, function(log) {
393 | angular.forEach(log, function (logItem) {
394 | errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || ''));
395 | });
396 | });
397 | });
398 | if (errors.length) {
399 | errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or an expected " +
400 | "log message was not checked and removed:");
401 | errors.push('');
402 | throw new Error(errors.join('\n---------\n'));
403 | }
404 | };
405 |
406 | $log.reset();
407 | return $log;
408 | };
409 | };
410 |
411 |
412 | (function() {
413 | var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
414 |
415 | function jsonStringToDate(string){
416 | var match;
417 | if (match = string.match(R_ISO8061_STR)) {
418 | var date = new Date(0),
419 | tzHour = 0,
420 | tzMin = 0;
421 | if (match[9]) {
422 | tzHour = int(match[9] + match[10]);
423 | tzMin = int(match[9] + match[11]);
424 | }
425 | date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
426 | date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
427 | return date;
428 | }
429 | return string;
430 | }
431 |
432 | function int(str) {
433 | return parseInt(str, 10);
434 | }
435 |
436 | function padNumber(num, digits, trim) {
437 | var neg = '';
438 | if (num < 0) {
439 | neg = '-';
440 | num = -num;
441 | }
442 | num = '' + num;
443 | while(num.length < digits) num = '0' + num;
444 | if (trim)
445 | num = num.substr(num.length - digits);
446 | return neg + num;
447 | }
448 |
449 |
450 | /**
451 | * @ngdoc object
452 | * @name angular.mock.TzDate
453 | * @description
454 | *
455 | * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
456 | *
457 | * Mock of the Date type which has its timezone specified via constructor arg.
458 | *
459 | * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
460 | * offset, so that we can test code that depends on local timezone settings without dependency on
461 | * the time zone settings of the machine where the code is running.
462 | *
463 | * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
464 | * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
465 | *
466 | * @example
467 | * !!!! WARNING !!!!!
468 | * This is not a complete Date object so only methods that were implemented can be called safely.
469 | * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
470 | *
471 | * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
472 | * incomplete we might be missing some non-standard methods. This can result in errors like:
473 | * "Date.prototype.foo called on incompatible Object".
474 | *
475 | *
476 | * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
477 | * newYearInBratislava.getTimezoneOffset() => -60;
478 | * newYearInBratislava.getFullYear() => 2010;
479 | * newYearInBratislava.getMonth() => 0;
480 | * newYearInBratislava.getDate() => 1;
481 | * newYearInBratislava.getHours() => 0;
482 | * newYearInBratislava.getMinutes() => 0;
483 | *
484 | *
485 | */
486 | angular.mock.TzDate = function (offset, timestamp) {
487 | var self = new Date(0);
488 | if (angular.isString(timestamp)) {
489 | var tsStr = timestamp;
490 |
491 | self.origDate = jsonStringToDate(timestamp);
492 |
493 | timestamp = self.origDate.getTime();
494 | if (isNaN(timestamp))
495 | throw {
496 | name: "Illegal Argument",
497 | message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
498 | };
499 | } else {
500 | self.origDate = new Date(timestamp);
501 | }
502 |
503 | var localOffset = new Date(timestamp).getTimezoneOffset();
504 | self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
505 | self.date = new Date(timestamp + self.offsetDiff);
506 |
507 | self.getTime = function() {
508 | return self.date.getTime() - self.offsetDiff;
509 | };
510 |
511 | self.toLocaleDateString = function() {
512 | return self.date.toLocaleDateString();
513 | };
514 |
515 | self.getFullYear = function() {
516 | return self.date.getFullYear();
517 | };
518 |
519 | self.getMonth = function() {
520 | return self.date.getMonth();
521 | };
522 |
523 | self.getDate = function() {
524 | return self.date.getDate();
525 | };
526 |
527 | self.getHours = function() {
528 | return self.date.getHours();
529 | };
530 |
531 | self.getMinutes = function() {
532 | return self.date.getMinutes();
533 | };
534 |
535 | self.getSeconds = function() {
536 | return self.date.getSeconds();
537 | };
538 |
539 | self.getTimezoneOffset = function() {
540 | return offset * 60;
541 | };
542 |
543 | self.getUTCFullYear = function() {
544 | return self.origDate.getUTCFullYear();
545 | };
546 |
547 | self.getUTCMonth = function() {
548 | return self.origDate.getUTCMonth();
549 | };
550 |
551 | self.getUTCDate = function() {
552 | return self.origDate.getUTCDate();
553 | };
554 |
555 | self.getUTCHours = function() {
556 | return self.origDate.getUTCHours();
557 | };
558 |
559 | self.getUTCMinutes = function() {
560 | return self.origDate.getUTCMinutes();
561 | };
562 |
563 | self.getUTCSeconds = function() {
564 | return self.origDate.getUTCSeconds();
565 | };
566 |
567 | self.getUTCMilliseconds = function() {
568 | return self.origDate.getUTCMilliseconds();
569 | };
570 |
571 | self.getDay = function() {
572 | return self.date.getDay();
573 | };
574 |
575 | // provide this method only on browsers that already have it
576 | if (self.toISOString) {
577 | self.toISOString = function() {
578 | return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
579 | padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
580 | padNumber(self.origDate.getUTCDate(), 2) + 'T' +
581 | padNumber(self.origDate.getUTCHours(), 2) + ':' +
582 | padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
583 | padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
584 | padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z'
585 | }
586 | }
587 |
588 | //hide all methods not implemented in this mock that the Date prototype exposes
589 | var unimplementedMethods = ['getMilliseconds', 'getUTCDay',
590 | 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
591 | 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
592 | 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
593 | 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
594 | 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
595 |
596 | angular.forEach(unimplementedMethods, function(methodName) {
597 | self[methodName] = function() {
598 | throw Error("Method '" + methodName + "' is not implemented in the TzDate mock");
599 | };
600 | });
601 |
602 | return self;
603 | };
604 |
605 | //make "tzDateInstance instanceof Date" return true
606 | angular.mock.TzDate.prototype = Date.prototype;
607 | })();
608 |
609 |
610 | /**
611 | * @ngdoc function
612 | * @name angular.mock.dump
613 | * @description
614 | *
615 | * *NOTE*: this is not an injectable instance, just a globally available function.
616 | *
617 | * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for debugging.
618 | *
619 | * This method is also available on window, where it can be used to display objects on debug console.
620 | *
621 | * @param {*} object - any object to turn into string.
622 | * @return {string} a serialized string of the argument
623 | */
624 | angular.mock.dump = function(object) {
625 | return serialize(object);
626 |
627 | function serialize(object) {
628 | var out;
629 |
630 | if (angular.isElement(object)) {
631 | object = angular.element(object);
632 | out = angular.element('');
633 | angular.forEach(object, function(element) {
634 | out.append(angular.element(element).clone());
635 | });
636 | out = out.html();
637 | } else if (angular.isArray(object)) {
638 | out = [];
639 | angular.forEach(object, function(o) {
640 | out.push(serialize(o));
641 | });
642 | out = '[ ' + out.join(', ') + ' ]';
643 | } else if (angular.isObject(object)) {
644 | if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
645 | out = serializeScope(object);
646 | } else if (object instanceof Error) {
647 | out = object.stack || ('' + object.name + ': ' + object.message);
648 | } else {
649 | out = angular.toJson(object, true);
650 | }
651 | } else {
652 | out = String(object);
653 | }
654 |
655 | return out;
656 | }
657 |
658 | function serializeScope(scope, offset) {
659 | offset = offset || ' ';
660 | var log = [offset + 'Scope(' + scope.$id + '): {'];
661 | for ( var key in scope ) {
662 | if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) {
663 | log.push(' ' + key + ': ' + angular.toJson(scope[key]));
664 | }
665 | }
666 | var child = scope.$$childHead;
667 | while(child) {
668 | log.push(serializeScope(child, offset + ' '));
669 | child = child.$$nextSibling;
670 | }
671 | log.push('}');
672 | return log.join('\n' + offset);
673 | }
674 | };
675 |
676 | /**
677 | * @ngdoc object
678 | * @name ngMock.$httpBackend
679 | * @description
680 | * Fake HTTP backend implementation suitable for unit testing applications that use the
681 | * {@link ng.$http $http service}.
682 | *
683 | * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
684 | * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
685 | *
686 | * During unit testing, we want our unit tests to run quickly and have no external dependencies so
687 | * we don’t want to send {@link https://developer.mozilla.org/en/xmlhttprequest XHR} or
688 | * {@link http://en.wikipedia.org/wiki/JSONP JSONP} requests to a real server. All we really need is
689 | * to verify whether a certain request has been sent or not, or alternatively just let the
690 | * application make requests, respond with pre-trained responses and assert that the end result is
691 | * what we expect it to be.
692 | *
693 | * This mock implementation can be used to respond with static or dynamic responses via the
694 | * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
695 | *
696 | * When an Angular application needs some data from a server, it calls the $http service, which
697 | * sends the request to a real server using $httpBackend service. With dependency injection, it is
698 | * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
699 | * the requests and respond with some testing data without sending a request to real server.
700 | *
701 | * There are two ways to specify what test data should be returned as http responses by the mock
702 | * backend when the code under test makes http requests:
703 | *
704 | * - `$httpBackend.expect` - specifies a request expectation
705 | * - `$httpBackend.when` - specifies a backend definition
706 | *
707 | *
708 | * # Request Expectations vs Backend Definitions
709 | *
710 | * Request expectations provide a way to make assertions about requests made by the application and
711 | * to define responses for those requests. The test will fail if the expected requests are not made
712 | * or they are made in the wrong order.
713 | *
714 | * Backend definitions allow you to define a fake backend for your application which doesn't assert
715 | * if a particular request was made or not, it just returns a trained response if a request is made.
716 | * The test will pass whether or not the request gets made during testing.
717 | *
718 | *
719 | *
720 | * | Request expectations | Backend definitions |
721 | *
722 | * Syntax |
723 | * .expect(...).respond(...) |
724 | * .when(...).respond(...) |
725 | *
726 | *
727 | * Typical usage |
728 | * strict unit tests |
729 | * loose (black-box) unit testing |
730 | *
731 | *
732 | * Fulfills multiple requests |
733 | * NO |
734 | * YES |
735 | *
736 | *
737 | * Order of requests matters |
738 | * YES |
739 | * NO |
740 | *
741 | *
742 | * Request required |
743 | * YES |
744 | * NO |
745 | *
746 | *
747 | * Response required |
748 | * optional (see below) |
749 | * YES |
750 | *
751 | *
752 | *
753 | * In cases where both backend definitions and request expectations are specified during unit
754 | * testing, the request expectations are evaluated first.
755 | *
756 | * If a request expectation has no response specified, the algorithm will search your backend
757 | * definitions for an appropriate response.
758 | *
759 | * If a request didn't match any expectation or if the expectation doesn't have the response
760 | * defined, the backend definitions are evaluated in sequential order to see if any of them match
761 | * the request. The response from the first matched definition is returned.
762 | *
763 | *
764 | * # Flushing HTTP requests
765 | *
766 | * The $httpBackend used in production, always responds to requests with responses asynchronously.
767 | * If we preserved this behavior in unit testing, we'd have to create async unit tests, which are
768 | * hard to write, follow and maintain. At the same time the testing mock, can't respond
769 | * synchronously because that would change the execution of the code under test. For this reason the
770 | * mock $httpBackend has a `flush()` method, which allows the test to explicitly flush pending
771 | * requests and thus preserving the async api of the backend, while allowing the test to execute
772 | * synchronously.
773 | *
774 | *
775 | * # Unit testing with mock $httpBackend
776 | *
777 | *
778 | // controller
779 | function MyController($scope, $http) {
780 | $http.get('/auth.py').success(function(data) {
781 | $scope.user = data;
782 | });
783 |
784 | this.saveMessage = function(message) {
785 | $scope.status = 'Saving...';
786 | $http.post('/add-msg.py', message).success(function(response) {
787 | $scope.status = '';
788 | }).error(function() {
789 | $scope.status = 'ERROR!';
790 | });
791 | };
792 | }
793 |
794 | // testing controller
795 | var $httpBackend;
796 |
797 | beforeEach(inject(function($injector) {
798 | $httpBackend = $injector.get('$httpBackend');
799 |
800 | // backend definition common for all tests
801 | $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
802 | }));
803 |
804 |
805 | afterEach(function() {
806 | $httpBackend.verifyNoOutstandingExpectation();
807 | $httpBackend.verifyNoOutstandingRequest();
808 | });
809 |
810 |
811 | it('should fetch authentication token', function() {
812 | $httpBackend.expectGET('/auth.py');
813 | var controller = scope.$new(MyController);
814 | $httpBackend.flush();
815 | });
816 |
817 |
818 | it('should send msg to server', function() {
819 | // now you don’t care about the authentication, but
820 | // the controller will still send the request and
821 | // $httpBackend will respond without you having to
822 | // specify the expectation and response for this request
823 | $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
824 |
825 | var controller = scope.$new(MyController);
826 | $httpBackend.flush();
827 | controller.saveMessage('message content');
828 | expect(controller.status).toBe('Saving...');
829 | $httpBackend.flush();
830 | expect(controller.status).toBe('');
831 | });
832 |
833 |
834 | it('should send auth header', function() {
835 | $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
836 | // check if the header was send, if it wasn't the expectation won't
837 | // match the request and the test will fail
838 | return headers['Authorization'] == 'xxx';
839 | }).respond(201, '');
840 |
841 | var controller = scope.$new(MyController);
842 | controller.saveMessage('whatever');
843 | $httpBackend.flush();
844 | });
845 |
846 | */
847 | angular.mock.$HttpBackendProvider = function() {
848 | this.$get = [createHttpBackendMock];
849 | };
850 |
851 | /**
852 | * General factory function for $httpBackend mock.
853 | * Returns instance for unit testing (when no arguments specified):
854 | * - passing through is disabled
855 | * - auto flushing is disabled
856 | *
857 | * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
858 | * - passing through (delegating request to real backend) is enabled
859 | * - auto flushing is enabled
860 | *
861 | * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
862 | * @param {Object=} $browser Auto-flushing enabled if specified
863 | * @return {Object} Instance of $httpBackend mock
864 | */
865 | function createHttpBackendMock($delegate, $browser) {
866 | var definitions = [],
867 | expectations = [],
868 | responses = [],
869 | responsesPush = angular.bind(responses, responses.push);
870 |
871 | function createResponse(status, data, headers) {
872 | if (angular.isFunction(status)) return status;
873 |
874 | return function() {
875 | return angular.isNumber(status)
876 | ? [status, data, headers]
877 | : [200, status, data];
878 | };
879 | }
880 |
881 | // TODO(vojta): change params to: method, url, data, headers, callback
882 | function $httpBackend(method, url, data, callback, headers) {
883 | var xhr = new MockXhr(),
884 | expectation = expectations[0],
885 | wasExpected = false;
886 |
887 | function prettyPrint(data) {
888 | return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
889 | ? data
890 | : angular.toJson(data);
891 | }
892 |
893 | if (expectation && expectation.match(method, url)) {
894 | if (!expectation.matchData(data))
895 | throw Error('Expected ' + expectation + ' with different data\n' +
896 | 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
897 |
898 | if (!expectation.matchHeaders(headers))
899 | throw Error('Expected ' + expectation + ' with different headers\n' +
900 | 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
901 | prettyPrint(headers));
902 |
903 | expectations.shift();
904 |
905 | if (expectation.response) {
906 | responses.push(function() {
907 | var response = expectation.response(method, url, data, headers);
908 | xhr.$$respHeaders = response[2];
909 | callback(response[0], response[1], xhr.getAllResponseHeaders());
910 | });
911 | return;
912 | }
913 | wasExpected = true;
914 | }
915 |
916 | var i = -1, definition;
917 | while ((definition = definitions[++i])) {
918 | if (definition.match(method, url, data, headers || {})) {
919 | if (definition.response) {
920 | // if $browser specified, we do auto flush all requests
921 | ($browser ? $browser.defer : responsesPush)(function() {
922 | var response = definition.response(method, url, data, headers);
923 | xhr.$$respHeaders = response[2];
924 | callback(response[0], response[1], xhr.getAllResponseHeaders());
925 | });
926 | } else if (definition.passThrough) {
927 | $delegate(method, url, data, callback, headers);
928 | } else throw Error('No response defined !');
929 | return;
930 | }
931 | }
932 | throw wasExpected ?
933 | Error('No response defined !') :
934 | Error('Unexpected request: ' + method + ' ' + url + '\n' +
935 | (expectation ? 'Expected ' + expectation : 'No more request expected'));
936 | }
937 |
938 | /**
939 | * @ngdoc method
940 | * @name ngMock.$httpBackend#when
941 | * @methodOf ngMock.$httpBackend
942 | * @description
943 | * Creates a new backend definition.
944 | *
945 | * @param {string} method HTTP method.
946 | * @param {string|RegExp} url HTTP url.
947 | * @param {(string|RegExp)=} data HTTP request body.
948 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
949 | * object and returns true if the headers match the current definition.
950 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
951 | * request is handled.
952 | *
953 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
954 | * – The respond method takes a set of static data to be returned or a function that can return
955 | * an array containing response status (number), response data (string) and response headers
956 | * (Object).
957 | */
958 | $httpBackend.when = function(method, url, data, headers) {
959 | var definition = new MockHttpExpectation(method, url, data, headers),
960 | chain = {
961 | respond: function(status, data, headers) {
962 | definition.response = createResponse(status, data, headers);
963 | }
964 | };
965 |
966 | if ($browser) {
967 | chain.passThrough = function() {
968 | definition.passThrough = true;
969 | };
970 | }
971 |
972 | definitions.push(definition);
973 | return chain;
974 | };
975 |
976 | /**
977 | * @ngdoc method
978 | * @name ngMock.$httpBackend#whenGET
979 | * @methodOf ngMock.$httpBackend
980 | * @description
981 | * Creates a new backend definition for GET requests. For more info see `when()`.
982 | *
983 | * @param {string|RegExp} url HTTP url.
984 | * @param {(Object|function(Object))=} headers HTTP headers.
985 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
986 | * request is handled.
987 | */
988 |
989 | /**
990 | * @ngdoc method
991 | * @name ngMock.$httpBackend#whenHEAD
992 | * @methodOf ngMock.$httpBackend
993 | * @description
994 | * Creates a new backend definition for HEAD requests. For more info see `when()`.
995 | *
996 | * @param {string|RegExp} url HTTP url.
997 | * @param {(Object|function(Object))=} headers HTTP headers.
998 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
999 | * request is handled.
1000 | */
1001 |
1002 | /**
1003 | * @ngdoc method
1004 | * @name ngMock.$httpBackend#whenDELETE
1005 | * @methodOf ngMock.$httpBackend
1006 | * @description
1007 | * Creates a new backend definition for DELETE requests. For more info see `when()`.
1008 | *
1009 | * @param {string|RegExp} url HTTP url.
1010 | * @param {(Object|function(Object))=} headers HTTP headers.
1011 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1012 | * request is handled.
1013 | */
1014 |
1015 | /**
1016 | * @ngdoc method
1017 | * @name ngMock.$httpBackend#whenPOST
1018 | * @methodOf ngMock.$httpBackend
1019 | * @description
1020 | * Creates a new backend definition for POST requests. For more info see `when()`.
1021 | *
1022 | * @param {string|RegExp} url HTTP url.
1023 | * @param {(string|RegExp)=} data HTTP request body.
1024 | * @param {(Object|function(Object))=} headers HTTP headers.
1025 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1026 | * request is handled.
1027 | */
1028 |
1029 | /**
1030 | * @ngdoc method
1031 | * @name ngMock.$httpBackend#whenPUT
1032 | * @methodOf ngMock.$httpBackend
1033 | * @description
1034 | * Creates a new backend definition for PUT requests. For more info see `when()`.
1035 | *
1036 | * @param {string|RegExp} url HTTP url.
1037 | * @param {(string|RegExp)=} data HTTP request body.
1038 | * @param {(Object|function(Object))=} headers HTTP headers.
1039 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1040 | * request is handled.
1041 | */
1042 |
1043 | /**
1044 | * @ngdoc method
1045 | * @name ngMock.$httpBackend#whenJSONP
1046 | * @methodOf ngMock.$httpBackend
1047 | * @description
1048 | * Creates a new backend definition for JSONP requests. For more info see `when()`.
1049 | *
1050 | * @param {string|RegExp} url HTTP url.
1051 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1052 | * request is handled.
1053 | */
1054 | createShortMethods('when');
1055 |
1056 |
1057 | /**
1058 | * @ngdoc method
1059 | * @name ngMock.$httpBackend#expect
1060 | * @methodOf ngMock.$httpBackend
1061 | * @description
1062 | * Creates a new request expectation.
1063 | *
1064 | * @param {string} method HTTP method.
1065 | * @param {string|RegExp} url HTTP url.
1066 | * @param {(string|RegExp)=} data HTTP request body.
1067 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1068 | * object and returns true if the headers match the current expectation.
1069 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1070 | * request is handled.
1071 | *
1072 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1073 | * – The respond method takes a set of static data to be returned or a function that can return
1074 | * an array containing response status (number), response data (string) and response headers
1075 | * (Object).
1076 | */
1077 | $httpBackend.expect = function(method, url, data, headers) {
1078 | var expectation = new MockHttpExpectation(method, url, data, headers);
1079 | expectations.push(expectation);
1080 | return {
1081 | respond: function(status, data, headers) {
1082 | expectation.response = createResponse(status, data, headers);
1083 | }
1084 | };
1085 | };
1086 |
1087 |
1088 | /**
1089 | * @ngdoc method
1090 | * @name ngMock.$httpBackend#expectGET
1091 | * @methodOf ngMock.$httpBackend
1092 | * @description
1093 | * Creates a new request expectation for GET requests. For more info see `expect()`.
1094 | *
1095 | * @param {string|RegExp} url HTTP url.
1096 | * @param {Object=} headers HTTP headers.
1097 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1098 | * request is handled. See #expect for more info.
1099 | */
1100 |
1101 | /**
1102 | * @ngdoc method
1103 | * @name ngMock.$httpBackend#expectHEAD
1104 | * @methodOf ngMock.$httpBackend
1105 | * @description
1106 | * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1107 | *
1108 | * @param {string|RegExp} url HTTP url.
1109 | * @param {Object=} headers HTTP headers.
1110 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1111 | * request is handled.
1112 | */
1113 |
1114 | /**
1115 | * @ngdoc method
1116 | * @name ngMock.$httpBackend#expectDELETE
1117 | * @methodOf ngMock.$httpBackend
1118 | * @description
1119 | * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1120 | *
1121 | * @param {string|RegExp} url HTTP url.
1122 | * @param {Object=} headers HTTP headers.
1123 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1124 | * request is handled.
1125 | */
1126 |
1127 | /**
1128 | * @ngdoc method
1129 | * @name ngMock.$httpBackend#expectPOST
1130 | * @methodOf ngMock.$httpBackend
1131 | * @description
1132 | * Creates a new request expectation for POST requests. For more info see `expect()`.
1133 | *
1134 | * @param {string|RegExp} url HTTP url.
1135 | * @param {(string|RegExp)=} data HTTP request body.
1136 | * @param {Object=} headers HTTP headers.
1137 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1138 | * request is handled.
1139 | */
1140 |
1141 | /**
1142 | * @ngdoc method
1143 | * @name ngMock.$httpBackend#expectPUT
1144 | * @methodOf ngMock.$httpBackend
1145 | * @description
1146 | * Creates a new request expectation for PUT requests. For more info see `expect()`.
1147 | *
1148 | * @param {string|RegExp} url HTTP url.
1149 | * @param {(string|RegExp)=} data HTTP request body.
1150 | * @param {Object=} headers HTTP headers.
1151 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1152 | * request is handled.
1153 | */
1154 |
1155 | /**
1156 | * @ngdoc method
1157 | * @name ngMock.$httpBackend#expectPATCH
1158 | * @methodOf ngMock.$httpBackend
1159 | * @description
1160 | * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1161 | *
1162 | * @param {string|RegExp} url HTTP url.
1163 | * @param {(string|RegExp)=} data HTTP request body.
1164 | * @param {Object=} headers HTTP headers.
1165 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1166 | * request is handled.
1167 | */
1168 |
1169 | /**
1170 | * @ngdoc method
1171 | * @name ngMock.$httpBackend#expectJSONP
1172 | * @methodOf ngMock.$httpBackend
1173 | * @description
1174 | * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1175 | *
1176 | * @param {string|RegExp} url HTTP url.
1177 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1178 | * request is handled.
1179 | */
1180 | createShortMethods('expect');
1181 |
1182 |
1183 | /**
1184 | * @ngdoc method
1185 | * @name ngMock.$httpBackend#flush
1186 | * @methodOf ngMock.$httpBackend
1187 | * @description
1188 | * Flushes all pending requests using the trained responses.
1189 | *
1190 | * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1191 | * all pending requests will be flushed. If there are no pending requests when the flush method
1192 | * is called an exception is thrown (as this typically a sign of programming error).
1193 | */
1194 | $httpBackend.flush = function(count) {
1195 | if (!responses.length) throw Error('No pending request to flush !');
1196 |
1197 | if (angular.isDefined(count)) {
1198 | while (count--) {
1199 | if (!responses.length) throw Error('No more pending request to flush !');
1200 | responses.shift()();
1201 | }
1202 | } else {
1203 | while (responses.length) {
1204 | responses.shift()();
1205 | }
1206 | }
1207 | $httpBackend.verifyNoOutstandingExpectation();
1208 | };
1209 |
1210 |
1211 | /**
1212 | * @ngdoc method
1213 | * @name ngMock.$httpBackend#verifyNoOutstandingExpectation
1214 | * @methodOf ngMock.$httpBackend
1215 | * @description
1216 | * Verifies that all of the requests defined via the `expect` api were made. If any of the
1217 | * requests were not made, verifyNoOutstandingExpectation throws an exception.
1218 | *
1219 | * Typically, you would call this method following each test case that asserts requests using an
1220 | * "afterEach" clause.
1221 | *
1222 | *
1223 | * afterEach($httpBackend.verifyExpectations);
1224 | *
1225 | */
1226 | $httpBackend.verifyNoOutstandingExpectation = function() {
1227 | if (expectations.length) {
1228 | throw Error('Unsatisfied requests: ' + expectations.join(', '));
1229 | }
1230 | };
1231 |
1232 |
1233 | /**
1234 | * @ngdoc method
1235 | * @name ngMock.$httpBackend#verifyNoOutstandingRequest
1236 | * @methodOf ngMock.$httpBackend
1237 | * @description
1238 | * Verifies that there are no outstanding requests that need to be flushed.
1239 | *
1240 | * Typically, you would call this method following each test case that asserts requests using an
1241 | * "afterEach" clause.
1242 | *
1243 | *
1244 | * afterEach($httpBackend.verifyNoOutstandingRequest);
1245 | *
1246 | */
1247 | $httpBackend.verifyNoOutstandingRequest = function() {
1248 | if (responses.length) {
1249 | throw Error('Unflushed requests: ' + responses.length);
1250 | }
1251 | };
1252 |
1253 |
1254 | /**
1255 | * @ngdoc method
1256 | * @name ngMock.$httpBackend#resetExpectations
1257 | * @methodOf ngMock.$httpBackend
1258 | * @description
1259 | * Resets all request expectations, but preserves all backend definitions. Typically, you would
1260 | * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1261 | * $httpBackend mock.
1262 | */
1263 | $httpBackend.resetExpectations = function() {
1264 | expectations.length = 0;
1265 | responses.length = 0;
1266 | };
1267 |
1268 | return $httpBackend;
1269 |
1270 |
1271 | function createShortMethods(prefix) {
1272 | angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) {
1273 | $httpBackend[prefix + method] = function(url, headers) {
1274 | return $httpBackend[prefix](method, url, undefined, headers)
1275 | }
1276 | });
1277 |
1278 | angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1279 | $httpBackend[prefix + method] = function(url, data, headers) {
1280 | return $httpBackend[prefix](method, url, data, headers)
1281 | }
1282 | });
1283 | }
1284 | }
1285 |
1286 | function MockHttpExpectation(method, url, data, headers) {
1287 |
1288 | this.data = data;
1289 | this.headers = headers;
1290 |
1291 | this.match = function(m, u, d, h) {
1292 | if (method != m) return false;
1293 | if (!this.matchUrl(u)) return false;
1294 | if (angular.isDefined(d) && !this.matchData(d)) return false;
1295 | if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1296 | return true;
1297 | };
1298 |
1299 | this.matchUrl = function(u) {
1300 | if (!url) return true;
1301 | if (angular.isFunction(url.test)) return url.test(u);
1302 | return url == u;
1303 | };
1304 |
1305 | this.matchHeaders = function(h) {
1306 | if (angular.isUndefined(headers)) return true;
1307 | if (angular.isFunction(headers)) return headers(h);
1308 | return angular.equals(headers, h);
1309 | };
1310 |
1311 | this.matchData = function(d) {
1312 | if (angular.isUndefined(data)) return true;
1313 | if (data && angular.isFunction(data.test)) return data.test(d);
1314 | if (data && !angular.isString(data)) return angular.toJson(data) == d;
1315 | return data == d;
1316 | };
1317 |
1318 | this.toString = function() {
1319 | return method + ' ' + url;
1320 | };
1321 | }
1322 |
1323 | function MockXhr() {
1324 |
1325 | // hack for testing $http, $httpBackend
1326 | MockXhr.$$lastInstance = this;
1327 |
1328 | this.open = function(method, url, async) {
1329 | this.$$method = method;
1330 | this.$$url = url;
1331 | this.$$async = async;
1332 | this.$$reqHeaders = {};
1333 | this.$$respHeaders = {};
1334 | };
1335 |
1336 | this.send = function(data) {
1337 | this.$$data = data;
1338 | };
1339 |
1340 | this.setRequestHeader = function(key, value) {
1341 | this.$$reqHeaders[key] = value;
1342 | };
1343 |
1344 | this.getResponseHeader = function(name) {
1345 | // the lookup must be case insensitive, that's why we try two quick lookups and full scan at last
1346 | var header = this.$$respHeaders[name];
1347 | if (header) return header;
1348 |
1349 | name = angular.lowercase(name);
1350 | header = this.$$respHeaders[name];
1351 | if (header) return header;
1352 |
1353 | header = undefined;
1354 | angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
1355 | if (!header && angular.lowercase(headerName) == name) header = headerVal;
1356 | });
1357 | return header;
1358 | };
1359 |
1360 | this.getAllResponseHeaders = function() {
1361 | var lines = [];
1362 |
1363 | angular.forEach(this.$$respHeaders, function(value, key) {
1364 | lines.push(key + ': ' + value);
1365 | });
1366 | return lines.join('\n');
1367 | };
1368 |
1369 | this.abort = angular.noop;
1370 | }
1371 |
1372 |
1373 | /**
1374 | * @ngdoc function
1375 | * @name ngMock.$timeout
1376 | * @description
1377 | *
1378 | * This service is just a simple decorator for {@link ng.$timeout $timeout} service
1379 | * that adds a "flush" method.
1380 | */
1381 |
1382 | /**
1383 | * @ngdoc method
1384 | * @name ngMock.$timeout#flush
1385 | * @methodOf ngMock.$timeout
1386 | * @description
1387 | *
1388 | * Flushes the queue of pending tasks.
1389 | */
1390 |
1391 | /**
1392 | *
1393 | */
1394 | angular.mock.$RootElementProvider = function() {
1395 | this.$get = function() {
1396 | return angular.element('');
1397 | }
1398 | };
1399 |
1400 | /**
1401 | * @ngdoc overview
1402 | * @name ngMock
1403 | * @description
1404 | *
1405 | * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful
1406 | * mocks to the {@link AUTO.$injector $injector}.
1407 | */
1408 | angular.module('ngMock', ['ng']).provider({
1409 | $browser: angular.mock.$BrowserProvider,
1410 | $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
1411 | $log: angular.mock.$LogProvider,
1412 | $httpBackend: angular.mock.$HttpBackendProvider,
1413 | $rootElement: angular.mock.$RootElementProvider
1414 | }).config(function($provide) {
1415 | $provide.decorator('$timeout', function($delegate, $browser) {
1416 | $delegate.flush = function() {
1417 | $browser.defer.flush();
1418 | };
1419 | return $delegate;
1420 | });
1421 | });
1422 |
1423 |
1424 | /**
1425 | * @ngdoc overview
1426 | * @name ngMockE2E
1427 | * @description
1428 | *
1429 | * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
1430 | * Currently there is only one mock present in this module -
1431 | * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
1432 | */
1433 | angular.module('ngMockE2E', ['ng']).config(function($provide) {
1434 | $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
1435 | });
1436 |
1437 | /**
1438 | * @ngdoc object
1439 | * @name ngMockE2E.$httpBackend
1440 | * @description
1441 | * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
1442 | * applications that use the {@link ng.$http $http service}.
1443 | *
1444 | * *Note*: For fake http backend implementation suitable for unit testing please see
1445 | * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
1446 | *
1447 | * This implementation can be used to respond with static or dynamic responses via the `when` api
1448 | * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
1449 | * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
1450 | * templates from a webserver).
1451 | *
1452 | * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
1453 | * is being developed with the real backend api replaced with a mock, it is often desirable for
1454 | * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
1455 | * templates or static files from the webserver). To configure the backend with this behavior
1456 | * use the `passThrough` request handler of `when` instead of `respond`.
1457 | *
1458 | * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
1459 | * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
1460 | * automatically, closely simulating the behavior of the XMLHttpRequest object.
1461 | *
1462 | * To setup the application to run with this http backend, you have to create a module that depends
1463 | * on the `ngMockE2E` and your application modules and defines the fake backend:
1464 | *
1465 | *
1466 | * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1467 | * myAppDev.run(function($httpBackend) {
1468 | * phones = [{name: 'phone1'}, {name: 'phone2'}];
1469 | *
1470 | * // returns the current list of phones
1471 | * $httpBackend.whenGET('/phones').respond(phones);
1472 | *
1473 | * // adds a new phone to the phones array
1474 | * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1475 | * phones.push(angular.fromJSON(data));
1476 | * });
1477 | * $httpBackend.whenGET(/^\/templates\//).passThrough();
1478 | * //...
1479 | * });
1480 | *
1481 | *
1482 | * Afterwards, bootstrap your app with this new module.
1483 | */
1484 |
1485 | /**
1486 | * @ngdoc method
1487 | * @name ngMockE2E.$httpBackend#when
1488 | * @methodOf ngMockE2E.$httpBackend
1489 | * @description
1490 | * Creates a new backend definition.
1491 | *
1492 | * @param {string} method HTTP method.
1493 | * @param {string|RegExp} url HTTP url.
1494 | * @param {(string|RegExp)=} data HTTP request body.
1495 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1496 | * object and returns true if the headers match the current definition.
1497 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1498 | * control how a matched request is handled.
1499 | *
1500 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
1501 | * – The respond method takes a set of static data to be returned or a function that can return
1502 | * an array containing response status (number), response data (string) and response headers
1503 | * (Object).
1504 | * - passThrough – `{function()}` – Any request matching a backend definition with `passThrough`
1505 | * handler, will be pass through to the real backend (an XHR request will be made to the
1506 | * server.
1507 | */
1508 |
1509 | /**
1510 | * @ngdoc method
1511 | * @name ngMockE2E.$httpBackend#whenGET
1512 | * @methodOf ngMockE2E.$httpBackend
1513 | * @description
1514 | * Creates a new backend definition for GET requests. For more info see `when()`.
1515 | *
1516 | * @param {string|RegExp} url HTTP url.
1517 | * @param {(Object|function(Object))=} headers HTTP headers.
1518 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1519 | * control how a matched request is handled.
1520 | */
1521 |
1522 | /**
1523 | * @ngdoc method
1524 | * @name ngMockE2E.$httpBackend#whenHEAD
1525 | * @methodOf ngMockE2E.$httpBackend
1526 | * @description
1527 | * Creates a new backend definition for HEAD requests. For more info see `when()`.
1528 | *
1529 | * @param {string|RegExp} url HTTP url.
1530 | * @param {(Object|function(Object))=} headers HTTP headers.
1531 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1532 | * control how a matched request is handled.
1533 | */
1534 |
1535 | /**
1536 | * @ngdoc method
1537 | * @name ngMockE2E.$httpBackend#whenDELETE
1538 | * @methodOf ngMockE2E.$httpBackend
1539 | * @description
1540 | * Creates a new backend definition for DELETE requests. For more info see `when()`.
1541 | *
1542 | * @param {string|RegExp} url HTTP url.
1543 | * @param {(Object|function(Object))=} headers HTTP headers.
1544 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1545 | * control how a matched request is handled.
1546 | */
1547 |
1548 | /**
1549 | * @ngdoc method
1550 | * @name ngMockE2E.$httpBackend#whenPOST
1551 | * @methodOf ngMockE2E.$httpBackend
1552 | * @description
1553 | * Creates a new backend definition for POST requests. For more info see `when()`.
1554 | *
1555 | * @param {string|RegExp} url HTTP url.
1556 | * @param {(string|RegExp)=} data HTTP request body.
1557 | * @param {(Object|function(Object))=} headers HTTP headers.
1558 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1559 | * control how a matched request is handled.
1560 | */
1561 |
1562 | /**
1563 | * @ngdoc method
1564 | * @name ngMockE2E.$httpBackend#whenPUT
1565 | * @methodOf ngMockE2E.$httpBackend
1566 | * @description
1567 | * Creates a new backend definition for PUT requests. For more info see `when()`.
1568 | *
1569 | * @param {string|RegExp} url HTTP url.
1570 | * @param {(string|RegExp)=} data HTTP request body.
1571 | * @param {(Object|function(Object))=} headers HTTP headers.
1572 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1573 | * control how a matched request is handled.
1574 | */
1575 |
1576 | /**
1577 | * @ngdoc method
1578 | * @name ngMockE2E.$httpBackend#whenPATCH
1579 | * @methodOf ngMockE2E.$httpBackend
1580 | * @description
1581 | * Creates a new backend definition for PATCH requests. For more info see `when()`.
1582 | *
1583 | * @param {string|RegExp} url HTTP url.
1584 | * @param {(string|RegExp)=} data HTTP request body.
1585 | * @param {(Object|function(Object))=} headers HTTP headers.
1586 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1587 | * control how a matched request is handled.
1588 | */
1589 |
1590 | /**
1591 | * @ngdoc method
1592 | * @name ngMockE2E.$httpBackend#whenJSONP
1593 | * @methodOf ngMockE2E.$httpBackend
1594 | * @description
1595 | * Creates a new backend definition for JSONP requests. For more info see `when()`.
1596 | *
1597 | * @param {string|RegExp} url HTTP url.
1598 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1599 | * control how a matched request is handled.
1600 | */
1601 | angular.mock.e2e = {};
1602 | angular.mock.e2e.$httpBackendDecorator = ['$delegate', '$browser', createHttpBackendMock];
1603 |
1604 |
1605 | angular.mock.clearDataCache = function() {
1606 | var key,
1607 | cache = angular.element.cache;
1608 |
1609 | for(key in cache) {
1610 | if (cache.hasOwnProperty(key)) {
1611 | var handle = cache[key].handle;
1612 |
1613 | handle && angular.element(handle.elem).unbind();
1614 | delete cache[key];
1615 | }
1616 | }
1617 | };
1618 |
1619 |
1620 | window.jstestdriver && (function(window) {
1621 | /**
1622 | * Global method to output any number of objects into JSTD console. Useful for debugging.
1623 | */
1624 | window.dump = function() {
1625 | var args = [];
1626 | angular.forEach(arguments, function(arg) {
1627 | args.push(angular.mock.dump(arg));
1628 | });
1629 | jstestdriver.console.log.apply(jstestdriver.console, args);
1630 | if (window.console) {
1631 | window.console.log.apply(window.console, args);
1632 | }
1633 | };
1634 | })(window);
1635 |
1636 |
1637 | window.jasmine && (function(window) {
1638 |
1639 | afterEach(function() {
1640 | var spec = getCurrentSpec();
1641 | var injector = spec.$injector;
1642 |
1643 | spec.$injector = null;
1644 | spec.$modules = null;
1645 |
1646 | if (injector) {
1647 | injector.get('$rootElement').unbind();
1648 | injector.get('$browser').pollFns.length = 0;
1649 | }
1650 |
1651 | angular.mock.clearDataCache();
1652 |
1653 | // clean up jquery's fragment cache
1654 | angular.forEach(angular.element.fragments, function(val, key) {
1655 | delete angular.element.fragments[key];
1656 | });
1657 |
1658 | MockXhr.$$lastInstance = null;
1659 |
1660 | angular.forEach(angular.callbacks, function(val, key) {
1661 | delete angular.callbacks[key];
1662 | });
1663 | angular.callbacks.counter = 0;
1664 | });
1665 |
1666 | function getCurrentSpec() {
1667 | return jasmine.getEnv().currentSpec;
1668 | }
1669 |
1670 | function isSpecRunning() {
1671 | var spec = getCurrentSpec();
1672 | return spec && spec.queue.running;
1673 | }
1674 |
1675 | /**
1676 | * @ngdoc function
1677 | * @name angular.mock.module
1678 | * @description
1679 | *
1680 | * *NOTE*: This function is also published on window for easy access.
1681 | * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}.
1682 | *
1683 | * This function registers a module configuration code. It collects the configuration information
1684 | * which will be used when the injector is created by {@link angular.mock.inject inject}.
1685 | *
1686 | * See {@link angular.mock.inject inject} for usage example
1687 | *
1688 | * @param {...(string|Function)} fns any number of modules which are represented as string
1689 | * aliases or as anonymous module initialization functions. The modules are used to
1690 | * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded.
1691 | */
1692 | window.module = angular.mock.module = function() {
1693 | var moduleFns = Array.prototype.slice.call(arguments, 0);
1694 | return isSpecRunning() ? workFn() : workFn;
1695 | /////////////////////
1696 | function workFn() {
1697 | var spec = getCurrentSpec();
1698 | if (spec.$injector) {
1699 | throw Error('Injector already created, can not register a module!');
1700 | } else {
1701 | var modules = spec.$modules || (spec.$modules = []);
1702 | angular.forEach(moduleFns, function(module) {
1703 | modules.push(module);
1704 | });
1705 | }
1706 | }
1707 | };
1708 |
1709 | /**
1710 | * @ngdoc function
1711 | * @name angular.mock.inject
1712 | * @description
1713 | *
1714 | * *NOTE*: This function is also published on window for easy access.
1715 | * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}.
1716 | *
1717 | * The inject function wraps a function into an injectable function. The inject() creates new
1718 | * instance of {@link AUTO.$injector $injector} per test, which is then used for
1719 | * resolving references.
1720 | *
1721 | * See also {@link angular.mock.module module}
1722 | *
1723 | * Example of what a typical jasmine tests looks like with the inject method.
1724 | *
1725 | *
1726 | * angular.module('myApplicationModule', [])
1727 | * .value('mode', 'app')
1728 | * .value('version', 'v1.0.1');
1729 | *
1730 | *
1731 | * describe('MyApp', function() {
1732 | *
1733 | * // You need to load modules that you want to test,
1734 | * // it loads only the "ng" module by default.
1735 | * beforeEach(module('myApplicationModule'));
1736 | *
1737 | *
1738 | * // inject() is used to inject arguments of all given functions
1739 | * it('should provide a version', inject(function(mode, version) {
1740 | * expect(version).toEqual('v1.0.1');
1741 | * expect(mode).toEqual('app');
1742 | * }));
1743 | *
1744 | *
1745 | * // The inject and module method can also be used inside of the it or beforeEach
1746 | * it('should override a version and test the new version is injected', function() {
1747 | * // module() takes functions or strings (module aliases)
1748 | * module(function($provide) {
1749 | * $provide.value('version', 'overridden'); // override version here
1750 | * });
1751 | *
1752 | * inject(function(version) {
1753 | * expect(version).toEqual('overridden');
1754 | * });
1755 | * ));
1756 | * });
1757 | *
1758 | *
1759 | *
1760 | * @param {...Function} fns any number of functions which will be injected using the injector.
1761 | */
1762 | window.inject = angular.mock.inject = function() {
1763 | var blockFns = Array.prototype.slice.call(arguments, 0);
1764 | var errorForStack = new Error('Declaration Location');
1765 | return isSpecRunning() ? workFn() : workFn;
1766 | /////////////////////
1767 | function workFn() {
1768 | var spec = getCurrentSpec();
1769 | var modules = spec.$modules || [];
1770 | modules.unshift('ngMock');
1771 | modules.unshift('ng');
1772 | var injector = spec.$injector;
1773 | if (!injector) {
1774 | injector = spec.$injector = angular.injector(modules);
1775 | }
1776 | for(var i = 0, ii = blockFns.length; i < ii; i++) {
1777 | try {
1778 | injector.invoke(blockFns[i] || angular.noop, this);
1779 | } catch (e) {
1780 | if(e.stack && errorForStack) e.stack += '\n' + errorForStack.stack;
1781 | throw e;
1782 | } finally {
1783 | errorForStack = null;
1784 | }
1785 | }
1786 | }
1787 | };
1788 | })(window);
1789 |
--------------------------------------------------------------------------------
/test/lib/angular/version.txt:
--------------------------------------------------------------------------------
1 | 1.0.7
2 |
--------------------------------------------------------------------------------
/test/unit/controllersSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for controllers go here */
4 |
5 | describe('controllers', function(){
6 | beforeEach(module('myApp.controllers'));
7 |
8 |
9 | it('should ....', inject(function() {
10 | //spec body
11 | }));
12 |
13 | it('should ....', inject(function() {
14 | //spec body
15 | }));
16 | });
17 |
--------------------------------------------------------------------------------
/test/unit/directivesSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for directives go here */
4 |
5 | describe('directives', function() {
6 | beforeEach(module('myApp.directives'));
7 |
8 | describe('app-version', function() {
9 | it('should print current version', function() {
10 | module(function($provide) {
11 | $provide.value('version', 'TEST_VER');
12 | });
13 | inject(function($compile, $rootScope) {
14 | var element = $compile('')($rootScope);
15 | expect(element.text()).toEqual('TEST_VER');
16 | });
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/unit/filtersSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for filters go here */
4 |
5 | describe('filter', function() {
6 | beforeEach(module('myApp.filters'));
7 |
8 |
9 | describe('interpolate', function() {
10 | beforeEach(module(function($provide) {
11 | $provide.value('version', 'TEST_VER');
12 | }));
13 |
14 |
15 | it('should replace VERSION', inject(function(interpolateFilter) {
16 | expect(interpolateFilter('before %VERSION% after')).toEqual('before TEST_VER after');
17 | }));
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/unit/servicesSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for services go here */
4 |
5 | describe('service', function() {
6 | beforeEach(module('myApp.services'));
7 |
8 |
9 | describe('version', function() {
10 | it('should return current version', inject(function(version) {
11 | expect(version).toEqual('0.1');
12 | }));
13 | });
14 | });
15 |
--------------------------------------------------------------------------------