@internetarchive/bookreader 5.0.0-6-14 → 5.0.0-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (271) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +72 -10
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +241 -140
  6. package/BookReader/BookReader.js +1 -1
  7. package/BookReader/BookReader.js.LICENSE.txt +24 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1519 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +17 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/close-circle-dark.svg +1 -0
  13. package/BookReader/icons/magnify-minus.svg +1 -1
  14. package/BookReader/icons/magnify-plus.svg +1 -1
  15. package/BookReader/icons/pause.svg +1 -1
  16. package/BookReader/icons/playback-speed.svg +1 -1
  17. package/BookReader/icons/read-aloud.svg +1 -1
  18. package/BookReader/icons/voice.svg +1 -0
  19. package/BookReader/images/BRicons.svg +2 -2
  20. package/BookReader/images/books_graphic.svg +1 -1
  21. package/BookReader/images/icon_book.svg +1 -1
  22. package/BookReader/images/icon_gear.svg +1 -1
  23. package/BookReader/images/icon_info.svg +1 -1
  24. package/BookReader/images/icon_playback-rate.svg +1 -1
  25. package/BookReader/images/icon_search_button.svg +1 -1
  26. package/BookReader/images/icon_share.svg +1 -1
  27. package/BookReader/images/icon_speaker.svg +1 -1
  28. package/BookReader/images/icon_speaker_open.svg +1 -1
  29. package/BookReader/images/marker_chap-off.svg +1 -1
  30. package/BookReader/images/marker_chap-on.svg +1 -1
  31. package/BookReader/images/marker_srch-on.svg +1 -1
  32. package/BookReader/jquery-3.js +2 -0
  33. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  34. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  35. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  36. package/BookReader/plugins/plugin.autoplay.js +1 -1
  37. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  38. package/BookReader/plugins/plugin.chapters.js +1 -1
  39. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  40. package/BookReader/plugins/plugin.iframe.js +1 -1
  41. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  42. package/BookReader/plugins/plugin.mobile_nav.js +1 -1
  43. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  44. package/BookReader/plugins/plugin.resume.js +1 -1
  45. package/BookReader/plugins/plugin.resume.js.map +1 -1
  46. package/BookReader/plugins/plugin.search.js +1 -1
  47. package/BookReader/plugins/plugin.search.js.map +1 -1
  48. package/BookReader/plugins/plugin.text_selection.js +1 -1
  49. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  50. package/BookReader/plugins/plugin.tts.js +1 -1
  51. package/BookReader/plugins/plugin.tts.js.map +1 -1
  52. package/BookReader/plugins/plugin.url.js +1 -1
  53. package/BookReader/plugins/plugin.url.js.map +1 -1
  54. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  55. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  56. package/BookReader/webcomponents-bundle.js +3 -0
  57. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  58. package/BookReader/webcomponents-bundle.js.map +1 -0
  59. package/BookReaderDemo/BookReaderDemo.css +14 -1
  60. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  61. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  62. package/BookReaderDemo/IADemoBr.js +147 -0
  63. package/BookReaderDemo/demo-advanced.html +2 -2
  64. package/BookReaderDemo/demo-autoplay.html +2 -1
  65. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  66. package/BookReaderDemo/demo-fullscreen-mobile.html +2 -1
  67. package/BookReaderDemo/demo-fullscreen.html +2 -1
  68. package/BookReaderDemo/demo-iiif.html +2 -1
  69. package/BookReaderDemo/demo-internetarchive.html +84 -17
  70. package/BookReaderDemo/demo-multiple.html +2 -1
  71. package/BookReaderDemo/demo-preview-pages.html +2 -1
  72. package/BookReaderDemo/demo-simple.html +2 -1
  73. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -1
  74. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  75. package/BookReaderDemo/immersion-1up.html +2 -1
  76. package/BookReaderDemo/immersion-mode.html +2 -1
  77. package/BookReaderDemo/toggle_controls.html +2 -1
  78. package/BookReaderDemo/view_mode.html +2 -1
  79. package/BookReaderDemo/viewmode-cycle.html +2 -3
  80. package/CHANGELOG.md +244 -0
  81. package/README.md +14 -1
  82. package/babel.config.js +19 -0
  83. package/codecov.yml +6 -0
  84. package/index.html +3 -0
  85. package/jsconfig.json +19 -0
  86. package/netlify.toml +5 -0
  87. package/package.json +70 -59
  88. package/renovate.json +52 -0
  89. package/scripts/preversion.js +4 -1
  90. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  91. package/src/BookNavigator/assets/button-base.js +9 -2
  92. package/src/BookNavigator/assets/ia-logo.js +17 -0
  93. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  94. package/src/BookNavigator/assets/icon_close.js +1 -1
  95. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  96. package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
  97. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  98. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  99. package/src/BookNavigator/book-navigator.js +585 -0
  100. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  101. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  102. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  103. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  104. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  105. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  106. package/src/BookNavigator/delete-modal-actions.js +1 -1
  107. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  108. package/src/BookNavigator/downloads/downloads.js +41 -25
  109. package/src/BookNavigator/search/search-provider.js +80 -28
  110. package/src/BookNavigator/search/search-results.js +34 -25
  111. package/src/BookNavigator/sharing.js +27 -0
  112. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  113. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  114. package/src/BookNavigator/volumes/volumes-provider.js +111 -0
  115. package/src/BookNavigator/volumes/volumes.js +188 -0
  116. package/src/BookReader/BookModel.js +59 -30
  117. package/src/BookReader/DebugConsole.js +3 -3
  118. package/src/BookReader/DragScrollable.js +233 -0
  119. package/src/BookReader/Mode1Up.js +56 -351
  120. package/src/BookReader/Mode1UpLit.js +391 -0
  121. package/src/BookReader/Mode2Up.js +73 -1318
  122. package/src/BookReader/Mode2UpLit.js +781 -0
  123. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  124. package/src/BookReader/ModeSmoothZoom.js +211 -0
  125. package/src/BookReader/ModeThumb.js +17 -11
  126. package/src/BookReader/Navbar/Navbar.js +10 -36
  127. package/src/BookReader/PageContainer.js +69 -6
  128. package/src/BookReader/ReduceSet.js +1 -1
  129. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  130. package/src/BookReader/events.js +2 -0
  131. package/src/BookReader/options.js +24 -2
  132. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  133. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  134. package/src/BookReader/utils.js +108 -13
  135. package/src/BookReader.js +481 -828
  136. package/src/assets/icons/close-circle-dark.svg +1 -0
  137. package/src/assets/icons/magnify-minus.svg +3 -7
  138. package/src/assets/icons/magnify-plus.svg +3 -7
  139. package/src/assets/icons/voice.svg +1 -0
  140. package/src/css/_BRBookmarks.scss +1 -1
  141. package/src/css/_BRComponent.scss +1 -1
  142. package/src/css/_BRmain.scss +33 -0
  143. package/src/css/_BRnav.scss +4 -26
  144. package/src/css/_BRpages.scss +147 -40
  145. package/src/css/_BRsearch.scss +25 -11
  146. package/src/css/_TextSelection.scss +16 -17
  147. package/src/css/_colorbox.scss +2 -2
  148. package/src/css/_controls.scss +17 -5
  149. package/src/css/_icons.scss +7 -1
  150. package/src/ia-bookreader/ia-bookreader.js +224 -0
  151. package/src/plugins/plugin.archive_analytics.js +3 -3
  152. package/src/plugins/plugin.autoplay.js +4 -9
  153. package/src/plugins/plugin.chapters.js +28 -35
  154. package/src/plugins/plugin.mobile_nav.js +11 -10
  155. package/src/plugins/plugin.resume.js +3 -3
  156. package/src/plugins/plugin.text_selection.js +32 -41
  157. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  158. package/src/plugins/search/plugin.search.js +179 -103
  159. package/src/plugins/search/view.js +59 -44
  160. package/src/plugins/tts/AbstractTTSEngine.js +46 -37
  161. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  162. package/src/plugins/tts/PageChunk.js +15 -21
  163. package/src/plugins/tts/PageChunkIterator.js +8 -12
  164. package/src/plugins/tts/WebTTSEngine.js +87 -71
  165. package/src/plugins/tts/plugin.tts.js +94 -125
  166. package/src/plugins/tts/utils.js +0 -25
  167. package/src/plugins/url/UrlPlugin.js +193 -0
  168. package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
  169. package/src/util/docCookies.js +21 -2
  170. package/tests/e2e/README.md +37 -0
  171. package/tests/e2e/autoplay.test.js +2 -2
  172. package/tests/e2e/base.test.js +7 -7
  173. package/tests/e2e/helpers/base.js +28 -23
  174. package/tests/e2e/helpers/debug.js +1 -1
  175. package/tests/e2e/helpers/desktopSearch.js +14 -13
  176. package/tests/e2e/helpers/mobileSearch.js +3 -3
  177. package/tests/e2e/helpers/params.js +17 -0
  178. package/tests/e2e/helpers/rightToLeft.js +4 -10
  179. package/tests/e2e/models/Navigation.js +13 -4
  180. package/tests/e2e/rightToLeft.test.js +4 -5
  181. package/tests/e2e/viewmode.test.js +40 -33
  182. package/tests/jest/BookNavigator/book-navigator.test.js +658 -0
  183. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  184. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  185. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  186. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  187. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  188. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  189. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  190. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +104 -60
  191. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  192. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  193. package/tests/jest/BookNavigator/volumes/volumes-provider.test.js +184 -0
  194. package/tests/jest/BookNavigator/volumes/volumes.test.js +97 -0
  195. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +59 -14
  196. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  197. package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
  198. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  199. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  200. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  201. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  202. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  203. package/tests/jest/BookReader/ModeSmoothZoom.test.js +175 -0
  204. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  205. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +10 -10
  206. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  207. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  208. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  209. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  210. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  211. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  212. package/tests/jest/BookReader/utils.test.js +217 -0
  213. package/tests/jest/BookReader.keyboard.test.js +190 -0
  214. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  215. package/tests/{BookReader.test.js → jest/BookReader.test.js} +26 -37
  216. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  217. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  218. package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +10 -11
  219. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  220. package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
  221. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  222. package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +39 -47
  223. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  224. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +57 -47
  225. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +35 -6
  226. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +9 -9
  227. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  228. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  229. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  230. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  231. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  232. package/tests/jest/plugins/url/UrlPlugin.test.js +190 -0
  233. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
  234. package/tests/jest/setup.js +3 -0
  235. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  236. package/tests/jest/util/docCookies.test.js +24 -0
  237. package/tests/{util → jest/util}/strings.test.js +1 -1
  238. package/tests/{utils.js → jest/utils.js} +38 -0
  239. package/webpack.config.js +11 -5
  240. package/.babelrc +0 -12
  241. package/.dependabot/config.yml +0 -6
  242. package/.testcaferc.json +0 -5
  243. package/BookReader/bookreader-component-bundle.js +0 -1450
  244. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  245. package/BookReader/bookreader-component-bundle.js.map +0 -1
  246. package/BookReader/jquery-1.10.1.js +0 -2
  247. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  248. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  249. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  250. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  251. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  252. package/karma.conf.js +0 -23
  253. package/src/BookNavigator/BookModel.js +0 -14
  254. package/src/BookNavigator/BookNavigator.js +0 -438
  255. package/src/BookNavigator/assets/book-loader.js +0 -27
  256. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  257. package/src/BookNavigator/search/a-search-result.js +0 -55
  258. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  259. package/src/ItemNavigator/ItemNavigator.js +0 -372
  260. package/src/ItemNavigator/providers/sharing.js +0 -29
  261. package/src/dragscrollable-br.js +0 -261
  262. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  263. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  264. package/tests/BookReader/Mode1Up.test.js +0 -164
  265. package/tests/BookReader/Mode2Up.test.js +0 -247
  266. package/tests/BookReader/utils.test.js +0 -109
  267. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  268. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  269. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  270. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  271. 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,22 @@
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';
9
8
 
10
9
  const container = (results = [], query = '') => (
11
10
  html`<ia-book-search-results .results=${results} .query=${query}></ia-book-search-results>`
12
11
  );
13
12
 
14
- console.log("ia-book-search-results-test")
15
-
16
13
  const searchQuery = 'Bristol';
17
14
 
18
15
  const results = [{
19
16
  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
17
  cover: '//placehold.it/30x44',
21
18
  title: 'Book title',
19
+ displayPageNumber: 'Page 24',
22
20
  par: [{
23
21
  boxes: [{
24
22
  r: 2672, b: 792, t: 689, page: 24, l: 2424,
@@ -33,6 +31,7 @@ const results = [{
33
31
  }],
34
32
  }, {
35
33
  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`,
34
+ displayPageNumber: 'Page 86',
36
35
  par: [{
37
36
  boxes: [{
38
37
  r: 698, b: 4460, t: 4324, page: 86, l: 450,
@@ -47,69 +46,101 @@ const results = [{
47
46
  }],
48
47
  }];
49
48
 
49
+ const resultWithScript = [{
50
+ text: `foo bar <script>const msg = 'test' + ' failure'; document.write(msg);</script> {{{${searchQuery}}}} baz`,
51
+ cover: '//placehold.it/30x44',
52
+ title: 'Book title',
53
+ displayPageNumber: 'Page 24',
54
+ par: [{
55
+ boxes: [{
56
+ r: 2672, b: 792, t: 689, page: 24, l: 2424,
57
+ }],
58
+ b: 1371,
59
+ t: 689,
60
+ page_width: 3658,
61
+ r: 3192,
62
+ l: 428,
63
+ page_height: 5357,
64
+ page: 24,
65
+ }],
66
+ }];
67
+
50
68
  describe('<ia-book-search-results>', () => {
51
69
  afterEach(() => {
52
70
  sinon.restore();
53
71
  });
54
72
 
55
- it('sets default properties', async () => {
73
+ test('sets default properties', async () => {
56
74
  const query = 'bristol';
57
75
  const el = await fixture(container(results, query));
58
76
 
59
- expect(el.results).to.equal(results);
60
- expect(el.query).to.equal(query);
77
+ expect(el.results).toEqual(results);
78
+ expect(el.query).toEqual(query);
61
79
  });
62
80
 
63
- it('sets results when passed in via event object', async () => {
81
+ test('sets results when passed in via event object', async () => {
64
82
  const el = await fixture(container());
65
83
 
66
84
  el.setResults({ detail: { results } });
67
- expect(el.results).to.equal(results);
85
+ expect(el.results).toEqual(results);
68
86
  });
69
87
 
70
- it('listens for a custom search callback event on the document', async () => {
88
+ test('listens for a custom search callback event on the document', async () => {
71
89
  IABookSearchResults.prototype.setResults = sinon.fake();
72
90
  const el = await fixture(container());
73
91
  const event = new Event('BookReader:SearchCallback');
74
92
 
75
93
  event.detail = { results };
76
94
  document.dispatchEvent(event);
77
- expect(el.setResults.callCount).to.equal(1);
78
- expect(el.setResults.firstArg).to.equal(event);
95
+ expect(el.setResults.callCount).toEqual(1);
96
+ expect(el.setResults.firstArg).toEqual(event);
79
97
  });
80
98
 
81
- it('renders results that contain the book title', async () => {
99
+ test('renders results that contain the book title', async () => {
82
100
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
83
101
  const el = await fixture(container(results));
84
102
 
85
- expect(el.innerHTML).to.include(`${results[0].title}`);
103
+ expect(el.innerHTML).toContain(`${results[0].title}`);
86
104
  });
87
105
 
88
- it('renders results that contain a highlighted match', async () => {
106
+ test('renders results that contain a highlighted match', async () => {
89
107
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
90
108
  const el = await fixture(container(results));
91
109
 
92
- expect(el.innerHTML).to.include(`<mark>${searchQuery}</mark>`);
110
+ // Lit inserts HTML comments that inhibit searching for exact innerHTML matches.
111
+ // So query the DOM for the match instead.
112
+ const match = el.querySelector('mark');
113
+ expect(match?.textContent).toEqual(searchQuery);
93
114
  });
94
115
 
95
- it('renders results that contain an optional cover image', async () => {
116
+ test('renders results that contain sanitized HTML tags', async () => {
117
+ sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
118
+ // A result whose text contains a <script> tag that will insert 'test failure' into the element if not sanitized
119
+ const el = await fixture(container(resultWithScript));
120
+
121
+ const match = el.querySelector('mark');
122
+ expect(match?.textContent).toEqual(searchQuery);
123
+ expect(el.innerHTML).not.toContain('test failure');
124
+ });
125
+
126
+ test('renders results that contain an optional cover image', async () => {
96
127
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
97
128
  const el = await fixture(container(results));
98
129
 
99
- expect(el.innerHTML).to.include(`<img src="${results[0].cover}">`);
130
+ expect(el.innerHTML).toContain(`<img src="${results[0].cover}">`);
100
131
  });
101
132
 
102
- it('sets a query prop when search input receives input', async () => {
133
+ test('sets a query prop when search input receives input', async () => {
103
134
  const el = await fixture(container(results));
104
135
  const searchInput = el.shadowRoot.querySelector('[name="query"]');
105
136
 
106
137
  searchInput.value = searchQuery;
107
138
  searchInput.dispatchEvent(new Event('keyup'));
108
139
 
109
- expect(el.query).to.equal(searchQuery);
140
+ expect(el.query).toEqual(searchQuery);
110
141
  });
111
142
 
112
- it('emits a custom event when search form submitted when input is populated', async () => {
143
+ test('emits a custom event when search form submitted when input is populated', async () => {
113
144
  const el = await fixture(container(results));
114
145
 
115
146
  setTimeout(() => {
@@ -119,122 +150,135 @@ describe('<ia-book-search-results>', () => {
119
150
  });
120
151
  const response = await oneEvent(el, 'bookSearchInitiated');
121
152
 
122
- expect(response).to.exist;
153
+ expect(response).toBeDefined();
123
154
  });
124
155
 
125
- it('uses a singular noun when one result given', async () => {
156
+ test('uses a singular noun when one result given', async () => {
126
157
  const el = await fixture(container([results[0]]));
127
158
  const resultsCount = await fixture(el.resultsCount);
128
159
 
129
- expect(resultsCount.innerHTML).to.include('1 result');
160
+ expect(resultsCount.innerHTML).toContain('1 result');
130
161
  });
131
162
 
132
- it('can render header with active options count', async () => {
163
+ test('can render header with active options count', async () => {
133
164
  const el = await fixture(container(results));
134
165
  el.renderHeader = true;
135
166
 
136
167
  await el.updateComplete;
137
168
 
138
- expect(el.shadowRoot.querySelector('header p').innerText).to.include('2');
169
+ expect(el.shadowRoot.querySelector('header p').textContent).toContain('2');
139
170
  });
140
171
 
141
- it('renders search all files checkbox when enabled', async () => {
172
+ test('renders search all files checkbox when enabled', async () => {
142
173
  const el = await fixture(container(results));
143
174
  el.renderSearchAllFiles = true;
144
175
 
145
176
  await el.updateComplete;
146
177
 
147
- expect(el.shadowRoot.querySelector('[name="all_files"]')).to.not.be.null;
178
+ expect(el.shadowRoot.querySelector('[name="all_files"]')).not.toBeNull();
148
179
  });
149
180
 
150
- it('emits a resultSelected event when a search result is clicked', async () => {
181
+ test('emits a resultSelected event when a search result is clicked', async () => {
151
182
  const el = await fixture(container(results));
152
183
 
153
184
  setTimeout(() => (
154
- el.shadowRoot.querySelector('book-search-result').querySelector('li').click()
185
+ el.shadowRoot.querySelector('li').click()
155
186
  ));
156
187
  const response = await oneEvent(el, 'resultSelected');
157
188
 
158
- expect(response).to.exist;
189
+ expect(response).toBeDefined();
159
190
  });
160
191
 
161
- it('emits a closeMenu event when a search result is clicked', async () => {
192
+ test('emits a closeMenu event when a search result is clicked', async () => {
162
193
  const el = await fixture(container(results));
163
194
 
164
195
  setTimeout(() => (
165
- el.shadowRoot.querySelector('book-search-result').querySelector('li').click()
196
+ el.shadowRoot.querySelector('li').click()
166
197
  ));
167
198
  const response = await oneEvent(el, 'closeMenu');
168
199
 
169
- expect(response).to.exist;
200
+ expect(response).toBeDefined();
170
201
  });
171
202
 
172
203
  describe('Search results placeholders', () => {
173
- it('renders a loading state when queryInProgress is true', async () => {
204
+ test('renders a loading state when queryInProgress is true', async () => {
174
205
  const el = await fixture(container(results));
175
206
 
176
207
  el.queryInProgress = true;
177
208
  await el.updateComplete;
178
209
 
179
- expect(el.shadowRoot.querySelector('.loading')).to.not.be.null;
210
+ expect(el.shadowRoot.querySelector('.loading')).not.toBeNull();
180
211
  });
181
- it('renders an error message when provided', async () => {
212
+ test('renders an error message when provided', async () => {
182
213
  const el = await fixture(container([]));
183
214
  const message = 'Sample error message';
184
215
  el.errorMessage = message;
185
216
  await el.updateComplete;
186
217
 
187
- expect(el.shadowRoot.querySelector('.error-message')).to.exist;
188
- expect(el.shadowRoot.querySelector('.search-cta')).to.be.null;
218
+ expect(el.shadowRoot.querySelector('.error-message')).toBeDefined();
219
+ expect(el.shadowRoot.querySelector('.search-cta')).toBeNull();
189
220
  });
190
- it('displays call to search when no results or search errors are showing', async () => {
221
+ test('displays call to search when no results or search errors are showing', async () => {
191
222
  const el = await fixture(container([]));
192
223
 
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;
224
+ expect(el.shadowRoot.querySelector('.search-cta')).toBeDefined();
225
+ expect(el.shadowRoot.querySelector('.error-message')).toBeNull();
226
+ expect(el.shadowRoot.querySelector('.results')).toBeNull();
196
227
  });
197
228
  });
198
229
 
199
- it('displays results images when told to', async () => {
230
+ test('displays results images when told to', async () => {
200
231
  const el = await fixture(container(results));
201
232
  el.displayResultImages = true;
202
233
  await el.updateComplete;
203
234
 
204
- expect(el.shadowRoot.querySelector('.results.show-image')).to.exist;
235
+ expect(el.shadowRoot.querySelector('.results.show-image')).toBeDefined();
205
236
  });
206
237
 
207
238
  describe('search input focus', () => {
208
- it('will always try to re-focus once the component updates', async () => {
239
+ test('will always try to re-focus once the component updates', async () => {
209
240
  const el = await fixture(container(results));
210
241
  el.focusOnInputIfNecessary = sinon.fake();
211
242
  // update any property to fire lifecycle
212
243
  el.results = [];
213
244
  await el.updateComplete;
214
245
 
215
- expect(el.focusOnInputIfNecessary.callCount).to.equal(1);
246
+ expect(el.focusOnInputIfNecessary.callCount).toEqual(1);
216
247
  });
217
- it('refocuses on input when results are empty', async () => {
248
+ test('refocuses on input when results are empty', async () => {
218
249
  const el = await fixture(container(results));
219
250
  el.results = [];
220
251
  await el.updateComplete;
221
252
 
222
253
  const searchInputField = el.shadowRoot.querySelector('input[type=\'search\']');
223
- expect(searchInputField).to.equal(el.shadowRoot.activeElement);
254
+ expect(searchInputField).toEqual(el.shadowRoot.activeElement);
224
255
  });
225
256
  });
226
257
 
227
- // it("emits a bookSearchCanceled event when loading state's cancel action clicked", async () => {
228
- // const el = await fixture(container(results));
258
+ test("emits a bookSearchCanceled event when loading state's cancel action clicked", async () => {
259
+ const el = await fixture(container(results));
229
260
 
230
- // el.queryInProgress = true;
231
- // await el.updateComplete;
261
+ el.queryInProgress = true;
262
+ await el.updateComplete;
232
263
 
233
- // setTimeout(() => (
234
- // el.shadowRoot.querySelector('button').click()
235
- // ));
236
- // const response = await oneEvent(el, 'bookSearchCanceled');
264
+ setTimeout(() => (
265
+ el.shadowRoot.querySelector('button').click()
266
+ ));
267
+ const response = await oneEvent(el, 'bookSearchCanceled');
237
268
 
238
- // expect(response).to.exist;
239
- // });
269
+ expect(response).toBeDefined();
270
+ });
271
+ it('cancels search when input is cleared', async () => {
272
+ const el = await fixture(container(results));
273
+
274
+ el.cancelSearch = sinon.fake();
275
+ await el.updateComplete;
276
+
277
+ const searchInput = el.shadowRoot.querySelector('[name="query"]');
278
+
279
+ searchInput.value = '';
280
+ searchInput.dispatchEvent(new Event('search'));
281
+
282
+ expect(el.cancelSearch.callCount).toEqual(1);
283
+ });
240
284
  });
@@ -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('IA-SHARING-OPTIONS');
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
+ });