|
|
import { describe, it, expect, beforeEach } from 'vitest'; |
|
|
import { load, type CheerioAPI } from '../index.js'; |
|
|
import { Cheerio } from '../cheerio.js'; |
|
|
import { type AnyNode, type Element, type Text, isText } from 'domhandler'; |
|
|
import { |
|
|
cheerio, |
|
|
food, |
|
|
fruits, |
|
|
eleven, |
|
|
drinks, |
|
|
text, |
|
|
forms, |
|
|
mixedText, |
|
|
vegetables, |
|
|
} from '../__fixtures__/fixtures.js'; |
|
|
|
|
|
function getText(el: Cheerio<Element>) { |
|
|
if (el.length === 0) return undefined; |
|
|
const [firstChild] = el[0].childNodes; |
|
|
return isText(firstChild) ? firstChild.data : undefined; |
|
|
} |
|
|
|
|
|
describe('$(...)', () => { |
|
|
let $: CheerioAPI; |
|
|
|
|
|
beforeEach(() => { |
|
|
$ = load(fruits); |
|
|
}); |
|
|
|
|
|
describe('.load', () => { |
|
|
it('should throw a TypeError if given invalid input', () => { |
|
|
expect(() => { |
|
|
|
|
|
load(); |
|
|
}).toThrow('cheerio.load() expects a string'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.find', () => { |
|
|
it('() : should find nothing', () => { |
|
|
expect($('ul').find()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(single) : should find one descendant', () => { |
|
|
expect($('#fruits').find('.apple')[0].attribs).toHaveProperty( |
|
|
'class', |
|
|
'apple', |
|
|
); |
|
|
}); |
|
|
|
|
|
|
|
|
it('(single) : should filter out text nodes', () => { |
|
|
const $root = $(`<html>\n${fruits.replace(/></g, '>\n<')}\n</html>`); |
|
|
expect($root.find('.apple')[0].attribs).toHaveProperty('class', 'apple'); |
|
|
}); |
|
|
|
|
|
it('(many) : should find all matching descendant', () => { |
|
|
expect($('#fruits').find('li')).toHaveLength(3); |
|
|
}); |
|
|
|
|
|
it('(many) : should merge all selected elems with matching descendants', () => { |
|
|
expect($('#fruits, #food', food).find('.apple')).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('(invalid single) : should return empty if cant find', () => { |
|
|
expect($('ul').find('blah')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(invalid single) : should query descendants only', () => { |
|
|
expect($('#fruits').find('ul')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('should return empty if search already empty result', () => { |
|
|
expect($('#not-fruits').find('li')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('should lowercase selectors', () => { |
|
|
expect($('#fruits').find('LI')).toHaveLength(3); |
|
|
}); |
|
|
|
|
|
it('should query immediate descendant only', () => { |
|
|
const q = load('<foo><bar><bar></bar><bar></bar></bar></foo>'); |
|
|
expect(q('foo').find('> bar')).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('should find siblings', () => { |
|
|
const q = load('<p class=a><p class=b></p>'); |
|
|
expect(q('.a').find('+.b')).toHaveLength(1); |
|
|
expect(q('.a').find('~.b')).toHaveLength(1); |
|
|
expect(q('.a').find('+.a')).toHaveLength(0); |
|
|
expect(q('.a').find('~.a')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('should query case-sensitively when in xml mode', () => { |
|
|
const q = load('<caseSenSitive allTheWay>', { xml: true }); |
|
|
expect(q('caseSenSitive')).toHaveLength(1); |
|
|
expect(q('[allTheWay]')).toHaveLength(1); |
|
|
expect(q('casesensitive')).toHaveLength(0); |
|
|
expect(q('[alltheway]')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('should throw an Error if given an invalid selector', () => { |
|
|
expect(() => { |
|
|
$('#fruits').find(':bah'); |
|
|
}).toThrow('Unknown pseudo-class :bah'); |
|
|
}); |
|
|
|
|
|
it('should respect the `lowerCaseTags` option (#3495)', () => { |
|
|
const q = load( |
|
|
`<parentTag class="myClass"> |
|
|
<firstTag> <child> blah </child> </firstTag> |
|
|
<secondTag> <child> blah </child> </secondTag> |
|
|
</parentTag> `, |
|
|
{ |
|
|
xml: { |
|
|
xmlMode: true, |
|
|
decodeEntities: false, |
|
|
lowerCaseTags: true, |
|
|
lowerCaseAttributeNames: false, |
|
|
recognizeSelfClosing: true, |
|
|
}, |
|
|
}, |
|
|
); |
|
|
expect(q('.myClass').find('firstTag > child')).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
describe('(cheerio object) :', () => { |
|
|
it('returns only those nodes contained within the current selection', () => { |
|
|
const q = load(food); |
|
|
const $selection = q('#fruits').find(q('li')); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe(q('.apple')[0]); |
|
|
expect($selection[1]).toBe(q('.orange')[0]); |
|
|
expect($selection[2]).toBe(q('.pear')[0]); |
|
|
}); |
|
|
it('returns only those nodes contained within any element in the current selection', () => { |
|
|
const q = load(food); |
|
|
const $selection = q('.apple, #vegetables').find(q('li')); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe(q('.carrot')[0]); |
|
|
expect($selection[1]).toBe(q('.sweetcorn')[0]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('(node) :', () => { |
|
|
it('returns node when contained within the current selection', () => { |
|
|
const q = load(food); |
|
|
const $selection = q('#fruits').find(q('.apple')[0]); |
|
|
|
|
|
expect($selection).toHaveLength(1); |
|
|
expect($selection[0]).toBe(q('.apple')[0]); |
|
|
}); |
|
|
it('returns node when contained within any element the current selection', () => { |
|
|
const q = load(food); |
|
|
const $selection = q('#fruits, #vegetables').find(q('.carrot')[0]); |
|
|
|
|
|
expect($selection).toHaveLength(1); |
|
|
expect($selection[0]).toBe(q('.carrot')[0]); |
|
|
}); |
|
|
it('does not return node that is not contained within the current selection', () => { |
|
|
const q = load(food); |
|
|
const $selection = q('#fruits').find(q('.carrot')[0]); |
|
|
|
|
|
expect($selection).toHaveLength(0); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.children', () => { |
|
|
it('() : should get all children', () => { |
|
|
expect($('ul').children()).toHaveLength(3); |
|
|
}); |
|
|
|
|
|
it('() : should skip text nodes', () => { |
|
|
expect($(mixedText).children()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should return children of all matched elements', () => { |
|
|
expect($('ul ul', food).children()).toHaveLength(5); |
|
|
}); |
|
|
|
|
|
it('(selector) : should return children matching selector', () => { |
|
|
const { attribs } = $('ul').children('.orange')[0]; |
|
|
expect(attribs).toHaveProperty('class', 'orange'); |
|
|
}); |
|
|
|
|
|
it('(invalid selector) : should return empty', () => { |
|
|
expect($('ul').children('.lulz')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('should only match immediate children, not ancestors', () => { |
|
|
expect($(food).children('li')).toHaveLength(0); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.contents', () => { |
|
|
beforeEach(() => { |
|
|
$ = load(text); |
|
|
}); |
|
|
|
|
|
it('() : should get all contents', () => { |
|
|
expect($('p').contents()).toHaveLength(5); |
|
|
}); |
|
|
|
|
|
it('() : should skip text nodes', () => { |
|
|
expect($(mixedText).contents()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('() : should include text nodes', () => { |
|
|
expect($('p').contents().first()[0].type).toBe('text'); |
|
|
}); |
|
|
|
|
|
it('() : should include comment nodes', () => { |
|
|
expect($('p').contents().last()[0].type).toBe('comment'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.next', () => { |
|
|
it('() : should return next element', () => { |
|
|
const { attribs } = $('.orange').next()[0]; |
|
|
expect(attribs).toHaveProperty('class', 'pear'); |
|
|
}); |
|
|
|
|
|
it('() : should skip text nodes', () => { |
|
|
expect($(mixedText).next()[0]).toHaveProperty('name', 'b'); |
|
|
}); |
|
|
|
|
|
it('(no next) : should return empty for last child', () => { |
|
|
expect($('.pear').next()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(next on empty object) : should return empty', () => { |
|
|
expect($('.banana').next()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should operate over all elements in the selection', () => { |
|
|
expect($('.apple, .orange', food).next()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('() : should return elements in order', () => { |
|
|
const result = load(eleven)('.red').next(); |
|
|
expect(result).toHaveLength(2); |
|
|
expect(result.eq(0).text()).toBe('Six'); |
|
|
expect(result.eq(1).text()).toBe('Ten'); |
|
|
}); |
|
|
|
|
|
it('should reject elements that violate the filter', () => { |
|
|
expect($('.apple').next('.non-existent')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('should accept elements that satisify the filter', () => { |
|
|
expect($('.apple').next('.orange')).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
describe('(selector) :', () => { |
|
|
it('should reject elements that violate the filter', () => { |
|
|
expect($('.apple').next('.non-existent')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('should accept elements that satisify the filter', () => { |
|
|
expect($('.apple').next('.orange')).toHaveLength(1); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.nextAll', () => { |
|
|
it('() : should return all following siblings', () => { |
|
|
const elems = $('.apple').nextAll(); |
|
|
expect(elems).toHaveLength(2); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'orange'); |
|
|
expect(elems[1].attribs).toHaveProperty('class', 'pear'); |
|
|
}); |
|
|
|
|
|
it('(no next) : should return empty for last child', () => { |
|
|
expect($('.pear').nextAll()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(nextAll on empty object) : should return empty', () => { |
|
|
expect($('.banana').nextAll()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should operate over all elements in the selection', () => { |
|
|
expect($('.apple, .carrot', food).nextAll()).toHaveLength(3); |
|
|
}); |
|
|
|
|
|
it('() : should not contain duplicate elements', () => { |
|
|
const elems = $('.apple, .orange', food); |
|
|
expect(elems.nextAll()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('() : should not contain text elements', () => { |
|
|
const elems = $('.apple', fruits.replace(/></g, '>\n<')); |
|
|
expect(elems.nextAll()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
describe('(selector) :', () => { |
|
|
it('should filter according to the provided selector', () => { |
|
|
expect($('.apple').nextAll('.pear')).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it("should not consider siblings' contents when filtering", () => { |
|
|
expect($('#fruits', food).nextAll('li')).toHaveLength(0); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.nextUntil', () => { |
|
|
it('() : should return all following siblings if no selector specified', () => { |
|
|
const elems = $('.apple', food).nextUntil(); |
|
|
expect(elems).toHaveLength(2); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'orange'); |
|
|
expect(elems[1].attribs).toHaveProperty('class', 'pear'); |
|
|
}); |
|
|
|
|
|
it('() : should filter out non-element nodes', () => { |
|
|
const elems = $('<div><div></div><!-- comment -->text<div></div></div>'); |
|
|
const div = elems.children().eq(0); |
|
|
expect(div.nextUntil()).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('() : should operate over all elements in the selection', () => { |
|
|
const elems = $('.apple, .carrot', food); |
|
|
expect(elems.nextUntil()).toHaveLength(3); |
|
|
}); |
|
|
|
|
|
it('() : should not contain duplicate elements', () => { |
|
|
const elems = $('.apple, .orange', food); |
|
|
expect(elems.nextUntil()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('(selector) : should return all following siblings until selector', () => { |
|
|
const elems = $('.apple', food).nextUntil('.pear'); |
|
|
expect(elems).toHaveLength(1); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'orange'); |
|
|
}); |
|
|
|
|
|
it('(selector) : should support selector matching multiple elements', () => { |
|
|
const elems = $('#disabled', forms).nextUntil('option, #unnamed'); |
|
|
expect(elems).toHaveLength(2); |
|
|
expect(elems[0].attribs).toHaveProperty('id', 'submit'); |
|
|
expect(elems[1].attribs).toHaveProperty('id', 'select'); |
|
|
}); |
|
|
|
|
|
it('(selector not sibling) : should return all following siblings', () => { |
|
|
const elems = $('.apple').nextUntil('#vegetables'); |
|
|
expect(elems).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('(selector, filterString) : should return all following siblings until selector, filtered by filter', () => { |
|
|
const elems = $('.beer', drinks).nextUntil('.water', '.milk'); |
|
|
expect(elems).toHaveLength(1); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'milk'); |
|
|
}); |
|
|
|
|
|
it('(null, filterString) : should return all following siblings until selector, filtered by filter', () => { |
|
|
const elems = $('<ul><li></li><li><p></p></li></ul>'); |
|
|
const empty = elems.find('li').eq(0).nextUntil(null, 'p'); |
|
|
expect(empty).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty object for last child', () => { |
|
|
expect($('.pear').nextUntil()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty object when called on an empty object', () => { |
|
|
expect($('.banana').nextUntil()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(node) : should return all following siblings until the node', () => { |
|
|
const $fruits = $('#fruits').children(); |
|
|
const elems = $fruits.eq(0).nextUntil($fruits[2]); |
|
|
expect(elems).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('(cheerio object) : should return all following siblings until any member of the cheerio object', () => { |
|
|
const $drinks = $(drinks).children(); |
|
|
const $until = $([$drinks[4], $drinks[3]]); |
|
|
const elems = $drinks.eq(0).nextUntil($until); |
|
|
expect(elems).toHaveLength(2); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.prev', () => { |
|
|
it('() : should return previous element', () => { |
|
|
const { attribs } = $('.orange').prev()[0]; |
|
|
expect(attribs).toHaveProperty('class', 'apple'); |
|
|
}); |
|
|
|
|
|
it('() : should skip text nodes', () => { |
|
|
expect($($(mixedText)[2]).prev()[0]).toHaveProperty('name', 'a'); |
|
|
}); |
|
|
|
|
|
it('(no prev) : should return empty for first child', () => { |
|
|
expect($('.apple').prev()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(prev on empty object) : should return empty', () => { |
|
|
expect($('.banana').prev()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should operate over all elements in the selection', () => { |
|
|
expect($('.orange, .pear', food).prev()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('() : should maintain elements order', () => { |
|
|
const sel = load(eleven)('.sel'); |
|
|
expect(sel).toHaveLength(3); |
|
|
expect(sel.eq(0).text()).toBe('Three'); |
|
|
expect(sel.eq(1).text()).toBe('Nine'); |
|
|
expect(sel.eq(2).text()).toBe('Eleven'); |
|
|
|
|
|
|
|
|
const el = sel[2]; |
|
|
sel[2] = sel[1]; |
|
|
sel[1] = el; |
|
|
|
|
|
const result = sel.prev(); |
|
|
expect(result).toHaveLength(3); |
|
|
expect(result.eq(0).text()).toBe('Two'); |
|
|
expect(result.eq(1).text()).toBe('Ten'); |
|
|
expect(result.eq(2).text()).toBe('Eight'); |
|
|
}); |
|
|
|
|
|
describe('(selector) :', () => { |
|
|
it('should reject elements that violate the filter', () => { |
|
|
expect($('.orange').prev('.non-existent')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('should accept elements that satisify the filter', () => { |
|
|
expect($('.orange').prev('.apple')).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('(selector) : should reject elements that violate the filter', () => { |
|
|
expect($('.orange').prev('.non-existent')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(selector) : should accept elements that satisify the filter', () => { |
|
|
expect($('.orange').prev('.apple')).toHaveLength(1); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.prevAll', () => { |
|
|
it('() : should return all preceding siblings', () => { |
|
|
const elems = $('.pear').prevAll(); |
|
|
expect(elems).toHaveLength(2); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'orange'); |
|
|
expect(elems[1].attribs).toHaveProperty('class', 'apple'); |
|
|
}); |
|
|
|
|
|
it('() : should not contain text elements', () => { |
|
|
const elems = $('.pear', fruits.replace(/></g, '>\n<')); |
|
|
expect(elems.prevAll()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('(no prev) : should return empty for first child', () => { |
|
|
expect($('.apple').prevAll()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(prevAll on empty object) : should return empty', () => { |
|
|
expect($('.banana').prevAll()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should operate over all elements in the selection', () => { |
|
|
expect($('.orange, .sweetcorn', food).prevAll()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('() : should not contain duplicate elements', () => { |
|
|
const elems = $('.orange, .pear', food); |
|
|
expect(elems.prevAll()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
describe('(selector) :', () => { |
|
|
it('should filter returned elements', () => { |
|
|
const elems = $('.pear').prevAll('.apple'); |
|
|
expect(elems).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it("should not consider siblings's descendents", () => { |
|
|
const elems = $('#vegetables', food).prevAll('li'); |
|
|
expect(elems).toHaveLength(0); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.prevUntil', () => { |
|
|
it('() : should return all preceding siblings if no selector specified', () => { |
|
|
const elems = $('.pear').prevUntil(); |
|
|
expect(elems).toHaveLength(2); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'orange'); |
|
|
expect(elems[1].attribs).toHaveProperty('class', 'apple'); |
|
|
}); |
|
|
|
|
|
it('() : should filter out non-element nodes', () => { |
|
|
const elems = $( |
|
|
'<div class="1"><div class="2"></div><!-- comment -->text<div class="3"></div></div>', |
|
|
); |
|
|
const div = elems.children().last(); |
|
|
expect(div.prevUntil()).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('() : should operate over all elements in the selection', () => { |
|
|
const elems = $('.pear, .sweetcorn', food); |
|
|
expect(elems.prevUntil()).toHaveLength(3); |
|
|
}); |
|
|
|
|
|
it('() : should not contain duplicate elements', () => { |
|
|
const elems = $('.orange, .pear', food); |
|
|
expect(elems.prevUntil()).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('(selector) : should return all preceding siblings until selector', () => { |
|
|
const elems = $('.pear').prevUntil('.apple'); |
|
|
expect(elems).toHaveLength(1); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'orange'); |
|
|
}); |
|
|
|
|
|
it('(selector) : should support selector matching multiple elements', () => { |
|
|
const elems = $('#unnamed', forms).prevUntil('option, #disabled'); |
|
|
expect(elems).toHaveLength(2); |
|
|
expect(elems[0].attribs).toHaveProperty('id', 'select'); |
|
|
expect(elems[1].attribs).toHaveProperty('id', 'submit'); |
|
|
}); |
|
|
|
|
|
it('(selector not sibling) : should return all preceding siblings', () => { |
|
|
const elems = $('.sweetcorn', food).prevUntil('#fruits'); |
|
|
expect(elems).toHaveLength(1); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'carrot'); |
|
|
}); |
|
|
|
|
|
it('(selector, filterString) : should return all preceding siblings until selector, filtered by filter', () => { |
|
|
const elems = $('.cider', drinks).prevUntil('.juice', '.water'); |
|
|
expect(elems).toHaveLength(1); |
|
|
expect(elems[0].attribs).toHaveProperty('class', 'water'); |
|
|
}); |
|
|
|
|
|
it('(selector, filterString) : should return all preceding siblings until selector', () => { |
|
|
const elems = $('<ul><li><p></p></li><li></li></ul>'); |
|
|
const empty = elems.find('li').eq(1).prevUntil(null, 'p'); |
|
|
expect(empty).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty object for first child', () => { |
|
|
expect($('.apple').prevUntil()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty object when called on an empty object', () => { |
|
|
expect($('.banana').prevUntil()).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(node) : should return all previous siblings until the node', () => { |
|
|
const $fruits = $('#fruits').children(); |
|
|
const elems = $fruits.eq(2).prevUntil($fruits[0]); |
|
|
expect(elems).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('(cheerio object) : should return all previous siblings until any member of the cheerio object', () => { |
|
|
const $drinks = $(drinks).children(); |
|
|
const $until = $([$drinks[0], $drinks[1]]); |
|
|
const elems = $drinks.eq(4).prevUntil($until); |
|
|
expect(elems).toHaveLength(2); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.siblings', () => { |
|
|
it('() : should get all the siblings', () => { |
|
|
expect($('.orange').siblings()).toHaveLength(2); |
|
|
expect($('#fruits').siblings()).toHaveLength(0); |
|
|
expect($('.apple, .carrot', food).siblings()).toHaveLength(3); |
|
|
}); |
|
|
|
|
|
it('(selector) : should get all siblings that match the selector', () => { |
|
|
expect($('.orange').siblings('.apple')).toHaveLength(1); |
|
|
expect($('.orange').siblings('.peach')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(selector) : should throw an Error if given an invalid selector', () => { |
|
|
expect(() => { |
|
|
$('.orange').siblings(':bah'); |
|
|
}).toThrow('Unknown pseudo-class :bah'); |
|
|
}); |
|
|
|
|
|
it('(selector) : does not consider the contents of siblings when filtering (GH-374)', () => { |
|
|
expect($('#fruits', food).siblings('li')).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : when two elements are siblings to each other they have to be included', () => { |
|
|
const result = load(eleven)('.sel').siblings(); |
|
|
expect(result).toHaveLength(7); |
|
|
expect(result.eq(0).text()).toBe('One'); |
|
|
expect(result.eq(1).text()).toBe('Two'); |
|
|
expect(result.eq(2).text()).toBe('Four'); |
|
|
expect(result.eq(3).text()).toBe('Eight'); |
|
|
expect(result.eq(4).text()).toBe('Nine'); |
|
|
expect(result.eq(5).text()).toBe('Ten'); |
|
|
expect(result.eq(6).text()).toBe('Eleven'); |
|
|
}); |
|
|
|
|
|
it('(selector) : when two elements are siblings to each other they have to be included', () => { |
|
|
const result = load(eleven)('.sel').siblings('.red'); |
|
|
expect(result).toHaveLength(2); |
|
|
expect(result.eq(0).text()).toBe('Four'); |
|
|
expect(result.eq(1).text()).toBe('Nine'); |
|
|
}); |
|
|
|
|
|
it('(cheerio) : test filtering with cheerio object', () => { |
|
|
const doc = load(eleven); |
|
|
const result = doc('.sel').siblings(doc(':not([class])')); |
|
|
expect(result).toHaveLength(4); |
|
|
expect(result.eq(0).text()).toBe('One'); |
|
|
expect(result.eq(1).text()).toBe('Two'); |
|
|
expect(result.eq(2).text()).toBe('Eight'); |
|
|
expect(result.eq(3).text()).toBe('Ten'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.parents', () => { |
|
|
beforeEach(() => { |
|
|
$ = load(food); |
|
|
}); |
|
|
|
|
|
it('() : should get all of the parents in logical order', () => { |
|
|
const orange = $('.orange').parents(); |
|
|
expect(orange).toHaveLength(4); |
|
|
expect(orange[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
expect(orange[1].attribs).toHaveProperty('id', 'food'); |
|
|
expect(orange[2].tagName).toBe('body'); |
|
|
expect(orange[3].tagName).toBe('html'); |
|
|
const fruits = $('#fruits').parents(); |
|
|
expect(fruits).toHaveLength(3); |
|
|
expect(fruits[0].attribs).toHaveProperty('id', 'food'); |
|
|
expect(fruits[1].tagName).toBe('body'); |
|
|
expect(fruits[2].tagName).toBe('html'); |
|
|
}); |
|
|
|
|
|
it('(selector) : should get all of the parents that match the selector in logical order', () => { |
|
|
const fruits = $('.orange').parents('#fruits'); |
|
|
expect(fruits).toHaveLength(1); |
|
|
expect(fruits[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
const uls = $('.orange').parents('ul'); |
|
|
expect(uls).toHaveLength(2); |
|
|
expect(uls[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
expect(uls[1].attribs).toHaveProperty('id', 'food'); |
|
|
}); |
|
|
|
|
|
it('() : should not break if the selector does not have any results', () => { |
|
|
const result = $('.saladbar').parents(); |
|
|
expect(result).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty set for top-level elements', () => { |
|
|
const result = $('html').parents(); |
|
|
expect(result).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should return the parents of every element in the *reveresed* collection, omitting duplicates', () => { |
|
|
const $parents = $('li').parents(); |
|
|
|
|
|
expect($parents).toHaveLength(5); |
|
|
expect($parents[0]).toBe($('#vegetables')[0]); |
|
|
expect($parents[1]).toBe($('#fruits')[0]); |
|
|
expect($parents[2]).toBe($('#food')[0]); |
|
|
expect($parents[3]).toBe($('body')[0]); |
|
|
expect($parents[4]).toBe($('html')[0]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.parentsUntil', () => { |
|
|
beforeEach(() => { |
|
|
$ = load(food); |
|
|
}); |
|
|
|
|
|
it('() : should get all of the parents in logical order', () => { |
|
|
const result = $('.orange').parentsUntil(); |
|
|
expect(result).toHaveLength(4); |
|
|
expect(result[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
expect(result[1].attribs).toHaveProperty('id', 'food'); |
|
|
expect(result[2].tagName).toBe('body'); |
|
|
expect(result[3].tagName).toBe('html'); |
|
|
}); |
|
|
|
|
|
it('() : should get all of the parents in reversed order, omitting duplicates', () => { |
|
|
const result = $('.apple, .sweetcorn').parentsUntil(); |
|
|
expect(result).toHaveLength(5); |
|
|
expect(result[0]).toBe($('#vegetables')[0]); |
|
|
expect(result[1]).toBe($('#fruits')[0]); |
|
|
expect(result[2]).toBe($('#food')[0]); |
|
|
expect(result[3]).toBe($('body')[0]); |
|
|
expect(result[4]).toBe($('html')[0]); |
|
|
}); |
|
|
|
|
|
it('(selector) : should get all of the parents until selector', () => { |
|
|
const food = $('.orange').parentsUntil('#food'); |
|
|
expect(food).toHaveLength(1); |
|
|
expect(food[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
const fruits = $('.orange').parentsUntil('#fruits'); |
|
|
expect(fruits).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(selector) : Less simple parentsUntil check with selector', () => { |
|
|
const result = $('#fruits').parentsUntil('html, body'); |
|
|
expect(result.eq(0).attr('id')).toBe('food'); |
|
|
}); |
|
|
|
|
|
it('(selector not parent) : should return all parents', () => { |
|
|
const result = $('.orange').parentsUntil('.apple'); |
|
|
expect(result).toHaveLength(4); |
|
|
expect(result[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
expect(result[1].attribs).toHaveProperty('id', 'food'); |
|
|
expect(result[2].tagName).toBe('body'); |
|
|
expect(result[3].tagName).toBe('html'); |
|
|
}); |
|
|
|
|
|
it('(selector, filter) : should get all of the parents that match the filter', () => { |
|
|
const result = $('.apple, .sweetcorn').parentsUntil( |
|
|
'.saladbar', |
|
|
'#vegetables', |
|
|
); |
|
|
expect(result).toHaveLength(1); |
|
|
expect(result[0].attribs).toHaveProperty('id', 'vegetables'); |
|
|
}); |
|
|
|
|
|
it('(selector, filter) : Multiple-filtered parentsUntil check', () => { |
|
|
const result = $('.orange').parentsUntil('html', 'ul,body'); |
|
|
expect(result).toHaveLength(3); |
|
|
expect(result.eq(0).attr('id')).toBe('fruits'); |
|
|
expect(result.eq(1).attr('id')).toBe('food'); |
|
|
expect(result.eq(2).prop('tagName')).toBe('BODY'); |
|
|
}); |
|
|
|
|
|
it('() : should return empty object when called on an empty object', () => { |
|
|
const result = $('.saladbar').parentsUntil(); |
|
|
expect(result).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty set for top-level elements', () => { |
|
|
const result = $('html').parentsUntil(); |
|
|
expect(result).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(cheerio object) : should return all parents until any member of the cheerio object', () => { |
|
|
const $fruits = $('#fruits'); |
|
|
const $until = $('#food'); |
|
|
const result = $fruits.children().eq(1).parentsUntil($until); |
|
|
expect(result).toHaveLength(1); |
|
|
expect(result[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
}); |
|
|
|
|
|
it('(cheerio object) : should return all parents until body element', () => { |
|
|
const body = $('body')[0]; |
|
|
const result = $('.carrot').parentsUntil(body); |
|
|
expect(result).toHaveLength(2); |
|
|
expect(result.eq(0).is('ul#vegetables')).toBe(true); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.parent', () => { |
|
|
it('() : should return the parent of each matched element', () => { |
|
|
let result = $('.orange').parent(); |
|
|
expect(result).toHaveLength(1); |
|
|
expect(result[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
result = $('li', food).parent(); |
|
|
expect(result).toHaveLength(2); |
|
|
expect(result[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
expect(result[1].attribs).toHaveProperty('id', 'vegetables'); |
|
|
}); |
|
|
|
|
|
it('(undefined) : should not throw an exception', () => { |
|
|
expect(() => { |
|
|
$('li').parent(undefined); |
|
|
}).not.toThrow(); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty object for top-level elements', () => { |
|
|
const result = $('html').parent(); |
|
|
expect(result).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('() : should not contain duplicate elements', () => { |
|
|
const result = $('li').parent(); |
|
|
expect(result).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('(selector) : should filter the matched parent elements by the selector', () => { |
|
|
const parents = $('.orange').parent(); |
|
|
expect(parents).toHaveLength(1); |
|
|
expect(parents[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
const fruits = $('li', food).parent('#fruits'); |
|
|
expect(fruits).toHaveLength(1); |
|
|
expect(fruits[0].attribs).toHaveProperty('id', 'fruits'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.closest', () => { |
|
|
it('() : should return an empty array', () => { |
|
|
const result = $('.orange').closest(); |
|
|
expect(result).toHaveLength(0); |
|
|
expect(result).toBeInstanceOf(Cheerio); |
|
|
}); |
|
|
|
|
|
it('(selector) : should find the closest element that matches the selector, searching through its ancestors and itself', () => { |
|
|
expect($('.orange').closest('.apple')).toHaveLength(0); |
|
|
expect( |
|
|
($('.orange', food).closest('#food')[0] as Element).attribs, |
|
|
).toHaveProperty('id', 'food'); |
|
|
expect( |
|
|
($('.orange', food).closest('ul')[0] as Element).attribs, |
|
|
).toHaveProperty('id', 'fruits'); |
|
|
expect( |
|
|
($('.orange', food).closest('li')[0] as Element).attribs, |
|
|
).toHaveProperty('class', 'orange'); |
|
|
}); |
|
|
|
|
|
it('(selector) : should find the closest element of each item, removing duplicates', () => { |
|
|
const result = $('li', food).closest('ul'); |
|
|
expect(result).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('() : should not break if the selector does not have any results', () => { |
|
|
const result = $('.saladbar', food).closest('ul'); |
|
|
expect(result).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(selector) : should find closest element for text nodes', () => { |
|
|
const textNode = $('.apple', food).contents().first(); |
|
|
const result = textNode.closest('#food') as Cheerio<Element>; |
|
|
expect(result[0].attribs).toHaveProperty('id', 'food'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.each', () => { |
|
|
it('( (i, elem) -> ) : should loop selected returning fn with (i, elem)', () => { |
|
|
const items: Element[] = []; |
|
|
const classes = ['apple', 'orange', 'pear']; |
|
|
$('li').each(function (idx, elem) { |
|
|
items[idx] = elem; |
|
|
expect(this.attribs).toHaveProperty('class', classes[idx]); |
|
|
}); |
|
|
expect(items[0].attribs).toHaveProperty('class', 'apple'); |
|
|
expect(items[1].attribs).toHaveProperty('class', 'orange'); |
|
|
expect(items[2].attribs).toHaveProperty('class', 'pear'); |
|
|
}); |
|
|
|
|
|
it('( (i, elem) -> ) : should break iteration when the iterator function returns false', () => { |
|
|
let iterationCount = 0; |
|
|
$('li').each((idx) => { |
|
|
iterationCount++; |
|
|
return idx < 1; |
|
|
}); |
|
|
|
|
|
expect(iterationCount).toBe(2); |
|
|
}); |
|
|
}); |
|
|
|
|
|
if (typeof Symbol !== 'undefined') { |
|
|
describe('[Symbol.iterator]', () => { |
|
|
it('should yield each element', () => { |
|
|
|
|
|
const $li = $('li'); |
|
|
const iterator = $li[Symbol.iterator]() as Iterator<Element, Element>; |
|
|
expect(iterator.next().value.attribs).toHaveProperty('class', 'apple'); |
|
|
expect(iterator.next().value.attribs).toHaveProperty('class', 'orange'); |
|
|
expect(iterator.next().value.attribs).toHaveProperty('class', 'pear'); |
|
|
expect(iterator.next().done).toBe(true); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
describe('.map', () => { |
|
|
it('(fn) : should be invoked with the correct arguments and context', () => { |
|
|
const $fruits = $('li'); |
|
|
const args: [number, AnyNode][] = []; |
|
|
const thisVals: AnyNode[] = []; |
|
|
|
|
|
$fruits.map(function (...myArgs) { |
|
|
args.push(myArgs); |
|
|
thisVals.push(this); |
|
|
return undefined; |
|
|
}); |
|
|
|
|
|
expect(args).toStrictEqual([ |
|
|
[0, $fruits[0]], |
|
|
[1, $fruits[1]], |
|
|
[2, $fruits[2]], |
|
|
]); |
|
|
expect(thisVals).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]); |
|
|
}); |
|
|
|
|
|
it('(fn) : should return an Cheerio object wrapping the returned items', () => { |
|
|
const $fruits = $('li'); |
|
|
const $mapped = $fruits.map((i) => $fruits[2 - i]); |
|
|
|
|
|
expect($mapped).toHaveLength(3); |
|
|
expect($mapped[0]).toBe($fruits[2]); |
|
|
expect($mapped[1]).toBe($fruits[1]); |
|
|
expect($mapped[2]).toBe($fruits[0]); |
|
|
}); |
|
|
|
|
|
it('(fn) : should ignore `null` and `undefined` returned by iterator', () => { |
|
|
const $fruits = $('li'); |
|
|
const retVals = [null, undefined, $fruits[1]]; |
|
|
|
|
|
const $mapped = $fruits.map((i) => retVals[i]); |
|
|
|
|
|
expect($mapped).toHaveLength(1); |
|
|
expect($mapped[0]).toBe($fruits[1]); |
|
|
}); |
|
|
|
|
|
it('(fn) : should preform a shallow merge on arrays returned by iterator', () => { |
|
|
const $fruits = $('li'); |
|
|
|
|
|
const $mapped = $fruits.map(() => [1, [3, 4]]); |
|
|
|
|
|
expect($mapped.get()).toStrictEqual([1, [3, 4], 1, [3, 4], 1, [3, 4]]); |
|
|
}); |
|
|
|
|
|
it('(fn) : should tolerate `null` and `undefined` when flattening arrays returned by iterator', () => { |
|
|
const $fruits = $('li'); |
|
|
|
|
|
const $mapped = $fruits.map(() => [null, undefined]); |
|
|
|
|
|
expect($mapped.get()).toStrictEqual([ |
|
|
null, |
|
|
undefined, |
|
|
null, |
|
|
undefined, |
|
|
null, |
|
|
undefined, |
|
|
]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.filter', () => { |
|
|
it('(selector) : should reduce the set of matched elements to those that match the selector', () => { |
|
|
const pear = $('li').filter('.pear').text(); |
|
|
expect(pear).toBe('Pear'); |
|
|
}); |
|
|
|
|
|
it('(selector) : should not consider nested elements', () => { |
|
|
const lis = $('#fruits').filter('li'); |
|
|
expect(lis).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(selection) : should reduce the set of matched elements to those that are contained in the provided selection', () => { |
|
|
const $fruits = $('li'); |
|
|
const $pear = $fruits.filter('.pear, .apple'); |
|
|
expect($fruits.filter($pear)).toHaveLength(2); |
|
|
}); |
|
|
|
|
|
it('(element) : should reduce the set of matched elements to those that specified directly', () => { |
|
|
const $fruits = $('li'); |
|
|
const pear = $fruits.filter('.pear')[0]; |
|
|
expect($fruits.filter(pear)).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it("(fn) : should reduce the set of matched elements to those that pass the function's test", () => { |
|
|
const orange = $('li') |
|
|
.filter(function (i, el) { |
|
|
expect(this).toBe(el); |
|
|
expect(el.tagName).toBe('li'); |
|
|
expect(typeof i).toBe('number'); |
|
|
return $(this).attr('class') === 'orange'; |
|
|
}) |
|
|
.text(); |
|
|
|
|
|
expect(orange).toBe('Orange'); |
|
|
}); |
|
|
|
|
|
it('should also iterate over text nodes (#1867)', () => { |
|
|
const text = $('<a>a</a>b<c></c>').filter((_, el): el is Text => |
|
|
isText(el), |
|
|
); |
|
|
|
|
|
expect(text[0].data).toBe('b'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.not', () => { |
|
|
it('(selector) : should reduce the set of matched elements to those that do not match the selector', () => { |
|
|
const $fruits = $('li'); |
|
|
|
|
|
const $notPear = $fruits.not('.pear'); |
|
|
|
|
|
expect($notPear).toHaveLength(2); |
|
|
expect($notPear[0]).toBe($fruits[0]); |
|
|
expect($notPear[1]).toBe($fruits[1]); |
|
|
}); |
|
|
|
|
|
it('(selector) : should not consider nested elements', () => { |
|
|
const lis = $('#fruits').not('li'); |
|
|
expect(lis).toHaveLength(1); |
|
|
}); |
|
|
|
|
|
it('(selection) : should reduce the set of matched elements to those that are mot contained in the provided selection', () => { |
|
|
const $fruits = $('li'); |
|
|
const $orange = $('.orange'); |
|
|
|
|
|
const $notOrange = $fruits.not($orange); |
|
|
|
|
|
expect($notOrange).toHaveLength(2); |
|
|
expect($notOrange[0]).toBe($fruits[0]); |
|
|
expect($notOrange[1]).toBe($fruits[2]); |
|
|
}); |
|
|
|
|
|
it('(element) : should reduce the set of matched elements to those that specified directly', () => { |
|
|
const $fruits = $('li'); |
|
|
const apple = $('.apple')[0]; |
|
|
|
|
|
const $notApple = $fruits.not(apple); |
|
|
|
|
|
expect($notApple).toHaveLength(2); |
|
|
expect($notApple[0]).toBe($fruits[1]); |
|
|
expect($notApple[1]).toBe($fruits[2]); |
|
|
}); |
|
|
|
|
|
it("(fn) : should reduce the set of matched elements to those that do not pass the function's test", () => { |
|
|
const $fruits = $('li'); |
|
|
|
|
|
const $notOrange = $fruits.not(function (i, el) { |
|
|
expect(this).toBe(el); |
|
|
expect(el).toHaveProperty('name', 'li'); |
|
|
expect(typeof i).toBe('number'); |
|
|
return $(this).attr('class') === 'orange'; |
|
|
}); |
|
|
|
|
|
expect($notOrange).toHaveLength(2); |
|
|
expect($notOrange[0]).toBe($fruits[0]); |
|
|
expect($notOrange[1]).toBe($fruits[2]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.has', () => { |
|
|
beforeEach(() => { |
|
|
$ = load(food); |
|
|
}); |
|
|
|
|
|
it('(selector) : should reduce the set of matched elements to those with descendants that match the selector', () => { |
|
|
const $fruits = $('#fruits,#vegetables').has('.pear'); |
|
|
expect($fruits).toHaveLength(1); |
|
|
expect($fruits[0]).toBe($('#fruits')[0]); |
|
|
}); |
|
|
|
|
|
it('(selector) : should only consider nested elements', () => { |
|
|
const $empty = $('#fruits').has('#fruits'); |
|
|
expect($empty).toHaveLength(0); |
|
|
}); |
|
|
|
|
|
it('(element) : should reduce the set of matched elements to those that are ancestors of the provided element', () => { |
|
|
const $fruits = $('#fruits,#vegetables').has($('.pear')[0]); |
|
|
expect($fruits).toHaveLength(1); |
|
|
expect($fruits[0]).toBe($('#fruits')[0]); |
|
|
}); |
|
|
|
|
|
it('(element) : should only consider nested elements', () => { |
|
|
const $fruits = $('#fruits'); |
|
|
const fruitsEl = $fruits[0]; |
|
|
const $empty = $fruits.has(fruitsEl); |
|
|
|
|
|
expect($empty).toHaveLength(0); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.first', () => { |
|
|
it('() : should return the first item', () => { |
|
|
const $src = $( |
|
|
'<span>foo</span><span>bar</span><span>baz</span>', |
|
|
) as Cheerio<Element>; |
|
|
const $elem = $src.first(); |
|
|
expect($elem.length).toBe(1); |
|
|
expect($elem[0].childNodes[0]).toHaveProperty('data', 'foo'); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty object for an empty object', () => { |
|
|
const $src = $(); |
|
|
const $first = $src.first(); |
|
|
expect($first.length).toBe(0); |
|
|
expect($first[0]).toBeUndefined(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.last', () => { |
|
|
it('() : should return the last element', () => { |
|
|
const $src = $( |
|
|
'<span>foo</span><span>bar</span><span>baz</span>', |
|
|
) as Cheerio<Element>; |
|
|
const $elem = $src.last(); |
|
|
expect($elem.length).toBe(1); |
|
|
expect($elem[0].childNodes[0]).toHaveProperty('data', 'baz'); |
|
|
}); |
|
|
|
|
|
it('() : should return an empty object for an empty object', () => { |
|
|
const $src = $(); |
|
|
const $last = $src.last(); |
|
|
expect($last.length).toBe(0); |
|
|
expect($last[0]).toBeUndefined(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.first & .last', () => { |
|
|
it('() : should return equivalent collections if only one element', () => { |
|
|
const $src = $('<span>bar</span>') as Cheerio<Element>; |
|
|
const $first = $src.first(); |
|
|
const $last = $src.last(); |
|
|
expect($first.length).toBe(1); |
|
|
expect($first[0].childNodes[0]).toHaveProperty('data', 'bar'); |
|
|
expect($last.length).toBe(1); |
|
|
expect($last[0].childNodes[0]).toHaveProperty('data', 'bar'); |
|
|
expect($first[0]).toBe($last[0]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.eq', () => { |
|
|
it('(i) : should return the element at the specified index', () => { |
|
|
expect(getText($('li').eq(0))).toBe('Apple'); |
|
|
expect(getText($('li').eq(1))).toBe('Orange'); |
|
|
expect(getText($('li').eq(2))).toBe('Pear'); |
|
|
expect(getText($('li').eq(3))).toBeUndefined(); |
|
|
expect(getText($('li').eq(-1))).toBe('Pear'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.get', () => { |
|
|
it('(i) : should return the element at the specified index', () => { |
|
|
const children = $('#fruits').children(); |
|
|
expect(children.get(0)).toBe(children[0]); |
|
|
expect(children.get(1)).toBe(children[1]); |
|
|
expect(children.get(2)).toBe(children[2]); |
|
|
}); |
|
|
|
|
|
it('(-1) : should return the element indexed from the end of the collection', () => { |
|
|
const children = $('#fruits').children(); |
|
|
expect(children.get(-1)).toBe(children[2]); |
|
|
expect(children.get(-2)).toBe(children[1]); |
|
|
expect(children.get(-3)).toBe(children[0]); |
|
|
}); |
|
|
|
|
|
it('() : should return an array containing all of the collection', () => { |
|
|
const children = $('#fruits').children(); |
|
|
const all = children.get(); |
|
|
expect(Array.isArray(all)).toBe(true); |
|
|
expect(all).toStrictEqual([children[0], children[1], children[2]]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.index', () => { |
|
|
describe('() :', () => { |
|
|
it('returns the index of a child amongst its siblings', () => { |
|
|
expect($('.orange').index()).toBe(1); |
|
|
}); |
|
|
it('returns -1 when the selection has no parent', () => { |
|
|
expect($('<div/>').index()).toBe(-1); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('(selector) :', () => { |
|
|
it('returns the index of the first element in the set matched by `selector`', () => { |
|
|
expect($('.apple').index('#fruits, li')).toBe(1); |
|
|
}); |
|
|
it('returns -1 when the item is not present in the set matched by `selector`', () => { |
|
|
expect($('.apple').index('#fuits')).toBe(-1); |
|
|
}); |
|
|
it('returns -1 when the first element in the set has no parent', () => { |
|
|
expect($('<div/>').index('*')).toBe(-1); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('(node) :', () => { |
|
|
it('returns the index of the given node within the current selection', () => { |
|
|
const $lis = $('li'); |
|
|
expect($lis.index($lis.get(1))).toBe(1); |
|
|
}); |
|
|
it('returns the index of the given node within the current selection when the current selection has no parent', () => { |
|
|
const $apple = $('.apple').remove(); |
|
|
|
|
|
expect($apple.index($apple.get(0))).toBe(0); |
|
|
}); |
|
|
it('returns -1 when the given node is not present in the current selection', () => { |
|
|
expect($('li').index($('#fruits').get(0))).toBe(-1); |
|
|
}); |
|
|
it('returns -1 when the current selection is empty', () => { |
|
|
expect($('.not-fruit').index($('#fruits').get(0))).toBe(-1); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('(selection) :', () => { |
|
|
it('returns the index of the first node in the provided selection within the current selection', () => { |
|
|
const $lis = $('li'); |
|
|
expect($lis.index($('.orange, .pear'))).toBe(1); |
|
|
}); |
|
|
it('returns -1 when the given node is not present in the current selection', () => { |
|
|
expect($('li').index($('#fruits'))).toBe(-1); |
|
|
}); |
|
|
it('returns -1 when the current selection is empty', () => { |
|
|
expect($('.not-fruit').index($('#fruits'))).toBe(-1); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.slice', () => { |
|
|
it('(start) : should return all elements after the given index', () => { |
|
|
const sliced = $('li').slice(1); |
|
|
expect(sliced).toHaveLength(2); |
|
|
expect(getText(sliced.eq(0))).toBe('Orange'); |
|
|
expect(getText(sliced.eq(1))).toBe('Pear'); |
|
|
}); |
|
|
|
|
|
it('(start, end) : should return all elements matching the given range', () => { |
|
|
const sliced = $('li').slice(1, 2); |
|
|
expect(sliced).toHaveLength(1); |
|
|
expect(getText(sliced.eq(0))).toBe('Orange'); |
|
|
}); |
|
|
|
|
|
it('(-start) : should return element matching the offset from the end', () => { |
|
|
const sliced = $('li').slice(-1); |
|
|
expect(sliced).toHaveLength(1); |
|
|
expect(getText(sliced.eq(0))).toBe('Pear'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.end() :', () => { |
|
|
let $fruits: Cheerio<Element>; |
|
|
|
|
|
beforeEach(() => { |
|
|
$fruits = $('#fruits').children(); |
|
|
}); |
|
|
|
|
|
it('returns an empty object at the end of the chain', () => { |
|
|
expect($fruits.end().end().end()).toBeTruthy(); |
|
|
expect($fruits.end().end().end()).toHaveLength(0); |
|
|
}); |
|
|
it('find', () => { |
|
|
expect($fruits.find('.apple').end()).toBe($fruits); |
|
|
}); |
|
|
it('filter', () => { |
|
|
expect($fruits.filter('.apple').end()).toBe($fruits); |
|
|
}); |
|
|
it('map', () => { |
|
|
expect( |
|
|
$fruits |
|
|
.map(function () { |
|
|
return this; |
|
|
}) |
|
|
.end(), |
|
|
).toBe($fruits); |
|
|
}); |
|
|
it('contents', () => { |
|
|
expect($fruits.contents().end()).toBe($fruits); |
|
|
}); |
|
|
it('eq', () => { |
|
|
expect($fruits.eq(1).end()).toBe($fruits); |
|
|
}); |
|
|
it('first', () => { |
|
|
expect($fruits.first().end()).toBe($fruits); |
|
|
}); |
|
|
it('last', () => { |
|
|
expect($fruits.last().end()).toBe($fruits); |
|
|
}); |
|
|
it('slice', () => { |
|
|
expect($fruits.slice(1).end()).toBe($fruits); |
|
|
}); |
|
|
it('children', () => { |
|
|
expect($fruits.children().end()).toBe($fruits); |
|
|
}); |
|
|
it('parent', () => { |
|
|
expect($fruits.parent().end()).toBe($fruits); |
|
|
}); |
|
|
it('parents', () => { |
|
|
expect($fruits.parents().end()).toBe($fruits); |
|
|
}); |
|
|
it('closest', () => { |
|
|
expect($fruits.closest('ul').end()).toBe($fruits); |
|
|
}); |
|
|
it('siblings', () => { |
|
|
expect($fruits.siblings().end()).toBe($fruits); |
|
|
}); |
|
|
it('next', () => { |
|
|
expect($fruits.next().end()).toBe($fruits); |
|
|
}); |
|
|
it('nextAll', () => { |
|
|
expect($fruits.nextAll().end()).toBe($fruits); |
|
|
}); |
|
|
it('prev', () => { |
|
|
expect($fruits.prev().end()).toBe($fruits); |
|
|
}); |
|
|
it('prevAll', () => { |
|
|
expect($fruits.prevAll().end()).toBe($fruits); |
|
|
}); |
|
|
it('clone', () => { |
|
|
expect($fruits.clone().end()).toBe($fruits); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.add()', () => { |
|
|
let $fruits: Cheerio<AnyNode>; |
|
|
let $apple: Cheerio<Element>; |
|
|
let $orange: Cheerio<Element>; |
|
|
let $pear: Cheerio<Element>; |
|
|
|
|
|
beforeEach(() => { |
|
|
$ = load(food); |
|
|
$fruits = $('#fruits'); |
|
|
$apple = $('.apple'); |
|
|
$orange = $('.orange'); |
|
|
$pear = $('.pear'); |
|
|
}); |
|
|
|
|
|
describe('(selector) matched element :', () => { |
|
|
it('occurs before current selection', () => { |
|
|
const $selection = $orange.add('.apple'); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
it('is identical to the current selection', () => { |
|
|
const $selection = $orange.add('.orange'); |
|
|
|
|
|
expect($selection).toHaveLength(1); |
|
|
expect($selection[0]).toBe($orange[0]); |
|
|
}); |
|
|
it('occurs after current selection', () => { |
|
|
const $selection = $orange.add('.pear'); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($orange[0]); |
|
|
expect($selection[1]).toBe($pear[0]); |
|
|
}); |
|
|
it('contains the current selection', () => { |
|
|
const $selection = $orange.add('#fruits'); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
it('is a child of the current selection', () => { |
|
|
const $selection = $fruits.add('.orange'); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
it('is root object preserved', () => { |
|
|
const $selection = $('<div></div>').add('#fruits'); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection.eq(0).is('div')).toBe(true); |
|
|
expect($selection.eq(1).is($fruits.eq(0))).toBe(true); |
|
|
}); |
|
|
}); |
|
|
describe('(selector) matched elements :', () => { |
|
|
it('occur before the current selection', () => { |
|
|
const $selection = $pear.add('.apple, .orange'); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('include the current selection', () => { |
|
|
const $selection = $pear.add('#fruits li'); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('occur after the current selection', () => { |
|
|
const $selection = $apple.add('.orange, .pear'); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('occur within the current selection', () => { |
|
|
const $selection = $fruits.add('#fruits li'); |
|
|
|
|
|
expect($selection).toHaveLength(4); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($apple[0]); |
|
|
expect($selection[2]).toBe($orange[0]); |
|
|
expect($selection[3]).toBe($pear[0]); |
|
|
}); |
|
|
}); |
|
|
describe('(selector, context) :', () => { |
|
|
it(', context)', () => { |
|
|
const $selection = $fruits.add('li', '#vegetables'); |
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($('.carrot')[0]); |
|
|
expect($selection[2]).toBe($('.sweetcorn')[0]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('(element) honors document order when element occurs :', () => { |
|
|
it('before the current selection', () => { |
|
|
const $selection = $orange.add($apple[0]); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
it('after the current selection', () => { |
|
|
const $selection = $orange.add($pear[0]); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($orange[0]); |
|
|
expect($selection[1]).toBe($pear[0]); |
|
|
}); |
|
|
it('within the current selection', () => { |
|
|
const $selection = $fruits.add($orange[0]); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
it('as an ancestor of the current selection', () => { |
|
|
const $selection = $orange.add($fruits[0]); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
it('does not insert an element already contained within the current selection', () => { |
|
|
const $selection = $apple.add($apple[0]); |
|
|
|
|
|
expect($selection).toHaveLength(1); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
}); |
|
|
}); |
|
|
describe('([elements]) : elements', () => { |
|
|
it('occur before the current selection', () => { |
|
|
const $selection = $pear.add($('.apple, .orange').get()); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('include the current selection', () => { |
|
|
const $selection = $pear.add($('#fruits li').get()); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('occur after the current selection', () => { |
|
|
const $selection = $apple.add($('.orange, .pear').get()); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('occur within the current selection', () => { |
|
|
const $selection = $fruits.add($('#fruits li').get()); |
|
|
|
|
|
expect($selection).toHaveLength(4); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($apple[0]); |
|
|
expect($selection[2]).toBe($orange[0]); |
|
|
expect($selection[3]).toBe($pear[0]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('(html) : correctly parses and adds the new elements', () => { |
|
|
const $selection = $apple.add('<li class="banana">banana</li>'); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection.is('.apple')).toBe(true); |
|
|
expect($selection.is('.banana')).toBe(true); |
|
|
}); |
|
|
|
|
|
describe('(selection) element in selection :', () => { |
|
|
it('occurs before current selection', () => { |
|
|
const $selection = $orange.add($('.apple')); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
it('is identical to the current selection', () => { |
|
|
const $selection = $orange.add($('.orange')); |
|
|
|
|
|
expect($selection).toHaveLength(1); |
|
|
expect($selection[0]).toBe($orange[0]); |
|
|
}); |
|
|
it('occurs after current selection', () => { |
|
|
const $selection = $orange.add($('.pear')); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($orange[0]); |
|
|
expect($selection[1]).toBe($pear[0]); |
|
|
}); |
|
|
it('contains the current selection', () => { |
|
|
const $selection = $orange.add($('#fruits')); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
it('is a child of the current selection', () => { |
|
|
const $selection = $fruits.add($('.orange')); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
}); |
|
|
}); |
|
|
describe('(selection) elements in the selection :', () => { |
|
|
it('occur before the current selection', () => { |
|
|
const $selection = $pear.add($('.apple, .orange')); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('include the current selection', () => { |
|
|
const $selection = $pear.add($('#fruits li')); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('occur after the current selection', () => { |
|
|
const $selection = $apple.add($('.orange, .pear')); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($apple[0]); |
|
|
expect($selection[1]).toBe($orange[0]); |
|
|
expect($selection[2]).toBe($pear[0]); |
|
|
}); |
|
|
it('occur within the current selection', () => { |
|
|
const $selection = $fruits.add($('#fruits li')); |
|
|
|
|
|
expect($selection).toHaveLength(4); |
|
|
expect($selection[0]).toBe($fruits[0]); |
|
|
expect($selection[1]).toBe($apple[0]); |
|
|
expect($selection[2]).toBe($orange[0]); |
|
|
expect($selection[3]).toBe($pear[0]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('(selection) :', () => { |
|
|
it('modifying nested selections should not impact the parent [#834]', () => { |
|
|
const apple_pear = $apple.add($pear); |
|
|
|
|
|
|
|
|
apple_pear.addClass('red'); |
|
|
|
|
|
expect($apple.hasClass('red')).toBe(true); |
|
|
expect($pear.hasClass('red')).toBe(true); |
|
|
|
|
|
|
|
|
$pear.addClass('green'); |
|
|
expect($pear.hasClass('green')).toBe(true); |
|
|
expect($apple.hasClass('green')).toBe(false); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.addBack', () => { |
|
|
describe('() :', () => { |
|
|
it('includes siblings and self', () => { |
|
|
const $selection = $('.orange').siblings().addBack(); |
|
|
|
|
|
expect($selection).toHaveLength(3); |
|
|
expect($selection[0]).toBe($('.apple')[0]); |
|
|
expect($selection[1]).toBe($('.orange')[0]); |
|
|
expect($selection[2]).toBe($('.pear')[0]); |
|
|
}); |
|
|
it('includes children and self', () => { |
|
|
const $selection = $('#fruits').children().addBack(); |
|
|
|
|
|
expect($selection).toHaveLength(4); |
|
|
expect($selection[0]).toBe($('#fruits')[0]); |
|
|
expect($selection[1]).toBe($('.apple')[0]); |
|
|
expect($selection[2]).toBe($('.orange')[0]); |
|
|
expect($selection[3]).toBe($('.pear')[0]); |
|
|
}); |
|
|
it('includes parent and self', () => { |
|
|
const $selection = $('.apple').parent().addBack(); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($('#fruits')[0]); |
|
|
expect($selection[1]).toBe($('.apple')[0]); |
|
|
}); |
|
|
it('includes parents and self', () => { |
|
|
const q = load(food); |
|
|
const $selection = q('.apple').parents().addBack(); |
|
|
|
|
|
expect($selection).toHaveLength(5); |
|
|
expect($selection[0]).toBe(q('html')[0]); |
|
|
expect($selection[1]).toBe(q('body')[0]); |
|
|
expect($selection[2]).toBe(q('#food')[0]); |
|
|
expect($selection[3]).toBe(q('#fruits')[0]); |
|
|
expect($selection[4]).toBe(q('.apple')[0]); |
|
|
}); |
|
|
}); |
|
|
it('(filter) : filters the previous selection', () => { |
|
|
const $selection = $('li').eq(1).addBack('.apple'); |
|
|
|
|
|
expect($selection).toHaveLength(2); |
|
|
expect($selection[0]).toBe($('.apple')[0]); |
|
|
expect($selection[1]).toBe($('.orange')[0]); |
|
|
}); |
|
|
it('() : fails gracefully when no args are passed', () => { |
|
|
const $div = cheerio('<div>'); |
|
|
expect($div.addBack()).toBe($div); |
|
|
}); |
|
|
}); |
|
|
|
|
|
describe('.is', () => { |
|
|
it('() : should return false', () => { |
|
|
expect($('li.apple').is()).toBe(false); |
|
|
}); |
|
|
|
|
|
it('(true selector) : should return true', () => { |
|
|
expect(cheerio('#vegetables', vegetables).is('ul')).toBe(true); |
|
|
}); |
|
|
|
|
|
it('(false selector) : should return false', () => { |
|
|
expect(cheerio('#vegetables', vegetables).is('div')).toBe(false); |
|
|
}); |
|
|
|
|
|
it('(true selection) : should return true', () => { |
|
|
const $vegetables = cheerio('li', vegetables); |
|
|
expect($vegetables.is($vegetables.eq(1))).toBe(true); |
|
|
}); |
|
|
|
|
|
it('(false selection) : should return false', () => { |
|
|
const $vegetableList = cheerio(vegetables); |
|
|
const $vegetables = $vegetableList.find('li'); |
|
|
expect($vegetables.is($vegetableList)).toBe(false); |
|
|
}); |
|
|
|
|
|
it('(true element) : should return true', () => { |
|
|
const $vegetables = cheerio('li', vegetables); |
|
|
expect($vegetables.is($vegetables[0])).toBe(true); |
|
|
}); |
|
|
|
|
|
it('(false element) : should return false', () => { |
|
|
const $vegetableList = cheerio(vegetables); |
|
|
const $vegetables = $vegetableList.find('li'); |
|
|
expect($vegetables.is($vegetableList[0])).toBe(false); |
|
|
}); |
|
|
|
|
|
it('(true predicate) : should return true', () => { |
|
|
const result = $('li').is(function () { |
|
|
return this.tagName === 'li' && $(this).hasClass('pear'); |
|
|
}); |
|
|
expect(result).toBe(true); |
|
|
}); |
|
|
|
|
|
it('(false predicate) : should return false', () => { |
|
|
const result = $('li') |
|
|
.last() |
|
|
.is(function () { |
|
|
return this.tagName === 'ul'; |
|
|
}); |
|
|
expect(result).toBe(false); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|