@internetarchive/ia-book-actions 0.0.29-alpha38 → 0.0.29-alpha39

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/demo/index.html CHANGED
@@ -133,8 +133,8 @@
133
133
 
134
134
  let isDevBox = true;
135
135
  iaBookActions.loanRenewConfig = {
136
- totalTime: isDevBox ? 10 : 3600, // 60 second = 1 min
137
- autoCheckAt: isDevBox ? 5 : 3540, // at last 10th minute
136
+ totalTime: isDevBox ? 13 : 3600, // 60 second = 1 min
137
+ autoCheckAt: isDevBox ? 10 : 3540, // at last 10th minute
138
138
  pageChangedInLast: isDevBox ? 5 : 900, // within last 15 second/minute
139
139
  isDevBox: isDevBox,
140
140
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-book-actions",
3
- "version": "0.0.29-alpha38",
3
+ "version": "0.0.29-alpha39",
4
4
  "description": "Bookreader actions for borrowable book",
5
5
  "author": "Internet Archive",
6
6
  "license": "AGPL-3.0-only",
@@ -220,6 +220,13 @@ export default class IABookActions extends LitElement {
220
220
  return;
221
221
  }
222
222
 
223
+ // initial timer state for 1-hour loan
224
+ if (this.borrowType === 'browsed' && !this.loanRenewResult.renewNow) {
225
+ setTimeout(() => {
226
+ this.resetTimerCountState();
227
+ }, 100);
228
+ }
229
+
223
230
  /**
224
231
  * tokenPoller determines if user has loan token for this book
225
232
  * - if book is going to renew, need to wait until renew is completed
@@ -228,17 +235,13 @@ export default class IABookActions extends LitElement {
228
235
  setTimeout(
229
236
  () => {
230
237
  this.startLoanTokenPoller();
231
-
232
- if (this.borrowType === 'browsed' && !this.loanRenewResult.renewNow) {
233
- this.resetTimerCountState();
234
- }
235
238
  },
236
239
  this.loanRenewResult.renewNow ? this.tokenDelay * 1000 : 100
237
240
  );
238
241
  }
239
242
 
240
243
  /**
241
- * Bind 1 hour loan auto renew,
244
+ * Bind 1 hour loan auto renew event,
242
245
  * There are two events we want to use,
243
246
  * 1. BookReader:pageChanged - dispatched from bookreader side
244
247
  * 2. IABookActions:loanRenew - dispatched from timer-countdown component
@@ -371,11 +374,11 @@ export default class IABookActions extends LitElement {
371
374
  * Execute when loan is expired
372
375
  */
373
376
  async browseHasExpired() {
377
+ this.tokenPoller?.disconnectedInterval();
378
+
374
379
  const currStatus = { ...this.lendingStatus, browsingExpired: true };
375
380
  this.lendingStatus = currStatus;
376
381
 
377
- this.tokenPoller?.disconnectedInterval();
378
-
379
382
  // remove respected key:value for loan-renew
380
383
  await this.localCache.delete(`${this.identifier}-loanTime`);
381
384
  await this.localCache.delete(`${this.identifier}-pageChangedTime`);
@@ -0,0 +1,47 @@
1
+ import { html, fixture, expect, aTimeout } from '@open-wc/testing';
2
+ import '../../src/components/timer-countdown.js';
3
+
4
+ const container = ({ timeLeftOnLoan, loanRenewConfig } = {}) =>
5
+ html`<timer-countdown
6
+ .timeLeftOnLoan=${timeLeftOnLoan}
7
+ .loanRenewConfig=${loanRenewConfig}
8
+ ></timer-countdown>`;
9
+
10
+ describe('<timer-countdown>', () => {
11
+ it('timer interval is undefined when loan is expired', async () => {
12
+ const el = await fixture(
13
+ container({
14
+ timeLeftOnLoan: 0, // in seconds
15
+ loanRenewConfig: {
16
+ totalTime: 14, // in seconds
17
+ autoCheckAt: 5, // in seconds
18
+ pageChangedInLast: 5, // in seconds
19
+ isDevBox: true,
20
+ },
21
+ })
22
+ );
23
+
24
+ await el.updateComplete;
25
+ expect(el.timerInterval).to.be.undefined;
26
+ });
27
+
28
+ it('timer interval is not undefined when loan is active', async () => {
29
+ const el = await fixture(
30
+ container({
31
+ timeLeftOnLoan: 2, // in seconds
32
+ loanRenewConfig: {
33
+ totalTime: 14, // in seconds
34
+ autoCheckAt: 5, // in seconds
35
+ pageChangedInLast: 5, // in seconds
36
+ isDevBox: true,
37
+ },
38
+ })
39
+ );
40
+
41
+ await aTimeout(1900);
42
+ await el.updateComplete;
43
+
44
+ expect(el.timeLeftOnLoan).to.equal(1);
45
+ expect(el.timerInterval).to.not.be.undefined;
46
+ });
47
+ });
@@ -0,0 +1,85 @@
1
+ import { expect, aTimeout } from '@open-wc/testing';
2
+
3
+ import { LocalCache } from '@internetarchive/local-cache';
4
+ import { LoanRenewHelper } from '../../../src/core/services/loan-renew-helper.js';
5
+
6
+ const identifier = 'booBar';
7
+ const loanRenewConfig = {
8
+ totalTime: 13,
9
+ autoCheckAt: 10,
10
+ pageChangedInLast: 12,
11
+ isDevBox: true,
12
+ };
13
+
14
+ // localCache used for auto-loan-renew
15
+ const localCache = new LocalCache({
16
+ namespace: 'loanRenew',
17
+ });
18
+
19
+ beforeEach(async () => {
20
+ await localCache.set({
21
+ key: `${identifier}-loanTime`,
22
+ value: new Date(new Date().getTime() + loanRenewConfig.totalTime * 1000),
23
+ ttl: Number(loanRenewConfig.totalTime),
24
+ });
25
+ });
26
+
27
+ describe('Loan Renew Determine', () => {
28
+ it('flip book page while reading a book', async () => {
29
+ await aTimeout(1500);
30
+
31
+ await localCache.set({
32
+ key: `${identifier}-pageChangedTime`,
33
+ value: new Date(), // current time
34
+ ttl: Number(loanRenewConfig.totalTime),
35
+ });
36
+
37
+ const loanRenewHelper = new LoanRenewHelper(
38
+ true, // hasPageChanged
39
+ identifier,
40
+ localCache,
41
+ loanRenewConfig
42
+ );
43
+ await loanRenewHelper.handleLoanRenew();
44
+
45
+ expect(loanRenewHelper.result.renewNow).to.be.false;
46
+ });
47
+
48
+ it('auto renewed book when user is currently reading', async () => {
49
+ await localCache.set({
50
+ key: `${identifier}-pageChangedTime`,
51
+ value: new Date(), // current time
52
+ ttl: Number(loanRenewConfig.totalTime),
53
+ });
54
+
55
+ await aTimeout(1500);
56
+
57
+ const loanRenewHelper = new LoanRenewHelper(
58
+ false, // timer-countdown event
59
+ identifier,
60
+ localCache,
61
+ loanRenewConfig
62
+ );
63
+ await loanRenewHelper.handleLoanRenew();
64
+
65
+ expect(loanRenewHelper.result.renewNow).to.be.true;
66
+ });
67
+
68
+ it('get toast message template', async () => {
69
+ const loanRenewHelper = new LoanRenewHelper(
70
+ false,
71
+ identifier,
72
+ localCache,
73
+ loanRenewConfig
74
+ );
75
+
76
+ const toastMsg = loanRenewHelper.getMessageTexts(
77
+ loanRenewHelper.loanReturnWarning,
78
+ '65'
79
+ );
80
+
81
+ expect(toastMsg).to.be.equal(
82
+ 'This book will be automatically returned in 1 minute unless you turn a page.'
83
+ );
84
+ });
85
+ });
@@ -4,11 +4,17 @@ import '../src/ia-book-actions.js';
4
4
  import Sinon from 'sinon';
5
5
 
6
6
  import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
7
+ import { LocalCache } from '@internetarchive/local-cache';
7
8
 
8
9
  afterEach(() => {
9
10
  Sinon.restore();
10
11
  });
11
12
 
13
+ // localCache used for auto-loan-renew
14
+ const localCache = new LocalCache({
15
+ namespace: 'loanRenew',
16
+ });
17
+
12
18
  const container = ({ userid, identifier, lendingStatus = {} } = {}) =>
13
19
  html`<ia-book-actions
14
20
  .userid=${userid}
@@ -264,54 +270,14 @@ describe('Browsing expired status', () => {
264
270
  // timer-countdown is gone now
265
271
  expect(el.shadowRoot.querySelector('timer-countdown')).to.be.null;
266
272
 
267
- // book has been expired
273
+ // // book has been expired
274
+ expect(el.loanRenewResult.renewNow).to.equal(false);
268
275
  expect(el.loanRenewResult.timeLeft).to.equal(0);
269
276
  expect(el.loanRenewResult.texts).to.equal(
270
277
  'This book has been automatically returned due to inactivity.'
271
278
  );
272
279
  });
273
280
 
274
- it('Book is browsing and eligible to renew it', async () => {
275
- const el = await fixture(
276
- container({
277
- userid: '@userid',
278
- lendingStatus: {
279
- is_lendable: true,
280
- user_has_browsed: true,
281
- browseHasExpired: false,
282
- secondsLeftOnLoan: 1,
283
- },
284
- })
285
- );
286
-
287
- // timer-countdown is still active
288
- // expect(el.shadowRoot.querySelector('timer-countdown')).to.not.be.null
289
-
290
- // let eventReceived = false;
291
- // const listener = () => {
292
- // eventReceived = true;
293
- // };
294
- // el.addEventListener('IABookReader:userAction', listener);
295
-
296
- el.loanRenewResult = {
297
- texts: 'book has been renewed',
298
- renewNow: true,
299
- timeLeft: 10,
300
- };
301
-
302
- await aTimeout(1500); // wait for 1.5 second
303
- await el.updateComplete;
304
-
305
- // // timer-countdown is gone now
306
- // expect(el.shadowRoot.querySelector('timer-countdown')).to.not.be.null
307
- // expect(el.shadowRoot.querySelector('toast-template')).to.not.be.null
308
-
309
- expect(el.loanRenewResult.timeLeft).to.equal(10);
310
- // // book has been expired
311
- // expect(el.loanRenewResult.timeLeft).to.equal(0);
312
- // expect(el.loanRenewResult.texts).to.equal('This book has been automatically returned due to inactivity.');
313
- });
314
-
315
281
  it('Expiring book cancels interval & emits event', async () => {
316
282
  const baseStatus = {
317
283
  is_lendable: true,
@@ -348,40 +314,47 @@ describe('Browsing expired status', () => {
348
314
  });
349
315
  });
350
316
 
351
- describe('Shared Resize Observer', () => {
352
- it('can receive a Shared Resize Observer', async () => {
353
- const sharedObserverStub = new SharedResizeObserver();
354
- const addObserverSpy = Sinon.spy(sharedObserverStub, 'addObserver');
355
- const component = await fixture(html` <ia-book-actions
356
- .userid=${'@userid'}
357
- .identifier=${'foo'}
358
- .lendingStatus=${{
359
- is_lendable: true,
360
- user_has_browsed: true,
361
- available_to_browse: true,
362
- available_to_borrow: true,
363
- }}
364
- .sharedObserver=${sharedObserverStub}
365
- ></ia-book-actions>`);
366
- await component.updateComplete;
317
+ describe('Auto renew one hour loan', () => {
318
+ it('Book is browsing and renewed it now', async () => {
319
+ const el = await fixture(
320
+ container({
321
+ userid: '@userid',
322
+ identifier: 'Foo',
323
+ lendingStatus: {
324
+ is_lendable: true,
325
+ user_has_browsed: true,
326
+ browseHasExpired: false,
327
+ secondsLeftOnLoan: 12,
328
+ },
329
+ })
330
+ );
367
331
 
368
- expect(addObserverSpy.callCount).to.equal(1);
369
- });
332
+ el.loanRenewResult = {
333
+ texts: 'This book has been renewed for 1 hour.',
334
+ renewNow: true,
335
+ timeLeft: 10,
336
+ };
370
337
 
371
- it('loads its own resize observer if it is not received', async () => {
372
- const component = await fixture(html` <ia-book-actions
373
- .userid=${'@userid'}
374
- .identifier=${'foo'}
375
- .lendingStatus=${{
376
- is_lendable: true,
377
- user_has_browsed: true,
378
- available_to_browse: true,
379
- available_to_borrow: true,
380
- }}
381
- ></ia-book-actions>`);
338
+ await localCache.set({
339
+ key: `${el.identifier}-loanTime`,
340
+ value: new Date(new Date().getTime() + 10 * 1000),
341
+ ttl: Number(10),
342
+ });
382
343
 
383
- await component.updateComplete;
384
- expect(component.sharedObserver).to.not.be.undefined;
344
+ const myEvent = new CustomEvent('loanAutoRenewed');
345
+ el.addEventListener('loanAutoRenewed', () => {
346
+ el.handleLoanAutoRenewed();
347
+ });
348
+ el.dispatchEvent(myEvent);
349
+
350
+ await aTimeout(1500); // wait for 1.5 second
351
+ await el.updateComplete;
352
+
353
+ expect(el.loanRenewResult.timeLeft).to.equal(10);
354
+ expect(el.loanRenewResult.renewNow).to.equal(true);
355
+ expect(el.loanRenewResult.texts).to.equal(
356
+ 'This book has been renewed for 1 hour.'
357
+ );
385
358
  });
386
359
  });
387
360