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,141 +1,141 @@
|
|
|
1
|
-
import './Counter.js';
|
|
2
|
-
|
|
3
|
-
const wait = ms => new Promise(r=>setTimeout(r,ms));
|
|
4
|
-
|
|
5
|
-
export const beforeAll = (log) => {
|
|
6
|
-
log('Setting up browser test environment for Counter component');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const beforeEach = (log) => {
|
|
10
|
-
log('Setting up test fixture - creating counter element');
|
|
11
|
-
const $counter = document.createElement('my-counter');
|
|
12
|
-
$counter.id = 'test-counter';
|
|
13
|
-
document.body.appendChild($counter);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const afterEach = (log) => {
|
|
17
|
-
log('Cleaning up - removing counter element');
|
|
18
|
-
const $counter = document.getElementById('test-counter');
|
|
19
|
-
if ($counter) {
|
|
20
|
-
$counter.remove();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const afterAll = (log) => {
|
|
25
|
-
log('Cleaning up browser test environment');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default {
|
|
29
|
-
'Counter component should be defined': ({pass, fail, log}) => {
|
|
30
|
-
log('Checking if my-counter element is defined');
|
|
31
|
-
if (window.customElements.get('my-counter')) {
|
|
32
|
-
pass('Counter component is properly registered');
|
|
33
|
-
} else {
|
|
34
|
-
fail('Counter component is not registered as a custom element');
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
'Counter should render with initial count of 0': ({pass, fail, log}) => {
|
|
39
|
-
const $counter = document.getElementById('test-counter');
|
|
40
|
-
log('Checking initial counter value');
|
|
41
|
-
|
|
42
|
-
if (!$counter) {
|
|
43
|
-
fail('Counter element not found');
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Check shadow DOM content
|
|
48
|
-
const countDisplay = $counter.shadowRoot.querySelector('div');
|
|
49
|
-
if (countDisplay && countDisplay.textContent.includes('Count: 0')) {
|
|
50
|
-
pass('Counter initializes with count of 0');
|
|
51
|
-
} else {
|
|
52
|
-
fail(`Counter does not show the expected initial value. Found: ${countDisplay?.textContent}`);
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
'Counter should increment when clicked': async ({pass, fail, log}) => {
|
|
57
|
-
const counter = document.getElementById('test-counter');
|
|
58
|
-
log('Testing click increment functionality');
|
|
59
|
-
|
|
60
|
-
if (!counter) {
|
|
61
|
-
fail('Counter element not found');
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const button = counter.shadowRoot.querySelector('button');
|
|
66
|
-
|
|
67
|
-
// Initial state check
|
|
68
|
-
let countDisplay = counter.shadowRoot.querySelector('div');
|
|
69
|
-
const initialText = countDisplay.textContent;
|
|
70
|
-
log(`Initial state: ${initialText}`);
|
|
71
|
-
|
|
72
|
-
// Trigger click event
|
|
73
|
-
button.click();
|
|
74
|
-
|
|
75
|
-
// Check updated state
|
|
76
|
-
countDisplay = counter.shadowRoot.querySelector('div');
|
|
77
|
-
const updatedText = countDisplay.textContent;
|
|
78
|
-
log(`After click: ${updatedText}`);
|
|
79
|
-
|
|
80
|
-
if (updatedText.includes('Count: 1')) {
|
|
81
|
-
pass('Counter successfully incremented to 1 after click');
|
|
82
|
-
} else {
|
|
83
|
-
fail(`Counter did not increment properly. Expected 'Count: 1' but found '${updatedText}'`);
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
'Counter should increment multiple times': async ({pass, fail, log}) => {
|
|
88
|
-
const counter = document.getElementById('test-counter');
|
|
89
|
-
log('Testing multiple increments');
|
|
90
|
-
|
|
91
|
-
if (!counter) {
|
|
92
|
-
fail('Counter element not found');
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const button = counter.shadowRoot.querySelector('button');
|
|
97
|
-
|
|
98
|
-
// Simply click the button multiple times
|
|
99
|
-
for (let i = 0; i < 3; i++) {
|
|
100
|
-
button.click();
|
|
101
|
-
await wait(100);
|
|
102
|
-
log(`After click ${i+1}, counter shows: ${counter.shadowRoot.querySelector('div').textContent}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const countDisplay = counter.shadowRoot.querySelector('div');
|
|
106
|
-
const countText = countDisplay.textContent;
|
|
107
|
-
|
|
108
|
-
if (countText.includes('Count: 3')) {
|
|
109
|
-
pass('Counter successfully incremented multiple times to 3');
|
|
110
|
-
} else {
|
|
111
|
-
fail(`Counter did not increment correctly after multiple clicks. Expected 'Count: 3' but found '${countText}'`);
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
'Counter should maintain internal state': ({pass, fail, log}) => {
|
|
116
|
-
const counter = document.getElementById('test-counter');
|
|
117
|
-
log('Testing internal state management');
|
|
118
|
-
|
|
119
|
-
if (!counter) {
|
|
120
|
-
fail('Counter element not found');
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Check initial internal state
|
|
125
|
-
if (counter.count === 0) {
|
|
126
|
-
// Manually update the internal state
|
|
127
|
-
counter.count = 42;
|
|
128
|
-
counter.render();
|
|
129
|
-
|
|
130
|
-
// Verify the rendered output matches the internal state
|
|
131
|
-
const countDisplay = counter.shadowRoot.querySelector('div');
|
|
132
|
-
if (countDisplay.textContent.includes('Count: 42')) {
|
|
133
|
-
pass('Counter maintains and displays internal state correctly');
|
|
134
|
-
} else {
|
|
135
|
-
fail(`Counter does not display the expected state. Expected 'Count: 42' but found '${countDisplay.textContent}'`);
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
fail(`Initial counter state is not 0, found: ${counter.count}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
1
|
+
import './Counter.js';
|
|
2
|
+
|
|
3
|
+
const wait = ms => new Promise(r=>setTimeout(r,ms));
|
|
4
|
+
|
|
5
|
+
export const beforeAll = (log) => {
|
|
6
|
+
log('Setting up browser test environment for Counter component');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const beforeEach = (log) => {
|
|
10
|
+
log('Setting up test fixture - creating counter element');
|
|
11
|
+
const $counter = document.createElement('my-counter');
|
|
12
|
+
$counter.id = 'test-counter';
|
|
13
|
+
document.body.appendChild($counter);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const afterEach = (log) => {
|
|
17
|
+
log('Cleaning up - removing counter element');
|
|
18
|
+
const $counter = document.getElementById('test-counter');
|
|
19
|
+
if ($counter) {
|
|
20
|
+
$counter.remove();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const afterAll = (log) => {
|
|
25
|
+
log('Cleaning up browser test environment');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
'Counter component should be defined': ({pass, fail, log}) => {
|
|
30
|
+
log('Checking if my-counter element is defined');
|
|
31
|
+
if (window.customElements.get('my-counter')) {
|
|
32
|
+
pass('Counter component is properly registered');
|
|
33
|
+
} else {
|
|
34
|
+
fail('Counter component is not registered as a custom element');
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
'Counter should render with initial count of 0': ({pass, fail, log}) => {
|
|
39
|
+
const $counter = document.getElementById('test-counter');
|
|
40
|
+
log('Checking initial counter value');
|
|
41
|
+
|
|
42
|
+
if (!$counter) {
|
|
43
|
+
fail('Counter element not found');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check shadow DOM content
|
|
48
|
+
const countDisplay = $counter.shadowRoot.querySelector('div');
|
|
49
|
+
if (countDisplay && countDisplay.textContent.includes('Count: 0')) {
|
|
50
|
+
pass('Counter initializes with count of 0');
|
|
51
|
+
} else {
|
|
52
|
+
fail(`Counter does not show the expected initial value. Found: ${countDisplay?.textContent}`);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
'Counter should increment when clicked': async ({pass, fail, log}) => {
|
|
57
|
+
const counter = document.getElementById('test-counter');
|
|
58
|
+
log('Testing click increment functionality');
|
|
59
|
+
|
|
60
|
+
if (!counter) {
|
|
61
|
+
fail('Counter element not found');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const button = counter.shadowRoot.querySelector('button');
|
|
66
|
+
|
|
67
|
+
// Initial state check
|
|
68
|
+
let countDisplay = counter.shadowRoot.querySelector('div');
|
|
69
|
+
const initialText = countDisplay.textContent;
|
|
70
|
+
log(`Initial state: ${initialText}`);
|
|
71
|
+
|
|
72
|
+
// Trigger click event
|
|
73
|
+
button.click();
|
|
74
|
+
|
|
75
|
+
// Check updated state
|
|
76
|
+
countDisplay = counter.shadowRoot.querySelector('div');
|
|
77
|
+
const updatedText = countDisplay.textContent;
|
|
78
|
+
log(`After click: ${updatedText}`);
|
|
79
|
+
|
|
80
|
+
if (updatedText.includes('Count: 1')) {
|
|
81
|
+
pass('Counter successfully incremented to 1 after click');
|
|
82
|
+
} else {
|
|
83
|
+
fail(`Counter did not increment properly. Expected 'Count: 1' but found '${updatedText}'`);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
'Counter should increment multiple times': async ({pass, fail, log}) => {
|
|
88
|
+
const counter = document.getElementById('test-counter');
|
|
89
|
+
log('Testing multiple increments');
|
|
90
|
+
|
|
91
|
+
if (!counter) {
|
|
92
|
+
fail('Counter element not found');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const button = counter.shadowRoot.querySelector('button');
|
|
97
|
+
|
|
98
|
+
// Simply click the button multiple times
|
|
99
|
+
for (let i = 0; i < 3; i++) {
|
|
100
|
+
button.click();
|
|
101
|
+
await wait(100);
|
|
102
|
+
log(`After click ${i+1}, counter shows: ${counter.shadowRoot.querySelector('div').textContent}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const countDisplay = counter.shadowRoot.querySelector('div');
|
|
106
|
+
const countText = countDisplay.textContent;
|
|
107
|
+
|
|
108
|
+
if (countText.includes('Count: 3')) {
|
|
109
|
+
pass('Counter successfully incremented multiple times to 3');
|
|
110
|
+
} else {
|
|
111
|
+
fail(`Counter did not increment correctly after multiple clicks. Expected 'Count: 3' but found '${countText}'`);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
'Counter should maintain internal state': ({pass, fail, log}) => {
|
|
116
|
+
const counter = document.getElementById('test-counter');
|
|
117
|
+
log('Testing internal state management');
|
|
118
|
+
|
|
119
|
+
if (!counter) {
|
|
120
|
+
fail('Counter element not found');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check initial internal state
|
|
125
|
+
if (counter.count === 0) {
|
|
126
|
+
// Manually update the internal state
|
|
127
|
+
counter.count = 42;
|
|
128
|
+
counter.render();
|
|
129
|
+
|
|
130
|
+
// Verify the rendered output matches the internal state
|
|
131
|
+
const countDisplay = counter.shadowRoot.querySelector('div');
|
|
132
|
+
if (countDisplay.textContent.includes('Count: 42')) {
|
|
133
|
+
pass('Counter maintains and displays internal state correctly');
|
|
134
|
+
} else {
|
|
135
|
+
fail(`Counter does not display the expected state. Expected 'Count: 42' but found '${countDisplay.textContent}'`);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
fail(`Initial counter state is not 0, found: ${counter.count}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
141
|
};
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
// Example Node test file for Kempo Testing Framework
|
|
2
|
-
// This demonstrates the basic structure and features
|
|
3
|
-
|
|
4
|
-
/* Export the optional lifecycle callbacks */
|
|
5
|
-
export const beforeAll = async (log) => {
|
|
6
|
-
log('Setting up Node test environment...');
|
|
7
|
-
// Setup that runs once before all tests
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const beforeEach = async (log) => {
|
|
11
|
-
log('Setting up for each test');
|
|
12
|
-
// Setup that runs before each individual test
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const afterEach = async (log) => {
|
|
16
|
-
log('Cleaning up after each test');
|
|
17
|
-
// Cleanup that runs after each individual test
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const afterAll = async (log) => {
|
|
21
|
-
log('Cleaning up Node test environment...');
|
|
22
|
-
// Cleanup that runs once after all tests
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/* Export the Tests */
|
|
26
|
-
export default {
|
|
27
|
-
'should handle basic string operations': async ({pass, fail, log}) => {
|
|
28
|
-
const input = 'hello world';
|
|
29
|
-
const expected = 'HELLO WORLD';
|
|
30
|
-
const result = input.toUpperCase();
|
|
31
|
-
|
|
32
|
-
log(`Testing string conversion: "${input}" -> "${result}"`);
|
|
33
|
-
|
|
34
|
-
if (result === expected) {
|
|
35
|
-
log('✓ String conversion works correctly');
|
|
36
|
-
pass('String uppercase conversion test passed');
|
|
37
|
-
} else {
|
|
38
|
-
fail(`Expected "${expected}", got "${result}"`);
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
'should validate array operations': async ({pass, fail, log}) => {
|
|
43
|
-
const numbers = [1, 2, 3, 4, 5];
|
|
44
|
-
const doubled = numbers.map(n => n * 2);
|
|
45
|
-
const expected = [2, 4, 6, 8, 10];
|
|
46
|
-
|
|
47
|
-
log(`Testing array mapping: [${numbers}] -> [${doubled}]`);
|
|
48
|
-
|
|
49
|
-
if (JSON.stringify(doubled) === JSON.stringify(expected)) {
|
|
50
|
-
log('✓ Array mapping works correctly');
|
|
51
|
-
pass('Array mapping test passed');
|
|
52
|
-
} else {
|
|
53
|
-
fail(`Expected [${expected}], got [${doubled}]`);
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
'should handle async operations': async ({pass, fail, log}) => {
|
|
58
|
-
log('Testing async operation...');
|
|
59
|
-
|
|
60
|
-
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
61
|
-
const start = Date.now();
|
|
62
|
-
|
|
63
|
-
await delay(100);
|
|
64
|
-
|
|
65
|
-
const elapsed = Date.now() - start;
|
|
66
|
-
log(`Async delay completed in ${elapsed}ms`);
|
|
67
|
-
|
|
68
|
-
if (elapsed >= 100 && elapsed < 200) {
|
|
69
|
-
log('✓ Async operation timing is correct');
|
|
70
|
-
pass('Async operation test passed');
|
|
71
|
-
} else {
|
|
72
|
-
fail(`Expected delay of ~100ms, got ${elapsed}ms`);
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
'should handle error conditions': async ({pass, fail, log}) => {
|
|
77
|
-
log('Testing error handling...');
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
JSON.parse('invalid json');
|
|
81
|
-
fail('Should have thrown an error for invalid JSON');
|
|
82
|
-
} catch (error) {
|
|
83
|
-
log(`✓ Caught expected error: ${error.message}`);
|
|
84
|
-
pass('Error handling test passed');
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
'should work with Node.js specific features': async ({pass, fail, log}) => {
|
|
89
|
-
// Test Node.js specific functionality
|
|
90
|
-
const process_exists = typeof process !== 'undefined';
|
|
91
|
-
const has_global = typeof global !== 'undefined';
|
|
92
|
-
|
|
93
|
-
log(`Process object exists: ${process_exists}`);
|
|
94
|
-
log(`Global object available: ${has_global}`);
|
|
95
|
-
|
|
96
|
-
if (process_exists && has_global) {
|
|
97
|
-
log('✓ Node.js environment detected correctly');
|
|
98
|
-
pass('Node.js environment test passed');
|
|
99
|
-
} else {
|
|
100
|
-
fail('Node.js environment features not available');
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
1
|
+
// Example Node test file for Kempo Testing Framework
|
|
2
|
+
// This demonstrates the basic structure and features
|
|
3
|
+
|
|
4
|
+
/* Export the optional lifecycle callbacks */
|
|
5
|
+
export const beforeAll = async (log) => {
|
|
6
|
+
log('Setting up Node test environment...');
|
|
7
|
+
// Setup that runs once before all tests
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const beforeEach = async (log) => {
|
|
11
|
+
log('Setting up for each test');
|
|
12
|
+
// Setup that runs before each individual test
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const afterEach = async (log) => {
|
|
16
|
+
log('Cleaning up after each test');
|
|
17
|
+
// Cleanup that runs after each individual test
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const afterAll = async (log) => {
|
|
21
|
+
log('Cleaning up Node test environment...');
|
|
22
|
+
// Cleanup that runs once after all tests
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Export the Tests */
|
|
26
|
+
export default {
|
|
27
|
+
'should handle basic string operations': async ({pass, fail, log}) => {
|
|
28
|
+
const input = 'hello world';
|
|
29
|
+
const expected = 'HELLO WORLD';
|
|
30
|
+
const result = input.toUpperCase();
|
|
31
|
+
|
|
32
|
+
log(`Testing string conversion: "${input}" -> "${result}"`);
|
|
33
|
+
|
|
34
|
+
if (result === expected) {
|
|
35
|
+
log('✓ String conversion works correctly');
|
|
36
|
+
pass('String uppercase conversion test passed');
|
|
37
|
+
} else {
|
|
38
|
+
fail(`Expected "${expected}", got "${result}"`);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
'should validate array operations': async ({pass, fail, log}) => {
|
|
43
|
+
const numbers = [1, 2, 3, 4, 5];
|
|
44
|
+
const doubled = numbers.map(n => n * 2);
|
|
45
|
+
const expected = [2, 4, 6, 8, 10];
|
|
46
|
+
|
|
47
|
+
log(`Testing array mapping: [${numbers}] -> [${doubled}]`);
|
|
48
|
+
|
|
49
|
+
if (JSON.stringify(doubled) === JSON.stringify(expected)) {
|
|
50
|
+
log('✓ Array mapping works correctly');
|
|
51
|
+
pass('Array mapping test passed');
|
|
52
|
+
} else {
|
|
53
|
+
fail(`Expected [${expected}], got [${doubled}]`);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
'should handle async operations': async ({pass, fail, log}) => {
|
|
58
|
+
log('Testing async operation...');
|
|
59
|
+
|
|
60
|
+
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
61
|
+
const start = Date.now();
|
|
62
|
+
|
|
63
|
+
await delay(100);
|
|
64
|
+
|
|
65
|
+
const elapsed = Date.now() - start;
|
|
66
|
+
log(`Async delay completed in ${elapsed}ms`);
|
|
67
|
+
|
|
68
|
+
if (elapsed >= 100 && elapsed < 200) {
|
|
69
|
+
log('✓ Async operation timing is correct');
|
|
70
|
+
pass('Async operation test passed');
|
|
71
|
+
} else {
|
|
72
|
+
fail(`Expected delay of ~100ms, got ${elapsed}ms`);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
'should handle error conditions': async ({pass, fail, log}) => {
|
|
77
|
+
log('Testing error handling...');
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
JSON.parse('invalid json');
|
|
81
|
+
fail('Should have thrown an error for invalid JSON');
|
|
82
|
+
} catch (error) {
|
|
83
|
+
log(`✓ Caught expected error: ${error.message}`);
|
|
84
|
+
pass('Error handling test passed');
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
'should work with Node.js specific features': async ({pass, fail, log}) => {
|
|
89
|
+
// Test Node.js specific functionality
|
|
90
|
+
const process_exists = typeof process !== 'undefined';
|
|
91
|
+
const has_global = typeof global !== 'undefined';
|
|
92
|
+
|
|
93
|
+
log(`Process object exists: ${process_exists}`);
|
|
94
|
+
log(`Global object available: ${has_global}`);
|
|
95
|
+
|
|
96
|
+
if (process_exists && has_global) {
|
|
97
|
+
log('✓ Node.js environment detected correctly');
|
|
98
|
+
pass('Node.js environment test passed');
|
|
99
|
+
} else {
|
|
100
|
+
fail('Node.js environment features not available');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
package/tests/fibonacci.js
CHANGED
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fibonacci sequence utility functions
|
|
3
|
-
* Works in both Node.js and browser environments
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Generate the nth Fibonacci number (0-indexed)
|
|
8
|
-
* @param {number} n - The position in the sequence (0-indexed)
|
|
9
|
-
* @returns {number} The Fibonacci number at position n
|
|
10
|
-
* @throws {Error} If n is negative or not a number
|
|
11
|
-
*/
|
|
12
|
-
const fibonacci = (n) => {
|
|
13
|
-
if(typeof n !== 'number' || !Number.isInteger(n)) {
|
|
14
|
-
throw new Error('Input must be a non-negative integer');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if(n < 0) {
|
|
18
|
-
throw new Error('Input must be a non-negative integer');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if(n === 0) return 0;
|
|
22
|
-
if(n === 1) return 1;
|
|
23
|
-
|
|
24
|
-
let a = 0;
|
|
25
|
-
let b = 1;
|
|
26
|
-
|
|
27
|
-
for(let i = 2; i <= n; i++) {
|
|
28
|
-
const temp = a + b;
|
|
29
|
-
a = b;
|
|
30
|
-
b = temp;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return b;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Generate a Fibonacci sequence up to n terms
|
|
38
|
-
* @param {number} count - Number of terms to generate
|
|
39
|
-
* @returns {number[]} Array containing the Fibonacci sequence
|
|
40
|
-
* @throws {Error} If count is negative or not a number
|
|
41
|
-
*/
|
|
42
|
-
const fibonacciSequence = (count) => {
|
|
43
|
-
if(typeof count !== 'number' || !Number.isInteger(count)) {
|
|
44
|
-
throw new Error('Count must be a non-negative integer');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if(count < 0) {
|
|
48
|
-
throw new Error('Count must be a non-negative integer');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if(count === 0) return [];
|
|
52
|
-
if(count === 1) return [0];
|
|
53
|
-
if(count === 2) return [0, 1];
|
|
54
|
-
|
|
55
|
-
const sequence = [0, 1];
|
|
56
|
-
|
|
57
|
-
for(let i = 2; i < count; i++) {
|
|
58
|
-
sequence.push(sequence[i - 1] + sequence[i - 2]);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return sequence;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Check if a number is a Fibonacci number
|
|
66
|
-
* @param {number} num - The number to check
|
|
67
|
-
* @returns {boolean} True if the number is in the Fibonacci sequence
|
|
68
|
-
*/
|
|
69
|
-
const isFibonacci = (num) => {
|
|
70
|
-
if(typeof num !== 'number' || !Number.isInteger(num) || num < 0) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if(num === 0 || num === 1) return true;
|
|
75
|
-
|
|
76
|
-
let a = 0;
|
|
77
|
-
let b = 1;
|
|
78
|
-
|
|
79
|
-
while (b < num) {
|
|
80
|
-
const temp = a + b;
|
|
81
|
-
a = b;
|
|
82
|
-
b = temp;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return b === num;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export default {
|
|
89
|
-
fibonacci,
|
|
90
|
-
fibonacciSequence,
|
|
91
|
-
isFibonacci
|
|
92
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Fibonacci sequence utility functions
|
|
3
|
+
* Works in both Node.js and browser environments
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate the nth Fibonacci number (0-indexed)
|
|
8
|
+
* @param {number} n - The position in the sequence (0-indexed)
|
|
9
|
+
* @returns {number} The Fibonacci number at position n
|
|
10
|
+
* @throws {Error} If n is negative or not a number
|
|
11
|
+
*/
|
|
12
|
+
const fibonacci = (n) => {
|
|
13
|
+
if(typeof n !== 'number' || !Number.isInteger(n)) {
|
|
14
|
+
throw new Error('Input must be a non-negative integer');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if(n < 0) {
|
|
18
|
+
throw new Error('Input must be a non-negative integer');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if(n === 0) return 0;
|
|
22
|
+
if(n === 1) return 1;
|
|
23
|
+
|
|
24
|
+
let a = 0;
|
|
25
|
+
let b = 1;
|
|
26
|
+
|
|
27
|
+
for(let i = 2; i <= n; i++) {
|
|
28
|
+
const temp = a + b;
|
|
29
|
+
a = b;
|
|
30
|
+
b = temp;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return b;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generate a Fibonacci sequence up to n terms
|
|
38
|
+
* @param {number} count - Number of terms to generate
|
|
39
|
+
* @returns {number[]} Array containing the Fibonacci sequence
|
|
40
|
+
* @throws {Error} If count is negative or not a number
|
|
41
|
+
*/
|
|
42
|
+
const fibonacciSequence = (count) => {
|
|
43
|
+
if(typeof count !== 'number' || !Number.isInteger(count)) {
|
|
44
|
+
throw new Error('Count must be a non-negative integer');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if(count < 0) {
|
|
48
|
+
throw new Error('Count must be a non-negative integer');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if(count === 0) return [];
|
|
52
|
+
if(count === 1) return [0];
|
|
53
|
+
if(count === 2) return [0, 1];
|
|
54
|
+
|
|
55
|
+
const sequence = [0, 1];
|
|
56
|
+
|
|
57
|
+
for(let i = 2; i < count; i++) {
|
|
58
|
+
sequence.push(sequence[i - 1] + sequence[i - 2]);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return sequence;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check if a number is a Fibonacci number
|
|
66
|
+
* @param {number} num - The number to check
|
|
67
|
+
* @returns {boolean} True if the number is in the Fibonacci sequence
|
|
68
|
+
*/
|
|
69
|
+
const isFibonacci = (num) => {
|
|
70
|
+
if(typeof num !== 'number' || !Number.isInteger(num) || num < 0) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if(num === 0 || num === 1) return true;
|
|
75
|
+
|
|
76
|
+
let a = 0;
|
|
77
|
+
let b = 1;
|
|
78
|
+
|
|
79
|
+
while (b < num) {
|
|
80
|
+
const temp = a + b;
|
|
81
|
+
a = b;
|
|
82
|
+
b = temp;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return b === num;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default {
|
|
89
|
+
fibonacci,
|
|
90
|
+
fibonacciSequence,
|
|
91
|
+
isFibonacci
|
|
92
|
+
};
|