kempo-testing-framework 1.2.4 → 1.3.3
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/.github/copilot-instructions.md +105 -105
- package/.github/workflows/publish-npm.yml +29 -0
- package/CONTRIBUTING.md +107 -107
- package/gui/components/Collapsible.js +54 -54
- package/gui/components/Icon.js +151 -151
- package/gui/components/Logs.js +73 -73
- package/gui/components/SettingCheckbox.js +42 -42
- package/gui/components/SettingNumber.js +77 -77
- package/gui/components/SettingSelect.js +67 -67
- package/gui/components/TestFramework.js +378 -378
- package/gui/components/TestSuite.js +181 -181
- package/gui/components/TestSummary.js +189 -189
- package/gui/components/Theme.js +40 -40
- package/gui/components/settingsStore.js +46 -46
- package/gui/icons/fail.svg +1 -1
- package/gui/icons/pass.svg +1 -1
- package/gui/icons/running.svg +1 -1
- package/gui/icons/settings.svg +1 -1
- package/package.json +6 -1
- package/src/browserTestServer.js +115 -115
- package/src/cli.js +198 -198
- package/src/gui.js +15 -2
- package/src/runBrowserTests.js +87 -87
- package/src/runTestFiles.js +94 -94
- package/src/runTests.js +83 -83
- package/src/utils/logLevels.js +7 -7
- package/test.html +30 -30
- package/tests/Counter.js +33 -33
- package/tests/cli-flags.node-test.js +54 -54
- package/tests/cli-help.node-test.js +61 -61
- package/tests/cli-loglevel.node-test.js +40 -40
- package/tests/collapsible.browser-test.js +49 -49
- package/tests/counter.browser-test.js +140 -140
- package/tests/example.node-test.js +103 -103
- package/tests/fibonacci.js +92 -92
- package/tests/fibonacci.test.js +285 -285
- package/tests/icon.browser-test.js +54 -54
- package/tests/logs.browser-test.js +47 -47
- package/tests/setting-checkbox.browser-test.js +48 -48
- package/tests/setting-number.browser-test.js +54 -54
- package/tests/setting-select.browser-test.js +47 -47
- package/tests/settings-store.browser-test.js +26 -26
- package/tests/src-browserTestServer.node-test.js +47 -47
- package/tests/src-cli.node-test.js +32 -32
- package/tests/src-findTests.node-test.js +41 -41
- package/tests/src-logLevels.node-test.js +29 -29
- package/tests/src-runBrowserTests.node-test.js +41 -41
- package/tests/src-runTestFiles.node-test.js +42 -42
- package/tests/src-runTests.node-test.js +56 -56
- package/tests/test-framework.browser-test.js +65 -65
- package/tests/test-summary.browser-test.js +78 -78
- package/tests/test.browser-test.js +56 -56
- package/tests/testfile.browser-test.js +60 -60
- package/tests/theme.browser-test.js +38 -38
|
@@ -1,189 +1,189 @@
|
|
|
1
|
-
import { LitElement, html, css } from '../lit-all.min.js';
|
|
2
|
-
import './Collapsible.js';
|
|
3
|
-
import './Icon.js';
|
|
4
|
-
|
|
5
|
-
window.customElements.define('ktf-test-summary', class extends LitElement {
|
|
6
|
-
/*
|
|
7
|
-
Properties
|
|
8
|
-
*/
|
|
9
|
-
static properties = {
|
|
10
|
-
status: { type: String, reflect: true },
|
|
11
|
-
fileCounts: { state: true },
|
|
12
|
-
testCounts: { state: true },
|
|
13
|
-
queueLength: { state: true }
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
constructor(){
|
|
17
|
-
super();
|
|
18
|
-
this.status = 'notran';
|
|
19
|
-
this.testsMap = new Map();
|
|
20
|
-
this.fileCounts = { total: 0, pass: 0, fail: 0, running: 0, notran: 0 };
|
|
21
|
-
this.testCounts = { total: 0, pass: 0, fail: 0, running: 0, notran: 0 };
|
|
22
|
-
this.queueLength = 0;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/*
|
|
26
|
-
Lifecycle Callbacks
|
|
27
|
-
*/
|
|
28
|
-
connectedCallback(){
|
|
29
|
-
super.connectedCallback();
|
|
30
|
-
const fw = this.getFrameworkEl();
|
|
31
|
-
(fw||window).addEventListener('testfile_status_change', this.onFileStatusChange);
|
|
32
|
-
(fw||window).addEventListener('test_status_change', this.onTestStatusChange);
|
|
33
|
-
if(fw) fw.addEventListener('ktf:queue-updated', this.onQueueUpdated);
|
|
34
|
-
}
|
|
35
|
-
disconnectedCallback(){
|
|
36
|
-
super.disconnectedCallback();
|
|
37
|
-
const fw = this.getFrameworkEl();
|
|
38
|
-
(fw||window).removeEventListener('testfile_status_change', this.onFileStatusChange);
|
|
39
|
-
(fw||window).removeEventListener('test_status_change', this.onTestStatusChange);
|
|
40
|
-
if(fw) fw.removeEventListener('ktf:queue-updated', this.onQueueUpdated);
|
|
41
|
-
}
|
|
42
|
-
firstUpdated(){
|
|
43
|
-
this.initializeFromChildren();
|
|
44
|
-
const fw = this.getFrameworkEl();
|
|
45
|
-
if(fw){ this.onQueueUpdated({ detail: { length: fw.queue?.length || 0 } }); }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/*
|
|
49
|
-
Event Handlers
|
|
50
|
-
*/
|
|
51
|
-
onSlotChange = () => { this.initializeFromChildren(); };
|
|
52
|
-
onFileStatusChange = () => { this.recountFiles(); this.updateSuiteStatus(); };
|
|
53
|
-
onTestStatusChange = (e) => {
|
|
54
|
-
const d = e?.detail || {};
|
|
55
|
-
if(d && d.file && d.name && d.status){
|
|
56
|
-
const key = `${d.file}::${d.name}`;
|
|
57
|
-
this.testsMap.set(key, d.status);
|
|
58
|
-
this.recountTests();
|
|
59
|
-
this.updateSuiteStatus();
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
onQueueUpdated = (e) => { this.queueLength = Number(e?.detail?.length||0); };
|
|
63
|
-
getFrameworkEl(){ return this.closest('ktf-test-framework'); }
|
|
64
|
-
runAllSuites = () => {
|
|
65
|
-
const fw = this.getFrameworkEl();
|
|
66
|
-
if(fw && typeof fw.runAllSuites==='function') fw.runAllSuites();
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/*
|
|
70
|
-
Utility Methods
|
|
71
|
-
*/
|
|
72
|
-
initializeFromChildren(){
|
|
73
|
-
const root = this.getFrameworkEl() || document;
|
|
74
|
-
const fileEls = Array.from(root.querySelectorAll('ktf-test-suite'));
|
|
75
|
-
for(const el of fileEls){
|
|
76
|
-
const file = el.file || el.getAttribute('file') || '';
|
|
77
|
-
const names = Array.isArray(el.testNames) ? el.testNames : [];
|
|
78
|
-
for(const name of names){
|
|
79
|
-
const key = `${file}::${name}`;
|
|
80
|
-
if(!this.testsMap.has(key)) this.testsMap.set(key, 'notran');
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
this.recountFiles();
|
|
84
|
-
this.recountTests();
|
|
85
|
-
this.updateSuiteStatus();
|
|
86
|
-
}
|
|
87
|
-
recountFiles(){
|
|
88
|
-
const root = this.getFrameworkEl() || document;
|
|
89
|
-
const fileEls = Array.from(root.querySelectorAll('ktf-test-suite'));
|
|
90
|
-
const counts = { total: fileEls.length, pass: 0, fail: 0, running: 0, notran: 0 };
|
|
91
|
-
for(const el of fileEls){
|
|
92
|
-
const s = el.getAttribute('status') || 'notran';
|
|
93
|
-
if(s==='pass') counts.pass++;
|
|
94
|
-
else if(s==='fail') counts.fail++;
|
|
95
|
-
else if(s==='running') counts.running++;
|
|
96
|
-
else counts.notran++;
|
|
97
|
-
}
|
|
98
|
-
this.fileCounts = counts;
|
|
99
|
-
}
|
|
100
|
-
recountTests(){
|
|
101
|
-
const counts = { total: 0, pass: 0, fail: 0, running: 0, notran: 0 };
|
|
102
|
-
for(const s of this.testsMap.values()){
|
|
103
|
-
counts.total++;
|
|
104
|
-
if(s==='pass') counts.pass++;
|
|
105
|
-
else if(s==='fail') counts.fail++;
|
|
106
|
-
else if(s==='running') counts.running++;
|
|
107
|
-
else counts.notran++;
|
|
108
|
-
}
|
|
109
|
-
this.testCounts = counts;
|
|
110
|
-
}
|
|
111
|
-
updateSuiteStatus(){
|
|
112
|
-
const f = this.fileCounts;
|
|
113
|
-
const t = this.testCounts;
|
|
114
|
-
let next = 'notran';
|
|
115
|
-
if(f.running>0 || t.running>0) next = 'running';
|
|
116
|
-
else if(f.fail>0 || t.fail>0) next = 'fail';
|
|
117
|
-
else if((f.total>0 || t.total>0) && f.fail===0 && t.fail===0 && f.running===0 && t.running===0 && f.notran===0 && t.notran===0) next = 'pass';
|
|
118
|
-
else next = 'notran';
|
|
119
|
-
if(this.status!==next) this.status = next;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/*
|
|
123
|
-
Rendering
|
|
124
|
-
*/
|
|
125
|
-
render(){
|
|
126
|
-
const f = this.fileCounts;
|
|
127
|
-
const t = this.testCounts;
|
|
128
|
-
return html`
|
|
129
|
-
<link rel="stylesheet" href="/kempo.css">
|
|
130
|
-
<ktf-collapsible opened>
|
|
131
|
-
<span slot="title" class="-ml"><b>Test Summary</b></span>
|
|
132
|
-
<div slot="actions">
|
|
133
|
-
${this.status==='notran' ? html`
|
|
134
|
-
<button class="no-btn d-ib ph" @click=${this.runAllSuites} aria-label="Run All Tests">
|
|
135
|
-
<ktf-icon name="play"></ktf-icon>
|
|
136
|
-
</button>
|
|
137
|
-
` : html`
|
|
138
|
-
<span class="d-ib ph status-color" aria-hidden="true">
|
|
139
|
-
<ktf-icon name="${this.status}" animation="${this.status==='running'?'spin':'none'}"></ktf-icon>
|
|
140
|
-
</span>
|
|
141
|
-
`}
|
|
142
|
-
</div>
|
|
143
|
-
<div class="summary mb">
|
|
144
|
-
<button class="primary mb" @click=${this.runAllSuites} ?disabled=${this.status==='running'}>Run All Tests</button>
|
|
145
|
-
<div class="counts mt">
|
|
146
|
-
<span class="muted">Queue: ${this.queueLength}</span>
|
|
147
|
-
</div>
|
|
148
|
-
<div class="row">
|
|
149
|
-
<div class="col">
|
|
150
|
-
<h6>Files</h6>
|
|
151
|
-
<div class="counts">
|
|
152
|
-
<span>Total: ${f.total}</span>
|
|
153
|
-
<span class="pass">Pass: ${f.pass}</span>
|
|
154
|
-
<span class="fail">Fail: ${f.fail}</span>
|
|
155
|
-
<span class="running">Running: ${f.running}</span>
|
|
156
|
-
<span class="notran">Not Ran: ${f.notran}</span>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
<div class="col">
|
|
160
|
-
<h6>Tests</h6>
|
|
161
|
-
<div class="counts">
|
|
162
|
-
<span>Total: ${t.total}</span>
|
|
163
|
-
<span class="pass">Pass: ${t.pass}</span>
|
|
164
|
-
<span class="fail">Fail: ${t.fail}</span>
|
|
165
|
-
<span class="running">Running: ${t.running}</span>
|
|
166
|
-
<span class="notran">Not Ran: ${t.notran}</span>
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
<slot></slot>
|
|
171
|
-
</div>
|
|
172
|
-
</ktf-collapsible>
|
|
173
|
-
`;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
static styles = css`
|
|
177
|
-
:host{ --suite_status: var(--tc_default, inherit); }
|
|
178
|
-
:host([status="running"]){ --suite_status: var(--tc_primary,#3366ff); }
|
|
179
|
-
:host([status="pass"]){ --suite_status: var(--tc_success, rgb(0,136,0)); }
|
|
180
|
-
:host([status="fail"]){ --suite_status: var(--tc_danger, rgb(255,0,51)); }
|
|
181
|
-
.summary h6{ color: var(--suite_status); }
|
|
182
|
-
div[slot="actions"]{ font-size: 1.25rem; }
|
|
183
|
-
.counts{ display:flex; gap: var(--spacer_h); flex-wrap: wrap; }
|
|
184
|
-
.counts .pass{ color: var(--tc_success, rgb(0,136,0)); }
|
|
185
|
-
.counts .fail{ color: var(--tc_danger, rgb(255,0,51)); }
|
|
186
|
-
.counts .running{ color: var(--tc_primary,#3366ff); }
|
|
187
|
-
.counts .notran{ color: var(--tc_muted); }
|
|
188
|
-
`;
|
|
189
|
-
});
|
|
1
|
+
import { LitElement, html, css } from '../lit-all.min.js';
|
|
2
|
+
import './Collapsible.js';
|
|
3
|
+
import './Icon.js';
|
|
4
|
+
|
|
5
|
+
window.customElements.define('ktf-test-summary', class extends LitElement {
|
|
6
|
+
/*
|
|
7
|
+
Properties
|
|
8
|
+
*/
|
|
9
|
+
static properties = {
|
|
10
|
+
status: { type: String, reflect: true },
|
|
11
|
+
fileCounts: { state: true },
|
|
12
|
+
testCounts: { state: true },
|
|
13
|
+
queueLength: { state: true }
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
constructor(){
|
|
17
|
+
super();
|
|
18
|
+
this.status = 'notran';
|
|
19
|
+
this.testsMap = new Map();
|
|
20
|
+
this.fileCounts = { total: 0, pass: 0, fail: 0, running: 0, notran: 0 };
|
|
21
|
+
this.testCounts = { total: 0, pass: 0, fail: 0, running: 0, notran: 0 };
|
|
22
|
+
this.queueLength = 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
Lifecycle Callbacks
|
|
27
|
+
*/
|
|
28
|
+
connectedCallback(){
|
|
29
|
+
super.connectedCallback();
|
|
30
|
+
const fw = this.getFrameworkEl();
|
|
31
|
+
(fw||window).addEventListener('testfile_status_change', this.onFileStatusChange);
|
|
32
|
+
(fw||window).addEventListener('test_status_change', this.onTestStatusChange);
|
|
33
|
+
if(fw) fw.addEventListener('ktf:queue-updated', this.onQueueUpdated);
|
|
34
|
+
}
|
|
35
|
+
disconnectedCallback(){
|
|
36
|
+
super.disconnectedCallback();
|
|
37
|
+
const fw = this.getFrameworkEl();
|
|
38
|
+
(fw||window).removeEventListener('testfile_status_change', this.onFileStatusChange);
|
|
39
|
+
(fw||window).removeEventListener('test_status_change', this.onTestStatusChange);
|
|
40
|
+
if(fw) fw.removeEventListener('ktf:queue-updated', this.onQueueUpdated);
|
|
41
|
+
}
|
|
42
|
+
firstUpdated(){
|
|
43
|
+
this.initializeFromChildren();
|
|
44
|
+
const fw = this.getFrameworkEl();
|
|
45
|
+
if(fw){ this.onQueueUpdated({ detail: { length: fw.queue?.length || 0 } }); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
Event Handlers
|
|
50
|
+
*/
|
|
51
|
+
onSlotChange = () => { this.initializeFromChildren(); };
|
|
52
|
+
onFileStatusChange = () => { this.recountFiles(); this.updateSuiteStatus(); };
|
|
53
|
+
onTestStatusChange = (e) => {
|
|
54
|
+
const d = e?.detail || {};
|
|
55
|
+
if(d && d.file && d.name && d.status){
|
|
56
|
+
const key = `${d.file}::${d.name}`;
|
|
57
|
+
this.testsMap.set(key, d.status);
|
|
58
|
+
this.recountTests();
|
|
59
|
+
this.updateSuiteStatus();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
onQueueUpdated = (e) => { this.queueLength = Number(e?.detail?.length||0); };
|
|
63
|
+
getFrameworkEl(){ return this.closest('ktf-test-framework'); }
|
|
64
|
+
runAllSuites = () => {
|
|
65
|
+
const fw = this.getFrameworkEl();
|
|
66
|
+
if(fw && typeof fw.runAllSuites==='function') fw.runAllSuites();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
Utility Methods
|
|
71
|
+
*/
|
|
72
|
+
initializeFromChildren(){
|
|
73
|
+
const root = this.getFrameworkEl() || document;
|
|
74
|
+
const fileEls = Array.from(root.querySelectorAll('ktf-test-suite'));
|
|
75
|
+
for(const el of fileEls){
|
|
76
|
+
const file = el.file || el.getAttribute('file') || '';
|
|
77
|
+
const names = Array.isArray(el.testNames) ? el.testNames : [];
|
|
78
|
+
for(const name of names){
|
|
79
|
+
const key = `${file}::${name}`;
|
|
80
|
+
if(!this.testsMap.has(key)) this.testsMap.set(key, 'notran');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
this.recountFiles();
|
|
84
|
+
this.recountTests();
|
|
85
|
+
this.updateSuiteStatus();
|
|
86
|
+
}
|
|
87
|
+
recountFiles(){
|
|
88
|
+
const root = this.getFrameworkEl() || document;
|
|
89
|
+
const fileEls = Array.from(root.querySelectorAll('ktf-test-suite'));
|
|
90
|
+
const counts = { total: fileEls.length, pass: 0, fail: 0, running: 0, notran: 0 };
|
|
91
|
+
for(const el of fileEls){
|
|
92
|
+
const s = el.getAttribute('status') || 'notran';
|
|
93
|
+
if(s==='pass') counts.pass++;
|
|
94
|
+
else if(s==='fail') counts.fail++;
|
|
95
|
+
else if(s==='running') counts.running++;
|
|
96
|
+
else counts.notran++;
|
|
97
|
+
}
|
|
98
|
+
this.fileCounts = counts;
|
|
99
|
+
}
|
|
100
|
+
recountTests(){
|
|
101
|
+
const counts = { total: 0, pass: 0, fail: 0, running: 0, notran: 0 };
|
|
102
|
+
for(const s of this.testsMap.values()){
|
|
103
|
+
counts.total++;
|
|
104
|
+
if(s==='pass') counts.pass++;
|
|
105
|
+
else if(s==='fail') counts.fail++;
|
|
106
|
+
else if(s==='running') counts.running++;
|
|
107
|
+
else counts.notran++;
|
|
108
|
+
}
|
|
109
|
+
this.testCounts = counts;
|
|
110
|
+
}
|
|
111
|
+
updateSuiteStatus(){
|
|
112
|
+
const f = this.fileCounts;
|
|
113
|
+
const t = this.testCounts;
|
|
114
|
+
let next = 'notran';
|
|
115
|
+
if(f.running>0 || t.running>0) next = 'running';
|
|
116
|
+
else if(f.fail>0 || t.fail>0) next = 'fail';
|
|
117
|
+
else if((f.total>0 || t.total>0) && f.fail===0 && t.fail===0 && f.running===0 && t.running===0 && f.notran===0 && t.notran===0) next = 'pass';
|
|
118
|
+
else next = 'notran';
|
|
119
|
+
if(this.status!==next) this.status = next;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
Rendering
|
|
124
|
+
*/
|
|
125
|
+
render(){
|
|
126
|
+
const f = this.fileCounts;
|
|
127
|
+
const t = this.testCounts;
|
|
128
|
+
return html`
|
|
129
|
+
<link rel="stylesheet" href="/kempo.css">
|
|
130
|
+
<ktf-collapsible opened>
|
|
131
|
+
<span slot="title" class="-ml"><b>Test Summary</b></span>
|
|
132
|
+
<div slot="actions">
|
|
133
|
+
${this.status==='notran' ? html`
|
|
134
|
+
<button class="no-btn d-ib ph" @click=${this.runAllSuites} aria-label="Run All Tests">
|
|
135
|
+
<ktf-icon name="play"></ktf-icon>
|
|
136
|
+
</button>
|
|
137
|
+
` : html`
|
|
138
|
+
<span class="d-ib ph status-color" aria-hidden="true">
|
|
139
|
+
<ktf-icon name="${this.status}" animation="${this.status==='running'?'spin':'none'}"></ktf-icon>
|
|
140
|
+
</span>
|
|
141
|
+
`}
|
|
142
|
+
</div>
|
|
143
|
+
<div class="summary mb">
|
|
144
|
+
<button class="primary mb" @click=${this.runAllSuites} ?disabled=${this.status==='running'}>Run All Tests</button>
|
|
145
|
+
<div class="counts mt">
|
|
146
|
+
<span class="muted">Queue: ${this.queueLength}</span>
|
|
147
|
+
</div>
|
|
148
|
+
<div class="row">
|
|
149
|
+
<div class="col">
|
|
150
|
+
<h6>Files</h6>
|
|
151
|
+
<div class="counts">
|
|
152
|
+
<span>Total: ${f.total}</span>
|
|
153
|
+
<span class="pass">Pass: ${f.pass}</span>
|
|
154
|
+
<span class="fail">Fail: ${f.fail}</span>
|
|
155
|
+
<span class="running">Running: ${f.running}</span>
|
|
156
|
+
<span class="notran">Not Ran: ${f.notran}</span>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="col">
|
|
160
|
+
<h6>Tests</h6>
|
|
161
|
+
<div class="counts">
|
|
162
|
+
<span>Total: ${t.total}</span>
|
|
163
|
+
<span class="pass">Pass: ${t.pass}</span>
|
|
164
|
+
<span class="fail">Fail: ${t.fail}</span>
|
|
165
|
+
<span class="running">Running: ${t.running}</span>
|
|
166
|
+
<span class="notran">Not Ran: ${t.notran}</span>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
<slot></slot>
|
|
171
|
+
</div>
|
|
172
|
+
</ktf-collapsible>
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static styles = css`
|
|
177
|
+
:host{ --suite_status: var(--tc_default, inherit); }
|
|
178
|
+
:host([status="running"]){ --suite_status: var(--tc_primary,#3366ff); }
|
|
179
|
+
:host([status="pass"]){ --suite_status: var(--tc_success, rgb(0,136,0)); }
|
|
180
|
+
:host([status="fail"]){ --suite_status: var(--tc_danger, rgb(255,0,51)); }
|
|
181
|
+
.summary h6{ color: var(--suite_status); }
|
|
182
|
+
div[slot="actions"]{ font-size: 1.25rem; }
|
|
183
|
+
.counts{ display:flex; gap: var(--spacer_h); flex-wrap: wrap; }
|
|
184
|
+
.counts .pass{ color: var(--tc_success, rgb(0,136,0)); }
|
|
185
|
+
.counts .fail{ color: var(--tc_danger, rgb(255,0,51)); }
|
|
186
|
+
.counts .running{ color: var(--tc_primary,#3366ff); }
|
|
187
|
+
.counts .notran{ color: var(--tc_muted); }
|
|
188
|
+
`;
|
|
189
|
+
});
|
package/gui/components/Theme.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import { LitElement, html, css } from '../lit-all.min.js';
|
|
2
|
-
import { getSettings, subscribe } from './settingsStore.js';
|
|
3
|
-
|
|
4
|
-
window.customElements.define('ktf-theme', class extends LitElement {
|
|
5
|
-
static properties = {
|
|
6
|
-
theme: { type: String, reflect: true }
|
|
7
|
-
}
|
|
8
|
-
#unsubscribe = null;
|
|
9
|
-
constructor(){
|
|
10
|
-
super();
|
|
11
|
-
this.theme = (getSettings().theme) || 'auto';
|
|
12
|
-
}
|
|
13
|
-
connectedCallback(){
|
|
14
|
-
super.connectedCallback();
|
|
15
|
-
this.applyTheme(this.theme);
|
|
16
|
-
this.#unsubscribe = subscribe(s => {
|
|
17
|
-
if (s.theme !== this.theme) {
|
|
18
|
-
this.theme = s.theme || 'auto';
|
|
19
|
-
this.applyTheme(this.theme);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
disconnectedCallback(){
|
|
24
|
-
super.disconnectedCallback();
|
|
25
|
-
if (this.#unsubscribe) this.#unsubscribe();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
applyTheme(theme){
|
|
29
|
-
document.documentElement.setAttribute('theme', theme || 'auto');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
render(){
|
|
33
|
-
// No UI; control is in Settings accordion. Keep a subtle indicator if needed.
|
|
34
|
-
return html``;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
static styles = css`
|
|
38
|
-
:host { display:none; }
|
|
39
|
-
`;
|
|
40
|
-
});
|
|
1
|
+
import { LitElement, html, css } from '../lit-all.min.js';
|
|
2
|
+
import { getSettings, subscribe } from './settingsStore.js';
|
|
3
|
+
|
|
4
|
+
window.customElements.define('ktf-theme', class extends LitElement {
|
|
5
|
+
static properties = {
|
|
6
|
+
theme: { type: String, reflect: true }
|
|
7
|
+
}
|
|
8
|
+
#unsubscribe = null;
|
|
9
|
+
constructor(){
|
|
10
|
+
super();
|
|
11
|
+
this.theme = (getSettings().theme) || 'auto';
|
|
12
|
+
}
|
|
13
|
+
connectedCallback(){
|
|
14
|
+
super.connectedCallback();
|
|
15
|
+
this.applyTheme(this.theme);
|
|
16
|
+
this.#unsubscribe = subscribe(s => {
|
|
17
|
+
if (s.theme !== this.theme) {
|
|
18
|
+
this.theme = s.theme || 'auto';
|
|
19
|
+
this.applyTheme(this.theme);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
disconnectedCallback(){
|
|
24
|
+
super.disconnectedCallback();
|
|
25
|
+
if (this.#unsubscribe) this.#unsubscribe();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
applyTheme(theme){
|
|
29
|
+
document.documentElement.setAttribute('theme', theme || 'auto');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
render(){
|
|
33
|
+
// No UI; control is in Settings accordion. Keep a subtle indicator if needed.
|
|
34
|
+
return html``;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static styles = css`
|
|
38
|
+
:host { display:none; }
|
|
39
|
+
`;
|
|
40
|
+
});
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
// Simple global settings store with localStorage persistence and event dispatch
|
|
2
|
-
const STORAGE_KEY = 'ktf_settings';
|
|
3
|
-
const DEFAULTS = {
|
|
4
|
-
showBrowser: false,
|
|
5
|
-
logLevel: 3,
|
|
6
|
-
theme: 'auto',
|
|
7
|
-
delayMs: 0,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
let state = (() => {
|
|
11
|
-
try {
|
|
12
|
-
const raw = localStorage.getItem(STORAGE_KEY);
|
|
13
|
-
if (!raw) return { ...DEFAULTS };
|
|
14
|
-
const parsed = JSON.parse(raw);
|
|
15
|
-
return { ...DEFAULTS, ...parsed };
|
|
16
|
-
} catch {
|
|
17
|
-
return { ...DEFAULTS };
|
|
18
|
-
}
|
|
19
|
-
})();
|
|
20
|
-
|
|
21
|
-
const save = () => {
|
|
22
|
-
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch {}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export const getSettings = () => ({ ...state });
|
|
26
|
-
|
|
27
|
-
export const setSettings = (partial) => {
|
|
28
|
-
state = { ...state, ...partial };
|
|
29
|
-
save();
|
|
30
|
-
const evt = new CustomEvent('ktf-settings-change', { detail: { ...state } });
|
|
31
|
-
window.dispatchEvent(evt);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const subscribe = (handler) => {
|
|
35
|
-
const wrapped = (e) => handler(e.detail);
|
|
36
|
-
window.addEventListener('ktf-settings-change', wrapped);
|
|
37
|
-
return () => window.removeEventListener('ktf-settings-change', wrapped);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Expose on window for debugging if needed
|
|
41
|
-
if (!window.KTF_SETTINGS) {
|
|
42
|
-
window.KTF_SETTINGS = {
|
|
43
|
-
get: getSettings,
|
|
44
|
-
set: setSettings,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
1
|
+
// Simple global settings store with localStorage persistence and event dispatch
|
|
2
|
+
const STORAGE_KEY = 'ktf_settings';
|
|
3
|
+
const DEFAULTS = {
|
|
4
|
+
showBrowser: false,
|
|
5
|
+
logLevel: 3,
|
|
6
|
+
theme: 'auto',
|
|
7
|
+
delayMs: 0,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let state = (() => {
|
|
11
|
+
try {
|
|
12
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
13
|
+
if (!raw) return { ...DEFAULTS };
|
|
14
|
+
const parsed = JSON.parse(raw);
|
|
15
|
+
return { ...DEFAULTS, ...parsed };
|
|
16
|
+
} catch {
|
|
17
|
+
return { ...DEFAULTS };
|
|
18
|
+
}
|
|
19
|
+
})();
|
|
20
|
+
|
|
21
|
+
const save = () => {
|
|
22
|
+
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch {}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const getSettings = () => ({ ...state });
|
|
26
|
+
|
|
27
|
+
export const setSettings = (partial) => {
|
|
28
|
+
state = { ...state, ...partial };
|
|
29
|
+
save();
|
|
30
|
+
const evt = new CustomEvent('ktf-settings-change', { detail: { ...state } });
|
|
31
|
+
window.dispatchEvent(evt);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const subscribe = (handler) => {
|
|
35
|
+
const wrapped = (e) => handler(e.detail);
|
|
36
|
+
window.addEventListener('ktf-settings-change', wrapped);
|
|
37
|
+
return () => window.removeEventListener('ktf-settings-change', wrapped);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Expose on window for debugging if needed
|
|
41
|
+
if (!window.KTF_SETTINGS) {
|
|
42
|
+
window.KTF_SETTINGS = {
|
|
43
|
+
get: getSettings,
|
|
44
|
+
set: setSettings,
|
|
45
|
+
};
|
|
46
|
+
}
|
package/gui/icons/fail.svg
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="m336-280 144-144 144 144 56-56-144-144 144-144-56-56-144 144-144-144-56 56 144 144-144 144 56 56ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="m336-280 144-144 144 144 56-56-144-144 144-144-56-56-144 144-144-144-56 56 144 144-144 144 56 56ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
package/gui/icons/pass.svg
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="m424-296 282-282-56-56-226 226-114-114-56 56 170 170Zm56 216q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="m424-296 282-282-56-56-226 226-114-114-56 56 170 170Zm56 216q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
package/gui/icons/running.svg
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-155.5t86-127Q252-817 325-848.5T480-880q17 0 28.5 11.5T520-840q0 17-11.5 28.5T480-800q-133 0-226.5 93.5T160-480q0 133 93.5 226.5T480-160q133 0 226.5-93.5T800-480q0-17 11.5-28.5T840-520q17 0 28.5 11.5T880-480q0 82-31.5 155t-86 127.5q-54.5 54.5-127 86T480-80Z"></path></svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M480-80q-82 0-155-31.5t-127.5-86Q143-252 111.5-325T80-480q0-83 31.5-155.5t86-127Q252-817 325-848.5T480-880q17 0 28.5 11.5T520-840q0 17-11.5 28.5T480-800q-133 0-226.5 93.5T160-480q0 133 93.5 226.5T480-160q133 0 226.5-93.5T800-480q0-17 11.5-28.5T840-520q17 0 28.5 11.5T880-480q0 82-31.5 155t-86 127.5q-54.5 54.5-127 86T480-80Z"></path></svg>
|
package/gui/icons/settings.svg
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm112-260q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Z"/></svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm112-260q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Z"/></svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kempo-testing-framework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "The Kempo Testing Framework is a simple testing framework built on the principle that code intended to be ran in the browser should be tested in the browser, code intended to be ran in Node should be tested in Node, and code intended to be ran in both should be tested in both. No mocking, no complexity—just testing.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -18,4 +18,9 @@
|
|
|
18
18
|
"kempo-css": "^1.0.2",
|
|
19
19
|
"puppeteer": "^24.9.0"
|
|
20
20
|
}
|
|
21
|
+
,
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/dustinpoissant/kempo-testing-framework"
|
|
25
|
+
}
|
|
21
26
|
}
|