Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

oboe

Package Overview
Dependencies
Maintainers
3
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

oboe - npm Package Compare versions

Comparing version 2.1.3 to 2.1.4

.dir-locals.el

2

dist/oboe-browser.min.js

@@ -1,1 +0,1 @@

!function(a,b,c,d,e,f){function g(a,b){return function(){return a.call(this,b.apply(this,arguments))}}function h(a){return function(b){return b[a]}}function i(a,b){return b.apply(f,a)}function j(a){var b=a.length-1,d=c.prototype.slice;if(0==b)return function(){return a.call(this,d.call(arguments))};if(1==b)return function(){return a.call(this,arguments[0],d.call(arguments,1))};var e=c(a.length);return function(){for(var c=0;b>c;c++)e[c]=arguments[c];return e[b]=d.call(arguments,b),a.apply(this,e)}}function k(a){return function(b,c){return a(c,b)}}function l(a,b){return function(c){return a(c)&&b(c)}}function m(){}function n(){return!0}function o(a){return function(){return a}}function p(a,b){return b&&b.constructor===a}function q(a){return a!==f}function r(a,c){return c instanceof b&&y(function(a){return a in c},a)}function s(a,b){return[a,b]}function t(a){return A(a.reduce(k(s),X))}function u(a){return w(function(a,b){return a.unshift(b),a},[],a)}function v(a,b){return b?s(a(Y(b)),v(a,Z(b))):X}function w(a,b,c){return c?a(w(a,b,Z(c)),Y(c)):b}function x(a,b,c){function d(a,c){return a?b(Y(a))?(c(Y(a)),Z(a)):s(Y(a),d(Z(a),c)):X}return d(a,c||m)}function y(a,b){return!b||a(Y(b))&&y(a,Z(b))}function z(a,b){a&&(Y(a).apply(null,b),z(Z(a),b))}function A(a){function b(a,c){return a?b(Z(a),s(Y(a),c)):c}return b(a,X)}function B(a,b){return b&&(a(Y(b))?Y(b):B(a,Z(b)))}function C(a){"use strict";function b(){var a=0;P!==f&&P.length>p&&(c("Max buffer length exceeded: textNode"),a=Math.max(a,P.length)),Q.length>p&&(c("Max buffer length exceeded: numberNode"),a=Math.max(a,Q.length)),O=p-a+Y}function c(a){P!==f&&(m(P),n(),P=f),i=d(a+"\nLn: "+$+"\nCol: "+Z+"\nChr: "+j),o(N(f,f,i))}function e(){return T==s?(m({}),n(),void(S=!0)):((T!==t||0!==X)&&c("Unexpected end"),P!==f&&(m(P),n(),P=f),void(S=!0))}function g(a){return"\r"==a||"\n"==a||" "==a||" "==a}function h(a){if(!i){if(S)return c("Cannot write after close");var d=0;for(j=a[0];j&&(k=j,j=a[d++]);)switch(Y++,"\n"==j?($++,Z=0):Z++,T){case s:if("{"===j)T=u;else if("["===j)T=w;else if(!g(j))return c("Non-whitespace before {[.");continue;case z:case u:if(g(j))continue;if(T===z)U.push(A);else{if("}"===j){m({}),n(),T=U.pop()||t;continue}U.push(v)}if('"'!==j)return c('Malformed object key should start with " ');T=y;continue;case A:case v:if(g(j))continue;if(":"===j)T===v?(U.push(v),P!==f&&(m({}),l(P),P=f),X++):P!==f&&(l(P),P=f),T=t;else if("}"===j)P!==f&&(m(P),n(),P=f),n(),X--,T=U.pop()||t;else{if(","!==j)return c("Bad object");T===v&&U.push(v),P!==f&&(m(P),n(),P=f),T=z}continue;case w:case t:if(g(j))continue;if(T===w){if(m([]),X++,T=t,"]"===j){n(),X--,T=U.pop()||t;continue}U.push(x)}if('"'===j)T=y;else if("{"===j)T=u;else if("["===j)T=w;else if("t"===j)T=B;else if("f"===j)T=E;else if("n"===j)T=I;else if("-"===j)Q+=j;else if("0"===j)Q+=j,T=M;else{if(-1==="123456789".indexOf(j))return c("Bad value");Q+=j,T=M}continue;case x:if(","===j)U.push(x),P!==f&&(m(P),n(),P=f),T=t;else{if("]"!==j){if(g(j))continue;return c("Bad array")}P!==f&&(m(P),n(),P=f),n(),X--,T=U.pop()||t}continue;case y:P===f&&(P="");var e=d-1;a:for(;;){for(;W>0;)if(V+=j,j=a.charAt(d++),4===W?(P+=String.fromCharCode(parseInt(V,16)),W=0,e=d-1):W++,!j)break a;if('"'===j&&!R){T=U.pop()||t,P+=a.substring(e,d-1);break}if("\\"===j&&!R&&(R=!0,P+=a.substring(e,d-1),j=a.charAt(d++),!j))break;if(R){if(R=!1,"n"===j?P+="\n":"r"===j?P+="\r":"t"===j?P+=" ":"f"===j?P+="\f":"b"===j?P+="\b":"u"===j?(W=1,V=""):P+=j,j=a.charAt(d++),e=d-1,j)continue;break}q.lastIndex=d;var h=q.exec(a);if(!h){d=a.length+1,P+=a.substring(e,d-1);break}if(d=h.index+1,j=a.charAt(h.index),!j){P+=a.substring(e,d-1);break}}continue;case B:if(!j)continue;if("r"!==j)return c("Invalid true started with t"+j);T=C;continue;case C:if(!j)continue;if("u"!==j)return c("Invalid true started with tr"+j);T=D;continue;case D:if(!j)continue;if("e"!==j)return c("Invalid true started with tru"+j);m(!0),n(),T=U.pop()||t;continue;case E:if(!j)continue;if("a"!==j)return c("Invalid false started with f"+j);T=F;continue;case F:if(!j)continue;if("l"!==j)return c("Invalid false started with fa"+j);T=G;continue;case G:if(!j)continue;if("s"!==j)return c("Invalid false started with fal"+j);T=H;continue;case H:if(!j)continue;if("e"!==j)return c("Invalid false started with fals"+j);m(!1),n(),T=U.pop()||t;continue;case I:if(!j)continue;if("u"!==j)return c("Invalid null started with n"+j);T=J;continue;case J:if(!j)continue;if("l"!==j)return c("Invalid null started with nu"+j);T=K;continue;case K:if(!j)continue;if("l"!==j)return c("Invalid null started with nul"+j);m(null),n(),T=U.pop()||t;continue;case L:if("."!==j)return c("Leading zero not followed by .");Q+=j,T=M;continue;case M:if(-1!=="0123456789".indexOf(j))Q+=j;else if("."===j){if(-1!==Q.indexOf("."))return c("Invalid number has two dots");Q+=j}else if("e"===j||"E"===j){if(-1!==Q.indexOf("e")||-1!==Q.indexOf("E"))return c("Invalid number has two exponential");Q+=j}else if("+"===j||"-"===j){if("e"!==k&&"E"!==k)return c("Invalid symbol in number");Q+=j}else Q&&(m(parseFloat(Q)),n(),Q=""),d--,T=U.pop()||t;continue;default:return c("Unknown state: "+T)}Y>=O&&b()}}var i,j,k,l=a(qa).emit,m=a(ra).emit,n=a(sa).emit,o=a(ja).emit,p=65536,q=/[\\"\n]/g,r=0,s=r++,t=r++,u=r++,v=r++,w=r++,x=r++,y=r++,z=r++,A=r++,B=r++,C=r++,D=r++,E=r++,F=r++,G=r++,H=r++,I=r++,J=r++,K=r++,L=r++,M=r,O=p,P=f,Q="",R=!1,S=!1,T=s,U=[],V=null,W=0,X=0,Y=0,Z=0,$=1;a(na).on(h),a(oa).on(e)}function D(a,b){"use strict";function c(a){return function(b){d=a(d,b)}}var d,e={};for(var f in b)a(f).on(c(b[f]),e);a(ha).on(function(a){var b,c=Y(d),e=aa(c),f=Z(d);f&&(b=ba(Y(f)),b[e]=a)}),a(ia).on(function(){var a,b=Y(d),c=aa(b),e=Z(d);e&&(a=ba(Y(e)),delete a[c])}),a(pa).on(function(){for(var c in b)a(c).un(e)})}function E(a){var b={};return a&&a.split("\r\n").forEach(function(a){var c=a.indexOf(": ");b[a.substring(0,c)]=a.substring(c+2)}),b}function F(a,b){function c(a){return{"http:":80,"https:":443}[a]}function d(b){return b.port||c(b.protocol||a.protocol)}return!!(b.protocol&&b.protocol!=a.protocol||b.host&&b.host!=a.host||b.host&&d(b)!=d(a))}function G(a){var b=/(\w+:)?(?:\/\/)([\w.-]+)?(?::(\d+))?\/?/,c=b.exec(a)||[];return{protocol:c[1]||"",host:c[2]||"",port:c[3]||""}}function H(){return new XMLHttpRequest}function I(b,c,d,e,g,h,i){"use strict";function j(){var a=c.responseText,b=a.substr(m);b&&k(b),m=V(a)}var k=b(na).emit,l=b(ja).emit,m=0,n=!0;b(pa).on(function(){c.onreadystatechange=null,c.abort()}),"onprogress"in c&&(c.onprogress=j),c.onreadystatechange=function(){function a(){try{n&&b(ma).emit(c.status,E(c.getAllResponseHeaders())),n=!1}catch(a){}}switch(c.readyState){case 2:case 3:return a();case 4:a();var d=2==String(c.status)[0];d?(j(),b(oa).emit()):l(N(c.status,c.responseText))}};try{c.open(d,e,!0);for(var o in h)c.setRequestHeader(o,h[o]);F(a.location,G(e))||c.setRequestHeader("X-Requested-With","XMLHttpRequest"),c.withCredentials=i,c.send(g)}catch(p){a.setTimeout(T(l,N(f,f,p)),0)}}function J(a,b){return{key:a,node:b}}function K(a){function b(a,b){var d=ba(Y(a));return p(c,d)?f(a,V(d),b):a}function d(a,c){if(!a)return j(c),f(a,ca,c);var d=b(a,c),g=Z(d),h=aa(Y(d));return e(g,h,c),s(J(h,c),g)}function e(a,b,c){ba(Y(a))[b]=c}function f(a,b,c){a&&e(a,b,c);var d=s(J(b,c),a);return h(d),d}function g(a){return i(a),Z(a)||k(ba(Y(a)))}var h=a(fa).emit,i=a(ga).emit,j=a(la).emit,k=a(ka).emit,l={};return l[ra]=d,l[sa]=g,l[qa]=f,l}function L(a,b,c){function d(a){return function(b){return b.id==a}}var e,f;return{on:function(c,d){var g={listener:c,id:d||c};return b&&b.emit(a,c,g.id),e=s(g,e),f=s(c,f),this},emit:function(){z(f,arguments)},un:function(b){var g;e=x(e,d(b),function(a){g=a}),g&&(f=x(f,function(a){return a==g.listener}),c&&c.emit(a,g.listener,g.id))},listeners:function(){return f},hasListener:function(a){var b=a?d(a):n;return q(B(b,e))}}}function M(){function a(a){return c[a]=L(a,d,e)}function b(b){return c[b]||a(b)}var c={},d=a("newListener"),e=a("removeListener");return["emit","on","un"].forEach(function(a){b[a]=j(function(c,d){i(d,b(c)[a])})}),b}function N(a,b,c){try{var d=e.parse(b)}catch(f){}return{statusCode:a,body:b,jsonBody:d,thrown:c}}function O(a,b){function c(a,b,c){var d=A(c);a(b,u(Z(v(aa,d))),u(v(ba,d)))}function d(b,d,e){var f=a(b).emit;d.on(function(a){var b=e(a);b!==!1&&c(f,ba(b),a)},b),a("removeListener").on(function(c){c==b&&(a(c).listeners()||d.un(b))})}var e={node:a(ga),path:a(fa)};a("newListener").on(function(a){var c=/(node|path):(.*)/.exec(a);if(c){var f=e[c[1]];f.hasListener(a)||d(a,f,b(c[2]))}})}function P(a,b){function c(b,c){return a(b).on(e(c),c),n}function d(a,b,c){c=c||b;var d=e(b);return a.on(function(){var b=!1;n.forget=function(){b=!0},i(arguments,d),delete n.forget,b&&a.un(c)},c),n}function e(a){return function(){try{return a.apply(n,arguments)}catch(b){setTimeout(function(){throw b})}}}function f(b,c){return a(b+":"+c)}function g(a){return function(){var b=a.apply(this,arguments);q(b)&&(b==S.drop?s():t(b))}}function h(a,b,c){var e;e="node"==a?g(c):c,d(f(a,b),e,c)}function k(a,b){for(var c in b)h(a,c,b[c])}function l(a,b,c){return W(b)?h(a,b,c):k(a,b),n}var n,p=/^(node|path):./,r=a(ka),s=a(ia).emit,t=a(ha).emit,u=j(function(b,c){if(n[b])i(c,n[b]);else{var e=a(b),f=c[0];p.test(b)?d(e,f):e.on(f)}return n}),v=function(b,c,d){if("done"==b)r.un(c);else if("node"==b||"path"==b)a.un(b+":"+c,d);else{var e=c;a(b).un(e)}return n};return a(la).on(function(a){n.root=o(a)}),a(ma).on(function(a,b){n.header=function(a){return a?b[a]:b}}),n={on:u,addListener:u,removeListener:v,emit:a.emit,node:T(l,"node"),path:T(l,"path"),done:T(d,r),start:T(c,ma),fail:a(ja).on,abort:a(pa).emit,header:m,root:m,source:b}}function Q(a,b,c,d,e){var f=M();return b&&I(f,H(),a,b,c,d,e),C(f),D(f,K(f)),O(f,da),P(f,b)}function R(a,b,c,d,f,g,h){function i(a,b){return b===!1&&(a+=-1==a.indexOf("?")?"?":"&",a+="_="+(new Date).getTime()),a}return f=f?e.parse(e.stringify(f)):{},d?W(d)||(d=e.stringify(d),f["Content-Type"]=f["Content-Type"]||"application/json"):d=null,a(c||"GET",i(b,h),d,f,g||!1)}function S(a){var b=$("resume","pause","pipe"),c=T(r,b);return a?c(a)||W(a)?R(Q,a):R(Q,a.url,a.method,a.body,a.headers,a.withCredentials,a.cached):Q()}var T=j(function(a,b){var c=b.length;return j(function(d){for(var e=0;e<d.length;e++)b[c+e]=d[e];return b.length=c+d.length,a.apply(this,b)})}),U=(j(function(a){function b(a,b){return[i(a,b)]}var c=t(a);return j(function(a){return w(b,a,c)[0]})}),j(function(a){return j(function(b){for(var c,d=0;d<V(a);d++)if(c=i(b,a[d]))return c})})),V=h("length"),W=T(p,String),X=null,Y=h(0),Z=h(1),$=j(t),_=function(){var a=function(a){return a.exec.bind(a)},b=j(function(b){return b.unshift(/^/),a(RegExp(b.map(h("source")).join("")))}),c=/(\$?)/,d=/([\w-_]+|\*)/,e=/()/,f=/\["([^"]+)"\]/,g=/\[(\d+|\*)\]/,i=/{([\w ]*?)}/,k=/(?:{([\w ]*?)})?/,l=b(c,d,k),m=b(c,f,k),n=b(c,g,k),o=b(c,e,i),p=b(/\.\./),q=b(/\./),r=b(c,/!/),s=b(/$/);return function(a){return a(U(l,m,n,o),p,q,r,s)}}(),aa=h("key"),ba=h("node"),ca={},da=_(function(a,b,c,e,f){function h(a,b){var c=b[z],d=c&&"*"!=c?function(a){return B(a)==c}:n;return l(d,a)}function i(a,b){var c=b[A];if(!c)return a;var d=T(r,t(c.split(/\W+/))),e=g(d,C);return l(e,a)}function j(a,b){var c=!!b[y];return c?l(a,Y):a}function k(a){function b(a){return B(a)!=ca}return a==n?n:l(b,g(a,Z))}function m(a){if(a==n)return n;var b=o(),c=a,d=k(function(a){return e(a)}),e=U(b,c,d);return e}function o(){return function(a){return B(a)==ca}}function p(a){return function(b){var c=a(b);return c===!0?Y(b):c}}function q(a,b,c){return w(function(a,b){return b(a,c)},b,a)}function s(a,b,c,d,e){var f=a(c);if(f){var g=q(b,d,f),h=c.substr(V(f[0]));return e(h,g)}}function u(a,b){return T(s,a,b)}function v(a,b){return b}function x(a,b){var c=a?x:v;return D(a,b,c)}var y=1,z=2,A=3,B=g(aa,Y),C=g(ba,Y),D=U(u(a,$(j,i,h,k)),u(b,$(m)),u(c,$()),u(e,$(j,o)),u(f,$(p)),function(a){throw d('"'+a+'" could not be tokenised')});return function(a){try{return x(a,n)}catch(b){throw d('Could not compile "'+a+'" because '+b.message)}}}),ea=1,fa=ea++,ga=ea++,ha=ea++,ia=ea++,ja="fail",ka=ea++,la=ea++,ma="start",na="data",oa="end",pa=ea++,qa=ea++,ra=ea++,sa=ea++;S.drop=function(){return S.drop},"function"==typeof define&&define.amd?define("oboe",[],function(){return S}):"object"==typeof exports?module.exports=S:a.oboe=S}(function(){try{return window}catch(a){return self}}(),Object,Array,Error,JSON);
!function(a,b,c,d,e,f){function g(a,b){return function(){return a.call(this,b.apply(this,arguments))}}function h(a){return function(b){return b[a]}}function i(a,b){return b.apply(f,a)}function j(a){var b=a.length-1,d=c.prototype.slice;if(0==b)return function(){return a.call(this,d.call(arguments))};if(1==b)return function(){return a.call(this,arguments[0],d.call(arguments,1))};var e=c(a.length);return function(){for(var c=0;b>c;c++)e[c]=arguments[c];return e[b]=d.call(arguments,b),a.apply(this,e)}}function k(a){return function(b,c){return a(c,b)}}function l(a,b){return function(c){return a(c)&&b(c)}}function m(){}function n(){return!0}function o(a){return function(){return a}}function p(a,b){return b&&b.constructor===a}function q(a){return a!==f}function r(a,c){return c instanceof b&&y(function(a){return a in c},a)}function s(a,b){return[a,b]}function t(a){return A(a.reduce(k(s),X))}function u(a){return w(function(a,b){return a.unshift(b),a},[],a)}function v(a,b){return b?s(a(Y(b)),v(a,Z(b))):X}function w(a,b,c){return c?a(w(a,b,Z(c)),Y(c)):b}function x(a,b,c){function d(a,c){return a?b(Y(a))?(c(Y(a)),Z(a)):s(Y(a),d(Z(a),c)):X}return d(a,c||m)}function y(a,b){return!b||a(Y(b))&&y(a,Z(b))}function z(a,b){a&&(Y(a).apply(null,b),z(Z(a),b))}function A(a){function b(a,c){return a?b(Z(a),s(Y(a),c)):c}return b(a,X)}function B(a,b){return b&&(a(Y(b))?Y(b):B(a,Z(b)))}function C(a){"use strict";function b(){var a=0;P!==f&&P.length>p&&(c("Max buffer length exceeded: textNode"),a=Math.max(a,P.length)),Q.length>p&&(c("Max buffer length exceeded: numberNode"),a=Math.max(a,Q.length)),O=p-a+Y}function c(a){P!==f&&(m(P),n(),P=f),i=d(a+"\nLn: "+$+"\nCol: "+Z+"\nChr: "+j),o(N(f,f,i))}function e(){return T==s?(m({}),n(),void(S=!0)):((T!==t||0!==X)&&c("Unexpected end"),P!==f&&(m(P),n(),P=f),void(S=!0))}function g(a){return"\r"==a||"\n"==a||" "==a||" "==a}function h(a){if(!i){if(S)return c("Cannot write after close");var d=0;for(j=a[0];j&&(d>0&&(k=j),j=a[d++]);)switch(Y++,"\n"==j?($++,Z=0):Z++,T){case s:if("{"===j)T=u;else if("["===j)T=w;else if(!g(j))return c("Non-whitespace before {[.");continue;case z:case u:if(g(j))continue;if(T===z)U.push(A);else{if("}"===j){m({}),n(),T=U.pop()||t;continue}U.push(v)}if('"'!==j)return c('Malformed object key should start with " ');T=y;continue;case A:case v:if(g(j))continue;if(":"===j)T===v?(U.push(v),P!==f&&(m({}),l(P),P=f),X++):P!==f&&(l(P),P=f),T=t;else if("}"===j)P!==f&&(m(P),n(),P=f),n(),X--,T=U.pop()||t;else{if(","!==j)return c("Bad object");T===v&&U.push(v),P!==f&&(m(P),n(),P=f),T=z}continue;case w:case t:if(g(j))continue;if(T===w){if(m([]),X++,T=t,"]"===j){n(),X--,T=U.pop()||t;continue}U.push(x)}if('"'===j)T=y;else if("{"===j)T=u;else if("["===j)T=w;else if("t"===j)T=B;else if("f"===j)T=E;else if("n"===j)T=I;else if("-"===j)Q+=j;else if("0"===j)Q+=j,T=M;else{if(-1==="123456789".indexOf(j))return c("Bad value");Q+=j,T=M}continue;case x:if(","===j)U.push(x),P!==f&&(m(P),n(),P=f),T=t;else{if("]"!==j){if(g(j))continue;return c("Bad array")}P!==f&&(m(P),n(),P=f),n(),X--,T=U.pop()||t}continue;case y:P===f&&(P="");var e=d-1;a:for(;;){for(;W>0;)if(V+=j,j=a.charAt(d++),4===W?(P+=String.fromCharCode(parseInt(V,16)),W=0,e=d-1):W++,!j)break a;if('"'===j&&!R){T=U.pop()||t,P+=a.substring(e,d-1);break}if("\\"===j&&!R&&(R=!0,P+=a.substring(e,d-1),j=a.charAt(d++),!j))break;if(R){if(R=!1,"n"===j?P+="\n":"r"===j?P+="\r":"t"===j?P+=" ":"f"===j?P+="\f":"b"===j?P+="\b":"u"===j?(W=1,V=""):P+=j,j=a.charAt(d++),e=d-1,j)continue;break}q.lastIndex=d;var h=q.exec(a);if(!h){d=a.length+1,P+=a.substring(e,d-1);break}if(d=h.index+1,j=a.charAt(h.index),!j){P+=a.substring(e,d-1);break}}continue;case B:if(!j)continue;if("r"!==j)return c("Invalid true started with t"+j);T=C;continue;case C:if(!j)continue;if("u"!==j)return c("Invalid true started with tr"+j);T=D;continue;case D:if(!j)continue;if("e"!==j)return c("Invalid true started with tru"+j);m(!0),n(),T=U.pop()||t;continue;case E:if(!j)continue;if("a"!==j)return c("Invalid false started with f"+j);T=F;continue;case F:if(!j)continue;if("l"!==j)return c("Invalid false started with fa"+j);T=G;continue;case G:if(!j)continue;if("s"!==j)return c("Invalid false started with fal"+j);T=H;continue;case H:if(!j)continue;if("e"!==j)return c("Invalid false started with fals"+j);m(!1),n(),T=U.pop()||t;continue;case I:if(!j)continue;if("u"!==j)return c("Invalid null started with n"+j);T=J;continue;case J:if(!j)continue;if("l"!==j)return c("Invalid null started with nu"+j);T=K;continue;case K:if(!j)continue;if("l"!==j)return c("Invalid null started with nul"+j);m(null),n(),T=U.pop()||t;continue;case L:if("."!==j)return c("Leading zero not followed by .");Q+=j,T=M;continue;case M:if(-1!=="0123456789".indexOf(j))Q+=j;else if("."===j){if(-1!==Q.indexOf("."))return c("Invalid number has two dots");Q+=j}else if("e"===j||"E"===j){if(-1!==Q.indexOf("e")||-1!==Q.indexOf("E"))return c("Invalid number has two exponential");Q+=j}else if("+"===j||"-"===j){if("e"!==k&&"E"!==k)return c("Invalid symbol in number");Q+=j}else Q&&(m(parseFloat(Q)),n(),Q=""),d--,T=U.pop()||t;continue;default:return c("Unknown state: "+T)}Y>=O&&b()}}var i,j,k,l=a(qa).emit,m=a(ra).emit,n=a(sa).emit,o=a(ja).emit,p=65536,q=/[\\"\n]/g,r=0,s=r++,t=r++,u=r++,v=r++,w=r++,x=r++,y=r++,z=r++,A=r++,B=r++,C=r++,D=r++,E=r++,F=r++,G=r++,H=r++,I=r++,J=r++,K=r++,L=r++,M=r,O=p,P=f,Q="",R=!1,S=!1,T=s,U=[],V=null,W=0,X=0,Y=0,Z=0,$=1;a(na).on(h),a(oa).on(e)}function D(a,b){"use strict";function c(a){return function(b){d=a(d,b)}}var d,e={};for(var f in b)a(f).on(c(b[f]),e);a(ha).on(function(a){var b,c=Y(d),e=aa(c),f=Z(d);f&&(b=ba(Y(f)),b[e]=a)}),a(ia).on(function(){var a,b=Y(d),c=aa(b),e=Z(d);e&&(a=ba(Y(e)),delete a[c])}),a(pa).on(function(){for(var c in b)a(c).un(e)})}function E(a){var b={};return a&&a.split("\r\n").forEach(function(a){var c=a.indexOf(": ");b[a.substring(0,c)]=a.substring(c+2)}),b}function F(a,b){function c(a){return{"http:":80,"https:":443}[a]}function d(b){return b.port||c(b.protocol||a.protocol)}return!!(b.protocol&&b.protocol!=a.protocol||b.host&&b.host!=a.host||b.host&&d(b)!=d(a))}function G(a){var b=/(\w+:)?(?:\/\/)([\w.-]+)?(?::(\d+))?\/?/,c=b.exec(a)||[];return{protocol:c[1]||"",host:c[2]||"",port:c[3]||""}}function H(){return new XMLHttpRequest}function I(b,c,d,e,g,h,i){"use strict";function j(){var a=c.responseText,b=a.substr(m);b&&k(b),m=V(a)}var k=b(na).emit,l=b(ja).emit,m=0,n=!0;b(pa).on(function(){c.onreadystatechange=null,c.abort()}),"onprogress"in c&&(c.onprogress=j),c.onreadystatechange=function(){function a(){try{n&&b(ma).emit(c.status,E(c.getAllResponseHeaders())),n=!1}catch(a){}}switch(c.readyState){case 2:case 3:return a();case 4:a();var d=2==String(c.status)[0];d?(j(),b(oa).emit()):l(N(c.status,c.responseText))}};try{c.open(d,e,!0);for(var o in h)c.setRequestHeader(o,h[o]);F(a.location,G(e))||c.setRequestHeader("X-Requested-With","XMLHttpRequest"),c.withCredentials=i,c.send(g)}catch(p){a.setTimeout(T(l,N(f,f,p)),0)}}function J(a,b){return{key:a,node:b}}function K(a){function b(a,b){var d=ba(Y(a));return p(c,d)?f(a,V(d),b):a}function d(a,c){if(!a)return j(c),f(a,ca,c);var d=b(a,c),g=Z(d),h=aa(Y(d));return e(g,h,c),s(J(h,c),g)}function e(a,b,c){ba(Y(a))[b]=c}function f(a,b,c){a&&e(a,b,c);var d=s(J(b,c),a);return h(d),d}function g(a){return i(a),Z(a)||k(ba(Y(a)))}var h=a(fa).emit,i=a(ga).emit,j=a(la).emit,k=a(ka).emit,l={};return l[ra]=d,l[sa]=g,l[qa]=f,l}function L(a,b,c){function d(a){return function(b){return b.id==a}}var e,f;return{on:function(c,d){var g={listener:c,id:d||c};return b&&b.emit(a,c,g.id),e=s(g,e),f=s(c,f),this},emit:function(){z(f,arguments)},un:function(b){var g;e=x(e,d(b),function(a){g=a}),g&&(f=x(f,function(a){return a==g.listener}),c&&c.emit(a,g.listener,g.id))},listeners:function(){return f},hasListener:function(a){var b=a?d(a):n;return q(B(b,e))}}}function M(){function a(a){return c[a]=L(a,d,e)}function b(b){return c[b]||a(b)}var c={},d=a("newListener"),e=a("removeListener");return["emit","on","un"].forEach(function(a){b[a]=j(function(c,d){i(d,b(c)[a])})}),b}function N(a,b,c){try{var d=e.parse(b)}catch(f){}return{statusCode:a,body:b,jsonBody:d,thrown:c}}function O(a,b){function c(a,b,c){var d=A(c);a(b,u(Z(v(aa,d))),u(v(ba,d)))}function d(b,d,e){var f=a(b).emit;d.on(function(a){var b=e(a);b!==!1&&c(f,ba(b),a)},b),a("removeListener").on(function(c){c==b&&(a(c).listeners()||d.un(b))})}var e={node:a(ga),path:a(fa)};a("newListener").on(function(a){var c=/(node|path):(.*)/.exec(a);if(c){var f=e[c[1]];f.hasListener(a)||d(a,f,b(c[2]))}})}function P(a,b){function c(b,c){return a(b).on(f(c),c),p}function e(a,b,c){c=c||b;var d=f(b);return a.on(function(){var b=!1;p.forget=function(){b=!0},i(arguments,d),delete p.forget,b&&a.un(c)},c),p}function f(a){return function(){try{return a.apply(p,arguments)}catch(b){setTimeout(function(){throw new d(b.message)})}}}function g(b,c){return a(b+":"+c)}function h(a){return function(){var b=a.apply(this,arguments);q(b)&&(b==S.drop?t():u(b))}}function k(a,b,c){var d;d="node"==a?h(c):c,e(g(a,b),d,c)}function l(a,b){for(var c in b)k(a,c,b[c])}function n(a,b,c){return W(b)?k(a,b,c):l(a,b),p}var p,r=/^(node|path):./,s=a(ka),t=a(ia).emit,u=a(ha).emit,v=j(function(b,c){if(p[b])i(c,p[b]);else{var d=a(b),f=c[0];r.test(b)?e(d,f):d.on(f)}return p}),w=function(b,c,d){if("done"==b)s.un(c);else if("node"==b||"path"==b)a.un(b+":"+c,d);else{var e=c;a(b).un(e)}return p};return a(la).on(function(a){p.root=o(a)}),a(ma).on(function(a,b){p.header=function(a){return a?b[a]:b}}),p={on:v,addListener:v,removeListener:w,emit:a.emit,node:T(n,"node"),path:T(n,"path"),done:T(e,s),start:T(c,ma),fail:a(ja).on,abort:a(pa).emit,header:m,root:m,source:b}}function Q(a,b,c,d,e){var f=M();return b&&I(f,H(),a,b,c,d,e),C(f),D(f,K(f)),O(f,da),P(f,b)}function R(a,b,c,d,f,g,h){function i(a,b){return b===!1&&(a+=-1==a.indexOf("?")?"?":"&",a+="_="+(new Date).getTime()),a}return f=f?e.parse(e.stringify(f)):{},d?(W(d)||(d=e.stringify(d),f["Content-Type"]=f["Content-Type"]||"application/json"),f["Content-Length"]=f["Content-Length"]||d.length):d=null,a(c||"GET",i(b,h),d,f,g||!1)}function S(a){var b=$("resume","pause","pipe"),c=T(r,b);return a?c(a)||W(a)?R(Q,a):R(Q,a.url,a.method,a.body,a.headers,a.withCredentials,a.cached):Q()}var T=j(function(a,b){var c=b.length;return j(function(d){for(var e=0;e<d.length;e++)b[c+e]=d[e];return b.length=c+d.length,a.apply(this,b)})}),U=(j(function(a){function b(a,b){return[i(a,b)]}var c=t(a);return j(function(a){return w(b,a,c)[0]})}),j(function(a){return j(function(b){for(var c,d=0;d<V(a);d++)if(c=i(b,a[d]))return c})})),V=h("length"),W=T(p,String),X=null,Y=h(0),Z=h(1),$=j(t),_=function(){var a=function(a){return a.exec.bind(a)},b=j(function(b){return b.unshift(/^/),a(RegExp(b.map(h("source")).join("")))}),c=/(\$?)/,d=/([\w-_]+|\*)/,e=/()/,f=/\["([^"]+)"\]/,g=/\[(\d+|\*)\]/,i=/{([\w ]*?)}/,k=/(?:{([\w ]*?)})?/,l=b(c,d,k),m=b(c,f,k),n=b(c,g,k),o=b(c,e,i),p=b(/\.\./),q=b(/\./),r=b(c,/!/),s=b(/$/);return function(a){return a(U(l,m,n,o),p,q,r,s)}}(),aa=h("key"),ba=h("node"),ca={},da=_(function(a,b,c,e,f){function h(a,b){var c=b[z],d=c&&"*"!=c?function(a){return B(a)==c}:n;return l(d,a)}function i(a,b){var c=b[A];if(!c)return a;var d=T(r,t(c.split(/\W+/))),e=g(d,C);return l(e,a)}function j(a,b){var c=!!b[y];return c?l(a,Y):a}function k(a){function b(a){return B(a)!=ca}return a==n?n:l(b,g(a,Z))}function m(a){if(a==n)return n;var b=o(),c=a,d=k(function(a){return e(a)}),e=U(b,c,d);return e}function o(){return function(a){return B(a)==ca}}function p(a){return function(b){var c=a(b);return c===!0?Y(b):c}}function q(a,b,c){return w(function(a,b){return b(a,c)},b,a)}function s(a,b,c,d,e){var f=a(c);if(f){var g=q(b,d,f),h=c.substr(V(f[0]));return e(h,g)}}function u(a,b){return T(s,a,b)}function v(a,b){return b}function x(a,b){var c=a?x:v;return D(a,b,c)}var y=1,z=2,A=3,B=g(aa,Y),C=g(ba,Y),D=U(u(a,$(j,i,h,k)),u(b,$(m)),u(c,$()),u(e,$(j,o)),u(f,$(p)),function(a){throw d('"'+a+'" could not be tokenised')});return function(a){try{return x(a,n)}catch(b){throw d('Could not compile "'+a+'" because '+b.message)}}}),ea=1,fa=ea++,ga=ea++,ha=ea++,ia=ea++,ja="fail",ka=ea++,la=ea++,ma="start",na="data",oa="end",pa=ea++,qa=ea++,ra=ea++,sa=ea++;S.drop=function(){return S.drop},"function"==typeof define&&define.amd?define("oboe",[],function(){return S}):"object"==typeof exports?module.exports=S:a.oboe=S}(function(){try{return window}catch(a){return self}}(),Object,Array,Error,JSON);
module.exports = function (grunt) {
var autoStartBrowsers = ['Chrome', 'Firefox', 'Safari'];
function runNpmScript(command, cb) {
var opts = {
cmd: 'npm',
args: ['run', command],
opts: {
stdio: 'inherit'
}
};
var STREAM_SOURCE_PORT_HTTP = 4567;
grunt.util.spawn(opts, function(error, result, code) {
if(error) {
grunt.fail.warn(command + " failed.");
}
cb();
});
}
// NB: source files are order sensitive
var OBOE_BROWSER_SOURCE_FILES = [
'build/version.js'
, 'src/LICENCE.js'
, 'src/functional.js'
, 'src/util.js'
, 'src/lists.js'
, 'src/libs/clarinet.js'
, 'src/ascentManager.js'
, 'src/parseResponseHeaders.browser.js'
, 'src/detectCrossOrigin.browser.js'
, 'src/streamingHttp.browser.js'
, 'src/jsonPathSyntax.js'
, 'src/ascent.js'
, 'src/incrementalContentBuilder.js'
, 'src/jsonPath.js'
, 'src/singleEventPubSub.js'
, 'src/pubSub.js'
, 'src/events.js'
, 'src/patternAdapter.js'
, 'src/instanceApi.js'
, 'src/wire.js'
, 'src/defaults.js'
, 'src/publicApi.js'
];
var OBOE_NODE_SOURCE_FILES = [
'build/version.js'
, 'src/LICENCE.js'
, 'src/functional.js'
, 'src/util.js'
, 'src/lists.js'
, 'src/libs/clarinet.js'
, 'src/ascentManager.js'
, 'src/streamingHttp.node.js'
, 'src/jsonPathSyntax.js'
, 'src/ascent.js'
, 'src/incrementalContentBuilder.js'
, 'src/jsonPath.js'
, 'src/singleEventPubSub.js'
, 'src/pubSub.js'
, 'src/events.js'
, 'src/patternAdapter.js'
, 'src/instanceApi.js'
, 'src/wire.js'
, 'src/defaults.js'
, 'src/publicApi.js'
];
var FILES_TRIGGERING_KARMA = [
'src/**/*.js',
'test/specs/*.spec.js',
'test/libs/*.js'
];
// load the wrapper file for packaging source targeted at either
// browser or node
function wrapper(target){
return require('fs')
.readFileSync('src/wrapper.' + target + '.js', 'utf8')
.split('// ---contents--- //');
}
grunt.initConfig({
var autoStartBrowsers = ['Chrome', 'Firefox', 'Safari'];
pkg:grunt.file.readJSON("package.json")
, clean: ['dist/*.js', 'build/*.js']
, concat: {
browser:{
src: OBOE_BROWSER_SOURCE_FILES,
dest: 'build/oboe-browser.concat.js'
},
node:{
src: OBOE_NODE_SOURCE_FILES,
dest: 'build/oboe-node.concat.js'
}
var STREAM_SOURCE_PORT_HTTP = 4567;
// NB: source files are order sensitive
var OBOE_BROWSER_SOURCE_FILES = [
'build/version.js'
, 'src/LICENCE.js'
, 'src/functional.js'
, 'src/util.js'
, 'src/lists.js'
, 'src/libs/clarinet.js'
, 'src/ascentManager.js'
, 'src/parseResponseHeaders.browser.js'
, 'src/detectCrossOrigin.browser.js'
, 'src/streamingHttp.browser.js'
, 'src/jsonPathSyntax.js'
, 'src/ascent.js'
, 'src/incrementalContentBuilder.js'
, 'src/jsonPath.js'
, 'src/singleEventPubSub.js'
, 'src/pubSub.js'
, 'src/events.js'
, 'src/patternAdapter.js'
, 'src/instanceApi.js'
, 'src/wire.js'
, 'src/defaults.js'
, 'src/publicApi.js'
];
var OBOE_NODE_SOURCE_FILES = [
'build/version.js'
, 'src/LICENCE.js'
, 'src/functional.js'
, 'src/util.js'
, 'src/lists.js'
, 'src/libs/clarinet.js'
, 'src/ascentManager.js'
, 'src/streamingHttp.node.js'
, 'src/jsonPathSyntax.js'
, 'src/ascent.js'
, 'src/incrementalContentBuilder.js'
, 'src/jsonPath.js'
, 'src/singleEventPubSub.js'
, 'src/pubSub.js'
, 'src/events.js'
, 'src/patternAdapter.js'
, 'src/instanceApi.js'
, 'src/wire.js'
, 'src/defaults.js'
, 'src/publicApi.js'
];
var FILES_TRIGGERING_KARMA = [
'src/**/*.js',
'test/specs/*.spec.js',
'test/libs/*.js'
];
// load the wrapper file for packaging source targeted at either
// browser or node
function wrapper(target){
return require('fs')
.readFileSync('src/wrapper.' + target + '.js', 'utf8')
.split('// ---contents--- //');
}
grunt.initConfig({
pkg:grunt.file.readJSON("package.json")
, clean: ['dist/*.js', 'build/*.js']
, concat: {
browser:{
src: OBOE_BROWSER_SOURCE_FILES,
dest: 'build/oboe-browser.concat.js'
},
node:{
src: OBOE_NODE_SOURCE_FILES,
dest: 'build/oboe-node.concat.js'
}
, wrap: {
browserPackage: {
src: 'build/oboe-browser.concat.js',
dest: '.',
wrapper: wrapper('browser')
},
nodePackage: {
src: 'build/oboe-node.concat.js',
dest: '.',
wrapper: wrapper('node')
}
}
, wrap: {
browserPackage: {
src: 'build/oboe-browser.concat.js',
dest: '.',
wrapper: wrapper('browser')
},
nodePackage: {
src: 'build/oboe-node.concat.js',
dest: '.',
wrapper: wrapper('node')
}
, uglify: {
build:{
files:{
'build/oboe-browser.min.js': 'build/oboe-browser.concat.js'
}
}
}
, uglify: {
build:{
files:{
'build/oboe-browser.min.js': 'build/oboe-browser.concat.js'
}
}
, karma: {
options:{
singleRun: true,
proxies: {
'/testServer' : 'http://localhost:' + STREAM_SOURCE_PORT_HTTP
},
// test results reporter to use
// possible values: 'dots', 'progress', 'junit'
reporters : ['progress'],
// enable / disable colors in the output (reporters and logs)
colors : true
}
}
, karma: {
options:{
singleRun: true,
proxies: {
'/testServer' : 'http://localhost:' + STREAM_SOURCE_PORT_HTTP
},
// test results reporter to use
// possible values: 'dots', 'progress', 'junit'
reporters : ['progress'],
// enable / disable colors in the output (reporters and logs)
colors : true
}
,
'coverage':{
reporters : ['coverage'],
preprocessors: {
// source files to generate coverage for
// (these files will be instrumented by Istanbul)
'src/**/*.js': ['coverage']
},
'browsers': ['PhantomJS'],
configFile: 'test/unit.conf.js'
}
,
'precaptured-dev': {
// for doing a single test run with already captured browsers during development.
// this is good for running tests in browsers karma can't easily start such as
// IE running inside a Windows VM on a unix dev environment
browsers: [],
configFile: 'test/unit.conf.js',
singleRun: 'true'
}
'coverage':{
reporters : ['coverage'],
preprocessors: {
// source files to generate coverage for
// (these files will be instrumented by Istanbul)
'src/**/*.js': ['coverage']
},
'browsers': ['PhantomJS'],
configFile: 'test/unit.conf.js'
}
,
'single-dev': {
browsers: autoStartBrowsers,
configFile: 'test/unit.conf.js'
}
'precaptured-dev': {
// for doing a single test run with already captured browsers during development.
// this is good for running tests in browsers karma can't easily start such as
// IE running inside a Windows VM on a unix dev environment
browsers: [],
configFile: 'test/unit.conf.js',
singleRun: 'true'
}
,
'single-concat': {
browsers: autoStartBrowsers,
configFile: 'test/concat.conf.js'
}
,
'single-minified': {
browsers: autoStartBrowsers,
configFile: 'test/min.conf.js'
}
,
'single-amd': {
browsers: autoStartBrowsers,
configFile: 'test/amd.conf.js'
}
,
'single-browser-http': {
browsers: autoStartBrowsers,
configFile: 'test/http.conf.js'
}
,
'persist': {
// for setting up a persistent karma server.
// To start the server, the task is:
// karma:persist
// To run these, the task is:
// karma:persist:run
configFile: 'test/unit.conf.js',
browsers: [],
singleRun:false,
background:true
}
'single-dev': {
browsers: autoStartBrowsers,
configFile: 'test/unit.conf.js'
}
, copy: {
browserDist: {
files: [
{src: ['build/oboe-browser.min.js'], dest: 'dist/oboe-browser.min.js'}
, {src: ['build/oboe-browser.concat.js'], dest: 'dist/oboe-browser.js' }
]
},
nodeDist: {
files: [
{src: ['build/oboe-node.concat.js'], dest: 'dist/oboe-node.js'}
]
}
}
, exec:{
// these might not go too well on Windows :-) - get Cygwin.
reportMinifiedSize:{
command: "echo minified size is `wc -c < dist/oboe-browser.min.js` bytes"
},
reportMinifiedAndGzippedSize:{
command: "echo Size after gzip is `gzip --best --stdout dist/oboe-browser.min.js | wc -c` bytes - max 5120"
},
createGitVersionJs:{
command: "echo \"// `git describe`\" > build/version.js"
}
,
'single-concat': {
browsers: autoStartBrowsers,
configFile: 'test/concat.conf.js'
}
, watch:{
karma:{
files:FILES_TRIGGERING_KARMA,
tasks:['karma:persist:run']
},
// like above but reports the file size. This is good for
// watching while developing to make sure it doesn't get
// too big. Doesn't run tests against minified.
karmaAndSize:{
files: FILES_TRIGGERING_KARMA,
tasks:[
'karma:persist:run',
'browser-build',
'dist-sizes']
},
// like above but reports the file size. This is good for
// watching while developing to make sure it doesn't get
// too big. Doesn't run tests against minified.
testNode:{
files: FILES_TRIGGERING_KARMA,
tasks:[
'node-build']
},
restartStreamSourceAndRunTests:{
// this fails at the moment because start-stream-source
// fails if run more than once - the port is taken.
files: ['test/streamsource.js'],
tasks: ['start-stream-source', 'karma:persist:run']
}
,
'single-minified': {
browsers: autoStartBrowsers,
configFile: 'test/min.conf.js'
}
, concurrent:{
watchDev: {
tasks:[ 'watch:karmaAndSize', 'watch:restartStreamSourceAndRunTests' ],
options:{
logConcurrentOutput: true
}
}
,
'single-amd': {
browsers: autoStartBrowsers,
configFile: 'test/amd.conf.js'
}
});
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
,
'single-browser-http': {
browsers: autoStartBrowsers,
configFile: 'test/http.conf.js'
}
var streamSource;
grunt.registerTask('start-stream-source', function () {
grunt.log.ok('do we have a streaming source already?', !!streamSource);
// if we previously loaded the streamsource, stop it to let the new one in:
if( streamSource ) {
grunt.log.ok('there seems to be a streaming server already, let\'s stop it');
streamSource.stop();
,
'persist': {
// for setting up a persistent karma server.
// To start the server, the task is:
// karma:persist
// To run these, the task is:
// karma:persist:run
configFile: 'test/unit.conf.js',
browsers: [],
singleRun:false,
background:true
}
streamSource = require('./test/streamsource.js');
streamSource.start(STREAM_SOURCE_PORT_HTTP, grunt);
});
}
grunt.registerTask("jasmine_node_oboe", "Runs jasmine-node.", function() {
require('jasmine-node').executeSpecsInFolder({
"specFolders":[process.cwd() + '/test/specs/'],
"isVerbose":true,
"showColors":true,
"teamcity":false,
"useRequireJs":false,
"regExpSpec":/oboe\.(?:integration|performance)\.spec\.(js)$/,
"junitreport":{"report":false, "savePath":"./reports/", "useDotNotation":true, "consolidate":true},
"includeStackTrace":true,
"growl":false,
onComplete: this.async()
});
});
, copy: {
browserDist: {
files: [
{src: ['build/oboe-browser.min.js'], dest: 'dist/oboe-browser.min.js'}
, {src: ['build/oboe-browser.concat.js'], dest: 'dist/oboe-browser.js' }
]
},
nodeDist: {
files: [
{src: ['build/oboe-node.concat.js'], dest: 'dist/oboe-node.js'}
]
}
}
// change the auto-starting browsers so that future tests will use
// phantomjs instead of actual browsers. Can do:
// grunt headless-mode default
// to run without any actual browsers
grunt.registerTask('headless-mode', function(){
autoStartBrowsers.length = 0;
autoStartBrowsers.push('PhantomJS');
});
grunt.registerTask('test-start-server', [
'karma:persist'
]);
grunt.registerTask('test-run', [
'karma:persist:run'
]);
grunt.registerTask('dist-sizes', [
'exec:reportMinifiedAndGzippedSize'
]);
grunt.registerTask('node-build', [
'exec:createGitVersionJs',
'concat:node',
'wrap:nodePackage',
'copy:nodeDist'
]);
, exec:{
// these might not go too well on Windows :-) - get Cygwin.
reportMinifiedSize:{
command: "echo minified size is `wc -c < dist/oboe-browser.min.js` bytes"
},
reportMinifiedAndGzippedSize:{
command: "echo Size after gzip is `gzip --best --stdout dist/oboe-browser.min.js | wc -c` bytes - max 5120"
},
createGitVersionJs:{
command: "echo \"// `git describe`\" > build/version.js"
}
}
grunt.registerTask('node-build-test', [
'node-build',
'jasmine_node_oboe'
]);
grunt.registerTask('node', [
'start-stream-source',
'node-build-test'
]);
, watch:{
karma:{
files:FILES_TRIGGERING_KARMA,
tasks:['karma:persist:run']
},
grunt.registerTask('browser-build', [
'exec:createGitVersionJs',
'concat:browser',
'concat:node',
'wrap:browserPackage',
'uglify',
'copy:browserDist'
]);
grunt.registerTask('browser-build-test', [
'karma:single-dev',
'karma:single-browser-http',
'browser-build',
'karma:single-concat',
'karma:single-minified',
'karma:single-amd'
]);
grunt.registerTask('build', [
'browser-build',
'node-build'
]);
// build and run just the integration tests.
grunt.registerTask('build-integration-test', [
'build',
'start-stream-source',
'karma:single-concat',
'jasmine_node_oboe',
'dist-sizes'
]);
grunt.registerTask('default', [
'clear',
'clean',
'start-stream-source',
'browser-build-test',
// like above but reports the file size. This is good for
// watching while developing to make sure it doesn't get
// too big. Doesn't run tests against minified.
karmaAndSize:{
files: FILES_TRIGGERING_KARMA,
tasks:[
'karma:persist:run',
'browser-build',
'dist-sizes']
},
'node-build-test',
'dist-sizes'
]);
// like above but reports the file size. This is good for
// watching while developing to make sure it doesn't get
// too big. Doesn't run tests against minified.
testNode:{
files: FILES_TRIGGERING_KARMA,
tasks:[
'node-build']
},
// browser-test-auto-run or node-test-auto-run
//
// The most useful for developing. Start this task, capture some browsers
// (unless node) then edit the code. Tests will be run as the code is
// saved.
grunt.registerTask('browser-test-auto-run', [
'start-stream-source',
'karma:persist',
'concurrent:watchDev'
]);
grunt.registerTask('node-test-auto-run', [
'start-stream-source',
'watch:testNode'
]);
grunt.registerTask('coverage', [
'karma:coverage'
]);
restartStreamSourceAndRunTests:{
// this fails at the moment because start-stream-source
// fails if run more than once - the port is taken.
files: ['test/streamsource.js'],
tasks: ['start-stream-source', 'karma:persist:run']
}
}
, concurrent:{
watchDev: {
tasks:[ 'watch:karmaAndSize', 'watch:restartStreamSourceAndRunTests' ],
options:{
logConcurrentOutput: true
}
}
}
});
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
var streamSource;
grunt.registerTask('start-stream-source', function () {
grunt.log.ok('do we have a streaming source already?', !!streamSource);
// if we previously loaded the streamsource, stop it to let the new one in:
if( streamSource ) {
grunt.log.ok('there seems to be a streaming server already, let\'s stop it');
streamSource.stop();
}
streamSource = require('./test/streamsource.js');
streamSource.start(STREAM_SOURCE_PORT_HTTP, grunt);
});
grunt.registerTask("jasmine_node_oboe", "Runs jasmine-node.", function() {
runNpmScript('test-node', this.async());
});
// change the auto-starting browsers so that future tests will use
// phantomjs instead of actual browsers. Can do:
// grunt headless-mode default
// to run without any actual browsers
grunt.registerTask('headless-mode', function(){
autoStartBrowsers.length = 0;
autoStartBrowsers.push('PhantomJS');
});
grunt.registerTask('test-start-server', [
'karma:persist'
]);
grunt.registerTask('test-run', [
'karma:persist:run'
]);
grunt.registerTask('dist-sizes', [
'exec:reportMinifiedAndGzippedSize'
]);
grunt.registerTask('node-build', [
'exec:createGitVersionJs',
'concat:node',
'wrap:nodePackage',
'copy:nodeDist'
]);
grunt.registerTask('node-build-test', [
'node-build',
'jasmine_node_oboe'
]);
grunt.registerTask('node', [
'start-stream-source',
'node-build-test'
]);
grunt.registerTask('browser-build', [
'exec:createGitVersionJs',
'concat:browser',
'concat:node',
'wrap:browserPackage',
'uglify',
'copy:browserDist'
]);
grunt.registerTask('browser-build-test', [
'karma:single-dev',
'karma:single-browser-http',
'browser-build',
'karma:single-concat',
'karma:single-minified',
'karma:single-amd'
]);
grunt.registerTask('build', [
'browser-build',
'node-build'
]);
// build and run just the integration tests.
grunt.registerTask('build-integration-test', [
'build',
'start-stream-source',
'karma:single-concat',
'jasmine_node_oboe',
'dist-sizes'
]);
grunt.registerTask('default', [
'clear',
'clean',
'start-stream-source',
'browser-build-test',
'node-build-test',
'dist-sizes'
]);
// browser-test-auto-run or node-test-auto-run
//
// The most useful for developing. Start this task, capture some browsers
// (unless node) then edit the code. Tests will be run as the code is
// saved.
grunt.registerTask('browser-test-auto-run', [
'start-stream-source',
'karma:persist',
'concurrent:watchDev'
]);
grunt.registerTask('node-test-auto-run', [
'start-stream-source',
'watch:testNode'
]);
grunt.registerTask('coverage', [
'karma:coverage'
]);
};
{
"name": "oboe",
"title": "Oboe.js",
"version": "2.1.3",
"version": "2.1.4",
"description": "Oboe.js reads json, giving you the objects as they are found without waiting for the stream to finish",

@@ -12,2 +12,3 @@ "main": "./dist/oboe-node.js",

"test-run": "node ./node_modules/grunt-cli/bin/grunt test-run",
"test-node": "jasmine JASMINE_CONFIG_PATH=jasmine.json",
"browser-test-auto-run": "node ./node_modules/grunt-cli/bin/grunt test-auto-run",

@@ -53,13 +54,17 @@ "node-test-auto-run": "node ./node_modules/grunt-cli/bin/grunt node-test-auto-run",

"grunt-exec": "~0.4.2",
"grunt-karma": "~0.6.2",
"grunt-karma": "2.0.0",
"grunt-micro": "~0.1.0",
"grunt-wrap": "~0.2.0",
"jasmine": "2.5.2",
"jasmine-core": "2.5.2",
"jasmine-node": "~1.11.0",
"karma": "~0.10.0",
"karma-coverage": "^0.2.4",
"karma-firefox-launcher": "~0.1.0",
"karma-jasmine": "~0.1.5",
"karma-safari-launcher": "~0.1.1",
"karma": "1.3.0",
"karma-coverage": "1.1.1",
"karma-firefox-launcher": "1.0.0",
"karma-jasmine": "1.0.2",
"karma-phantomjs-launcher": "1.0.2",
"karma-safari-launcher": "1.0.0",
"matchdep": "~0.1.2",
"request": "^2.55.0"
"request": "2.81.0",
"sinon": "=1.17.3"
},

@@ -66,0 +71,0 @@ "dependencies": {

@@ -5,10 +5,10 @@ module.exports = function(config) {

frameworks:['jasmine'],
// base path, that will be used to resolve files and exclude
basePath : '..',
// list of files / patterns to load in the browser
files : [
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
, 'test/libs/sinon.js'

@@ -19,13 +19,13 @@ , 'test/libs/sinon-ie.js'

, 'dist/oboe-browser.min.js'
, 'test/specs/amd.integration.spec.js'
],
// 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 : false,
// Continuous Integration mode

@@ -32,0 +32,0 @@ // if true, it capture browsers, run tests and exit

@@ -8,23 +8,23 @@ module.exports = function(config) {

basePath : '..',
// list of files / patterns to load in the browser
files : [
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
files : [
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
, 'test/libs/sinon.js'
, 'test/libs/sinon-ie.js'
, 'test/libs/*.js'
, 'dist/oboe-browser.js'
, 'dist/oboe-browser.js'
, 'test/specs/oboe.component.spec.js'
, 'test/specs/oboe.integration.spec.js'
],
// 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 : false,
// Continuous Integration mode

@@ -31,0 +31,0 @@ // if true, it capture browsers, run tests and exit

@@ -8,7 +8,7 @@ module.exports = function(config) {

basePath : '..',
// list of files / patterns to load in the browser
files : [
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
files : [
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
, 'test/libs/sinon.js'

@@ -19,19 +19,19 @@ , 'test/libs/sinon-ie.js'

, 'src/events.js'
, 'src/functional.js'
, 'src/functional.js'
, 'src/util.js'
, 'src/parseResponseHeaders.browser.js'
, 'src/detectCrossOrigin.browser.js'
, 'src/streamingHttp.browser.js'
, 'src/parseResponseHeaders.browser.js'
, 'src/detectCrossOrigin.browser.js'
, 'src/streamingHttp.browser.js'
, 'test/specs/streamingHttp.integration.spec.js'
],
// 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 : false,
// Continuous Integration mode

@@ -38,0 +38,0 @@ // if true, it capture browsers, run tests and exit

function listMatcher(expectedList){
return eq.call( this, this.actual, expectedList );
function eq( actual, expected ) {
if( !actual && !expected ) {
return true;
}
if( !actual || !expected ) {
this.message = function(){
return 'one false but not both ' +
actual + ' ' + expected
};
actual + ' ' + expected;
};
return false;
}
if( head(actual) != head(expected) ) {
this.message = function(){
return 'different items in list' +
head(actual) + head(expected)
};
head(actual) + head(expected);
};
return false;
}
return eq.call( this, tail(actual), tail(expected) );
}
}
}

@@ -0,181 +1,195 @@

var oboe;
var sinon;
var globalContext;
var module = module ? module : undefined;
if(module && module.exports) {
sinon = require('sinon');
oboe = require('../../dist/oboe-node.js');
globalContext = GLOBAL;
} else {
sinon = window.sinon;
oboe = window.oboe;
globalContext = window;
}
/*
Assertion helpers for testing the interface exposed as window.oboe
These assertions mostly rely on everything that sits behind there as well (so they aren't
true unit testing assertions, more of a suite of component testing helpers).
Assertion helpers for testing the interface exposed as window.oboe
These assertions mostly rely on everything that sits behind there as well (so they aren't
true unit testing assertions, more of a suite of component testing helpers).
*/
function givenAnOboeInstance(jsonFileName) {
function OboeAsserter() {
function OboeAsserter() {
var asserter = this,
oboeInstance,
var asserter = this,
oboeInstance,
expectingErrors = false,
givenErrors = [],
completeJson, // assigned in the requestCompleteCallback
expectingErrors = false,
spiedCallback; //erk: only one callback stub per Asserter right now :-s
givenErrors = [],
function requestComplete(completeJsonFromJsonCompleteCall){
completeJson = completeJsonFromJsonCompleteCall;
asserter.isComplete = true;
}
oboeInstance = oboe( jsonFileName
).done(requestComplete);
oboeInstance.fail(function(e) {
// Unless set up to expect them, the test isn't expecting errors.
// Fail the test on getting an error:
expect(expectingErrors).toBeTruthy();
});
completeJson, // assigned in the requestCompleteCallback
// designed for use with jasmine's waitsFor, ie:
// waitsFor(asserter.toComplete())
this.toComplete = function() {
return function() {
return asserter.isComplete;
}
spiedCallback; //erk: only one callback stub per Asserter right now :-s
function requestComplete(completeJsonFromJsonCompleteCall){
completeJson = completeJsonFromJsonCompleteCall;
asserter.isComplete = true;
}
oboeInstance = oboe( jsonFileName
).done(requestComplete);
oboeInstance.fail(function(e) {
// Unless set up to expect them, the test isn't expecting errors.
// Fail the test on getting an error:
expect(expectingErrors).toBeTruthy();
});
// designed for use with jasmine's waitsFor, ie:
// waitsFor(asserter.toComplete())
this.toComplete = function() {
return function() {
return asserter.isComplete;
}
this.andWeAreListeningForNodes = function(pattern, callback, scope) {
spiedCallback = callback ? sinon.spy(callback) : sinon.stub();
oboeInstance.node(pattern, argumentClone(spiedCallback), scope);
return this;
};
}
this.andWeAreListeningForPaths = function(pattern, callback, scope) {
spiedCallback = callback ? sinon.spy(callback) : sinon.stub();
oboeInstance.path(pattern, argumentClone(spiedCallback), scope);
return this;
};
this.andWeHaveAFaultyCallbackListeningFor = function(pattern) {
spiedCallback = sinon.stub().throws();
oboeInstance.path(pattern, argumentClone(spiedCallback));
return this;
};
this.andWeAreExpectingSomeErrors = function() {
expectingErrors = true;
spiedCallback = sinon.stub();
oboeInstance.fail(argumentClone(spiedCallback));
return this;
};
this.andWeAbortTheRequest = function() {
oboeInstance.abort();
return this;
};
this.andWeAreListeningForNodes = function(pattern, callback, scope) {
spiedCallback = callback ? sinon.spy(callback) : sinon.stub();
this._whenGivenInput = function(input, close) {
var jsonSerialisedInput;
oboeInstance.node(pattern, argumentClone(spiedCallback), scope);
return this;
};
if (typeof input == 'string') {
jsonSerialisedInput = input;
} else {
jsonSerialisedInput = JSON.stringify(input);
}
this.andWeAreListeningForPaths = function(pattern, callback, scope) {
spiedCallback = callback ? sinon.spy(callback) : sinon.stub();
if( !jsonSerialisedInput || jsonSerialisedInput.length == 0 ) {
throw new Error('Faulty test - input not valid:' + input);
}
oboeInstance.path(pattern, argumentClone(spiedCallback), scope);
return this;
};
oboeInstance.emit('data', jsonSerialisedInput );
if( close ) {
oboeInstance.emit('end');
}
this.andWeHaveAFaultyCallbackListeningFor = function(pattern) {
spiedCallback = sinon.stub().throws();
return this;
};
this.whenGivenInput = function(input) {
return this._whenGivenInput(input, true);
};
this.whenGivenInputPart = function(input) {
return this._whenGivenInput(input, false);
};
oboeInstance.path(pattern, argumentClone(spiedCallback));
return this;
};
this.whenGivenInputOneCharAtATime = function(input) {
var jsonSerialisedInput;
this.andWeAreExpectingSomeErrors = function() {
expectingErrors = true;
if (typeof input == 'string') {
jsonSerialisedInput = input;
} else {
jsonSerialisedInput = JSON.stringify(input);
}
spiedCallback = sinon.stub();
if( !jsonSerialisedInput || jsonSerialisedInput.length == 0 ) {
throw new Error('Faulty test - input not valid:' + input);
}
oboeInstance.fail(argumentClone(spiedCallback));
return this;
};
for( var i = 0; i< jsonSerialisedInput.length; i++) {
oboeInstance.emit('data', jsonSerialisedInput.charAt(i) );
}
this.andWeAbortTheRequest = function() {
oboeInstance.abort();
return this;
};
return this;
};
this.whenInputFinishes = function() {
oboeInstance.emit('end');
this._whenGivenInput = function(input, close) {
var jsonSerialisedInput;
return this;
};
if (typeof input == 'string') {
jsonSerialisedInput = input;
} else {
jsonSerialisedInput = JSON.stringify(input);
}
/**
* Assert any number of conditions were met on the spied callback
*/
this.thenTheInstance = function( /* ... functions ... */ ){
if( givenErrors.length > 0 ) {
throw new Error('error found during previous stages\n' + givenErrors[0].stack);
}
for (var i = 0; i < arguments.length; i++) {
var assertion = arguments[i];
assertion.testAgainst(spiedCallback, oboeInstance, completeJson);
}
if( !jsonSerialisedInput || jsonSerialisedInput.length == 0 ) {
throw new Error('Faulty test - input not valid:' + input);
}
return this;
oboeInstance.emit('data', jsonSerialisedInput );
if( close ) {
oboeInstance.emit('end');
}
return this;
};
this.whenGivenInput = function(input) {
return this._whenGivenInput(input, true);
};
this.whenGivenInputPart = function(input) {
return this._whenGivenInput(input, false);
};
this.whenGivenInputOneCharAtATime = function(input) {
var jsonSerialisedInput;
if (typeof input == 'string') {
jsonSerialisedInput = input;
} else {
jsonSerialisedInput = JSON.stringify(input);
}
if( !jsonSerialisedInput || jsonSerialisedInput.length == 0 ) {
throw new Error('Faulty test - input not valid:' + input);
}
for( var i = 0; i< jsonSerialisedInput.length; i++) {
oboeInstance.emit('data', jsonSerialisedInput.charAt(i) );
}
return this;
};
this.whenInputFinishes = function() {
oboeInstance.emit('end');
return this;
};
/**
* Assert any number of conditions were met on the spied callback
*/
this.thenTheInstance = function( /* ... functions ... */ ){
if( givenErrors.length > 0 ) {
throw new Error('error found during previous stages\n' + givenErrors[0].stack);
}
for (var i = 0; i < arguments.length; i++) {
var assertion = arguments[i];
assertion.testAgainst(spiedCallback, oboeInstance, completeJson);
}
return this;
};
/** sinon stub is only really used to record arguments given.
* However, we want to preserve the arguments given at the time of calling, because they might subsequently
* be changed inside the parser so everything gets cloned before going to the stub
*/
function argumentClone(delegateCallback) {
return function(){
function clone(original){
// Note: window.eval being used here instead of JSON.parse because
// eval can handle 'undefined' in the string but JSON.parse cannot.
// This isn't wholy ideal since this means we're relying on JSON.
// stringify to create invalid JSON. But at least there are no
// security concerns with this being a test.
return globalContext.eval( '(' + JSON.stringify( original ) + ')' );
}
function toArray(args) {
return Array.prototype.slice.call(args);
}
var cloneArguments = toArray(arguments).map(clone);
return delegateCallback.apply( this, cloneArguments );
};
/** sinon stub is only really used to record arguments given.
* However, we want to preserve the arguments given at the time of calling, because they might subsequently
* be changed inside the parser so everything gets cloned before going to the stub
*/
function argumentClone(delegateCallback) {
return function(){
function clone(original){
// Note: window.eval being used here instead of JSON.parse because
// eval can handle 'undefined' in the string but JSON.parse cannot.
// This isn't wholy ideal since this means we're relying on JSON.
// stringify to create invalid JSON. But at least there are no
// security concerns with this being a test.
return window.eval( '(' + JSON.stringify( original ) + ')' );
}
function toArray(args) {
return Array.prototype.slice.call(args);
}
var cloneArguments = toArray(arguments).map(clone);
return delegateCallback.apply( this, cloneArguments );
};
}
}
return new OboeAsserter();
}
}
return new OboeAsserter();
}

@@ -185,9 +199,9 @@

var wasPassedAnErrorObject = {
testAgainst: function failIfNotPassedAnError(callback, oboeInstance) {
if( !callback.args[0][0] instanceof Error ) {
throw new Error("Callback should have been given an error but was given" + callback.constructor.name);
}
}
testAgainst: function failIfNotPassedAnError(callback, oboeInstance) {
if( !callback.args[0][0] instanceof Error ) {
throw new Error("Callback should have been given an error but was given" + callback.constructor.name);
}
}
};

@@ -199,14 +213,14 @@

function foundNMatches(n){
return {
testAgainst:
function(callback, oboeInstance) {
if( n != callback.callCount ) {
throw new Error('expected to have been called ' + n + ' times but has been called ' +
callback.callCount + ' times. \n' +
"all calls were with:" +
reportArgumentsToCallback(callback.args)
)
}
return {
testAgainst:
function(callback, oboeInstance) {
if( n != callback.callCount ) {
throw new Error('expected to have been called ' + n + ' times but has been called ' +
callback.callCount + ' times. \n' +
"all calls were with:" +
reportArgumentsToCallback(callback.args)
)
}
}
}
}
}

@@ -216,9 +230,9 @@

function hasRootJson(expected){
return {
testAgainst:
function(callback, oboeInstance) {
expect(oboeInstance.root()).toEqual(expected);
}
}
return {
testAgainst:
function(callback, oboeInstance) {
expect(oboeInstance.root()).toEqual(expected);
}
}
}

@@ -229,48 +243,48 @@

function gaveFinalCallbackWithRootJson(expected) {
return {
testAgainst:
function(callback, oboeInstance, completeJson) {
expect(completeJson).toEqual(expected);
}
}
return {
testAgainst:
function(callback, oboeInstance, completeJson) {
expect(completeJson).toEqual(expected);
}
}
}
var foundOneMatch = foundNMatches(1),
calledCallbackOnce = foundNMatches(1),
calledCallbackOnce = foundNMatches(1),
foundNoMatches = foundNMatches(0);
function wasCalledbackWithContext(callbackScope) {
return {
testAgainst:
function(callbackStub, oboeInstance) {
if(!callbackStub.calledOn(callbackScope)){
if( !callbackStub.called ) {
throw new Error('Expected to be called with context ' + callbackScope + ' but has not been called at all');
}
throw new Error('was not called in the expected context. Expected ' + callbackScope + ' but got ' +
callbackStub.getCall(0).thisValue);
}
return {
testAgainst:
function(callbackStub, oboeInstance) {
if(!callbackStub.calledOn(callbackScope)){
if( !callbackStub.called ) {
throw new Error('Expected to be called with context ' + callbackScope + ' but has not been called at all');
}
throw new Error('was not called in the expected context. Expected ' + callbackScope + ' but got ' +
callbackStub.getCall(0).thisValue);
}
};
}
};
}
function wasGivenTheOboeAsContext() {
return {
testAgainst:
function(callbackStub, oboeInstance) {
return wasCalledbackWithContext(oboeInstance).testAgainst(callbackStub, oboeInstance);
}
};
return {
testAgainst:
function(callbackStub, oboeInstance) {
return wasCalledbackWithContext(oboeInstance).testAgainst(callbackStub, oboeInstance);
}
};
}
function lastOf(array){
return array && array[array.length-1];
return array && array[array.length-1];
}
function penultimateOf(array){
return array && array[array.length-2];
return array && array[array.length-2];
}
function prepenultimateOf(array){
return array && array[array.length-3];
return array && array[array.length-3];
}

@@ -284,15 +298,15 @@

return "\n" + callbackArgs.map( function( args, i ){
return "\n" + callbackArgs.map( function( args, i ){
var ancestors = args[2];
return "Call number " + i + " was: \n" +
"\tnode: " + JSON.stringify( args[0] ) + "\n" +
"\tpath: " + JSON.stringify( args[1] ) + "\n" +
"\tparent: " + JSON.stringify( lastOf(ancestors) ) + "\n" +
"\tgrandparent: " + JSON.stringify( penultimateOf(ancestors) ) + "\n" +
"\tancestors: " + JSON.stringify( ancestors );
}).join("\n\n");
var ancestors = args[2];
return "Call number " + i + " was: \n" +
"\tnode: " + JSON.stringify( args[0] ) + "\n" +
"\tpath: " + JSON.stringify( args[1] ) + "\n" +
"\tparent: " + JSON.stringify( lastOf(ancestors) ) + "\n" +
"\tgrandparent: " + JSON.stringify( penultimateOf(ancestors) ) + "\n" +
"\tancestors: " + JSON.stringify( ancestors );
}).join("\n\n");
}

@@ -303,105 +317,121 @@

return {
testAgainst: function assertMatchedRightObject( callbackStub ) {
if(!callbackStub.calledWith(obj)) {
return {
testAgainst: function assertMatchedRightObject( callbackStub ) {
var objectPassedToCall = function(callArgs){return callArgs[0]};
throw new Error( "was not called with the object " + JSON.stringify(obj) + "\n" +
"objects that I got are:" +
JSON.stringify(callbackStub.args.map(objectPassedToCall) ) + "\n" +
"all calls were with:" +
reportArgumentsToCallback(callbackStub.args));
}
}
, atPath: function assertAtRightPath(path) {
var oldAssertion = this.testAgainst;
this.testAgainst = function( callbackStub ){
oldAssertion.apply(this, arguments);
if(!callbackStub.calledWithMatch(sinon.match.any, path)) {
throw new Error( "was not called with the path " + JSON.stringify(path) + "\n" +
"paths that I have are:\n" +
callbackStub.args.map(function(callArgs){
return "\t" + JSON.stringify(callArgs[1]) + "\n";
}) + "\n" +
"all calls were with:" +
reportArgumentsToCallback(callbackStub.args));
}
};
return this;
}
, withParent: function( expectedParent ) {
var oldAssertion = this.testAgainst;
this.testAgainst = function( callbackStub ){
oldAssertion.apply(this, arguments);
var parentMatcher = sinon.match(function (array) {
if(!callbackStub.calledWith(obj)) {
var foundParent = penultimateOf(array);
// since this is a matcher, we can't ues expect().toEqual()
// because then the test would fail on the first non-match
// under jasmine. Using stringify is slightly brittle and
// if this breaks we need to work out how to plug into Jasmine's
// inner equals(a,b) function
return JSON.stringify(foundParent) == JSON.stringify(expectedParent)
}, "had the right parent");
if(!callbackStub.calledWithMatch(obj, sinon.match.any, parentMatcher)) {
throw new Error( "was not called with the object" + JSON.stringify(obj) +
" and parent object " + JSON.stringify(expectedParent) +
"all calls were with:" +
reportArgumentsToCallback(callbackStub.args));
}
};
return this;
var objectPassedToCall = function(callArgs){return callArgs[0]};
throw new Error( "was not called with the object " + JSON.stringify(obj) + "\n" +
"objects that I got are:" +
JSON.stringify(callbackStub.args.map(objectPassedToCall) ) + "\n" +
"all calls were with:" +
reportArgumentsToCallback(callbackStub.args));
}
, withGrandparent: function( expectedGrandparent ) {
var oldAssertion = this.testAgainst;
this.testAgainst = function( callbackStub ){
oldAssertion.apply(this, arguments);
var grandparentMatcher = sinon.match(function (array) {
}
// since this is a matcher, we can't ues expect().toEqual()
// because then the test would fail on the first non-match
// under jasmine. Using stringify is slightly brittle and
// if this breaks we need to work out how to plug into Jasmine's
// inner equals(a,b) function
, atPath: function assertAtRightPath(path) {
var oldAssertion = this.testAgainst;
var foundGrandparent = prepenultimateOf(array);
return JSON.stringify(foundGrandparent) == JSON.stringify(expectedGrandparent);
}, "had the right grandparent");
if(!callbackStub.calledWithMatch(obj, sinon.match.any, grandparentMatcher)) {
throw new Error( "was not called with the object" + JSON.stringify(obj) +
" and garndparent object " + JSON.stringify(expectedGrandparent) +
"all calls were with:" +
reportArgumentsToCallback(callbackStub.args));
}
};
return this;
}
, atRootOfJson: function assertAtRootOfJson() {
this.atPath([]);
return this;
}
};
this.testAgainst = function( callbackStub ){
oldAssertion.apply(this, arguments);
if(!callbackStub.calledWithMatch(sinon.match.any, path)) {
throw new Error( "was not called with the path " + JSON.stringify(path) + "\n" +
"paths that I have are:\n" +
callbackStub.args.map(function(callArgs){
return "\t" + JSON.stringify(callArgs[1]) + "\n";
}) + "\n" +
"all calls were with:" +
reportArgumentsToCallback(callbackStub.args));
}
};
return this;
}
, withParent: function( expectedParent ) {
var oldAssertion = this.testAgainst;
this.testAgainst = function( callbackStub ){
oldAssertion.apply(this, arguments);
var parentMatcher = sinon.match(function (array) {
var foundParent = penultimateOf(array);
// since this is a matcher, we can't ues expect().toEqual()
// because then the test would fail on the first non-match
// under jasmine. Using stringify is slightly brittle and
// if this breaks we need to work out how to plug into Jasmine's
// inner equals(a,b) function
return JSON.stringify(foundParent) == JSON.stringify(expectedParent)
}, "had the right parent");
if(!callbackStub.calledWithMatch(obj, sinon.match.any, parentMatcher)) {
throw new Error( "was not called with the object" + JSON.stringify(obj) +
" and parent object " + JSON.stringify(expectedParent) +
"all calls were with:" +
reportArgumentsToCallback(callbackStub.args));
}
};
return this;
}
, withGrandparent: function( expectedGrandparent ) {
var oldAssertion = this.testAgainst;
this.testAgainst = function( callbackStub ){
oldAssertion.apply(this, arguments);
var grandparentMatcher = sinon.match(function (array) {
// since this is a matcher, we can't ues expect().toEqual()
// because then the test would fail on the first non-match
// under jasmine. Using stringify is slightly brittle and
// if this breaks we need to work out how to plug into Jasmine's
// inner equals(a,b) function
var foundGrandparent = prepenultimateOf(array);
return JSON.stringify(foundGrandparent) == JSON.stringify(expectedGrandparent);
}, "had the right grandparent");
if(!callbackStub.calledWithMatch(obj, sinon.match.any, grandparentMatcher)) {
throw new Error( "was not called with the object" + JSON.stringify(obj) +
" and garndparent object " + JSON.stringify(expectedGrandparent) +
"all calls were with:" +
reportArgumentsToCallback(callbackStub.args));
}
};
return this;
}
, atRootOfJson: function assertAtRootOfJson() {
this.atPath([]);
return this;
}
};
}
// Export if node
if (module && module.exports) {
module.exports = {
givenAnOboeInstance: givenAnOboeInstance,
matched: matched,
hasRootJson: hasRootJson,
wasGivenTheOboeAsContext: wasGivenTheOboeAsContext,
foundOneMatch: foundOneMatch,
gaveFinalCallbackWithRootJson: gaveFinalCallbackWithRootJson,
calledCallbackOnce: calledCallbackOnce,
foundNoMatches: foundNoMatches,
foundNMatches: foundNMatches,
wasPassedAnErrorObject: wasPassedAnErrorObject
};
}
function spiedPubSub() {
var realPubSub = pubSub();
var realPubSub = pubSub();
function fakedPubSub( eventName ) {
var single = realPubSub(eventName);
function fakedPubSub( eventName ) {
var alreadySpied = !!single.emit.isSpy;
if( !alreadySpied ) {
spyOn( single, 'emit' ).andCallThrough();
spyOn( single, 'on' ).andCallThrough();
spyOn( single, 'un' ).andCallThrough();
}
return single;
}
fakedPubSub.emit = realPubSub.emit;
fakedPubSub.on = realPubSub.on;
fakedPubSub.un = realPubSub.un;
return fakedPubSub;
var single = realPubSub(eventName);
var alreadySpied = jasmine.isSpy(single.emit);
if( !alreadySpied ) {
spyOn( single, 'emit' ).and.callThrough();
spyOn( single, 'on' ).and.callThrough();
spyOn( single, 'un' ).and.callThrough();
}
return single;
}
fakedPubSub.emit = realPubSub.emit;
fakedPubSub.on = realPubSub.on;
fakedPubSub.un = realPubSub.un;
return fakedPubSub;
}

@@ -29,73 +29,73 @@

var eventTypes = {};
var eventsEmitted = [];
var eventNamesEmitted = [];
var eventTypesEmitted = {};
var callCount = {};
function record(eventName){
return function() {
var args = Array.prototype.slice.apply(arguments);
eventsEmitted.push({
type: eventName,
args: args
});
eventNamesEmitted.push(eventName);
eventTypesEmitted[eventName].push(args);
callCount[eventName]++;
}
}
eventNames.forEach( function( eventName ){
eventTypes[eventName] = {
'emit': jasmine.createSpy(eventName + '/emit')
.andCallFake(record(eventName))
var eventTypes = {};
var eventsEmitted = [];
var eventNamesEmitted = [];
var eventTypesEmitted = {};
var callCount = {};
function record(eventName){
return function() {
var args = Array.prototype.slice.apply(arguments);
eventsEmitted.push({
type: eventName,
args: args
});
eventNamesEmitted.push(eventName);
eventTypesEmitted[eventName].push(args);
callCount[eventName]++;
}
}
eventNames.forEach( function( eventName ){
eventTypes[eventName] = {
'emit': jasmine.createSpy(eventName + '/emit')
.and.callFake(record(eventName))
, 'on': jasmine.createSpy(eventName + '/on')
, 'un': jasmine.createSpy(eventName + '/un')
};
eventTypesEmitted[eventName] = [];
callCount[eventName] = 0;
});
};
function bus( eventName ) {
return eventTypes[eventName];
}
bus.events = eventsEmitted;
bus.eventNames = eventNamesEmitted;
bus.eventTypesEmitted = eventTypesEmitted;
bus.callCount = callCount;
eventTypesEmitted[eventName] = [];
callCount[eventName] = 0;
});
['emit', 'on'].forEach(function(methodName){
bus[methodName] = varArgs(function(eventName, parameters){
apply( parameters, eventTypes[eventName][methodName]);
});
});
function bus( eventName ) {
return bus;
return eventTypes[eventName];
}
bus.events = eventsEmitted;
bus.eventNames = eventNamesEmitted;
bus.eventTypesEmitted = eventTypesEmitted;
bus.callCount = callCount;
['emit', 'on'].forEach(function(methodName){
bus[methodName] = varArgs(function(eventName, parameters){
apply( parameters, eventTypes[eventName][methodName]);
});
});
return bus;
}
function eventBlackBox( pubsub, eventNames ) {
var recording = [];
eventNames.forEach(function(eventName){
pubsub(eventName).on(function(val, val2){
recording.push({
type: eventName,
values: arguments,
val: val,
val2: val
});
var recording = [];
eventNames.forEach(function(eventName){
pubsub(eventName).on(function(val, val2){
recording.push({
type: eventName,
values: arguments,
val: val,
val2: val
});
});
return recording;
});
});
return recording;
}

@@ -8,7 +8,7 @@ module.exports = function(config) {

basePath : '..',
// list of files / patterns to load in the browser
files : [
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
, 'test/libs/sinon.js'

@@ -19,3 +19,3 @@ , 'test/libs/sinon-ie.js'

, 'dist/oboe-browser.min.js'
, 'test/specs/oboe.component.spec.js'

@@ -25,10 +25,10 @@ , 'test/specs/oboe.integration.spec.js'

],
// 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 : false,
// Continuous Integration mode

@@ -35,0 +35,0 @@ // if true, it capture browsers, run tests and exit

describe("oboe loaded using require", function() {
it('is not on the global namespace by default', function () {
expect(window.oboe).toBe(undefined)
})
it('can be loaded using require', function () {
it('is not on the global namespace by default', function () {
var doneTest;
expect(window.oboe).toBe(undefined);
});
require(['oboe'], function(oboe){
expect(require('oboe')).toBeOboe()
doneTest = true;
});
waitsFor('oboe to load using require', function(){return doneTest});
it('can be loaded using require', function (done) {
})
it('it not on global after being loaded', function () {
require(['oboe'], function(oboe){
expect(oboe).not.toBe(undefined);
expect(oboe('foo.json')).not.toBe(undefined);
done();
});
});
var doneTest;
it('it not on global after being loaded', function (done) {
require(['oboe'], function(oboe){
expect(window.oboe).toBe(undefined)
doneTest = true;
});
waitsFor('oboe to load using require', function(){return doneTest});
})
beforeEach(function(){
this.addMatchers({
toBeOboe:function(){
var potentialOboe = this.actual;
return !!( potentialOboe &&
potentialOboe('foo.json').node
);
}
})
});
});
require(['oboe'], function(oboe){
expect(window.oboe).toBe(undefined);
done();
});
});
});

@@ -5,3 +5,3 @@

var docs =
{
{
empty_input:

@@ -31,4 +31,4 @@ { text : '',

, [SAX_VALUE_CLOSE , undefined]
]

@@ -43,4 +43,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -55,4 +55,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -69,4 +69,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -81,4 +81,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -92,4 +92,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -103,4 +103,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -113,4 +113,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -125,4 +125,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -139,4 +139,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -146,3 +146,3 @@ }

{ text : '["one", "two"]'
, events :
, events :
[ [SAX_VALUE_OPEN, []]

@@ -152,4 +152,4 @@ , [SAX_VALUE_OPEN, 'one'], [SAX_VALUE_CLOSE, undefined]

, [SAX_VALUE_CLOSE , undefined]
]

@@ -160,3 +160,3 @@ }

'[null,null,null,[]]," \\\\ "]'
, events :
, events :
[ [SAX_VALUE_OPEN, []]

@@ -182,4 +182,4 @@ , [SAX_VALUE_OPEN, 'foo'], [SAX_VALUE_CLOSE, undefined]

, [SAX_VALUE_CLOSE , undefined]
]

@@ -189,8 +189,8 @@ }

{ text : '[10e-01]'
, events :
, events :
[ [SAX_VALUE_OPEN, []]
, [SAX_VALUE_OPEN, 10e-01], [SAX_VALUE_CLOSE, undefined]
, [SAX_VALUE_CLOSE , undefined]
]

@@ -208,4 +208,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -223,4 +223,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -241,4 +241,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -255,4 +255,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -271,8 +271,8 @@ }

, [SAX_VALUE_CLOSE , undefined]
]
}
, obj_strange_strings :
{ text :
{ text :
'{"foo": "bar and all\\\"", "bar": "its \\\"nice\\\""}'

@@ -286,8 +286,8 @@ , events :

, [SAX_VALUE_CLOSE , undefined]
]
}
}
, bad_foo_bar :
{ text :
{ text :
'["foo", "bar"'

@@ -302,3 +302,3 @@ , events :

, string_invalid_escape:
{ text :
{ text :
'["and you can\'t escape thi\s"]'

@@ -324,4 +324,4 @@ , events :

, [SAX_VALUE_CLOSE , undefined]
]

@@ -335,4 +335,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -348,11 +348,11 @@ }

, [SAX_VALUE_CLOSE , undefined]
]
}
, non_utf8 :
{ text :
{ text :
'{"CoreletAPIVersion":2,"CoreletType":"standalone",' +
'"documentation":"A corelet that provides the capability to upload' +
' a folder’s contents into a user’s locker.","functions":[' +
' a folder’s contents into a user’s locker.","functions":[' +
'{"documentation":"Displays a dialog box that allows user to ' +

@@ -383,3 +383,3 @@ 'select a folder on the local system.","name":' +

'"micro":1,"minor":0},"versionString":"0.0.1"}'
, events :
, events :
[ [SAX_VALUE_OPEN, {}], [SAX_KEY, "CoreletAPIVersion" ]

@@ -514,3 +514,3 @@ , [SAX_VALUE_OPEN, 2 ], [SAX_VALUE_CLOSE, undefined]

{ text : '[[[["foo"]]]]'
, events :
, events :
[ [SAX_VALUE_OPEN, []]

@@ -525,4 +525,4 @@ , [SAX_VALUE_OPEN, []]

, [SAX_VALUE_CLOSE , undefined]
]

@@ -532,8 +532,8 @@ }

{ text : '[-9223372036854775808]'
, events :
, events :
[ [SAX_VALUE_OPEN, []]
, [SAX_VALUE_OPEN, -9223372036854775808], [SAX_VALUE_CLOSE, undefined]
, [SAX_VALUE_CLOSE , undefined]
]

@@ -543,8 +543,8 @@ }

{ text : '[9223372036854775808]'
, events :
, events :
[ [SAX_VALUE_OPEN, []]
, [SAX_VALUE_OPEN, 9223372036854775808], [SAX_VALUE_CLOSE, undefined]
, [SAX_VALUE_CLOSE , undefined]
]

@@ -561,4 +561,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -602,4 +602,4 @@ }

, [SAX_VALUE_CLOSE , undefined]
]

@@ -609,6 +609,6 @@ }

{ text : '{ "firstName": "John", "lastName" : "Smith", "age" : ' +
'25, "address" : { "streetAddress": "21 2nd Street", ' +
'25, "address" : { "streetAddress": "21 2nd Street", ' +
'"city" : "New York", "state" : "NY", "postalCode" : ' +
' "10021" }, "phoneNumber": [ { "type" : "home", ' +
'"number": "212 555-1234" }, { "type" : "fax", ' +
' "10021" }, "phoneNumber": [ { "type" : "home", ' +
'"number": "212 555-1234" }, { "type" : "fax", ' +
'"number": "646 555-4567" } ] }'

@@ -675,4 +675,4 @@ , events :

, [SAX_VALUE_CLOSE , undefined]
]

@@ -690,4 +690,4 @@ }

, [SAX_VALUE_OPEN, []]
, [SAX_VALUE_OPEN, 4], [SAX_VALUE_CLOSE, undefined]
, [FAIL_EVENT , undefined]
, [SAX_VALUE_OPEN, 4], [SAX_VALUE_CLOSE, undefined]
, [FAIL_EVENT , undefined]
]

@@ -708,3 +708,3 @@ }

, json_org :
{ text :
{ text :
('{\r\n' +

@@ -719,3 +719,3 @@ ' "glossary": {\n' +

' \t\t\t\t\t"SortAs": "SGML",\r\n' +
' \t\t\t\t\t"GlossTerm": "Standard Generalized ' +
' \t\t\t\t\t"GlossTerm": "Standard Generalized ' +
'Markup Language",\r\n' +

@@ -776,4 +776,4 @@ ' \t\t\t\t\t"Acronym": "SGML",\r\n' +

, [SAX_VALUE_CLOSE , undefined]
]

@@ -785,26 +785,31 @@ }

var expectedEventNames = [ SAX_KEY
, SAX_VALUE_OPEN
, SAX_VALUE_CLOSE
var expectedEventNames = [ SAX_KEY
, SAX_VALUE_OPEN
, SAX_VALUE_CLOSE
, FAIL_EVENT
];
var times = false;
for (var key in docs) {
if(times) {
continue;
}
times = true;
var doc = docs[key];
describe('case ' + key, function(doc){
describe('case ' + key, function(){
var bus = pubSub(),
blackBoxRecording = eventBlackBox(bus, expectedEventNames);
clarinet(bus);
bus(STREAM_DATA).emit(doc.text);
bus(STREAM_END).emit();
it('should have the correct event types', function(){
expect( blackBoxRecording ).toMatchOrder( doc.events );
});
doc.events.forEach(function( expectedEvent, i ){

@@ -822,3 +827,3 @@

}.bind(null, doc));
});
}

@@ -828,32 +833,39 @@

this.addMatchers({
toMatchOrder: function(expected){
jasmine.addMatchers({
toMatchOrder: function(){
return {
compare: function(actual, expected) {
var result = {};
var actual = this.actual;
var actualEventOrder = actual.map( function(e){
return e.type;
});
var expectedEventOrder = expected.map( function(a){
return a[0];
});
var actualEventOrder = actual.map( function(e){
return e.type;
});
var expectedEventOrder = expected.map( function(a){
return a[0];
});
var lengthsMatch = actualEventOrder.length == expectedEventOrder.length;
var everyEventMatches = actualEventOrder.every(function( actualEvent, i ){
return actualEvent == expectedEventOrder[i];
});
this.message = function(){
return 'events not in correct order. We have:\n' +
JSON.stringify(
actualEventOrder.map(prettyPrintEvent)
) + '\nbut wanted:\n' +
JSON.stringify(
expectedEventOrder.map(prettyPrintEvent)
);
};
result.pass = lengthsMatch && everyEventMatches;
if(!result.pass) {
result.message = 'events not in correct order. We have:\n' +
JSON.stringify(
actualEventOrder.map(prettyPrintEvent)
) + '\nbut wanted:\n' +
JSON.stringify(
expectedEventOrder.map(prettyPrintEvent)
);
}
return actualEventOrder.length == expectedEventOrder.length &&
actualEventOrder.every(function( actualEvent, i ){
return actualEvent == expectedEventOrder[i];
});
return result;
}
};
}
})
});
});
});

@@ -16,3 +16,3 @@

// given in ajax, different from page
// not given in ajax url
// not given in ajax url
// host:

@@ -30,3 +30,3 @@ // given in ajax, the same as page

// given in page but not ajax url
describe('can parse URLs', function() {

@@ -38,100 +38,100 @@

port: ''
};
};
it( 'parses absolute path only', function() {
expect('/foo/bar').toParseTo(noInformationRegardingOrigin);
expect('/foo/barz').toParseTo(noInformationRegardingOrigin);
});
it( 'parses absolute path with extension', function() {
expect('/foo/bar.jpg').toParseTo(noInformationRegardingOrigin);
});
// it( 'parses absolute path with extension', function() {
// expect('/foo/bar.jpg').toParseTo(noInformationRegardingOrigin);
// });
it( 'parses absolute path with extension and query', function() {
expect('/foo/bar.jpg?foo=bar&woo=doo').toParseTo(noInformationRegardingOrigin);
});
// it( 'parses absolute path with extension and query', function() {
// expect('/foo/bar.jpg?foo=bar&woo=doo').toParseTo(noInformationRegardingOrigin);
// });
it( 'parses relative path only', function() {
expect('foo/bar').toParseTo(noInformationRegardingOrigin);
});
// it( 'parses relative path only', function() {
// expect('foo/bar').toParseTo(noInformationRegardingOrigin);
// });
it( 'parses relative path with extension', function() {
expect('foo/bar.jpg').toParseTo(noInformationRegardingOrigin);
});
// it( 'parses relative path with extension', function() {
// expect('foo/bar.jpg').toParseTo(noInformationRegardingOrigin);
// });
it( 'parses a url with domain but no protocol', function() {
expect('//example.com/foo/bar.jpg').toParseTo({
protocol:'',
host:'example.com',
port:''
});
});
// it( 'parses a url with domain but no protocol', function() {
it( 'parses a url with one-word domain', function() {
// expect('//example.com/foo/bar.jpg').toParseTo({
// protocol:'',
// host:'example.com',
// port:''
// });
// });
expect('//database/foo/bar.jpg').toParseTo({
protocol:'',
host:'database',
port:''
});
});
// it( 'parses a url with one-word domain', function() {
it( 'parses a url with one-word domain and port', function() {
// expect('//database/foo/bar.jpg').toParseTo({
// protocol:'',
// host:'database',
// port:''
// });
// });
expect('//search:9200/foo/bar').toParseTo({
protocol:'',
host:'search',
port:'9200'
});
});
// it( 'parses a url with one-word domain and port', function() {
// expect('//search:9200/foo/bar').toParseTo({
// protocol:'',
// host:'search',
// port:'9200'
// });
// });
it( 'parses a url with domain with a hyphen', function() {
expect('//example-site.org/foo/bar.jpg').toParseTo({
protocol:'',
host:'example-site.org',
port:''
});
});
it( 'parses a url with domain with a number', function() {
// it( 'parses a url with domain with a hyphen', function() {
expect('//123.org.uk/foo/bar.jpg').toParseTo({
protocol:'',
host:'123.org.uk',
port:''
});
});
// expect('//example-site.org/foo/bar.jpg').toParseTo({
// protocol:'',
// host:'example-site.org',
// port:''
// });
// });
it( 'parses a url with a protocol', function() {
// it( 'parses a url with domain with a number', function() {
expect('http://example.com/foo').toParseTo({
protocol:'http:',
host:'example.com',
port:''
});
});
// expect('//123.org.uk/foo/bar.jpg').toParseTo({
// protocol:'',
// host:'123.org.uk',
// port:''
// });
// });
it( 'parses a url with a protocol and a port', function() {
// it( 'parses a url with a protocol', function() {
expect('http://elasticsearch:9200/tweets').toParseTo({
protocol:'http:',
host:'elasticsearch',
port:'9200'
});
});
// expect('http://example.com/foo').toParseTo({
// protocol:'http:',
// host:'example.com',
// port:''
// });
// });
it( 'parses a url with a protocol and a port implicitly at the root', function() {
// it( 'parses a url with a protocol and a port', function() {
expect('http://elasticsearch:9200').toParseTo({
protocol:'http:',
host:'elasticsearch',
port:'9200'
});
});
// expect('http://elasticsearch:9200/tweets').toParseTo({
// protocol:'http:',
// host:'elasticsearch',
// port:'9200'
// });
// });
// it( 'parses a url with a protocol and a port implicitly at the root', function() {
// expect('http://elasticsearch:9200').toParseTo({
// protocol:'http:',
// host:'elasticsearch',
// port:'9200'
// });
// });
});
var testCases = {

@@ -152,6 +152,6 @@ 'http://www.current-site.co.uk':{

}
, 'http://www.current-site.co.uk:8080/some/page.html': {
'/foo/bar': false,
'foo/bar': false,
'foo/bar': false,
'http://www.current-site.co.uk/index.html': true,

@@ -175,3 +175,3 @@ '//www.current-site.co.uk:8080/index.html': false,

'//www.current-site.co.uk/foo': false,
'//www.current-site.co.uk:80/foo': false
'//www.current-site.co.uk:80/foo': false
}

@@ -183,3 +183,3 @@

'//www.current-site.co.uk:80/foo': false,
'//www.current-site.co.uk/foo': false
'//www.current-site.co.uk/foo': false
}

@@ -191,3 +191,3 @@

'//www.current-site.co.uk/foo': false,
'//www.current-site.co.uk:443/foo': false
'//www.current-site.co.uk:443/foo': false
}

@@ -200,9 +200,9 @@

'//www.current-site.co.uk/foo': false
}
}
};
describe('detection of x-origin-ness', function() {
for( var currentPage in testCases ) {
describe('testing from page ' + currentPage, function() {

@@ -217,5 +217,5 @@

it('should detect ' + ajaxUrl + ' as ' + crossOriginDesc,
it('should detect ' + ajaxUrl + ' as ' + crossOriginDesc,
function (currentPage, ajaxUrl, expectToBeCrossOrigin) {
if( expectToBeCrossOrigin ) {

@@ -226,3 +226,3 @@ expect(ajaxUrl).toBeCrossOriginOnPage(currentPage);

}
}.bind(this, currentPage, ajaxUrl, expectToBeCrossOrigin)

@@ -237,29 +237,39 @@ );

beforeEach(function() {
this.addMatchers({
toParseTo: function(expected) {
jasmine.addMatchers({
toParseTo: function() {
return {
compare: function(actual, expected) {
var result = {};
var actualUrlParsed = parseUrlOrigin(actual);
result.pass = (actualUrlParsed.protocol == expected.protocol) &&
(actualUrlParsed.host == expected.host) &&
(actualUrlParsed.port == expected.port);
var actualUrl = this.actual;
var actualUrlParsed = parseUrlOrigin(actualUrl);
if (!result.pass) {
result.message = 'expected ' + actual
+ ' to parse to ' + JSON.stringify(expected)
+ ' but got ' + JSON.stringify(actualUrlParsed);
}
this.message = function(){
return 'expected ' + actualUrl
+ ' to parse to ' + JSON.stringify(expected)
+ ' but got ' + JSON.stringify(actualUrlParsed);
};
return result;
}
};
},
return (actualUrlParsed.protocol == expected.protocol) &&
(actualUrlParsed.host == expected.host) &&
(actualUrlParsed.port == expected.port);
}
toBeCrossOriginOnPage: function() {
return {
compare: function(actual, expected) {
var ajaxUrl = actual;
var ajaxHost = parseUrlOrigin(ajaxUrl);
var curPageHost = parseUrlOrigin(expected);
, toBeCrossOriginOnPage: function(curPageUrl) {
var ajaxUrl = this.actual,
ajaxHost = parseUrlOrigin(ajaxUrl),
curPageHost = parseUrlOrigin(curPageUrl);
return {
pass: isCrossOrigin(curPageHost, ajaxHost)
};
return isCrossOrigin(curPageHost, ajaxHost);
}
}
};
}
});
});
});
});
describe("incremental content builder", function(){
function IncrementalContentBuilderAsserter(){
var eventBus = pubSub();
sinon.spy(eventBus(NODE_CLOSED), 'emit');
sinon.spy(eventBus(NODE_CLOSED), 'on');
sinon.spy(eventBus(NODE_OPENED), 'emit');
sinon.spy(eventBus(NODE_OPENED), 'on');
sinon.spy(eventBus(ROOT_PATH_FOUND), 'emit');
sinon.spy(eventBus(ROOT_PATH_FOUND), 'on');
this._eventBus = eventBus;
var builderInstance = incrementalContentBuilder(eventBus);
ascentManager( this._eventBus, builderInstance);
}
IncrementalContentBuilderAsserter.prototype.receivingEvent = function(eventName /* args */){
var args = Array.prototype.slice.call(arguments, 1);
this._eventBus(eventName).emit.apply(undefined, args);
return this;
};
describe('when root object opens', function() {
var builder = aContentBuilder().receivingEvent(SAX_VALUE_OPEN, {});
it('emits correct event', function(){
expect( builder)
.toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{}}
)
)
});
it('reports correct root', function () {
function IncrementalContentBuilderAsserter(){
expect(builder).toHaveEmittedRootWhichIsNow({})
var eventBus = pubSub();
});
})
describe('after key is found in root object', function(){
// above test, plus some extra events from clarinet
var builder = aContentBuilder()
sinon.spy(eventBus(NODE_CLOSED), 'emit');
sinon.spy(eventBus(NODE_CLOSED), 'on');
sinon.spy(eventBus(NODE_OPENED), 'emit');
sinon.spy(eventBus(NODE_OPENED), 'on');
sinon.spy(eventBus(ROOT_PATH_FOUND), 'emit');
sinon.spy(eventBus(ROOT_PATH_FOUND), 'on');
this._eventBus = eventBus;
var builderInstance = incrementalContentBuilder(eventBus);
ascentManager( this._eventBus, builderInstance);
}
IncrementalContentBuilderAsserter.prototype.receivingEvent = function(eventName /* args */){
var args = Array.prototype.slice.call(arguments, 1);
this._eventBus(eventName).emit.apply(undefined, args);
return this;
};
describe('when root object opens', function() {
var builder = aContentBuilder().receivingEvent(SAX_VALUE_OPEN, {});
it('emits correct event', function(){
expect( builder)
.toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{}}
)
)
});
it('reports correct root', function () {
expect(builder).toHaveEmittedRootWhichIsNow({})
});
})
describe('after key is found in root object', function(){
// above test, plus some extra events from clarinet
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, {})
.receivingEvent(SAX_KEY, 'flavour');
it('emits correct event', function(){
expect( builder )
.toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{flavour:undefined}}
, {key:'flavour', node:undefined}
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({flavour:undefined});
});
})
describe('if key is found at same time as root object', function() {
// above test, plus some extra events from clarinet
it('emits correct event', function(){
var builder = aContentBuilder()
expect( builder )
.toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{flavour:undefined}}
, {key:'flavour', node:undefined}
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({flavour:undefined});
});
})
describe('if key is found at same time as root object', function() {
// above test, plus some extra events from clarinet
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, {}, undefined)
.receivingEvent(SAX_KEY, 'flavour');
it('emits correct event', function(){
expect(builder).toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{flavour:undefined}}
, {key:'flavour', node:undefined}
)
)
});
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({flavour:undefined});
});
})
describe('after value is found for that key', function() {
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, {})
.receivingEvent(SAX_KEY , 'flavour')
.receivingEvent(SAX_VALUE_OPEN , 'strawberry')
.receivingEvent(SAX_VALUE_CLOSE);
it('emits correct event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:{flavour:'strawberry'}}
, {key:'flavour', node:'strawberry'}
)
)
});
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({flavour:'strawberry'});
});
})
describe('emits node found after root object closes', function() {
it('emits correct event', function(){
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, {})
.receivingEvent(SAX_KEY, 'flavour')
.receivingEvent(SAX_VALUE_OPEN, 'strawberry').receivingEvent(SAX_VALUE_CLOSE)
.receivingEvent(SAX_VALUE_CLOSE);
it('emits correct event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:{flavour:'strawberry'}}
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({flavour:'strawberry'});
});
})
describe('first array element', function() {
expect(builder).toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{flavour:undefined}}
, {key:'flavour', node:undefined}
)
)
});
var builder = aContentBuilder()
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({flavour:undefined});
});
})
describe('after value is found for that key', function() {
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, {})
.receivingEvent(SAX_KEY , 'flavour')
.receivingEvent(SAX_VALUE_OPEN , 'strawberry')
.receivingEvent(SAX_VALUE_CLOSE);
it('emits correct event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:{flavour:'strawberry'}}
, {key:'flavour', node:'strawberry'}
)
)
});
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({flavour:'strawberry'});
});
})
describe('emits node found after root object closes', function() {
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, {})
.receivingEvent(SAX_KEY, 'flavour')
.receivingEvent(SAX_VALUE_OPEN, 'strawberry').receivingEvent(SAX_VALUE_CLOSE)
.receivingEvent(SAX_VALUE_CLOSE);
it('emits correct event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:{flavour:'strawberry'}}
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({flavour:'strawberry'});
});
})
describe('first array element', function() {
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, {})
.receivingEvent(SAX_KEY, 'alphabet')
.receivingEvent(SAX_VALUE_OPEN, [])
.receivingEvent(SAX_VALUE_OPEN, 'a').receivingEvent(SAX_VALUE_CLOSE);
it('emits path event with numeric paths', function(){
expect(builder).toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{'alphabet':['a']} }
, {key:'alphabet', node:['a'] }
, {key:0, node:'a' }
)
);
})
it('emitted node event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:{'alphabet':['a']} }
, {key:'alphabet', node:['a'] }
, {key:0, node:'a' }
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({'alphabet':['a']});
});
})
describe('second array element', function() {
it('emits path event with numeric paths', function(){
var builder = aContentBuilder()
expect(builder).toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{'alphabet':['a']} }
, {key:'alphabet', node:['a'] }
, {key:0, node:'a' }
)
);
})
it('emitted node event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:{'alphabet':['a']} }
, {key:'alphabet', node:['a'] }
, {key:0, node:'a' }
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({'alphabet':['a']});
});
})
describe('second array element', function() {
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, {})

@@ -197,165 +197,172 @@ .receivingEvent(SAX_KEY, 'alphabet')

.receivingEvent(SAX_VALUE_OPEN, 'b').receivingEvent(SAX_VALUE_CLOSE);
it('emits events with numeric paths', function(){
expect(builder).toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{'alphabet':['a','b']} }
, {key:'alphabet', node:['a','b'] }
, {key:1, node:'b' }
)
)
})
it('emitted node event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:{'alphabet':['a', 'b']} }
, {key:'alphabet', node:['a','b'] }
, {key:1, node:'b' }
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({'alphabet':['a','b']});
});
})
describe('array at root', function() {
it('emits events with numeric paths', function(){
var builder = aContentBuilder()
expect(builder).toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:{'alphabet':['a','b']} }
, {key:'alphabet', node:['a','b'] }
, {key:1, node:'b' }
)
)
})
it('emitted node event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:{'alphabet':['a', 'b']} }
, {key:'alphabet', node:['a','b'] }
, {key:1, node:'b' }
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow({'alphabet':['a','b']});
});
})
describe('array at root', function() {
var builder = aContentBuilder()
.receivingEvent(SAX_VALUE_OPEN, [])
.receivingEvent(SAX_VALUE_OPEN, 'a').receivingEvent(SAX_VALUE_CLOSE)
.receivingEvent(SAX_VALUE_OPEN, 'b').receivingEvent(SAX_VALUE_CLOSE);
it('emits events with numeric paths', function(){
expect(builder).toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:['a','b'] }
, {key:1, node:'b' }
)
)
})
it('emitted node event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:['a','b'] }
, {key:1, node:'b' }
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow(['a','b']);
});
})
function aContentBuilder() {
return new IncrementalContentBuilderAsserter();
}
beforeEach(function(){
this.addMatchers({
toHaveEmittedRootWhichIsNow: function( expectedRootObj ) {
var asserter = this.actual;
it('emits events with numeric paths', function(){
expect(builder).toHaveEmitted(
NODE_OPENED
, anAscentContaining(
{key:ROOT_PATH, node:['a','b'] }
, {key:1, node:'b' }
)
)
})
it('emitted node event', function(){
expect(builder).toHaveEmitted(
NODE_CLOSED
, anAscentContaining(
{key:ROOT_PATH, node:['a','b'] }
, {key:1, node:'b' }
)
)
})
it('reports correct root', function(){
expect(builder).toHaveEmittedRootWhichIsNow(['a','b']);
});
})
function aContentBuilder() {
return new IncrementalContentBuilderAsserter();
}
beforeEach(function(){
jasmine.addMatchers({
toHaveEmittedRootWhichIsNow: function() {
return {
compare: function(actual, expectedRootObj) {
var asserter = actual;
var emit = asserter._eventBus(ROOT_PATH_FOUND).emit;
return emit.calledWith(expectedRootObj);
},
toHaveEmitted: function( eventName, expectedAscent ){
var asserter = this.actual;
return {
pass: emit.calledWith(expectedRootObj)
}
}
}
},
toHaveEmitted: function() {
return {
compare: function(actual, eventName, expectedAscent){
var asserter = actual;
var emit = asserter._eventBus(eventName).emit;
var ascentMatch = sinon.match(function ( foundAscent ) {
function matches( expect, found ) {
if( !expect && !found ) {
return true;
}
if( !expect || !found ) {
// Both not empty, but one is. Unequal length ascents.
return false;
}
if( head(expect).key != head(found).key ) {
// keys unequal
return false;
}
if( JSON.stringify( head(expect).node ) != JSON.stringify( head(found).node ) ) {
// nodes unequal
return false;
}
return matches(tail(expect), tail(found));
}
return matches(expectedAscent, foundAscent);
function matches( expect, found ) {
if( !expect && !found ) {
return true;
}
if( !expect || !found ) {
// Both not empty, but one is. Unequal length ascents.
return false;
}
if( head(expect).key != head(found).key ) {
// keys unequal
return false;
}
if( JSON.stringify( head(expect).node ) != JSON.stringify( head(found).node ) ) {
// nodes unequal
return false;
}
return matches(tail(expect), tail(found));
}
return matches(expectedAscent, foundAscent);
}, 'ascent match');
this.message = function(){
if( !emit.called ) {
return 'no events have been emitted at all';
}
if( !emit.called ) {
return 'no events have been emitted at all';
}
function reportCall(eventName, ascentList) {
var argArray = listAsArray(ascentList);
var toJson = JSON.stringify.bind(JSON);
return 'type:' + eventName + ', ascent:[' + argArray.map(toJson).join(', \t') + ']';
}
function reportArgs(args){
return reportCall(args[0], args[1]);
}
return 'expected a call with : \t' + reportCall(eventName, expectedAscent) +
'\n' +
'latest call had : \t' + reportArgs(emit.lastCall.args) +
'\n' +
'all calls were :' +
'\n \t' +
emit.args.map( reportArgs ).join('\n \t')
function reportCall(eventName, ascentList) {
var argArray = listAsArray(ascentList);
var toJson = JSON.stringify.bind(JSON);
return 'type:' + eventName + ', ascent:[' + argArray.map(toJson).join(', \t') + ']';
}
function reportArgs(args){
return reportCall(args[0], args[1]);
}
return 'expected a call with : \t' + reportCall(eventName, expectedAscent) +
'\n' +
'latest call had : \t' + reportArgs(emit.lastCall.args) +
'\n' +
'all calls were :' +
'\n \t' +
emit.args.map( reportArgs ).join('\n \t')
};
return emit.calledWithMatch( ascentMatch );
}
});
});
function anAscentContaining ( /* descriptors */ ) {
var ascentArray = Array.prototype.slice.call(arguments),
ascentList = emptyList;
ascentArray.forEach( function(ascentNode){
ascentList = cons(ascentNode, ascentList);
});
return ascentList;
}
return {pass: emit.calledWithMatch( ascentMatch )};
}
}
}
});
});
function anAscentContaining ( /* descriptors */ ) {
var ascentArray = Array.prototype.slice.call(arguments),
ascentList = emptyList;
ascentArray.forEach( function(ascentNode){
ascentList = cons(ascentNode, ascentList);
});
return ascentList;
}
});
describe('instance api and pattern adaptor composed',function(){
"use strict";
var bus, api, matches;
beforeEach(function(){
bus = spiedPubSub();
matches = {};
function jsonPathCompiler(pattern){
"use strict";
function compiled ( ascent ){
if( matches[pattern] === ascent ) {
return head(ascent);
} else {
return false;
}
}
return compiled;
var bus, api, matches;
beforeEach(function(){
bus = spiedPubSub();
matches = {};
function jsonPathCompiler(pattern){
function compiled ( ascent ){
if( matches[pattern] === ascent ) {
return head(ascent);
} else {
return false;
}
}
api = instanceApi(bus);
// For now, tie the patternAdapter into the bus. These tests are
// for the composition between patternAdaptor and instanceApi
patternAdapter(bus, jsonPathCompiler);
});
function anAscentMatching(pattern) {
var ascent = list(namedNode('node', {}));
return compiled;
}
matches[pattern] = ascent;
api = instanceApi(bus);
return ascent;
}
it('has chainable methods that don\'t explode', function() {
// test that nothing forgot to return 'this':
// For now, tie the patternAdapter into the bus. These tests are
// for the composition between patternAdaptor and instanceApi
patternAdapter(bus, jsonPathCompiler);
});
expect(function(){
function fn(){}
api
.path('*', fn)
.node('*', fn)
.fail(fn).path('*', fn)
.path({'*':fn})
.node({'*': fn})
.done(fn)
.path({'*':fn})
.start(fn)
.on('path','*', fn)
.on('node','*', fn)
.fail(fn)
.on('path','*', fn)
.on('path',{'*':fn})
.on('node',{'*': fn})
.on('path',{'*':fn})
.on('done',fn)
.on('start',fn);
}).not.toThrow();
})
function anAscentMatching(pattern) {
var ascent = list(namedNode('node', {}));
describe('header method', function(){
matches[pattern] = ascent;
it('returns undefined if not available', function() {
expect( api.header() ).toBeUndefined();
});
it('can provide object once available', function() {
var headers = {"x-remainingRequests": 100};
bus(HTTP_START).emit( 200, headers );
expect( api.header() ).toEqual(headers);
});
it('can provide single header once available', function() {
var headers = {"x-remainingRequests": 100};
bus(HTTP_START).emit( 200, headers );
expect( api.header('x-remainingRequests') ).toEqual(100);
});
it('gives undefined for non-existent single header', function() {
var headers = {"x-remainingRequests": 100};
bus(HTTP_START).emit( 200, headers );
expect( api.header('x-remainingBathtubs') ).toBeUndefined();
});
});
describe('root method', function(){
it('returns undefined if not available', function() {
expect( api.root() ).toBeUndefined();
});
it('can provide object once available', function() {
var root = {I:'am', the:'root'};
bus(ROOT_PATH_FOUND).emit( root);
expect( api.root() ).toEqual(root);
});
});
describe('node and path callbacks', function(){
it('calls node callback on matching node', function() {
var callback = jasmine.createSpy('node callback'),
ascent = anAscentMatching('a_pattern');
api.on('node', 'a_pattern', callback);
expect(callback).not.toHaveBeenCalled()
bus(NODE_CLOSED).emit( ascent)
return ascent;
}
expect(callback).toHaveBeenCalled()
});
it('calls path callback on matching path', function() {
var callback = jasmine.createSpy(),
ascent = anAscentMatching('a_pattern');
api.on('path', 'a_pattern', callback);
expect(callback).not.toHaveBeenCalled()
bus(NODE_OPENED).emit( ascent)
expect(callback).toHaveBeenCalled()
});
it('does not call node callback on non-matching node', function() {
var callback = jasmine.createSpy(),
ascent = anAscentMatching('a_pattern');
api.on('node', 'a_different_pattern', callback);
bus(NODE_CLOSED).emit( ascent)
expect(callback).not.toHaveBeenCalled()
});
it('calls node callback again on second match', function() {
var callback = jasmine.createSpy(),
ascent = anAscentMatching('a_pattern');
api.on('node', 'a_pattern', callback);
bus(NODE_CLOSED).emit( ascent)
expect(callback.call.length).toBe(1)
bus(NODE_CLOSED).emit( ascent)
expect(callback.calls.length).toBe(2)
});
});
xit('has chainable methods that don\'t explode', function() {
// test that nothing forgot to return 'this':
expect(function(){
function fn(){}
api
.path('*', fn)
.node('*', fn)
.fail(fn)
// fails at this point, fail return undefined
.path('*', fn)
.path({'*':fn})
.node({'*': fn})
.done(fn)
.path({'*':fn})
.start(fn)
.on('path','*', fn)
.on('node','*', fn)
.fail(fn)
.on('path','*', fn)
.on('path',{'*':fn})
.on('node',{'*': fn})
.on('path',{'*':fn})
.on('done',fn)
.on('start',fn);
}).not.toThrow();
});
describe('header method', function(){
it('returns undefined if not available', function() {
expect( api.header() ).toBeUndefined();
});
it('can provide object once available', function() {
var headers = {"x-remainingRequests": 100};
bus(HTTP_START).emit( 200, headers );
expect( api.header() ).toEqual(headers);
});
it('can provide single header once available', function() {
var headers = {"x-remainingRequests": 100};
bus(HTTP_START).emit( 200, headers );
expect( api.header('x-remainingRequests') ).toEqual(100);
});
it('gives undefined for non-existent single header', function() {
var headers = {"x-remainingRequests": 100};
bus(HTTP_START).emit( 200, headers );
expect( api.header('x-remainingBathtubs') ).toBeUndefined();
});
});
describe('root method', function(){
it('returns undefined if not available', function() {
expect( api.root() ).toBeUndefined();
});
it('can provide object once available', function() {
var root = {I:'am', the:'root'};
bus(ROOT_PATH_FOUND).emit( root);
expect( api.root() ).toEqual(root);
});
});
describe('node and path callbacks', function(){
it('calls node callback on matching node', function() {
var callback = jasmine.createSpy('node callback'),
ascent = anAscentMatching('a_pattern');
api.on('node', 'a_pattern', callback);
expect(callback).not.toHaveBeenCalled()
bus(NODE_CLOSED).emit( ascent)
expect(callback).toHaveBeenCalled()
});
it('calls path callback on matching path', function() {
var callback = jasmine.createSpy(),
ascent = anAscentMatching('a_pattern');
api.on('path', 'a_pattern', callback);
expect(callback).not.toHaveBeenCalled()
bus(NODE_OPENED).emit( ascent)
expect(callback).toHaveBeenCalled()
});
it('does not call node callback on non-matching node', function() {
var callback = jasmine.createSpy(),
ascent = anAscentMatching('a_pattern');
api.on('node', 'a_different_pattern', callback);
bus(NODE_CLOSED).emit( ascent)
expect(callback).not.toHaveBeenCalled()
});
it('calls node callback again on second match', function() {
var callback = jasmine.createSpy(),
ascent = anAscentMatching('a_pattern');
api.on('node', 'a_pattern', callback);
bus(NODE_CLOSED).emit( ascent)
expect(callback.calls.count()).toBe(1)
bus(NODE_CLOSED).emit( ascent)
expect(callback.calls.count()).toBe(2)
});
});
});
describe('instance api',function() {
"use strict";
"use strict";
var oboeBus, oboeInstance,
sampleUrl = 'http://example.com';
var oboeBus, oboeInstance,
sampleUrl = 'http://example.com';
beforeEach(function () {
oboeBus = spiedPubSub();
beforeEach(function () {
oboeBus = spiedPubSub();
spyOn(oboeBus, 'emit' );
spyOn(oboeBus, 'on' );
spyOn(oboeBus, 'un' );
oboeInstance = instanceApi(oboeBus, sampleUrl);
});
oboeInstance = instanceApi(oboeBus, sampleUrl);
});
function anAscent() {
return list(namedNode(ROOT_PATH, {}));
}
function anAscent() {
return list(namedNode(ROOT_PATH, {}));
}
it('puts the url on the oboe instance', function(){
expect( oboeInstance.source).toBe( sampleUrl );
});
it('puts the url on the oboe instance', function(){
expect( oboeInstance.source).toBe( sampleUrl );
});
describe('header method', function(){
describe('header method', function(){
it('returns undefined if not available', function() {
it('returns undefined if not available', function() {
expect( oboeInstance.header() ).toBeUndefined();
});
expect( oboeInstance.header() ).toBeUndefined();
});
it('can provide object once available', function() {
it('can provide object once available', function() {
var headers = {"x-remainingRequests": 100};
var headers = {"x-remainingRequests": 100};
oboeBus(HTTP_START).emit( 200, headers );
oboeBus(HTTP_START).emit( 200, headers );
expect( oboeInstance.header() ).toEqual(headers);
});
expect( oboeInstance.header() ).toEqual(headers);
});
it('can provide single header once available', function() {
var headers = {"x-remainingRequests": 100};
it('can provide single header once available', function() {
var headers = {"x-remainingRequests": 100};
oboeBus(HTTP_START).emit( 200, headers );
oboeBus(HTTP_START).emit( 200, headers );
expect( oboeInstance.header('x-remainingRequests') ).toEqual(100);
});
expect( oboeInstance.header('x-remainingRequests') ).toEqual(100);
});
it('gives undefined for non-existent single header', function() {
var headers = {"x-remainingRequests": 100};
it('gives undefined for non-existent single header', function() {
var headers = {"x-remainingRequests": 100};
oboeBus(HTTP_START).emit( 200, headers );
oboeBus(HTTP_START).emit( 200, headers );
expect( oboeInstance.header('x-remainingBathtubs') ).toBeUndefined();
});
});
expect( oboeInstance.header('x-remainingBathtubs') ).toBeUndefined();
});
});
describe('root method', function(){
describe('root method', function(){
it('returns undefined if not available', function() {
it('returns undefined if not available', function() {
expect( oboeInstance.root() ).toBeUndefined();
});
expect( oboeInstance.root() ).toBeUndefined();
});
it('can provide object once available', function() {
it('can provide object once available', function() {
var root = {I:'am', the:'root'};
var root = {I:'am', the:'root'};
oboeBus(ROOT_PATH_FOUND).emit( root);
oboeBus(ROOT_PATH_FOUND).emit( root);
expect( oboeInstance.root() ).toEqual(root);
});
});
expect( oboeInstance.root() ).toEqual(root);
});
});
describe('node and path callbacks', function(){
describe('node and path callbacks', function(){
it('calls node callback when notified of matching node', function() {
it('calls node callback when notified of matching node', function() {
var callback = jasmine.createSpy('node callback'),
node = {},
path = [],
ancestors = [];
var callback = jasmine.createSpy('node callback'),
node = {},
path = [],
ancestors = [];
oboeInstance.on('node', 'a_pattern', callback);
oboeInstance.on('node', 'a_pattern', callback);
expect(callback).not.toHaveBeenCalled()
expect(callback).not.toHaveBeenCalled()
oboeBus('node:a_pattern').emit( node, path, ancestors );
oboeBus('node:a_pattern').emit( node, path, ancestors );
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
it('calls path callback when notified of matching path', function() {
it('calls path callback when notified of matching path', function() {
var callback = jasmine.createSpy('path callback'),
node = {},
path = [],
ancestors = [];
var callback = jasmine.createSpy('path callback'),
node = {},
path = [],
ancestors = [];
oboeInstance.on('path', 'a_pattern', callback);
oboeInstance.on('path', 'a_pattern', callback);
expect(callback).not.toHaveBeenCalled()
expect(callback).not.toHaveBeenCalled()
oboeBus('path:a_pattern').emit( node, path, ancestors );
oboeBus('path:a_pattern').emit( node, path, ancestors );
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
it('allows short-cut node matching', function() {
var pattern1Callback = jasmine.createSpy('pattern 1 callback'),
pattern2Callback = jasmine.createSpy('pattern 2 callback');
oboeInstance.on('node', {
pattern1: pattern1Callback,
pattern2: pattern2Callback
});
it('allows short-cut node matching', function() {
expect(pattern1Callback).not.toHaveBeenCalled()
expect(pattern2Callback).not.toHaveBeenCalled()
var pattern1Callback = jasmine.createSpy('pattern 1 callback'),
pattern2Callback = jasmine.createSpy('pattern 2 callback');
oboeBus('node:pattern1').emit( {}, anAscent())
oboeInstance.on('node', {
pattern1: pattern1Callback,
pattern2: pattern2Callback
});
expect(pattern1Callback).toHaveBeenCalled()
expect(pattern2Callback).not.toHaveBeenCalled()
expect(pattern1Callback).not.toHaveBeenCalled()
expect(pattern2Callback).not.toHaveBeenCalled()
oboeBus('node:pattern2').emit( {}, anAscent())
oboeBus('node:pattern1').emit( {}, anAscent())
expect(pattern2Callback).toHaveBeenCalled()
});
expect(pattern1Callback).toHaveBeenCalled()
expect(pattern2Callback).not.toHaveBeenCalled()
it('calls node callback added using 2-arg mode when notified of match to pattern', function() {
oboeBus('node:pattern2').emit( {}, anAscent())
var callback = jasmine.createSpy('node callback'),
node = {},
path = [],
ancestors = [];
expect(pattern2Callback).toHaveBeenCalled()
});
oboeInstance.on('node:a_pattern', callback)
it('calls node callback added using 2-arg mode when notified of match to pattern', function() {
expect(callback).not.toHaveBeenCalled()
var callback = jasmine.createSpy('node callback'),
node = {},
path = [],
ancestors = [];
oboeBus('node:a_pattern').emit( node, path, ancestors );
oboeInstance.on('node:a_pattern', callback)
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
expect(callback).not.toHaveBeenCalled()
it('allows adding using addListener method', function() {
oboeBus('node:a_pattern').emit( node, path, ancestors );
var callback = jasmine.createSpy('node callback'),
node = {},
path = [],
ancestors = [];
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
oboeInstance.addListener('node:a_pattern', callback)
it('allows adding using addListener method', function() {
expect(callback).not.toHaveBeenCalled()
var callback = jasmine.createSpy('node callback'),
node = {},
path = [],
ancestors = [];
oboeBus('node:a_pattern').emit( node, path, ancestors );
oboeInstance.addListener('node:a_pattern', callback)
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
expect(callback).not.toHaveBeenCalled()
it('calls path callback added using 2-arg mode when notified of match to pattern', function() {
oboeBus('node:a_pattern').emit( node, path, ancestors );
var callback = jasmine.createSpy('path callback'),
node = {},
path = [],
ancestors = [];
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
oboeInstance.on('path:a_pattern', callback);
it('calls path callback added using 2-arg mode when notified of match to pattern', function() {
expect(callback).not.toHaveBeenCalled()
var callback = jasmine.createSpy('path callback'),
node = {},
path = [],
ancestors = [];
oboeBus('path:a_pattern').emit( node, path, ancestors );
oboeInstance.on('path:a_pattern', callback);
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
expect(callback).not.toHaveBeenCalled()
it('doesn\'t call node callback on path found', function() {
oboeBus('path:a_pattern').emit( node, path, ancestors );
var callback = jasmine.createSpy('node callback');
expect(callback).toHaveBeenCalledWith( node, path, ancestors );
});
oboeInstance.on('node', 'a_pattern', callback);
it('doesn\'t call node callback on path found', function() {
expect(callback).not.toHaveBeenCalled()
var callback = jasmine.createSpy('node callback');
oboeBus('path:a_pattern').emit( {}, list(namedNode(ROOT_PATH, {}) ) );
oboeInstance.on('node', 'a_pattern', callback);
expect(callback).not.toHaveBeenCalled();
});
expect(callback).not.toHaveBeenCalled()
it('doesn\'t call again after forget called from inside callback', function() {
oboeBus('path:a_pattern').emit( {}, list(namedNode(ROOT_PATH, {}) ) );
var nodeCallback = jasmine.createSpy('node callback').and.callFake(function(){
this.forget();
}),
ascent = list(namedNode('node', {}));
expect(callback).not.toHaveBeenCalled();
});
oboeInstance.on('node', 'a_pattern', nodeCallback);
it('doesn\'t call again after forget called from inside callback', function() {
oboeBus('node:a_pattern').emit( {}, ascent);
var nodeCallback = jasmine.createSpy('node callback').andCallFake(function(){
this.forget();
}),
ascent = list(namedNode('node', {}));
expect(nodeCallback.calls.count()).toBe(1)
oboeInstance.on('node', 'a_pattern', nodeCallback);
oboeBus('node:a_pattern').emit( {}, ascent);
oboeBus('node:a_pattern').emit( {}, ascent);
expect(nodeCallback.calls.count()).toBe(1)
});
expect(nodeCallback.call.length).toBe(1)
xit('doesn\'t call node callback after callback is removed', function() {
oboeBus('node:a_pattern').emit( {}, ascent);
var nodeCallback = jasmine.createSpy('node callback'),
ascent = list(namedNode('node', {}));
expect(nodeCallback.calls.length).toBe(1)
});
oboeInstance.on('node', 'a_pattern', nodeCallback);
oboeInstance.removeListener('node', 'a_pattern', nodeCallback);
it('doesn\'t call node callback after callback is removed', function() {
oboeBus('node:a_pattern').emit( {}, ascent);
var nodeCallback = jasmine.createSpy('node callback'),
ascent = list(namedNode('node', {}));
expect(nodeCallback).not.toHaveBeenCalled()
});
oboeInstance.on('node', 'a_pattern', nodeCallback);
oboeInstance.removeListener('node', 'a_pattern', nodeCallback);
it('doesn\'t call node callback after callback is removed using 2-arg form', function() {
oboeBus('node:a_pattern').emit( {}, ascent);
var nodeCallback = jasmine.createSpy('node callback'),
ascent = list(namedNode('node', {}));
expect(nodeCallback).not.toHaveBeenCalled()
});
// oboeInstance.on('node', 'a_pattern', nodeCallback);
oboeInstance.on('node', 'a_pattern', nodeCallback);
oboeInstance.removeListener('node:a_pattern', nodeCallback);
it('doesn\'t call node callback after callback is removed using 2-arg form', function() {
oboeBus('node:a_pattern').emit( {}, ascent);
var nodeCallback = jasmine.createSpy('node callback'),
ascent = list(namedNode('node', {}));
expect(nodeCallback).not.toHaveBeenCalled()
});
oboeInstance.on('node', 'a_pattern', nodeCallback);
oboeInstance.removeListener('node:a_pattern', nodeCallback);
xit('doesn\'t call path callback after callback is removed', function() {
oboeBus('node:a_pattern').emit( {}, ascent);
var pathCallback = jasmine.createSpy('path callback'),
ascent = list(namedNode('path', {}));
expect(nodeCallback).not.toHaveBeenCalled()
});
oboeInstance.on('path', 'a_pattern', pathCallback);
oboeInstance.removeListener('path', 'a_pattern', pathCallback);
it('doesn\'t call path callback after callback is removed', function() {
oboeBus('path:a_pattern').emit( {}, ascent);
var pathCallback = jasmine.createSpy('path callback'),
ascent = list(namedNode('path', {}));
expect(pathCallback).not.toHaveBeenCalled()
});
oboeInstance.on('path', 'a_pattern', pathCallback);
oboeInstance.removeListener('path', 'a_pattern', pathCallback);
it('doesn\'t call path callback after callback is removed using 2-arg form', function() {
oboeBus('path:a_pattern').emit( {}, ascent);
var pathCallback = jasmine.createSpy('path callback'),
ascent = list(namedNode('path', {}));
expect(pathCallback).not.toHaveBeenCalled()
});
oboeInstance.on('path', 'a_pattern', pathCallback);
oboeInstance.removeListener('path:a_pattern', pathCallback);
it('doesn\'t remove callback if wrong pattern is removed', function() {
oboeBus('path:a_pattern').emit( {}, ascent);
var nodeCallback = jasmine.createSpy('node callback'),
ascent = list(namedNode('node', {}));
expect(pathCallback).not.toHaveBeenCalled()
});
oboeInstance.on('node', 'a_pattern', nodeCallback);
it('doesn\'t remove callback if wrong pattern is removed', function() {
oboeInstance.removeListener('node', 'wrong_pattern', nodeCallback);
var nodeCallback = jasmine.createSpy('node callback'),
ascent = list(namedNode('node', {}));
oboeBus('node:a_pattern').emit( {}, ascent);
oboeInstance.on('node', 'a_pattern', nodeCallback);
expect(nodeCallback).toHaveBeenCalled()
});
oboeInstance.removeListener('node', 'wrong_pattern', nodeCallback);
it('doesn\'t remove callback if wrong callback is removed', function() {
oboeBus('node:a_pattern').emit( {}, ascent);
var correctCallback = jasmine.createSpy('correct callback'),
wrongCallback = jasmine.createSpy('wrong callback'),
ascent = list(namedNode('node', {}));
expect(nodeCallback).toHaveBeenCalled()
});
oboeInstance.on('node', 'a_pattern', correctCallback);
it('doesn\'t remove callback if wrong callback is removed', function() {
oboeInstance.removeListener('node', 'a_pattern', wrongCallback);
var correctCallback = jasmine.createSpy('correct callback'),
wrongCallback = jasmine.createSpy('wrong callback'),
ascent = list(namedNode('node', {}));
oboeBus('node:a_pattern').emit( {}, ascent);
oboeInstance.on('node', 'a_pattern', correctCallback);
expect(correctCallback).toHaveBeenCalled()
});
oboeInstance.removeListener('node', 'a_pattern', wrongCallback);
it('allows node listeners to be removed in a different style than they were added', function() {
oboeBus('node:a_pattern').emit( {}, ascent);
var
callback1 = jasmine.createSpy('callback 1'),
callback2 = jasmine.createSpy('callback 2'),
callback3 = jasmine.createSpy('callback 3'),
ascent = list(namedNode('node', {}));
expect(correctCallback).toHaveBeenCalled()
});
oboeInstance.node('pattern1', callback1);
oboeInstance.on('node', 'pattern2', callback2);
oboeInstance.on('node', {pattern3: callback3});
it('allows node listeners to be removed in a different style than they were added', function() {
oboeInstance.removeListener('node:pattern1', callback1);
oboeInstance.removeListener('node:pattern2', callback2);
oboeInstance.removeListener('node:pattern3', callback3);
var
callback1 = jasmine.createSpy('callback 1'),
callback2 = jasmine.createSpy('callback 2'),
callback3 = jasmine.createSpy('callback 3'),
ascent = list(namedNode('node', {}));
oboeBus('node:pattern1').emit( {}, ascent);
oboeBus('node:pattern2').emit( {}, ascent);
oboeBus('node:pattern3').emit( {}, ascent);
oboeInstance.node('pattern1', callback1);
oboeInstance.on('node', 'pattern2', callback2);
oboeInstance.on('node', {pattern3: callback3});
expect(callback1).not.toHaveBeenCalled()
expect(callback2).not.toHaveBeenCalled()
expect(callback3).not.toHaveBeenCalled()
});
});
oboeInstance.removeListener('node:pattern1', callback1);
oboeInstance.removeListener('node:pattern2', callback2);
oboeInstance.removeListener('node:pattern3', callback3);
describe('start event', function() {
it('notifies .on(start) listener when http response starts', function(){
var startCallback = jasmine.createSpy('start callback');
oboeBus('node:pattern1').emit( {}, ascent);
oboeBus('node:pattern2').emit( {}, ascent);
oboeBus('node:pattern3').emit( {}, ascent);
oboeInstance.on('start', startCallback);
expect(callback1).not.toHaveBeenCalled()
expect(callback2).not.toHaveBeenCalled()
expect(callback3).not.toHaveBeenCalled()
});
});
expect(startCallback).not.toHaveBeenCalled()
describe('start event', function() {
it('notifies .on(start) listener when http response starts', function(){
var startCallback = jasmine.createSpy('start callback');
oboeBus(HTTP_START).emit( 200, {a_header:'foo'} )
oboeInstance.on('start', startCallback);
expect(startCallback).toHaveBeenCalledWith( 200, {a_header:'foo'} )
});
expect(startCallback).not.toHaveBeenCalled()
it('notifies .start listener when http response starts', function(){
var startCallback = jasmine.createSpy('start callback');
oboeBus(HTTP_START).emit( 200, {a_header:'foo'} )
oboeInstance.start(startCallback);
expect(startCallback).toHaveBeenCalledWith( 200, {a_header:'foo'} )
});
expect(startCallback).not.toHaveBeenCalled()
it('notifies .start listener when http response starts', function(){
var startCallback = jasmine.createSpy('start callback');
oboeBus(HTTP_START).emit( 200, {a_header:'foo'} )
oboeInstance.start(startCallback);
expect(startCallback).toHaveBeenCalledWith( 200, {a_header:'foo'} )
});
expect(startCallback).not.toHaveBeenCalled()
it('can be de-registered', function() {
var startCallback = jasmine.createSpy('start callback');
oboeBus(HTTP_START).emit( 200, {a_header:'foo'} )
oboeInstance.on('start', startCallback);
oboeInstance.removeListener('start', startCallback);
expect(startCallback).toHaveBeenCalledWith( 200, {a_header:'foo'} )
});
oboeBus(HTTP_START).emit( 200, {a_header:'foo'} )
it('can be de-registered', function() {
var startCallback = jasmine.createSpy('start callback');
expect(startCallback).not.toHaveBeenCalled()
});
});
oboeInstance.on('start', startCallback);
oboeInstance.removeListener('start', startCallback);
oboeBus(HTTP_START).emit( 200, {a_header:'foo'} )
describe('done event', function(){
expect(startCallback).not.toHaveBeenCalled()
});
});
it('calls listener on end of JSON when added using .on(done)', function() {
var doneCallback = jasmine.createSpy('done callback');
oboeInstance.on('done', doneCallback);
describe('done event', function(){
expect(doneCallback).not.toHaveBeenCalled()
it('calls listener on end of JSON when added using .on(done)', function() {
var doneCallback = jasmine.createSpy('done callback');
oboeBus(ROOT_NODE_FOUND).emit( {}, anAscent())
oboeInstance.on('done', doneCallback);
expect(doneCallback).toHaveBeenCalled()
});
expect(doneCallback).not.toHaveBeenCalled()
it('calls listener on end of JSON when added using .done', function() {
var doneCallback = jasmine.createSpy('done callback');
oboeBus(ROOT_NODE_FOUND).emit( {}, anAscent())
oboeInstance.done(doneCallback);
expect(doneCallback).toHaveBeenCalled()
});
expect(doneCallback).not.toHaveBeenCalled()
it('calls listener on end of JSON when added using .done', function() {
var doneCallback = jasmine.createSpy('done callback');
oboeBus(ROOT_NODE_FOUND).emit( {}, anAscent())
oboeInstance.done(doneCallback);
expect(doneCallback).toHaveBeenCalled()
});
expect(doneCallback).not.toHaveBeenCalled()
it('can be de-registered', function() {
var doneCallback = jasmine.createSpy('done callback');
oboeBus(ROOT_NODE_FOUND).emit( {}, anAscent())
oboeInstance.on('done', doneCallback);
oboeInstance.removeListener('done', doneCallback);
expect(doneCallback).toHaveBeenCalled()
});
oboeBus('node:!').emit( {}, anAscent())
it('can be de-registered', function() {
var doneCallback = jasmine.createSpy('done callback');
expect(doneCallback).not.toHaveBeenCalled()
});
});
oboeInstance.on('done', doneCallback);
oboeInstance.removeListener('done', doneCallback);
oboeBus('node:!').emit( {}, anAscent())
it('emits ABORTING when .abort() is called', function() {
oboeInstance.abort();
expect(oboeBus(ABORTING).emit).toHaveBeenCalled()
});
expect(doneCallback).not.toHaveBeenCalled()
});
});
describe('errors cases', function(){
describe('calling fail listener', function() {
it('emits ABORTING when .abort() is called', function() {
oboeInstance.abort();
expect(oboeBus(ABORTING).emit.calls.count()).toEqual(1)
expect(oboeBus(ABORTING).emit).toHaveBeenCalled()
});
it('notifies .on(fail) listener when something fails', function(){
var failCallback = jasmine.createSpy('fail callback');
describe('errors cases', function(){
oboeInstance.on('fail', failCallback);
describe('calling fail listener', function() {
expect(failCallback).not.toHaveBeenCalled()
it('notifies .on(fail) listener when something fails', function(){
var failCallback = jasmine.createSpy('fail callback');
oboeBus(FAIL_EVENT).emit( 'something went wrong' )
oboeInstance.on('fail', failCallback);
expect(failCallback).toHaveBeenCalledWith( 'something went wrong' )
});
expect(failCallback).not.toHaveBeenCalled()
it('notifies .fail listener when something fails', function(){
var failCallback = jasmine.createSpy('fail callback');
oboeBus(FAIL_EVENT).emit( 'something went wrong' )
oboeInstance.fail(failCallback);
expect(failCallback).toHaveBeenCalledWith( 'something went wrong' )
});
expect(failCallback).not.toHaveBeenCalled()
it('notifies .fail listener when something fails', function(){
var failCallback = jasmine.createSpy('fail callback');
oboeBus(FAIL_EVENT).emit( 'something went wrong' )
oboeInstance.fail(failCallback);
expect(failCallback).toHaveBeenCalledWith( 'something went wrong' )
});
expect(failCallback).not.toHaveBeenCalled()
it('can be de-registered', function() {
var failCallback = jasmine.createSpy('fail callback');
oboeBus(FAIL_EVENT).emit( 'something went wrong' )
oboeInstance.on('fail', failCallback);
oboeInstance.removeListener('fail', failCallback);
expect(failCallback).toHaveBeenCalledWith( 'something went wrong' )
});
oboeBus(FAIL_EVENT).emit( 'something went wrong' )
it('can be de-registered', function() {
var failCallback = jasmine.createSpy('fail callback');
expect(failCallback).not.toHaveBeenCalled()
});
oboeInstance.on('fail', failCallback);
oboeInstance.removeListener('fail', failCallback);
oboeBus(FAIL_EVENT).emit( 'something went wrong' )
expect(failCallback).not.toHaveBeenCalled()
});
});
it('is protected from error in node callback', function() {
var e = "an error";
var nodeCallback = jasmine.createSpy('nodeCallback').andThrow(e);
it('is protected from error in node callback', function() {
var e = "an error";
var nodeCallback = jasmine.createSpy('nodeCallback').and.throwError(e);
oboeInstance.on('node', 'a_pattern', nodeCallback);
oboeInstance.on('node', 'a_pattern', nodeCallback);
spyOn(window, 'setTimeout');
expect(function(){
oboeBus('node:a_pattern').emit( {}, anAscent())
}).not.toThrow()
spyOn(window, 'setTimeout');
expect(function(){
oboeBus('node:a_pattern').emit( {}, anAscent())
}).not.toThrow()
expect(nodeCallback).toHaveBeenCalled()
expect(window.setTimeout.mostRecentCall.args[0]).toThrow(e)
});
expect(nodeCallback).toHaveBeenCalled()
expect(window.setTimeout.calls.mostRecent().args[0]).toThrow(new Error(e))
});
it('is protected from error in node callback added via shortcut', function() {
var e = "an error";
var nodeCallback = jasmine.createSpy('node callback').andThrow(e);
it('is protected from error in node callback added via shortcut', function() {
var e = "an error";
var nodeCallback = jasmine.createSpy('node callback').and.throwError(e);
oboeInstance.on('node', {'a_pattern': nodeCallback});
oboeInstance.on('node', {'a_pattern': nodeCallback});
spyOn(window, 'setTimeout');
expect(function(){
oboeBus('node:a_pattern').emit( {}, anAscent())
}).not.toThrow()
spyOn(window, 'setTimeout');
expect(function(){
oboeBus('node:a_pattern').emit( {}, anAscent())
}).not.toThrow()
expect(nodeCallback).toHaveBeenCalled()
expect(window.setTimeout.mostRecentCall.args[0]).toThrow(e)
});
expect(nodeCallback).toHaveBeenCalled()
expect(window.setTimeout.calls.mostRecent().args[0]).toThrow(new Error(e))
});
it('is protected from error in path callback', function() {
var e = "an error";
var pathCallback = jasmine.createSpy('path callback').andThrow(e);
it('is protected from error in path callback', function() {
var e = "an error";
var pathCallback = jasmine.createSpy('path callback').and.throwError(e);
oboeInstance.on('path', 'a_pattern', pathCallback);
oboeInstance.on('path', 'a_pattern', pathCallback);
spyOn(window, 'setTimeout');
expect(function(){
oboeBus('path:a_pattern').emit( {}, anAscent())
}).not.toThrow()
spyOn(window, 'setTimeout');
expect(function(){
oboeBus('path:a_pattern').emit( {}, anAscent())
}).not.toThrow()
expect(pathCallback).toHaveBeenCalled()
expect(window.setTimeout.mostRecentCall.args[0]).toThrow(e)
});
expect(pathCallback).toHaveBeenCalled()
expect(window.setTimeout.calls.mostRecent().args[0]).toThrow(new Error(e))
});
it('is protected from error in start callback', function() {
var e = "an error";
var startCallback = jasmine.createSpy('start callback').andThrow(e);
it('is protected from error in start callback', function() {
var e = "an error";
var startCallback = jasmine.createSpy('start callback').and.throwError(e);
oboeInstance.on('start', startCallback);
oboeInstance.on('start', startCallback);
spyOn(window, 'setTimeout');
expect(function(){
oboeBus(HTTP_START).emit()
}).not.toThrow()
spyOn(window, 'setTimeout');
expect(function(){
oboeBus(HTTP_START).emit()
}).not.toThrow()
expect(startCallback).toHaveBeenCalled()
expect(window.setTimeout.mostRecentCall.args[0]).toThrow(e)
});
expect(startCallback).toHaveBeenCalled()
expect(window.setTimeout.calls.mostRecent().args[0]).toThrow(new Error(e))
});
it('is protected from error in done callback', function() {
var e = "an error";
var doneCallback = jasmine.createSpy('done callback').andThrow(e);
it('is protected from error in done callback', function() {
var e = "an error";
var doneCallback = jasmine.createSpy('done callback').and.throwError(e);
oboeInstance.done( doneCallback);
oboeInstance.done( doneCallback);
spyOn(window, 'setTimeout');
expect(function(){
oboeBus(ROOT_NODE_FOUND).emit( {}, anAscent())
}).not.toThrow()
spyOn(window, 'setTimeout');
expect(function(){
oboeBus(ROOT_NODE_FOUND).emit( {}, anAscent())
}).not.toThrow()
expect(doneCallback).toHaveBeenCalled()
expect(window.setTimeout.mostRecentCall.args[0]).toThrow(e)
});
expect(doneCallback).toHaveBeenCalled()
expect(window.setTimeout.calls.mostRecent().args[0]).toThrow(new Error(e))
});
});
});
describe('unknown event types', function() {
xdescribe('unknown event types', function() {
it('can be added and fired', function() {
var spy1 = jasmine.createSpy('xyzzy callback');
var spy2 = jasmine.createSpy('end of universe callback');
it('can be added and fired', function() {
var spy1 = jasmine.createSpy('xyzzy callback');
var spy2 = jasmine.createSpy('end of universe callback');
expect(function(){
oboeInstance
.on('xyzzy', spy1)
.on('end_of_universe', spy2);
}).not.toThrow();
var setUp = function(){
oboeInstance
.on('xyzzy', spy1)
.on('end_of_universe', spy2);
};
expect(setUp).not.toThrow();
oboeInstance.emit('xyzzy', 'hello');
oboeInstance.emit('end_of_universe', 'oh no!');
oboeInstance
.on('xyzzy', spy1)
.on('end_of_universe', spy2);
expect( spy1 ).toHaveBeenCalledWith('hello');
expect( spy2 ).toHaveBeenCalledWith('oh no!');
});
oboeInstance.emit('xyzzy', 'hello');
oboeInstance.emit('end_of_universe', 'oh no!');
it('is allows removal', function() {
var spy1 = jasmine.createSpy('xyzzy callback');
var spy2 = jasmine.createSpy('end of universe callback');
expect( spy1 ).toHaveBeenCalledWith('hello');
expect( spy2 ).toHaveBeenCalledWith('oh no!');
});
oboeInstance
.on('xyzzy', spy1)
.on('end_of_universe', spy2);
it('is allows removal', function() {
var spy1 = jasmine.createSpy('xyzzy callback');
var spy2 = jasmine.createSpy('end of universe callback');
oboeInstance.removeListener('xyzzy', spy1);
oboeInstance.removeListener('end_of_universe', spy2);
oboeInstance
.on('xyzzy', spy1)
.on('end_of_universe', spy2);
oboeInstance.emit('xyzzy', 'hello');
oboeInstance.emit('end_of_universe', 'oh no!');
oboeInstance.removeListener('xyzzy', spy1);
oboeInstance.removeListener('end_of_universe', spy2);
expect( spy1 ).not.toHaveBeenCalled()
expect( spy2 ).not.toHaveBeenCalled()
});
});
oboeInstance.emit('xyzzy', 'hello');
oboeInstance.emit('end_of_universe', 'oh no!');
expect( spy1 ).not.toHaveBeenCalled()
expect( spy2 ).not.toHaveBeenCalled()
});
});
});
describe('jsonPath', function(){
describe('compiles valid syntax while rejecting invalid', function() {
it("compiles a basic pattern without throwing", function(){
expect(compiling('!')).not.toThrow();
describe('compiles valid syntax while rejecting invalid', function() {
it("compiles a basic pattern without throwing", function(){
expect(compiling('!')).not.toThrow();
});
describe("syntactically invalid patterns", function() {
it("fail on single invalid token", function(){
expect(compiling('/')).toThrow();
});
describe("syntactically invalid patterns", function() {
it("fail on single invalid token", function(){
expect(compiling('/')).toThrow();
});
it("fail on invalid pattern with some valid tokens", function(){
expect(compiling('foo/')).toThrow();
});
it("fail on unclosed duck clause", function(){
expect(compiling('{foo')).toThrow();
});
it("fail on token with capture alone", function(){
expect(compiling('foo$')).toThrow();
});
});
});
describe('patterns match correct paths', function() {
describe('when pattern has only bang', function() {
it("should match root", function(){
expect('!').toMatchPath([]);
});
it("should miss non-root", function(){
expect('!').not.toMatchPath(['a']);
expect('!').not.toMatchPath(['a', 'b']);
});
it("fail on invalid pattern with some valid tokens", function(){
expect(compiling('foo/')).toThrow();
});
it('should match * universally', function() {
expect('*').toMatchPath( [] );
expect('*').toMatchPath( ['a'] );
expect('*').toMatchPath( ['a', 2] );
expect('*').toMatchPath( ['a','b'] );
it("fail on unclosed duck clause", function(){
expect(compiling('{foo')).toThrow();
});
it('should match empty pattern universally', function() {
expect('').toMatchPath( [] );
expect('').toMatchPath( ['a'] );
expect('').toMatchPath( ['a', 2] );
expect('').toMatchPath( ['a','b'] );
});
it('should match !.* against any top-level path node', function() {
expect('!.*').toMatchPath( ['foo'])
expect('!.*').toMatchPath( ['bar'])
expect('!.*').not.toMatchPath( [])
expect('!.*').not.toMatchPath( ['foo', 'bar'])
});
it('should match !..* against anything but the root', function() {
expect('!..*').not.toMatchPath( [] );
expect('!..*').toMatchPath( ['a'] );
expect('!..*').toMatchPath( ['a','b'] );
it("fail on token with capture alone", function(){
expect(compiling('foo$')).toThrow();
});
it('should match *..* against anything except the root since it requires a decendant ' +
'which the root will never satisfy because it cannot have an ancestor', function() {
});
});
describe('patterns match correct paths', function() {
describe('when pattern has only bang', function() {
it("should match root", function(){
expect('!').toMatchPath([]);
});
it("should miss non-root", function(){
expect('!').not.toMatchPath(['a']);
expect('!').not.toMatchPath(['a', 'b']);
});
});
it('should match * universally', function() {
expect('*').toMatchPath( [] );
expect('*').toMatchPath( ['a'] );
expect('*').toMatchPath( ['a', 2] );
expect('*').toMatchPath( ['a','b'] );
});
it('should match empty pattern universally', function() {
expect('').toMatchPath( [] );
expect('').toMatchPath( ['a'] );
expect('').toMatchPath( ['a', 2] );
expect('').toMatchPath( ['a','b'] );
});
it('should match !.* against any top-level path node', function() {
expect('!.*').toMatchPath( ['foo'])
expect('!.*').toMatchPath( ['bar'])
expect('!.*').not.toMatchPath( [])
expect('!.*').not.toMatchPath( ['foo', 'bar'])
});
it('should match !..* against anything but the root', function() {
expect('!..*').not.toMatchPath( [] );
expect('!..*').toMatchPath( ['a'] );
expect('!..*').toMatchPath( ['a','b'] );
});
it('should match *..* against anything except the root since it requires a decendant ' +
'which the root will never satisfy because it cannot have an ancestor', function() {
expect('*..*').not.toMatchPath( [] );

@@ -89,511 +89,515 @@ expect('*..*').toMatchPath( ['a'] );

});
it('should match !.foo against foo node at first level only', function(){
expect('!.foo').toMatchPath( ['foo'] );
expect('!.foo').not.toMatchPath( [] );
expect('!.foo').not.toMatchPath( ['foo', 'bar']);
expect('!.foo').not.toMatchPath( ['bar'] );
});
it('should match !.foo.bar against paths with foo as first node and bar as second', function() {
expect('!.a.b').toMatchPath( ['a', 'b'])
expect('!.a.b').not.toMatchPath( [])
expect('!.a.b').not.toMatchPath( ['a'])
});
it('should match !..foo against any path ending in foo', function(){
expect('!..foo').not.toMatchPath( []);
expect('!..foo').toMatchPath( ['foo']);
expect('!..foo').toMatchPath( ['a', 'foo']);
expect('!..foo').not.toMatchPath( ['a', 'foo', 'a']);
expect('!..foo').toMatchPath( ['a', 'foo', 'foo']);
expect('!..foo').toMatchPath( ['a', 'a', 'foo']);
expect('!..foo').not.toMatchPath( ['a', 'a', 'foot']);
expect('!..foo').not.toMatchPath( ['a', 'foo', 'foo', 'a']);
});
it('should match ..foo like !..foo', function() {
expect('..foo').not.toMatchPath( []);
expect('..foo').toMatchPath( ['foo']);
expect('..foo').toMatchPath( ['a', 'foo']);
expect('..foo').not.toMatchPath( ['a', 'foo', 'a']);
expect('..foo').toMatchPath( ['a', 'foo', 'foo']);
expect('..foo').toMatchPath( ['a', 'a', 'foo']);
expect('..foo').not.toMatchPath( ['a', 'a', 'foot']);
expect('..foo').not.toMatchPath( ['a', 'foo', 'foo', 'a']);
});
it('should match foo like !..foo or ..foo', function() {
expect('foo').not.toMatchPath( []);
expect('foo').toMatchPath( ['foo']);
expect('foo').toMatchPath( ['a', 'foo']);
expect('foo').not.toMatchPath( ['a', 'foo', 'a']);
expect('foo').toMatchPath( ['a', 'foo', 'foo']);
expect('foo').toMatchPath( ['a', 'a', 'foo']);
expect('foo').not.toMatchPath( ['a', 'a', 'foot']);
expect('foo').not.toMatchPath( ['a', 'foo', 'foo', 'a']);
});
it('is not fooled by substrings in path nodes', function(){
expect('!.foo').not.toMatchPath( ['foot'])
});
it('matches !..foo.bar against bars which are direct children of a foo anywhere in the document', function() {
expect('!..foo.bar').not.toMatchPath( []);
expect('!..foo.bar').not.toMatchPath( ['foo']);
expect('!..foo.bar').not.toMatchPath( ['a', 'foo']);
expect('!..foo.bar').toMatchPath( ['a', 'foo', 'bar']);
expect('!..foo.bar').not.toMatchPath( ['a', 'foo', 'foo']);
expect('!..foo.bar').toMatchPath( ['a', 'a', 'a', 'foo', 'bar']);
expect('!..foo.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'bar', 'a']);
});
it('matches foo.bar like !..foo.bar', function() {
expect('foo.bar').not.toMatchPath( [])
expect('foo.bar').not.toMatchPath( ['foo'])
expect('foo.bar').not.toMatchPath( ['a', 'foo'])
expect('foo.bar').toMatchPath( ['a', 'foo', 'bar'])
expect('foo.bar').not.toMatchPath( ['a', 'foo', 'foo'])
expect('foo.bar').toMatchPath( ['a', 'a', 'a', 'foo', 'bar'])
expect('foo.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'bar', 'a'])
});
it('matches !..foo.*.bar only if there is an intermediate node between foo and bar', function(){
expect('!..foo.*.bar').not.toMatchPath( [])
expect('!..foo.*.bar').not.toMatchPath( ['foo'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'foo'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'foo', 'bar'])
expect('!..foo.*.bar').toMatchPath( ['a', 'foo', 'a', 'bar'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'foo', 'foo'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'bar'])
expect('!..foo.*.bar').toMatchPath( ['a', 'a', 'a', 'foo', 'a', 'bar'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'bar', 'a'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'a', 'bar', 'a'])
});
describe('with numeric path nodes in the pattern', function() {
it('should be able to handle numeric nodes in object notation', function(){
expect('!.a.2').toMatchPath( ['a', 2])
expect('!.a.2').toMatchPath( ['a', '2'])
expect('!.a.2').not.toMatchPath( [])
expect('!.a.2').not.toMatchPath( ['a'])
});
it('should match !.foo against foo node at first level only', function(){
expect('!.foo').toMatchPath( ['foo'] );
expect('!.foo').not.toMatchPath( [] );
expect('!.foo').not.toMatchPath( ['foo', 'bar']);
expect('!.foo').not.toMatchPath( ['bar'] );
it('should be able to handle numberic nodes in array notation', function(){
expect('!.a[2]').toMatchPath( ['a', 2])
expect('!.a[2]').toMatchPath( ['a', '2'])
expect('!.a[2]').not.toMatchPath( [])
expect('!.a[2]').not.toMatchPath( ['a'])
});
it('should match !.foo.bar against paths with foo as first node and bar as second', function() {
expect('!.a.b').toMatchPath( ['a', 'b'])
expect('!.a.b').not.toMatchPath( [])
expect('!.a.b').not.toMatchPath( ['a'])
});
describe('with array notation', function() {
it('should handle adjacent array notations', function(){
expect('!["a"][2]').toMatchPath( ['a', 2])
expect('!["a"][2]').toMatchPath( ['a', '2'])
expect('!["a"][2]').not.toMatchPath( [])
expect('!["a"][2]').not.toMatchPath( ['a'])
});
it('should match !..foo against any path ending in foo', function(){
expect('!..foo').not.toMatchPath( []);
expect('!..foo').toMatchPath( ['foo']);
expect('!..foo').toMatchPath( ['a', 'foo']);
expect('!..foo').not.toMatchPath( ['a', 'foo', 'a']);
expect('!..foo').toMatchPath( ['a', 'foo', 'foo']);
expect('!..foo').toMatchPath( ['a', 'a', 'foo']);
expect('!..foo').not.toMatchPath( ['a', 'a', 'foot']);
expect('!..foo').not.toMatchPath( ['a', 'foo', 'foo', 'a']);
it('should allow to specify child of root', function(){
expect('![2]').toMatchPath( [2])
expect('![2]').toMatchPath( ['2'])
expect('![2]').not.toMatchPath( [])
expect('![2]').not.toMatchPath( ['a'])
});
it('should match ..foo like !..foo', function() {
expect('..foo').not.toMatchPath( []);
expect('..foo').toMatchPath( ['foo']);
expect('..foo').toMatchPath( ['a', 'foo']);
expect('..foo').not.toMatchPath( ['a', 'foo', 'a']);
expect('..foo').toMatchPath( ['a', 'foo', 'foo']);
expect('..foo').toMatchPath( ['a', 'a', 'foo']);
expect('..foo').not.toMatchPath( ['a', 'a', 'foot']);
expect('..foo').not.toMatchPath( ['a', 'foo', 'foo', 'a']);
it('should be allowed to contain a star', function(){
expect('![*]').toMatchPath( [2])
expect('![*]').toMatchPath( ['2'])
expect('![*]').toMatchPath( ['a'])
expect('![*]').not.toMatchPath( [])
});
it('should match foo like !..foo or ..foo', function() {
expect('foo').not.toMatchPath( []);
expect('foo').toMatchPath( ['foo']);
expect('foo').toMatchPath( ['a', 'foo']);
expect('foo').not.toMatchPath( ['a', 'foo', 'a']);
expect('foo').toMatchPath( ['a', 'foo', 'foo']);
expect('foo').toMatchPath( ['a', 'a', 'foo']);
expect('foo').not.toMatchPath( ['a', 'a', 'foot']);
expect('foo').not.toMatchPath( ['a', 'foo', 'foo', 'a']);
});
it('is not fooled by substrings in path nodes', function(){
expect('!.foo').not.toMatchPath( ['foot'])
});
describe('composition of several tokens into complex patterns', function() {
it('should be able to handle more than one double dot', function() {
expect('!..foods..fr')
.toMatchPath( ['foods', 2, 'name', 'fr']);
});
it('matches !..foo.bar against bars which are direct children of a foo anywhere in the document', function() {
expect('!..foo.bar').not.toMatchPath( []);
expect('!..foo.bar').not.toMatchPath( ['foo']);
expect('!..foo.bar').not.toMatchPath( ['a', 'foo']);
expect('!..foo.bar').toMatchPath( ['a', 'foo', 'bar']);
expect('!..foo.bar').not.toMatchPath( ['a', 'foo', 'foo']);
expect('!..foo.bar').toMatchPath( ['a', 'a', 'a', 'foo', 'bar']);
expect('!..foo.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'bar', 'a']);
});
it('matches foo.bar like !..foo.bar', function() {
expect('foo.bar').not.toMatchPath( [])
expect('foo.bar').not.toMatchPath( ['foo'])
expect('foo.bar').not.toMatchPath( ['a', 'foo'])
expect('foo.bar').toMatchPath( ['a', 'foo', 'bar'])
expect('foo.bar').not.toMatchPath( ['a', 'foo', 'foo'])
expect('foo.bar').toMatchPath( ['a', 'a', 'a', 'foo', 'bar'])
expect('foo.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'bar', 'a'])
it('should be able to match ..* or ..[*] as if it were * because .. matches zero nodes', function(){
expect('!..*.bar')
.toMatchPath(['anything', 'bar']);
expect('!..[*].bar')
.toMatchPath(['anything', 'bar']);
});
it('matches !..foo.*.bar only if there is an intermediate node between foo and bar', function(){
expect('!..foo.*.bar').not.toMatchPath( [])
expect('!..foo.*.bar').not.toMatchPath( ['foo'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'foo'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'foo', 'bar'])
expect('!..foo.*.bar').toMatchPath( ['a', 'foo', 'a', 'bar'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'foo', 'foo'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'bar'])
expect('!..foo.*.bar').toMatchPath( ['a', 'a', 'a', 'foo', 'a', 'bar'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'bar', 'a'])
expect('!..foo.*.bar').not.toMatchPath( ['a', 'a', 'a', 'foo', 'a', 'bar', 'a'])
});
describe('using css4-style syntax', function() {
it('returns deepest node when no css4-style syntax is used', function(){
expect( matchOf( 'l2.*' ).against(
ascentFrom({ l1: {l2: {l3:'leaf'}}})
)).toSpecifyNode('leaf');
});
describe('with numeric path nodes in the pattern', function() {
it('should be able to handle numeric nodes in object notation', function(){
expect('!.a.2').toMatchPath( ['a', 2])
expect('!.a.2').toMatchPath( ['a', '2'])
expect('!.a.2').not.toMatchPath( [])
expect('!.a.2').not.toMatchPath( ['a'])
});
it('should be able to handle numberic nodes in array notation', function(){
expect('!.a[2]').toMatchPath( ['a', 2])
expect('!.a[2]').toMatchPath( ['a', '2'])
expect('!.a[2]').not.toMatchPath( [])
expect('!.a[2]').not.toMatchPath( ['a'])
});
it('returns correct named node', function(){
expect( matchOf( '$l2.*' ).against(
ascentFrom({ l1: {l2: {l3:'leaf'}}})
)).toSpecifyNode({l3:'leaf'});
});
describe('with array notation', function() {
it('should handle adjacent array notations', function(){
expect('!["a"][2]').toMatchPath( ['a', 2])
expect('!["a"][2]').toMatchPath( ['a', '2'])
expect('!["a"][2]').not.toMatchPath( [])
expect('!["a"][2]').not.toMatchPath( ['a'])
});
it('should allow to specify child of root', function(){
expect('![2]').toMatchPath( [2])
expect('![2]').toMatchPath( ['2'])
expect('![2]').not.toMatchPath( [])
expect('![2]').not.toMatchPath( ['a'])
});
it('should be allowed to contain a star', function(){
expect('![*]').toMatchPath( [2])
expect('![*]').toMatchPath( ['2'])
expect('![*]').toMatchPath( ['a'])
expect('![*]').not.toMatchPath( [])
});
it('returns correct node when css4-style pattern is followed by double dot', function() {
expect( matchOf( '!..$foo..bar' ).against(
ascentFrom({ l1: {foo: {l3: {bar: 'leaf'}}}})
)).toSpecifyNode({l3: {bar: 'leaf'}});
});
describe('composition of several tokens into complex patterns', function() {
it('should be able to handle more than one double dot', function() {
expect('!..foods..fr')
.toMatchPath( ['foods', 2, 'name', 'fr']);
});
it('should be able to match ..* or ..[*] as if it were * because .. matches zero nodes', function(){
expect('!..*.bar')
.toMatchPath(['anything', 'bar']);
expect('!..[*].bar')
.toMatchPath(['anything', 'bar']);
});
it('can match children of root while capturing the root', function() {
expect( matchOf( '$!.*' ).against(
ascentFrom({ l1: 'leaf' })
)).toSpecifyNode({ l1: 'leaf' });
});
describe('using css4-style syntax', function() {
it('returns deepest node when no css4-style syntax is used', function(){
expect( matchOf( 'l2.*' ).against(
ascentFrom({ l1: {l2: {l3:'leaf'}}})
)).toSpecifyNode('leaf');
});
it('returns correct named node', function(){
expect( matchOf( '$l2.*' ).against(
ascentFrom({ l1: {l2: {l3:'leaf'}}})
)).toSpecifyNode({l3:'leaf'});
});
it('returns captured node with array notation', function() {
it('returns correct node when css4-style pattern is followed by double dot', function() {
expect( matchOf( '!..$foo..bar' ).against(
ascentFrom({ l1: {foo: {l3: {bar: 'leaf'}}}})
)).toSpecifyNode({l3: {bar: 'leaf'}});
});
it('can match children of root while capturing the root', function() {
expect( matchOf( '$!.*' ).against(
ascentFrom({ l1: 'leaf' })
)).toSpecifyNode({ l1: 'leaf' });
});
it('returns captured node with array notation', function() {
expect( matchOf( '$["l1"].l2' ).against(
ascentFrom({ l1: {l2:'leaf'} })
)).toSpecifyNode({ l2: 'leaf' });
});
it('returns captured node with array numbered notation', function() {
expect( matchOf( '$["2"].l2' ).against(
ascentFrom({ '2': {l2:'leaf'} })
)).toSpecifyNode({ l2: 'leaf' });
});
it('returns captured node with star notation', function() {
expect( matchOf( '!..$*.l3' ).against(
ascentFrom({ l1: {l2:{l3:'leaf'}} })
)).toSpecifyNode({ l3: 'leaf' });
});
it('returns captured node with array star notation', function(){
expect( matchOf( '$["l1"].l2' ).against(
expect( matchOf( '!..$[*].l3' ).against(
ascentFrom({ l1: {l2:{l3:'leaf'}} })
)).toSpecifyNode({ l3: 'leaf' });
});
ascentFrom({ l1: {l2:'leaf'} })
)).toSpecifyNode({ l2: 'leaf' });
});
describe('with duck matching', function() {
it('can do basic ducking', function(){
var rootJson = {
people:{
jack:{
name: 'Jack'
, email: 'jack@example.com'
}
}
};
expect( matchOf( '{name email}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('can duck on two levels of a path', function(){
var rootJson = {
people:{
jack:{
name: 'Jack'
, email: 'jack@example.com'
}
}
};
expect( matchOf( '{people}.{jack}.{name email}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('fails if one duck is unsatisfied', function(){
var rootJson = {
people:{
jack:{
name: 'Jack'
, email: 'jack@example.com'
}
}
};
expect( matchOf( '{people}.{alberto}.{name email}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).not.toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('can construct the root duck type', function(){
var rootJson = {
people:{
jack:{
name: 'Jack'
, email: 'jack@example.com'
}
}
};
expect( matchOf( '{}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('does not match if not all fields are there', function(){
var rootJson = {
people:{
jack:{
// no name here!
email: 'jack@example.com'
}
}
};
expect( matchOf( '{name email}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).not.toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('fails if something upstream fails', function(){
var rootJson = {
women:{
betty:{
name:'Betty'
, email: 'betty@example.com'
}
},
men:{
// we don't have no menz!
}
};
expect( matchOf( 'men.{name email}' ).against(
asAscent(
[ 'women', 'betty' ],
[rootJson, rootJson.women, rootJson.women.betty ]
)
)).not.toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('does not crash given ascent starting from non-objects', function(){
var rootJson = [ 1, 2, 3 ];
expect( function(){ matchOf( '{spin taste}' ).against(
asAscent(
[ '0' ],
[rootJson, rootJson[0] ]
)
)}).not.toThrow();
});
it('does not match when given non-object', function(){
it('returns captured node with array numbered notation', function() {
var rootJson = [ 1, 2, 3 ];
expect( matchOf( '{spin taste}' ).against(
asAscent(
[ '0' ],
[rootJson, rootJson[0] ]
)
)).toBeFalsy();
});
expect( matchOf( '$["2"].l2' ).against(
ascentFrom({ '2': {l2:'leaf'} })
)).toSpecifyNode({ l2: 'leaf' });
});
});
beforeEach(function(){
this.addMatchers({
toSpecifyNode: function( expectedNode ){
it('returns captured node with star notation', function() {
expect( matchOf( '!..$*.l3' ).against(
ascentFrom({ l1: {l2:{l3:'leaf'}} })
)).toSpecifyNode({ l3: 'leaf' });
});
it('returns captured node with array star notation', function(){
expect( matchOf( '!..$[*].l3' ).against(
ascentFrom({ l1: {l2:{l3:'leaf'}} })
)).toSpecifyNode({ l3: 'leaf' });
});
});
describe('with duck matching', function() {
it('can do basic ducking', function(){
var rootJson = {
people:{
jack:{
name: 'Jack'
, email: 'jack@example.com'
}
}
};
expect( matchOf( '{name email}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('can duck on two levels of a path', function(){
var rootJson = {
people:{
jack:{
name: 'Jack'
, email: 'jack@example.com'
}
}
};
expect( matchOf( '{people}.{jack}.{name email}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('fails if one duck is unsatisfied', function(){
var rootJson = {
people:{
jack:{
name: 'Jack'
, email: 'jack@example.com'
}
}
};
expect( matchOf( '{people}.{alberto}.{name email}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).not.toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('can construct the root duck type', function(){
var rootJson = {
people:{
jack:{
name: 'Jack'
, email: 'jack@example.com'
}
}
};
expect( matchOf( '{}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('does not match if not all fields are there', function(){
var rootJson = {
people:{
jack:{
// no name here!
email: 'jack@example.com'
}
}
};
expect( matchOf( '{name email}' ).against(
asAscent(
[ 'people', 'jack' ],
[rootJson, rootJson.people, rootJson.people.jack ]
)
)).not.toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('fails if something upstream fails', function(){
var rootJson = {
women:{
betty:{
name:'Betty'
, email: 'betty@example.com'
}
},
men:{
// we don't have no menz!
}
};
expect( matchOf( 'men.{name email}' ).against(
asAscent(
[ 'women', 'betty' ],
[rootJson, rootJson.women, rootJson.women.betty ]
)
)).not.toSpecifyNode({name: 'Jack', email: 'jack@example.com'});
});
it('does not crash given ascent starting from non-objects', function(){
var rootJson = [ 1, 2, 3 ];
expect( function(){ matchOf( '{spin taste}' ).against(
asAscent(
[ '0' ],
[rootJson, rootJson[0] ]
)
)}).not.toThrow();
});
it('does not match when given non-object', function(){
var rootJson = [ 1, 2, 3 ];
expect( matchOf( '{spin taste}' ).against(
asAscent(
[ '0' ],
[rootJson, rootJson[0] ]
)
)).toBeFalsy();
});
});
});
beforeEach(function(){
jasmine.addMatchers({
toSpecifyNode: function() {
return {
compare: function(actual, expectedNode) {
var result = {};
function jsonSame(a,b) {
return JSON.stringify(a) == JSON.stringify(b);
return JSON.stringify(a) == JSON.stringify(b);
}
var match = this.actual;
var notClause = this.isNot? ' any node except ' : 'node';
this.message = function () {
return "Expected " + notClause + ' ' + JSON.stringify(expectedNode) + " but got " +
(match.node? JSON.stringify(match.node) : 'no match');
}
return jsonSame( expectedNode, match.node );
}
, toMatchPath:function( pathStack ) {
var pattern = this.actual;
try{
return !!matchOf(pattern).against(asAscent(pathStack));
var match = actual;
result.message = "Expected node " + JSON.stringify(expectedNode) +
"but got " + (match.node? JSON.stringify(match.node) : 'no match');
result.pass = jsonSame( expectedNode, match.node );
return result;
}
};
},
toMatchPath: function() {
return {
compare: function(actual, pathStack) {
var result = {};
var pattern = actual;
try {
result.pass = !!matchOf(pattern).against(asAscent(pathStack));
} catch( e ) {
this.message = function(){
return 'Error thrown running pattern "' + pattern +
'" against path [' + pathStack.join(',') + ']' + "\n" + (e.stack || e.message)
};
return false;
}
}
});
});
result.message = 'Error thrown running pattern "' + pattern +
'" against path [' + pathStack.join(',') + ']' + "\n" +
(e.stack || e.message);
result.pass = false;
};
function compiling(pattern) {
return function(){
jsonPathCompiler(pattern);
return result;
}
};
}
}
function matchOf(pattern) {
var compiledPattern = jsonPathCompiler(pattern);
return {
against:function(ascent) {
return compiledPattern(ascent);
}
};
}
// for the given pattern, return an array of empty objects of the one greater length to
// stand in for the nodestack in the cases where we only care about match or not match.
// one greater because the root node doesnt have a name
function fakeNodeStack(path){
var rtn = path.map(function(){return {}});
rtn.unshift({iAm:'root'});
return rtn;
}
function asAscent(pathStack, nodeStack){
// first, make a defensive copy of the vars so that we can mutate them at will:
pathStack = pathStack && JSON.parse(JSON.stringify(pathStack));
nodeStack = nodeStack && JSON.parse(JSON.stringify(nodeStack));
// change the two parameters into the test from arrays (which are easy to write as in-line js) to
// lists (which is what the code under test needs)
nodeStack = nodeStack || fakeNodeStack(pathStack);
pathStack.unshift(ROOT_PATH);
// NB: can't use the more functional Array.prototype.reduce here, IE8 doesn't have it and might not
// be polyfilled
var ascent = emptyList;
});
});
for (var i = 0; i < pathStack.length; i++) {
var mapping = {key: pathStack[i], node:nodeStack[i]};
ascent = cons( mapping, ascent );
function compiling(pattern) {
return function(){
jsonPathCompiler(pattern);
};
}
function matchOf(pattern) {
var compiledPattern = jsonPathCompiler(pattern);
return {
against:function(ascent) {
return compiledPattern(ascent);
}
return ascent;
}
});
};
}
// for the given pattern, return an array of empty objects of the one greater length to
// stand in for the nodestack in the cases where we only care about match or not match.
// one greater because the root node doesnt have a name
function fakeNodeStack(path){
var rtn = path.map(function(){return {}});
rtn.unshift({iAm:'root'});
return rtn;
}
function asAscent(pathStack, nodeStack){
// first, make a defensive copy of the vars so that we can mutate them at will:
pathStack = pathStack && JSON.parse(JSON.stringify(pathStack));
nodeStack = nodeStack && JSON.parse(JSON.stringify(nodeStack));
// change the two parameters into the test from arrays (which are easy to write as in-line js) to
// lists (which is what the code under test needs)
nodeStack = nodeStack || fakeNodeStack(pathStack);
pathStack.unshift(ROOT_PATH);
// NB: can't use the more functional Array.prototype.reduce here, IE8 doesn't have it and might not
// be polyfilled
var ascent = emptyList;
for (var i = 0; i < pathStack.length; i++) {
var mapping = {key: pathStack[i], node:nodeStack[i]};
ascent = cons( mapping, ascent );
}
return ascent;
}
});
jsonPathSyntax(function (pathNodeDesc, doubleDotDesc, dotDesc, bangDesc, emptyDesc) {
describe('json path token parser', function () {
describe('field list', function () {
describe('json path token parser', function () {
it('parses zero-length list', function () {
expect(pathNodeDesc('{}')).toContainMatches({fieldList:''})
});
describe('field list', function () {
it('parses single field', function () {
expect(pathNodeDesc('{a}')).toContainMatches({fieldList:'a' })
})
it('parses zero-length list', function () {
expect(pathNodeDesc('{}')).toContainMatches({fieldList:''});
});
it('parses two fields', function () {
expect(pathNodeDesc('{r2 d2}')).toContainMatches({fieldList:'r2 d2' })
})
it('parses single field', function () {
expect(pathNodeDesc('{a}')).toContainMatches({fieldList:'a' });
});
it('parses numeric fields', function () {
expect(pathNodeDesc('{1 2}')).toContainMatches({fieldList:'1 2' })
})
it('parses two fields', function () {
expect(pathNodeDesc('{r2 d2}')).toContainMatches({fieldList:'r2 d2' });
});
it('ignores whitespace', function () {
expect(pathNodeDesc('{a b}')).toContainMatches({fieldList:'a b' })
})
it('parses numeric fields', function () {
expect(pathNodeDesc('{1 2}')).toContainMatches({fieldList:'1 2' });
});
it('ignores more whitespace', function () {
expect(pathNodeDesc('{a b}')).toContainMatches({fieldList:'a b' })
})
it('ignores whitespace', function () {
expect(pathNodeDesc('{a b}')).toContainMatches({fieldList:'a b' });
});
it('parses 3 fields', function () {
expect(pathNodeDesc('{a b c}')).toContainMatches({fieldList:'a b c'})
})
it('ignores more whitespace', function () {
expect(pathNodeDesc('{a b}')).toContainMatches({fieldList:'a b' });
});
it('needs a closing brace', function () {
expect(pathNodeDesc('{a')).toNotMatch()
})
})
it('parses 3 fields', function () {
expect(pathNodeDesc('{a b c}')).toContainMatches({fieldList:'a b c'});
});
describe('object notation', function () {
it('needs a closing brace', function () {
expect(pathNodeDesc('{a')).toNotMatch();
});
});
it('parses a name', function () {
expect(pathNodeDesc('aaa')).toContainMatches({name:'aaa'})
})
it('parses a name containing a hyphen', function () {
expect(pathNodeDesc('x-security-token')).toContainMatches({name:'x-security-token'})
})
it('parses a name containing an underscore', function () {
expect(pathNodeDesc('x_security_token')).toContainMatches({name:'x_security_token'})
})
it('parses a name and recognises the capturing flag', function () {
expect(pathNodeDesc('$aaa')).toContainMatches({name:'aaa', capturing:true})
})
it('parses a name and field list', function () {
expect(pathNodeDesc('aaa{a b c}')).toContainMatches({name:'aaa', fieldList:'a b c'})
})
it('parses a name with field list and capturing flag', function () {
expect(pathNodeDesc('$aaa{a b c}')).toContainMatches({name:'aaa', capturing:true, fieldList:'a b c'})
})
it('wont parse unless the name is at the start', function () {
expect(pathNodeDesc('.a')).toNotMatch()
})
it('parses only the first name', function () {
expect(pathNodeDesc('a.b')).toContainMatches({name:'a'})
})
it('ignores invalid', function () {
expect(pathNodeDesc('$$a')).toNotMatch()
})
it('needs field list to close', function () {
expect(pathNodeDesc('.a{')).toNotMatch()
})
})
describe('object notation', function () {
describe('named array notation', function () {
it('parses a name', function () {
expect(pathNodeDesc('aaa')).toContainMatches({name:'aaa'});
});
it('parses a name containing a hyphen', function () {
expect(pathNodeDesc('x-security-token')).toContainMatches({name:'x-security-token'});
});
it('parses a name containing an underscore', function () {
expect(pathNodeDesc('x_security_token')).toContainMatches({name:'x_security_token'});
});
it('parses a name and recognises the capturing flag', function () {
expect(pathNodeDesc('$aaa')).toContainMatches({name:'aaa', capturing:true});
});
it('parses a name and field list', function () {
expect(pathNodeDesc('aaa{a b c}')).toContainMatches({name:'aaa', fieldList:'a b c'});
});
it('parses a name with field list and capturing flag', function () {
expect(pathNodeDesc('$aaa{a b c}')).toContainMatches({name:'aaa', capturing:true, fieldList:'a b c'});
});
it('wont parse unless the name is at the start', function () {
expect(pathNodeDesc('.a')).toNotMatch();
});
it('parses only the first name', function () {
expect(pathNodeDesc('a.b')).toContainMatches({name:'a'});
});
it('ignores invalid', function () {
expect(pathNodeDesc('$$a')).toNotMatch();
});
it('needs field list to close', function () {
expect(pathNodeDesc('.a{')).toNotMatch();
});
});
it('parses quoted', function () {
expect(pathNodeDesc('["foo"]')).toContainMatches({name:'foo'})
})
it('parses quoted and capturing', function () {
expect(pathNodeDesc('$["foo"]')).toContainMatches({name:'foo', capturing:true})
})
it('parses quoted with field list', function () {
expect(pathNodeDesc('["foo"]{a b c}')).toContainMatches({name:'foo', fieldList:'a b c'})
})
it('parses quoted with field list and capturing', function () {
expect(pathNodeDesc('$["foo"]{a b c}')).toContainMatches({name:'foo', capturing:true, fieldList:'a b c'})
})
it('ignores without a path name', function () {
expect(pathNodeDesc('[]')).toNotMatch()
})
it('fails with too many quotes', function () {
expect(pathNodeDesc('["""]')).toNotMatch()
})
it('parses unquoted', function () {
expect(pathNodeDesc('[foo]')).toNotMatch()
})
it('ignores unnamed because of an empty string', function () {
expect(pathNodeDesc('[""]')).toNotMatch()
})
it('parses first token only', function () {
expect(pathNodeDesc('["foo"]["bar"]')).toContainMatches({name:'foo'})
})
it('allows dot char inside quotes that would otherwise have a special meaning', function () {
expect(pathNodeDesc('[".foo"]')).toContainMatches({name:'.foo'})
})
it('allows star char inside quotes that would otherwise have a special meaning', function () {
expect(pathNodeDesc('["*"]')).toContainMatches({name:'*'})
})
it('allows dollar char inside quotes that would otherwise have a special meaning', function () {
expect(pathNodeDesc('["$"]')).toContainMatches({name:'$'})
})
it('allows underscore in quotes', function () {
expect(pathNodeDesc('["foo_bar"]')).toContainMatches({name:'foo_bar'})
})
it('allows non-ASCII chars in quotes', function () {
expect(pathNodeDesc('["你好"]')).toContainMatches({name:'你好'})
})
})
describe('named array notation', function () {
describe('numbered array notation', function () {
it('parses quoted', function () {
expect(pathNodeDesc('["foo"]')).toContainMatches({name:'foo'});
});
it('parses quoted and capturing', function () {
expect(pathNodeDesc('$["foo"]')).toContainMatches({name:'foo', capturing:true});
});
it('parses quoted with field list', function () {
expect(pathNodeDesc('["foo"]{a b c}')).toContainMatches({name:'foo', fieldList:'a b c'});
});
it('parses quoted with field list and capturing', function () {
expect(pathNodeDesc('$["foo"]{a b c}')).toContainMatches({name:'foo', capturing:true, fieldList:'a b c'});
});
it('ignores without a path name', function () {
expect(pathNodeDesc('[]')).toNotMatch();
});
it('fails with too many quotes', function () {
expect(pathNodeDesc('["""]')).toNotMatch();
});
it('parses unquoted', function () {
expect(pathNodeDesc('[foo]')).toNotMatch();
});
it('ignores unnamed because of an empty string', function () {
expect(pathNodeDesc('[""]')).toNotMatch();
});
it('parses first token only', function () {
expect(pathNodeDesc('["foo"]["bar"]')).toContainMatches({name:'foo'});
});
it('allows dot char inside quotes that would otherwise have a special meaning', function () {
expect(pathNodeDesc('[".foo"]')).toContainMatches({name:'.foo'});
});
it('allows star char inside quotes that would otherwise have a special meaning', function () {
expect(pathNodeDesc('["*"]')).toContainMatches({name:'*'});
});
it('allows dollar char inside quotes that would otherwise have a special meaning', function () {
expect(pathNodeDesc('["$"]')).toContainMatches({name:'$'});
});
it('allows underscore in quotes', function () {
expect(pathNodeDesc('["foo_bar"]')).toContainMatches({name:'foo_bar'});
});
it('allows non-ASCII chars in quotes', function () {
expect(pathNodeDesc('["你好"]')).toContainMatches({name:'你好'});
});
});
it('parses single digit', function () {
expect(pathNodeDesc('[2]')).toContainMatches({name:'2'})
})
it('parses multiple digits', function () {
expect(pathNodeDesc('[123]')).toContainMatches({name:'123'})
})
it('parses with capture flag', function () {
expect(pathNodeDesc('$[2]')).toContainMatches({name:'2', capturing:true})
})
it('parses with field list', function () {
expect(pathNodeDesc('[2]{a b c}')).toContainMatches({name:'2', fieldList:'a b c'})
})
it('parses with field list and capture', function () {
expect(pathNodeDesc('$[2]{a b c}')).toContainMatches({name:'2', capturing:true, fieldList:'a b c'})
})
it('ignores without a name', function () {
expect(pathNodeDesc('[]')).toNotMatch()
})
it('ignores empty string as a name', function () {
expect(pathNodeDesc('[""]')).toNotMatch()
})
})
beforeEach(function () {
this.addMatchers({
toContainMatches:function (expectedResults) {
describe('numbered array notation', function () {
var foundResults = this.actual;
it('parses single digit', function () {
expect(pathNodeDesc('[2]')).toContainMatches({name:'2'});
});
it('parses multiple digits', function () {
expect(pathNodeDesc('[123]')).toContainMatches({name:'123'});
});
it('parses with capture flag', function () {
expect(pathNodeDesc('$[2]')).toContainMatches({name:'2', capturing:true});
});
it('parses with field list', function () {
expect(pathNodeDesc('[2]{a b c}')).toContainMatches({name:'2', fieldList:'a b c'});
});
it('parses with field list and capture', function () {
expect(pathNodeDesc('$[2]{a b c}')).toContainMatches({name:'2', capturing:true, fieldList:'a b c'});
});
it('ignores without a name', function () {
expect(pathNodeDesc('[]')).toNotMatch();
});
it('ignores empty string as a name', function () {
expect(pathNodeDesc('[""]')).toNotMatch();
});
});
if (expectedResults && !foundResults) {
if (!expectedResults.capturing && !expectedResults.name && !expectedResults.fieldList) {
return true; // wasn't expecting to find anything
}
beforeEach(function () {
jasmine.addMatchers({
toContainMatches: function() {
return {
compare: function (actual, expectedResults) {
var result = {};
var foundResults = actual;
this.message = function () {
return 'did not find anything'
};
return false;
}
if (expectedResults && !foundResults) {
if (!expectedResults.capturing && !expectedResults.name && !expectedResults.fieldList) {
result.pass = true; // wasn't expecting to find anything
}
if ((!!foundResults[1] ) != (!!expectedResults.capturing)) {
return false
}
if ((foundResults[2] ) != (expectedResults.name || '')) {
return false
}
if ((foundResults[3] || '') != (expectedResults.fieldList || '')) {
return false
}
result.message = 'did not find anything';
result.pass = false;
return true;
}, toNotMatch:function () {
} else if ((!!foundResults[1]) != (!!expectedResults.capturing)) {
result.pass = false;
} else if ((foundResults[2]) != (expectedResults.name || '')) {
result.pass = false;
} else if ((foundResults[3] || '') != (expectedResults.fieldList || '')) {
result.pass = false;
} else {
result.pass = true;
}
var foundResults = this.actual;
return result;
}
};
},
toNotMatch: function () {
return {
compare: function(actual) {
var foundResults = this.actual;
return !foundResults;
return {
pass: !foundResults
};
}
});
});
});
});
};
}
});
});
});
});
describe("Lists", function(){
"use strict";
var listA = cons('a', emptyList);
var listBA = cons('b', listA);
var listCBA = cons('c', listBA);
it("can use cons, head and tail", function() {
expect(head(listCBA)).toBe('c');
expect(head(tail(listCBA))).toBe('b');
expect(head(tail(tail(listCBA)))).toBe('a');
});
"use strict";
/*
While desirable, freezing has been disabled because it
var listA = cons('a', emptyList);
var listBA = cons('b', listA);
var listCBA = cons('c', listBA);
it("can use cons, head and tail", function() {
expect(head(listCBA)).toBe('c');
expect(head(tail(listCBA))).toBe('b');
expect(head(tail(tail(listCBA)))).toBe('a');
});
/*
While desirable, freezing has been disabled because it
runs too slowly on current JS engines.
if( !Platform.isInternetExplorer || Platform.isInternetExplorer >= 9 ) {
it("freezes lists on supporting browsers", function() {
expect( listA ).toBeFrozen();
expect( listBA ).toBeFrozen();
expect( listCBA ).toBeFrozen();
})
} else {
console.warn('platform does not support object freezing');
it("freezes lists on supporting browsers", function() {
expect( listA ).toBeFrozen();
expect( listBA ).toBeFrozen();
expect( listCBA ).toBeFrozen();
})
} else {
console.warn('platform does not support object freezing');
}
*/
it("can convert to an array", function() {
var listCBA = cons('c', cons('b', cons('a', emptyList)));
expect( listAsArray(listCBA) ).toEqual( ['c','b','a'] );
});
it("can convert empty list to an array", function(){
expect( listAsArray(emptyList) ).toEqual([]);
});
it("can reverse the order of a list", function() {
var listCBA = cons('c', cons('b', cons('a', emptyList)));
expect( listAsArray(reverseList(listCBA)) ).toEqual( ['a','b','c'] );
});
it("can map over a list", function() {
var naturals = cons(1, cons(2, cons(3, emptyList)));
var evens = cons(2, cons(4, cons(6, emptyList)));
function doubleit(n){return n * 2}
expect( map(doubleit, naturals) ).toEqual( evens );
});
describe('without', function(){
function equalTo(val){
return function(candidate){
return candidate == val;
}
it("can convert to an array", function() {
var listCBA = cons('c', cons('b', cons('a', emptyList)));
expect( listAsArray(listCBA) ).toEqual( ['c','b','a'] );
});
it("can convert empty list to an array", function(){
expect( listAsArray(emptyList) ).toEqual([]);
});
it("can reverse the order of a list", function() {
var listCBA = cons('c', cons('b', cons('a', emptyList)));
expect( listAsArray(reverseList(listCBA)) ).toEqual( ['a','b','c'] );
});
it("can map over a list", function() {
var naturals = cons(1, cons(2, cons(3, emptyList)));
var evens = cons(2, cons(4, cons(6, emptyList)));
function doubleit(n){return n * 2}
expect( map(doubleit, naturals) ).toEqual( evens );
});
describe('without', function(){
function equalTo(val){
return function(candidate){
return candidate == val;
}
it("can remove from the middle of a list", function() {
var naturals = list(1, 2, 3);
expect( without(naturals, equalTo(2)) ).toEqual( list(1, 3) );
});
it("can remove from the end of a list", function() {
var naturals = list(1, 2, 3);
expect( without(naturals, equalTo(3)) ).toEqual( list(1, 2) );
});
it("can not remove", function() {
var naturals = list(1, 2, 3);
expect( without(naturals, equalTo(4)) ).toEqual( list(1, 2, 3) );
});
it("works with the empty list", function() {
expect( without(emptyList, equalTo(4)) ).toEqual( emptyList );
});
it("can give removed item to a callback", function() {
var callback = jasmine.createSpy();
var naturals = list(1, 2, 3);
without(naturals, equalTo(2), callback)
expect( callback ).toHaveBeenCalledWith( 2 );
});
});
it("can convert non empty list to an array", function(){
expect( listAsArray( list('a','b','c') ) ).toEqual( ['a','b','c'] );
});
it("can convert empty array to list", function() {
expect( listAsArray( arrayAsList([]) ) ).toEqual( [] );
});
it("can assert every xitem in a list holds for a given test", function(){
var l = list(1,2,3,4,5,6,7,8,9,10);
function isANumber(n) {
return typeof n == 'number';
}
it("can remove from the middle of a list", function() {
var naturals = list(1, 2, 3);
expect( without(naturals, equalTo(2)) ).toEqual( list(1, 3) );
});
it("can remove from the end of a list", function() {
var naturals = list(1, 2, 3);
expect( without(naturals, equalTo(3)) ).toEqual( list(1, 2) );
});
it("can not remove", function() {
var naturals = list(1, 2, 3);
expect( without(naturals, equalTo(4)) ).toEqual( list(1, 2, 3) );
});
it("works with the empty list", function() {
expect( without(emptyList, equalTo(4)) ).toEqual( emptyList );
});
it("can give removed item to a callback", function() {
var callback = jasmine.createSpy();
var naturals = list(1, 2, 3);
without(naturals, equalTo(2), callback)
expect( callback ).toHaveBeenCalledWith( 2 );
});
});
it("can convert non empty list to an array", function(){
expect( listAsArray( list('a','b','c') ) ).toEqual( ['a','b','c'] );
});
it("can convert empty array to list", function() {
expect( listAsArray( arrayAsList([]) ) ).toEqual( [] );
});
it("can assert every xitem in a list holds for a given test", function(){
var l = list(1,2,3,4,5,6,7,8,9,10);
function isANumber(n) {
return typeof n == 'number';
}
function isOdd(n) {
return n % 2 == 0;
}
function isLessThanTen(n) {
return n < 10;
}
function isLessThanOrEqualToTen(n) {
return n <= 10;
}
expect( all(isANumber, l )).toBe(true);
expect( all(isOdd, l )).toBe(false);
expect( all(isLessThanTen, l )).toBe(false);
expect( all(isLessThanOrEqualToTen, l )).toBe(true);
});
describe('foldR', function(){
it("can fold list where order doesnt matter", function(){
function add(n, m){ return n+m }
var sum = foldR(add, 0, list(1,2,3,4));
expect( sum ).toEqual( 1 + 2 + 3 + 4 );
});
it("can fold list where start value matters", function(){
function divide(n, m){ return n / m }
var result = foldR(divide, 100, list(2, 2));
expect( result ).toBe( 25 ); // (100/2) / 2 = 25
});
it("can fold list in the correct order", function(){
function functionString(param, fnName){ return fnName + '(' + param + ')' }
var functionStringResult = foldR(functionString, 'x', list('a', 'b', 'c'));
// if order were wrong, might give c(b(a(x)))
expect( functionStringResult ).toEqual( 'a(b(c(x)))' );
});
});
describe('foldR1', function(){
it("can fold list where order doesnt matter", function(){
function add(n, m){ return n+m }
var sum = foldR1(add, list(1,2,3,4));
expect( sum ).toEqual( 1 + 2 + 3 + 4 );
});
it("can fold list in the correct order", function(){
function functionString(param, fnName){ return fnName + '(' + param + ')' }
var functionStringResult = foldR1(functionString, list('a', 'b', 'c'));
// if order were wrong, might give c(b(a))
expect( functionStringResult ).toEqual( 'a(b(c))' );
});
});
it('may apply a function with side effects to each item of a list', function(){
var callback1 = jasmine.createSpy(),
callback2 = jasmine.createSpy(),
callback3 = jasmine.createSpy(),
callback4 = jasmine.createSpy(),
functions = list(callback1, callback2, callback3, callback4);
applyEach( functions, ['a','b','c','d'] );
expect(callback1).toHaveBeenCalledWith('a','b','c','d');
expect(callback2).toHaveBeenCalledWith('a','b','c','d');
expect(callback3).toHaveBeenCalledWith('a','b','c','d');
expect(callback4).toHaveBeenCalledWith('a','b','c','d');
});
it('may apply a list of zero functions with side effects', function(){
expect(function(){
applyEach( list(), ['a','b','c','d'] );
}).not.toThrow();
});
beforeEach(function(){
jasmine.addMatchers({
toBeFrozen: function(){
return {
compare: function(actual) {
var obj = actual;
return {
pass: Object.isFrozen(obj)
};
}
};
}
function isOdd(n) {
return n % 2 == 0;
}
function isLessThanTen(n) {
return n < 10;
}
function isLessThanOrEqualToTen(n) {
return n <= 10;
}
expect( all(isANumber, l )).toBe(true);
expect( all(isOdd, l )).toBe(false);
expect( all(isLessThanTen, l )).toBe(false);
expect( all(isLessThanOrEqualToTen, l )).toBe(true);
});
describe('foldR', function(){
it("can fold list where order doesnt matter", function(){
function add(n, m){ return n+m }
var sum = foldR(add, 0, list(1,2,3,4));
expect( sum ).toEqual( 1 + 2 + 3 + 4 );
});
it("can fold list where start value matters", function(){
function divide(n, m){ return n / m }
var result = foldR(divide, 100, list(2, 2));
expect( result ).toBe( 25 ); // (100/2) / 2 = 25
});
it("can fold list in the correct order", function(){
function functionString(param, fnName){ return fnName + '(' + param + ')' }
var functionStringResult = foldR(functionString, 'x', list('a', 'b', 'c'));
// if order were wrong, might give c(b(a(x)))
expect( functionStringResult ).toEqual( 'a(b(c(x)))' );
});
});
describe('foldR1', function(){
it("can fold list where order doesnt matter", function(){
function add(n, m){ return n+m }
var sum = foldR1(add, list(1,2,3,4));
expect( sum ).toEqual( 1 + 2 + 3 + 4 );
});
it("can fold list in the correct order", function(){
function functionString(param, fnName){ return fnName + '(' + param + ')' }
var functionStringResult = foldR1(functionString, list('a', 'b', 'c'));
// if order were wrong, might give c(b(a))
expect( functionStringResult ).toEqual( 'a(b(c))' );
});
});
it('may apply a function with side effects to each item of a list', function(){
var callback1 = jasmine.createSpy(),
callback2 = jasmine.createSpy(),
callback3 = jasmine.createSpy(),
callback4 = jasmine.createSpy(),
functions = list(callback1, callback2, callback3, callback4);
applyEach( functions, ['a','b','c','d'] );
expect(callback1).toHaveBeenCalledWith('a','b','c','d');
expect(callback2).toHaveBeenCalledWith('a','b','c','d');
expect(callback3).toHaveBeenCalledWith('a','b','c','d');
expect(callback4).toHaveBeenCalledWith('a','b','c','d');
});
it('may apply a list of zero functions with side effects', function(){
expect(function(){
applyEach( list(), ['a','b','c','d'] );
}).not.toThrow();
});
beforeEach(function(){
this.addMatchers({
toBeFrozen: function(){
var obj = this.actual;
return Object.isFrozen(obj);
}
});
});
});
});
});

@@ -1,86 +0,131 @@

describe("oboe component (no http, content fed in externally)", function(){
(function(Platform) {
describe("oboe component (no http, content fed in externally)", function(){
var path;
var givenAnOboeInstance;
var oboe;
var asserter;
var matched;
var wasGivenTheOboeAsContext;
var hasRootJson;
var calledCallbackOnce;
var gaveFinalCallbackWithRootJson;
var foundOneMatch;
var wasPassedAnErrorObject;
var foundNMatches;
var foundNoMatches;
/*
a more jasmine-y version of the next test might look like this:
// Used to spy on global functions like setTimeout
var globalContext;
if ( !Platform.isNode ) {
globalContext = window;
givenAnOboeInstance = window.givenAnOboeInstance;
oboe = window.oboe;
matched = window.matched;
wasGivenTheOboeAsContext = window.wasGivenTheOboeAsContext;
hasRootJson = window.hasRootJson;
calledCallbackOnce = window.calledCallbackOnce;
gaveFinalCallbackWithRootJson = window.gaveFinalCallbackWithRootJson;
foundOneMatch = window.foundOneMatch;
wasPassedAnErrorObject = window.wasPassedAnErrorObject;
foundNMatches = window.foundNMatches;
foundNoMatches = window.foundNoMatches;
} else {
globalContext = GLOBAL;
oboe = require('../../dist/oboe-node.js');
asserter = require('../libs/oboeAsserter.js');
givenAnOboeInstance = asserter.givenAnOboeInstance;
matched = asserter.matched;
wasGivenTheOboeAsContext = asserter.wasGivenTheOboeAsContext;
hasRootJson = asserter.hasRootJson;
calledCallbackOnce = asserter.calledCallbackOnce;
gaveFinalCallbackWithRootJson = asserter.gaveFinalCallbackWithRootJson;
foundOneMatch = asserter.foundOneMatch;
wasPassedAnErrorObject = asserter.wasPassedAnErrorObject;
foundNMatches = asserter.foundNMatches;
foundNoMatches = asserter.foundNoMatches;
}
describe('empty object detected with bang', function() {
/*
a more jasmine-y version of the next test might look like this:
var callback = jasmine.createSpy('callback');
var oboe = anOboe().node('!', callback).afterInput('{}')
giveInput(oboe, '{}');
describe('empty object detected with bang', function() {
it( 'should find the empty object at root', function(){
var callback = jasmine.createSpy('callback');
var oboe = anOboe().node('!', callback).afterInput('{}')
giveInput(oboe, '{}');
expect(
it( 'should find the empty object at root', function(){
nodesFoundBy(
anOboe().listeningForNodesAt('!').afterInput('{}')
)
expect(
).toIncludeNode( {}, atRoot )
})
nodesFoundBy(
anOboe().listeningForNodesAt('!').afterInput('{}')
)
it( 'should not find anything else', function(){
).toIncludeNode( {}, atRoot )
})
expect(
it( 'should not find anything else', function(){
nodesFoundBy(
anOboe().listeningForNodes('!').afterInput('{}')
).length
expect(
).toBe(1)
})
nodesFoundBy(
anOboe().listeningForNodes('!').afterInput('{}')
).length
})*/
).toBe(1)
})
it('handles empty object detected with bang', function() {
})*/
it('handles empty object detected with bang', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
.andWeAreListeningForNodes('!')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
it('handles empty object detected with bang when explicitly selected', function() {
it('handles empty object detected with bang when explicitly selected', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('$!')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
.andWeAreListeningForNodes('$!')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
})
it('gives the oboe instance as context', function() {
it('gives the oboe instance as context', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!')
.whenGivenInput('{}')
.thenTheInstance( wasGivenTheOboeAsContext() );
})
.andWeAreListeningForNodes('!')
.whenGivenInput('{}')
.thenTheInstance( wasGivenTheOboeAsContext() );
})
it('find only emits when has whole object', function() {
it('find only emits when has whole object', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!')
.whenGivenInputPart('{')
.thenTheInstance(
foundNoMatches
)
.whenGivenInput('}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
.andWeAreListeningForNodes('!')
.whenGivenInputPart('{')
.thenTheInstance(
foundNoMatches
)
.whenGivenInput('}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
})
it('emits path to listener when root object starts', function() {
it('emits path to listener when root object starts', function() {

@@ -92,11 +137,11 @@ // clarinet doesn't notify of matches to objects (SAX_OPEN_OBJECT) until the

givenAnOboeInstance()
.andWeAreListeningForPaths('!')
.whenGivenInputPart('{"foo":')
.thenTheInstance(
foundNMatches(1),
matched({}).atRootOfJson()
);
})
.andWeAreListeningForPaths('!')
.whenGivenInputPart('{"foo":')
.thenTheInstance(
foundNMatches(1),
matched({}).atRootOfJson()
);
})
it('emits path to listener when root array starts', function() {
it('emits path to listener when root array starts', function() {

@@ -108,111 +153,111 @@ // clarinet doesn't notify of matches to objects (SAX_OPEN_OBJECT) until the

givenAnOboeInstance()
.andWeAreListeningForPaths('!')
.whenGivenInputPart('[1') // the minimum string required for clarinet
// to emit SAX_OPEN_ARRAY. Won't emit with '['.
.thenTheInstance(
foundNMatches(1),
matched([]).atRootOfJson()
);
})
.andWeAreListeningForPaths('!')
.whenGivenInputPart('[1') // the minimum string required for clarinet
// to emit SAX_OPEN_ARRAY. Won't emit with '['.
.thenTheInstance(
foundNMatches(1),
matched([]).atRootOfJson()
);
})
it('emits empty object node detected with single star', function() {
it('emits empty object node detected with single star', function() {
// *
givenAnOboeInstance()
.andWeAreListeningForNodes('*')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
.andWeAreListeningForNodes('*')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
it('doesnt detect spurious path off empty object', function() {
it('doesnt detect spurious path off empty object', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.foo.*')
.whenGivenInput( {foo:{}} )
.thenTheInstance(
foundNoMatches
);
})
.andWeAreListeningForPaths('!.foo.*')
.whenGivenInput( {foo:{}} )
.thenTheInstance(
foundNoMatches
);
})
it('handles empty object detected with double dot', function() {
it('handles empty object detected with double dot', function() {
// *
givenAnOboeInstance()
.andWeAreListeningForNodes('*')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
.andWeAreListeningForNodes('*')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
it('notifies of strings when listened to', function() {
it('notifies of strings when listened to', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.string')
.whenGivenInput('{"string":"s"}')
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
.andWeAreListeningForNodes('!.string')
.whenGivenInput('{"string":"s"}')
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with hyphen in the name', function() {
it('can detect nodes with hyphen in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.a-string')
.whenGivenInput({"a-string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
.andWeAreListeningForNodes('!.a-string')
.whenGivenInput({"a-string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with underscore in the name', function() {
it('can detect nodes with underscore in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.a_string')
.whenGivenInput({"a_string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
.andWeAreListeningForNodes('!.a_string')
.whenGivenInput({"a_string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with quoted hyphen in the name', function() {
it('can detect nodes with quoted hyphen in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!["a-string"]')
.whenGivenInput({"a-string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
.andWeAreListeningForNodes('!["a-string"]')
.whenGivenInput({"a-string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with quoted underscore in the name', function() {
it('can detect nodes with quoted underscore in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!["a_string"]')
.whenGivenInput({"a_string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
.andWeAreListeningForNodes('!["a_string"]')
.whenGivenInput({"a_string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with quoted unusual ascii chars in the name', function() {
it('can detect nodes with quoted unusual ascii chars in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!["£@$%^"]')
.whenGivenInput({"£@$%^":"s"}) // ridiculous JSON!
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
.andWeAreListeningForNodes('!["£@$%^"]')
.whenGivenInput({"£@$%^":"s"}) // ridiculous JSON!
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with non-ascii keys', function() {
it('can detect nodes with non-ascii keys', function() {

@@ -222,11 +267,11 @@ //pinyin: Wǒ tǎoyàn IE liúlǎn qì!

givenAnOboeInstance()
.andWeAreListeningForNodes('!["我讨厌IE浏览器!"]')
.whenGivenInput({"我讨厌IE浏览器!":"indeed!"}) // ridiculous JSON!
.thenTheInstance(
matched("indeed!"),
foundOneMatch
);
})
.andWeAreListeningForNodes('!["我讨厌IE浏览器!"]')
.whenGivenInput({"我讨厌IE浏览器!":"indeed!"}) // ridiculous JSON!
.thenTheInstance(
matched("indeed!"),
foundOneMatch
);
})
it('can detect nodes with non-ascii keys and values', function() {
it('can detect nodes with non-ascii keys and values', function() {

@@ -236,148 +281,148 @@ // hope you have a good unicode font!

givenAnOboeInstance()
.andWeAreListeningForNodes('!["☂"]')
.whenGivenInput({"☂":"☁"}) // ridiculous JSON!
.thenTheInstance(
matched("☁"),
foundOneMatch
);
})
.andWeAreListeningForNodes('!["☂"]')
.whenGivenInput({"☂":"☁"}) // ridiculous JSON!
.thenTheInstance(
matched("☁"),
foundOneMatch
);
})
it('notifies of path before given the json value for a property', function() {
it('notifies of path before given the json value for a property', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.string')
.whenGivenInputPart('{"string":')
.thenTheInstance(
foundOneMatch
);
})
.andWeAreListeningForPaths('!.string')
.whenGivenInputPart('{"string":')
.thenTheInstance(
foundOneMatch
);
})
it('notifies of second property name with incomplete json', function() {
it('notifies of second property name with incomplete json', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.pencils')
.whenGivenInputPart('{"pens":4, "pencils":')
.thenTheInstance(
// undefined because the parser hasn't been given the value yet.
// can't be null because that is an allowed value
matched(undefined).atPath(['pencils']),
foundOneMatch
);
})
.andWeAreListeningForPaths('!.pencils')
.whenGivenInputPart('{"pens":4, "pencils":')
.thenTheInstance(
// undefined because the parser hasn't been given the value yet.
// can't be null because that is an allowed value
matched(undefined).atPath(['pencils']),
foundOneMatch
);
})
it('is able to notify of null', function() {
it('is able to notify of null', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.pencils')
.whenGivenInput('{"pens":4, "pencils":null}')
.thenTheInstance(
// undefined because the parser hasn't been given the value yet.
// can't be null because that is an allowed value
matched(null).atPath(['pencils']),
foundOneMatch
);
})
.andWeAreListeningForNodes('!.pencils')
.whenGivenInput('{"pens":4, "pencils":null}')
.thenTheInstance(
// undefined because the parser hasn't been given the value yet.
// can't be null because that is an allowed value
matched(null).atPath(['pencils']),
foundOneMatch
);
})
it('is able to notify of boolean true', function() {
it('is able to notify of boolean true', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.pencils')
.whenGivenInput('{"pens":false, "pencils":true}')
.thenTheInstance(
// undefined because the parser hasn't been given the value yet.
// can't be null because that is an allowed value
matched(true).atPath(['pencils']),
foundOneMatch
);
})
.andWeAreListeningForNodes('!.pencils')
.whenGivenInput('{"pens":false, "pencils":true}')
.thenTheInstance(
// undefined because the parser hasn't been given the value yet.
// can't be null because that is an allowed value
matched(true).atPath(['pencils']),
foundOneMatch
);
})
it('is able to notify of boolean false', function() {
it('is able to notify of boolean false', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.pens')
.whenGivenInput('{"pens":false, "pencils":true}')
.thenTheInstance(
// undefined because the parser hasn't been given the value yet.
// can't be null because that is an allowed value
matched(false).atPath(['pens']),
foundOneMatch
);
})
.andWeAreListeningForNodes('!.pens')
.whenGivenInput('{"pens":false, "pencils":true}')
.thenTheInstance(
// undefined because the parser hasn't been given the value yet.
// can't be null because that is an allowed value
matched(false).atPath(['pens']),
foundOneMatch
);
})
it('notifies of multiple children of root', function() {
it('notifies of multiple children of root', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput('{"a":"A","b":"B","c":"C"}')
.thenTheInstance(
matched('A').atPath(['a'])
, matched('B').atPath(['b'])
, matched('C').atPath(['c'])
, foundNMatches(3)
);
})
.andWeAreListeningForNodes('!.*')
.whenGivenInput('{"a":"A","b":"B","c":"C"}')
.thenTheInstance(
matched('A').atPath(['a'])
, matched('B').atPath(['b'])
, matched('C').atPath(['c'])
, foundNMatches(3)
);
})
it('notifies of multiple children of root when selecting the root', function() {
it('notifies of multiple children of root when selecting the root', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('$!.*')
.whenGivenInput({"a":"A", "b":"B", "c":"C"})
.thenTheInstance(
// rather than getting the fully formed objects, we should now see the root object
// being grown step by step:
matched({"a":"A"})
, matched({"a":"A", "b":"B"})
, matched({"a":"A", "b":"B", "c":"C"})
, foundNMatches(3)
);
})
.andWeAreListeningForNodes('$!.*')
.whenGivenInput({"a":"A", "b":"B", "c":"C"})
.thenTheInstance(
// rather than getting the fully formed objects, we should now see the root object
// being grown step by step:
matched({"a":"A"})
, matched({"a":"A", "b":"B"})
, matched({"a":"A", "b":"B", "c":"C"})
, foundNMatches(3)
);
})
it('does not notify spuriously of descendant of roots when key is actually in another object', function() {
it('does not notify spuriously of descendant of roots when key is actually in another object', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.a')
.whenGivenInput([{a:'a'}])
.thenTheInstance(foundNoMatches);
})
.andWeAreListeningForPaths('!.a')
.whenGivenInput([{a:'a'}])
.thenTheInstance(foundNoMatches);
})
it('does not notify spuriously of found child of root when ndoe is not child of root', function() {
it('does not notify spuriously of found child of root when ndoe is not child of root', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.a')
.whenGivenInput([{a:'a'}])
.thenTheInstance(foundNoMatches);
})
.andWeAreListeningForNodes('!.a')
.whenGivenInput([{a:'a'}])
.thenTheInstance(foundNoMatches);
})
describe('progressive output', function(){
describe('progressive output', function(){
describe('giving notification of multiple properties of an object without waiting for entire object', function(){
it( 'gives no notification on seeing just the key', function(){
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInputPart('{"a":')
.thenTheInstance(
foundNoMatches
)
});
it( 'gives no notification on seeing just the key', function(){
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInputPart('{"a":')
.thenTheInstance(
foundNoMatches
)
});
it( 'gives one notification on seeing just the first value', function(){
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInputPart('{"a":"A",')
.thenTheInstance(
matched('A').atPath(['a'])
, foundOneMatch
)
});
it( 'gives one notification on seeing just the first value', function(){
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInputPart('{"a":"A",')
.thenTheInstance(
matched('A').atPath(['a'])
, foundOneMatch
)
});
it( 'gives another notification on seeing just the second key/value', function(){
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput('{"a":"A","b":"B"}')
.thenTheInstance(
matched('B').atPath(['b'])
, foundNMatches(2)
);
});
it( 'gives another notification on seeing just the second key/value', function(){
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput('{"a":"A","b":"B"}')
.thenTheInstance(
matched('B').atPath(['b'])
, foundNMatches(2)
);
});
})

@@ -387,42 +432,42 @@

it('can supply root after seeing first key with undefined value', function(){
givenAnOboeInstance()
.whenGivenInputPart('{"a":')
.thenTheInstance(
hasRootJson({a:undefined})
);
});
it('can supply root after seeing first key with undefined value', function(){
givenAnOboeInstance()
.whenGivenInputPart('{"a":')
.thenTheInstance(
hasRootJson({a:undefined})
);
});
it('can supply root after seeing first key/value with defined value', function(){
givenAnOboeInstance()
.whenGivenInputPart('{"a":"A",')
.thenTheInstance(
hasRootJson({a:'A'})
)
});
it('can supply root after seeing first key/value with defined value', function(){
givenAnOboeInstance()
.whenGivenInputPart('{"a":"A",')
.thenTheInstance(
hasRootJson({a:'A'})
)
});
it('gives second key with undefined value', function(){
givenAnOboeInstance()
.whenGivenInputPart('{"a":"A","b":')
.thenTheInstance(
hasRootJson({a:'A', b:undefined})
)
});
it('gives second key with undefined value', function(){
givenAnOboeInstance()
.whenGivenInputPart('{"a":"A","b":')
.thenTheInstance(
hasRootJson({a:'A', b:undefined})
)
});
it('gives second key with defined value', function(){
givenAnOboeInstance()
.whenGivenInput('{"a":"A","b":"B"}')
.thenTheInstance(
hasRootJson({a:'A', b:'B'})
)
});
it('gives second key with defined value', function(){
givenAnOboeInstance()
.whenGivenInput('{"a":"A","b":"B"}')
.thenTheInstance(
hasRootJson({a:'A', b:'B'})
)
});
it('gives final callback when done', function(){
givenAnOboeInstance()
.whenGivenInput('{"a":"A","b":"B"}')
.whenInputFinishes()
.thenTheInstance(
gaveFinalCallbackWithRootJson({a:'A', b:'B'})
);
});
it('gives final callback when done', function(){
givenAnOboeInstance()
.whenGivenInput('{"a":"A","b":"B"}')
.whenInputFinishes()
.thenTheInstance(
gaveFinalCallbackWithRootJson({a:'A', b:'B'})
);
});
})

@@ -432,196 +477,196 @@

// let's feed it the array [11,22] in drips of one or two chars at a time:
it('has nothing on array open', function(){
givenAnOboeInstance()
.whenGivenInputPart('[')
.thenTheInstance(
// I would like this to be [] but clarinet doesn't emit array found until it has seen
// the first element
hasRootJson(undefined)
)
});
it('has empty array soon afterwards', function(){
givenAnOboeInstance()
.whenGivenInputPart('[1')
.thenTheInstance(
// since we haven't seen a comma yet, the 1 could be the start of a multi-digit number
// so nothing can be added to the root json
hasRootJson([])
)
});
it('has the first element on seeing the comma', function(){
givenAnOboeInstance()
.whenGivenInputPart('[11,')
.thenTheInstance(
hasRootJson([11])
)
});
it('has no more on seeing the start of the next element', function(){
givenAnOboeInstance()
.whenGivenInputPart('[11,2')
.thenTheInstance(
hasRootJson([11])
)
});
it('has everything when the array closes', function(){
givenAnOboeInstance()
.whenGivenInput('[11,22]')
.thenTheInstance(
hasRootJson([11,22])
)
});
it('notified correctly of the final root', function(){
givenAnOboeInstance()
.whenGivenInput('[11,22]')
.whenInputFinishes()
.thenTheInstance(
gaveFinalCallbackWithRootJson([11,22])
)
});
// let's feed it the array [11,22] in drips of one or two chars at a time:
it('has nothing on array open', function(){
givenAnOboeInstance()
.whenGivenInputPart('[')
.thenTheInstance(
// I would like this to be [] but clarinet doesn't emit array found until it has seen
// the first element
hasRootJson(undefined)
)
});
it('has empty array soon afterwards', function(){
givenAnOboeInstance()
.whenGivenInputPart('[1')
.thenTheInstance(
// since we haven't seen a comma yet, the 1 could be the start of a multi-digit number
// so nothing can be added to the root json
hasRootJson([])
)
});
it('has the first element on seeing the comma', function(){
givenAnOboeInstance()
.whenGivenInputPart('[11,')
.thenTheInstance(
hasRootJson([11])
)
});
it('has no more on seeing the start of the next element', function(){
givenAnOboeInstance()
.whenGivenInputPart('[11,2')
.thenTheInstance(
hasRootJson([11])
)
});
it('has everything when the array closes', function(){
givenAnOboeInstance()
.whenGivenInput('[11,22]')
.thenTheInstance(
hasRootJson([11,22])
)
});
it('notified correctly of the final root', function(){
givenAnOboeInstance()
.whenGivenInput('[11,22]')
.whenInputFinishes()
.thenTheInstance(
gaveFinalCallbackWithRootJson([11,22])
)
});
})
});
});
it('notifies of named child of root', function() {
it('notifies of named child of root', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.b')
.whenGivenInput('{"a":"A","b":"B","c":"C"}')
.thenTheInstance(
matched('B').atPath(['b'])
, foundOneMatch
);
})
it('notifies of array elements', function() {
.andWeAreListeningForNodes('!.b')
.whenGivenInput('{"a":"A","b":"B","c":"C"}')
.thenTheInstance(
matched('B').atPath(['b'])
, foundOneMatch
);
})
it('notifies of array elements', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.testArray.*')
.whenGivenInput('{"testArray":["a","b","c"]}')
.thenTheInstance(
matched('a').atPath(['testArray',0])
, matched('b').atPath(['testArray',1])
, matched('c').atPath(['testArray',2])
, foundNMatches(3)
);
})
it('notifies of path match when array starts', function() {
.andWeAreListeningForNodes('!.testArray.*')
.whenGivenInput('{"testArray":["a","b","c"]}')
.thenTheInstance(
matched('a').atPath(['testArray',0])
, matched('b').atPath(['testArray',1])
, matched('c').atPath(['testArray',2])
, foundNMatches(3)
);
})
it('notifies of path match when array starts', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.testArray')
.whenGivenInputPart('{"testArray":["a"')
.thenTheInstance(
foundNMatches(1)
, matched(undefined) // when path is matched, it is not known yet
// that it contains an array. Null should not
// be used here because that is an allowed
// value in json
);
})
it('notifies of path match when second array starts', function() {
.andWeAreListeningForPaths('!.testArray')
.whenGivenInputPart('{"testArray":["a"')
.thenTheInstance(
foundNMatches(1)
, matched(undefined) // when path is matched, it is not known yet
// that it contains an array. Null should not
// be used here because that is an allowed
// value in json
);
})
it('notifies of path match when second array starts', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.array2')
.whenGivenInputPart('{"array1":["a","b"], "array2":["a"')
.thenTheInstance(
foundNMatches(1)
, matched(undefined) // when path is matched, it is not known yet
// that it contains an array. Null should not
// be used here because that is an allowed
// value in json
);
})
it('notifies of paths inside arrays', function() {
.andWeAreListeningForPaths('!.array2')
.whenGivenInputPart('{"array1":["a","b"], "array2":["a"')
.thenTheInstance(
foundNMatches(1)
, matched(undefined) // when path is matched, it is not known yet
// that it contains an array. Null should not
// be used here because that is an allowed
// value in json
);
})
it('notifies of paths inside arrays', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![*]')
.whenGivenInput( [{}, 'b', 2, []] )
.thenTheInstance(
foundNMatches(4)
);
})
.andWeAreListeningForPaths('![*]')
.whenGivenInput( [{}, 'b', 2, []] )
.thenTheInstance(
foundNMatches(4)
);
})
describe('correctly give index inside arrays', function(){
describe('correctly give index inside arrays', function(){
it('when finding objects in array', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( [{}, {}, 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( [{}, {}, 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
})
it('when finding arrays inside array', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( [[], [], 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( [[], [], 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
})
it('when finding arrays inside arrays etc', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![2][2]')
.whenGivenInput( [
[],
[],
[
[],
[],
['this_array']
]
] )
.thenTheInstance(
foundNMatches(1)
);
givenAnOboeInstance()
.andWeAreListeningForPaths('![2][2]')
.whenGivenInput( [
[],
[],
[
[],
[],
['this_array']
]
] )
.thenTheInstance(
foundNMatches(1)
);
})
it('when finding strings inside array', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( ['', '', 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( ['', '', 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
})
it('when finding numbers inside array', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( [1, 1, 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( [1, 1, 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
})
it('when finding nulls inside array', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( [null, null, 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
givenAnOboeInstance()
.andWeAreListeningForPaths('![2]')
.whenGivenInput( [null, null, 'this_one'] )
.thenTheInstance(
foundNMatches(1)
);
})
})
})
it('notifies of paths inside objects', function() {
it('notifies of paths inside objects', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![*]')
.whenGivenInput( {a:{}, b:'b', c:2, d:[]} )
.thenTheInstance(
foundNMatches(4)
);
})
.andWeAreListeningForPaths('![*]')
.whenGivenInput( {a:{}, b:'b', c:2, d:[]} )
.thenTheInstance(
foundNMatches(4)
);
})
describe('selecting by index', function(){
describe('selecting by index', function(){
it('notifies of array elements', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.testArray[2]')
.whenGivenInput('{"testArray":["a","b","this_one"]}')
.thenTheInstance(
matched('this_one').atPath(['testArray',2])
givenAnOboeInstance()
.andWeAreListeningForNodes('!.testArray[2]')
.whenGivenInput('{"testArray":["a","b","this_one"]}')
.thenTheInstance(
matched('this_one').atPath(['testArray',2])
, foundOneMatch
);
);
})

@@ -631,135 +676,135 @@

givenAnOboeInstance()
.andWeAreListeningForNodes('!.testArray[2][2]')
.whenGivenInput( {"testArray":
["a","b",
["x","y","this_one"]
]
}
)
.thenTheInstance(
matched('this_one')
.atPath(['testArray',2,2])
.withParent( ["x","y","this_one"] )
.withGrandparent( ["a","b", ["x","y","this_one"]] )
givenAnOboeInstance()
.andWeAreListeningForNodes('!.testArray[2][2]')
.whenGivenInput( {"testArray":
["a","b",
["x","y","this_one"]
]
}
)
.thenTheInstance(
matched('this_one')
.atPath(['testArray',2,2])
.withParent( ["x","y","this_one"] )
.withGrandparent( ["a","b", ["x","y","this_one"]] )
, foundOneMatch
);
);
})
it('can notify nested array elements by passing the root array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.$testArray[2][2]')
.whenGivenInput( {"testArray":
["a","b",
["x","y","this_one"]
]
}
)
.thenTheInstance(
matched( ["a","b",
["x","y","this_one"]
])
givenAnOboeInstance()
.andWeAreListeningForNodes('!.$testArray[2][2]')
.whenGivenInput( {"testArray":
["a","b",
["x","y","this_one"]
]
}
)
.thenTheInstance(
matched( ["a","b",
["x","y","this_one"]
])
, foundOneMatch
);
);
})
});
});
describe('deeply nested objects', function(){
describe('deeply nested objects', function(){
it('notifies with star pattern', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('*')
.whenGivenInput({"a":{"b":{"c":{"d":"e"}}}})
.thenTheInstance(
matched('e')
.atPath(['a', 'b', 'c', 'd'])
.withParent({d:'e'})
givenAnOboeInstance()
.andWeAreListeningForNodes('*')
.whenGivenInput({"a":{"b":{"c":{"d":"e"}}}})
.thenTheInstance(
matched('e')
.atPath(['a', 'b', 'c', 'd'])
.withParent({d:'e'})
, matched({d:"e"})
.atPath(['a', 'b', 'c'])
.atPath(['a', 'b', 'c'])
, matched({c:{d:"e"}})
.atPath(['a', 'b'])
.atPath(['a', 'b'])
, matched({b:{c:{d:"e"}}})
.atPath(['a'])
.atPath(['a'])
, matched({a:{b:{c:{d:"e"}}}})
.atRootOfJson()
.atRootOfJson()
, foundNMatches(5)
);
);
})
it('notifies of with double dot pattern', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('..')
.whenGivenInput({"a":{"b":{"c":{"d":"e"}}}})
.thenTheInstance(
matched('e')
.atPath(['a', 'b', 'c', 'd'])
.withParent({d:'e'})
givenAnOboeInstance()
.andWeAreListeningForNodes('..')
.whenGivenInput({"a":{"b":{"c":{"d":"e"}}}})
.thenTheInstance(
matched('e')
.atPath(['a', 'b', 'c', 'd'])
.withParent({d:'e'})
, matched({d:"e"})
.atPath(['a', 'b', 'c'])
.atPath(['a', 'b', 'c'])
, matched({c:{d:"e"}})
.atPath(['a', 'b'])
.atPath(['a', 'b'])
, matched({b:{c:{d:"e"}}})
.atPath(['a'])
.atPath(['a'])
, matched({a:{b:{c:{d:"e"}}}})
.atRootOfJson()
.atRootOfJson()
, foundNMatches(5)
);
);
})
it('notifies of objects with double dot star pattern', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('..*')
.whenGivenInput({"a":{"b":{"c":{"d":"e"}}}})
.thenTheInstance(
matched('e')
.atPath(['a', 'b', 'c', 'd'])
.withParent({d:'e'})
givenAnOboeInstance()
.andWeAreListeningForNodes('..*')
.whenGivenInput({"a":{"b":{"c":{"d":"e"}}}})
.thenTheInstance(
matched('e')
.atPath(['a', 'b', 'c', 'd'])
.withParent({d:'e'})
, matched({d:"e"})
.atPath(['a', 'b', 'c'])
.atPath(['a', 'b', 'c'])
, matched({c:{d:"e"}})
.atPath(['a', 'b'])
.atPath(['a', 'b'])
, matched({b:{c:{d:"e"}}})
.atPath(['a'])
.atPath(['a'])
, matched({a:{b:{c:{d:"e"}}}})
.atRootOfJson()
.atRootOfJson()
, foundNMatches(5)
);
);
})
});
});
it('can express all but root as a pattern', function() {
it('can express all but root as a pattern', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('*..*')
.whenGivenInput({"a":{"b":{"c":{"d":"e"}}}})
.thenTheInstance(
matched('e')
.atPath(['a', 'b', 'c', 'd'])
.withParent({d:'e'})
, matched({d:"e"})
.atPath(['a', 'b', 'c'])
, matched({c:{d:"e"}})
.atPath(['a', 'b'])
, matched({b:{c:{d:"e"}}})
.atPath(['a'])
.andWeAreListeningForNodes('*..*')
.whenGivenInput({"a":{"b":{"c":{"d":"e"}}}})
.thenTheInstance(
matched('e')
.atPath(['a', 'b', 'c', 'd'])
.withParent({d:'e'})
, matched({d:"e"})
.atPath(['a', 'b', 'c'])
, matched({c:{d:"e"}})
.atPath(['a', 'b'])
, matched({b:{c:{d:"e"}}})
.atPath(['a'])
, foundNMatches(4)
);
})
it('can detect similar ancestors', function() {
, foundNMatches(4)
);
})
it('can detect similar ancestors', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('foo..foo')
.andWeAreListeningForNodes('foo..foo')
.whenGivenInput({"foo":{"foo":{"foo":{"foo":"foo"}}}})
.thenTheInstance(
matched("foo")
, matched({"foo":"foo"})
, matched({"foo":{"foo":"foo"}})
, matched({"foo":{"foo":{"foo":"foo"}}})
, foundNMatches(4)
);
})
.whenGivenInput({"foo":{"foo":{"foo":{"foo":"foo"}}}})
.thenTheInstance(
matched("foo")
, matched({"foo":"foo"})
, matched({"foo":{"foo":"foo"}})
, matched({"foo":{"foo":{"foo":"foo"}}})
, foundNMatches(4)
);
})
it('can detect inside the second object element of an array', function() {
it('can detect inside the second object element of an array', function() {

@@ -771,151 +816,151 @@ // this fails in incrementalJsonBuilder if we don't set the curKey to the

givenAnOboeInstance()
.andWeAreListeningForNodes('!..find')
.whenGivenInput(
{
array:[
{a:'A'}
, {find:'should_find_this'}
]
}
)
.thenTheInstance(
matched('should_find_this')
.atPath(['array',1,'find'])
);
})
it('ignores keys if only start matches', function() {
.andWeAreListeningForNodes('!..find')
.whenGivenInput(
{
array:[
{a:'A'}
, {find:'should_find_this'}
]
}
)
.thenTheInstance(
matched('should_find_this')
.atPath(['array',1,'find'])
);
})
it('ignores keys if only start matches', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..a')
.whenGivenInput({
ab:'should_not_find_this'
, a0:'nor this'
, a:'but_should_find_this'
}
)
.thenTheInstance(
matched('but_should_find_this')
, foundOneMatch
);
})
it('ignores keys if only end of pattern matches', function() {
.andWeAreListeningForNodes('!..a')
.whenGivenInput({
ab:'should_not_find_this'
, a0:'nor this'
, a:'but_should_find_this'
}
)
.thenTheInstance(
matched('but_should_find_this')
, foundOneMatch
);
})
it('ignores keys if only end of pattern matches', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..a')
.whenGivenInput({
aa:'should_not_find_this'
, ba:'nor this'
, a:'but_should_find_this'
}
)
.thenTheInstance(
matched('but_should_find_this')
, foundOneMatch
);
})
it('ignores partial path matches in array indices', function() {
.andWeAreListeningForNodes('!..a')
.whenGivenInput({
aa:'should_not_find_this'
, ba:'nor this'
, a:'but_should_find_this'
}
)
.thenTheInstance(
matched('but_should_find_this')
, foundOneMatch
);
})
it('ignores partial path matches in array indices', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..[1]')
.whenGivenInput({
array : [0,1,2,3,4,5,6,7,8,9,10,11,12]
}
)
.thenTheInstance(
matched(1)
.withParent([0,1])
, foundOneMatch
);
})
it('can give an array back when just partially done', function() {
.andWeAreListeningForNodes('!..[1]')
.whenGivenInput({
array : [0,1,2,3,4,5,6,7,8,9,10,11,12]
}
)
.thenTheInstance(
matched(1)
.withParent([0,1])
, foundOneMatch
);
})
it('can give an array back when just partially done', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('$![5]')
.whenGivenInput([0,1,2,3,4,5,6,7,8,9,10,11,12])
.thenTheInstance(
matched([0,1,2,3,4,5])
, foundOneMatch
);
})
.andWeAreListeningForNodes('$![5]')
.whenGivenInput([0,1,2,3,4,5,6,7,8,9,10,11,12])
.thenTheInstance(
matched([0,1,2,3,4,5])
, foundOneMatch
);
})
describe('json arrays give correct parent and grandparent', function(){
describe('json arrays give correct parent and grandparent', function(){
it('gives parent and grandparent for every item of an array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : ['a','b','c']
}
)
.thenTheInstance(
matched('a')
.withParent(['a'])
.withGrandparent({array:['a']})
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : ['a','b','c']
}
)
.thenTheInstance(
matched('a')
.withParent(['a'])
.withGrandparent({array:['a']})
, matched('b')
.withParent(['a', 'b'])
.withGrandparent({array:['a','b']})
.withParent(['a', 'b'])
.withGrandparent({array:['a','b']})
, matched('c')
.withParent(['a', 'b', 'c'])
.withGrandparent({array:['a','b','c']})
);
.withParent(['a', 'b', 'c'])
.withGrandparent({array:['a','b','c']})
);
})
it('is correct for array of objects', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},{'b':2},{'c':3}]
}
)
.thenTheInstance(
matched({'a':1})
.withParent([{'a':1}])
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},{'b':2},{'c':3}]
}
)
.thenTheInstance(
matched({'a':1})
.withParent([{'a':1}])
, matched({'b':2})
.withParent([{'a':1},{'b':2}])
.withParent([{'a':1},{'b':2}])
, matched({'c':3})
.withParent([{'a':1},{'b':2},{'c':3}])
);
.withParent([{'a':1},{'b':2},{'c':3}])
);
})
it('is correct for object in a mixed array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
matched({'a':1})
.withParent([{'a':1}])
);
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
matched({'a':1})
.withParent([{'a':1}])
);
})
it('has correct parent for string in mixed array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
matched('b')
.withParent([{'a':1},'b'])
matched('b')
.withParent([{'a':1},'b'])
);
);
})
it('has correct parent for second object in mixed array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
matched({'c':3})
.withParent([{'a':1},'b',{'c':3}])
matched({'c':3})
.withParent([{'a':1},'b',{'c':3}])
);
);
})

@@ -925,14 +970,14 @@

givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
matched({})
.withParent([{'a':1},'b',{'c':3}, {}])
matched({})
.withParent([{'a':1},'b',{'c':3}, {}])
);
);

@@ -942,86 +987,86 @@ })

givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
matched(['d'])
.withParent([{'a':1},'b',{'c':3}, {}, ['d']])
matched(['d'])
.withParent([{'a':1},'b',{'c':3}, {}, ['d']])
);
);
})
it('gives correct parent for singleton string array in singleton array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [['d']]
}
)
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [['d']]
}
)
.thenTheInstance(
matched(['d'])
.withParent([['d']])
matched(['d'])
.withParent([['d']])
);
);
})
it('gives correct parent for last string in a mixed array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
matched('e')
.withParent([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
matched('e')
.withParent([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
);
);
})
it('gives correct parent for opening object in a mixed array at root of json', function() {
// same test as above but without the object wrapper around the array:
// same test as above but without the object wrapper around the array:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched({'a':1})
.withParent([{'a':1}])
matched({'a':1})
.withParent([{'a':1}])
);
);
})
it('gives correct parent for string in a mixed array at root of json', function() {
// same test as above but without the object wrapper around the array:
// same test as above but without the object wrapper around the array:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched('b')
.withParent([{'a':1},'b'])
matched('b')
.withParent([{'a':1},'b'])
);
);
})
it('gives correct parent for second object in a mixed array at root of json', function() {
// same test as above but without the object wrapper around the array:
// same test as above but without the object wrapper around the array:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched({'c':3})
.withParent([{'a':1},'b',{'c':3}])
matched({'c':3})
.withParent([{'a':1},'b',{'c':3}])
);
);
})

@@ -1031,13 +1076,13 @@

// same test as above but without the object wrapper around the array:
// same test as above but without the object wrapper around the array:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched({})
.withParent([{'a':1},'b',{'c':3}, {}])
matched({})
.withParent([{'a':1},'b',{'c':3}, {}])
);
);

@@ -1047,245 +1092,245 @@ })

it('gives correct parent for singleton string array in a mixed array at root of json', function() {
// same test as above but without the object wrapper around the array:
// same test as above but without the object wrapper around the array:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched(['d'])
.withParent([{'a':1},'b',{'c':3}, {}, ['d']])
matched(['d'])
.withParent([{'a':1},'b',{'c':3}, {}, ['d']])
);
);
})
it('gives correct parent for singleton string array in a singleton array at root of json', function() {
// non-mixed array, easier version:
// non-mixed array, easier version:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([['d']])
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([['d']])
.thenTheInstance(
matched(['d'])
.withParent([['d']])
matched(['d'])
.withParent([['d']])
);
);
})
it('gives correct parent for final string in a mixed array at root of json', function() {
// same test as above but without the object wrapper around the array:
// same test as above but without the object wrapper around the array:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched('e')
.withParent([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
);
matched('e')
.withParent([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
);
})
});
});
it('can detect at multiple depths using double dot', function() {
it('can detect at multiple depths using double dot', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..find')
.whenGivenInput({
.andWeAreListeningForNodes('!..find')
.whenGivenInput({
array:[
{find:'first_find'}
array:[
{find:'first_find'}
, {padding:{find:'second_find'}, find:'third_find'}
]
, find: {
find:'fourth_find'
}
]
, find: {
find:'fourth_find'
}
})
.thenTheInstance(
matched('first_find').atPath(['array',0,'find'])
, matched('second_find').atPath(['array',1,'padding','find'])
, matched('third_find').atPath(['array',1,'find'])
, matched('fourth_find').atPath(['find','find'])
, matched({find:'fourth_find'}).atPath(['find'])
})
.thenTheInstance(
matched('first_find').atPath(['array',0,'find'])
, matched('second_find').atPath(['array',1,'padding','find'])
, matched('third_find').atPath(['array',1,'find'])
, matched('fourth_find').atPath(['find','find'])
, matched({find:'fourth_find'}).atPath(['find'])
, foundNMatches(5)
);
})
it('passes ancestors of found object correctly', function() {
, foundNMatches(5)
);
})
it('passes ancestors of found object correctly', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..find')
.whenGivenInput({
.andWeAreListeningForNodes('!..find')
.whenGivenInput({
array:[
{find:'first_find'}
array:[
{find:'first_find'}
, {padding:{find:'second_find'}, find:'third_find'}
]
, find: {
find:'fourth_find'
}
]
, find: {
find:'fourth_find'
}
})
.thenTheInstance(
matched('first_find')
.withParent( {find:'first_find'} )
.withGrandparent( [{find:'first_find'}] )
})
.thenTheInstance(
matched('first_find')
.withParent( {find:'first_find'} )
.withGrandparent( [{find:'first_find'}] )
, matched('second_find')
.withParent({find:'second_find'})
.withGrandparent({padding:{find:'second_find'}})
, matched('second_find')
.withParent({find:'second_find'})
.withGrandparent({padding:{find:'second_find'}})
, matched('third_find')
.withParent({padding:{find:'second_find'}, find:'third_find'})
.withGrandparent([
{find:'first_find'}
, {padding:{find:'second_find'}, find:'third_find'}
])
);
})
, matched('third_find')
.withParent({padding:{find:'second_find'}, find:'third_find'})
.withGrandparent([
{find:'first_find'}
, {padding:{find:'second_find'}, find:'third_find'}
])
);
})
it('can detect at multiple depths using implied ancestor of root relationship', function() {
it('can detect at multiple depths using implied ancestor of root relationship', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('find')
.whenGivenInput({
.andWeAreListeningForNodes('find')
.whenGivenInput({
array:[
{find:'first_find'}
array:[
{find:'first_find'}
, {padding:{find:'second_find'}, find:'third_find'}
]
, find: {
find:'fourth_find'
}
]
, find: {
find:'fourth_find'
}
})
.thenTheInstance(
matched('first_find').atPath(['array',0,'find'])
, matched('second_find').atPath(['array',1,'padding','find'])
, matched('third_find').atPath(['array',1,'find'])
, matched('fourth_find').atPath(['find','find'])
, matched({find:'fourth_find'}).atPath(['find'])
})
.thenTheInstance(
matched('first_find').atPath(['array',0,'find'])
, matched('second_find').atPath(['array',1,'padding','find'])
, matched('third_find').atPath(['array',1,'find'])
, matched('fourth_find').atPath(['find','find'])
, matched({find:'fourth_find'}).atPath(['find'])
, foundNMatches(5)
);
})
, foundNMatches(5)
);
})
it('matches nested adjacent selector', function() {
it('matches nested adjacent selector', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..[0].colour')
.whenGivenInput({
.andWeAreListeningForNodes('!..[0].colour')
.whenGivenInput({
foods: [
{ name:'aubergine',
colour:'purple' // match this
},
{name:'apple', colour:'red'},
{name:'nuts', colour:'brown'}
],
non_foods: [
{ name:'brick',
colour:'red' // and this
},
{name:'poison', colour:'pink'},
{name:'broken_glass', colour:'green'}
]
})
.thenTheInstance
( matched('purple')
, matched('red')
, foundNMatches(2)
);
})
it('matches nested selector separated by a single star selector', function() {
foods: [
{ name:'aubergine',
colour:'purple' // match this
},
{name:'apple', colour:'red'},
{name:'nuts', colour:'brown'}
],
non_foods: [
{ name:'brick',
colour:'red' // and this
},
{name:'poison', colour:'pink'},
{name:'broken_glass', colour:'green'}
]
})
.thenTheInstance
( matched('purple')
, matched('red')
, foundNMatches(2)
);
})
it('matches nested selector separated by a single star selector', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..foods.*.name')
.whenGivenInput({
.andWeAreListeningForNodes('!..foods.*.name')
.whenGivenInput({
foods: [
{name:'aubergine', colour:'purple'},
{name:'apple', colour:'red'},
{name:'nuts', colour:'brown'}
],
non_foods: [
{name:'brick', colour:'red'},
{name:'poison', colour:'pink'},
{name:'broken_glass', colour:'green'}
]
})
.thenTheInstance
( matched('aubergine')
, matched('apple')
, matched('nuts')
, foundNMatches(3)
);
})
it('gets all simple objects from an array', function() {
foods: [
{name:'aubergine', colour:'purple'},
{name:'apple', colour:'red'},
{name:'nuts', colour:'brown'}
],
non_foods: [
{name:'brick', colour:'red'},
{name:'poison', colour:'pink'},
{name:'broken_glass', colour:'green'}
]
})
.thenTheInstance
( matched('aubergine')
, matched('apple')
, matched('nuts')
, foundNMatches(3)
);
})
it('gets all simple objects from an array', function() {
// this test is similar to the following one, except it does not use ! in the pattern
givenAnOboeInstance()
.andWeAreListeningForNodes('foods.*')
.whenGivenInput({
foods: [
{name:'aubergine'},
{name:'apple'},
{name:'nuts'}
]
})
.thenTheInstance
( foundNMatches(3)
, matched({name:'aubergine'})
, matched({name:'apple'})
, matched({name:'nuts'})
);
})
.andWeAreListeningForNodes('foods.*')
.whenGivenInput({
foods: [
{name:'aubergine'},
{name:'apple'},
{name:'nuts'}
]
})
.thenTheInstance
( foundNMatches(3)
, matched({name:'aubergine'})
, matched({name:'apple'})
, matched({name:'nuts'})
);
})
it('gets same object repeatedly using css4 syntax', function() {
it('gets same object repeatedly using css4 syntax', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('$foods.*')
.whenGivenInput({
foods: [
{name:'aubergine'},
{name:'apple'},
{name:'nuts'}
]
})
// essentially, the parser should have been called three times with the same object, but each time
// an additional item should have been added
.thenTheInstance
( foundNMatches(3)
, matched([{name:'aubergine'}])
, matched([{name:'aubergine'},{name:'apple'}])
, matched([{name:'aubergine'},{name:'apple'},{name:'nuts'}])
);
})
.andWeAreListeningForNodes('$foods.*')
.whenGivenInput({
foods: [
{name:'aubergine'},
{name:'apple'},
{name:'nuts'}
]
})
// essentially, the parser should have been called three times with the same object, but each time
// an additional item should have been added
.thenTheInstance
( foundNMatches(3)
, matched([{name:'aubergine'}])
, matched([{name:'aubergine'},{name:'apple'}])
, matched([{name:'aubergine'},{name:'apple'},{name:'nuts'}])
);
})
it('matches nested selector separated by double dot', function() {
it('matches nested selector separated by double dot', function() {
givenAnOboeInstance()
// we just want the French names of foods:
.andWeAreListeningForNodes('!..foods..fr')
.whenGivenInput({
// we just want the French names of foods:
.andWeAreListeningForNodes('!..foods..fr')
.whenGivenInput({
foods: [
{name:{en:'aubergine', fr:'aubergine'}, colour:'purple'},
{name:{en:'apple', fr:'pomme'}, colour:'red'},
{name:{en:'nuts', fr:'noix'}, colour:'brown'}
],
non_foods: [
{name:{en:'brick'}, colour:'red'},
{name:{en:'poison'}, colour:'pink'},
{name:{en:'broken_glass'}, colour:'green'}
]
})
.thenTheInstance
( matched('aubergine')
, matched('pomme')
, matched('noix')
, foundNMatches(3)
);
})
foods: [
{name:{en:'aubergine', fr:'aubergine'}, colour:'purple'},
{name:{en:'apple', fr:'pomme'}, colour:'red'},
{name:{en:'nuts', fr:'noix'}, colour:'brown'}
],
non_foods: [
{name:{en:'brick'}, colour:'red'},
{name:{en:'poison'}, colour:'pink'},
{name:{en:'broken_glass'}, colour:'green'}
]
})
.thenTheInstance
( matched('aubergine')
, matched('pomme')
, matched('noix')
, foundNMatches(3)
);
})
describe('duck types', function(){
describe('duck types', function(){
// only smoke-testing duck types here, tested thoroughly in jsonpath unit tests

@@ -1295,107 +1340,107 @@

givenAnOboeInstance()
// we want the bi-lingual objects
.andWeAreListeningForNodes('{en fr}')
.whenGivenInput({
givenAnOboeInstance()
// we want the bi-lingual objects
.andWeAreListeningForNodes('{en fr}')
.whenGivenInput({
foods: [
{name:{en:'aubergine', fr:'aubergine' }, colour:'purple'},
{name:{en:'apple', fr:'pomme' }, colour:'red' },
{name:{en:'nuts', fr:'noix' }, colour:'brown' }
],
non_foods: [
{name:{en:'brick' }, colour:'red' },
{name:{en:'poison' }, colour:'pink' },
{name:{en:'broken_glass'}, colour:'green' }
]
})
.thenTheInstance
( matched({en:'aubergine', fr:'aubergine' })
, matched({en:'apple', fr:'pomme' })
, matched({en:'nuts', fr:'noix' })
, foundNMatches(3)
);
foods: [
{name:{en:'aubergine', fr:'aubergine' }, colour:'purple'},
{name:{en:'apple', fr:'pomme' }, colour:'red' },
{name:{en:'nuts', fr:'noix' }, colour:'brown' }
],
non_foods: [
{name:{en:'brick' }, colour:'red' },
{name:{en:'poison' }, colour:'pink' },
{name:{en:'broken_glass'}, colour:'green' }
]
})
.thenTheInstance
( matched({en:'aubergine', fr:'aubergine' })
, matched({en:'apple', fr:'pomme' })
, matched({en:'nuts', fr:'noix' })
, foundNMatches(3)
);
})
it('can detect by matches with additional keys', function() {
givenAnOboeInstance()
// we want the bi-lingual English and German words, but we still want the ones that have
// French as well
.andWeAreListeningForNodes('{en de}')
.whenGivenInput({
givenAnOboeInstance()
// we want the bi-lingual English and German words, but we still want the ones that have
// French as well
.andWeAreListeningForNodes('{en de}')
.whenGivenInput({
foods: [
{name:{en:'aubergine', fr:'aubergine', de: 'aubergine' }, colour:'purple'},
{name:{en:'apple', fr:'pomme', de: 'apfel' }, colour:'red' },
{name:{en:'nuts', de: 'eier' }, colour:'brown' }
],
non_foods: [
{name:{en:'brick' }, colour:'red' },
{name:{en:'poison' }, colour:'pink' },
{name:{en:'broken_glass'}, colour:'green'}
]
})
.thenTheInstance
( matched({en:'aubergine', fr:'aubergine', de:'aubergine' })
, matched({en:'apple', fr:'pomme', de: 'apfel' })
, matched({en:'nuts', de: 'eier' })
, foundNMatches(3)
);
foods: [
{name:{en:'aubergine', fr:'aubergine', de: 'aubergine' }, colour:'purple'},
{name:{en:'apple', fr:'pomme', de: 'apfel' }, colour:'red' },
{name:{en:'nuts', de: 'eier' }, colour:'brown' }
],
non_foods: [
{name:{en:'brick' }, colour:'red' },
{name:{en:'poison' }, colour:'pink' },
{name:{en:'broken_glass'}, colour:'green'}
]
})
.thenTheInstance
( matched({en:'aubergine', fr:'aubergine', de:'aubergine' })
, matched({en:'apple', fr:'pomme', de: 'apfel' })
, matched({en:'nuts', de: 'eier' })
, foundNMatches(3)
);
})
})
})
describe('error cases:', function() {
describe('error cases:', function() {
describe('errors on unquoted keys', function(){
var invalidJson = '{invalid:"json"}'; // key not quoted, invalid json
var invalidJson = '{invalid:"json"}'; // key not quoted, invalid json
// there have been bugs where this passes when passed in one char at a time
// but not when given as a single call
// there have been bugs where this passes when passed in one char at a time
// but not when given as a single call
it('fed in as a lump', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart(invalidJson)
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('fed a char at a time', function() {
it('fed in as a lump', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart(invalidJson)
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('fed a char at a time', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputOneCharAtATime(invalidJson)
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputOneCharAtATime(invalidJson)
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
});
describe('errors on malformed json', function(){
var malformedJson = '{{'; // invalid!
var malformedJson = '{{'; // invalid!
it('works as a lump', function() {
it('works as a lump', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart(malformedJson)
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('works as chars', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart(malformedJson)
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('works as chars', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputOneCharAtATime(malformedJson) // invalid!
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputOneCharAtATime(malformedJson) // invalid!
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
});

@@ -1405,72 +1450,73 @@

givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart('[[1,2,3],[4,5')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart('[[1,2,3],[4,5')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('detects error when stream halts early between children of root fed in a char at a time', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputOneCharAtATime('[[1,2,3],[4,5')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputOneCharAtATime('[[1,2,3],[4,5')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('detects error when stream halts early between children of root', function() {
// currently failing: clarinet is not detecting the error
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart('[[1,2,3],')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
// currently failing: clarinet is not detecting the error
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart('[[1,2,3],')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('detects error when stream halts early inside mid-tree node', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart('[[1,2,3')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInputPart('[[1,2,3')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
);
})
it('calls error listener if an error is thrown in the callback', function() {
spyOn(window, 'setTimeout');
spyOn(globalContext, 'setTimeout');
givenAnOboeInstance()
.andWeHaveAFaultyCallbackListeningFor('!') // just want the root object
.whenGivenInputPart('{}') // valid json, should provide callback
.thenTheInstance
({
testAgainst: function() {
expect(window.setTimeout.mostRecentCall.args[0]).toThrow()
}
});
givenAnOboeInstance()
.andWeHaveAFaultyCallbackListeningFor('!') // just want the root object
.whenGivenInputPart('{}') // valid json, should provide callback
.thenTheInstance
({
testAgainst: function() {
// console.log(globalContext.setTimeout.calls.mostRecent())
expect(globalContext.setTimeout.calls.argsFor(0)[0]).toThrow()
}
});
})
});
});
describe('aborting a request', function(){
describe('aborting a request', function(){
it('does not throw an error', function(){
expect( function(){
expect( function(){
givenAnOboeInstance()
.andWeAreListeningForPaths('*')
.whenGivenInputPart('[1')
.andWeAbortTheRequest();
givenAnOboeInstance()
.andWeAreListeningForPaths('*')
.whenGivenInputPart('[1')
.andWeAbortTheRequest();
}).not.toThrow();
}).not.toThrow();
});

@@ -1480,35 +1526,35 @@

// we should be able to abort even when given all the content at once
// we should be able to abort even when given all the content at once
var asserter = givenAnOboeInstance();
asserter.andWeAreListeningForNodes('![5]', function(){
asserter.andWeAbortTheRequest();
})
.whenGivenInput([0,1,2,3,4,5,6,7,8,9])
.thenTheInstance(
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
hasRootJson([0,1,2,3,4,5])
);
var asserter = givenAnOboeInstance();
asserter.andWeAreListeningForNodes('![5]', function(){
asserter.andWeAbortTheRequest();
})
.whenGivenInput([0,1,2,3,4,5,6,7,8,9])
.thenTheInstance(
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
hasRootJson([0,1,2,3,4,5])
);
})
});
});
describe('dropping nodes', function() {
describe('dropping nodes', function() {
it('drops from an array leaving holes', function() {
function isEven(n) {
return (n % 2) == 0;
}
function isEven(n) {
return (n % 2) == 0;
}
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*', function( number ) {
if( isEven(number) ) {
return oboe.drop;
}
})
.whenGivenInput([1,2,3,4,5,6,7])
.thenTheInstance(
hasRootJson([1, ,3, ,5, ,7]) // note holes in array!
);
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*', function( number ) {
if( isEven(number) ) {
return oboe.drop;
}
})
.whenGivenInput([1,2,3,4,5,6,7])
.thenTheInstance(
hasRootJson([1, ,3, ,5, ,7]) // note holes in array!
);
})

@@ -1518,21 +1564,21 @@

givenAnOboeInstance()
.andWeAreListeningForNodes('!.*', function notDrinking( course ) {
if( course == 'wine' ) {
return oboe.drop;
}
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*', function notDrinking( course ) {
if( course == 'wine' ) {
return oboe.drop;
}
})
.whenGivenInput({
starter:'soup',
main:'fish',
desert:'fresh cheesecake',
drink:'wine'
})
.thenTheInstance(
hasRootJson({
starter:'soup',
main:'fish',
desert:'fresh cheesecake'
})
.whenGivenInput({
starter:'soup',
main:'fish',
desert:'fresh cheesecake',
drink:'wine'
})
.thenTheInstance(
hasRootJson({
starter:'soup',
main:'fish',
desert:'fresh cheesecake'
})
);
);
});

@@ -1542,45 +1588,45 @@

givenAnOboeInstance()
.andWeAreListeningForNodes('starter', oboe.drop)
.whenGivenInput({
starter:'soup',
main:'fish',
desert:'fresh cheesecake',
drink:'wine'
})
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('starter', oboe.drop)
.whenGivenInput({
starter:'soup',
main:'fish',
desert:'fresh cheesecake',
drink:'wine'
})
.thenTheInstance(
hasRootJson({
main:'fish',
desert:'fresh cheesecake',
drink:'wine'
main:'fish',
desert:'fresh cheesecake',
drink:'wine'
})
);
);
})
});
});
describe('swapping out nodes', function() {
describe('swapping out nodes', function() {
it('can selectively drop objects from an array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*', function( obj ) {
if( obj.nullfily ) {
return null;
}
})
.whenGivenInput([
{nullfily:true},
{keep:true},
{nullfily:true}
])
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*', function( obj ) {
if( obj.nullfily ) {
return null;
}
})
.whenGivenInput([
{nullfily:true},
{keep:true},
{nullfily:true}
])
.thenTheInstance(
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
hasRootJson([
null,
{keep:true},
null
null,
{keep:true},
null
])
);
);
});

@@ -1590,65 +1636,66 @@

givenAnOboeInstance()
.andWeAreListeningForNodes('{replace}', function( obj ) {
return {replaced:true};
})
.whenGivenInput({
a: {replace: true},
b: {keep: true},
c: {replace: true}
})
.thenTheInstance(
givenAnOboeInstance()
.andWeAreListeningForNodes('{replace}', function( obj ) {
return {replaced:true};
})
.whenGivenInput({
a: {replace: true},
b: {keep: true},
c: {replace: true}
})
.thenTheInstance(
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
hasRootJson({
a: {replaced: true},
b: {keep: true},
c: {replaced: true}
a: {replaced: true},
b: {keep: true},
c: {replaced: true}
})
);
);
});
/*
it('can replace the root', function() {
it('can replace the root', function() {
// replacing the root currently isn't supported
// replacing the root currently isn't supported
givenAnOboeInstance()
.andWeAreListeningForNodes('!', function( obj ) {
return 'different_root';
})
.whenGivenInput({
a: 'a',
b: 'b'
})
.thenTheInstance(
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
hasRootJson('different_root')
);
});
*/
givenAnOboeInstance()
.andWeAreListeningForNodes('!', function( obj ) {
return 'different_root';
})
.whenGivenInput({
a: 'a',
b: 'b'
})
.thenTheInstance(
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
hasRootJson('different_root')
);
});
*/
it('can transform scalar values', function() {
function isEven(n) {
return (n % 2) == 0;
}
function isEven(n) {
return (n % 2) == 0;
}
givenAnOboeInstance()
.andWeAreListeningForNodes('*', function( number ) {
givenAnOboeInstance()
.andWeAreListeningForNodes('*', function( number ) {
if (isEven(number)) {
return number * 2;
}
})
.whenGivenInput([1,2,3,4,5])
.thenTheInstance(
if (isEven(number)) {
return number * 2;
}
})
.whenGivenInput([1,2,3,4,5])
.thenTheInstance(
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
hasRootJson([1,4,3,8,5])
);
hasRootJson([1,4,3,8,5])
);
});
});
});
});
});
})(typeof Platform == 'undefined'? require('../libs/platform.js') : Platform)

@@ -1,857 +0,728 @@

(function(Platform) {
// Used to spy on global functions like setTimeout
var globalContext;
if ( !Platform.isNode ) {
globalContext = window;
} else {
globalContext = GLOBAL;
}
// Used to spy on global functions like setTimeout
var globalContext;
describe("oboe integration (real http)", function() {
if ( !Platform.isNode ) {
globalContext = window;
} else {
globalContext = GLOBAL;
}
var oboe = Platform.isNode
? require('../../dist/oboe-node.js')
: (window.oboe)
;
var testUrl = Platform.isNode
? require('../libs/testUrl.js')
: (window.testUrl)
;
describe("oboe integration (real http)", function() {
var ASYNC_TEST_TIMEOUT = 15 * 1000; // 15 seconds
var ASYNC_TEST_TIMEOUT = 15 * 1000; // 15 seconds
jasmine.DEFAULT_TIMEOUT_INTERVAL = ASYNC_TEST_TIMEOUT;
describe('advertising the url on the Oboe instance', function() {
var oboe;
var testUrl;
var exampleUrl = testUrl('echoBackHeadersAsBodyJson');
if (Platform.isNode) {
oboe = require('../../dist/oboe-node.js') ;
testUrl = require('../libs/testUrl.js');
} else {
oboe = window.oboe;
testUrl = window.testUrl;
}
it('works after instatiation', function() {
describe('advertising the url on the Oboe instance', function() {
var instance = oboe({
url: exampleUrl
});
var exampleUrl = testUrl('echoBackHeadersAsBodyJson');
expect(instance.source).toBe(exampleUrl);
});
it('works after instatiation', function() {
it('works from callbacks', function() {
var instance = oboe({
url: exampleUrl
});
var urlOnDone;
expect(instance.source).toBe(exampleUrl);
});
oboe({
url: exampleUrl
}).done(function() {
urlOnDone = this.source;
});
it('works from callbacks', function(done) {
waitsFor(function() {
return urlOnDone
}, 'the request to have called done', ASYNC_TEST_TIMEOUT);
var urlOnDone;
runs(function() {
expect(urlOnDone).toBe(exampleUrl);
});
});
oboe({
url: exampleUrl
}).done(function() {
urlOnDone = this.source;
expect(urlOnDone).toBe(exampleUrl);
done();
});
});
});
Platform.isNode && describe('when running under node', function(){
Platform.isNode && describe('when running under node', function(){
var http = require('http'),
fs = require('fs'),
request = require('request');
var http = require('http'),
fs = require('fs'),
request = require('request');
it('can read from a stream that is passed in', function() {
it('can read from a stream that is passed in', function(done) {
var callbackSpy = jasmine.createSpy('callbackSpy');
http.request( 'http://localhost:4567/tenSlowNumbers' )
.on('response', function( res ) {
http.request( 'http://localhost:4567/tenSlowNumbers' )
.on('response', function( res ) {
oboe(res)
.node('![*]', callbackSpy)
.done(whenDoneFn);
oboe(res)
.node('![*]', callbackSpy)
.done(function() {
expect(callbackSpy.calls.count()).toBe(10);
done();
});
}).end();
});
}).on('error', function(e) {
it('can read from a stream from Request - https://github.com/jimhigson/oboe.js/issues/65', function(done) {
var callbackSpy = jasmine.createSpy('callbackSpy');
console.log(e);
}).end();
oboe(request('http://localhost:4567/tenSlowNumbers'))
.node('![*]', callbackSpy)
.done(function() {
expect(callbackSpy.calls.count()).toBe(10);
done();
});
});
it('can read from a local file', function(done) {
var callbackSpy = jasmine.createSpy('callbackSpy');
var fileStream = fs.createReadStream('test/json/firstTenNaturalNumbers.json');
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
oboe(fileStream)
.node('![*]', callbackSpy)
.done(function() {
expect(callbackSpy.calls.count()).toBe(10);
done();
});
});
runs(function () {
it('can read an empty key', function(done) {
var callbackSpy = jasmine.createSpy('callbackSpy');
var fileStream = fs.createReadStream('test/json/emptyKey.json');
expect( callbackSpy.calls.length ).toBe(10);
});
});
oboe(fileStream)
.node('!.*', callbackSpy)
.done(function() {
expect(callbackSpy.calls.count()).toBe(2);
done();
});
});
it('can read from a stream from Request - https://github.com/jimhigson/oboe.js/issues/65', function() {
it('doesnt get confused if a stream has a "url" property', function(done) {
var fileStream = fs.createReadStream('test/json/firstTenNaturalNumbers.json');
fileStream.url = 'http://howodd.com';
oboe(request('http://localhost:4567/tenSlowNumbers'))
.node('![*]', callbackSpy)
.done(whenDoneFn)
.on('error', function(e) {
console.log(e);
});
oboe(fileStream)
.done(done);
});
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
// Tests that depend on network connections can be brittle. Skip for now.
xit('can read from https', function(done) {
var callbackSpy = jasmine.createSpy('callbackSpy');
runs(function () {
// rather than set up a https server in the tests
// let's just use npm since this is an integration test
// by confirming the Oboe.js homepage...
expect( callbackSpy.calls.length ).toBe(10);
});
});
oboe({
url: 'https://registry.npmjs.org/oboe'
})
.node('!.homepage', callbackSpy)
.done(function(obj) {
var oboeHomepage = 'http://oboejs.com';
expect(callbackSpy.calls.mostRecent().args[0]).toEqual(oboeHomepage);
done();
});
});
xit('complains if given a non-http(s) url', function(done) {
expect(function() {
oboe('ftp://ftp.mozilla.org/pub/mozilla.org/')
}).toThrow();
it('can read from a local file', function() {
expect(function() {
oboe('http://registry.npmjs.org/oboe')
}).not.toThrow();
});
var fileStream = fs.createReadStream('test/json/firstTenNaturalNumbers.json');
});
oboe(fileStream)
.node('![*]', callbackSpy)
.done(whenDoneFn);
(!Platform.isNode) && describe('running under a browser', function(){
it('does not send cookies cross-domain by default', function(done) {
document.cookie = "oboeIntegrationDontSend=123; path=/";
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
oboe({
url: crossDomainUrl('/echoBackHeadersAsBodyJson')
}).done(function() {
expect( this.root().cookie ).toBeUndefined();
done();
});
});
runs(function () {
it('can send cookies cross-domain', function(done) {
document.cookie = "oboeIntegrationSend=123; path=/";
expect( callbackSpy.calls.length ).toBe(10);
});
});
oboe({
url: crossDomainUrl('/echoBackHeadersAsBodyJson'),
withCredentials: true
}).done(function(obj) {
expect(this.root().cookie ).toMatch('oboeIntegrationSend=123');
done();
});
});
it('can read an empty key', function() {
it('adds X-Requested-With to cross-domain by default on cross-domain', function(done) {
var fileStream = fs.createReadStream('test/json/emptyKey.json');
oboe({
url: testUrl('echoBackHeadersAsBodyJson')
}).done(function(obj) {
expect(this.root()['x-requested-with'] ).toEqual('XMLHttpRequest');
done();
});
});
oboe(fileStream)
.node('!.*', callbackSpy)
.done(whenDoneFn);
it('does not add X-Requested-With to cross-domain by default on cross-domain', function(done) {
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
oboe({
url: crossDomainUrl('/echoBackHeadersAsBodyJson')
}).done(function(obj) {
expect(this.root()['x-requested-with'] ).toBeUndefined();
done();
});
});
});
runs(function () {
it('gets all expected callbacks by time request finishes', function(done) {
var callbackSpy = jasmine.createSpy('callbackSpy');
expect( callbackSpy.calls.length ).toBe(2);
});
});
oboe(testUrl('tenSlowNumbers'))
.node('![*]', callbackSpy)
.done(function(fullResponse) {
expect(callbackSpy).toHaveBeenCalledWith(0, [0], [fullResponse, 0]);
expect(callbackSpy).toHaveBeenCalledWith(1, [1], [fullResponse, 1]);
expect(callbackSpy).toHaveBeenCalledWith(2, [2], [fullResponse, 2]);
expect(callbackSpy).toHaveBeenCalledWith(3, [3], [fullResponse, 3]);
expect(callbackSpy).toHaveBeenCalledWith(4, [4], [fullResponse, 4]);
expect(callbackSpy).toHaveBeenCalledWith(5, [5], [fullResponse, 5]);
expect(callbackSpy).toHaveBeenCalledWith(6, [6], [fullResponse, 6]);
expect(callbackSpy).toHaveBeenCalledWith(7, [7], [fullResponse, 7]);
expect(callbackSpy).toHaveBeenCalledWith(8, [8], [fullResponse, 8]);
expect(callbackSpy).toHaveBeenCalledWith(9, [9], [fullResponse, 9]);
done();
});
});
it('doesnt get confused if a stream has a "url" property', function() {
var fileStream = fs.createReadStream('test/json/firstTenNaturalNumbers.json');
fileStream.url = 'http://howodd.com';
it('can make nested requests from arrays', function(done) {
var fullResponse;
var callbackSpy = jasmine.createSpy('callbackSpy');
oboe(fileStream)
.done(whenDoneFn);
oboe(testUrl('tenSlowNumbers'))
.node('![*]', function(outerNumber){
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
});
it('can read from https', function() {
// rather than set up a https server in the tests
// let's just use npm since this is an integration test
// by confirming the Oboe.js homepage...
oboe({
url: 'https://registry.npmjs.org/oboe'
})
.node('!.homepage', callbackSpy)
.done(whenDoneFn);
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
runs(function () {
var oboeHomepage = 'http://oboejs.com';
expect(callbackSpy.mostRecentCall.args[0]).toEqual(oboeHomepage);
oboe(testUrl('tenSlowNumbers'))
.node('![*]', function(innerNumber){
callbackSpy();
});
})
.done(function(obj) {
fullResponse = obj;
});
});
// makes a lot of requests so give it a while to complete
waitsForAndRuns(function() {
// console.log('callbackSpy.calls.count()', callbackSpy.calls.count())
return callbackSpy.calls.count() == 100;
}, done, 40 * 1000);
});
it('complains if given a non-http(s) url but is happy otherwise', function() {
expect(function() {
oboe('ftp://ftp.mozilla.org/pub/mozilla.org/')
}).toThrow();
xit('continues to parse after a callback throws an exception', function(done) {
expect(function() {
oboe('http://registry.npmjs.org/oboe')
}).not.toThrow();
var fullResponse;
var callbackSpy = jasmine.createSpy('callbackSpy');
expect(function() {
oboe('https://registry.npmjs.org/oboe')
}).not.toThrow();
});
spyOn(globalContext, 'setTimeout');
oboe(testUrl('static/json/tenRecords.json'))
.node('{id name}', function(){
});
callbackSpy()
throw new Error('uh oh!');
})
.done(function(obj) {
fullResponse = obj;
});
(!Platform.isNode) && describe('running under a browser', function(){
it('does not send cookies cross-domain by default', function () {
waitsForAndRuns(function () {
return callbackSpy.calls.count() == 10;
}, function() {
expect(globalContext.setTimeout.calls.count()).toEqual(10);
for(var i = 0; i < 10; i++) {
expect(function() {
globalContext.setTimeout.calls.argsFor(i)[0]()
}).toThrowError('uh oh!');
expect(globalContext.setTimeout.calls.argsFor(i)[0]).toThrow('uh oh!')
}
done();
}, ASYNC_TEST_TIMEOUT);
});
document.cookie = "oboeIntegrationDontSend=123; path=/";
it('can abort once some data has been found in streamed response', function(done) {
var req = oboe({
url: crossDomainUrl('/echoBackHeadersAsBodyJson')
}).done(whenDoneFn);
var aborted;
var req = oboe(testUrl('tenSlowNumbers'))
.node('![5]', function() {
this.abort();
aborted = true;
});
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
waitsForAndRuns(function() {
return aborted;
}, function() {
expect(req.root()).toEqual([0, 1, 2, 3, 4, 5]);
done();
}, ASYNC_TEST_TIMEOUT);
});
runs(function(){
expect( req.root().cookie ).toBeUndefined();
})
});
it('can deregister from inside the callback', function(done) {
var callbackSpy = jasmine.createSpy('callbackSpy');
it('can send cookies cross-domain', function () {
document.cookie = "oboeIntegrationSend=123; path=/";
oboe(testUrl('tenSlowNumbers'))
.node('![*]', function() {
callbackSpy();
if(callbackSpy.calls.count() === 5) {
this.forget();
}
})
.done(function(obj) {
expect(callbackSpy.calls.count()).toBe(5);
done();
});
});
var req = oboe({
url: crossDomainUrl('/echoBackHeadersAsBodyJson'),
withCredentials: true
}).done(whenDoneFn);
it('can still gets the whole resource after deregistering the callback', function(done) {
var callbackSpy = jasmine.createSpy('callbackSpy');
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
oboe(testUrl('tenSlowNumbers'))
.node('![*]', function() {
callbackSpy();
if(callbackSpy.calls.count() === 5) {
this.forget();
}
})
.done(function(fullResponse) {
expect(fullResponse).toEqual([0,1,2,3,4,5,6,7,8,9]);
done();
});
});
runs(function(){
expect( req.root().cookie ).toMatch('oboeIntegrationSend=123');
})
});
it('can send query params', function(done) {
oboe(testUrl('echoBackQueryParamsAsBodyJson?apiKey=123'))
.node('apiKey', function(keyArg) {
expect(keyArg).toBe('123');
done();
});
});
it('adds X-Requested-With to cross-domain by default on cross-domain', function () {
it('can abort once some data has been found in not very streamed response', function(done) {
var req = oboe({
url: testUrl('echoBackHeadersAsBodyJson')
}).done(whenDoneFn);
// like above but we're getting a static file not the streamed numbers. This means
// we'll almost certainly read in the whole response as one onprogress it is on localhost
// and the json is very small
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
var aborted;
var req = oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.node('![5]', function() {
this.abort();
aborted = true;
});
runs(function(){
expect( req.root()['x-requested-with'] ).toEqual('XMLHttpRequest');
})
});
waitsForAndRuns(function() {
return aborted;
}, function() {
expect(req.root()).toEqual([0, 1, 2, 3, 4, 5]);
done();
}, ASYNC_TEST_TIMEOUT);
});
it('does not add X-Requested-With to cross-domain by default on cross-domain', function () {
it('gives full json to callback when request finishes', function(done) {
var req = oboe({
url: crossDomainUrl('/echoBackHeadersAsBodyJson')
}).done(whenDoneFn);
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.done(function(json) {
expect(json).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
done();
});
});
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
it('gives content type as JSON when provided with an Object body', function(done) {
runs(function(){
expect( req.root()['x-requested-with'] ).toBeUndefined();
})
});
oboe({
method:'PUT',
url:testUrl('echoBackHeadersAsBodyJson'),
body:{'potatoes':3, 'cabbages':4}
}).done(function(json){
contentType = json['content-type'].split(';')[0];
expect(contentType).toBe('application/json');
done();
});
});
it('gets all expected callbacks by time request finishes', function () {
it('gives allows content type to be overridden when provided with an Object body', function(done) {
oboe(testUrl('tenSlowNumbers'))
.node('![*]', callbackSpy)
.done(whenDoneFn);
oboe({
method:'PUT',
url:testUrl('echoBackHeadersAsBodyJson'),
body:{'potatoes':3, 'cabbages':4},
headers:{'Content-Type':'application/vegetableDiffThing'}
}).done(function(json){
contentType = json['content-type'].split(';')[0];
expect(contentType).toBe('application/vegetableDiffThing');
done();
});
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
});
runs(function () {
it('gives header to server side', function(done) {
expect(callbackSpy).toHaveBeenCalledWith(0, [0], [fullResponse, 0]);
expect(callbackSpy).toHaveBeenCalledWith(1, [1], [fullResponse, 1]);
expect(callbackSpy).toHaveBeenCalledWith(2, [2], [fullResponse, 2]);
expect(callbackSpy).toHaveBeenCalledWith(3, [3], [fullResponse, 3]);
expect(callbackSpy).toHaveBeenCalledWith(4, [4], [fullResponse, 4]);
expect(callbackSpy).toHaveBeenCalledWith(5, [5], [fullResponse, 5]);
expect(callbackSpy).toHaveBeenCalledWith(6, [6], [fullResponse, 6]);
expect(callbackSpy).toHaveBeenCalledWith(7, [7], [fullResponse, 7]);
expect(callbackSpy).toHaveBeenCalledWith(8, [8], [fullResponse, 8]);
expect(callbackSpy).toHaveBeenCalledWith(9, [9], [fullResponse, 9]);
var countGotBack = 0;
});
})
oboe({
url:testUrl('echoBackHeadersAsBodyJson'),
headers:{'x-snarfu':'SNARF', 'x-foo':'BAR'}
}).node('x-snarfu', function (headerValue) {
expect(headerValue).toBe('SNARF');
countGotBack++;
}).node('x-foo', function (headerValue) {
expect(headerValue).toBe('BAR');
countGotBack++;
}).done(function() {
expect(countGotBack).toBe(2);
done();
});
});
it('can read response headers', function(done) {
it('can make nested requests from arrays', function () {
oboe({
url:testUrl('echoBackHeadersAsHeaders'),
headers:{'x-sso':'sso', 'x-sso2':'sso2'}
}).done(function(){
expect(this.header('x-sso')).toEqual('sso');
expect(this.header('x-sso2')).toEqual('sso2');
oboe(testUrl('tenSlowNumbers'))
.node('![*]', function(outerNumber){
expect(this.header()['x-sso']).toEqual('sso');
expect(this.header()['x-sso2']).toEqual('sso2');
expect(this.header()['x-sso3']).toBeUndefined();
oboe(testUrl('tenSlowNumbers'))
.node('![*]', function(innerNumber){
callbackSpy();
});
})
.done(whenDoneFn);
done();
});
});
waitsFor(
function () {
return callbackSpy.calls.length == 100;
},
'100 callbacks',
30 * 1000 // makes a lot of requests so give it a while to complete
);
})
it('gives undefined for headers before they are ready', function(done) {
var res = oboe({
url:testUrl('echoBackHeadersAsHeaders'),
headers:{'x-sso':'sso', 'x-sso2':'sso2'}
});
it('continues to parse after a callback throws an exception', function () {
expect(res.header()).toBeUndefined();
expect(res.header('x-sso')).toBeUndefined();
expect(res.header('x-sso2')).toBeUndefined();
done();
});
spyOn(globalContext, 'setTimeout');
oboe(testUrl('static/json/tenRecords.json'))
.node('{id name}', function(){
it('notifies of response starting by giving status code and headers to callback', function(done) {
var functionCalled;
var eventCalled;
callbackSpy()
throw new Error('uh oh!');
})
.done(whenDoneFn);
oboe({
url:testUrl('echoBackHeadersAsHeaders'),
headers:{'x-a':'A', 'x-b':'B'}
}).start(function(statusCode, headers){
expect(statusCode).toBe(200);
expect(headers['x-a']).toBe('A');
functionCalled = true;
}).on('start', function(statusCode, headers){
expect(statusCode).toBe(200);
expect(headers['x-b']).toBe('B');
eventCalled = true;
}).done(function() {
expect(functionCalled).toBe(true);
expect(eventCalled).toBe(true);
done();
});
waitsFor(
function () {
return callbackSpy.calls.length == 10;
},
'100 callbacks'
);
});
runs(function () {
expect(globalContext.setTimeout.calls.length).toEqual(10);
for(var i = 0; i < 10; i++) {
expect(globalContext.setTimeout.calls[i].args[0]).toThrow('uh oh!')
}
});
})
it('can listen for nodes nodejs style', function(done) {
it('can abort once some data has been found in streamed response', function () {
var countGotBack = 0;
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.on('node', '!.*', function () {
countGotBack++;
})
.done(function() {
expect(countGotBack).toBe(10);
done();
});
});
var req = oboe(testUrl('tenSlowNumbers'))
.node('![5]', abortCallback);
it('can listen for paths nodejs style', function(done) {
waitsFor(theRequestToBeAborted, 'the request to be aborted', ASYNC_TEST_TIMEOUT);
var countGotBack = 0;
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.on('path', '!.*', function (number) {
countGotBack++;
})
.done(function() {
expect(countGotBack).toBe(10);
done();
});
});
// in case we didn't abort, wait a little longer. If we didn't really abort we'd get the
// rest of the data now and the test would fail:
waitsFor(someSecondsToPass(2), ASYNC_TEST_TIMEOUT);
it('can listen for done nodejs style', function(done) {
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.on('done', done);
});
runs(function () {
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
it('gets all callbacks and they are in correct order', function (done) {
var order = [];
expect(req.root()).toEqual([0, 1, 2, 3, 4, 5]);
oboe({
method:'POST',
url: testUrl('echoBackBody'),
body: {a:'A', b:'B', c:'C'}
}).path('!', function() {
order.push(1);
}).path('a', function() {
order.push(2);
}).node('a', function() {
order.push(3);
}).path('b', function() {
order.push(4);
}).node('b', function() {
order.push(5);
}).path('c', function() {
order.push(6);
}).node('c', function() {
order.push(7);
}).done(function() {
order.push(8);
expect(order).toEqual([1,2,3,4,5,6,7,8]);
done();
});
});
})
});
it('can deregister from inside the callback', function () {
it('emits error on 404', function(done) {
var nodeCallback = jasmine.createSpy().andCallFake(function(){
if( nodeCallback.calls.length == 5 ) {
this.forget();
}
});
var stubCallback = jasmine.createSpy('error callback');
oboe(testUrl('tenSlowNumbers'))
.node('![*]', nodeCallback)
.done(whenDoneFn);
oboe(testUrl('404json'))
.fail(function(err) {
expect(err.statusCode).toBe(404);
expect(err.jsonBody).toEqual({
found: "false",
errorMessage: "was not found"
});
done();
});
});
// in case we didn't abort, wait a little longer. If we didn't really abort we'd get the
// rest of the data now and the test would fail:
waitsFor(doneCalled, ASYNC_TEST_TIMEOUT);
xit('emits error on 404 in nodejs style too', function(done) {
runs(function () {
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
expect(nodeCallback.calls.length).toBe(5);
});
})
it('can still gets the whole resource after deregistering the callback', function () {
var callback = jasmine.createSpy().andCallFake(function(){
if( callback.calls.length == 5 ) {
this.forget();
}
});
oboe(testUrl('tenSlowNumbers'))
.node('![*]', callback)
.done(whenDoneFn);
// in case we didn't abort, wait a little longer. If we didn't really abort we'd get the
// rest of the data now and the test would fail:
waitsFor(doneCalled, ASYNC_TEST_TIMEOUT);
runs(function () {
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
expect(fullResponse).toEqual([0,1,2,3,4,5,6,7,8,9]);
});
})
it('can send query params', function () {
var apiKeyCallback = jasmine.createSpy();
oboe(testUrl('echoBackQueryParamsAsBodyJson?apiKey=123'))
.node('apiKey', apiKeyCallback)
.done(whenDoneFn);
waitsFor(doneCalled, ASYNC_TEST_TIMEOUT);
runs(function () {
expect(apiKeyCallback.mostRecentCall.args[0]).toBe('123');
});
})
it('can abort once some data has been found in not very streamed response', function () {
// like above but we're getting a static file not the streamed numbers. This means
// we'll almost certainly read in the whole response as one onprogress it is on localhost
// and the json is very small
var req = oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.node('![5]', abortCallback);
waitsFor(theRequestToBeAborted, 'the request to be aborted', ASYNC_TEST_TIMEOUT);
// in case we didn't abort, wait a little longer. If we didn't really abort we'd get the
// rest of the data now and the test would fail:
waitsFor(someSecondsToPass(2), ASYNC_TEST_TIMEOUT);
runs(function () {
// because the request was aborted on index array 5, we got 6 numbers (inc zero)
// not the whole ten.
expect(req.root()).toEqual([0, 1, 2, 3, 4, 5]);
});
})
it('gives full json to callback when request finishes', function () {
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.done(whenDoneFn);
waitsFor(doneCalled, 'the request to give full response', ASYNC_TEST_TIMEOUT)
runs(function () {
expect(fullResponse).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
});
})
it('gives content type as JSON when provided with an Object body', function () {
var done;
oboe({
method:'PUT',
url:testUrl('echoBackHeadersAsBodyJson'),
body:{'potatoes':3, 'cabbages':4}
}).done(function(json){
done = true;
var contentType = contextTypeFrom(json['content-type']);
expect(contentType).toBe('application/json');
});
waitsFor(function (){ return !!done; }, 'the request to complete', ASYNC_TEST_TIMEOUT)
})
it('gives allows content type to be overridden when provided with an Object body', function () {
var done;
oboe({
method:'PUT',
url:testUrl('echoBackHeadersAsBodyJson'),
body:{'potatoes':3, 'cabbages':4},
headers:{'Content-Type':'application/vegetableDiffThing'}
}).done(function(json){
done = true;
var contentType = contextTypeFrom(json['content-type']);
expect(contentType).toBe('application/vegetableDiffThing');
oboe(testUrl('doesNotExist'))
.on('fail', function(err) {
expect(err.statusCode).toBe(404);
expect(err.jsonBody).toEqual({
found: "false",
errorMessage: "was not found"
});
done();
});
});
waitsFor(function (){ return !!done; }, 'the request to complete', ASYNC_TEST_TIMEOUT)
})
/*
This isn't reliable enough, too many false negatives for a ci
it('emits error on unreachable url', function () {
it('gives header to server side', function () {
var stubCallback = jasmine.createSpy('error callback');
var countGotBack = 0;
oboe('nowhere.ox.ac.uk:754196/fooz/barz')
.fail(stubCallback);
oboe(
{ url:testUrl('echoBackHeadersAsBodyJson'),
headers:{'x-snarfu':'SNARF', 'x-foo':'BAR'}
}
waitsForAndRuns(function () {
return !!stubCallback.calls.length;
}, 'the request to fail', 30*1000)
).node('x-snarfu', function (headerValue) {
runs( function() {
expect(stubCallback).toHaveBeenGivenAnyError();
});
})
*/
expect(headerValue).toBe('SNARF')
countGotBack++;
it('throws node callback errors in a separate event loop', function(done) {
}).node('x-foo', function (headerValue) {
var callbackError = new Error('I am a bad callback');
expect(headerValue).toBe('BAR')
countGotBack++;
})
spyOn(globalContext, 'setTimeout');
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.node('!.*', function() {
throw new Error('I am a bad callback');
})
.done(function() {
expect(globalContext.setTimeout.calls.mostRecent().args[0]).toThrow(callbackError);
done();
});
});
waitsFor(function () {
return countGotBack == 2
}, 'all headers to have been detected back here', ASYNC_TEST_TIMEOUT)
})
xit('throws done callback errors in a separate event loop', function(done) {
it('can read response headers', function () {
var callbackError = new Error('I am a bad callback');
var errorCallback = function() {
throw callbackError;
};
var done = false;
spyOn(globalContext, 'setTimeout');
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.done(callbackError);
oboe(
{ url:testUrl('echoBackHeadersAsHeaders'),
headers:{'x-sso':'sso', 'x-sso2':'sso2'}
}
waitsForAndRuns(function() {
return globalContext.setTimeout.calls.count() > 0;
}, function() {
expect(globalContext.setTimeout.calls.mostRecent().args[0]).toThrowError('I am a bad callback');
done();
}, ASYNC_TEST_TIMEOUT);
});
).done(function(){
it('can load an empty response with 204 status code', function(done) {
expect(this.header('x-sso')).toEqual('sso');
expect(this.header('x-sso2')).toEqual('sso2');
expect(this.header()['x-sso']).toEqual('sso');
expect(this.header()['x-sso2']).toEqual('sso2');
expect(this.header()['x-sso3']).toBeUndefined();
done = true;
})
waitsFor(function () {
return done;
}, 'the response to complete', ASYNC_TEST_TIMEOUT)
})
it('gives undefined for headers before they are ready', function () {
var o = oboe(
{ url:testUrl('echoBackHeadersAsHeaders'),
headers:{'x-sso':'sso', 'x-sso2':'sso2'}
}
)
expect(o.header()).toBeUndefined();
expect(o.header('x-sso')).toBeUndefined();
expect(o.header('x-sso2')).toBeUndefined();
})
it('notifies of response starting by giving status code and headers to callback', function () {
var called = 0;
oboe(
{ url:testUrl('echoBackHeadersAsHeaders'),
headers:{'x-a':'A', 'x-b':'B'}
}
).start(function(statusCode, headers){
expect(statusCode).toBe(200);
expect(headers['x-a']).toBe('A');
called++;
}).on('start', function(statusCode, headers){
expect(statusCode).toBe(200);
expect(headers['x-b']).toBe('B');
called++;
});
waitsFor(function () {
return called == 2;
}, 'the response to call both start callbacks', ASYNC_TEST_TIMEOUT)
oboe({
url: testUrl('204noData')
}).start(function(status){
expect(status).toEqual(204);
}).done(function(fullResponse) {
expect(fullResponse).toEqual({});
done();
});
it('can listen for nodes nodejs style', function () {
});
var countGotBack = 0;
xit('emits error with incomplete json', function(done) {
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.on('node', '!.*', function () {
countGotBack++;
});
var stubCallback = jasmine.createSpy('error callback');
waitsFor(function () {
return countGotBack == 10
}, 'ten callbacks', ASYNC_TEST_TIMEOUT)
})
oboe(testUrl('static/json/incomplete.json'))
.fail(function(err) {
console.log(err);
expect(err.jsonBody).toEqual({
found: "false",
errorMessage: "some error"
});
done();
});
});
it('can listen for paths nodejs style', function () {
if( !Platform.isNode ) {
// only worry about this in the browser
var countGotBack = 0;
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.on('path', '!.*', function (number) {
countGotBack++;
});
waitsFor(function () {
return countGotBack == 10
}, 'ten callbacks', ASYNC_TEST_TIMEOUT)
})
it('can listen for done nodejs style', function () {
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.on('done', whenDoneFn);
waitsFor(doneCalled, 'the request to finish', ASYNC_TEST_TIMEOUT)
})
it('gets all callbacks and they are in correct order', function () {
var order = [];
oboe({
method:'POST',
url: testUrl('echoBackBody')
, body: {a:'A', b:'B', c:'C'}
})
.path('!', function(){ order.push(1) })
.path('a', function(){ order.push(2) })
.node('a', function(){ order.push(3) })
.path('b', function(){ order.push(4) })
.node('b', function(){ order.push(5) })
.path('c', function(){ order.push(6) })
.node('c', function(){ order.push(7) })
.done(function(){ order.push(8) })
waitsFor(function(){ return order.length == 8 },
'all 8 callbacks', ASYNC_TEST_TIMEOUT);
runs(function(){
expect(order).toEqual([1,2,3,4,5,6,7,8]);
});
it( "hasn't put clarinet in the global namespace", function(){
expect( window.clarinet ).toBeUndefined();
});
}
it('emits error on 404', function () {
// This is the equivalent of the old waitsFor/runs syntax
// which was removed from Jasmine 2
function waitsForAndRuns(escapeFunction, runFunction, escapeTime) {
// check the escapeFunction every millisecond so as soon as it is met we can escape the function
var interval = setInterval(function() {
if (escapeFunction()) {
clearMe();
runFunction();
}
}, 1);
var stubCallback = jasmine.createSpy('error callback');
// in case we never reach the escapeFunction, we will time out
// at the escapeTime
var timeOut = setTimeout(function() {
clearMe();
runFunction();
}, escapeTime);
oboe(testUrl('404json'))
.fail(stubCallback);
waitsFor(function () {
return !!stubCallback.calls.length;
}, 'the request to fail', ASYNC_TEST_TIMEOUT)
runs( function() {
expect( stubCallback ).toHaveBeenGivenErrorStatusCode( 404 );
expect( stubCallback ).toHaveBeenGivenBodyJson(
{ "found":"false",
"errorMessage":"was not found"
}
);
});
})
it('emits error on 404 in nodejs style too', function () {
var stubCallback = jasmine.createSpy('error callback');
oboe(testUrl('doesNotExist'))
.on('fail', stubCallback);
waitsFor(function () {
return !!stubCallback.calls.length;
}, 'the request to fail', ASYNC_TEST_TIMEOUT)
})
/*
This isn't reliable enough, too many false negatives for a ci
it('emits error on unreachable url', function () {
var stubCallback = jasmine.createSpy('error callback');
oboe('nowhere.ox.ac.uk:754196/fooz/barz')
.fail(stubCallback);
waitsFor(function () {
return !!stubCallback.calls.length;
}, 'the request to fail', 30*1000)
runs( function() {
expect(stubCallback).toHaveBeenGivenAnyError();
});
})
*/
it('throws node callback errors in a separate event loop', function () {
var callbackError = new Error('I am a bad callback');
stubCallback = jasmine.createSpy('error callback').andThrow(callbackError);
spyOn(globalContext, 'setTimeout');
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.node('!.*', stubCallback)
waitsFor(function () {
return !!stubCallback.calls.length;
}, 'the callback to be called', ASYNC_TEST_TIMEOUT)
runs( function() {
expect(globalContext.setTimeout.mostRecentCall.args[0]).toThrow(callbackError)
});
})
it('throws done callback errors in a separate event loop', function () {
var callbackError = new Error('I am a bad callback');
stubCallback = jasmine.createSpy('error callback').andThrow(callbackError);
spyOn(globalContext, 'setTimeout');
oboe(testUrl('static/json/firstTenNaturalNumbers.json'))
.done(stubCallback)
waitsFor(function () {
return !!stubCallback.calls.length;
}, 'the request to fail', ASYNC_TEST_TIMEOUT)
runs( function() {
expect(globalContext.setTimeout.mostRecentCall.args[0]).toThrow(callbackError)
});
})
it('can load an empty response with 204 status code', function() {
var status;
oboe({
url: testUrl('204noData')
})
.start(function(s){
status = s;
})
.done(whenDoneFn);
waitsFor(doneCalled, 'the request to have called done', ASYNC_TEST_TIMEOUT);
runs(function() {
expect(fullResponse).toEqual({});
expect(status).toBe(204);
})
});
it('emits error with incomplete json', function () {
var stubCallback = jasmine.createSpy('error callback');
oboe(testUrl('static/json/incomplete.json'))
.fail(stubCallback);
waitsFor(function () {
return !!stubCallback.calls.length;
}, 'the request to fail', ASYNC_TEST_TIMEOUT);
})
if( !Platform.isNode ) {
// only worry about this in the browser
it( 'hasn\'t put clarinet in the global namespace', function(){
expect( window.clarinet ).toBeUndefined();
});
// clear the interval and the timeout
function clearMe(){
clearInterval(interval);
clearTimeout(timeOut);
}
};
function someSecondsToPass(waitSecs) {
beforeEach(function () {
jasmine.addMatchers({
toHaveBeenGivenAnyError: function(){
return {
compare: function(actual, expected) {
var result = {};
var errorReport = actual.calls.argsFor(0)[0],
maybeErr = errorReport.thrown;
function now() {
// IE8 doesn't have Date.now()
return new Date().getTime();
}
if( typeof maybeErr != 'object' ) {
return {passed: false};
}
var waitStart = now(),
waitMs = waitSecs * 1000;
if( maybeErr instanceof Error ) {
return {passed: true};
}
return function () {
return now() > (waitStart + waitMs);
}
}
// instanceof Error doesn't always work in Safari (tested v6.0.5)
// because:
//
// ((new XMLHttpRequestException()) instanceof Error) == false
if( window && window.XMLHttpRequestException &&
maybeErr instanceof XMLHttpRequestException ) {
return {passed: true};
}
var callbackSpy,
fullResponse,
aborted;
// if that didn't work fallback to some duck typing:
function abortCallback() {
this.abort();
aborted = true;
}
function theRequestToBeAborted() {
return aborted
}
function whenDoneFn(obj) {
fullResponse = obj;
}
function doneCalled(){
return !!fullResponse
}
/**
* A context-type header can come back like:
* 'application/json'
*
* or like:
* 'application/json; charset=UTF-8'
*/
function contextTypeFrom(header){
return header.split(';')[0];
}
beforeEach(function () {
aborted = false;
fullResponse = null;
callbackSpy = jasmine.createSpy('callbackSpy');
this.addMatchers({
toHaveBeenGivenAnyError:function(){
var errorReport = this.actual.mostRecentCall.args[0],
maybeErr = errorReport.thrown;
if( typeof maybeErr != 'object' ) {
return false;
}
if( maybeErr instanceof Error ) {
return true;
}
// instanceof Error doesn't always work in Safari (tested v6.0.5)
// because:
//
// ((new XMLHttpRequestException()) instanceof Error) == false
if( window && window.XMLHttpRequestException &&
maybeErr instanceof XMLHttpRequestException ) {
return true;
}
// if that didn't work fallback to some duck typing:
return( (typeof maybeErr.message != 'undefined') &&
(typeof maybeErr.lineNumber != 'undefined') );
},
toHaveBeenGivenThrowee:function(expectedError){
var errorReport = this.actual.mostRecentCall.args[0];
return errorReport.thrown === expectedError;
},
toHaveBeenGivenErrorStatusCode:function(expectedCode){
var errorReport = this.actual.mostRecentCall.args[0];
return errorReport.statusCode === expectedCode;
},
toHaveBeenGivenBodyJson:function(expectedBodyJson){
var errorReport = this.actual.mostRecentCall.args[0];
return JSON.stringify(expectedBodyJson)
===
JSON.stringify(errorReport.jsonBody);
result.pass = (typeof maybeErr.message != 'undefined') &&
(typeof maybeErr.lineNumber != 'undefined');
return result;
}
});
};
},
toHaveBeenGivenThrowee:function(expectedError){
return {
compare: function(actual, expected) {
var errorReport = actual.calls.getArgsFor(0)[0];
return {
passed: errorReport.thrown === expectedError
};
}
};
},
toHaveBeenGivenErrorStatusCode:function(expectedCode){
return {
compare: function(actual, expected) {
var errorReport = actual.calls.getArgsFor(0)[0];
return {
passed: errorReport.statusCode === expectedCode
};
}
};
},
toHaveBeenGivenBodyJson:function(expectedBodyJson){
return {
compare: function(actual, expected) {
var errorReport = actual.calls.getArgsFor(0)[0];
return {
passed: JSON.stringify(expectedBodyJson)
===
JSON.stringify(errorReport.jsonBody)
};
}
};
}
});
});
});
});
})(typeof Platform == 'undefined'? require('../libs/platform.js') : Platform)
(function(Platform) {
describe("oboe performance (real http)", function(){
var oboe = Platform.isNode
? require('../../dist/oboe-node.js')
: (window.oboe)
;
function url( path ){
if( Platform.isNode ) {
return 'http://localhost:4567/' + path;
} else {
return '/testServer/' + path;
}
}
it('is benchmarked with a complex jsonpath', function() {
var startTime = now();
var doneFn = jasmine.createSpy('done');
var callCount = 0;
oboe(url('static/json/oneHundredRecords.json'))
.node('!.$result..{age name company}', function(){callCount++})
.done( doneFn );
waitsFor( function(){ return doneFn.calls.length == 1 },
'the computation under test to be performed',
5000 )
runs( function(){
expect(callCount).toBe(100);
console.log('took ' + (now() - startTime) + 'ms to evaluate a complex ' +
'expression many times, finding 100 matches');
});
})
it('is benchmarked with a simple jsonpath', function() {
var startTime = now();
var doneFn = jasmine.createSpy('done');
var callCount = 0;
oboe(url('static/json/oneHundredRecords.json'))
.node('name', function(){callCount++})
.done( doneFn );
waitsFor( function(){ return doneFn.calls.length == 1 },
'the computation under test to be performed',
5000 )
runs( function(){
expect(callCount).toBe(100);
console.log('took ' + (now() - startTime) + 'ms to evaluate a simple ' +
'expression many times, finding 100 matches');
});
})
function now() {
return new Date().valueOf()
}
});
function now() {
return new Date().valueOf();
}
})(typeof Platform == 'undefined'? require('../libs/platform.js') : Platform)
describe("oboe performance (real http)", function(){
// Used to spy on global functions like setTimeout
var globalContext;
var oboe;
if ( !Platform.isNode ) {
globalContext = window;
oboe = window.oboe;
} else {
globalContext = GLOBAL;
oboe = require('../../dist/oboe-node.js');
}
function url( path ){
if( Platform.isNode ) {
return 'http://localhost:4567/' + path;
} else {
return '/testServer/' + path;
}
}
it('is benchmarked with a complex jsonpath', function(done) {
var callCount = 0;
var startTime = now();
oboe(url('static/json/oneHundredRecords.json'))
.node('!.$result..{age name company}', function(){
callCount++;
})
.done(function() {
var timeDiff = now() - startTime;
expect(callCount).toBe(100);
console.log('took ' + timeDiff + 'ms to evaluate a complex ' +
'expression many times, finding 100 matches');
done();
});
});
it('is benchmarked with a simple jsonpath', function() {
var callCount = 0;
var startTime = now();
var doneFn = jasmine.createSpy('done');
oboe(url('static/json/oneHundredRecords.json'))
.node('name', function(){
callCount++;
})
.done(function(){
var timeDiff = now() - startTime;
expect(callCount).toBe(100);
console.log('took ' + timeDiff + 'ms to evaluate a complex ' +
'expression many times, finding 100 matches');
done();
});
});
});
})(typeof Platform == 'undefined'? require('../libs/platform.js') : Platform);
describe('patternAdapter', function() {
it('compiles the correct pattern when patterns are listened to', function(){
bus('newListener').emit( 'node:test_pattern');
expect( jsonPathCompiler ).toHaveBeenCalledWith('test_pattern');
})
});
it('listens for NODE_CLOSED events when node:pattern is added', function(){
bus('newListener').emit( 'node:test_pattern');
expect(bus(NODE_CLOSED).on)
.toHaveBeenCalledWith(
.toHaveBeenCalledWith(
jasmine.any(Function)
, 'node:test_pattern'
)
})
);
});
it('does not listen for NODE_CLOSED events second time same node:pattern is added', function(){
bus('newListener').emit( 'node:test_pattern');
bus('newListener').emit( 'node:test_pattern');
expect(bus(NODE_CLOSED).on.calls.length).toBe(1);
})
expect(bus(NODE_CLOSED).on.calls.count()).toBe(1);
});
it('stops listening for node events when node:pattern is removed again', function(){
bus('node:test_pattern').on( noop);
bus('node:test_pattern').un( noop);
expect(bus(NODE_CLOSED).un)
.toHaveBeenCalledWith(
'node:test_pattern'
)
})
);
});
it('starts listening again for node events when node:pattern is added, removed and added again', function(){
bus('node:test_pattern').on( noop);
expect(bus(NODE_CLOSED).on.calls.length).toBe(1);
expect(bus(NODE_CLOSED).on.calls.count()).toBe(1);
bus('node:test_pattern').on( noop);
expect(bus(NODE_CLOSED).on.calls.length).toBe(1);
expect(bus(NODE_CLOSED).on.calls.count()).toBe(1);
bus('node:test_pattern').un( noop);
expect(bus(NODE_CLOSED).un.calls.length).toBe(0);
expect(bus(NODE_CLOSED).un.calls.count()).toBe(0);
bus('node:test_pattern').un( noop);
expect(bus(NODE_CLOSED).un.calls.length).toBe(1);
bus('node:test_pattern').on( noop);
expect(bus(NODE_CLOSED).on.calls.length).toBe(2);
})
expect(bus(NODE_CLOSED).un.calls.count()).toBe(1);
bus('node:test_pattern').on( noop);
expect(bus(NODE_CLOSED).on.calls.count()).toBe(2);
});
it('doesn\'t stop listening is there are still other node:pattern listeners', function(){
bus('node:test_pattern').on( noop);
bus('node:test_pattern').on( noop);
bus('node:test_pattern').un( noop);
expect(bus(NODE_CLOSED).un)
.not.toHaveBeenCalledWith(
.not.toHaveBeenCalledWith(
'node:test_pattern'
)
})
);
});
it('doesn\'t stop listening if a different pattern is unsubscribed from', function(){
bus('node:test_pattern').on( noop);
bus('node:other_test_pattern').on( noop);
bus('node:other_test_pattern').un( noop);
expect(bus(NODE_CLOSED).un)
.not.toHaveBeenCalledWith(
.not.toHaveBeenCalledWith(
'node:test_pattern'
)
})
);
});
it('doesn\'t stop listening if wrong event type is unsubscribed', function(){
bus('node:test_pattern').on( noop);
bus('path:test_pattern').on( noop);
bus('path:test_pattern').un( noop);
expect(bus(NODE_CLOSED).un)
.not.toHaveBeenCalledWith(
.not.toHaveBeenCalledWith(
'node:test_pattern'
)
})
);
});
it('only listens once to NODE_CLOSED when same pattern is added twice', function(){
bus('node:test_pattern').on( noop);
bus('node:test_pattern').on( noop);
expect( listAsArray( bus(NODE_CLOSED).listeners ).length ).toBe(1);
})
});
it('listens to NODE_CLOSED and NODE_OPENED when given node: and path: listeners', function(){
bus('node:test_pattern').on( noop);
bus('path:test_pattern').on( noop);
expect(bus(NODE_CLOSED).on)
.toHaveBeenCalledWith(
.toHaveBeenCalledWith(
jasmine.any(Function)
, 'node:test_pattern'
)
);
expect(bus(NODE_OPENED).on)
.toHaveBeenCalledWith(
.toHaveBeenCalledWith(
jasmine.any(Function)
, 'path:test_pattern'
)
})
);
});
it('fires node:pattern events when match is found', function(){
var ascent = anAscentMatching('test_pattern');
bus('node:test_pattern').on( noop);
bus(NODE_CLOSED).emit( ascent);
expect( bus('node:test_pattern').emit ).toHaveBeenCalled();
})
});
it('fires gives node:pattern the node, path ' +
'and ancestors from the given ascent', function(){
var testJson = {animals:{mammals:{humans:'hi there!'}}},
ascent = anAscentMatching('test_pattern', testJson);
bus('node:test_pattern').on(noop);
bus(NODE_CLOSED).emit( ascent);
expect( bus('node:test_pattern').emit )
.toHaveBeenCalledWith(
'hi there!',
.toHaveBeenCalledWith(
'hi there!',
[ 'animals', 'mammals', 'humans'],
[ testJson,
testJson.animals,
testJson.animals.mammals,
[ testJson,
testJson.animals,
testJson.animals.mammals,
testJson.animals.mammals.humans
]
]
);
})
});

@@ -165,11 +165,11 @@

beforeEach(function(){
matches = {};
bus = spiedPubSub();
jsonPathCompiler = jasmine.createSpy('jsonPathCompiler').andCallFake(
jsonPathCompiler = jasmine.createSpy('jsonPathCompiler').and.callFake(
function (pattern){
function compiled ( ascent ){
function compiled ( ascent ){
if( matches[pattern] === ascent ) {

@@ -181,15 +181,15 @@ return head(ascent);

}
return compiled;
}
);
patternAdapter(bus, jsonPathCompiler);
})
);
patternAdapter(bus, jsonPathCompiler);
});
function anAscentMatching(pattern, json) {
var ascent
var ascent;
if( json ) {
ascent = ascentFrom(json);
ascent = ascentFrom(json);
} else {

@@ -199,3 +199,3 @@ ascent = list(namedNode('node', {}));

matches[pattern] = ascent;
matches[pattern] = ascent;

@@ -202,0 +202,0 @@ return ascent;

describe('single event pub sub', function(){
beforeEach(function(){
this.addMatchers({
toBeList: listMatcher
});
});
beforeEach(function(){
jasmine.addMatchers({
toBeList: function() {
return {
compare: function(actual, expected) {
if(!actual || !actual.length || !expected || !expected.length) {
return {pass: false};
}
it('is able to subscribe', function(){
var events = singleEventPubSub('someEventName');
expect(function(){
events.on(function(){});
}).not.toThrow();
var filtered = actual.filter(function(item, i) {
var match = expected[i];
// deep compare lists
if(item.length) {
var subFiltered = item.filter(function(subItem, subI) {
return subItem == match[subI];
});
return {pass: subFiltered.length === match.length};
}
});
it('is able to notify a subscribed function without an event object', function(){
var events = singleEventPubSub('someEventName'),
listener = jasmine.createSpy('listener');
return item == expected[i];
});
events.on(listener);
events.emit();
expect(listener).toHaveBeenCalled();
});
it('is able to notify a subscribed function with a event parameter', function(){
var events = singleEventPubSub('someEventName'),
listener = jasmine.createSpy('listener');
return {pass: filtered.length === expected.length};
}
};
}
});
});
events.on(listener);
events.emit('somethingFunky');
expect(listener).toHaveBeenCalledWith('somethingFunky');
});
xit('is able to subscribe', function(){
it('notifies of new listeners when added without an id', function() {
var newListener = singleEventPubSub('newListener'),
someEventName = singleEventPubSub('someEventName', newListener),
listenerListener = jasmine.createSpy('listenerListener');
var events = singleEventPubSub('someEventName');
newListener.on(listenerListener);
someEventName.on(noop);
expect(listenerListener).toHaveBeenCalledWith('someEventName', noop, noop);
});
it('notifies of new listeners when added with an id', function() {
var newListener = singleEventPubSub('newListener'),
someEventName = singleEventPubSub('someEventName', newListener),
listenerListener = jasmine.createSpy('listenerListener');
expect(function(){
events.on(function(){});
}).not.toThrow();
newListener.on(listenerListener);
someEventName.on(noop, 'id1');
expect(listenerListener).toHaveBeenCalledWith('someEventName', noop, 'id1');
});
});
it('notifies multiple listeners of the same event', function(){
var events = singleEventPubSub('someEventName'),
listenerA = jasmine.createSpy('listenerA'),
listenerA2 = jasmine.createSpy('listenerA2');
xit('is able to notify a subscribed function without an event object', function(){
var events = singleEventPubSub('someEventName'),
listener = jasmine.createSpy('listener');
events.on(listener);
events.emit();
expect(listener).toHaveBeenCalled();
});
xit('is able to notify a subscribed function with a event parameter', function(){
var events = singleEventPubSub('someEventName'),
listener = jasmine.createSpy('listener');
events.on(listener);
events.emit('somethingFunky');
expect(listener).toHaveBeenCalledWith('somethingFunky');
});
xit('notifies of new listeners when added without an id', function() {
var newListener = singleEventPubSub('newListener'),
someEventName = singleEventPubSub('someEventName', newListener),
listenerListener = jasmine.createSpy('listenerListener');
newListener.on(listenerListener);
someEventName.on(noop);
expect(listenerListener).toHaveBeenCalledWith('someEventName', noop, noop);
});
xit('notifies of new listeners when added with an id', function() {
var newListener = singleEventPubSub('newListener'),
someEventName = singleEventPubSub('someEventName', newListener),
listenerListener = jasmine.createSpy('listenerListener');
newListener.on(listenerListener);
someEventName.on(noop, 'id1');
expect(listenerListener).toHaveBeenCalledWith('someEventName', noop, 'id1');
});
xit('notifies multiple listeners of the same event', function(){
var events = singleEventPubSub('someEventName'),
listenerA = jasmine.createSpy('listenerA'),
listenerA2 = jasmine.createSpy('listenerA2');
events.on(listenerA);
events.on(listenerA2);
events.emit();
expect(listenerA).toHaveBeenCalled();
expect(listenerA2).toHaveBeenCalled();
});
xit('can pass through multiple parameters', function(){
var events = singleEventPubSub('someEventName'),
listener = jasmine.createSpy('listener');
events.on(listener);
events.emit('a', 'b', 'c');
expect(listener).toHaveBeenCalledWith('a', 'b', 'c');
});
xit('can pass multiple parameters to multiple listeners', function(){
var events = singleEventPubSub('someEventName'),
listener = jasmine.createSpy('listener' ),
listener2 = jasmine.createSpy('listener2');
events.on(listener );
events.on(listener2);
events.emit('a', 'b', 'c');
expect(listener ).toHaveBeenCalledWith('a', 'b', 'c');
expect(listener2).toHaveBeenCalledWith('a', 'b', 'c');
});
xit('allows many listeners to be registered for an event', function(){
var events = singleEventPubSub('someEventName'),
listenerA = jasmine.createSpy('listenerA');
for (var i = 0; i < 10; i++) {
// listen ten times
events.on(listenerA);
events.on(listenerA2);
}
for (var j = 0; j < 3; j++) {
// emit 3 times
events.emit();
expect(listenerA).toHaveBeenCalled();
expect(listenerA2).toHaveBeenCalled();
});
it('can pass through multiple parameters', function(){
var events = singleEventPubSub('someEventName'),
listener = jasmine.createSpy('listener');
events.on(listener);
events.emit('a', 'b', 'c');
expect(listener).toHaveBeenCalledWith('a', 'b', 'c');
});
it('can pass multiple parameters to multiple listeners', function(){
var events = singleEventPubSub('someEventName'),
listener = jasmine.createSpy('listener' ),
listener2 = jasmine.createSpy('listener2');
events.on(listener );
events.on(listener2);
events.emit('a', 'b', 'c');
expect(listener ).toHaveBeenCalledWith('a', 'b', 'c');
expect(listener2).toHaveBeenCalledWith('a', 'b', 'c');
});
it('allows many listeners to be registered for an event', function(){
var events = singleEventPubSub('someEventName'),
listenerA = jasmine.createSpy('listenerA');
}
for (var i = 0; i < 10; i++) {
// listen ten times
events.on(listenerA);
}
expect(listenerA.calls.count()).toBe(30);
});
for (var j = 0; j < 3; j++) {
// emit 3 times
events.emit();
}
expect(listenerA.calls.length).toBe(30);
});
it('has a chainable on function', function(){
var events = singleEventPubSub('someEventName'),
listenerA = jasmine.createSpy('listenerA'),
listenerB = jasmine.createSpy('listenerB');
events.on(listenerA)
.on(listenerB)
.emit();
expect(listenerA).toHaveBeenCalled();
expect(listenerB).toHaveBeenCalled();
});
it('allows an event to be removed', function(){
var events = singleEventPubSub('someEventName'),
listener1 = jasmine.createSpy('listener1'),
listener2 = jasmine.createSpy('listener2');
events.on(listener1);
events.on(listener2);
xit('has a chainable on function', function(){
events.emit();
expect(listener1.calls.length).toBe(1);
events.un(listener1);
var events = singleEventPubSub('someEventName'),
listenerA = jasmine.createSpy('listenerA'),
listenerB = jasmine.createSpy('listenerB');
events.emit();
events.on(listenerA)
.on(listenerB)
.emit();
expect(listener1.calls.length).toBe(1);
expect(listener2.calls.length).toBe(2);
});
it('allows an event to be removed by an id', function(){
var events = singleEventPubSub('someEventName'),
listener1 = jasmine.createSpy('listener1'),
listener2 = jasmine.createSpy('listener2');
events.on(listener1, 'FOO_ID');
events.on(listener2, 'BAR_ID');
expect(listenerA).toHaveBeenCalled();
expect(listenerB).toHaveBeenCalled();
});
events.emit();
expect(listener1.calls.length).toBe(1);
events.un('FOO_ID');
xit('allows an event to be removed', function(){
events.emit();
var events = singleEventPubSub('someEventName'),
listener1 = jasmine.createSpy('listener1'),
listener2 = jasmine.createSpy('listener2');
expect(listener1.calls.length).toBe(1);
expect(listener2.calls.length).toBe(2);
events.on(listener1);
events.on(listener2);
events.un('BAR_ID');
events.emit();
expect(listener2.calls.length).toBe(2);
})
it('does not fire removeListener if nothing is removed', function() {
var newListener = singleEventPubSub('newListener'),
removeListener = singleEventPubSub('removeListener'),
events = singleEventPubSub('someEventName', newListener, removeListener),
removeListenerListener = jasmine.createSpy('removeListenerListener'),
fooListener = jasmine.createSpy('fooListener');
removeListener.on(removeListenerListener);
events.emit();
events.on(fooListener);
events.un('wrong_item');
expect(removeListenerListener).not.toHaveBeenCalled();
});
it('fires removeListener when a listener is removed', function(){
var newListener = singleEventPubSub('newListener'),
removeListener = singleEventPubSub('removeListener'),
events = singleEventPubSub('someEventName', newListener, removeListener),
removeListenerListener = jasmine.createSpy('removeListenerListener'),
fooListener = jasmine.createSpy('fooListener');
removeListener.on(removeListenerListener);
expect(listener1.calls.count()).toBe(1);
events.on(fooListener);
events.un(fooListener);
expect(removeListenerListener).toHaveBeenCalled();
})
it('does not crash if asked to emit without listeners', function(){
events.un(listener1);
events.emit();
expect(listener1.calls.count()).toBe(1);
expect(listener2.calls.count()).toBe(2);
});
xit('allows an event to be removed by an id', function(){
var events = singleEventPubSub('someEventName'),
listener1 = jasmine.createSpy('listener1'),
listener2 = jasmine.createSpy('listener2');
events.on(listener1, 'FOO_ID');
events.on(listener2, 'BAR_ID');
events.emit();
expect(listener1.calls.count()).toBe(1);
events.un('FOO_ID');
events.emit();
expect(listener1.calls.count()).toBe(1);
expect(listener2.calls.count()).toBe(2);
events.un('BAR_ID');
events.emit();
expect(listener2.calls.count()).toBe(2);
})
xit('does not fire removeListener if nothing is removed', function() {
var newListener = singleEventPubSub('newListener'),
removeListener = singleEventPubSub('removeListener'),
events = singleEventPubSub('someEventName', newListener, removeListener),
removeListenerListener = jasmine.createSpy('removeListenerListener'),
fooListener = jasmine.createSpy('fooListener');
removeListener.on(removeListenerListener);
events.on(fooListener);
events.un('wrong_item');
expect(removeListenerListener).not.toHaveBeenCalled();
});
xit('fires removeListener when a listener is removed', function(){
var newListener = singleEventPubSub('newListener'),
removeListener = singleEventPubSub('removeListener'),
events = singleEventPubSub('someEventName', newListener, removeListener),
removeListenerListener = jasmine.createSpy('removeListenerListener'),
fooListener = jasmine.createSpy('fooListener');
removeListener.on(removeListenerListener);
events.on(fooListener);
events.un(fooListener);
expect(removeListenerListener).toHaveBeenCalled();
});
xit('does not crash if asked to emit without listeners', function(){
var events = singleEventPubSub('someEventName');
expect(function(){
events.emit('unknown event');
}).not.toThrow();
});
describe('listeners method', function(){
xit('can return listeners when there haven\'t been any', function(){
var events = singleEventPubSub('someEventName');
expect(function(){
events.emit('unknown event');
}).not.toThrow();
});
describe('listeners method', function(){
it('can return listeners when there haven\'t been any', function(){
var events = singleEventPubSub('someEventName');
expect( events.listeners('testEventType') ).toBeFalsy();
})
it('can return listeners when one has been added', function(){
var events = singleEventPubSub('someEventName');
events.on(noop);
expect( events.listeners('testEventType') ).toBeList(list(noop));
})
it('can return listeners when second is added', function(){
var events = singleEventPubSub('someEventName');
events.on(noop);
events.on(noop);
expect( events.listeners('testEventType') ).toBeList(list(noop, noop));
})
it('can return listeners when one is removed', function(){
var events = singleEventPubSub('someEventName');
events.on(noop);
events.on(noop);
events.un(noop);
expect( events.listeners('testEventType') ).toBeList(list(noop));
})
it('can return listeners when all are removed', function(){
var events = singleEventPubSub('someEventName');
events.on(noop);
events.on(noop);
events.un(noop);
events.un(noop);
expect( events.listeners('testEventType') ).toBeFalsy();
})
})
describe('hasListener method', function() {
it('returns false when there are no listeners', function() {
var event = singleEventPubSub('someEventName');
expect( event.hasListener() ).toBeFalsy();
});
expect( events.listeners('testEventType') ).toBeFalsy();
});
it('returns false for an id when there are no listeners', function() {
var event = singleEventPubSub('someEventName');
xit('can return listeners when one has been added', function(){
var events = singleEventPubSub('someEventName');
expect( event.hasListener('some_id') ).toBeFalsy();
});
events.on(noop);
it('returns true for a listener specified as the function', function() {
function exampleListener(){}
var event = singleEventPubSub('someEventName');
event.on(exampleListener);
expect( events.listeners('testEventType') ).toBeList(list(noop));
});
expect( event.hasListener(exampleListener) ).toBeTruthy();
});
it('can return listeners when second is added', function(){
var events = singleEventPubSub('someEventName');
it('returns true for a listener specified using an id', function() {
function exampleListener(){}
events.on(noop);
events.on(noop);
var event = singleEventPubSub('someEventName');
event.on(exampleListener, 'exampleId');
expect( events.listeners('testEventType') ).toBeList(list(noop, noop));
});
expect( event.hasListener('exampleId') ).toBeTruthy();
});
xit('can return listeners when one is removed', function(){
var events = singleEventPubSub('someEventName');
it('returns false given the wrong id', function() {
function exampleListener(){}
events.on(noop);
events.on(noop);
events.un(noop);
var event = singleEventPubSub('someEventName');
event.on(exampleListener, 'exampleId');
expect( events.listeners('testEventType') ).toBeList(list(noop));
});
expect( event.hasListener('wrongId') ).toBeFalsy();
});
});
xit('can return listeners when all are removed', function(){
var events = singleEventPubSub('someEventName');
events.on(noop);
events.on(noop);
events.un(noop);
events.un(noop);
expect( events.listeners('testEventType') ).toBeFalsy();
});
});
describe('hasListener method', function() {
xit('returns false when there are no listeners', function() {
var event = singleEventPubSub('someEventName');
expect( event.hasListener() ).toBeFalsy();
});
xit('returns false for an id when there are no listeners', function() {
var event = singleEventPubSub('someEventName');
expect( event.hasListener('some_id') ).toBeFalsy();
});
xit('returns true for a listener specified as the function', function() {
function exampleListener(){}
var event = singleEventPubSub('someEventName');
event.on(exampleListener);
expect( event.hasListener(exampleListener) ).toBeTruthy();
});
xit('returns true for a listener specified using an id', function() {
function exampleListener(){}
var event = singleEventPubSub('someEventName');
event.on(exampleListener, 'exampleId');
expect( event.hasListener('exampleId') ).toBeTruthy();
});
xit('returns false given the wrong id', function() {
function exampleListener(){}
var event = singleEventPubSub('someEventName');
event.on(exampleListener, 'exampleId');
expect( event.hasListener('wrongId') ).toBeFalsy();
});
});
});

@@ -1,2 +0,2 @@

/* Tests the streaming xhr without stubbing anything. Really just a test that
/* Tests the streaming xhr without stubbing anything. Really just a test that
* we've got the interface of the in-browser XHR object pinned down */

@@ -7,110 +7,111 @@

"use strict";
var emittedEvents = [HTTP_START, STREAM_DATA, STREAM_END, FAIL_EVENT, ABORTING];
it('completes', function() {
it('completes', function(done) {
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
'GET',
'GET',
'/testServer/static/json/smallestPossible.json',
null // this is a GET, no data to send
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus)
);
waitUntil(STREAM_END).isFiredOn(oboeBus, done);
})
it('can ajax in a small known file', function() {
it('can ajax in a small known file', function(done) {
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
'GET',
'GET',
'/testServer/static/json/smallestPossible.json',
null // this is a GET, no data to send
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
);
runs(function(){
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
expect(streamedContentPassedTo(oboeBus)).toParseTo({})
});
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
expect(streamedContentPassedTo(oboeBus)).toParseTo({})
done();
});
})
it('fires HTTP_START with status and headers', function() {
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
it('fires HTTP_START with status and headers', function(done) {
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
'GET',
'GET',
'/testServer/echoBackHeadersAsHeaders',
null, // this is a GET, no data to send
{'specialheader':'specialValue'}
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
);
runs(function(){
expect(oboeBus(HTTP_START).emit)
.toHaveBeenCalledWith(
200,
headerObjectContaining('specialheader', 'specialValue')
);
});
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
expect(oboeBus(HTTP_START).emit)
.toHaveBeenCalledWith(
200,
jasmine.objectContaining({
'specialheader': 'specialValue'
})
);
done();
});
})
it('gives XHR header so server knows this is an xhr request', function() {
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
it('gives XHR header so server knows this is an xhr request', function(done) {
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
'GET',
'GET',
'/testServer/echoBackHeadersAsHeaders'
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
);
runs(function(){
expect(oboeBus(HTTP_START).emit)
.toHaveBeenCalledWith(
200,
headerObjectContaining('X-Requested-With', 'XMLHttpRequest')
);
});
})
it('fires HTTP_START, STREAM_DATA and STREAM_END in correct order', function() {
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
expect(oboeBus(HTTP_START).emit)
.toHaveBeenCalledWith(
200,
jasmine.objectContaining({
'x-requested-with': 'XMLHttpRequest'
})
);
done();
});
})
it('fires HTTP_START, STREAM_DATA and STREAM_END in correct order', function(done) {
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
'GET',
'GET',
'/testServer/echoBackHeadersAsHeaders',
null, // this is a GET, no data to send
{'specialheader':'specialValue'}
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
);
runs(function(){
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
});
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
done();
});
})
it('fires FAIL_EVENT if url does not exist', function () {
it('fires FAIL_EVENT if url does not exist', function(done) {
var oboeBus = fakePubSub(emittedEvents)

@@ -125,65 +126,60 @@ streamingHttp(

waitUntil(FAIL_EVENT).isFiredOn(oboeBus);
waitUntil(FAIL_EVENT).isFiredOn(oboeBus, done);
})
it('can ajax in a very large file without missing any', function() {
it('can ajax in a very large file without missing any', function(done) {
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
'GET',
httpTransport(),
'GET',
'/testServer/static/json/twentyThousandRecords.json',
null // this is a GET, no data to send
null // this is a GET, no data to send
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
runs(function(){
var parsedResult;
expect(function(){
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
var runFunction = function() {
return JSON.parse(streamedContentPassedTo(oboeBus));
};
expect(runFunction).not.toThrow();
var parsedResult = runFunction();
parsedResult = JSON.parse(streamedContentPassedTo(oboeBus));
}).not.toThrow();
// as per the name, should have 20,000 records in that file:
expect(parsedResult.result.length).toEqual(20000);
done()
});
})
// as per the name, should have 20,000 records in that file:
expect(parsedResult.result.length).toEqual(20000);
});
})
it('can ajax in a streaming file without missing any', function() {
it('can ajax in a streaming file without missing any', function(done) {
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
'GET',
httpTransport(),
'GET',
'/testServer/tenSlowNumbers?withoutMissingAny',
null // this is a GET, no data to send
null // this is a GET, no data to send
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
runs(function(){
// as per the name, should have ten numbers in that file:
expect(streamedContentPassedTo(oboeBus)).toParseTo([0,1,2,3,4,5,6,7,8,9]);
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
});
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
// as per the name, should have ten numbers in that file:
expect(streamedContentPassedTo(oboeBus)).toParseTo([0,1,2,3,4,5,6,7,8,9]);
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
done();
});
})
it('sends cookies by default', function() {
it('sends cookies by default', function(done) {
document.cookie = "token=123456; path=/";
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)

@@ -198,13 +194,12 @@ streamingHttp(

waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
var parsedResult = JSON.parse(streamedContentPassedTo(oboeBus));
expect(parsedResult.cookie).toMatch('token=123456');
done();
});
runs(function(){
var parsedResult = JSON.parse(streamedContentPassedTo(oboeBus));
expect(parsedResult.cookie).toMatch('token=123456');
});
})
it('does not send cookies by default to cross-domain requests', function() {
it('does not send cookies by default to cross-domain requests', function(done) {
document.cookie = "deniedToken=123456; path=/";

@@ -223,11 +218,10 @@

waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
runs(function(){
var parsedResult = JSON.parse(streamedContentPassedTo(oboeBus));
expect(parsedResult.cookie).not.toMatch('deniedToken=123456');
});
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
var parsedResult = JSON.parse(streamedContentPassedTo(oboeBus));
expect(parsedResult.cookie).not.toMatch('deniedToken=123456');
done();
});
})
it('sends cookies to cross-domain requests if withCredentials is true', function() {
it('sends cookies to cross-domain requests if withCredentials is true', function(done) {

@@ -249,61 +243,58 @@ document.cookie = "corsToken=123456; path=/";

waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
var parsedResult = JSON.parse(streamedContentPassedTo(oboeBus));
expect(parsedResult.cookie).toMatch('corsToken=123456');
done();
});
})
runs(function(){
var parsedResult = JSON.parse(streamedContentPassedTo(oboeBus));
expect(parsedResult.cookie).toMatch('corsToken=123456');
});
})
it('can make a post request', function() {
it('can make a post request', function(done) {
var payload = {'thisWill':'bePosted','andShould':'be','echoed':'back'};
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
httpTransport(),
'POST',
'/testServer/echoBackBody',
JSON.stringify(payload)
JSON.stringify(payload)
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
runs(function(){
expect(streamedContentPassedTo(oboeBus)).toParseTo(payload);
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
expect(streamedContentPassedTo(oboeBus)).toParseTo(payload);
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
done();
});
})
it('can make a put request', function() {
it('can make a put request', function(done) {
var payload = {'thisWill':'bePut','andShould':'be','echoed':'back'};
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
httpTransport(),
'PUT',
'/testServer/echoBackBody',
JSON.stringify(payload)
JSON.stringify(payload)
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
runs(function(){
expect(streamedContentPassedTo(oboeBus)).toParseTo(payload);
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
expect(streamedContentPassedTo(oboeBus)).toParseTo(payload);
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
done();
});
})
it('can make a patch request', function() {
})
it('can make a patch request', function(done) {
if( Platform.isInternetExplorer ) {

@@ -313,209 +304,226 @@ console.warn('PATCH requests don\'t work well under IE. Skipping PATCH integration test');

}
var payload = {'thisWill':'bePatched','andShould':'be','echoed':'back'};
// in practice, since we're running on an internal network and this is a small file,
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
// we'll probably only get one callback
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
httpTransport(),
'PATCH',
'/testServer/echoBackBody',
JSON.stringify(payload)
JSON.stringify(payload)
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
runs(function(){
if( streamedContentPassedTo(oboeBus) == '' &&
(Platform.isPhantom) ) {
console.warn( 'this user agent seems not to support giving content'
+ ' back for of PATCH requests.'
+ ' This happens on PhantomJS');
} else {
expect(streamedContentPassedTo(oboeBus)).toParseTo(payload);
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder();
}
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
if( streamedContentPassedTo(oboeBus) == '' &&
(Platform.isPhantom) ) {
console.warn( 'this user agent seems not to support giving content'
+ ' back for of PATCH requests.'
+ ' This happens on PhantomJS');
} else {
expect(streamedContentPassedTo(oboeBus)).toParseTo(payload);
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder();
}
done();
});
})
// this test is only activated for non-IE browsers and IE 10 or newer.
// old and rubbish browsers buffer the xhr response meaning that this
// old and rubbish browsers buffer the xhr response meaning that this
// will never pass. But for good browsers it is good to have an integration
// test to confirm that we're getting it right.
if( !Platform.isInternetExplorer || Platform.isInternetExplorer >= 10 ) {
it('gives multiple callbacks when loading a streaming resource', function() {
// test to confirm that we're getting it right.
if( !Platform.isInternetExplorer || Platform.isInternetExplorer >= 10 ) {
it('gives multiple callbacks when loading a streaming resource', function(done) {
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
streamingHttp(
oboeBus,
httpTransport(),
httpTransport(),
'GET',
'/testServer/tenSlowNumbers',
null // this is a get: no data to send
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
runs(function(){
// realistically, should have had 10 or 20, but this isn't deterministic so
// 3 is enough to indicate the results didn't all arrive in one big blob.
expect(oboeBus.callCount[STREAM_DATA]).toBeGreaterThan(3)
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
});
null // this is a get: no data to send
);
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
// realistically, should have had 10 or 20, but this isn't deterministic so
// 3 is enough to indicate the results didn't all arrive in one big blob.
expect(oboeBus.callCount[STREAM_DATA]).toBeGreaterThan(3)
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder()
done();
});
})
it('gives multiple callbacks when loading a gzipped streaming resource', function() {
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
// TODO, this test should work
xit('gives multiple callbacks when loading a gzipped streaming resource', function(done) {
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
httpTransport(),
'GET',
'/testServer/gzippedTwoHundredItems',
null // this is a get: no data to send
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus);
runs(function(){
// some platforms can't help but not work here so warn but don't
// fail the test:
if( oboeBus.callCount[STREAM_DATA] == 1 &&
(Platform.isInternetExplorer || Platform.isPhantom) ) {
console.warn('This user agent seems to give gzipped responses' +
'as a single event, not progressively. This happens on ' +
'PhantomJS and IE < 9');
} else {
expect(oboeBus.callCount[STREAM_DATA]).toBeGreaterThan(1);
}
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder();
});
})
null // this is a get: no data to send
);
waitUntil(STREAM_END).isFiredOn(oboeBus, function() {
// some platforms can't help but not work here so warn but don't
// fail the test:
if( oboeBus.callCount[STREAM_DATA] == 1 &&
(Platform.isInternetExplorer || Platform.isPhantom) ) {
console.warn('This user agent seems to give gzipped responses' +
'as a single event, not progressively. This happens on ' +
'PhantomJS and IE < 9');
} else {
expect(oboeBus.callCount[STREAM_DATA]).toBeGreaterThan(1);
}
expect(oboeBus).toHaveGivenStreamEventsInCorrectOrder();
done();
});
})
}
it('does not call back with zero-length bites', function() {
// since this is a large file, even serving locally we're going to get multiple callbacks:
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
// TODO test should pass
xit('does not call back with zero-length bites', function(done) {
// since this is a large file, even serving locally we're going to get multiple callbacks:
var oboeBus = fakePubSub(emittedEvents)
streamingHttp(
oboeBus,
httpTransport(),
'GET',
httpTransport(),
'GET',
'/testServer/static/json/oneHundredRecords.json',
null // this is a GET: no data to send
);
null // this is a GET: no data to send
);
waitUntil(STREAM_END, 'the stream to end').isFiredOn(oboeBus)
runs(function(){
var dripsReceived = oboeBus.eventTypesEmitted[STREAM_DATA].map(function( args ){
return args[0];
});
expect(dripsReceived.length).toBeGreaterThan(0);
dripsReceived.forEach(function(drip) {
expect(drip.length).not.toEqual(0);
});
})
})
waitUntil(STREAM_END).isFiredOn(oboeBus, function () {
function waitUntil(event, messageName) {
return {isFiredOn: function (eventBus){
waitsFor(function(){
return !!eventBus(event).emit.calls.length;
}, 'event ' + event + (messageName?'('+messageName+')':'') + ' to be fired', ASYNC_TEST_TIMEOUT);
}
var dripsReceived = oboeBus.eventTypesEmitted[STREAM_DATA].map(function( args ){
return args[0];
});
expect(dripsReceived.length).toBeGreaterThan(0);
dripsReceived.forEach(function(drip) {
expect(drip.length).not.toEqual(0);
});
});
})
// This is the equivalent of the old waitsFor/runs syntax
// which was removed from Jasmine 2
function waitsForAndRuns(escapeFunction, runFunction, escapeTime) {
// check the escapeFunction every millisecond so as soon as it is met we can escape the function
var interval = setInterval(function() {
if (escapeFunction()) {
clearMe();
runFunction();
}
}
}, 1);
// in case we never reach the escapeFunction, we will time out
// at the escapeTime
var timeOut = setTimeout(function() {
clearMe();
runFunction();
}, escapeTime);
// clear the interval and the timeout
function clearMe(){
clearInterval(interval);
clearTimeout(timeOut);
}
};
function waitUntil(event) {
return {
isFiredOn: function (eventBus, cb){
waitsForAndRuns(function() {
return !!eventBus(event).emit.calls.count();
}, cb, ASYNC_TEST_TIMEOUT);
}
};
}
function streamedContentPassedTo(eventBus){
return eventBus.eventTypesEmitted[STREAM_DATA].map(function(args){
return args[0];
}).join('');
}).join('');
}
beforeEach(function(){
this.addMatchers({
jasmine.addMatchers({
toHaveGivenStreamEventsInCorrectOrder: function(){
var eventNames = this.actual.eventNames;
this.message = function(){
return 'events not in correct order. We have: ' +
JSON.stringify(
eventNames.map(prettyPrintEvent)
) + ' but should follow "start", "data"*, "end"'
};
return eventNames[0] === HTTP_START
&& eventNames[1] === STREAM_DATA
&& eventNames[eventNames.length-1] === STREAM_END;
return {
compare: function(actual, expected) {
var result = {};
var eventNames = actual.eventNames;
result.pass = eventNames[0] === HTTP_START
&& eventNames[1] === STREAM_DATA
&& eventNames[eventNames.length-1] === STREAM_END;
if(!result.pass) {
result.message = 'events not in correct order. We have: ' +
JSON.stringify(
eventNames.map(prettyPrintEvent)
) + ' but should follow "start", "data"*, "end"';
}
return result;
}
};
},
toParseTo:function( expectedObj ){
var actual = this.actual;
var normalisedActual;
if( !actual ) {
this.message = function(){
return 'no content has been received';
toParseTo: function(){
return {
compare: function(actual, expected) {
var result = {};
var normalisedActual;
if( !actual ) {
result.pass = false;
result.message = 'no content has been received';
return result;
}
return false;
}
try{
normalisedActual = JSON.stringify( JSON.parse(actual) );
}catch(e){
this.message = function(){
return "Expected to be able to parse the found " +
"content as json '" + actual + "' but it " +
"could not be parsed";
try {
normalisedActual = JSON.stringify( JSON.parse(actual) );
} catch (e) {
result.pass = false;
result.message = "Expected to be able to parse the found " +
"content as json '" + actual + "' but it " +
"could not be parsed";
return result;
}
return false;
}
this.message = function(){
return "The found json parsed but did not match " + JSON.stringify(expectedObj) +
" because found " + this.actual;
}
return (normalisedActual === JSON.stringify(expectedObj));
result.pass = normalisedActual === JSON.stringify(expected);
if (!result.pass) {
result.message = "The found json parsed but did not match " + JSON.stringify(expected) +
" because found " + this.actual;
}
return result;
}
};
}
});
});
function headerObjectContaining(key, val) {
// some browsers lowercase the header keys. Compare upper and lower
// case versions:
return {
jasmineMatches: function(obj){
return obj[key] == val || obj[key.toLowerCase()] == val;
}
}
}
});
module.exports = function(config) {
config.set({
config.set({
frameworks:['jasmine'],
// base path, that will be used to resolve files and exclude
basePath : '..',
// list of files / patterns to load in the browser
files : [
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
frameworks:['jasmine'],
// base path, that will be used to resolve files and exclude
basePath : '..',
// list of files / patterns to load in the browser
files : [
'test/libs/es5-shim.js'
, 'test/libs/es5-sham.js'
, 'src/functional.js'
, 'src/util.js'
, 'src/lists.js'
, 'src/lists.js'
, 'test/libs/sinon.js'

@@ -21,3 +21,3 @@ , 'test/libs/sinon-ie.js'

, 'src/ascentManager.js'
, 'src/parseResponseHeaders.browser.js'
, 'src/parseResponseHeaders.browser.js'
, 'src/detectCrossOrigin.browser.js'

@@ -29,4 +29,4 @@ , 'src/streamingHttp.browser.js'

, 'src/jsonPath.js'
, 'src/singleEventPubSub.js'
, 'src/pubSub.js'
, 'src/singleEventPubSub.js'
, 'src/pubSub.js'
, 'src/events.js'

@@ -38,18 +38,19 @@ , 'src/patternAdapter.js'

, 'src/publicApi.js'
, 'test/specs/clarinet.unit.spec.js'
, 'test/specs/*.unit.spec.js'
, 'test/specs/*.component.spec.js'
],
// 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 : false,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun : true
, 'test/specs/*.component.spec.js'
],
// 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 : false,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun : true
});
};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc