@internetarchive/bookreader 5.0.0-42 → 5.0.0-43

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/.github/workflows/node.js.yml +14 -14
  2. package/.github/workflows/npm-publish.yml +4 -4
  3. package/BookReader/BookReader.css +13 -13
  4. package/BookReader/BookReader.js +1 -1
  5. package/BookReader/BookReader.js.map +1 -1
  6. package/BookReader/ia-bookreader-bundle.js +87 -87
  7. package/BookReader/ia-bookreader-bundle.js.map +1 -1
  8. package/BookReader/icons/pause.svg +1 -1
  9. package/BookReader/icons/playback-speed.svg +1 -1
  10. package/BookReader/icons/read-aloud.svg +1 -1
  11. package/BookReader/images/BRicons.svg +2 -2
  12. package/BookReader/images/books_graphic.svg +1 -1
  13. package/BookReader/images/icon_book.svg +1 -1
  14. package/BookReader/images/icon_gear.svg +1 -1
  15. package/BookReader/images/icon_info.svg +1 -1
  16. package/BookReader/images/icon_playback-rate.svg +1 -1
  17. package/BookReader/images/icon_search_button.svg +1 -1
  18. package/BookReader/images/icon_share.svg +1 -1
  19. package/BookReader/images/icon_speaker.svg +1 -1
  20. package/BookReader/images/icon_speaker_open.svg +1 -1
  21. package/BookReader/images/marker_chap-off.svg +1 -1
  22. package/BookReader/images/marker_chap-on.svg +1 -1
  23. package/BookReader/images/marker_srch-on.svg +1 -1
  24. package/BookReader/jquery-1.10.1.js +1 -1
  25. package/BookReader/jquery-1.10.1.js.LICENSE.txt +6 -6
  26. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  27. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  28. package/BookReader/plugins/plugin.resume.js +1 -1
  29. package/BookReader/plugins/plugin.resume.js.map +1 -1
  30. package/BookReader/plugins/plugin.search.js +1 -1
  31. package/BookReader/plugins/plugin.search.js.map +1 -1
  32. package/BookReader/plugins/plugin.text_selection.js +1 -1
  33. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  34. package/BookReader/plugins/plugin.tts.js +1 -1
  35. package/BookReader/plugins/plugin.tts.js.map +1 -1
  36. package/BookReader/plugins/plugin.url.js +1 -1
  37. package/BookReader/plugins/plugin.url.js.map +1 -1
  38. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  39. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  40. package/CHANGELOG.md +5 -0
  41. package/package.json +17 -16
  42. package/renovate.json +6 -0
  43. package/scripts/preversion.js +4 -1
  44. package/src/BookNavigator/search/a-search-result.js +4 -6
  45. package/src/plugins/search/plugin.search.js +17 -4
  46. package/src/plugins/search/view.js +1 -3
  47. package/tests/{karma → jest}/BookNavigator/search/search-provider.test.js +29 -29
  48. package/tests/{karma → jest}/BookNavigator/search/search-results.test.js +57 -56
  49. package/tests/jest/plugins/search/plugin.search.test.js +40 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-42",
3
+ "version": "5.0.0-43",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,19 +41,19 @@
41
41
  "lit": "^2.2.2"
42
42
  },
43
43
  "devDependencies": {
44
- "@babel/core": "7.17.5",
44
+ "@babel/core": "7.17.9",
45
45
  "@babel/eslint-parser": "7.17.0",
46
46
  "@babel/plugin-proposal-class-properties": "7.16.7",
47
- "@babel/plugin-proposal-decorators": "7.17.2",
47
+ "@babel/plugin-proposal-decorators": "7.17.9",
48
48
  "@babel/preset-env": "7.16.11",
49
- "@open-wc/testing": "^3.1.3",
49
+ "@open-wc/testing": "^3.1.4",
50
50
  "@open-wc/testing-karma": "^4.0.9",
51
51
  "@types/jest": "^27.4.1",
52
52
  "@webcomponents/webcomponentsjs": "^2.6.0",
53
- "babel-loader": "8.2.3",
53
+ "babel-loader": "8.2.5",
54
54
  "codecov": "^3.8.3",
55
- "concurrently": "7.0.0",
56
- "core-js": "3.16.2",
55
+ "concurrently": "7.1.0",
56
+ "core-js": "3.22.3",
57
57
  "cpx2": "4.2.0",
58
58
  "eslint": "^7.32.0",
59
59
  "eslint-plugin-no-jquery": "^2.7.0",
@@ -61,30 +61,31 @@
61
61
  "hammerjs": "^2.0.8",
62
62
  "http-server": "14.1.0",
63
63
  "iso-language-codes": "1.1.0",
64
- "jest": "^27.5.1",
65
- "jquery": "1.11.3",
64
+ "jest": "^28.0.3",
65
+ "jest-environment-jsdom": "^28.0.2",
66
+ "jquery": "1.12.4",
66
67
  "jquery-colorbox": "1.6.4",
67
68
  "jquery-ui": "1.12.1",
68
69
  "jquery-ui-touch-punch": "0.2.3",
69
70
  "jquery.browser": "0.1.0",
70
71
  "jquery.mmenu": "5.6.5",
71
72
  "karma-coverage": "^2.1.0",
72
- "live-server": "1.2.1",
73
- "node-fetch": "2.6.7",
73
+ "live-server": "1.2.2",
74
+ "node-fetch": "3.2.4",
74
75
  "regenerator-runtime": "0.13.9",
75
- "sass": "1.38.2",
76
- "sinon": "^12.0.1",
76
+ "sass": "1.51.0",
77
+ "sinon": "^13.0.2",
77
78
  "soundmanager2": "2.97.20170602",
78
- "svgo": "2.4.0",
79
+ "svgo": "2.8.0",
79
80
  "testcafe": "^1.18.6",
80
81
  "testcafe-browser-provider-browserstack": "^1.13.2-alpha.1",
81
82
  "webpack": "5.51.1",
82
- "webpack-cli": "4.8.0"
83
+ "webpack-cli": "4.9.2"
83
84
  },
84
85
  "jest": {
85
86
  "testEnvironment": "jsdom",
86
87
  "transformIgnorePatterns": [
87
- "node_modules/(?!(lit-html|lit-element|lit|@lit|@internetarchive)/)"
88
+ "node_modules/(?!(lit-html|lit-element|lit|@lit|@internetarchive|@open-wc)/)"
88
89
  ],
89
90
  "moduleNameMapper": {
90
91
  "^@/(.*)$": "<rootDir>/$1"
package/renovate.json CHANGED
@@ -14,6 +14,7 @@
14
14
  "eslint-plugin-no-jquery",
15
15
  "eslint-plugin-testcafe",
16
16
  "jest",
17
+ "jest-environment-jsdom",
17
18
  "karma-coverage",
18
19
  "sinon",
19
20
  "testcafe"
@@ -38,6 +39,11 @@
38
39
  {
39
40
  "matchPackagePatterns": ["*"],
40
41
  "rangeStrategy": "bump"
42
+ },
43
+ {
44
+ "matchPackagePatterns": ["^actions/"],
45
+ "groupName": "GitHub Actions",
46
+ "automerge": true
41
47
  }
42
48
  ]
43
49
  }
@@ -1,8 +1,11 @@
1
1
  const { version: OLD_VERSION } = require('../package.json');
2
- const fetch = require('node-fetch');
3
2
  const OLD_RELEASE_URL = `https://api.github.com/repos/internetarchive/bookreader/releases/tags/v${OLD_VERSION}`;
4
3
 
5
4
  async function main() {
5
+ // Need this because fetch is ESM-only, and we're on Node 16. Someday we should
6
+ // be able to move this up to the top without renaming this file to a .mjs or whatever
7
+ const {default: fetch} = await import('node-fetch');
8
+
6
9
  const {created_at} = await fetch(OLD_RELEASE_URL).then(r => r.json());
7
10
  const today = new Date().toISOString().slice(0, -5);
8
11
  const searchUrl = 'https://github.com/internetarchive/bookreader/pulls?' + new URLSearchParams({
@@ -1,5 +1,6 @@
1
1
  import { html, LitElement, nothing } from 'lit';
2
2
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
3
+ /** @typedef {import('@/src/plugins/search/plugin.search.js').SearchInsideMatch} SearchInsideMatch */
3
4
 
4
5
  export class BookSearchResult extends LitElement {
5
6
  static get properties() {
@@ -35,17 +36,14 @@ export class BookSearchResult extends LitElement {
35
36
  }
36
37
 
37
38
  render() {
38
- const { match } = this;
39
- const { par = [] } = match;
40
- const [resultDetails = {}] = par;
41
- const pageNumber = Number.isInteger(resultDetails.page)
42
- ? html`<p class="page-num">Page -${resultDetails.page}-</p>` : nothing;
39
+ /** @type {SearchInsideMatch} */
40
+ const match = this.match;
43
41
  const coverImage = html`<img src="${match.cover}" />`;
44
42
  return html`
45
43
  <li @click=${this.resultSelected}>
46
44
  ${match.cover ? coverImage : nothing}
47
45
  <h4>${match.title || nothing}</h4>
48
- ${pageNumber}
46
+ <p class="page-num">Page ${match.displayPageNumber}</p>
49
47
  ${this.highlightedHit(match.text)}
50
48
  </li>
51
49
  `;
@@ -29,6 +29,8 @@ import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.
29
29
  import SearchView from './view.js';
30
30
  /** @typedef {import('../../BookReader/PageContainer').PageContainer} PageContainer */
31
31
  /** @typedef {import('../../BookReader/BookModel').PageIndex} PageIndex */
32
+ /** @typedef {import('../../BookReader/BookModel').LeafNum} LeafNum */
33
+ /** @typedef {import('../../BookReader/BookModel').PageNumString} PageNumString */
32
34
 
33
35
  jQuery.extend(BookReader.defaultOptions, {
34
36
  server: 'ia600609.us.archive.org',
@@ -257,6 +259,7 @@ BookReader.prototype.cancelSearchRequest = function () {
257
259
  /**
258
260
  * @typedef {object} SearchInsideMatch
259
261
  * @property {number} matchIndex This is a fake field! Not part of the API response. It is added by the JS.
262
+ * @property {string} displayPageNumber (fake field) The page number as it should be displayed in the UI.
260
263
  * @property {string} text
261
264
  * @property {Array<{ page: number, boxes: SearchInsideMatchBox[] }>} par
262
265
  */
@@ -269,22 +272,32 @@ BookReader.prototype.cancelSearchRequest = function () {
269
272
  */
270
273
 
271
274
  /**
272
- * Search Results return handler
275
+ * Attach some fields to search inside results
273
276
  * @param {SearchInsideResults} results
274
- * @param {object} options
275
- * @param {boolean} options.goToFirstResult
277
+ * @param {(pageNum: LeafNum) => PageNumString} displayPageNumberFn
276
278
  */
277
- BookReader.prototype.BRSearchCallback = function(results, options) {
279
+ export function marshallSearchResults(results, displayPageNumberFn) {
278
280
  // Attach matchIndex to a few things to make it easier to identify
279
281
  // an active/selected match
280
282
  for (const [index, match] of results.matches.entries()) {
281
283
  match.matchIndex = index;
284
+ match.displayPageNumber = displayPageNumberFn(match.par[0].page);
282
285
  for (const par of match.par) {
283
286
  for (const box of par.boxes) {
284
287
  box.matchIndex = index;
285
288
  }
286
289
  }
287
290
  }
291
+ }
292
+
293
+ /**
294
+ * Search Results return handler
295
+ * @param {SearchInsideResults} results
296
+ * @param {object} options
297
+ * @param {boolean} options.goToFirstResult
298
+ */
299
+ BookReader.prototype.BRSearchCallback = function(results, options) {
300
+ marshallSearchResults(results, pageNum => this.getPageNum(this.leafNumToIndex(pageNum)));
288
301
  this.searchResults = results || [];
289
302
 
290
303
  this.updateSearchHilites();
@@ -230,9 +230,7 @@ class SearchView {
230
230
  matches.forEach((match) => {
231
231
  const queryString = match.text;
232
232
  const pageIndex = this.br.leafNumToIndex(match.par[0].page);
233
- const pageNumber = this.br.getPageNum(pageIndex);
234
233
  const uiStringSearch = "Search result"; // i18n
235
- const uiStringPage = "Page"; // i18n
236
234
 
237
235
  const percentThrough = this.br.constructor.util.cssPercentage(pageIndex, this.br.getNumLeafs() - 1);
238
236
 
@@ -257,7 +255,7 @@ class SearchView {
257
255
  .append(`
258
256
  <div class="BRquery">
259
257
  <div>${queryStringWithBTruncated || queryStringWithB}</div>
260
- <div>${uiStringPage} ${pageNumber}</div>
258
+ <div>Page ${match.displayPageNumber}</div>
261
259
  </div>
262
260
  `)
263
261
  .appendTo(this.br.$('.BRnavline'))
@@ -1,6 +1,6 @@
1
- import { expect, fixtureCleanup, fixtureSync } from '@open-wc/testing';
1
+ import { fixtureCleanup, fixtureSync } from '@open-wc/testing-helpers';
2
2
  import sinon from 'sinon';
3
- import searchProvider from '../../../../src/BookNavigator/search/search-provider';
3
+ import searchProvider from '@/src/BookNavigator/search/search-provider';
4
4
 
5
5
  afterEach(() => {
6
6
  sinon.restore();
@@ -14,26 +14,26 @@ describe('Search Provider', () => {
14
14
  bookreader: {}
15
15
  });
16
16
 
17
- expect(provider.bookreader).to.exist;
18
- expect(provider.onProviderChange).to.exist;
19
- expect(provider.id).to.equal('search');
20
- expect(provider.icon).to.exist;
21
- expect(fixtureSync(provider.icon).tagName).to.equal('IA-ICON-SEARCH');
22
- expect(provider.label).to.equal('Search inside');
23
- expect(provider.menuDetails).to.exist;
24
- expect(provider.component).to.exist;
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
25
  });
26
26
  describe('Search request life cycles', () => {
27
- it('Event: catches `BookReader:SearchStarted`', async() => {
27
+ test('Event: catches `BookReader:SearchStarted`', async() => {
28
28
  const provider = new searchProvider({
29
29
  onProviderChange: sinon.fake(),
30
30
  bookreader: {}
31
31
  });
32
32
  sinon.spy(provider, 'updateMenu');
33
33
  window.dispatchEvent(new CustomEvent('BookReader:SearchStarted', { detail: { props: { term: 'foo' }}}));
34
- expect(provider.updateMenu.callCount).to.equal(1);
34
+ expect(provider.updateMenu.callCount).toEqual(1);
35
35
  });
36
- it('Event: catches `BookReader:SearchCallback`', async() => {
36
+ test('Event: catches `BookReader:SearchCallback`', async() => {
37
37
  const provider = new searchProvider({
38
38
  onProviderChange: sinon.fake(),
39
39
  bookreader: {}
@@ -41,10 +41,10 @@ describe('Search Provider', () => {
41
41
  sinon.spy(provider, 'updateMenu');
42
42
  const brStub = {};
43
43
  window.dispatchEvent(new CustomEvent('BookReader:SearchCallback', { detail: { props: { instance: brStub, results: { matches: []} }}}));
44
- expect(provider.updateMenu.callCount).to.equal(1);
45
- expect(provider.bookreader).to.equal(brStub);
44
+ expect(provider.updateMenu.callCount).toEqual(1);
45
+ expect(provider.bookreader).toEqual(brStub);
46
46
  });
47
- it('Event: catches `BookReader:SearchCallbackEmpty`', async() => {
47
+ test('Event: catches `BookReader:SearchCallbackEmpty`', async() => {
48
48
  const provider = new searchProvider({
49
49
  onProviderChange: sinon.fake(),
50
50
  bookreader: {}
@@ -53,11 +53,11 @@ describe('Search Provider', () => {
53
53
  sinon.spy(provider, 'updateMenu');
54
54
  const brStub = {};
55
55
  window.dispatchEvent(new CustomEvent('BookReader:SearchCallbackEmpty', { detail: { props: { instance: brStub }}}));
56
- expect(provider.onSearchRequestError.getCall(0).args[1]).to.equal('noResults');
57
- expect(provider.updateMenu.callCount).to.equal(1);
58
- expect(provider.bookreader).to.equal(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
59
  });
60
- it('Event: catches `BookReader:SearchCallbackNotIndexed`', async() => {
60
+ test('Event: catches `BookReader:SearchCallbackNotIndexed`', async() => {
61
61
  const provider = new searchProvider({
62
62
  onProviderChange: sinon.fake(),
63
63
  bookreader: {}
@@ -66,11 +66,11 @@ describe('Search Provider', () => {
66
66
  sinon.spy(provider, 'onSearchRequestError');
67
67
  sinon.spy(provider, 'updateMenu');
68
68
  window.dispatchEvent(new CustomEvent('BookReader:SearchCallbackNotIndexed', { detail: { props: { instance: brStub }}}));
69
- expect(provider.onSearchRequestError.getCall(0).args[1]).to.equal('notIndexed');
70
- expect(provider.updateMenu.callCount).to.equal(1);
71
- expect(provider.bookreader).to.equal(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
72
  });
73
- it('Event: catches `BookReader:SearchCallbackError`', async() => {
73
+ test('Event: catches `BookReader:SearchCallbackError`', async() => {
74
74
  const provider = new searchProvider({
75
75
  onProviderChange: sinon.fake(),
76
76
  bookreader: {}
@@ -79,11 +79,11 @@ describe('Search Provider', () => {
79
79
  sinon.spy(provider, 'updateMenu');
80
80
  const brStub = {};
81
81
  window.dispatchEvent(new CustomEvent('BookReader:SearchCallbackError', { detail: { props: { instance: brStub }}}));
82
- expect(provider.onSearchRequestError.getCall(0).args[1]).to.equal(undefined);
83
- expect(provider.updateMenu.callCount).to.equal(1);
84
- expect(provider.bookreader).to.equal(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
85
  });
86
- it('Event: catches `component@resultSelected` - user clicks result in side panel - & turns page', async() => {
86
+ test('Event: catches `component@resultSelected` - user clicks result in side panel - & turns page', async() => {
87
87
  const provider = new searchProvider({
88
88
  onProviderChange: sinon.fake(),
89
89
  bookreader: {
@@ -100,7 +100,7 @@ describe('Search Provider', () => {
100
100
  { detail: searchResultStub })
101
101
  );
102
102
 
103
- expect(provider.bookreader._searchPluginGoToResult.callCount).to.equal(1);
103
+ expect(provider.bookreader._searchPluginGoToResult.callCount).toEqual(1);
104
104
  });
105
105
  });
106
106
  });
@@ -1,11 +1,10 @@
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>`
@@ -17,6 +16,7 @@ const results = [{
17
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.`,
18
17
  cover: '//placehold.it/30x44',
19
18
  title: 'Book title',
19
+ displayPageNumber: 'Page 24',
20
20
  par: [{
21
21
  boxes: [{
22
22
  r: 2672, b: 792, t: 689, page: 24, l: 2424,
@@ -31,6 +31,7 @@ const results = [{
31
31
  }],
32
32
  }, {
33
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',
34
35
  par: [{
35
36
  boxes: [{
36
37
  r: 698, b: 4460, t: 4324, page: 86, l: 450,
@@ -50,64 +51,64 @@ describe('<ia-book-search-results>', () => {
50
51
  sinon.restore();
51
52
  });
52
53
 
53
- it('sets default properties', async () => {
54
+ test('sets default properties', async () => {
54
55
  const query = 'bristol';
55
56
  const el = await fixture(container(results, query));
56
57
 
57
- expect(el.results).to.equal(results);
58
- expect(el.query).to.equal(query);
58
+ expect(el.results).toEqual(results);
59
+ expect(el.query).toEqual(query);
59
60
  });
60
61
 
61
- it('sets results when passed in via event object', async () => {
62
+ test('sets results when passed in via event object', async () => {
62
63
  const el = await fixture(container());
63
64
 
64
65
  el.setResults({ detail: { results } });
65
- expect(el.results).to.equal(results);
66
+ expect(el.results).toEqual(results);
66
67
  });
67
68
 
68
- it('listens for a custom search callback event on the document', async () => {
69
+ test('listens for a custom search callback event on the document', async () => {
69
70
  IABookSearchResults.prototype.setResults = sinon.fake();
70
71
  const el = await fixture(container());
71
72
  const event = new Event('BookReader:SearchCallback');
72
73
 
73
74
  event.detail = { results };
74
75
  document.dispatchEvent(event);
75
- expect(el.setResults.callCount).to.equal(1);
76
- expect(el.setResults.firstArg).to.equal(event);
76
+ expect(el.setResults.callCount).toEqual(1);
77
+ expect(el.setResults.firstArg).toEqual(event);
77
78
  });
78
79
 
79
- it('renders results that contain the book title', async () => {
80
+ test('renders results that contain the book title', async () => {
80
81
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
81
82
  const el = await fixture(container(results));
82
83
 
83
- expect(el.innerHTML).to.include(`${results[0].title}`);
84
+ expect(el.innerHTML).toContain(`${results[0].title}`);
84
85
  });
85
86
 
86
- it('renders results that contain a highlighted match', async () => {
87
+ test('renders results that contain a highlighted match', async () => {
87
88
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
88
89
  const el = await fixture(container(results));
89
90
 
90
- expect(el.innerHTML).to.include(`<mark>${searchQuery}</mark>`);
91
+ expect(el.innerHTML).toContain(`<mark>${searchQuery}</mark>`);
91
92
  });
92
93
 
93
- it('renders results that contain an optional cover image', async () => {
94
+ test('renders results that contain an optional cover image', async () => {
94
95
  sinon.replace(IABookSearchResults.prototype, 'createRenderRoot', function createRenderRoot() { return this; });
95
96
  const el = await fixture(container(results));
96
97
 
97
- expect(el.innerHTML).to.include(`<img src="${results[0].cover}">`);
98
+ expect(el.innerHTML).toContain(`<img src="${results[0].cover}">`);
98
99
  });
99
100
 
100
- it('sets a query prop when search input receives input', async () => {
101
+ test('sets a query prop when search input receives input', async () => {
101
102
  const el = await fixture(container(results));
102
103
  const searchInput = el.shadowRoot.querySelector('[name="query"]');
103
104
 
104
105
  searchInput.value = searchQuery;
105
106
  searchInput.dispatchEvent(new Event('keyup'));
106
107
 
107
- expect(el.query).to.equal(searchQuery);
108
+ expect(el.query).toEqual(searchQuery);
108
109
  });
109
110
 
110
- it('emits a custom event when search form submitted when input is populated', async () => {
111
+ test('emits a custom event when search form submitted when input is populated', async () => {
111
112
  const el = await fixture(container(results));
112
113
 
113
114
  setTimeout(() => {
@@ -117,35 +118,35 @@ describe('<ia-book-search-results>', () => {
117
118
  });
118
119
  const response = await oneEvent(el, 'bookSearchInitiated');
119
120
 
120
- expect(response).to.exist;
121
+ expect(response).toBeDefined();
121
122
  });
122
123
 
123
- it('uses a singular noun when one result given', async () => {
124
+ test('uses a singular noun when one result given', async () => {
124
125
  const el = await fixture(container([results[0]]));
125
126
  const resultsCount = await fixture(el.resultsCount);
126
127
 
127
- expect(resultsCount.innerHTML).to.include('1 result');
128
+ expect(resultsCount.innerHTML).toContain('1 result');
128
129
  });
129
130
 
130
- it('can render header with active options count', async () => {
131
+ test('can render header with active options count', async () => {
131
132
  const el = await fixture(container(results));
132
133
  el.renderHeader = true;
133
134
 
134
135
  await el.updateComplete;
135
136
 
136
- expect(el.shadowRoot.querySelector('header p').innerText).to.include('2');
137
+ expect(el.shadowRoot.querySelector('header p').textContent).toContain('2');
137
138
  });
138
139
 
139
- it('renders search all files checkbox when enabled', async () => {
140
+ test('renders search all files checkbox when enabled', async () => {
140
141
  const el = await fixture(container(results));
141
142
  el.renderSearchAllFiles = true;
142
143
 
143
144
  await el.updateComplete;
144
145
 
145
- expect(el.shadowRoot.querySelector('[name="all_files"]')).to.not.be.null;
146
+ expect(el.shadowRoot.querySelector('[name="all_files"]')).not.toBeNull();
146
147
  });
147
148
 
148
- it('emits a resultSelected event when a search result is clicked', async () => {
149
+ test('emits a resultSelected event when a search result is clicked', async () => {
149
150
  const el = await fixture(container(results));
150
151
 
151
152
  setTimeout(() => (
@@ -153,10 +154,10 @@ describe('<ia-book-search-results>', () => {
153
154
  ));
154
155
  const response = await oneEvent(el, 'resultSelected');
155
156
 
156
- expect(response).to.exist;
157
+ expect(response).toBeDefined();
157
158
  });
158
159
 
159
- it('emits a closeMenu event when a search result is clicked', async () => {
160
+ test('emits a closeMenu event when a search result is clicked', async () => {
160
161
  const el = await fixture(container(results));
161
162
 
162
163
  setTimeout(() => (
@@ -164,75 +165,75 @@ describe('<ia-book-search-results>', () => {
164
165
  ));
165
166
  const response = await oneEvent(el, 'closeMenu');
166
167
 
167
- expect(response).to.exist;
168
+ expect(response).toBeDefined();
168
169
  });
169
170
 
170
171
  describe('Search results placeholders', () => {
171
- it('renders a loading state when queryInProgress is true', async () => {
172
+ test('renders a loading state when queryInProgress is true', async () => {
172
173
  const el = await fixture(container(results));
173
174
 
174
175
  el.queryInProgress = true;
175
176
  await el.updateComplete;
176
177
 
177
- expect(el.shadowRoot.querySelector('.loading')).to.not.be.null;
178
+ expect(el.shadowRoot.querySelector('.loading')).not.toBeNull();
178
179
  });
179
- it('renders an error message when provided', async () => {
180
+ test('renders an error message when provided', async () => {
180
181
  const el = await fixture(container([]));
181
182
  const message = 'Sample error message';
182
183
  el.errorMessage = message;
183
184
  await el.updateComplete;
184
185
 
185
- expect(el.shadowRoot.querySelector('.error-message')).to.exist;
186
- expect(el.shadowRoot.querySelector('.search-cta')).to.be.null;
186
+ expect(el.shadowRoot.querySelector('.error-message')).toBeDefined();
187
+ expect(el.shadowRoot.querySelector('.search-cta')).toBeNull();
187
188
  });
188
- it('displays call to search when no results or search errors are showing', async () => {
189
+ test('displays call to search when no results or search errors are showing', async () => {
189
190
  const el = await fixture(container([]));
190
191
 
191
- expect(el.shadowRoot.querySelector('.search-cta')).to.exist;
192
- expect(el.shadowRoot.querySelector('.error-message')).to.be.null;
193
- expect(el.shadowRoot.querySelector('.results')).to.be.null;
192
+ expect(el.shadowRoot.querySelector('.search-cta')).toBeDefined();
193
+ expect(el.shadowRoot.querySelector('.error-message')).toBeNull();
194
+ expect(el.shadowRoot.querySelector('.results')).toBeNull();
194
195
  });
195
196
  });
196
197
 
197
- it('displays results images when told to', async () => {
198
+ test('displays results images when told to', async () => {
198
199
  const el = await fixture(container(results));
199
200
  el.displayResultImages = true;
200
201
  await el.updateComplete;
201
202
 
202
- expect(el.shadowRoot.querySelector('.results.show-image')).to.exist;
203
+ expect(el.shadowRoot.querySelector('.results.show-image')).toBeDefined();
203
204
  });
204
205
 
205
206
  describe('search input focus', () => {
206
- it('will always try to re-focus once the component updates', async () => {
207
+ test('will always try to re-focus once the component updates', async () => {
207
208
  const el = await fixture(container(results));
208
209
  el.focusOnInputIfNecessary = sinon.fake();
209
210
  // update any property to fire lifecycle
210
211
  el.results = [];
211
212
  await el.updateComplete;
212
213
 
213
- expect(el.focusOnInputIfNecessary.callCount).to.equal(1);
214
+ expect(el.focusOnInputIfNecessary.callCount).toEqual(1);
214
215
  });
215
- it('refocuses on input when results are empty', async () => {
216
+ test('refocuses on input when results are empty', async () => {
216
217
  const el = await fixture(container(results));
217
218
  el.results = [];
218
219
  await el.updateComplete;
219
220
 
220
221
  const searchInputField = el.shadowRoot.querySelector('input[type=\'search\']');
221
- expect(searchInputField).to.equal(el.shadowRoot.activeElement);
222
+ expect(searchInputField).toEqual(el.shadowRoot.activeElement);
222
223
  });
223
224
  });
224
225
 
225
- // it("emits a bookSearchCanceled event when loading state's cancel action clicked", async () => {
226
- // const el = await fixture(container(results));
226
+ test("emits a bookSearchCanceled event when loading state's cancel action clicked", async () => {
227
+ const el = await fixture(container(results));
227
228
 
228
- // el.queryInProgress = true;
229
- // await el.updateComplete;
229
+ el.queryInProgress = true;
230
+ await el.updateComplete;
230
231
 
231
- // setTimeout(() => (
232
- // el.shadowRoot.querySelector('button').click()
233
- // ));
234
- // const response = await oneEvent(el, 'bookSearchCanceled');
232
+ setTimeout(() => (
233
+ el.shadowRoot.querySelector('button').click()
234
+ ));
235
+ const response = await oneEvent(el, 'bookSearchCanceled');
235
236
 
236
- // expect(response).to.exist;
237
- // });
237
+ expect(response).toBeDefined();
238
+ });
238
239
  });