kempo 1.0.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.
Files changed (194) hide show
  1. package/.gitattributes +2 -0
  2. package/LICENSE +21 -0
  3. package/README.md +2 -0
  4. package/dist/components/Card.js +1 -0
  5. package/dist/components/CodeEditor.js +1 -0
  6. package/dist/components/Component.js +1 -0
  7. package/dist/components/Dialog.js +1 -0
  8. package/dist/components/FocusCapture.js +1 -0
  9. package/dist/components/Icon.js +1 -0
  10. package/dist/components/Import.js +1 -0
  11. package/dist/components/LazyComponent.js +1 -0
  12. package/dist/components/ReactiveComponent.js +1 -0
  13. package/dist/components/ReactiveLazyComponent.js +1 -0
  14. package/dist/components/Resize.js +1 -0
  15. package/dist/components/Search.js +1 -0
  16. package/dist/components/ShowMore.js +1 -0
  17. package/dist/components/SideMenu.js +1 -0
  18. package/dist/components/Split.js +1 -0
  19. package/dist/components/Tabs.js +1 -0
  20. package/dist/components/ThemeSwitcher.js +1 -0
  21. package/dist/components/Timestamp.js +1 -0
  22. package/dist/components/WebSnippetEditor.js +1 -0
  23. package/dist/index.js +1 -0
  24. package/dist/kempo-hljs.css +1 -0
  25. package/dist/kempo-styles.css +1 -0
  26. package/dist/kempo-vars.css +1 -0
  27. package/dist/kempo.css +1 -0
  28. package/dist/kempo.js +1 -0
  29. package/dist/utils/cli.js +1 -0
  30. package/dist/utils/cookie.js +1 -0
  31. package/dist/utils/debounce.js +1 -0
  32. package/dist/utils/drag.js +1 -0
  33. package/dist/utils/element.js +1 -0
  34. package/dist/utils/formatTimestamp.js +1 -0
  35. package/dist/utils/fuzzyMatch.js +1 -0
  36. package/dist/utils/getPreferredTheme.js +1 -0
  37. package/dist/utils/loadCss.js +1 -0
  38. package/dist/utils/loadScript.js +1 -0
  39. package/dist/utils/object.js +1 -0
  40. package/dist/utils/string.js +1 -0
  41. package/dist/utils/toFunction.js +1 -0
  42. package/dist/utils/type.js +1 -0
  43. package/dist/utils/waitFrames.js +1 -0
  44. package/docs/components/_template.html +22 -0
  45. package/docs/components/card.html +35 -0
  46. package/docs/components/codeeditor.html +29 -0
  47. package/docs/components/component.html +88 -0
  48. package/docs/components/dialog.html +89 -0
  49. package/docs/components/icon.html +73 -0
  50. package/docs/components/import.html +34 -0
  51. package/docs/components/lazycomponent.html +50 -0
  52. package/docs/components/resize.html +107 -0
  53. package/docs/components/show-more.html +92 -0
  54. package/docs/components/sidemenu.html +97 -0
  55. package/docs/components/split.html +70 -0
  56. package/docs/components/tabs.html +33 -0
  57. package/docs/components/websnippeteditor.html +42 -0
  58. package/docs/index.html +17 -0
  59. package/docs/kempo/components/Card.js +1 -0
  60. package/docs/kempo/components/CodeEditor.js +1 -0
  61. package/docs/kempo/components/Collapse.js +1 -0
  62. package/docs/kempo/components/Component.js +1 -0
  63. package/docs/kempo/components/Dialog.js +1 -0
  64. package/docs/kempo/components/FocusCapture.js +1 -0
  65. package/docs/kempo/components/Icon.js +1 -0
  66. package/docs/kempo/components/Import.js +1 -0
  67. package/docs/kempo/components/LazyComponent.js +1 -0
  68. package/docs/kempo/components/ReactiveComponent.js +1 -0
  69. package/docs/kempo/components/ReactiveLazyComponent.js +1 -0
  70. package/docs/kempo/components/Resize.js +1 -0
  71. package/docs/kempo/components/Search.js +1 -0
  72. package/docs/kempo/components/ShowMore.js +1 -0
  73. package/docs/kempo/components/SideMenu.js +1 -0
  74. package/docs/kempo/components/Split.js +1 -0
  75. package/docs/kempo/components/Tabs.js +1 -0
  76. package/docs/kempo/components/ThemeSwitcher.js +1 -0
  77. package/docs/kempo/components/Timestamp.js +1 -0
  78. package/docs/kempo/components/WebSnippetEditor.js +1 -0
  79. package/docs/kempo/icons/arrow-down-double.svg +1 -0
  80. package/docs/kempo/icons/arrow-up-double.svg +1 -0
  81. package/docs/kempo/icons/close.svg +2 -0
  82. package/docs/kempo/icons/compare.svg +1 -0
  83. package/docs/kempo/icons/dark-mode.svg +1 -0
  84. package/docs/kempo/icons/delete.svg +1 -0
  85. package/docs/kempo/icons/filter-off.svg +1 -0
  86. package/docs/kempo/icons/filter.svg +1 -0
  87. package/docs/kempo/icons/light-mode.svg +1 -0
  88. package/docs/kempo/icons/menu.svg +1 -0
  89. package/docs/kempo/icons/pause.svg +1 -0
  90. package/docs/kempo/icons/play.svg +1 -0
  91. package/docs/kempo/icons/search.svg +1 -0
  92. package/docs/kempo/icons/settings.svg +1 -0
  93. package/docs/kempo/icons/storage.svg +1 -0
  94. package/docs/kempo/icons/trash-x.svg +1 -0
  95. package/docs/kempo/index.js +1 -0
  96. package/docs/kempo/kempo-hljs.css +1 -0
  97. package/docs/kempo/kempo-styles.css +1 -0
  98. package/docs/kempo/kempo-vars.css +1 -0
  99. package/docs/kempo/kempo.css +1 -0
  100. package/docs/kempo/kempo.js +1 -0
  101. package/docs/kempo/utils/cli.js +1 -0
  102. package/docs/kempo/utils/cookie.js +1 -0
  103. package/docs/kempo/utils/debounce.js +1 -0
  104. package/docs/kempo/utils/drag.js +1 -0
  105. package/docs/kempo/utils/element.js +1 -0
  106. package/docs/kempo/utils/formatTimestamp.js +1 -0
  107. package/docs/kempo/utils/fuzzyMatch.js +1 -0
  108. package/docs/kempo/utils/getPreferredTheme.js +1 -0
  109. package/docs/kempo/utils/loadCss.js +1 -0
  110. package/docs/kempo/utils/loadScript.js +1 -0
  111. package/docs/kempo/utils/object.js +1 -0
  112. package/docs/kempo/utils/string.js +1 -0
  113. package/docs/kempo/utils/toFunction.js +1 -0
  114. package/docs/kempo/utils/type.js +1 -0
  115. package/docs/kempo/utils/waitFrames.js +1 -0
  116. package/docs/nav-1.inc.html +66 -0
  117. package/docs/nav.inc.html +66 -0
  118. package/docs/search.json +94 -0
  119. package/docs/styles/_template.html +22 -0
  120. package/docs/styles/borders-spacing.html +193 -0
  121. package/docs/styles/buttons.html +113 -0
  122. package/docs/styles/colors.html +65 -0
  123. package/docs/styles/displays-layouts.html +156 -0
  124. package/docs/styles/embed-1.html +35 -0
  125. package/docs/styles/inputs.html +89 -0
  126. package/docs/styles/tables.html +143 -0
  127. package/docs/styles/typography.html +116 -0
  128. package/icons/arrow-down-double.svg +1 -0
  129. package/icons/arrow-up-double.svg +1 -0
  130. package/icons/close.svg +2 -0
  131. package/icons/compare.svg +1 -0
  132. package/icons/dark-mode.svg +1 -0
  133. package/icons/delete.svg +1 -0
  134. package/icons/filter-off.svg +1 -0
  135. package/icons/filter.svg +1 -0
  136. package/icons/light-mode.svg +1 -0
  137. package/icons/menu.svg +1 -0
  138. package/icons/pause.svg +1 -0
  139. package/icons/play.svg +1 -0
  140. package/icons/search.svg +1 -0
  141. package/icons/settings.svg +1 -0
  142. package/icons/storage.svg +1 -0
  143. package/icons/trash-x.svg +1 -0
  144. package/kempo/components/Card.js +65 -0
  145. package/kempo/components/CodeEditor.js +141 -0
  146. package/kempo/components/Component.js +109 -0
  147. package/kempo/components/Dialog.js +277 -0
  148. package/kempo/components/FocusCapture.js +30 -0
  149. package/kempo/components/Icon.js +60 -0
  150. package/kempo/components/Import.js +40 -0
  151. package/kempo/components/LazyComponent.js +40 -0
  152. package/kempo/components/ReactiveComponent.js +11 -0
  153. package/kempo/components/ReactiveLazyComponent.js +11 -0
  154. package/kempo/components/Resize.js +168 -0
  155. package/kempo/components/Search.js +136 -0
  156. package/kempo/components/ShowMore.js +82 -0
  157. package/kempo/components/SideMenu.js +156 -0
  158. package/kempo/components/Split.js +133 -0
  159. package/kempo/components/Tabs.js +254 -0
  160. package/kempo/components/ThemeSwitcher.js +77 -0
  161. package/kempo/components/Timestamp.js +25 -0
  162. package/kempo/components/WebSnippetEditor.js +159 -0
  163. package/kempo/kempo-hljs.css +145 -0
  164. package/kempo/kempo-styles.css +893 -0
  165. package/kempo/kempo-vars.css +154 -0
  166. package/kempo/kempo.js +48 -0
  167. package/kempo/utils/cli.js +54 -0
  168. package/kempo/utils/cookie.js +16 -0
  169. package/kempo/utils/debounce.js +9 -0
  170. package/kempo/utils/drag.js +74 -0
  171. package/kempo/utils/element.js +69 -0
  172. package/kempo/utils/formatTimestamp.js +11 -0
  173. package/kempo/utils/fuzzyMatch.js +55 -0
  174. package/kempo/utils/getPreferredTheme.js +1 -0
  175. package/kempo/utils/loadCss.js +10 -0
  176. package/kempo/utils/loadScript.js +10 -0
  177. package/kempo/utils/object.js +177 -0
  178. package/kempo/utils/string.js +54 -0
  179. package/kempo/utils/toFunction.js +24 -0
  180. package/kempo/utils/type.js +8 -0
  181. package/kempo/utils/waitFrames.js +17 -0
  182. package/package.json +33 -0
  183. package/scripts/build.js +131 -0
  184. package/scripts/docs.js +14 -0
  185. package/test/components/SuiteList.js +26 -0
  186. package/test/components/Test.js +357 -0
  187. package/test/index.html +49 -0
  188. package/test/index.js +25 -0
  189. package/test/suite.html +45 -0
  190. package/test/suites.inc.html +4 -0
  191. package/test/tests/components/Component.test.js +138 -0
  192. package/test/tests/utils/cli.test.js +113 -0
  193. package/test/tests/utils/cookie.test.js +87 -0
  194. package/test/tests/utils/debounce.test.js +71 -0
@@ -0,0 +1,357 @@
1
+ import Component from '../kempo/components/Component.js';
2
+
3
+ const html = str => new DOMParser().parseFromString(str, 'text/html').body.childNodes[0];
4
+ const runClickHandler = Symbol('runClickHandler'),
5
+ toggleLogsClickHandler = Symbol('toggleLogsClickHandler'),
6
+ testUpdatedHandler = Symbol('testUpdatedHandler');
7
+
8
+ export default class TestSuite extends Component {
9
+ constructor(){
10
+ super();
11
+
12
+ this[runClickHandler] = () => {
13
+ this.runAll();
14
+ }
15
+
16
+ this.registerAttribute('name', '');
17
+ }
18
+ async render(force){
19
+ if(await super.render(force)){
20
+ await this.loadTests();
21
+ this.shadowRoot.getElementById('runAll').addEventListener('click', this[runClickHandler]);
22
+ return true;
23
+ }
24
+ return false;
25
+ }
26
+ disconnectedCallback(){
27
+ if(this.rendered){
28
+ this.shadowRoot.getElementById('runAll').removeEventListener('click', this[runClickHandler]);
29
+ }
30
+ super.disconnectedCallback();
31
+ }
32
+ async loadTests(){
33
+ const {
34
+ name,
35
+ description,
36
+ tests
37
+ } = await import(`../tests/${this.name}.test.js`);
38
+ const testSuiteLink = `/suite.html?name=${this.name}`;
39
+ const currentPath = location.pathname + location.search;
40
+ if(testSuiteLink === currentPath){
41
+ this.shadowRoot.getElementById('name').innerHTML = name;
42
+ } else {
43
+ this.shadowRoot.getElementById('name').innerHTML = `<a href="${testSuiteLink}">${name}</a>`;
44
+ }
45
+ this.shadowRoot.getElementById('description').innerHTML = description;
46
+ tests.forEach( ({name, description, test}) => {
47
+ this.appendChild(new Test(name, description, test));
48
+ });
49
+ }
50
+ async runAll(){
51
+ const $tests = [...this.querySelectorAll('k-test')];
52
+ for(const $test of $tests){
53
+ await $test.run();
54
+ }
55
+ }
56
+ get shadowTemplate(){
57
+ return /*html*/`
58
+ <h3 id="name"></h3>
59
+ <p id="description"></p>
60
+ <button
61
+ id="runAll"
62
+ class="secondary mb"
63
+ >
64
+ Run All Tests in this Suite
65
+ </button>
66
+ <slot name="test"></slot>
67
+ ${super.shadowTemplate}
68
+ `;
69
+ }
70
+ get shadowStyles(){
71
+ return /*css*/`
72
+ ${super.shadowStyles}
73
+ :host {
74
+ display: block;
75
+ border: 1px solid var(--c_border);
76
+ border-radius: var(--radius);
77
+ padding: var(--spacer);
78
+ margin-bottom: var(--spacer);
79
+ }
80
+ `;
81
+ }
82
+ }
83
+ window.customElements.define('k-test-suite', TestSuite);
84
+
85
+
86
+ class Test extends Component {
87
+ constructor(name, description, test){
88
+ super();
89
+
90
+ this[runClickHandler] = () => this.run();
91
+ this[toggleLogsClickHandler] = () => {
92
+ this.showLogs = !this.showLogs;
93
+ }
94
+
95
+ this.registerAttribute('status', 'Not Ran');
96
+ this.registerAttribute('name', name);
97
+ this.registerAttribute('description', description);
98
+ this.registerAttribute('showLogs', false);
99
+ this.registerProp('test', test);
100
+ this.registerProp('logs', []);
101
+ }
102
+ async render(force){
103
+ if(super.render(force)){
104
+ this.shadowRoot.getElementById('name').innerHTML = this.name;
105
+ this.shadowRoot.getElementById('description').innerHTML = this.description;
106
+ this.shadowRoot.getElementById('run').addEventListener('click', this[runClickHandler]);
107
+ this.shadowRoot.getElementById('toggleLogs').addEventListener('click', this[toggleLogsClickHandler]);
108
+ return true;
109
+ }
110
+ return false;
111
+ }
112
+ connectedCallback(){
113
+ super.connectedCallback();
114
+ this.dispatchEvent(new CustomEvent('testadded', {
115
+ bubbles: true,
116
+ test: this
117
+ }));
118
+ }
119
+ disconnectedCallback(){
120
+ super.disconnectedCallback();
121
+ this.shadowRoot.getElementById('run').removeEventListener('click', this[runClickHandler]);
122
+ this.shadowRoot.getElementById('toggleLogs').removeEventListener('click', this[toggleLogsClickHandler]);
123
+ }
124
+ log(...args){
125
+ let type = 'log',
126
+ messages = [...args];
127
+ if(['log', 'error', 'warning', 'success'].includes(args[0])){
128
+ type = args[0];
129
+ messages = [...args].slice(1);
130
+ }
131
+ this.logs.push({ type, messages });
132
+ if(this.rendered){
133
+ this.shadowRoot.getElementById('logs').appendChild(html(/*html*/`
134
+ <div class="log log-${type}">${
135
+ messages.map(message => /*html*/`<div class="log-message">${message}</div>`).join('')
136
+ }</div>
137
+ `));
138
+ }
139
+ }
140
+ logError(...messages){
141
+ this.log('error', messages);
142
+ }
143
+ logWarning(...messages){
144
+ this.log('warning', messages);
145
+ }
146
+ logSuccess(...messages){
147
+ this.log('success', messages);
148
+ }
149
+ renderLogs(){
150
+ return this.logs.reverse().map( ({type, messages}) => /*html*/`
151
+ <div class="log log-${type}">${
152
+ messages.map(message => /*html*/`<div class="log-message">${message}</div>`).join('')
153
+ }</div>
154
+ `).join('');
155
+ }
156
+ async run(){
157
+ if(this.status !== 'Not Ran') this.log('<br />-----<br /><br />');
158
+ this.log('Starting Test');
159
+ const pass = (...messages) => {
160
+ const msg = messages.length?messages.join('<br />'):'No message';
161
+ this.log(`<div class='log-success'>Passed: ${msg}</div>`);
162
+ this.status = 'Passed';
163
+ this.shadowRoot.getElementById('status').innerText = this.status;
164
+ this.dispatchEvent(new CustomEvent('testrun', {
165
+ detail: {
166
+ test: this,
167
+ status: this.status,
168
+ passed: true
169
+ },
170
+ bubbles: true
171
+ }));
172
+ }
173
+ const fail = (...messages) => {
174
+ const msg = messages.length?messages.join('<br />'):'No Message';
175
+ this.log(`<div class='log-error'>Failed: ${msg}</div>`);
176
+ this.status = 'Failed';
177
+ this.shadowRoot.getElementById('status').innerText = this.status;
178
+ this.dispatchEvent(new CustomEvent('testrun', {
179
+ detail: {
180
+ test: this,
181
+ status: this.status,
182
+ passed: false
183
+ },
184
+ bubbles: true
185
+ }));
186
+ }
187
+ await this.test({
188
+ pass,
189
+ fail,
190
+ log: this.log.bind(this),
191
+ logError: this.logError.bind(this),
192
+ logWarning: this.logWarning.bind(this),
193
+ logSuccess: this.logSuccess.bind(this)
194
+ });
195
+ }
196
+
197
+ get shadowTemplate(){
198
+ return /*html*/`
199
+ ${super.shadowTemplate}
200
+ <h5>
201
+ <span id="name"></span>
202
+ <small>(
203
+ <span id="status">
204
+ ${this.status}
205
+ </span>
206
+ )</small>
207
+ </h5>
208
+ <p id="description"></p>
209
+ <button
210
+ id="run"
211
+ class="d-b mb"
212
+ >Run Test</button>
213
+ <button
214
+ id="toggleLogs"
215
+ class="link"
216
+ >
217
+ <span class="show-logs-true">Hide</span>
218
+ <span class="show-logs-false">Show</span>
219
+ Logs
220
+ </button>
221
+ <pre class="show-logs-true"><code id="logs"></code></pre>
222
+ `;
223
+ }
224
+ get shadowStyles(){
225
+ return /*css*/`
226
+ ${super.shadowStyles}
227
+ :host {
228
+ display: block;
229
+ border: 1px solid var(--c_border);
230
+ border-radius: var(--radius);
231
+ margin-bottom: var(--spacer);
232
+ padding: var(--spacer);
233
+ }
234
+ :host(:not([show-logs="true"])) .show-logs-true,
235
+ :host([show-logs="true"]) .show-logs-false {
236
+ display: none;
237
+ }
238
+ #logs {
239
+ min-height: 6rem;
240
+ max-height: 36rem;
241
+ resize: vertical;
242
+ }
243
+ :host([status="Passed"]) h5,
244
+ .log-success {
245
+ color: var(--tc_success);
246
+ }
247
+ :host([status="Failed"]) h5,
248
+ .log-error {
249
+ color: var(--tc_danger);
250
+ }
251
+ .log-warning {
252
+ color: var(--tc_warning);
253
+ }
254
+ .log-error .log-message::before {
255
+ content: 'Error: ';
256
+ }
257
+ .log-warning .log-message::before {
258
+ content: 'Warning: ';
259
+ }
260
+ `;
261
+ }
262
+ }
263
+ window.customElements.define('k-test', Test);
264
+
265
+ class TestSummary extends Component {
266
+ constructor(){
267
+ super();
268
+
269
+ this[testUpdatedHandler] = () => {
270
+ this.update();
271
+ }
272
+ this[runClickHandler] = () => {
273
+ this.runAll();
274
+ }
275
+
276
+ this.registerAttributes({
277
+ passed: 0,
278
+ failed: 0,
279
+ notRan: 0
280
+ });
281
+ }
282
+ connectedCallback(){
283
+ super.connectedCallback();
284
+ this.update();
285
+ window.addEventListener('testadded', this[testUpdatedHandler]);
286
+ window.addEventListener('testrun', this[testUpdatedHandler]);
287
+ }
288
+ disconnectedCallback(){
289
+ window.removeEventListener('testadded', this[testUpdatedHandler]);
290
+ window.removeEventListener('testrun', this[testUpdatedHandler]);
291
+ if(this.rendered){
292
+ this.shadowRoot.getElementById('runAll').removeEventListener('click', this[runClickHandler]);
293
+ }
294
+ super.disconnectedCallback();
295
+ }
296
+ async render(force){
297
+ if(super.render(force)){
298
+ this.shadowRoot.getElementById('runAll').addEventListener('click', this[runClickHandler]);
299
+ return true;
300
+ }
301
+ return false;
302
+ }
303
+ update(){
304
+ let notRan = 0,
305
+ failed = 0,
306
+ passed = 0;
307
+ const $tests = [...document.querySelectorAll('k-test')];
308
+ $tests.forEach( $test => {
309
+ if($test.status === 'Passed'){
310
+ passed++;
311
+ } else if($test.status === 'Failed'){
312
+ failed++;
313
+ } else {
314
+ notRan++;
315
+ }
316
+ });
317
+ this.passed = passed;
318
+ this.failed = failed;
319
+ this.notRan = notRan;
320
+ if(this.rendered){
321
+ this.shadowRoot.getElementById('notRan').innerText = this.notRan;
322
+ this.shadowRoot.getElementById('passed').innerText = this.passed;
323
+ this.shadowRoot.getElementById('failed').innerText = this.failed;
324
+ }
325
+ }
326
+ async runAll(){
327
+ const $tests = [...document.querySelectorAll('k-test')];
328
+ for(const $test of $tests){
329
+ await $test.run();
330
+ }
331
+ }
332
+ get shadowTemplate(){
333
+ return /*html*/`
334
+ <h3>Test Summary</h3>
335
+ <button
336
+ id="runAll"
337
+ class="primary mb"
338
+ >Run All Tests</button>
339
+ <p>Not Ran: <span id="notRan"></span></p>
340
+ <p>Passed: <span id="passed"></span></p>
341
+ <p>Failed: <span id="failed"></span></p>
342
+ `;
343
+ }
344
+ get shadowStyles(){
345
+ return /*css*/`
346
+ ${super.shadowStyles}
347
+ :host {
348
+ display: block;
349
+ border: 1px solid var(--c_border);
350
+ border-radius: var(--radius);
351
+ padding: var(--spacer) var(--spacer) 0;
352
+ margin-bottom: var(--spacer);
353
+ }
354
+ `;
355
+ }
356
+ }
357
+ window.customElements.define('k-test-summary', TestSummary);
@@ -0,0 +1,49 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Tests</title>
7
+ <link rel="stylesheet" href="../kempo/kempo-vars.css" /><link rel="stylesheet" href="../kempo/kempo-styles.css" />
8
+ <style>
9
+ #test {
10
+ position: fixed;
11
+ top: 0;
12
+ right: 0;
13
+ min-width: 16px;
14
+ max-width: 640px;
15
+ min-height: 16px;
16
+ max-height: 640px;
17
+ opacity: 0.05;
18
+ pointer-events: none;
19
+ }
20
+ :root {
21
+ --container_width: 130rem;
22
+ }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <main>
27
+ <div class="row -mx">
28
+ <div class="col d-span-3 t-span-4 m-span-12 p">
29
+ <k-import src="/suites.inc.html"></k-import>
30
+ </div>
31
+ <div class="col d-span-9 t-span-8 m-span-12 p">
32
+ <div id="test"></div>
33
+ <h1>Kempo Tests</h1>
34
+ <k-test-summary></k-test-summary>
35
+
36
+ <h3>Components</h3>
37
+ <k-test-suite name="components/Component"></k-test-suite>
38
+
39
+ <h3>Utilities</h3>
40
+ <k-test-suite name="utils/cookie"></k-test-suite>
41
+ <k-test-suite name="utils/debounce"></k-test-suite>
42
+ </div>
43
+ </div>
44
+ </main>
45
+ <script src="/kempo/kempo.js" type="module"></script>
46
+ <script src="/components/Test.js" type="module"></script>
47
+ <script src="/components/SuiteList.js" type="module"></script>
48
+ </body>
49
+ </html>
package/test/index.js ADDED
@@ -0,0 +1,25 @@
1
+ import express from 'express';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import cli from '../kempo/utils/cli.js';
5
+
6
+ const {
7
+ port = 8084
8
+ } = cli();
9
+
10
+ const app = express();
11
+
12
+ app.use(express.static('test'));
13
+ app.use('/kempo', express.static('kempo'));
14
+ app.get('/api/tests', async (_, res) => {
15
+ const tests = {};
16
+ const categories = await fs.readdir('./test/tests/');
17
+ for(let i = 0; i < categories.length; i++){
18
+ tests[categories[i]] = (await fs.readdir(`./test/tests/${categories[i]}/`)).map( t => t.slice(0, t.length - 8));
19
+ }
20
+ res.json(tests);
21
+ });
22
+
23
+ app.listen(port, () => {
24
+ console.log(`Tests Running on: http://localhost:${port}`);
25
+ });
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Tests</title>
7
+ <link rel="stylesheet" href="../kempo/kempo-vars.css" /><link rel="stylesheet" href="../kempo/kempo-styles.css" />
8
+ <style>
9
+ #test {
10
+ position: fixed;
11
+ top: 0;
12
+ right: 0;
13
+ min-width: 16px;
14
+ max-width: 640px;
15
+ min-height: 16px;
16
+ max-height: 640px;
17
+ opacity: 0.05;
18
+ pointer-events: none;
19
+ }
20
+ :root {
21
+ --container_width: 130rem;
22
+ }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <main>
27
+ <div class="row -mx">
28
+ <div class="col d-span-3 t-span-4 m-span-12 p">
29
+ <a href="/"><h3>Tests Home</h3></a>
30
+ <k-import src="/suites.inc.html"></k-import>
31
+ </div>
32
+ <div class="col d-span-9 t-span-8 m-span-12 p">
33
+ <div id="test"></div>
34
+ <div id="suite"></div>
35
+ </div>
36
+ </div>
37
+ </main>
38
+ <script src="/kempo/kempo.js" type="module"></script>
39
+ <script src="/components/Test.js" type="module"></script>
40
+ <script src="/components/SuiteList.js" type="module"></script>
41
+ <script>
42
+ document.getElementById('suite').innerHTML = `<k-test-suite name="${new URLSearchParams(location.search).get('name')}"></k-test-suite>`;
43
+ </script>
44
+ </body>
45
+ </html>
@@ -0,0 +1,4 @@
1
+ <aside>
2
+ <h3>Test Suites</h4>
3
+ <k-suite-list></k-suite-list>
4
+ </aside>
@@ -0,0 +1,138 @@
1
+ import Component from '../../../kempo/components/Component.js';
2
+
3
+ const raf = async () => new Promise(r=>requestAnimationFrame(r));
4
+
5
+ export const name = 'Component Test';
6
+ export const description = 'A set of tests for the core Component class';
7
+ export const tests = [
8
+ {
9
+ name: 'Can be instatiated',
10
+ description: 'An instance of the class can be created',
11
+ test: ({
12
+ pass,
13
+ fail,
14
+ log
15
+ }) => {
16
+ log('Creating an instance of Component');
17
+ try {
18
+ new Component();
19
+ pass('An instance of Component was created');
20
+ } catch (error) {
21
+ fail(error.message);
22
+ }
23
+ }
24
+ },
25
+ {
26
+ name: 'Is Initiated when created via the tag',
27
+ description: 'When a <code>&lt;k-component></code> created, is it an instance of <code>Component</code>?',
28
+ test: ({
29
+ pass,
30
+ fail,
31
+ log
32
+ }) => {
33
+ log('Creating k-component');
34
+ const $comp = document.createElement('k-component');
35
+ log('Testing if result is an instanceof of Component');
36
+ if($comp instanceof Component){
37
+ pass('The result is an instance of Component');
38
+ } else {
39
+ fail('The result is not an instance of Component');
40
+ }
41
+ }
42
+ },
43
+ {
44
+ name: "Has all required properties",
45
+ description: "Has all required properties and they are the correct type",
46
+ test: ({
47
+ pass,
48
+ fail,
49
+ log
50
+ }) => {
51
+ log('Creating an instance of Component');
52
+ const $comp = new Component();
53
+ const requiredProps = {
54
+ shadowTemplate: 'string',
55
+ shadowStyles: 'string',
56
+ rendered: 'boolean',
57
+ render: 'function'
58
+ };
59
+ if(
60
+ Object.keys(requiredProps).every( propName => {
61
+ log(`Testing that ${propName} is a ${requiredProps[propName]}`);
62
+ return typeof($comp[propName]) === requiredProps[propName];
63
+ })
64
+ ){
65
+ pass('The component contains all required properties and they are of the correct type');
66
+ } else {
67
+ fail('The component does not have all properties, or they are not of the correc type', JSON.stringify(requiredProps));
68
+ }
69
+ }
70
+ },
71
+ {
72
+ name: 'Is rendered when connected to a DOM',
73
+ description: 'The element should have it\'s rendered attribute set to true when it is connected to the DOM',
74
+ test: async ({
75
+ pass,
76
+ fail,
77
+ log
78
+ }) => {
79
+ log('Creating an instance of Component');
80
+ const $comp = new Component();
81
+ log('Attaching the component to the DOM');
82
+ document.getElementById('test').appendChild($comp);
83
+ log('Waiting 1 frame for render');
84
+ await raf();
85
+ log('Checking if the component is rendered');
86
+ if($comp.rendered){
87
+ pass('The component was rendered');
88
+ } else {
89
+ fail('The component was not rendered');
90
+ }
91
+ log('Cleanup: Removing the component from the DOM');
92
+ $comp.remove();
93
+ }
94
+ },
95
+ {
96
+ name: 'Contains a Shadow DOM',
97
+ description: 'The element should contain a Shadow DOM that contains the <code>shadowTemplate</code> and the <code>shadowStyles</code>.',
98
+ test: async ({
99
+ pass,
100
+ fail,
101
+ log
102
+ }) => {
103
+ log('Creating an instance of Component');
104
+ const $comp = new Component();
105
+
106
+ const cleanup = () => {
107
+ log('Cleanup: Removing the component from the DOM');
108
+ $comp.remove();
109
+ }
110
+
111
+ log('Attaching the component to the DOM');
112
+ document.getElementById('test').appendChild($comp);
113
+ log('Waiting 1 frame for render');
114
+ await raf();
115
+
116
+ log('Verifing if it contains a shadowRoot');
117
+ if(!$comp.shadowRoot){
118
+ fail('The component does not have a shadowRoot');
119
+ cleanup();
120
+ return;
121
+ }
122
+ log('Verifing that the shadowRoot contains the shadowTemplate');
123
+ if(!$comp.shadowRoot.innerHTML.includes($comp.shadowTemplate)){
124
+ fail('The component\'s shadowRoot does not contain the shadowTemplate');
125
+ cleanup();
126
+ return;
127
+ }
128
+ log('Verifing that the shadowRoot contains the shadowStyles');
129
+ if(!$comp.shadowRoot.innerHTML.includes($comp.shadowStyles)){
130
+ fail('The component\'s shadowRoot does not contain the shadowStyles');
131
+ cleanup();
132
+ return;
133
+ }
134
+ pass('The elemnet contains a proper Shadow DOM');
135
+ cleanup();
136
+ }
137
+ }
138
+ ];