@internetarchive/bookreader 5.0.0-58 → 5.0.0-59

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.
Files changed (50) hide show
  1. package/BookReader/BookReader.css +110 -39
  2. package/BookReader/BookReader.js +1 -1
  3. package/BookReader/BookReader.js.LICENSE.txt +0 -20
  4. package/BookReader/BookReader.js.map +1 -1
  5. package/BookReader/ia-bookreader-bundle.js +1 -1
  6. package/BookReader/ia-bookreader-bundle.js.map +1 -1
  7. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  8. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  9. package/BookReader/plugins/plugin.autoplay.js +1 -1
  10. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  11. package/BookReader/plugins/plugin.resume.js +1 -1
  12. package/BookReader/plugins/plugin.resume.js.map +1 -1
  13. package/BookReader/plugins/plugin.tts.js +1 -1
  14. package/BookReader/plugins/plugin.tts.js.map +1 -1
  15. package/BookReader/plugins/plugin.url.js +1 -1
  16. package/BookReader/plugins/plugin.url.js.map +1 -1
  17. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  18. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  19. package/BookReaderDemo/IADemoBr.js +1 -2
  20. package/CHANGELOG.md +4 -0
  21. package/package.json +1 -1
  22. package/src/BookReader/BookModel.js +59 -1
  23. package/src/BookReader/Mode1UpLit.js +13 -70
  24. package/src/BookReader/Mode2Up.js +72 -1332
  25. package/src/BookReader/Mode2UpLit.js +774 -0
  26. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  27. package/src/BookReader/ModeSmoothZoom.js +32 -0
  28. package/src/BookReader/options.js +8 -2
  29. package/src/BookReader/utils.js +16 -0
  30. package/src/BookReader.js +24 -217
  31. package/src/css/_BRBookmarks.scss +1 -1
  32. package/src/css/_BRmain.scss +14 -0
  33. package/src/css/_BRpages.scss +113 -41
  34. package/src/plugins/plugin.autoplay.js +1 -6
  35. package/src/plugins/tts/WebTTSEngine.js +2 -2
  36. package/src/plugins/tts/plugin.tts.js +3 -17
  37. package/src/plugins/tts/utils.js +0 -16
  38. package/tests/e2e/helpers/base.js +20 -20
  39. package/tests/e2e/helpers/rightToLeft.js +4 -10
  40. package/tests/e2e/viewmode.test.js +10 -8
  41. package/tests/jest/BookReader/BookModel.test.js +25 -0
  42. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +28 -11
  43. package/tests/jest/BookReader/Mode1UpLit.test.js +0 -19
  44. package/tests/jest/BookReader/Mode2Up.test.js +55 -225
  45. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  46. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  47. package/tests/jest/BookReader/ModeSmoothZoom.test.js +26 -0
  48. package/tests/jest/BookReader/Navbar/Navbar.test.js +3 -3
  49. package/tests/jest/BookReader/utils.test.js +32 -1
  50. package/tests/jest/plugins/tts/utils.test.js +0 -34
@@ -1,7 +1,9 @@
1
1
 
2
2
  import sinon from 'sinon';
3
- import { deepCopy } from '../utils.js';
3
+ import { Mode2Up } from '@/src/BookReader/Mode2Up.js';
4
4
  import BookReader from '@/src/BookReader.js';
5
+ import { BookModel } from '@/src/BookReader/BookModel.js';
6
+ import { afterEventLoop } from '../utils';
5
7
  /** @typedef {import('@/src/BookReader/options.js').BookReaderOptions} BookReaderOptions */
6
8
 
7
9
  beforeAll(() => {
@@ -31,238 +33,66 @@ const SAMPLE_DATA = [
31
33
  ];
32
34
 
33
35
 
34
- describe('zoom', () => {
35
- const br = new BookReader({ data: SAMPLE_DATA });
36
- br.init();
36
+ describe('Mode2Up', () => {
37
+ /** @type {BookReader} */
38
+ let br;
39
+ /** @type {BookModel} */
40
+ let bookModel;
41
+ /** @type {Mode2Up} */
42
+ let mode2Up;
37
43
 
38
- const stopAnim = sinon.spy(br, 'stopFlipAnimations');
39
- const resizeSpread = sinon.spy(br._modes.mode2Up, 'resizeSpread');
40
- br._modes.mode2Up.zoom('in');
41
-
42
- test('stops animations when zooming', () => {
43
- expect(stopAnim.callCount).toBe(1);
44
- });
45
- test('always redraws when zooming', () => {
46
- expect(resizeSpread.callCount).toBe(0);
47
- });
48
- });
49
-
50
- describe('page flip directions', () => {
51
- test('animates the left page in the correct direction', () => {
52
- const br = new BookReader({ data: SAMPLE_DATA });
53
- br.init();
54
-
55
- const fake = sinon.fake();
56
- const fakeAnimWithCB = sinon.fake.yields();
57
- const fakeAnim = sinon.fake((...args) =>
58
- typeof args[args.length - 1] === 'function' ? fakeAnimWithCB(...args) : fake
59
- );
60
- sinon.replace(jQuery.prototype, 'animate', fakeAnim);
61
-
62
- const fakeCSS = sinon.spy(jQuery.prototype, 'css');
63
-
64
- br.next();
65
-
66
- expect(fakeAnimWithCB.callCount).toBe(2);
67
- // Find the call to .css() immediately preceding the second animation with a callback (i.e., the left page animation)
68
- const preSecondAnimCssCallIndex = fakeCSS.getCalls().findIndex(call => call.calledAfter(fakeAnimWithCB.getCall(1))) - 1;
69
- expect(fakeCSS.getCall(preSecondAnimCssCallIndex).args[0].left).toBe('');
70
- });
71
- });
72
-
73
- describe('prefetch', () => {
74
- test('loads nearby pages', () => {
75
- const br = new BookReader({ data: SAMPLE_DATA });
76
- const mode2Up = br._modes.mode2Up;
77
- br.init();
78
- mode2Up.prefetch();
79
- expect(Object.keys(mode2Up.pageContainers).length).toBeGreaterThan(2);
80
- });
81
-
82
- test('works when at end of book', () => {
83
- const br = new BookReader({ data: SAMPLE_DATA });
84
- const mode2Up = br._modes.mode2Up;
85
- br.init();
86
- br.jumpToIndex(-1);
87
- mode2Up.prefetch();
88
- expect(Object.keys(mode2Up.pageContainers).length).toBeGreaterThan(2);
89
- });
90
-
91
- test('skips consecutive unviewables', () => {
92
- const data = deepCopy(SAMPLE_DATA);
93
- data[1].forEach(page => page.viewable = false);
94
- const br = new BookReader({ data });
95
- const mode2Up = br._modes.mode2Up;
96
- br.init();
97
- mode2Up.prefetch();
98
- expect(mode2Up.pageContainers).not.toContain(2);
99
- });
100
- });
101
-
102
- describe('draw 2up leaves', () => {
103
- test('calls `drawLeafs` on init as default', () => {
104
- const br = new BookReader({ data: SAMPLE_DATA });
105
- const drawLeafs = sinon.spy(br._modes.mode2Up, 'drawLeafs');
106
-
107
- br.init();
108
- expect(drawLeafs.callCount).toBe(1);
109
- });
110
-
111
- test('sets `this.displayedIndices`', () => {
112
- const extremelyWrongValueForDisplayedIndices = null;
113
- const br = new BookReader({ data: SAMPLE_DATA });
114
-
115
- br.init();
116
- br.displayedIndices = extremelyWrongValueForDisplayedIndices;
117
- expect(br.displayedIndices).toBe(extremelyWrongValueForDisplayedIndices);
118
-
119
- br._modes.mode2Up.drawLeafs();
120
-
121
- expect(br.displayedIndices).not.toBe(extremelyWrongValueForDisplayedIndices);
122
- expect(br.displayedIndices.length).toBe(2); // is array
123
- expect(br.displayedIndices).toEqual([-1, 0]); // default to starting index on right, placeholder for left
124
- });
125
- });
126
-
127
- describe('resizeSpread', () => {
128
- test('only resizes spread', () => {
129
- const br = new BookReader({ data: SAMPLE_DATA });
130
- br.init();
131
- const resizeBRcontainer = sinon.spy(br, 'resizeBRcontainer');
132
- const calculateSpreadSize = sinon.spy(br._modes.mode2Up, 'calculateSpreadSize');
133
- const drawLeafs = sinon.spy(br._modes.mode2Up, 'drawLeafs');
134
- const centerView = sinon.spy(br._modes.mode2Up, 'centerView');
135
-
136
- br._modes.mode2Up.resizeSpread();
137
- expect(drawLeafs.callCount).toBe(0); // no draw
138
- expect(resizeBRcontainer.callCount).toBe(1);
139
- expect(calculateSpreadSize.callCount).toBe(1);
140
- expect(centerView.callCount).toBe(1);
141
- });
142
- });
143
-
144
- describe('2up Container sizing', () => {
145
- test('baseLeafCss', () => {
146
- const br = new BookReader({ data: SAMPLE_DATA });
147
- br.init();
148
- br._modes.mode2Up.calculateSpreadSize();
149
- expect(Object.keys(br._modes.mode2Up.baseLeafCss)).toEqual(['position', 'right', 'top', 'zIndex']);
150
- });
151
- test('heightCss', () => {
152
- const br = new BookReader({ data: SAMPLE_DATA });
153
- br.init();
154
- br._modes.mode2Up.calculateSpreadSize();
155
- const heightStub = 1000;
156
- br.twoPage.height = heightStub;
157
- expect(Object.keys(br._modes.mode2Up.heightCss)).toEqual(['height']);
158
- expect(br._modes.mode2Up.heightCss).toEqual({height: `${heightStub}px`});
159
- });
160
- describe('left side', () => {
161
- const br = new BookReader({ data: SAMPLE_DATA });
162
- br.init();
163
- br._modes.mode2Up.calculateSpreadSize();
164
-
165
- test('leftLeafCss', () => {
166
- expect(Object.keys(br._modes.mode2Up.leftLeafCss)).toEqual([
167
- 'position',
168
- 'right',
169
- 'top',
170
- 'zIndex',
171
- 'height',
172
- 'left',
173
- 'width',
174
- ]);
44
+ beforeEach(() => {
45
+ br = new BookReader({
46
+ data: SAMPLE_DATA,
47
+ el: document.createElement('div'),
175
48
  });
176
- test('leafEdgeLCss', () => {
177
- expect(Object.keys(br._modes.mode2Up.leafEdgeLCss)).toEqual([
178
- 'height',
179
- 'width',
180
- 'left',
181
- 'top',
182
- 'border'
183
- ]);
184
- });
185
- });
186
- describe('right side', () => {
187
- const br = new BookReader({ data: SAMPLE_DATA });
188
49
  br.init();
189
- br._modes.mode2Up.calculateSpreadSize();
190
-
191
- test('rightLeafCss', () => {
192
- expect(Object.keys(br._modes.mode2Up.rightLeafCss)).toEqual([
193
- 'position',
194
- 'right',
195
- 'top',
196
- 'zIndex',
197
- 'height',
198
- 'left',
199
- 'width',
200
- ]);
201
- });
202
- test('leafEdgeRCss', () => {
203
- expect(Object.keys(br._modes.mode2Up.leafEdgeRCss)).toEqual([
204
- 'height',
205
- 'width',
206
- 'left',
207
- 'top',
208
- 'border'
209
- ]);
210
- });
50
+ bookModel = new BookModel(br);
51
+ mode2Up = new Mode2Up(br, bookModel);
211
52
  });
212
- describe('full width container, overlay + spine', () => {
213
- test('mainContainerCss', () => {
214
- const br = new BookReader({ data: SAMPLE_DATA });
215
- br.init();
216
- br._modes.mode2Up.calculateSpreadSize();
217
53
 
218
- expect(Object.keys(br._modes.mode2Up.mainContainerCss)).toEqual(['height', 'width', 'position']);
219
- });
220
- test('spreadCoverCss', () => {
221
- const br = new BookReader({ data: SAMPLE_DATA });
222
- br.init();
223
- br._modes.mode2Up.calculateSpreadSize();
224
- expect(Object.keys(br._modes.mode2Up.spreadCoverCss)).toEqual(['width', 'height', 'visibility']);
225
- });
226
- test('spineCss', () => {
227
- const br = new BookReader({ data: SAMPLE_DATA });
228
- br.init();
229
- br._modes.mode2Up.calculateSpreadSize();
230
- expect(Object.keys(br._modes.mode2Up.spineCss)).toEqual(['width', 'height', 'left', 'top']);
54
+ describe('prepare', () => {
55
+ it('initializes DOM properly', async () => {
56
+ const spyAppend = sinon.spy(br.refs.$brContainer, 'append');
57
+ const spyRequestUpdate = sinon.spy(mode2Up.mode2UpLit, 'requestUpdate');
58
+ const initFirstRenderSpy = sinon.spy(mode2Up.mode2UpLit, 'initFirstRender');
59
+ const jumpToIndexSpy = sinon.spy(mode2Up.mode2UpLit, 'jumpToIndex');
60
+ sinon.stub(mode2Up.mode2UpLit, 'updateComplete').get(() => Promise.resolve());
61
+ mode2Up.prepare();
62
+
63
+ expect(br.refs.$brContainer[0].style.overflow).toEqual('hidden');
64
+ expect(spyAppend.calledWith(mode2Up.$el)).toBe(true);
65
+ expect(mode2Up.mode2UpLit.style.opacity).toEqual('0');
66
+
67
+ await afterEventLoop();
68
+
69
+ expect(initFirstRenderSpy.called).toBe(true);
70
+ expect(jumpToIndexSpy.called).toBe(false);
71
+ expect(mode2Up.everShown).toBe(true);
72
+ expect(spyRequestUpdate.called).toBe(true);
73
+ expect(mode2Up.mode2UpLit.style.opacity).toEqual('1');
231
74
  });
232
75
  });
233
- });
234
-
235
- describe('prepareTwoPageView', () => {
236
- describe('drawing spread', () => {
237
- test('always draws new spread if `drawNewSpread` is true ', () => {
238
- const br = new BookReader({ data: SAMPLE_DATA });
239
- const mode2Up = br._modes.mode2Up;
240
- br.init();
241
- const drawLeafs = sinon.spy(mode2Up, 'drawLeafs');
242
- const resizeSpread = sinon.spy(mode2Up, 'resizeSpread');
243
- const calculateSpreadSize = sinon.spy(mode2Up, 'calculateSpreadSize');
244
- const prunePageContainers = sinon.spy(mode2Up, 'prunePageContainers');
245
- const prefetch = sinon.spy(mode2Up, 'prefetch');
246
- const centerView = sinon.spy(mode2Up, 'centerView');
247
- const preparePopUp = sinon.spy(mode2Up, 'preparePopUp');
248
- const updateBrClasses = sinon.spy(br, 'updateBrClasses');
249
76
 
250
- mode2Up.prepare(undefined, undefined, true);
251
- expect(prefetch.callCount).toBe(1);
252
-
253
- expect(resizeSpread.callCount).toBe(0);
254
- expect(drawLeafs.callCount).toBe(1);
255
- expect(calculateSpreadSize.callCount).toBe(1);
256
- expect(prunePageContainers.callCount).toBe(1);
257
- expect(centerView.callCount).toBe(1);
258
- expect(preparePopUp.callCount).toBe(1);
259
- expect(updateBrClasses.callCount).toBe(1);
77
+ describe('resizePageView', () => {
78
+ it('updates autoFit when possible', async () => {
79
+ const updateClientSizesSpy = sinon.stub(mode2Up.mode2UpLit.htmlDimensionsCacher, 'updateClientSizes');
80
+ const resizeViaAutofitSpy = sinon.spy(mode2Up.mode2UpLit, 'resizeViaAutofit');
81
+ const recenterStub = sinon.stub(mode2Up.mode2UpLit, 'recenter');
82
+
83
+ mode2Up.resizePageView();
84
+ expect(updateClientSizesSpy.called).toBe(true);
85
+ expect(resizeViaAutofitSpy.called).toBe(true);
86
+ expect(recenterStub.called).toBe(true);
87
+
88
+ // Test with scale and autoFit as 'none'
89
+ mode2Up.mode2UpLit.scale = 0.1;
90
+ mode2Up.mode2UpLit.autoFit = 'none';
91
+ mode2Up.resizePageView();
92
+
93
+ expect(mode2Up.mode2UpLit.autoFit).toEqual('auto');
94
+ expect(resizeViaAutofitSpy.callCount).toEqual(2);
95
+ expect(recenterStub.callCount).toEqual(2);
260
96
  });
261
97
  });
262
98
  });
263
-
264
- test('uses ImageCache', () => {
265
- const br = new BookReader({ data: SAMPLE_DATA });
266
- br.init();
267
- expect(Object.keys(br.imageCache.cache).length).toBeGreaterThan(2);
268
- });
@@ -0,0 +1,190 @@
1
+ import sinon from "sinon";
2
+ import { BookModel } from "@/src/BookReader/BookModel.js";
3
+ import { Mode2UpLit } from "@/src/BookReader/Mode2UpLit.js";
4
+
5
+ /** @type {import('@/src/BookReader/options.js').BookReaderOptions['data']} */
6
+ const SAMPLE_DATA = [
7
+ [
8
+ {
9
+ width: 123,
10
+ height: 123,
11
+ uri: "https://archive.org/image0.jpg",
12
+ pageNum: "1",
13
+ },
14
+ ],
15
+ [
16
+ {
17
+ width: 123,
18
+ height: 123,
19
+ uri: "https://archive.org/image1.jpg",
20
+ pageNum: "2",
21
+ },
22
+ {
23
+ width: 123,
24
+ height: 123,
25
+ uri: "https://archive.org/image2.jpg",
26
+ pageNum: "3",
27
+ },
28
+ ],
29
+ [
30
+ {
31
+ width: 123,
32
+ height: 123,
33
+ uri: "https://archive.org/image3.jpg",
34
+ pageNum: "4",
35
+ },
36
+ {
37
+ width: 123,
38
+ height: 123,
39
+ uri: "https://archive.org/image4.jpg",
40
+ pageNum: "5",
41
+ },
42
+ ],
43
+ [
44
+ {
45
+ width: 123,
46
+ height: 123,
47
+ uri: "https://archive.org/image5.jpg",
48
+ pageNum: "6",
49
+ },
50
+ ],
51
+ ];
52
+
53
+ function make_dummy_br(overrides = {}) {
54
+ return Object.assign({
55
+ updateFirstIndex() {},
56
+ _components: {
57
+ navbar: {
58
+ updateNavIndexThrottled() {},
59
+ },
60
+ },
61
+ data: [],
62
+ }, overrides);
63
+ }
64
+
65
+ afterEach(() => {
66
+ sinon.restore();
67
+ });
68
+
69
+ describe("computePageHeight", () => {
70
+ test("Always returns the median", () => {
71
+ const mode = new Mode2UpLit({
72
+ getMedianPageSizeInches: () => ({ width: 100, height: 200 }),
73
+ }, null);
74
+ expect(mode.computePageHeight(null)).toEqual(200);
75
+ expect(mode.computePageHeight({ widthInches: 300, heightInches: 400 }))
76
+ .toEqual(200);
77
+ });
78
+ });
79
+
80
+ describe("computePageWidth", () => {
81
+ test("returns relative to pageHeight", () => {
82
+ const mode = new Mode2UpLit(null, null);
83
+ sinon.stub(mode, "computePageHeight").returns(6);
84
+
85
+ expect(mode.computePageWidth({ widthInches: 2, heightInches: 6 })).toEqual(
86
+ 2,
87
+ );
88
+ expect(mode.computePageWidth({ widthInches: 3, heightInches: 6 })).toEqual(
89
+ 3,
90
+ );
91
+ expect(mode.computePageWidth({ widthInches: 2, heightInches: 4 })).toEqual(
92
+ 2 * 6 / 4,
93
+ );
94
+ });
95
+ });
96
+
97
+ describe("computePositions", () => {
98
+ const LEFT_COVER_EXPECTED = {
99
+ leafEdgesLeftStart: -0.246,
100
+ leafEdgesLeftMainWidth: 0,
101
+ leafEdgesLeftMovingStart: -0.246,
102
+ leafEdgesLeftMovingWidth: 0,
103
+ leafEdgesLeftEnd: -0.246,
104
+ leafEdgesLeftFullWidth: 0,
105
+ pageLeftStart: -0.246,
106
+ pageLeftWidth: 0.246,
107
+ pageLeftEnd: 0,
108
+ gutter: 0,
109
+ pageRightStart: 0,
110
+ pageRightWidth: 0.246,
111
+ pageRightEnd: 0.246,
112
+ leafEdgesRightStart: 0.246,
113
+ leafEdgesRightMovingWidth: 0,
114
+ leafEdgesRightMainStart: 0.246,
115
+ leafEdgesRightMainWidth: 0.006,
116
+ leafEdgesRightEnd: 0.252,
117
+ leafEdgesRightFullWidth: 0.006,
118
+ spreadWidth: 0.492,
119
+ bookWidth: 0.498
120
+ };
121
+ const SPREAD_EXPECTED = {
122
+ leafEdgesLeftStart: -0.246,
123
+ leafEdgesLeftMainWidth: 0.002,
124
+ leafEdgesLeftMovingStart: -0.244,
125
+ leafEdgesLeftMovingWidth: 0,
126
+ leafEdgesLeftEnd: -0.244,
127
+ leafEdgesLeftFullWidth: 0.002,
128
+ pageLeftStart: -0.244,
129
+ pageLeftWidth: 0.246,
130
+ pageLeftEnd: 0.002,
131
+ gutter: 0.002,
132
+ pageRightStart: 0.002,
133
+ pageRightWidth: 0.246,
134
+ pageRightEnd: 0.248,
135
+ leafEdgesRightStart: 0.248,
136
+ leafEdgesRightMovingWidth: 0,
137
+ leafEdgesRightMainStart: 0.248,
138
+ leafEdgesRightMainWidth: 0.004,
139
+ leafEdgesRightEnd: 0.252,
140
+ leafEdgesRightFullWidth: 0.004,
141
+ spreadWidth: 0.492,
142
+ bookWidth: 0.498
143
+ };
144
+ const RIGHT_COVER_EXPECTED = {
145
+ leafEdgesLeftStart: -0.242,
146
+ leafEdgesLeftMainWidth: 0.006,
147
+ leafEdgesLeftMovingStart: -0.236,
148
+ leafEdgesLeftMovingWidth: 0,
149
+ leafEdgesLeftEnd: -0.236,
150
+ leafEdgesLeftFullWidth: 0.006,
151
+ pageLeftStart: -0.236,
152
+ pageLeftWidth: 0.246,
153
+ pageLeftEnd: 0.01,
154
+ gutter: 0.01,
155
+ pageRightStart: 0.01,
156
+ pageRightWidth: 0,
157
+ pageRightEnd: 0.01,
158
+ leafEdgesRightStart: 0.01,
159
+ leafEdgesRightMovingWidth: 0,
160
+ leafEdgesRightMainStart: 0.01,
161
+ leafEdgesRightMainWidth: 0,
162
+ leafEdgesRightEnd: 0.01,
163
+ leafEdgesRightFullWidth: 0,
164
+ spreadWidth: 0.246,
165
+ bookWidth: 0.252
166
+ };
167
+
168
+ test("left cover page", () => {
169
+ const br = make_dummy_br({ data: SAMPLE_DATA });
170
+ const book = new BookModel(br);
171
+ const mode = new Mode2UpLit(book, br);
172
+ expect(mode.computePositions(null, book.getPage(0))).toEqual(LEFT_COVER_EXPECTED);
173
+ });
174
+
175
+ test("spread", () => {
176
+ const br = make_dummy_br({ data: SAMPLE_DATA });
177
+ const book = new BookModel(br);
178
+ const mode = new Mode2UpLit(book, br);
179
+
180
+ expect(mode.computePositions(book.getPage(1), book.getPage(2))).toEqual(SPREAD_EXPECTED);
181
+ });
182
+
183
+ test("right cover page", () => {
184
+ const br = make_dummy_br({ data: SAMPLE_DATA });
185
+ const book = new BookModel(br);
186
+ const mode = new Mode2UpLit(book, br);
187
+
188
+ expect(mode.computePositions(book.getPage(-1), null)).toEqual(RIGHT_COVER_EXPECTED);
189
+ });
190
+ });
@@ -0,0 +1,16 @@
1
+ import { ModeCoordinateSpace } from "@/src/BookReader/ModeCoordinateSpace";
2
+
3
+ describe("worldUnitsToRenderedPixels", () => {
4
+ test("0 case", () => {
5
+ const mcs = new ModeCoordinateSpace({ scale: 1 });
6
+ expect(mcs.worldUnitsToRenderedPixels(0)).toBe(0);
7
+ });
8
+
9
+ test("Misc cases", () => {
10
+ const mcs = new ModeCoordinateSpace({ scale: 1 });
11
+ mcs.screenDPI = 100;
12
+ expect(mcs.worldUnitsToRenderedPixels(1)).toBe(100);
13
+ mcs.screenDPI = 78;
14
+ expect(mcs.worldUnitsToRenderedPixels(1)).toBe(78);
15
+ });
16
+ });
@@ -146,4 +146,30 @@ describe('ModeSmoothZoom', () => {
146
146
  expect(mode.scale).not.toBe(1);
147
147
  });
148
148
  });
149
+
150
+ describe("updateViewportOnZoom", () => {
151
+ test("adjusts scroll position when zooming in", () => {
152
+ const mode = dummy_mode();
153
+ const msz = new ModeSmoothZoom(mode);
154
+ mode.$container.scrollTop = 100;
155
+ mode.$container.scrollLeft = 100;
156
+
157
+ msz.updateViewportOnZoom(2, 1);
158
+
159
+ expect(mode.$container.scrollTop).toBeGreaterThan(100);
160
+ expect(mode.$container.scrollLeft).toBeGreaterThan(100);
161
+ });
162
+
163
+ test("updates scroll position when zooming out", () => {
164
+ const mode = dummy_mode();
165
+ const msz = new ModeSmoothZoom(mode);
166
+ mode.$container.scrollTop = 100;
167
+ mode.$container.scrollLeft = 100;
168
+
169
+ msz.updateViewportOnZoom(0.5, 1);
170
+
171
+ expect(mode.$container.scrollTop).toBeLessThan(100);
172
+ expect(mode.$container.scrollLeft).toBeLessThan(100);
173
+ });
174
+ });
149
175
  });
@@ -63,14 +63,14 @@ describe('Navbar slider', () => {
63
63
 
64
64
  test('on slide change, actual page changes', () => {
65
65
  const $slider = navbar.$root.find('.BRpager');
66
- const jumpToIndexSpy = sinon.spy(br, 'jumpToIndex');
66
+ const jumpToIndexStub = sinon.stub(br, 'jumpToIndex');
67
67
  expect(br.currentIndex()).toBe(0);
68
68
 
69
69
  $slider.trigger('slidechange', { value: 3 });
70
70
 
71
71
  expect(navbar.$root.find('.BRcurrentpage').text().includes('3'));
72
- expect(jumpToIndexSpy.callCount).toBe(1);
73
- expect(jumpToIndexSpy.args[0][0]).toBe(3);
72
+ expect(jumpToIndexStub.callCount).toBe(1);
73
+ expect(jumpToIndexStub.args[0][0]).toBe(3);
74
74
  });
75
75
  });
76
76
 
@@ -1,5 +1,5 @@
1
1
  import sinon from 'sinon';
2
- import { afterEventLoop } from '../utils.js';
2
+ import { afterEventLoop, eventTargetMixin } from '../utils.js';
3
3
  import {
4
4
  clamp,
5
5
  cssPercentage,
@@ -12,6 +12,7 @@ import {
12
12
  poll,
13
13
  polyfillCustomEvent,
14
14
  PolyfilledCustomEvent,
15
+ promisifyEvent,
15
16
  sleep,
16
17
  } from '@/src/BookReader/utils.js';
17
18
 
@@ -184,3 +185,33 @@ describe('sleep', () => {
184
185
  expect(spy.callCount).toBe(1);
185
186
  });
186
187
  });
188
+
189
+ describe('promisifyEvent', () => {
190
+ test('Resolves once event fires', async () => {
191
+ const fakeTarget = eventTargetMixin();
192
+ const resolveSpy = sinon.spy();
193
+ promisifyEvent(fakeTarget, 'pause').then(resolveSpy);
194
+
195
+ await afterEventLoop();
196
+ expect(resolveSpy.callCount).toBe(0);
197
+ fakeTarget.dispatchEvent('pause', {});
198
+ await afterEventLoop();
199
+ expect(resolveSpy.callCount).toBe(1);
200
+ });
201
+
202
+ test('Only resolves once', async () => {
203
+ const fakeTarget = eventTargetMixin();
204
+ const resolveSpy = sinon.spy();
205
+ promisifyEvent(fakeTarget, 'pause').then(resolveSpy);
206
+
207
+ await afterEventLoop();
208
+ expect(resolveSpy.callCount).toBe(0);
209
+ fakeTarget.dispatchEvent('pause', {});
210
+ fakeTarget.dispatchEvent('pause', {});
211
+ fakeTarget.dispatchEvent('pause', {});
212
+ fakeTarget.dispatchEvent('pause', {});
213
+
214
+ await afterEventLoop();
215
+ expect(resolveSpy.callCount).toBe(1);
216
+ });
217
+ });
@@ -1,39 +1,5 @@
1
- import sinon from 'sinon';
2
- import { afterEventLoop, eventTargetMixin } from '../../utils.js';
3
1
  import * as utils from '@/src/plugins/tts/utils.js';
4
2
 
5
- describe('promisifyEvent', () => {
6
- const { promisifyEvent } = utils;
7
-
8
- test('Resolves once event fires', async () => {
9
- const fakeTarget = eventTargetMixin();
10
- const resolveSpy = sinon.spy();
11
- promisifyEvent(fakeTarget, 'pause').then(resolveSpy);
12
-
13
- await afterEventLoop();
14
- expect(resolveSpy.callCount).toBe(0);
15
- fakeTarget.dispatchEvent('pause', {});
16
- await afterEventLoop();
17
- expect(resolveSpy.callCount).toBe(1);
18
- });
19
-
20
- test('Only resolves once', async () => {
21
- const fakeTarget = eventTargetMixin();
22
- const resolveSpy = sinon.spy();
23
- promisifyEvent(fakeTarget, 'pause').then(resolveSpy);
24
-
25
- await afterEventLoop();
26
- expect(resolveSpy.callCount).toBe(0);
27
- fakeTarget.dispatchEvent('pause', {});
28
- fakeTarget.dispatchEvent('pause', {});
29
- fakeTarget.dispatchEvent('pause', {});
30
- fakeTarget.dispatchEvent('pause', {});
31
-
32
- await afterEventLoop();
33
- expect(resolveSpy.callCount).toBe(1);
34
- });
35
- });
36
-
37
3
  describe('approximateWordCount', () => {
38
4
  const { approximateWordCount } = utils;
39
5