| describe("incremental content builder", function(){ |
|
|
| function IncrementalContentBuilderAsserter(){ |
| |
| var eventBus = pubSub(); |
| |
| sinon.spy(eventBus(NODE_FOUND), 'emit'); |
| sinon.spy(eventBus(NODE_FOUND), 'on'); |
| sinon.spy(eventBus(PATH_FOUND), 'emit'); |
| sinon.spy(eventBus(PATH_FOUND), 'on'); |
| sinon.spy(eventBus(ROOT_FOUND), 'emit'); |
| sinon.spy(eventBus(ROOT_FOUND), 'on'); |
| |
| this._clarinetStub = {}; |
| this._eventBus = eventBus; |
| |
| var builderInstance = incrementalContentBuilder(eventBus, this._clarinetStub); |
| |
| clarinetListenerAdaptor( this._clarinetStub, builderInstance); |
| } |
| |
| IncrementalContentBuilderAsserter.prototype.receivingParserEvent = function(fnName ){ |
| |
| var args = Array.prototype.slice.call(arguments, 1); |
| |
| var handlerFn = this._clarinetStub[fnName]; |
| |
| |
| handlerFn && handlerFn.apply( undefined, args ); |
| |
| return this; |
| }; |
| |
| describe('when root object opens', function() { |
| |
| var builder = aContentBuilder().receivingParserEvent('onopenobject'); |
| |
| it('emits correct event', function(){ |
| expect( builder) |
| .toHaveEmitted( |
| PATH_FOUND |
| , anAscentContaining( |
| {key:ROOT_PATH, node:{}} |
| ) |
| |
| ) |
| }); |
|
|
| it('reports correct root', function () { |
|
|
| expect(builder).toHaveEmittedRootWhichIsNow({}) |
|
|
| }); |
| }) |
| |
| describe('after key is found in root object', function(){ |
| |
| var builder = aContentBuilder() |
| .receivingParserEvent('onopenobject') |
| .receivingParserEvent('onkey', 'flavour'); |
| |
| it('emits correct event', function(){ |
|
|
| expect( builder ) |
| .toHaveEmitted( |
| PATH_FOUND |
| , 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() { |
| |
|
|
| var builder = aContentBuilder() |
| .receivingParserEvent('onopenobject', 'flavour'); |
| |
| it('emits correct event', function(){ |
| |
| expect(builder).toHaveEmitted( |
| PATH_FOUND |
| , 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() |
| .receivingParserEvent('onopenobject') |
| .receivingParserEvent('onkey' , 'flavour') |
| .receivingParserEvent('onvalue' , 'strawberry'); |
| |
| it('emits correct event', function(){ |
| expect(builder).toHaveEmitted( |
| NODE_FOUND |
| , 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() |
| .receivingParserEvent('onopenobject') |
| .receivingParserEvent('onkey', 'flavour') |
| .receivingParserEvent('onvalue', 'strawberry') |
| .receivingParserEvent('oncloseobject'); |
| |
| it('emits correct event', function(){ |
| expect(builder).toHaveEmitted( |
| NODE_FOUND |
| , 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() |
| .receivingParserEvent('onopenobject') |
| .receivingParserEvent('onkey', 'alphabet') |
| .receivingParserEvent('onopenarray') |
| .receivingParserEvent('onvalue', 'a'); |
| |
| it('emits path event with numeric paths', function(){ |
| |
| expect(builder).toHaveEmitted( |
| PATH_FOUND |
| , anAscentContaining( |
| {key:ROOT_PATH, node:{'alphabet':['a']} } |
| , {key:'alphabet', node:['a'] } |
| , {key:0, node:'a' } |
| ) |
| ); |
| }) |
| |
| it('emitted node event', function(){ |
| expect(builder).toHaveEmitted( |
| NODE_FOUND |
| , 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() |
| .receivingParserEvent('onopenobject') |
| .receivingParserEvent('onkey', 'alphabet') |
| .receivingParserEvent('onopenarray') |
| .receivingParserEvent('onvalue', 'a') |
| .receivingParserEvent('onvalue', 'b'); |
| |
| it('emits events with numeric paths', function(){ |
| |
| expect(builder).toHaveEmitted( |
| PATH_FOUND |
| , 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_FOUND |
| , 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() |
| .receivingParserEvent('onopenarray') |
| .receivingParserEvent('onvalue', 'a') |
| .receivingParserEvent('onvalue', 'b'); |
| |
| it('emits events with numeric paths', function(){ |
| |
| expect(builder).toHaveEmitted( |
| PATH_FOUND |
| , anAscentContaining( |
| {key:ROOT_PATH, node:['a','b'] } |
| , {key:1, node:'b' } |
| ) |
| ) |
| }) |
| |
| it('emitted node event', function(){ |
| expect(builder).toHaveEmitted( |
| NODE_FOUND |
| , 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; |
| var emit = asserter._eventBus(ROOT_FOUND).emit; |
|
|
| return emit.calledWith(expectedRootObj); |
| }, |
| |
| toHaveEmitted: function( eventName, expectedAscent ){ |
| |
| var asserter = this.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 ) { |
| |
| return false; |
| } |
| |
| if( head(expect).key != head(found).key ) { |
| |
| return false; |
| } |
| |
| if( JSON.stringify( head(expect).node ) != JSON.stringify( head(found).node ) ) { |
| |
| 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'; |
| } |
|
|
| 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 ( ) { |
| |
| var ascentArray = Array.prototype.slice.call(arguments), |
| ascentList = emptyList; |
| |
| ascentArray.forEach( function(ascentNode){ |
| ascentList = cons(ascentNode, ascentList); |
| }); |
| |
| return ascentList; |
| } |
|
|
| }); |