kempo-testing-framework 1.3.6 → 1.3.7

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/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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo-testing-framework",
3
- "version": "1.3.6",
3
+ "version": "1.3.7",
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>