diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 11f84b0f9..217c7226b 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -30,7 +30,7 @@ module.exports = (grunt) -> test: unit: 'simplemocha:unit' client: 'test/client/karma.conf.js' - e2e: ['test/e2e/*/karma.conf.js', 'test/e2e/*/karma.conf.coffee', 'test/e2e/*/karma.conf.ls'] + e2e: 'cucumberjs:ci' watch: client: @@ -46,6 +46,22 @@ module.exports = (grunt) -> 'test/unit/mocha-globals.coffee' 'test/unit/**/*.coffee' ] + cucumberjs: + options: + steps: 'test/e2e/steps' + format: 'progress' + all: 'test/e2e/*.feature' + current: + files: + src: 'test/e2e/*.feature' + options: + tags: '@current' + ci: + files: + src: 'test/e2e/*.feature' + options: + tags: '~@not-jenkins' + # JSHint options # http://www.jshint.com/options/ @@ -132,6 +148,7 @@ module.exports = (grunt) -> grunt.loadTasks 'tasks' + # Load grunt tasks automatically require('load-grunt-tasks') grunt diff --git a/package.json b/package.json index fce60e656..ec8835e9d 100644 --- a/package.json +++ b/package.json @@ -181,46 +181,50 @@ "source-map": "~0.1.31" }, "devDependencies": { + "LiveScript": "~1.2.0", + "chai": "~1.9.1", + "chai-as-promised": "~4.1.0", + "coffee-errors": "~0.8.6", + "coffee-script": "~1.7.1", + "cucumber": "^0.4.7", "grunt": "~0.4", - "grunt-simple-mocha": "*", + "grunt-auto-release": "~0.0.3", + "grunt-browserify": "~2.1.3", + "grunt-bump": "~0.0.10", + "grunt-coffeelint": "~0.0.6", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-watch": "~0.6.1", - "grunt-coffeelint": "~0.0.6", - "grunt-npm": "~0.0.1", - "grunt-bump": "~0.0.10", "grunt-conventional-changelog": "~1.1.0", - "grunt-auto-release": "~0.0.3", - "grunt-browserify": "~2.1.3", + "grunt-cucumberjs": "^0.5.0", + "grunt-jscs-checker": "~0.6.1", + "grunt-npm": "~0.0.1", + "grunt-simple-mocha": "*", + "karma-browserstack-launcher": "*", + "karma-chrome-launcher": "*", + "karma-coffee-preprocessor": "*", + "karma-commonjs": "*", + "karma-coverage": "*", + "karma-firefox-launcher": "*", + "karma-growl-reporter": "*", + "karma-html2js-preprocessor": "*", + "karma-jasmine": "~0.1.1", + "karma-junit-reporter": "*", + "karma-live-preprocessor": "*", + "karma-mocha": "*", + "karma-ng-scenario": "*", + "karma-phantomjs-launcher": "*", + "karma-qunit": "*", + "karma-requirejs": "*", + "karma-sauce-launcher": "*", + "karma-script-launcher": "^0.1.0", "load-grunt-tasks": "~0.6.0", - "mocks": "~0.0.10", - "which": "~1.0", + "mkdirp": "~0.3.5", "mocha": "~1.20.1", - "chai": "~1.9.1", - "chai-as-promised": "~4.1.0", + "mocks": "~0.0.10", "sinon": "~1.10.3", "sinon-chai": "~2.5.0", "timer-shim": "~0.3.0", - "karma-jasmine": "~0.1.0", - "karma-mocha": "*", - "karma-qunit": "*", - "karma-coverage": "*", - "karma-requirejs": "*", - "karma-commonjs": "*", - "karma-growl-reporter": "*", - "karma-junit-reporter": "*", - "karma-chrome-launcher": "*", - "karma-firefox-launcher": "*", - "karma-sauce-launcher": "*", - "karma-phantomjs-launcher": "*", - "karma-ng-scenario": "*", - "karma-coffee-preprocessor": "*", - "karma-live-preprocessor": "*", - "karma-html2js-preprocessor": "*", - "karma-browserstack-launcher": "*", - "LiveScript": "~1.2.0", - "coffee-errors": "~0.8.6", - "coffee-script": "~1.7.1", - "grunt-jscs-checker": "~0.6.1" + "which": "~1.0" }, "main": "./lib/index", "bin": {}, diff --git a/tasks/test.js b/tasks/test.js index f42458abd..cae66e9f5 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -6,7 +6,6 @@ module.exports = function(grunt) { * grunt test * grunt test:unit * grunt test:client - * grunt test:e2e */ grunt.registerMultiTask('test', 'Run tests.', function() { var specDone = this.async(); @@ -37,88 +36,6 @@ module.exports = function(grunt) { }); }; - - // E2E tests - if (this.target === 'e2e') { - var tests = grunt.file.expand(this.data); - var processToKill; - var args = [ - 'start', null, '--single-run', '--no-auto-watch' - ]; - - - var next = function(err, result, code) { - var testArgs = []; - if (processToKill) { - processToKill.kill(); - } - - if (err || code) { - console.error(err); - grunt.fail.fatal('E2E test "' + args[1] + '" failed.', code); - } else { - args[1] = tests.shift(); - if (args[1]) { - if (args[1] === 'test/e2e/angular-scenario/karma.conf.js') { - processToKill = grunt.util.spawn({ - cmd: node, - args: ['test/e2e/angular-scenario/server.js'] - }, function() {}); - } - - if (args[1] === 'test/e2e/pass-opts/karma.conf.js') { - var serverArgs = args.slice(); - serverArgs.splice(args.indexOf('--single-run'), 1); - var done = false; - var cont = function() { - if (!done) { - done = true; - next.apply(this, arguments); - } - }; - - processToKill = grunt.util.spawn({ - cmd: node, - args: [cmd].concat(serverArgs), - opts: {stdio: [process.stdin, 'pipe', process.stderr]} - }, cont); - - var onData = function(data) { - data = data.toString(); - // wait for the browser to connect - if (/Connected on socket/.test(data)) { - processToKill.stdout.removeListener('data', onData); - spawnKarma(['run', '--','arg1','arg2','arg3'], cont); - } else { - console.log(data); - } - }; - - processToKill.stdout.on('data', onData); - } else { - spawnKarma(args.concat(testArgs), next); - } - } else { - specDone(); - } - } - }; - - - // run only e2e tests specified by args - if (arguments.length) { - var slicedArgs = Array.prototype.slice.call(arguments); - - tests = tests.filter(function(configFile) { - return slicedArgs.some(function(test) { - return configFile.indexOf(test) !== -1; - }); - }); - } - - return next(); - } - // CLIENT unit tests if (this.target === 'client') { return exec(['start', this.data, '--single-run', '--no-auto-watch', '--reporters=dots'], diff --git a/test/e2e/angular-scenario/angular.min.js b/test/e2e/angular-scenario/angular.min.js deleted file mode 100644 index 2b220688d..000000000 --- a/test/e2e/angular-scenario/angular.min.js +++ /dev/null @@ -1,163 +0,0 @@ -/* - AngularJS v1.0.7 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(P,T,q){'use strict';function m(b,a,c){var d;if(b)if(H(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==m)b.forEach(a,c);else if(!b||typeof b.length!=="number"?0:typeof b.hasOwnProperty!="function"&&typeof b.constructor!="function"||b instanceof K||ca&&b instanceof ca||wa.call(b)!=="[object Object]"||typeof b.callee==="function")for(d=0;d=0&&b.splice(c,1);return a}function U(b,a){if(oa(b)||b&&b.$evalAsync&&b.$watch)throw Error("Can't copy Window or Scope");if(a){if(b===a)throw Error("Can't copy equivalent objects or arrays");if(E(b))for(var c=a.length=0;c2?ha.call(arguments,2):[];return H(a)&&!(a instanceof RegExp)?c.length?function(){return arguments.length?a.apply(b,c.concat(ha.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function ic(b,a){var c=a;/^\$+/.test(b)?c=q:oa(a)?c="$WINDOW":a&&T===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function da(b,a){return JSON.stringify(b, -ic,a?" ":null)}function pb(b){return B(b)?JSON.parse(b):b}function Ua(b){b&&b.length!==0?(b=z(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1;return b}function pa(b){b=u(b).clone();try{b.html("")}catch(a){}var c=u("
").append(b).html();try{return b[0].nodeType===3?z(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+z(b)})}catch(d){return z(c)}}function Va(b){var a={},c,d;m((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]), -a[d]=y(c[1])?decodeURIComponent(c[1]):!0)});return a}function qb(b){var a=[];m(b,function(b,d){a.push(Wa(d,!0)+(b===!0?"":"="+Wa(b,!0)))});return a.length?a.join("&"):""}function Xa(b){return Wa(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Wa(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function jc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,h=["ng:app","ng-app","x-ng-app", -"data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;m(h,function(a){h[a]=!0;c(T.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(m(b.querySelectorAll("."+a),c),m(b.querySelectorAll("."+a+"\\:"),c),m(b.querySelectorAll("["+a+"]"),c))});m(d,function(a){if(!e){var b=f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):m(a.attributes,function(b){if(!e&&h[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function rb(b,a){var c=function(){b=u(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement", -b)}]);a.unshift("ng");var c=sb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(P&&!d.test(P.name))return c();P.name=P.name.replace(d,"");Ya.resumeBootstrap=function(b){m(b,function(b){a.push(b)});c()}}function Za(b,a){a=a||"_";return b.replace(kc,function(b,d){return(d?a:"")+b.toLowerCase()})}function $a(b,a,c){if(!b)throw Error("Argument '"+(a||"?")+"' is "+(c||"required")); -return b}function qa(b,a,c){c&&E(b)&&(b=b[b.length-1]);$a(H(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function lc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object),"module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c,d,e){return function(){b[e||"push"]([c,d,arguments]);return k}}if(!e)throw Error("No module: "+d);var b=[],c=[],j=a("$injector", -"invoke"),k={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:j,run:function(a){c.push(a);return this}};g&&j(g);return k})}})}function tb(b){return b.replace(mc,function(a,b,d,e){return e?d.toUpperCase(): -d}).replace(nc,"Moz$1")}function ab(b,a){function c(){var e;for(var b=[this],c=a,h,f,i,j,k,l;b.length;){h=b.shift();f=0;for(i=h.length;f 
"+b;a.removeChild(a.firstChild);bb(this,a.childNodes);this.remove()}else bb(this,b)}function cb(b){return b.cloneNode(!0)}function ra(b){ub(b);for(var a=0,b=b.childNodes||[];a-1}function xb(b,a){a&&m(a.split(" "),function(a){b.className=Q((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+Q(a)+" "," "))})} -function yb(b,a){a&&m(a.split(" "),function(a){if(!Ca(b,a))b.className=Q(b.className+" "+Q(a))})}function bb(b,a){if(a)for(var a=!a.nodeName&&y(a.length)&&!oa(a)?a:[a],c=0;c4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!")}else{if(i.cookie!==$){$=i.cookie;d=$.split("; ");r={};for(f=0;f0&&(a=unescape(e.substring(0,j)),r[a]===q&&(r[a]=unescape(e.substring(j+1))))}return r}};f.defer=function(a,b){var c; -p++;c=l(function(){delete o[c];e(a)},b||0);o[c]=!0;return c};f.defer.cancel=function(a){return o[a]?(delete o[a],n(a),e(C),!0):!1}}function wc(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new vc(b,d,a,c)}]}function xc(){this.$get=function(){function b(b,d){function e(a){if(a!=l){if(n){if(n==a)n=a.n}else n=a;g(a.n,a.p);g(a,l);l=a;l.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw Error("cacheId "+b+" taken");var h=0,f=v({},d,{id:b}),i={},j=d&& -d.capacity||Number.MAX_VALUE,k={},l=null,n=null;return a[b]={put:function(a,b){var c=k[a]||(k[a]={key:a});e(c);w(b)||(a in i||h++,i[a]=b,h>j&&this.remove(n.key))},get:function(a){var b=k[a];if(b)return e(b),i[a]},remove:function(a){var b=k[a];if(b){if(b==l)l=b.p;if(b==n)n=b.n;g(b.n,b.p);delete k[a];delete i[a];h--}},removeAll:function(){i={};h=0;k={};l=n=null},destroy:function(){k=f=i=null;delete a[b]},info:function(){return v({},f,{size:h})}}}var a={};b.info=function(){var b={};m(a,function(a,e){b[e]= -a.info()});return b};b.get=function(b){return a[b]};return b}}function yc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Db(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ",h=/^\s*(https?|ftp|mailto|file):/;this.directive=function i(d,e){B(d)?($a(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];m(a[d], -function(a){try{var g=b.invoke(a);if(H(g))g={compile:I(g)};else if(!g.compile&&g.link)g.compile=I(g.link);g.priority=g.priority||0;g.name=g.name||d;g.require=g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(h){c(h)}});return e}])),a[d].push(e)):m(d,nb(i));return this};this.urlSanitizationWhitelist=function(a){return y(a)?(h=a,this):h};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document",function(b, -j,k,l,n,o,p,s,t){function x(a,b,c){a instanceof u||(a=u(a));m(a,function(b,c){b.nodeType==3&&b.nodeValue.match(/\S+/)&&(a[c]=u(b).wrap("").parent()[0])});var d=A(a,b,a,c);return function(b,c){$a(b,"scope");for(var e=c?ua.clone.call(a):a,j=0,g=e.length;jr.priority)break;if(Y=r.scope)ta("isolated scope",J,r,D),L(Y)&&(M(D,"ng-isolate-scope"),J=r),M(D,"ng-scope"),s=s||r;F=r.name;if(Y=r.controller)y=y||{},ta("'"+F+"' controller",y[F],r,D),y[F]=r;if(Y=r.transclude)ta("transclusion",ja,r,D),ja=r,l=r.priority,Y=="element"?(W=u(b),D=c.$$element=u(T.createComment(" "+ -F+": "+c[F]+" ")),b=D[0],C(e,u(W[0]),b),V=x(W,d,l)):(W=u(cb(b)).contents(),D.html(""),V=x(W,d));if(Y=r.template)if(ta("template",A,r,D),A=r,Y=Fb(Y),r.replace){W=u("
"+Q(Y)+"
").contents();b=W[0];if(W.length!=1||b.nodeType!==1)throw Error(g+Y);C(e,D,b);F={$attr:{}};a=a.concat(N(b,a.splice(v+1,a.length-(v+1)),F));$(c,F);z=a.length}else D.html(Y);if(r.templateUrl)ta("template",A,r,D),A=r,i=R(a.splice(v,a.length-v),i,D,c,e,r.replace,V),z=a.length;else if(r.compile)try{w=r.compile(D,c,V),H(w)? -j(null,w):w&&j(w.pre,w.post)}catch(G){k(G,pa(D))}if(r.terminal)i.terminal=!0,l=Math.max(l,r.priority)}i.scope=s&&s.scope;i.transclude=ja&&V;return i}function r(d,e,g,j){var h=!1;if(a.hasOwnProperty(e))for(var o,e=b.get(e+c),l=0,p=e.length;lo.priority)&&o.restrict.indexOf(g)!=-1)d.push(o),h=!0}catch(n){k(n)}return h}function $(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;m(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});m(b, -function(b,g){g=="class"?(M(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):g=="style"?e.attr("style",e.attr("style")+";"+b):g.charAt(0)!="$"&&!a.hasOwnProperty(g)&&(a[g]=b,d[g]=c[g])})}function R(a,b,c,d,e,j,h){var i=[],k,o,p=c[0],t=a.shift(),s=v({},t,{controller:null,templateUrl:null,transclude:null,scope:null});c.html("");l.get(t.templateUrl,{cache:n}).success(function(l){var n,t,l=Fb(l);if(j){t=u("
"+Q(l)+"
").contents();n=t[0];if(t.length!=1||n.nodeType!==1)throw Error(g+l);l={$attr:{}}; -C(e,c,n);N(n,a,l);$(d,l)}else n=p,c.html(l);a.unshift(s);k=J(a,n,d,h);for(o=A(c[0].childNodes,h);i.length;){var r=i.pop(),l=i.pop();t=i.pop();var ia=i.pop(),D=n;t!==p&&(D=cb(n),C(l,u(t),D));k(function(){b(o,ia,D,e,r)},ia,D,e,r)}i=null}).error(function(a,b,c,d){throw Error("Failed to load template: "+d.url);});return function(a,c,d,e,g){i?(i.push(c),i.push(d),i.push(e),i.push(g)):k(function(){b(o,c,d,e,g)},c,d,e,g)}}function F(a,b){return b.priority-a.priority}function ta(a,b,c,d){if(b)throw Error("Multiple directives ["+ -b.name+", "+c.name+"] asking for "+a+" on: "+pa(d));}function y(a,b){var c=j(b,!0);c&&a.push({priority:0,compile:I(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);M(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function V(a,b,c,d){var e=j(c,!0);e&&b.push({priority:100,compile:I(function(a,b,c){b=c.$$observers||(c.$$observers={});d==="class"&&(e=j(c[d],!0));c[d]=q;(b[d]||(b[d]=[])).$$inter=!0;(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e, -function(a){c.$set(d,a)})})})}function C(a,b,c){var d=b[0],e=d.parentNode,g,j;if(a){g=0;for(j=a.length;g -0){var e=R[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,c,d,f){return(b=h(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),R.shift(),b):!1}function i(a){f(a)||e("is unexpected, expecting ["+a+"]",h())}function j(a,b){return function(c,d){return a(c,d,b)}}function k(a,b,c){return function(d,e){return b(d,e,a,c)}}function l(){for(var a=[];;)if(R.length>0&&!h("}",")",";","]")&&a.push(w()),!f(";"))return a.length==1?a[0]:function(b,c){for(var d,e=0;e","<=",">="))a=k(a,b.fn,t());return a}function x(){for(var a=m(),b;b=f("*","/","%");)a=k(a,b.fn,m());return a}function m(){var a;return f("+")?A():(a=f("-"))?k(r,a.fn,m()):(a=f("!"))?j(a.fn,m()):A()}function A(){var a;if(f("("))a=w(),i(")");else if(f("["))a=N();else if(f("{"))a=J();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=y(a,c),c=null):b.text==="["?(c=a,a=V(a)):b.text==="."?(c=a,a=u(a)):e("IMPOSSIBLE");return a}function N(){var a= -[];if(g().text!="]"){do a.push(F());while(f(","))}i("]");return function(b,c){for(var d=[],e=0;e1;d++){var e=a.shift(),g=b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]= -c}function gb(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,h=0;h7),hasEvent:function(c){if(c=="input"&&Z==9)return!1;if(w(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Vc(){this.$get=I(P)}function Ob(b){var a={},c,d,e;if(!b)return a;m(b.split("\n"),function(b){e=b.indexOf(":");c=z(Q(b.substr(0, -e)));d=Q(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Pb(b){var a=L(b)?b:q;return function(c){a||(a=Ob(b));return c?a[z(c)]||null:a}}function Qb(b,a,c){if(H(c))return c(b,a);m(c,function(c){b=c(b,a)});return b}function Wc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){B(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=pb(d,!0)));return d}],transformRequest:[function(a){return L(a)&&wa.apply(a)!=="[object File]"?da(a):a}], -headers:{common:{Accept:"application/json, text/plain, */*","X-Requested-With":"XMLHttpRequest"},post:{"Content-Type":"application/json;charset=utf-8"},put:{"Content-Type":"application/json;charset=utf-8"}}},e=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,i,j,k){function l(a){function c(a){var b=v({},a,{data:Qb(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:j.reject(b)}a.method=la(a.method);var e=a.transformRequest|| -d.transformRequest,f=a.transformResponse||d.transformResponse,g=d.headers,g=v({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},g.common,g[z(a.method)],a.headers),e=Qb(a.data,Pb(g),e),i;w(a.data)&&delete g["Content-Type"];i=n(a,e,g);i=i.then(c,c);m(s,function(a){i=a(i)});i.success=function(b){i.then(function(c){b(c.data,c.status,c.headers,a)});return i};i.error=function(b){i.then(null,function(c){b(c.data,c.status,c.headers,a)});return i};return i}function n(b,c,d){function e(a,b,c){m&&(200<=a&&a<300?m.put(q, -[a,b,Ob(c)]):m.remove(q));f(b,a,c);i.$apply()}function f(a,c,d){c=Math.max(c,0);(200<=c&&c<300?k.resolve:k.reject)({data:a,status:c,headers:Pb(d),config:b})}function h(){var a=za(l.pendingRequests,b);a!==-1&&l.pendingRequests.splice(a,1)}var k=j.defer(),n=k.promise,m,s,q=o(b.url,b.params);l.pendingRequests.push(b);n.then(h,h);b.cache&&b.method=="GET"&&(m=L(b.cache)?b.cache:p);if(m)if(s=m.get(q))if(s.then)return s.then(h,h),s;else E(s)?f(s[1],s[0],U(s[2])):f(s,200,{});else m.put(q,n);s||a(b.method, -q,c,e,d,b.timeout,b.withCredentials);return n}function o(a,b){if(!b)return a;var c=[];fc(b,function(a,b){a==null||a==q||(L(a)&&(a=da(a)),c.push(encodeURIComponent(b)+"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var p=c("$http"),s=[];m(e,function(a){s.push(B(a)?k.get(a):k.invoke(a))});l.pendingRequests=[];(function(a){m(arguments,function(a){l[a]=function(b,c){return l(v(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){m(arguments,function(a){l[a]= -function(b,c,d){return l(v(d||{},{method:a,url:b,data:c}))}})})("post","put");l.defaults=d;return l}]}function Xc(){this.$get=["$browser","$window","$document",function(b,a,c){return Yc(b,Zc,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function Yc(b,a,c,d,e,g){function h(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;Z?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror= -d;e.body.appendChild(c)}return function(e,i,j,k,l,n,o){function p(a,c,d,e){c=(i.match(Hb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(C)}b.$$incOutstandingRequestCount();i=i||b.url();if(z(e)=="jsonp"){var s="_"+(d.counter++).toString(36);d[s]=function(a){d[s].data=a};h(i.replace("JSON_CALLBACK","angular.callbacks."+s),function(){d[s].data?p(k,200,d[s].data):p(k,-2);delete d[s]})}else{var t=new a;t.open(e,i,!0);m(l,function(a,b){a&&t.setRequestHeader(b,a)}); -var q;t.onreadystatechange=function(){if(t.readyState==4){var a=t.getAllResponseHeaders(),b=["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"];a||(a="",m(b,function(b){var c=t.getResponseHeader(b);c&&(a+=b+": "+c+"\n")}));p(k,q||t.status,t.responseText,a)}};if(o)t.withCredentials=!0;t.send(j||"");n>0&&c(function(){q=-1;t.abort()},n)}}}function $c(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0, -maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","), -AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function ad(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,i){var j=c.defer(),k=j.promise,l=y(i)&&!i,f=a.defer(function(){try{j.resolve(e())}catch(a){j.reject(a),d(a)}l||b.$apply()},f),i=function(){delete g[k.$$timeoutId]}; -k.$$timeoutId=f;g[f]=j;k.then(i,i);return k}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):!1};return e}]}function Rb(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Sb);a("date",Tb);a("filter",bd);a("json",cd);a("limitTo",dd);a("lowercase",ed);a("number",Ub);a("orderBy",Vb);a("uppercase",fd)}function bd(){return function(b, -a){if(!E(b))return b;var c=[];c.check=function(a){for(var b=0;b-1;case "object":for(var c in a)if(c.charAt(0)!=="$"&&d(a[c],b))return!0;return!1;case "array":for(c=0;ce+1?h="0":(f=h,j=!0)}if(!j){h=(h.split(Xb)[1]||"").length;w(e)&&(e=Math.min(Math.max(a.minFrac,h),a.maxFrac));var h=Math.pow(10,e),b=Math.round(b*h)/h,b=(""+b).split(Xb),h=b[0],b=b[1]||"",j=0,k=a.lgSize, -l=a.gSize;if(h.length>=k+l)for(var j=h.length-k,n=0;n0||e> --c)e+=c;e===0&&c==-12&&(e=12);return jb(e,a,d)}}function Ja(b,a){return function(c,d){var e=c["get"+b](),g=la(a?"SHORT"+b:b);return d[g][e]}}function Tb(b){function a(a){var b;if(b=a.match(c)){var a=new Date(0),g=0,h=0;b[9]&&(g=G(b[9]+b[10]),h=G(b[9]+b[11]));a.setUTCFullYear(G(b[1]),G(b[2])-1,G(b[3]));a.setUTCHours(G(b[4]||0)-g,G(b[5]||0)-h,G(b[6]||0),G(b[7]||0))}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c, -e){var g="",h=[],f,i,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;B(c)&&(c=gd.test(c)?G(c):a(c));Qa(c)&&(c=new Date(c));if(!na(c))return c;for(;e;)(i=hd.exec(e))?(h=h.concat(ha.call(i,1)),e=h.pop()):(h.push(e),e=null);m(h,function(a){f=id[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function cd(){return function(b){return da(b,!0)}}function dd(){return function(b,a){if(!(b instanceof Array))return b;var a=G(a),c=[],d,e;if(!b||!(b instanceof Array))return c; -a>b.length?a=b.length:a<-b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;dn?(d.$setValidity("maxlength",!1),q):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function kb(b,a){b="ngClass"+b;return S(function(c,d,e){function g(b){if(a===!0||c.$index%2===a)i&&!fa(b,i)&&h(i),f(b);i=U(b)}function h(a){L(a)&& -!E(a)&&(a=Ra(a,function(a,b){if(a)return b}));d.removeClass(E(a)?a.join(" "):a)}function f(a){L(a)&&!E(a)&&(a=Ra(a,function(a,b){if(a)return b}));a&&d.addClass(E(a)?a.join(" "):a)}var i=q;c.$watch(e[b],g,!0);e.$observe("class",function(){var a=c.$eval(e[b]);g(a,a)});b!=="ngClass"&&c.$watch("$index",function(d,g){var i=d&1;i!==g&1&&(i===a?f(c.$eval(e[b])):h(c.$eval(e[b])))})})}var z=function(b){return B(b)?b.toLowerCase():b},la=function(b){return B(b)?b.toUpperCase():b},Z=G((/msie (\d+)/.exec(z(navigator.userAgent))|| -[])[1]),u,ca,ha=[].slice,Pa=[].push,wa=Object.prototype.toString,Ya=P.angular||(P.angular={}),sa,fb,aa=["0","0","0"];C.$inject=[];ma.$inject=[];fb=Z<9?function(b){b=b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?la(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var kc=/[A-Z]/g,jd={full:"1.0.7",major:1,minor:0,dot:7,codeName:"monochromatic-rainbow"},Ba=K.cache={},Aa=K.expando="ng-"+(new Date).getTime(),oc=1,$b=P.document.addEventListener? -function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},db=P.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},mc=/([\:\-\_]+(.))/g,nc=/^moz([A-Z])/,ua=K.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;this.bind("DOMContentLoaded",a);K(P).bind("load",a)},toString:function(){var b=[];m(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?u(this[b]):u(this[this.length+ -b])},length:0,push:Pa,sort:[].sort,splice:[].splice},Ea={};m("multiple,selected,checked,disabled,readOnly,required".split(","),function(b){Ea[z(b)]=b});var Bb={};m("input,select,option,textarea,button,form".split(","),function(b){Bb[la(b)]=!0});m({data:wb,inheritedData:Da,scope:function(b){return Da(b,"$scope")},controller:zb,injector:function(b){return Da(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ca,css:function(b,a,c){a=tb(a);if(y(c))b.style[a]=c;else{var d;Z<=8&&(d= -b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];Z<=8&&(d=d===""?q:d);return d}},attr:function(b,a,c){var d=z(a);if(Ea[d])if(y(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||C).specified?d:q;else if(y(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?q:b},prop:function(b,a,c){if(y(c))b[a]=c;else return b[a]},text:v(Z<9?function(b,a){if(b.nodeType==1){if(w(a))return b.innerText; -b.innerText=a}else{if(w(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(w(a))return b.textContent;b.textContent=a},{$dv:""}),val:function(b,a){if(w(a))return b.value;b.value=a},html:function(b,a){if(w(a))return b.innerHTML;for(var c=0,d=b.childNodes;c":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Mc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},ib={},Zc=P.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw Error("This browser does not support XMLHttpRequest."); -};Rb.$inject=["$provide"];Sb.$inject=["$locale"];Ub.$inject=["$locale"];var Xb=".",id={yyyy:O("FullYear",4),yy:O("FullYear",2,0,!0),y:O("FullYear",1),MMMM:Ja("Month"),MMM:Ja("Month",!0),MM:O("Month",2,1),M:O("Month",1,1),dd:O("Date",2),d:O("Date",1),HH:O("Hours",2),H:O("Hours",1),hh:O("Hours",2,-12),h:O("Hours",1,-12),mm:O("Minutes",2),m:O("Minutes",1),ss:O("Seconds",2),s:O("Seconds",1),EEEE:Ja("Day"),EEE:Ja("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){var a= --1*a.getTimezoneOffset(),c=a>=0?"+":"";c+=jb(Math[a>0?"floor":"ceil"](a/60),2)+jb(Math.abs(a%60),2);return c}},hd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,gd=/^\d+$/;Tb.$inject=["$locale"];var ed=I(z),fd=I(la);Vb.$inject=["$parse"];var kd=I({restrict:"E",compile:function(a,c){Z<=8&&(!c.href&&!c.name&&c.$set("href",""),a.append(T.createComment("IE fix")));return function(a,c){c.bind("click",function(a){c.attr("href")||a.preventDefault()})}}}),lb={};m(Ea,function(a, -c){var d=ea("ng-"+c);lb[d]=function(){return{priority:100,compile:function(){return function(a,g,h){a.$watch(h[d],function(a){h.$set(c,!!a)})}}}}});m(["src","href"],function(a){var c=ea("ng-"+a);lb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){c&&(g.$set(a,c),Z&&e.prop(a,g[a]))})}}}});var Ma={$addControl:C,$removeControl:C,$setValidity:C,$setDirty:C};Yb.$inject=["$element","$attrs","$scope"];var Pa=function(a){return["$timeout",function(c){var d={name:"form",restrict:"E", -controller:Yb,compile:function(){return{pre:function(a,d,h,f){if(!h.action){var i=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};$b(d[0],"submit",i);d.bind("$destroy",function(){c(function(){db(d[0],"submit",i)},0,!1)})}var j=d.parent().controller("form"),k=h.name||h.ngForm;k&&(a[k]=f);j&&d.bind("$destroy",function(){j.$removeControl(f);k&&(a[k]=q);v(f,Ma)})}}}};return a?v(U(d),{restrict:"EAC"}):d}]},ld=Pa(),md=Pa(!0),nd=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/, -od=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,pd=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,bc={text:Oa,number:function(a,c,d,e,g,h){Oa(a,c,d,e,g,h);e.$parsers.push(function(a){var c=X(a);return c||pd.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),q)});e.$formatters.push(function(a){return X(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!X(a)&&ai?(e.$setValidity("max",!1),q):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return X(a)||Qa(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),q)})},url:function(a,c,d,e,g,h){Oa(a,c,d,e,g,h);a=function(a){return X(a)||nd.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),q)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a, -c,d,e,g,h){Oa(a,c,d,e,g,h);a=function(a){return X(a)||od.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email",!1),q)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){w(d.name)&&c.attr("name",xa());c.bind("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,h=d.ngFalseValue;B(g)||(g=!0);B(h)||(h=!1);c.bind("click", -function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:h})},hidden:C,button:C,submit:C,reset:C},cc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,h){h&&(bc[z(g.type)]||bc.text)(d,e,g,h,c,a)}}}],La="ng-valid",Ka="ng-invalid",Na="ng-pristine",Zb="ng-dirty",qd=["$scope","$exceptionHandler","$attrs","$element","$parse", -function(a,c,d,e,g){function h(a,c){c=c?"-"+Za(c,"-"):"";e.removeClass((a?Ka:La)+c).addClass((a?La:Ka)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var f=g(d.ngModel),i=f.assign;if(!i)throw Error(Eb+d.ngModel+" ("+pa(e)+")");this.$render=C;var j=e.inheritedData("$formController")||Ma,k=0,l=this.$error={};e.addClass(Na);h(!0);this.$setValidity=function(a, -c){if(l[a]!==!c){if(c){if(l[a]&&k--,!k)h(!0),this.$valid=!0,this.$invalid=!1}else h(!1),this.$invalid=!0,this.$valid=!1,k++;l[a]=!c;h(c,a);j.$setValidity(a,c,this)}};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty=!0,this.$pristine=!1,e.removeClass(Na).addClass(Zb),j.$setDirty();m(this.$parsers,function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,i(a,d),m(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};var n=this;a.$watch(function(){var c= -f(a);if(n.$modelValue!==c){var d=n.$formatters,e=d.length;for(n.$modelValue=c;e--;)c=d[e](c);if(n.$viewValue!==c)n.$viewValue=c,n.$render()}})}],rd=function(){return{require:["ngModel","^?form"],controller:qd,link:function(a,c,d,e){var g=e[0],h=e[1]||Ma;h.$addControl(g);c.bind("$destroy",function(){h.$removeControl(g)})}}},sd=I({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),dc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required= -!0;var g=function(a){if(d.required&&(X(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},td=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){var c=[];a&&m(a.split(g),function(a){a&&c.push(Q(a))});return c});e.$formatters.push(function(a){return E(a)?a.join(", "): -q})}}},ud=/^(true|false|\d+)$/,vd=function(){return{priority:100,compile:function(a,c){return ud.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}},wd=S(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==q?"":a)})}),xd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate", -function(a){d.text(a)})}}],yd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,function(a){c.html(a||"")})}}],zd=kb("",!0),Ad=kb("Odd",0),Bd=kb("Even",1),Cd=S({compile:function(a,c){c.$set("ngCloak",q);a.removeClass("ng-cloak")}}),Dd=[function(){return{scope:!0,controller:"@"}}],Ed=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],ec={};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "), -function(a){var c=ea("ng-"+a);ec[c]=["$parse",function(d){return function(e,g,h){var f=d(h[c]);g.bind(z(a),function(a){e.$apply(function(){f(e,{$event:a})})})}}]});var Fd=S(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}),Gd=["$http","$templateCache","$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g,h){var f=h.ngInclude||h.src,i=h.onload||"",j=h.autoscroll;return function(g,h){var n=0,o,p=function(){o&&(o.$destroy(),o=null);h.html("")}; -g.$watch(f,function(f){var m=++n;f?a.get(f,{cache:c}).success(function(a){m===n&&(o&&o.$destroy(),o=g.$new(),h.html(a),e(h.contents())(o),y(j)&&(!j||g.$eval(j))&&d(),o.$emit("$includeContentLoaded"),g.$eval(i))}).error(function(){m===n&&p()}):p()})}}}}],Hd=S({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Id=S({terminal:!0,priority:1E3}),Jd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,h){var f=h.count,i=g.attr(h.$attr.when),j=h.offset|| -0,k=e.$eval(i),l={},n=c.startSymbol(),o=c.endSymbol();m(k,function(a,e){l[e]=c(a.replace(d,n+f+"-"+j+o))});e.$watch(function(){var c=parseFloat(e.$eval(f));return isNaN(c)?"":(c in k||(c=a.pluralCat(c-j)),l[c](e,g,!0))},function(a){g.text(a)})}}}],Kd=S({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,c,h){var f=h.ngRepeat,h=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),i,j,k;if(!h)throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f= -h[1];i=h[2];h=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!h)throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '"+f+"'.");j=h[3]||h[1];k=h[2];var l=new eb;a.$watch(function(a){var e,f,h=a.$eval(i),m=c,q=new eb,y,A,u,w,r,v;if(E(h))r=h||[];else{r=[];for(u in h)h.hasOwnProperty(u)&&u.charAt(0)!="$"&&r.push(u);r.sort()}y=r.length-1;e=0;for(f=r.length;ez;)u.pop().element.remove()}for(;r.length> -x;)r.pop()[0].element.remove()}var i;if(!(i=s.match(d)))throw Error("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+s+"'.");var j=c(i[2]||i[1]),k=i[4]||i[6],l=i[5],m=c(i[3]||""),n=c(i[2]?i[1]:k),o=c(i[7]),r=[[{element:f,label:""}]];t&&(a(t)(e),t.removeClass("ng-scope"),t.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,c=o(e)||[],d={},h,i,j,m,s,t;if(p){i=[];m=0;for(t=r.length;m@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}'); diff --git a/test/e2e/angular-scenario/e2eSpec.js b/test/e2e/angular-scenario/e2eSpec.js deleted file mode 100644 index d8f913afa..000000000 --- a/test/e2e/angular-scenario/e2eSpec.js +++ /dev/null @@ -1,15 +0,0 @@ -/** A Sample Angular E2E test */ - -describe('My Sample App', function() { - - it('should let Angular do its work', function() { - browser().navigateTo('/index.html'); - input('yourName').enter('A Pirate!'); - expect(element('.ng-binding').text()).toEqual('Hello A Pirate!!'); - }); - - xit('should skip this e2e test', function() { - sleep(15); - browser().navigateTo('/index.html'); - }); -}); diff --git a/test/e2e/angular-scenario/index.html b/test/e2e/angular-scenario/index.html deleted file mode 100644 index 87f076014..000000000 --- a/test/e2e/angular-scenario/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Sample Angular App - - - -
- - -
-

Hello {{yourName}}!

-
- - diff --git a/test/e2e/angular-scenario/karma.conf.js b/test/e2e/angular-scenario/karma.conf.js deleted file mode 100644 index d65765b3e..000000000 --- a/test/e2e/angular-scenario/karma.conf.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = function(config) { - config.set({ - frameworks: ['ng-scenario'], - - files: [ - 'e2eSpec.js' - ], - - urlRoot: '/__karma/', - - autoWatch: true, - - proxies: { - '/': 'http://localhost:8000/test/e2e/angular-scenario/' - }, - - browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'], - - reporters: ['dots'], - - plugins: [ - 'karma-ng-scenario', - 'karma-chrome-launcher', - 'karma-firefox-launcher' - ] - }); -}; diff --git a/test/e2e/angular-scenario/server.js b/test/e2e/angular-scenario/server.js deleted file mode 100644 index 59765839b..000000000 --- a/test/e2e/angular-scenario/server.js +++ /dev/null @@ -1,269 +0,0 @@ -var sys = require('sys'), - http = require('http'), - fs = require('fs'), - url = require('url'), - events = require('events'); - -var DEFAULT_PORT = 8000; - -function main(argv) { - new HttpServer({ - 'GET': createServlet(StaticServlet), - 'HEAD': createServlet(StaticServlet) - }).start(Number(argv[2]) || DEFAULT_PORT); -} - -function escapeHtml(value) { - return value.toString(). - replace('<', '<'). - replace('>', '>'). - replace('"', '"'); -} - -function createServlet(Class) { - var servlet = new Class(); - return servlet.handleRequest.bind(servlet); -} - -/** - * An Http server implementation that uses a map of methods to decide - * action routing. - * - * @param {Object} Map of method => Handler function - */ -function HttpServer(handlers) { - this.handlers = handlers; - this.server = http.createServer(this.handleRequest_.bind(this)); -} - -HttpServer.prototype.start = function(port) { - this.port = port; - this.server.listen(port); - sys.puts('Http Server running at http://127.0.0.1:' + port + '/'); -}; - -HttpServer.prototype.parseUrl_ = function(urlString) { - var parsed = url.parse(urlString); - parsed.pathname = url.resolve('/', parsed.pathname); - return url.parse(url.format(parsed), true); -}; - -HttpServer.prototype.handleRequest_ = function(req, res) { - var logEntry = req.method + ' ' + req.url; - if (req.headers['user-agent']) { - logEntry += ' ' + req.headers['user-agent']; - } - sys.puts(logEntry); - req.url = this.parseUrl_(req.url); - var handler = this.handlers[req.method]; - if (!handler) { - res.writeHead(501); - res.end(); - } else { - handler.call(this, req, res); - } -}; - -/** - * Handles static content. - */ -function StaticServlet() {} - -StaticServlet.MimeMap = { - 'txt': 'text/plain', - 'html': 'text/html', - 'css': 'text/css', - 'xml': 'application/xml', - 'json': 'application/json', - 'js': 'application/javascript', - 'jpg': 'image/jpeg', - 'jpeg': 'image/jpeg', - 'gif': 'image/gif', - 'png': 'image/png', - 'manifest': 'text/cache-manifest' -}; - -StaticServlet.prototype.handleRequest = function(req, res) { - var self = this; - var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){ - return String.fromCharCode(parseInt(hex, 16)); - }); - var parts = path.split('/'); - if (parts[parts.length-1].charAt(0) === '.') - return self.sendForbidden_(req, res, path); - - // favicon rewriting - if (path === './favicon.ico') - return self.sendFile_(req, res, './lib/nodeserver/favicon.ico'); - - // docs rewriting - var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/, - IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/, - match; - - if (!IGNORED.test(path) && (match = path.match(REWRITE))) { - path = path.replace(match[0], '/index.html'); - sys.puts('Rewrite to ' + path); - } - // end of docs rewriting - - fs.stat(path, function(err, stat) { - if (err) - return self.sendMissing_(req, res, path); - if (stat.isDirectory()) - return fs.stat(path + 'index.html', function(err, stat) { - // send index.html if exists - if (!err) - return self.sendFile_(req, res, path + 'index.html'); - - // list files otherwise - return self.sendDirectory_(req, res, path); - }); - - return self.sendFile_(req, res, path); - }); -}; - -StaticServlet.prototype.sendError_ = function(req, res, error) { - res.writeHead(500, { - 'Content-Type': 'text/html' - }); - res.write('\n'); - res.write('Internal Server Error\n'); - res.write('

Internal Server Error

'); - res.write('
' + escapeHtml(sys.inspect(error)) + '
'); - sys.puts('500 Internal Server Error'); - sys.puts(sys.inspect(error)); -}; - -StaticServlet.prototype.sendMissing_ = function(req, res, path) { - path = path.substring(1); - res.writeHead(404, { - 'Content-Type': 'text/html' - }); - res.write('\n'); - res.write('404 Not Found\n'); - res.write('

Not Found

'); - res.write( - '

The requested URL ' + - escapeHtml(path) + - ' was not found on this server.

' - ); - res.end(); - sys.puts('404 Not Found: ' + path); -}; - -StaticServlet.prototype.sendForbidden_ = function(req, res, path) { - path = path.substring(1); - res.writeHead(403, { - 'Content-Type': 'text/html' - }); - res.write('\n'); - res.write('403 Forbidden\n'); - res.write('

Forbidden

'); - res.write( - '

You do not have permission to access ' + - escapeHtml(path) + ' on this server.

' - ); - res.end(); - sys.puts('403 Forbidden: ' + path); -}; - -StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) { - res.writeHead(301, { - 'Content-Type': 'text/html', - 'Location': redirectUrl - }); - res.write('\n'); - res.write('301 Moved Permanently\n'); - res.write('

Moved Permanently

'); - res.write( - '

The document has moved here.

' - ); - res.end(); - sys.puts('401 Moved Permanently: ' + redirectUrl); -}; - -StaticServlet.prototype.sendFile_ = function(req, res, path) { - var self = this; - var file = fs.createReadStream(path); - res.writeHead(200, { - // CSP headers, uncomment to enable CSP - //"X-WebKit-CSP": "default-src 'self';", - //"X-Content-Security-Policy": "default-src 'self'", - 'Content-Type': StaticServlet. - MimeMap[path.split('.').pop()] || 'text/plain' - }); - if (req.method === 'HEAD') { - res.end(); - } else { - file.on('data', res.write.bind(res)); - file.on('close', function() { - res.end(); - }); - file.on('error', function(error) { - self.sendError_(req, res, error); - }); - } -}; - -StaticServlet.prototype.sendDirectory_ = function(req, res, path) { - var self = this; - if (path.match(/[^\/]$/)) { - req.url.pathname += '/'; - var redirectUrl = url.format(url.parse(url.format(req.url))); - return self.sendRedirect_(req, res, redirectUrl); - } - fs.readdir(path, function(err, files) { - if (err) - return self.sendError_(req, res, error); - - if (!files.length) - return self.writeDirectoryIndex_(req, res, path, []); - - var remaining = files.length; - files.forEach(function(fileName, index) { - fs.stat(path + '/' + fileName, function(err, stat) { - if (err) - return self.sendError_(req, res, err); - if (stat.isDirectory()) { - files[index] = fileName + '/'; - } - if (!(--remaining)) - return self.writeDirectoryIndex_(req, res, path, files); - }); - }); - }); -}; - -StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) { - path = path.substring(1); - res.writeHead(200, { - 'Content-Type': 'text/html' - }); - if (req.method === 'HEAD') { - res.end(); - return; - } - res.write('\n'); - res.write('' + escapeHtml(path) + '\n'); - res.write('\n'); - res.write('

Directory: ' + escapeHtml(path) + '

'); - res.write('
    '); - files.forEach(function(fileName) { - if (fileName.charAt(0) !== '.') { - res.write('
  1. ' + - escapeHtml(fileName) + '
  2. '); - } - }); - res.write('
'); - res.end(); -}; - -// Must be last, -main(process.argv); diff --git a/test/e2e/basic.feature b/test/e2e/basic.feature new file mode 100644 index 000000000..7e59bc254 --- /dev/null +++ b/test/e2e/basic.feature @@ -0,0 +1,55 @@ +Feature: Basic Testrunner + In order to use Karma + As a person who wants to write great tests + I want to be able to run tests from the command line. + + Scenario: Execute a test in PhantomJS + Given a configuration with: + """ + files = ['basic/plus.js', 'basic/test.js']; + browsers = ['PhantomJS']; + plugins = [ + 'karma-jasmine', + 'karma-phantomjs-launcher' + ]; + """ + When I start Karma + Then it passes with: + """ + .. + PhantomJS + """ + @not-jenkins + Scenario: Execute a test in Chrome + Given a configuration with: + """ + files = ['basic/plus.js', 'basic/test.js'] + browsers = ['Chrome'] + plugins = [ + 'karma-jasmine', + 'karma-chrome-launcher' + ] + """ + When I start Karma + Then it passes with: + """ + .. + Chrome + """ + + Scenario: Execute a test in Firefox + Given a configuration with: + """ + files = ['basic/plus.js', 'basic/test.js'] + browsers = ['Firefox'] + plugins = [ + 'karma-jasmine', + 'karma-firefox-launcher' + ] + """ + When I start Karma + Then it passes with: + """ + .. + Firefox + """ diff --git a/test/e2e/basic/karma.conf.js b/test/e2e/basic/karma.conf.js deleted file mode 100644 index f5f92e1a9..000000000 --- a/test/e2e/basic/karma.conf.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = function(config) { - config.set({ - frameworks: ['jasmine'], - - files: [ - '*.js' - ], - - autoWatch: true, - - browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'], - - reporters: ['dots'], - - plugins: [ - 'karma-jasmine', - 'karma-chrome-launcher', - 'karma-firefox-launcher' - ], - }); -}; diff --git a/test/e2e/browserstack/karma.ignore.conf.js b/test/e2e/browserstack/karma.ignore.conf.js deleted file mode 100644 index 50b75150f..000000000 --- a/test/e2e/browserstack/karma.ignore.conf.js +++ /dev/null @@ -1,45 +0,0 @@ -var TRAVIS_WITHOUT_BS = process.env.TRAVIS_SECURE_ENV_VARS === 'false'; - -module.exports = function(config) { - config.set({ - frameworks: ['jasmine'], - - files: [ - '*.js' - ], - - autoWatch: true, - - browsers: TRAVIS_WITHOUT_BS ? ['Firefox'] : ['bs_ff_mac', 'bs_ch_mac'], - - reporters: ['dots'], - - browserStack: { - username: 'vojta.jina@gmail.com', - accessKey: process.env.BROWSER_STACK_ACCESS_KEY - }, - - customLaunchers: { - bs_ff_mac: { - base: 'BrowserStack', - browser: 'firefox', - browser_version: 'latest', - os: 'Windows', - os_version: 'XP' - }, - bs_ch_mac: { - base: 'BrowserStack', - browser: 'chrome', - browser_version: 'latest', - os: 'OS X', - os_version: 'Lion' - } - }, - - plugins: [ - 'karma-jasmine', - 'karma-firefox-launcher', - 'karma-browserstack-launcher' - ] - }); -}; diff --git a/test/e2e/error.feature b/test/e2e/error.feature new file mode 100644 index 000000000..f15cea3ce --- /dev/null +++ b/test/e2e/error.feature @@ -0,0 +1,20 @@ +Feature: Error Display + In order to use Karma + As a person who wants to write great tests + I want Karma to log errors + + Scenario: Syntax Error in a test file + Given a configuration with: + """ + files = ['error/test.js', 'error/under-test.js']; + browsers = ['PhantomJS']; + plugins = [ + 'karma-jasmine', + 'karma-phantomjs-launcher' + ]; + """ + When I start Karma + Then it fails with: + """ + SyntaxError: Parse error + """ diff --git a/test/e2e/pass-opts.feature b/test/e2e/pass-opts.feature new file mode 100644 index 000000000..f92092027 --- /dev/null +++ b/test/e2e/pass-opts.feature @@ -0,0 +1,23 @@ +Feature: Passing Options + In order to use Karma + As a person who wants to write great tests + I want to be able to pass arguments from the config file to the browser. + + Scenario: Passing Options to run on the Command Line + Given a configuration with: + """ + files = ['pass-opts/test.js']; + browsers = ['PhantomJS']; + plugins = [ + 'karma-jasmine', + 'karma-phantomjs-launcher' + ]; + singleRun = false; + """ + And command line arguments of: "-- arg1 arg2" + When I run Karma + Then it passes with no debug: + """ + . + PhantomJS + """ diff --git a/test/e2e/pass-opts/karma.conf.js b/test/e2e/pass-opts/karma.conf.js deleted file mode 100644 index 7150ae04f..000000000 --- a/test/e2e/pass-opts/karma.conf.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = function(config) { - config.set({ - frameworks: ['jasmine'], - - files: [ - '*.js' - ], - - browsers: [ process.env.TRAVIS ? 'Firefox' : 'Chrome' ], - - reporters: ['dots'], - - plugins: [ - 'karma-jasmine', - 'karma-chrome-launcher', - 'karma-firefox-launcher' - ], - }); -}; diff --git a/test/e2e/proxy.feature b/test/e2e/proxy.feature new file mode 100644 index 000000000..48872cf34 --- /dev/null +++ b/test/e2e/proxy.feature @@ -0,0 +1,24 @@ +Feature: Proxying + In order to use Karma + As a person who wants to write great tests + I want to Karma to proxy requests. + + Scenario: Simple file proxy + Given a configuration with: + """ + files = ['proxy/*.js']; + browsers = ['PhantomJS']; + plugins = [ + 'karma-jasmine', + 'karma-phantomjs-launcher' + ]; + proxies = { + '/foo.js': '/base/proxy/foo.js' + } + """ + When I start Karma + Then it passes with: + """ + . + PhantomJS + """ diff --git a/test/e2e/proxy/foo.js b/test/e2e/proxy/foo.js deleted file mode 100644 index 090813905..000000000 --- a/test/e2e/proxy/foo.js +++ /dev/null @@ -1 +0,0 @@ -'/base/foo.js source' diff --git a/test/e2e/proxy/karma.conf.js b/test/e2e/proxy/karma.conf.js deleted file mode 100644 index 23f00ad4d..000000000 --- a/test/e2e/proxy/karma.conf.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = function(config) { - config.set({ - frameworks: ['jasmine'], - - files: [ - '*.js' - ], - - // Test local proxying, with non-default port. - port: 9877, - proxies: { - '/foo.js': '/base/foo.js' - }, - - autoWatch: true, - - browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'], - - reporters: ['dots'], - - plugins: [ - 'karma-jasmine', - 'karma-chrome-launcher', - 'karma-firefox-launcher' - ], - }); -}; diff --git a/test/e2e/reconnecting.feature b/test/e2e/reconnecting.feature new file mode 100644 index 000000000..30af18c7c --- /dev/null +++ b/test/e2e/reconnecting.feature @@ -0,0 +1,21 @@ +Feature: Passing Options + In order to use Karma + As a person who wants to write great tests + I want to the browser to reconnect to Karma when it gets disconnected. + + Scenario: Manual disconnect from the browser + Given a configuration with: + """ + files = ['reconnecting/test.js', 'reconnecting/plus.js']; + browsers = ['PhantomJS']; + plugins = [ + 'karma-jasmine', + 'karma-phantomjs-launcher' + ]; + """ + When I start Karma + Then it passes with: + """ + ..... + PhantomJS + """ diff --git a/test/e2e/reconnecting/karma.conf.js b/test/e2e/reconnecting/karma.conf.js deleted file mode 100644 index f5f92e1a9..000000000 --- a/test/e2e/reconnecting/karma.conf.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = function(config) { - config.set({ - frameworks: ['jasmine'], - - files: [ - '*.js' - ], - - autoWatch: true, - - browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'], - - reporters: ['dots'], - - plugins: [ - 'karma-jasmine', - 'karma-chrome-launcher', - 'karma-firefox-launcher' - ], - }); -}; diff --git a/test/e2e/reconnecting/plus.js b/test/e2e/reconnecting/plus.js deleted file mode 100644 index c3aa2d83c..000000000 --- a/test/e2e/reconnecting/plus.js +++ /dev/null @@ -1,4 +0,0 @@ -// Some code under test -function plus(a, b) { - return a + b; -} diff --git a/test/e2e/steps/core_steps.coffee b/test/e2e/steps/core_steps.coffee new file mode 100644 index 000000000..7bab41fdb --- /dev/null +++ b/test/e2e/steps/core_steps.coffee @@ -0,0 +1,139 @@ +coreSteps = -> + fs = require 'fs' + path = require 'path' + {Buffer} = require 'buffer' + cp = require 'child_process' + {exec, spawn} = require 'child_process' + util = require 'util' + + rimraf = require 'rimraf' + + # Load world + @World = require('../support/world').World + + # Load after hooks + require('../support/after_hooks').call this + + baseDir = fs.realpathSync(__dirname + '/../../..') + tmpDir = path.join(baseDir, 'tmp', 'sandbox') + tmpConfigFile = 'karma.conf.js' + + cleansingNeeded = true + + additionalArgs = [] + + tmpPath = (path) -> path.join(tmpDir, path) + + cleanseIfNeeded = => + if cleansingNeeded + try rimraf.sync tmpDir catch error + cleansingNeeded = false + + @Given /^a configuration with:$/, (fileContent, callback) -> + cleanseIfNeeded() + @addConfigContent fileContent + callback() + + @Given /^command line arguments of: "([^"]*)"$/, (args, callback) -> + additionalArgs = args + callback() + + @When /^I (run|start|init) Karma$/, (command, callback) -> + @writeConfigFile tmpDir, tmpConfigFile, (err) => + callback.fail new Error(err) if err + + configFile = path.join tmpDir, tmpConfigFile + options = + stdio: 'pipe' + cwd: baseDir + detached: false + + runtimePath = path.join baseDir, 'bin', 'karma' + + execKarma = (done) -> + cmd = "#{runtimePath} #{command} --log-level warn #{configFile} #{additionalArgs}" + exec cmd, {cwd: baseDir}, done + + if command is 'run' + @child = spawn "#{runtimePath}", ['start', '--log-level', 'warn', configFile] + + done = => + cleansingNeeded = true + @child.kill() + callback() + + @child.on 'error', (error) => + @lastRun.error = error + done() + + @child.stderr.on 'data', (chunk) -> + lastRun.stderr += chunk.toString() + + @child.stdout.on 'data', (chunk) => + @lastRun.stdout += chunk.toString() + + cmd = "#{runtimePath} run #{configFile} #{additionalArgs}" + exec cmd, {cwd: baseDir}, (error, stdout) => + @lastRun.error = error if error + done() + + else + execKarma (error, stdout, stderr) => + @lastRun.error = error if error + @lastRun.stdout = stdout + @lastRun.stderr = stderr + + cleansingNeeded = true + callback() + + + + @Then /^it passes with( no debug)?:$/, (noDebug, expectedOutput, callback) -> + noDebug = noDebug is ' no debug' + actualOutput = @lastRun.stdout.toString() + actualError = @lastRun.error + actualStderr = @lastRun.stderr.toString() + + # Filter out debug lines + if noDebug + lines = actualOutput.split('\n').filter (line) -> not line.match /\[DEBUG\]/ + actualOutput = lines.join '\n' + + return callback() if actualOutput.indexOf(expectedOutput) is 0 + if actualError or actualStderr + callback.fail new Error """ + Expected output to match the following: + #{expectedOutput} + Got: + #{actualOutput} + """ + + @Then /^it fails with:$/, (expectedOutput, callback) -> + actualOutput = @lastRun.stdout.toString() + actualError = @lastRun.error + actualStderr = @lastRun.stderr.toString() + + return callback() if not not actualOutput.match(expectedOutput) + if actualError or actualStderr + callback.fail new Error """ + Expected output to match the following: + #{expectedOutput} + Got: + #{actualOutput} + """ + + @Then /^it fails with like:$/, (expectedOutput, callback) -> + actualOutput = @lastRun.stdout.toString() + actualError = @lastRun.error + actualStderr = @lastRun.stderr.toString() + + return callback() if not not actualOutput.match new RegExp(expectedOutput) + if actualError or actualStderr + callback.fail new Error """ + Expected output to match the following: + #{expectedOutput} + Got: + #{actualOutput} + """ + +module.exports = coreSteps diff --git a/test/e2e/support/after_hooks.coffee b/test/e2e/support/after_hooks.coffee new file mode 100644 index 000000000..f8abe3dde --- /dev/null +++ b/test/e2e/support/after_hooks.coffee @@ -0,0 +1,14 @@ + +afterHooks = -> + + @After (callback) -> + + if @child? and @child?kill? + @child.kill() + @child = null + callback() + + else + callback() + +module.exports = afterHooks diff --git a/test/e2e/basic/plus.js b/test/e2e/support/basic/plus.js similarity index 100% rename from test/e2e/basic/plus.js rename to test/e2e/support/basic/plus.js diff --git a/test/e2e/basic/test.js b/test/e2e/support/basic/test.js similarity index 100% rename from test/e2e/basic/test.js rename to test/e2e/support/basic/test.js diff --git a/test/e2e/browserstack/test.js b/test/e2e/support/error/test.js similarity index 100% rename from test/e2e/browserstack/test.js rename to test/e2e/support/error/test.js diff --git a/test/e2e/syntax-error/under-test.js b/test/e2e/support/error/under-test.js similarity index 100% rename from test/e2e/syntax-error/under-test.js rename to test/e2e/support/error/under-test.js diff --git a/test/e2e/pass-opts/test.js b/test/e2e/support/pass-opts/test.js similarity index 67% rename from test/e2e/pass-opts/test.js rename to test/e2e/support/pass-opts/test.js index 4f37f715a..e283afff4 100644 --- a/test/e2e/pass-opts/test.js +++ b/test/e2e/support/pass-opts/test.js @@ -1,6 +1,6 @@ describe('config', function() { it('should be passed through to the browser', function() { expect(window.__karma__.config).toBeDefined(); - expect(window.__karma__.config.args).toEqual(['arg1','arg2','arg3']); + expect(window.__karma__.config.args).toEqual(['arg1','arg2']); }); }); diff --git a/test/e2e/support/proxy/.tern-port b/test/e2e/support/proxy/.tern-port new file mode 100644 index 000000000..54ad97d20 --- /dev/null +++ b/test/e2e/support/proxy/.tern-port @@ -0,0 +1 @@ +63683 \ No newline at end of file diff --git a/test/e2e/support/proxy/foo.js b/test/e2e/support/proxy/foo.js new file mode 100644 index 000000000..3eee2d995 --- /dev/null +++ b/test/e2e/support/proxy/foo.js @@ -0,0 +1 @@ +'/base/proxy/foo.js source' diff --git a/test/e2e/proxy/test.js b/test/e2e/support/proxy/test.js similarity index 77% rename from test/e2e/proxy/test.js rename to test/e2e/support/proxy/test.js index 7eed76f19..d80ef1b8c 100644 --- a/test/e2e/proxy/test.js +++ b/test/e2e/support/proxy/test.js @@ -9,6 +9,6 @@ function httpGet(url) { describe('foo', function() { it('should should serve /foo.js', function() { - expect(httpGet('/foo.js')).toBe("'/base/foo.js source'\n"); + expect(httpGet('/foo.js')).toBe("'/base/proxy/foo.js source'\n"); }); }); diff --git a/test/e2e/browserstack/plus.js b/test/e2e/support/reconnecting/plus.js similarity index 100% rename from test/e2e/browserstack/plus.js rename to test/e2e/support/reconnecting/plus.js diff --git a/test/e2e/reconnecting/test.js b/test/e2e/support/reconnecting/test.js similarity index 89% rename from test/e2e/reconnecting/test.js rename to test/e2e/support/reconnecting/test.js index 3cb4c9c51..b58430e91 100644 --- a/test/e2e/reconnecting/test.js +++ b/test/e2e/support/reconnecting/test.js @@ -17,30 +17,22 @@ describe('plus', function() { return window.parent.io.sockets[location.protocol + '//' + location.host]; }; - it('should pass', function() { - console.log(1); expect(1).toBe(1); }); - it('should disconnect', function() { - console.log(2); expect(2).toBe(2); socket().disconnect(); breath(); }); - it('should work', function() { - console.log(3); expect(plus(1, 2)).toBe(3); }); - it('should re-connect', function() { - console.log(4); expect(4).toBe(4); socket().reconnect(); // window.parent.socket.socket.connect(); @@ -48,9 +40,7 @@ describe('plus', function() { breath(); }); - it('should work', function() { - console.log(5); expect(plus(3, 2)).toBe(5); }); }); diff --git a/test/e2e/timeout/fake-browser.sh b/test/e2e/support/timeout/fake-browser.sh similarity index 100% rename from test/e2e/timeout/fake-browser.sh rename to test/e2e/support/timeout/fake-browser.sh diff --git a/test/e2e/timeout/specs.js b/test/e2e/support/timeout/specs.js similarity index 100% rename from test/e2e/timeout/specs.js rename to test/e2e/support/timeout/specs.js diff --git a/test/e2e/support/world.coffee b/test/e2e/support/world.coffee new file mode 100644 index 000000000..b8cf279c0 --- /dev/null +++ b/test/e2e/support/world.coffee @@ -0,0 +1,50 @@ +fs = require 'fs' +vm = require 'vm' +path = require 'path' + +mkdirp = require 'mkdirp' +_ = require 'lodash' + + +World = (callback) -> + + @template = """ + module.exports = function (config) { + config.set( + <%= content %> + ); + }; + """ + @configFile = + singleRun: true + reporters: ['dots'] + frameworks: ['jasmine'] + basePath: __dirname + colors: false + __dirname: __dirname + + @addConfigContent = (content='') => + vm.runInNewContext(content, @configFile) + + # Generate a configuration file and save it to path. + @writeConfigFile = (dir, file, done) => + mkdirp dir, 0o0755, (err) => + throw new Error(err) if err + + # Remove dirname from config again + delete @configFile.__dirname + + content = @generateJS @configFile + fs.writeFile path.join(dir, file), content, done + + @generateJS = (config) -> + _.template @template, {content: JSON.stringify config} + + @lastRun = + error: null + stdout: '' + stderr: '' + + callback() + +exports.World = World diff --git a/test/e2e/syntax-error/karma.conf.ignore.js b/test/e2e/syntax-error/karma.conf.ignore.js deleted file mode 100644 index 24bacfacc..000000000 --- a/test/e2e/syntax-error/karma.conf.ignore.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports = function(config) { - config.set({ - frameworks: ['jasmine'], - - // files to load - files: [ - '*.js' - ], - - autoWatch: true, - logLevel: config.LOG_INFO, - logColors: true, - - browsers: ['Chrome'], - - reporters: ['dots'], - - plugins: [ - 'karma-jasmine', - 'karma-chrome-launcher', - 'karma-firefox-launcher' - ], - }); -}; diff --git a/test/e2e/syntax-error/test.js b/test/e2e/syntax-error/test.js deleted file mode 100644 index 9e34557ef..000000000 --- a/test/e2e/syntax-error/test.js +++ /dev/null @@ -1,9 +0,0 @@ -describe('plus', function() { - it('should pass', function() { - expect(true).toBe(true); - }); - - it('should work', function() { - expect(plus(1, 2)).toBe(3); - }); -}); diff --git a/test/e2e/timeout.feature b/test/e2e/timeout.feature new file mode 100644 index 000000000..43d292c6f --- /dev/null +++ b/test/e2e/timeout.feature @@ -0,0 +1,21 @@ +Feature: Timeout + In order to use Karma + As a person who wants to write great tests + I want Karma to timeout if a browser fails to connect. + + Scenario: Broken Browser + Given a configuration with: + """ + files = ['timeout/specs.js']; + browsers = [__dirname + '/timeout/fake-browser.sh']; + plugins = [ + 'karma-jasmine', + 'karma-script-launcher' + ]; + captureTimeout = 100 + """ + When I run Karma + Then it fails with like: + """ + have not captured in 100 ms + """ diff --git a/test/e2e/timeout/karma.conf.ignore.js b/test/e2e/timeout/karma.conf.ignore.js deleted file mode 100644 index cc70405a1..000000000 --- a/test/e2e/timeout/karma.conf.ignore.js +++ /dev/null @@ -1,61 +0,0 @@ -// Karma configuration -// Generated on Sun Sep 30 2012 22:44:01 GMT-0700 (PDT) - -module.exports = function(config) { - config.set({ - - // base path, that will be used to resolve files and exclude - basePath: '', - - frameworks: ['jasmine'], - - // list of files / patterns to load in the browser - files: [ - '*.js' - ], - - - // test results reporter to use - // possible values: 'dots', 'progress', 'junit' - reporters: ['progress'], - - - // web server port - port: 8080, - - - // enable / disable colors in the output (reporters and logs) - colors: true, - - - // level of logging - // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG - logLevel: config.LOG_INFO, - - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - - // Start these browsers, currently available: - // - Chrome - // - ChromeCanary - // - Firefox - // - Opera - // - Safari (only Mac) - // - PhantomJS - // - IE (only Windows) - browsers: [__dirname + '/fake-browser.sh'], - - - // Continuous Integration mode - // if true, it capture browsers, run tests and exit - singleRun: false, - - plugins: [ - 'karma-jasmine', - 'karma-chrome-launcher', - 'karma-firefox-launcher' - ], - }); -};