@sitecore-feaas/clientside 0.1.27 → 0.1.29
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/build/cdn.js +115 -0
- package/build/cdn.js.map +1 -0
- package/build/components/FEAASBase.js +212 -79
- package/build/components/FEAASBase.js.map +1 -1
- package/build/components/FEAASComponent.js +51 -17
- package/build/components/FEAASComponent.js.map +1 -1
- package/build/components/FEAASLoader.js +6 -0
- package/build/components/FEAASLoader.js.map +1 -1
- package/build/components/FEAASStylesheet.js +18 -13
- package/build/components/FEAASStylesheet.js.map +1 -1
- package/build/dom/component.js +34 -0
- package/build/dom/component.js.map +1 -0
- package/build/dom/data.js +105 -0
- package/build/dom/data.js.map +1 -0
- package/build/{dom.js → dom/rendering.js} +3 -194
- package/build/dom/rendering.js.map +1 -0
- package/build/dom/scripts.js +48 -0
- package/build/dom/scripts.js.map +1 -0
- package/build/dom/stylesheet.js +39 -0
- package/build/dom/stylesheet.js.map +1 -0
- package/build/fetch.js +72 -22
- package/build/fetch.js.map +1 -1
- package/build/{basic.js → headless.js} +11 -3
- package/build/headless.js.map +1 -0
- package/build/ui/FEAASContext.js +10 -3
- package/build/ui/FEAASContext.js.map +1 -1
- package/build/ui/FEAASEditor.js +5 -0
- package/build/ui/FEAASEditor.js.map +1 -1
- package/build/ui/FEAASPicker.js +7 -1
- package/build/ui/FEAASPicker.js.map +1 -1
- package/build/ui/index.js +1 -1
- package/build/ui/index.js.map +1 -1
- package/build/ui/react.js +6 -0
- package/build/ui/react.js.map +1 -0
- package/build/utils/promise.js +26 -0
- package/build/utils/promise.js.map +1 -0
- package/dist/browser/headless.esm.js +69 -0
- package/dist/browser/headless.esm.js.map +1 -0
- package/dist/browser/headless.js +69 -0
- package/dist/browser/headless.js.map +1 -0
- package/dist/browser/index.esm.js +3 -5
- package/dist/browser/index.esm.js.map +1 -1
- package/dist/browser/index.js +5 -5
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/react.js +67 -0
- package/dist/browser/react.js.map +1 -0
- package/dist/node/headless.esm.js +67 -0
- package/dist/node/headless.esm.js.map +7 -0
- package/dist/node/headless.js +67 -0
- package/dist/node/headless.js.map +7 -0
- package/dist/node/index.esm.js +3 -3
- package/dist/node/index.esm.js.map +4 -4
- package/dist/node/index.js +3 -3
- package/dist/node/index.js.map +4 -4
- package/dist/node/react.js +67 -0
- package/dist/node/react.js.map +7 -0
- package/examples/index.html +0 -5
- package/package.json +23 -12
- package/scripts/build.js +17 -6
- package/scripts/test-setup.ts +1 -0
- package/src/cdn.ts +188 -0
- package/src/components/FEAASBase.tsx +234 -82
- package/src/components/FEAASComponent.tsx +59 -21
- package/src/components/FEAASLoader.tsx +4 -0
- package/src/components/FEAASStylesheet.tsx +14 -13
- package/src/dom/component.ts +35 -0
- package/src/dom/data.ts +124 -0
- package/src/{dom.ts → dom/rendering.ts} +4 -218
- package/src/dom/scripts.ts +47 -0
- package/src/dom/stylesheet.ts +48 -0
- package/src/fetch.ts +79 -25
- package/src/{basic.ts → headless.ts} +11 -2
- package/src/ui/FEAASContext.tsx +12 -4
- package/src/ui/FEAASEditor.tsx +5 -0
- package/src/ui/FEAASPicker.tsx +6 -2
- package/src/ui/index.ts +1 -1
- package/src/ui/react.tsx +8 -0
- package/src/utils/promise.ts +34 -0
- package/test/FEAASBase.test.tsx +195 -0
- package/test/FEAASComponent.test.tsx +284 -0
- package/test/FEAASContext.test.tsx +76 -0
- package/test/FEAASPicker.test.tsx +40 -0
- package/test/FEAASStylesheet.test.tsx +159 -0
- package/test/cdn.ts +39 -0
- package/test/{render.ts → dom.test.ts} +12 -6
- package/test/extras.ts +1 -1
- package/test/fetch.ts +254 -0
- package/test/tsconfig.json +4 -1
- package/types/cdn.d.ts +57 -0
- package/types/cdn.d.ts.map +1 -0
- package/types/components/FEAASBase.d.ts +220 -16
- package/types/components/FEAASBase.d.ts.map +1 -1
- package/types/components/FEAASComponent.d.ts +34 -7
- package/types/components/FEAASComponent.d.ts.map +1 -1
- package/types/components/FEAASLoader.d.ts +4 -0
- package/types/components/FEAASLoader.d.ts.map +1 -1
- package/types/components/FEAASStylesheet.d.ts +5 -13
- package/types/components/FEAASStylesheet.d.ts.map +1 -1
- package/types/dom/component.d.ts +6 -0
- package/types/dom/component.d.ts.map +1 -0
- package/types/dom/data.d.ts +15 -0
- package/types/dom/data.d.ts.map +1 -0
- package/types/dom/rendering.d.ts +20 -0
- package/types/dom/rendering.d.ts.map +1 -0
- package/types/dom/scripts.d.ts +4 -0
- package/types/dom/scripts.d.ts.map +1 -0
- package/types/dom/stylesheet.d.ts +8 -0
- package/types/dom/stylesheet.d.ts.map +1 -0
- package/types/fetch.d.ts +25 -11
- package/types/fetch.d.ts.map +1 -1
- package/types/headless.d.ts +24 -0
- package/types/headless.d.ts.map +1 -0
- package/types/tsbuildinfo +1 -1
- package/types/tsbuildinfo_ui +1 -1
- package/types/ui/FEAASContext.d.ts +15 -5
- package/types/ui/FEAASContext.d.ts.map +1 -1
- package/types/ui/FEAASEditor.d.ts +18 -0
- package/types/ui/FEAASEditor.d.ts.map +1 -1
- package/types/ui/FEAASPicker.d.ts +15 -3
- package/types/ui/FEAASPicker.d.ts.map +1 -1
- package/types/ui/index.d.ts +1 -1
- package/types/ui/index.d.ts.map +1 -1
- package/types/ui/react.d.ts +7 -0
- package/types/ui/react.d.ts.map +1 -0
- package/types/utils/promise.d.ts +8 -0
- package/types/utils/promise.d.ts.map +1 -0
- package/build/basic.js.map +0 -1
- package/build/cloud.js +0 -71
- package/build/cloud.js.map +0 -1
- package/build/dom.js.map +0 -1
- package/build/node.js +0 -10
- package/build/node.js.map +0 -1
- package/build/ui/node.js +0 -3
- package/build/ui/node.js.map +0 -1
- package/dist/browser/basic.esm.js +0 -69
- package/dist/browser/basic.esm.js.map +0 -1
- package/dist/browser/basic.js +0 -69
- package/dist/browser/basic.js.map +0 -1
- package/dist/node/basic.esm.js +0 -67
- package/dist/node/basic.esm.js.map +0 -7
- package/dist/node/basic.js +0 -67
- package/dist/node/basic.js.map +0 -7
- package/src/cloud.ts +0 -118
- package/src/node.ts +0 -10
- package/src/ui/node.ts +0 -2
- package/test/components.ts +0 -31
- package/types/basic.d.ts +0 -16
- package/types/basic.d.ts.map +0 -1
- package/types/cloud.d.ts +0 -28
- package/types/cloud.d.ts.map +0 -1
- package/types/dom.d.ts +0 -41
- package/types/dom.d.ts.map +0 -1
- package/types/node.d.ts +0 -4
- package/types/node.d.ts.map +0 -1
- package/types/ui/node.d.ts +0 -3
- package/types/ui/node.d.ts.map +0 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { beforeEach, afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { FEAASComponent } from '../src/components/FEAASComponent.js'
|
|
3
|
+
import { renderDOMContent } from '../src/dom/rendering.js'
|
|
4
|
+
import { clearFetchCache } from '../src/fetch.js'
|
|
5
|
+
|
|
6
|
+
const earliest = new Date(2000, 1, 1, 0, 0, 0)
|
|
7
|
+
const earlier = new Date(2000, 1, 2, 0, 0, 0)
|
|
8
|
+
const now = new Date(2000, 1, 2, 12, 0, 0)
|
|
9
|
+
describe('Components', () => {
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
// vi.useFakeTimers()
|
|
12
|
+
vi.setSystemTime(now)
|
|
13
|
+
})
|
|
14
|
+
afterAll(() => {
|
|
15
|
+
// vi.useRealTimers()
|
|
16
|
+
})
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
document.write()
|
|
19
|
+
clearFetchCache()
|
|
20
|
+
})
|
|
21
|
+
it('should load component using', async () => {
|
|
22
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
23
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
24
|
+
if (url.match('CDN/components/L/C/V/R')) {
|
|
25
|
+
return new Response('TODAY', {
|
|
26
|
+
headers: {
|
|
27
|
+
date: now.toUTCString(),
|
|
28
|
+
'last-modified': earlier.toUTCString()
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Fixme change to production component
|
|
35
|
+
document.body.innerHTML =
|
|
36
|
+
'<feaas-component with-stylesheet="false" library="L" cdn="CDN" component="C" version="V" revision="R"></feaas-component>'
|
|
37
|
+
|
|
38
|
+
const component = document.body.firstElementChild as FEAASComponent
|
|
39
|
+
|
|
40
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
41
|
+
expect(spy).toHaveBeenCalledTimes(1)
|
|
42
|
+
|
|
43
|
+
expect(component.innerHTML).toContain('TODAY')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should load component using src attribute shorthand', async () => {
|
|
47
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
48
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
49
|
+
if (url.match('http://a/components/b/c/d/e.html')) {
|
|
50
|
+
return new Response('TODAY', {
|
|
51
|
+
headers: {
|
|
52
|
+
date: now.toUTCString(),
|
|
53
|
+
'last-modified': earlier.toUTCString()
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Fixme change to production component
|
|
60
|
+
document.body.innerHTML =
|
|
61
|
+
'<feaas-component with-stylesheet="false" src="http://a/components/b/c/d/e.html"></feaas-component>'
|
|
62
|
+
|
|
63
|
+
const component = document.body.firstElementChild as FEAASComponent
|
|
64
|
+
|
|
65
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
66
|
+
expect(spy).toHaveBeenCalledTimes(1)
|
|
67
|
+
|
|
68
|
+
expect(component.outerHTML).toBe(
|
|
69
|
+
`<feaas-component with-stylesheet="false" src="http://a/components/b/c/d/e.html" cdn="http://a" library="b" component="c" version="d" revision="e" class="-feaas">TODAY</feaas-component>`
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should revalidate cached component response', async () => {
|
|
74
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
75
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
76
|
+
if (url.match('CDN/components/L/C/V/R') && options.method != 'HEAD') {
|
|
77
|
+
// 1. Here's cached component
|
|
78
|
+
return new Response('TODAY', {
|
|
79
|
+
headers: {
|
|
80
|
+
date: earlier.toUTCString(),
|
|
81
|
+
'last-modified': earlier.toUTCString()
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
} else {
|
|
85
|
+
// 2. Is my cached response up to date? Yes.
|
|
86
|
+
return new Response(null, {
|
|
87
|
+
headers: {
|
|
88
|
+
date: now.toUTCString(),
|
|
89
|
+
'last-modified': earlier.toUTCString()
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// Fixme change to production component
|
|
96
|
+
document.body.innerHTML =
|
|
97
|
+
'<feaas-component with-stylesheet="false" library="L" cdn="CDN" component="C" version="V" revision="R"></feaas-component>'
|
|
98
|
+
|
|
99
|
+
const component = document.body.firstElementChild as FEAASComponent
|
|
100
|
+
|
|
101
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
102
|
+
expect(spy).toHaveBeenCalledTimes(2)
|
|
103
|
+
|
|
104
|
+
expect(component.innerHTML).toContain('TODAY')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('should reload component while using stale cache', async () => {
|
|
108
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
109
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
110
|
+
if (options.method != 'HEAD' && options['cache'] != 'reload') {
|
|
111
|
+
// Here's cached component
|
|
112
|
+
return new Response('YESTERDAY', {
|
|
113
|
+
headers: {
|
|
114
|
+
date: earlier.toUTCString(),
|
|
115
|
+
'last-modified': earliest.toUTCString()
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
} else if (options.method == 'HEAD') {
|
|
119
|
+
// Is my cached response up to date?
|
|
120
|
+
return new Response(null, {
|
|
121
|
+
headers: {
|
|
122
|
+
date: now.toUTCString(),
|
|
123
|
+
'last-modified': earlier.toUTCString()
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
} else {
|
|
127
|
+
// Up to date version
|
|
128
|
+
return new Response('TODAY', {
|
|
129
|
+
headers: {
|
|
130
|
+
date: now.toUTCString(),
|
|
131
|
+
'last-modified': earlier.toUTCString()
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// Fixme change to production component
|
|
138
|
+
document.body.innerHTML =
|
|
139
|
+
'<feaas-component with-stylesheet="false" library="L" cdn="CDN" component="C" version="V" revision="R"></feaas-component>'
|
|
140
|
+
|
|
141
|
+
const component = document.body.firstElementChild as FEAASComponent
|
|
142
|
+
// Renders cached version initially
|
|
143
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
144
|
+
expect(component.innerHTML).toContain('YESTERDAY')
|
|
145
|
+
expect(spy).toHaveBeenCalledTimes(2)
|
|
146
|
+
|
|
147
|
+
// whenLoaded only fires when ensures that up-to-date version is loaded
|
|
148
|
+
await expect(component.whenLoaded).resolves.toBeTruthy()
|
|
149
|
+
expect(spy).toHaveBeenCalledTimes(3)
|
|
150
|
+
expect(component.innerHTML).toContain('TODAY')
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should re-fetch component when attributes change', async () => {
|
|
154
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
155
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
156
|
+
if (url.match('CDN/components/L/C2/V/R')) {
|
|
157
|
+
return new Response('C2', {
|
|
158
|
+
headers: {
|
|
159
|
+
date: now.toUTCString(),
|
|
160
|
+
'last-modified': earlier.toUTCString()
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
} else if (url.match('CDN/components/L/C1/V/R')) {
|
|
164
|
+
return new Response('C1', {
|
|
165
|
+
headers: {
|
|
166
|
+
date: now.toUTCString(),
|
|
167
|
+
'last-modified': earlier.toUTCString()
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// Fixme change to production component
|
|
174
|
+
document.body.innerHTML =
|
|
175
|
+
'<feaas-component with-stylesheet="false" library="L" cdn="CDN" component="C1" version="V" revision="R"></feaas-component>'
|
|
176
|
+
|
|
177
|
+
const component = document.body.firstElementChild as FEAASComponent
|
|
178
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
179
|
+
expect(spy).toHaveBeenNthCalledWith(1, 'CDN/components/L/C1/V/R.html', undefined)
|
|
180
|
+
expect(spy).toHaveBeenCalledTimes(1)
|
|
181
|
+
expect(component.innerHTML).toContain('C1')
|
|
182
|
+
|
|
183
|
+
component.setAttribute('component', 'C2')
|
|
184
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
185
|
+
expect(spy).toHaveBeenNthCalledWith(2, 'CDN/components/L/C2/V/R.html', undefined)
|
|
186
|
+
expect(spy).toHaveBeenCalledTimes(2)
|
|
187
|
+
expect(component.innerHTML).toContain('C2')
|
|
188
|
+
})
|
|
189
|
+
it('should wait for stylesheet to load', async () => {
|
|
190
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
191
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
192
|
+
if (url.includes('.css')) {
|
|
193
|
+
if (url.match('/styles/L/published.css') && options?.method != 'HEAD' && options?.['cache'] != 'reload') {
|
|
194
|
+
// Serve from cache
|
|
195
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
196
|
+
return new Response('YESTERDAY {}', {
|
|
197
|
+
headers: {
|
|
198
|
+
date: earlier.toUTCString(),
|
|
199
|
+
'last-modified': earliest.toUTCString()
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
} else if (url.match('/styles/L/published.css') && options?.method == 'HEAD') {
|
|
203
|
+
// Validate
|
|
204
|
+
return new Response(null, {
|
|
205
|
+
headers: {
|
|
206
|
+
date: now.toUTCString(),
|
|
207
|
+
'last-modified': earlier.toUTCString()
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
} else {
|
|
211
|
+
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
212
|
+
return new Response('TODAY {}', {
|
|
213
|
+
headers: {
|
|
214
|
+
date: now.toUTCString(),
|
|
215
|
+
'last-modified': earlier.toUTCString()
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
if (options.method != 'HEAD' && options['cache'] != 'reload') {
|
|
221
|
+
// Here's cached component
|
|
222
|
+
return new Response('YESTERDAY', {
|
|
223
|
+
headers: {
|
|
224
|
+
date: earlier.toUTCString(),
|
|
225
|
+
'last-modified': earliest.toUTCString()
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
} else if (options.method == 'GET' && options['cache'] == 'reload') {
|
|
229
|
+
// Is my cached response up to date?
|
|
230
|
+
return new Response(null, {
|
|
231
|
+
headers: {
|
|
232
|
+
date: now.toUTCString(),
|
|
233
|
+
'last-modified': earlier.toUTCString()
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
} else {
|
|
237
|
+
// Latest
|
|
238
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
239
|
+
return new Response('TODAY', {
|
|
240
|
+
headers: {
|
|
241
|
+
date: now.toUTCString(),
|
|
242
|
+
'last-modified': earlier.toUTCString()
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
})
|
|
248
|
+
// Fixme change to production component
|
|
249
|
+
document.body.innerHTML = '<feaas-component src="L/C"></feaas-component>'
|
|
250
|
+
const component = document.body.firstElementChild as FEAASComponent
|
|
251
|
+
expect(document.head.querySelector('style')).toBeNull()
|
|
252
|
+
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
253
|
+
const stylesheet = document.head.querySelector('style') as HTMLStyleElement
|
|
254
|
+
expect(stylesheet.innerHTML).toEqual('')
|
|
255
|
+
expect(component.innerHTML).toEqual('')
|
|
256
|
+
// Component will rendered with stale cached stylesheet
|
|
257
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
258
|
+
expect(stylesheet.innerHTML).toEqual('YESTERDAY {}')
|
|
259
|
+
expect(component.innerHTML).toEqual('YESTERDAY')
|
|
260
|
+
// Component will update itself with stale cached stylesheet
|
|
261
|
+
await expect(component.whenLoaded).resolves.toEqual('TODAY')
|
|
262
|
+
expect(stylesheet.innerHTML).toEqual('YESTERDAY {}')
|
|
263
|
+
expect(component.innerHTML).toEqual('TODAY')
|
|
264
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
265
|
+
// Stylesheet will update later
|
|
266
|
+
expect(stylesheet.innerHTML).toEqual('TODAY {}')
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('render without data', () => {
|
|
270
|
+
const component = new FEAASComponent()
|
|
271
|
+
const div = document.createElement('div')
|
|
272
|
+
div.appendChild(component)
|
|
273
|
+
document.body.appendChild(div)
|
|
274
|
+
renderDOMContent(component, `<section class="-theme--abc test"></section><section class="-theme--cde"></section>`)
|
|
275
|
+
expect(div.innerHTML).toEqual(
|
|
276
|
+
`<feaas-component class="-feaas"><section class="-theme--abc test"></section><section class="-theme--cde"></section></feaas-component>`
|
|
277
|
+
)
|
|
278
|
+
component.className = '-theme--override test2'
|
|
279
|
+
renderDOMContent(component, `<section class="-theme--abc test"></section><section class="-theme--cde"></section>`)
|
|
280
|
+
expect(div.innerHTML).toEqual(
|
|
281
|
+
`<feaas-component class="-theme--override test2 -feaas"><section class="test"></section><section class=""></section></feaas-component>`
|
|
282
|
+
)
|
|
283
|
+
})
|
|
284
|
+
})
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { beforeEach, describe, it, assert, beforeAll, expect } from 'vitest'
|
|
2
|
+
import { FEAASBase, ReactPayload } from '../src/headless.js'
|
|
3
|
+
import { FEAASContextProps, FEAASContext } from '../src/ui/FEAASContext.js'
|
|
4
|
+
import { JSDOM } from 'jsdom'
|
|
5
|
+
import { vi } from 'vitest'
|
|
6
|
+
|
|
7
|
+
describe('React', function () {
|
|
8
|
+
describe('FEAASContext', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
document.write()
|
|
11
|
+
})
|
|
12
|
+
it('should initiate loading with isolation', async () => {
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
const spyLoad = vi.spyOn(FEAASContext.prototype, 'import')
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
spyLoad.mockImplementation(async (url: string) => {
|
|
17
|
+
if (url.match('test.js')) {
|
|
18
|
+
return Promise.all([import('react'), import('react-dom'), import('react-dom/client')]).then(
|
|
19
|
+
([React, ReactDOM, ReactDOMClient]) => {
|
|
20
|
+
return {
|
|
21
|
+
React,
|
|
22
|
+
ReactDOM,
|
|
23
|
+
ReactDOMClient,
|
|
24
|
+
Chakra: { Tooltip: {} },
|
|
25
|
+
AppContext: ({ libraryId }: any) => <p>Context {libraryId}</p>
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
// Fixme change to production component
|
|
32
|
+
document.body.innerHTML = '<feaas-context src="test.js" library="abc" isolate="false"></feaas-context>'
|
|
33
|
+
|
|
34
|
+
const component = document.body.firstElementChild as FEAASContext
|
|
35
|
+
|
|
36
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
37
|
+
expect(spyLoad).toHaveBeenCalledTimes(1)
|
|
38
|
+
|
|
39
|
+
expect(component.outerHTML).toBe(
|
|
40
|
+
'<feaas-context src="test.js" library="abc" isolate="false" frontend="https://components.sitecoreclud.io" backend="https://components-api.sitecoreclud.io" cdn="https://feaascompontsapiprod.blob.core.windows.net"><div><p>Context abc</p></div></feaas-context>'
|
|
41
|
+
)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
it('should initiate loading without isolation', async () => {
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
const spyLoad = vi.spyOn(FEAASContext.prototype, 'import')
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
spyLoad.mockImplementation(async (url: string) => {
|
|
49
|
+
if (url.match('test.js')) {
|
|
50
|
+
return Promise.all([import('react'), import('react-dom'), import('react-dom/client')]).then(
|
|
51
|
+
([React, ReactDOM, ReactDOMClient]) => {
|
|
52
|
+
return {
|
|
53
|
+
React,
|
|
54
|
+
ReactDOM,
|
|
55
|
+
ReactDOMClient,
|
|
56
|
+
Chakra: { Tooltip: {} },
|
|
57
|
+
AppContext: ({ libraryId }: any) => <p>Context {libraryId}</p>
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
// Fixme change to production component
|
|
64
|
+
document.body.innerHTML = '<feaas-context src="test.js" library="abc"></feaas-context>'
|
|
65
|
+
|
|
66
|
+
const component = document.body.firstElementChild as FEAASContext
|
|
67
|
+
|
|
68
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
69
|
+
expect(spyLoad).toHaveBeenCalledTimes(1)
|
|
70
|
+
|
|
71
|
+
expect(component.outerHTML).toBe(
|
|
72
|
+
'<feaas-context src="test.js" library="abc" frontend="https://components.sitecoreclud.io" backend="https://components-api.sitecoreclud.io" cdn="https://feaascompontsapiprod.blob.core.windows.net"></feaas-context>'
|
|
73
|
+
)
|
|
74
|
+
expect(component.shadowRoot.innerHTML).toBe('<div><p>Context abc</p></div>')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { beforeEach, describe, it, assert, beforeAll, expect } from 'vitest'
|
|
2
|
+
import { FEAASBase, ReactPayload } from '../src/headless.js'
|
|
3
|
+
import { FEAASPickerProps, FEAASPicker } from '../src/ui/FEAASPicker.js'
|
|
4
|
+
import { JSDOM } from 'jsdom'
|
|
5
|
+
import { vi } from 'vitest'
|
|
6
|
+
|
|
7
|
+
describe('React', function () {
|
|
8
|
+
describe('FEAASPicker', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
document.write()
|
|
11
|
+
})
|
|
12
|
+
it('should initiate loading', async () => {
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
const spyLoad = vi.spyOn(FEAASPicker.prototype, 'import')
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
spyLoad.mockImplementation(async (url: string) => {
|
|
17
|
+
if (url.match('test.js')) {
|
|
18
|
+
return Promise.all([import('react'), import('react-dom'), import('react-dom/client')]).then(
|
|
19
|
+
([React, ReactDOM, ReactDOMClient]) => {
|
|
20
|
+
return { React, ReactDOM, ReactDOMClient, Picker: () => <p>Picker</p> }
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
const spyRender = vi.spyOn(FEAASPicker.prototype, 'render')
|
|
26
|
+
spyRender.mockImplementation((props: React.PropsWithChildren<FEAASPickerProps>, { React, Picker }) => {
|
|
27
|
+
return <Picker>123</Picker>
|
|
28
|
+
})
|
|
29
|
+
// Fixme change to production component
|
|
30
|
+
document.body.innerHTML = '<feaas-picker src="test.js"></feaas-picker>'
|
|
31
|
+
|
|
32
|
+
const component = document.body.firstElementChild as FEAASPicker
|
|
33
|
+
|
|
34
|
+
await expect(component.whenRendered).resolves.toEqual(component)
|
|
35
|
+
expect(spyLoad).toHaveBeenCalledTimes(1)
|
|
36
|
+
|
|
37
|
+
expect(component.innerHTML).toContain('Picker')
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
})
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { afterAll, beforeEach, beforeAll, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { FEAASStylesheet } from '../src/components/FEAASStylesheet.js'
|
|
3
|
+
import { renderDOMContent } from '../src/dom/rendering.js'
|
|
4
|
+
import { clearFetchCache, loadStylesheet } from '../src/headless.js'
|
|
5
|
+
|
|
6
|
+
const earliest = new Date(2000, 1, 1, 0, 0, 0)
|
|
7
|
+
const earlier = new Date(2000, 1, 2, 0, 0, 0)
|
|
8
|
+
const now = new Date(2000, 1, 2, 12, 0, 0)
|
|
9
|
+
|
|
10
|
+
describe('Stylesheet', () => {
|
|
11
|
+
beforeAll(() => {
|
|
12
|
+
// vi.useFakeTimers()
|
|
13
|
+
vi.setSystemTime(now)
|
|
14
|
+
})
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
document.open()
|
|
17
|
+
document.write('<html><head></head><body></body></html')
|
|
18
|
+
document.close()
|
|
19
|
+
clearFetchCache()
|
|
20
|
+
})
|
|
21
|
+
it('should instantiate', () => {
|
|
22
|
+
expect(new FEAASStylesheet()).toBeInstanceOf(FEAASStylesheet)
|
|
23
|
+
})
|
|
24
|
+
it('should not load unconfigured stylesheet', async () => {
|
|
25
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
26
|
+
document.body.innerHTML = '<feaas-stylesheet cdn="CDN" revision="R"></feaas-stylesheet>'
|
|
27
|
+
expect(spy).toHaveBeenCalledTimes(0)
|
|
28
|
+
})
|
|
29
|
+
it('should load stylesheet (cold cache)', async () => {
|
|
30
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
31
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
32
|
+
return new Response('TODAY {}', {
|
|
33
|
+
headers: {
|
|
34
|
+
date: now.toUTCString(),
|
|
35
|
+
'last-modified': earlier.toUTCString()
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
document.body.innerHTML = '<feaas-stylesheet library="L" cdn="CDN" revision="R"></feaas-stylesheet>'
|
|
41
|
+
|
|
42
|
+
const stylesheet = document.body.firstElementChild as FEAASStylesheet
|
|
43
|
+
|
|
44
|
+
await expect(stylesheet.whenRendered).resolves.toEqual(stylesheet)
|
|
45
|
+
expect(spy).toHaveBeenCalledTimes(1)
|
|
46
|
+
|
|
47
|
+
expect(document.head.querySelector('style').textContent).toContain('TODAY')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('should invalidate stylesheet cache', async () => {
|
|
51
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
52
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
53
|
+
if (url.match('CDN/styles/L/R.css') && options?.method != 'HEAD') {
|
|
54
|
+
return new Response('TODAY {}', {
|
|
55
|
+
headers: {
|
|
56
|
+
date: earlier.toUTCString(),
|
|
57
|
+
'last-modified': earlier.toUTCString()
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
} else if (url.match('CDN/styles/L/R.css') && options?.method == 'HEAD') {
|
|
61
|
+
return new Response(null, {
|
|
62
|
+
headers: {
|
|
63
|
+
date: earlier.toUTCString(),
|
|
64
|
+
'last-modified': earlier.toUTCString()
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
document.body.innerHTML = '<feaas-stylesheet library="L" cdn="CDN" revision="R"></feaas-stylesheet>'
|
|
71
|
+
|
|
72
|
+
const stylesheet = document.body.firstElementChild as FEAASStylesheet
|
|
73
|
+
|
|
74
|
+
await expect(stylesheet.whenRendered).resolves.toEqual(stylesheet)
|
|
75
|
+
expect(spy).toHaveBeenCalledTimes(2)
|
|
76
|
+
|
|
77
|
+
expect(document.head.querySelector('style').textContent).toContain('TODAY')
|
|
78
|
+
})
|
|
79
|
+
it('should update stylesheet while using stale cache', async () => {
|
|
80
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
81
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
82
|
+
if (url.match('CDN/styles/L/R.css') && options?.method != 'HEAD' && options?.['cache'] != 'reload') {
|
|
83
|
+
// Serve from cache
|
|
84
|
+
return new Response('YESTERDAY {}', {
|
|
85
|
+
headers: {
|
|
86
|
+
date: earlier.toUTCString(),
|
|
87
|
+
'last-modified': earliest.toUTCString()
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
} else if (url.match('CDN/styles/L/R.css') && options?.method == 'HEAD') {
|
|
91
|
+
// Validate
|
|
92
|
+
return new Response(null, {
|
|
93
|
+
headers: {
|
|
94
|
+
date: now.toUTCString(),
|
|
95
|
+
'last-modified': earlier.toUTCString()
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
} else {
|
|
99
|
+
return new Response('TODAY {}', {
|
|
100
|
+
headers: {
|
|
101
|
+
date: now.toUTCString(),
|
|
102
|
+
'last-modified': earlier.toUTCString()
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
document.body.innerHTML = '<feaas-stylesheet library="L" cdn="CDN" revision="R"></feaas-stylesheet>'
|
|
109
|
+
|
|
110
|
+
const stylesheet = document.body.firstElementChild as FEAASStylesheet
|
|
111
|
+
|
|
112
|
+
await expect(stylesheet.whenRendered).resolves.toEqual(stylesheet)
|
|
113
|
+
expect(spy).toHaveBeenCalledTimes(2)
|
|
114
|
+
expect(document.head.querySelector('style').textContent).toContain('YESTERDAY {}')
|
|
115
|
+
|
|
116
|
+
// whenLoaded only fires when ensures that up-to-date version is loaded
|
|
117
|
+
await expect(stylesheet.whenLoaded).resolves.toBeTruthy()
|
|
118
|
+
expect(spy).toHaveBeenCalledTimes(3)
|
|
119
|
+
expect(document.head.querySelector('style').textContent).toContain('TODAY {}')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('should load stylesheet statically', async () => {
|
|
123
|
+
const spy = vi.spyOn(global, 'fetch')
|
|
124
|
+
spy.mockImplementation(async (url: string, options: Parameters<typeof fetch>[1] = {}) => {
|
|
125
|
+
if (url.match('CDN/styles/L/published.css') && options?.method != 'HEAD' && options?.['cache'] != 'reload') {
|
|
126
|
+
// Serve from cache
|
|
127
|
+
return new Response('YESTERDAY {}', {
|
|
128
|
+
headers: {
|
|
129
|
+
date: earlier.toUTCString(),
|
|
130
|
+
'last-modified': earliest.toUTCString()
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
} else if (url.match('CDN/styles/L/published.css') && options?.method == 'HEAD') {
|
|
134
|
+
// Validate
|
|
135
|
+
return new Response(null, {
|
|
136
|
+
headers: {
|
|
137
|
+
date: now.toUTCString(),
|
|
138
|
+
'last-modified': earlier.toUTCString()
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
} else {
|
|
142
|
+
return new Response('TODAY {}', {
|
|
143
|
+
headers: {
|
|
144
|
+
date: now.toUTCString(),
|
|
145
|
+
'last-modified': earlier.toUTCString()
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
await expect(
|
|
152
|
+
loadStylesheet({
|
|
153
|
+
cdn: 'CDN',
|
|
154
|
+
library: 'L',
|
|
155
|
+
revision: 'published'
|
|
156
|
+
}).then((r) => r.textContent)
|
|
157
|
+
).resolves.toEqual('TODAY {}')
|
|
158
|
+
})
|
|
159
|
+
})
|
package/test/cdn.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it, assert, beforeAll, expect } from 'vitest'
|
|
2
|
+
import { parseComponentSource, getComponentSource, DEFAULT_CDN_URL } from '../src/cdn.js'
|
|
3
|
+
import { CH1Node, serializeCH1RichText } from '../src/dom/data.js'
|
|
4
|
+
describe('CDN', function () {
|
|
5
|
+
it('should parse and serialize component src', function () {
|
|
6
|
+
;[
|
|
7
|
+
'library/component',
|
|
8
|
+
'cdn/components/library/component',
|
|
9
|
+
'https://cdn/components/library/component',
|
|
10
|
+
'//cdn/components/library/component',
|
|
11
|
+
'cdn/components/library/component/version',
|
|
12
|
+
'cdn/components/library/component/version/1',
|
|
13
|
+
'library/component/version/1',
|
|
14
|
+
'https://feaaslocal.blob.core.windows.net/components/library/component/version/1'
|
|
15
|
+
].forEach((expression) => {
|
|
16
|
+
expect(getComponentSource(parseComponentSource(expression))).toEqual(expression)
|
|
17
|
+
})
|
|
18
|
+
expect(getComponentSource(parseComponentSource('library/component/version/1.html'))).toEqual(
|
|
19
|
+
'library/component/version/1'
|
|
20
|
+
)
|
|
21
|
+
expect(
|
|
22
|
+
parseComponentSource('https://feaaslocal.blob.core.windows.net/components/library/component/version/1.html')
|
|
23
|
+
).toEqual({
|
|
24
|
+
cdn: 'https://feaaslocal.blob.core.windows.net',
|
|
25
|
+
component: 'component',
|
|
26
|
+
library: 'library',
|
|
27
|
+
revision: '1',
|
|
28
|
+
version: 'version'
|
|
29
|
+
})
|
|
30
|
+
expect(
|
|
31
|
+
getComponentSource(parseComponentSource(DEFAULT_CDN_URL + '/components/library/component/version/1.html'))
|
|
32
|
+
).toEqual('library/component/version/1')
|
|
33
|
+
expect(
|
|
34
|
+
getComponentSource(
|
|
35
|
+
parseComponentSource(DEFAULT_CDN_URL + '/components/library/component/responsive/published.html')
|
|
36
|
+
)
|
|
37
|
+
).toEqual('library/component')
|
|
38
|
+
})
|
|
39
|
+
})
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { JSDOM } from 'jsdom'
|
|
2
|
-
import * as FEAAS from '../src/
|
|
2
|
+
import * as FEAAS from '../src/headless.js'
|
|
3
3
|
import * as FEAASServerSide from '../src/node.js'
|
|
4
4
|
import { describe, it, assert, beforeAll } from 'vitest'
|
|
5
|
+
import { parseHTML } from 'linkedom'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
export function renderHTMLContentFallback(
|
|
8
|
+
template: string,
|
|
9
|
+
data?: FEAAS.Datascopes,
|
|
10
|
+
config?: FEAAS.FEAASCustomizations
|
|
11
|
+
) {
|
|
12
|
+
const newTemplate = config?.processTemplate?.(null, template, data) || template
|
|
13
|
+
const { document } = parseHTML(newTemplate)
|
|
14
|
+
return FEAAS.renderDOMElement(document, data, config).toString()
|
|
15
|
+
}
|
|
7
16
|
|
|
8
17
|
function assertScriptLoaded(element: HTMLElement, src: string) {
|
|
9
18
|
assert.equal(
|
|
@@ -51,10 +60,7 @@ function assertRenderedHTML(
|
|
|
51
60
|
if (!ignoreHTML) {
|
|
52
61
|
assert.equal(normalize(FEAAS.renderHTMLContent('<body>' + input + '</body>', data, customizations)), expectation)
|
|
53
62
|
|
|
54
|
-
assert.equal(
|
|
55
|
-
normalize(FEAASServerSide.renderHTMLContent('<body>' + input + '</body>', data, customizations)),
|
|
56
|
-
expectation
|
|
57
|
-
)
|
|
63
|
+
assert.equal(normalize(renderHTMLContentFallback('<body>' + input + '</body>', data, customizations)), expectation)
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
describe('DOM Clientside', function () {
|
package/test/extras.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, assert, beforeAll, expect } from 'vitest'
|
|
2
|
-
import { CH1Node, serializeCH1RichText } from '../src/dom.js'
|
|
2
|
+
import { CH1Node, serializeCH1RichText } from '../src/dom/data.js'
|
|
3
3
|
describe('Extras', function () {
|
|
4
4
|
it('should serialize content hub', function () {
|
|
5
5
|
const data: CH1Node = {
|