8
0

rect.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* global document */
  6. import global from '../../src/dom/global';
  7. import Rect from '../../src/dom/rect';
  8. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  9. testUtils.createSinonSandbox();
  10. describe( 'Rect', () => {
  11. let geometry;
  12. beforeEach( () => {
  13. geometry = {
  14. top: 10,
  15. right: 40,
  16. bottom: 30,
  17. left: 20,
  18. width: 20,
  19. height: 20
  20. };
  21. } );
  22. describe( 'constructor()', () => {
  23. it( 'should store passed object in #_source property', () => {
  24. const obj = {};
  25. const rect = new Rect( obj );
  26. expect( rect._source ).to.equal( obj );
  27. } );
  28. it( 'should accept HTMLElement', () => {
  29. const element = document.createElement( 'div' );
  30. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
  31. assertRect( new Rect( element ), geometry );
  32. } );
  33. it( 'should accept HTMLElement (excludeScrollbarsAndBorders)', () => {
  34. const element = document.createElement( 'div' );
  35. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
  36. testUtils.sinon.stub( global.window, 'getComputedStyle' ).returns( {
  37. borderTopWidth: '5px',
  38. borderRightWidth: '10px',
  39. borderLeftWidth: '5px',
  40. borderBottomWidth: '10px'
  41. } );
  42. // Simulate 5px srollbars.
  43. Object.defineProperties( element, {
  44. offsetWidth: {
  45. value: 20
  46. },
  47. offsetHeight: {
  48. value: 20
  49. },
  50. clientWidth: {
  51. value: 10
  52. },
  53. clientHeight: {
  54. value: 10
  55. }
  56. } );
  57. assertRect( new Rect( element, { excludeScrollbarsAndBorders: true } ), {
  58. top: 15,
  59. right: 35,
  60. bottom: 25,
  61. left: 25,
  62. width: 10,
  63. height: 10
  64. } );
  65. } );
  66. it( 'should accept Range (non–collapsed)', () => {
  67. const range = document.createRange();
  68. range.selectNode( document.body );
  69. testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
  70. assertRect( new Rect( range ), geometry );
  71. } );
  72. // https://github.com/ckeditor/ckeditor5-utils/issues/153
  73. it( 'should accept Range (collapsed)', () => {
  74. const range = document.createRange();
  75. range.collapse();
  76. testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
  77. assertRect( new Rect( range ), geometry );
  78. } );
  79. // https://github.com/ckeditor/ckeditor5-utils/issues/153
  80. it( 'should accept Range (collapsed, no Range rects available)', () => {
  81. const range = document.createRange();
  82. const element = document.createElement( 'div' );
  83. range.setStart( element, 0 );
  84. range.collapse();
  85. testUtils.sinon.stub( range, 'getClientRects' ).returns( [] );
  86. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
  87. const expectedGeometry = Object.assign( {}, geometry );
  88. expectedGeometry.right = expectedGeometry.left;
  89. expectedGeometry.width = 0;
  90. assertRect( new Rect( range ), expectedGeometry );
  91. } );
  92. it( 'should accept Rect', () => {
  93. const sourceRect = new Rect( geometry );
  94. const rect = new Rect( sourceRect );
  95. expect( rect ).to.not.equal( sourceRect );
  96. assertRect( rect, geometry );
  97. } );
  98. it( 'should accept ClientRect', () => {
  99. const clientRect = document.body.getBoundingClientRect();
  100. const { top, right, bottom, left, width, height } = clientRect;
  101. const rect = new Rect( clientRect );
  102. assertRect( rect, { top, right, bottom, left, width, height } );
  103. } );
  104. it( 'should accept geometry object', () => {
  105. assertRect( new Rect( geometry ), geometry );
  106. } );
  107. it( 'should copy the properties (Rect)', () => {
  108. const sourceGeometry = Object.assign( {}, geometry );
  109. const sourceRect = new Rect( geometry );
  110. const rect = new Rect( sourceRect );
  111. assertRect( rect, geometry );
  112. rect.top = 100;
  113. rect.width = 200;
  114. assertRect( sourceRect, sourceGeometry );
  115. } );
  116. it( 'should copy the properties (geomerty object)', () => {
  117. const sourceGeometry = Object.assign( {}, geometry );
  118. const rect = new Rect( geometry );
  119. assertRect( rect, geometry );
  120. rect.top = 100;
  121. rect.width = 200;
  122. assertRect( geometry, sourceGeometry );
  123. } );
  124. } );
  125. describe( 'clone()', () => {
  126. it( 'should clone the source rect', () => {
  127. const rect = new Rect( geometry );
  128. const clone = rect.clone();
  129. expect( clone ).to.be.instanceOf( Rect );
  130. expect( clone ).not.equal( rect );
  131. assertRect( clone, rect );
  132. } );
  133. it( 'should preserve #_source', () => {
  134. const rect = new Rect( geometry );
  135. const clone = rect.clone();
  136. expect( clone._source ).to.equal( rect._source );
  137. assertRect( clone, rect );
  138. } );
  139. } );
  140. describe( 'moveTo()', () => {
  141. it( 'should return the source rect', () => {
  142. const rect = new Rect( geometry );
  143. const returned = rect.moveTo( 100, 200 );
  144. expect( returned ).to.equal( rect );
  145. } );
  146. it( 'should move the rect', () => {
  147. const rect = new Rect( geometry );
  148. rect.moveTo( 100, 200 );
  149. assertRect( rect, {
  150. top: 200,
  151. right: 120,
  152. bottom: 220,
  153. left: 100,
  154. width: 20,
  155. height: 20
  156. } );
  157. } );
  158. } );
  159. describe( 'moveBy()', () => {
  160. it( 'should return the source rect', () => {
  161. const rect = new Rect( geometry );
  162. const returned = rect.moveBy( 100, 200 );
  163. expect( returned ).to.equal( rect );
  164. } );
  165. it( 'should move the rect', () => {
  166. const rect = new Rect( geometry );
  167. rect.moveBy( 100, 200 );
  168. assertRect( rect, {
  169. top: 210,
  170. right: 140,
  171. bottom: 230,
  172. left: 120,
  173. width: 20,
  174. height: 20
  175. } );
  176. } );
  177. } );
  178. describe( 'getIntersection()', () => {
  179. it( 'should return a new rect', () => {
  180. const rect = new Rect( geometry );
  181. const insersect = rect.getIntersection( new Rect( geometry ) );
  182. expect( insersect ).to.be.instanceOf( Rect );
  183. expect( insersect ).to.not.equal( rect );
  184. } );
  185. it( 'should calculate the geometry (#1)', () => {
  186. const rectA = new Rect( {
  187. top: 0,
  188. right: 100,
  189. bottom: 100,
  190. left: 0,
  191. width: 100,
  192. height: 100
  193. } );
  194. const rectB = new Rect( {
  195. top: 50,
  196. right: 150,
  197. bottom: 150,
  198. left: 50,
  199. width: 100,
  200. height: 100
  201. } );
  202. const insersect = rectA.getIntersection( rectB );
  203. assertRect( insersect, {
  204. top: 50,
  205. right: 100,
  206. bottom: 100,
  207. left: 50,
  208. width: 50,
  209. height: 50
  210. } );
  211. } );
  212. it( 'should calculate the geometry (#2)', () => {
  213. const rectA = new Rect( {
  214. top: 0,
  215. right: 100,
  216. bottom: 100,
  217. left: 0,
  218. width: 100,
  219. height: 100
  220. } );
  221. const rectB = new Rect( {
  222. top: 0,
  223. right: 200,
  224. bottom: 100,
  225. left: 100,
  226. width: 100,
  227. height: 100
  228. } );
  229. const insersect = rectA.getIntersection( rectB );
  230. assertRect( insersect, {
  231. top: 0,
  232. right: 100,
  233. bottom: 100,
  234. left: 100,
  235. width: 0,
  236. height: 100
  237. } );
  238. } );
  239. it( 'should calculate the geometry (#3)', () => {
  240. const rectA = new Rect( {
  241. top: 0,
  242. right: 100,
  243. bottom: 100,
  244. left: 0,
  245. width: 100,
  246. height: 100
  247. } );
  248. const rectB = new Rect( {
  249. top: 100,
  250. right: 300,
  251. bottom: 200,
  252. left: 200,
  253. width: 100,
  254. height: 100
  255. } );
  256. expect( rectA.getIntersection( rectB ) ).to.be.null;
  257. } );
  258. } );
  259. describe( 'getIntersectionArea()', () => {
  260. it( 'should calculate the area (#1)', () => {
  261. const rectA = new Rect( {
  262. top: 0,
  263. right: 100,
  264. bottom: 100,
  265. left: 0,
  266. width: 100,
  267. height: 100
  268. } );
  269. const rectB = new Rect( {
  270. top: 50,
  271. right: 150,
  272. bottom: 150,
  273. left: 50,
  274. width: 100,
  275. height: 100
  276. } );
  277. expect( rectA.getIntersectionArea( rectB ) ).to.equal( 2500 );
  278. } );
  279. it( 'should calculate the area (#2)', () => {
  280. const rectA = new Rect( {
  281. top: 0,
  282. right: 100,
  283. bottom: 100,
  284. left: 0,
  285. width: 100,
  286. height: 100
  287. } );
  288. const rectB = new Rect( {
  289. top: 0,
  290. right: 200,
  291. bottom: 100,
  292. left: 100,
  293. width: 100,
  294. height: 100
  295. } );
  296. expect( rectA.getIntersectionArea( rectB ) ).to.equal( 0 );
  297. } );
  298. it( 'should calculate the area (#3)', () => {
  299. const rectA = new Rect( {
  300. top: 0,
  301. right: 100,
  302. bottom: 100,
  303. left: 0,
  304. width: 100,
  305. height: 100
  306. } );
  307. const rectB = new Rect( {
  308. top: 100,
  309. right: 300,
  310. bottom: 200,
  311. left: 200,
  312. width: 100,
  313. height: 100
  314. } );
  315. expect( rectA.getIntersectionArea( rectB ) ).to.equal( 0 );
  316. } );
  317. } );
  318. describe( 'getArea()', () => {
  319. it( 'should calculate the area', () => {
  320. const rect = new Rect( {
  321. width: 100,
  322. height: 50
  323. } );
  324. expect( rect.getArea() ).to.equal( 5000 );
  325. } );
  326. } );
  327. describe( 'getVisible()', () => {
  328. let element, range, ancestorA, ancestorB;
  329. beforeEach( () => {
  330. element = document.createElement( 'div' );
  331. range = document.createRange();
  332. ancestorA = document.createElement( 'div' );
  333. ancestorB = document.createElement( 'div' );
  334. ancestorA.append( element );
  335. document.body.appendChild( ancestorA );
  336. } );
  337. afterEach( () => {
  338. ancestorA.remove();
  339. ancestorB.remove();
  340. } );
  341. it( 'should return a new rect', () => {
  342. const rect = new Rect( {} );
  343. const visible = rect.getVisible();
  344. expect( visible ).to.not.equal( rect );
  345. } );
  346. it( 'should not fail when the rect is for document#body', () => {
  347. testUtils.sinon.stub( document.body, 'getBoundingClientRect' ).returns( {
  348. top: 0,
  349. right: 100,
  350. bottom: 100,
  351. left: 0,
  352. width: 100,
  353. height: 100
  354. } );
  355. assertRect( new Rect( document.body ).getVisible(), {
  356. top: 0,
  357. right: 100,
  358. bottom: 100,
  359. left: 0,
  360. width: 100,
  361. height: 100
  362. } );
  363. } );
  364. it( 'should return the visible rect (HTMLElement), partially cropped', () => {
  365. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
  366. top: 0,
  367. right: 100,
  368. bottom: 100,
  369. left: 0,
  370. width: 100,
  371. height: 100
  372. } );
  373. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  374. top: 50,
  375. right: 150,
  376. bottom: 150,
  377. left: 50,
  378. width: 100,
  379. height: 100
  380. } );
  381. assertRect( new Rect( element ).getVisible(), {
  382. top: 50,
  383. right: 100,
  384. bottom: 100,
  385. left: 50,
  386. width: 50,
  387. height: 50
  388. } );
  389. } );
  390. it( 'should return the visible rect (HTMLElement), fully visible', () => {
  391. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
  392. top: 0,
  393. right: 100,
  394. bottom: 100,
  395. left: 0,
  396. width: 100,
  397. height: 100
  398. } );
  399. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  400. top: 0,
  401. right: 150,
  402. bottom: 150,
  403. left: 0,
  404. width: 150,
  405. height: 150
  406. } );
  407. assertRect( new Rect( element ).getVisible(), {
  408. top: 0,
  409. right: 100,
  410. bottom: 100,
  411. left: 0,
  412. width: 100,
  413. height: 100
  414. } );
  415. } );
  416. it( 'should return the visible rect (HTMLElement), partially cropped, deep ancestor overflow', () => {
  417. ancestorB.append( ancestorA );
  418. document.body.appendChild( ancestorB );
  419. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
  420. top: 0,
  421. right: 100,
  422. bottom: 100,
  423. left: 0,
  424. width: 100,
  425. height: 100
  426. } );
  427. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  428. top: 50,
  429. right: 100,
  430. bottom: 100,
  431. left: 0,
  432. width: 50,
  433. height: 50
  434. } );
  435. testUtils.sinon.stub( ancestorB, 'getBoundingClientRect' ).returns( {
  436. top: 0,
  437. right: 150,
  438. bottom: 100,
  439. left: 50,
  440. width: 100,
  441. height: 100
  442. } );
  443. assertRect( new Rect( element ).getVisible(), {
  444. top: 50,
  445. right: 100,
  446. bottom: 100,
  447. left: 50,
  448. width: 50,
  449. height: 50
  450. } );
  451. } );
  452. it( 'should return the visible rect (Range), partially cropped', () => {
  453. range.setStart( ancestorA, 0 );
  454. range.setEnd( ancestorA, 1 );
  455. testUtils.sinon.stub( range, 'getClientRects' ).returns( [ {
  456. top: 0,
  457. right: 100,
  458. bottom: 100,
  459. left: 0,
  460. width: 100,
  461. height: 100
  462. } ] );
  463. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  464. top: 50,
  465. right: 150,
  466. bottom: 150,
  467. left: 50,
  468. width: 100,
  469. height: 100
  470. } );
  471. assertRect( new Rect( range ).getVisible(), {
  472. top: 50,
  473. right: 100,
  474. bottom: 100,
  475. left: 50,
  476. width: 50,
  477. height: 50
  478. } );
  479. } );
  480. it( 'should return null if there\'s no visible rect', () => {
  481. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
  482. top: 0,
  483. right: 100,
  484. bottom: 100,
  485. left: 0,
  486. width: 100,
  487. height: 100
  488. } );
  489. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  490. top: 150,
  491. right: 200,
  492. bottom: 200,
  493. left: 150,
  494. width: 50,
  495. height: 50
  496. } );
  497. expect( new Rect( element ).getVisible() ).to.equal( null );
  498. } );
  499. } );
  500. describe( 'isEqual()', () => {
  501. it( 'returns `true` when rects are equal', () => {
  502. const rectA = new Rect( {
  503. top: 10,
  504. right: 20,
  505. bottom: 30,
  506. left: 40,
  507. width: 10,
  508. height: 20
  509. } );
  510. const rectB = new Rect( {
  511. top: 10,
  512. right: 20,
  513. bottom: 30,
  514. left: 40,
  515. width: 10,
  516. height: 20
  517. } );
  518. expect( rectA.isEqual( rectB ) ).to.be.true;
  519. expect( rectB.isEqual( rectA ) ).to.be.true;
  520. expect( rectA.isEqual( rectA ) ).to.be.true;
  521. } );
  522. it( 'returns `false` when rects are different', () => {
  523. const rectA = new Rect( {
  524. top: 10,
  525. right: 20,
  526. bottom: 30,
  527. left: 40,
  528. width: 10,
  529. height: 20
  530. } );
  531. const rectB = new Rect( {
  532. top: 10,
  533. right: 20,
  534. bottom: 30,
  535. left: 40,
  536. width: 10,
  537. height: 30 // !
  538. } );
  539. expect( rectA.isEqual( rectB ) ).to.be.false;
  540. expect( rectB.isEqual( rectA ) ).to.be.false;
  541. } );
  542. } );
  543. describe( 'contains()', () => {
  544. it( 'should return true if rects are the same', () => {
  545. const rectA = new Rect( {
  546. top: 0,
  547. right: 100,
  548. bottom: 100,
  549. left: 0,
  550. width: 100,
  551. height: 100
  552. } );
  553. const rectB = new Rect( {
  554. top: 0,
  555. right: 100,
  556. bottom: 100,
  557. left: 0,
  558. width: 100,
  559. height: 100
  560. } );
  561. expect( rectA.isEqual( rectB ) ).to.be.true;
  562. expect( rectA.contains( rectB ) ).to.be.true;
  563. expect( rectB.contains( rectA ) ).to.be.true;
  564. } );
  565. it( 'should return true if rect is within', () => {
  566. const rectA = new Rect( {
  567. top: 0,
  568. right: 100,
  569. bottom: 100,
  570. left: 0,
  571. width: 100,
  572. height: 100
  573. } );
  574. const rectB = new Rect( {
  575. top: 10,
  576. right: 90,
  577. bottom: 90,
  578. left: 10,
  579. width: 80,
  580. height: 80
  581. } );
  582. expect( rectA.contains( rectB ) ).to.be.true;
  583. expect( rectB.contains( rectA ) ).to.be.false;
  584. } );
  585. it( 'should return false if rect extends beyond the boundaries', () => {
  586. const rectA = new Rect( {
  587. top: 0,
  588. right: 100,
  589. bottom: 100,
  590. left: 0,
  591. width: 100,
  592. height: 100
  593. } );
  594. const rectB = new Rect( {
  595. top: 10,
  596. right: 100,
  597. bottom: 110,
  598. left: 0,
  599. width: 100,
  600. height: 100
  601. } );
  602. expect( rectA.contains( rectB ) ).to.be.false;
  603. expect( rectB.contains( rectA ) ).to.be.false;
  604. } );
  605. it( 'should return false if rect is completely beyond the boundaries', () => {
  606. const rectA = new Rect( {
  607. top: 0,
  608. right: 100,
  609. bottom: 100,
  610. left: 0,
  611. width: 100,
  612. height: 100
  613. } );
  614. const rectB = new Rect( {
  615. top: 110,
  616. right: 100,
  617. bottom: 210,
  618. left: 0,
  619. width: 100,
  620. height: 100
  621. } );
  622. expect( rectA.contains( rectB ) ).to.be.false;
  623. expect( rectB.contains( rectA ) ).to.be.false;
  624. } );
  625. } );
  626. describe( 'getViewportRect()', () => {
  627. it( 'should reaturn a rect', () => {
  628. expect( Rect.getViewportRect() ).to.be.instanceOf( Rect );
  629. } );
  630. it( 'should return the viewport\'s rect', () => {
  631. testUtils.sinon.stub( global, 'window', {
  632. innerWidth: 1000,
  633. innerHeight: 500,
  634. scrollX: 100,
  635. scrollY: 200
  636. } );
  637. assertRect( Rect.getViewportRect(), {
  638. top: 0,
  639. right: 1000,
  640. bottom: 500,
  641. left: 0,
  642. width: 1000,
  643. height: 500
  644. } );
  645. } );
  646. describe( 'excludeScrollbars', () => {
  647. it( 'should exclude scrollbars from viewport\'s rect', () => {
  648. testUtils.sinon.stub( global, 'window', {
  649. innerWidth: 1000,
  650. innerHeight: 500,
  651. scrollX: 100,
  652. scrollY: 200
  653. } );
  654. testUtils.sinon.stub( global, 'document', {
  655. documentElement: {
  656. clientWidth: 990,
  657. clientHeight: 490
  658. }
  659. } );
  660. assertRect( Rect.getViewportRect( { excludeScrollbars: true } ), {
  661. top: 0,
  662. right: 990,
  663. bottom: 490,
  664. left: 0,
  665. width: 990,
  666. height: 490
  667. } );
  668. } );
  669. } );
  670. } );
  671. describe( 'getDomRangeRects() ', () => {
  672. it( 'should return rects for a Range (non–collapsed)', () => {
  673. const range = document.createRange();
  674. range.selectNode( document.body );
  675. testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
  676. const rects = Rect.getDomRangeRects( range );
  677. expect( rects ).to.have.length( 1 );
  678. assertRect( rects[ 0 ], geometry );
  679. } );
  680. // https://github.com/ckeditor/ckeditor5-utils/issues/153
  681. it( 'should return rects for a Range (collapsed)', () => {
  682. const range = document.createRange();
  683. const secondGeometry = { top: 20, right: 80, bottom: 60, left: 40, width: 40, height: 40 };
  684. range.collapse();
  685. testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry, secondGeometry ] );
  686. const rects = Rect.getDomRangeRects( range );
  687. expect( rects ).to.have.length( 2 );
  688. assertRect( rects[ 0 ], geometry );
  689. assertRect( rects[ 1 ], secondGeometry );
  690. } );
  691. // https://github.com/ckeditor/ckeditor5-utils/issues/153
  692. it( 'should return rects for a Range (collapsed, no Range rects available)', () => {
  693. const range = document.createRange();
  694. const element = document.createElement( 'div' );
  695. range.setStart( element, 0 );
  696. range.collapse();
  697. testUtils.sinon.stub( range, 'getClientRects' ).returns( [] );
  698. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
  699. const expectedGeometry = Object.assign( {}, geometry );
  700. expectedGeometry.right = expectedGeometry.left;
  701. expectedGeometry.width = 0;
  702. const rects = Rect.getDomRangeRects( range );
  703. expect( rects ).to.have.length( 1 );
  704. assertRect( rects[ 0 ], expectedGeometry );
  705. } );
  706. } );
  707. } );
  708. function assertRect( rect, expected ) {
  709. expect( rect ).to.deep.equal( expected );
  710. }