@webex/plugin-meetings 3.12.0-next.22 → 3.12.0-next.23
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/dist/aiEnableRequest/index.js +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/interceptors/locusRetry.js +23 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/types/interceptors/locusRetry.d.ts +4 -4
- package/dist/webinar/index.js +1 -1
- package/package.json +1 -1
- package/src/interceptors/locusRetry.ts +25 -4
- package/test/unit/spec/interceptors/locusRetry.ts +205 -4
|
@@ -178,7 +178,7 @@ var AIEnableRequest = _webexCore.WebexPlugin.extend({
|
|
|
178
178
|
method: _constants.HTTP_VERBS.PUT
|
|
179
179
|
});
|
|
180
180
|
},
|
|
181
|
-
version: "3.12.0-next.
|
|
181
|
+
version: "3.12.0-next.23"
|
|
182
182
|
});
|
|
183
183
|
var _default = exports.default = AIEnableRequest;
|
|
184
184
|
//# sourceMappingURL=index.js.map
|
package/dist/breakouts/index.js
CHANGED
|
@@ -1109,7 +1109,7 @@ var Breakouts = _webexCore.WebexPlugin.extend({
|
|
|
1109
1109
|
this.trigger(_constants.BREAKOUTS.EVENTS.ASK_RETURN_TO_MAIN);
|
|
1110
1110
|
}
|
|
1111
1111
|
},
|
|
1112
|
-
version: "3.12.0-next.
|
|
1112
|
+
version: "3.12.0-next.23"
|
|
1113
1113
|
});
|
|
1114
1114
|
var _default = exports.default = Breakouts;
|
|
1115
1115
|
//# sourceMappingURL=index.js.map
|
|
@@ -31,14 +31,12 @@ var LocusRetryStatusInterceptor = exports.default = /*#__PURE__*/function (_Inte
|
|
|
31
31
|
(0, _inherits2.default)(LocusRetryStatusInterceptor, _Interceptor);
|
|
32
32
|
return (0, _createClass2.default)(LocusRetryStatusInterceptor, [{
|
|
33
33
|
key: "onResponseError",
|
|
34
|
-
value:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*/
|
|
41
|
-
function onResponseError(options, reason) {
|
|
34
|
+
value: function onResponseError(options, reason) {
|
|
35
|
+
// Don't retry /hashtree or /sync calls for 429 or any 5xx — during a sync storm retries
|
|
36
|
+
// make things worse. The normal sync timers will handle recovery for these endpoints.
|
|
37
|
+
if ((reason.statusCode === 429 || reason.statusCode >= 500) && LocusRetryStatusInterceptor.isLocusHashtreeOrSync(options.uri)) {
|
|
38
|
+
return _promise.default.reject(reason);
|
|
39
|
+
}
|
|
42
40
|
if ((reason.statusCode === 503 || reason.statusCode === 429) && options.uri.includes('locus')) {
|
|
43
41
|
var hasRetriedLocusRequest = rateLimitExpiryTime.get(this);
|
|
44
42
|
var retryAfterTime = options.headers['retry-after'] || 2000;
|
|
@@ -87,6 +85,23 @@ var LocusRetryStatusInterceptor = exports.default = /*#__PURE__*/function (_Inte
|
|
|
87
85
|
webex: this
|
|
88
86
|
});
|
|
89
87
|
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check whether a URI is a Locus /hashtree or /sync endpoint.
|
|
91
|
+
* @param {string} uri
|
|
92
|
+
* @returns {boolean}
|
|
93
|
+
*/
|
|
94
|
+
}, {
|
|
95
|
+
key: "isLocusHashtreeOrSync",
|
|
96
|
+
value: function isLocusHashtreeOrSync(uri) {
|
|
97
|
+
try {
|
|
98
|
+
var _URL = new URL(uri),
|
|
99
|
+
pathname = _URL.pathname;
|
|
100
|
+
return pathname.includes('/locus/') && (pathname.endsWith('/hashtree') || pathname.endsWith('/sync'));
|
|
101
|
+
} catch (_unused) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
90
105
|
}]);
|
|
91
106
|
}(_httpCore.Interceptor);
|
|
92
107
|
//# sourceMappingURL=locusRetry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_httpCore","require","_callSuper","t","o","e","_getPrototypeOf2","default","_possibleConstructorReturn2","_isNativeReflectConstruct","_Reflect$construct","constructor","apply","Boolean","prototype","valueOf","call","rateLimitExpiryTime","_weakMap","LocusRetryStatusInterceptor","exports","_Interceptor","_classCallCheck2","arguments","_inherits2","_createClass2","key","value","onResponseError","options","reason","statusCode","uri","includes","hasRetriedLocusRequest","get","retryAfterTime","headers","set","
|
|
1
|
+
{"version":3,"names":["_httpCore","require","_callSuper","t","o","e","_getPrototypeOf2","default","_possibleConstructorReturn2","_isNativeReflectConstruct","_Reflect$construct","constructor","apply","Boolean","prototype","valueOf","call","rateLimitExpiryTime","_weakMap","LocusRetryStatusInterceptor","exports","_Interceptor","_classCallCheck2","arguments","_inherits2","_createClass2","key","value","onResponseError","options","reason","statusCode","isLocusHashtreeOrSync","uri","_promise","reject","includes","hasRetriedLocusRequest","get","retryAfterTime","headers","set","handleRetryRequestLocusServiceError","_this","resolve","timeout","setTimeout","clearTimeout","webex","request","method","body","then","catch","create","_URL","URL","pathname","endsWith","_unused","Interceptor"],"sources":["locusRetry.ts"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {Interceptor} from '@webex/http-core';\n\nconst rateLimitExpiryTime = new WeakMap();\n/**\n * @class\n */\nexport default class LocusRetryStatusInterceptor extends Interceptor {\n /**\n * @returns {LocusRetryStatusInterceptor}\n */\n static create() {\n // @ts-ignore\n return new LocusRetryStatusInterceptor({webex: this});\n }\n\n /**\n * Check whether a URI is a Locus /hashtree or /sync endpoint.\n * @param {string} uri\n * @returns {boolean}\n */\n private static isLocusHashtreeOrSync(uri: string): boolean {\n try {\n const {pathname} = new URL(uri);\n\n return (\n pathname.includes('/locus/') &&\n (pathname.endsWith('/hashtree') || pathname.endsWith('/sync'))\n );\n } catch {\n return false;\n }\n }\n\n onResponseError(options, reason) {\n // Don't retry /hashtree or /sync calls for 429 or any 5xx — during a sync storm retries\n // make things worse. The normal sync timers will handle recovery for these endpoints.\n if (\n (reason.statusCode === 429 || reason.statusCode >= 500) &&\n LocusRetryStatusInterceptor.isLocusHashtreeOrSync(options.uri)\n ) {\n return Promise.reject(reason);\n }\n\n if ((reason.statusCode === 503 || reason.statusCode === 429) && options.uri.includes('locus')) {\n const hasRetriedLocusRequest = rateLimitExpiryTime.get(this);\n const retryAfterTime = options.headers['retry-after'] || 2000;\n\n if (hasRetriedLocusRequest) {\n rateLimitExpiryTime.set(this, false);\n\n return Promise.reject(options);\n }\n rateLimitExpiryTime.set(this, true);\n\n return this.handleRetryRequestLocusServiceError(options, retryAfterTime);\n }\n\n return Promise.reject(reason);\n }\n\n /**\n * Handle retries for locus service unavailable errors\n * @param {Object} options associated with the request\n * @param {number} retryAfterTime retry after time in milliseconds\n * @returns {Promise}\n */\n handleRetryRequestLocusServiceError(options, retryAfterTime) {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n clearTimeout(timeout);\n\n // @ts-ignore\n this.webex\n .request({\n method: options.method,\n uri: options.uri,\n body: options.body,\n })\n .then(resolve)\n .catch(reject);\n }, retryAfterTime);\n });\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -372,7 +372,7 @@ var SimultaneousInterpretation = _webexCore.WebexPlugin.extend({
|
|
|
372
372
|
throw error;
|
|
373
373
|
});
|
|
374
374
|
},
|
|
375
|
-
version: "3.12.0-next.
|
|
375
|
+
version: "3.12.0-next.23"
|
|
376
376
|
});
|
|
377
377
|
var _default = exports.default = SimultaneousInterpretation;
|
|
378
378
|
//# sourceMappingURL=index.js.map
|
|
@@ -18,7 +18,7 @@ var SILanguage = _webexCore.WebexPlugin.extend({
|
|
|
18
18
|
languageCode: 'number',
|
|
19
19
|
languageName: 'string'
|
|
20
20
|
},
|
|
21
|
-
version: "3.12.0-next.
|
|
21
|
+
version: "3.12.0-next.23"
|
|
22
22
|
});
|
|
23
23
|
var _default = exports.default = SILanguage;
|
|
24
24
|
//# sourceMappingURL=siLanguage.js.map
|
|
@@ -11,11 +11,11 @@ export default class LocusRetryStatusInterceptor extends Interceptor {
|
|
|
11
11
|
*/
|
|
12
12
|
static create(): LocusRetryStatusInterceptor;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
* @param {
|
|
16
|
-
* @
|
|
17
|
-
* @returns {Promise<WebexHttpError>}
|
|
14
|
+
* Check whether a URI is a Locus /hashtree or /sync endpoint.
|
|
15
|
+
* @param {string} uri
|
|
16
|
+
* @returns {boolean}
|
|
18
17
|
*/
|
|
18
|
+
private static isLocusHashtreeOrSync;
|
|
19
19
|
onResponseError(options: any, reason: any): Promise<unknown>;
|
|
20
20
|
/**
|
|
21
21
|
* Handle retries for locus service unavailable errors
|
package/dist/webinar/index.js
CHANGED
package/package.json
CHANGED
|
@@ -18,12 +18,33 @@ export default class LocusRetryStatusInterceptor extends Interceptor {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @param {
|
|
23
|
-
* @
|
|
24
|
-
* @returns {Promise<WebexHttpError>}
|
|
21
|
+
* Check whether a URI is a Locus /hashtree or /sync endpoint.
|
|
22
|
+
* @param {string} uri
|
|
23
|
+
* @returns {boolean}
|
|
25
24
|
*/
|
|
25
|
+
private static isLocusHashtreeOrSync(uri: string): boolean {
|
|
26
|
+
try {
|
|
27
|
+
const {pathname} = new URL(uri);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
pathname.includes('/locus/') &&
|
|
31
|
+
(pathname.endsWith('/hashtree') || pathname.endsWith('/sync'))
|
|
32
|
+
);
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
onResponseError(options, reason) {
|
|
39
|
+
// Don't retry /hashtree or /sync calls for 429 or any 5xx — during a sync storm retries
|
|
40
|
+
// make things worse. The normal sync timers will handle recovery for these endpoints.
|
|
41
|
+
if (
|
|
42
|
+
(reason.statusCode === 429 || reason.statusCode >= 500) &&
|
|
43
|
+
LocusRetryStatusInterceptor.isLocusHashtreeOrSync(options.uri)
|
|
44
|
+
) {
|
|
45
|
+
return Promise.reject(reason);
|
|
46
|
+
}
|
|
47
|
+
|
|
27
48
|
if ((reason.statusCode === 503 || reason.statusCode === 429) && options.uri.includes('locus')) {
|
|
28
49
|
const hasRetriedLocusRequest = rateLimitExpiryTime.get(this);
|
|
29
50
|
const retryAfterTime = options.headers['retry-after'] || 2000;
|
|
@@ -36,6 +36,27 @@ describe('plugin-meetings', () => {
|
|
|
36
36
|
uri: `https://locus-test.webex.com/locus/api/v1/loci/call`,
|
|
37
37
|
body: 'foo'
|
|
38
38
|
};
|
|
39
|
+
|
|
40
|
+
const hashTreeOptions = {
|
|
41
|
+
method: 'GET',
|
|
42
|
+
headers: {
|
|
43
|
+
trackingid: 'test',
|
|
44
|
+
'retry-after': 1000,
|
|
45
|
+
},
|
|
46
|
+
uri: `https://locus-test.webex.com/locus/api/v1/loci/12345/session/abc/datasets/main/hashtree`,
|
|
47
|
+
body: undefined,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const syncOptions = {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
trackingid: 'test',
|
|
54
|
+
'retry-after': 1000,
|
|
55
|
+
},
|
|
56
|
+
uri: `https://locus-test.webex.com/locus/api/v1/loci/12345/session/abc/datasets/main/sync`,
|
|
57
|
+
body: 'foo',
|
|
58
|
+
};
|
|
59
|
+
|
|
39
60
|
const reason1 = new WebexHttpError.MethodNotAllowed({
|
|
40
61
|
statusCode: 403,
|
|
41
62
|
options: {
|
|
@@ -68,14 +89,194 @@ describe('plugin-meetings', () => {
|
|
|
68
89
|
});
|
|
69
90
|
|
|
70
91
|
it('calls handleRetryRequestLocusServiceError with correct retry time when locus service unavailable error', () => {
|
|
71
|
-
|
|
72
|
-
|
|
92
|
+
interceptor.webex.request = sinon.stub().returns(Promise.resolve());
|
|
93
|
+
const handleRetryStub = sinon.stub(
|
|
94
|
+
interceptor,
|
|
95
|
+
'handleRetryRequestLocusServiceError'
|
|
96
|
+
);
|
|
97
|
+
handleRetryStub.returns(Promise.resolve());
|
|
98
|
+
|
|
99
|
+
return interceptor.onResponseError(options, reason2).then(() => {
|
|
100
|
+
expect(handleRetryStub.calledWith(options, 1000)).to.be.true;
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
[429, 500, 502, 503, 504].forEach((statusCode) => {
|
|
105
|
+
it(`does not retry /hashtree requests on ${statusCode}`, () => {
|
|
106
|
+
const reason = new WebexHttpError.MethodNotAllowed({
|
|
107
|
+
statusCode,
|
|
108
|
+
options: {
|
|
109
|
+
headers: {trackingid: 'test', 'retry-after': 1000},
|
|
110
|
+
uri: hashTreeOptions.uri,
|
|
111
|
+
},
|
|
112
|
+
body: {error: `Fake ${statusCode}`},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const handleRetryStub = sinon.stub(
|
|
116
|
+
interceptor,
|
|
117
|
+
'handleRetryRequestLocusServiceError'
|
|
118
|
+
);
|
|
119
|
+
handleRetryStub.returns(Promise.resolve());
|
|
120
|
+
|
|
121
|
+
return interceptor.onResponseError(hashTreeOptions, reason).then(
|
|
122
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
123
|
+
(err) => {
|
|
124
|
+
expect(err).to.equal(reason);
|
|
125
|
+
expect(handleRetryStub.called).to.be.false;
|
|
126
|
+
handleRetryStub.restore();
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it(`does not retry /sync requests on ${statusCode}`, () => {
|
|
132
|
+
const reason = new WebexHttpError.MethodNotAllowed({
|
|
133
|
+
statusCode,
|
|
134
|
+
options: {
|
|
135
|
+
headers: {trackingid: 'test', 'retry-after': 1000},
|
|
136
|
+
uri: syncOptions.uri,
|
|
137
|
+
},
|
|
138
|
+
body: {error: `Fake ${statusCode}`},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const handleRetryStub = sinon.stub(
|
|
142
|
+
interceptor,
|
|
143
|
+
'handleRetryRequestLocusServiceError'
|
|
144
|
+
);
|
|
73
145
|
handleRetryStub.returns(Promise.resolve());
|
|
74
146
|
|
|
75
|
-
return interceptor.onResponseError(
|
|
76
|
-
|
|
147
|
+
return interceptor.onResponseError(syncOptions, reason).then(
|
|
148
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
149
|
+
(err) => {
|
|
150
|
+
expect(err).to.equal(reason);
|
|
151
|
+
expect(handleRetryStub.called).to.be.false;
|
|
152
|
+
handleRetryStub.restore();
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('still retries other locus requests on 429', () => {
|
|
159
|
+
const reason429 = new WebexHttpError.MethodNotAllowed({
|
|
160
|
+
statusCode: 429,
|
|
161
|
+
options: {
|
|
162
|
+
headers: {trackingid: 'test', 'retry-after': 1000},
|
|
163
|
+
uri: options.uri,
|
|
164
|
+
},
|
|
165
|
+
body: {error: 'Too Many Requests'},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
interceptor.webex.request = sinon.stub().returns(Promise.resolve());
|
|
169
|
+
const handleRetryStub = sinon.stub(
|
|
170
|
+
interceptor,
|
|
171
|
+
'handleRetryRequestLocusServiceError'
|
|
172
|
+
);
|
|
173
|
+
handleRetryStub.returns(Promise.resolve());
|
|
77
174
|
|
|
175
|
+
return interceptor.onResponseError(options, reason429).then(() => {
|
|
176
|
+
expect(handleRetryStub.calledOnce).to.be.true;
|
|
177
|
+
handleRetryStub.restore();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('still retries other locus requests on 503', () => {
|
|
182
|
+
interceptor.webex.request = sinon.stub().returns(Promise.resolve());
|
|
183
|
+
const handleRetryStub = sinon.stub(
|
|
184
|
+
interceptor,
|
|
185
|
+
'handleRetryRequestLocusServiceError'
|
|
186
|
+
);
|
|
187
|
+
handleRetryStub.returns(Promise.resolve());
|
|
188
|
+
|
|
189
|
+
return interceptor.onResponseError(options, reason2).then(() => {
|
|
190
|
+
expect(handleRetryStub.calledOnce).to.be.true;
|
|
191
|
+
handleRetryStub.restore();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('URI parsing edge cases', () => {
|
|
196
|
+
const make503Reason = (uri) =>
|
|
197
|
+
new WebexHttpError.MethodNotAllowed({
|
|
198
|
+
statusCode: 503,
|
|
199
|
+
options: {headers: {trackingid: 'test', 'retry-after': 1000}, uri},
|
|
200
|
+
body: {error: 'Service Unavailable'},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const makeOptions = (uri) => ({
|
|
204
|
+
method: 'GET',
|
|
205
|
+
headers: {trackingid: 'test', 'retry-after': 1000},
|
|
206
|
+
uri,
|
|
207
|
+
body: undefined,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
[
|
|
211
|
+
'https://locus.webex.com/locus/api/v1/loci/123/session/abc/datasets/main/hashtree?rootHash=xyz',
|
|
212
|
+
'https://locus.webex.com/locus/api/v1/loci/123/session/abc/datasets/main/sync?seq=5',
|
|
213
|
+
].forEach((uri) => {
|
|
214
|
+
it(`skips retry even with query params: ${uri.split('/').pop()}`, () => {
|
|
215
|
+
const opts = makeOptions(uri);
|
|
216
|
+
const reason = make503Reason(uri);
|
|
217
|
+
const stub = sinon
|
|
218
|
+
.stub(interceptor, 'handleRetryRequestLocusServiceError')
|
|
219
|
+
.returns(Promise.resolve());
|
|
220
|
+
|
|
221
|
+
return interceptor.onResponseError(opts, reason).then(
|
|
222
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
223
|
+
(err) => {
|
|
224
|
+
expect(err).to.equal(reason);
|
|
225
|
+
expect(stub.called).to.be.false;
|
|
226
|
+
stub.restore();
|
|
227
|
+
}
|
|
228
|
+
);
|
|
78
229
|
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
[
|
|
233
|
+
'https://locus.webex.com/locus/api/v1/loci/123/hashtree-v2',
|
|
234
|
+
'https://locus.webex.com/locus/api/v1/loci/123/syncData',
|
|
235
|
+
'https://locus.webex.com/locus/api/v1/loci/123/async',
|
|
236
|
+
'https://locus.webex.com/locus/api/v1/loci/123/hashtree/metadata',
|
|
237
|
+
].forEach((uri) => {
|
|
238
|
+
it(`still retries when path only partially matches: ${uri
|
|
239
|
+
.split('/')
|
|
240
|
+
.pop()}`, () => {
|
|
241
|
+
const opts = makeOptions(uri);
|
|
242
|
+
const reason = make503Reason(uri);
|
|
243
|
+
interceptor.webex.request = sinon.stub().returns(Promise.resolve());
|
|
244
|
+
const stub = sinon
|
|
245
|
+
.stub(interceptor, 'handleRetryRequestLocusServiceError')
|
|
246
|
+
.returns(Promise.resolve());
|
|
247
|
+
|
|
248
|
+
return interceptor.onResponseError(opts, reason).then(() => {
|
|
249
|
+
expect(stub.calledOnce).to.be.true;
|
|
250
|
+
stub.restore();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('still retries when /hashtree is on a non-locus host', () => {
|
|
256
|
+
const uri = 'https://other-service.webex.com/api/v1/hashtree';
|
|
257
|
+
const opts = makeOptions(uri);
|
|
258
|
+
const reason = make503Reason(uri);
|
|
259
|
+
|
|
260
|
+
return interceptor.onResponseError(opts, reason).then(
|
|
261
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
262
|
+
(err) => {
|
|
263
|
+
expect(err).to.equal(reason);
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('still retries when URI is malformed', () => {
|
|
269
|
+
const uri = 'not-a-valid-url';
|
|
270
|
+
const opts = makeOptions(uri);
|
|
271
|
+
const reason = make503Reason(uri);
|
|
272
|
+
|
|
273
|
+
return interceptor.onResponseError(opts, reason).then(
|
|
274
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
275
|
+
(err) => {
|
|
276
|
+
expect(err).to.equal(reason);
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
});
|
|
79
280
|
});
|
|
80
281
|
});
|
|
81
282
|
|