@magnit-ce/code-tests 0.0.13 → 0.0.14
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/README.md +95 -64
- package/dist/code-tests.d.ts +172 -58
- package/dist/code-tests.js +1178 -1012
- package/dist/code-tests.min.js +1 -475
- package/dist/code-tests.umd.js +1434 -0
- package/dist/code-tests.umd.min.js +1 -0
- package/package.json +25 -24
- package/dist/code-tests.cjs +0 -1296
- package/dist/code-tests.d.cts +0 -58
|
@@ -0,0 +1,1434 @@
|
|
|
1
|
+
(function(global, factory) {
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory((global["code-tests"] = global["code-tests"] || {}, global["code-tests"].umd = global["code-tests"].umd || {}, global["code-tests"].umd.js = {})));
|
|
3
|
+
})(this, (function(exports2) {
|
|
4
|
+
"use strict";
|
|
5
|
+
const style = `:host
|
|
6
|
+
{
|
|
7
|
+
--gap: 7px;
|
|
8
|
+
--gap-small: 3px;
|
|
9
|
+
--gap-medium: 14px;
|
|
10
|
+
--gap-large: 24px;
|
|
11
|
+
|
|
12
|
+
--surface-success: oklch(93.96% 0.05 148.74);
|
|
13
|
+
--primary-success: oklch(58.83% 0.158 145.05);
|
|
14
|
+
--border-success: solid 1px var(--primary-success);
|
|
15
|
+
|
|
16
|
+
--surface-fail: oklch(88.98% 0.052 3.28);
|
|
17
|
+
--primary-fail: oklch(45.8% 0.177 17.7);
|
|
18
|
+
--border-fail: solid 1px var(--primary-fail);
|
|
19
|
+
|
|
20
|
+
--surface-process: oklch(89.66% 0.046 260.67);
|
|
21
|
+
--primary-process: oklch(43.48% 0.17 260.2);
|
|
22
|
+
--border-process: solid 1px var(--primary-process);
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
--surface-test-summary: var(--uchu-gray);
|
|
27
|
+
--surface-hook-summary: var(--uchu-light-purple);
|
|
28
|
+
--surface-hook-any-summary: var(--uchu-light-blue);
|
|
29
|
+
|
|
30
|
+
--border-test: solid 1px var(--uchu-dark-gray);
|
|
31
|
+
--border-hook: solid 1px var(--uchu-dark-purple);
|
|
32
|
+
--border-hook-any: solid 1px var(--uchu-dark-blue);
|
|
33
|
+
--border-button: solid 1px var(--uchu-blue);
|
|
34
|
+
|
|
35
|
+
--success-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="%232e943a" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg>');
|
|
36
|
+
--info-icon: url('data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2022.812714%2022.814663%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20style%3D%22fill%3Atransparent%3Bstroke%3Atransparent%3Bstroke-width%3A0.999999%3Bstroke-linejoin%3Around%3Bstroke-miterlimit%3A6.3%3Bstroke-dasharray%3Anone%3Bstroke-dashoffset%3A29.2913%3Bstroke-opacity%3A1%22%20d%3D%22M%2022.295505%2C11.407332%20A%2010.889144%2C10.889144%200%200%201%2011.406424%2C22.296479%2010.889144%2C10.889144%200%200%201%200.51720881%2C11.407332%2010.889144%2C10.889144%200%200%201%2011.406424%2C0.51818382%2010.889144%2C10.889144%200%200%201%2022.295505%2C11.407332%20Z%22%3E%3C%2Fpath%3E%3Cpath%20d%3D%22m%2013.945668%2C4.3053761%20c%200.150778%2C-0.96462%20-0.30687%2C-1.43709%20-1.36997%2C-1.43709%20-1.063%2C0%20-1.668452%2C0.47247%20-1.81923%2C1.43709%20-0.150779%2C0.96462%200.306971%2C1.43708%201.369971%2C1.43708%201.004%2C0%201.66845%2C-0.47246%201.819229%2C-1.43708%20z%20M%2011.693889%2C17.829726%2013.373994%2C7.0811161%20h%20-2.9333%20L%208.7605887%2C17.829726%20Z%22%20style%3D%22font-size%3A19.6861px%3Bfont-family%3APassageway%3B-inkscape-font-specification%3APassageway%3Bfill%3A%23a30d30%3Bstroke-width%3A2.5%3Bstroke-linejoin%3Around%3Bstroke-miterlimit%3A6.3%3Bstroke-dashoffset%3A29.2913%22%20aria-label%3D%22i%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E');
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
display: grid;
|
|
40
|
+
gap: var(--gap);
|
|
41
|
+
grid-auto-rows: max-content;
|
|
42
|
+
font-family: sans-serif;
|
|
43
|
+
font-size: 12px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#header
|
|
47
|
+
{
|
|
48
|
+
flex: 1;
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: var(--gap);
|
|
52
|
+
padding: var(--gap-small) var(--gap);
|
|
53
|
+
}
|
|
54
|
+
#title
|
|
55
|
+
{
|
|
56
|
+
flex: 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#tests
|
|
60
|
+
,#component-content
|
|
61
|
+
{
|
|
62
|
+
display: grid;
|
|
63
|
+
gap: var(--gap);
|
|
64
|
+
grid-auto-rows: max-content;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#header button
|
|
68
|
+
{
|
|
69
|
+
align-self: stretch;
|
|
70
|
+
display: inline-flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: var(--gap-small);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
button .reset-icon
|
|
76
|
+
{
|
|
77
|
+
width: 1em;
|
|
78
|
+
height: 1em;
|
|
79
|
+
transform: scaleX(-1);
|
|
80
|
+
margin-inline: 3px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Run button */
|
|
84
|
+
button .arrow-icon
|
|
85
|
+
{
|
|
86
|
+
width: .75em;
|
|
87
|
+
height: .75em;
|
|
88
|
+
transform: rotate(-90deg);
|
|
89
|
+
margin-inline: 3px;
|
|
90
|
+
}
|
|
91
|
+
:host(.running) .run-all-button-icon
|
|
92
|
+
,:host(.fail) .run-all-button-icon
|
|
93
|
+
{
|
|
94
|
+
transform: rotate(0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Dropdown Layout */
|
|
98
|
+
summary > .run-test-button
|
|
99
|
+
, #run-all-button
|
|
100
|
+
{
|
|
101
|
+
justify-self: flex-end;
|
|
102
|
+
margin-left: auto;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
summary:not(#component-summary)
|
|
106
|
+
{
|
|
107
|
+
padding: var(--gap-small) var(--gap);
|
|
108
|
+
}
|
|
109
|
+
#component-summary
|
|
110
|
+
{
|
|
111
|
+
padding-block: var(--gap-small);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
/* Dropdown Markers */
|
|
116
|
+
summary
|
|
117
|
+
{
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
gap: var(--gap);
|
|
121
|
+
}
|
|
122
|
+
summary > .arrow-icon
|
|
123
|
+
{
|
|
124
|
+
background: var(--arrow-icon);
|
|
125
|
+
transform: rotate(-90deg);
|
|
126
|
+
transition: transform ease-out 90ms;
|
|
127
|
+
width: .6em;
|
|
128
|
+
height: .6em;
|
|
129
|
+
}
|
|
130
|
+
details[open] > summary > .arrow-icon
|
|
131
|
+
{
|
|
132
|
+
transform: rotate(0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Result Icon */
|
|
136
|
+
.result-icon
|
|
137
|
+
{
|
|
138
|
+
--background-size: 16px;
|
|
139
|
+
width: var(--background-size);
|
|
140
|
+
height: var(--background-size);
|
|
141
|
+
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
|
|
146
|
+
border: solid 1px currentColor;
|
|
147
|
+
border-radius: 50%;
|
|
148
|
+
}
|
|
149
|
+
.result-icon::before
|
|
150
|
+
{
|
|
151
|
+
content: '⋯';
|
|
152
|
+
font-size: 10px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
:host(.running) #component-summary .result-icon
|
|
156
|
+
,.test.running .test-summary > .result-icon
|
|
157
|
+
,.hook.running .result-icon
|
|
158
|
+
,.processing-details.running .processing-result-icon
|
|
159
|
+
{
|
|
160
|
+
border: var(--border-process);
|
|
161
|
+
background: var(--surface-process);
|
|
162
|
+
}
|
|
163
|
+
:host(.success) #component-summary .result-icon
|
|
164
|
+
,.test.success .test-summary > .result-icon
|
|
165
|
+
,.hook.success .result-icon
|
|
166
|
+
,.processing-details.success .processing-result-icon
|
|
167
|
+
{
|
|
168
|
+
border: var(--border-success);
|
|
169
|
+
background: var(--surface-success)
|
|
170
|
+
var(--success-icon);
|
|
171
|
+
background-repeat: no-repeat;
|
|
172
|
+
background-position: center;
|
|
173
|
+
background-size: var(--icon-size, 12px) var(--icon-size, 12px);
|
|
174
|
+
}
|
|
175
|
+
:host(.fail) #component-summary .result-icon
|
|
176
|
+
,.test.fail .test-summary > .result-icon
|
|
177
|
+
,.hook.fail .result-icon
|
|
178
|
+
,.processing-details.fail .processing-result-icon
|
|
179
|
+
{
|
|
180
|
+
border: var(--border-fail);
|
|
181
|
+
background: var(--surface-fail)
|
|
182
|
+
var(--info-icon);
|
|
183
|
+
background-size: var(--icon-size, 16px) var(--icon-size, 16px);
|
|
184
|
+
background-repeat: no-repeat;
|
|
185
|
+
background-position: center;
|
|
186
|
+
transform: rotate(175deg);
|
|
187
|
+
}
|
|
188
|
+
:host(:is(.success,.fail)) #component-summary .result-icon::before
|
|
189
|
+
,.test:is(.success,.fail) .test-summary > .result-icon::before
|
|
190
|
+
,.hook:is(.success,.fail) .result-icon::before
|
|
191
|
+
,.processing-details:is(.success,.fail) .processing-result-icon::before
|
|
192
|
+
{
|
|
193
|
+
display: none;
|
|
194
|
+
}
|
|
195
|
+
:host(.running) #component-summary .result-icon::before
|
|
196
|
+
,.test:is(.running) .test-summary > .result-icon::before
|
|
197
|
+
,.hook:is(.running) .result-icon::before
|
|
198
|
+
,.processing-details:is(.running) .processing-result-icon::before
|
|
199
|
+
{
|
|
200
|
+
content: '';
|
|
201
|
+
--color: var(--primary-process, currentColor);
|
|
202
|
+
--animation-timing-function: linear;
|
|
203
|
+
--animation-duration: 2s;
|
|
204
|
+
width: var(--icon-size, 14px);
|
|
205
|
+
height: var(--icon-size, 14px);
|
|
206
|
+
mask-image: radial-gradient(circle at 50% 50%, transparent calc(var(--icon-size, 14px) / 3), black calc(var(--icon-size, 14px) / 3));
|
|
207
|
+
background-image: conic-gradient(transparent, transparent 135deg, var(--color));
|
|
208
|
+
border-radius: 50%;
|
|
209
|
+
animation: var(--animation-timing-function) var(--animation-duration) infinite spin;
|
|
210
|
+
margin: 2px;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Smaller Result icon settings for sub-test icons */
|
|
214
|
+
.before-each-result-icon
|
|
215
|
+
,.after-each-result-icon
|
|
216
|
+
,.processing-result-icon
|
|
217
|
+
{
|
|
218
|
+
--background-size: 12px;
|
|
219
|
+
}
|
|
220
|
+
.before-each-result-icon::before
|
|
221
|
+
,.after-each-result-icon::before
|
|
222
|
+
,.processing-result-icon::before
|
|
223
|
+
{
|
|
224
|
+
font-size: 9px;
|
|
225
|
+
}
|
|
226
|
+
.hook.success .before-each-result-icon
|
|
227
|
+
,.hook.success .after-each-result-icon
|
|
228
|
+
,.processing-details.success .processing-result-icon
|
|
229
|
+
{
|
|
230
|
+
--icon-size: 8px;
|
|
231
|
+
}
|
|
232
|
+
.hook.fail .before-each-result-icon
|
|
233
|
+
,.hook.fail .after-each-result-icon
|
|
234
|
+
,.processing-details.fail .processing-result-icon
|
|
235
|
+
{
|
|
236
|
+
--icon-size: 12px;
|
|
237
|
+
}
|
|
238
|
+
.hook:is(.running) .before-each-result-icon::before
|
|
239
|
+
,.hook:is(.running) .after-each-result-icon::before
|
|
240
|
+
,.processing-details:is(.running) .processing-result-icon::before
|
|
241
|
+
{
|
|
242
|
+
--icon-size: 9px;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Hook Display */
|
|
246
|
+
.hook
|
|
247
|
+
{
|
|
248
|
+
display: none;
|
|
249
|
+
}
|
|
250
|
+
:host(.has-before-all-hook) #before-all-details
|
|
251
|
+
,:host(.has-after-all-hook) #after-all-details
|
|
252
|
+
{
|
|
253
|
+
display: initial;
|
|
254
|
+
}
|
|
255
|
+
:host(.has-before-each-hook) .before-each-details
|
|
256
|
+
,:host(.has-after-each-hook) .after-each-details
|
|
257
|
+
{
|
|
258
|
+
display: initial;
|
|
259
|
+
}
|
|
260
|
+
:host(.has-required-before-hook) #required-before-any-details
|
|
261
|
+
,:host(.has-required-after-hook) #required-after-any-details
|
|
262
|
+
{
|
|
263
|
+
display: initial;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* Test Display */
|
|
267
|
+
code-test .results
|
|
268
|
+
{
|
|
269
|
+
display: grid;
|
|
270
|
+
gap: var(--gap-small);
|
|
271
|
+
padding-inline-start: 1em;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
code-test .results details .result
|
|
275
|
+
,.hook > .results
|
|
276
|
+
{
|
|
277
|
+
margin-inline-start: 1em;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.result.message:empty
|
|
281
|
+
{
|
|
282
|
+
padding: .5em 1em;
|
|
283
|
+
}
|
|
284
|
+
.result.message:empty::before
|
|
285
|
+
{
|
|
286
|
+
content: '[ this function has not been run ]';
|
|
287
|
+
font-family: monospace;
|
|
288
|
+
font-size: 12px;
|
|
289
|
+
font-style: italic;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* Ordered Display */
|
|
293
|
+
#tests
|
|
294
|
+
{
|
|
295
|
+
counter-reset: tests;
|
|
296
|
+
}
|
|
297
|
+
code-test
|
|
298
|
+
{
|
|
299
|
+
counter-increment: tests;
|
|
300
|
+
}
|
|
301
|
+
code-test > details > summary > .description
|
|
302
|
+
{
|
|
303
|
+
flex: 1;
|
|
304
|
+
}
|
|
305
|
+
:host(:not([ordered="false"])) code-test > details > summary > .description::before
|
|
306
|
+
{
|
|
307
|
+
content: counter(tests) ". ";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* Hook Name */
|
|
311
|
+
.hook-name,.processing-description
|
|
312
|
+
{
|
|
313
|
+
border-radius: 3px;
|
|
314
|
+
border: solid 1px;
|
|
315
|
+
/* background: rgb(0 0 0 / .2); */
|
|
316
|
+
font-family: monospace;
|
|
317
|
+
text-transform: uppercase;
|
|
318
|
+
font-size: 11px;
|
|
319
|
+
padding: 3px 7px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
#footer
|
|
323
|
+
{
|
|
324
|
+
display: flex;
|
|
325
|
+
justify-content: flex-end;
|
|
326
|
+
}
|
|
327
|
+
#group-results
|
|
328
|
+
{
|
|
329
|
+
display: flex;
|
|
330
|
+
gap: var(--gap);
|
|
331
|
+
padding: var(--gap);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
#passed-total-percent::before
|
|
335
|
+
{
|
|
336
|
+
content: '(';
|
|
337
|
+
}
|
|
338
|
+
#passed-total-percent::after
|
|
339
|
+
{
|
|
340
|
+
content: ')';
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@keyframes spin
|
|
344
|
+
{
|
|
345
|
+
from { transform: rotate(0deg); }
|
|
346
|
+
to { transform: rotate(360deg); }
|
|
347
|
+
}`;
|
|
348
|
+
const html = '<details id="component-details" class="details">\n <summary id="component-summary" class="summary">\n <svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>\n <slot name="header">\n <header id="header">\n <span id="component-result-icon" class="result-icon"></span>\n <span id="title"><slot name="title"><span id="title-text">Tests</span></slot></span>\n <slot name="reload-button">\n <button type="button" id="reload-button" title="Reload">\n <slot name="reload-button-content">\n <slot name="reload-button-icon"><svg class="icon reset-icon reload-button-icon"><use href="#icon-definition_reset"></use></svg></slot>\n <slot name="reload-button-label"><span class="reload-button-label button-label label icon">Reload</span></slot>\n </slot>\n </button>\n </slot>\n <slot name="run-all-button">\n <button type="button" id="run-all-button" title="Run All Tests">\n <slot name="run-all-button-content">\n <slot name="run-all-button-icon"><svg class="icon arrow-icon run-button-icon run-all-button-icon"><use href="#icon-definition_arrow"></use></svg></slot>\n <slot name="run-all-button-label"><span class="run-all-button-label run-button-label button-label label icon">Run Tests</span></slot>\n </slot>\n </button>\n </slot>\n <slot name="header-details"></slot>\n </header>\n </slot>\n </summary>\n <div id="component-content" class="content">\n <details id="required-before-any-details" class="hook">\n <summary id="required-before-any-summary">\n <svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>\n <span id="required-before-any-result-icon" class="result-icon"></span>\n <span id="required-before-any-description" class="description hook-name">Required Before Any Hook</span>\n </summary>\n <div class="results">\n <div id="required-before-any-results" class="result message"></div>\n </div>\n </details>\n <details id="before-all-details" class="hook">\n <summary id="before-all-summary">\n <svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>\n <span id="before-all-result-icon" class="result-icon"></span>\n <span id="before-all-description" class="description hook-name">Before All Hook</span>\n </summary>\n <div class="results">\n <div id="before-all-results" class="result message"></div>\n </div>\n </details>\n <div id="tests"></div>\n <details id="after-all-details" class="hook">\n <summary id="after-all-summary">\n <svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>\n <span id="after-all-result-icon" class="result-icon"></span>\n <span id="after-all-description" class="description hook-name">After All Hook</span>\n </summary>\n <div class="results">\n <div id="after-all-results" class="result message"></div>\n </div>\n </details>\n <details id="required-after-any-details" class="hook">\n <summary id="required-after-any-summary">\n <svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>\n <span id="required-after-any-result-icon" class="result-icon"></span>\n <span id="required-after-any-description" class="description hook-name">Required After Any Hook</span>\n </summary>\n <div class="results">\n <div id="required-after-any-results" class="result message"></div>\n </div>\n </details>\n </div>\n \n <slot name="footer">\n <foooter id="footer">\n <span id="group-results">\n <span id="duration">\n <span id="duration-label">Duration</span>\n <span id="duration-value"></span>\n <span id="duration-unit">ms</span>\n </span>\n <span id="results-progress">\n <progress id="results-progress-value"></progress>\n </span>\n <span id="passed-total-pair">\n <span id="total-tests-passed-value"></span>\n <span id="passed-total-delimiter">of</span>\n <span id="total-tests-count-value"></span>\n <span id="total-tests-passed-label">Passed</span>\n </span>\n <span id="passed-total-percent">\n <span id="passed-total-percent-value"></span>\n <span id="passed-total-percent-label">%</span>\n </span>\n </span>\n </foooter>\n </slot>\n</details>\n<template id="prompt-template">\n <div class="prompt">\n <div class="prompt-display">\n <span class="icon prompt-icon"></span>\n <span class="label prompt-label"></span>\n </div>\n <div class="prompt-actions">\n <button class="prompt-button accept" type="button">Accept</button>\n <button class="prompt-button reject" type="button">Reject</button>\n </div>\n </div>\n</template>\n<div id="icon-definitions" style="display: none;">\n <svg id="icon-definition_arrow" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">\n <path d="m3.26,6.81l-2.93,-4.69c-0.37,-0.58 0.05,-1.34 0.74,-1.34l5.87,0c0.69,0 1.11,0.76 0.74,1.34l-2.93,4.69c-0.35,0.55 -1.14,0.55 -1.49,0z" fill="var(--fill-color, currentcolor)" />\n </svg>\n <svg id="icon-definition_reset" class="icon reset" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">\n <path\n style="fill:var(--fill-color, currentcolor);stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;paint-order:markers stroke fill"\n d="M 5.484375 0.43359375 A 1.5224222 1.5224222 0 0 0 4.2558594 1.2011719 L 0.99804688 6.9453125 A 1.5224222 1.5224222 0 0 0 2.5488281 9.2011719 L 9.34375 8.171875 A 1.5224222 1.5224222 0 0 0 10.332031 5.7539062 L 9.4746094 4.6113281 C 11.949333 3.8016718 14.718209 4.258351 16.822266 5.9570312 C 19.510764 8.1275534 20.456787 11.785479 19.160156 14.988281 C 17.863527 18.191083 14.6405 20.15873 11.199219 19.847656 C 7.7579362 19.536584 4.9376009 17.022073 4.2363281 13.638672 A 1.5 1.5 0 0 0 2.4628906 12.474609 A 1.5 1.5 0 0 0 1.2988281 14.248047 C 2.2656928 18.912838 6.1831413 22.407052 10.927734 22.835938 C 15.672328 23.264824 20.153706 20.531029 21.941406 16.115234 C 23.729107 11.699441 22.413741 6.6156073 18.707031 3.6230469 C 16.853677 2.1267667 14.61485 1.3255701 12.347656 1.2324219 C 10.738216 1.1662975 9.1150542 1.4598646 7.6035156 2.1132812 L 6.7988281 1.0390625 A 1.5224222 1.5224222 0 0 0 5.484375 0.43359375 z " />\n </svg>\n <svg id="icon-definition_cancel" class="icon cancel" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">\n <path\n style="fill:var(--fill-color, currentcolor);stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;paint-order:markers stroke fill"\n d="M -7.063234 9.5244002 C -5.8135728 7.6769245 -5.0820528 5.453265 -5.0820528 3.0633809 C -5.0820529 -3.3096433 -10.278145 -8.5098163 -16.65117 -8.5098163 C -23.024193 -8.5098163 -28.22385 -3.3110105 -28.22385 3.0620137 C -28.22385 9.4350379 -23.025043 14.634694 -16.65202 14.634694 C -12.668879 14.634694 -9.1460028 12.603526 -7.063234 9.5244002 z M -9.5406311 7.8637601 C -11.076991 10.143147 -13.683157 11.63463 -16.652974 11.63463 C -19.960499 11.63463 -22.814085 9.782061 -24.244824 7.0543029 L -24.236684 7.0527515 L -8.1332524 3.983715 L -8.1305391 3.9831979 C -8.2815631 5.4121635 -8.7798709 6.7350751 -9.5406311 7.8637601 z M -9.0610781 -0.92890828 L -9.069218 -0.92735695 L -25.17265 2.1416796 L -25.175363 2.1421967 C -24.719122 -2.1725739 -21.093311 -5.5092358 -16.652928 -5.5092358 C -13.345403 -5.5092358 -10.491243 -3.6566663 -9.0610781 -0.92890828 z "\n transform="rotate(-124.20981)" />\n </svg>\n</div>';
|
|
349
|
+
const CodeTestEvent = {
|
|
350
|
+
BeforeAll: "beforeall",
|
|
351
|
+
AfterAll: "afterall",
|
|
352
|
+
BeforeTest: "beforetest",
|
|
353
|
+
AfterTest: "aftertest",
|
|
354
|
+
BeforeHook: "beforehook",
|
|
355
|
+
AfterHook: "afterhook",
|
|
356
|
+
Cancel: "cancel",
|
|
357
|
+
Context: "context",
|
|
358
|
+
Reset: "reset"
|
|
359
|
+
};
|
|
360
|
+
const COMPONENT_TAG_NAME$1 = "code-test";
|
|
361
|
+
class CodeTestElement extends HTMLElement {
|
|
362
|
+
state = {
|
|
363
|
+
testId: "",
|
|
364
|
+
description: "none",
|
|
365
|
+
isDisabled: false,
|
|
366
|
+
testState: void 0,
|
|
367
|
+
beforeEachState: void 0,
|
|
368
|
+
afterEachState: void 0
|
|
369
|
+
};
|
|
370
|
+
setState(state) {
|
|
371
|
+
this.state = state;
|
|
372
|
+
this.#render();
|
|
373
|
+
}
|
|
374
|
+
setStateProperties(state) {
|
|
375
|
+
this.setState({
|
|
376
|
+
...this.state,
|
|
377
|
+
...state
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
setTestStateProperties(key, state) {
|
|
381
|
+
if (this.state[key] == null) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
this.setState({
|
|
385
|
+
...this.state,
|
|
386
|
+
[key]: {
|
|
387
|
+
...this.state[key],
|
|
388
|
+
...state
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
findElement(query) {
|
|
393
|
+
return this.querySelector(query);
|
|
394
|
+
}
|
|
395
|
+
findElements(query) {
|
|
396
|
+
return Array.from(this.querySelectorAll(query));
|
|
397
|
+
}
|
|
398
|
+
isRunning() {
|
|
399
|
+
return this.state.testState?.isRunning == true || this.state.beforeEachState?.isRunning == true || this.state.afterEachState?.isRunning == true;
|
|
400
|
+
}
|
|
401
|
+
hasRun() {
|
|
402
|
+
return this.state.testState?.hasRun == true || this.state.beforeEachState?.hasRun == true || this.state.afterEachState?.hasRun == true;
|
|
403
|
+
}
|
|
404
|
+
resultCategory() {
|
|
405
|
+
const testCategory = this.state.testState?.resultCategory ?? "none";
|
|
406
|
+
const beforeEachCategory = this.state.beforeEachState?.resultCategory ?? "none";
|
|
407
|
+
const afterEachCategory = this.state.afterEachState?.resultCategory ?? "none";
|
|
408
|
+
if (testCategory == "none" && beforeEachCategory == "none" && afterEachCategory == "none") {
|
|
409
|
+
return "none";
|
|
410
|
+
}
|
|
411
|
+
if (this.state.beforeEachState == null && this.state.afterEachState == null) {
|
|
412
|
+
return testCategory;
|
|
413
|
+
} else if (this.state.beforeEachState != null && this.state.afterEachState == null) {
|
|
414
|
+
if (testCategory == "fail" || beforeEachCategory == "fail") {
|
|
415
|
+
return "fail";
|
|
416
|
+
}
|
|
417
|
+
if (testCategory == "success" && beforeEachCategory == "success") {
|
|
418
|
+
return "success";
|
|
419
|
+
}
|
|
420
|
+
return "none";
|
|
421
|
+
} else if (this.state.beforeEachState == null && this.state.afterEachState != null) {
|
|
422
|
+
if (testCategory == "fail" || afterEachCategory == "fail") {
|
|
423
|
+
return "fail";
|
|
424
|
+
}
|
|
425
|
+
if (testCategory == "success" && afterEachCategory == "success") {
|
|
426
|
+
return "success";
|
|
427
|
+
}
|
|
428
|
+
return "none";
|
|
429
|
+
} else if (this.state.beforeEachState != null && this.state.afterEachState != null) {
|
|
430
|
+
if (testCategory == "fail" || beforeEachCategory == "fail" || afterEachCategory == "fail") {
|
|
431
|
+
return "fail";
|
|
432
|
+
}
|
|
433
|
+
if (testCategory == "success" && beforeEachCategory == "success" && afterEachCategory == "success") {
|
|
434
|
+
return "success";
|
|
435
|
+
}
|
|
436
|
+
return "none";
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
connectedCallback() {
|
|
440
|
+
this.#render();
|
|
441
|
+
}
|
|
442
|
+
#render() {
|
|
443
|
+
const resultMessage = this.state.testState == null ? "" : typeof this.state.testState.resultContent == "boolean" ? this.state.testState.resultContent == true ? `<code class="code" part="code"><pre class="pre success-message" part="pre success-message">Passed</pre></code>` : `<code class="code" part="code"><pre class="pre error-message" part="pre error-message">Failed</pre></code>` : typeof this.state.testState.resultContent == "number" ? `<code class="code" part="code"><pre class="pre success-message" part="pre success-message">Passed: ${this.state.testState.resultContent.toString()}</pre></code>` : typeof this.state.testState.resultContent == "string" ? this.state.testState.resultContent : "";
|
|
444
|
+
this.innerHTML = `<details class="test-details" part="test-details" ${this.isRunning() == true || this.hasRun() == true ? " open" : ""}>
|
|
445
|
+
<summary class="test-summary" part="test-summary">
|
|
446
|
+
<svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>
|
|
447
|
+
<div class="result-icon test-result-icon${this.state.testState?.resultCategory != "none" ? ` ${this.state.testState?.resultCategory}` : ""}" part="result-icon"></div>
|
|
448
|
+
<span class="test-description description">${this.state.description}</span>
|
|
449
|
+
${this.state.testState?.duration != null && this.state.testState.duration > 0 ? `<span class="test-duration duration">
|
|
450
|
+
<span class="test-duration-value">${this.state.testState.duration > 10 ? this.state.testState.duration.toFixed(0) : this.state.testState.duration.toFixed(2)}</span>
|
|
451
|
+
<span class="test-duration-unit">ms</span>
|
|
452
|
+
</span>` : ""}
|
|
453
|
+
<button type="button" class="run-test-button" part="run-test-button" title="Run Test"${this.state.isDisabled == true ? " disabled" : ""}>
|
|
454
|
+
<slot name="run-button-content">
|
|
455
|
+
<slot name="run-button-icon"><svg class="icon arrow-icon run-button-icon"><use href="#icon-definition_arrow"></use></svg></slot>
|
|
456
|
+
<slot name="run-button-label"><span class="run-button-label button-label label icon">Run Test</span></slot>
|
|
457
|
+
</slot>
|
|
458
|
+
</button>
|
|
459
|
+
</summary>
|
|
460
|
+
<div class="results" part="results">
|
|
461
|
+
${this.state.beforeEachState == null ? "" : `<details class="before-each-details hook${this.state.beforeEachState.resultCategory == "none" ? "" : ` ${this.state.beforeEachState.resultCategory}`}${this.state.beforeEachState.isRunning == true ? " running" : ""}" part="before-each-details hook">
|
|
462
|
+
<summary class="before-each-summary" part="before-each-summary">
|
|
463
|
+
<svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>
|
|
464
|
+
<div class="before-each-result-icon result-icon${this.state.beforeEachState.resultCategory != "none" ? ` ${this.state.beforeEachState.resultCategory}` : ""}" part="before-each-result-icon"></div>
|
|
465
|
+
<span class="before-each-description description hook-name">Before Each Hook</span>
|
|
466
|
+
</summary>
|
|
467
|
+
<div class="before-each-result result message" part="before-each-result result message">${typeof this.state.beforeEachState.resultContent != "string" ? "" : this.state.beforeEachState.resultContent}</div>
|
|
468
|
+
</details>`}
|
|
469
|
+
${this.state.testState == null ? "" : this.state.beforeEachState == null && this.state.afterEachState == null ? `<div class="test-result result message" part="test-result result message">${resultMessage}</div>` : `<details class="processing-details${this.state.testState.resultCategory == "none" ? "" : ` ${this.state.testState.resultCategory}`}${this.state.testState.isRunning == true ? " running" : ""}" part="processing-details"${this.state.testState.hasRun == true ? " open" : ""}>
|
|
470
|
+
<summary class="processing-summary" part="processing-summary">
|
|
471
|
+
<svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>
|
|
472
|
+
<div class="processing-result-icon result-icon${this.state.testState.resultCategory != "none" ? ` ${this.state.testState.resultCategory}` : ""}" part="processing-result-icon result-icon"></div>
|
|
473
|
+
<span class="processing-description description">Test</span>
|
|
474
|
+
</summary>
|
|
475
|
+
<div class="test-result result message" part="test-result result message">${resultMessage}</div>
|
|
476
|
+
</details>`}
|
|
477
|
+
${this.state.afterEachState == null ? "" : `<details class="after-each-details hook${this.state.afterEachState.resultCategory == "none" ? "" : ` ${this.state.afterEachState.resultCategory}`}${this.state.afterEachState.isRunning == true ? " running" : ""}" part="after-each-detail hooks">
|
|
478
|
+
<summary class="after-each-summary" part="after-each-summary">
|
|
479
|
+
<svg class="icon arrow-icon"><use href="#icon-definition_arrow"></use></svg>
|
|
480
|
+
<div class="after-each-result-icon result-icon${this.state.afterEachState.resultCategory != "none" ? ` ${this.state.afterEachState.resultCategory}` : ""}" part="before-each-result-icon"></div>
|
|
481
|
+
<span class="after-each-description description hook-name">After Each Hook</span>
|
|
482
|
+
</summary>
|
|
483
|
+
<div class="after-each-result result message" part="after-each-result result message">${typeof this.state.afterEachState.resultContent != "string" ? "" : this.state.afterEachState.resultContent}</div>
|
|
484
|
+
</details>`}
|
|
485
|
+
</div>
|
|
486
|
+
</details>`;
|
|
487
|
+
this.dataset.testId = this.state.testId;
|
|
488
|
+
this.classList.add("test");
|
|
489
|
+
this.part.add("test");
|
|
490
|
+
this.toggleAttribute("success", this.resultCategory() == "success");
|
|
491
|
+
this.classList.toggle("success", this.resultCategory() == "success");
|
|
492
|
+
this.part.toggle("success", this.resultCategory() == "success");
|
|
493
|
+
this.classList.toggle("fail", this.resultCategory() == "fail");
|
|
494
|
+
this.part.toggle("fail", this.resultCategory() == "fail");
|
|
495
|
+
this.classList.toggle("running", this.isRunning());
|
|
496
|
+
this.part.toggle("running", this.isRunning());
|
|
497
|
+
if (this.state.beforeEachState?.resultContent instanceof HTMLElement) {
|
|
498
|
+
this.querySelector(".before-each-result").append(this.state.beforeEachState.resultContent);
|
|
499
|
+
}
|
|
500
|
+
if (this.state.testState?.resultContent instanceof HTMLElement) {
|
|
501
|
+
this.querySelector(".test-result").append(this.state.testState.resultContent);
|
|
502
|
+
}
|
|
503
|
+
if (this.state.afterEachState?.resultContent instanceof HTMLElement) {
|
|
504
|
+
this.querySelector(".after-each-result").append(this.state.afterEachState.resultContent);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
enable() {
|
|
508
|
+
this.state.isDisabled = false;
|
|
509
|
+
this.findElement(".run-test-button").toggleAttribute("disabled", false);
|
|
510
|
+
}
|
|
511
|
+
disable() {
|
|
512
|
+
this.state.isDisabled = true;
|
|
513
|
+
this.findElement(".run-test-button").toggleAttribute("disabled", true);
|
|
514
|
+
}
|
|
515
|
+
async runTest(contextManager, testContext) {
|
|
516
|
+
if (this.state.testState?.test == null) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
this.reset();
|
|
520
|
+
let testResult;
|
|
521
|
+
let stateProperties = {};
|
|
522
|
+
let duration = 0;
|
|
523
|
+
try {
|
|
524
|
+
if (contextManager.shouldContinueRunningTests == false) {
|
|
525
|
+
throw new Error("Tests have been disabled from continuing to run.");
|
|
526
|
+
}
|
|
527
|
+
const allowTest = this.dispatchEvent(new CustomEvent(CodeTestEvent.BeforeTest, { bubbles: true, cancelable: true, composed: true, detail: { testElement: this } }));
|
|
528
|
+
if (allowTest == false) {
|
|
529
|
+
throw new Error("Test has been prevented.");
|
|
530
|
+
}
|
|
531
|
+
if (testContext.codeTestsElement.state.isCanceled == true) {
|
|
532
|
+
throw new Error("Testing has been canceled.");
|
|
533
|
+
}
|
|
534
|
+
this.setTestStateProperties("testState", { isRunning: true });
|
|
535
|
+
contextManager.codeTestsElement.setState(contextManager.codeTestsElement.state);
|
|
536
|
+
const startTime = performance?.now() ?? Date.now();
|
|
537
|
+
testResult = await this.state.testState.test(testContext);
|
|
538
|
+
const endTime = performance?.now() ?? Date.now();
|
|
539
|
+
duration = endTime - startTime;
|
|
540
|
+
this.setTestStateProperties("testState", { isRunning: false, hasRun: true });
|
|
541
|
+
contextManager.codeTestsElement.setState(contextManager.codeTestsElement.state);
|
|
542
|
+
const testParsedResult = contextManager.parseTestResult(testResult, true);
|
|
543
|
+
stateProperties = {
|
|
544
|
+
testState: {
|
|
545
|
+
test: this.state.testState.test,
|
|
546
|
+
resultContent: testParsedResult.result,
|
|
547
|
+
resultCategory: testParsedResult.resultCategory,
|
|
548
|
+
hasRun: this.state.testState.hasRun,
|
|
549
|
+
isRunning: false,
|
|
550
|
+
duration
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
} catch (error) {
|
|
554
|
+
const errorParsedResult = contextManager.parseTestResult(testResult, false, error);
|
|
555
|
+
stateProperties = {
|
|
556
|
+
testState: {
|
|
557
|
+
test: this.state.testState.test,
|
|
558
|
+
resultContent: errorParsedResult.result,
|
|
559
|
+
resultCategory: errorParsedResult.resultCategory,
|
|
560
|
+
hasRun: this.state.testState.hasRun,
|
|
561
|
+
isRunning: false,
|
|
562
|
+
duration
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
console.error(error);
|
|
566
|
+
contextManager.shouldContinueRunningTests = false;
|
|
567
|
+
} finally {
|
|
568
|
+
this.setStateProperties({ ...stateProperties });
|
|
569
|
+
contextManager.codeTestsElement.setState(contextManager.codeTestsElement.state);
|
|
570
|
+
this.dispatchEvent(new CustomEvent(CodeTestEvent.AfterTest, { bubbles: true, cancelable: true, composed: true, detail: { testElement: this } }));
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
reset() {
|
|
574
|
+
const testState = this.state.testState != null ? {
|
|
575
|
+
resultCategory: "none",
|
|
576
|
+
resultContent: "",
|
|
577
|
+
test: this.state.testState.test,
|
|
578
|
+
hasRun: this.state.testState.hasRun,
|
|
579
|
+
isRunning: this.state.testState.isRunning,
|
|
580
|
+
duration: 0
|
|
581
|
+
} : void 0;
|
|
582
|
+
if (testState == void 0) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
this.setTestStateProperties("testState", testState);
|
|
586
|
+
}
|
|
587
|
+
getMessageElement() {
|
|
588
|
+
return this.findElement(".test-result");
|
|
589
|
+
}
|
|
590
|
+
static observedAttributes = ["open"];
|
|
591
|
+
attributeChangedCallback(attributeName, _oldValue, newValue) {
|
|
592
|
+
if (attributeName == "open") {
|
|
593
|
+
this.findElement(".test-details").toggleAttribute("open", newValue != void 0);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (customElements.get(COMPONENT_TAG_NAME$1) == null) {
|
|
598
|
+
customElements.define(COMPONENT_TAG_NAME$1, CodeTestElement);
|
|
599
|
+
}
|
|
600
|
+
const NOTESTDEFINED = /* @__PURE__ */ Symbol("No Test Defined");
|
|
601
|
+
class ContextManager {
|
|
602
|
+
codeTestsElement;
|
|
603
|
+
// testContext?: TestContext;
|
|
604
|
+
constructor(parent) {
|
|
605
|
+
this.codeTestsElement = parent;
|
|
606
|
+
}
|
|
607
|
+
//#region Loading
|
|
608
|
+
async loadTests(path) {
|
|
609
|
+
if (path == null) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
try {
|
|
613
|
+
const { tests, hooks } = await this.#loadTests(path);
|
|
614
|
+
const beforeAll = hooks[Hook.BeforeAll];
|
|
615
|
+
const afterAll = hooks[Hook.AfterAll];
|
|
616
|
+
const beforeEach = hooks[Hook.BeforeEach];
|
|
617
|
+
const afterEach = hooks[Hook.AfterEach];
|
|
618
|
+
const requiredBeforeAny = hooks[Hook.RequiredBeforeAny];
|
|
619
|
+
const requiredAfterAny = hooks[Hook.RequiredAfterAny];
|
|
620
|
+
const beforeAllState = beforeAll == null ? void 0 : { resultCategory: "none", resultContent: "", test: beforeAll, hasRun: false, isRunning: false, duration: 0 };
|
|
621
|
+
const afterAllState = afterAll == null ? void 0 : { resultCategory: "none", resultContent: "", test: afterAll, hasRun: false, isRunning: false, duration: 0 };
|
|
622
|
+
const beforeEachState = beforeEach == null ? void 0 : { resultCategory: "none", resultContent: "", test: beforeEach, hasRun: false, isRunning: false, duration: 0 };
|
|
623
|
+
const afterEachState = afterEach == null ? void 0 : { resultCategory: "none", resultContent: "", test: afterEach, hasRun: false, isRunning: false, duration: 0 };
|
|
624
|
+
const requiredBeforeAnyState = requiredBeforeAny == null ? void 0 : { resultCategory: "none", resultContent: "", test: requiredBeforeAny, hasRun: false, isRunning: false, duration: 0 };
|
|
625
|
+
const requiredAfterAnyState = requiredAfterAny == null ? void 0 : { resultCategory: "none", resultContent: "", test: requiredAfterAny, hasRun: false, isRunning: false, duration: 0 };
|
|
626
|
+
this.codeTestsElement.state.beforeEachState = beforeEachState;
|
|
627
|
+
this.codeTestsElement.state.afterEachState = afterEachState;
|
|
628
|
+
for (const [description, test] of Object.entries(tests)) {
|
|
629
|
+
this.#addTest(description, test);
|
|
630
|
+
}
|
|
631
|
+
this.codeTestsElement.setStateProperties({
|
|
632
|
+
beforeAllState,
|
|
633
|
+
afterAllState,
|
|
634
|
+
beforeEachState,
|
|
635
|
+
afterEachState,
|
|
636
|
+
requiredBeforeAnyState,
|
|
637
|
+
requiredAfterAnyState,
|
|
638
|
+
resetHook: hooks.reset,
|
|
639
|
+
contextHook: hooks.context
|
|
640
|
+
});
|
|
641
|
+
} catch (error) {
|
|
642
|
+
console.error(error);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
async #loadTests(path) {
|
|
646
|
+
const module2 = await this.#loadModule(path);
|
|
647
|
+
const tests = module2.tests ?? module2.default;
|
|
648
|
+
if (tests == void 0) {
|
|
649
|
+
throw new Error(`Unable to find tests definition in file at path: ${path}`);
|
|
650
|
+
}
|
|
651
|
+
const hooks = {};
|
|
652
|
+
const beforeAll = tests[Hook.BeforeAll];
|
|
653
|
+
if (beforeAll != null) {
|
|
654
|
+
hooks[Hook.BeforeAll] = beforeAll;
|
|
655
|
+
delete tests[Hook.BeforeAll];
|
|
656
|
+
}
|
|
657
|
+
const afterAll = tests[Hook.AfterAll];
|
|
658
|
+
if (afterAll != null) {
|
|
659
|
+
hooks[Hook.AfterAll] = afterAll;
|
|
660
|
+
delete tests[Hook.AfterAll];
|
|
661
|
+
}
|
|
662
|
+
const beforeEach = tests[Hook.BeforeEach];
|
|
663
|
+
if (beforeEach != null) {
|
|
664
|
+
hooks[Hook.BeforeEach] = beforeEach;
|
|
665
|
+
delete tests[Hook.BeforeEach];
|
|
666
|
+
}
|
|
667
|
+
const afterEach = tests[Hook.AfterEach];
|
|
668
|
+
if (afterEach != null) {
|
|
669
|
+
hooks[Hook.AfterEach] = afterEach;
|
|
670
|
+
delete tests[Hook.AfterEach];
|
|
671
|
+
}
|
|
672
|
+
const requiredBeforeAny = tests[Hook.RequiredBeforeAny];
|
|
673
|
+
if (requiredBeforeAny != null) {
|
|
674
|
+
hooks[Hook.RequiredBeforeAny] = requiredBeforeAny;
|
|
675
|
+
delete tests[Hook.RequiredBeforeAny];
|
|
676
|
+
}
|
|
677
|
+
const requiredAfterAny = tests[Hook.RequiredAfterAny];
|
|
678
|
+
if (requiredAfterAny != null) {
|
|
679
|
+
hooks[Hook.RequiredAfterAny] = requiredAfterAny;
|
|
680
|
+
delete tests[Hook.RequiredAfterAny];
|
|
681
|
+
}
|
|
682
|
+
const resetHook = tests[Hook.Reset];
|
|
683
|
+
if (resetHook != null) {
|
|
684
|
+
hooks[Hook.Reset] = resetHook;
|
|
685
|
+
delete tests[Hook.Reset];
|
|
686
|
+
}
|
|
687
|
+
const contextHook = tests[Hook.Context];
|
|
688
|
+
if (contextHook != null) {
|
|
689
|
+
hooks[Hook.Context] = contextHook;
|
|
690
|
+
delete tests[Hook.Context];
|
|
691
|
+
}
|
|
692
|
+
return { tests, hooks };
|
|
693
|
+
}
|
|
694
|
+
async #loadModule(path) {
|
|
695
|
+
const lastSlashIndexInCurrentPath = window.location.href.lastIndexOf("/");
|
|
696
|
+
const currentPathHasExtension = window.location.href.substring(lastSlashIndexInCurrentPath).indexOf(".") != -1;
|
|
697
|
+
const currentPath = currentPathHasExtension == true ? window.location.href.substring(0, lastSlashIndexInCurrentPath + 1) : window.location.href;
|
|
698
|
+
const moduleDirectory = currentPath + path.substring(0, path.lastIndexOf("/") + 1);
|
|
699
|
+
const modulePath = currentPath + path;
|
|
700
|
+
let moduleContent = await (await fetch(modulePath)).text();
|
|
701
|
+
moduleContent = moduleContent.replaceAll(/['"`](((\.\/)|(\.\.\/))+(.*))['"`]/g, `'${moduleDirectory}$1'`);
|
|
702
|
+
const moduleFile = new File([moduleContent], path.substring(path.lastIndexOf("/")), { type: "text/javascript" });
|
|
703
|
+
const moduleURL = URL.createObjectURL(moduleFile);
|
|
704
|
+
const module2 = await import(
|
|
705
|
+
/* @vite-ignore */
|
|
706
|
+
moduleURL
|
|
707
|
+
);
|
|
708
|
+
return module2;
|
|
709
|
+
}
|
|
710
|
+
#addTest(description, test) {
|
|
711
|
+
const testId = generateId();
|
|
712
|
+
const testElement = new CodeTestElement();
|
|
713
|
+
testElement.setStateProperties({
|
|
714
|
+
testId,
|
|
715
|
+
description,
|
|
716
|
+
testState: { test, resultCategory: "none", resultContent: "", isRunning: false, hasRun: false, duration: 0 },
|
|
717
|
+
beforeEachState: this.codeTestsElement.state.beforeEachState == null ? void 0 : {
|
|
718
|
+
isRunning: this.codeTestsElement.state.beforeEachState.isRunning,
|
|
719
|
+
hasRun: this.codeTestsElement.state.beforeEachState.hasRun,
|
|
720
|
+
resultCategory: this.codeTestsElement.state.beforeEachState.resultCategory,
|
|
721
|
+
resultContent: this.codeTestsElement.state.beforeEachState.resultContent,
|
|
722
|
+
duration: 0
|
|
723
|
+
},
|
|
724
|
+
afterEachState: this.codeTestsElement.state.afterEachState == null ? void 0 : {
|
|
725
|
+
isRunning: this.codeTestsElement.state.afterEachState.isRunning,
|
|
726
|
+
hasRun: this.codeTestsElement.state.afterEachState.hasRun,
|
|
727
|
+
resultCategory: this.codeTestsElement.state.afterEachState.resultCategory,
|
|
728
|
+
resultContent: this.codeTestsElement.state.afterEachState.resultContent,
|
|
729
|
+
duration: 0
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
this.codeTestsElement.findElement("#tests").append(testElement);
|
|
733
|
+
return testId;
|
|
734
|
+
}
|
|
735
|
+
//#endregion Loading
|
|
736
|
+
//#region Running
|
|
737
|
+
shouldContinueRunningTests = true;
|
|
738
|
+
async runTests(tests) {
|
|
739
|
+
const allowTests = this.codeTestsElement.dispatchEvent(new CustomEvent(CodeTestEvent.BeforeAll, { bubbles: true, composed: true, cancelable: true }));
|
|
740
|
+
if (allowTests == false) {
|
|
741
|
+
throw new Error("Tests have been prevented.");
|
|
742
|
+
}
|
|
743
|
+
await this.codeTestsElement.reset();
|
|
744
|
+
for (let i = 0; i < tests.length; i++) {
|
|
745
|
+
tests[i].disable();
|
|
746
|
+
}
|
|
747
|
+
const context = await this.createTestContext();
|
|
748
|
+
await this.runHook("requiredBeforeAnyState", void 0, context);
|
|
749
|
+
if (this.shouldContinueRunningTests == false) {
|
|
750
|
+
await this.#endTests(tests, context);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
await this.runHook("beforeAllState", void 0, context);
|
|
754
|
+
if (this.shouldContinueRunningTests == false) {
|
|
755
|
+
await this.#endTests(tests, context);
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
const inOrder = this.codeTestsElement.getAttribute("ordered") != "false";
|
|
759
|
+
if (inOrder == false) {
|
|
760
|
+
const promises = tests.map((item) => this.runTest(item, true, context));
|
|
761
|
+
await Promise.all(promises);
|
|
762
|
+
} else {
|
|
763
|
+
for (let i = 0; i < tests.length; i++) {
|
|
764
|
+
const test = tests[i];
|
|
765
|
+
if (this.shouldContinueRunningTests == false) {
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
await this.runTest(test, true, context);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (this.shouldContinueRunningTests == false) {
|
|
772
|
+
await this.#endTests(tests, context);
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
await this.runHook("afterAllState", void 0, context);
|
|
776
|
+
await this.#endTests(tests, context);
|
|
777
|
+
}
|
|
778
|
+
async #endTests(tests, context) {
|
|
779
|
+
await this.runHook("requiredAfterAnyState", void 0, context);
|
|
780
|
+
for (let i = 0; i < tests.length; i++) {
|
|
781
|
+
tests[i].enable();
|
|
782
|
+
}
|
|
783
|
+
this.codeTestsElement.dispatchEvent(new CustomEvent(CodeTestEvent.AfterAll, { bubbles: true, composed: true }));
|
|
784
|
+
}
|
|
785
|
+
async runTest(test, inLoop, testContext) {
|
|
786
|
+
if (test == null) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (inLoop == false) {
|
|
790
|
+
const tests = this.codeTestsElement.findElements("code-test");
|
|
791
|
+
for (let i = 0; i < tests.length; i++) {
|
|
792
|
+
tests[i].disable();
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
testContext = testContext ?? await this.createTestContext(test);
|
|
796
|
+
if (inLoop == false) {
|
|
797
|
+
await this.runHook("requiredBeforeAnyState", void 0, testContext);
|
|
798
|
+
if (this.shouldContinueRunningTests == false) {
|
|
799
|
+
await this.#endTest(testContext, inLoop);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
await this.runHook("beforeEachState", test, testContext);
|
|
804
|
+
if (this.shouldContinueRunningTests == false) {
|
|
805
|
+
await this.#endTest(testContext, inLoop);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
await test.runTest(this, testContext);
|
|
809
|
+
if (this.shouldContinueRunningTests == false) {
|
|
810
|
+
await this.#endTest(testContext, inLoop);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
await this.runHook("afterEachState", test, testContext);
|
|
814
|
+
await this.#endTest(testContext, inLoop);
|
|
815
|
+
}
|
|
816
|
+
async #endTest(context, inLoop) {
|
|
817
|
+
if (inLoop == true) {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
await this.runHook("requiredAfterAnyState", void 0, context);
|
|
821
|
+
const tests = this.codeTestsElement.findElements("code-test");
|
|
822
|
+
for (let i = 0; i < tests.length; i++) {
|
|
823
|
+
tests[i].enable();
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
async runHook(testStateName, test, testContext) {
|
|
827
|
+
const testState = this.codeTestsElement.state[testStateName];
|
|
828
|
+
if (testState == null) {
|
|
829
|
+
return NOTESTDEFINED;
|
|
830
|
+
}
|
|
831
|
+
if (this.codeTestsElement.state.isCanceled == true) {
|
|
832
|
+
return { success: false, value: "Testing has been canceled." };
|
|
833
|
+
}
|
|
834
|
+
let hookResult;
|
|
835
|
+
try {
|
|
836
|
+
if (this.shouldContinueRunningTests == false && (testStateName != "requiredAfterAnyState" || this.codeTestsElement.getAttribute("required-after") == "error")) {
|
|
837
|
+
throw new Error("Tests have been disabled from continuing to run.");
|
|
838
|
+
}
|
|
839
|
+
if (test != null) {
|
|
840
|
+
test.setTestStateProperties(testStateName, { isRunning: true });
|
|
841
|
+
}
|
|
842
|
+
this.codeTestsElement.setTestStateProperties(testStateName, { isRunning: true });
|
|
843
|
+
hookResult = await testState.test(testContext);
|
|
844
|
+
if (test != null) {
|
|
845
|
+
test.setTestStateProperties(testStateName, { isRunning: false, hasRun: true });
|
|
846
|
+
}
|
|
847
|
+
this.codeTestsElement.setTestStateProperties(testStateName, { isRunning: false, hasRun: true });
|
|
848
|
+
const hookParsedResult = this.parseTestResult(hookResult, true, void 0);
|
|
849
|
+
if (test != null) {
|
|
850
|
+
test.setTestStateProperties(testStateName, {
|
|
851
|
+
resultCategory: hookParsedResult.resultCategory,
|
|
852
|
+
resultContent: hookParsedResult.result
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
this.codeTestsElement.setTestStateProperties(testStateName, {
|
|
856
|
+
resultCategory: hookParsedResult.resultCategory,
|
|
857
|
+
resultContent: hookParsedResult.result
|
|
858
|
+
});
|
|
859
|
+
} catch (error) {
|
|
860
|
+
console.error(error);
|
|
861
|
+
this.shouldContinueRunningTests = false;
|
|
862
|
+
hookResult = { success: false, value: `Failed: ${error.message}` };
|
|
863
|
+
const errorParsedResult = this.parseTestResult(hookResult, false, error);
|
|
864
|
+
if (test != null) {
|
|
865
|
+
test.setTestStateProperties(
|
|
866
|
+
testStateName,
|
|
867
|
+
{
|
|
868
|
+
isRunning: false,
|
|
869
|
+
hasRun: true,
|
|
870
|
+
resultContent: errorParsedResult.result,
|
|
871
|
+
resultCategory: errorParsedResult.resultCategory
|
|
872
|
+
}
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
this.codeTestsElement.setTestStateProperties(testStateName, {
|
|
876
|
+
isRunning: false,
|
|
877
|
+
hasRun: true,
|
|
878
|
+
resultCategory: errorParsedResult.resultCategory,
|
|
879
|
+
resultContent: errorParsedResult.result
|
|
880
|
+
});
|
|
881
|
+
} finally {
|
|
882
|
+
this.codeTestsElement.dispatchEvent(new CustomEvent(CodeTestEvent.AfterHook, { bubbles: true, composed: true, detail: hookResult }));
|
|
883
|
+
return hookResult;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
//#endregion Running
|
|
887
|
+
//#region Utils
|
|
888
|
+
parseTestResult(result, finishedTest, error) {
|
|
889
|
+
if (result == NOTESTDEFINED) {
|
|
890
|
+
return { result: "", resultCategory: "none" };
|
|
891
|
+
}
|
|
892
|
+
if (result == void 0) {
|
|
893
|
+
const message = finishedTest == true ? "Passed" : `Failed${error != null ? `:
|
|
894
|
+
${error.message}` : ""}`;
|
|
895
|
+
const className = finishedTest == true ? "success-message" : "error-message";
|
|
896
|
+
return {
|
|
897
|
+
result: `<code class="code" part="code">
|
|
898
|
+
<pre class="pre ${className}" part="pre ${className}">${message}</pre>
|
|
899
|
+
</code>`,
|
|
900
|
+
resultCategory: finishedTest == true ? "success" : "fail"
|
|
901
|
+
};
|
|
902
|
+
} else if (typeof result == "boolean") {
|
|
903
|
+
return { result, resultCategory: result == true ? "success" : "fail" };
|
|
904
|
+
} else if (typeof result == "function") {
|
|
905
|
+
console.log("function");
|
|
906
|
+
return { result: `[A function was returned]`, resultCategory: "none" };
|
|
907
|
+
} else if (result instanceof HTMLElement || typeof result == "string") {
|
|
908
|
+
return { result, resultCategory: "none" };
|
|
909
|
+
} else if (typeof result == "object") {
|
|
910
|
+
const objectResult = result;
|
|
911
|
+
const className = finishedTest == true ? "success-message" : "error-message";
|
|
912
|
+
if (objectResult.success != void 0 && objectResult.expected != void 0 && objectResult.value != void 0) {
|
|
913
|
+
return {
|
|
914
|
+
result: `<code class="code" part="code">
|
|
915
|
+
<pre class="pre ${className}" part="pre ${className}">${objectResult.success == true ? "Passed" : "Failed"}
|
|
916
|
+
Expected:${objectResult.expected}
|
|
917
|
+
Result:${objectResult.value}</pre>
|
|
918
|
+
</code>`,
|
|
919
|
+
resultCategory: objectResult.success == true ? "success" : "fail"
|
|
920
|
+
};
|
|
921
|
+
} else if (objectResult.success != void 0) {
|
|
922
|
+
return {
|
|
923
|
+
result: `<code class="code" part="code">
|
|
924
|
+
<pre class="pre ${className}" part="pre ${className}">${JSON.stringify(result, void 0, 2)}</pre>
|
|
925
|
+
</code>`,
|
|
926
|
+
resultCategory: objectResult.success == true ? "success" : "fail"
|
|
927
|
+
};
|
|
928
|
+
} else {
|
|
929
|
+
const className2 = finishedTest == true ? "success-message" : "error-message";
|
|
930
|
+
return {
|
|
931
|
+
result: `<code class="code" part="code">
|
|
932
|
+
<pre class="pre ${className2}" part="pre ${className2}">${JSON.stringify(result, void 0, 2)}</pre>
|
|
933
|
+
</code>`,
|
|
934
|
+
resultCategory: finishedTest == true ? "success" : "fail"
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
throw new Error("Unable to parse result type: Unknown result type");
|
|
939
|
+
}
|
|
940
|
+
async createTestContext(testElement) {
|
|
941
|
+
const context = {
|
|
942
|
+
detail: {},
|
|
943
|
+
codeTestsElement: this.codeTestsElement,
|
|
944
|
+
testElement,
|
|
945
|
+
messageElement: testElement?.findElement(".message")
|
|
946
|
+
};
|
|
947
|
+
if (this.codeTestsElement.state.contextHook != null) {
|
|
948
|
+
await this.codeTestsElement.state.contextHook(context);
|
|
949
|
+
}
|
|
950
|
+
this.codeTestsElement.dispatchEvent(new CustomEvent(CodeTestEvent.Context, { bubbles: true, composed: true, detail: { context } }));
|
|
951
|
+
return context;
|
|
952
|
+
}
|
|
953
|
+
//#endregion Utils
|
|
954
|
+
}
|
|
955
|
+
function generateId() {
|
|
956
|
+
const rnd = new Uint8Array(20);
|
|
957
|
+
crypto.getRandomValues(rnd);
|
|
958
|
+
const b64 = [].slice.apply(rnd).map(function(ch) {
|
|
959
|
+
return String.fromCharCode(ch);
|
|
960
|
+
}).join("");
|
|
961
|
+
const secret = btoa(b64).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
|
|
962
|
+
return secret;
|
|
963
|
+
}
|
|
964
|
+
class TestPromise extends Promise {
|
|
965
|
+
async toBeDefined(valueName) {
|
|
966
|
+
const target = await this;
|
|
967
|
+
if (target == void 0) {
|
|
968
|
+
throw new Error(`${valueName != null ? valueName : "Value"} is undefined`);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
async toBe(value, exact = false) {
|
|
972
|
+
const target = await this;
|
|
973
|
+
const result = exact == true ? target === value : target == value;
|
|
974
|
+
if (result == false) {
|
|
975
|
+
throw new Error(` Value is not equal.
|
|
976
|
+
Expected: ${value}
|
|
977
|
+
Result: ${target}`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
async toContainText(value) {
|
|
981
|
+
const target = await this;
|
|
982
|
+
if (target instanceof HTMLElement) {
|
|
983
|
+
return { success: target.textContent.includes(value), index: target.textContent.indexOf(value) };
|
|
984
|
+
}
|
|
985
|
+
if (typeof target != "string") {
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
return { success: target.includes(value), index: target.indexOf(value) };
|
|
989
|
+
}
|
|
990
|
+
async toHaveAttribute(value) {
|
|
991
|
+
const target = await this;
|
|
992
|
+
if (!(target instanceof HTMLElement)) {
|
|
993
|
+
throw new Error("Unable to check for attribute on non-HTMLElement target");
|
|
994
|
+
}
|
|
995
|
+
if (target.getAttribute(value)) {
|
|
996
|
+
throw new Error("Target does not have attribute");
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
const DEFAULT_ELEMENT_SELECTOR = ":not(slot,defs,g,rect,path,circle,ellipse,line,polygon,text,tspan,use,svg image,svg title,desc,template,template *)";
|
|
1001
|
+
function assignClassAndIdToPart(shadowRoot) {
|
|
1002
|
+
assignIdToPart(shadowRoot);
|
|
1003
|
+
assignClassToPart(shadowRoot);
|
|
1004
|
+
}
|
|
1005
|
+
function assignIdToPart(shadowRoot) {
|
|
1006
|
+
const identifiedElements = [...shadowRoot.querySelectorAll(`${DEFAULT_ELEMENT_SELECTOR}[id]`)];
|
|
1007
|
+
for (let i = 0; i < identifiedElements.length; i++) {
|
|
1008
|
+
identifiedElements[i].part.add(identifiedElements[i].id);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
function assignClassToPart(shadowRoot) {
|
|
1012
|
+
const classedElements = [...shadowRoot.querySelectorAll(`${DEFAULT_ELEMENT_SELECTOR}[class]`)];
|
|
1013
|
+
for (let i = 0; i < classedElements.length; i++) {
|
|
1014
|
+
classedElements[i].part.add(...classedElements[i].classList);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
const Hook = {
|
|
1018
|
+
BeforeAll: "beforeall",
|
|
1019
|
+
AfterAll: "afterall",
|
|
1020
|
+
BeforeEach: "beforeeach",
|
|
1021
|
+
AfterEach: "aftereach",
|
|
1022
|
+
RequiredBeforeAny: "requiredbeforeany",
|
|
1023
|
+
RequiredAfterAny: "requiredafterany",
|
|
1024
|
+
Reset: "reset",
|
|
1025
|
+
Context: "context"
|
|
1026
|
+
};
|
|
1027
|
+
const COMPONENT_STYLESHEET = new CSSStyleSheet();
|
|
1028
|
+
COMPONENT_STYLESHEET.replaceSync(style);
|
|
1029
|
+
const COMPONENT_TAG_NAME = "code-tests";
|
|
1030
|
+
class CodeTestsElement extends HTMLElement {
|
|
1031
|
+
state = {
|
|
1032
|
+
isCanceled: false,
|
|
1033
|
+
beforeAllState: void 0,
|
|
1034
|
+
afterAllState: void 0,
|
|
1035
|
+
beforeEachState: void 0,
|
|
1036
|
+
afterEachState: void 0,
|
|
1037
|
+
requiredBeforeAnyState: void 0,
|
|
1038
|
+
requiredAfterAnyState: void 0,
|
|
1039
|
+
resetHook: void 0,
|
|
1040
|
+
contextHook: void 0
|
|
1041
|
+
};
|
|
1042
|
+
setState(state) {
|
|
1043
|
+
this.state = state;
|
|
1044
|
+
this.#render();
|
|
1045
|
+
}
|
|
1046
|
+
setStateProperties(state) {
|
|
1047
|
+
this.setState({
|
|
1048
|
+
...this.state,
|
|
1049
|
+
...state
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
setTestStateProperties(key, state) {
|
|
1053
|
+
if (this.state[key] == null) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
this.setState({
|
|
1057
|
+
...this.state,
|
|
1058
|
+
[key]: {
|
|
1059
|
+
...this.state[key],
|
|
1060
|
+
...state
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
findElement(query) {
|
|
1065
|
+
return this.shadowRoot.querySelector(query);
|
|
1066
|
+
}
|
|
1067
|
+
findElements(query) {
|
|
1068
|
+
return Array.from(this.shadowRoot.querySelectorAll(query));
|
|
1069
|
+
}
|
|
1070
|
+
getIsRunning() {
|
|
1071
|
+
const testsAreRunning = this.findElements("code-test").find((item) => item.isRunning() == true) != null;
|
|
1072
|
+
return testsAreRunning == true || this.state.requiredBeforeAnyState?.isRunning == true || this.state.requiredAfterAnyState?.isRunning == true || this.state.beforeAllState?.isRunning == true || this.state.afterAllState?.isRunning == true;
|
|
1073
|
+
}
|
|
1074
|
+
getResultCategory() {
|
|
1075
|
+
const testCategory = this.findElements("code-test").reduce((result, item, _index) => {
|
|
1076
|
+
const category = item.resultCategory();
|
|
1077
|
+
if (result == "fail" || category == "fail") {
|
|
1078
|
+
return "fail";
|
|
1079
|
+
}
|
|
1080
|
+
if ((result == "success" || result == "") && category == "success") {
|
|
1081
|
+
return "success";
|
|
1082
|
+
}
|
|
1083
|
+
if (category == "none") {
|
|
1084
|
+
return "none";
|
|
1085
|
+
}
|
|
1086
|
+
return "none";
|
|
1087
|
+
}, "");
|
|
1088
|
+
const states = [
|
|
1089
|
+
this.state.requiredBeforeAnyState,
|
|
1090
|
+
this.state.requiredAfterAnyState,
|
|
1091
|
+
this.state.beforeAllState,
|
|
1092
|
+
this.state.afterAllState
|
|
1093
|
+
];
|
|
1094
|
+
const statesCategory = states.reduce((result, item, _index) => {
|
|
1095
|
+
if (item == null) {
|
|
1096
|
+
return null;
|
|
1097
|
+
}
|
|
1098
|
+
const category = item?.resultCategory;
|
|
1099
|
+
if (result == "fail" || category == "fail") {
|
|
1100
|
+
return "fail";
|
|
1101
|
+
}
|
|
1102
|
+
if ((result == "success" || result == "") && category == "success") {
|
|
1103
|
+
return "success";
|
|
1104
|
+
}
|
|
1105
|
+
if (category == "none") {
|
|
1106
|
+
return "none";
|
|
1107
|
+
}
|
|
1108
|
+
return "none";
|
|
1109
|
+
}, "");
|
|
1110
|
+
if (testCategory == "none" && statesCategory == null) {
|
|
1111
|
+
return "none";
|
|
1112
|
+
} else if (testCategory == "fail" || statesCategory == "fail") {
|
|
1113
|
+
return "fail";
|
|
1114
|
+
} else if (testCategory == "success" && statesCategory == "success") {
|
|
1115
|
+
return "success";
|
|
1116
|
+
}
|
|
1117
|
+
return "none";
|
|
1118
|
+
}
|
|
1119
|
+
#contextManager;
|
|
1120
|
+
constructor() {
|
|
1121
|
+
super();
|
|
1122
|
+
this.attachShadow({ mode: "open" });
|
|
1123
|
+
this.shadowRoot.innerHTML = html;
|
|
1124
|
+
this.shadowRoot.adoptedStyleSheets.push(COMPONENT_STYLESHEET);
|
|
1125
|
+
}
|
|
1126
|
+
connectedCallback() {
|
|
1127
|
+
this.#init();
|
|
1128
|
+
}
|
|
1129
|
+
disconnectedCallback() {
|
|
1130
|
+
this.#destroy();
|
|
1131
|
+
}
|
|
1132
|
+
#isInitialized = false;
|
|
1133
|
+
async #init() {
|
|
1134
|
+
if (this.#isInitialized == true) {
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
assignClassAndIdToPart(this.shadowRoot);
|
|
1138
|
+
this.addEventListener("click", this.#boundClickHandler);
|
|
1139
|
+
await new Promise((resolve) => requestAnimationFrame(() => {
|
|
1140
|
+
this.#contextManager = new ContextManager(this);
|
|
1141
|
+
resolve();
|
|
1142
|
+
}));
|
|
1143
|
+
this.#isInitialized = true;
|
|
1144
|
+
if (this.getAttribute("auto") == "false") {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
this.reloadTests();
|
|
1148
|
+
}
|
|
1149
|
+
#destroy() {
|
|
1150
|
+
this.removeEventListener("click", this.#boundClickHandler);
|
|
1151
|
+
}
|
|
1152
|
+
#boundClickHandler = this.#onClick.bind(this);
|
|
1153
|
+
#onClick(event) {
|
|
1154
|
+
const runAllButton = event.composedPath().find((item) => item instanceof HTMLButtonElement && item.id == "run-all-button");
|
|
1155
|
+
if (runAllButton != null) {
|
|
1156
|
+
if (this.classList.contains("running")) {
|
|
1157
|
+
this.cancel();
|
|
1158
|
+
} else if (this.classList.contains("fail") || this.classList.contains("success")) {
|
|
1159
|
+
this.reset();
|
|
1160
|
+
} else {
|
|
1161
|
+
this.runTests();
|
|
1162
|
+
}
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
const reloadButton = event.composedPath().find((item) => item instanceof HTMLButtonElement && item.id == "reload-button");
|
|
1166
|
+
if (reloadButton != null) {
|
|
1167
|
+
this.reloadTests();
|
|
1168
|
+
}
|
|
1169
|
+
const runButton = event.composedPath().find((item) => item instanceof HTMLButtonElement && item.classList.contains("run-test-button"));
|
|
1170
|
+
if (runButton == null) {
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
const test = runButton.closest("code-test") ?? void 0;
|
|
1174
|
+
this.#contextManager.runTest(test, false);
|
|
1175
|
+
}
|
|
1176
|
+
#getCurrentTestsPath() {
|
|
1177
|
+
return this.getAttribute("src") ?? this.getAttribute("test") ?? this.getAttribute("tests") ?? this.getAttribute("run") ?? this.getAttribute("path") ?? void 0;
|
|
1178
|
+
}
|
|
1179
|
+
#render() {
|
|
1180
|
+
const isRunning = this.getIsRunning();
|
|
1181
|
+
const resultCategory = this.getResultCategory();
|
|
1182
|
+
const hasBeforeAllState = this.state.beforeAllState != null;
|
|
1183
|
+
const hasBeforeEachState = this.state.beforeEachState != null;
|
|
1184
|
+
const hasRequiredBeforeAnyState = this.state.requiredAfterAnyState != null;
|
|
1185
|
+
const hasAfterAllState = this.state.beforeAllState != null;
|
|
1186
|
+
const hasAfterEachState = this.state.beforeEachState != null;
|
|
1187
|
+
const hasRequiredAfterAnyState = this.state.requiredAfterAnyState != null;
|
|
1188
|
+
const hasBeforeHook = hasRequiredBeforeAnyState == true || hasBeforeAllState == true || hasBeforeEachState == true;
|
|
1189
|
+
const hasAfterHook = hasRequiredAfterAnyState == true || hasAfterAllState == true || hasAfterEachState == true;
|
|
1190
|
+
this.classList.toggle("canceled", this.state.isCanceled);
|
|
1191
|
+
this.part.toggle("canceled", this.state.isCanceled);
|
|
1192
|
+
this.classList.toggle("running", isRunning == true);
|
|
1193
|
+
this.part.toggle("running", isRunning == true);
|
|
1194
|
+
this.toggleAttribute("success", resultCategory == "success");
|
|
1195
|
+
this.classList.toggle("success", resultCategory == "success");
|
|
1196
|
+
this.part.toggle("success", resultCategory == "success");
|
|
1197
|
+
this.classList.toggle("fail", resultCategory == "fail");
|
|
1198
|
+
this.part.toggle("fail", resultCategory == "fail");
|
|
1199
|
+
this.classList.toggle("has-before-hook", hasBeforeHook);
|
|
1200
|
+
this.part.toggle("has-before-hook", hasBeforeHook);
|
|
1201
|
+
this.classList.toggle("has-after-hook", hasAfterHook);
|
|
1202
|
+
this.part.toggle("has-after-hook", hasAfterHook);
|
|
1203
|
+
this.classList.toggle("has-before-all-hook", hasBeforeAllState);
|
|
1204
|
+
this.part.toggle("has-before-all-hook", hasBeforeAllState);
|
|
1205
|
+
this.classList.toggle("has-after-all-hook", hasAfterAllState);
|
|
1206
|
+
this.part.toggle("has-after-all-hook", hasAfterAllState);
|
|
1207
|
+
this.classList.toggle("has-before-each-hook", hasBeforeEachState);
|
|
1208
|
+
this.part.toggle("has-before-each-hook", hasBeforeEachState);
|
|
1209
|
+
this.classList.toggle("has-after-each-hook", hasAfterEachState);
|
|
1210
|
+
this.part.toggle("has-after-each-hook", hasAfterEachState);
|
|
1211
|
+
this.classList.toggle("has-required-before-hook", hasRequiredBeforeAnyState);
|
|
1212
|
+
this.part.toggle("has-required-before-hook", hasRequiredBeforeAnyState);
|
|
1213
|
+
this.classList.toggle("has-required-after-hook", hasRequiredAfterAnyState);
|
|
1214
|
+
this.part.toggle("has-required-after-hook", hasRequiredAfterAnyState);
|
|
1215
|
+
const runAllButtonLabel = this.findElement(".run-all-button-label");
|
|
1216
|
+
if (runAllButtonLabel != null) {
|
|
1217
|
+
runAllButtonLabel.textContent = isRunning == true ? "Cancel" : resultCategory == "fail" ? "Reset" : "Run Tests";
|
|
1218
|
+
runAllButtonLabel.title = isRunning == true ? "Cancel the testing" : resultCategory == "fail" ? "Reset the tests so they can be run again" : "Run the tests";
|
|
1219
|
+
}
|
|
1220
|
+
const runAllIcon = this.findElement(".run-all-button-icon");
|
|
1221
|
+
if (runAllIcon != null) {
|
|
1222
|
+
runAllIcon.innerHTML = isRunning == true ? '<use href="#icon-definition_cancel"></use>' : resultCategory == "fail" ? '<use href="#icon-definition_reset"></use>' : '<use href="#icon-definition_arrow"></use>';
|
|
1223
|
+
}
|
|
1224
|
+
this.#renderHook(this.state.beforeAllState, "#before-all-results");
|
|
1225
|
+
this.#renderHook(this.state.afterAllState, "#after-all-results");
|
|
1226
|
+
this.#renderHook(this.state.requiredBeforeAnyState, "#required-before-any-results");
|
|
1227
|
+
this.#renderHook(this.state.requiredAfterAnyState, "#required-after-any-results");
|
|
1228
|
+
const title = this.findElement("#title");
|
|
1229
|
+
if (title != null) {
|
|
1230
|
+
title.textContent = this.getAttribute("label") ?? "Tests";
|
|
1231
|
+
}
|
|
1232
|
+
this.#renderGroupResults();
|
|
1233
|
+
}
|
|
1234
|
+
#renderHook(hookState, elementSelector) {
|
|
1235
|
+
const resultsElement = this.findElement(elementSelector);
|
|
1236
|
+
if (hookState?.resultContent instanceof HTMLElement) {
|
|
1237
|
+
resultsElement.append(hookState.resultContent);
|
|
1238
|
+
} else if (typeof hookState?.resultContent == "string") {
|
|
1239
|
+
resultsElement.innerHTML = hookState.resultContent;
|
|
1240
|
+
}
|
|
1241
|
+
const detailsElement = resultsElement.closest("details");
|
|
1242
|
+
detailsElement.toggleAttribute("open", hookState != void 0 && hookState.resultCategory != "none");
|
|
1243
|
+
detailsElement.classList.toggle("running", hookState?.isRunning == true);
|
|
1244
|
+
detailsElement.part.toggle("running", hookState?.isRunning == true);
|
|
1245
|
+
detailsElement.toggleAttribute("success", hookState?.resultCategory == "success");
|
|
1246
|
+
detailsElement.classList.toggle("success", hookState?.resultCategory == "success");
|
|
1247
|
+
detailsElement.part.toggle("success", hookState?.resultCategory == "success");
|
|
1248
|
+
detailsElement.classList.toggle("fail", hookState?.resultCategory == "fail");
|
|
1249
|
+
detailsElement.part.toggle("fail", hookState?.resultCategory == "fail");
|
|
1250
|
+
}
|
|
1251
|
+
#renderGroupResults() {
|
|
1252
|
+
const results = this.collectTestResults();
|
|
1253
|
+
const progress = this.findElement("#results-progress-value");
|
|
1254
|
+
if (progress != null) {
|
|
1255
|
+
progress.max = results.totalTests;
|
|
1256
|
+
progress.value = results.totalPassed;
|
|
1257
|
+
}
|
|
1258
|
+
const totalPassedElement = this.findElement("#total-tests-passed-value");
|
|
1259
|
+
if (totalPassedElement != null) {
|
|
1260
|
+
totalPassedElement.textContent = results.totalPassed.toString();
|
|
1261
|
+
}
|
|
1262
|
+
const totalTestsElement = this.findElement("#total-tests-count-value");
|
|
1263
|
+
if (totalTestsElement != null) {
|
|
1264
|
+
totalTestsElement.textContent = results.totalTests.toString();
|
|
1265
|
+
}
|
|
1266
|
+
const totalPercentElement = this.findElement("#passed-total-percent-value");
|
|
1267
|
+
if (totalPercentElement != null) {
|
|
1268
|
+
totalPercentElement.textContent = results.totalPercentage.toFixed(1);
|
|
1269
|
+
}
|
|
1270
|
+
const durationElement = this.findElement("#duration-value");
|
|
1271
|
+
if (durationElement != null) {
|
|
1272
|
+
durationElement.textContent = results.duration > 10 ? results.duration.toFixed(0) : results.duration.toFixed(2);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
collectTestResults() {
|
|
1276
|
+
const tests = this.findElements("code-test");
|
|
1277
|
+
const totalTests = tests.length;
|
|
1278
|
+
const totalPassed = tests.filter((item) => item.state.testState?.resultCategory == "success").length;
|
|
1279
|
+
const totalPercentage = totalTests == 0 ? 0 : totalPassed / totalTests * 100;
|
|
1280
|
+
const duration = tests.reduce((result, item, _index) => {
|
|
1281
|
+
return result + (item.state.testState?.duration ?? 0);
|
|
1282
|
+
}, 0);
|
|
1283
|
+
const results = {
|
|
1284
|
+
totalTests,
|
|
1285
|
+
totalPassed,
|
|
1286
|
+
totalPercentage,
|
|
1287
|
+
duration
|
|
1288
|
+
};
|
|
1289
|
+
return results;
|
|
1290
|
+
}
|
|
1291
|
+
async runTests() {
|
|
1292
|
+
const tests = this.findElements("code-test");
|
|
1293
|
+
return this.#contextManager.runTests(tests);
|
|
1294
|
+
}
|
|
1295
|
+
async reloadTests() {
|
|
1296
|
+
this.findElement("#tests").innerHTML = "";
|
|
1297
|
+
await this.reset();
|
|
1298
|
+
const testsPath = this.#getCurrentTestsPath();
|
|
1299
|
+
this.#contextManager.loadTests(testsPath);
|
|
1300
|
+
}
|
|
1301
|
+
async reset() {
|
|
1302
|
+
const tests = this.findElements("code-test");
|
|
1303
|
+
for (let i = 0; i < tests.length; i++) {
|
|
1304
|
+
const test = tests[i];
|
|
1305
|
+
const beforeEachState2 = this.state.beforeEachState != null ? {
|
|
1306
|
+
resultCategory: "none",
|
|
1307
|
+
resultContent: "",
|
|
1308
|
+
hasRun: this.state.beforeEachState.hasRun,
|
|
1309
|
+
isRunning: this.state.beforeEachState.isRunning,
|
|
1310
|
+
duration: 0
|
|
1311
|
+
} : void 0;
|
|
1312
|
+
const afterEachState2 = this.state.afterEachState != null ? {
|
|
1313
|
+
resultCategory: "none",
|
|
1314
|
+
resultContent: "",
|
|
1315
|
+
hasRun: this.state.afterEachState.hasRun,
|
|
1316
|
+
isRunning: this.state.afterEachState.isRunning,
|
|
1317
|
+
duration: 0
|
|
1318
|
+
} : void 0;
|
|
1319
|
+
if (beforeEachState2 != null) {
|
|
1320
|
+
test.state.beforeEachState = beforeEachState2;
|
|
1321
|
+
}
|
|
1322
|
+
if (afterEachState2 != null) {
|
|
1323
|
+
test.state.afterEachState = afterEachState2;
|
|
1324
|
+
}
|
|
1325
|
+
test.reset();
|
|
1326
|
+
}
|
|
1327
|
+
const beforeAllState = this.state.beforeAllState == void 0 ? void 0 : { resultContent: "", resultCategory: "none", test: this.state.beforeAllState.test, isRunning: false, hasRun: false, duration: 0 };
|
|
1328
|
+
const afterAllState = this.state.afterAllState == void 0 ? void 0 : { resultContent: "", resultCategory: "none", test: this.state.afterAllState.test, isRunning: false, hasRun: false, duration: 0 };
|
|
1329
|
+
const beforeEachState = this.state.beforeEachState == void 0 ? void 0 : { resultContent: "", resultCategory: "none", test: this.state.beforeEachState.test, isRunning: false, hasRun: false, duration: 0 };
|
|
1330
|
+
const afterEachState = this.state.afterEachState == void 0 ? void 0 : { resultContent: "", resultCategory: "none", test: this.state.afterEachState.test, isRunning: false, hasRun: false, duration: 0 };
|
|
1331
|
+
const requiredBeforeAnyState = this.state.requiredBeforeAnyState == void 0 ? void 0 : { resultContent: "", resultCategory: "none", test: this.state.requiredBeforeAnyState.test, isRunning: false, hasRun: false, duration: 0 };
|
|
1332
|
+
const requiredAfterAnyState = this.state.requiredAfterAnyState == void 0 ? void 0 : { resultContent: "", resultCategory: "none", test: this.state.requiredAfterAnyState.test, isRunning: false, hasRun: false, duration: 0 };
|
|
1333
|
+
this.setStateProperties({
|
|
1334
|
+
isCanceled: false,
|
|
1335
|
+
beforeAllState,
|
|
1336
|
+
afterAllState,
|
|
1337
|
+
beforeEachState,
|
|
1338
|
+
afterEachState,
|
|
1339
|
+
requiredAfterAnyState,
|
|
1340
|
+
requiredBeforeAnyState
|
|
1341
|
+
});
|
|
1342
|
+
this.#contextManager.shouldContinueRunningTests = true;
|
|
1343
|
+
if (this.state.resetHook != null) {
|
|
1344
|
+
await this.state.resetHook(await this.#contextManager.createTestContext());
|
|
1345
|
+
}
|
|
1346
|
+
this.dispatchEvent(new CustomEvent(CodeTestEvent.Reset, { bubbles: true, composed: true }));
|
|
1347
|
+
}
|
|
1348
|
+
cancel() {
|
|
1349
|
+
this.state.isCanceled = true;
|
|
1350
|
+
this.#contextManager.shouldContinueRunningTests = false;
|
|
1351
|
+
this.classList.add("canceled");
|
|
1352
|
+
this.part.add("canceled");
|
|
1353
|
+
this.dispatchEvent(new CustomEvent(CodeTestEvent.Cancel, { bubbles: true, composed: true }));
|
|
1354
|
+
}
|
|
1355
|
+
static observedAttributes = ["open", "label"];
|
|
1356
|
+
attributeChangedCallback(attributeName, _oldValue, newValue) {
|
|
1357
|
+
if (attributeName == "open") {
|
|
1358
|
+
this.findElement("#component-details").toggleAttribute("open", newValue != void 0);
|
|
1359
|
+
} else if (attributeName == "label") {
|
|
1360
|
+
const title = this.findElement("#title");
|
|
1361
|
+
if (title != null) {
|
|
1362
|
+
title.textContent = newValue ?? "Tests";
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
if (customElements.get(COMPONENT_TAG_NAME) == null) {
|
|
1368
|
+
customElements.define(COMPONENT_TAG_NAME, CodeTestsElement);
|
|
1369
|
+
}
|
|
1370
|
+
function expect(value) {
|
|
1371
|
+
const promise = new TestPromise(async (resolve, _reject) => {
|
|
1372
|
+
if (value instanceof Promise) {
|
|
1373
|
+
const result = await value;
|
|
1374
|
+
resolve(result);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
resolve(value);
|
|
1378
|
+
});
|
|
1379
|
+
return promise;
|
|
1380
|
+
}
|
|
1381
|
+
async function prompt(host, parentElement, message, options) {
|
|
1382
|
+
return new Promise((resolve, _reject) => {
|
|
1383
|
+
const template = options?.template ?? host.querySelector(".prompt-template") ?? host.findElement("#prompt-template");
|
|
1384
|
+
const promptElement = createElementFromTemplate(template);
|
|
1385
|
+
promptElement.querySelector(".label").textContent = message;
|
|
1386
|
+
const clickHandler = (event) => {
|
|
1387
|
+
const composedPath = event.composedPath();
|
|
1388
|
+
const acceptButton = composedPath.find((item) => item instanceof HTMLButtonElement && item.classList.contains("accept"));
|
|
1389
|
+
if (acceptButton != null) {
|
|
1390
|
+
const result = options?.onAccept?.() ?? true;
|
|
1391
|
+
promptElement.removeEventListener("click", clickHandler);
|
|
1392
|
+
resolve(result);
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
const rejectButton = composedPath.find((item) => item instanceof HTMLButtonElement && item.classList.contains("reject"));
|
|
1396
|
+
if (rejectButton != null) {
|
|
1397
|
+
const result = options?.onReject?.() ?? false;
|
|
1398
|
+
promptElement.removeEventListener("click", clickHandler);
|
|
1399
|
+
resolve(result);
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
promptElement.addEventListener("click", clickHandler);
|
|
1404
|
+
if (options?.acceptLabel != null) {
|
|
1405
|
+
promptElement.querySelector(".accept").textContent = options.acceptLabel;
|
|
1406
|
+
}
|
|
1407
|
+
if (options?.rejectLabel != null) {
|
|
1408
|
+
promptElement.querySelector(".reject").textContent = options.rejectLabel;
|
|
1409
|
+
}
|
|
1410
|
+
parentElement.append(promptElement);
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
function createElementFromTemplate(target, parent) {
|
|
1414
|
+
const templateNode = target instanceof HTMLTemplateElement ? target : document.querySelector(target);
|
|
1415
|
+
if (templateNode == null) {
|
|
1416
|
+
throw new Error(`Unable to find template element from selector: ${target}`);
|
|
1417
|
+
}
|
|
1418
|
+
const firstChild = templateNode.content.cloneNode(true).firstElementChild;
|
|
1419
|
+
if (firstChild == null) {
|
|
1420
|
+
throw new Error(`Unable to find first child of template element`);
|
|
1421
|
+
}
|
|
1422
|
+
parent?.append(firstChild);
|
|
1423
|
+
return firstChild;
|
|
1424
|
+
}
|
|
1425
|
+
exports2.CodeTestElement = CodeTestElement;
|
|
1426
|
+
exports2.CodeTestEvent = CodeTestEvent;
|
|
1427
|
+
exports2.CodeTestsElement = CodeTestsElement;
|
|
1428
|
+
exports2.Hook = Hook;
|
|
1429
|
+
exports2.TestPromise = TestPromise;
|
|
1430
|
+
exports2.createElementFromTemplate = createElementFromTemplate;
|
|
1431
|
+
exports2.expect = expect;
|
|
1432
|
+
exports2.prompt = prompt;
|
|
1433
|
+
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
1434
|
+
}));
|