@internetarchive/bookreader 5.0.0-37 → 5.0.0-38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # 5.0.0-38
2
+ Dev: Add Renovate Bot @cdrini
3
+ Dev: Update node-fetch @cdrini
4
+ Fix: Search request promise err & fix tests @cdrini
5
+ Dev: Split node workflow into different jobs @cdrini
6
+ Dev: Give cache steps better names in GHA @cdrini
7
+ Dev: Update concurrently + Small speedup to build & test @cdrini
8
+ Dev: Renovate - Auto-update dev dependencies for minor/patch @cdrini
9
+ Fix: Better MS Edge voice selection @cdrini
10
+ Dev: Allow small drops in codecov coverage (< 0.5%) @cdrini
11
+ Dev: Renovate - add `^@internetarchive/icon-` @cdrini
12
+
1
13
  # 5.0.0-37
2
14
  Fix: Update all `.then()` to async/await @sancodes
3
15
  Fix: Upgrade to Lit 2 @Aadilhassan
package/codecov.yml CHANGED
@@ -1,6 +1,12 @@
1
1
  codecov:
2
2
  notify:
3
3
  require_ci_to_pass: yes
4
+ status:
5
+ project:
6
+ default:
7
+ # Allow small drops in coverage
8
+ threshold: 0.005 # .5 %
9
+ if_not_found: failure
4
10
 
5
11
  coverage:
6
12
  precision: 2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-37",
3
+ "version": "5.0.0-38",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,25 +41,25 @@
41
41
  "lit": "^2.1.3"
42
42
  },
43
43
  "devDependencies": {
44
- "@babel/core": "7.15.0",
45
- "@babel/eslint-parser": "^7.16.5",
46
- "@babel/plugin-proposal-class-properties": "^7.14.5",
47
- "@babel/plugin-proposal-decorators": "^7.14.5",
48
- "@babel/preset-env": "7.15.0",
44
+ "@babel/core": "7.17.5",
45
+ "@babel/eslint-parser": "7.17.0",
46
+ "@babel/plugin-proposal-class-properties": "7.16.7",
47
+ "@babel/plugin-proposal-decorators": "7.17.2",
48
+ "@babel/preset-env": "7.16.11",
49
49
  "@open-wc/testing": "^3.0.4",
50
50
  "@open-wc/testing-karma": "^4.0.9",
51
51
  "@types/jest": "^27.4.0",
52
52
  "@webcomponents/webcomponentsjs": "^2.6.0",
53
- "babel-loader": "8.2.2",
53
+ "babel-loader": "8.2.3",
54
54
  "codecov": "^3.8.3",
55
- "concurrently": "6.0.2",
55
+ "concurrently": "7.0.0",
56
56
  "core-js": "3.16.2",
57
57
  "cpx2": "3.0.0",
58
58
  "eslint": "^7.32.0",
59
59
  "eslint-plugin-no-jquery": "^2.7.0",
60
60
  "eslint-plugin-testcafe": "^0.2.1",
61
61
  "hammerjs": "^2.0.8",
62
- "http-server": "0.12.3",
62
+ "http-server": "14.1.0",
63
63
  "iso-language-codes": "1.1.0",
64
64
  "jest": "^27.4.7",
65
65
  "jquery": "1.11.3",
@@ -70,7 +70,7 @@
70
70
  "jquery.mmenu": "5.6.5",
71
71
  "karma-coverage": "^2.1.0",
72
72
  "live-server": "1.2.1",
73
- "node-fetch": "2.6.1",
73
+ "node-fetch": "2.6.7",
74
74
  "regenerator-runtime": "0.13.9",
75
75
  "sass": "1.38.2",
76
76
  "sinon": "^12.0.1",
@@ -102,7 +102,7 @@
102
102
  "preversion": "npm run test && node scripts/preversion.js",
103
103
  "version": "node scripts/version.js",
104
104
  "postversion": "node scripts/postversion.js",
105
- "build": "npm run clean && npm run build-js && npm run build-css && npm run build-assets",
105
+ "build": "npm run clean && npx concurrently --group npm:build-js npm:build-css npm:build-assets",
106
106
  "build-assets": "npx cpx \"src/assets/**/*\" BookReader && npx svgo -f BookReader/icons && npx svgo -f BookReader/images",
107
107
  "build-assets:watch": "npx cpx --watch --verbose \"src/assets/**/*\" BookReader",
108
108
  "build-js": "npx webpack",
@@ -115,7 +115,7 @@
115
115
  "serve": "npx http-server . --port 8000",
116
116
  "serve-live": "npx live-server . --port 8000 --watch=index.html,BookReader,BookReaderDemo",
117
117
  "serve-dev": "npm run build-css && npx concurrently --kill-others npm:serve-live npm:build-*:watch",
118
- "test": "npm run test-jest && npm run test-karma",
118
+ "test": "npx concurrently --group npm:test-jest npm:test-karma",
119
119
  "test:e2e": "npm run build && npx testcafe",
120
120
  "test:e2e:dev": "npx testcafe --live --dev",
121
121
  "test-jest:watch": "npx jest --watch",
package/renovate.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "extends": [
3
+ "config:base"
4
+ ],
5
+ "packageRules": [
6
+ {
7
+ "matchPackageNames": [
8
+ "@babel/eslint-parser",
9
+ "@open-wc/testing",
10
+ "@open-wc/testing-karma",
11
+ "@types/jest",
12
+ "codecov",
13
+ "eslint",
14
+ "eslint-plugin-no-jquery",
15
+ "eslint-plugin-testcafe",
16
+ "jest",
17
+ "karma-coverage",
18
+ "sinon",
19
+ "testcafe"
20
+ ],
21
+ "automerge": true
22
+ },
23
+ {
24
+ "matchPackageNames": [
25
+ "concurrently",
26
+ "http-server",
27
+ "live-server",
28
+ "node-fetch"
29
+ ],
30
+ "matchUpdateTypes": ["minor", "patch"],
31
+ "automerge": true
32
+ },
33
+ {
34
+ "matchPackagePatterns": ["^@internetarchive/icon-"],
35
+ "groupName": "@internetarchive icons",
36
+ "rangeStrategy": "bump"
37
+ },
38
+ {
39
+ "matchPackagePatterns": ["^@internetarchive"],
40
+ "rangeStrategy": "bump"
41
+ }
42
+ ]
43
+ }
@@ -144,6 +144,7 @@ BookReader.prototype.search = async function(term = '', overrides = {}) {
144
144
  };
145
145
  const options = jQuery.extend({}, defaultOptions, overrides);
146
146
  this.suppressFragmentChange = options.suppressFragmentChange;
147
+ this.searchCancelled = false;
147
148
 
148
149
  // strip slashes, since this goes in the url
149
150
  this.searchTerm = term.replace(/\//g, ' ');
@@ -179,13 +180,8 @@ BookReader.prototype.search = async function(term = '', overrides = {}) {
179
180
 
180
181
  const url = `${baseUrl}${paramStr}`;
181
182
 
182
- const cleanup = () => {
183
- this.searchXHR = null;
184
- window.BRSearchInProgress = () => {};
185
- };
186
-
187
183
  const processSearchResults = (searchInsideResults) => {
188
- if (!this.searchXHR) {
184
+ if (this.searchCancelled) {
189
185
  return;
190
186
  }
191
187
  const responseHasError = searchInsideResults.error || !searchInsideResults.matches.length;
@@ -201,12 +197,6 @@ BookReader.prototype.search = async function(term = '', overrides = {}) {
201
197
  ? options.success.call(this, searchInsideResults, options)
202
198
  : this.BRSearchCallback(searchInsideResults, options);
203
199
  }
204
- cleanup();
205
- };
206
-
207
- const beforeSend = (xhr) => {
208
- this.searchXHR = xhr;
209
- window.BRSearchInProgress = processSearchResults;
210
200
  };
211
201
 
212
202
  this.trigger('SearchStarted', { term: this.searchTerm, instance: this });
@@ -214,8 +204,7 @@ BookReader.prototype.search = async function(term = '', overrides = {}) {
214
204
  url: url,
215
205
  dataType: 'jsonp',
216
206
  cache: true,
217
- beforeSend,
218
- jsonpCallback: 'BRSearchInProgress'
207
+ beforeSend: xhr => { this.searchXHR = xhr; },
219
208
  }));
220
209
  };
221
210
 
@@ -228,8 +217,8 @@ BookReader.prototype._cancelSearch = function () {
228
217
  this.searchView.clearSearchFieldAndResults(false);
229
218
  this.searchTerm = '';
230
219
  this.searchXHR = null;
220
+ this.searchCancelled = true;
231
221
  this.searchResults = [];
232
- window.BRSearchInProgress = () => {};
233
222
  };
234
223
 
235
224
  /**
@@ -237,6 +226,7 @@ BookReader.prototype._cancelSearch = function () {
237
226
  * checks for term & xhr in flight before running
238
227
  */
239
228
  BookReader.prototype.cancelSearchRequest = function () {
229
+ this.searchCancelled = true;
240
230
  if (this.searchXHR !== null) {
241
231
  this._cancelSearch();
242
232
  this.searchView.toggleSearchPending();
@@ -149,10 +149,12 @@ class SearchView {
149
149
  }
150
150
 
151
151
  updateResultsPosition() {
152
+ if (!this.dom.searchNavigation) return;
152
153
  this.dom.searchNavigation.find('[data-id=resultsCount]').text(this.resultsPosition());
153
154
  }
154
155
 
155
156
  updateSearchNavigationButtons() {
157
+ if (!this.dom.searchNavigation) return;
156
158
  this.dom.searchNavigation.find('.prev').attr('disabled', !this.currentMatchIndex);
157
159
  this.dom.searchNavigation.find('.next').attr('disabled', this.currentMatchIndex + 1 === this.matches.length);
158
160
  }
@@ -51,9 +51,7 @@ export default class AbstractTTSEngine {
51
51
  /** @type {SpeechSynthesisVoice} */
52
52
  this.voice = null;
53
53
  // Listen for voice changes (fired by subclasses)
54
- this.events.on('voiceschanged', () => {
55
- this.voice = AbstractTTSEngine.getBestBookVoice(this.getVoices(), this.opts.bookLanguage);
56
- });
54
+ this.events.on('voiceschanged', this.updateBestVoice);
57
55
  this.events.trigger('voiceschanged');
58
56
  }
59
57
 
@@ -72,6 +70,10 @@ export default class AbstractTTSEngine {
72
70
  /** @abstract */
73
71
  init() { return null; }
74
72
 
73
+ updateBestVoice = () => {
74
+ this.voice = AbstractTTSEngine.getBestBookVoice(this.getVoices(), this.opts.bookLanguage);
75
+ }
76
+
75
77
  /**
76
78
  * @param {number} leafIndex
77
79
  * @param {number} numLeafs total number of leafs in the current book
@@ -134,8 +136,11 @@ export default class AbstractTTSEngine {
134
136
  this.step();
135
137
  }
136
138
 
137
- /** @param {number} newRate */
139
+ /** @param {string} voiceURI */
138
140
  setVoice(voiceURI) {
141
+ // if the user actively selects a voice, don't re-choose best voice anymore
142
+ // MS Edge fires voices changed randomly very often
143
+ this.events.off('voiceschanged', this.updateBestVoice);
139
144
  this.voice = this.getVoices().find(voice => voice.voiceURI === voiceURI);
140
145
  if (this.activeSound) this.activeSound.setVoice(this.voice);
141
146
  }
@@ -1,3 +1,44 @@
1
+ /*
2
+ <script src="foo-plugin.js"></script>
3
+
4
+ foo-plugin.js
5
+
6
+ $('#foo-plugin').dataSet('MainController', 'bar');
7
+
8
+ <ia-bookreader>
9
+ <div slot="plugins">
10
+ <ia-search></ia-search>
11
+ <book-marks></book-marks>
12
+ </div>
13
+ </ia-bookreader>
14
+
15
+ iaBookreader.registerPlugin('foo-plugin', Class);
16
+
17
+
18
+ class IABr extends LItElement {
19
+
20
+ render() {
21
+
22
+ registerPlugins() {
23
+ this.pluginSlots.map(slot => {
24
+ .. to slot registry
25
+ each slot - do handshake
26
+
27
+
28
+ });
29
+ }
30
+
31
+ html`
32
+ <div slot="plugins" @onslotchange=${() => x}></div>
33
+ `;
34
+ }
35
+ }
36
+ */
37
+
38
+
39
+
40
+
41
+
1
42
  import { css, html, LitElement } from 'lit-element';
2
43
  import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
3
44
  import SearchProvider from './search/search-provider.js';
@@ -113,6 +154,7 @@ export class BookNavigator extends LitElement {
113
154
  // };
114
155
 
115
156
  this.menuProviders = {
157
+ // if enableSearch ?
116
158
  search: new SearchProvider(
117
159
  /**
118
160
  * Search specific menu updates
@@ -1,6 +1,6 @@
1
1
  import { runBaseTests } from './helpers/base';
2
2
  import BookReader from './models/BookReader';
3
- // import { runDesktopSearchTests } from './helpers/desktopSearch';
3
+ import { runDesktopSearchTests } from './helpers/desktopSearch';
4
4
  // import { runMobileSearchTests } from './helpers/mobileSearch';
5
5
  import params from './helpers/params';
6
6
 
@@ -22,10 +22,9 @@ ocaids.forEach(ocaid => {
22
22
  runBaseTests(new BookReader());
23
23
 
24
24
 
25
- // Todo: Re-enable when testing side panel
26
- // fixture `Desktop Search Tests for: ${ocaid}`
27
- // .page `${url}`;
28
- // runDesktopSearchTests(new BookReader());
25
+ fixture `Desktop Search Tests for: ${ocaid}`
26
+ .page `${url}`;
27
+ runDesktopSearchTests(new BookReader());
29
28
 
30
29
  // Todo: deprecated, will remove once mmenu is removed.
31
30
  // fixture `Mobile Search Tests for: ${ocaid}`
@@ -19,14 +19,15 @@ export function runDesktopSearchTests(br) {
19
19
  const nav = br.nav;
20
20
 
21
21
  //assuring that the search bar is enabled
22
- await t.expect(nav.desktop.searchBox.visible).ok();
22
+ await t.expect(nav.desktop.searchIcon.visible).ok();
23
+ await t.click(nav.desktop.searchIcon);
23
24
 
24
25
  //testing search for a word found in the book
25
- await t
26
- .selectText(nav.desktop.searchBox.child('.BRsearchInput'))
27
- .pressKey('delete');
28
- await t.typeText(nav.desktop.searchBox.child('.BRsearchInput'), TEST_TEXT_FOUND);
29
- await t.click((nav.desktop.searchBox).child('.BRsearchSubmit'));
26
+ await t.selectText(nav.desktop.searchBox).pressKey('delete');
27
+ // FIXME: Why is it only typing every other letter?!?!
28
+ await t.typeText(nav.desktop.searchBox, TEST_TEXT_FOUND.split('').join('_'));
29
+ await t.pressKey('enter');
30
+
30
31
  await t.expect(nav.desktop.searchPin.exists).ok();
31
32
  await t.expect(nav.desktop.searchPin.child('.BRquery').child('div').exists).ok();
32
33
  await t.expect(nav.desktop.searchPin.child('.BRquery').child('div').innerText).contains(TEST_TEXT_FOUND);
@@ -54,14 +55,14 @@ export function runDesktopSearchTests(br) {
54
55
  const nav = br.nav;
55
56
 
56
57
  //assuring that the search bar is enabled
57
- await t.expect(nav.desktop.searchBox.visible).ok();
58
+ await t.expect(nav.desktop.searchIcon.visible).ok();
59
+ await t.click(nav.desktop.searchIcon);
58
60
 
59
61
  //testing search for a word not found in the book
60
- await t
61
- .selectText(nav.desktop.searchBox.child('.BRsearchInput'))
62
- .pressKey('delete');
63
- await t.typeText(nav.desktop.searchBox.child('.BRsearchInput'), TEST_TEXT_NOT_FOUND);
64
- await t.click((nav.desktop.searchBox).child('.BRsearchSubmit'));
62
+ await t.selectText(nav.desktop.searchBox).pressKey('delete');
63
+ // FIXME: Why is it only typing every other letter?!?!
64
+ await t.typeText(nav.desktop.searchBox, TEST_TEXT_NOT_FOUND.split('').join('_'));
65
+ await t.pressKey('enter');
65
66
  await t.expect(nav.desktop.searchPin.child('.BRquery').child('div').withText(TEST_TEXT_NOT_FOUND).exists).notOk();
66
67
 
67
68
  const getPageUrl = ClientFunction(() => window.location.href.toString());
@@ -6,7 +6,8 @@ export default class Navigation {
6
6
  this.topNavShell = new Selector('.BRtoolbar');
7
7
  this.bottomNavShell = new Selector('.BRfooter');
8
8
  this.mobileMenu = new Selector('.BRmobileMenu');
9
- this.desktop = new DesktopNav(this.bottomNavShell, this.topNavShell);
9
+ this.itemNav = Selector('ia-bookreader').shadowRoot().find('ia-item-navigator').shadowRoot();
10
+ this.desktop = new DesktopNav(this.bottomNavShell, this.itemNav);
10
11
  this.mobile = new MobileNav(this.mobileMenu, this.topNavShell);
11
12
  }
12
13
  }
@@ -17,7 +18,11 @@ export default class Navigation {
17
18
  * @classdesc defines DesktopNav base elements
18
19
  */
19
20
  class DesktopNav {
20
- constructor(bottomToolbar, topToolbar) {
21
+ /**
22
+ * @param {Selector} bottomToolbar
23
+ * @param {Selector} itemNav
24
+ */
25
+ constructor(bottomToolbar, itemNav) {
21
26
  // flipping
22
27
  this.goLeft = bottomToolbar.find('.BRicon.book_left');
23
28
  this.goRight = bottomToolbar.find('.BRicon.book_right');
@@ -35,7 +40,11 @@ class DesktopNav {
35
40
  this.zoomOut = bottomToolbar.find('.BRicon.zoom_out');
36
41
 
37
42
  // search
38
- this.searchBox = topToolbar.find('.BRbooksearch.desktop');
43
+ this.searchIcon = itemNav.find('button.shortcut.search');
44
+ this.searchBox = itemNav
45
+ .find('ia-menu-slider').shadowRoot()
46
+ .find('ia-book-search-results').shadowRoot()
47
+ .find('input[name=query]');
39
48
  this.searchPin = bottomToolbar.find('.BRsearch');
40
49
  this.searchNavigation = bottomToolbar.find('.BRsearch-navigation');
41
50
 
@@ -9,7 +9,7 @@ const ocaids = params.ocaids || [
9
9
  ];
10
10
 
11
11
  ocaids.forEach(ocaid => {
12
- const url = `${params.baseUrl}/BookReaderDemo/demo-internetarchive.html?ocaid=${ocaid}`;
12
+ const url = `${params.getArchiveUrl(ocaid)}`;
13
13
 
14
14
  fixture `Base Tests for right to left book: ${ocaid}`.page `${url}`;
15
15
  runBaseTests(new BookReader({ pageProgression: 'rl' }));
@@ -2,35 +2,41 @@ import { Selector } from 'testcafe';
2
2
  import BookReader from './models/BookReader';
3
3
  import params from './helpers/params';
4
4
 
5
- fixture `Viewmode carousel`.page `${params.baseUrl}/BookReaderDemo/demo-internetarchive.html?ocaid=goody`;
6
-
7
- test('Clicking `view mode` cycles through view modes', async t => {
8
- const { nav } = (new BookReader());
9
-
10
- // viewmode button only appear on mobile devices
11
- await t.resizeWindow(400, 800);
12
- // Flip forward one
13
- await t.pressKey('right');
14
-
15
- // 2up to thumb
16
- await t.click(nav.desktop.viewmode);
17
- const thumbnailContainer = Selector('.BRmodeThumb');
18
- await t.expect(thumbnailContainer.visible).ok();
19
- const thumbImages = thumbnailContainer.find('.BRpageview img');
20
- await t.expect(thumbImages.count).gt(0);
21
-
22
- // thumb to 1up
23
- await t.click(nav.desktop.viewmode);
24
- const onePageViewContainer = Selector('br-mode-1up');
25
- await t.expect(onePageViewContainer.visible).ok();
26
- const onePageImages = onePageViewContainer.find('.BRmode1up .BRpagecontainer');
27
- // we usually pre-fetch the page in question & 1 before/after it
28
- await t.expect(onePageImages.count).gte(3);
29
-
30
- // 1up to 2up
31
- await t.click(nav.desktop.viewmode);
32
- const twoPageContainer = Selector('.BRtwopageview');
33
- await t.expect(twoPageContainer.visible).ok();
34
- const twoPageImages = twoPageContainer.find('img.BRpageimage');
35
- await t.expect(twoPageImages.count).gte(2);
5
+ const ocaids = params.ocaids || ['goody'];
6
+
7
+ ocaids.forEach(ocaid => {
8
+ const url = params.getArchiveUrl(ocaid);
9
+
10
+ fixture `Viewmode carousel`.page `${url}`;
11
+
12
+ test('Clicking `view mode` cycles through view modes', async t => {
13
+ const { nav } = (new BookReader());
14
+
15
+ // viewmode button only appear on mobile devices
16
+ await t.resizeWindow(400, 800);
17
+ // Flip forward one
18
+ await t.pressKey('right');
19
+
20
+ // 2up to thumb
21
+ await t.click(nav.desktop.viewmode);
22
+ const thumbnailContainer = Selector('.BRmodeThumb');
23
+ await t.expect(thumbnailContainer.visible).ok();
24
+ const thumbImages = thumbnailContainer.find('.BRpageview img');
25
+ await t.expect(thumbImages.count).gt(0);
26
+
27
+ // thumb to 1up
28
+ await t.click(nav.desktop.viewmode);
29
+ const onePageViewContainer = Selector('br-mode-1up');
30
+ await t.expect(onePageViewContainer.visible).ok();
31
+ const onePageImages = onePageViewContainer.find('.BRmode1up .BRpagecontainer');
32
+ // we usually pre-fetch the page in question & 1 before/after it
33
+ await t.expect(onePageImages.count).gte(3);
34
+
35
+ // 1up to 2up
36
+ await t.click(nav.desktop.viewmode);
37
+ const twoPageContainer = Selector('.BRtwopageview');
38
+ await t.expect(twoPageContainer.visible).ok();
39
+ const twoPageImages = twoPageContainer.find('img.BRpageimage');
40
+ await t.expect(twoPageImages.count).gte(2);
41
+ });
36
42
  });
@@ -1,8 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: npm
4
- directory: "/"
5
- schedule:
6
- interval: monthly
7
- time: "13:00"
8
- open-pull-requests-limit: 10