pdf_find_controller_spec.js 14 KB

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