traversing.spec.ts 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677
  1. import { describe, it, expect, beforeEach } from 'vitest';
  2. import { load, type CheerioAPI } from '../index.js';
  3. import { Cheerio } from '../cheerio.js';
  4. import { type AnyNode, type Element, type Text, isText } from 'domhandler';
  5. import {
  6. cheerio,
  7. food,
  8. fruits,
  9. eleven,
  10. drinks,
  11. text,
  12. forms,
  13. mixedText,
  14. vegetables,
  15. } from '../__fixtures__/fixtures.js';
  16. function getText(el: Cheerio<Element>) {
  17. if (el.length === 0) return undefined;
  18. const [firstChild] = el[0].childNodes;
  19. return isText(firstChild) ? firstChild.data : undefined;
  20. }
  21. describe('$(...)', () => {
  22. let $: CheerioAPI;
  23. beforeEach(() => {
  24. $ = load(fruits);
  25. });
  26. describe('.load', () => {
  27. it('should throw a TypeError if given invalid input', () => {
  28. expect(() => {
  29. (load as any)();
  30. }).toThrow('cheerio.load() expects a string');
  31. });
  32. });
  33. describe('.find', () => {
  34. it('() : should find nothing', () => {
  35. expect($('ul').find()).toHaveLength(0);
  36. });
  37. it('(single) : should find one descendant', () => {
  38. expect($('#fruits').find('.apple')[0].attribs).toHaveProperty(
  39. 'class',
  40. 'apple',
  41. );
  42. });
  43. // #1679 - text tags not filtered
  44. it('(single) : should filter out text nodes', () => {
  45. const $root = $(`<html>\n${fruits.replace(/></g, '>\n<')}\n</html>`);
  46. expect($root.find('.apple')[0].attribs).toHaveProperty('class', 'apple');
  47. });
  48. it('(many) : should find all matching descendant', () => {
  49. expect($('#fruits').find('li')).toHaveLength(3);
  50. });
  51. it('(many) : should merge all selected elems with matching descendants', () => {
  52. expect($('#fruits, #food', food).find('.apple')).toHaveLength(1);
  53. });
  54. it('(invalid single) : should return empty if cant find', () => {
  55. expect($('ul').find('blah')).toHaveLength(0);
  56. });
  57. it('(invalid single) : should query descendants only', () => {
  58. expect($('#fruits').find('ul')).toHaveLength(0);
  59. });
  60. it('should return empty if search already empty result', () => {
  61. expect($('#not-fruits').find('li')).toHaveLength(0);
  62. });
  63. it('should lowercase selectors', () => {
  64. expect($('#fruits').find('LI')).toHaveLength(3);
  65. });
  66. it('should query immediate descendant only', () => {
  67. const q = load('<foo><bar><bar></bar><bar></bar></bar></foo>');
  68. expect(q('foo').find('> bar')).toHaveLength(1);
  69. });
  70. it('should find siblings', () => {
  71. const q = load('<p class=a><p class=b></p>');
  72. expect(q('.a').find('+.b')).toHaveLength(1);
  73. expect(q('.a').find('~.b')).toHaveLength(1);
  74. expect(q('.a').find('+.a')).toHaveLength(0);
  75. expect(q('.a').find('~.a')).toHaveLength(0);
  76. });
  77. it('should query case-sensitively when in xml mode', () => {
  78. const q = load('<caseSenSitive allTheWay>', { xml: true });
  79. expect(q('caseSenSitive')).toHaveLength(1);
  80. expect(q('[allTheWay]')).toHaveLength(1);
  81. expect(q('casesensitive')).toHaveLength(0);
  82. expect(q('[alltheway]')).toHaveLength(0);
  83. });
  84. it('should throw an Error if given an invalid selector', () => {
  85. expect(() => {
  86. $('#fruits').find(':bah');
  87. }).toThrow('Unknown pseudo-class :bah');
  88. });
  89. it('should respect the `lowerCaseTags` option (#3495)', () => {
  90. const q = load(
  91. `<parentTag class="myClass">
  92. <firstTag> <child> blah </child> </firstTag>
  93. <secondTag> <child> blah </child> </secondTag>
  94. </parentTag> `,
  95. {
  96. xml: {
  97. xmlMode: true,
  98. decodeEntities: false,
  99. lowerCaseTags: true,
  100. lowerCaseAttributeNames: false,
  101. recognizeSelfClosing: true,
  102. },
  103. },
  104. );
  105. expect(q('.myClass').find('firstTag > child')).toHaveLength(1);
  106. });
  107. describe('(cheerio object) :', () => {
  108. it('returns only those nodes contained within the current selection', () => {
  109. const q = load(food);
  110. const $selection = q('#fruits').find(q('li'));
  111. expect($selection).toHaveLength(3);
  112. expect($selection[0]).toBe(q('.apple')[0]);
  113. expect($selection[1]).toBe(q('.orange')[0]);
  114. expect($selection[2]).toBe(q('.pear')[0]);
  115. });
  116. it('returns only those nodes contained within any element in the current selection', () => {
  117. const q = load(food);
  118. const $selection = q('.apple, #vegetables').find(q('li'));
  119. expect($selection).toHaveLength(2);
  120. expect($selection[0]).toBe(q('.carrot')[0]);
  121. expect($selection[1]).toBe(q('.sweetcorn')[0]);
  122. });
  123. });
  124. describe('(node) :', () => {
  125. it('returns node when contained within the current selection', () => {
  126. const q = load(food);
  127. const $selection = q('#fruits').find(q('.apple')[0]);
  128. expect($selection).toHaveLength(1);
  129. expect($selection[0]).toBe(q('.apple')[0]);
  130. });
  131. it('returns node when contained within any element the current selection', () => {
  132. const q = load(food);
  133. const $selection = q('#fruits, #vegetables').find(q('.carrot')[0]);
  134. expect($selection).toHaveLength(1);
  135. expect($selection[0]).toBe(q('.carrot')[0]);
  136. });
  137. it('does not return node that is not contained within the current selection', () => {
  138. const q = load(food);
  139. const $selection = q('#fruits').find(q('.carrot')[0]);
  140. expect($selection).toHaveLength(0);
  141. });
  142. });
  143. });
  144. describe('.children', () => {
  145. it('() : should get all children', () => {
  146. expect($('ul').children()).toHaveLength(3);
  147. });
  148. it('() : should skip text nodes', () => {
  149. expect($(mixedText).children()).toHaveLength(0);
  150. });
  151. it('() : should return children of all matched elements', () => {
  152. expect($('ul ul', food).children()).toHaveLength(5);
  153. });
  154. it('(selector) : should return children matching selector', () => {
  155. const { attribs } = $('ul').children('.orange')[0];
  156. expect(attribs).toHaveProperty('class', 'orange');
  157. });
  158. it('(invalid selector) : should return empty', () => {
  159. expect($('ul').children('.lulz')).toHaveLength(0);
  160. });
  161. it('should only match immediate children, not ancestors', () => {
  162. expect($(food).children('li')).toHaveLength(0);
  163. });
  164. });
  165. describe('.contents', () => {
  166. beforeEach(() => {
  167. $ = load(text);
  168. });
  169. it('() : should get all contents', () => {
  170. expect($('p').contents()).toHaveLength(5);
  171. });
  172. it('() : should skip text nodes', () => {
  173. expect($(mixedText).contents()).toHaveLength(2);
  174. });
  175. it('() : should include text nodes', () => {
  176. expect($('p').contents().first()[0].type).toBe('text');
  177. });
  178. it('() : should include comment nodes', () => {
  179. expect($('p').contents().last()[0].type).toBe('comment');
  180. });
  181. });
  182. describe('.next', () => {
  183. it('() : should return next element', () => {
  184. const { attribs } = $('.orange').next()[0];
  185. expect(attribs).toHaveProperty('class', 'pear');
  186. });
  187. it('() : should skip text nodes', () => {
  188. expect($(mixedText).next()[0]).toHaveProperty('name', 'b');
  189. });
  190. it('(no next) : should return empty for last child', () => {
  191. expect($('.pear').next()).toHaveLength(0);
  192. });
  193. it('(next on empty object) : should return empty', () => {
  194. expect($('.banana').next()).toHaveLength(0);
  195. });
  196. it('() : should operate over all elements in the selection', () => {
  197. expect($('.apple, .orange', food).next()).toHaveLength(2);
  198. });
  199. it('() : should return elements in order', () => {
  200. const result = load(eleven)('.red').next();
  201. expect(result).toHaveLength(2);
  202. expect(result.eq(0).text()).toBe('Six');
  203. expect(result.eq(1).text()).toBe('Ten');
  204. });
  205. it('should reject elements that violate the filter', () => {
  206. expect($('.apple').next('.non-existent')).toHaveLength(0);
  207. });
  208. it('should accept elements that satisify the filter', () => {
  209. expect($('.apple').next('.orange')).toHaveLength(1);
  210. });
  211. describe('(selector) :', () => {
  212. it('should reject elements that violate the filter', () => {
  213. expect($('.apple').next('.non-existent')).toHaveLength(0);
  214. });
  215. it('should accept elements that satisify the filter', () => {
  216. expect($('.apple').next('.orange')).toHaveLength(1);
  217. });
  218. });
  219. });
  220. describe('.nextAll', () => {
  221. it('() : should return all following siblings', () => {
  222. const elems = $('.apple').nextAll();
  223. expect(elems).toHaveLength(2);
  224. expect(elems[0].attribs).toHaveProperty('class', 'orange');
  225. expect(elems[1].attribs).toHaveProperty('class', 'pear');
  226. });
  227. it('(no next) : should return empty for last child', () => {
  228. expect($('.pear').nextAll()).toHaveLength(0);
  229. });
  230. it('(nextAll on empty object) : should return empty', () => {
  231. expect($('.banana').nextAll()).toHaveLength(0);
  232. });
  233. it('() : should operate over all elements in the selection', () => {
  234. expect($('.apple, .carrot', food).nextAll()).toHaveLength(3);
  235. });
  236. it('() : should not contain duplicate elements', () => {
  237. const elems = $('.apple, .orange', food);
  238. expect(elems.nextAll()).toHaveLength(2);
  239. });
  240. it('() : should not contain text elements', () => {
  241. const elems = $('.apple', fruits.replace(/></g, '>\n<'));
  242. expect(elems.nextAll()).toHaveLength(2);
  243. });
  244. describe('(selector) :', () => {
  245. it('should filter according to the provided selector', () => {
  246. expect($('.apple').nextAll('.pear')).toHaveLength(1);
  247. });
  248. it("should not consider siblings' contents when filtering", () => {
  249. expect($('#fruits', food).nextAll('li')).toHaveLength(0);
  250. });
  251. });
  252. });
  253. describe('.nextUntil', () => {
  254. it('() : should return all following siblings if no selector specified', () => {
  255. const elems = $('.apple', food).nextUntil();
  256. expect(elems).toHaveLength(2);
  257. expect(elems[0].attribs).toHaveProperty('class', 'orange');
  258. expect(elems[1].attribs).toHaveProperty('class', 'pear');
  259. });
  260. it('() : should filter out non-element nodes', () => {
  261. const elems = $('<div><div></div><!-- comment -->text<div></div></div>');
  262. const div = elems.children().eq(0);
  263. expect(div.nextUntil()).toHaveLength(1);
  264. });
  265. it('() : should operate over all elements in the selection', () => {
  266. const elems = $('.apple, .carrot', food);
  267. expect(elems.nextUntil()).toHaveLength(3);
  268. });
  269. it('() : should not contain duplicate elements', () => {
  270. const elems = $('.apple, .orange', food);
  271. expect(elems.nextUntil()).toHaveLength(2);
  272. });
  273. it('(selector) : should return all following siblings until selector', () => {
  274. const elems = $('.apple', food).nextUntil('.pear');
  275. expect(elems).toHaveLength(1);
  276. expect(elems[0].attribs).toHaveProperty('class', 'orange');
  277. });
  278. it('(selector) : should support selector matching multiple elements', () => {
  279. const elems = $('#disabled', forms).nextUntil('option, #unnamed');
  280. expect(elems).toHaveLength(2);
  281. expect(elems[0].attribs).toHaveProperty('id', 'submit');
  282. expect(elems[1].attribs).toHaveProperty('id', 'select');
  283. });
  284. it('(selector not sibling) : should return all following siblings', () => {
  285. const elems = $('.apple').nextUntil('#vegetables');
  286. expect(elems).toHaveLength(2);
  287. });
  288. it('(selector, filterString) : should return all following siblings until selector, filtered by filter', () => {
  289. const elems = $('.beer', drinks).nextUntil('.water', '.milk');
  290. expect(elems).toHaveLength(1);
  291. expect(elems[0].attribs).toHaveProperty('class', 'milk');
  292. });
  293. it('(null, filterString) : should return all following siblings until selector, filtered by filter', () => {
  294. const elems = $('<ul><li></li><li><p></p></li></ul>');
  295. const empty = elems.find('li').eq(0).nextUntil(null, 'p');
  296. expect(empty).toHaveLength(0);
  297. });
  298. it('() : should return an empty object for last child', () => {
  299. expect($('.pear').nextUntil()).toHaveLength(0);
  300. });
  301. it('() : should return an empty object when called on an empty object', () => {
  302. expect($('.banana').nextUntil()).toHaveLength(0);
  303. });
  304. it('(node) : should return all following siblings until the node', () => {
  305. const $fruits = $('#fruits').children();
  306. const elems = $fruits.eq(0).nextUntil($fruits[2]);
  307. expect(elems).toHaveLength(1);
  308. });
  309. it('(cheerio object) : should return all following siblings until any member of the cheerio object', () => {
  310. const $drinks = $(drinks).children();
  311. const $until = $([$drinks[4], $drinks[3]]);
  312. const elems = $drinks.eq(0).nextUntil($until);
  313. expect(elems).toHaveLength(2);
  314. });
  315. });
  316. describe('.prev', () => {
  317. it('() : should return previous element', () => {
  318. const { attribs } = $('.orange').prev()[0];
  319. expect(attribs).toHaveProperty('class', 'apple');
  320. });
  321. it('() : should skip text nodes', () => {
  322. expect($($(mixedText)[2]).prev()[0]).toHaveProperty('name', 'a');
  323. });
  324. it('(no prev) : should return empty for first child', () => {
  325. expect($('.apple').prev()).toHaveLength(0);
  326. });
  327. it('(prev on empty object) : should return empty', () => {
  328. expect($('.banana').prev()).toHaveLength(0);
  329. });
  330. it('() : should operate over all elements in the selection', () => {
  331. expect($('.orange, .pear', food).prev()).toHaveLength(2);
  332. });
  333. it('() : should maintain elements order', () => {
  334. const sel = load(eleven)('.sel');
  335. expect(sel).toHaveLength(3);
  336. expect(sel.eq(0).text()).toBe('Three');
  337. expect(sel.eq(1).text()).toBe('Nine');
  338. expect(sel.eq(2).text()).toBe('Eleven');
  339. // Swap last elements
  340. const el = sel[2];
  341. sel[2] = sel[1];
  342. sel[1] = el;
  343. const result = sel.prev();
  344. expect(result).toHaveLength(3);
  345. expect(result.eq(0).text()).toBe('Two');
  346. expect(result.eq(1).text()).toBe('Ten');
  347. expect(result.eq(2).text()).toBe('Eight');
  348. });
  349. describe('(selector) :', () => {
  350. it('should reject elements that violate the filter', () => {
  351. expect($('.orange').prev('.non-existent')).toHaveLength(0);
  352. });
  353. it('should accept elements that satisify the filter', () => {
  354. expect($('.orange').prev('.apple')).toHaveLength(1);
  355. });
  356. it('(selector) : should reject elements that violate the filter', () => {
  357. expect($('.orange').prev('.non-existent')).toHaveLength(0);
  358. });
  359. it('(selector) : should accept elements that satisify the filter', () => {
  360. expect($('.orange').prev('.apple')).toHaveLength(1);
  361. });
  362. });
  363. });
  364. describe('.prevAll', () => {
  365. it('() : should return all preceding siblings', () => {
  366. const elems = $('.pear').prevAll();
  367. expect(elems).toHaveLength(2);
  368. expect(elems[0].attribs).toHaveProperty('class', 'orange');
  369. expect(elems[1].attribs).toHaveProperty('class', 'apple');
  370. });
  371. it('() : should not contain text elements', () => {
  372. const elems = $('.pear', fruits.replace(/></g, '>\n<'));
  373. expect(elems.prevAll()).toHaveLength(2);
  374. });
  375. it('(no prev) : should return empty for first child', () => {
  376. expect($('.apple').prevAll()).toHaveLength(0);
  377. });
  378. it('(prevAll on empty object) : should return empty', () => {
  379. expect($('.banana').prevAll()).toHaveLength(0);
  380. });
  381. it('() : should operate over all elements in the selection', () => {
  382. expect($('.orange, .sweetcorn', food).prevAll()).toHaveLength(2);
  383. });
  384. it('() : should not contain duplicate elements', () => {
  385. const elems = $('.orange, .pear', food);
  386. expect(elems.prevAll()).toHaveLength(2);
  387. });
  388. describe('(selector) :', () => {
  389. it('should filter returned elements', () => {
  390. const elems = $('.pear').prevAll('.apple');
  391. expect(elems).toHaveLength(1);
  392. });
  393. it("should not consider siblings's descendents", () => {
  394. const elems = $('#vegetables', food).prevAll('li');
  395. expect(elems).toHaveLength(0);
  396. });
  397. });
  398. });
  399. describe('.prevUntil', () => {
  400. it('() : should return all preceding siblings if no selector specified', () => {
  401. const elems = $('.pear').prevUntil();
  402. expect(elems).toHaveLength(2);
  403. expect(elems[0].attribs).toHaveProperty('class', 'orange');
  404. expect(elems[1].attribs).toHaveProperty('class', 'apple');
  405. });
  406. it('() : should filter out non-element nodes', () => {
  407. const elems = $(
  408. '<div class="1"><div class="2"></div><!-- comment -->text<div class="3"></div></div>',
  409. );
  410. const div = elems.children().last();
  411. expect(div.prevUntil()).toHaveLength(1);
  412. });
  413. it('() : should operate over all elements in the selection', () => {
  414. const elems = $('.pear, .sweetcorn', food);
  415. expect(elems.prevUntil()).toHaveLength(3);
  416. });
  417. it('() : should not contain duplicate elements', () => {
  418. const elems = $('.orange, .pear', food);
  419. expect(elems.prevUntil()).toHaveLength(2);
  420. });
  421. it('(selector) : should return all preceding siblings until selector', () => {
  422. const elems = $('.pear').prevUntil('.apple');
  423. expect(elems).toHaveLength(1);
  424. expect(elems[0].attribs).toHaveProperty('class', 'orange');
  425. });
  426. it('(selector) : should support selector matching multiple elements', () => {
  427. const elems = $('#unnamed', forms).prevUntil('option, #disabled');
  428. expect(elems).toHaveLength(2);
  429. expect(elems[0].attribs).toHaveProperty('id', 'select');
  430. expect(elems[1].attribs).toHaveProperty('id', 'submit');
  431. });
  432. it('(selector not sibling) : should return all preceding siblings', () => {
  433. const elems = $('.sweetcorn', food).prevUntil('#fruits');
  434. expect(elems).toHaveLength(1);
  435. expect(elems[0].attribs).toHaveProperty('class', 'carrot');
  436. });
  437. it('(selector, filterString) : should return all preceding siblings until selector, filtered by filter', () => {
  438. const elems = $('.cider', drinks).prevUntil('.juice', '.water');
  439. expect(elems).toHaveLength(1);
  440. expect(elems[0].attribs).toHaveProperty('class', 'water');
  441. });
  442. it('(selector, filterString) : should return all preceding siblings until selector', () => {
  443. const elems = $('<ul><li><p></p></li><li></li></ul>');
  444. const empty = elems.find('li').eq(1).prevUntil(null, 'p');
  445. expect(empty).toHaveLength(0);
  446. });
  447. it('() : should return an empty object for first child', () => {
  448. expect($('.apple').prevUntil()).toHaveLength(0);
  449. });
  450. it('() : should return an empty object when called on an empty object', () => {
  451. expect($('.banana').prevUntil()).toHaveLength(0);
  452. });
  453. it('(node) : should return all previous siblings until the node', () => {
  454. const $fruits = $('#fruits').children();
  455. const elems = $fruits.eq(2).prevUntil($fruits[0]);
  456. expect(elems).toHaveLength(1);
  457. });
  458. it('(cheerio object) : should return all previous siblings until any member of the cheerio object', () => {
  459. const $drinks = $(drinks).children();
  460. const $until = $([$drinks[0], $drinks[1]]);
  461. const elems = $drinks.eq(4).prevUntil($until);
  462. expect(elems).toHaveLength(2);
  463. });
  464. });
  465. describe('.siblings', () => {
  466. it('() : should get all the siblings', () => {
  467. expect($('.orange').siblings()).toHaveLength(2);
  468. expect($('#fruits').siblings()).toHaveLength(0);
  469. expect($('.apple, .carrot', food).siblings()).toHaveLength(3);
  470. });
  471. it('(selector) : should get all siblings that match the selector', () => {
  472. expect($('.orange').siblings('.apple')).toHaveLength(1);
  473. expect($('.orange').siblings('.peach')).toHaveLength(0);
  474. });
  475. it('(selector) : should throw an Error if given an invalid selector', () => {
  476. expect(() => {
  477. $('.orange').siblings(':bah');
  478. }).toThrow('Unknown pseudo-class :bah');
  479. });
  480. it('(selector) : does not consider the contents of siblings when filtering (GH-374)', () => {
  481. expect($('#fruits', food).siblings('li')).toHaveLength(0);
  482. });
  483. it('() : when two elements are siblings to each other they have to be included', () => {
  484. const result = load(eleven)('.sel').siblings();
  485. expect(result).toHaveLength(7);
  486. expect(result.eq(0).text()).toBe('One');
  487. expect(result.eq(1).text()).toBe('Two');
  488. expect(result.eq(2).text()).toBe('Four');
  489. expect(result.eq(3).text()).toBe('Eight');
  490. expect(result.eq(4).text()).toBe('Nine');
  491. expect(result.eq(5).text()).toBe('Ten');
  492. expect(result.eq(6).text()).toBe('Eleven');
  493. });
  494. it('(selector) : when two elements are siblings to each other they have to be included', () => {
  495. const result = load(eleven)('.sel').siblings('.red');
  496. expect(result).toHaveLength(2);
  497. expect(result.eq(0).text()).toBe('Four');
  498. expect(result.eq(1).text()).toBe('Nine');
  499. });
  500. it('(cheerio) : test filtering with cheerio object', () => {
  501. const doc = load(eleven);
  502. const result = doc('.sel').siblings(doc(':not([class])'));
  503. expect(result).toHaveLength(4);
  504. expect(result.eq(0).text()).toBe('One');
  505. expect(result.eq(1).text()).toBe('Two');
  506. expect(result.eq(2).text()).toBe('Eight');
  507. expect(result.eq(3).text()).toBe('Ten');
  508. });
  509. });
  510. describe('.parents', () => {
  511. beforeEach(() => {
  512. $ = load(food);
  513. });
  514. it('() : should get all of the parents in logical order', () => {
  515. const orange = $('.orange').parents();
  516. expect(orange).toHaveLength(4);
  517. expect(orange[0].attribs).toHaveProperty('id', 'fruits');
  518. expect(orange[1].attribs).toHaveProperty('id', 'food');
  519. expect(orange[2].tagName).toBe('body');
  520. expect(orange[3].tagName).toBe('html');
  521. const fruits = $('#fruits').parents();
  522. expect(fruits).toHaveLength(3);
  523. expect(fruits[0].attribs).toHaveProperty('id', 'food');
  524. expect(fruits[1].tagName).toBe('body');
  525. expect(fruits[2].tagName).toBe('html');
  526. });
  527. it('(selector) : should get all of the parents that match the selector in logical order', () => {
  528. const fruits = $('.orange').parents('#fruits');
  529. expect(fruits).toHaveLength(1);
  530. expect(fruits[0].attribs).toHaveProperty('id', 'fruits');
  531. const uls = $('.orange').parents('ul');
  532. expect(uls).toHaveLength(2);
  533. expect(uls[0].attribs).toHaveProperty('id', 'fruits');
  534. expect(uls[1].attribs).toHaveProperty('id', 'food');
  535. });
  536. it('() : should not break if the selector does not have any results', () => {
  537. const result = $('.saladbar').parents();
  538. expect(result).toHaveLength(0);
  539. });
  540. it('() : should return an empty set for top-level elements', () => {
  541. const result = $('html').parents();
  542. expect(result).toHaveLength(0);
  543. });
  544. it('() : should return the parents of every element in the *reveresed* collection, omitting duplicates', () => {
  545. const $parents = $('li').parents();
  546. expect($parents).toHaveLength(5);
  547. expect($parents[0]).toBe($('#vegetables')[0]);
  548. expect($parents[1]).toBe($('#fruits')[0]);
  549. expect($parents[2]).toBe($('#food')[0]);
  550. expect($parents[3]).toBe($('body')[0]);
  551. expect($parents[4]).toBe($('html')[0]);
  552. });
  553. });
  554. describe('.parentsUntil', () => {
  555. beforeEach(() => {
  556. $ = load(food);
  557. });
  558. it('() : should get all of the parents in logical order', () => {
  559. const result = $('.orange').parentsUntil();
  560. expect(result).toHaveLength(4);
  561. expect(result[0].attribs).toHaveProperty('id', 'fruits');
  562. expect(result[1].attribs).toHaveProperty('id', 'food');
  563. expect(result[2].tagName).toBe('body');
  564. expect(result[3].tagName).toBe('html');
  565. });
  566. it('() : should get all of the parents in reversed order, omitting duplicates', () => {
  567. const result = $('.apple, .sweetcorn').parentsUntil();
  568. expect(result).toHaveLength(5);
  569. expect(result[0]).toBe($('#vegetables')[0]);
  570. expect(result[1]).toBe($('#fruits')[0]);
  571. expect(result[2]).toBe($('#food')[0]);
  572. expect(result[3]).toBe($('body')[0]);
  573. expect(result[4]).toBe($('html')[0]);
  574. });
  575. it('(selector) : should get all of the parents until selector', () => {
  576. const food = $('.orange').parentsUntil('#food');
  577. expect(food).toHaveLength(1);
  578. expect(food[0].attribs).toHaveProperty('id', 'fruits');
  579. const fruits = $('.orange').parentsUntil('#fruits');
  580. expect(fruits).toHaveLength(0);
  581. });
  582. it('(selector) : Less simple parentsUntil check with selector', () => {
  583. const result = $('#fruits').parentsUntil('html, body');
  584. expect(result.eq(0).attr('id')).toBe('food');
  585. });
  586. it('(selector not parent) : should return all parents', () => {
  587. const result = $('.orange').parentsUntil('.apple');
  588. expect(result).toHaveLength(4);
  589. expect(result[0].attribs).toHaveProperty('id', 'fruits');
  590. expect(result[1].attribs).toHaveProperty('id', 'food');
  591. expect(result[2].tagName).toBe('body');
  592. expect(result[3].tagName).toBe('html');
  593. });
  594. it('(selector, filter) : should get all of the parents that match the filter', () => {
  595. const result = $('.apple, .sweetcorn').parentsUntil(
  596. '.saladbar',
  597. '#vegetables',
  598. );
  599. expect(result).toHaveLength(1);
  600. expect(result[0].attribs).toHaveProperty('id', 'vegetables');
  601. });
  602. it('(selector, filter) : Multiple-filtered parentsUntil check', () => {
  603. const result = $('.orange').parentsUntil('html', 'ul,body');
  604. expect(result).toHaveLength(3);
  605. expect(result.eq(0).attr('id')).toBe('fruits');
  606. expect(result.eq(1).attr('id')).toBe('food');
  607. expect(result.eq(2).prop('tagName')).toBe('BODY');
  608. });
  609. it('() : should return empty object when called on an empty object', () => {
  610. const result = $('.saladbar').parentsUntil();
  611. expect(result).toHaveLength(0);
  612. });
  613. it('() : should return an empty set for top-level elements', () => {
  614. const result = $('html').parentsUntil();
  615. expect(result).toHaveLength(0);
  616. });
  617. it('(cheerio object) : should return all parents until any member of the cheerio object', () => {
  618. const $fruits = $('#fruits');
  619. const $until = $('#food');
  620. const result = $fruits.children().eq(1).parentsUntil($until);
  621. expect(result).toHaveLength(1);
  622. expect(result[0].attribs).toHaveProperty('id', 'fruits');
  623. });
  624. it('(cheerio object) : should return all parents until body element', () => {
  625. const body = $('body')[0];
  626. const result = $('.carrot').parentsUntil(body);
  627. expect(result).toHaveLength(2);
  628. expect(result.eq(0).is('ul#vegetables')).toBe(true);
  629. });
  630. });
  631. describe('.parent', () => {
  632. it('() : should return the parent of each matched element', () => {
  633. let result = $('.orange').parent();
  634. expect(result).toHaveLength(1);
  635. expect(result[0].attribs).toHaveProperty('id', 'fruits');
  636. result = $('li', food).parent();
  637. expect(result).toHaveLength(2);
  638. expect(result[0].attribs).toHaveProperty('id', 'fruits');
  639. expect(result[1].attribs).toHaveProperty('id', 'vegetables');
  640. });
  641. it('(undefined) : should not throw an exception', () => {
  642. expect(() => {
  643. $('li').parent(undefined);
  644. }).not.toThrow();
  645. });
  646. it('() : should return an empty object for top-level elements', () => {
  647. const result = $('html').parent();
  648. expect(result).toHaveLength(0);
  649. });
  650. it('() : should not contain duplicate elements', () => {
  651. const result = $('li').parent();
  652. expect(result).toHaveLength(1);
  653. });
  654. it('(selector) : should filter the matched parent elements by the selector', () => {
  655. const parents = $('.orange').parent();
  656. expect(parents).toHaveLength(1);
  657. expect(parents[0].attribs).toHaveProperty('id', 'fruits');
  658. const fruits = $('li', food).parent('#fruits');
  659. expect(fruits).toHaveLength(1);
  660. expect(fruits[0].attribs).toHaveProperty('id', 'fruits');
  661. });
  662. });
  663. describe('.closest', () => {
  664. it('() : should return an empty array', () => {
  665. const result = $('.orange').closest();
  666. expect(result).toHaveLength(0);
  667. expect(result).toBeInstanceOf(Cheerio);
  668. });
  669. it('(selector) : should find the closest element that matches the selector, searching through its ancestors and itself', () => {
  670. expect($('.orange').closest('.apple')).toHaveLength(0);
  671. expect(
  672. ($('.orange', food).closest('#food')[0] as Element).attribs,
  673. ).toHaveProperty('id', 'food');
  674. expect(
  675. ($('.orange', food).closest('ul')[0] as Element).attribs,
  676. ).toHaveProperty('id', 'fruits');
  677. expect(
  678. ($('.orange', food).closest('li')[0] as Element).attribs,
  679. ).toHaveProperty('class', 'orange');
  680. });
  681. it('(selector) : should find the closest element of each item, removing duplicates', () => {
  682. const result = $('li', food).closest('ul');
  683. expect(result).toHaveLength(2);
  684. });
  685. it('() : should not break if the selector does not have any results', () => {
  686. const result = $('.saladbar', food).closest('ul');
  687. expect(result).toHaveLength(0);
  688. });
  689. it('(selector) : should find closest element for text nodes', () => {
  690. const textNode = $('.apple', food).contents().first();
  691. const result = textNode.closest('#food') as Cheerio<Element>;
  692. expect(result[0].attribs).toHaveProperty('id', 'food');
  693. });
  694. });
  695. describe('.each', () => {
  696. it('( (i, elem) -> ) : should loop selected returning fn with (i, elem)', () => {
  697. const items: Element[] = [];
  698. const classes = ['apple', 'orange', 'pear'];
  699. $('li').each(function (idx, elem) {
  700. items[idx] = elem;
  701. expect(this.attribs).toHaveProperty('class', classes[idx]);
  702. });
  703. expect(items[0].attribs).toHaveProperty('class', 'apple');
  704. expect(items[1].attribs).toHaveProperty('class', 'orange');
  705. expect(items[2].attribs).toHaveProperty('class', 'pear');
  706. });
  707. it('( (i, elem) -> ) : should break iteration when the iterator function returns false', () => {
  708. let iterationCount = 0;
  709. $('li').each((idx) => {
  710. iterationCount++;
  711. return idx < 1;
  712. });
  713. expect(iterationCount).toBe(2);
  714. });
  715. });
  716. if (typeof Symbol !== 'undefined') {
  717. describe('[Symbol.iterator]', () => {
  718. it('should yield each element', () => {
  719. // The equivalent of: for (const element of $('li')) ...
  720. const $li = $('li');
  721. const iterator = $li[Symbol.iterator]();
  722. expect(iterator.next().value.attribs).toHaveProperty('class', 'apple');
  723. expect(iterator.next().value.attribs).toHaveProperty('class', 'orange');
  724. expect(iterator.next().value.attribs).toHaveProperty('class', 'pear');
  725. expect(iterator.next().done).toBe(true);
  726. });
  727. });
  728. }
  729. describe('.map', () => {
  730. it('(fn) : should be invoked with the correct arguments and context', () => {
  731. const $fruits = $('li');
  732. const args: [number, AnyNode][] = [];
  733. const thisVals: AnyNode[] = [];
  734. $fruits.map(function (...myArgs) {
  735. args.push(myArgs);
  736. thisVals.push(this);
  737. return undefined;
  738. });
  739. expect(args).toStrictEqual([
  740. [0, $fruits[0]],
  741. [1, $fruits[1]],
  742. [2, $fruits[2]],
  743. ]);
  744. expect(thisVals).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);
  745. });
  746. it('(fn) : should return an Cheerio object wrapping the returned items', () => {
  747. const $fruits = $('li');
  748. const $mapped = $fruits.map((i) => $fruits[2 - i]);
  749. expect($mapped).toHaveLength(3);
  750. expect($mapped[0]).toBe($fruits[2]);
  751. expect($mapped[1]).toBe($fruits[1]);
  752. expect($mapped[2]).toBe($fruits[0]);
  753. });
  754. it('(fn) : should ignore `null` and `undefined` returned by iterator', () => {
  755. const $fruits = $('li');
  756. const retVals = [null, undefined, $fruits[1]];
  757. const $mapped = $fruits.map((i) => retVals[i]);
  758. expect($mapped).toHaveLength(1);
  759. expect($mapped[0]).toBe($fruits[1]);
  760. });
  761. it('(fn) : should preform a shallow merge on arrays returned by iterator', () => {
  762. const $fruits = $('li');
  763. const $mapped = $fruits.map(() => [1, [3, 4]]);
  764. expect($mapped.get()).toStrictEqual([1, [3, 4], 1, [3, 4], 1, [3, 4]]);
  765. });
  766. it('(fn) : should tolerate `null` and `undefined` when flattening arrays returned by iterator', () => {
  767. const $fruits = $('li');
  768. const $mapped = $fruits.map(() => [null, undefined]);
  769. expect($mapped.get()).toStrictEqual([
  770. null,
  771. undefined,
  772. null,
  773. undefined,
  774. null,
  775. undefined,
  776. ]);
  777. });
  778. });
  779. describe('.filter', () => {
  780. it('(selector) : should reduce the set of matched elements to those that match the selector', () => {
  781. const pear = $('li').filter('.pear').text();
  782. expect(pear).toBe('Pear');
  783. });
  784. it('(selector) : should not consider nested elements', () => {
  785. const lis = $('#fruits').filter('li');
  786. expect(lis).toHaveLength(0);
  787. });
  788. it('(selection) : should reduce the set of matched elements to those that are contained in the provided selection', () => {
  789. const $fruits = $('li');
  790. const $pear = $fruits.filter('.pear, .apple');
  791. expect($fruits.filter($pear)).toHaveLength(2);
  792. });
  793. it('(element) : should reduce the set of matched elements to those that specified directly', () => {
  794. const $fruits = $('li');
  795. const pear = $fruits.filter('.pear')[0];
  796. expect($fruits.filter(pear)).toHaveLength(1);
  797. });
  798. it("(fn) : should reduce the set of matched elements to those that pass the function's test", () => {
  799. const orange = $('li')
  800. .filter(function (i, el) {
  801. expect(this).toBe(el);
  802. expect(el.tagName).toBe('li');
  803. expect(typeof i).toBe('number');
  804. return $(this).attr('class') === 'orange';
  805. })
  806. .text();
  807. expect(orange).toBe('Orange');
  808. });
  809. it('should also iterate over text nodes (#1867)', () => {
  810. const text = $('<a>a</a>b<c></c>').filter((_, el): el is Text =>
  811. isText(el),
  812. );
  813. expect(text[0].data).toBe('b');
  814. });
  815. });
  816. describe('.not', () => {
  817. it('(selector) : should reduce the set of matched elements to those that do not match the selector', () => {
  818. const $fruits = $('li');
  819. const $notPear = $fruits.not('.pear');
  820. expect($notPear).toHaveLength(2);
  821. expect($notPear[0]).toBe($fruits[0]);
  822. expect($notPear[1]).toBe($fruits[1]);
  823. });
  824. it('(selector) : should not consider nested elements', () => {
  825. const lis = $('#fruits').not('li');
  826. expect(lis).toHaveLength(1);
  827. });
  828. it('(selection) : should reduce the set of matched elements to those that are mot contained in the provided selection', () => {
  829. const $fruits = $('li');
  830. const $orange = $('.orange');
  831. const $notOrange = $fruits.not($orange);
  832. expect($notOrange).toHaveLength(2);
  833. expect($notOrange[0]).toBe($fruits[0]);
  834. expect($notOrange[1]).toBe($fruits[2]);
  835. });
  836. it('(element) : should reduce the set of matched elements to those that specified directly', () => {
  837. const $fruits = $('li');
  838. const apple = $('.apple')[0];
  839. const $notApple = $fruits.not(apple);
  840. expect($notApple).toHaveLength(2);
  841. expect($notApple[0]).toBe($fruits[1]);
  842. expect($notApple[1]).toBe($fruits[2]);
  843. });
  844. it("(fn) : should reduce the set of matched elements to those that do not pass the function's test", () => {
  845. const $fruits = $('li');
  846. const $notOrange = $fruits.not(function (i, el) {
  847. expect(this).toBe(el);
  848. expect(el).toHaveProperty('name', 'li');
  849. expect(typeof i).toBe('number');
  850. return $(this).attr('class') === 'orange';
  851. });
  852. expect($notOrange).toHaveLength(2);
  853. expect($notOrange[0]).toBe($fruits[0]);
  854. expect($notOrange[1]).toBe($fruits[2]);
  855. });
  856. });
  857. describe('.has', () => {
  858. beforeEach(() => {
  859. $ = load(food);
  860. });
  861. it('(selector) : should reduce the set of matched elements to those with descendants that match the selector', () => {
  862. const $fruits = $('#fruits,#vegetables').has('.pear');
  863. expect($fruits).toHaveLength(1);
  864. expect($fruits[0]).toBe($('#fruits')[0]);
  865. });
  866. it('(selector) : should only consider nested elements', () => {
  867. const $empty = $('#fruits').has('#fruits');
  868. expect($empty).toHaveLength(0);
  869. });
  870. it('(element) : should reduce the set of matched elements to those that are ancestors of the provided element', () => {
  871. const $fruits = $('#fruits,#vegetables').has($('.pear')[0]);
  872. expect($fruits).toHaveLength(1);
  873. expect($fruits[0]).toBe($('#fruits')[0]);
  874. });
  875. it('(element) : should only consider nested elements', () => {
  876. const $fruits = $('#fruits');
  877. const fruitsEl = $fruits[0];
  878. const $empty = $fruits.has(fruitsEl);
  879. expect($empty).toHaveLength(0);
  880. });
  881. });
  882. describe('.first', () => {
  883. it('() : should return the first item', () => {
  884. const $src = $(
  885. '<span>foo</span><span>bar</span><span>baz</span>',
  886. ) as Cheerio<Element>;
  887. const $elem = $src.first();
  888. expect($elem.length).toBe(1);
  889. expect($elem[0].childNodes[0]).toHaveProperty('data', 'foo');
  890. });
  891. it('() : should return an empty object for an empty object', () => {
  892. const $src = $();
  893. const $first = $src.first();
  894. expect($first.length).toBe(0);
  895. expect($first[0]).toBeUndefined();
  896. });
  897. });
  898. describe('.last', () => {
  899. it('() : should return the last element', () => {
  900. const $src = $(
  901. '<span>foo</span><span>bar</span><span>baz</span>',
  902. ) as Cheerio<Element>;
  903. const $elem = $src.last();
  904. expect($elem.length).toBe(1);
  905. expect($elem[0].childNodes[0]).toHaveProperty('data', 'baz');
  906. });
  907. it('() : should return an empty object for an empty object', () => {
  908. const $src = $();
  909. const $last = $src.last();
  910. expect($last.length).toBe(0);
  911. expect($last[0]).toBeUndefined();
  912. });
  913. });
  914. describe('.first & .last', () => {
  915. it('() : should return equivalent collections if only one element', () => {
  916. const $src = $('<span>bar</span>') as Cheerio<Element>;
  917. const $first = $src.first();
  918. const $last = $src.last();
  919. expect($first.length).toBe(1);
  920. expect($first[0].childNodes[0]).toHaveProperty('data', 'bar');
  921. expect($last.length).toBe(1);
  922. expect($last[0].childNodes[0]).toHaveProperty('data', 'bar');
  923. expect($first[0]).toBe($last[0]);
  924. });
  925. });
  926. describe('.eq', () => {
  927. it('(i) : should return the element at the specified index', () => {
  928. expect(getText($('li').eq(0))).toBe('Apple');
  929. expect(getText($('li').eq(1))).toBe('Orange');
  930. expect(getText($('li').eq(2))).toBe('Pear');
  931. expect(getText($('li').eq(3))).toBeUndefined();
  932. expect(getText($('li').eq(-1))).toBe('Pear');
  933. });
  934. });
  935. describe('.get', () => {
  936. it('(i) : should return the element at the specified index', () => {
  937. const children = $('#fruits').children();
  938. expect(children.get(0)).toBe(children[0]);
  939. expect(children.get(1)).toBe(children[1]);
  940. expect(children.get(2)).toBe(children[2]);
  941. });
  942. it('(-1) : should return the element indexed from the end of the collection', () => {
  943. const children = $('#fruits').children();
  944. expect(children.get(-1)).toBe(children[2]);
  945. expect(children.get(-2)).toBe(children[1]);
  946. expect(children.get(-3)).toBe(children[0]);
  947. });
  948. it('() : should return an array containing all of the collection', () => {
  949. const children = $('#fruits').children();
  950. const all = children.get();
  951. expect(Array.isArray(all)).toBe(true);
  952. expect(all).toStrictEqual([children[0], children[1], children[2]]);
  953. });
  954. });
  955. describe('.index', () => {
  956. describe('() :', () => {
  957. it('returns the index of a child amongst its siblings', () => {
  958. expect($('.orange').index()).toBe(1);
  959. });
  960. it('returns -1 when the selection has no parent', () => {
  961. expect($('<div/>').index()).toBe(-1);
  962. });
  963. });
  964. describe('(selector) :', () => {
  965. it('returns the index of the first element in the set matched by `selector`', () => {
  966. expect($('.apple').index('#fruits, li')).toBe(1);
  967. });
  968. it('returns -1 when the item is not present in the set matched by `selector`', () => {
  969. expect($('.apple').index('#fuits')).toBe(-1);
  970. });
  971. it('returns -1 when the first element in the set has no parent', () => {
  972. expect($('<div/>').index('*')).toBe(-1);
  973. });
  974. });
  975. describe('(node) :', () => {
  976. it('returns the index of the given node within the current selection', () => {
  977. const $lis = $('li');
  978. expect($lis.index($lis.get(1))).toBe(1);
  979. });
  980. it('returns the index of the given node within the current selection when the current selection has no parent', () => {
  981. const $apple = $('.apple').remove();
  982. expect($apple.index($apple.get(0))).toBe(0);
  983. });
  984. it('returns -1 when the given node is not present in the current selection', () => {
  985. expect($('li').index($('#fruits').get(0))).toBe(-1);
  986. });
  987. it('returns -1 when the current selection is empty', () => {
  988. expect($('.not-fruit').index($('#fruits').get(0))).toBe(-1);
  989. });
  990. });
  991. describe('(selection) :', () => {
  992. it('returns the index of the first node in the provided selection within the current selection', () => {
  993. const $lis = $('li');
  994. expect($lis.index($('.orange, .pear'))).toBe(1);
  995. });
  996. it('returns -1 when the given node is not present in the current selection', () => {
  997. expect($('li').index($('#fruits'))).toBe(-1);
  998. });
  999. it('returns -1 when the current selection is empty', () => {
  1000. expect($('.not-fruit').index($('#fruits'))).toBe(-1);
  1001. });
  1002. });
  1003. });
  1004. describe('.slice', () => {
  1005. it('(start) : should return all elements after the given index', () => {
  1006. const sliced = $('li').slice(1);
  1007. expect(sliced).toHaveLength(2);
  1008. expect(getText(sliced.eq(0))).toBe('Orange');
  1009. expect(getText(sliced.eq(1))).toBe('Pear');
  1010. });
  1011. it('(start, end) : should return all elements matching the given range', () => {
  1012. const sliced = $('li').slice(1, 2);
  1013. expect(sliced).toHaveLength(1);
  1014. expect(getText(sliced.eq(0))).toBe('Orange');
  1015. });
  1016. it('(-start) : should return element matching the offset from the end', () => {
  1017. const sliced = $('li').slice(-1);
  1018. expect(sliced).toHaveLength(1);
  1019. expect(getText(sliced.eq(0))).toBe('Pear');
  1020. });
  1021. });
  1022. describe('.end() :', () => {
  1023. let $fruits: Cheerio<Element>;
  1024. beforeEach(() => {
  1025. $fruits = $('#fruits').children();
  1026. });
  1027. it('returns an empty object at the end of the chain', () => {
  1028. expect($fruits.end().end().end()).toBeTruthy();
  1029. expect($fruits.end().end().end()).toHaveLength(0);
  1030. });
  1031. it('find', () => {
  1032. expect($fruits.find('.apple').end()).toBe($fruits);
  1033. });
  1034. it('filter', () => {
  1035. expect($fruits.filter('.apple').end()).toBe($fruits);
  1036. });
  1037. it('map', () => {
  1038. expect(
  1039. $fruits
  1040. .map(function () {
  1041. return this;
  1042. })
  1043. .end(),
  1044. ).toBe($fruits);
  1045. });
  1046. it('contents', () => {
  1047. expect($fruits.contents().end()).toBe($fruits);
  1048. });
  1049. it('eq', () => {
  1050. expect($fruits.eq(1).end()).toBe($fruits);
  1051. });
  1052. it('first', () => {
  1053. expect($fruits.first().end()).toBe($fruits);
  1054. });
  1055. it('last', () => {
  1056. expect($fruits.last().end()).toBe($fruits);
  1057. });
  1058. it('slice', () => {
  1059. expect($fruits.slice(1).end()).toBe($fruits);
  1060. });
  1061. it('children', () => {
  1062. expect($fruits.children().end()).toBe($fruits);
  1063. });
  1064. it('parent', () => {
  1065. expect($fruits.parent().end()).toBe($fruits);
  1066. });
  1067. it('parents', () => {
  1068. expect($fruits.parents().end()).toBe($fruits);
  1069. });
  1070. it('closest', () => {
  1071. expect($fruits.closest('ul').end()).toBe($fruits);
  1072. });
  1073. it('siblings', () => {
  1074. expect($fruits.siblings().end()).toBe($fruits);
  1075. });
  1076. it('next', () => {
  1077. expect($fruits.next().end()).toBe($fruits);
  1078. });
  1079. it('nextAll', () => {
  1080. expect($fruits.nextAll().end()).toBe($fruits);
  1081. });
  1082. it('prev', () => {
  1083. expect($fruits.prev().end()).toBe($fruits);
  1084. });
  1085. it('prevAll', () => {
  1086. expect($fruits.prevAll().end()).toBe($fruits);
  1087. });
  1088. it('clone', () => {
  1089. expect($fruits.clone().end()).toBe($fruits);
  1090. });
  1091. });
  1092. describe('.add()', () => {
  1093. let $fruits: Cheerio<AnyNode>;
  1094. let $apple: Cheerio<Element>;
  1095. let $orange: Cheerio<Element>;
  1096. let $pear: Cheerio<Element>;
  1097. beforeEach(() => {
  1098. $ = load(food);
  1099. $fruits = $('#fruits');
  1100. $apple = $('.apple');
  1101. $orange = $('.orange');
  1102. $pear = $('.pear');
  1103. });
  1104. describe('(selector) matched element :', () => {
  1105. it('occurs before current selection', () => {
  1106. const $selection = $orange.add('.apple');
  1107. expect($selection).toHaveLength(2);
  1108. expect($selection[0]).toBe($apple[0]);
  1109. expect($selection[1]).toBe($orange[0]);
  1110. });
  1111. it('is identical to the current selection', () => {
  1112. const $selection = $orange.add('.orange');
  1113. expect($selection).toHaveLength(1);
  1114. expect($selection[0]).toBe($orange[0]);
  1115. });
  1116. it('occurs after current selection', () => {
  1117. const $selection = $orange.add('.pear');
  1118. expect($selection).toHaveLength(2);
  1119. expect($selection[0]).toBe($orange[0]);
  1120. expect($selection[1]).toBe($pear[0]);
  1121. });
  1122. it('contains the current selection', () => {
  1123. const $selection = $orange.add('#fruits');
  1124. expect($selection).toHaveLength(2);
  1125. expect($selection[0]).toBe($fruits[0]);
  1126. expect($selection[1]).toBe($orange[0]);
  1127. });
  1128. it('is a child of the current selection', () => {
  1129. const $selection = $fruits.add('.orange');
  1130. expect($selection).toHaveLength(2);
  1131. expect($selection[0]).toBe($fruits[0]);
  1132. expect($selection[1]).toBe($orange[0]);
  1133. });
  1134. it('is root object preserved', () => {
  1135. const $selection = $('<div></div>').add('#fruits');
  1136. expect($selection).toHaveLength(2);
  1137. expect($selection.eq(0).is('div')).toBe(true);
  1138. expect($selection.eq(1).is($fruits.eq(0))).toBe(true);
  1139. });
  1140. });
  1141. describe('(selector) matched elements :', () => {
  1142. it('occur before the current selection', () => {
  1143. const $selection = $pear.add('.apple, .orange');
  1144. expect($selection).toHaveLength(3);
  1145. expect($selection[0]).toBe($apple[0]);
  1146. expect($selection[1]).toBe($orange[0]);
  1147. expect($selection[2]).toBe($pear[0]);
  1148. });
  1149. it('include the current selection', () => {
  1150. const $selection = $pear.add('#fruits li');
  1151. expect($selection).toHaveLength(3);
  1152. expect($selection[0]).toBe($apple[0]);
  1153. expect($selection[1]).toBe($orange[0]);
  1154. expect($selection[2]).toBe($pear[0]);
  1155. });
  1156. it('occur after the current selection', () => {
  1157. const $selection = $apple.add('.orange, .pear');
  1158. expect($selection).toHaveLength(3);
  1159. expect($selection[0]).toBe($apple[0]);
  1160. expect($selection[1]).toBe($orange[0]);
  1161. expect($selection[2]).toBe($pear[0]);
  1162. });
  1163. it('occur within the current selection', () => {
  1164. const $selection = $fruits.add('#fruits li');
  1165. expect($selection).toHaveLength(4);
  1166. expect($selection[0]).toBe($fruits[0]);
  1167. expect($selection[1]).toBe($apple[0]);
  1168. expect($selection[2]).toBe($orange[0]);
  1169. expect($selection[3]).toBe($pear[0]);
  1170. });
  1171. });
  1172. describe('(selector, context) :', () => {
  1173. it(', context)', () => {
  1174. const $selection = $fruits.add('li', '#vegetables');
  1175. expect($selection).toHaveLength(3);
  1176. expect($selection[0]).toBe($fruits[0]);
  1177. expect($selection[1]).toBe($('.carrot')[0]);
  1178. expect($selection[2]).toBe($('.sweetcorn')[0]);
  1179. });
  1180. });
  1181. describe('(element) honors document order when element occurs :', () => {
  1182. it('before the current selection', () => {
  1183. const $selection = $orange.add($apple[0]);
  1184. expect($selection).toHaveLength(2);
  1185. expect($selection[0]).toBe($apple[0]);
  1186. expect($selection[1]).toBe($orange[0]);
  1187. });
  1188. it('after the current selection', () => {
  1189. const $selection = $orange.add($pear[0]);
  1190. expect($selection).toHaveLength(2);
  1191. expect($selection[0]).toBe($orange[0]);
  1192. expect($selection[1]).toBe($pear[0]);
  1193. });
  1194. it('within the current selection', () => {
  1195. const $selection = $fruits.add($orange[0]);
  1196. expect($selection).toHaveLength(2);
  1197. expect($selection[0]).toBe($fruits[0]);
  1198. expect($selection[1]).toBe($orange[0]);
  1199. });
  1200. it('as an ancestor of the current selection', () => {
  1201. const $selection = $orange.add($fruits[0]);
  1202. expect($selection).toHaveLength(2);
  1203. expect($selection[0]).toBe($fruits[0]);
  1204. expect($selection[1]).toBe($orange[0]);
  1205. });
  1206. it('does not insert an element already contained within the current selection', () => {
  1207. const $selection = $apple.add($apple[0]);
  1208. expect($selection).toHaveLength(1);
  1209. expect($selection[0]).toBe($apple[0]);
  1210. });
  1211. });
  1212. describe('([elements]) : elements', () => {
  1213. it('occur before the current selection', () => {
  1214. const $selection = $pear.add($('.apple, .orange').get());
  1215. expect($selection).toHaveLength(3);
  1216. expect($selection[0]).toBe($apple[0]);
  1217. expect($selection[1]).toBe($orange[0]);
  1218. expect($selection[2]).toBe($pear[0]);
  1219. });
  1220. it('include the current selection', () => {
  1221. const $selection = $pear.add($('#fruits li').get());
  1222. expect($selection).toHaveLength(3);
  1223. expect($selection[0]).toBe($apple[0]);
  1224. expect($selection[1]).toBe($orange[0]);
  1225. expect($selection[2]).toBe($pear[0]);
  1226. });
  1227. it('occur after the current selection', () => {
  1228. const $selection = $apple.add($('.orange, .pear').get());
  1229. expect($selection).toHaveLength(3);
  1230. expect($selection[0]).toBe($apple[0]);
  1231. expect($selection[1]).toBe($orange[0]);
  1232. expect($selection[2]).toBe($pear[0]);
  1233. });
  1234. it('occur within the current selection', () => {
  1235. const $selection = $fruits.add($('#fruits li').get());
  1236. expect($selection).toHaveLength(4);
  1237. expect($selection[0]).toBe($fruits[0]);
  1238. expect($selection[1]).toBe($apple[0]);
  1239. expect($selection[2]).toBe($orange[0]);
  1240. expect($selection[3]).toBe($pear[0]);
  1241. });
  1242. });
  1243. /**
  1244. * Element order is undefined in this case, so it should not be asserted
  1245. * here.
  1246. *
  1247. * If the collection consists of elements from different documents or ones
  1248. * not in any document, the sort order is undefined.
  1249. *
  1250. * @see {@link https://api.jquery.com/add/}
  1251. */
  1252. it('(html) : correctly parses and adds the new elements', () => {
  1253. const $selection = $apple.add('<li class="banana">banana</li>');
  1254. expect($selection).toHaveLength(2);
  1255. expect($selection.is('.apple')).toBe(true);
  1256. expect($selection.is('.banana')).toBe(true);
  1257. });
  1258. describe('(selection) element in selection :', () => {
  1259. it('occurs before current selection', () => {
  1260. const $selection = $orange.add($('.apple'));
  1261. expect($selection).toHaveLength(2);
  1262. expect($selection[0]).toBe($apple[0]);
  1263. expect($selection[1]).toBe($orange[0]);
  1264. });
  1265. it('is identical to the current selection', () => {
  1266. const $selection = $orange.add($('.orange'));
  1267. expect($selection).toHaveLength(1);
  1268. expect($selection[0]).toBe($orange[0]);
  1269. });
  1270. it('occurs after current selection', () => {
  1271. const $selection = $orange.add($('.pear'));
  1272. expect($selection).toHaveLength(2);
  1273. expect($selection[0]).toBe($orange[0]);
  1274. expect($selection[1]).toBe($pear[0]);
  1275. });
  1276. it('contains the current selection', () => {
  1277. const $selection = $orange.add($('#fruits'));
  1278. expect($selection).toHaveLength(2);
  1279. expect($selection[0]).toBe($fruits[0]);
  1280. expect($selection[1]).toBe($orange[0]);
  1281. });
  1282. it('is a child of the current selection', () => {
  1283. const $selection = $fruits.add($('.orange'));
  1284. expect($selection).toHaveLength(2);
  1285. expect($selection[0]).toBe($fruits[0]);
  1286. expect($selection[1]).toBe($orange[0]);
  1287. });
  1288. });
  1289. describe('(selection) elements in the selection :', () => {
  1290. it('occur before the current selection', () => {
  1291. const $selection = $pear.add($('.apple, .orange'));
  1292. expect($selection).toHaveLength(3);
  1293. expect($selection[0]).toBe($apple[0]);
  1294. expect($selection[1]).toBe($orange[0]);
  1295. expect($selection[2]).toBe($pear[0]);
  1296. });
  1297. it('include the current selection', () => {
  1298. const $selection = $pear.add($('#fruits li'));
  1299. expect($selection).toHaveLength(3);
  1300. expect($selection[0]).toBe($apple[0]);
  1301. expect($selection[1]).toBe($orange[0]);
  1302. expect($selection[2]).toBe($pear[0]);
  1303. });
  1304. it('occur after the current selection', () => {
  1305. const $selection = $apple.add($('.orange, .pear'));
  1306. expect($selection).toHaveLength(3);
  1307. expect($selection[0]).toBe($apple[0]);
  1308. expect($selection[1]).toBe($orange[0]);
  1309. expect($selection[2]).toBe($pear[0]);
  1310. });
  1311. it('occur within the current selection', () => {
  1312. const $selection = $fruits.add($('#fruits li'));
  1313. expect($selection).toHaveLength(4);
  1314. expect($selection[0]).toBe($fruits[0]);
  1315. expect($selection[1]).toBe($apple[0]);
  1316. expect($selection[2]).toBe($orange[0]);
  1317. expect($selection[3]).toBe($pear[0]);
  1318. });
  1319. });
  1320. describe('(selection) :', () => {
  1321. it('modifying nested selections should not impact the parent [#834]', () => {
  1322. const apple_pear = $apple.add($pear);
  1323. // Applies red to apple and pear
  1324. apple_pear.addClass('red');
  1325. expect($apple.hasClass('red')).toBe(true); // This is true
  1326. expect($pear.hasClass('red')).toBe(true); // This is true
  1327. // Applies green to pear... AND should not affect apple
  1328. $pear.addClass('green');
  1329. expect($pear.hasClass('green')).toBe(true); // Currently this is true
  1330. expect($apple.hasClass('green')).toBe(false); // And this should be false!
  1331. });
  1332. });
  1333. });
  1334. describe('.addBack', () => {
  1335. describe('() :', () => {
  1336. it('includes siblings and self', () => {
  1337. const $selection = $('.orange').siblings().addBack();
  1338. expect($selection).toHaveLength(3);
  1339. expect($selection[0]).toBe($('.apple')[0]);
  1340. expect($selection[1]).toBe($('.orange')[0]);
  1341. expect($selection[2]).toBe($('.pear')[0]);
  1342. });
  1343. it('includes children and self', () => {
  1344. const $selection = $('#fruits').children().addBack();
  1345. expect($selection).toHaveLength(4);
  1346. expect($selection[0]).toBe($('#fruits')[0]);
  1347. expect($selection[1]).toBe($('.apple')[0]);
  1348. expect($selection[2]).toBe($('.orange')[0]);
  1349. expect($selection[3]).toBe($('.pear')[0]);
  1350. });
  1351. it('includes parent and self', () => {
  1352. const $selection = $('.apple').parent().addBack();
  1353. expect($selection).toHaveLength(2);
  1354. expect($selection[0]).toBe($('#fruits')[0]);
  1355. expect($selection[1]).toBe($('.apple')[0]);
  1356. });
  1357. it('includes parents and self', () => {
  1358. const q = load(food);
  1359. const $selection = q('.apple').parents().addBack();
  1360. expect($selection).toHaveLength(5);
  1361. expect($selection[0]).toBe(q('html')[0]);
  1362. expect($selection[1]).toBe(q('body')[0]);
  1363. expect($selection[2]).toBe(q('#food')[0]);
  1364. expect($selection[3]).toBe(q('#fruits')[0]);
  1365. expect($selection[4]).toBe(q('.apple')[0]);
  1366. });
  1367. });
  1368. it('(filter) : filters the previous selection', () => {
  1369. const $selection = $('li').eq(1).addBack('.apple');
  1370. expect($selection).toHaveLength(2);
  1371. expect($selection[0]).toBe($('.apple')[0]);
  1372. expect($selection[1]).toBe($('.orange')[0]);
  1373. });
  1374. it('() : fails gracefully when no args are passed', () => {
  1375. const $div = cheerio('<div>');
  1376. expect($div.addBack()).toBe($div);
  1377. });
  1378. });
  1379. describe('.is', () => {
  1380. it('() : should return false', () => {
  1381. expect($('li.apple').is()).toBe(false);
  1382. });
  1383. it('(true selector) : should return true', () => {
  1384. expect(cheerio('#vegetables', vegetables).is('ul')).toBe(true);
  1385. });
  1386. it('(false selector) : should return false', () => {
  1387. expect(cheerio('#vegetables', vegetables).is('div')).toBe(false);
  1388. });
  1389. it('(true selection) : should return true', () => {
  1390. const $vegetables = cheerio('li', vegetables);
  1391. expect($vegetables.is($vegetables.eq(1))).toBe(true);
  1392. });
  1393. it('(false selection) : should return false', () => {
  1394. const $vegetableList = cheerio(vegetables);
  1395. const $vegetables = $vegetableList.find('li');
  1396. expect($vegetables.is($vegetableList)).toBe(false);
  1397. });
  1398. it('(true element) : should return true', () => {
  1399. const $vegetables = cheerio('li', vegetables);
  1400. expect($vegetables.is($vegetables[0])).toBe(true);
  1401. });
  1402. it('(false element) : should return false', () => {
  1403. const $vegetableList = cheerio(vegetables);
  1404. const $vegetables = $vegetableList.find('li');
  1405. expect($vegetables.is($vegetableList[0])).toBe(false);
  1406. });
  1407. it('(true predicate) : should return true', () => {
  1408. const result = $('li').is(function () {
  1409. return this.tagName === 'li' && $(this).hasClass('pear');
  1410. });
  1411. expect(result).toBe(true);
  1412. });
  1413. it('(false predicate) : should return false', () => {
  1414. const result = $('li')
  1415. .last()
  1416. .is(function () {
  1417. return this.tagName === 'ul';
  1418. });
  1419. expect(result).toBe(false);
  1420. });
  1421. });
  1422. });