@internetarchive/bookreader 5.0.0-8 → 5.0.0-80

Sign up to get free protection for your applications and to get access to all the features.
Files changed (313) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +73 -10
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +398 -1133
  6. package/BookReader/BookReader.js +1 -1
  7. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1782 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/1up.svg +1 -1
  13. package/BookReader/icons/2up.svg +1 -1
  14. package/BookReader/icons/advance.svg +1 -1
  15. package/BookReader/icons/chevron-right.svg +1 -1
  16. package/BookReader/icons/close-circle-dark.svg +1 -1
  17. package/BookReader/icons/close-circle.svg +1 -1
  18. package/BookReader/icons/fullscreen.svg +1 -1
  19. package/BookReader/icons/fullscreen_exit.svg +1 -1
  20. package/BookReader/icons/hamburger.svg +1 -1
  21. package/BookReader/icons/left-arrow.svg +1 -1
  22. package/BookReader/icons/magnify-minus.svg +1 -1
  23. package/BookReader/icons/magnify-plus.svg +1 -1
  24. package/BookReader/icons/magnify.svg +1 -1
  25. package/BookReader/icons/pause.svg +1 -1
  26. package/BookReader/icons/play.svg +1 -1
  27. package/BookReader/icons/playback-speed.svg +1 -1
  28. package/BookReader/icons/read-aloud.svg +1 -1
  29. package/BookReader/icons/review.svg +1 -1
  30. package/BookReader/icons/thumbnails.svg +1 -1
  31. package/BookReader/icons/voice.svg +1 -0
  32. package/BookReader/icons/volume-full.svg +1 -1
  33. package/BookReader/images/BRicons.svg +3 -3
  34. package/BookReader/images/books_graphic.svg +1 -1
  35. package/BookReader/images/icon_book.svg +1 -1
  36. package/BookReader/images/icon_bookmark.svg +1 -1
  37. package/BookReader/images/icon_gear.svg +1 -1
  38. package/BookReader/images/icon_hamburger.svg +1 -1
  39. package/BookReader/images/icon_home.svg +1 -1
  40. package/BookReader/images/icon_info.svg +1 -1
  41. package/BookReader/images/icon_one_page.svg +1 -1
  42. package/BookReader/images/icon_pause.svg +1 -1
  43. package/BookReader/images/icon_play.svg +1 -1
  44. package/BookReader/images/icon_playback-rate.svg +1 -1
  45. package/BookReader/images/icon_search_button.svg +1 -1
  46. package/BookReader/images/icon_share.svg +1 -1
  47. package/BookReader/images/icon_skip-ahead.svg +1 -1
  48. package/BookReader/images/icon_skip-back.svg +1 -1
  49. package/BookReader/images/icon_speaker.svg +1 -1
  50. package/BookReader/images/icon_speaker_open.svg +1 -1
  51. package/BookReader/images/icon_thumbnails.svg +1 -1
  52. package/BookReader/images/icon_toc.svg +1 -1
  53. package/BookReader/images/icon_two_pages.svg +1 -1
  54. package/BookReader/images/marker_chap-off.svg +1 -1
  55. package/BookReader/images/marker_chap-on.svg +1 -1
  56. package/BookReader/images/marker_srch-on.svg +1 -1
  57. package/BookReader/jquery-3.js +2 -0
  58. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  59. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  60. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  61. package/BookReader/plugins/plugin.autoplay.js +1 -1
  62. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  63. package/BookReader/plugins/plugin.chapters.js +25 -1
  64. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  65. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  66. package/BookReader/plugins/plugin.iframe.js +1 -1
  67. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  68. package/BookReader/plugins/plugin.iiif.js +2 -0
  69. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  70. package/BookReader/plugins/plugin.resume.js +1 -1
  71. package/BookReader/plugins/plugin.resume.js.map +1 -1
  72. package/BookReader/plugins/plugin.search.js +2 -1
  73. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  74. package/BookReader/plugins/plugin.search.js.map +1 -1
  75. package/BookReader/plugins/plugin.text_selection.js +2 -1
  76. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  77. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  78. package/BookReader/plugins/plugin.tts.js +1 -1
  79. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  80. package/BookReader/plugins/plugin.tts.js.map +1 -1
  81. package/BookReader/plugins/plugin.url.js +1 -1
  82. package/BookReader/plugins/plugin.url.js.map +1 -1
  83. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  84. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  85. package/BookReader/webcomponents-bundle.js +3 -0
  86. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  87. package/BookReader/webcomponents-bundle.js.map +1 -0
  88. package/BookReaderDemo/BookReaderDemo.css +18 -19
  89. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
  90. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  91. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  92. package/BookReaderDemo/IADemoBr.js +147 -0
  93. package/BookReaderDemo/demo-advanced.html +2 -2
  94. package/BookReaderDemo/demo-autoplay.html +2 -3
  95. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  96. package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
  97. package/BookReaderDemo/demo-fullscreen.html +2 -4
  98. package/BookReaderDemo/demo-iiif.html +99 -12
  99. package/BookReaderDemo/demo-internetarchive.html +214 -18
  100. package/BookReaderDemo/demo-multiple.html +2 -1
  101. package/BookReaderDemo/demo-preview-pages.html +2 -1
  102. package/BookReaderDemo/demo-simple.html +2 -1
  103. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
  104. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  105. package/BookReaderDemo/immersion-1up.html +2 -2
  106. package/BookReaderDemo/immersion-mode.html +2 -4
  107. package/BookReaderDemo/toggle_controls.html +3 -2
  108. package/BookReaderDemo/view_mode.html +2 -1
  109. package/BookReaderDemo/viewmode-cycle.html +2 -3
  110. package/CHANGELOG.md +536 -33
  111. package/README.md +14 -1
  112. package/babel.config.js +20 -0
  113. package/codecov.yml +6 -0
  114. package/index.html +4 -1
  115. package/jsconfig.json +19 -0
  116. package/netlify.toml +9 -0
  117. package/package.json +70 -60
  118. package/renovate.json +52 -0
  119. package/scripts/preversion.js +0 -1
  120. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  121. package/src/BookNavigator/assets/button-base.js +4 -2
  122. package/src/BookNavigator/assets/ia-logo.js +17 -0
  123. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  124. package/src/BookNavigator/assets/icon_close.js +1 -1
  125. package/src/BookNavigator/book-navigator.js +590 -0
  126. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  127. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  128. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  129. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  130. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  131. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  132. package/src/BookNavigator/delete-modal-actions.js +1 -1
  133. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  134. package/src/BookNavigator/downloads/downloads.js +41 -25
  135. package/src/BookNavigator/search/search-provider.js +49 -27
  136. package/src/BookNavigator/search/search-results.js +23 -9
  137. package/src/BookNavigator/sharing.js +27 -0
  138. package/src/BookNavigator/viewable-files.js +95 -0
  139. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  140. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  141. package/src/BookReader/BookModel.js +64 -34
  142. package/src/BookReader/DragScrollable.js +233 -0
  143. package/src/BookReader/Mode1Up.js +56 -351
  144. package/src/BookReader/Mode1UpLit.js +388 -0
  145. package/src/BookReader/Mode2Up.js +73 -1318
  146. package/src/BookReader/Mode2UpLit.js +776 -0
  147. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  148. package/src/BookReader/ModeSmoothZoom.js +312 -0
  149. package/src/BookReader/ModeThumb.js +18 -12
  150. package/src/BookReader/Navbar/Navbar.js +14 -40
  151. package/src/BookReader/PageContainer.js +81 -6
  152. package/src/BookReader/ReduceSet.js +1 -1
  153. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  154. package/src/BookReader/events.js +2 -3
  155. package/src/BookReader/options.js +27 -2
  156. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  157. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  158. package/src/BookReader/utils/SelectionObserver.js +45 -0
  159. package/src/BookReader/utils.js +118 -13
  160. package/src/BookReader.js +427 -1061
  161. package/src/assets/icons/magnify-minus.svg +3 -7
  162. package/src/assets/icons/magnify-plus.svg +3 -7
  163. package/src/assets/icons/voice.svg +1 -0
  164. package/src/css/BookReader.scss +1 -5
  165. package/src/css/_BRBookmarks.scss +1 -1
  166. package/src/css/_BRComponent.scss +1 -1
  167. package/src/css/_BRmain.scss +16 -3
  168. package/src/css/_BRnav.scss +12 -39
  169. package/src/css/_BRpages.scss +149 -40
  170. package/src/css/_BRsearch.scss +68 -25
  171. package/src/css/_BRtoolbar.scss +5 -5
  172. package/src/css/_TextSelection.scss +87 -27
  173. package/src/css/_colorbox.scss +2 -2
  174. package/src/css/_controls.scss +20 -7
  175. package/src/css/_icons.scss +1 -1
  176. package/src/ia-bookreader/ia-bookreader.js +224 -0
  177. package/src/plugins/plugin.archive_analytics.js +3 -3
  178. package/src/plugins/plugin.autoplay.js +5 -11
  179. package/src/plugins/plugin.chapters.js +237 -191
  180. package/src/plugins/plugin.iiif.js +151 -0
  181. package/src/plugins/plugin.resume.js +3 -3
  182. package/src/plugins/plugin.text_selection.js +464 -134
  183. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  184. package/src/plugins/search/plugin.search.js +142 -125
  185. package/src/plugins/search/utils.js +43 -0
  186. package/src/plugins/search/view.js +33 -58
  187. package/src/plugins/tts/AbstractTTSEngine.js +71 -40
  188. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  189. package/src/plugins/tts/PageChunk.js +15 -21
  190. package/src/plugins/tts/PageChunkIterator.js +8 -12
  191. package/src/plugins/tts/WebTTSEngine.js +87 -71
  192. package/src/plugins/tts/plugin.tts.js +96 -127
  193. package/src/plugins/tts/utils.js +15 -25
  194. package/src/plugins/url/UrlPlugin.js +191 -0
  195. package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
  196. package/src/util/browserSniffing.js +22 -0
  197. package/src/util/docCookies.js +21 -2
  198. package/tests/e2e/README.md +37 -0
  199. package/tests/e2e/autoplay.test.js +2 -2
  200. package/tests/e2e/base.test.js +8 -16
  201. package/tests/e2e/helpers/base.js +53 -48
  202. package/tests/e2e/helpers/debug.js +1 -1
  203. package/tests/e2e/helpers/params.js +17 -0
  204. package/tests/e2e/helpers/rightToLeft.js +8 -14
  205. package/tests/e2e/helpers/search.js +73 -0
  206. package/tests/e2e/models/Navigation.js +20 -37
  207. package/tests/e2e/rightToLeft.test.js +4 -5
  208. package/tests/e2e/viewmode.test.js +40 -33
  209. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  210. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  211. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  212. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  213. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  214. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  215. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  216. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  217. package/tests/{karma → jest}/BookNavigator/search/search-results.test.js +109 -60
  218. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  219. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  220. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  221. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  222. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  223. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  224. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  225. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  226. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  227. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  228. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  229. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  230. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +10 -10
  231. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  232. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  233. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  234. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  235. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  236. package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  237. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  238. package/tests/jest/BookReader/utils.test.js +229 -0
  239. package/tests/jest/BookReader.keyboard.test.js +190 -0
  240. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  241. package/tests/{BookReader.test.js → jest/BookReader.test.js} +26 -37
  242. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  243. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  244. package/tests/jest/plugins/plugin.chapters.test.js +195 -0
  245. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  246. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  247. package/tests/jest/plugins/plugin.text_selection.test.js +317 -0
  248. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  249. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +25 -47
  250. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +39 -6
  251. package/tests/jest/plugins/search/utils.js +25 -0
  252. package/tests/jest/plugins/search/utils.test.js +29 -0
  253. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +29 -9
  254. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  255. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  256. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  257. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  258. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  259. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  260. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
  261. package/tests/jest/setup.js +3 -0
  262. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  263. package/tests/jest/util/docCookies.test.js +24 -0
  264. package/tests/{util → jest/util}/strings.test.js +1 -1
  265. package/tests/{utils.js → jest/utils.js} +38 -0
  266. package/webpack.config.js +12 -6
  267. package/.babelrc +0 -12
  268. package/.dependabot/config.yml +0 -6
  269. package/.testcaferc.json +0 -5
  270. package/BookReader/bookreader-component-bundle.js +0 -1450
  271. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  272. package/BookReader/bookreader-component-bundle.js.map +0 -1
  273. package/BookReader/jquery-1.10.1.js +0 -2
  274. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  275. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  276. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  277. package/BookReader/plugins/plugin.mobile_nav.js +0 -2
  278. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  279. package/BookReaderDemo/IIIFBookReader.js +0 -207
  280. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  281. package/BookReaderDemo/demo-iiif.js +0 -26
  282. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  283. package/karma.conf.js +0 -23
  284. package/src/BookNavigator/BookModel.js +0 -14
  285. package/src/BookNavigator/BookNavigator.js +0 -446
  286. package/src/BookNavigator/assets/book-loader.js +0 -27
  287. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  288. package/src/BookNavigator/search/a-search-result.js +0 -55
  289. package/src/BookReader/DebugConsole.js +0 -54
  290. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  291. package/src/ItemNavigator/ItemNavigator.js +0 -376
  292. package/src/ItemNavigator/providers/sharing.js +0 -29
  293. package/src/css/_MobileNav.scss +0 -194
  294. package/src/dragscrollable-br.js +0 -261
  295. package/src/lit-wrapper.js +0 -2
  296. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  297. package/src/plugins/plugin.mobile_nav.js +0 -287
  298. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  299. package/tests/BookReader/DebugConsole.test.js +0 -25
  300. package/tests/BookReader/Mode1Up.test.js +0 -164
  301. package/tests/BookReader/Mode2Up.test.js +0 -247
  302. package/tests/BookReader/utils.test.js +0 -109
  303. package/tests/e2e/helpers/desktopSearch.js +0 -72
  304. package/tests/e2e/helpers/mobileSearch.js +0 -85
  305. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  306. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  307. package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
  308. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  309. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  310. package/tests/plugins/plugin.chapters.test.js +0 -130
  311. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  312. package/tests/plugins/plugin.text_selection.test.js +0 -203
  313. package/tests/util/docCookies.test.js +0 -15
@@ -0,0 +1,167 @@
1
+ import { fixtureCleanup, fixtureSync } from '@open-wc/testing-helpers';
2
+ import sinon from 'sinon';
3
+ import searchProvider from '@/src/BookNavigator/search/search-provider';
4
+
5
+ afterEach(() => {
6
+ sinon.restore();
7
+ fixtureCleanup();
8
+ });
9
+
10
+ describe('Search Provider', () => {
11
+ describe('constructor', () => {
12
+ const provider = new searchProvider({
13
+ onProviderChange: sinon.fake(),
14
+ bookreader: {}
15
+ });
16
+
17
+ expect(provider.bookreader).toBeDefined();
18
+ expect(provider.onProviderChange).toBeDefined();
19
+ expect(provider.id).toEqual('search');
20
+ expect(provider.icon).toBeDefined();
21
+ expect(fixtureSync(provider.icon).tagName).toEqual('IA-ICON-SEARCH');
22
+ expect(provider.label).toEqual('Search inside');
23
+ expect(provider.menuDetails).toBeDefined();
24
+ expect(provider.component).toBeDefined();
25
+ });
26
+ describe('Search request life cycles', () => {
27
+ test('Event: catches `BookReader:SearchStarted`', async() => {
28
+ const provider = new searchProvider({
29
+ onProviderChange: sinon.fake(),
30
+ bookreader: {}
31
+ });
32
+ sinon.spy(provider, 'updateMenu');
33
+ window.dispatchEvent(new CustomEvent('BookReader:SearchStarted', { detail: { props: { term: 'foo' }}}));
34
+ expect(provider.updateMenu.callCount).toEqual(1);
35
+ });
36
+ test('Event: catches `BookReader:SearchCallback`', async() => {
37
+ const provider = new searchProvider({
38
+ onProviderChange: sinon.fake(),
39
+ bookreader: {}
40
+ });
41
+ sinon.spy(provider, 'updateMenu');
42
+ const brStub = {};
43
+ window.dispatchEvent(new CustomEvent('BookReader:SearchCallback', { detail: { props: { instance: brStub, results: { matches: []} }}}));
44
+ expect(provider.updateMenu.callCount).toEqual(1);
45
+ expect(provider.bookreader).toEqual(brStub);
46
+ });
47
+ test('Event: catches `BookReader:SearchCallbackEmpty`', async() => {
48
+ const provider = new searchProvider({
49
+ onProviderChange: sinon.fake(),
50
+ bookreader: {}
51
+ });
52
+ sinon.spy(provider, 'onSearchRequestError');
53
+ sinon.spy(provider, 'updateMenu');
54
+ const brStub = {};
55
+ window.dispatchEvent(new CustomEvent('BookReader:SearchCallbackEmpty', { detail: { props: { instance: brStub }}}));
56
+ expect(provider.onSearchRequestError.getCall(0).args[1]).toEqual('noResults');
57
+ expect(provider.updateMenu.callCount).toEqual(1);
58
+ expect(provider.bookreader).toEqual(brStub);
59
+ });
60
+ test('Event: catches `BookReader:SearchCallbackNotIndexed`', async() => {
61
+ const provider = new searchProvider({
62
+ onProviderChange: sinon.fake(),
63
+ bookreader: {}
64
+ });
65
+ const brStub = {};
66
+ sinon.spy(provider, 'onSearchRequestError');
67
+ sinon.spy(provider, 'updateMenu');
68
+ window.dispatchEvent(new CustomEvent('BookReader:SearchCallbackNotIndexed', { detail: { props: { instance: brStub }}}));
69
+ expect(provider.onSearchRequestError.getCall(0).args[1]).toEqual('notIndexed');
70
+ expect(provider.updateMenu.callCount).toEqual(1);
71
+ expect(provider.bookreader).toEqual(brStub);
72
+ });
73
+ test('Event: catches `BookReader:SearchCallbackError`', async() => {
74
+ const provider = new searchProvider({
75
+ onProviderChange: sinon.fake(),
76
+ bookreader: {}
77
+ });
78
+ sinon.spy(provider, 'onSearchRequestError');
79
+ sinon.spy(provider, 'updateMenu');
80
+ const brStub = {};
81
+ window.dispatchEvent(new CustomEvent('BookReader:SearchCallbackError', { detail: { props: { instance: brStub }}}));
82
+ expect(provider.onSearchRequestError.getCall(0).args[1]).toEqual(undefined);
83
+ expect(provider.updateMenu.callCount).toEqual(1);
84
+ expect(provider.bookreader).toEqual(brStub);
85
+ });
86
+ test('Event: catches `component@resultSelected` - user clicks result in side panel - & turns page', async() => {
87
+ const provider = new searchProvider({
88
+ onProviderChange: sinon.fake(),
89
+ bookreader: {
90
+ leafNumToIndex: sinon.fake(),
91
+ _searchPluginGoToResult: sinon.fake()
92
+ }
93
+ });
94
+
95
+ const searchResultStub = {
96
+ match: { par: [{ text: 'foo', page: 3 }] },
97
+ };
98
+ fixtureSync(provider.component).dispatchEvent(
99
+ new CustomEvent('resultSelected',
100
+ { detail: searchResultStub })
101
+ );
102
+
103
+ expect(provider.bookreader._searchPluginGoToResult.callCount).toEqual(1);
104
+ });
105
+ test('update url when search is cancelled or input cleared', async() => {
106
+ const urlPluginMock = {
107
+ pullFromAddressBar: sinon.fake(),
108
+ removeUrlParam: sinon.fake()
109
+ };
110
+ const provider = new searchProvider({
111
+ onProviderChange: sinon.fake(),
112
+ bookreader: {
113
+ leafNumToIndex: sinon.fake(),
114
+ _searchPluginGoToResult: sinon.fake(),
115
+ urlPlugin: urlPluginMock
116
+ }
117
+ });
118
+
119
+ provider.onSearchCanceled();
120
+ await provider.updateComplete;
121
+
122
+ expect(urlPluginMock.pullFromAddressBar.callCount).toEqual(1);
123
+ expect(urlPluginMock.removeUrlParam.callCount).toEqual(1);
124
+
125
+ provider.onSearchCanceled();
126
+ await provider.updateComplete;
127
+
128
+ expect(urlPluginMock.pullFromAddressBar.callCount).toEqual(2);
129
+ expect(urlPluginMock.removeUrlParam.callCount).toEqual(2);
130
+ });
131
+ it('updateSearchInUrl', async () => {
132
+ let fieldToSet;
133
+ let valueOfFieldToSet;
134
+ let setUrlParamCalled = false;
135
+ const urlPluginMock = {
136
+ pullFromAddressBar: sinon.fake(),
137
+ removeUrlParam: sinon.fake(),
138
+ setUrlParam: (field, val) => {
139
+ fieldToSet = field;
140
+ valueOfFieldToSet = val;
141
+ setUrlParamCalled = true;
142
+ }
143
+ };
144
+ const provider = new searchProvider({
145
+ onProviderChange: sinon.fake(),
146
+ bookreader: {
147
+ leafNumToIndex: sinon.fake(),
148
+ _searchPluginGoToResult: sinon.fake(),
149
+ urlPlugin: urlPluginMock,
150
+ search: sinon.fake()
151
+ }
152
+ });
153
+
154
+ const searchInitiatedEvent = new CustomEvent('bookSearchInitiated', { detail: { query: 'foobar' } });
155
+ // set initial seachState with a query
156
+ provider.onBookSearchInitiated(searchInitiatedEvent);
157
+ await provider.updateComplete;
158
+ // checking this fn:
159
+ provider.updateSearchInUrl();
160
+ await provider.updateComplete;
161
+
162
+ expect(fieldToSet).toEqual('q');
163
+ expect(valueOfFieldToSet).toEqual('foobar');
164
+ expect(setUrlParamCalled).toBe(true);
165
+ });
166
+ });
167
+ });
@@ -1,24 +1,23 @@
1
1
  import {
2
2
  html,
3
3
  fixture,
4
- expect,
5
4
  oneEvent,
6
- } from '@open-wc/testing';
5
+ } from '@open-wc/testing-helpers';
7
6
  import sinon from 'sinon';
8
- import { IABookSearchResults } from '../../../../src/BookNavigator/search/search-results.js';
7
+ import { IABookSearchResults } from '@/src/BookNavigator/search/search-results.js';
8
+ import { marshallSearchResults } from '@/src/plugins/search/utils.js';
9
9
 
10
10
  const container = (results = [], query = '') => (
11
11
  html`<ia-book-search-results .results=${results} .query=${query}></ia-book-search-results>`
12
12
  );
13
13
 
14
- console.log("ia-book-search-results-test")
15
-
16
14
  const searchQuery = 'Bristol';
17
15
 
18
16
  const results = [{
19
17
  text: `In the drawing of caricatures and cartoons\u2014or any other com' mercial art, for that matter\u2014the artist should know something about the processes of reproduction for that particular form of art work. For pen and ink work the engraving is made on a sine printing plate. It is not necessary, however, to know all about these processes of reproduction. The artist should know that all work intended for line rqproducttons should be made on white paper or {{{${searchQuery}}}} Board with black drawing ink. The drawing to be reproduced is photographed on a chemically treated sine plate, which is then treated with acid. This acid eats away the surface of the sine, except the photographed' lines, which are left in relief, somewhat like printing type. Colored inks do not photograph well; neither does black ink on colored paper.`,
20
18
  cover: '//placehold.it/30x44',
21
19
  title: 'Book title',
20
+ displayPageNumber: 'Page 24',
22
21
  par: [{
23
22
  boxes: [{
24
23
  r: 2672, b: 792, t: 689, page: 24, l: 2424,
@@ -33,6 +32,7 @@ const results = [{
33
32
  }],
34
33
  }, {
35
34
  text: `Drawings intended for sale should be made on a good grade of {{{${searchQuery}}}} Board, and a margin left all the way around the drawings. They should be mailed flat, and'require first class postage. Enclose postage for the return of the drawings. Only send good drawings of a reason- able quantity. Enclose a neat and terse letter to the one you are sending the drawings to, written with pen and ink or typewriter if possible, on`,
35
+ displayPageNumber: 'Page 86',
36
36
  par: [{
37
37
  boxes: [{
38
38
  r: 698, b: 4460, t: 4324, page: 86, l: 450,
@@ -44,72 +44,108 @@ const results = [{
44
44
  l: 432,
45
45
  page_height: 5357,
46
46
  page: 86,
47
+ }]
48
+ }];
49
+
50
+ marshallSearchResults({ matches: results }, () => '', '{{{', '}}}');
51
+
52
+ const resultWithScript = [{
53
+ text: `foo bar <script>const msg = 'test' + ' failure'; document.write(msg);</script> {{{${searchQuery}}}} baz`,
54
+ cover: '//placehold.it/30x44',
55
+ title: 'Book title',
56
+ displayPageNumber: 'Page 24',
57
+ par: [{
58
+ boxes: [{
59
+ r: 2672, b: 792, t: 689, page: 24, l: 2424,
60
+ }],
61
+ b: 1371,
62
+ t: 689,
63
+ page_width: 3658,
64
+ r: 3192,
65
+ l: 428,
66
+ page_height: 5357,
67
+ page: 24,
47
68
  }],
48
69
  }];
49
70
 
71
+ marshallSearchResults({ matches: resultWithScript }, () => '', '{{{', '}}}');
72
+
50
73
  describe('<ia-book-search-results>', () => {
51
74
  afterEach(() => {
52
75
  sinon.restore();
53
76
  });
54
77
 
55
- it('sets default properties', async () => {
78
+ test('sets default properties', async () => {
56
79
  const query = 'bristol';
57
80
  const el = await fixture(container(results, query));
58
81
 
59
- expect(el.results).to.equal(results);
60
- expect(el.query).to.equal(query);
82
+ expect(el.results).toEqual(results);
83
+ expect(el.query).toEqual(query);
61
84
  });
62
85
 
63
- it('sets results when passed in via event object', async () => {
86
+ test('sets results when passed in via event object', async () => {
64
87
  const el = await fixture(container());
65
88
 
66
89
  el.setResults({ detail: { results } });
67
- expect(el.results).to.equal(results);
90
+ expect(el.results).toEqual(results);
68
91
  });
69
92
 
70
- it('listens for a custom search callback event on the document', async () => {
93
+ test('listens for a custom search callback event on the document', async () => {
71
94
  IABookSearchResults.prototype.setResults = sinon.fake();
72
95
  const el = await fixture(container());
73
96
  const event = new Event('BookReader:SearchCallback');
74
97
 
75
98
  event.detail = { results };
76
99
  document.dispatchEvent(event);
77
- expect(el.setResults.callCount).to.equal(1);
78
- expect(el.setResults.firstArg).to.equal(event);
100
+ expect(el.setResults.callCount).toEqual(1);
101
+ expect(el.setResults.firstArg).toEqual(event);
79
102
  });
80
103
 
81
- it('renders results that contain the book title', async () => {
104
+ test('renders results that contain the book title', async () => {
82
105
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
83
106
  const el = await fixture(container(results));
84
107
 
85
- expect(el.innerHTML).to.include(`${results[0].title}`);
108
+ expect(el.innerHTML).toContain(`${results[0].title}`);
86
109
  });
87
110
 
88
- it('renders results that contain a highlighted match', async () => {
111
+ test('renders results that contain a highlighted match', async () => {
89
112
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
90
113
  const el = await fixture(container(results));
91
114
 
92
- expect(el.innerHTML).to.include(`<mark>${searchQuery}</mark>`);
115
+ // Lit inserts HTML comments that inhibit searching for exact innerHTML matches.
116
+ // So query the DOM for the match instead.
117
+ const match = el.querySelector('mark');
118
+ expect(match?.textContent).toEqual(searchQuery);
93
119
  });
94
120
 
95
- it('renders results that contain an optional cover image', async () => {
121
+ test('renders results that contain sanitized HTML tags', async () => {
122
+ sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
123
+ // A result whose text contains a <script> tag that will insert 'test failure' into the element if not sanitized
124
+ const el = await fixture(container(resultWithScript));
125
+
126
+ const match = el.querySelector('mark');
127
+ expect(match?.textContent).toEqual(searchQuery);
128
+ expect(el.innerHTML).not.toContain('test failure');
129
+ });
130
+
131
+ test('renders results that contain an optional cover image', async () => {
96
132
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
97
133
  const el = await fixture(container(results));
98
134
 
99
- expect(el.innerHTML).to.include(`<img src="${results[0].cover}">`);
135
+ expect(el.innerHTML).toContain(`<img src="${results[0].cover}">`);
100
136
  });
101
137
 
102
- it('sets a query prop when search input receives input', async () => {
138
+ test('sets a query prop when search input receives input', async () => {
103
139
  const el = await fixture(container(results));
104
140
  const searchInput = el.shadowRoot.querySelector('[name="query"]');
105
141
 
106
142
  searchInput.value = searchQuery;
107
143
  searchInput.dispatchEvent(new Event('keyup'));
108
144
 
109
- expect(el.query).to.equal(searchQuery);
145
+ expect(el.query).toEqual(searchQuery);
110
146
  });
111
147
 
112
- it('emits a custom event when search form submitted when input is populated', async () => {
148
+ test('emits a custom event when search form submitted when input is populated', async () => {
113
149
  const el = await fixture(container(results));
114
150
 
115
151
  setTimeout(() => {
@@ -119,122 +155,135 @@ describe('<ia-book-search-results>', () => {
119
155
  });
120
156
  const response = await oneEvent(el, 'bookSearchInitiated');
121
157
 
122
- expect(response).to.exist;
158
+ expect(response).toBeDefined();
123
159
  });
124
160
 
125
- it('uses a singular noun when one result given', async () => {
161
+ test('uses a singular noun when one result given', async () => {
126
162
  const el = await fixture(container([results[0]]));
127
163
  const resultsCount = await fixture(el.resultsCount);
128
164
 
129
- expect(resultsCount.innerHTML).to.include('1 result');
165
+ expect(resultsCount.innerHTML).toContain('1 result');
130
166
  });
131
167
 
132
- it('can render header with active options count', async () => {
168
+ test('can render header with active options count', async () => {
133
169
  const el = await fixture(container(results));
134
170
  el.renderHeader = true;
135
171
 
136
172
  await el.updateComplete;
137
173
 
138
- expect(el.shadowRoot.querySelector('header p').innerText).to.include('2');
174
+ expect(el.shadowRoot.querySelector('header p').textContent).toContain('2');
139
175
  });
140
176
 
141
- it('renders search all files checkbox when enabled', async () => {
177
+ test('renders search all files checkbox when enabled', async () => {
142
178
  const el = await fixture(container(results));
143
179
  el.renderSearchAllFiles = true;
144
180
 
145
181
  await el.updateComplete;
146
182
 
147
- expect(el.shadowRoot.querySelector('[name="all_files"]')).to.not.be.null;
183
+ expect(el.shadowRoot.querySelector('[name="all_files"]')).not.toBeNull();
148
184
  });
149
185
 
150
- it('emits a resultSelected event when a search result is clicked', async () => {
186
+ test('emits a resultSelected event when a search result is clicked', async () => {
151
187
  const el = await fixture(container(results));
152
188
 
153
189
  setTimeout(() => (
154
- el.shadowRoot.querySelector('book-search-result').querySelector('li').click()
190
+ el.shadowRoot.querySelector('li').click()
155
191
  ));
156
192
  const response = await oneEvent(el, 'resultSelected');
157
193
 
158
- expect(response).to.exist;
194
+ expect(response).toBeDefined();
159
195
  });
160
196
 
161
- it('emits a closeMenu event when a search result is clicked', async () => {
197
+ test('emits a closeMenu event when a search result is clicked', async () => {
162
198
  const el = await fixture(container(results));
163
199
 
164
200
  setTimeout(() => (
165
- el.shadowRoot.querySelector('book-search-result').querySelector('li').click()
201
+ el.shadowRoot.querySelector('li').click()
166
202
  ));
167
203
  const response = await oneEvent(el, 'closeMenu');
168
204
 
169
- expect(response).to.exist;
205
+ expect(response).toBeDefined();
170
206
  });
171
207
 
172
208
  describe('Search results placeholders', () => {
173
- it('renders a loading state when queryInProgress is true', async () => {
209
+ test('renders a loading state when queryInProgress is true', async () => {
174
210
  const el = await fixture(container(results));
175
211
 
176
212
  el.queryInProgress = true;
177
213
  await el.updateComplete;
178
214
 
179
- expect(el.shadowRoot.querySelector('.loading')).to.not.be.null;
215
+ expect(el.shadowRoot.querySelector('.loading')).not.toBeNull();
180
216
  });
181
- it('renders an error message when provided', async () => {
217
+ test('renders an error message when provided', async () => {
182
218
  const el = await fixture(container([]));
183
219
  const message = 'Sample error message';
184
220
  el.errorMessage = message;
185
221
  await el.updateComplete;
186
222
 
187
- expect(el.shadowRoot.querySelector('.error-message')).to.exist;
188
- expect(el.shadowRoot.querySelector('.search-cta')).to.be.null;
223
+ expect(el.shadowRoot.querySelector('.error-message')).toBeDefined();
224
+ expect(el.shadowRoot.querySelector('.search-cta')).toBeNull();
189
225
  });
190
- it('displays call to search when no results or search errors are showing', async () => {
226
+ test('displays call to search when no results or search errors are showing', async () => {
191
227
  const el = await fixture(container([]));
192
228
 
193
- expect(el.shadowRoot.querySelector('.search-cta')).to.exist;
194
- expect(el.shadowRoot.querySelector('.error-message')).to.be.null;
195
- expect(el.shadowRoot.querySelector('.results')).to.be.null;
229
+ expect(el.shadowRoot.querySelector('.search-cta')).toBeDefined();
230
+ expect(el.shadowRoot.querySelector('.error-message')).toBeNull();
231
+ expect(el.shadowRoot.querySelector('.results')).toBeNull();
196
232
  });
197
233
  });
198
234
 
199
- it('displays results images when told to', async () => {
235
+ test('displays results images when told to', async () => {
200
236
  const el = await fixture(container(results));
201
237
  el.displayResultImages = true;
202
238
  await el.updateComplete;
203
239
 
204
- expect(el.shadowRoot.querySelector('.results.show-image')).to.exist;
240
+ expect(el.shadowRoot.querySelector('.results.show-image')).toBeDefined();
205
241
  });
206
242
 
207
243
  describe('search input focus', () => {
208
- it('will always try to re-focus once the component updates', async () => {
244
+ test('will always try to re-focus once the component updates', async () => {
209
245
  const el = await fixture(container(results));
210
246
  el.focusOnInputIfNecessary = sinon.fake();
211
247
  // update any property to fire lifecycle
212
248
  el.results = [];
213
249
  await el.updateComplete;
214
250
 
215
- expect(el.focusOnInputIfNecessary.callCount).to.equal(1);
251
+ expect(el.focusOnInputIfNecessary.callCount).toEqual(1);
216
252
  });
217
- it('refocuses on input when results are empty', async () => {
253
+ test('refocuses on input when results are empty', async () => {
218
254
  const el = await fixture(container(results));
219
255
  el.results = [];
220
256
  await el.updateComplete;
221
257
 
222
258
  const searchInputField = el.shadowRoot.querySelector('input[type=\'search\']');
223
- expect(searchInputField).to.equal(el.shadowRoot.activeElement);
259
+ expect(searchInputField).toEqual(el.shadowRoot.activeElement);
224
260
  });
225
261
  });
226
262
 
227
- // it("emits a bookSearchCanceled event when loading state's cancel action clicked", async () => {
228
- // const el = await fixture(container(results));
263
+ test("emits a bookSearchCanceled event when loading state's cancel action clicked", async () => {
264
+ const el = await fixture(container(results));
229
265
 
230
- // el.queryInProgress = true;
231
- // await el.updateComplete;
266
+ el.queryInProgress = true;
267
+ await el.updateComplete;
232
268
 
233
- // setTimeout(() => (
234
- // el.shadowRoot.querySelector('button').click()
235
- // ));
236
- // const response = await oneEvent(el, 'bookSearchCanceled');
269
+ setTimeout(() => (
270
+ el.shadowRoot.querySelector('button').click()
271
+ ));
272
+ const response = await oneEvent(el, 'bookSearchCanceled');
237
273
 
238
- // expect(response).to.exist;
239
- // });
274
+ expect(response).toBeDefined();
275
+ });
276
+ it('cancels search when input is cleared', async () => {
277
+ const el = await fixture(container(results));
278
+
279
+ el.cancelSearch = sinon.fake();
280
+ await el.updateComplete;
281
+
282
+ const searchInput = el.shadowRoot.querySelector('[name="query"]');
283
+
284
+ searchInput.value = '';
285
+ searchInput.dispatchEvent(new Event('search'));
286
+
287
+ expect(el.cancelSearch.callCount).toEqual(1);
288
+ });
240
289
  });
@@ -0,0 +1,49 @@
1
+ import { fixtureSync } from '@open-wc/testing-helpers';
2
+ import sinon from 'sinon';
3
+ import SharingProvider from '@/src/BookNavigator/sharing.js';
4
+
5
+ afterEach(() => {
6
+ sinon.restore();
7
+ });
8
+
9
+ const item = {
10
+ metadata: {
11
+ identifier: 'stubby-id',
12
+ creator: 'mr. big',
13
+ title: 'Stubby title',
14
+ }
15
+ };
16
+
17
+ const baseHost = 'foo.org';
18
+ const subPrefix = 'beep-boop_12 4 5';
19
+
20
+ describe('Sharing Provider', () => {
21
+ describe('constructor', () => {
22
+ const provider = new SharingProvider({
23
+ item,
24
+ baseHost,
25
+ bookreader: {
26
+ options: { subPrefix }
27
+ }
28
+ });
29
+
30
+ expect(provider.id).toEqual('share');
31
+ expect(provider.icon).toBeDefined();
32
+ expect(provider.label).toEqual('Share this book');
33
+ expect(fixtureSync(provider.component).tagName).toContain('IAUX-IN-SHARE-PANEL');
34
+ });
35
+
36
+ describe('Handles being a sub file/volume', () => {
37
+ test('encodes the subprefix if it has one', async () => {
38
+ const provider = new SharingProvider({
39
+ item,
40
+ baseHost,
41
+ bookreader: {
42
+ options: { subPrefix }
43
+ }
44
+ });
45
+
46
+ expect(fixtureSync(provider.component).fileSubPrefix).toEqual(subPrefix);
47
+ });
48
+ });
49
+ });
@@ -0,0 +1,80 @@
1
+ import { fixtureCleanup, fixtureSync } from '@open-wc/testing-helpers';
2
+ import sinon from 'sinon';
3
+ import ViewableFilesProvider from '@/src/BookNavigator/viewable-files';
4
+
5
+ const brOptions = {
6
+ "options": {
7
+ "subPrefix": 'special-subprefix',
8
+ "enableMultipleBooks": true,
9
+ "multipleBooksList": {
10
+ "by_subprefix": {
11
+ "/details/SubBookTest": {
12
+ "url_path": "/details/SubBookTest",
13
+ "file_subprefix": "book1/GPORFP",
14
+ "orig_sort": 1,
15
+ "title": "book1/GPORFP.pdf",
16
+ "file_source": "/book1/GPORFP_jp2.zip"
17
+ },
18
+ "/details/SubBookTest/subdir/book2/brewster_kahle_internet_archive": {
19
+ "url_path": "/details/SubBookTest/subdir/book2/brewster_kahle_internet_archive",
20
+ "file_subprefix": "subdir/book2/brewster_kahle_internet_archive",
21
+ "orig_sort": 2,
22
+ "title": "subdir/book2/brewster_kahle_internet_archive.pdf",
23
+ "file_source": "/subdir/book2/brewster_kahle_internet_archive_jp2.zip"
24
+ },
25
+ "/details/SubBookTest/subdir/subsubdir/book3/Rfp008011ResponseInternetArchive-without-resume": {
26
+ "url_path": "/details/SubBookTest/subdir/subsubdir/book3/Rfp008011ResponseInternetArchive-without-resume",
27
+ "file_subprefix": "subdir/subsubdir/book3/Rfp008011ResponseInternetArchive-without-resume",
28
+ "orig_sort": 3,
29
+ "title": "subdir/subsubdir/book3/Rfp008011ResponseInternetArchive-without-resume.pdf",
30
+ "file_source": "/subdir/subsubdir/book3/Rfp008011ResponseInternetArchive-without-resume_jp2.zip"
31
+ }
32
+ }
33
+ }
34
+ }
35
+ };
36
+
37
+ afterEach(() => {
38
+ sinon.restore();
39
+ fixtureCleanup();
40
+ });
41
+
42
+ describe('Volumes Provider', () => {
43
+ test('initiating & sorting', () => {
44
+ const onProviderChange = sinon.fake();
45
+
46
+ const baseHost = "https://archive.org";
47
+ const provider = new ViewableFilesProvider({
48
+ baseHost,
49
+ bookreader: brOptions,
50
+ onProviderChange
51
+ });
52
+
53
+ const files = brOptions.options.multipleBooksList.by_subprefix;
54
+ const volumeCount = Object.keys(files).length;
55
+
56
+ expect(provider.onProviderChange).toEqual(onProviderChange);
57
+ expect(provider.id).toEqual('volumes');
58
+ expect(provider.icon).toBeDefined();
59
+ expect(fixtureSync(provider.icon).tagName).toEqual('svg');
60
+ expect(provider.sortOrderBy).toEqual('default');
61
+
62
+ expect(provider.label).toEqual(`Viewable files (${volumeCount})`);
63
+ expect(provider.viewableFiles).toBeDefined();
64
+ expect(provider.viewableFiles.length).toEqual(3);
65
+ expect(provider.volumeCount).toEqual(3);
66
+
67
+ expect(provider.component.baseHost).toEqual(baseHost);
68
+ expect(provider.component.fileList).toEqual(provider.viewableFiles);
69
+ expect(provider.component.subPrefix).toEqual(brOptions.options.subPrefix);
70
+
71
+ expect(provider.actionButton).toBeDefined();
72
+ expect(provider.actionButton).toEqual(provider.sortFilesComponent);
73
+ expect(provider.actionButton.fileListRaw).toEqual(provider.viewableFiles);
74
+
75
+ const callbackSpy = sinon.spy(provider, 'handleFileListSorted');
76
+ provider.actionButton.sortVolumes('title_asc');
77
+
78
+ expect(callbackSpy.callCount).toEqual(1);
79
+ });
80
+ });