ui_utils_spec.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2020 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. var _ui_utils = require("../../web/ui_utils.js");
  24. var _util = require("../../shared/util.js");
  25. var _is_node = require("../../shared/is_node.js");
  26. describe("ui_utils", function () {
  27. describe("binary search", function () {
  28. function isTrue(boolean) {
  29. return boolean;
  30. }
  31. function isGreater3(number) {
  32. return number > 3;
  33. }
  34. it("empty array", function () {
  35. expect((0, _ui_utils.binarySearchFirstItem)([], isTrue)).toEqual(0);
  36. });
  37. it("single boolean entry", function () {
  38. expect((0, _ui_utils.binarySearchFirstItem)([false], isTrue)).toEqual(1);
  39. expect((0, _ui_utils.binarySearchFirstItem)([true], isTrue)).toEqual(0);
  40. });
  41. it("three boolean entries", function () {
  42. expect((0, _ui_utils.binarySearchFirstItem)([true, true, true], isTrue)).toEqual(0);
  43. expect((0, _ui_utils.binarySearchFirstItem)([false, true, true], isTrue)).toEqual(1);
  44. expect((0, _ui_utils.binarySearchFirstItem)([false, false, true], isTrue)).toEqual(2);
  45. expect((0, _ui_utils.binarySearchFirstItem)([false, false, false], isTrue)).toEqual(3);
  46. });
  47. it("three numeric entries", function () {
  48. expect((0, _ui_utils.binarySearchFirstItem)([0, 1, 2], isGreater3)).toEqual(3);
  49. expect((0, _ui_utils.binarySearchFirstItem)([2, 3, 4], isGreater3)).toEqual(2);
  50. expect((0, _ui_utils.binarySearchFirstItem)([4, 5, 6], isGreater3)).toEqual(0);
  51. });
  52. });
  53. describe("getPDFFileNameFromURL", function () {
  54. it("gets PDF filename", function () {
  55. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/file1.pdf")).toEqual("file1.pdf");
  56. expect((0, _ui_utils.getPDFFileNameFromURL)("http://www.example.com/pdfs/file2.pdf")).toEqual("file2.pdf");
  57. });
  58. it("gets fallback filename", function () {
  59. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/file1.txt")).toEqual("document.pdf");
  60. expect((0, _ui_utils.getPDFFileNameFromURL)("http://www.example.com/pdfs/file2.txt")).toEqual("document.pdf");
  61. });
  62. it("gets custom fallback filename", function () {
  63. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/file1.txt", "qwerty1.pdf")).toEqual("qwerty1.pdf");
  64. expect((0, _ui_utils.getPDFFileNameFromURL)("http://www.example.com/pdfs/file2.txt", "qwerty2.pdf")).toEqual("qwerty2.pdf");
  65. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/file3.txt", "")).toEqual("");
  66. });
  67. it("gets fallback filename when url is not a string", function () {
  68. expect((0, _ui_utils.getPDFFileNameFromURL)(null)).toEqual("document.pdf");
  69. expect((0, _ui_utils.getPDFFileNameFromURL)(null, "file.pdf")).toEqual("file.pdf");
  70. });
  71. it("gets PDF filename from URL containing leading/trailing whitespace", function () {
  72. expect((0, _ui_utils.getPDFFileNameFromURL)(" /pdfs/file1.pdf ")).toEqual("file1.pdf");
  73. expect((0, _ui_utils.getPDFFileNameFromURL)(" http://www.example.com/pdfs/file2.pdf ")).toEqual("file2.pdf");
  74. });
  75. it("gets PDF filename from query string", function () {
  76. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/pdfs.html?name=file1.pdf")).toEqual("file1.pdf");
  77. expect((0, _ui_utils.getPDFFileNameFromURL)("http://www.example.com/pdfs/pdf.html?file2.pdf")).toEqual("file2.pdf");
  78. });
  79. it("gets PDF filename from hash string", function () {
  80. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/pdfs.html#name=file1.pdf")).toEqual("file1.pdf");
  81. expect((0, _ui_utils.getPDFFileNameFromURL)("http://www.example.com/pdfs/pdf.html#file2.pdf")).toEqual("file2.pdf");
  82. });
  83. it("gets correct PDF filename when multiple ones are present", function () {
  84. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/file1.pdf?name=file.pdf")).toEqual("file1.pdf");
  85. expect((0, _ui_utils.getPDFFileNameFromURL)("http://www.example.com/pdfs/file2.pdf#file.pdf")).toEqual("file2.pdf");
  86. });
  87. it("gets PDF filename from URI-encoded data", function () {
  88. var encodedUrl = encodeURIComponent("http://www.example.com/pdfs/file1.pdf");
  89. expect((0, _ui_utils.getPDFFileNameFromURL)(encodedUrl)).toEqual("file1.pdf");
  90. var encodedUrlWithQuery = encodeURIComponent("http://www.example.com/pdfs/file.txt?file2.pdf");
  91. expect((0, _ui_utils.getPDFFileNameFromURL)(encodedUrlWithQuery)).toEqual("file2.pdf");
  92. });
  93. it("gets PDF filename from data mistaken for URI-encoded", function () {
  94. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/%AA.pdf")).toEqual("%AA.pdf");
  95. expect((0, _ui_utils.getPDFFileNameFromURL)("/pdfs/%2F.pdf")).toEqual("%2F.pdf");
  96. });
  97. it("gets PDF filename from (some) standard protocols", function () {
  98. expect((0, _ui_utils.getPDFFileNameFromURL)("http://www.example.com/file1.pdf")).toEqual("file1.pdf");
  99. expect((0, _ui_utils.getPDFFileNameFromURL)("https://www.example.com/file2.pdf")).toEqual("file2.pdf");
  100. expect((0, _ui_utils.getPDFFileNameFromURL)("file:///path/to/files/file3.pdf")).toEqual("file3.pdf");
  101. expect((0, _ui_utils.getPDFFileNameFromURL)("ftp://www.example.com/file4.pdf")).toEqual("file4.pdf");
  102. });
  103. it('gets PDF filename from query string appended to "blob:" URL', function () {
  104. if (_is_node.isNodeJS) {
  105. pending("Blob in not supported in Node.js.");
  106. }
  107. var typedArray = new Uint8Array([1, 2, 3, 4, 5]);
  108. var blobUrl = (0, _util.createObjectURL)(typedArray, "application/pdf");
  109. expect(blobUrl.startsWith("blob:")).toEqual(true);
  110. expect((0, _ui_utils.getPDFFileNameFromURL)(blobUrl + "?file.pdf")).toEqual("file.pdf");
  111. });
  112. it('gets fallback filename from query string appended to "data:" URL', function () {
  113. var typedArray = new Uint8Array([1, 2, 3, 4, 5]);
  114. var dataUrl = (0, _util.createObjectURL)(typedArray, "application/pdf", true);
  115. expect(dataUrl.startsWith("data:")).toEqual(true);
  116. expect((0, _ui_utils.getPDFFileNameFromURL)(dataUrl + "?file1.pdf")).toEqual("document.pdf");
  117. expect((0, _ui_utils.getPDFFileNameFromURL)(" " + dataUrl + "?file2.pdf")).toEqual("document.pdf");
  118. });
  119. });
  120. describe("EventBus", function () {
  121. it("dispatch event", function () {
  122. var eventBus = new _ui_utils.EventBus();
  123. var count = 0;
  124. eventBus.on("test", function (evt) {
  125. expect(evt).toEqual(undefined);
  126. count++;
  127. });
  128. eventBus.dispatch("test");
  129. expect(count).toEqual(1);
  130. });
  131. it("dispatch event with arguments", function () {
  132. const eventBus = new _ui_utils.EventBus();
  133. let count = 0;
  134. eventBus.on("test", function (evt) {
  135. expect(evt).toEqual({
  136. abc: 123
  137. });
  138. count++;
  139. });
  140. eventBus.dispatch("test", {
  141. abc: 123
  142. });
  143. expect(count).toEqual(1);
  144. });
  145. it("dispatch different event", function () {
  146. var eventBus = new _ui_utils.EventBus();
  147. var count = 0;
  148. eventBus.on("test", function () {
  149. count++;
  150. });
  151. eventBus.dispatch("nottest");
  152. expect(count).toEqual(0);
  153. });
  154. it("dispatch event multiple times", function () {
  155. var eventBus = new _ui_utils.EventBus();
  156. var count = 0;
  157. eventBus.dispatch("test");
  158. eventBus.on("test", function () {
  159. count++;
  160. });
  161. eventBus.dispatch("test");
  162. eventBus.dispatch("test");
  163. expect(count).toEqual(2);
  164. });
  165. it("dispatch event to multiple handlers", function () {
  166. var eventBus = new _ui_utils.EventBus();
  167. var count = 0;
  168. eventBus.on("test", function () {
  169. count++;
  170. });
  171. eventBus.on("test", function () {
  172. count++;
  173. });
  174. eventBus.dispatch("test");
  175. expect(count).toEqual(2);
  176. });
  177. it("dispatch to detached", function () {
  178. var eventBus = new _ui_utils.EventBus();
  179. var count = 0;
  180. var listener = function () {
  181. count++;
  182. };
  183. eventBus.on("test", listener);
  184. eventBus.dispatch("test");
  185. eventBus.off("test", listener);
  186. eventBus.dispatch("test");
  187. expect(count).toEqual(1);
  188. });
  189. it("dispatch to wrong detached", function () {
  190. var eventBus = new _ui_utils.EventBus();
  191. var count = 0;
  192. eventBus.on("test", function () {
  193. count++;
  194. });
  195. eventBus.dispatch("test");
  196. eventBus.off("test", function () {
  197. count++;
  198. });
  199. eventBus.dispatch("test");
  200. expect(count).toEqual(2);
  201. });
  202. it("dispatch to detached during handling", function () {
  203. var eventBus = new _ui_utils.EventBus();
  204. var count = 0;
  205. var listener1 = function () {
  206. eventBus.off("test", listener2);
  207. count++;
  208. };
  209. var listener2 = function () {
  210. eventBus.off("test", listener1);
  211. count++;
  212. };
  213. eventBus.on("test", listener1);
  214. eventBus.on("test", listener2);
  215. eventBus.dispatch("test");
  216. eventBus.dispatch("test");
  217. expect(count).toEqual(2);
  218. });
  219. it("should not, by default, re-dispatch to DOM", function (done) {
  220. if (_is_node.isNodeJS) {
  221. pending("Document in not supported in Node.js.");
  222. }
  223. const eventBus = new _ui_utils.EventBus();
  224. let count = 0;
  225. eventBus.on("test", function (evt) {
  226. expect(evt).toEqual(undefined);
  227. count++;
  228. });
  229. function domEventListener() {
  230. done.fail("shall not dispatch DOM event.");
  231. }
  232. document.addEventListener("test", domEventListener);
  233. eventBus.dispatch("test");
  234. Promise.resolve().then(() => {
  235. expect(count).toEqual(1);
  236. document.removeEventListener("test", domEventListener);
  237. done();
  238. });
  239. });
  240. it("should re-dispatch to DOM", function (done) {
  241. if (_is_node.isNodeJS) {
  242. pending("Document in not supported in Node.js.");
  243. }
  244. const eventBus = new _ui_utils.EventBus({
  245. dispatchToDOM: true
  246. });
  247. let count = 0;
  248. eventBus.on("test", function (evt) {
  249. expect(evt).toEqual(undefined);
  250. count++;
  251. });
  252. function domEventListener(evt) {
  253. expect(evt.detail).toEqual({});
  254. count++;
  255. }
  256. document.addEventListener("test", domEventListener);
  257. eventBus.dispatch("test");
  258. Promise.resolve().then(() => {
  259. expect(count).toEqual(2);
  260. document.removeEventListener("test", domEventListener);
  261. done();
  262. });
  263. });
  264. it("should re-dispatch to DOM, with arguments (without internal listeners)", function (done) {
  265. if (_is_node.isNodeJS) {
  266. pending("Document in not supported in Node.js.");
  267. }
  268. const eventBus = new _ui_utils.EventBus({
  269. dispatchToDOM: true
  270. });
  271. let count = 0;
  272. function domEventListener(evt) {
  273. expect(evt.detail).toEqual({
  274. abc: 123
  275. });
  276. count++;
  277. }
  278. document.addEventListener("test", domEventListener);
  279. eventBus.dispatch("test", {
  280. abc: 123
  281. });
  282. Promise.resolve().then(() => {
  283. expect(count).toEqual(1);
  284. document.removeEventListener("test", domEventListener);
  285. done();
  286. });
  287. });
  288. });
  289. describe("isValidRotation", function () {
  290. it("should reject non-integer angles", function () {
  291. expect((0, _ui_utils.isValidRotation)()).toEqual(false);
  292. expect((0, _ui_utils.isValidRotation)(null)).toEqual(false);
  293. expect((0, _ui_utils.isValidRotation)(NaN)).toEqual(false);
  294. expect((0, _ui_utils.isValidRotation)([90])).toEqual(false);
  295. expect((0, _ui_utils.isValidRotation)("90")).toEqual(false);
  296. expect((0, _ui_utils.isValidRotation)(90.5)).toEqual(false);
  297. });
  298. it("should reject non-multiple of 90 degree angles", function () {
  299. expect((0, _ui_utils.isValidRotation)(45)).toEqual(false);
  300. expect((0, _ui_utils.isValidRotation)(-123)).toEqual(false);
  301. });
  302. it("should accept valid angles", function () {
  303. expect((0, _ui_utils.isValidRotation)(0)).toEqual(true);
  304. expect((0, _ui_utils.isValidRotation)(90)).toEqual(true);
  305. expect((0, _ui_utils.isValidRotation)(-270)).toEqual(true);
  306. expect((0, _ui_utils.isValidRotation)(540)).toEqual(true);
  307. });
  308. });
  309. describe("isPortraitOrientation", function () {
  310. it("should be portrait orientation", function () {
  311. expect((0, _ui_utils.isPortraitOrientation)({
  312. width: 200,
  313. height: 400
  314. })).toEqual(true);
  315. expect((0, _ui_utils.isPortraitOrientation)({
  316. width: 500,
  317. height: 500
  318. })).toEqual(true);
  319. });
  320. it("should be landscape orientation", function () {
  321. expect((0, _ui_utils.isPortraitOrientation)({
  322. width: 600,
  323. height: 300
  324. })).toEqual(false);
  325. });
  326. });
  327. describe("waitOnEventOrTimeout", function () {
  328. let eventBus;
  329. beforeAll(function (done) {
  330. eventBus = new _ui_utils.EventBus();
  331. done();
  332. });
  333. afterAll(function () {
  334. eventBus = null;
  335. });
  336. it("should reject invalid parameters", function (done) {
  337. const invalidTarget = (0, _ui_utils.waitOnEventOrTimeout)({
  338. target: "window",
  339. name: "DOMContentLoaded"
  340. }).then(function () {
  341. throw new Error("Should reject invalid parameters.");
  342. }, function (reason) {
  343. expect(reason instanceof Error).toEqual(true);
  344. });
  345. const invalidName = (0, _ui_utils.waitOnEventOrTimeout)({
  346. target: eventBus,
  347. name: ""
  348. }).then(function () {
  349. throw new Error("Should reject invalid parameters.");
  350. }, function (reason) {
  351. expect(reason instanceof Error).toEqual(true);
  352. });
  353. const invalidDelay = (0, _ui_utils.waitOnEventOrTimeout)({
  354. target: eventBus,
  355. name: "pagerendered",
  356. delay: -1000
  357. }).then(function () {
  358. throw new Error("Should reject invalid parameters.");
  359. }, function (reason) {
  360. expect(reason instanceof Error).toEqual(true);
  361. });
  362. Promise.all([invalidTarget, invalidName, invalidDelay]).then(done, done.fail);
  363. });
  364. it("should resolve on event, using the DOM", function (done) {
  365. if (_is_node.isNodeJS) {
  366. pending("Document in not supported in Node.js.");
  367. }
  368. const button = document.createElement("button");
  369. const buttonClicked = (0, _ui_utils.waitOnEventOrTimeout)({
  370. target: button,
  371. name: "click",
  372. delay: 10000
  373. });
  374. button.click();
  375. buttonClicked.then(function (type) {
  376. expect(type).toEqual(_ui_utils.WaitOnType.EVENT);
  377. done();
  378. }, done.fail);
  379. });
  380. it("should resolve on timeout, using the DOM", function (done) {
  381. if (_is_node.isNodeJS) {
  382. pending("Document in not supported in Node.js.");
  383. }
  384. const button = document.createElement("button");
  385. const buttonClicked = (0, _ui_utils.waitOnEventOrTimeout)({
  386. target: button,
  387. name: "click",
  388. delay: 10
  389. });
  390. buttonClicked.then(function (type) {
  391. expect(type).toEqual(_ui_utils.WaitOnType.TIMEOUT);
  392. done();
  393. }, done.fail);
  394. });
  395. it("should resolve on event, using the EventBus", function (done) {
  396. const pageRendered = (0, _ui_utils.waitOnEventOrTimeout)({
  397. target: eventBus,
  398. name: "pagerendered",
  399. delay: 10000
  400. });
  401. eventBus.dispatch("pagerendered");
  402. pageRendered.then(function (type) {
  403. expect(type).toEqual(_ui_utils.WaitOnType.EVENT);
  404. done();
  405. }, done.fail);
  406. });
  407. it("should resolve on timeout, using the EventBus", function (done) {
  408. const pageRendered = (0, _ui_utils.waitOnEventOrTimeout)({
  409. target: eventBus,
  410. name: "pagerendered",
  411. delay: 10
  412. });
  413. pageRendered.then(function (type) {
  414. expect(type).toEqual(_ui_utils.WaitOnType.TIMEOUT);
  415. done();
  416. }, done.fail);
  417. });
  418. });
  419. describe("getPageSizeInches", function () {
  420. it("gets page size (in inches)", function () {
  421. const page = {
  422. view: [0, 0, 595.28, 841.89],
  423. userUnit: 1.0,
  424. rotate: 0
  425. };
  426. const {
  427. width,
  428. height
  429. } = (0, _ui_utils.getPageSizeInches)(page);
  430. expect(+width.toPrecision(3)).toEqual(8.27);
  431. expect(+height.toPrecision(4)).toEqual(11.69);
  432. });
  433. it("gets page size (in inches), for non-default /Rotate entry", function () {
  434. const pdfPage1 = {
  435. view: [0, 0, 612, 792],
  436. userUnit: 1,
  437. rotate: 0
  438. };
  439. const {
  440. width: width1,
  441. height: height1
  442. } = (0, _ui_utils.getPageSizeInches)(pdfPage1);
  443. expect(width1).toEqual(8.5);
  444. expect(height1).toEqual(11);
  445. const pdfPage2 = {
  446. view: [0, 0, 612, 792],
  447. userUnit: 1,
  448. rotate: 90
  449. };
  450. const {
  451. width: width2,
  452. height: height2
  453. } = (0, _ui_utils.getPageSizeInches)(pdfPage2);
  454. expect(width2).toEqual(11);
  455. expect(height2).toEqual(8.5);
  456. });
  457. });
  458. describe("getVisibleElements", function () {
  459. const BORDER_WIDTH = 9;
  460. const SPACING = 2 * BORDER_WIDTH - 7;
  461. function makePages(lines) {
  462. const result = [];
  463. let lineTop = 0,
  464. id = 0;
  465. for (const line of lines) {
  466. const lineHeight = line.reduce(function (maxHeight, pair) {
  467. return Math.max(maxHeight, pair[1]);
  468. }, 0);
  469. let offsetLeft = -BORDER_WIDTH;
  470. for (const [clientWidth, clientHeight] of line) {
  471. const offsetTop = lineTop + (lineHeight - clientHeight) / 2 - BORDER_WIDTH;
  472. const div = {
  473. offsetLeft,
  474. offsetTop,
  475. clientWidth,
  476. clientHeight,
  477. clientLeft: BORDER_WIDTH,
  478. clientTop: BORDER_WIDTH
  479. };
  480. result.push({
  481. id,
  482. div
  483. });
  484. ++id;
  485. offsetLeft += clientWidth + SPACING;
  486. }
  487. lineTop += lineHeight + SPACING;
  488. }
  489. return result;
  490. }
  491. function slowGetVisibleElements(scroll, pages) {
  492. const views = [];
  493. const {
  494. scrollLeft,
  495. scrollTop
  496. } = scroll;
  497. const scrollRight = scrollLeft + scroll.clientWidth;
  498. const scrollBottom = scrollTop + scroll.clientHeight;
  499. for (const view of pages) {
  500. const {
  501. div
  502. } = view;
  503. const viewLeft = div.offsetLeft + div.clientLeft;
  504. const viewRight = viewLeft + div.clientWidth;
  505. const viewTop = div.offsetTop + div.clientTop;
  506. const viewBottom = viewTop + div.clientHeight;
  507. if (viewLeft < scrollRight && viewRight > scrollLeft && viewTop < scrollBottom && viewBottom > scrollTop) {
  508. const hiddenHeight = Math.max(0, scrollTop - viewTop) + Math.max(0, viewBottom - scrollBottom);
  509. const hiddenWidth = Math.max(0, scrollLeft - viewLeft) + Math.max(0, viewRight - scrollRight);
  510. const visibleArea = (div.clientHeight - hiddenHeight) * (div.clientWidth - hiddenWidth);
  511. const percent = visibleArea * 100 / div.clientHeight / div.clientWidth | 0;
  512. views.push({
  513. id: view.id,
  514. x: viewLeft,
  515. y: viewTop,
  516. view,
  517. percent
  518. });
  519. }
  520. }
  521. return {
  522. first: views[0],
  523. last: views[views.length - 1],
  524. views
  525. };
  526. }
  527. function scrollOverDocument(pages, horizontally = false) {
  528. const size = pages.reduce(function (max, {
  529. div
  530. }) {
  531. return Math.max(max, horizontally ? div.offsetLeft + div.clientLeft + div.clientWidth : div.offsetTop + div.clientTop + div.clientHeight);
  532. }, 0);
  533. for (let i = 0; i < size; i += 7) {
  534. for (let j = i + 5; j < size; j += j - i) {
  535. const scroll = horizontally ? {
  536. scrollTop: 0,
  537. scrollLeft: i,
  538. clientHeight: 10000,
  539. clientWidth: j - i
  540. } : {
  541. scrollTop: i,
  542. scrollLeft: 0,
  543. clientHeight: j - i,
  544. clientWidth: 10000
  545. };
  546. expect((0, _ui_utils.getVisibleElements)(scroll, pages, false, horizontally)).toEqual(slowGetVisibleElements(scroll, pages));
  547. }
  548. }
  549. }
  550. it("with pages of varying height", function () {
  551. const pages = makePages([[[50, 20], [20, 50]], [[30, 12], [12, 30]], [[20, 50], [50, 20]], [[50, 20], [20, 50]]]);
  552. scrollOverDocument(pages);
  553. });
  554. it("widescreen challenge", function () {
  555. const pages = makePages([[[10, 50], [10, 60], [10, 70], [10, 80], [10, 90]], [[10, 90], [10, 80], [10, 70], [10, 60], [10, 50]], [[10, 50], [10, 60], [10, 70], [10, 80], [10, 90]]]);
  556. scrollOverDocument(pages);
  557. });
  558. it("works with horizontal scrolling", function () {
  559. const pages = makePages([[[10, 50], [20, 20], [30, 10]]]);
  560. scrollOverDocument(pages, true);
  561. });
  562. it("handles `sortByVisibility` correctly", function () {
  563. const scrollEl = {
  564. scrollTop: 75,
  565. scrollLeft: 0,
  566. clientHeight: 750,
  567. clientWidth: 1500
  568. };
  569. const views = makePages([[[100, 150]], [[100, 150]], [[100, 150]]]);
  570. const visible = (0, _ui_utils.getVisibleElements)(scrollEl, views);
  571. const visibleSorted = (0, _ui_utils.getVisibleElements)(scrollEl, views, true);
  572. const viewsOrder = [],
  573. viewsSortedOrder = [];
  574. for (const view of visible.views) {
  575. viewsOrder.push(view.id);
  576. }
  577. for (const view of visibleSorted.views) {
  578. viewsSortedOrder.push(view.id);
  579. }
  580. expect(viewsOrder).toEqual([0, 1, 2]);
  581. expect(viewsSortedOrder).toEqual([1, 2, 0]);
  582. });
  583. it("handles views being empty", function () {
  584. const scrollEl = {
  585. scrollTop: 10,
  586. scrollLeft: 0,
  587. clientHeight: 750,
  588. clientWidth: 1500
  589. };
  590. const views = [];
  591. expect((0, _ui_utils.getVisibleElements)(scrollEl, views)).toEqual({
  592. first: undefined,
  593. last: undefined,
  594. views: []
  595. });
  596. });
  597. it("handles all views being hidden (without errors)", function () {
  598. const scrollEl = {
  599. scrollTop: 100000,
  600. scrollLeft: 0,
  601. clientHeight: 750,
  602. clientWidth: 1500
  603. };
  604. const views = makePages([[[100, 150]], [[100, 150]], [[100, 150]]]);
  605. expect((0, _ui_utils.getVisibleElements)(scrollEl, views)).toEqual({
  606. first: undefined,
  607. last: undefined,
  608. views: []
  609. });
  610. });
  611. describe("backtrackBeforeAllVisibleElements", function () {
  612. const tallPage = [10, 50];
  613. const shortPage = [10, 10];
  614. const top1 = 20 + SPACING + 40;
  615. const top2 = 20 + SPACING + 10;
  616. it("handles case 1", function () {
  617. const pages = makePages([[[10, 20], [10, 20], [10, 20], [10, 20]], [tallPage, shortPage, tallPage, shortPage], [[10, 50], [10, 50], [10, 50], [10, 50]], [[10, 20], [10, 20], [10, 20], [10, 20]], [[10, 20]]]);
  618. const bsResult = 4;
  619. expect((0, _ui_utils.backtrackBeforeAllVisibleElements)(bsResult, pages, top1)).toEqual(4);
  620. });
  621. it("handles case 2", function () {
  622. const pages = makePages([[[10, 20], [10, 20], [10, 20], [10, 20]], [tallPage, shortPage, tallPage, tallPage], [[10, 50], [10, 50], [10, 50], [10, 50]], [[10, 20], [10, 20], [10, 20], [10, 20]]]);
  623. const bsResult = 6;
  624. expect((0, _ui_utils.backtrackBeforeAllVisibleElements)(bsResult, pages, top1)).toEqual(4);
  625. });
  626. it("handles case 3", function () {
  627. const pages = makePages([[[10, 20], [10, 20], [10, 20], [10, 20]], [tallPage, shortPage, tallPage, shortPage], [[10, 50], [10, 50], [10, 50], [10, 50]], [[10, 20], [10, 20], [10, 20], [10, 20]]]);
  628. const bsResult = 8;
  629. expect((0, _ui_utils.backtrackBeforeAllVisibleElements)(bsResult, pages, top1)).toEqual(4);
  630. });
  631. it("handles case 4", function () {
  632. const pages = makePages([[[10, 20], [10, 20], [10, 20], [10, 20]], [tallPage, shortPage, tallPage, shortPage], [[10, 50], [10, 50], [10, 50], [10, 50]], [[10, 20], [10, 20], [10, 20], [10, 20]]]);
  633. const bsResult = 4;
  634. expect((0, _ui_utils.backtrackBeforeAllVisibleElements)(bsResult, pages, top2)).toEqual(4);
  635. });
  636. });
  637. });
  638. describe("moveToEndOfArray", function () {
  639. it("works on empty arrays", function () {
  640. const data = [];
  641. (0, _ui_utils.moveToEndOfArray)(data, function () {});
  642. expect(data).toEqual([]);
  643. });
  644. it("works when moving everything", function () {
  645. const data = [1, 2, 3, 4, 5];
  646. (0, _ui_utils.moveToEndOfArray)(data, function () {
  647. return true;
  648. });
  649. expect(data).toEqual([1, 2, 3, 4, 5]);
  650. });
  651. it("works when moving some things", function () {
  652. const data = [1, 2, 3, 4, 5];
  653. (0, _ui_utils.moveToEndOfArray)(data, function (x) {
  654. return x % 2 === 0;
  655. });
  656. expect(data).toEqual([1, 3, 5, 2, 4]);
  657. });
  658. it("works when moving one thing", function () {
  659. const data = [1, 2, 3, 4, 5];
  660. (0, _ui_utils.moveToEndOfArray)(data, function (x) {
  661. return x === 1;
  662. });
  663. expect(data).toEqual([2, 3, 4, 5, 1]);
  664. });
  665. it("works when moving nothing", function () {
  666. const data = [1, 2, 3, 4, 5];
  667. (0, _ui_utils.moveToEndOfArray)(data, function (x) {
  668. return x === 0;
  669. });
  670. expect(data).toEqual([1, 2, 3, 4, 5]);
  671. });
  672. });
  673. });