@internetarchive/ia-book-actions 1.4.4-alpha17 → 1.4.4-alpha18
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/package.json +1 -1
- package/src/core/services/actions-handler/actions-handler-service.js +4 -3
- package/src/core/services/actions-handler/actions-handler.js +4 -6
- package/src/core/services/loan-token-poller.js +0 -8
- package/src/ia-book-actions.js +18 -37
- package/test/core/services/loan-token-poller.test.js +1 -43
- package/test/ia-book-actions.test.js +70 -79
package/package.json
CHANGED
|
@@ -106,9 +106,9 @@ export default async function ActionsHandlerService(options) {
|
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
108
|
* Report it, don't just swallow it. A rejected fetch (offline, dropped
|
|
109
|
-
* connection) or a non-JSON body (a 405 returning HTML
|
|
110
|
-
*
|
|
111
|
-
*
|
|
109
|
+
* connection) or a non-JSON body (a 405 returning HTML) lands here, and
|
|
110
|
+
* calling neither `success` nor `error` leaves every caller waiting on a
|
|
111
|
+
* callback that never comes.
|
|
112
112
|
*
|
|
113
113
|
* For renew_loan that's not merely a missing modal: IABookActions
|
|
114
114
|
* clears its loanRenewInProgress guard from these callbacks, so a
|
|
@@ -117,5 +117,6 @@ export default async function ActionsHandlerService(options) {
|
|
|
117
117
|
* connection is exactly when this fires.
|
|
118
118
|
*/
|
|
119
119
|
log(`[IABookActions] ✗ ${option.action} threw`, error);
|
|
120
|
+
option?.error?.({ error: String(error) });
|
|
120
121
|
}
|
|
121
122
|
}
|
|
@@ -185,7 +185,7 @@ export default class ActionsHandler extends LitElement {
|
|
|
185
185
|
log('RENEW_LOAN --- ', data, this.identifier);
|
|
186
186
|
const activeLoan = data.loan ? data.loan : undefined;
|
|
187
187
|
// Optional chaining: `data.loan` is genuinely absent on some
|
|
188
|
-
// failures, and
|
|
188
|
+
// failures, and `activeLoan` can be undefined here.
|
|
189
189
|
const isRenewal = activeLoan?.renewal;
|
|
190
190
|
|
|
191
191
|
if (activeLoan && isRenewal) {
|
|
@@ -210,11 +210,9 @@ export default class ActionsHandler extends LitElement {
|
|
|
210
210
|
this.identifier
|
|
211
211
|
);
|
|
212
212
|
|
|
213
|
-
// Dispatch the success outcome ONLY for a confirmed renewal
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
// success: the error modal went up while handleLoanAutoRenewed
|
|
217
|
-
// simultaneously put a fresh countdown back on the bar.
|
|
213
|
+
// Dispatch the success outcome ONLY for a confirmed renewal —
|
|
214
|
+
// a `{loan: {renewal: false}}` response goes through
|
|
215
|
+
// dispatchActionError below instead, never both.
|
|
218
216
|
this.dispatchEvent(
|
|
219
217
|
new CustomEvent('loanAutoRenewed', {
|
|
220
218
|
detail: { action, data: { ...data, loan: activeLoan } },
|
|
@@ -26,11 +26,6 @@ export class LoanTokenPoller {
|
|
|
26
26
|
* attempt_to_renew_loan() already minted a valid access token as part
|
|
27
27
|
* of the renew_loan response itself — an immediate confirming
|
|
28
28
|
* create_token call is redundant.
|
|
29
|
-
* @param {Function} [options.onTokenRefreshed] - called after EVERY
|
|
30
|
-
* successful create_token, initial or routine — unlike successCallback,
|
|
31
|
-
* which only fires for the initial one. Lets the consumer retry any
|
|
32
|
-
* page image that failed during a prior access gap, now that access is
|
|
33
|
-
* confirmed good again.
|
|
34
29
|
*/
|
|
35
30
|
constructor(options = {}) {
|
|
36
31
|
const {
|
|
@@ -40,7 +35,6 @@ export class LoanTokenPoller {
|
|
|
40
35
|
errorCallback,
|
|
41
36
|
pollerDelay,
|
|
42
37
|
skipInitialCall = false,
|
|
43
|
-
onTokenRefreshed,
|
|
44
38
|
} = options;
|
|
45
39
|
|
|
46
40
|
this.identifier = identifier;
|
|
@@ -49,7 +43,6 @@ export class LoanTokenPoller {
|
|
|
49
43
|
this.errorCallback = errorCallback; // callback function to be called after loan token is created
|
|
50
44
|
this.pollerDelay = pollerDelay; // value in seconds
|
|
51
45
|
this.skipInitialCall = skipInitialCall === true;
|
|
52
|
-
this.onTokenRefreshed = onTokenRefreshed;
|
|
53
46
|
|
|
54
47
|
this.loanTokenInterval = undefined;
|
|
55
48
|
|
|
@@ -118,7 +111,6 @@ export class LoanTokenPoller {
|
|
|
118
111
|
identifier: this.identifier,
|
|
119
112
|
isInitial,
|
|
120
113
|
});
|
|
121
|
-
this.onTokenRefreshed?.();
|
|
122
114
|
if (isInitial) this.successCallback();
|
|
123
115
|
},
|
|
124
116
|
});
|
package/src/ia-book-actions.js
CHANGED
|
@@ -55,10 +55,6 @@ export default class IABookActions extends LitElement {
|
|
|
55
55
|
type: Function,
|
|
56
56
|
attribute: false,
|
|
57
57
|
},
|
|
58
|
-
reloadPageImages: {
|
|
59
|
-
type: Function,
|
|
60
|
-
attribute: false,
|
|
61
|
-
},
|
|
62
58
|
barType: { type: String },
|
|
63
59
|
sharedObserver: { attribute: false },
|
|
64
60
|
disableActionGroup: { type: Boolean },
|
|
@@ -81,7 +77,6 @@ export default class IABookActions extends LitElement {
|
|
|
81
77
|
this.width = 0;
|
|
82
78
|
this.bwbPurchaseUrl = '';
|
|
83
79
|
this.lendingBarPostInit = () => {};
|
|
84
|
-
this.reloadPageImages = () => {};
|
|
85
80
|
this.barType = 'action'; // 'title'|'action'
|
|
86
81
|
this.sharedObserver = undefined;
|
|
87
82
|
this.disableActionGroup = false;
|
|
@@ -767,12 +762,6 @@ export default class IABookActions extends LitElement {
|
|
|
767
762
|
// triggered this renewal.
|
|
768
763
|
this.skipNextInitialTokenCall = true;
|
|
769
764
|
|
|
770
|
-
// Retry any page image that failed during the gap this renewal just
|
|
771
|
-
// closed, right away rather than waiting for the next routine
|
|
772
|
-
// create_token tick (up to tokenDelay seconds away, now that the
|
|
773
|
-
// initial one above is skipped).
|
|
774
|
-
this.reloadPageImages();
|
|
775
|
-
|
|
776
765
|
if (this.recoveringFromLoanExpiry) {
|
|
777
766
|
// Only now is it safe to re-initialize BookReader — not for a
|
|
778
767
|
// routine top-up, where that would be disruptive. Call it
|
|
@@ -946,13 +935,6 @@ export default class IABookActions extends LitElement {
|
|
|
946
935
|
const errorCallback = eventObj => {
|
|
947
936
|
this.handleLendingActionError(eventObj);
|
|
948
937
|
};
|
|
949
|
-
// BookReader has no self-healing for a page <img> that failed while
|
|
950
|
-
// access was briefly invalid — retry any of those now that this
|
|
951
|
-
// create_token confirms access is good again, on every success, not
|
|
952
|
-
// just the initial one.
|
|
953
|
-
const onTokenRefreshed = () => {
|
|
954
|
-
this.reloadPageImages();
|
|
955
|
-
};
|
|
956
938
|
|
|
957
939
|
// Tear down any previous poller first, so only one is ever controlling
|
|
958
940
|
// window.IALendingIntervals.tokenPoller at a time.
|
|
@@ -964,7 +946,6 @@ export default class IABookActions extends LitElement {
|
|
|
964
946
|
errorCallback,
|
|
965
947
|
pollerDelay: this.tokenDelay, // in seconds
|
|
966
948
|
skipInitialCall,
|
|
967
|
-
onTokenRefreshed,
|
|
968
949
|
});
|
|
969
950
|
}
|
|
970
951
|
|
|
@@ -989,7 +970,12 @@ export default class IABookActions extends LitElement {
|
|
|
989
970
|
this.disableActionGroup = false;
|
|
990
971
|
|
|
991
972
|
const action = event?.detail?.action;
|
|
992
|
-
|
|
973
|
+
// handleLoanRenewNow's failure path sets `.error` to a boolean flag and
|
|
974
|
+
// puts the real text in `.message` — only take `.error` when it's
|
|
975
|
+
// actually a string, or the modal renders the literal word "true".
|
|
976
|
+
const rawErrorMsg = event?.detail?.data?.error;
|
|
977
|
+
const errorMsg =
|
|
978
|
+
typeof rawErrorMsg === 'string' ? rawErrorMsg : undefined;
|
|
993
979
|
const isInitial = event?.detail?.isInitial === true;
|
|
994
980
|
|
|
995
981
|
log('[IABookActions] handleLendingActionError', {
|
|
@@ -1002,20 +988,14 @@ export default class IABookActions extends LitElement {
|
|
|
1002
988
|
});
|
|
1003
989
|
|
|
1004
990
|
if (action === 'create_token') {
|
|
1005
|
-
//
|
|
1006
|
-
//
|
|
1007
|
-
//
|
|
1008
|
-
//
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
// The book never opened, and we don't retry — reset the bar to
|
|
1017
|
-
// Borrow and the timer to 0 rather than leaving a red "Return now"
|
|
1018
|
-
// with a stale countdown for a session that isn't accessible.
|
|
991
|
+
// Any create_token failure — initial or a routine interval refresh —
|
|
992
|
+
// means access can no longer be confirmed (e.g. a stale loan-record
|
|
993
|
+
// cache on some other node can make this request fail even though
|
|
994
|
+
// the loan is fine, or the loan may genuinely have just been
|
|
995
|
+
// returned via BookReaderImages.php's revokeAccess()). Don't retry
|
|
996
|
+
// and don't stay silent either way: reset the bar to Borrow and the
|
|
997
|
+
// timer to 0 rather than leaving a stale countdown for a session
|
|
998
|
+
// that isn't accessible.
|
|
1019
999
|
window?.IALendingIntervals?.clearAll();
|
|
1020
1000
|
this.tokenPoller?.disconnectedCallback();
|
|
1021
1001
|
this.lendingStatus = {
|
|
@@ -1026,8 +1006,9 @@ export default class IABookActions extends LitElement {
|
|
|
1026
1006
|
};
|
|
1027
1007
|
|
|
1028
1008
|
// showErrorModal has dedicated create_token messaging (refresh
|
|
1029
|
-
// button + support email)
|
|
1030
|
-
|
|
1009
|
+
// button + support email) and shows regardless of whether a
|
|
1010
|
+
// specific error string came back.
|
|
1011
|
+
this.showErrorModal(errorMsg, action);
|
|
1031
1012
|
} else if (action === 'renew_loan') {
|
|
1032
1013
|
window?.IALendingIntervals?.clearAll();
|
|
1033
1014
|
this.loanRenewInProgress = false;
|
|
@@ -1095,7 +1076,7 @@ export default class IABookActions extends LitElement {
|
|
|
1095
1076
|
.identifier}"
|
|
1096
1077
|
>info@archive.org</a
|
|
1097
1078
|
><br /><br />
|
|
1098
|
-
|
|
1079
|
+
${errorMsg ? html`<code>errorLog: ${errorMsg}</code>` : nothing}`;
|
|
1099
1080
|
}
|
|
1100
1081
|
|
|
1101
1082
|
await this.modal?.showModal({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
2
|
import Sinon from 'sinon';
|
|
3
3
|
|
|
4
4
|
import { LoanTokenPoller } from '../../../src/core/services/loan-token-poller.js';
|
|
@@ -152,45 +152,3 @@ describe('skipInitialCall option', () => {
|
|
|
152
152
|
poller.disconnectedCallback();
|
|
153
153
|
});
|
|
154
154
|
});
|
|
155
|
-
|
|
156
|
-
describe('onTokenRefreshed option', () => {
|
|
157
|
-
// BookReader has no self-healing for a page <img> that failed while
|
|
158
|
-
// access was briefly invalid, so the consumer needs a signal on EVERY
|
|
159
|
-
// create_token success (not just the initial one) to know it's safe to
|
|
160
|
-
// retry those images.
|
|
161
|
-
it('fires on the initial create_token success', async () => {
|
|
162
|
-
const onTokenRefreshed = Sinon.spy();
|
|
163
|
-
const poller = new LoanTokenPoller({
|
|
164
|
-
identifier: 'foo',
|
|
165
|
-
borrowType: 'browsed',
|
|
166
|
-
successCallback: () => {},
|
|
167
|
-
errorCallback: () => {},
|
|
168
|
-
pollerDelay: 120,
|
|
169
|
-
onTokenRefreshed,
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
await aTimeout(50);
|
|
173
|
-
|
|
174
|
-
expect(onTokenRefreshed.called).to.be.true;
|
|
175
|
-
poller.disconnectedCallback();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('fires on a routine (non-initial) create_token success too', async () => {
|
|
179
|
-
const onTokenRefreshed = Sinon.spy();
|
|
180
|
-
const poller = new LoanTokenPoller({
|
|
181
|
-
identifier: 'foo',
|
|
182
|
-
borrowType: 'browsed',
|
|
183
|
-
successCallback: () => {},
|
|
184
|
-
errorCallback: () => {},
|
|
185
|
-
pollerDelay: 120,
|
|
186
|
-
onTokenRefreshed,
|
|
187
|
-
});
|
|
188
|
-
onTokenRefreshed.resetHistory();
|
|
189
|
-
|
|
190
|
-
await poller.handleLoanTokenPoller(false);
|
|
191
|
-
await aTimeout(50);
|
|
192
|
-
|
|
193
|
-
expect(onTokenRefreshed.called).to.be.true;
|
|
194
|
-
poller.disconnectedCallback();
|
|
195
|
-
});
|
|
196
|
-
});
|
|
@@ -958,7 +958,6 @@ describe('autoRenewExpiredLoan', () => {
|
|
|
958
958
|
);
|
|
959
959
|
await el.updateComplete;
|
|
960
960
|
el.postInitComplete = true;
|
|
961
|
-
el.reloadPageImages = Sinon.spy();
|
|
962
961
|
|
|
963
962
|
await el.localCache.set({
|
|
964
963
|
key: 'foobar-loanTime',
|
|
@@ -986,9 +985,6 @@ describe('autoRenewExpiredLoan', () => {
|
|
|
986
985
|
await el.updateComplete;
|
|
987
986
|
|
|
988
987
|
expect(startTokenPollerSpy.calledOnceWith(true)).to.be.true;
|
|
989
|
-
// Retried right away rather than waiting for the (now-skipped) initial
|
|
990
|
-
// create_token or the next routine tick.
|
|
991
|
-
expect(el.reloadPageImages.called).to.be.true;
|
|
992
988
|
});
|
|
993
989
|
|
|
994
990
|
it('calls lendingBarPostInit directly on a recovery renewal and skips the initial create_token', async () => {
|
|
@@ -1010,7 +1006,6 @@ describe('autoRenewExpiredLoan', () => {
|
|
|
1010
1006
|
);
|
|
1011
1007
|
await el.updateComplete;
|
|
1012
1008
|
el.lendingBarPostInit = Sinon.spy();
|
|
1013
|
-
el.reloadPageImages = Sinon.spy();
|
|
1014
1009
|
|
|
1015
1010
|
await el.localCache.set({
|
|
1016
1011
|
key: 'foobar-loanTime',
|
|
@@ -1035,7 +1030,6 @@ describe('autoRenewExpiredLoan', () => {
|
|
|
1035
1030
|
// create_token success callback.
|
|
1036
1031
|
expect(el.lendingBarPostInit.calledOnce).to.be.true;
|
|
1037
1032
|
expect(el.postInitComplete).to.be.true;
|
|
1038
|
-
expect(el.reloadPageImages.called).to.be.true;
|
|
1039
1033
|
|
|
1040
1034
|
await el.updateComplete;
|
|
1041
1035
|
await aTimeout(150); // let the 100ms token-poller-restart check run
|
|
@@ -1176,75 +1170,75 @@ describe('handleLendingActionError - loanRenewInProgress reset', () => {
|
|
|
1176
1170
|
});
|
|
1177
1171
|
});
|
|
1178
1172
|
|
|
1179
|
-
describe('handleLendingActionError - create_token
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1173
|
+
describe('handleLendingActionError - every create_token failure resets the bar (WEBDEV-8322 follow-up)', () => {
|
|
1174
|
+
// A create_token failure can't be told apart from "the loan was actually
|
|
1175
|
+
// returned" (e.g. a stale loan-record cache on another node, or a genuine
|
|
1176
|
+
// revokeAccess() in BookReaderImages.php) from here, so initial and
|
|
1177
|
+
// routine interval failures are treated identically: no retry, reset to
|
|
1178
|
+
// Borrow/0, show the modal every time.
|
|
1179
|
+
for (const isInitial of [true, false]) {
|
|
1180
|
+
it(`shows the error modal and resets to Borrow/0 on a failed create_token (isInitial=${isInitial})`, async () => {
|
|
1181
|
+
const el = await fixture(
|
|
1182
|
+
container({
|
|
1183
|
+
userid: '@user1',
|
|
1184
|
+
identifier: 'foobar',
|
|
1185
|
+
lendingStatus: {
|
|
1186
|
+
user_has_browsed: true,
|
|
1187
|
+
browsingExpired: false,
|
|
1188
|
+
secondsLeftOnLoan: 100,
|
|
1189
|
+
},
|
|
1190
|
+
})
|
|
1191
|
+
);
|
|
1192
|
+
await el.updateComplete;
|
|
1193
|
+
|
|
1194
|
+
const showErrorModalSpy = Sinon.spy(el, 'showErrorModal');
|
|
1195
|
+
const errorMsg = 'loan token not found. please try again later.';
|
|
1196
|
+
|
|
1197
|
+
el.handleLendingActionError({
|
|
1198
|
+
detail: {
|
|
1199
|
+
action: 'create_token',
|
|
1200
|
+
isInitial,
|
|
1201
|
+
data: { error: errorMsg },
|
|
1189
1202
|
},
|
|
1190
|
-
})
|
|
1191
|
-
);
|
|
1192
|
-
await el.updateComplete;
|
|
1193
|
-
expect(window.IALendingIntervals.timerCountdown).to.not.equal(0);
|
|
1203
|
+
});
|
|
1194
1204
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1205
|
+
expect(showErrorModalSpy.calledOnceWith(errorMsg, 'create_token')).to.be
|
|
1206
|
+
.true;
|
|
1207
|
+
expect(el.lendingStatus.user_has_browsed).to.be.false;
|
|
1208
|
+
expect(el.lendingStatus.available_to_browse).to.be.true;
|
|
1209
|
+
expect(el.lendingStatus.secondsLeftOnLoan).to.equal(0);
|
|
1200
1210
|
});
|
|
1201
1211
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1212
|
+
it(`stops the countdown/expiry timers on a failed create_token (isInitial=${isInitial})`, async () => {
|
|
1213
|
+
const el = await fixture(
|
|
1214
|
+
container({
|
|
1215
|
+
userid: '@user1',
|
|
1216
|
+
identifier: 'foobar',
|
|
1217
|
+
lendingStatus: {
|
|
1218
|
+
user_has_browsed: true,
|
|
1219
|
+
browsingExpired: false,
|
|
1220
|
+
secondsLeftOnLoan: 100,
|
|
1221
|
+
},
|
|
1222
|
+
})
|
|
1223
|
+
);
|
|
1224
|
+
await el.updateComplete;
|
|
1225
|
+
expect(window.IALendingIntervals.timerCountdown).to.not.equal(0);
|
|
1226
|
+
expect(window.IALendingIntervals.browseExpireTimeout).to.not.equal(0);
|
|
1206
1227
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
const el = await fixture(
|
|
1213
|
-
container({
|
|
1214
|
-
userid: '@user1',
|
|
1215
|
-
identifier: 'foobar',
|
|
1216
|
-
lendingStatus: {
|
|
1217
|
-
user_has_browsed: true,
|
|
1218
|
-
browsingExpired: false,
|
|
1219
|
-
secondsLeftOnLoan: 100,
|
|
1228
|
+
el.handleLendingActionError({
|
|
1229
|
+
detail: {
|
|
1230
|
+
action: 'create_token',
|
|
1231
|
+
isInitial,
|
|
1232
|
+
data: { error: 'loan token not found. please try again later.' },
|
|
1220
1233
|
},
|
|
1221
|
-
})
|
|
1222
|
-
);
|
|
1223
|
-
await el.updateComplete;
|
|
1224
|
-
|
|
1225
|
-
const showErrorModalSpy = Sinon.spy(el, 'showErrorModal');
|
|
1226
|
-
const errorMsg = 'loan token not found. please try again later.';
|
|
1234
|
+
});
|
|
1227
1235
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
action: 'create_token',
|
|
1231
|
-
isInitial: true,
|
|
1232
|
-
data: { error: errorMsg },
|
|
1233
|
-
},
|
|
1236
|
+
expect(window.IALendingIntervals.timerCountdown).to.equal(0);
|
|
1237
|
+
expect(window.IALendingIntervals.browseExpireTimeout).to.equal(0);
|
|
1234
1238
|
});
|
|
1239
|
+
}
|
|
1235
1240
|
|
|
1236
|
-
|
|
1237
|
-
.true;
|
|
1238
|
-
expect(el.lendingStatus.user_has_browsed).to.be.false;
|
|
1239
|
-
expect(el.lendingStatus.available_to_browse).to.be.true;
|
|
1240
|
-
expect(el.lendingStatus.secondsLeftOnLoan).to.equal(0);
|
|
1241
|
-
});
|
|
1242
|
-
|
|
1243
|
-
it('stops the countdown/expiry timers on a failed INITIAL create_token — nothing to read yet', async () => {
|
|
1244
|
-
// Unlike the interval-refresh case above (pages already loaded, keep
|
|
1245
|
-
// ticking), an initial create_token failure means BookReader never got
|
|
1246
|
-
// page images and we're resetting the bar to Borrow — both timers
|
|
1247
|
-
// should stop rather than keep counting down a dead session.
|
|
1241
|
+
it('shows the modal even without a specific error message', async () => {
|
|
1248
1242
|
const el = await fixture(
|
|
1249
1243
|
container({
|
|
1250
1244
|
userid: '@user1',
|
|
@@ -1257,19 +1251,14 @@ describe('handleLendingActionError - create_token failures must not stop the cou
|
|
|
1257
1251
|
})
|
|
1258
1252
|
);
|
|
1259
1253
|
await el.updateComplete;
|
|
1260
|
-
|
|
1261
|
-
|
|
1254
|
+
|
|
1255
|
+
const showErrorModalSpy = Sinon.spy(el, 'showErrorModal');
|
|
1262
1256
|
|
|
1263
1257
|
el.handleLendingActionError({
|
|
1264
|
-
detail: {
|
|
1265
|
-
action: 'create_token',
|
|
1266
|
-
isInitial: true,
|
|
1267
|
-
data: { error: 'loan token not found. please try again later.' },
|
|
1268
|
-
},
|
|
1258
|
+
detail: { action: 'create_token', isInitial: false, data: {} },
|
|
1269
1259
|
});
|
|
1270
1260
|
|
|
1271
|
-
expect(
|
|
1272
|
-
expect(window.IALendingIntervals.browseExpireTimeout).to.equal(0);
|
|
1261
|
+
expect(showErrorModalSpy.calledOnce).to.be.true;
|
|
1273
1262
|
});
|
|
1274
1263
|
|
|
1275
1264
|
it('still clears everything on a renew_loan failure', async () => {
|
|
@@ -1614,7 +1603,10 @@ describe('WEBDEV-8322 review fixes', () => {
|
|
|
1614
1603
|
expect(el.loanRenewInProgress).to.be.true;
|
|
1615
1604
|
});
|
|
1616
1605
|
|
|
1617
|
-
it('
|
|
1606
|
+
it('resets the bar and shows the modal on an interval create_token refresh failure too', async () => {
|
|
1607
|
+
// A routine refresh failing is treated the same as the initial one --
|
|
1608
|
+
// there's no reliable way to tell "harmless blip" from "the loan is
|
|
1609
|
+
// actually gone" from here (see handleLendingActionError).
|
|
1618
1610
|
const el = await fixture(
|
|
1619
1611
|
container({
|
|
1620
1612
|
userid: '@user1',
|
|
@@ -1640,9 +1632,8 @@ describe('WEBDEV-8322 review fixes', () => {
|
|
|
1640
1632
|
});
|
|
1641
1633
|
await el.updateComplete;
|
|
1642
1634
|
|
|
1643
|
-
expect(spy.
|
|
1644
|
-
|
|
1645
|
-
expect(el.lendingStatus.user_has_browsed).to.be.true;
|
|
1635
|
+
expect(spy.calledOnce).to.be.true;
|
|
1636
|
+
expect(el.lendingStatus.user_has_browsed).to.be.false;
|
|
1646
1637
|
});
|
|
1647
1638
|
|
|
1648
1639
|
it('tears down the previous poller before starting a new one', async () => {
|