spyne 0.24.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.LICENSE.txt +2 -2
- 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/spyne-app.js +3 -2
- package/src/spyne/spyne.js +5 -2
- package/src/spyne/utils/channel-fetch-util.js +3 -2
- package/src/spyne/utils/sanitize-data.js +65 -6
- package/src/spyne/utils/sanitize-html.js +8 -2
- package/src/spyne/utils/spyne-warn.js +26 -0
- package/src/spyne/views/dom-element.js +2 -1
- package/src/spyne/views/view-stream-selector.js +52 -47
- package/src/tests/index.test.js +5 -0
- package/src/tests/utils/security.test.js +65 -1
- package/src/tests/views/dom-el-selectors.test.js +7 -7
- package/src/tests/views/view-stream-selector.test.js +178 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
const { expect } = require('chai')
|
|
2
|
+
import { ViewStreamSelector } from '../../spyne/views/view-stream-selector'
|
|
3
|
+
|
|
4
|
+
describe('ViewStreamSelector', () => {
|
|
5
|
+
let container
|
|
6
|
+
|
|
7
|
+
const el$ = (sel) => ViewStreamSelector(container, sel)
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
container = document.createElement('div')
|
|
11
|
+
container.innerHTML =
|
|
12
|
+
'<ul>' +
|
|
13
|
+
'<li class="item">A</li>' +
|
|
14
|
+
'<li class="item">A</li>' +
|
|
15
|
+
'<li class="item special">C</li>' +
|
|
16
|
+
'</ul>' +
|
|
17
|
+
'<p class="solo">only</p>'
|
|
18
|
+
document.body.appendChild(container)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
container.remove()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
describe('el (always a single element)', () => {
|
|
26
|
+
it('should return the element for a single match', () => {
|
|
27
|
+
const el = el$('.solo').el
|
|
28
|
+
expect(el.nodeType).to.equal(1)
|
|
29
|
+
expect(el.textContent).to.equal('only')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should return the first element for multiple matches', () => {
|
|
33
|
+
const el = el$('.item').el
|
|
34
|
+
expect(el.nodeType).to.equal(1)
|
|
35
|
+
expect(el.textContent).to.equal('A')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should return null for no matches, mirroring querySelector', () => {
|
|
39
|
+
expect(el$('.nope').el).to.equal(null)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should support direct element methods on multi-match selectors', () => {
|
|
43
|
+
el$('.item').el.setAttribute('data-first', 'true')
|
|
44
|
+
const items = container.querySelectorAll('.item')
|
|
45
|
+
expect(items[0].dataset.first).to.equal('true')
|
|
46
|
+
expect(items[1].dataset.first).to.be.undefined
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('els (always an Array)', () => {
|
|
51
|
+
it('should return an Array for multiple matches', () => {
|
|
52
|
+
const els = el$('.item').els
|
|
53
|
+
expect(Array.isArray(els)).to.be.true
|
|
54
|
+
expect(els).to.have.length(3)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should return a single-item Array for one match', () => {
|
|
58
|
+
const els = el$('.solo').els
|
|
59
|
+
expect(Array.isArray(els)).to.be.true
|
|
60
|
+
expect(els).to.have.length(1)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should return an empty Array for no matches', () => {
|
|
64
|
+
const els = el$('.nope').els
|
|
65
|
+
expect(Array.isArray(els)).to.be.true
|
|
66
|
+
expect(els).to.have.length(0)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('should support Array methods directly', () => {
|
|
70
|
+
const texts = el$('.item').els.map(el => el.textContent)
|
|
71
|
+
expect(texts).to.deep.equal(['A', 'A', 'C'])
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('should keep arr as an equivalent legacy alias', () => {
|
|
75
|
+
expect(el$('.item').arr).to.deep.equal(el$('.item').els)
|
|
76
|
+
expect(Array.isArray(el$('.item').arr)).to.be.true
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('length and len', () => {
|
|
81
|
+
it('should return the match count from length', () => {
|
|
82
|
+
expect(el$('.item').length).to.equal(3)
|
|
83
|
+
expect(el$('.solo').length).to.equal(1)
|
|
84
|
+
expect(el$('.nope').length).to.equal(0)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should keep len in agreement with length', () => {
|
|
88
|
+
expect(el$('.item').len).to.equal(el$('.item').length)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('chainability', () => {
|
|
93
|
+
it('should chain class mutators', () => {
|
|
94
|
+
el$('.item').addClass('x').toggleClass('y', true).removeClass('x')
|
|
95
|
+
|
|
96
|
+
const item = container.querySelector('.item')
|
|
97
|
+
expect(item.classList.contains('y')).to.be.true
|
|
98
|
+
expect(item.classList.contains('x')).to.be.false
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should return the selector from mutators', () => {
|
|
102
|
+
const s = el$('.item')
|
|
103
|
+
expect(s.addClass('z')).to.equal(s)
|
|
104
|
+
expect(s.toggleClass('z')).to.equal(s)
|
|
105
|
+
expect(s.setClass('q')).to.equal(s)
|
|
106
|
+
expect(s.setActiveItem('active', '.special')).to.equal(s)
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe('appendChild', () => {
|
|
111
|
+
it('should append to the first element on a multi-match selector', () => {
|
|
112
|
+
const span = document.createElement('span')
|
|
113
|
+
span.className = 'appended'
|
|
114
|
+
|
|
115
|
+
el$('.item').appendChild(span)
|
|
116
|
+
|
|
117
|
+
const items = container.querySelectorAll('.item')
|
|
118
|
+
expect(items[0].querySelector('.appended')).to.not.be.null
|
|
119
|
+
expect(items[1].querySelector('.appended')).to.be.null
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('should warn and not throw on a non-matching selector', () => {
|
|
123
|
+
expect(() => el$('.nope').appendChild(document.createElement('b'))).to.not.throw()
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
describe('setActiveItem', () => {
|
|
128
|
+
it('should activate only the exact matching node', () => {
|
|
129
|
+
el$('.item').setActiveItem('active', '.special')
|
|
130
|
+
|
|
131
|
+
const items = container.querySelectorAll('.item')
|
|
132
|
+
expect(items[0].classList.contains('active')).to.be.false
|
|
133
|
+
expect(items[1].classList.contains('active')).to.be.false
|
|
134
|
+
expect(items[2].classList.contains('active')).to.be.true
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should use identity, not deep equality, for duplicate siblings', () => {
|
|
138
|
+
const items = container.querySelectorAll('.item')
|
|
139
|
+
|
|
140
|
+
// items[0] and items[1] are identical markup; only the exact node activates
|
|
141
|
+
el$('.item').setActiveItem('active', items[1])
|
|
142
|
+
|
|
143
|
+
expect(items[0].classList.contains('active')).to.be.false
|
|
144
|
+
expect(items[1].classList.contains('active')).to.be.true
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe('elLegacy (undocumented pre-0.24 el contract)', () => {
|
|
149
|
+
it('should return a single element for a single match', () => {
|
|
150
|
+
expect(el$('.solo').elLegacy.nodeType).to.equal(1)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should return a NodeList for multiple matches', () => {
|
|
154
|
+
const el = el$('.item').elLegacy
|
|
155
|
+
expect(el.length).to.equal(3)
|
|
156
|
+
expect(el.nodeType).to.be.undefined
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should return an empty list for no matches', () => {
|
|
160
|
+
expect(el$('.nope').elLegacy.length).to.equal(0)
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('removed legacy members', () => {
|
|
165
|
+
it('should no longer define setActiveItem2, element, exist, nodeList, or getNodeListArray', () => {
|
|
166
|
+
const s = el$('.item')
|
|
167
|
+
expect(s.setActiveItem2).to.be.undefined
|
|
168
|
+
expect(s.element).to.be.undefined
|
|
169
|
+
expect(s.exist).to.be.undefined
|
|
170
|
+
expect(s.nodeList).to.be.undefined
|
|
171
|
+
expect(s.getNodeListArray).to.be.undefined
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should keep unmount as a callable no-op for legacy apps', () => {
|
|
175
|
+
expect(() => el$('.item').unmount()).to.not.throw()
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
})
|