treewalker.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. import Model from '../../src/model/model';
  6. import DocumentFragment from '../../src/model/documentfragment';
  7. import Element from '../../src/model/element';
  8. import Text from '../../src/model/text';
  9. import TreeWalker from '../../src/model/treewalker';
  10. import Position from '../../src/model/position';
  11. import Range from '../../src/model/range';
  12. import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  13. describe( 'TreeWalker', () => {
  14. let model, doc, root, img1, paragraph, ba, r, img2, x,
  15. rootBeginning, rootEnding;
  16. beforeEach( () => {
  17. model = new Model();
  18. doc = model.document;
  19. root = doc.createRoot();
  20. // root
  21. // |- img1
  22. // |- p
  23. // |- B -bold
  24. // |- A -bold
  25. // |- R
  26. // |
  27. // |- img2
  28. // |
  29. // |- X
  30. ba = new Text( 'ba', { bold: true } );
  31. r = new Text( 'r' );
  32. img2 = new Element( 'img2' );
  33. x = new Text( 'x' );
  34. paragraph = new Element( 'p', [], [ ba, r, img2, x ] );
  35. img1 = new Element( 'img1' );
  36. root._insertChild( 0, [ img1, paragraph ] );
  37. rootBeginning = new Position( root, [ 0 ] );
  38. rootEnding = new Position( root, [ 2 ] );
  39. } );
  40. describe( 'constructor()', () => {
  41. it( 'should throw if neither boundaries nor starting position is set', () => {
  42. expectToThrowCKEditorError( () => {
  43. new TreeWalker(); // eslint-disable-line no-new
  44. }, /^model-tree-walker-no-start-position/, null );
  45. expectToThrowCKEditorError( () => {
  46. new TreeWalker( {} ); // eslint-disable-line no-new
  47. }, /^model-tree-walker-no-start-position/, null );
  48. expectToThrowCKEditorError( () => {
  49. new TreeWalker( { singleCharacters: true } ); // eslint-disable-line no-new
  50. }, /^model-tree-walker-no-start-position/, null );
  51. } );
  52. it( 'should throw if walking direction is unknown', () => {
  53. expectToThrowCKEditorError( () => {
  54. new TreeWalker( { startPosition: rootBeginning, direction: 'unknown' } ); // eslint-disable-line no-new
  55. }, /^model-tree-walker-unknown-direction/, model );
  56. } );
  57. } );
  58. describe( 'is()', () => {
  59. let treeWalker;
  60. beforeEach( () => {
  61. treeWalker = new TreeWalker( { startPosition: rootBeginning } );
  62. } );
  63. it( 'should return true for "treeWalker"', () => {
  64. expect( treeWalker.is( 'treeWalker' ) ).to.be.true;
  65. expect( treeWalker.is( 'model:treeWalker' ) ).to.be.true;
  66. } );
  67. it( 'should return false for incorrect values', () => {
  68. expect( treeWalker.is( 'model' ) ).to.be.false;
  69. expect( treeWalker.is( 'model:node' ) ).to.be.false;
  70. expect( treeWalker.is( 'text' ) ).to.be.false;
  71. expect( treeWalker.is( 'element', 'paragraph' ) ).to.be.false;
  72. } );
  73. } );
  74. describe( 'iterate from start position `startPosition`', () => {
  75. let expected;
  76. beforeEach( () => {
  77. expected = [
  78. { type: 'elementStart', item: img1 },
  79. { type: 'elementEnd', item: img1 },
  80. { type: 'elementStart', item: paragraph },
  81. { type: 'text', data: 'ba', attrs: [ [ 'bold', true ] ] },
  82. { type: 'text', data: 'r', attrs: [] },
  83. { type: 'elementStart', item: img2 },
  84. { type: 'elementEnd', item: img2 },
  85. { type: 'text', data: 'x', attrs: [] },
  86. { type: 'elementEnd', item: paragraph }
  87. ];
  88. } );
  89. it( 'should provide iterator interface with default forward direction', () => {
  90. const iterator = new TreeWalker( { startPosition: rootBeginning } );
  91. let i = 0;
  92. for ( const value of iterator ) {
  93. expectValue( value, expected[ i ] );
  94. i++;
  95. }
  96. expect( i ).to.equal( expected.length );
  97. } );
  98. it( 'should provide iterator interface with forward direction', () => {
  99. const iterator = new TreeWalker( { startPosition: rootBeginning, direction: 'forward' } );
  100. let i = 0;
  101. for ( const value of iterator ) {
  102. expectValue( value, expected[ i ] );
  103. i++;
  104. }
  105. expect( i ).to.equal( expected.length );
  106. } );
  107. it( 'should provide iterator interface which backward direction', () => {
  108. const iterator = new TreeWalker( { startPosition: rootEnding, direction: 'backward' } );
  109. let i = expected.length;
  110. for ( const value of iterator ) {
  111. expectValue( value, expected[ --i ], { direction: 'backward' } );
  112. }
  113. expect( i ).to.equal( 0 );
  114. } );
  115. it( 'should start iterating at the startPosition witch is not a root bound', () => {
  116. const iterator = new TreeWalker( { startPosition: new Position( root, [ 1 ] ) } );
  117. let i = 2;
  118. for ( const value of iterator ) {
  119. expectValue( value, expected[ i ] );
  120. i++;
  121. }
  122. expect( i ).to.equal( expected.length );
  123. } );
  124. it( 'should start iterating at the startPosition witch is not a root bound, going backward', () => {
  125. const expected = [
  126. { type: 'elementStart', item: img1 },
  127. { type: 'elementEnd', item: img1 }
  128. ];
  129. const iterator = new TreeWalker( { startPosition: new Position( root, [ 1 ] ), direction: 'backward' } );
  130. let i = expected.length;
  131. for ( const value of iterator ) {
  132. expectValue( value, expected[ --i ], { direction: 'backward' } );
  133. }
  134. expect( i ).to.equal( 0 );
  135. } );
  136. } );
  137. describe( 'iterate trough the range `boundary`', () => {
  138. describe( 'range starts between elements', () => {
  139. let expected, range;
  140. beforeEach( () => {
  141. expected = [
  142. { type: 'elementStart', item: paragraph },
  143. { type: 'text', data: 'ba', attrs: [ [ 'bold', true ] ] },
  144. { type: 'text', data: 'r', attrs: [] },
  145. { type: 'elementStart', item: img2 },
  146. { type: 'elementEnd', item: img2 }
  147. ];
  148. range = new Range( new Position( root, [ 1 ] ), new Position( root, [ 1, 4 ] ) );
  149. } );
  150. it( 'should iterate over the range', () => {
  151. const iterator = new TreeWalker( { boundaries: range } );
  152. let i = 0;
  153. for ( const value of iterator ) {
  154. expectValue( value, expected[ i ] );
  155. i++;
  156. }
  157. expect( i ).to.equal( expected.length );
  158. } );
  159. it( 'should iterate over the range going backward', () => {
  160. const iterator = new TreeWalker( { boundaries: range, direction: 'backward' } );
  161. let i = expected.length;
  162. for ( const value of iterator ) {
  163. expectValue( value, expected[ --i ], { direction: 'backward' } );
  164. }
  165. expect( i ).to.equal( 0 );
  166. } );
  167. } );
  168. describe( 'range starts inside the text', () => {
  169. let expected, range;
  170. beforeEach( () => {
  171. expected = [
  172. { type: 'text', data: 'a', attrs: [ [ 'bold', true ] ] },
  173. { type: 'text', data: 'r', attrs: [] },
  174. { type: 'elementStart', item: img2 },
  175. { type: 'elementEnd', item: img2 }
  176. ];
  177. range = new Range( new Position( root, [ 1, 1 ] ), new Position( root, [ 1, 4 ] ) );
  178. } );
  179. it( 'should return part of the text', () => {
  180. const iterator = new TreeWalker( { boundaries: range } );
  181. let i = 0;
  182. for ( const value of iterator ) {
  183. expectValue( value, expected[ i ] );
  184. i++;
  185. }
  186. expect( i ).to.equal( expected.length );
  187. } );
  188. it( 'should return part of the text going backward', () => {
  189. const iterator = new TreeWalker( {
  190. boundaries: range,
  191. direction: 'backward' }
  192. );
  193. let i = expected.length;
  194. for ( const value of iterator ) {
  195. expectValue( value, expected[ --i ], { direction: 'backward' } );
  196. }
  197. expect( i ).to.equal( 0 );
  198. } );
  199. } );
  200. describe( 'range ends inside the text', () => {
  201. let expected, range;
  202. beforeEach( () => {
  203. expected = [
  204. { type: 'elementStart', item: img1 },
  205. { type: 'elementEnd', item: img1 },
  206. { type: 'elementStart', item: paragraph },
  207. { type: 'text', data: 'b', attrs: [ [ 'bold', true ] ] }
  208. ];
  209. range = new Range( rootBeginning, new Position( root, [ 1, 1 ] ) );
  210. } );
  211. it( 'should return part of the text', () => {
  212. const iterator = new TreeWalker( { boundaries: range } );
  213. let i = 0;
  214. for ( const value of iterator ) {
  215. expectValue( value, expected[ i ] );
  216. i++;
  217. }
  218. expect( i ).to.equal( expected.length );
  219. } );
  220. it( 'should return part of the text going backward', () => {
  221. const iterator = new TreeWalker( {
  222. boundaries: range,
  223. startPosition: range.end,
  224. direction: 'backward'
  225. } );
  226. let i = expected.length;
  227. for ( const value of iterator ) {
  228. expectValue( value, expected[ --i ], { direction: 'backward' } );
  229. }
  230. expect( i ).to.equal( 0 );
  231. } );
  232. } );
  233. describe( 'custom start position', () => {
  234. it( 'should iterating from the start position', () => {
  235. const expected = [
  236. { type: 'text', data: 'r', attrs: [] },
  237. { type: 'elementStart', item: img2 },
  238. { type: 'elementEnd', item: img2 }
  239. ];
  240. const range = new Range( new Position( root, [ 1 ] ), new Position( root, [ 1, 4 ] ) );
  241. const iterator = new TreeWalker( {
  242. boundaries: range,
  243. startPosition: new Position( root, [ 1, 2 ] )
  244. } );
  245. let i = 0;
  246. for ( const value of iterator ) {
  247. expectValue( value, expected[ i ] );
  248. i++;
  249. }
  250. expect( i ).to.equal( expected.length );
  251. } );
  252. it( 'should iterating from the start position going backward', () => {
  253. const expected = [
  254. { type: 'text', data: 'r', attrs: [] },
  255. { type: 'elementStart', item: img2 },
  256. { type: 'elementEnd', item: img2 }
  257. ];
  258. const range = new Range( new Position( root, [ 1, 2 ] ), new Position( root, [ 1, 6 ] ) );
  259. const iterator = new TreeWalker( {
  260. boundaries: range,
  261. startPosition: new Position( root, [ 1, 4 ] ),
  262. direction: 'backward'
  263. } );
  264. let i = expected.length;
  265. for ( const value of iterator ) {
  266. expectValue( value, expected[ --i ], { direction: 'backward' } );
  267. }
  268. expect( i ).to.equal( 0 );
  269. } );
  270. } );
  271. } );
  272. describe( 'iterate by every single characters `singleCharacter`', () => {
  273. describe( 'whole root', () => {
  274. let expected;
  275. beforeEach( () => {
  276. expected = [
  277. { type: 'elementStart', item: img1 },
  278. { type: 'elementEnd', item: img1 },
  279. { type: 'elementStart', item: paragraph },
  280. { type: 'text', data: 'b', attrs: [ [ 'bold', true ] ] },
  281. { type: 'text', data: 'a', attrs: [ [ 'bold', true ] ] },
  282. { type: 'text', data: 'r', attrs: [] },
  283. { type: 'elementStart', item: img2 },
  284. { type: 'elementEnd', item: img2 },
  285. { type: 'text', data: 'x', attrs: [] },
  286. { type: 'elementEnd', item: paragraph }
  287. ];
  288. } );
  289. it( 'should return single characters', () => {
  290. const iterator = new TreeWalker( { startPosition: rootBeginning, singleCharacters: true } );
  291. let i = 0;
  292. for ( const value of iterator ) {
  293. expectValue( value, expected[ i ] );
  294. i++;
  295. }
  296. expect( i ).to.equal( expected.length );
  297. } );
  298. it( 'should return single characters going backward', () => {
  299. const iterator = new TreeWalker( {
  300. startPosition: rootEnding,
  301. singleCharacters: true,
  302. direction: 'backward' }
  303. );
  304. let i = expected.length;
  305. for ( const value of iterator ) {
  306. expectValue( value, expected[ --i ], { direction: 'backward' } );
  307. }
  308. expect( i ).to.equal( 0 );
  309. } );
  310. } );
  311. describe( 'range', () => {
  312. let start, end, range, expected;
  313. beforeEach( () => {
  314. expected = [
  315. { type: 'text', data: 'b', attrs: [ [ 'bold', true ] ] },
  316. { type: 'text', data: 'a', attrs: [ [ 'bold', true ] ] },
  317. { type: 'text', data: 'r', attrs: [] },
  318. { type: 'elementStart', item: img2 }
  319. ];
  320. start = new Position( root, [ 1, 0 ] ); // p, 0
  321. end = new Position( root, [ 1, 3, 0 ] ); // img2, 0
  322. range = new Range( start, end );
  323. } );
  324. it( 'should respect boundaries', () => {
  325. const iterator = new TreeWalker( { boundaries: range, singleCharacters: true } );
  326. let i = 0;
  327. for ( const value of iterator ) {
  328. expectValue( value, expected[ i ] );
  329. i++;
  330. }
  331. expect( i ).to.equal( expected.length );
  332. } );
  333. it( 'should respect boundaries going backward', () => {
  334. const iterator = new TreeWalker( {
  335. boundaries: range,
  336. singleCharacters: true,
  337. startPosition: range.end,
  338. direction: 'backward'
  339. } );
  340. let i = expected.length;
  341. for ( const value of iterator ) {
  342. expectValue( value, expected[ --i ], { direction: 'backward' } );
  343. }
  344. expect( i ).to.equal( 0 );
  345. } );
  346. } );
  347. } );
  348. describe( 'iterate omitting child nodes and elementEnd `shallow`', () => {
  349. let expected;
  350. beforeEach( () => {
  351. expected = [
  352. { type: 'elementStart', item: img1 },
  353. { type: 'elementStart', item: paragraph }
  354. ];
  355. } );
  356. it( 'should not enter elements', () => {
  357. const iterator = new TreeWalker( { startPosition: rootBeginning, shallow: true } );
  358. let i = 0;
  359. for ( const value of iterator ) {
  360. expectValue( value, expected[ i ], { shallow: true } );
  361. i++;
  362. }
  363. expect( i ).to.equal( expected.length );
  364. } );
  365. it( 'should not enter elements going backward', () => {
  366. const iterator = new TreeWalker( { startPosition: rootEnding, shallow: true, direction: 'backward' } );
  367. let i = expected.length;
  368. for ( const value of iterator ) {
  369. expectValue( value, expected[ --i ], { shallow: true, direction: 'backward' } );
  370. }
  371. expect( i ).to.equal( 0 );
  372. } );
  373. } );
  374. describe( 'iterate omitting elementEnd `ignoreElementEnd`', () => {
  375. describe( 'merged text', () => {
  376. let expected;
  377. beforeEach( () => {
  378. expected = [
  379. { type: 'elementStart', item: img1 },
  380. { type: 'elementStart', item: paragraph },
  381. { type: 'text', data: 'ba', attrs: [ [ 'bold', true ] ] },
  382. { type: 'text', data: 'r', attrs: [] },
  383. { type: 'elementStart', item: img2 },
  384. { type: 'text', data: 'x', attrs: [] }
  385. ];
  386. } );
  387. it( 'should iterate ignoring elementEnd', () => {
  388. const iterator = new TreeWalker( { startPosition: rootBeginning, ignoreElementEnd: true } );
  389. let i = 0;
  390. for ( const value of iterator ) {
  391. expectValue( value, expected[ i ] );
  392. i++;
  393. }
  394. expect( i ).to.equal( expected.length );
  395. } );
  396. it( 'should iterate ignoring elementEnd going backward', () => {
  397. const iterator = new TreeWalker( {
  398. startPosition: rootEnding,
  399. ignoreElementEnd: true,
  400. direction: 'backward'
  401. } );
  402. let i = expected.length;
  403. for ( const value of iterator ) {
  404. expectValue( value, expected[ --i ], { direction: 'backward' } );
  405. }
  406. expect( i ).to.equal( 0 );
  407. } );
  408. } );
  409. describe( 'single character', () => {
  410. let expected;
  411. beforeEach( () => {
  412. expected = [
  413. { type: 'elementStart', item: img1 },
  414. { type: 'elementStart', item: paragraph },
  415. { type: 'text', data: 'b', attrs: [ [ 'bold', true ] ] },
  416. { type: 'text', data: 'a', attrs: [ [ 'bold', true ] ] },
  417. { type: 'text', data: 'r', attrs: [] },
  418. { type: 'elementStart', item: img2 },
  419. { type: 'text', data: 'x', attrs: [] }
  420. ];
  421. } );
  422. it( 'should return single characters ignoring elementEnd', () => {
  423. const iterator = new TreeWalker( {
  424. startPosition: rootBeginning,
  425. singleCharacters: true,
  426. ignoreElementEnd: true
  427. } );
  428. let i = 0;
  429. for ( const value of iterator ) {
  430. expectValue( value, expected[ i ] );
  431. i++;
  432. }
  433. expect( i ).to.equal( expected.length );
  434. } );
  435. it( 'should return single characters ignoring elementEnd going backward', () => {
  436. const iterator = new TreeWalker( {
  437. startPosition: rootEnding,
  438. singleCharacters: true,
  439. ignoreElementEnd: true,
  440. direction: 'backward'
  441. } );
  442. let i = expected.length;
  443. for ( const value of iterator ) {
  444. expectValue( value, expected[ --i ], { direction: 'backward' } );
  445. }
  446. expect( i ).to.equal( 0 );
  447. } );
  448. } );
  449. } );
  450. it( 'should iterate over document fragment', () => {
  451. const foo = new Text( 'foo' );
  452. const bar = new Text( 'bar' );
  453. const p = new Element( 'p', null, [ foo, bar ] );
  454. const docFrag = new DocumentFragment( [ p ] );
  455. const iterator = new TreeWalker( {
  456. startPosition: new Position( docFrag, [ 0 ] ),
  457. ignoreElementEnd: true
  458. } );
  459. const expected = [
  460. { type: 'elementStart', item: p },
  461. { type: 'text', data: 'foo', attrs: [] },
  462. { type: 'text', data: 'bar', attrs: [] }
  463. ];
  464. let i = 0;
  465. for ( const value of iterator ) {
  466. expectValue( value, expected[ i++ ], { ignoreElementEnd: true } );
  467. }
  468. } );
  469. describe( 'skip', () => {
  470. describe( 'forward treewalker', () => {
  471. it( 'should jump over all text nodes', () => {
  472. const walker = new TreeWalker( {
  473. startPosition: Position._createAt( paragraph, 0 )
  474. } );
  475. walker.skip( value => value.type == 'text' );
  476. expect( walker.position.parent ).to.equal( paragraph );
  477. expect( walker.position.offset ).to.equal( 3 );
  478. } );
  479. it( 'should do not move if the condition is false', () => {
  480. const walker = new TreeWalker( {
  481. startPosition: Position._createAt( paragraph, 1 )
  482. } );
  483. walker.skip( () => false );
  484. expect( walker.position.parent ).to.equal( paragraph );
  485. expect( walker.position.offset ).to.equal( 1 );
  486. } );
  487. it( 'should move to the end if the condition is true', () => {
  488. const walker = new TreeWalker( {
  489. startPosition: Position._createAt( paragraph, 1 )
  490. } );
  491. walker.skip( () => true );
  492. expect( walker.position.parent ).to.equal( rootEnding.parent );
  493. expect( walker.position.offset ).to.equal( rootEnding.offset );
  494. } );
  495. } );
  496. describe( 'backward treewalker', () => {
  497. it( 'should jump over all text nodes', () => {
  498. const walker = new TreeWalker( {
  499. startPosition: Position._createAt( paragraph, 3 ),
  500. direction: 'backward'
  501. } );
  502. walker.skip( value => value.type == 'text' );
  503. expect( walker.position.parent ).to.equal( paragraph );
  504. expect( walker.position.offset ).to.equal( 0 );
  505. } );
  506. it( 'should do not move if the condition is false', () => {
  507. const walker = new TreeWalker( {
  508. startPosition: Position._createAt( paragraph, 1 ),
  509. direction: 'backward'
  510. } );
  511. walker.skip( () => false );
  512. expect( walker.position.parent ).to.equal( paragraph );
  513. expect( walker.position.offset ).to.equal( 1 );
  514. } );
  515. it( 'should move to the end if the condition is true', () => {
  516. const walker = new TreeWalker( {
  517. startPosition: Position._createAt( paragraph, 1 ),
  518. direction: 'backward'
  519. } );
  520. walker.skip( () => true );
  521. expect( walker.position.parent ).to.equal( rootBeginning.parent );
  522. expect( walker.position.offset ).to.equal( rootBeginning.offset );
  523. } );
  524. } );
  525. } );
  526. } );
  527. function expectValue( value, expected, options ) {
  528. expect( value.type ).to.equal( expected.type );
  529. if ( value.type == 'text' ) {
  530. expectText( value, expected, options );
  531. } else if ( value.type == 'elementStart' ) {
  532. expectStart( value, expected, options );
  533. } else if ( value.type == 'elementEnd' ) {
  534. expectEnd( value, expected, options );
  535. }
  536. }
  537. function expectText( value, expected, options = {} ) {
  538. let previousPosition, nextPosition;
  539. expect( value.item.data ).to.equal( expected.data );
  540. expect( Array.from( value.item.getAttributes() ) ).to.deep.equal( expected.attrs );
  541. expect( value.length ).to.equal( value.item.data.length );
  542. if ( options.direction == 'backward' ) {
  543. previousPosition = Position._createAfter( value.item );
  544. nextPosition = Position._createBefore( value.item );
  545. } else {
  546. previousPosition = Position._createBefore( value.item );
  547. nextPosition = Position._createAfter( value.item );
  548. }
  549. expect( value.previousPosition ).to.deep.equal( previousPosition );
  550. expect( value.nextPosition ).to.deep.equal( nextPosition );
  551. }
  552. function expectStart( value, expected, options = {} ) {
  553. let previousPosition, nextPosition;
  554. expect( value.item ).to.equal( expected.item );
  555. expect( value.length ).to.equal( 1 );
  556. if ( options.direction == 'backward' ) {
  557. previousPosition = Position._createAfter( value.item );
  558. nextPosition = Position._createBefore( value.item );
  559. } else {
  560. previousPosition = Position._createBefore( value.item );
  561. nextPosition = Position._createAt( value.item, 0 );
  562. }
  563. if ( options.shallow ) {
  564. expect( value.previousPosition ).to.deep.equal( previousPosition );
  565. } else {
  566. expect( value.nextPosition ).to.deep.equal( nextPosition );
  567. }
  568. }
  569. function expectEnd( value, expected, options = {} ) {
  570. let previousPosition, nextPosition;
  571. expect( value.item ).to.equal( expected.item );
  572. expect( value.length ).to.be.undefined;
  573. if ( options.direction == 'backward' ) {
  574. previousPosition = Position._createAfter( value.item );
  575. nextPosition = Position._createAt( value.item, value.item.maxOffset );
  576. } else {
  577. previousPosition = Position._createAt( value.item, value.item.maxOffset );
  578. nextPosition = Position._createAfter( value.item );
  579. }
  580. expect( value.previousPosition ).to.deep.equal( previousPosition );
  581. expect( value.nextPosition ).to.deep.equal( nextPosition );
  582. }