@internetarchive/bookreader 5.0.0-90 → 5.0.0-92
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/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +2 -2
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.archive_analytics.js +1 -1
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js +1 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +2 -2
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.iiif.js +1 -1
- package/BookReader/plugins/plugin.iiif.js.map +1 -1
- package/BookReader/plugins/plugin.resume.js +1 -1
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +1 -1
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +1 -1
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +1 -1
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReaderDemo/IADemoBr.js +29 -1
- package/BookReaderDemo/ia-multiple-volumes-manifest.js +0 -1
- package/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/BookNavigator/book-navigator.js +5 -2
- package/src/BookNavigator/search/search-provider.js +13 -7
- package/src/BookNavigator/sharing.js +1 -1
- package/src/BookReader/BookModel.js +5 -4
- package/src/BookReader/Toolbar/Toolbar.js +5 -0
- package/src/BookReader/options.js +10 -6
- package/src/BookReader.js +49 -23
- package/src/BookReaderPlugin.js +8 -0
- package/src/plugins/plugin.chapters.js +220 -157
- package/src/plugins/plugin.text_selection.js +19 -1
- package/src/plugins/search/plugin.search.js +330 -376
- package/src/plugins/search/view.js +13 -9
- package/src/plugins/tts/WebTTSEngine.js +67 -41
- package/src/plugins/tts/plugin.tts.js +1 -3
- package/src/plugins/tts/utils.js +13 -0
- package/src/util/browserSniffing.js +11 -1
- package/tests/e2e/helpers/mockSearch.js +1 -1
- package/tests/jest/BookNavigator/book-navigator.test.js +8 -3
- package/tests/jest/BookNavigator/search/search-provider.test.js +16 -4
- package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +1 -1
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +70 -0
- package/tests/jest/BookReader.test.js +26 -1
- package/tests/jest/plugins/plugin.chapters.test.js +56 -58
- package/tests/jest/plugins/search/plugin.search.test.js +17 -42
- package/tests/jest/plugins/search/plugin.search.view.test.js +10 -18
- package/tests/jest/plugins/tts/WebTTSEngine.test.js +18 -12
- package/tests/jest/plugins/url/plugin.url.test.js +1 -1
- package/tests/jest/util/browserSniffing.test.js +9 -3
- package/tests/jest/utils.js +4 -1
@@ -1,7 +1,7 @@
|
|
1
1
|
import sinon from "sinon";
|
2
2
|
|
3
|
-
import
|
4
|
-
import "@/src/plugins/plugin.chapters.js";
|
3
|
+
import "@/src/BookReader.js";
|
4
|
+
import {ChaptersPlugin} from "@/src/plugins/plugin.chapters.js";
|
5
5
|
import { BookModel } from "@/src/BookReader/BookModel";
|
6
6
|
import { deepCopy } from "../utils";
|
7
7
|
/** @typedef {import('@/src/plugins/plugin.chapters').TocEntry} TocEntry */
|
@@ -68,50 +68,44 @@ afterEach(() => {
|
|
68
68
|
sinon.restore();
|
69
69
|
});
|
70
70
|
|
71
|
-
describe("
|
71
|
+
describe("ChaptersPlugin", () => {
|
72
72
|
beforeEach(() => {
|
73
73
|
sinon.stub(BookModel.prototype, "getPageIndex").callsFake((str) =>
|
74
74
|
parseFloat(str),
|
75
75
|
);
|
76
76
|
});
|
77
77
|
|
78
|
-
describe("
|
79
|
-
test("does not render when
|
80
|
-
const
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
await BookReader.prototype._chapterInit.call(fakeBR);
|
86
|
-
expect(fakeBR._chaptersRender.callCount).toBe(0);
|
78
|
+
describe("init", () => {
|
79
|
+
test("does not render when open library has no record", async () => {
|
80
|
+
const p = new ChaptersPlugin({ options: { vars: {} } });
|
81
|
+
sinon.stub(p, "getOpenLibraryRecord").resolves(null);
|
82
|
+
sinon.spy(p, "_render");
|
83
|
+
await p.init();
|
84
|
+
expect(p._render.callCount).toBe(0);
|
87
85
|
});
|
88
86
|
|
89
|
-
test("does not render when open library record
|
90
|
-
const
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
await BookReader.prototype._chapterInit.call(fakeBR);
|
96
|
-
expect(fakeBR._chaptersRender.callCount).toBe(0);
|
87
|
+
test("does not render when open library record has no TOC", async () => {
|
88
|
+
const p = new ChaptersPlugin({ options: { vars: {} } });
|
89
|
+
sinon.stub(p, "getOpenLibraryRecord").resolves({ key: "/books/OL1M" });
|
90
|
+
sinon.spy(p, "_render");
|
91
|
+
await p.init();
|
92
|
+
expect(p._render.callCount).toBe(0);
|
97
93
|
});
|
98
94
|
|
99
95
|
test("renders if valid TOC on open library", async () => {
|
100
96
|
const fakeBR = {
|
101
|
-
options: {},
|
97
|
+
options: { vars: {} },
|
102
98
|
bind: sinon.stub(),
|
103
|
-
book: {
|
104
|
-
getPageIndex: (str) => parseFloat(str),
|
105
|
-
},
|
106
|
-
getOpenLibraryRecord: async () => ({
|
107
|
-
"title": "The Adventures of Sherlock Holmes",
|
108
|
-
"table_of_contents": deepCopy(SAMPLE_TOC_OPTION),
|
109
|
-
"ocaid": "adventureofsherl0000unse",
|
110
|
-
}),
|
111
|
-
_chaptersRender: sinon.stub(),
|
112
99
|
};
|
113
|
-
|
114
|
-
|
100
|
+
const p = new ChaptersPlugin(fakeBR);
|
101
|
+
sinon.stub(p, "getOpenLibraryRecord").resolves({
|
102
|
+
"title": "The Adventures of Sherlock Holmes",
|
103
|
+
"table_of_contents": deepCopy(SAMPLE_TOC_OPTION),
|
104
|
+
"ocaid": "adventureofsherl0000unse",
|
105
|
+
});
|
106
|
+
sinon.stub(p, "_render");
|
107
|
+
await p.init();
|
108
|
+
expect(p._render.callCount).toBe(1);
|
115
109
|
});
|
116
110
|
|
117
111
|
test("does not fetch open library record if table of contents in options", async () => {
|
@@ -120,12 +114,13 @@ describe("BRChaptersPlugin", () => {
|
|
120
114
|
table_of_contents: deepCopy(SAMPLE_TOC_UNDEF),
|
121
115
|
},
|
122
116
|
bind: sinon.stub(),
|
123
|
-
getOpenLibraryRecord: sinon.stub(),
|
124
|
-
_chaptersRender: sinon.stub(),
|
125
117
|
};
|
126
|
-
|
127
|
-
|
128
|
-
|
118
|
+
const p = new ChaptersPlugin(fakeBR);
|
119
|
+
sinon.stub(p, "getOpenLibraryRecord");
|
120
|
+
sinon.stub(p, "_render");
|
121
|
+
await p.init();
|
122
|
+
expect(p.getOpenLibraryRecord.callCount).toBe(0);
|
123
|
+
expect(p._render.callCount).toBe(1);
|
129
124
|
});
|
130
125
|
|
131
126
|
test("converts leafs and pagenums to page index", async () => {
|
@@ -141,55 +136,58 @@ describe("BRChaptersPlugin", () => {
|
|
141
136
|
leafNumToIndex: (leaf) => leaf + 1,
|
142
137
|
getPageIndex: (str) => parseFloat(str),
|
143
138
|
},
|
144
|
-
_chaptersRender: sinon.stub(),
|
145
139
|
};
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
expect(
|
140
|
+
const p = new ChaptersPlugin(fakeBR);
|
141
|
+
sinon.stub(p, "_render");
|
142
|
+
await p.init();
|
143
|
+
expect(p._render.callCount).toBe(1);
|
144
|
+
expect(p._tocEntries[0].pageIndex).toBe(1);
|
145
|
+
expect(p._tocEntries[1].pageIndex).toBe(17);
|
150
146
|
});
|
151
147
|
});
|
152
148
|
|
153
|
-
describe('
|
149
|
+
describe('_render', () => {
|
154
150
|
test('renders markers and panel', () => {
|
155
151
|
const fakeBR = {
|
156
|
-
_tocEntries: SAMPLE_TOC,
|
157
|
-
_chaptersRenderMarker: sinon.stub(),
|
158
152
|
shell: {
|
159
153
|
menuProviders: {},
|
160
154
|
addMenuShortcut: sinon.stub(),
|
161
155
|
updateMenuContents: sinon.stub(),
|
162
156
|
},
|
163
157
|
};
|
164
|
-
|
158
|
+
const p = new ChaptersPlugin(fakeBR);
|
159
|
+
sinon.stub(p, '_renderMarker');
|
160
|
+
p._tocEntries = deepCopy(SAMPLE_TOC);
|
161
|
+
p._render();
|
165
162
|
expect(fakeBR.shell.menuProviders['chapters']).toBeTruthy();
|
166
163
|
expect(fakeBR.shell.addMenuShortcut.callCount).toBe(1);
|
167
164
|
expect(fakeBR.shell.updateMenuContents.callCount).toBe(1);
|
168
|
-
expect(
|
165
|
+
expect(p._renderMarker.callCount).toBeGreaterThan(1);
|
169
166
|
});
|
170
167
|
});
|
171
168
|
|
172
|
-
describe('
|
169
|
+
describe('_updateCurrent', () => {
|
173
170
|
test('highlights the current chapter', () => {
|
174
171
|
const fakeBR = {
|
175
172
|
mode: 2,
|
176
173
|
firstIndex: 16,
|
177
174
|
displayedIndices: [16, 17],
|
178
|
-
_tocEntries: SAMPLE_TOC,
|
179
|
-
_chaptersPanel: {
|
180
|
-
currentChapter: null,
|
181
|
-
},
|
182
175
|
};
|
183
|
-
|
184
|
-
|
176
|
+
const p = new ChaptersPlugin(fakeBR);
|
177
|
+
p._tocEntries = deepCopy(SAMPLE_TOC);
|
178
|
+
p._chaptersPanel = {
|
179
|
+
currentChapter: null,
|
180
|
+
};
|
181
|
+
p._updateCurrent();
|
182
|
+
expect(p._chaptersPanel.currentChapter).toEqual(SAMPLE_TOC[1]);
|
185
183
|
|
186
184
|
fakeBR.mode = 1;
|
187
|
-
|
188
|
-
expect(
|
185
|
+
p._updateCurrent();
|
186
|
+
expect(p._chaptersPanel.currentChapter).toEqual(SAMPLE_TOC[0]);
|
189
187
|
|
190
188
|
fakeBR.firstIndex = 0;
|
191
|
-
|
192
|
-
expect(
|
189
|
+
p._updateCurrent();
|
190
|
+
expect(p._chaptersPanel.currentChapter).toBeUndefined();
|
193
191
|
});
|
194
192
|
});
|
195
193
|
});
|
@@ -5,6 +5,7 @@ import { DUMMY_RESULTS } from './utils.js';
|
|
5
5
|
|
6
6
|
jest.mock('@/src/plugins/search/view.js');
|
7
7
|
|
8
|
+
/** @type {BookReader} */
|
8
9
|
let br;
|
9
10
|
const namespace = 'BookReader:';
|
10
11
|
const triggeredEvents = () => {
|
@@ -23,10 +24,14 @@ beforeEach(() => {
|
|
23
24
|
|
24
25
|
$.fn.trigger = jest.fn();
|
25
26
|
document.body.innerHTML = '<div id="BookReader">';
|
26
|
-
br = new BookReader(
|
27
|
+
br = new BookReader({
|
28
|
+
server: 'foo.bar.com',
|
29
|
+
bookPath: '/13/items/foo/foobar',
|
30
|
+
subPrefix: '/foobar',
|
31
|
+
});
|
27
32
|
br.initToolbar = jest.fn();
|
28
33
|
br.showProgressPopup = jest.fn();
|
29
|
-
br.searchXHR = jest.fn();
|
34
|
+
br._plugins.search.searchXHR = jest.fn();
|
30
35
|
});
|
31
36
|
|
32
37
|
afterEach(() => {
|
@@ -34,54 +39,24 @@ afterEach(() => {
|
|
34
39
|
});
|
35
40
|
|
36
41
|
describe('Plugin: Search', () => {
|
37
|
-
test('has option flag', () => {
|
38
|
-
expect(BookReader.defaultOptions.enableSearch).toEqual(true);
|
39
|
-
});
|
40
|
-
|
41
|
-
test('has added BR property: server', () => {
|
42
|
-
expect(br).toHaveProperty('server');
|
43
|
-
expect(br.server).toBeTruthy();
|
44
|
-
});
|
45
|
-
|
46
|
-
test('has added BR property: bookId', () => {
|
47
|
-
expect(br).toHaveProperty('bookId');
|
48
|
-
expect(br.bookId).toBeFalsy();
|
49
|
-
});
|
50
|
-
|
51
|
-
test('has added BR property: subPrefix', () => {
|
52
|
-
expect(br).toHaveProperty('subPrefix');
|
53
|
-
expect(br.subPrefix).toBeFalsy();
|
54
|
-
});
|
55
|
-
|
56
|
-
test('has added BR property: bookPath', () => {
|
57
|
-
expect(br).toHaveProperty('bookPath');
|
58
|
-
expect(br.bookPath).toBeFalsy();
|
59
|
-
});
|
60
|
-
|
61
|
-
test('has added BR property: searchInsideUrl', () => {
|
62
|
-
expect(br).toHaveProperty('searchInsideUrl');
|
63
|
-
expect(br.searchInsideUrl).toBeTruthy();
|
64
|
-
});
|
65
|
-
|
66
|
-
test('has added BR property: initialSearchTerm', () => {
|
67
|
-
expect(br.options).toHaveProperty('initialSearchTerm');
|
68
|
-
expect(br.options.initialSearchTerm).toBeFalsy();
|
69
|
-
});
|
70
|
-
|
71
42
|
test('On init, it loads the toolbar', () => {
|
72
43
|
br.init();
|
73
44
|
expect(br.initToolbar).toHaveBeenCalled();
|
74
45
|
});
|
75
46
|
|
47
|
+
test('Constructs SearchView', () => {
|
48
|
+
expect(br._plugins.search.searchView).toBeDefined();
|
49
|
+
});
|
50
|
+
|
76
51
|
test('On init, it will run a search if given `options.initialSearchTerm`', () => {
|
77
|
-
br.search = jest.fn();
|
78
|
-
br.options.initialSearchTerm = 'foo';
|
52
|
+
br._plugins.search.search = jest.fn();
|
53
|
+
br.options.plugins.search.initialSearchTerm = 'foo';
|
79
54
|
br.init();
|
80
55
|
|
81
|
-
expect(br.search).toHaveBeenCalled();
|
82
|
-
expect(br.search.mock.calls[0][1])
|
56
|
+
expect(br._plugins.search.search).toHaveBeenCalled();
|
57
|
+
expect(br._plugins.search.search.mock.calls[0][1])
|
83
58
|
.toHaveProperty('goToFirstResult', true);
|
84
|
-
expect(br.search.mock.calls[0][1])
|
59
|
+
expect(br._plugins.search.search.mock.calls[0][1])
|
85
60
|
.toHaveProperty('suppressFragmentChange', false);
|
86
61
|
});
|
87
62
|
|
@@ -100,7 +75,7 @@ describe('Plugin: Search', () => {
|
|
100
75
|
test('SearchStarted event fires and should go to first result', () => {
|
101
76
|
br.init();
|
102
77
|
br.search('foo', { goToFirstResult: true});
|
103
|
-
expect(br.options.goToFirstResult).toBeTruthy();
|
78
|
+
expect(br._plugins.search.options.goToFirstResult).toBeTruthy();
|
104
79
|
});
|
105
80
|
|
106
81
|
test('SearchCallback event fires when AJAX search returns results', async () => {
|
@@ -1,9 +1,10 @@
|
|
1
|
-
|
1
|
+
// @ts-check
|
2
2
|
import BookReader from '@/src/BookReader.js';
|
3
3
|
import '@/src/plugins/search/plugin.search.js';
|
4
4
|
import { marshallSearchResults } from '@/src/plugins/search/utils.js';
|
5
5
|
import '@/src/plugins/search/view.js';
|
6
6
|
|
7
|
+
/** @type {BookReader} */
|
7
8
|
let br;
|
8
9
|
const namespace = 'BookReader:';
|
9
10
|
const results = {
|
@@ -71,31 +72,22 @@ afterEach(() => {
|
|
71
72
|
});
|
72
73
|
|
73
74
|
describe('View: Plugin: Search', () => {
|
74
|
-
test('When search runs, the view gets created.', () => {
|
75
|
-
br.search = jest.fn();
|
76
|
-
br.options.initialSearchTerm = 'foo';
|
77
|
-
br.init();
|
78
|
-
|
79
|
-
expect(br.searchView).toBeDefined();
|
80
|
-
expect(br.searchView.handleSearchCallback).toBeDefined();
|
81
|
-
});
|
82
|
-
|
83
75
|
describe("Search results navigation bar", () => {
|
84
76
|
test('Search Results callback creates the results nav', () => {
|
85
77
|
br.init();
|
86
78
|
const event = new CustomEvent(`${namespace}SearchCallback`);
|
87
79
|
const options = { goToFirstResult: false };
|
88
80
|
|
89
|
-
expect(br.searchView.dom.searchNavigation).toBeUndefined();
|
81
|
+
expect(br._plugins.search.searchView.dom.searchNavigation).toBeUndefined();
|
90
82
|
|
91
|
-
br.searchView.handleSearchCallback(event, { results, options});
|
92
|
-
expect(br.searchView.dom.searchNavigation).toBeDefined();
|
83
|
+
br._plugins.search.searchView.handleSearchCallback(event, { results, options});
|
84
|
+
expect(br._plugins.search.searchView.dom.searchNavigation).toBeDefined();
|
93
85
|
});
|
94
86
|
test('has controls', () => {
|
95
87
|
br.init();
|
96
88
|
const event = new CustomEvent(`${namespace}SearchCallback`);
|
97
89
|
const options = { goToFirstResult: false };
|
98
|
-
br.searchView.handleSearchCallback(event, { results, options});
|
90
|
+
br._plugins.search.searchView.handleSearchCallback(event, { results, options});
|
99
91
|
|
100
92
|
const searchResultsNav = document.querySelector('.BRsearch-navigation');
|
101
93
|
expect(searchResultsNav).toBeDefined();
|
@@ -110,9 +102,9 @@ describe('View: Plugin: Search', () => {
|
|
110
102
|
br.init();
|
111
103
|
const event = new CustomEvent(`${namespace}SearchCallback`);
|
112
104
|
const options = { goToFirstResult: false };
|
113
|
-
br.searchView.handleSearchCallback(event, { results: resultWithScript, options });
|
105
|
+
br._plugins.search.searchView.handleSearchCallback(event, { results: resultWithScript, options });
|
114
106
|
|
115
|
-
expect(br.searchView.dom.searchNavigation.parent().html()).not.toContain('<script>alert(1);</script>');
|
107
|
+
expect(br._plugins.search.searchView.dom.searchNavigation.parent().html()).not.toContain('<script>alert(1);</script>');
|
116
108
|
});
|
117
109
|
|
118
110
|
describe('Click events handlers', () => {
|
@@ -122,7 +114,7 @@ describe('View: Plugin: Search', () => {
|
|
122
114
|
br.trigger = (eventName) => eventNameTriggered = eventName;
|
123
115
|
|
124
116
|
expect(eventNameTriggered).toBeFalsy();
|
125
|
-
br.searchView.toggleSidebar();
|
117
|
+
br._plugins.search.searchView.toggleSidebar();
|
126
118
|
expect(eventNameTriggered).toEqual('ToggleSearchMenu');
|
127
119
|
});
|
128
120
|
it('triggers custom event when closing navbar', () => {
|
@@ -131,7 +123,7 @@ describe('View: Plugin: Search', () => {
|
|
131
123
|
br.trigger = (eventName) => eventNameTriggered = eventName;
|
132
124
|
|
133
125
|
expect(eventNameTriggered).toBeFalsy();
|
134
|
-
br.searchView.clearSearchFieldAndResults();
|
126
|
+
br._plugins.search.searchView.clearSearchFieldAndResults();
|
135
127
|
expect(eventNameTriggered).toEqual('SearchResultsCleared');
|
136
128
|
});
|
137
129
|
});
|
@@ -93,51 +93,57 @@ describe('WebTTSSound', () => {
|
|
93
93
|
});
|
94
94
|
|
95
95
|
describe('_chromePausingBugFix', () => {
|
96
|
+
/** @type {sinon.SinonFakeTimers} */
|
97
|
+
let clock = null;
|
98
|
+
|
99
|
+
beforeEach(() => {
|
100
|
+
clock = sinon.useFakeTimers();
|
101
|
+
});
|
102
|
+
|
103
|
+
afterEach(() => {
|
104
|
+
clock.restore();
|
105
|
+
});
|
106
|
+
|
96
107
|
test('if speech less than 15s, nothing special', async () => {
|
97
|
-
const clock = sinon.useFakeTimers();
|
98
108
|
const sound = new WebTTSSound('hello world foo bar');
|
99
109
|
sound.load();
|
100
110
|
sound.play();
|
101
111
|
sound._chromePausingBugFix();
|
102
112
|
clock.tick(10000);
|
103
113
|
sound.utterance.dispatchEvent('end', {});
|
104
|
-
clock.restore();
|
105
114
|
await afterEventLoop();
|
106
115
|
expect(speechSynthesis.pause.callCount).toBe(0);
|
107
116
|
});
|
108
117
|
|
109
118
|
test('if speech greater than 15s, pause called', async () => {
|
110
|
-
const clock = sinon.useFakeTimers();
|
111
119
|
const sound = new WebTTSSound('foo bah');
|
112
120
|
sound.load();
|
113
121
|
sound.play();
|
114
122
|
sound._chromePausingBugFix();
|
115
123
|
clock.tick(20000);
|
116
|
-
clock.restore();
|
117
124
|
|
118
125
|
await afterEventLoop();
|
119
126
|
expect(speechSynthesis.pause.callCount).toBe(1);
|
120
127
|
});
|
121
128
|
|
122
|
-
test('on pause
|
123
|
-
const clock = sinon.useFakeTimers();
|
129
|
+
test('on pause, stops sound if timed out', async () => {
|
124
130
|
const sound = new WebTTSSound('foo bah');
|
125
131
|
sound.load();
|
126
132
|
sound.play();
|
133
|
+
sound.stop = sinon.stub();
|
127
134
|
sound._chromePausingBugFix();
|
128
|
-
|
129
|
-
|
130
|
-
|
135
|
+
clock.tick(5000);
|
136
|
+
sound.utterance.dispatchEvent('pause', {});
|
137
|
+
await afterEventLoop();
|
138
|
+
clock.tick(15000);
|
131
139
|
|
132
140
|
await afterEventLoop();
|
133
|
-
expect(
|
141
|
+
expect(sound.stop.callCount).toBe(1);
|
134
142
|
});
|
135
143
|
});
|
136
144
|
|
137
145
|
test('fire pause if browser does not do it', async () => {
|
138
146
|
const clock = sinon.useFakeTimers();
|
139
|
-
const languageGetter = jest.spyOn(window.navigator, 'userAgent', 'get');
|
140
|
-
languageGetter.mockReturnValue('firefox android');
|
141
147
|
const sound = new WebTTSSound('foo bah');
|
142
148
|
sound.load();
|
143
149
|
sound.play();
|
@@ -145,7 +145,7 @@ describe('Plugin: URL controller', () => {
|
|
145
145
|
search: 'foo',
|
146
146
|
}));
|
147
147
|
BookReader.prototype.search = jest.fn();
|
148
|
-
br.options.
|
148
|
+
br.options.plugins.search = { initialSearchTerm: 'foo' };
|
149
149
|
br.options.urlMode = 'history';
|
150
150
|
br.init();
|
151
151
|
br.urlUpdateFragment();
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import {
|
2
|
-
isChrome, isFirefox, isSafari,
|
2
|
+
isChrome, isEdge, isFirefox, isSafari,
|
3
3
|
} from '@/src/util/browserSniffing.js';
|
4
4
|
|
5
5
|
const TESTS = [
|
@@ -19,7 +19,13 @@ const TESTS = [
|
|
19
19
|
name: 'Edge on Windows 10',
|
20
20
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ServiceUI 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362',
|
21
21
|
vendor: '',
|
22
|
-
machingFn:
|
22
|
+
machingFn: isEdge,
|
23
|
+
},
|
24
|
+
{
|
25
|
+
name: 'Edge on Windows 11',
|
26
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0',
|
27
|
+
vendor: 'Google Inc.',
|
28
|
+
machingFn: isEdge,
|
23
29
|
},
|
24
30
|
{
|
25
31
|
name: 'IE11 on Windows 10',
|
@@ -47,7 +53,7 @@ const TESTS = [
|
|
47
53
|
},
|
48
54
|
];
|
49
55
|
|
50
|
-
for (const fn of [isChrome, isFirefox, isSafari]) {
|
56
|
+
for (const fn of [isChrome, isEdge, isFirefox, isSafari]) {
|
51
57
|
describe(fn.name, () => {
|
52
58
|
for (const { name, userAgent, vendor, machingFn } of TESTS) {
|
53
59
|
test(name, () => expect(fn(userAgent, vendor)).toBe(machingFn == fn));
|
package/tests/jest/utils.js
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
// Keep a copy of this, since it can be overridden by sinon timers.
|
2
|
+
const _realTimeout = setTimeout;
|
3
|
+
|
1
4
|
/**
|
2
5
|
* Resolves after all enqueued callbacks in the event loop have resolved.
|
3
6
|
* @return {Promise}
|
@@ -5,7 +8,7 @@
|
|
5
8
|
export function afterEventLoop() {
|
6
9
|
// Waiting 0 seconds essentially lets us run at the end of the event
|
7
10
|
// loop (i.e. after any promises which aren't _actually_ async have finished)
|
8
|
-
return new Promise(res =>
|
11
|
+
return new Promise(res => _realTimeout(res, 0));
|
9
12
|
}
|
10
13
|
|
11
14
|
/**
|