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,181 +1,181 @@
|
|
|
1
|
-
import { LitElement, html, css, render } from '../lit-all.min.js';
|
|
2
|
-
import './Icon.js';
|
|
3
|
-
import './Test.js';
|
|
4
|
-
import './Logs.js';
|
|
5
|
-
import './Collapsible.js';
|
|
6
|
-
import { subscribe } from './settingsStore.js';
|
|
7
|
-
|
|
8
|
-
export const statusMap = {
|
|
9
|
-
notran: 'Not Ran',
|
|
10
|
-
queued: 'Queued',
|
|
11
|
-
running: 'Running',
|
|
12
|
-
pass: 'Pass',
|
|
13
|
-
fail: 'Fail'
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
class TestSuiteEl extends LitElement {
|
|
17
|
-
/*
|
|
18
|
-
Properties
|
|
19
|
-
*/
|
|
20
|
-
static properties = {
|
|
21
|
-
file: { type: String, reflect: true },
|
|
22
|
-
testNames: { type: Array },
|
|
23
|
-
status: { type: String, reflect: true }
|
|
24
|
-
};
|
|
25
|
-
#unsubscribe = null;
|
|
26
|
-
#testsContainer = null;
|
|
27
|
-
constructor(){
|
|
28
|
-
super();
|
|
29
|
-
this.file = '';
|
|
30
|
-
this.testNames = [];
|
|
31
|
-
this.status = 'notran';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/*
|
|
35
|
-
LifecycleCallbacks
|
|
36
|
-
*/
|
|
37
|
-
connectedCallback(){
|
|
38
|
-
super.connectedCallback();
|
|
39
|
-
}
|
|
40
|
-
firstUpdated(){
|
|
41
|
-
// Listen on host so light-DOM children bubble here
|
|
42
|
-
this.addEventListener('test_status_change', this.testStatusChangeHandler);
|
|
43
|
-
this.#unsubscribe = subscribe(() => this.requestUpdate());
|
|
44
|
-
// Ensure a light-DOM container exists for tests and render them there
|
|
45
|
-
const existing = this.querySelector('[slot="tests"]');
|
|
46
|
-
this.#testsContainer = existing || (() => {
|
|
47
|
-
const c = document.createElement('div');
|
|
48
|
-
c.setAttribute('slot', 'tests');
|
|
49
|
-
this.appendChild(c);
|
|
50
|
-
return c;
|
|
51
|
-
})();
|
|
52
|
-
this.renderTests();
|
|
53
|
-
}
|
|
54
|
-
disconnectedCallback(){
|
|
55
|
-
super.disconnectedCallback();
|
|
56
|
-
this.removeEventListener('test_status_change', this.testStatusChangeHandler);
|
|
57
|
-
if(this.#unsubscribe) this.#unsubscribe();
|
|
58
|
-
}
|
|
59
|
-
updated(changedProps){
|
|
60
|
-
if(changedProps.has('status')){
|
|
61
|
-
this.dispatchEvent(new CustomEvent('testfile_status_change', {
|
|
62
|
-
detail: { file: this.file, status: this.status },
|
|
63
|
-
bubbles: true,
|
|
64
|
-
composed: true
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
if(changedProps.has('file') || changedProps.has('testNames')){
|
|
68
|
-
this.renderTests();
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/*
|
|
73
|
-
Methods
|
|
74
|
-
*/
|
|
75
|
-
runAllTests = () => {
|
|
76
|
-
const getFrameworkEl = () => {
|
|
77
|
-
let node = this;
|
|
78
|
-
while(node){
|
|
79
|
-
if(node && node.closest){
|
|
80
|
-
const fw = node.closest('ktf-test-framework');
|
|
81
|
-
if(fw) return fw;
|
|
82
|
-
}
|
|
83
|
-
const root = node?.getRootNode?.();
|
|
84
|
-
const host = root && root.host ? root.host : null;
|
|
85
|
-
if(!host) break;
|
|
86
|
-
node = host;
|
|
87
|
-
}
|
|
88
|
-
return document.querySelector('ktf-test-framework');
|
|
89
|
-
};
|
|
90
|
-
const fw = getFrameworkEl();
|
|
91
|
-
if(fw && typeof fw.enqueueSuite==='function') fw.enqueueSuite({ file: this.file, testNames: this.testNames, el: this });
|
|
92
|
-
};
|
|
93
|
-
testStatusChangeHandler = () => { this.status = this.calcStatus(); };
|
|
94
|
-
|
|
95
|
-
/*
|
|
96
|
-
Utility Methods
|
|
97
|
-
*/
|
|
98
|
-
getFrameworkEl = () => {
|
|
99
|
-
let node = this;
|
|
100
|
-
while(node){
|
|
101
|
-
if(node && node.closest){
|
|
102
|
-
const fw = node.closest('ktf-test-framework');
|
|
103
|
-
if(fw) return fw;
|
|
104
|
-
}
|
|
105
|
-
const root = node?.getRootNode?.();
|
|
106
|
-
const host = root && root.host ? root.host : null;
|
|
107
|
-
if(!host) break;
|
|
108
|
-
node = host;
|
|
109
|
-
}
|
|
110
|
-
return document.querySelector('ktf-test-framework');
|
|
111
|
-
};
|
|
112
|
-
calcStatus = () => {
|
|
113
|
-
const root = this.#testsContainer || this;
|
|
114
|
-
const testElements = root.querySelectorAll('ktf-test');
|
|
115
|
-
const statuses = Array.from(testElements).map(el => el.status);
|
|
116
|
-
if(statuses.includes('running')) return 'running';
|
|
117
|
-
if(statuses.includes('fail')) return 'fail';
|
|
118
|
-
if(statuses.length && statuses.every(s => s==='pass')) return 'pass';
|
|
119
|
-
return 'notran';
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
/*
|
|
123
|
-
Light DOM rendering for tests
|
|
124
|
-
*/
|
|
125
|
-
renderTests(){
|
|
126
|
-
if(!this.#testsContainer) return;
|
|
127
|
-
const list = Array.isArray(this.testNames) ? this.testNames : [];
|
|
128
|
-
const tpl = html`${list.map(name => html`<ktf-test .file=${this.file} .name=${name}></ktf-test>`)}`;
|
|
129
|
-
render(tpl, this.#testsContainer);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/*
|
|
133
|
-
Rendering
|
|
134
|
-
*/
|
|
135
|
-
render(){
|
|
136
|
-
if(!this.file || !this.testNames.length) return html``;
|
|
137
|
-
const titleText = this.file.replace('tests/', '').replace('.node-test.js', '').replace('.browser-test.js', '').replace('.test.js', '');
|
|
138
|
-
return html`
|
|
139
|
-
<link rel="stylesheet" href="/kempo.css">
|
|
140
|
-
<ktf-collapsible>
|
|
141
|
-
<span slot="title" id="title" class="-ml">${titleText}</span>
|
|
142
|
-
<div slot="actions">
|
|
143
|
-
${this.status==='notran' || this.status==='queued' ? html`
|
|
144
|
-
<button class="no-btn d-ib ph" @click=${this.runAllTests} aria-label="Run Test Suite" ?disabled=${this.status==='queued'}>
|
|
145
|
-
<ktf-icon name="${this.status==='queued'?'scheduled':'play'}"></ktf-icon>
|
|
146
|
-
</button>
|
|
147
|
-
` : html`
|
|
148
|
-
<span class="d-ib ph status-color" aria-hidden="true">
|
|
149
|
-
<ktf-icon name="${this.status}" animation="${this.status==='running'?'spin':'none'}"></ktf-icon>
|
|
150
|
-
</span>
|
|
151
|
-
`}
|
|
152
|
-
</div>
|
|
153
|
-
<div id="details">
|
|
154
|
-
<div id="status">
|
|
155
|
-
<h6>${statusMap[this.status]}</h6>
|
|
156
|
-
${this.status!=='running'?html`
|
|
157
|
-
<button class="primary mb" @click=${this.runAllTests} ?disabled=${this.status==='queued'}>Run Test Suite</button>
|
|
158
|
-
`:html``}
|
|
159
|
-
</div>
|
|
160
|
-
<ktf-logs id="fileLogs"></ktf-logs>
|
|
161
|
-
<slot name="tests"></slot>
|
|
162
|
-
</div>
|
|
163
|
-
</ktf-collapsible>
|
|
164
|
-
`;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
static styles = css`
|
|
168
|
-
:host{ --tf_status: var(--tc_default, inherit); }
|
|
169
|
-
:host([status="running"]){ --tf_status: var(--tc_primary, #3366ff); }
|
|
170
|
-
:host([status="pass"]){ --tf_status: var(--tc_success, rgb(0, 136, 0)); }
|
|
171
|
-
:host([status="fail"]){ --tf_status: var(--tc_danger, rgb(255, 0, 51)); }
|
|
172
|
-
#title{ font-size: 1.25rem; font-weight: 600; }
|
|
173
|
-
div[slot="actions"]{ font-size: 1.25rem; }
|
|
174
|
-
#title,
|
|
175
|
-
#status{ color: var(--tf_status); }
|
|
176
|
-
.status-color{ color: var(--tf_status); }
|
|
177
|
-
`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
window.customElements.define('ktf-test-suite', TestSuiteEl);
|
|
181
|
-
|
|
1
|
+
import { LitElement, html, css, render } from '../lit-all.min.js';
|
|
2
|
+
import './Icon.js';
|
|
3
|
+
import './Test.js';
|
|
4
|
+
import './Logs.js';
|
|
5
|
+
import './Collapsible.js';
|
|
6
|
+
import { subscribe } from './settingsStore.js';
|
|
7
|
+
|
|
8
|
+
export const statusMap = {
|
|
9
|
+
notran: 'Not Ran',
|
|
10
|
+
queued: 'Queued',
|
|
11
|
+
running: 'Running',
|
|
12
|
+
pass: 'Pass',
|
|
13
|
+
fail: 'Fail'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
class TestSuiteEl extends LitElement {
|
|
17
|
+
/*
|
|
18
|
+
Properties
|
|
19
|
+
*/
|
|
20
|
+
static properties = {
|
|
21
|
+
file: { type: String, reflect: true },
|
|
22
|
+
testNames: { type: Array },
|
|
23
|
+
status: { type: String, reflect: true }
|
|
24
|
+
};
|
|
25
|
+
#unsubscribe = null;
|
|
26
|
+
#testsContainer = null;
|
|
27
|
+
constructor(){
|
|
28
|
+
super();
|
|
29
|
+
this.file = '';
|
|
30
|
+
this.testNames = [];
|
|
31
|
+
this.status = 'notran';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
LifecycleCallbacks
|
|
36
|
+
*/
|
|
37
|
+
connectedCallback(){
|
|
38
|
+
super.connectedCallback();
|
|
39
|
+
}
|
|
40
|
+
firstUpdated(){
|
|
41
|
+
// Listen on host so light-DOM children bubble here
|
|
42
|
+
this.addEventListener('test_status_change', this.testStatusChangeHandler);
|
|
43
|
+
this.#unsubscribe = subscribe(() => this.requestUpdate());
|
|
44
|
+
// Ensure a light-DOM container exists for tests and render them there
|
|
45
|
+
const existing = this.querySelector('[slot="tests"]');
|
|
46
|
+
this.#testsContainer = existing || (() => {
|
|
47
|
+
const c = document.createElement('div');
|
|
48
|
+
c.setAttribute('slot', 'tests');
|
|
49
|
+
this.appendChild(c);
|
|
50
|
+
return c;
|
|
51
|
+
})();
|
|
52
|
+
this.renderTests();
|
|
53
|
+
}
|
|
54
|
+
disconnectedCallback(){
|
|
55
|
+
super.disconnectedCallback();
|
|
56
|
+
this.removeEventListener('test_status_change', this.testStatusChangeHandler);
|
|
57
|
+
if(this.#unsubscribe) this.#unsubscribe();
|
|
58
|
+
}
|
|
59
|
+
updated(changedProps){
|
|
60
|
+
if(changedProps.has('status')){
|
|
61
|
+
this.dispatchEvent(new CustomEvent('testfile_status_change', {
|
|
62
|
+
detail: { file: this.file, status: this.status },
|
|
63
|
+
bubbles: true,
|
|
64
|
+
composed: true
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
if(changedProps.has('file') || changedProps.has('testNames')){
|
|
68
|
+
this.renderTests();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
Methods
|
|
74
|
+
*/
|
|
75
|
+
runAllTests = () => {
|
|
76
|
+
const getFrameworkEl = () => {
|
|
77
|
+
let node = this;
|
|
78
|
+
while(node){
|
|
79
|
+
if(node && node.closest){
|
|
80
|
+
const fw = node.closest('ktf-test-framework');
|
|
81
|
+
if(fw) return fw;
|
|
82
|
+
}
|
|
83
|
+
const root = node?.getRootNode?.();
|
|
84
|
+
const host = root && root.host ? root.host : null;
|
|
85
|
+
if(!host) break;
|
|
86
|
+
node = host;
|
|
87
|
+
}
|
|
88
|
+
return document.querySelector('ktf-test-framework');
|
|
89
|
+
};
|
|
90
|
+
const fw = getFrameworkEl();
|
|
91
|
+
if(fw && typeof fw.enqueueSuite==='function') fw.enqueueSuite({ file: this.file, testNames: this.testNames, el: this });
|
|
92
|
+
};
|
|
93
|
+
testStatusChangeHandler = () => { this.status = this.calcStatus(); };
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
Utility Methods
|
|
97
|
+
*/
|
|
98
|
+
getFrameworkEl = () => {
|
|
99
|
+
let node = this;
|
|
100
|
+
while(node){
|
|
101
|
+
if(node && node.closest){
|
|
102
|
+
const fw = node.closest('ktf-test-framework');
|
|
103
|
+
if(fw) return fw;
|
|
104
|
+
}
|
|
105
|
+
const root = node?.getRootNode?.();
|
|
106
|
+
const host = root && root.host ? root.host : null;
|
|
107
|
+
if(!host) break;
|
|
108
|
+
node = host;
|
|
109
|
+
}
|
|
110
|
+
return document.querySelector('ktf-test-framework');
|
|
111
|
+
};
|
|
112
|
+
calcStatus = () => {
|
|
113
|
+
const root = this.#testsContainer || this;
|
|
114
|
+
const testElements = root.querySelectorAll('ktf-test');
|
|
115
|
+
const statuses = Array.from(testElements).map(el => el.status);
|
|
116
|
+
if(statuses.includes('running')) return 'running';
|
|
117
|
+
if(statuses.includes('fail')) return 'fail';
|
|
118
|
+
if(statuses.length && statuses.every(s => s==='pass')) return 'pass';
|
|
119
|
+
return 'notran';
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
Light DOM rendering for tests
|
|
124
|
+
*/
|
|
125
|
+
renderTests(){
|
|
126
|
+
if(!this.#testsContainer) return;
|
|
127
|
+
const list = Array.isArray(this.testNames) ? this.testNames : [];
|
|
128
|
+
const tpl = html`${list.map(name => html`<ktf-test .file=${this.file} .name=${name}></ktf-test>`)}`;
|
|
129
|
+
render(tpl, this.#testsContainer);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
Rendering
|
|
134
|
+
*/
|
|
135
|
+
render(){
|
|
136
|
+
if(!this.file || !this.testNames.length) return html``;
|
|
137
|
+
const titleText = this.file.replace('tests/', '').replace('.node-test.js', '').replace('.browser-test.js', '').replace('.test.js', '');
|
|
138
|
+
return html`
|
|
139
|
+
<link rel="stylesheet" href="/kempo.css">
|
|
140
|
+
<ktf-collapsible>
|
|
141
|
+
<span slot="title" id="title" class="-ml">${titleText}</span>
|
|
142
|
+
<div slot="actions">
|
|
143
|
+
${this.status==='notran' || this.status==='queued' ? html`
|
|
144
|
+
<button class="no-btn d-ib ph" @click=${this.runAllTests} aria-label="Run Test Suite" ?disabled=${this.status==='queued'}>
|
|
145
|
+
<ktf-icon name="${this.status==='queued'?'scheduled':'play'}"></ktf-icon>
|
|
146
|
+
</button>
|
|
147
|
+
` : html`
|
|
148
|
+
<span class="d-ib ph status-color" aria-hidden="true">
|
|
149
|
+
<ktf-icon name="${this.status}" animation="${this.status==='running'?'spin':'none'}"></ktf-icon>
|
|
150
|
+
</span>
|
|
151
|
+
`}
|
|
152
|
+
</div>
|
|
153
|
+
<div id="details">
|
|
154
|
+
<div id="status">
|
|
155
|
+
<h6>${statusMap[this.status]}</h6>
|
|
156
|
+
${this.status!=='running'?html`
|
|
157
|
+
<button class="primary mb" @click=${this.runAllTests} ?disabled=${this.status==='queued'}>Run Test Suite</button>
|
|
158
|
+
`:html``}
|
|
159
|
+
</div>
|
|
160
|
+
<ktf-logs id="fileLogs"></ktf-logs>
|
|
161
|
+
<slot name="tests"></slot>
|
|
162
|
+
</div>
|
|
163
|
+
</ktf-collapsible>
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static styles = css`
|
|
168
|
+
:host{ --tf_status: var(--tc_default, inherit); }
|
|
169
|
+
:host([status="running"]){ --tf_status: var(--tc_primary, #3366ff); }
|
|
170
|
+
:host([status="pass"]){ --tf_status: var(--tc_success, rgb(0, 136, 0)); }
|
|
171
|
+
:host([status="fail"]){ --tf_status: var(--tc_danger, rgb(255, 0, 51)); }
|
|
172
|
+
#title{ font-size: 1.25rem; font-weight: 600; }
|
|
173
|
+
div[slot="actions"]{ font-size: 1.25rem; }
|
|
174
|
+
#title,
|
|
175
|
+
#status{ color: var(--tf_status); }
|
|
176
|
+
.status-color{ color: var(--tf_status); }
|
|
177
|
+
`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
window.customElements.define('ktf-test-suite', TestSuiteEl);
|
|
181
|
+
|