module( 'es/bases' ); /* Stubs */ function DocumentBranchNodeStub( items, name, size ) { // Inheritance es.DocumentBranchNode.call( this, items ); // Properties this.name = name; this.size = size; } DocumentBranchNodeStub.prototype.getContentLength = function() { return this.size; }; DocumentBranchNodeStub.prototype.getElementLength = function() { // Mimic document data which has an opening and closing around the content return this.size + 2; }; es.extendClass( DocumentBranchNodeStub, es.DocumentBranchNode ); /* Tests */ test( 'es.DocumentBranchNodeStub.getElementLength', 1, function() { // Test 1 strictEqual( ( new DocumentBranchNodeStub( [], 'a', 0 ) ).getElementLength(), 2, 'DocumentBranchNodeStub.getElementLength() returns initialized length plus 2 for elements' ); } ); // Common stubs var a = new DocumentBranchNodeStub( [], 'a', 0 ), b = new DocumentBranchNodeStub( [], 'b', 1 ), c = new DocumentBranchNodeStub( [], 'c', 2 ), d = new DocumentBranchNodeStub( [], 'd', 3 ), e = new DocumentBranchNodeStub( [], 'e', 4 ), root1 = new DocumentBranchNodeStub( [a, b, c, d, e], 'root1', 20 ); test( 'es.DocumentBranchNode.getRangeFromNode', 6, function() { // Tests 1 .. 6 var getRangeFromNodeTests = [ { 'input': a, 'output': new es.Range( 0, 2 ) }, { 'input': b, 'output': new es.Range( 2, 5 ) }, { 'input': c, 'output': new es.Range( 5, 9 ) }, { 'input': d, 'output': new es.Range( 9, 14 ) }, { 'input': e, 'output': new es.Range( 14, 20 ) }, { 'input': null, 'output': null } ]; for ( var i = 0; i < getRangeFromNodeTests.length; i++ ) { deepEqual( root1.getRangeFromNode( getRangeFromNodeTests[i].input ), getRangeFromNodeTests[i].output, 'getRangeFromNode returns the correct range or null if item is not found' ); } } ); test( 'es.DocumentBranchNode.getNodeFromOffset', 23, function() { // Tests 1 .. 22 var getNodeFromOffsetTests = [ // Test 1 - |[ ] { 'input': -1, 'output': null }, // Test 2 - [| ] { 'input': 0, 'output': root1 }, // Test 3 - [| ] { 'input': 1, 'output': a }, // Test 4 - [| ] { 'input': 2, 'output': root1 }, // Test 5 - [| ] { 'input': 3, 'output': b }, // Test 6 - [ | ] { 'input': 4, 'output': b }, // Test 7 - [ | ] { 'input': 5, 'output': root1 }, // Test 8 - [ | ] { 'input': 6, 'output': c }, // Test 9 - [ | ] { 'input': 7, 'output': c }, // Test 10 - [ | ] { 'input': 8, 'output': c }, // Test 11 - [ | ] { 'input': 9, 'output': root1 }, // Test 12 - [ | ] { 'input': 10, 'output': d }, // Test 13 - [ | ] { 'input': 11, 'output': d }, // Test 14 - [ | ] { 'input': 12, 'output': d }, // Test 15 - [ | ] { 'input': 13, 'output': d }, // Test 16 - [ | ] { 'input': 14, 'output': root1 }, // Test 17 - [ | ] { 'input': 15, 'output': e }, // Test 18 - [ | ] { 'input': 16, 'output': e }, // Test 19 - [ | ] { 'input': 17, 'output': e }, // Test 20 - [ | ] { 'input': 18, 'output': e }, // Test 21 - [ |] { 'input': 19, 'output': e }, // Test 22 - [ |] { 'input': 20, 'output': root1 }, // Test 22 - [ ]| { 'input': 21, 'output': null } ]; for ( var i = 0; i < getNodeFromOffsetTests.length; i++ ) { ok( root1.getNodeFromOffset( getNodeFromOffsetTests[i].input ) === getNodeFromOffsetTests[i].output, 'getNodeFromOffset finds the right item or returns null when out of range ' + '(' + getNodeFromOffsetTests[i].input + ')' ); } } ); test( 'es.DocumentBranchNode.getOffsetFromNode', 6, function() { // Tests 1 .. 6 var getOffsetFromNodeTests = [ { 'input': a, 'output': 0 }, { 'input': b, 'output': 2 }, { 'input': c, 'output': 5 }, { 'input': d, 'output': 9 }, { 'input': e, 'output': 14 }, { 'input': null, 'output': -1 } ]; for ( var i = 0; i < getOffsetFromNodeTests.length; i++ ) { strictEqual( root1.getOffsetFromNode( getOffsetFromNodeTests[i].input ), getOffsetFromNodeTests[i].output, 'getOffsetFromNode finds the right offset or returns -1 when node is not found' ); } } ); test( 'es.DocumentBranchNode.selectNodes', 75, function() { // selectNodes tests // a b c d e f g h a b c d e f g h a b c d e f g h //^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ //0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 // 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8 var f = new DocumentBranchNodeStub( [], 'f', 8 ), g = new DocumentBranchNodeStub( [], 'g', 8 ), h = new DocumentBranchNodeStub( [], 'h', 8 ), root2 = new DocumentBranchNodeStub( [f, g, h], 'root2', 30 ), big = es.DocumentModel.newFromPlainObject( esTest.obj ); // Tests 1 ... 22 // Possible positions are: // * before beginning // * at beginning // * middle // * at end // * past end var selectNodesTests = [ // Complete set of combinations within the same node: // Test 1 { 'node': root2, 'input': new es.Range( 0, 0 ), 'output': [], 'desc': 'Zero-length range before the beginning of a node' }, // Test 2 { 'node': root2, 'input': new es.Range( 0, 1 ), 'output': [{ 'node': f, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 1, 1 ) }], 'desc': 'Range starting before the beginning of a node and ending at the beginning' }, // Test 3 { 'node': root2, 'input': new es.Range( 10, 15 ), 'output': [{ 'node': g, 'range': new es.Range( 0, 4 ), 'globalRange': new es.Range( 11, 15 ) }], 'desc': 'Range starting before the beginning of a node and ending in the middle' }, // Test 4 { 'node': root2, 'input': new es.Range( 20, 29 ), 'output': [{ 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) }], 'desc': 'Range starting before the beginning of a node and ending at the end' }, // Test 5 { 'node': root2, 'input': new es.Range( 0, 10 ), 'output': [{ 'node': f } ], 'desc': 'Range starting before the beginning of a node and ending past the end' }, // Test 6 { 'node': root2, 'input': new es.Range( 11, 11 ), 'output': [{ 'node': g, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 11, 11 ) }], 'desc': 'Zero-length range at the beginning of a node' }, // Test 7 { 'node': root2, 'input': new es.Range( 21, 26 ), 'output': [{ 'node': h, 'range': new es.Range( 0, 5 ), 'globalRange': new es.Range( 21, 26 ) }], 'desc': 'Range starting at the beginning of a node and ending in the middle' }, // Test 8 { 'node': root2, 'input': new es.Range( 1, 9 ), 'output': [{ 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }], 'desc': 'Range starting at the beginning of a node and ending at the end' }, // Test 9 { 'node': root2, 'input': new es.Range( 11, 20 ), 'output': [{ 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) }], 'desc': 'Range starting at the beginning of a node and ending past the end' }, // Test 10 { 'node': root2, 'input': new es.Range( 22, 22 ), 'output': [{ 'node': h, 'range': new es.Range( 1, 1 ), 'globalRange': new es.Range( 22, 22 ) }], 'desc': 'Zero-length range in the middle of a node' }, // Test 11 { 'node': root2, 'input': new es.Range( 2, 7 ), 'output': [{ 'node': f, 'range': new es.Range( 1, 6 ), 'globalRange': new es.Range( 2, 7 ) }], 'desc': 'Range starting and ending in the middle of the same node' }, // Test 12 { 'node': root2, 'input': new es.Range( 13, 19 ), 'output': [{ 'node': g, 'range': new es.Range( 2, 8 ), 'globalRange': new es.Range( 13, 19 ) }], 'desc': 'Range starting in the middle of a node and ending at the end' }, // Test 13 { 'node': root2, 'input': new es.Range( 24, 30 ), 'output': [{ 'node': h, 'range': new es.Range( 3, 8 ), 'globalRange': new es.Range( 24, 29 ) }], 'desc': 'Range starting in the middle of a node and ending past the end' }, // Test 14 { 'node': root2, 'input': new es.Range( 9, 9 ), 'output': [{ 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }], 'desc': 'Zero-length range at the end of a node' }, // Test 15 { 'node': root2, 'input': new es.Range( 19, 20 ), 'output': [{ 'node': g, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 19, 19 ) }], 'desc': 'Range starting at the end of a node and ending past the end' }, // Test 16 { 'node': root2, 'input': new es.Range( 30, 30 ), 'output': [], 'desc': 'Zero-length range past the end of a node' }, // Test 17 { 'node': root2, 'input': new es.Range( 20, 20 ), 'output': [], 'desc': 'Zero-length range between two nodes' }, // Complete set of combinations for cross-node selections. Generated with help of a script // Test 18 { 'node': root2, 'input': new es.Range( 0, 11 ), 'output': [ { 'node': f }, { 'node': g, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 11, 11 ) } ], 'desc': 'Range starting before the beginning of the first node and ending at the beginning of the second node' }, // Test 19 { 'node': root2, 'input': new es.Range( 0, 14 ), 'output': [ { 'node': f }, { 'node': g, 'range': new es.Range( 0, 3 ), 'globalRange': new es.Range( 11, 14 ) } ], 'desc': 'Range starting before the beginning of the first node and ending in the middle of the second node' }, // Test 20 { 'node': root2, 'input': new es.Range( 0, 19 ), 'output': [ { 'node': f }, { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) } ], 'desc': 'Range starting before the beginning of the first node and ending at the end of the second node' }, // Test 21 { 'node': root2, 'input': new es.Range( 0, 20 ), 'output': [ { 'node': f }, { 'node': g } ], 'desc': 'Range starting before the beginning of the first node and ending between the second and the third node' }, // Test 22 { 'node': root2, 'input': new es.Range( 0, 21 ), 'output': [ { 'node': f }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 21, 21 ) } ], 'desc': 'Range starting before the beginning of the first node and ending at the beginning of the third node' }, // Test 23 { 'node': root2, 'input': new es.Range( 0, 27 ), 'output': [ { 'node': f }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 6 ), 'globalRange': new es.Range( 21, 27 ) } ], 'desc': 'Range starting before the beginning of the first node and ending in the middle of the third node' }, // Test 24 { 'node': root2, 'input': new es.Range( 0, 29 ), 'output': [ { 'node': f }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) } ], 'desc': 'Range starting before the beginning of the first node and ending at the end of the third node' }, // Test 25 { 'node': root2, 'input': new es.Range( 0, 30 ), 'output': [ { 'node': f }, { 'node': g }, { 'node': h } ], 'desc': 'Range starting before the beginning of the first node and ending past the end of the third node' }, // Test 26 { 'node': root2, 'input': new es.Range( 1, 11 ), 'output': [ { 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }, { 'node': g, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 11, 11 ) } ], 'desc': 'Range starting at the beginning of the first node and ending at the beginning of the second node' }, // Test 27 { 'node': root2, 'input': new es.Range( 1, 14 ), 'output': [ { 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }, { 'node': g, 'range': new es.Range( 0, 3 ), 'globalRange': new es.Range( 11, 14 ) } ], 'desc': 'Range starting at the beginning of the first node and ending in the middle of the second node' }, // Test 28 { 'node': root2, 'input': new es.Range( 1, 19 ), 'output': [ { 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }, { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) } ], 'desc': 'Range starting at the beginning of the first node and ending at the end of the second node' }, // Test 29 { 'node': root2, 'input': new es.Range( 1, 20 ), 'output': [ { 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }, { 'node': g } ], 'desc': 'Range starting at the beginning of the first node and ending between the second and the third node' }, // Test 30 { 'node': root2, 'input': new es.Range( 1, 21 ), 'output': [ { 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 21, 21 ) } ], 'desc': 'Range starting at the beginning of the first node and ending at the beginning of the third node' }, // Test 31 { 'node': root2, 'input': new es.Range( 1, 27 ), 'output': [ { 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 6 ), 'globalRange': new es.Range( 21, 27 ) } ], 'desc': 'Range starting at the beginning of the first node and ending in the middle of the third node' }, // Test 32 { 'node': root2, 'input': new es.Range( 1, 29 ), 'output': [ { 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) } ], 'desc': 'Range starting at the beginning of the first node and ending at the end of the third node' }, // Test 33 { 'node': root2, 'input': new es.Range( 1, 30 ), 'output': [ { 'node': f, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 1, 9 ) }, { 'node': g }, { 'node': h } ], 'desc': 'Range starting at the beginning of the first node and ending past the end of the third node' }, // Test 34 { 'node': root2, 'input': new es.Range( 5, 11 ), 'output': [ { 'node': f, 'range': new es.Range( 4, 8 ), 'globalRange': new es.Range( 5, 9 ) }, { 'node': g, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 11, 11 ) } ], 'desc': 'Range starting in the middle of the first node and ending at the beginning of the second node' }, // Test 35 { 'node': root2, 'input': new es.Range( 5, 14 ), 'output': [ { 'node': f, 'range': new es.Range( 4, 8 ), 'globalRange': new es.Range( 5, 9 ) }, { 'node': g, 'range': new es.Range( 0, 3 ), 'globalRange': new es.Range( 11, 14 ) } ], 'desc': 'Range starting in the middle of the first node and ending in the middle of the second node' }, // Test 36 { 'node': root2, 'input': new es.Range( 5, 19 ), 'output': [ { 'node': f, 'range': new es.Range( 4, 8 ), 'globalRange': new es.Range( 5, 9 ) }, { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) } ], 'desc': 'Range starting in the middle of the first node and ending at the end of the second node' }, // Test 37 { 'node': root2, 'input': new es.Range( 5, 20 ), 'output': [ { 'node': f, 'range': new es.Range( 4, 8 ), 'globalRange': new es.Range( 5, 9 ) }, { 'node': g } ], 'desc': 'Range starting in the middle of the first node and ending between the second and the third node' }, // Test 38 { 'node': root2, 'input': new es.Range( 5, 21 ), 'output': [ { 'node': f, 'range': new es.Range( 4, 8 ), 'globalRange': new es.Range( 5, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 21, 21 ) } ], 'desc': 'Range starting in the middle of the first node and ending at the beginning of the third node' }, // Test 39 { 'node': root2, 'input': new es.Range( 5, 27 ), 'output': [ { 'node': f, 'range': new es.Range( 4, 8 ), 'globalRange': new es.Range( 5, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 6 ), 'globalRange': new es.Range( 21, 27 ) } ], 'desc': 'Range starting in the middle of the first node and ending in the middle of the third node' }, // Test 40 { 'node': root2, 'input': new es.Range( 5, 29 ), 'output': [ { 'node': f, 'range': new es.Range( 4, 8 ), 'globalRange': new es.Range( 5, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) } ], 'desc': 'Range starting in the middle of the first node and ending at the end of the third node' }, // Test 41 { 'node': root2, 'input': new es.Range( 5, 30 ), 'output': [ { 'node': f, 'range': new es.Range( 4, 8 ), 'globalRange': new es.Range( 5, 9 ) }, { 'node': g }, { 'node': h } ], 'desc': 'Range starting in the middle of the first node and ending past the end of the third node' }, // Test 42 { 'node': root2, 'input': new es.Range( 9, 11 ), 'output': [ { 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }, { 'node': g, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 11, 11 ) } ], 'desc': 'Range starting at the end of the first node and ending at the beginning of the second node' }, // Test 43 { 'node': root2, 'input': new es.Range( 9, 14 ), 'output': [ { 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }, { 'node': g, 'range': new es.Range( 0, 3 ), 'globalRange': new es.Range( 11, 14 ) } ], 'desc': 'Range starting at the end of the first node and ending in the middle of the second node' }, // Test 44 { 'node': root2, 'input': new es.Range( 9, 19 ), 'output': [ { 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }, { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) } ], 'desc': 'Range starting at the end of the first node and ending at the end of the second node' }, // Test 45 { 'node': root2, 'input': new es.Range( 9, 20 ), 'output': [ { 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }, { 'node': g } ], 'desc': 'Range starting at the end of the first node and ending between the second and the third node' }, // Test 46 { 'node': root2, 'input': new es.Range( 9, 21 ), 'output': [ { 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 21, 21 ) } ], 'desc': 'Range starting at the end of the first node and ending at the beginning of the third node' }, // Test 47 { 'node': root2, 'input': new es.Range( 9, 27 ), 'output': [ { 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 6 ), 'globalRange': new es.Range( 21, 27 ) } ], 'desc': 'Range starting at the end of the first node and ending in the middle of the third node' }, // Test 48 { 'node': root2, 'input': new es.Range( 9, 29 ), 'output': [ { 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }, { 'node': g }, { 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) } ], 'desc': 'Range starting at the end of the first node and ending at the end of the third node' }, // Test 49 { 'node': root2, 'input': new es.Range( 9, 30 ), 'output': [ { 'node': f, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 9, 9 ) }, { 'node': g }, { 'node': h } ], 'desc': 'Range starting at the end of the first node and ending past the end of the third node' }, // Test 50 { 'node': root2, 'input': new es.Range( 10, 21 ), 'output': [ { 'node': g }, { 'node': h, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 21, 21 ) } ], 'desc': 'Range starting between the first and the second node and ending at the beginning of the third node' }, // Test 51 { 'node': root2, 'input': new es.Range( 10, 27 ), 'output': [ { 'node': g }, { 'node': h, 'range': new es.Range( 0, 6 ), 'globalRange': new es.Range( 21, 27 ) } ], 'desc': 'Range starting between the first and the second node and ending in the middle of the third node' }, // Test 52 { 'node': root2, 'input': new es.Range( 10, 29 ), 'output': [ { 'node': g }, { 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) } ], 'desc': 'Range starting between the first and the second node and ending at the end of the third node' }, // Test 53 { 'node': root2, 'input': new es.Range( 10, 30 ), 'output': [ { 'node': g }, { 'node': h } ], 'desc': 'Range starting between the first and the second node and ending past the end of the third node' }, // Test 54 { 'node': root2, 'input': new es.Range( 11, 21 ), 'output': [ { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) }, { 'node': h, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 21, 21 ) } ], 'desc': 'Range starting at the beginning of the second node and ending at the beginning of the third node' }, // Test 55 { 'node': root2, 'input': new es.Range( 11, 27 ), 'output': [ { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) }, { 'node': h, 'range': new es.Range( 0, 6 ), 'globalRange': new es.Range( 21, 27 ) } ], 'desc': 'Range starting at the beginning of the second node and ending in the middle of the third node' }, // Test 56 { 'node': root2, 'input': new es.Range( 11, 29 ), 'output': [ { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) }, { 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) } ], 'desc': 'Range starting at the beginning of the second node and ending at the end of the third node' }, // Test 57 { 'node': root2, 'input': new es.Range( 11, 30 ), 'output': [ { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 11, 19 ) }, { 'node': h } ], 'desc': 'Range starting at the beginning of the second node and ending past the end of the third node' }, // Test 58 { 'node': root2, 'input': new es.Range( 14, 21 ), 'output': [ { 'node': g, 'range': new es.Range( 3, 8 ), 'globalRange': new es.Range( 14, 19 ) }, { 'node': h, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 21, 21 ) } ], 'desc': 'Range starting in the middle of the second node and ending at the beginning of the third node' }, // Test 59 { 'node': root2, 'input': new es.Range( 14, 27 ), 'output': [ { 'node': g, 'range': new es.Range( 3, 8 ), 'globalRange': new es.Range( 14, 19 ) }, { 'node': h, 'range': new es.Range( 0, 6 ), 'globalRange': new es.Range( 21, 27 ) } ], 'desc': 'Range starting in the middle of the second node and ending in the middle of the third node' }, // Test 60 { 'node': root2, 'input': new es.Range( 14, 29 ), 'output': [ { 'node': g, 'range': new es.Range( 3, 8 ), 'globalRange': new es.Range( 14, 19 ) }, { 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) } ], 'desc': 'Range starting in the middle of the second node and ending at the end of the third node' }, // Test 61 { 'node': root2, 'input': new es.Range( 14, 30 ), 'output': [ { 'node': g, 'range': new es.Range( 3, 8 ), 'globalRange': new es.Range( 14, 19 ) }, { 'node': h } ], 'desc': 'Range starting in the middle of the second node and ending past the end of the third node' }, // Test 62 { 'node': root2, 'input': new es.Range( 19, 21 ), 'output': [ { 'node': g, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 19, 19 ) }, { 'node': h, 'range': new es.Range( 0, 0 ), 'globalRange': new es.Range( 21, 21 ) } ], 'desc': 'Range starting at the end of the second node and ending at the beginning of the third node' }, // Test 63 { 'node': root2, 'input': new es.Range( 19, 27 ), 'output': [ { 'node': g, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 19, 19 ) }, { 'node': h, 'range': new es.Range( 0, 6 ), 'globalRange': new es.Range( 21, 27 ) } ], 'desc': 'Range starting at the end of the second node and ending in the middle of the third node' }, // Test 64 { 'node': root2, 'input': new es.Range( 19, 29 ), 'output': [ { 'node': g, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 19, 19 ) }, { 'node': h, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 21, 29 ) } ], 'desc': 'Range starting at the end of the second node and ending at the end of the third node' }, // Test 65 { 'node': root2, 'input': new es.Range( 19, 30 ), 'output': [ { 'node': g, 'range': new es.Range( 8, 8 ), 'globalRange': new es.Range( 19, 19 ) }, { 'node': h } ], 'desc': 'Range starting at the end of the second node and ending past the end of the third node' }, // Tests for childless nodes // Test 66 { 'node': g, 'input': new es.Range( 1, 3 ), 'output': [ { 'node': g, 'range': new es.Range( 1, 3 ), 'globalRange': new es.Range( 1, 3 ) } ], 'desc': 'Childless node given, range not out of bounds' }, // Test 67 { 'node': g, 'input': new es.Range( 0, 8 ), 'output': [ { 'node': g, 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 0, 8 ) } ], 'desc': 'Childless node given, range covers entire node' }, // Tests for out-of-bounds cases // Test 68 { 'node': g, 'input': new es.Range( -1, 3 ), 'exception': /^The start offset of the range is negative$/, 'desc': 'Childless node given, range start out of bounds' }, // Test 69 { 'node': g, 'input': new es.Range( 1, 9 ), 'exception': /^The end offset of the range is past the end of the node$/, 'desc': 'Childless node given, range end out of bounds' }, // Test 70 { 'node': root2, 'input': new es.Range( 31, 35 ), 'exception': /^The start offset of the range is past the end of the node$/, 'desc': 'Node with children given, range start out of bounds' }, // Test 71 { 'node': root2, 'input': new es.Range( 30, 35 ), 'exception': /^The end offset of the range is past the end of the node$/, 'desc': 'Node with children given, range end out of bounds' }, // Tests for recursion cases // Test 72 { 'node': big, 'input': new es.Range( 2, 10 ), 'output': [ { 'node': big.children[0], 'range': new es.Range( 1, 3 ), 'globalRange': new es.Range( 2, 4 ) }, { 'node': big.children[1], 'range': new es.Range( 0, 4 ), 'globalRange': new es.Range( 6, 10 ) } ], 'desc': 'Select from before the b to after the d' }, // Test 73 { 'node': big, 'input': new es.Range( 3, 33 ), 'output': [ { 'node': big.children[0], 'range': new es.Range( 2, 3 ), 'globalRange': new es.Range( 3, 4 ) }, { 'node': big.children[1] }, { 'node': big.children[2], 'range': new es.Range( 0, 1 ), 'globalRange': new es.Range( 32, 33 ) } ], 'desc': 'Select from before the c to after the h' }, // Test 74 { 'node': big, 'input': new es.Range( 9, 20 ), 'output': [ { 'node': big.children[1].children[0].children[0].children[0], 'range': new es.Range( 0, 1 ), 'globalRange': new es.Range( 9, 10 ) }, { 'node': big.children[1].children[0].children[0].children[1], 'range': new es.Range( 0, 8 ), 'globalRange': new es.Range( 12, 20 ) } ], 'desc': 'Select from before the d to after the f, with recursion' }, // Test 75 { 'node': big, 'input': new es.Range( 9, 20 ), 'shallow': true, 'output': [ { 'node': big.children[1], 'range': new es.Range( 3, 14 ), 'globalRange': new es.Range( 9, 20 ) } ], 'desc': 'Select from before the d to after the f, without recursion' } ]; function compare( a, b ) { if ( $.isArray( a ) && $.isArray( b ) && a.length === b.length ) { for ( var i = 0; i < a.length; i++ ) { if ( a[i].node !== b[i].node || ( ( typeof a[i].range !== typeof b[i].range ) || ( a[i].range !== undefined && ( a[i].range.start !== b[i].range.start || a[i].range.end !== b[i].range.end ) ) ) || ( ( typeof a[i].globalRange !== typeof b[i].globalRange ) || ( a[i].globalRange !== undefined && ( a[i].globalRange.start !== b[i].globalRange.start || a[i].globalRange.end !== b[i].globalRange.end ) ) ) ) { return false; } } } return true; } function select( input, shallow ) { return function() { selectNodesTests[i].node.selectNodes( input, shallow ); }; } for ( var i = 0; i < selectNodesTests.length; i++ ) { if ( 'output' in selectNodesTests[i] ) { var result = selectNodesTests[i].node.selectNodes( selectNodesTests[i].input, selectNodesTests[i].shallow ); ok( compare( result, selectNodesTests[i].output ), selectNodesTests[i].desc + ' (from ' + selectNodesTests[i].input.start + ' to ' + selectNodesTests[i].input.end + ')' ); } else if ( 'exception' in selectNodesTests[i] ) { raises( function() { selectNodesTests[i].node.selectNodes( selectNodesTests[i].input, selectNodesTests[i].shallow ); }, selectNodesTests[i].exception, selectNodesTests[i].desc ); } } } ); test( 'es.DocumentBranchNode.traverseLeafNodes', 11, function() { var root3 = es.DocumentModel.newFromPlainObject( esTest.obj ); var tests = [ // Test 1 & 2 { 'node': root3, 'output': [ root3.children[0], root3.children[1].children[0].children[0].children[0], root3.children[1].children[0].children[0].children[1].children[0].children[0], root3.children[1].children[0].children[0].children[1].children[1].children[0], root3.children[1].children[0].children[0].children[1].children[2].children[0], root3.children[2] ], 'reverse': true, 'desc': 'Traversing the entire document returns all leaf nodes' }, // Test 3 & 4 { 'node': root3, 'output': [ root3.children[0], root3.children[1].children[0].children[0].children[0], root3.children[1].children[0].children[0].children[1].children[0].children[0] ], 'reverse': [ root3.children[2], root3.children[1].children[0].children[0].children[1].children[2].children[0], root3.children[1].children[0].children[0].children[1].children[1].children[0], root3.children[1].children[0].children[0].children[1].children[0].children[0] ], 'callback': function( node ) { if ( node === root3.children[1].children[0].children[0].children[1].children[0].children[0] ) { return false; } }, 'desc': 'Returning false from the callback stops the traversal' }, // Test 5 & 6 { 'node': root3, 'output': [ root3.children[1].children[0].children[0].children[1].children[1].children[0], root3.children[1].children[0].children[0].children[1].children[2].children[0], root3.children[2] ], 'reverse': [ root3.children[1].children[0].children[0].children[1].children[1].children[0], root3.children[1].children[0].children[0].children[1].children[0].children[0], root3.children[1].children[0].children[0].children[0], root3.children[0] ], 'from': root3.children[1].children[0].children[0].children[1].children[1].children[0], 'desc': 'Starting at a leaf node returns that leaf node and everything after it', 'reverseDesc': 'Starting at a leaf node returns that leaf node and everything before it (in reverse)' }, // Test 7 & 8 { 'node': root3, 'output': [ root3.children[1].children[0].children[0].children[0], root3.children[1].children[0].children[0].children[1].children[0].children[0], root3.children[1].children[0].children[0].children[1].children[1].children[0], root3.children[1].children[0].children[0].children[1].children[2].children[0], root3.children[2] ], 'reverse': [ root3.children[0] ], 'from': root3.children[1], 'desc': 'Starting at a non-leaf node returns all leaf nodes inside and after it', 'reverseDesc': 'Starting at a non-leaf node returns all leaf nodes before it and none inside (in reverse)' }, // Test 9 & 10 { 'node': root3.children[1], 'output': [ root3.children[1].children[0].children[0].children[0], root3.children[1].children[0].children[0].children[1].children[0].children[0], root3.children[1].children[0].children[0].children[1].children[1].children[0], root3.children[1].children[0].children[0].children[1].children[2].children[0] ], 'reverse': true, 'desc': 'Calling traverseLeafNodes() on a non-root node only returns leaf nodes inside that node' }, // Test 11 { 'node': root3.children[1], 'from': root3.children[2], 'exception': /^from parameter passed to traverseLeafNodes\(\) must be a descendant$/, 'desc': 'Passing a sibling for from results in an exception' } ]; for ( var i = 0; i < tests.length; i++ ) { executeTest( tests[i] ); if ( tests[i].reverse !== undefined ) { var reversed = { 'node': tests[i].node, 'from': tests[i].from, 'callback': tests[i].callback, 'exception': tests[i].exception, 'isReversed': true, 'desc': tests[i].reverseDesc || tests[i].desc + ' (in reverse)' }; if ( tests[i].output !== undefined && tests[i].reverse === true ) { reversed.output = tests[i].output.reverse(); } else { reversed.output = tests[i].reverse; } executeTest( reversed ); } } function executeTest( test ) { var realLeaves = [], callback = function( node ) { var retval; realLeaves.push( node ); if ( test.callback ) { retval = test.callback( node ); if ( retval !== undefined ) { return retval; } } }, f = function() { test.node.traverseLeafNodes( callback, test.from, test.isReversed ); }; if ( test.exception ) { raises( f, test.exception, test.desc ); } else { f(); ok( es.compareArrays( realLeaves, test.output ), test.desc ); } } } );