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.
Files changed (54) hide show
  1. package/.github/copilot-instructions.md +105 -105
  2. package/.github/workflows/publish-npm.yml +29 -0
  3. package/CONTRIBUTING.md +107 -107
  4. package/gui/components/Collapsible.js +54 -54
  5. package/gui/components/Icon.js +151 -151
  6. package/gui/components/Logs.js +73 -73
  7. package/gui/components/SettingCheckbox.js +42 -42
  8. package/gui/components/SettingNumber.js +77 -77
  9. package/gui/components/SettingSelect.js +67 -67
  10. package/gui/components/TestFramework.js +378 -378
  11. package/gui/components/TestSuite.js +181 -181
  12. package/gui/components/TestSummary.js +189 -189
  13. package/gui/components/Theme.js +40 -40
  14. package/gui/components/settingsStore.js +46 -46
  15. package/gui/icons/fail.svg +1 -1
  16. package/gui/icons/pass.svg +1 -1
  17. package/gui/icons/running.svg +1 -1
  18. package/gui/icons/settings.svg +1 -1
  19. package/package.json +6 -1
  20. package/src/browserTestServer.js +115 -115
  21. package/src/cli.js +198 -198
  22. package/src/gui.js +15 -2
  23. package/src/runBrowserTests.js +87 -87
  24. package/src/runTestFiles.js +94 -94
  25. package/src/runTests.js +83 -83
  26. package/src/utils/logLevels.js +7 -7
  27. package/test.html +30 -30
  28. package/tests/Counter.js +33 -33
  29. package/tests/cli-flags.node-test.js +54 -54
  30. package/tests/cli-help.node-test.js +61 -61
  31. package/tests/cli-loglevel.node-test.js +40 -40
  32. package/tests/collapsible.browser-test.js +49 -49
  33. package/tests/counter.browser-test.js +140 -140
  34. package/tests/example.node-test.js +103 -103
  35. package/tests/fibonacci.js +92 -92
  36. package/tests/fibonacci.test.js +285 -285
  37. package/tests/icon.browser-test.js +54 -54
  38. package/tests/logs.browser-test.js +47 -47
  39. package/tests/setting-checkbox.browser-test.js +48 -48
  40. package/tests/setting-number.browser-test.js +54 -54
  41. package/tests/setting-select.browser-test.js +47 -47
  42. package/tests/settings-store.browser-test.js +26 -26
  43. package/tests/src-browserTestServer.node-test.js +47 -47
  44. package/tests/src-cli.node-test.js +32 -32
  45. package/tests/src-findTests.node-test.js +41 -41
  46. package/tests/src-logLevels.node-test.js +29 -29
  47. package/tests/src-runBrowserTests.node-test.js +41 -41
  48. package/tests/src-runTestFiles.node-test.js +42 -42
  49. package/tests/src-runTests.node-test.js +56 -56
  50. package/tests/test-framework.browser-test.js +65 -65
  51. package/tests/test-summary.browser-test.js +78 -78
  52. package/tests/test.browser-test.js +56 -56
  53. package/tests/testfile.browser-test.js +60 -60
  54. package/tests/theme.browser-test.js +38 -38
@@ -1,47 +1,47 @@
1
- import http from 'http';
2
- import { startServer, stopServer } from '../src/browserTestServer.js';
3
-
4
- const get = (port, path) => new Promise((resolve, reject) => {
5
- const req = http.request({ hostname: 'localhost', port, path, method: 'GET' }, (res) => {
6
- let data = '';
7
- res.on('data', chunk => { data += chunk.toString(); });
8
- res.on('end', () => resolve({ status: res.statusCode, body: data }));
9
- });
10
- req.on('error', reject);
11
- req.end();
12
- });
13
-
14
- export default {
15
- 'serves index html at /': async ({ pass, fail, log }) => {
16
- const port = 3101;
17
- try {
18
- const url = await startServer(port);
19
- const { status, body } = await get(port, '/');
20
- log(`GET / -> ${status}`);
21
- if (status === 200 && /<title>Test<\/title>/.test(body)) pass('Browser test server served index.html');
22
- else fail(`Bad response from /: status=${status} body=${body?.slice(0,80)}`);
23
- } catch (e) {
24
- fail(e.stack || String(e));
25
- } finally {
26
- await stopServer();
27
- }
28
- },
29
- 'serves test runner script and static files, 404s missing': async ({ pass, fail, log }) => {
30
- const port = 3101;
31
- try {
32
- await startServer(port);
33
- const a = await get(port, '/runTests.js');
34
- const b = await get(port, '/tests/counter.browser-test.js');
35
- const c = await get(port, '/does-not-exist.js');
36
- log(`Statuses: /runTests.js=${a.status} /tests/counter.browser-test.js=${b.status} /does-not-exist.js=${c.status}`);
37
- const ok = a.status === 200 && /export default|const wait/.test(a.body)
38
- && b.status === 200 && /Counter component/.test(b.body)
39
- && c.status === 404;
40
- ok ? pass('Static and test files served; missing files 404 as expected') : fail(`Unexpected statuses: a=${a.status}, b=${b.status}, c=${c.status}`);
41
- } catch (e) {
42
- fail(e.stack || String(e));
43
- } finally {
44
- await stopServer();
45
- }
46
- }
47
- };
1
+ import http from 'http';
2
+ import { startServer, stopServer } from '../src/browserTestServer.js';
3
+
4
+ const get = (port, path) => new Promise((resolve, reject) => {
5
+ const req = http.request({ hostname: 'localhost', port, path, method: 'GET' }, (res) => {
6
+ let data = '';
7
+ res.on('data', chunk => { data += chunk.toString(); });
8
+ res.on('end', () => resolve({ status: res.statusCode, body: data }));
9
+ });
10
+ req.on('error', reject);
11
+ req.end();
12
+ });
13
+
14
+ export default {
15
+ 'serves index html at /': async ({ pass, fail, log }) => {
16
+ const port = 3101;
17
+ try {
18
+ const url = await startServer(port);
19
+ const { status, body } = await get(port, '/');
20
+ log(`GET / -> ${status}`);
21
+ if (status === 200 && /<title>Test<\/title>/.test(body)) pass('Browser test server served index.html');
22
+ else fail(`Bad response from /: status=${status} body=${body?.slice(0,80)}`);
23
+ } catch (e) {
24
+ fail(e.stack || String(e));
25
+ } finally {
26
+ await stopServer();
27
+ }
28
+ },
29
+ 'serves test runner script and static files, 404s missing': async ({ pass, fail, log }) => {
30
+ const port = 3101;
31
+ try {
32
+ await startServer(port);
33
+ const a = await get(port, '/runTests.js');
34
+ const b = await get(port, '/tests/counter.browser-test.js');
35
+ const c = await get(port, '/does-not-exist.js');
36
+ log(`Statuses: /runTests.js=${a.status} /tests/counter.browser-test.js=${b.status} /does-not-exist.js=${c.status}`);
37
+ const ok = a.status === 200 && /export default|const wait/.test(a.body)
38
+ && b.status === 200 && /Counter component/.test(b.body)
39
+ && c.status === 404;
40
+ ok ? pass('Static and test files served; missing files 404 as expected') : fail(`Unexpected statuses: a=${a.status}, b=${b.status}, c=${c.status}`);
41
+ } catch (e) {
42
+ fail(e.stack || String(e));
43
+ } finally {
44
+ await stopServer();
45
+ }
46
+ }
47
+ };
@@ -1,32 +1,32 @@
1
- import { spawn } from 'child_process';
2
-
3
- const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
4
-
5
- const run = (args, timeoutMs = 10000) => new Promise((resolve) => {
6
- const child = spawn(process.execPath, ['index.js', ...args], {
7
- cwd: process.cwd(),
8
- stdio: ['ignore', 'pipe', 'pipe']
9
- });
10
- let out = '';
11
- const finish = () => { try { child.kill('SIGTERM'); } catch {} resolve(out); };
12
- child.stdout.on('data', c => { out += c.toString(); });
13
- child.stderr.on('data', c => { out += c.toString(); });
14
- child.on('exit', finish);
15
- setTimeout(finish, timeoutMs);
16
- });
17
-
18
- export default {
19
- 'passes delay to browser runner and prints delay logs in verbose': async ({ pass, fail }) => {
20
- const out = await run(['-b', '-w', '-l', 'verbose', '-d', '200', 'counter']);
21
- const txt = stripAnsi(out);
22
- const ok = /Applying pre-test browser delay: 200ms/.test(txt) && /Applying post-test browser delay: 200ms/.test(txt);
23
- ok ? pass('CLI printed verbose delay logs for browser run') : fail(`Unexpected CLI output:\n${out}`);
24
- },
25
- 'node-only ignores delay and still runs filtered tests': async ({ pass, fail }) => {
26
- const out = await run(['-n', '-l', 'minimal', 'example']);
27
- const txt = stripAnsi(out);
28
- const hasSummary = /=== Test Summary ===/.test(txt) && /Total Tests:\s*\d+/.test(txt);
29
- const noBrowser = !/=== Browser Test Results ===/.test(txt) && !/Running Browser test:/.test(txt);
30
- hasSummary && noBrowser ? pass('Node-only run produced summary without browser section') : fail(`Unexpected CLI output:\n${out}`);
31
- }
32
- };
1
+ import { spawn } from 'child_process';
2
+
3
+ const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
4
+
5
+ const run = (args, timeoutMs = 10000) => new Promise((resolve) => {
6
+ const child = spawn(process.execPath, ['index.js', ...args], {
7
+ cwd: process.cwd(),
8
+ stdio: ['ignore', 'pipe', 'pipe']
9
+ });
10
+ let out = '';
11
+ const finish = () => { try { child.kill('SIGTERM'); } catch {} resolve(out); };
12
+ child.stdout.on('data', c => { out += c.toString(); });
13
+ child.stderr.on('data', c => { out += c.toString(); });
14
+ child.on('exit', finish);
15
+ setTimeout(finish, timeoutMs);
16
+ });
17
+
18
+ export default {
19
+ 'passes delay to browser runner and prints delay logs in verbose': async ({ pass, fail }) => {
20
+ const out = await run(['-b', '-w', '-l', 'verbose', '-d', '200', 'counter']);
21
+ const txt = stripAnsi(out);
22
+ const ok = /Applying pre-test browser delay: 200ms/.test(txt) && /Applying post-test browser delay: 200ms/.test(txt);
23
+ ok ? pass('CLI printed verbose delay logs for browser run') : fail(`Unexpected CLI output:\n${out}`);
24
+ },
25
+ 'node-only ignores delay and still runs filtered tests': async ({ pass, fail }) => {
26
+ const out = await run(['-n', '-l', 'minimal', 'example']);
27
+ const txt = stripAnsi(out);
28
+ const hasSummary = /=== Test Summary ===/.test(txt) && /Total Tests:\s*\d+/.test(txt);
29
+ const noBrowser = !/=== Browser Test Results ===/.test(txt) && !/Running Browser test:/.test(txt);
30
+ hasSummary && noBrowser ? pass('Node-only run produced summary without browser section') : fail(`Unexpected CLI output:\n${out}`);
31
+ }
32
+ };
@@ -1,41 +1,41 @@
1
- import findTests from '../src/findTests.js';
2
-
3
- export default {
4
- 'finds node and browser test files with no filters': async ({ pass, fail, log }) => {
5
- try {
6
- const { nodeTests, browserTests } = await findTests('', '', true, true);
7
- log(`Found node tests: ${nodeTests.length}, browser tests: ${browserTests.length}`);
8
- const hasCounter = browserTests.some(f => f.endsWith('tests/counter.browser-test.js'));
9
- const hasExample = nodeTests.some(f => f.endsWith('tests/example.node-test.js'));
10
- if (hasCounter && hasExample) pass('Discovered expected canonical files (counter + example)');
11
- else fail(`Missing expected files. node: ${JSON.stringify(nodeTests)}, browser: ${JSON.stringify(browserTests)}`);
12
- } catch (e) {
13
- fail(e.stack || String(e));
14
- }
15
- },
16
- 'honors suite filter for file substring': async ({ pass, fail, log }) => {
17
- try {
18
- const { nodeTests, browserTests } = await findTests('counter', '', true, true);
19
- log(`Filtered node tests: ${nodeTests.join(', ')}`);
20
- log(`Filtered browser tests: ${browserTests.join(', ')}`);
21
- const okNode = nodeTests.every(f => f.includes('counter') === true) || nodeTests.length === 0;
22
- const okBrowser = browserTests.every(f => f.includes('counter') === true);
23
- okNode && okBrowser ? pass('Suite filter correctly applied to file names') : fail(`Unexpected filtering. node: ${nodeTests}, browser: ${browserTests}`);
24
- } catch (e) {
25
- fail(e.stack || String(e));
26
- }
27
- },
28
- 'respects environment toggles': async ({ pass, fail, log }) => {
29
- try {
30
- const a = await findTests('', '', false, true); // node only
31
- const b = await findTests('', '', true, false); // browser only
32
- log(`Node-only counts: node=${a.nodeTests.length}, browser=${a.browserTests.length}`);
33
- log(`Browser-only counts: node=${b.nodeTests.length}, browser=${b.browserTests.length}`);
34
- const okA = a.browserTests.length === 0 && a.nodeTests.length > 0;
35
- const okB = b.nodeTests.length === 0 && b.browserTests.length > 0;
36
- okA && okB ? pass('Environment toggles respected (node-only / browser-only)') : fail(`Env mismatch: a=${JSON.stringify(a)}, b=${JSON.stringify(b)}`);
37
- } catch (e) {
38
- fail(e.stack || String(e));
39
- }
40
- }
41
- };
1
+ import findTests from '../src/findTests.js';
2
+
3
+ export default {
4
+ 'finds node and browser test files with no filters': async ({ pass, fail, log }) => {
5
+ try {
6
+ const { nodeTests, browserTests } = await findTests('', '', true, true);
7
+ log(`Found node tests: ${nodeTests.length}, browser tests: ${browserTests.length}`);
8
+ const hasCounter = browserTests.some(f => f.endsWith('tests/counter.browser-test.js'));
9
+ const hasExample = nodeTests.some(f => f.endsWith('tests/example.node-test.js'));
10
+ if (hasCounter && hasExample) pass('Discovered expected canonical files (counter + example)');
11
+ else fail(`Missing expected files. node: ${JSON.stringify(nodeTests)}, browser: ${JSON.stringify(browserTests)}`);
12
+ } catch (e) {
13
+ fail(e.stack || String(e));
14
+ }
15
+ },
16
+ 'honors suite filter for file substring': async ({ pass, fail, log }) => {
17
+ try {
18
+ const { nodeTests, browserTests } = await findTests('counter', '', true, true);
19
+ log(`Filtered node tests: ${nodeTests.join(', ')}`);
20
+ log(`Filtered browser tests: ${browserTests.join(', ')}`);
21
+ const okNode = nodeTests.every(f => f.includes('counter') === true) || nodeTests.length === 0;
22
+ const okBrowser = browserTests.every(f => f.includes('counter') === true);
23
+ okNode && okBrowser ? pass('Suite filter correctly applied to file names') : fail(`Unexpected filtering. node: ${nodeTests}, browser: ${browserTests}`);
24
+ } catch (e) {
25
+ fail(e.stack || String(e));
26
+ }
27
+ },
28
+ 'respects environment toggles': async ({ pass, fail, log }) => {
29
+ try {
30
+ const a = await findTests('', '', false, true); // node only
31
+ const b = await findTests('', '', true, false); // browser only
32
+ log(`Node-only counts: node=${a.nodeTests.length}, browser=${a.browserTests.length}`);
33
+ log(`Browser-only counts: node=${b.nodeTests.length}, browser=${b.browserTests.length}`);
34
+ const okA = a.browserTests.length === 0 && a.nodeTests.length > 0;
35
+ const okB = b.nodeTests.length === 0 && b.browserTests.length > 0;
36
+ okA && okB ? pass('Environment toggles respected (node-only / browser-only)') : fail(`Env mismatch: a=${JSON.stringify(a)}, b=${JSON.stringify(b)}`);
37
+ } catch (e) {
38
+ fail(e.stack || String(e));
39
+ }
40
+ }
41
+ };
@@ -1,29 +1,29 @@
1
- import LOG_LEVELS from '../src/utils/logLevels.js';
2
-
3
- export default {
4
- 'defines expected numeric levels': async ({ pass, fail, log }) => {
5
- try {
6
- const want = { SILENT: 0, MINIMAL: 1, NORMAL: 2, VERBOSE: 3, DEBUG: 4 };
7
- log(`Actual levels: ${JSON.stringify(LOG_LEVELS)}`);
8
- const ok = want.SILENT === LOG_LEVELS.SILENT &&
9
- want.MINIMAL === LOG_LEVELS.MINIMAL &&
10
- want.NORMAL === LOG_LEVELS.NORMAL &&
11
- want.VERBOSE === LOG_LEVELS.VERBOSE &&
12
- want.DEBUG === LOG_LEVELS.DEBUG;
13
- ok ? pass('Log level constants match expected numeric values') : fail(`Unexpected values: ${JSON.stringify(LOG_LEVELS)}`);
14
- } catch (e) {
15
- fail(e.stack || String(e));
16
- }
17
- },
18
- 'level ordering is ascending': async ({ pass, fail, log }) => {
19
- try {
20
- const arr = [LOG_LEVELS.SILENT, LOG_LEVELS.MINIMAL, LOG_LEVELS.NORMAL, LOG_LEVELS.VERBOSE, LOG_LEVELS.DEBUG];
21
- const sorted = [...arr].sort((a,b)=>a-b);
22
- log(`Order: ${arr.join(' < ')}`);
23
- const ok = JSON.stringify(arr) === JSON.stringify(sorted);
24
- ok ? pass('Log levels are in ascending order') : fail(`Not ascending: ${arr}`);
25
- } catch (e) {
26
- fail(e.stack || String(e));
27
- }
28
- }
29
- };
1
+ import LOG_LEVELS from '../src/utils/logLevels.js';
2
+
3
+ export default {
4
+ 'defines expected numeric levels': async ({ pass, fail, log }) => {
5
+ try {
6
+ const want = { SILENT: 0, MINIMAL: 1, NORMAL: 2, VERBOSE: 3, DEBUG: 4 };
7
+ log(`Actual levels: ${JSON.stringify(LOG_LEVELS)}`);
8
+ const ok = want.SILENT === LOG_LEVELS.SILENT &&
9
+ want.MINIMAL === LOG_LEVELS.MINIMAL &&
10
+ want.NORMAL === LOG_LEVELS.NORMAL &&
11
+ want.VERBOSE === LOG_LEVELS.VERBOSE &&
12
+ want.DEBUG === LOG_LEVELS.DEBUG;
13
+ ok ? pass('Log level constants match expected numeric values') : fail(`Unexpected values: ${JSON.stringify(LOG_LEVELS)}`);
14
+ } catch (e) {
15
+ fail(e.stack || String(e));
16
+ }
17
+ },
18
+ 'level ordering is ascending': async ({ pass, fail, log }) => {
19
+ try {
20
+ const arr = [LOG_LEVELS.SILENT, LOG_LEVELS.MINIMAL, LOG_LEVELS.NORMAL, LOG_LEVELS.VERBOSE, LOG_LEVELS.DEBUG];
21
+ const sorted = [...arr].sort((a,b)=>a-b);
22
+ log(`Order: ${arr.join(' < ')}`);
23
+ const ok = JSON.stringify(arr) === JSON.stringify(sorted);
24
+ ok ? pass('Log levels are in ascending order') : fail(`Not ascending: ${arr}`);
25
+ } catch (e) {
26
+ fail(e.stack || String(e));
27
+ }
28
+ }
29
+ };
@@ -1,41 +1,41 @@
1
- import runBrowserTests from '../src/runBrowserTests.js';
2
-
3
- export default {
4
- 'runs a specific browser test headless and returns results': async ({ pass, fail, log }) => {
5
- try {
6
- const res = await runBrowserTests({
7
- testFile: 'tests/counter.browser-test.js',
8
- filter: '',
9
- showBrowser: false,
10
- port: 3111,
11
- logLevel: 2,
12
- delayMs: 0
13
- });
14
- const names = Object.keys(res.tests || {});
15
- log(`Ran browser tests: ${names.join(', ')}`);
16
- if (names.length >= 3 && res.beforeAllLogs?.length >= 1 && res.afterAllLogs?.length >= 1) pass('Headless run produced tests and lifecycle logs');
17
- else fail(`Unexpected browser test results: ${JSON.stringify(res)}`);
18
- } catch (e) {
19
- fail(e.stack || String(e));
20
- }
21
- },
22
- 'applies pre/post delay only when showBrowser is true': async ({ pass, fail, log }) => {
23
- try {
24
- // Use a single test to avoid between-test delays dominating timings
25
- const filter = 'Counter component should be defined';
26
- const delay = 500; // Reduced from 1000ms to avoid timeouts
27
- const start1 = Date.now();
28
- await runBrowserTests({ testFile: 'tests/counter.browser-test.js', filter, showBrowser: false, port: 3112, logLevel: 2, delayMs: delay });
29
- const t1 = Date.now() - start1;
30
- const start2 = Date.now();
31
- await runBrowserTests({ testFile: 'tests/counter.browser-test.js', filter, showBrowser: true, port: 3113, logLevel: 2, delayMs: delay });
32
- const t2 = Date.now() - start2;
33
- // Headful should incur roughly +1000ms (pre+post). Allow slack for env variance.
34
- log(`Timing — headless=${t1}ms, headful=${t2}ms, delta=${t2 - t1}ms`);
35
- if (t2 - t1 >= 800) pass('Headful mode applied pre/post delay as expected');
36
- else fail(`Timing not increased as expected: headless=${t1}ms headful=${t2}ms`);
37
- } catch (e) {
38
- fail(e.stack || String(e));
39
- }
40
- }
41
- };
1
+ import runBrowserTests from '../src/runBrowserTests.js';
2
+
3
+ export default {
4
+ 'runs a specific browser test headless and returns results': async ({ pass, fail, log }) => {
5
+ try {
6
+ const res = await runBrowserTests({
7
+ testFile: 'tests/counter.browser-test.js',
8
+ filter: '',
9
+ showBrowser: false,
10
+ port: 3111,
11
+ logLevel: 2,
12
+ delayMs: 0
13
+ });
14
+ const names = Object.keys(res.tests || {});
15
+ log(`Ran browser tests: ${names.join(', ')}`);
16
+ if (names.length >= 3 && res.beforeAllLogs?.length >= 1 && res.afterAllLogs?.length >= 1) pass('Headless run produced tests and lifecycle logs');
17
+ else fail(`Unexpected browser test results: ${JSON.stringify(res)}`);
18
+ } catch (e) {
19
+ fail(e.stack || String(e));
20
+ }
21
+ },
22
+ 'applies pre/post delay only when showBrowser is true': async ({ pass, fail, log }) => {
23
+ try {
24
+ // Use a single test to avoid between-test delays dominating timings
25
+ const filter = 'Counter component should be defined';
26
+ const delay = 500; // Reduced from 1000ms to avoid timeouts
27
+ const start1 = Date.now();
28
+ await runBrowserTests({ testFile: 'tests/counter.browser-test.js', filter, showBrowser: false, port: 3112, logLevel: 2, delayMs: delay });
29
+ const t1 = Date.now() - start1;
30
+ const start2 = Date.now();
31
+ await runBrowserTests({ testFile: 'tests/counter.browser-test.js', filter, showBrowser: true, port: 3113, logLevel: 2, delayMs: delay });
32
+ const t2 = Date.now() - start2;
33
+ // Headful should incur roughly +1000ms (pre+post). Allow slack for env variance.
34
+ log(`Timing — headless=${t1}ms, headful=${t2}ms, delta=${t2 - t1}ms`);
35
+ if (t2 - t1 >= 800) pass('Headful mode applied pre/post delay as expected');
36
+ else fail(`Timing not increased as expected: headless=${t1}ms headful=${t2}ms`);
37
+ } catch (e) {
38
+ fail(e.stack || String(e));
39
+ }
40
+ }
41
+ };
@@ -1,42 +1,42 @@
1
- import runTestFiles from '../src/runTestFiles.js';
2
-
3
- export default {
4
- 'discovers and runs node tests only when requested': async ({ pass, fail, log }) => {
5
- try {
6
- const { nodeResults, browserResults } = await runTestFiles({
7
- suiteFilter: 'example',
8
- testFilter: '',
9
- shouldRunBrowser: false,
10
- shouldRunNode: true,
11
- showBrowser: false,
12
- port: 3000,
13
- logLevel: 2
14
- });
15
- log(`Node files run: ${Object.keys(nodeResults).length}, browser files run: ${Object.keys(browserResults).length}`);
16
- const onlyNode = Object.keys(browserResults).length === 0 && Object.keys(nodeResults).length >= 1;
17
- onlyNode ? pass('Ran node tests only when requested (browser skipped)') : fail(`Unexpected results: node=${Object.keys(nodeResults)}, browser=${Object.keys(browserResults)}`);
18
- } catch (e) {
19
- fail(e.stack || String(e));
20
- }
21
- },
22
- 'passes filter into node runs': async ({ pass, fail, log }) => {
23
- try {
24
- const { nodeResults } = await runTestFiles({
25
- suiteFilter: 'example',
26
- testFilter: 'async',
27
- shouldRunBrowser: false,
28
- shouldRunNode: true,
29
- showBrowser: false,
30
- port: 3000,
31
- logLevel: 2
32
- });
33
- const [file, res] = Object.entries(nodeResults)[0];
34
- if (!res) return fail('No node results returned');
35
- const names = Object.keys(res.tests || {});
36
- log(`Filtered test names: ${names.join(', ')}`);
37
- names.length === 1 && names[0].toLowerCase().includes('async') ? pass('Per-file test filter applied to node run') : fail(`Unexpected filtered tests: ${names}`);
38
- } catch (e) {
39
- fail(e.stack || String(e));
40
- }
41
- }
42
- };
1
+ import runTestFiles from '../src/runTestFiles.js';
2
+
3
+ export default {
4
+ 'discovers and runs node tests only when requested': async ({ pass, fail, log }) => {
5
+ try {
6
+ const { nodeResults, browserResults } = await runTestFiles({
7
+ suiteFilter: 'example',
8
+ testFilter: '',
9
+ shouldRunBrowser: false,
10
+ shouldRunNode: true,
11
+ showBrowser: false,
12
+ port: 3000,
13
+ logLevel: 2
14
+ });
15
+ log(`Node files run: ${Object.keys(nodeResults).length}, browser files run: ${Object.keys(browserResults).length}`);
16
+ const onlyNode = Object.keys(browserResults).length === 0 && Object.keys(nodeResults).length >= 1;
17
+ onlyNode ? pass('Ran node tests only when requested (browser skipped)') : fail(`Unexpected results: node=${Object.keys(nodeResults)}, browser=${Object.keys(browserResults)}`);
18
+ } catch (e) {
19
+ fail(e.stack || String(e));
20
+ }
21
+ },
22
+ 'passes filter into node runs': async ({ pass, fail, log }) => {
23
+ try {
24
+ const { nodeResults } = await runTestFiles({
25
+ suiteFilter: 'example',
26
+ testFilter: 'async',
27
+ shouldRunBrowser: false,
28
+ shouldRunNode: true,
29
+ showBrowser: false,
30
+ port: 3000,
31
+ logLevel: 2
32
+ });
33
+ const [file, res] = Object.entries(nodeResults)[0];
34
+ if (!res) return fail('No node results returned');
35
+ const names = Object.keys(res.tests || {});
36
+ log(`Filtered test names: ${names.join(', ')}`);
37
+ names.length === 1 && names[0].toLowerCase().includes('async') ? pass('Per-file test filter applied to node run') : fail(`Unexpected filtered tests: ${names}`);
38
+ } catch (e) {
39
+ fail(e.stack || String(e));
40
+ }
41
+ }
42
+ };
@@ -1,56 +1,56 @@
1
- import runTests from '../src/runTests.js';
2
-
3
- const mkSuite = () => ({
4
- beforeAll: async (log) => { log('before all'); },
5
- beforeEach: async (log) => { log('before each'); },
6
- afterEach: async (log) => { log('after each'); },
7
- afterAll: async (log) => { log('after all'); },
8
- default: {
9
- 'one passes': async ({ pass, log }) => { log('running one'); pass('ok'); },
10
- 'two passes': async ({ pass, log }) => { log('running two'); pass('ok'); },
11
- }
12
- });
13
-
14
- export default {
15
- 'runs all tests and records logs': async ({ pass, fail, log }) => {
16
- try {
17
- const res = await runTests(mkSuite(), false, 0);
18
- const names = Object.keys(res.tests);
19
- log(`Discovered tests: ${names.join(', ')}`);
20
- if (names.length !== 2) return fail(`Expected 2 tests, got ${names.length} [${names.join(', ')}]`);
21
- const hasBeforeAll = res.beforeAllLogs.some(l => l.message.includes('before all'));
22
- const hasAfterAll = res.afterAllLogs.some(l => l.message.includes('after all'));
23
- const eachs = Object.values(res.tests).flatMap(r => r.logs.map(l=>l.message));
24
- const hasBefores = eachs.some(m => m.includes('== Before Each =='));
25
- const hasAfters = eachs.some(m => m.includes('== After Each =='));
26
- log(`Lifecycle logs — beforeAll:${hasBeforeAll} afterAll:${hasAfterAll} beforeEach:${hasBefores} afterEach:${hasAfters}`);
27
- if (hasBeforeAll && hasAfterAll && hasBefores && hasAfters) pass('Recorded lifecycle and per-test logs as expected');
28
- else fail('Missing expected lifecycle log sections');
29
- } catch (e) {
30
- fail(e.stack || String(e));
31
- }
32
- },
33
- 'applies per-test delay': async ({ pass, fail, log }) => {
34
- try {
35
- const t0 = Date.now();
36
- await runTests(mkSuite(), false, 200);
37
- const elapsed = Date.now() - t0;
38
- log(`Elapsed with delay: ${elapsed}ms`);
39
- if (elapsed >= 200 && elapsed < 800) pass(`Applied per-test delay successfully (~${elapsed}ms >= 200ms)`);
40
- else fail(`Unexpected timing with delay=200ms: ${elapsed}ms`);
41
- } catch (e) {
42
- fail(e.stack || String(e));
43
- }
44
- },
45
- 'filters tests by substring': async ({ pass, fail, log }) => {
46
- try {
47
- const res = await runTests(mkSuite(), 'two', 0);
48
- const names = Object.keys(res.tests);
49
- log(`Filtered tests: ${names.join(', ')}`);
50
- if (names.length === 1 && names[0].includes('two')) pass(`Filtered to expected test: ${names[0]}`);
51
- else fail(`Unexpected filtered set: [${names.join(', ')}]`);
52
- } catch (e) {
53
- fail(e.stack || String(e));
54
- }
55
- }
56
- };
1
+ import runTests from '../src/runTests.js';
2
+
3
+ const mkSuite = () => ({
4
+ beforeAll: async (log) => { log('before all'); },
5
+ beforeEach: async (log) => { log('before each'); },
6
+ afterEach: async (log) => { log('after each'); },
7
+ afterAll: async (log) => { log('after all'); },
8
+ default: {
9
+ 'one passes': async ({ pass, log }) => { log('running one'); pass('ok'); },
10
+ 'two passes': async ({ pass, log }) => { log('running two'); pass('ok'); },
11
+ }
12
+ });
13
+
14
+ export default {
15
+ 'runs all tests and records logs': async ({ pass, fail, log }) => {
16
+ try {
17
+ const res = await runTests(mkSuite(), false, 0);
18
+ const names = Object.keys(res.tests);
19
+ log(`Discovered tests: ${names.join(', ')}`);
20
+ if (names.length !== 2) return fail(`Expected 2 tests, got ${names.length} [${names.join(', ')}]`);
21
+ const hasBeforeAll = res.beforeAllLogs.some(l => l.message.includes('before all'));
22
+ const hasAfterAll = res.afterAllLogs.some(l => l.message.includes('after all'));
23
+ const eachs = Object.values(res.tests).flatMap(r => r.logs.map(l=>l.message));
24
+ const hasBefores = eachs.some(m => m.includes('== Before Each =='));
25
+ const hasAfters = eachs.some(m => m.includes('== After Each =='));
26
+ log(`Lifecycle logs — beforeAll:${hasBeforeAll} afterAll:${hasAfterAll} beforeEach:${hasBefores} afterEach:${hasAfters}`);
27
+ if (hasBeforeAll && hasAfterAll && hasBefores && hasAfters) pass('Recorded lifecycle and per-test logs as expected');
28
+ else fail('Missing expected lifecycle log sections');
29
+ } catch (e) {
30
+ fail(e.stack || String(e));
31
+ }
32
+ },
33
+ 'applies per-test delay': async ({ pass, fail, log }) => {
34
+ try {
35
+ const t0 = Date.now();
36
+ await runTests(mkSuite(), false, 200);
37
+ const elapsed = Date.now() - t0;
38
+ log(`Elapsed with delay: ${elapsed}ms`);
39
+ if (elapsed >= 200 && elapsed < 800) pass(`Applied per-test delay successfully (~${elapsed}ms >= 200ms)`);
40
+ else fail(`Unexpected timing with delay=200ms: ${elapsed}ms`);
41
+ } catch (e) {
42
+ fail(e.stack || String(e));
43
+ }
44
+ },
45
+ 'filters tests by substring': async ({ pass, fail, log }) => {
46
+ try {
47
+ const res = await runTests(mkSuite(), 'two', 0);
48
+ const names = Object.keys(res.tests);
49
+ log(`Filtered tests: ${names.join(', ')}`);
50
+ if (names.length === 1 && names[0].includes('two')) pass(`Filtered to expected test: ${names[0]}`);
51
+ else fail(`Unexpected filtered set: [${names.join(', ')}]`);
52
+ } catch (e) {
53
+ fail(e.stack || String(e));
54
+ }
55
+ }
56
+ };