@nexustools/nwjstestrunner 0.0.0-stage → 1.0.0-beta.1

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 (79) hide show
  1. package/AGENTS.md +21 -0
  2. package/README.md +142 -2
  3. package/bin/nwjstestrunner.js +13 -0
  4. package/dist/cli/nwjs-cli-launcher.d.ts +10 -0
  5. package/dist/cli/nwjs-cli-launcher.d.ts.map +1 -0
  6. package/dist/cli/nwjs-cli-launcher.js +281 -0
  7. package/dist/cli/nwjs-cli-launcher.js.map +1 -0
  8. package/dist/config/harness-config-loader.d.ts +10 -0
  9. package/dist/config/harness-config-loader.d.ts.map +1 -0
  10. package/dist/config/harness-config-loader.js +242 -0
  11. package/dist/config/harness-config-loader.js.map +1 -0
  12. package/dist/core/test-runner.d.ts +61 -0
  13. package/dist/core/test-runner.d.ts.map +1 -0
  14. package/dist/core/test-runner.js +450 -0
  15. package/dist/core/test-runner.js.map +1 -0
  16. package/dist/esm/index.d.mts +2 -0
  17. package/dist/esm/index.mjs +26 -0
  18. package/dist/harness/graphical-overlay-view.d.ts +18 -0
  19. package/dist/harness/graphical-overlay-view.d.ts.map +1 -0
  20. package/dist/harness/graphical-overlay-view.js +213 -0
  21. package/dist/harness/graphical-overlay-view.js.map +1 -0
  22. package/dist/harness/nwjs-graphical-test-harness.d.ts +40 -0
  23. package/dist/harness/nwjs-graphical-test-harness.d.ts.map +1 -0
  24. package/dist/harness/nwjs-graphical-test-harness.js +398 -0
  25. package/dist/harness/nwjs-graphical-test-harness.js.map +1 -0
  26. package/dist/harness/test-http-server.d.ts +12 -0
  27. package/dist/harness/test-http-server.d.ts.map +1 -0
  28. package/dist/harness/test-http-server.js +183 -0
  29. package/dist/harness/test-http-server.js.map +1 -0
  30. package/dist/harness/test-log-writer.d.ts +29 -0
  31. package/dist/harness/test-log-writer.d.ts.map +1 -0
  32. package/dist/harness/test-log-writer.js +173 -0
  33. package/dist/harness/test-log-writer.js.map +1 -0
  34. package/dist/harness/webview-test-controller.d.ts +18 -0
  35. package/dist/harness/webview-test-controller.d.ts.map +1 -0
  36. package/dist/harness/webview-test-controller.js +163 -0
  37. package/dist/harness/webview-test-controller.js.map +1 -0
  38. package/dist/index.d.ts +13 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +52 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/matchers/deep-equality-helper.d.ts +10 -0
  43. package/dist/matchers/deep-equality-helper.d.ts.map +1 -0
  44. package/dist/matchers/deep-equality-helper.js +188 -0
  45. package/dist/matchers/deep-equality-helper.js.map +1 -0
  46. package/dist/matchers/expect-matcher.d.ts +7 -0
  47. package/dist/matchers/expect-matcher.d.ts.map +1 -0
  48. package/dist/matchers/expect-matcher.js +261 -0
  49. package/dist/matchers/expect-matcher.js.map +1 -0
  50. package/dist/mock/jest-mock-factory.d.ts +20 -0
  51. package/dist/mock/jest-mock-factory.d.ts.map +1 -0
  52. package/dist/mock/jest-mock-factory.js +141 -0
  53. package/dist/mock/jest-mock-factory.js.map +1 -0
  54. package/dist/tests/NwjsTestRunnerSuites.d.ts +3 -0
  55. package/dist/tests/NwjsTestRunnerSuites.d.ts.map +1 -0
  56. package/dist/tests/NwjsTestRunnerSuites.js +266 -0
  57. package/dist/tests/NwjsTestRunnerSuites.js.map +1 -0
  58. package/dist/types/test-runner-types.d.ts +321 -0
  59. package/dist/types/test-runner-types.d.ts.map +1 -0
  60. package/dist/types/test-runner-types.js +3 -0
  61. package/dist/types/test-runner-types.js.map +1 -0
  62. package/package.json +56 -4
  63. package/runner/demo-client.html +70 -0
  64. package/runner/index.html +223 -0
  65. package/runner/package.json +27 -0
  66. package/src/cli/nwjs-cli-launcher.ts +243 -0
  67. package/src/config/harness-config-loader.ts +214 -0
  68. package/src/core/test-runner.ts +553 -0
  69. package/src/harness/graphical-overlay-view.ts +219 -0
  70. package/src/harness/nwjs-graphical-test-harness.ts +468 -0
  71. package/src/harness/test-http-server.ts +162 -0
  72. package/src/harness/test-log-writer.ts +154 -0
  73. package/src/harness/webview-test-controller.ts +192 -0
  74. package/src/index.ts +28 -0
  75. package/src/matchers/deep-equality-helper.ts +193 -0
  76. package/src/matchers/expect-matcher.ts +361 -0
  77. package/src/mock/jest-mock-factory.ts +170 -0
  78. package/src/tests/NwjsTestRunnerSuites.ts +267 -0
  79. package/src/types/test-runner-types.ts +335 -0
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GraphicalOverlayView = void 0;
4
+ class GraphicalOverlayView {
5
+ options;
6
+ webviewEl = null;
7
+ titleEl = null;
8
+ statusEl = null;
9
+ metricCurrentEl = null;
10
+ metricPassedEl = null;
11
+ metricFailedEl = null;
12
+ progressFillEl = null;
13
+ constructor(options = {}) {
14
+ this.options = options;
15
+ }
16
+ mount(doc = document) {
17
+ const title = this.options.title || '🧪 NW.js Live Webview Test Runner';
18
+ const enableWebview = this.options.enableWebview ?? true;
19
+ const partition = this.options.webviewPartition || 'trusted';
20
+ const allowNw = this.options.allowNwInWebview ?? true;
21
+ if (!doc.getElementById('nwjstestrunner-styles')) {
22
+ const styleEl = doc.createElement('style');
23
+ styleEl.id = 'nwjstestrunner-styles';
24
+ styleEl.textContent = `
25
+ body, html {
26
+ margin: 0;
27
+ padding: 0;
28
+ width: 100vw;
29
+ height: 100vh;
30
+ background: #11111b;
31
+ overflow: hidden;
32
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
33
+ }
34
+ #webview-container {
35
+ width: 100vw;
36
+ height: 100vh;
37
+ position: absolute;
38
+ top: 0;
39
+ left: 0;
40
+ z-index: 1;
41
+ }
42
+ webview {
43
+ width: 100%;
44
+ height: 100%;
45
+ border: none;
46
+ opacity: 0.95;
47
+ }
48
+ #test-runner-overlay {
49
+ pointer-events: none;
50
+ position: fixed;
51
+ top: 0;
52
+ left: 0;
53
+ width: 100vw;
54
+ height: 100vh;
55
+ z-index: 9999;
56
+ background: rgba(0, 0, 0, 0.28);
57
+ display: flex;
58
+ flex-direction: column;
59
+ align-items: center;
60
+ justify-content: center;
61
+ color: #cdd6f4;
62
+ text-align: center;
63
+ }
64
+ .overlay-card {
65
+ background: #1e1e2e;
66
+ border: 1px solid #313244;
67
+ padding: 30px 45px;
68
+ border-radius: 12px;
69
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
70
+ min-width: 440px;
71
+ max-width: 560px;
72
+ }
73
+ .overlay-title {
74
+ font-size: 22px;
75
+ font-weight: bold;
76
+ margin-bottom: 12px;
77
+ color: #cba6f7;
78
+ }
79
+ .overlay-status {
80
+ font-size: 14px;
81
+ margin-bottom: 14px;
82
+ color: #89b4fa;
83
+ font-family: monospace;
84
+ white-space: nowrap;
85
+ overflow: hidden;
86
+ text-overflow: ellipsis;
87
+ max-width: 460px;
88
+ }
89
+ .overlay-progress-track {
90
+ width: 100%;
91
+ height: 6px;
92
+ background: #313244;
93
+ border-radius: 3px;
94
+ overflow: hidden;
95
+ margin-bottom: 16px;
96
+ }
97
+ .overlay-progress-fill {
98
+ width: 0%;
99
+ height: 100%;
100
+ background: linear-gradient(90deg, #89b4fa, #cba6f7);
101
+ transition: width 0.12s ease-out;
102
+ }
103
+ .overlay-metric {
104
+ font-size: 18px;
105
+ margin: 8px 0;
106
+ font-weight: 600;
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: space-between;
110
+ }
111
+ .status-running { color: #89b4fa; }
112
+ .status-passed { color: #a6e3a1; }
113
+ .status-failed { color: #f38ba8; }
114
+ `;
115
+ doc.head.appendChild(styleEl);
116
+ }
117
+ if (enableWebview && !doc.getElementById('client-webview')) {
118
+ let container = doc.getElementById('webview-container');
119
+ if (!container) {
120
+ container = doc.createElement('div');
121
+ container.id = 'webview-container';
122
+ doc.body.appendChild(container);
123
+ }
124
+ const wv = doc.createElement('webview');
125
+ wv.id = 'client-webview';
126
+ if (allowNw) {
127
+ wv.setAttribute('allownw', '');
128
+ }
129
+ wv.setAttribute('partition', partition);
130
+ container.appendChild(wv);
131
+ }
132
+ if (!doc.getElementById('test-runner-overlay')) {
133
+ const overlay = doc.createElement('div');
134
+ overlay.id = 'test-runner-overlay';
135
+ overlay.innerHTML = `
136
+ <div class="overlay-card">
137
+ <div id="overlay-title" class="overlay-title"></div>
138
+ <div id="overlay-status" class="overlay-status">Initializing test harness...</div>
139
+ <div class="overlay-progress-track">
140
+ <div id="overlay-progress-fill" class="overlay-progress-fill"></div>
141
+ </div>
142
+ <div class="overlay-metric status-running">
143
+ <span>Completed:</span>
144
+ <span id="metric-current">0 of 0</span>
145
+ </div>
146
+ <div class="overlay-metric status-passed">
147
+ <span>✅ Tests Passed:</span>
148
+ <span id="metric-passed">0</span>
149
+ </div>
150
+ <div class="overlay-metric status-failed">
151
+ <span>❌ Tests Failed:</span>
152
+ <span id="metric-failed">0</span>
153
+ </div>
154
+ </div>
155
+ `;
156
+ doc.body.appendChild(overlay);
157
+ }
158
+ this.webviewEl = doc.getElementById('client-webview');
159
+ this.titleEl =
160
+ doc.getElementById('overlay-title') ||
161
+ doc.querySelector('#test-runner-overlay .overlay-title');
162
+ this.statusEl = doc.getElementById('overlay-status');
163
+ this.metricCurrentEl = doc.getElementById('metric-current');
164
+ this.metricPassedEl = doc.getElementById('metric-passed');
165
+ this.metricFailedEl = doc.getElementById('metric-failed');
166
+ this.progressFillEl = doc.getElementById('overlay-progress-fill');
167
+ if (this.titleEl && this.options.title) {
168
+ this.titleEl.textContent = title;
169
+ }
170
+ try {
171
+ if (this.options.title && doc.title !== this.options.title) {
172
+ doc.title = this.options.title;
173
+ }
174
+ }
175
+ catch (_e) { }
176
+ }
177
+ getWebviewElement() {
178
+ return this.webviewEl;
179
+ }
180
+ setTitle(title) {
181
+ if (this.titleEl) {
182
+ this.titleEl.textContent = title;
183
+ }
184
+ }
185
+ setStatus(statusText) {
186
+ if (this.statusEl) {
187
+ this.statusEl.textContent = statusText;
188
+ }
189
+ }
190
+ updateMetrics(completed, total, passed, failed) {
191
+ if (this.metricCurrentEl) {
192
+ this.metricCurrentEl.textContent = `${completed} of ${total}`;
193
+ }
194
+ if (this.metricPassedEl) {
195
+ this.metricPassedEl.textContent = `${passed}`;
196
+ }
197
+ if (this.metricFailedEl) {
198
+ this.metricFailedEl.textContent = `${failed}`;
199
+ }
200
+ if (this.progressFillEl) {
201
+ const pct = total > 0 ? Math.min(100, Math.round((completed / total) * 100)) : 0;
202
+ this.progressFillEl.style.width = `${pct}%`;
203
+ if (failed > 0) {
204
+ this.progressFillEl.style.background = 'linear-gradient(90deg, #f38ba8, #eba0ac)';
205
+ }
206
+ else if (completed >= total && total > 0) {
207
+ this.progressFillEl.style.background = 'linear-gradient(90deg, #a6e3a1, #94e2d5)';
208
+ }
209
+ }
210
+ }
211
+ }
212
+ exports.GraphicalOverlayView = GraphicalOverlayView;
213
+ //# sourceMappingURL=graphical-overlay-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphical-overlay-view.js","sourceRoot":"","sources":["../../src/harness/graphical-overlay-view.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { NwjsHarnessConfig } from '../types/test-runner-types';
2
+ import { TestLogWriter } from './test-log-writer';
3
+ export declare class NwjsGraphicalTestHarness {
4
+ private readonly config;
5
+ private readonly projectRootDir;
6
+ private readonly testSandboxDir;
7
+ private readonly testConfigDir;
8
+ private readonly logWriter;
9
+ private readonly overlayView;
10
+ private httpServer;
11
+ private webviewController;
12
+ private initTimeoutTimer;
13
+ private globalTimeoutTimer;
14
+ private hasStartedExecution;
15
+ private hasFinished;
16
+ constructor(config?: NwjsHarnessConfig);
17
+ private shouldEnableWebview;
18
+ getLogWriter(): TestLogWriter;
19
+ getProjectRootDir(): string;
20
+ getSandboxDir(): string;
21
+ getConfigDir(): string;
22
+ start(): Promise<{
23
+ exitCode: number;
24
+ logText: string;
25
+ }>;
26
+ private markExecutionStarted;
27
+ private setupWindowAndProcessLifecycle;
28
+ private startHttpServerAndNavigateWebview;
29
+ private prepareTestSuites;
30
+ private executeAllTests;
31
+ finishAndExit(): Promise<{
32
+ exitCode: number;
33
+ logText: string;
34
+ }>;
35
+ }
36
+ export declare function runNwjsTestHarness(config?: NwjsHarnessConfig): Promise<{
37
+ exitCode: number;
38
+ logText: string;
39
+ }>;
40
+ //# sourceMappingURL=nwjs-graphical-test-harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nwjs-graphical-test-harness.d.ts","sourceRoot":"","sources":["../../src/harness/nwjs-graphical-test-harness.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAqB,MAAM,4BAA4B,CAAC;AAGlF,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGlD,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;IACnD,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,iBAAiB,CAAsC;IAC/D,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,kBAAkB,CAA8C;IACxE,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,GAAE,iBAAsB;IAiC1C,OAAO,CAAC,mBAAmB;IAapB,YAAY,IAAI,aAAa;IAI7B,iBAAiB,IAAI,MAAM;IAI3B,aAAa,IAAI,MAAM;IAIvB,YAAY,IAAI,MAAM;IAIhB,KAAK,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAgFpE,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,8BAA8B;YAiCxB,iCAAiC;YAiCjC,iBAAiB;YAkDjB,eAAe;IAqHhB,aAAa,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CA4C7E;AAED,wBAAsB,kBAAkB,CACtC,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAIhD"}
@@ -0,0 +1,398 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.NwjsGraphicalTestHarness = void 0;
37
+ exports.runNwjsTestHarness = runNwjsTestHarness;
38
+ const fs = __importStar(require("fs"));
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
41
+ const harness_config_loader_1 = require("../config/harness-config-loader");
42
+ const test_runner_1 = require("../core/test-runner");
43
+ const graphical_overlay_view_1 = require("./graphical-overlay-view");
44
+ const test_http_server_1 = require("./test-http-server");
45
+ const test_log_writer_1 = require("./test-log-writer");
46
+ const webview_test_controller_1 = require("./webview-test-controller");
47
+ class NwjsGraphicalTestHarness {
48
+ config;
49
+ projectRootDir;
50
+ testSandboxDir;
51
+ testConfigDir;
52
+ logWriter;
53
+ overlayView;
54
+ httpServer = null;
55
+ webviewController = null;
56
+ initTimeoutTimer = null;
57
+ globalTimeoutTimer = null;
58
+ hasStartedExecution = false;
59
+ hasFinished = false;
60
+ constructor(config = {}) {
61
+ this.config = config;
62
+ this.projectRootDir = harness_config_loader_1.HarnessConfigLoader.resolveProjectRoot(config.projectRootDir);
63
+ const sandboxPrefix = config.sandboxPrefix || 'nwjs-webview-test-';
64
+ this.testSandboxDir = fs.mkdtempSync(path.join(os.tmpdir(), sandboxPrefix));
65
+ this.testConfigDir = path.join(this.testSandboxDir, 'os-config');
66
+ fs.mkdirSync(this.testConfigDir, { recursive: true });
67
+ const defaultLogPath = path.join(this.projectRootDir, 'testrunner', 'test-results', 'desktop-tests.log');
68
+ const winAny = typeof window !== 'undefined' ? window : undefined;
69
+ const resolvedLogPath = config.testLogPath ||
70
+ (winAny && winAny.__testLogPath) ||
71
+ process.env.NWJS_TEST_LOG_PATH ||
72
+ defaultLogPath;
73
+ const headerTitle = config.logHeaderTitle ||
74
+ (config.title ? `${config.title} Log` : 'NW.js Desktop Client Test Harness Log');
75
+ this.logWriter = new test_log_writer_1.TestLogWriter(resolvedLogPath, headerTitle);
76
+ this.overlayView = new graphical_overlay_view_1.GraphicalOverlayView({
77
+ title: config.title || '🧪 NW.js Live Webview Test Runner',
78
+ enableWebview: this.shouldEnableWebview()
79
+ });
80
+ }
81
+ shouldEnableWebview() {
82
+ if (typeof this.config.enableWebview === 'boolean') {
83
+ return this.config.enableWebview;
84
+ }
85
+ if (this.config.liveWebviewTests && this.config.liveWebviewTests.length > 0) {
86
+ return true;
87
+ }
88
+ if (this.config.webviewEntryHtml) {
89
+ return true;
90
+ }
91
+ return false;
92
+ }
93
+ getLogWriter() {
94
+ return this.logWriter;
95
+ }
96
+ getProjectRootDir() {
97
+ return this.projectRootDir;
98
+ }
99
+ getSandboxDir() {
100
+ return this.testSandboxDir;
101
+ }
102
+ getConfigDir() {
103
+ return this.testConfigDir;
104
+ }
105
+ async start() {
106
+ this.logWriter.writeProgressSnapshot('Tests interupted (Initializing test harness...)');
107
+ if (typeof document !== 'undefined') {
108
+ this.overlayView.mount(document);
109
+ }
110
+ this.setupWindowAndProcessLifecycle();
111
+ const initTimeoutMs = this.config.initTimeoutMs ?? 8000;
112
+ const globalTimeoutMs = this.config.globalTimeoutMs ?? 35000;
113
+ this.initTimeoutTimer = setTimeout(() => {
114
+ if (!this.hasStartedExecution && !this.hasFinished) {
115
+ this.logWriter.recordError('InitializationTimeout', `Tests interupted: Test runner failed to initialize within ${initTimeoutMs}ms.`);
116
+ this.finishAndExit();
117
+ }
118
+ }, initTimeoutMs);
119
+ this.globalTimeoutTimer = setTimeout(() => {
120
+ if (!this.hasFinished) {
121
+ this.logWriter.recordError('GlobalHarnessTimeout', `Test runner harness timed out after ${globalTimeoutMs}ms.`);
122
+ this.finishAndExit();
123
+ }
124
+ }, globalTimeoutMs);
125
+ try {
126
+ await this.prepareTestSuites();
127
+ if (this.shouldEnableWebview() && typeof document !== 'undefined') {
128
+ const webviewEl = this.overlayView.getWebviewElement();
129
+ if (!webviewEl) {
130
+ this.logWriter.recordError('WebviewElementMissing', 'Expected #client-webview element in DOM when enableWebview is true');
131
+ return await this.finishAndExit();
132
+ }
133
+ this.webviewController = new webview_test_controller_1.WebviewTestController(webviewEl, {
134
+ consoleErrorLevel: this.config.webviewConsoleErrorLevel ?? 2,
135
+ onError: (source, err) => this.logWriter.recordError(source, err),
136
+ onLoadAbort: () => {
137
+ this.finishAndExit();
138
+ }
139
+ });
140
+ return await new Promise((resolve) => {
141
+ this.webviewController.attachEventListeners(() => {
142
+ if (this.hasStartedExecution)
143
+ return;
144
+ this.markExecutionStarted();
145
+ this.executeAllTests()
146
+ .then((res) => resolve(res))
147
+ .catch((err) => {
148
+ this.logWriter.recordError('TestRunnerExecution', err);
149
+ this.finishAndExit().then(resolve);
150
+ });
151
+ });
152
+ this.startHttpServerAndNavigateWebview().catch((err) => {
153
+ this.logWriter.recordError('HttpServerStartError', err);
154
+ this.finishAndExit().then(resolve);
155
+ });
156
+ });
157
+ }
158
+ else {
159
+ this.markExecutionStarted();
160
+ return await this.executeAllTests();
161
+ }
162
+ }
163
+ catch (err) {
164
+ this.logWriter.recordError('TopLevelHarnessError', err);
165
+ return await this.finishAndExit();
166
+ }
167
+ }
168
+ markExecutionStarted() {
169
+ this.hasStartedExecution = true;
170
+ if (this.initTimeoutTimer) {
171
+ clearTimeout(this.initTimeoutTimer);
172
+ this.initTimeoutTimer = null;
173
+ }
174
+ if (typeof window !== 'undefined') {
175
+ const winAny = window;
176
+ winAny.__harnessInitialized = true;
177
+ if (winAny.__initWatchdogTimer) {
178
+ clearTimeout(winAny.__initWatchdogTimer);
179
+ }
180
+ }
181
+ }
182
+ setupWindowAndProcessLifecycle() {
183
+ try {
184
+ const nwGlobal = typeof globalThis.nw !== 'undefined' ? globalThis.nw : undefined;
185
+ if (nwGlobal && nwGlobal.Window && typeof nwGlobal.Window.get === 'function') {
186
+ const win = nwGlobal.Window.get();
187
+ win.on('close', () => {
188
+ if (!this.hasFinished) {
189
+ const m = this.logWriter.getMetrics();
190
+ this.logWriter.writeProgressSnapshot(`Tests interupted (Closed by user at ${m.passedCount + m.failedCount} of ${m.totalTests})`);
191
+ }
192
+ try {
193
+ win.close(true);
194
+ }
195
+ catch (_e) { }
196
+ process.exit(1);
197
+ });
198
+ }
199
+ }
200
+ catch (_e) { }
201
+ if (typeof window !== 'undefined') {
202
+ window.onerror = (message, _source, _lineno, _colno, error) => {
203
+ this.logWriter.recordError('HostWindowOnError', error || message);
204
+ this.finishAndExit();
205
+ };
206
+ window.addEventListener('unhandledrejection', (e) => {
207
+ this.logWriter.recordError('HostUnhandledRejection', e.reason);
208
+ this.finishAndExit();
209
+ });
210
+ }
211
+ }
212
+ async startHttpServerAndNavigateWebview() {
213
+ const entryHtml = this.config.webviewEntryHtml || 'index.html';
214
+ this.httpServer = new test_http_server_1.TestHttpServer({
215
+ projectRootDir: this.projectRootDir,
216
+ defaultEntryFile: entryHtml,
217
+ ignore404Paths: this.config.ignore404Paths,
218
+ routeHandler: this.config.httpRouteHandler,
219
+ onHttp404: (reqPath, resolvedFilePath) => {
220
+ this.logWriter.recordError('Http404Error', `HTTP 404 Not Found: ${reqPath} (resolved: ${resolvedFilePath})`);
221
+ }
222
+ });
223
+ const port = await this.httpServer.start();
224
+ const queryParams = new URLSearchParams();
225
+ if (this.config.injectSandboxQueryParams !== false) {
226
+ queryParams.set('projectRoot', this.testSandboxDir);
227
+ queryParams.set('configDir', this.testConfigDir);
228
+ }
229
+ if (this.config.webviewQueryParams) {
230
+ for (const [k, v] of Object.entries(this.config.webviewQueryParams)) {
231
+ queryParams.set(k, v);
232
+ }
233
+ }
234
+ const cleanEntry = entryHtml.startsWith('/') ? entryHtml.slice(1) : entryHtml;
235
+ const qs = queryParams.toString();
236
+ const targetUrl = `http://127.0.0.1:${port}/${cleanEntry}${qs ? `?${qs}` : ''}`;
237
+ this.webviewController?.navigate(targetUrl);
238
+ }
239
+ async prepareTestSuites() {
240
+ (0, test_runner_1.installJestGlobals)(globalThis);
241
+ if (typeof window !== 'undefined') {
242
+ (0, test_runner_1.installJestGlobals)(window);
243
+ }
244
+ const runner = test_runner_1.TestRunner.getInstance();
245
+ runner.reset();
246
+ const context = {
247
+ projectRootDir: this.projectRootDir,
248
+ sandboxDir: this.testSandboxDir,
249
+ configDir: this.testConfigDir
250
+ };
251
+ if (this.config.registerSuites) {
252
+ await this.config.registerSuites(runner, context);
253
+ }
254
+ const shouldLoadFiles = (this.config.testFiles && this.config.testFiles.length > 0) || !this.config.registerSuites;
255
+ if (shouldLoadFiles) {
256
+ const files = harness_config_loader_1.HarnessConfigLoader.resolveTestFiles(this.projectRootDir, this.config.testFiles);
257
+ for (const filePath of files) {
258
+ try {
259
+ delete require.cache[require.resolve(filePath)];
260
+ }
261
+ catch (_e) { }
262
+ const mod = require(filePath);
263
+ if (typeof mod === 'function') {
264
+ await mod(runner, context);
265
+ }
266
+ else if (mod && typeof mod === 'object') {
267
+ for (const [exportName, exportVal] of Object.entries(mod)) {
268
+ if (typeof exportVal === 'function' &&
269
+ (exportName === 'registerSuites' ||
270
+ exportName === 'registerTestSuites' ||
271
+ (/^register.*Suites$/i.test(exportName)))) {
272
+ await exportVal(runner, context);
273
+ }
274
+ }
275
+ }
276
+ }
277
+ }
278
+ }
279
+ async executeAllTests() {
280
+ try {
281
+ const runner = test_runner_1.TestRunner.getInstance();
282
+ const liveTests = this.config.liveWebviewTests || [];
283
+ const unitCount = runner.totalCasesCount;
284
+ const totalTests = unitCount + liveTests.length;
285
+ this.logWriter.setTotalTests(totalTests);
286
+ this.overlayView.updateMetrics(0, totalTests, 0, 0);
287
+ this.overlayView.setStatus(`Running ${totalTests} tests...`);
288
+ this.logWriter.writeProgressSnapshot(`Tests interupted (Running unit suites: 0 of ${totalTests})`);
289
+ if (this.webviewController && this.shouldEnableWebview()) {
290
+ const screenLoaded = await this.webviewController.waitForScreenLoad(this.config.screenLoadOptions);
291
+ if (!screenLoaded) {
292
+ this.logWriter.recordError('WebviewScreenTimeout', 'Client screen inside webview failed readiness check within timeout.');
293
+ }
294
+ }
295
+ if (unitCount > 0) {
296
+ const jestSummary = await runner.runAll({
297
+ maxDurationMs: this.config.unitSuiteTimeoutMs ?? 15000,
298
+ defaultTestTimeoutMs: this.config.defaultTestTimeoutMs ?? 2000,
299
+ filter: this.config.testFilter,
300
+ onProgress: (ev) => {
301
+ if (ev.type === 'test_start') {
302
+ this.overlayView.setStatus(`Running: ${ev.suiteName} > ${ev.testName}`);
303
+ }
304
+ else if (ev.type === 'test_complete') {
305
+ this.logWriter.setCounts(ev.passedCount, ev.failedCount);
306
+ this.overlayView.updateMetrics(ev.completedCount, totalTests, ev.passedCount, ev.failedCount);
307
+ this.logWriter.writeProgressSnapshot(`Tests interupted (Running unit suites: ${ev.completedCount} of ${totalTests})`);
308
+ }
309
+ }
310
+ });
311
+ this.logWriter.setCounts(jestSummary.passed, jestSummary.failed);
312
+ this.overlayView.updateMetrics(jestSummary.passed + jestSummary.failed, totalTests, jestSummary.passed, jestSummary.failed);
313
+ this.logWriter.writeProgressSnapshot(`Tests interupted (Completed unit suites: ${jestSummary.passed + jestSummary.failed} of ${totalTests})`);
314
+ if (jestSummary.failures && jestSummary.failures.length > 0) {
315
+ for (const f of jestSummary.failures) {
316
+ this.logWriter.recordError(`JestSuite[${f.suiteName}]`, `${f.testName} -> ${f.error?.message || f.error}`);
317
+ }
318
+ }
319
+ }
320
+ if (this.webviewController && liveTests.length > 0) {
321
+ const totalLive = liveTests.length;
322
+ for (let i = 0; i < totalLive; i++) {
323
+ const spec = liveTests[i];
324
+ const completedSoFar = unitCount + i + 1;
325
+ this.overlayView.setStatus(`Running [${i + 1}/${totalLive}]: ${spec.name}`);
326
+ this.logWriter.writeProgressSnapshot(`Tests interupted (Running live test [${i + 1}/${totalLive}]: ${spec.id})`);
327
+ const res = await this.webviewController.executeWebviewTest(spec.code, spec.timeoutMs ?? 4000);
328
+ if (res.ok) {
329
+ this.logWriter.incrementPassed(1);
330
+ }
331
+ else {
332
+ this.logWriter.incrementFailed(1);
333
+ const errs = res.errors && res.errors.length > 0
334
+ ? res.errors
335
+ : [res.error || 'Unknown webview test error'];
336
+ for (const err of errs) {
337
+ this.logWriter.recordError(spec.id, err);
338
+ }
339
+ }
340
+ const m = this.logWriter.getMetrics();
341
+ this.overlayView.updateMetrics(completedSoFar, totalTests, m.passedCount, m.failedCount);
342
+ }
343
+ }
344
+ const finalMetrics = this.logWriter.getMetrics();
345
+ this.overlayView.setStatus('All tests completed!');
346
+ this.overlayView.updateMetrics(totalTests, totalTests, finalMetrics.passedCount, finalMetrics.failedCount);
347
+ }
348
+ catch (err) {
349
+ this.logWriter.recordError('TestRunnerExecution', err);
350
+ }
351
+ return await this.finishAndExit();
352
+ }
353
+ async finishAndExit() {
354
+ if (this.hasFinished) {
355
+ return this.logWriter.writeFinalSummary();
356
+ }
357
+ this.hasFinished = true;
358
+ if (typeof window !== 'undefined') {
359
+ window.__harnessCompleted = true;
360
+ if (window.__initWatchdogTimer) {
361
+ clearTimeout(window.__initWatchdogTimer);
362
+ }
363
+ }
364
+ if (this.initTimeoutTimer) {
365
+ clearTimeout(this.initTimeoutTimer);
366
+ this.initTimeoutTimer = null;
367
+ }
368
+ if (this.globalTimeoutTimer) {
369
+ clearTimeout(this.globalTimeoutTimer);
370
+ this.globalTimeoutTimer = null;
371
+ }
372
+ if (this.httpServer) {
373
+ await this.httpServer.stop();
374
+ this.httpServer = null;
375
+ }
376
+ try {
377
+ if (this.testSandboxDir && fs.existsSync(this.testSandboxDir)) {
378
+ fs.rmSync(this.testSandboxDir, { recursive: true, force: true });
379
+ }
380
+ }
381
+ catch (_e) { }
382
+ const result = this.logWriter.writeFinalSummary();
383
+ console.log(`Test runner finished with exit code ${result.exitCode}`);
384
+ const autoExit = this.config.autoExit ?? true;
385
+ const keepOpen = this.config.keepOpen ?? false;
386
+ if (autoExit && !keepOpen) {
387
+ process.exit(result.exitCode);
388
+ }
389
+ return result;
390
+ }
391
+ }
392
+ exports.NwjsGraphicalTestHarness = NwjsGraphicalTestHarness;
393
+ async function runNwjsTestHarness(config) {
394
+ const effectiveConfig = config || harness_config_loader_1.HarnessConfigLoader.loadConfig();
395
+ const harness = new NwjsGraphicalTestHarness(effectiveConfig);
396
+ return harness.start();
397
+ }
398
+ //# sourceMappingURL=nwjs-graphical-test-harness.js.map