2
0

pdf_find_controller_spec.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  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, { ...CMAP_PARAMS
  55. }));
  56. const pdfDocument = await loadingTask.promise;
  57. const eventBus = new _event_utils.EventBus();
  58. const linkService = new MockLinkService();
  59. linkService.setDocument(pdfDocument);
  60. const pdfFindController = new _pdf_find_controller.PDFFindController({
  61. linkService,
  62. eventBus
  63. });
  64. pdfFindController.setDocument(pdfDocument);
  65. return {
  66. eventBus,
  67. pdfFindController
  68. };
  69. }
  70. function testSearch({
  71. eventBus,
  72. pdfFindController,
  73. state,
  74. matchesPerPage,
  75. selectedMatch,
  76. pageMatches = null,
  77. pageMatchesLength = null
  78. }) {
  79. return new Promise(function (resolve) {
  80. const eventState = Object.assign(Object.create(null), {
  81. source: this,
  82. type: "",
  83. query: null,
  84. caseSensitive: false,
  85. entireWord: false,
  86. phraseSearch: true,
  87. findPrevious: false,
  88. matchDiacritics: false
  89. }, state);
  90. eventBus.dispatch("find", eventState);
  91. let totalPages = matchesPerPage.length;
  92. for (let i = totalPages - 1; i >= 0; i--) {
  93. if (matchesPerPage[i] > 0) {
  94. totalPages = i + 1;
  95. break;
  96. }
  97. }
  98. const totalMatches = matchesPerPage.reduce((a, b) => {
  99. return a + b;
  100. });
  101. eventBus.on("updatefindmatchescount", function onUpdateFindMatchesCount(evt) {
  102. if (pdfFindController.pageMatches.length !== totalPages) {
  103. return;
  104. }
  105. eventBus.off("updatefindmatchescount", onUpdateFindMatchesCount);
  106. expect(evt.matchesCount.total).toBe(totalMatches);
  107. for (let i = 0; i < totalPages; i++) {
  108. expect(pdfFindController.pageMatches[i].length).toEqual(matchesPerPage[i]);
  109. }
  110. expect(pdfFindController.selected.pageIdx).toEqual(selectedMatch.pageIndex);
  111. expect(pdfFindController.selected.matchIdx).toEqual(selectedMatch.matchIndex);
  112. if (pageMatches) {
  113. expect(pdfFindController.pageMatches).toEqual(pageMatches);
  114. expect(pdfFindController.pageMatchesLength).toEqual(pageMatchesLength);
  115. }
  116. resolve();
  117. });
  118. });
  119. }
  120. describe("pdf_find_controller", function () {
  121. it("performs a normal search", async function () {
  122. const {
  123. eventBus,
  124. pdfFindController
  125. } = await initPdfFindController();
  126. await testSearch({
  127. eventBus,
  128. pdfFindController,
  129. state: {
  130. query: "Dynamic"
  131. },
  132. matchesPerPage: [11, 5, 0, 3, 0, 0, 0, 1, 1, 1, 0, 3, 4, 4],
  133. selectedMatch: {
  134. pageIndex: 0,
  135. matchIndex: 0
  136. }
  137. });
  138. });
  139. it("performs a normal search and finds the previous result", async function () {
  140. const {
  141. eventBus,
  142. pdfFindController
  143. } = await initPdfFindController();
  144. await testSearch({
  145. eventBus,
  146. pdfFindController,
  147. state: {
  148. query: "conference",
  149. findPrevious: true
  150. },
  151. matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
  152. selectedMatch: {
  153. pageIndex: 13,
  154. matchIndex: 4
  155. }
  156. });
  157. });
  158. it("performs a case sensitive search", async function () {
  159. const {
  160. eventBus,
  161. pdfFindController
  162. } = await initPdfFindController();
  163. await testSearch({
  164. eventBus,
  165. pdfFindController,
  166. state: {
  167. query: "Dynamic",
  168. caseSensitive: true
  169. },
  170. matchesPerPage: [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3],
  171. selectedMatch: {
  172. pageIndex: 0,
  173. matchIndex: 0
  174. }
  175. });
  176. });
  177. it("performs an entire word search", async function () {
  178. const {
  179. eventBus,
  180. pdfFindController
  181. } = await initPdfFindController();
  182. await testSearch({
  183. eventBus,
  184. pdfFindController,
  185. state: {
  186. query: "Government",
  187. entireWord: true
  188. },
  189. matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
  190. selectedMatch: {
  191. pageIndex: 12,
  192. matchIndex: 0
  193. }
  194. });
  195. });
  196. it("performs a multiple term (no phrase) search", async function () {
  197. const {
  198. eventBus,
  199. pdfFindController
  200. } = await initPdfFindController();
  201. await testSearch({
  202. eventBus,
  203. pdfFindController,
  204. state: {
  205. query: "alternate solution",
  206. phraseSearch: false
  207. },
  208. matchesPerPage: [0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0, 0, 0, 0],
  209. selectedMatch: {
  210. pageIndex: 5,
  211. matchIndex: 0
  212. }
  213. });
  214. });
  215. it("performs a normal search, where the text is normalized", async function () {
  216. const {
  217. eventBus,
  218. pdfFindController
  219. } = await initPdfFindController("fraction-highlight.pdf");
  220. await testSearch({
  221. eventBus,
  222. pdfFindController,
  223. state: {
  224. query: "fraction"
  225. },
  226. matchesPerPage: [3],
  227. selectedMatch: {
  228. pageIndex: 0,
  229. matchIndex: 0
  230. },
  231. pageMatches: [[19, 46, 62]],
  232. pageMatchesLength: [[8, 8, 8]]
  233. });
  234. await testSearch({
  235. eventBus,
  236. pdfFindController,
  237. state: {
  238. query: "1/2"
  239. },
  240. matchesPerPage: [2],
  241. selectedMatch: {
  242. pageIndex: 0,
  243. matchIndex: 0
  244. },
  245. pageMatches: [[27, 54]],
  246. pageMatchesLength: [[1, 1]]
  247. });
  248. await testSearch({
  249. eventBus,
  250. pdfFindController,
  251. state: {
  252. query: "½"
  253. },
  254. matchesPerPage: [2],
  255. selectedMatch: {
  256. pageIndex: 0,
  257. matchIndex: 0
  258. },
  259. pageMatches: [[27, 54]],
  260. pageMatchesLength: [[1, 1]]
  261. });
  262. });
  263. it("performs a normal search, where the text with diacritics is normalized", async function () {
  264. const {
  265. eventBus,
  266. pdfFindController
  267. } = await initPdfFindController("french_diacritics.pdf");
  268. await testSearch({
  269. eventBus,
  270. pdfFindController,
  271. state: {
  272. query: "a"
  273. },
  274. matchesPerPage: [6],
  275. selectedMatch: {
  276. pageIndex: 0,
  277. matchIndex: 0
  278. },
  279. pageMatches: [[0, 2, 4, 6, 8, 10]],
  280. pageMatchesLength: [[1, 1, 1, 1, 1, 1]]
  281. });
  282. await testSearch({
  283. eventBus,
  284. pdfFindController,
  285. state: {
  286. query: "u"
  287. },
  288. matchesPerPage: [6],
  289. selectedMatch: {
  290. pageIndex: 0,
  291. matchIndex: 0
  292. },
  293. pageMatches: [[44, 46, 48, 50, 52, 54]],
  294. pageMatchesLength: [[1, 1, 1, 1, 1, 1]]
  295. });
  296. await testSearch({
  297. eventBus,
  298. pdfFindController,
  299. state: {
  300. query: "ë",
  301. matchDiacritics: true
  302. },
  303. matchesPerPage: [2],
  304. selectedMatch: {
  305. pageIndex: 0,
  306. matchIndex: 0
  307. },
  308. pageMatches: [[28, 30]],
  309. pageMatchesLength: [[1, 1]]
  310. });
  311. });
  312. it("performs a search where one of the results contains an hyphen", async function () {
  313. const {
  314. eventBus,
  315. pdfFindController
  316. } = await initPdfFindController();
  317. await testSearch({
  318. eventBus,
  319. pdfFindController,
  320. state: {
  321. query: "optimiz"
  322. },
  323. matchesPerPage: [1, 4, 2, 3, 3, 0, 2, 9, 1, 0, 0, 6, 3, 4],
  324. selectedMatch: {
  325. pageIndex: 0,
  326. matchIndex: 0
  327. }
  328. });
  329. });
  330. it("performs a search where the result is on two lines", async function () {
  331. const {
  332. eventBus,
  333. pdfFindController
  334. } = await initPdfFindController();
  335. await testSearch({
  336. eventBus,
  337. pdfFindController,
  338. state: {
  339. query: "user experience"
  340. },
  341. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  342. selectedMatch: {
  343. pageIndex: 0,
  344. matchIndex: 0
  345. },
  346. pageMatches: [[2743]],
  347. pageMatchesLength: [[14]]
  348. });
  349. });
  350. it("performs a search where the result is on two lines with a punctuation at eol", async function () {
  351. const {
  352. eventBus,
  353. pdfFindController
  354. } = await initPdfFindController();
  355. await testSearch({
  356. eventBus,
  357. pdfFindController,
  358. state: {
  359. query: "version.the"
  360. },
  361. matchesPerPage: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  362. selectedMatch: {
  363. pageIndex: 1,
  364. matchIndex: 0
  365. },
  366. pageMatches: [[], [1493]],
  367. pageMatchesLength: [[], [11]]
  368. });
  369. });
  370. it("performs a search with a minus sign in the query", async function () {
  371. const {
  372. eventBus,
  373. pdfFindController
  374. } = await initPdfFindController();
  375. await testSearch({
  376. eventBus,
  377. pdfFindController,
  378. state: {
  379. query: "trace-based just-in-time"
  380. },
  381. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
  382. selectedMatch: {
  383. pageIndex: 0,
  384. matchIndex: 0
  385. },
  386. pageMatches: [[0], [], [], [], [], [], [], [], [], [], [], [], [], [2087]],
  387. pageMatchesLength: [[24], [], [], [], [], [], [], [], [], [], [], [], [], [24]]
  388. });
  389. });
  390. it("performs a search with square brackets in the query", async function () {
  391. const {
  392. eventBus,
  393. pdfFindController
  394. } = await initPdfFindController();
  395. await testSearch({
  396. eventBus,
  397. pdfFindController,
  398. state: {
  399. query: "[Programming Languages]"
  400. },
  401. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  402. selectedMatch: {
  403. pageIndex: 0,
  404. matchIndex: 0
  405. },
  406. pageMatches: [[1501]],
  407. pageMatchesLength: [[25]]
  408. });
  409. });
  410. it("performs a search with parenthesis in the query", async function () {
  411. const {
  412. eventBus,
  413. pdfFindController
  414. } = await initPdfFindController();
  415. await testSearch({
  416. eventBus,
  417. pdfFindController,
  418. state: {
  419. query: "\t (checks)"
  420. },
  421. matchesPerPage: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  422. selectedMatch: {
  423. pageIndex: 1,
  424. matchIndex: 0
  425. },
  426. pageMatches: [[], [201]],
  427. pageMatchesLength: [[], [9]]
  428. });
  429. });
  430. it("performs a search with a final dot in the query", async function () {
  431. const {
  432. eventBus,
  433. pdfFindController
  434. } = await initPdfFindController();
  435. const query = "complex applications.";
  436. await testSearch({
  437. eventBus,
  438. pdfFindController,
  439. state: {
  440. query
  441. },
  442. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  443. selectedMatch: {
  444. pageIndex: 0,
  445. matchIndex: 0
  446. },
  447. pageMatches: [[1946]],
  448. pageMatchesLength: [[21]]
  449. });
  450. });
  451. it("performs a search with a dot in the query and a missing whitespace", async function () {
  452. const {
  453. eventBus,
  454. pdfFindController
  455. } = await initPdfFindController();
  456. const query = "complex applications.J";
  457. await testSearch({
  458. eventBus,
  459. pdfFindController,
  460. state: {
  461. query
  462. },
  463. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  464. selectedMatch: {
  465. pageIndex: 0,
  466. matchIndex: 0
  467. },
  468. pageMatches: [[1946]],
  469. pageMatchesLength: [[23]]
  470. });
  471. });
  472. it("performs a search with a dot followed by a whitespace in the query", async function () {
  473. const {
  474. eventBus,
  475. pdfFindController
  476. } = await initPdfFindController();
  477. const query = "complex applications. j";
  478. await testSearch({
  479. eventBus,
  480. pdfFindController,
  481. state: {
  482. query
  483. },
  484. matchesPerPage: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  485. selectedMatch: {
  486. pageIndex: 0,
  487. matchIndex: 0
  488. },
  489. pageMatches: [[1946]],
  490. pageMatchesLength: [[23]]
  491. });
  492. });
  493. it("performs a search in a text containing diacritics before -\\n", async function () {
  494. if (_is_node.isNodeJS) {
  495. pending("Linked test-cases are not supported in Node.js.");
  496. }
  497. const {
  498. eventBus,
  499. pdfFindController
  500. } = await initPdfFindController("issue14562.pdf");
  501. await testSearch({
  502. eventBus,
  503. pdfFindController,
  504. state: {
  505. query: "ä",
  506. matchDiacritics: true
  507. },
  508. matchesPerPage: [80],
  509. selectedMatch: {
  510. pageIndex: 0,
  511. matchIndex: 0
  512. },
  513. 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]],
  514. 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]]
  515. });
  516. });
  517. it("performs a search in a text containing some Hangul syllables", async function () {
  518. const {
  519. eventBus,
  520. pdfFindController
  521. } = await initPdfFindController("bug1771477.pdf");
  522. await testSearch({
  523. eventBus,
  524. pdfFindController,
  525. state: {
  526. query: "안녕하세요 세계"
  527. },
  528. matchesPerPage: [1],
  529. selectedMatch: {
  530. pageIndex: 0,
  531. matchIndex: 0
  532. },
  533. pageMatches: [[139]],
  534. pageMatchesLength: [[8]]
  535. });
  536. });
  537. });