|
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; |
|
} |
|
|
|
}); |