spyne 0.23.0 → 0.25.0
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/lib/spyne.esm.js +3 -3
- package/lib/spyne.esm.js.map +1 -1
- package/lib/spyne.umd.js +2 -2
- package/lib/spyne.umd.js.LICENSE.txt +2 -2
- package/package.json +1 -1
- package/src/spyne/channels/channel-fetch-class.js +14 -5
- package/src/spyne/spyne-app.js +7 -3
- package/src/spyne/spyne.js +5 -2
- package/src/spyne/utils/channel-fetch-util.js +239 -95
- package/src/spyne/utils/sanitize-data.js +320 -35
- package/src/spyne/utils/sanitize-html.js +59 -12
- package/src/spyne/utils/spyne-app-properties.js +35 -6
- package/src/spyne/utils/spyne-warn.js +26 -0
- package/src/spyne/views/dom-element-template.js +5 -0
- package/src/spyne/views/dom-element.js +22 -2
- package/src/spyne/views/view-stream-selector.js +52 -47
- package/src/tests/channels/channel-fetch.test.js +88 -1
- package/src/tests/index.test.js +5 -0
- package/src/tests/utils/channel-fetch-util.test.js +44 -2
- package/src/tests/utils/security.test.js +352 -8
- package/src/tests/views/dom-el-selectors.test.js +7 -7
- package/src/tests/views/view-stream-selector.test.js +178 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { baseCoreMixins } from '../utils/mixins/base-core-mixins.js'
|
|
2
|
+
import { spyneWarn } from '../utils/spyne-warn.js'
|
|
2
3
|
import { DomElementTemplate } from './dom-element-template.js'
|
|
3
4
|
import { deepMerge } from '../utils/deep-merge.js'
|
|
5
|
+
import { sanitizeAttribute, applyIframeHardening } from '../utils/sanitize-data.js'
|
|
4
6
|
import { is, defaultTo, pick, mapObjIndexed, forEachObjIndexed, pipe } from 'ramda'
|
|
5
7
|
|
|
6
8
|
class DomElement {
|
|
@@ -53,15 +55,33 @@ class DomElement {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
setElAttrs(el, params) {
|
|
58
|
+
const testMode = this.testMode === true
|
|
56
59
|
const addAttributes = (val, key) => {
|
|
57
60
|
const addToDataset = (val, key) => { el.dataset[key] = val }
|
|
58
61
|
if (key === 'dataset') {
|
|
62
|
+
// Dataset values are inert text and are the framework's payload
|
|
63
|
+
// surface; they are applied as-is.
|
|
59
64
|
forEachObjIndexed(addToDataset, val)
|
|
60
|
-
|
|
61
|
-
el.setAttribute(key, val)
|
|
65
|
+
return
|
|
62
66
|
}
|
|
67
|
+
|
|
68
|
+
if (testMode !== true) {
|
|
69
|
+
const { allowed, value } = sanitizeAttribute(key, val, el.tagName)
|
|
70
|
+
if (allowed !== true) {
|
|
71
|
+
spyneWarn(`SPYNE WARNING: The attribute "${key}" was removed during sanitization.`, el)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
val = value
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
el.setAttribute(key, val)
|
|
63
78
|
}
|
|
64
79
|
this.getProp('attrs').forEach(addAttributes)
|
|
80
|
+
|
|
81
|
+
if (testMode !== true) {
|
|
82
|
+
applyIframeHardening(el)
|
|
83
|
+
}
|
|
84
|
+
|
|
65
85
|
return el
|
|
66
86
|
}
|
|
67
87
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { head, compose, reject, split, isEmpty, lte, defaultTo, prop } from 'ramda'
|
|
2
|
+
import { spyneWarn } from '../utils/spyne-warn.js'
|
|
2
3
|
import { SpyneAppProperties } from '../utils/spyne-app-properties.js'
|
|
3
4
|
|
|
4
5
|
function generateSpyneSelectorId(el) {
|
|
@@ -32,7 +33,7 @@ function testSelectors(cxt, sel, verboseBool) {
|
|
|
32
33
|
const elIsDomElement = compose(lte(0), defaultTo(-1), prop('nodeType'))
|
|
33
34
|
|
|
34
35
|
if (el !== null && elIsDomElement(el) === false) {
|
|
35
|
-
|
|
36
|
+
spyneWarn(`Spyne Warning: the el object is not a valid single element, ${el}`)
|
|
36
37
|
return
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -48,7 +49,7 @@ function testSelectors(cxt, sel, verboseBool) {
|
|
|
48
49
|
|
|
49
50
|
const isValidSelectorBool = isValidSelector(sel)
|
|
50
51
|
if (verboseBool === true && isDevMode() === true && isValidSelectorBool === false) {
|
|
51
|
-
|
|
52
|
+
spyneWarn(`Spyne Warning: the selector, ${sel} does not exist in this el, ${cxt}`)
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
}
|
|
@@ -127,8 +128,6 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
127
128
|
*/
|
|
128
129
|
selector.forEach = (fn) => Array.from(getNodeListArray(cxt, sel)).map(fn)
|
|
129
130
|
|
|
130
|
-
selector.getNodeListArray = () => getNodeListArray(cxt, sel)
|
|
131
|
-
|
|
132
131
|
/**
|
|
133
132
|
*
|
|
134
133
|
* Adds the class to the Element or to the NodeList.
|
|
@@ -142,7 +141,7 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
142
141
|
const arr = getNodeListArray(cxt, sel)
|
|
143
142
|
const addClass = item => item.classList.add(c)
|
|
144
143
|
arr.forEach(addClass)
|
|
145
|
-
return
|
|
144
|
+
return selector
|
|
146
145
|
}
|
|
147
146
|
|
|
148
147
|
/**
|
|
@@ -156,7 +155,7 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
156
155
|
item.classList.remove(c)
|
|
157
156
|
}
|
|
158
157
|
arr.forEach(removeClass)
|
|
159
|
-
return
|
|
158
|
+
return selector
|
|
160
159
|
}
|
|
161
160
|
|
|
162
161
|
/**
|
|
@@ -181,7 +180,7 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
181
180
|
classStrArr.forEach(adder)
|
|
182
181
|
}
|
|
183
182
|
arr.forEach(setTheClass)
|
|
184
|
-
return
|
|
183
|
+
return selector
|
|
185
184
|
}
|
|
186
185
|
|
|
187
186
|
selector.unmount = () => {
|
|
@@ -205,23 +204,25 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
205
204
|
bool ? item.classList.add(c) : item.classList.remove(c)
|
|
206
205
|
}
|
|
207
206
|
arr.forEach(toggleClass)
|
|
208
|
-
return
|
|
207
|
+
return selector
|
|
209
208
|
}
|
|
210
209
|
|
|
211
210
|
selector.toggle = (c, bool) => {
|
|
212
211
|
selector.toggleClass(c, bool)
|
|
213
|
-
return
|
|
212
|
+
return selector
|
|
214
213
|
}
|
|
215
214
|
|
|
216
215
|
/**
|
|
217
|
-
* Attaches html to the Selector's element
|
|
216
|
+
* Attaches html to the Selector's element (the first match when the
|
|
217
|
+
* selector matches multiple elements).
|
|
218
218
|
* @param htmlElement
|
|
219
219
|
*/
|
|
220
220
|
selector.appendChild = (htmlElement) => {
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
const el = selector.el
|
|
222
|
+
if (el !== null) {
|
|
223
|
+
el.appendChild(htmlElement)
|
|
223
224
|
} else {
|
|
224
|
-
|
|
225
|
+
spyneWarn(`Spyne Warning: The selector, ${sel} does not appear to be valid!`)
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
return selector.el
|
|
@@ -244,7 +245,7 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
244
245
|
arr.forEach(addClass)
|
|
245
246
|
}
|
|
246
247
|
requestAnimationFrame(delayAddClass)
|
|
247
|
-
|
|
248
|
+
return selector
|
|
248
249
|
}
|
|
249
250
|
|
|
250
251
|
/**
|
|
@@ -253,19 +254,6 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
253
254
|
* @param {String|HTMLElement} elSel The selector for the element.
|
|
254
255
|
* @desc Sets the class active HTMLElement from a NodeList.
|
|
255
256
|
*/
|
|
256
|
-
selector.setActiveItem2 = (c, elSel) => {
|
|
257
|
-
const arr = getNodeListArray(cxt, sel)
|
|
258
|
-
const currentEl = typeof (elSel) === 'string' ? getElOrList(cxt, elSel) : elSel
|
|
259
|
-
const toggleBool = item => item.isEqualNode(currentEl) ? item.classList.add(c) : item.classList.remove(c)
|
|
260
|
-
if (isNodeElement(currentEl) === true) {
|
|
261
|
-
arr.forEach(toggleBool)
|
|
262
|
-
} else if (isDevMode() === true) {
|
|
263
|
-
// console.log("SEL IS ",elSel,c);
|
|
264
|
-
console.warn(`Spyne Warning: The selector, ${elSel}, does not appear to be a valid item in setActiveItem: ${c}`)
|
|
265
|
-
}
|
|
266
|
-
return this
|
|
267
|
-
}
|
|
268
|
-
|
|
269
257
|
selector.setActiveItem = (c, elSel) => {
|
|
270
258
|
const arr = getNodeListArray(cxt, sel)
|
|
271
259
|
const currentEl = typeof elSel === 'string'
|
|
@@ -280,17 +268,17 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
280
268
|
if (matchingEl) {
|
|
281
269
|
matchingEl.classList.add(c)
|
|
282
270
|
} else if (isDevMode() === true) {
|
|
283
|
-
|
|
271
|
+
spyneWarn(
|
|
284
272
|
`Spyne Warning: The selector, ${elSel}, is valid but does not match any item in setActiveItem: ${c}`
|
|
285
273
|
)
|
|
286
274
|
}
|
|
287
275
|
} else if (isDevMode() === true) {
|
|
288
|
-
|
|
276
|
+
spyneWarn(
|
|
289
277
|
`Spyne Warning: The selector, ${elSel}, does not appear to be a valid item in setActiveItem: ${c}`
|
|
290
278
|
)
|
|
291
279
|
}
|
|
292
280
|
|
|
293
|
-
return
|
|
281
|
+
return selector
|
|
294
282
|
}
|
|
295
283
|
|
|
296
284
|
/**
|
|
@@ -298,10 +286,24 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
298
286
|
* @function el
|
|
299
287
|
*
|
|
300
288
|
* @desc
|
|
301
|
-
* getter
|
|
289
|
+
* getter that always resolves to a single element: the matched element,
|
|
290
|
+
* or the first item when the selector matches multiple elements (a
|
|
291
|
+
* warning is logged in debug mode).
|
|
302
292
|
*
|
|
303
293
|
* @returns
|
|
304
|
-
*
|
|
294
|
+
* An HTMLElement, or null when the selector matches nothing
|
|
295
|
+
* (mirroring querySelector)
|
|
296
|
+
*/
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
*
|
|
300
|
+
* @function els
|
|
301
|
+
*
|
|
302
|
+
* @desc
|
|
303
|
+
* getter that always resolves to an Array of the matched elements.
|
|
304
|
+
*
|
|
305
|
+
* @returns
|
|
306
|
+
* An Array of HTMLElements; empty when the selector matches nothing
|
|
305
307
|
*/
|
|
306
308
|
|
|
307
309
|
/**
|
|
@@ -312,7 +314,7 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
312
314
|
* Determines the length of the NodeList
|
|
313
315
|
*
|
|
314
316
|
* @returns
|
|
315
|
-
* The
|
|
317
|
+
* The number of elements matched by the selector
|
|
316
318
|
*/
|
|
317
319
|
|
|
318
320
|
/**
|
|
@@ -326,24 +328,27 @@ function ViewStreamSelector(cxt, sel) {
|
|
|
326
328
|
* Boolean
|
|
327
329
|
*/
|
|
328
330
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
Object.defineProperty(selector, '
|
|
332
|
-
Object.defineProperty(selector, 'exists', { get: () => getNodeListArray(cxt, sel, false).length >= 1 })
|
|
333
|
-
Object.defineProperty(selector, 'exist', { get: () => getNodeListArray(cxt, sel, false).length >= 1 })
|
|
334
|
-
Object.defineProperty(selector, 'nodeList', { get: () => getNodeListArray(cxt, sel) })
|
|
335
|
-
Object.defineProperty(selector, 'arr', {
|
|
331
|
+
const getElsArray = (verboseBool = true) => Array.from(getNodeListArray(cxt, sel, verboseBool))
|
|
332
|
+
|
|
333
|
+
Object.defineProperty(selector, 'el', {
|
|
336
334
|
get: () => {
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
339
|
-
|
|
340
|
-
} else if (el.length === undefined) {
|
|
341
|
-
return [el]
|
|
342
|
-
} else {
|
|
343
|
-
return Array.from(el)
|
|
335
|
+
const list = getElsArray()
|
|
336
|
+
if (list.length > 1 && isDevMode() === true) {
|
|
337
|
+
spyneWarn(`Spyne Warning: el$("${sel !== undefined ? sel : cxt}").el matched ${list.length} elements; returning the first. Use .els for the full list.`)
|
|
344
338
|
}
|
|
339
|
+
return list.length >= 1 ? list[0] : null
|
|
345
340
|
}
|
|
346
341
|
})
|
|
342
|
+
Object.defineProperty(selector, 'els', { get: () => getElsArray() })
|
|
343
|
+
Object.defineProperty(selector, 'arr', { get: () => getElsArray() })
|
|
344
|
+
|
|
345
|
+
// Undocumented. Retains the pre-0.24 polymorphic el contract — a single
|
|
346
|
+
// element, a NodeList, or an empty NodeList depending on match count —
|
|
347
|
+
// to assist in debugging and migrating older SpyneJS apps.
|
|
348
|
+
Object.defineProperty(selector, 'elLegacy', { get: () => getElOrList(cxt, sel, true) })
|
|
349
|
+
Object.defineProperty(selector, 'len', { get: () => getNodeListArray(cxt, sel, false).length })
|
|
350
|
+
Object.defineProperty(selector, 'length', { get: () => getNodeListArray(cxt, sel, false).length })
|
|
351
|
+
Object.defineProperty(selector, 'exists', { get: () => getNodeListArray(cxt, sel, false).length >= 1 })
|
|
347
352
|
|
|
348
353
|
Object.defineProperty(selector, 'inline', { set: (val) => setInlineCss(val, cxt, sel), get: () => selector })
|
|
349
354
|
Object.defineProperty(selector, 'inlineCss', { set: (val) => setInlineCss(val, cxt, sel), get: () => selector })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//const { expect, assert } = require('chai')
|
|
2
2
|
//const { ChannelFetch } = require('../../spyne/channels/channel-fetch-class')
|
|
3
3
|
const { expect, assert } = require('chai')
|
|
4
|
-
import { ChannelFetch } from '../../spyne/spyne';
|
|
4
|
+
import { ChannelFetch, SpyneApp } from '../../spyne/spyne';
|
|
5
5
|
|
|
6
6
|
describe('should test ChannelFetch', () => {
|
|
7
7
|
const mapFn = (p) => {
|
|
@@ -95,4 +95,91 @@ describe('should test ChannelFetch', () => {
|
|
|
95
95
|
const mapTypeUndefined = ChannelFetch.validateMapMethod(props, 'MY_TEST_NAME_2', true)
|
|
96
96
|
// console.log('map methods is not of correct type added and is valid4 ',{mapTypeUndefined}) ; expect(mapTypeUndefined).to.be.false;
|
|
97
97
|
})
|
|
98
|
+
|
|
99
|
+
describe('conformed error action', () => {
|
|
100
|
+
const createPayloadContext = () => {
|
|
101
|
+
const context = {
|
|
102
|
+
props: { name: channelName },
|
|
103
|
+
sentActions: [],
|
|
104
|
+
createChannelPayloadItem(payload, action) {
|
|
105
|
+
this.sentActions.push({ payload, action })
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return context
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
it('should register the generic error action', () => {
|
|
112
|
+
const context = { props: {} }
|
|
113
|
+
const actionsArr = ChannelFetch.prototype.addRegisteredActions.call(context)
|
|
114
|
+
|
|
115
|
+
expect(actionsArr).to.include('CHANNEL_ERROR_EVENT')
|
|
116
|
+
expect(actionsArr).to.include('CHANNEL_RESPONSE_EVENT')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should register the instance-derived error action alongside the response action', () => {
|
|
120
|
+
const context = {
|
|
121
|
+
props: {
|
|
122
|
+
extendedActionsArr: [
|
|
123
|
+
`${channelName}_RESPONSE_EVENT`,
|
|
124
|
+
`${channelName}_ERROR_EVENT`,
|
|
125
|
+
[`${channelName}_REQUEST_EVENT`, 'onFetchUpdate']
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const actionsArr = ChannelFetch.prototype.addRegisteredActions.call(context)
|
|
130
|
+
|
|
131
|
+
expect(actionsArr).to.include(`${channelName}_ERROR_EVENT`)
|
|
132
|
+
expect(actionsArr).to.include(`${channelName}_RESPONSE_EVENT`)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should emit the response action for a successful fetch payload', () => {
|
|
136
|
+
const context = createPayloadContext()
|
|
137
|
+
const streamItem = { id: 1, title: 'foo' }
|
|
138
|
+
|
|
139
|
+
ChannelFetch.prototype.onFetchReturned.call(context, streamItem)
|
|
140
|
+
|
|
141
|
+
expect(context.sentActions[0].action).to.equal(`${channelName}_RESPONSE_EVENT`)
|
|
142
|
+
expect(context.sentActions[0].payload).to.deep.equal(streamItem)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should emit the error action for a conformed fetch error payload', () => {
|
|
146
|
+
const context = createPayloadContext()
|
|
147
|
+
const streamItem = {
|
|
148
|
+
isChannelFetchError: true,
|
|
149
|
+
isError: true,
|
|
150
|
+
error: true,
|
|
151
|
+
errorType: 'FETCH_HTTP_ERROR',
|
|
152
|
+
message: 'Fetch request failed with status 404 Not Found',
|
|
153
|
+
status: 404,
|
|
154
|
+
statusText: 'Not Found',
|
|
155
|
+
url: 'https://jsonplaceholder.typicode.com/posts/does-not-exist'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ChannelFetch.prototype.onFetchReturned.call(context, streamItem)
|
|
159
|
+
|
|
160
|
+
expect(context.sentActions[0].action).to.equal(`${channelName}_ERROR_EVENT`)
|
|
161
|
+
expect(context.sentActions[0].payload).to.deep.equal(streamItem)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should not route a payload to the error action without the discriminator flag', () => {
|
|
165
|
+
const context = createPayloadContext()
|
|
166
|
+
const streamItem = { isError: true, message: 'data that merely resembles an error' }
|
|
167
|
+
|
|
168
|
+
ChannelFetch.prototype.onFetchReturned.call(context, streamItem)
|
|
169
|
+
|
|
170
|
+
expect(context.sentActions[0].action).to.equal(`${channelName}_RESPONSE_EVENT`)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should add the derived error action to channelActions on a constructed instance', () => {
|
|
174
|
+
SpyneApp.init({})
|
|
175
|
+
|
|
176
|
+
const instanceChannelName = 'CHANNEL_FETCH_ERROR_ACTION_TEST'
|
|
177
|
+
const fetchChannel = new ChannelFetch(instanceChannelName, { url, pause: true })
|
|
178
|
+
|
|
179
|
+
expect(fetchChannel.channelActions).to.have.property(`${instanceChannelName}_ERROR_EVENT`)
|
|
180
|
+
expect(fetchChannel.channelActions).to.have.property(`${instanceChannelName}_RESPONSE_EVENT`)
|
|
181
|
+
expect(fetchChannel.channelActions).to.have.property(`${instanceChannelName}_REQUEST_EVENT`)
|
|
182
|
+
expect(fetchChannel.channelActions).to.have.property('CHANNEL_ERROR_EVENT')
|
|
183
|
+
})
|
|
184
|
+
})
|
|
98
185
|
})
|
package/src/tests/index.test.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// Silence SpyneJS developer-hint warnings across all test bundles.
|
|
2
|
+
// Expected-failure cases (invalid selectors, blocked URIs, policy removals)
|
|
3
|
+
// would otherwise flood the karma reporter. Security warnings still print.
|
|
4
|
+
window.SPYNE_SUPPRESS_WARNINGS = true
|
|
5
|
+
|
|
1
6
|
import { SpyneApp } from '../spyne/spyne'
|
|
2
7
|
|
|
3
8
|
// import {AppView} from "../app/app-view";
|
|
@@ -49,9 +49,11 @@ describe('ChannelFetchUtil Tests', () => {
|
|
|
49
49
|
|
|
50
50
|
describe('stringify body method', () => {
|
|
51
51
|
it('should convert body object to string', () => {
|
|
52
|
-
|
|
53
|
-
bodyStr = R.omit(['mapFn'],
|
|
52
|
+
const result = ChannelFetchUtil.stringifyBodyIfItExists(propsPost)
|
|
53
|
+
const bodyStr = R.omit(['mapFn', 'headers'], result)
|
|
54
|
+
|
|
54
55
|
expect(bodyStr).to.deep.equal(propsStringified)
|
|
56
|
+
expect(result.headers.get('Content-Type')).to.equal('application/json')
|
|
55
57
|
})
|
|
56
58
|
|
|
57
59
|
it('should not parse body when its a string', () => {
|
|
@@ -127,4 +129,44 @@ describe('ChannelFetchUtil Tests', () => {
|
|
|
127
129
|
expect(channelFetchUtil.mapFn).to.be.a('function')
|
|
128
130
|
})
|
|
129
131
|
})
|
|
132
|
+
|
|
133
|
+
describe('conformed fetch error payload', () => {
|
|
134
|
+
const metadata = {
|
|
135
|
+
channelName: 'CHANNEL_MY_FETCHED_DATA',
|
|
136
|
+
url,
|
|
137
|
+
responseType: 'json'
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
it('should conform an http error into a flat payload with the discriminator flag', () => {
|
|
141
|
+
const httpError = ChannelFetchUtil.createFetchError({
|
|
142
|
+
errorType: 'FETCH_HTTP_ERROR',
|
|
143
|
+
message: 'Fetch request failed with status 404 Not Found',
|
|
144
|
+
metadata: { ...metadata, status: 404, statusText: 'Not Found', ok: false },
|
|
145
|
+
rawBody: '{"error":"not found"}'
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const errorPayload = ChannelFetchUtil.createFetchErrorPayload(httpError, metadata)
|
|
149
|
+
|
|
150
|
+
expect(errorPayload.isChannelFetchError).to.be.true
|
|
151
|
+
expect(errorPayload.isError).to.be.true
|
|
152
|
+
expect(errorPayload.errorType).to.equal('FETCH_HTTP_ERROR')
|
|
153
|
+
expect(errorPayload.status).to.equal(404)
|
|
154
|
+
expect(errorPayload.statusText).to.equal('Not Found')
|
|
155
|
+
expect(errorPayload.url).to.equal(url)
|
|
156
|
+
expect(errorPayload.channelName).to.equal('CHANNEL_MY_FETCHED_DATA')
|
|
157
|
+
expect(errorPayload.rawBodyPreview).to.equal('{"error":"not found"}')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should conform a network error using fallback metadata and error type', () => {
|
|
161
|
+
const networkError = new TypeError('Failed to fetch')
|
|
162
|
+
|
|
163
|
+
const errorPayload = ChannelFetchUtil.createFetchErrorPayload(networkError, metadata)
|
|
164
|
+
|
|
165
|
+
expect(errorPayload.isChannelFetchError).to.be.true
|
|
166
|
+
expect(errorPayload.errorType).to.equal('FETCH_UNKNOWN_ERROR')
|
|
167
|
+
expect(errorPayload.message).to.equal('Failed to fetch')
|
|
168
|
+
expect(errorPayload.url).to.equal(url)
|
|
169
|
+
expect(errorPayload.status).to.be.undefined
|
|
170
|
+
})
|
|
171
|
+
})
|
|
130
172
|
})
|