kempo-testing-framework 1.3.6 → 1.3.8

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.
@@ -0,0 +1,39 @@
1
+ name: Publish Major Version to npmjs
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ jobs:
7
+ publish:
8
+ runs-on: ubuntu-latest
9
+ permissions:
10
+ contents: write
11
+ id-token: write # enables npm provenance
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ with:
15
+ token: ${{ secrets.GITHUB_TOKEN }}
16
+
17
+ # Setup Node and create an .npmrc that uses the token from NODE_AUTH_TOKEN
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: '20.x'
21
+ registry-url: 'https://registry.npmjs.org'
22
+
23
+ - run: npm ci
24
+
25
+ # Auto-increment major version, commit, and push
26
+ - name: Bump major version
27
+ run: |
28
+ git config --global user.name "github-actions"
29
+ git config --global user.email "github-actions@github.com"
30
+ npm version major --no-git-tag-version
31
+ git add package.json package-lock.json || true
32
+ git commit -m "ci: bump major version [skip ci]" || echo "No changes to commit"
33
+ git push origin HEAD:main || echo "No changes to push"
34
+ env:
35
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36
+
37
+ - run: npm publish --provenance --access public
38
+ env:
39
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,39 @@
1
+ name: Publish Minor Version to npmjs
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ jobs:
7
+ publish:
8
+ runs-on: ubuntu-latest
9
+ permissions:
10
+ contents: write
11
+ id-token: write # enables npm provenance
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ with:
15
+ token: ${{ secrets.GITHUB_TOKEN }}
16
+
17
+ # Setup Node and create an .npmrc that uses the token from NODE_AUTH_TOKEN
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: '20.x'
21
+ registry-url: 'https://registry.npmjs.org'
22
+
23
+ - run: npm ci
24
+
25
+ # Auto-increment minor version, commit, and push
26
+ - name: Bump minor version
27
+ run: |
28
+ git config --global user.name "github-actions"
29
+ git config --global user.email "github-actions@github.com"
30
+ npm version minor --no-git-tag-version
31
+ git add package.json package-lock.json || true
32
+ git commit -m "ci: bump minor version [skip ci]" || echo "No changes to commit"
33
+ git push origin HEAD:main || echo "No changes to push"
34
+ env:
35
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36
+
37
+ - run: npm publish --provenance --access public
38
+ env:
39
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,5 +1,5 @@
1
1
 
2
- name: Publish Package to npmjs
2
+ name: Publish Patch Version to npmjs
3
3
 
4
4
  on:
5
5
  push:
package/README.md CHANGED
@@ -66,8 +66,65 @@ Test files can export the following optional lifecycle functions:
66
66
  - **`beforeEach`** - Runs before each individual test
67
67
  - **`afterEach`** - Runs after each individual test
68
68
 
69
+
69
70
  **Note:** All test functions and lifecycle callbacks can be `async` functions if you need to await asynchronous operations.
70
71
 
72
+ ### Custom Test Pages for Browser Tests
73
+
74
+ You can specify a custom HTML page for your browser tests by exporting a `page` string in your test file. This is useful for testing in a specific static environment or reusing a custom fixture across multiple test files.
75
+
76
+ - The path should be relative to the test file location.
77
+ - If no `page` export is present, the default test page (`test.html`) will be used.
78
+
79
+ #### Example
80
+
81
+ Suppose you have the following files in your `tests` directory:
82
+ - `custom-test-page.html`
83
+ - `custom-page.browser-test.js`
84
+
85
+ Your test file:
86
+ ```javascript
87
+ // tests/custom-page.browser-test.js
88
+ export const page = './custom-test-page.html';
89
+
90
+ export default {
91
+ 'should find custom element in custom page': ({ pass, fail }) => {
92
+ const el = document.getElementById('custom-test-element');
93
+ if (el && el.textContent === 'Hello from custom page!') {
94
+ pass('Custom element found and content matches');
95
+ } else {
96
+ fail('Custom element not found or content mismatch');
97
+ }
98
+ }
99
+ };
100
+ ```
101
+
102
+ Your custom HTML page:
103
+ ```html
104
+ <!-- tests/custom-test-page.html -->
105
+ <!DOCTYPE html>
106
+ <html lang="en">
107
+ <head>
108
+ <meta charset="UTF-8">
109
+ <title>Custom Test Page</title>
110
+ </head>
111
+ <body>
112
+ <div id="custom-test-element">Hello from custom page!</div>
113
+ <!-- The test runner will inject scripts here -->
114
+ </body>
115
+ </html>
116
+ ```
117
+
118
+ When you run:
119
+ ```bash
120
+ npx kempo-test custom-page
121
+ ```
122
+ Kempo will serve your custom HTML page for this test file and inject the test runner, so your tests run in the context of your custom page.
123
+
124
+ You can share the same custom page across multiple test files by exporting the appropriate relative path in each file.
125
+
126
+ ---
127
+
71
128
  ## Example Test File
72
129
 
73
130
  `[name].test.js`, `[name].browser-test.js`, or `[name].node-test.js`... they all should look exactly the same.
package/gui/index.html CHANGED
@@ -5,29 +5,6 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Kempo Testing Library GUI</title>
7
7
  <link rel="stylesheet" href="/kempo.css" />
8
- <style>
9
- .loading-state {
10
- padding: 1rem;
11
- text-align: center;
12
- color: #666;
13
- font-style: italic;
14
- }
15
-
16
- .empty-state {
17
- padding: 1rem;
18
- color: #888;
19
- background-color: #f5f5f5;
20
- border-radius: 4px;
21
- margin: 0.5rem 0;
22
- }
23
-
24
- .empty-state code {
25
- background-color: #e8e8e8;
26
- padding: 0.2rem 0.4rem;
27
- border-radius: 3px;
28
- font-family: monospace;
29
- }
30
- </style>
31
8
  </head>
32
9
  <body>
33
10
  <main>
@@ -52,20 +29,20 @@
52
29
  <div id="universalTestsContainer">
53
30
  <h2>Universal Tests</h2>
54
31
  <p>Tests that run in both Node and Browser environments</p>
55
- <div id="universalTestsLoading" class="loading-state">Loading universal tests...</div>
56
- <div id="universalTestsEmpty" class="empty-state" style="display: none;">No universal tests found. Create test files with <code>.test.js</code> extension to run tests in both environments.</div>
32
+ <div id="universalTestsLoading" class="p ta-center tc-muted" style="font-style: italic;">Loading universal tests...</div>
33
+ <div id="universalTestsEmpty" class="p bg-alt r my" style="display: none;">No universal tests found. Create test files with <code>.test.js</code> extension to run tests in both environments.</div>
57
34
  <div id="universalTests"></div>
58
35
  </div>
59
36
  <div id="nodeTestsContainer">
60
37
  <h2>Node Tests</h2>
61
- <div id="nodeTestsLoading" class="loading-state">Loading Node tests...</div>
62
- <div id="nodeTestsEmpty" class="empty-state" style="display: none;">No Node-only tests found. Create test files with <code>.node-test.js</code> extension to run tests only in Node.</div>
38
+ <div id="nodeTestsLoading" class="p ta-center tc-muted" style="font-style: italic;">Loading Node tests...</div>
39
+ <div id="nodeTestsEmpty" class="p bg-alt r my" style="display: none;">No Node-only tests found. Create test files with <code>.node-test.js</code> extension to run tests only in Node.</div>
63
40
  <div id="nodeTests"></div>
64
41
  </div>
65
42
  <div id="browserTestsContainer">
66
43
  <h2>Browser Tests</h2>
67
- <div id="browserTestsLoading" class="loading-state">Loading browser tests...</div>
68
- <div id="browserTestsEmpty" class="empty-state" style="display: none;">No browser-only tests found. Create test files with <code>.browser-test.js</code> extension to run tests only in the browser.</div>
44
+ <div id="browserTestsLoading" class="p ta-center tc-muted" style="font-style: italic;">Loading browser tests...</div>
45
+ <div id="browserTestsEmpty" class="p bg-alt r my" style="display: none;">No browser-only tests found. Create test files with <code>.browser-test.js</code> extension to run tests only in the browser.</div>
69
46
  <div id="browserTests"></div>
70
47
  </div>
71
48
  </ktf-test-framework>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo-testing-framework",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "description": "The Kempo Testing Framework is a simple testing framework built on the principle that code intended to be ran in the browser should be tested in the browser, code intended to be ran in Node should be tested in Node, and code intended to be ran in both should be tested in both. No mocking, no complexity—just testing.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,6 +1,8 @@
1
1
  import { startServer, stopServer } from './browserTestServer.js';
2
2
  import puppeteer from 'puppeteer';
3
3
  import LOG_LEVELS from './utils/logLevels.js';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
4
6
 
5
7
  export default async ({
6
8
  testFile,
@@ -14,11 +16,42 @@ export default async ({
14
16
  * Start Test Server
15
17
  */
16
18
  const url = await startServer(port);
17
-
19
+
18
20
  if(logLevel >= LOG_LEVELS.VERBOSE){
19
21
  console.log(`\x1b[90m Browser test server started at ${url}\x1b[0m`);
20
22
  }
21
-
23
+
24
+
25
+ // Setup __dirname for ESM
26
+ const __filename = fileURLToPath(import.meta.url);
27
+ const __dirname = path.dirname(__filename);
28
+
29
+ // Load the test module to check for 'page' export
30
+ let customPage = null;
31
+ try {
32
+ const testFilePath = path.resolve(process.cwd(), testFile.replace(/\//g, path.sep));
33
+ const testModule = await import(`file://${testFilePath}`);
34
+ if (typeof testModule.page === 'string') {
35
+ customPage = testModule.page;
36
+ }
37
+ } catch (e) {
38
+ // Ignore errors, fallback to default page
39
+ }
40
+
41
+ // Determine which HTML page to serve
42
+ let pageUrl;
43
+ if (customPage) {
44
+ // Resolve custom page relative to test file location
45
+ const testDir = path.dirname(testFile);
46
+ const customPagePath = path.join(testDir, customPage);
47
+ pageUrl = `${url.replace(/\/$/, '')}/${customPagePath.replace(/\\/g, '/')}`;
48
+ if(logLevel >= LOG_LEVELS.VERBOSE){
49
+ console.log(`\x1b[90m Using custom test page: ${customPagePath}\x1b[0m`);
50
+ }
51
+ } else {
52
+ pageUrl = `${url}?testFile=${testFile}&testFilter=${filter}&delay=${delayMs}`;
53
+ }
54
+
22
55
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));
23
56
  try {
24
57
  /*
@@ -45,9 +78,33 @@ export default async ({
45
78
  await sleep(delayMs);
46
79
  }
47
80
 
48
- await page.goto(`${url}?testFile=${testFile}&testFilter=${filter}&delay=${delayMs}`);
81
+ await page.goto(pageUrl);
49
82
  await page.waitForFunction(() => document.readyState === 'complete' || document.readyState === 'interactive');
50
-
83
+
84
+ // If using a custom page, inject the test runner script
85
+ if (customPage) {
86
+ // Inject a script that mimics test.html: loads runTests.js, imports the test file, and sets window.results
87
+ await page.addScriptTag({
88
+ content: `
89
+ (async () => {
90
+ try {
91
+ const runTestsModule = await import('/src/runTests.js');
92
+ const urlParams = new URLSearchParams(window.location.search);
93
+ const testFile = urlParams.get('testFile') || '${testFile}';
94
+ const testFilter = urlParams.get('testFilter') || '';
95
+ const delay = parseInt(urlParams.get('delay') || '0', 10) || 0;
96
+ const testsModule = await import('/' + testFile);
97
+ const results = await runTestsModule.default(testsModule, testFilter, delay);
98
+ window.results = results;
99
+ } catch (error) {
100
+ console.error('Browser test error:', error);
101
+ window.error = error.message || String(error);
102
+ }
103
+ })();
104
+ `
105
+ });
106
+ }
107
+
51
108
  // Ensure the test page has focus when browser is visible
52
109
  if (showBrowser) {
53
110
  await page.bringToFront();
@@ -55,7 +112,7 @@ export default async ({
55
112
  console.log(`\x1b[90m Bringing test page to front\x1b[0m`);
56
113
  }
57
114
  }
58
-
115
+
59
116
  // Check for any errors first
60
117
  const hasError = await page.evaluate(() => window.error !== undefined);
61
118
  if (hasError) {
@@ -0,0 +1,12 @@
1
+ export const page = './custom-test-page.html';
2
+
3
+ export default {
4
+ 'should find custom element in custom page': ({ pass, fail }) => {
5
+ const el = document.getElementById('custom-test-element');
6
+ if (el && el.textContent === 'Hello from custom page!') {
7
+ pass('Custom element found and content matches');
8
+ } else {
9
+ fail('Custom element not found or content mismatch');
10
+ }
11
+ }
12
+ };
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Custom Test Page</title>
6
+ </head>
7
+ <body>
8
+ <div id="custom-test-element">Hello from custom page!</div>
9
+ <!-- The test runner will inject scripts here -->
10
+ </body>
11
+ </html>