2
0

pdf_find_controller_spec.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * JavaScript code in this page
  4. *
  5. * Copyright 2022 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 _test_utils = require("./test_utils.js");
  24. var _event_utils = require("../../web/event_utils.js");
  25. var _api = require("../../display/api.js");
  26. var _is_node = require("../../shared/is_node.js");
  27. var _pdf_find_controller = require("../../web/pdf_find_controller.js");
  28. var _pdf_link_service = require("../../web/pdf_link_service.js");
  29. const tracemonkeyFileName = "tracemonkey.pdf";
  30. const CMAP_PARAMS = {
  31. cMapUrl: _is_node.isNodeJS ? "./external/bcmaps/" : "../../../external/bcmaps/",
  32. cMapPacked: true
  33. };
  34. class MockLinkService extends _pdf_link_service.SimpleLinkService {
  35. constructor() {
  36. super();
  37. this._page = 1;
  38. this._pdfDocument = null;
  39. }
  40. setDocument(pdfDocument) {
  41. this._pdfDocument = pdfDocument;
  42. }
  43. get pagesCount() {
  44. return this._pdfDocument.numPages;
  45. }
  46. get page() {
  47. return this._page;
  48. }
  49. set page(value) {
  50. this._page = value;
  51. }
  52. }
  53. async function initPdfFindController(filename) {
  54. const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename || tracemonkeyFileName, {
  55. ...CMAP_PARAMS
  56. }));
  57. const pdfDocument = await loadingTask.promise;
  58. const eventBus = new _event_utils.EventBus();
  59. const linkService = new MockLinkService();
  60. linkService.setDocument(pdfDocument);
  61. const pdfFindController = new _pdf_find_controller.PDFFindController({
  62. linkService,
  63. eventBus
  64. });
  65. pdfFindController.setDocument(pdfDocument);
  66. return {
  67. eventBus,
  68. pdfFindController
  69. };
  70. }
  71. function testSearch({
  72. eventBus,
  73. pdfFindController,
  74. state,
  75. matchesPerPage,
  76. selectedMatch,
  77. pageMatches = null,
  78. pageMatchesLength = null
  79. }) {
  80. return new Promise(function (resolve) {
  81. const eventState = Object.assign(Object.create(null), {
  82. source: this,
  83. type: "",
  84. query: null,
  85. caseSensitive: false,
  86. entireWord: false,
  87. phraseSearch: true,
  88. findPrevious: false,
  89. matchDiacritics: false
  90. }, state);
  91. eventBus.dispatch("find", eventState);
  92. let totalPages = matchesPerPage.length;
  93. for (let i = totalPages - 1; i >= 0; i--) {
  94. if (matchesPerPage[i] > 0) {
  95. totalPages = i + 1;
  96. break;
  97. }
  98. }
  99. const totalMatches = matchesPerPage.reduce((a, b) => {
  100. return a + b;
  101. });
  102. eventBus.on("updatefindmatchescount", function onUpdateFindMatchesCount(evt) {
  103. if (pdfFindController.pageMatches.length !== totalPages) {
  104. return;
  105. }
  106. eventBus.off("updatefindmatchescount", onUpdateFindMatchesCount);
  107. expect(evt.matchesCount.total).toBe(totalMatches);
  108. for (let i = 0; i < totalPages; i++) {
  109. expect(pdfFindController.pageMatches[i].length).toEqual(matchesPerPage[i]);
  110. }
  111. expect(pdfFindController.selected.pageIdx).toEqual(selectedMatch.pageIndex);
  112. expect(pdfFindController.selected.matchIdx).toEqual(selectedMatch.matchIndex);
  113. if (pageMatches) {
  114. expect(pdfFindController.pageMatches).toEqual(pageMatches);
  115. expect(pdfFindController.pageMatchesLength).toEqual(pageMatchesLength);
  116. }
  117. resolve();
  118. });
  119. });
  120. }
  121. describe("pdf_find_controller", function () {
  122. it("performs a normal search", async function () {
  123. const {
  124. eventBus,
  125. pdfFindController
  126. } = await initPdfFindController();
  127. await testSearch({
  128. eventBus,
  129. pdfFindController,
  130. state: {
  131. query: "Dynamic"
  132. },
  133. matchesPerPage: [11, 5, 0, 3, 0, 0, 0, 1, 1, 1, 0, 3, 4, 4],
  134. selectedMatch: {
  135. pageIndex: 0,
  136. matchIndex: 0
  137. }
  138. });
  139. });
  140. it("performs a normal search and finds the previous result", async function () {
  141. const {
  142. eventBus,
  143. pdfFindController
  144. } = await initPdfFindController();
  145. await testSearch({
  146. eventBus,
  147. pdfFindController,
  148. state: {
  149. query: "conference",
  150. findPrevious: true
  151. },
  152. matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
  153. selectedMatch: {
  154. pageIndex: 13,
  155. matchIndex: 4
  156. }
  157. });
  158. });
  159. it("performs a case sensitive search", async function () {
  160. const {
  161. eventBus,
  162. pdfFindController
  163. } = await initPdfFindController();
  164. await testSearch({
  165. eventBus,
  166. pdfFindController,
  167. state: {
  168. query: "Dynamic",
  169. caseSensitive: true
  170. },
  171. matchesPerPage: [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3],
  172. selectedMatch: {
  173. pageIndex: 0,
  174. matchIndex: 0
  175. }
  176. });
  177. });
  178. it("performs an entire word search", async function () {
  179. const {
  180. eventBus,
  181. pdfFindController
  182. } = await initPdfFindController();
  183. await testSearch({
  184. eventBus,
  185. pdfFindController,
  186. state: {
  187. query: "Government",
  188. entireWord: true
  189. },
  190. matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
  191. selectedMatch: {
  192. pageIndex: 12,
  193. matchIndex: 0
  194. }
  195. });
  196. });
  197. it("performs a multiple term (no phrase) search", async function () {
  198. const {
  199. eventBus,
  200. pdfFindController
  201. } = await initPdfFindController();
  202. await testSearch({
  203. eventBus,
  204. pdfFindController,
  205. state: {
  206. query: "alternate solution",
  207. phraseSearch: false
  208. },
  209. matchesPerPage: [0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0, 0, 0, 0],
  210. selectedMatch: {
  211. pageIndex: 5,
  212. matchIndex: 0
  213. }
  214. });
  215. });
  216. it("performs a normal search, where the text is normalized", async function () {
  217. const {
  218. eventBus,
  219. pdfFindController
  220. } = await initPdfFindController("fraction-highlight.pdf");
  221. await testSearch({
  222. eventBus,
  223. pdfFindController,
  224. state: {
  225. query: "fraction"
  226. },
  227. matchesPerPage: [3],
  228. selectedMatch: {
  229. pageIndex: 0,
  230. matchIndex: 0
  231. },
  232. pageMatches: [[19, 46, 62]],
  233. pageMatchesLength: [[8, 8, 8]]
  234. });
  235. await testSearch({
  236. eventBus,
  237. pdfFindController,
  238. state: {
  239. query: "1/2"
  240. },
  241. matchesPerPage: [2],
  242. selectedMatch: {
  243. pageIndex: 0,
  244. matchIndex: 0
  245. },
  246. pageMatches: [[27, 54]],
  247. pageMatchesLength: [[1, 1]]
  248. });
  249. await testSearch({
  250. eventBus,
  251. pdfFindController,
  252. state: {
  253. query: "½"
  254. },
  255. matchesPerPage: [2],
  256. selectedMatch: {
  257. pageIndex: 0,
  258. matchIndex: 0
  259. },
  260. pageMatches: [[27, 54]],
  261. pageMatchesLength: [[1, 1]]
  262. });
  263. });
  264. it("performs a normal search, where the text with diacritics is normalized", async function () {
  265. const {
  266. eventBus,
  267. pdfFindController
  268. } = await initPdfFindController("french_diacritics.pdf");
  269. await testSearch({
  270. eventBus,
  271. pdfFindController,
  272. state: {
  273. query: "a"
  274. },
  275. matchesPerPage: [6],
  276. selectedMatch: {
  277. pageIndex: 0,
  278. matchIndex: 0
  279. },
  280. pageMatches: [[0, 2, 4, 6, 8, 10]],
  281. pageMatchesLength: [[1, 1, 1, 1, 1, 1]]
  282. });
  283. await testSearch({
  284. eventBus,
  285. pdfFindController,
  286. state: {
  287. query: "u"
  288. },
  289. matchesPerPage: [6],
  290. selectedMatch: {
  291. pageIndex: 0,
  292. matchIndex: 0
  293. },
  294. pageMatches: [[44, 46, 48, 50, 52, 54]],
  295. pageMatchesLength: [[1, 1, 1, 1, 1, 1]]
  296. });
  297. await testSearch({
  298. eventBus,
  299. pdfFindController,
  300. state: {
  301. query: "ë",
  302. matchDiacritics: true
  303. },
  304. matchesPerPage: [2],
  305. selectedMatch: {
  306. pageIndex: 0,
  307. matchIndex: 0
  308. },
  309. pageMatches: [[28, 30]],
  310. pageMatchesLength: [[1, 1]]
  311. });
  312. });
  313. it("performs a search where one of the results contains an hyphen", async function () {
  314. const {
  315. eventBus,
  316. pdfFindController
  317. } = await initPdfFindController();
  318. await testSearch({
  319. eventBus,
  320. pdfFindController,
  321. state: {
  322. query: "optimiz"
  323. },
  324. matchesPerPage: [1, 4, 2, 3, 3, 0, 2, 9, 1, 0, 0, 6, 3, 4],
  325. selectedMatch: {
  326. pageIndex: 0,
  327. matchIndex: 0
  328. }
  329. });
  330. });
  331. it("performs a search where the result is on two lines", async function () {
  332. const {
  333. eventBus,
  334. pdfFindController
  335. } = await initPdfFindController();
  336. await testSearch({
  337. eventBus,
  338. pdfFindController,
  339. state: {
  340. query: "user experience"
  341. },
  342. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  343. selectedMatch: {
  344. pageIndex: 0,
  345. matchIndex: 0
  346. },
  347. pageMatches: [[2743]],
  348. pageMatchesLength: [[14]]
  349. });
  350. });
  351. it("performs a search where the result is on two lines with a punctuation at eol", async function () {
  352. const {
  353. eventBus,
  354. pdfFindController
  355. } = await initPdfFindController();
  356. await testSearch({
  357. eventBus,
  358. pdfFindController,
  359. state: {
  360. query: "version.the"
  361. },
  362. matchesPerPage: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  363. selectedMatch: {
  364. pageIndex: 1,
  365. matchIndex: 0
  366. },
  367. pageMatches: [[], [1493]],
  368. pageMatchesLength: [[], [11]]
  369. });
  370. });
  371. it("performs a search with a minus sign in the query", async function () {
  372. const {
  373. eventBus,
  374. pdfFindController
  375. } = await initPdfFindController();
  376. await testSearch({
  377. eventBus,
  378. pdfFindController,
  379. state: {
  380. query: "trace-based just-in-time"
  381. },
  382. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
  383. selectedMatch: {
  384. pageIndex: 0,
  385. matchIndex: 0
  386. },
  387. pageMatches: [[0], [], [], [], [], [], [], [], [], [], [], [], [], [2087]],
  388. pageMatchesLength: [[24], [], [], [], [], [], [], [], [], [], [], [], [], [24]]
  389. });
  390. });
  391. it("performs a search with square brackets in the query", async function () {
  392. const {
  393. eventBus,
  394. pdfFindController
  395. } = await initPdfFindController();
  396. await testSearch({
  397. eventBus,
  398. pdfFindController,
  399. state: {
  400. query: "[Programming Languages]"
  401. },
  402. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  403. selectedMatch: {
  404. pageIndex: 0,
  405. matchIndex: 0
  406. },
  407. pageMatches: [[1501]],
  408. pageMatchesLength: [[25]]
  409. });
  410. });
  411. it("performs a search with parenthesis in the query", async function () {
  412. const {
  413. eventBus,
  414. pdfFindController
  415. } = await initPdfFindController();
  416. await testSearch({
  417. eventBus,
  418. pdfFindController,
  419. state: {
  420. query: "\t (checks)"
  421. },
  422. matchesPerPage: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  423. selectedMatch: {
  424. pageIndex: 1,
  425. matchIndex: 0
  426. },
  427. pageMatches: [[], [201]],
  428. pageMatchesLength: [[], [9]]
  429. });
  430. });
  431. it("performs a search with a final dot in the query", async function () {
  432. const {
  433. eventBus,
  434. pdfFindController
  435. } = await initPdfFindController();
  436. const query = "complex applications.";
  437. await testSearch({
  438. eventBus,
  439. pdfFindController,
  440. state: {
  441. query
  442. },
  443. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  444. selectedMatch: {
  445. pageIndex: 0,
  446. matchIndex: 0
  447. },
  448. pageMatches: [[1946]],
  449. pageMatchesLength: [[21]]
  450. });
  451. });
  452. it("performs a search with a dot in the query and a missing whitespace", async function () {
  453. const {
  454. eventBus,
  455. pdfFindController
  456. } = await initPdfFindController();
  457. const query = "complex applications.J";
  458. await testSearch({
  459. eventBus,
  460. pdfFindController,
  461. state: {
  462. query
  463. },
  464. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  465. selectedMatch: {
  466. pageIndex: 0,
  467. matchIndex: 0
  468. },
  469. pageMatches: [[1946]],
  470. pageMatchesLength: [[23]]
  471. });
  472. });
  473. it("performs a search with a dot followed by a whitespace in the query", async function () {
  474. const {
  475. eventBus,
  476. pdfFindController
  477. } = await initPdfFindController();
  478. const query = "complex applications. j";
  479. await testSearch({
  480. eventBus,
  481. pdfFindController,
  482. state: {
  483. query
  484. },
  485. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  486. selectedMatch: {
  487. pageIndex: 0,
  488. matchIndex: 0
  489. },
  490. pageMatches: [[1946]],
  491. pageMatchesLength: [[23]]
  492. });
  493. });
  494. it("performs a search in a text containing diacritics before -\\n", async function () {
  495. if (_is_node.isNodeJS) {
  496. pending("Linked test-cases are not supported in Node.js.");
  497. }
  498. const {
  499. eventBus,
  500. pdfFindController
  501. } = await initPdfFindController("issue14562.pdf");
  502. await testSearch({
  503. eventBus,
  504. pdfFindController,
  505. state: {
  506. query: "ä",
  507. matchDiacritics: true
  508. },
  509. matchesPerPage: [80],
  510. selectedMatch: {
  511. pageIndex: 0,
  512. matchIndex: 0
  513. },
  514. pageMatches: [[302, 340, 418, 481, 628, 802, 983, 989, 1015, 1063, 1084, 1149, 1157, 1278, 1346, 1394, 1402, 1424, 1500, 1524, 1530, 1686, 1776, 1788, 1859, 1881, 1911, 1948, 2066, 2076, 2163, 2180, 2215, 2229, 2274, 2324, 2360, 2402, 2413, 2424, 2463, 2532, 2538, 2553, 2562, 2576, 2602, 2613, 2638, 2668, 2792, 2805, 2836, 2847, 2858, 2895, 2901, 2915, 2939, 2959, 3089, 3236, 3246, 3336, 3384, 3391, 3465, 3474, 3482, 3499, 3687, 3693, 3708, 3755, 3786, 3862, 3974, 4049, 4055, 4068]],
  515. pageMatchesLength: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
  516. });
  517. });
  518. it("performs a search in a text containing some Hangul syllables", async function () {
  519. const {
  520. eventBus,
  521. pdfFindController
  522. } = await initPdfFindController("bug1771477.pdf");
  523. await testSearch({
  524. eventBus,
  525. pdfFindController,
  526. state: {
  527. query: "안녕하세요 세계"
  528. },
  529. matchesPerPage: [1],
  530. selectedMatch: {
  531. pageIndex: 0,
  532. matchIndex: 0
  533. },
  534. pageMatches: [[139]],
  535. pageMatchesLength: [[8]]
  536. });
  537. });
  538. it("performs a search in a text containing an ideographic at the end of a line", async function () {
  539. const {
  540. eventBus,
  541. pdfFindController
  542. } = await initPdfFindController("issue15340.pdf");
  543. await testSearch({
  544. eventBus,
  545. pdfFindController,
  546. state: {
  547. query: "検知機構"
  548. },
  549. matchesPerPage: [1],
  550. selectedMatch: {
  551. pageIndex: 0,
  552. matchIndex: 0
  553. },
  554. pageMatches: [[29]],
  555. pageMatchesLength: [[4]]
  556. });
  557. });
  558. it("performs a search in a text containing fullwidth chars", async function () {
  559. const {
  560. eventBus,
  561. pdfFindController
  562. } = await initPdfFindController("issue15690.pdf");
  563. await testSearch({
  564. eventBus,
  565. pdfFindController,
  566. state: {
  567. query: "o"
  568. },
  569. matchesPerPage: [13],
  570. selectedMatch: {
  571. pageIndex: 0,
  572. matchIndex: 0
  573. },
  574. pageMatches: [[0, 10, 13, 30, 39, 41, 55, 60, 66, 84, 102, 117, 134]],
  575. pageMatchesLength: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
  576. });
  577. });
  578. it("performs a search in a text with some Katakana at the end of a line", async function () {
  579. const {
  580. eventBus,
  581. pdfFindController
  582. } = await initPdfFindController("issue15759.pdf");
  583. await testSearch({
  584. eventBus,
  585. pdfFindController,
  586. state: {
  587. query: "ソレノイド"
  588. },
  589. matchesPerPage: [1],
  590. selectedMatch: {
  591. pageIndex: 0,
  592. matchIndex: 0
  593. },
  594. pageMatches: [[6]],
  595. pageMatchesLength: [[5]]
  596. });
  597. });
  598. });