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.
- package/.github/workflows/publish-major.yml +39 -0
- package/.github/workflows/publish-minor.yml +39 -0
- package/.github/workflows/{publish-npm.yml → publish-patch.yml} +1 -1
- package/README.md +57 -0
- package/gui/index.html +6 -29
- package/package.json +1 -1
- package/src/runBrowserTests.js +62 -5
- package/tests/custom-page.browser-test.js +12 -0
- package/tests/custom-test-page.html +11 -0
|
@@ -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 }}
|
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="
|
|
56
|
-
<div id="universalTestsEmpty" class="
|
|
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="
|
|
62
|
-
<div id="nodeTestsEmpty" class="
|
|
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="
|
|
68
|
-
<div id="browserTestsEmpty" class="
|
|
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.
|
|
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": {
|
package/src/runBrowserTests.js
CHANGED
|
@@ -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(
|
|
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
|
+
};
|