gradio / node_modules /oboe /test /specs /oboe.component.spec.js
reisarod's picture
Upload folder using huggingface_hub
5fae594 verified
describe("oboe component (sXHR stubbed)", function(){
/*
a more jasmine-y version of the next test might look like this:
describe('empty object detected with bang', function() {
var callback = jasmine.createSpy('callback');
var oboe = anOboe().node('!', callback).afterInput('{}')
giveInput(oboe, '{}');
it( 'should find the empty object at root', function(){
expect(
nodesFoundBy(
anOboe().listeningForNodesAt('!').afterInput('{}')
)
).toIncludeNode( {}, atRoot )
})
it( 'should not find anything else', function(){
expect(
nodesFoundBy(
anOboe().listeningForNodes('!').afterInput('{}')
).length
).toBe(1)
})
})*/
it('handles empty object detected with bang', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
it('handles empty object detected with bang when explicitly selected', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('$!')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
it('gives the oboe instance as context', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!')
.whenGivenInput('{}')
.thenTheInstance( wasGivenTheOboeAsContext() );
})
it('find only emits when has whole object', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!')
.whenGivenInput('{')
.thenTheInstance(
foundNoMatches
)
.whenGivenInput('}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
it('emits path to listener when root object starts', function() {
// clarinet doesn't notify of matches to objects (onopenobject) until the
// first key is found, that is why we don't just give '{' here as the partial
// input.
givenAnOboeInstance()
.andWeAreListeningForPaths('!')
.whenGivenInput('{"foo":')
.thenTheInstance(
foundNMatches(1),
matched({}).atRootOfJson()
);
})
it('emits path to listener when root array starts', function() {
// clarinet doesn't notify of matches to objects (onopenobject) until the
// first key is found, that is why we don't just give '{' here as the partial
// input.
givenAnOboeInstance()
.andWeAreListeningForPaths('!')
.whenGivenInput('[1') // the minimum string required for clarinet
// to emit onopenarray. Won't emit with '['.
.thenTheInstance(
foundNMatches(1),
matched([]).atRootOfJson()
);
})
it('emits empty object node detected with single star', function() {
// *
givenAnOboeInstance()
.andWeAreListeningForNodes('*')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
it('doesnt detect spurious path off empty object', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.foo.*')
.whenGivenInput( {foo:{}} )
.thenTheInstance(
foundNoMatches
);
})
it('handles empty object detected with double dot', function() {
// *
givenAnOboeInstance()
.andWeAreListeningForNodes('*')
.whenGivenInput('{}')
.thenTheInstance(
matched({}).atRootOfJson(),
foundOneMatch
);
})
it('notifies of strings when listened to', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.string')
.whenGivenInput('{"string":"s"}')
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with hyphen in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.a-string')
.whenGivenInput({"a-string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with underscore in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.a_string')
.whenGivenInput({"a_string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with quoted hyphen in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!["a-string"]')
.whenGivenInput({"a-string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with quoted underscore in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!["a_string"]')
.whenGivenInput({"a_string":"s"})
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with quoted unusual ascii chars in the name', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!["£@$%^"]')
.whenGivenInput({"£@$%^":"s"}) // ridiculous JSON!
.thenTheInstance(
matched("s"),
foundOneMatch
);
})
it('can detect nodes with non-ascii keys', function() {
//pinyin: Wǒ tǎoyàn IE liúlǎn qì!
givenAnOboeInstance()
.andWeAreListeningForNodes('!["我讨厌IE浏览器!"]')
.whenGivenInput({"我讨厌IE浏览器!":"indeed!"}) // ridiculous JSON!
.thenTheInstance(
matched("indeed!"),
foundOneMatch
);
})
it('can detect nodes with non-ascii keys and values', function() {
// hope you have a good unicode font!
givenAnOboeInstance()
.andWeAreListeningForNodes('!["☂"]')
.whenGivenInput({"☂":"☁"}) // ridiculous JSON!
.thenTheInstance(
matched("☁"),
foundOneMatch
);
})
it('notifies of path before given the json value for a property', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.string')
.whenGivenInput('{"string":')
.thenTheInstance(
foundOneMatch
);
})
it('notifies of second property name with incomplete json', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.pencils')
.whenGivenInput('{"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() {
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
);
})
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
);
})
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
);
})
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)
);
})
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)
);
})
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);
})
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);
})
it('notifies of multiple properties of an object without waiting for entire object', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput('{"a":')
.thenTheInstance(
foundNoMatches
)
.whenGivenInput('"A",')
.thenTheInstance(
matched('A').atPath(['a'])
, foundOneMatch
)
.whenGivenInput('"b":"B"}')
.thenTheInstance(
matched('B').atPath(['b'])
, foundNMatches(2)
);
})
it('can get root json as json object is built up', function() {
givenAnOboeInstance()
.whenGivenInput('{"a":')
.thenTheInstance(
hasRootJson({a:undefined})
)
.whenGivenInput('"A",')
.thenTheInstance(
hasRootJson({a:'A'})
)
.whenGivenInput('"b":')
.thenTheInstance(
hasRootJson({a:'A', b:undefined})
)
.whenGivenInput('"B"}')
.thenTheInstance(
hasRootJson({a:'A', b:'B'})
)
.whenInputFinishes()
.thenTheInstance(
gaveFinalCallbackWithRootJson({a:'A', b:'B'})
);
})
it('can notify progressively as root json array is built up', function() {
// let's feed it the array [11,22] in drips of one or two chars at a time:
givenAnOboeInstance()
.whenGivenInput('[')
.thenTheInstance(
// I would like this to be [] but clarinet doesn't emit array found until it has seen
// the first element
hasRootJson(undefined)
)
.whenGivenInput('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([])
)
.whenGivenInput('1,')
.thenTheInstance(
hasRootJson([11])
)
.whenGivenInput('2')
.thenTheInstance(
hasRootJson([11])
)
.whenGivenInput('2]')
.thenTheInstance(
hasRootJson([11,22])
)
.whenInputFinishes()
.thenTheInstance(
gaveFinalCallbackWithRootJson([11,22])
);
})
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() {
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() {
givenAnOboeInstance()
.andWeAreListeningForPaths('!.testArray')
.whenGivenInput('{"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')
.whenGivenInput('{"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)
);
})
describe('correctly give index inside arrays', function(){
it('when finding objects in array', function() {
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)
);
})
it('when finding arrays inside arrays etc', function() {
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)
);
})
it('when finding numbers inside array', function() {
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)
);
})
})
it('notifies of paths inside objects', function() {
givenAnOboeInstance()
.andWeAreListeningForPaths('![*]')
.whenGivenInput( {a:{}, b:'b', c:2, d:[]} )
.thenTheInstance(
foundNMatches(4)
);
})
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])
, foundOneMatch
);
})
it('notifies nested array elements', function() {
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"]
])
, foundOneMatch
);
})
});
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'})
, matched({d:"e"})
.atPath(['a', 'b', 'c'])
, matched({c:{d:"e"}})
.atPath(['a', 'b'])
, matched({b:{c:{d:"e"}}})
.atPath(['a'])
, matched({a:{b:{c:{d:"e"}}}})
.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'})
, matched({d:"e"})
.atPath(['a', 'b', 'c'])
, matched({c:{d:"e"}})
.atPath(['a', 'b'])
, matched({b:{c:{d:"e"}}})
.atPath(['a'])
, matched({a:{b:{c:{d:"e"}}}})
.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'})
, matched({d:"e"})
.atPath(['a', 'b', 'c'])
, matched({c:{d:"e"}})
.atPath(['a', 'b'])
, matched({b:{c:{d:"e"}}})
.atPath(['a'])
, matched({a:{b:{c:{d:"e"}}}})
.atRootOfJson()
, foundNMatches(5)
);
})
});
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'])
, foundNMatches(4)
);
})
it('can detect similar ancestors', function() {
givenAnOboeInstance()
.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)
);
})
it('can detect inside the second object element of an array', function() {
// this fails in incrementalJsonBuilder if we don't set the curKey to the
// length of the array when we detect an object and and the parent of the
// object that ended was an array
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() {
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() {
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() {
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() {
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
);
})
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']})
, matched('b')
.withParent(['a', 'b'])
.withGrandparent({array:['a','b']})
, matched('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}])
, matched({'b':2})
.withParent([{'a':1},{'b':2}])
, matched({'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}])
);
})
it('has correct parent for string in mixed array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
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(
matched({'c':3})
.withParent([{'a':1},'b',{'c':3}])
);
})
it('has correct parent for empty object in mixed array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
matched({})
.withParent([{'a':1},'b',{'c':3}, {}])
);
})
it('has correct parent for singleton string array in mixed array', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!.array.*')
.whenGivenInput({
array : [{'a':1},'b',{'c':3}, {}, ['d'], 'e']
}
)
.thenTheInstance(
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(
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(
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:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
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:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
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:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched({'c':3})
.withParent([{'a':1},'b',{'c':3}])
);
})
it('gives correct parent for empty object in a mixed array at root of json', function() {
// same test as above but without the object wrapper around the array:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched({})
.withParent([{'a':1},'b',{'c':3}, {}])
);
})
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:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
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:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([['d']])
.thenTheInstance(
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:
givenAnOboeInstance()
.andWeAreListeningForNodes('!.*')
.whenGivenInput([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
.thenTheInstance(
matched('e')
.withParent([{'a':1},'b',{'c':3}, {}, ['d'], 'e'])
);
})
});
it('can detect at multiple depths using double dot', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..find')
.whenGivenInput({
array:[
{find:'first_find'}
, {padding:{find:'second_find'}, find:'third_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'])
, foundNMatches(5)
);
})
it('passes ancestors of found object correctly', function() {
givenAnOboeInstance()
.andWeAreListeningForNodes('!..find')
.whenGivenInput({
array:[
{find:'first_find'}
, {padding:{find:'second_find'}, find:'third_find'}
]
, find: {
find:'fourth_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('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() {
givenAnOboeInstance()
.andWeAreListeningForNodes('find')
.whenGivenInput({
array:[
{find:'first_find'}
, {padding:{find:'second_find'}, find:'third_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'])
, foundNMatches(5)
);
})
it('matches nested adjacent selector', function() {
givenAnOboeInstance()
.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() {
givenAnOboeInstance()
.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() {
// 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'})
);
})
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'}])
);
})
it('matches nested selector separated by double dot', function() {
givenAnOboeInstance()
// 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)
);
})
describe('duck types', function(){
// only smoke-testing duck types here, tested thoroughly in jsonpath unit tests
it('can detect', function() {
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)
);
})
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({
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() {
it('notifies of error given unquoted string keys', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInput('{invalid:"json"}') // key not quoted, invalid json
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('errors on malformed json', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInput('{{') // invalid!
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('detects error when stream halts early between children of root', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInput('[[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()
.whenGivenInput('[[1,2,3],')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('detects error when stream halts early inside mid-tree node', function() {
givenAnOboeInstance()
.andWeAreExpectingSomeErrors()
.whenGivenInput('[[1,2,3')
.whenInputFinishes()
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
it('calls error listener if an error is thrown in the callback', function() {
givenAnOboeInstance()
.andWeHaveAFaultyCallbackListeningFor('!') // just want the root object
.andWeAreExpectingSomeErrors()
.whenGivenInput('{}') // valid json, should provide callback
.thenTheInstance
( calledCallbackOnce
, wasPassedAnErrorObject
);
})
});
describe('aborting a request', function(){
it('does not throw an error', function(){
expect( function(){
givenAnOboeInstance()
.andWeAreListeningForPaths('*')
.whenGivenInput('[1')
.andWeAbortTheRequest();
}).not.toThrow();
});
it('can abort once some data has been found in response', function() {
// 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])
);
})
});
beforeEach(function() {
sinon.stub(window, 'streamingHttp')
.returns(sinon.stub());
})
afterEach(function() {
window.streamingHttp.restore();
})
});