kempo-testing-framework 1.2.4 → 1.3.4
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 +41 -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 +5 -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
package/src/runBrowserTests.js
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
import { startServer, stopServer } from './browserTestServer.js';
|
|
2
|
-
import puppeteer from 'puppeteer';
|
|
3
|
-
import LOG_LEVELS from './utils/logLevels.js';
|
|
4
|
-
|
|
5
|
-
export default async ({
|
|
6
|
-
testFile,
|
|
7
|
-
filter = false,
|
|
8
|
-
showBrowser = false,
|
|
9
|
-
port = 3000,
|
|
10
|
-
logLevel,
|
|
11
|
-
delayMs = 0
|
|
12
|
-
}) => {
|
|
13
|
-
/*
|
|
14
|
-
* Start Test Server
|
|
15
|
-
*/
|
|
16
|
-
const url = await startServer(port);
|
|
17
|
-
|
|
18
|
-
if(logLevel >= LOG_LEVELS.VERBOSE){
|
|
19
|
-
console.log(`\x1b[90m Browser test server started at ${url}\x1b[0m`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
23
|
-
try {
|
|
24
|
-
/*
|
|
25
|
-
* Launch Browser and Execute Tests
|
|
26
|
-
*/
|
|
27
|
-
const launchOptions = {
|
|
28
|
-
headless: !showBrowser,
|
|
29
|
-
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
30
|
-
defaultViewport: { width: 1600, height: 900 }
|
|
31
|
-
};
|
|
32
|
-
if (showBrowser) {
|
|
33
|
-
launchOptions.devtools = true;
|
|
34
|
-
launchOptions.args.push('--auto-open-devtools-for-tabs');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const browser = await puppeteer.launch(launchOptions);
|
|
38
|
-
const page = await browser.newPage();
|
|
39
|
-
|
|
40
|
-
// Optional pre-delay when a visible browser is requested
|
|
41
|
-
if (showBrowser && delayMs > 0) {
|
|
42
|
-
if (logLevel >= LOG_LEVELS.VERBOSE) {
|
|
43
|
-
console.log(`\x1b[90m Applying pre-test browser delay: ${delayMs}ms\x1b[0m`);
|
|
44
|
-
}
|
|
45
|
-
await sleep(delayMs);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
await page.goto(`${url}?testFile=${testFile}&testFilter=${filter}&delay=${delayMs}`);
|
|
49
|
-
await page.waitForFunction(() => document.readyState === 'complete' || document.readyState === 'interactive');
|
|
50
|
-
|
|
51
|
-
// Ensure the test page has focus when browser is visible
|
|
52
|
-
if (showBrowser) {
|
|
53
|
-
await page.bringToFront();
|
|
54
|
-
if (logLevel >= LOG_LEVELS.VERBOSE) {
|
|
55
|
-
console.log(`\x1b[90m Bringing test page to front\x1b[0m`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Check for any errors first
|
|
60
|
-
const hasError = await page.evaluate(() => window.error !== undefined);
|
|
61
|
-
if (hasError) {
|
|
62
|
-
const error = await page.evaluate(() => window.error);
|
|
63
|
-
throw new Error(`Browser test error: ${error}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
await page.waitForFunction(() => window.results !== undefined, { timeout: 20000 });
|
|
67
|
-
const results = await page.evaluate(() => window.results);
|
|
68
|
-
|
|
69
|
-
// Optional post-delay when a visible browser is requested
|
|
70
|
-
if (showBrowser && delayMs > 0) {
|
|
71
|
-
if (logLevel >= LOG_LEVELS.VERBOSE) {
|
|
72
|
-
console.log(`\x1b[90m Applying post-test browser delay: ${delayMs}ms\x1b[0m`);
|
|
73
|
-
}
|
|
74
|
-
await sleep(delayMs);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/*
|
|
78
|
-
* Cleanup
|
|
79
|
-
*/
|
|
80
|
-
await browser.close();
|
|
81
|
-
await stopServer();
|
|
82
|
-
return results;
|
|
83
|
-
} catch(error){
|
|
84
|
-
await stopServer();
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
1
|
+
import { startServer, stopServer } from './browserTestServer.js';
|
|
2
|
+
import puppeteer from 'puppeteer';
|
|
3
|
+
import LOG_LEVELS from './utils/logLevels.js';
|
|
4
|
+
|
|
5
|
+
export default async ({
|
|
6
|
+
testFile,
|
|
7
|
+
filter = false,
|
|
8
|
+
showBrowser = false,
|
|
9
|
+
port = 3000,
|
|
10
|
+
logLevel,
|
|
11
|
+
delayMs = 0
|
|
12
|
+
}) => {
|
|
13
|
+
/*
|
|
14
|
+
* Start Test Server
|
|
15
|
+
*/
|
|
16
|
+
const url = await startServer(port);
|
|
17
|
+
|
|
18
|
+
if(logLevel >= LOG_LEVELS.VERBOSE){
|
|
19
|
+
console.log(`\x1b[90m Browser test server started at ${url}\x1b[0m`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
23
|
+
try {
|
|
24
|
+
/*
|
|
25
|
+
* Launch Browser and Execute Tests
|
|
26
|
+
*/
|
|
27
|
+
const launchOptions = {
|
|
28
|
+
headless: !showBrowser,
|
|
29
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
30
|
+
defaultViewport: { width: 1600, height: 900 }
|
|
31
|
+
};
|
|
32
|
+
if (showBrowser) {
|
|
33
|
+
launchOptions.devtools = true;
|
|
34
|
+
launchOptions.args.push('--auto-open-devtools-for-tabs');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const browser = await puppeteer.launch(launchOptions);
|
|
38
|
+
const page = await browser.newPage();
|
|
39
|
+
|
|
40
|
+
// Optional pre-delay when a visible browser is requested
|
|
41
|
+
if (showBrowser && delayMs > 0) {
|
|
42
|
+
if (logLevel >= LOG_LEVELS.VERBOSE) {
|
|
43
|
+
console.log(`\x1b[90m Applying pre-test browser delay: ${delayMs}ms\x1b[0m`);
|
|
44
|
+
}
|
|
45
|
+
await sleep(delayMs);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await page.goto(`${url}?testFile=${testFile}&testFilter=${filter}&delay=${delayMs}`);
|
|
49
|
+
await page.waitForFunction(() => document.readyState === 'complete' || document.readyState === 'interactive');
|
|
50
|
+
|
|
51
|
+
// Ensure the test page has focus when browser is visible
|
|
52
|
+
if (showBrowser) {
|
|
53
|
+
await page.bringToFront();
|
|
54
|
+
if (logLevel >= LOG_LEVELS.VERBOSE) {
|
|
55
|
+
console.log(`\x1b[90m Bringing test page to front\x1b[0m`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check for any errors first
|
|
60
|
+
const hasError = await page.evaluate(() => window.error !== undefined);
|
|
61
|
+
if (hasError) {
|
|
62
|
+
const error = await page.evaluate(() => window.error);
|
|
63
|
+
throw new Error(`Browser test error: ${error}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await page.waitForFunction(() => window.results !== undefined, { timeout: 20000 });
|
|
67
|
+
const results = await page.evaluate(() => window.results);
|
|
68
|
+
|
|
69
|
+
// Optional post-delay when a visible browser is requested
|
|
70
|
+
if (showBrowser && delayMs > 0) {
|
|
71
|
+
if (logLevel >= LOG_LEVELS.VERBOSE) {
|
|
72
|
+
console.log(`\x1b[90m Applying post-test browser delay: ${delayMs}ms\x1b[0m`);
|
|
73
|
+
}
|
|
74
|
+
await sleep(delayMs);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
* Cleanup
|
|
79
|
+
*/
|
|
80
|
+
await browser.close();
|
|
81
|
+
await stopServer();
|
|
82
|
+
return results;
|
|
83
|
+
} catch(error){
|
|
84
|
+
await stopServer();
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
};
|
package/src/runTestFiles.js
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
import findTests from './findTests.js';
|
|
2
|
-
import runNodeTests from './runTests.js';
|
|
3
|
-
import runBrowserTests from './runBrowserTests.js';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Runs test files and returns results without logging
|
|
8
|
-
* @param {Object} options - Configuration options
|
|
9
|
-
* @param {string} options.suiteFilter - Filter for test suites
|
|
10
|
-
* @param {string} options.testFilter - Filter for individual tests
|
|
11
|
-
* @param {boolean} options.shouldRunBrowser - Whether to run browser tests
|
|
12
|
-
* @param {boolean} options.shouldRunNode - Whether to run node tests
|
|
13
|
-
* @param {boolean} options.showBrowser - Whether to show browser during tests
|
|
14
|
-
* @param {number} options.port - Port for browser tests
|
|
15
|
-
* @param {number} options.logLevel - Log level for browser tests
|
|
16
|
-
* @param {number} options.delayMs - Optional delay in ms to apply for browser runs
|
|
17
|
-
* @param {Function} options.onNodeTestStart - Callback when a node test starts
|
|
18
|
-
* @param {Function} options.onBrowserTestStart - Callback when a browser test starts
|
|
19
|
-
* @param {string[]} options.specificFiles - Array of specific test files to run (overrides findTests)
|
|
20
|
-
* @returns {Promise<{nodeResults: Object, browserResults: Object}>}
|
|
21
|
-
*/
|
|
22
|
-
export default async ({
|
|
23
|
-
suiteFilter = '',
|
|
24
|
-
testFilter = '',
|
|
25
|
-
shouldRunBrowser = true,
|
|
26
|
-
shouldRunNode = true,
|
|
27
|
-
showBrowser = false,
|
|
28
|
-
port = 3000,
|
|
29
|
-
logLevel = 2,
|
|
30
|
-
delayMs = 0,
|
|
31
|
-
onNodeTestStart,
|
|
32
|
-
onBrowserTestStart,
|
|
33
|
-
specificFiles = null
|
|
34
|
-
}) => {
|
|
35
|
-
let nodeTests = [];
|
|
36
|
-
let browserTests = [];
|
|
37
|
-
|
|
38
|
-
if (specificFiles) {
|
|
39
|
-
// When specific files are provided, use them directly
|
|
40
|
-
for (const file of specificFiles) {
|
|
41
|
-
if (file.endsWith('.node-test.js') && shouldRunNode) {
|
|
42
|
-
nodeTests.push(file);
|
|
43
|
-
} else if (file.endsWith('.browser-test.js') && shouldRunBrowser) {
|
|
44
|
-
browserTests.push(file);
|
|
45
|
-
} else if (file.endsWith('.test.js')) {
|
|
46
|
-
// Generic test files can be both
|
|
47
|
-
if (shouldRunNode) nodeTests.push(file);
|
|
48
|
-
if (shouldRunBrowser) browserTests.push(file);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
} else {
|
|
52
|
-
// Use findTests for discovery
|
|
53
|
-
const result = await findTests(suiteFilter, testFilter, shouldRunBrowser, shouldRunNode);
|
|
54
|
-
nodeTests = result.nodeTests;
|
|
55
|
-
browserTests = result.browserTests;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/*
|
|
59
|
-
* Node Test Execution
|
|
60
|
-
*/
|
|
61
|
-
const nodeResults = {};
|
|
62
|
-
if(nodeTests.length){
|
|
63
|
-
for(const file of nodeTests){
|
|
64
|
-
if(onNodeTestStart) onNodeTestStart(file);
|
|
65
|
-
// Convert forward slashes back to OS-specific path separators for file system operations
|
|
66
|
-
const normalizedFile = file.replace(/\//g, path.sep);
|
|
67
|
-
const module = await import(`file://${path.resolve(process.cwd(), normalizedFile)}`);
|
|
68
|
-
nodeResults[file] = await runNodeTests(module, testFilter);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/*
|
|
73
|
-
* Browser Test Execution
|
|
74
|
-
*/
|
|
75
|
-
const browserResults = {};
|
|
76
|
-
if(browserTests.length){
|
|
77
|
-
for(const testFile of browserTests){
|
|
78
|
-
if(onBrowserTestStart) onBrowserTestStart(testFile);
|
|
79
|
-
browserResults[testFile] = await runBrowserTests({
|
|
80
|
-
testFile,
|
|
81
|
-
filter: testFilter, // Changed from testFilter to filter
|
|
82
|
-
showBrowser,
|
|
83
|
-
port,
|
|
84
|
-
logLevel,
|
|
85
|
-
delayMs
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
nodeResults,
|
|
92
|
-
browserResults
|
|
93
|
-
};
|
|
94
|
-
};
|
|
1
|
+
import findTests from './findTests.js';
|
|
2
|
+
import runNodeTests from './runTests.js';
|
|
3
|
+
import runBrowserTests from './runBrowserTests.js';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Runs test files and returns results without logging
|
|
8
|
+
* @param {Object} options - Configuration options
|
|
9
|
+
* @param {string} options.suiteFilter - Filter for test suites
|
|
10
|
+
* @param {string} options.testFilter - Filter for individual tests
|
|
11
|
+
* @param {boolean} options.shouldRunBrowser - Whether to run browser tests
|
|
12
|
+
* @param {boolean} options.shouldRunNode - Whether to run node tests
|
|
13
|
+
* @param {boolean} options.showBrowser - Whether to show browser during tests
|
|
14
|
+
* @param {number} options.port - Port for browser tests
|
|
15
|
+
* @param {number} options.logLevel - Log level for browser tests
|
|
16
|
+
* @param {number} options.delayMs - Optional delay in ms to apply for browser runs
|
|
17
|
+
* @param {Function} options.onNodeTestStart - Callback when a node test starts
|
|
18
|
+
* @param {Function} options.onBrowserTestStart - Callback when a browser test starts
|
|
19
|
+
* @param {string[]} options.specificFiles - Array of specific test files to run (overrides findTests)
|
|
20
|
+
* @returns {Promise<{nodeResults: Object, browserResults: Object}>}
|
|
21
|
+
*/
|
|
22
|
+
export default async ({
|
|
23
|
+
suiteFilter = '',
|
|
24
|
+
testFilter = '',
|
|
25
|
+
shouldRunBrowser = true,
|
|
26
|
+
shouldRunNode = true,
|
|
27
|
+
showBrowser = false,
|
|
28
|
+
port = 3000,
|
|
29
|
+
logLevel = 2,
|
|
30
|
+
delayMs = 0,
|
|
31
|
+
onNodeTestStart,
|
|
32
|
+
onBrowserTestStart,
|
|
33
|
+
specificFiles = null
|
|
34
|
+
}) => {
|
|
35
|
+
let nodeTests = [];
|
|
36
|
+
let browserTests = [];
|
|
37
|
+
|
|
38
|
+
if (specificFiles) {
|
|
39
|
+
// When specific files are provided, use them directly
|
|
40
|
+
for (const file of specificFiles) {
|
|
41
|
+
if (file.endsWith('.node-test.js') && shouldRunNode) {
|
|
42
|
+
nodeTests.push(file);
|
|
43
|
+
} else if (file.endsWith('.browser-test.js') && shouldRunBrowser) {
|
|
44
|
+
browserTests.push(file);
|
|
45
|
+
} else if (file.endsWith('.test.js')) {
|
|
46
|
+
// Generic test files can be both
|
|
47
|
+
if (shouldRunNode) nodeTests.push(file);
|
|
48
|
+
if (shouldRunBrowser) browserTests.push(file);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
// Use findTests for discovery
|
|
53
|
+
const result = await findTests(suiteFilter, testFilter, shouldRunBrowser, shouldRunNode);
|
|
54
|
+
nodeTests = result.nodeTests;
|
|
55
|
+
browserTests = result.browserTests;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* Node Test Execution
|
|
60
|
+
*/
|
|
61
|
+
const nodeResults = {};
|
|
62
|
+
if(nodeTests.length){
|
|
63
|
+
for(const file of nodeTests){
|
|
64
|
+
if(onNodeTestStart) onNodeTestStart(file);
|
|
65
|
+
// Convert forward slashes back to OS-specific path separators for file system operations
|
|
66
|
+
const normalizedFile = file.replace(/\//g, path.sep);
|
|
67
|
+
const module = await import(`file://${path.resolve(process.cwd(), normalizedFile)}`);
|
|
68
|
+
nodeResults[file] = await runNodeTests(module, testFilter);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
* Browser Test Execution
|
|
74
|
+
*/
|
|
75
|
+
const browserResults = {};
|
|
76
|
+
if(browserTests.length){
|
|
77
|
+
for(const testFile of browserTests){
|
|
78
|
+
if(onBrowserTestStart) onBrowserTestStart(testFile);
|
|
79
|
+
browserResults[testFile] = await runBrowserTests({
|
|
80
|
+
testFile,
|
|
81
|
+
filter: testFilter, // Changed from testFilter to filter
|
|
82
|
+
showBrowser,
|
|
83
|
+
port,
|
|
84
|
+
logLevel,
|
|
85
|
+
delayMs
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
nodeResults,
|
|
92
|
+
browserResults
|
|
93
|
+
};
|
|
94
|
+
};
|
package/src/runTests.js
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
const wait = ms => new Promise(r => setTimeout(r, ms));
|
|
2
|
-
|
|
3
|
-
export default async ({
|
|
4
|
-
beforeAll = async () => {},
|
|
5
|
-
beforeEach = async () => {},
|
|
6
|
-
afterEach = async () => {},
|
|
7
|
-
afterAll = async () => {},
|
|
8
|
-
default: tests
|
|
9
|
-
} = {}, filter = false, delay = 0) => {
|
|
10
|
-
const testsToRun = filter ? Object.keys(tests).filter(name => name.trim().toLowerCase().includes(filter.trim().toLowerCase())) : Object.keys(tests);
|
|
11
|
-
if(!testsToRun.length) throw new Error('No tests found matching the filter');
|
|
12
|
-
|
|
13
|
-
/*
|
|
14
|
-
* Test Results Structure
|
|
15
|
-
*/
|
|
16
|
-
const results = {
|
|
17
|
-
beforeAllLogs: [],
|
|
18
|
-
tests: {},
|
|
19
|
-
afterAllLogs: []
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/*
|
|
23
|
-
* Execute beforeAll Hook
|
|
24
|
-
*/
|
|
25
|
-
await beforeAll(message => {
|
|
26
|
-
results.beforeAllLogs.push({
|
|
27
|
-
message,
|
|
28
|
-
type: 'log',
|
|
29
|
-
level: 3
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
/*
|
|
34
|
-
* Execute Individual Tests
|
|
35
|
-
*/
|
|
36
|
-
for (const name of testsToRun) {
|
|
37
|
-
const result = {
|
|
38
|
-
logs: [],
|
|
39
|
-
passed: null
|
|
40
|
-
};
|
|
41
|
-
const log = (message, type = 'log', level = 3) => {
|
|
42
|
-
result.logs.push({
|
|
43
|
-
message,
|
|
44
|
-
type,
|
|
45
|
-
level
|
|
46
|
-
});
|
|
47
|
-
};
|
|
48
|
-
log(`== Starting Test "${name}" ==`, 'progress', 3);
|
|
49
|
-
log('== Before Each ==', 'progress', 3);
|
|
50
|
-
await beforeEach(log);
|
|
51
|
-
await tests[name]({
|
|
52
|
-
log,
|
|
53
|
-
pass: message => {
|
|
54
|
-
result.passed = true;
|
|
55
|
-
log(message, 'pass', 2); // was 3; make visible at NORMAL
|
|
56
|
-
},
|
|
57
|
-
fail: message => {
|
|
58
|
-
result.passed = false;
|
|
59
|
-
log(message, 'fail', 2);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
await afterEach(log);
|
|
63
|
-
log('== After Each ==', 'progress', 3);
|
|
64
|
-
if(!result.passed){ // If the test failed, elevate all logs to a level 2
|
|
65
|
-
result.logs.forEach(log => log.level = 2);
|
|
66
|
-
}
|
|
67
|
-
results.tests[name] = result;
|
|
68
|
-
await wait(delay);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/*
|
|
72
|
-
* Execute afterAll Hook
|
|
73
|
-
*/
|
|
74
|
-
await afterAll(message => {
|
|
75
|
-
results.afterAllLogs.push({
|
|
76
|
-
message,
|
|
77
|
-
type: 'log',
|
|
78
|
-
level: 3
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
return results;
|
|
83
|
-
};
|
|
1
|
+
const wait = ms => new Promise(r => setTimeout(r, ms));
|
|
2
|
+
|
|
3
|
+
export default async ({
|
|
4
|
+
beforeAll = async () => {},
|
|
5
|
+
beforeEach = async () => {},
|
|
6
|
+
afterEach = async () => {},
|
|
7
|
+
afterAll = async () => {},
|
|
8
|
+
default: tests
|
|
9
|
+
} = {}, filter = false, delay = 0) => {
|
|
10
|
+
const testsToRun = filter ? Object.keys(tests).filter(name => name.trim().toLowerCase().includes(filter.trim().toLowerCase())) : Object.keys(tests);
|
|
11
|
+
if(!testsToRun.length) throw new Error('No tests found matching the filter');
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Test Results Structure
|
|
15
|
+
*/
|
|
16
|
+
const results = {
|
|
17
|
+
beforeAllLogs: [],
|
|
18
|
+
tests: {},
|
|
19
|
+
afterAllLogs: []
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* Execute beforeAll Hook
|
|
24
|
+
*/
|
|
25
|
+
await beforeAll(message => {
|
|
26
|
+
results.beforeAllLogs.push({
|
|
27
|
+
message,
|
|
28
|
+
type: 'log',
|
|
29
|
+
level: 3
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Execute Individual Tests
|
|
35
|
+
*/
|
|
36
|
+
for (const name of testsToRun) {
|
|
37
|
+
const result = {
|
|
38
|
+
logs: [],
|
|
39
|
+
passed: null
|
|
40
|
+
};
|
|
41
|
+
const log = (message, type = 'log', level = 3) => {
|
|
42
|
+
result.logs.push({
|
|
43
|
+
message,
|
|
44
|
+
type,
|
|
45
|
+
level
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
log(`== Starting Test "${name}" ==`, 'progress', 3);
|
|
49
|
+
log('== Before Each ==', 'progress', 3);
|
|
50
|
+
await beforeEach(log);
|
|
51
|
+
await tests[name]({
|
|
52
|
+
log,
|
|
53
|
+
pass: message => {
|
|
54
|
+
result.passed = true;
|
|
55
|
+
log(message, 'pass', 2); // was 3; make visible at NORMAL
|
|
56
|
+
},
|
|
57
|
+
fail: message => {
|
|
58
|
+
result.passed = false;
|
|
59
|
+
log(message, 'fail', 2);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
await afterEach(log);
|
|
63
|
+
log('== After Each ==', 'progress', 3);
|
|
64
|
+
if(!result.passed){ // If the test failed, elevate all logs to a level 2
|
|
65
|
+
result.logs.forEach(log => log.level = 2);
|
|
66
|
+
}
|
|
67
|
+
results.tests[name] = result;
|
|
68
|
+
await wait(delay);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
* Execute afterAll Hook
|
|
73
|
+
*/
|
|
74
|
+
await afterAll(message => {
|
|
75
|
+
results.afterAllLogs.push({
|
|
76
|
+
message,
|
|
77
|
+
type: 'log',
|
|
78
|
+
level: 3
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return results;
|
|
83
|
+
};
|
package/src/utils/logLevels.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
SILENT: 0, // No output except final summary
|
|
3
|
-
MINIMAL: 1, // Only test names and pass/fail status
|
|
4
|
-
NORMAL: 2, // Test names, status, and logs from failed tests
|
|
5
|
-
VERBOSE: 3, // All test output including logs from passing tests
|
|
6
|
-
DEBUG: 4 // Everything including framework internals
|
|
7
|
-
};
|
|
1
|
+
export default {
|
|
2
|
+
SILENT: 0, // No output except final summary
|
|
3
|
+
MINIMAL: 1, // Only test names and pass/fail status
|
|
4
|
+
NORMAL: 2, // Test names, status, and logs from failed tests
|
|
5
|
+
VERBOSE: 3, // All test output including logs from passing tests
|
|
6
|
+
DEBUG: 4 // Everything including framework internals
|
|
7
|
+
};
|
package/test.html
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Test</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<script type="module">
|
|
10
|
-
import runTests from './runTests.js';
|
|
11
|
-
|
|
12
|
-
(async () => {
|
|
13
|
-
try {
|
|
14
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
15
|
-
const testFile = urlParams.get('testFile');
|
|
16
|
-
const testFilter = urlParams.get('testFilter');
|
|
17
|
-
const delay = parseInt(urlParams.get('delay') || '0', 10) || 0;
|
|
18
|
-
if(!testFile){
|
|
19
|
-
throw new Error('No test file provided');
|
|
20
|
-
}
|
|
21
|
-
const testsModule = await import(`/${testFile}`);
|
|
22
|
-
const results = await runTests(testsModule, testFilter, delay);
|
|
23
|
-
window.results = results;
|
|
24
|
-
} catch (error) {
|
|
25
|
-
console.error('Browser test error:', error);
|
|
26
|
-
window.error = error.message || String(error);
|
|
27
|
-
}
|
|
28
|
-
})();
|
|
29
|
-
</script>
|
|
30
|
-
</body>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Test</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<script type="module">
|
|
10
|
+
import runTests from './runTests.js';
|
|
11
|
+
|
|
12
|
+
(async () => {
|
|
13
|
+
try {
|
|
14
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
15
|
+
const testFile = urlParams.get('testFile');
|
|
16
|
+
const testFilter = urlParams.get('testFilter');
|
|
17
|
+
const delay = parseInt(urlParams.get('delay') || '0', 10) || 0;
|
|
18
|
+
if(!testFile){
|
|
19
|
+
throw new Error('No test file provided');
|
|
20
|
+
}
|
|
21
|
+
const testsModule = await import(`/${testFile}`);
|
|
22
|
+
const results = await runTests(testsModule, testFilter, delay);
|
|
23
|
+
window.results = results;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('Browser test error:', error);
|
|
26
|
+
window.error = error.message || String(error);
|
|
27
|
+
}
|
|
28
|
+
})();
|
|
29
|
+
</script>
|
|
30
|
+
</body>
|
|
31
31
|
</html>
|
package/tests/Counter.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
class Counter extends HTMLElement {
|
|
2
|
-
constructor() {
|
|
3
|
-
super();
|
|
4
|
-
this.attachShadow({ mode: 'open' });
|
|
5
|
-
this.count = 0;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
connectedCallback() {
|
|
9
|
-
this.render();
|
|
10
|
-
this.shadowRoot.querySelector('button').addEventListener('click', this.increment);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
disconnectedCallback() {
|
|
14
|
-
const button = this.shadowRoot.querySelector('button');
|
|
15
|
-
if (button) {
|
|
16
|
-
button.removeEventListener('click', this.increment);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
increment = () => {
|
|
21
|
-
this.count++;
|
|
22
|
-
this.render();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
render() {
|
|
26
|
-
this.shadowRoot.innerHTML = `
|
|
27
|
-
<button>Increment</button>
|
|
28
|
-
<div>Count: ${this.count}</div>
|
|
29
|
-
`;
|
|
30
|
-
this.shadowRoot.querySelector('button').addEventListener('click', this.increment);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
1
|
+
class Counter extends HTMLElement {
|
|
2
|
+
constructor() {
|
|
3
|
+
super();
|
|
4
|
+
this.attachShadow({ mode: 'open' });
|
|
5
|
+
this.count = 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
connectedCallback() {
|
|
9
|
+
this.render();
|
|
10
|
+
this.shadowRoot.querySelector('button').addEventListener('click', this.increment);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
disconnectedCallback() {
|
|
14
|
+
const button = this.shadowRoot.querySelector('button');
|
|
15
|
+
if (button) {
|
|
16
|
+
button.removeEventListener('click', this.increment);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
increment = () => {
|
|
21
|
+
this.count++;
|
|
22
|
+
this.render();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
render() {
|
|
26
|
+
this.shadowRoot.innerHTML = `
|
|
27
|
+
<button>Increment</button>
|
|
28
|
+
<div>Count: ${this.count}</div>
|
|
29
|
+
`;
|
|
30
|
+
this.shadowRoot.querySelector('button').addEventListener('click', this.increment);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
34
|
customElements.define('my-counter', Counter);
|