@sitecore-feaas/clientside 0.1.28 → 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.
Files changed (156) hide show
  1. package/build/cdn.js +115 -0
  2. package/build/cdn.js.map +1 -0
  3. package/build/components/FEAASBase.js +212 -79
  4. package/build/components/FEAASBase.js.map +1 -1
  5. package/build/components/FEAASComponent.js +50 -17
  6. package/build/components/FEAASComponent.js.map +1 -1
  7. package/build/components/FEAASLoader.js +6 -0
  8. package/build/components/FEAASLoader.js.map +1 -1
  9. package/build/components/FEAASStylesheet.js +18 -13
  10. package/build/components/FEAASStylesheet.js.map +1 -1
  11. package/build/dom/component.js +34 -0
  12. package/build/dom/component.js.map +1 -0
  13. package/build/dom/data.js +105 -0
  14. package/build/dom/data.js.map +1 -0
  15. package/build/{dom.js → dom/rendering.js} +3 -194
  16. package/build/dom/rendering.js.map +1 -0
  17. package/build/dom/scripts.js +48 -0
  18. package/build/dom/scripts.js.map +1 -0
  19. package/build/dom/stylesheet.js +39 -0
  20. package/build/dom/stylesheet.js.map +1 -0
  21. package/build/fetch.js +72 -22
  22. package/build/fetch.js.map +1 -1
  23. package/build/{basic.js → headless.js} +11 -3
  24. package/build/headless.js.map +1 -0
  25. package/build/ui/FEAASContext.js +10 -3
  26. package/build/ui/FEAASContext.js.map +1 -1
  27. package/build/ui/FEAASEditor.js +5 -0
  28. package/build/ui/FEAASEditor.js.map +1 -1
  29. package/build/ui/FEAASPicker.js +7 -1
  30. package/build/ui/FEAASPicker.js.map +1 -1
  31. package/build/ui/index.js +1 -1
  32. package/build/ui/index.js.map +1 -1
  33. package/build/ui/react.js +6 -0
  34. package/build/ui/react.js.map +1 -0
  35. package/build/utils/promise.js +26 -0
  36. package/build/utils/promise.js.map +1 -0
  37. package/dist/browser/headless.esm.js +69 -0
  38. package/dist/browser/headless.esm.js.map +1 -0
  39. package/dist/browser/headless.js +69 -0
  40. package/dist/browser/headless.js.map +1 -0
  41. package/dist/browser/index.esm.js +3 -5
  42. package/dist/browser/index.esm.js.map +1 -1
  43. package/dist/browser/index.js +5 -5
  44. package/dist/browser/index.js.map +1 -1
  45. package/dist/browser/react.js +67 -0
  46. package/dist/browser/react.js.map +1 -0
  47. package/dist/node/headless.esm.js +67 -0
  48. package/dist/node/headless.esm.js.map +7 -0
  49. package/dist/node/headless.js +67 -0
  50. package/dist/node/headless.js.map +7 -0
  51. package/dist/node/index.esm.js +3 -3
  52. package/dist/node/index.esm.js.map +4 -4
  53. package/dist/node/index.js +3 -3
  54. package/dist/node/index.js.map +4 -4
  55. package/dist/node/react.js +67 -0
  56. package/dist/node/react.js.map +7 -0
  57. package/examples/index.html +0 -5
  58. package/package.json +23 -12
  59. package/scripts/build.js +17 -6
  60. package/scripts/test-setup.ts +1 -0
  61. package/src/cdn.ts +188 -0
  62. package/src/components/FEAASBase.tsx +234 -82
  63. package/src/components/FEAASComponent.tsx +56 -21
  64. package/src/components/FEAASLoader.tsx +4 -0
  65. package/src/components/FEAASStylesheet.tsx +14 -13
  66. package/src/dom/component.ts +35 -0
  67. package/src/dom/data.ts +124 -0
  68. package/src/{dom.ts → dom/rendering.ts} +4 -218
  69. package/src/dom/scripts.ts +47 -0
  70. package/src/dom/stylesheet.ts +48 -0
  71. package/src/fetch.ts +79 -25
  72. package/src/{basic.ts → headless.ts} +11 -2
  73. package/src/ui/FEAASContext.tsx +12 -4
  74. package/src/ui/FEAASEditor.tsx +5 -0
  75. package/src/ui/FEAASPicker.tsx +6 -2
  76. package/src/ui/index.ts +1 -1
  77. package/src/ui/react.tsx +8 -0
  78. package/src/utils/promise.ts +34 -0
  79. package/test/FEAASBase.test.tsx +195 -0
  80. package/test/FEAASComponent.test.tsx +284 -0
  81. package/test/FEAASContext.test.tsx +76 -0
  82. package/test/FEAASPicker.test.tsx +40 -0
  83. package/test/FEAASStylesheet.test.tsx +159 -0
  84. package/test/cdn.ts +39 -0
  85. package/test/{render.ts → dom.test.ts} +12 -6
  86. package/test/extras.ts +1 -1
  87. package/test/fetch.ts +254 -0
  88. package/test/tsconfig.json +4 -1
  89. package/types/cdn.d.ts +57 -0
  90. package/types/cdn.d.ts.map +1 -0
  91. package/types/components/FEAASBase.d.ts +220 -16
  92. package/types/components/FEAASBase.d.ts.map +1 -1
  93. package/types/components/FEAASComponent.d.ts +28 -7
  94. package/types/components/FEAASComponent.d.ts.map +1 -1
  95. package/types/components/FEAASLoader.d.ts +4 -0
  96. package/types/components/FEAASLoader.d.ts.map +1 -1
  97. package/types/components/FEAASStylesheet.d.ts +5 -13
  98. package/types/components/FEAASStylesheet.d.ts.map +1 -1
  99. package/types/dom/component.d.ts +6 -0
  100. package/types/dom/component.d.ts.map +1 -0
  101. package/types/dom/data.d.ts +15 -0
  102. package/types/dom/data.d.ts.map +1 -0
  103. package/types/dom/rendering.d.ts +20 -0
  104. package/types/dom/rendering.d.ts.map +1 -0
  105. package/types/dom/scripts.d.ts +4 -0
  106. package/types/dom/scripts.d.ts.map +1 -0
  107. package/types/dom/stylesheet.d.ts +8 -0
  108. package/types/dom/stylesheet.d.ts.map +1 -0
  109. package/types/fetch.d.ts +25 -11
  110. package/types/fetch.d.ts.map +1 -1
  111. package/types/headless.d.ts +24 -0
  112. package/types/headless.d.ts.map +1 -0
  113. package/types/tsbuildinfo +1 -1
  114. package/types/tsbuildinfo_ui +1 -1
  115. package/types/ui/FEAASContext.d.ts +15 -5
  116. package/types/ui/FEAASContext.d.ts.map +1 -1
  117. package/types/ui/FEAASEditor.d.ts +18 -0
  118. package/types/ui/FEAASEditor.d.ts.map +1 -1
  119. package/types/ui/FEAASPicker.d.ts +15 -3
  120. package/types/ui/FEAASPicker.d.ts.map +1 -1
  121. package/types/ui/index.d.ts +1 -1
  122. package/types/ui/index.d.ts.map +1 -1
  123. package/types/ui/react.d.ts +7 -0
  124. package/types/ui/react.d.ts.map +1 -0
  125. package/types/utils/promise.d.ts +8 -0
  126. package/types/utils/promise.d.ts.map +1 -0
  127. package/build/basic.js.map +0 -1
  128. package/build/cloud.js +0 -71
  129. package/build/cloud.js.map +0 -1
  130. package/build/dom.js.map +0 -1
  131. package/build/node.js +0 -10
  132. package/build/node.js.map +0 -1
  133. package/build/ui/node.js +0 -3
  134. package/build/ui/node.js.map +0 -1
  135. package/dist/browser/basic.esm.js +0 -69
  136. package/dist/browser/basic.esm.js.map +0 -1
  137. package/dist/browser/basic.js +0 -69
  138. package/dist/browser/basic.js.map +0 -1
  139. package/dist/node/basic.esm.js +0 -67
  140. package/dist/node/basic.esm.js.map +0 -7
  141. package/dist/node/basic.js +0 -67
  142. package/dist/node/basic.js.map +0 -7
  143. package/src/cloud.ts +0 -119
  144. package/src/node.ts +0 -10
  145. package/src/ui/node.ts +0 -2
  146. package/test/components.ts +0 -31
  147. package/types/basic.d.ts +0 -16
  148. package/types/basic.d.ts.map +0 -1
  149. package/types/cloud.d.ts +0 -39
  150. package/types/cloud.d.ts.map +0 -1
  151. package/types/dom.d.ts +0 -41
  152. package/types/dom.d.ts.map +0 -1
  153. package/types/node.d.ts +0 -4
  154. package/types/node.d.ts.map +0 -1
  155. package/types/ui/node.d.ts +0 -3
  156. 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/dom.js'
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
- const { renderDOMElement } = FEAAS
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 = {