cheerio
Version:
The fast, flexible & elegant library for parsing and manipulating HTML and XML.
1,356 lines (1,106 loc) • 72.1 kB
text/typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { load, type CheerioAPI, type Cheerio } from '../index.js';
import {
fruits,
divcontainers,
mixedText,
unwrapspans,
} from '../__fixtures__/fixtures.js';
import type { AnyNode, Element } from 'domhandler';
describe('$(...)', () => {
let $: CheerioAPI;
let $fruits: Cheerio<Element>;
beforeEach(() => {
$ = load(fruits);
$fruits = $('#fruits');
});
describe('.wrap', () => {
it('(Cheerio object) : should insert the element and add selected element(s) as its child', () => {
const $redFruits = $('<div class="red-fruits"></div>');
$('.apple').wrap($redFruits);
expect($fruits.children().eq(0).hasClass('red-fruits')).toBe(true);
expect($('.red-fruits').children().eq(0).hasClass('apple')).toBe(true);
expect($fruits.children().eq(1).hasClass('orange')).toBe(true);
expect($redFruits.children()).toHaveLength(1);
});
it('(element) : should wrap the base element correctly', () => {
$('ul').wrap('<a></a>');
expect($('body').children()[0].tagName).toBe('a');
});
it('(document) : should ignore document node', () => {
$.root().wrap('<a></a>');
expect($.root()[0]).toHaveProperty('type', 'root');
});
it('(element) : should insert the element and add selected element(s) as its child', () => {
const $redFruits = $('<div class="red-fruits"></div>');
$('.apple').wrap($redFruits[0]);
expect($fruits.children()[0]).toBe($redFruits[0]);
expect($redFruits.children()).toHaveLength(1);
expect($redFruits.children()[0]).toBe($('.apple')[0]);
expect($fruits.children()[1]).toBe($('.orange')[0]);
});
it('(html) : should insert the markup and add selected element(s) as its child', () => {
$('.apple').wrap('<div class="red-fruits"> </div>');
expect($fruits.children().eq(0).hasClass('red-fruits')).toBe(true);
expect($('.red-fruits').children().eq(0).hasClass('apple')).toBe(true);
expect($fruits.children().eq(1).hasClass('orange')).toBe(true);
expect($('.red-fruits').children()).toHaveLength(1);
});
it('(html) : discards extraneous markup', () => {
$('.apple').wrap('<div></div><p></p>');
expect($('div')).toHaveLength(1);
expect($('p')).toHaveLength(0);
});
it('(html) : wraps with nested elements', () => {
const $orangeFruits = $(
'<div class="orange-fruits"><div class="and-stuff"></div></div>',
);
$('.orange').wrap($orangeFruits);
expect($fruits.children().eq(1).hasClass('orange-fruits')).toBe(true);
expect($('.orange-fruits').children().eq(0).hasClass('and-stuff')).toBe(
true,
);
expect($fruits.children().eq(2).hasClass('pear')).toBe(true);
expect($('.orange-fruits').children()).toHaveLength(1);
});
it('(html) : should only worry about the first tag children', () => {
const delicious = '<span> This guy is delicious: <b></b></span>';
$('.apple').wrap(delicious);
expect($('b>.apple')).toHaveLength(1);
});
it('(selector) : wraps the content with a copy of the first matched element', () => {
$('.apple').wrap('.orange, .pear');
const $oranges = $('.orange');
expect($('.pear')).toHaveLength(1);
expect($oranges).toHaveLength(2);
expect($oranges.eq(0).parent()[0]).toBe($fruits[0]);
expect($oranges.eq(0).children()).toHaveLength(1);
expect($oranges.eq(0).children()[0]).toBe($('.apple')[0]);
expect($('.apple').parent()[0]).toBe($oranges[0]);
expect($oranges.eq(1).children()).toHaveLength(0);
});
it('(fn) : should invoke the provided function with the correct arguments and context', () => {
const $children = $fruits.children();
const args: [number, AnyNode][] = [];
const thisValues: AnyNode[] = [];
$children.wrap(function (...myArgs) {
args.push(myArgs);
thisValues.push(this);
return '';
});
expect(args).toStrictEqual([
[0, $children[0]],
[1, $children[1]],
[2, $children[2]],
]);
expect(thisValues).toStrictEqual([
$children[0],
$children[1],
$children[2],
]);
});
it('(fn) : should use the returned HTML to wrap each element', () => {
const $children = $fruits.children();
const tagNames = ['div', 'span', 'p'];
$children.wrap(() => `<${tagNames.shift()}>`);
expect($fruits.find('div')).toHaveLength(1);
expect($fruits.find('div')[0]).toBe($fruits.children()[0]);
expect($fruits.find('.apple')).toHaveLength(1);
expect($fruits.find('.apple').parent()[0]).toBe($fruits.find('div')[0]);
expect($fruits.find('span')).toHaveLength(1);
expect($fruits.find('span')[0]).toBe($fruits.children()[1]);
expect($fruits.find('.orange')).toHaveLength(1);
expect($fruits.find('.orange').parent()[0]).toBe($fruits.find('span')[0]);
expect($fruits.find('p')).toHaveLength(1);
expect($fruits.find('p')[0]).toBe($fruits.children()[2]);
expect($fruits.find('.pear')).toHaveLength(1);
expect($fruits.find('.pear').parent()[0]).toBe($fruits.find('p')[0]);
});
it('(fn) : should use the returned Cheerio object to wrap each element', () => {
const $children = $fruits.children();
const tagNames = ['span', 'p', 'div'];
$children.wrap(() => $(`<${tagNames.shift()}>`));
expect($fruits.find('span')).toHaveLength(1);
expect($fruits.find('span')[0]).toBe($fruits.children()[0]);
expect($fruits.find('.apple')).toHaveLength(1);
expect($fruits.find('.apple').parent()[0]).toBe($fruits.find('span')[0]);
expect($fruits.find('p')).toHaveLength(1);
expect($fruits.find('p')[0]).toBe($fruits.children()[1]);
expect($fruits.find('.orange')).toHaveLength(1);
expect($fruits.find('.orange').parent()[0]).toBe($fruits.find('p')[0]);
expect($fruits.find('div')).toHaveLength(1);
expect($fruits.find('div')[0]).toBe($fruits.children()[2]);
expect($fruits.find('.pear')).toHaveLength(1);
expect($fruits.find('.pear').parent()[0]).toBe($fruits.find('div')[0]);
});
it('($(...)) : for each element it should add a wrapper elment and add the selected element as its child', () => {
const $fruitDecorator = $('<div class="fruit-decorator"></div>');
$('li').wrap($fruitDecorator);
expect($fruits.children().eq(0).hasClass('fruit-decorator')).toBe(true);
expect($fruits.children().eq(0).children().eq(0).hasClass('apple')).toBe(
true,
);
expect($fruits.children().eq(1).hasClass('fruit-decorator')).toBe(true);
expect($fruits.children().eq(1).children().eq(0).hasClass('orange')).toBe(
true,
);
expect($fruits.children().eq(2).hasClass('fruit-decorator')).toBe(true);
expect($fruits.children().eq(2).children().eq(0).hasClass('pear')).toBe(
true,
);
});
});
describe('.wrapInner', () => {
it('(Cheerio object) : should insert the element and add selected element(s) as its parent', () => {
const $container = $('<div class="container"></div>') as Cheerio<Element>;
$fruits.wrapInner($container);
expect($fruits.children()[0]).toBe($container[0]);
expect($container[0].parent).toBe($fruits[0]);
expect($container[0].children[0]).toBe($('.apple')[0]);
expect($container[0].children[1]).toBe($('.orange')[0]);
expect($('.apple')[0].parent).toBe($container[0]);
expect($fruits.children()).toHaveLength(1);
expect($container.children()).toHaveLength(3);
});
it('(element) : should insert the element and add selected element(s) as its parent', () => {
const $container = $('<div class="container"></div>') as Cheerio<Element>;
$fruits.wrapInner($container[0]);
expect($fruits.children()[0]).toBe($container[0]);
expect($container[0].parent).toBe($fruits[0]);
expect($container[0].children[0]).toBe($('.apple')[0]);
expect($container[0].children[1]).toBe($('.orange')[0]);
expect($('.apple')[0].parent).toBe($container[0]);
expect($fruits.children()).toHaveLength(1);
expect($container.children()).toHaveLength(3);
});
it('(html) : should ignore text nodes', () => {
const $test = load(mixedText);
$test($test('body')[0].children).wrapInner('<test>');
expect($test('body').html()).toBe(
'<a><test>1</test></a>TEXT<b><test>2</test></b>',
);
});
it('(html) : should insert the element and add selected element(s) as its parent', () => {
$fruits.wrapInner('<div class="container"></div>');
expect($fruits.children()[0]).toBe($('.container')[0]);
expect($('.container')[0].parent).toBe($fruits[0]);
expect($('.container')[0].children[0]).toBe($('.apple')[0]);
expect($('.container')[0].children[1]).toBe($('.orange')[0]);
expect($('.apple')[0].parent).toBe($('.container')[0]);
expect($fruits.children()).toHaveLength(1);
expect($('.container').children()).toHaveLength(3);
});
it("(selector) : should wrap the html of the element with the selector's first match", () => {
$('.apple').wrapInner('.orange, .pear');
const $oranges = $('.orange');
expect($('.pear')).toHaveLength(1);
expect($oranges).toHaveLength(2);
expect($oranges.eq(0).parent()[0]).toBe($('.apple')[0]);
expect($oranges.eq(0).text()).toBe('Apple');
expect($('.apple').eq(0).children()[0]).toBe($oranges[0]);
expect($oranges.eq(1).parent()[0]).toBe($fruits[0]);
expect($oranges.eq(1).text()).toBe('Orange');
});
it('(fn) : should invoke the provided function with the correct arguments and context', () => {
const $children = $fruits.children();
const args: [number, AnyNode][] = [];
const thisValues: AnyNode[] = [];
$children.wrapInner(function (...myArgs) {
args.push(myArgs);
thisValues.push(this);
return this;
});
expect(args).toStrictEqual([
[0, $children[0]],
[1, $children[1]],
[2, $children[2]],
]);
expect(thisValues).toStrictEqual([
$children[0],
$children[1],
$children[2],
]);
});
it("(fn) : should use the returned HTML to wrap each element's contents", () => {
const $children = $fruits.children();
const tagNames = ['div', 'span', 'p'];
$children.wrapInner(() => `<${tagNames.shift()}>`);
expect($fruits.find('div')).toHaveLength(1);
expect($fruits.find('div')[0]).toBe($('.apple').children()[0]);
expect($fruits.find('.apple')).toHaveLength(1);
expect($fruits.find('span')).toHaveLength(1);
expect($fruits.find('span')[0]).toBe($('.orange').children()[0]);
expect($fruits.find('.orange')).toHaveLength(1);
expect($fruits.find('p')).toHaveLength(1);
expect($fruits.find('p')[0]).toBe($('.pear').children()[0]);
expect($fruits.find('.pear')).toHaveLength(1);
});
it("(fn) : should use the returned Cheerio object to wrap each element's contents", () => {
const $children = $fruits.children();
const tags = [$('<div></div>'), $('<span></span>'), $('<p></p>')];
$children.wrapInner(() => tags.shift()!);
expect($fruits.find('div')).toHaveLength(1);
expect($fruits.find('div')[0]).toBe($('.apple').children()[0]);
expect($fruits.find('.apple')).toHaveLength(1);
expect($fruits.find('span')).toHaveLength(1);
expect($fruits.find('span')[0]).toBe($('.orange').children()[0]);
expect($fruits.find('.orange')).toHaveLength(1);
expect($fruits.find('p')).toHaveLength(1);
expect($fruits.find('p')[0]).toBe($('.pear').children()[0]);
expect($fruits.find('.pear')).toHaveLength(1);
});
it('($(...)) : for each element it should add a wrapper elment and add the selected element as its child', () => {
const $fruitDecorator = $('<div class="fruit-decorator"></div>');
const $children = $fruits.children();
$('li').wrapInner($fruitDecorator);
expect($('.fruit-decorator')).toHaveLength(3);
expect($children.eq(0).children().eq(0).hasClass('fruit-decorator')).toBe(
true,
);
expect($children.eq(0).hasClass('apple')).toBe(true);
expect($children.eq(1).children().eq(0).hasClass('fruit-decorator')).toBe(
true,
);
expect($children.eq(1).hasClass('orange')).toBe(true);
expect($children.eq(2).children().eq(0).hasClass('fruit-decorator')).toBe(
true,
);
expect($children.eq(2).hasClass('pear')).toBe(true);
});
it('(html) : wraps with nested elements', () => {
const $badOrangeJoke = $(
'<div class="orange-you-glad"><div class="i-didnt-say-apple"></div></div>',
);
$('.orange').wrapInner($badOrangeJoke);
expect($('.orange').children().eq(0).hasClass('orange-you-glad')).toBe(
true,
);
expect(
$('.orange-you-glad').children().eq(0).hasClass('i-didnt-say-apple'),
).toBe(true);
expect($fruits.children().eq(2).hasClass('pear')).toBe(true);
expect($('.orange-you-glad').children()).toHaveLength(1);
});
it('(html) : should only worry about the first tag children', () => {
const delicious = '<span> This guy is delicious: <b></b></span>';
$('.apple').wrapInner(delicious);
expect($('.apple>span>b')).toHaveLength(1);
expect($('.apple>span>b').text()).toBe('Apple');
});
});
describe('.unwrap', () => {
let $elem: CheerioAPI;
beforeEach(() => {
$elem = load(unwrapspans);
});
it('() : should be unwrap span elements', () => {
const abcd = $elem('#unwrap1 > span, #unwrap2 > span').get();
const abcdef = $elem('#unwrap span').get();
// Make #unwrap1 and #unwrap2 go away
expect(
$elem('#unwrap1 span').add('#unwrap2 span:first-child').unwrap(),
).toHaveLength(3);
/*
* .toEqual
* all four spans should still exist
*/
expect($elem('#unwrap > span').get()).toEqual(abcd);
// Make all b elements in #unwrap3 go away
expect($elem('#unwrap3 span').unwrap().get()).toEqual(
$elem('#unwrap3 > span').get(),
);
// Make #unwrap3 go away
expect($elem('#unwrap3 span').unwrap().get()).toEqual(
$elem('#unwrap > span.unwrap3').get(),
);
// #unwrap only contains 6 child spans
expect($elem('#unwrap').children().get()).toEqual(abcdef);
// Make the 6 spans become children of body
expect($elem('#unwrap > span').unwrap().get()).toEqual(
$elem('body > span.unwrap').get(),
);
// Can't unwrap children of body
expect($elem('body > span.unwrap').unwrap().get()).toEqual(
$elem('body > span.unwrap').get(),
);
// Can't unwrap children of body
expect($elem('body > span.unwrap').unwrap().get()).toEqual(abcdef);
// Can't unwrap children of body
expect($elem('body > span.unwrap').get()).toEqual(abcdef);
});
it('(selector) : should only unwrap element parent what specified', () => {
const abcd = $elem('#unwrap1 > span, #unwrap2 > span').get();
// Shouldn't unwrap, no match
$elem('#unwrap1 span').unwrap('#unwrap2');
expect($elem('#unwrap1')).toHaveLength(1);
// Shouldn't unwrap, no match
$elem('#unwrap1 span').unwrap('span');
expect($elem('#unwrap1')).toHaveLength(1);
// Unwraps
$elem('#unwrap1 span').unwrap('#unwrap1');
expect($elem('#unwrap1')).toHaveLength(0);
// Should not unwrap - unmatched unwrap
$elem('#unwrap2 span').unwrap('quote');
expect($elem('#unwrap > span')).toHaveLength(2);
// Check return values - matched unwrap
$elem('#unwrap2 span').unwrap('#unwrap2');
expect($elem('#unwrap > span').get()).toEqual(abcd);
});
});
describe('.wrapAll', () => {
let doc: CheerioAPI;
let $inner: Cheerio<Element>;
beforeEach(() => {
doc = load(divcontainers);
$inner = doc('.inner');
});
it('(Cheerio object) : should insert the element and wrap elements with it', () => {
$inner.wrapAll(doc('#new'));
const $container = doc('.container');
const $wrap = doc('b');
expect($container).toHaveLength(2);
expect($container[0].children).toHaveLength(1);
expect($container[1].children).toHaveLength(0);
expect($container[0].children[0]).toBe(doc('#new')[0]);
expect($inner).toHaveLength(4);
expect($wrap[0].children).toHaveLength(4);
expect($inner[0].parent).toBe($wrap[0]);
expect($inner[1].parent).toBe($wrap[0]);
expect($inner[2].parent).toBe($wrap[0]);
expect($inner[3].parent).toBe($wrap[0]);
});
it('(html) : should wrap elements with it', () => {
$inner.wrapAll('<div class="wrap"></div>');
const $container = doc('.container');
const $wrap = doc('.wrap');
expect($inner).toHaveLength(4);
expect($container).toHaveLength(2);
expect($wrap).toHaveLength(1);
expect($wrap[0].children).toHaveLength(4);
expect($container[0].children).toHaveLength(1);
expect($container[1].children).toHaveLength(0);
expect($inner[0].parent).toBe($wrap[0]);
expect($inner[1].parent).toBe($wrap[0]);
expect($inner[2].parent).toBe($wrap[0]);
expect($inner[3].parent).toBe($wrap[0]);
expect($wrap[0].parent).toBe($container[0]);
expect($container[0].children[0]).toBe($wrap[0]);
});
it('(html) : should wrap single element with it', () => {
const parent = doc('<p>').wrapAll('<div></div>').parent();
expect(parent).toHaveLength(1);
expect(parent.is('div')).toBe(true);
});
it('(selector) : should find element from dom, wrap elements with it', () => {
$inner.wrapAll('#new');
const $container = doc('.container');
const $wrap = doc('b');
const $new = doc('#new');
expect($inner).toHaveLength(4);
expect($container).toHaveLength(2);
expect($container[0].children).toHaveLength(1);
expect($container[1].children).toHaveLength(0);
expect($wrap[0].children).toHaveLength(4);
expect($inner[0].parent).toBe($wrap[0]);
expect($inner[1].parent).toBe($wrap[0]);
expect($inner[2].parent).toBe($wrap[0]);
expect($inner[3].parent).toBe($wrap[0]);
expect($new[0].parent).toBe($container[0]);
expect($container[0].children[0]).toBe($new[0]);
});
it('(function) : check execution', () => {
const $container = doc('.container');
const p = $container[0].parent;
const result = $container.wrapAll(
() => "<div class='red'><div class='tmp'></div></div>",
);
expect(result.parent()).toHaveLength(1);
expect($container.eq(0).parent().parent().is('.red')).toBe(true);
expect($container.eq(1).parent().parent().is('.red')).toBe(true);
expect($container.eq(0).parent().parent().parent().is(p)).toBe(true);
});
it('(function) : check execution characteristics', () => {
const $new = doc('#new');
let i = 0;
doc('no-result').wrapAll(() => {
i++;
return '';
});
expect(i).toBeFalsy();
$new.wrapAll(function (index) {
expect(this).toBe($new[0]);
expect(index).toBe(0);
return this;
});
});
it('(nodes) : should skip text nodes', () => {
const $text = load(mixedText);
const $body = $text($text('body')[0].children);
$body.wrapAll($text('body')[0].children.slice(1));
expect($text('body').html()).toBe('TEXT<b>2<a>1</a>TEXT<b>2</b></b>');
});
});
describe('.append', () => {
it('() : should do nothing', () => {
expect($('#fruits').append()[0].tagName).toBe('ul');
});
it('(null) : should do nothing', () => {
$fruits.append(null as never);
expect($fruits.children()).toHaveLength(3);
});
it('(html) : should add element as last child', () => {
$fruits.append('<li class="plum">Plum</li>');
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
it('(html) : should not fail on text nodes', () => {
expect($(mixedText).append(' UP').html()).toBe('1 UP');
});
it('($(...)) : should add element as last child', () => {
const $plum = $('<li class="plum">Plum</li>');
$fruits.append($plum);
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
it('(Node) : should add element as last child', () => {
const plum = $('<li class="plum">Plum</li>')[0];
$fruits.append(plum);
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
it('(existing Node) : should remove node from previous location', () => {
const apple = $fruits.children()[0];
expect($fruits.children()).toHaveLength(3);
$fruits.append(apple);
const $children = $fruits.children();
expect($children).toHaveLength(3);
expect($children[0]).not.toBe(apple);
expect($children[2]).toBe(apple);
});
it('(existing Node) : should remove existing node from previous location', () => {
const apple = $fruits.children()[0];
const $dest = $('<div></div>');
expect($fruits.children()).toHaveLength(3);
$dest.append(apple);
const $children = $fruits.children();
expect($children).toHaveLength(2);
expect($children[0]).not.toBe(apple);
expect($dest.children()).toHaveLength(1);
expect($dest.children()[0]).toBe(apple);
});
it('(existing Node) : should update original direct siblings', () => {
$('.pear').append($('.orange'));
expect($('.apple').next()[0]).toBe($('.pear')[0]);
expect($('.pear').prev()[0]).toBe($('.apple')[0]);
});
it('(existing Node) : should clone all but the last occurrence', () => {
const $originalApple = $('.apple');
$('.orange, .pear').append($originalApple);
const $apples = $('.apple');
expect($apples).toHaveLength(2);
expect($apples.eq(0).parent()[0]).toBe($('.orange')[0]);
expect($apples.eq(1).parent()[0]).toBe($('.pear')[0]);
expect($apples[1]).toBe($originalApple[0]);
});
it('(elem) : should NOP if removed', () => {
const $apple = $('.apple');
$apple.remove();
$fruits.append($apple);
expect($fruits.children().eq(2).hasClass('apple')).toBe(true);
});
it('($(...), html) : should add multiple elements as last children', () => {
const $plum = $('<li class="plum">Plum</li>');
const grape = '<li class="grape">Grape</li>';
$fruits.append($plum, grape);
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
expect($fruits.children().eq(4).hasClass('grape')).toBe(true);
});
it('(Array) : should append all elements in the array', () => {
const more = $(
'<li class="plum">Plum</li><li class="grape">Grape</li>',
).get();
$fruits.append(more);
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
expect($fruits.children().eq(4).hasClass('grape')).toBe(true);
});
it('(fn) : should invoke the callback with the correct arguments and context', () => {
const $fruits = $('#fruits').children();
const args: [number, string][] = [];
const thisValues: AnyNode[] = [];
$fruits.append(function (...myArgs) {
args.push(myArgs);
thisValues.push(this);
return this;
});
expect(args).toStrictEqual([
[0, 'Apple'],
[1, 'Orange'],
[2, 'Pear'],
]);
expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);
});
it('(fn) : should add returned string as last child', () => {
const $fruits = $('#fruits').children();
$fruits.append(() => '<div class="first">');
const $apple = $fruits.eq(0);
const $orange = $fruits.eq(1);
const $pear = $fruits.eq(2);
expect($apple.find('.first')[0]).toBe($apple.contents()[1]);
expect($orange.find('.first')[0]).toBe($orange.contents()[1]);
expect($pear.find('.first')[0]).toBe($pear.contents()[1]);
});
it('(fn) : should add returned Cheerio object as last child', () => {
const $fruits = $('#fruits').children();
$fruits.append(() => $('<div class="second">'));
const $apple = $fruits.eq(0);
const $orange = $fruits.eq(1);
const $pear = $fruits.eq(2);
expect($apple.find('.second')[0]).toBe($apple.contents()[1]);
expect($orange.find('.second')[0]).toBe($orange.contents()[1]);
expect($pear.find('.second')[0]).toBe($pear.contents()[1]);
});
it('(fn) : should add returned Node as last child', () => {
const $fruits = $('#fruits').children();
$fruits.append(() => $('<div class="third">')[0]);
const $apple = $fruits.eq(0);
const $orange = $fruits.eq(1);
const $pear = $fruits.eq(2);
expect($apple.find('.third')[0]).toBe($apple.contents()[1]);
expect($orange.find('.third')[0]).toBe($orange.contents()[1]);
expect($pear.find('.third')[0]).toBe($pear.contents()[1]);
});
it('should maintain correct object state (Issue: #10)', () => {
const $obj = $('<div></div>')
.append('<div><div></div></div>')
.children()
.children()
.parent();
expect($obj).toBeTruthy();
});
it('($(...)) : should remove from root element', () => {
const $plum = $('<li class="plum">Plum</li>');
const { parent } = $plum[0];
expect(parent).toBeTruthy();
$fruits.append($plum);
expect($plum[0].parent?.type).not.toBe('root');
expect(parent?.childNodes).not.toContain($plum[0]);
});
});
describe('.prepend', () => {
it('() : should do nothing', () => {
expect($('#fruits').prepend()[0].tagName).toBe('ul');
});
it('(html) : should add element as first child', () => {
$fruits.prepend('<li class="plum">Plum</li>');
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
it('($(...)) : should add element as first child', () => {
const $plum = $('<li class="plum">Plum</li>');
$fruits.prepend($plum);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
it('($(...)) : should add style element as first child', () => {
const $style = $('<style>.foo {}</style>');
$fruits.prepend($style);
const styleTag = $fruits.children().get(0);
expect(styleTag?.tagName).toBe('style');
expect(styleTag?.children[0]).toHaveProperty('data', '.foo {}');
});
it('($(...)) : should add script element as first child', () => {
const $script = $('<script>var foo;</script>');
$fruits.prepend($script);
const scriptTag = $fruits.children().get(0);
expect(scriptTag?.tagName).toBe('script');
expect(scriptTag?.children[0]).toHaveProperty('data', 'var foo;');
});
it('(Node) : should add node as first child', () => {
const plum = $('<li class="plum">Plum</li>')[0];
$fruits.prepend(plum);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
it('(existing Node) : should remove existing nodes from previous locations', () => {
const pear = $fruits.children()[2];
expect($fruits.children()).toHaveLength(3);
$fruits.prepend(pear);
const $children = $fruits.children();
expect($children).toHaveLength(3);
expect($children[2]).not.toBe(pear);
expect($children[0]).toBe(pear);
});
it('(existing Node) : should update original direct siblings', () => {
$('.pear').prepend($('.orange'));
expect($('.apple').next()[0]).toBe($('.pear')[0]);
expect($('.pear').prev()[0]).toBe($('.apple')[0]);
});
it('(existing Node) : should clone all but the last occurrence', () => {
const $originalApple = $('.apple');
$('.orange, .pear').prepend($originalApple);
const $apples = $('.apple');
expect($apples).toHaveLength(2);
expect($apples.eq(0).parent()[0]).toBe($('.orange')[0]);
expect($apples.eq(1).parent()[0]).toBe($('.pear')[0]);
expect($apples[1]).toBe($originalApple[0]);
});
it('(elem) : should handle if removed', () => {
const $apple = $('.apple');
$apple.remove();
$fruits.prepend($apple);
expect($fruits.children().eq(0).hasClass('apple')).toBe(true);
});
it('(Array) : should add all elements in the array as initial children', () => {
const more = $(
'<li class="plum">Plum</li><li class="grape">Grape</li>',
).get();
$fruits.prepend(more);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
expect($fruits.children().eq(1).hasClass('grape')).toBe(true);
});
it('(html, $(...), html) : should add multiple elements as first children', () => {
const $plum = $('<li class="plum">Plum</li>');
const grape = '<li class="grape">Grape</li>';
$fruits.prepend($plum, grape);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
expect($fruits.children().eq(1).hasClass('grape')).toBe(true);
});
it('(fn) : should invoke the callback with the correct arguments and context', () => {
const args: [number, string][] = [];
const thisValues: AnyNode[] = [];
const $fruits = $('#fruits').children();
$fruits.prepend(function (...myArgs) {
args.push(myArgs);
thisValues.push(this);
return this;
});
expect(args).toStrictEqual([
[0, 'Apple'],
[1, 'Orange'],
[2, 'Pear'],
]);
expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);
});
it('(fn) : should add returned string as first child', () => {
const $fruits = $('#fruits').children();
$fruits.prepend(() => '<div class="first">');
const $apple = $fruits.eq(0);
const $orange = $fruits.eq(1);
const $pear = $fruits.eq(2);
expect($apple.find('.first')[0]).toBe($apple.contents()[0]);
expect($orange.find('.first')[0]).toBe($orange.contents()[0]);
expect($pear.find('.first')[0]).toBe($pear.contents()[0]);
});
it('(fn) : should add returned Cheerio object as first child', () => {
const $fruits = $('#fruits').children();
$fruits.prepend(() => $('<div class="second">'));
const $apple = $fruits.eq(0);
const $orange = $fruits.eq(1);
const $pear = $fruits.eq(2);
expect($apple.find('.second')[0]).toBe($apple.contents()[0]);
expect($orange.find('.second')[0]).toBe($orange.contents()[0]);
expect($pear.find('.second')[0]).toBe($pear.contents()[0]);
});
it('(fn) : should add returned Node as first child', () => {
const $fruits = $('#fruits').children();
$fruits.prepend(() => $('<div class="third">')[0]);
const $apple = $fruits.eq(0);
const $orange = $fruits.eq(1);
const $pear = $fruits.eq(2);
expect($apple.find('.third')[0]).toBe($apple.contents()[0]);
expect($orange.find('.third')[0]).toBe($orange.contents()[0]);
expect($pear.find('.third')[0]).toBe($pear.contents()[0]);
});
it('($(...)) : should remove from root element', () => {
const $plum = $('<li class="plum">Plum</li>');
const root = $plum[0].parent;
expect(root?.type).toBe('root');
$fruits.prepend($plum);
expect($plum[0].parent?.type).not.toBe('root');
expect(root?.childNodes).not.toContain($plum[0]);
});
});
describe('.appendTo', () => {
it('(html) : should add element as last child', () => {
const $plum = $('<li class="plum">Plum</li>').appendTo(fruits);
expect($plum.parent().children().eq(3).hasClass('plum')).toBe(true);
});
it('($(...)) : should add element as last child', () => {
$('<li class="plum">Plum</li>').appendTo($fruits);
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
it('(Node) : should add element as last child', () => {
$('<li class="plum">Plum</li>').appendTo($fruits[0]);
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
it('(selector) : should add element as last child', () => {
$('<li class="plum">Plum</li>').appendTo('#fruits');
expect($fruits.children().eq(3).hasClass('plum')).toBe(true);
});
it('(Array) : should add element as last child of all elements in the array', () => {
const $multiple = $('<ul><li>Apple</li></ul><ul><li>Orange</li></ul>');
$('<li class="plum">Plum</li>').appendTo($multiple.get());
expect($multiple.first().children().eq(1).hasClass('plum')).toBe(true);
expect($multiple.last().children().eq(1).hasClass('plum')).toBe(true);
});
});
describe('.prependTo', () => {
it('(html) : should add element as first child', () => {
const $plum = $('<li class="plum">Plum</li>').prependTo(fruits);
expect($plum.parent().children().eq(0).hasClass('plum')).toBe(true);
});
it('($(...)) : should add element as first child', () => {
$('<li class="plum">Plum</li>').prependTo($fruits);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
it('(Node) : should add node as first child', () => {
$('<li class="plum">Plum</li>').prependTo($fruits[0]);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
it('(selector) : should add element as first child', () => {
$('<li class="plum">Plum</li>').prependTo('#fruits');
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
});
it('(Array) : should add element as first child of all elements in the array', () => {
const $multiple = $('<ul><li>Apple</li></ul><ul><li>Orange</li></ul>');
$('<li class="plum">Plum</li>').prependTo($multiple.get());
expect($multiple.first().children().eq(0).hasClass('plum')).toBe(true);
expect($multiple.last().children().eq(0).hasClass('plum')).toBe(true);
});
});
describe('.after', () => {
it('() : should do nothing', () => {
expect($fruits.after()[0].tagName).toBe('ul');
});
it('(html) : should add element as next sibling', () => {
const grape = '<li class="grape">Grape</li>';
$('.apple').after(grape);
expect($('.apple').next().hasClass('grape')).toBe(true);
});
it('(Array) : should add all elements in the array as next sibling', () => {
const more = $(
'<li class="plum">Plum</li><li class="grape">Grape</li>',
).get();
$('.apple').after(more);
expect($fruits.children().eq(1).hasClass('plum')).toBe(true);
expect($fruits.children().eq(2).hasClass('grape')).toBe(true);
});
it('($(...)) : should add element as next sibling', () => {
const $plum = $('<li class="plum">Plum</li>');
$('.apple').after($plum);
expect($('.apple').next().hasClass('plum')).toBe(true);
});
it('(Node) : should add element as next sibling', () => {
const plum = $('<li class="plum">Plum</li>')[0];
$('.apple').after(plum);
expect($('.apple').next().hasClass('plum')).toBe(true);
});
it('(existing Node) : should remove existing nodes from previous locations', () => {
const pear = $fruits.children()[2];
$('.apple').after(pear);
const $children = $fruits.children();
expect($children).toHaveLength(3);
expect($children[1]).toBe(pear);
});
it('(existing Node) : should update original direct siblings', () => {
$('.pear').after($('.orange'));
expect($('.apple').next()[0]).toBe($('.pear')[0]);
expect($('.pear').prev()[0]).toBe($('.apple')[0]);
});
it('(existing Node) : should clone all but the last occurrence', () => {
const $originalApple = $('.apple');
$('.orange, .pear').after($originalApple);
expect($('.apple')).toHaveLength(2);
expect($('.apple').eq(0).prev()[0]).toBe($('.orange')[0]);
expect($('.apple').eq(0).next()[0]).toBe($('.pear')[0]);
expect($('.apple').eq(1).prev()[0]).toBe($('.pear')[0]);
expect($('.apple').eq(1).next()).toHaveLength(0);
expect($('.apple')[0]).not.toStrictEqual($originalApple[0]);
expect($('.apple')[1]).toStrictEqual($originalApple[0]);
});
it('(elem) : should handle if removed', () => {
const $apple = $('.apple');
const $plum = $('<li class="plum">Plum</li>');
$apple.remove();
$apple.after($plum);
expect($plum.prev()).toHaveLength(0);
});
it('($(...), html) : should add multiple elements as next siblings', () => {
const $plum = $('<li class="plum">Plum</li>');
const grape = '<li class="grape">Grape</li>';
$('.apple').after($plum, grape);
expect($('.apple').next().hasClass('plum')).toBe(true);
expect($('.plum').next().hasClass('grape')).toBe(true);
});
it('(fn) : should invoke the callback with the correct arguments and context', () => {
const args: [number, string][] = [];
const thisValues: AnyNode[] = [];
const $fruits = $('#fruits').children();
$fruits.after(function (...myArgs) {
args.push(myArgs);
thisValues.push(this);
return this;
});
expect(args).toStrictEqual([
[0, 'Apple'],
[1, 'Orange'],
[2, 'Pear'],
]);
expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);
});
it('(fn) : should add returned string as next sibling', () => {
const $fruits = $('#fruits').children();
$fruits.after(() => '<li class="first">');
expect($('.first')[0]).toBe($('#fruits').contents()[1]);
expect($('.first')[1]).toBe($('#fruits').contents()[3]);
expect($('.first')[2]).toBe($('#fruits').contents()[5]);
});
it('(fn) : should add returned Cheerio object as next sibling', () => {
const $fruits = $('#fruits').children();
$fruits.after(() => $('<li class="second">'));
expect($('.second')[0]).toBe($('#fruits').contents()[1]);
expect($('.second')[1]).toBe($('#fruits').contents()[3]);
expect($('.second')[2]).toBe($('#fruits').contents()[5]);
});
it('(fn) : should add returned element as next sibling', () => {
const $fruits = $('#fruits').children();
$fruits.after(() => $('<li class="third">')[0]);
expect($('.third')[0]).toBe($('#fruits').contents()[1]);
expect($('.third')[1]).toBe($('#fruits').contents()[3]);
expect($('.third')[2]).toBe($('#fruits').contents()[5]);
});
it('(fn) : should support text nodes', () => {
const $text = load(mixedText);
$text($text('body')[0].children).after(
(_, content) => `<c>${content}added</c>`,
);
expect($text('body').html()).toBe(
'<a>1</a><c>1added</c>TEXT<b>2</b><c>2added</c>',
);
});
it('($(...)) : should remove from root element', () => {
const $plum = $('<li class="plum">Plum</li>');
const root = $plum[0].parent;
expect(root?.type).toBe('root');
$fruits.after($plum);
expect($plum[0].parent?.type).not.toBe('root');
expect(root?.childNodes).not.toContain($plum[0]);
});
});
describe('.insertAfter', () => {
it('(selector) : should create element and add as next sibling', () => {
const grape = $('<li class="grape">Grape</li>');
grape.insertAfter('.apple');
expect($('.apple').next().hasClass('grape')).toBe(true);
});
it('(selector) : should create element and add as next sibling of multiple elements', () => {
const grape = $('<li class="grape">Grape</li>');
grape.insertAfter('.apple, .pear');
expect($('.apple').next().hasClass('grape')).toBe(true);
expect($('.pear').next().hasClass('grape')).toBe(true);
});
it('($(...)) : should create element and add as next sibling', () => {
const grape = $('<li class="grape">Grape</li>');
grape.insertAfter($('.apple'));
expect($('.apple').next().hasClass('grape')).toBe(true);
});
it('($(...)) : should create element and add as next sibling of multiple elements', () => {
const grape = $('<li class="grape">Grape</li>');
grape.insertAfter($('.apple, .pear'));
expect($('.apple').next().hasClass('grape')).toBe(true);
expect($('.pear').next().hasClass('grape')).toBe(true);
});
it('($(...)) : should create all elements in the array and add as next siblings', () => {
const more = $('<li class="plum">Plum</li><li class="grape">Grape</li>');
more.insertAfter($('.apple'));
expect($fruits.children().eq(0).hasClass('apple')).toBe(true);
expect($fruits.children().eq(1).hasClass('plum')).toBe(true);
expect($fruits.children().eq(2).hasClass('grape')).toBe(true);
});
it('(existing Node) : should remove existing nodes from previous locations', () => {
$('.orange').insertAfter('.pear');
expect($fruits.children().eq(1).hasClass('orange')).toBe(false);
expect($fruits.children().length).toBe(3);
expect($('.orange').length).toBe(1);
});
it('(existing Node) : should update original direct siblings', () => {
$('.orange').insertAfter('.pear');
expect($('.apple').next().hasClass('pear')).toBe(true);
expect($('.pear').prev().hasClass('apple')).toBe(true);
expect($('.pear').next().hasClass('orange')).toBe(true);
expect($('.orange').next()).toHaveLength(0);
});
it('(existing Node) : should update original direct siblings of multiple elements', () => {
$('.apple').insertAfter('.orange, .pear');
expect($('.orange').prev()).toHaveLength(0);
expect($('.orange').next().hasClass('apple')).toBe(true);
expect($('.pear').next().hasClass('apple')).toBe(true);
expect($('.pear').prev().hasClass('apple')).toBe(true);
expect($fruits.children().length).toBe(4);
const apples = $('.apple');
expect(apples.length).toBe(2);
expect(apples.eq(0).prev().hasClass('orange')).toBe(true);
expect(apples.eq(1).prev().hasClass('pear')).toBe(true);
});
it('(elem) : should handle if removed', () => {
const $apple = $('.apple');
const $plum = $('<li class="plum">Plum</li>');
$apple.remove();
$plum.insertAfter($apple);
expect($plum.prev()).toHaveLength(0);
});
it('(single) should return the new element for chaining', () => {
const $grape = $('<li class="grape">Grape</li>').insertAfter('.apple');
expect($grape.cheerio).toBeTruthy();
expect($grape.each).toBeTruthy();
expect($grape.length).toBe(1);
expect($grape.hasClass('grape')).toBe(true);
});
it('(single) should return the new elements for chaining', () => {
const $purple = $(
'<li class="grape">Grape</li><li class="plum">Plum</li>',
).insertAfter('.apple');
expect($purple.cheerio).toBeTruthy();
expect($purple.each).toBeTruthy();
expect($purple.length).toBe(2);
expect($purple.eq(0).hasClass('grape')).toBe(true);
expect($purple.eq(1).hasClass('plum')).toBe(true);
});
it('(multiple) should return the new elements for chaining', () => {
const $purple = $(
'<li class="grape">Grape</li><li class="plum">Plum</li>',
).insertAfter('.apple, .pear');
expect($purple.cheerio).toBeTruthy();
expect($purple.each).toBeTruthy();
expect($purple.length).toBe(4);
expect($purple.eq(0).hasClass('grape')).toBe(true);
expect($purple.eq(1).hasClass('plum')).toBe(true);
expect($purple.eq(2).hasClass('grape')).toBe(true);
expect($purple.eq(3).hasClass('plum')).toBe(true);
});
it('(single) should return the existing element for chaining', () => {
const $pear = $('.pear').insertAfter('.apple');
expect($pear.cheerio).toBeTruthy();
expect($pear.each).toBeTruthy();
expect($pear.length).toBe(1);
expect($pear.hasClass('pear')).toBe(true);
});
it('(single) should return the existing elements for chaining', () => {
const $things = $('.orange, .apple').insertAfter('.pear');
expect($things.cheerio).toBeTruthy();
expect($things.each).toBeTruthy();
expect($things.length).toBe(2);
expect($things.eq(0).hasClass('apple')).toBe(true);
expect($things.eq(1).hasClass('orange')).toBe(true);
});
it('(multiple) should return the existing elements for chaining', () => {
$('<li class="grape">Grape</li>').insertAfter('.apple');
const $things = $('.orange, .apple').insertAfter('.pear, .grape');
expect($things.cheerio).toBeTruthy();
expect($things.each).toBeTruthy();
expect($things.length).toBe(4);
expect($things.eq(0).hasClass('apple')).toBe(true);
expect($things.eq(1).hasClass('orange')).toBe(true);
expect($things.eq(2).hasClass('apple')).toBe(true);
expect($things.eq(3).hasClass('orange')).toBe(true);
});
});
describe('.before', () => {
it('() : should do nothing', () => {
expect($('#fruits').before()[0].tagName).toBe('ul');
});
it('(html) : should add element as previous sibling', () => {
const grape = '<li class="grape">Grape</li>';
$('.apple').before(grape);
expect($('.apple').prev().hasClass('grape')).toBe(true);
});
it('($(...)) : should add element as previous sibling', () => {
const $plum = $('<li class="plum">Plum</li>');
$('.apple').before($plum);
expect($('.apple').prev().hasClass('plum')).toBe(true);
});
it('(Node) : should add element as previous sibling', () => {
const plum = $('<li class="plum">Plum</li>')[0];
$('.apple').before(plum);
expect($('.apple').prev().hasClass('plum')).toBe(true);
});
it('(existing Node) : should remove existing nodes from previous locations', () => {
const pear = $fruits.children()[2];
$('.apple').before(pear);
const $children = $fruits.children();
expect($children).toHaveLength(3);
expect($children[0]).toBe(pear);
});
it('(existing Node) : should update original direct siblings', () => {
$('.apple').before($('.orange'));
expect($('.apple').next()[0]).toBe($('.pear')[0]);
expect($('.pear').prev()[0]).toBe($('.apple')[0]);
});
it('(existing Node) : should clone all but the last occurrence', () => {
const $originalPear = $('.pear');
$('.apple, .orange').before($originalPear);
expect($('.pear')).toHaveLength(2);
expect($('.pear').eq(0).prev()).toHaveLength(0);
expect($('.pear').eq(0).next()[0]).toBe($('.apple')[0]);
expect($('.pear').eq(1).prev()[0]).toBe($('.apple')[0]);
expect($('.pear').eq(1).next()[0]).toBe($('.orange')[0]);
expect($('.pear')[0]).not.toStrictEqual($originalPear[0]);
expect($('.pear')[1]).toStrictEqual($originalPear[0]);
});
it('(elem) : should handle if removed', () => {
const $apple = $('.apple');
const $plum = $('<li class="plum">Plum</li>');
$apple.remove();
$apple.before($plum);
expect($plum.next()).toHaveLength(0);
});
it('(Array) : should add all elements in the array as previous sibling', () => {
const more = $(
'<li class="plum">Plum</li><li class="grape">Grape</li>',
).get();
$('.apple').before(more);
expect($fruits.children().eq(0).hasClass('plum')).toBe(true);
expect($fruits.children().eq(1).hasClass('grape')).toBe(true);
});
it('($(...), html) : should add multiple elements as previous siblings', () => {
const $plum = $('<li class="plum">Plum</li>');
const grape = '<li class="grape">Grape</li>';
$('.apple').before($plum, grape);
expect($('.apple').prev().hasClass('grape')).toBe(true);
expect($('.grape').prev().hasClass('plum')).toBe(true);
});
it('(fn) : should invoke the callback with the correct arguments and context', () => {
const args: [number, string][] = [];
const thisValues: AnyNode[] = [];
const $fruits = $('#fruits').children();
$fruits.before(function (...myArgs) {
args.push(myArgs);
thisValues.push(this);
return this;
});
expect(args).toStrictEqual([
[0, 'Apple'],
[1, 'Orange'],
[2, 'Pear'],
]);
expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);
});
it('(fn) : should add returned string as previous sibling', () => {
const $fruits = $('#fruits').children();
$fruits.before(() => '<li class="first">');
expect($('.first')[0]).toBe($('#fruits').contents()[0]);
expect($('.first')[1]).toBe($('#fruits').contents()[2]);
expect($('.first')[2]).toBe($('#fruits').contents()[4]);
});
it('(fn) : should add returned Cheerio object as previous sibling', () => {
const $fruits = $('#fruits').children();
$fruits.before(() => $('<li class="second">'));
expect($('.second')[0]).toBe($('#fruits').contents()[0]);
expect($('.second')[1]).toBe($('#fruits').contents()[2]);
expect($('.second')[2]).toBe($('#fruits').contents()[4]);
});
it('(fn) : should add returned Node as previous sibling', () => {
const $fruits = $('#fruits').children();
$fruits.before(() => $('<li class="third">')[0]);
expect($('.third')[0]).toBe($('#fruits').contents()[0]);
expect($('.third')[1]).toBe($('#fruits').contents()[2]);
expect($('.third')[2]).toBe($('#fruits').contents()[4]);
});
it('(fn) : should support text nodes', () => {
const $text = load(mixedText);
$text($text('body')[0].children).before(
(_,