@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.
- package/AGENTS.md +21 -0
- package/README.md +142 -2
- package/bin/nwjstestrunner.js +13 -0
- package/dist/cli/nwjs-cli-launcher.d.ts +10 -0
- package/dist/cli/nwjs-cli-launcher.d.ts.map +1 -0
- package/dist/cli/nwjs-cli-launcher.js +281 -0
- package/dist/cli/nwjs-cli-launcher.js.map +1 -0
- package/dist/config/harness-config-loader.d.ts +10 -0
- package/dist/config/harness-config-loader.d.ts.map +1 -0
- package/dist/config/harness-config-loader.js +242 -0
- package/dist/config/harness-config-loader.js.map +1 -0
- package/dist/core/test-runner.d.ts +61 -0
- package/dist/core/test-runner.d.ts.map +1 -0
- package/dist/core/test-runner.js +450 -0
- package/dist/core/test-runner.js.map +1 -0
- package/dist/esm/index.d.mts +2 -0
- package/dist/esm/index.mjs +26 -0
- package/dist/harness/graphical-overlay-view.d.ts +18 -0
- package/dist/harness/graphical-overlay-view.d.ts.map +1 -0
- package/dist/harness/graphical-overlay-view.js +213 -0
- package/dist/harness/graphical-overlay-view.js.map +1 -0
- package/dist/harness/nwjs-graphical-test-harness.d.ts +40 -0
- package/dist/harness/nwjs-graphical-test-harness.d.ts.map +1 -0
- package/dist/harness/nwjs-graphical-test-harness.js +398 -0
- package/dist/harness/nwjs-graphical-test-harness.js.map +1 -0
- package/dist/harness/test-http-server.d.ts +12 -0
- package/dist/harness/test-http-server.d.ts.map +1 -0
- package/dist/harness/test-http-server.js +183 -0
- package/dist/harness/test-http-server.js.map +1 -0
- package/dist/harness/test-log-writer.d.ts +29 -0
- package/dist/harness/test-log-writer.d.ts.map +1 -0
- package/dist/harness/test-log-writer.js +173 -0
- package/dist/harness/test-log-writer.js.map +1 -0
- package/dist/harness/webview-test-controller.d.ts +18 -0
- package/dist/harness/webview-test-controller.d.ts.map +1 -0
- package/dist/harness/webview-test-controller.js +163 -0
- package/dist/harness/webview-test-controller.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/matchers/deep-equality-helper.d.ts +10 -0
- package/dist/matchers/deep-equality-helper.d.ts.map +1 -0
- package/dist/matchers/deep-equality-helper.js +188 -0
- package/dist/matchers/deep-equality-helper.js.map +1 -0
- package/dist/matchers/expect-matcher.d.ts +7 -0
- package/dist/matchers/expect-matcher.d.ts.map +1 -0
- package/dist/matchers/expect-matcher.js +261 -0
- package/dist/matchers/expect-matcher.js.map +1 -0
- package/dist/mock/jest-mock-factory.d.ts +20 -0
- package/dist/mock/jest-mock-factory.d.ts.map +1 -0
- package/dist/mock/jest-mock-factory.js +141 -0
- package/dist/mock/jest-mock-factory.js.map +1 -0
- package/dist/tests/NwjsTestRunnerSuites.d.ts +3 -0
- package/dist/tests/NwjsTestRunnerSuites.d.ts.map +1 -0
- package/dist/tests/NwjsTestRunnerSuites.js +266 -0
- package/dist/tests/NwjsTestRunnerSuites.js.map +1 -0
- package/dist/types/test-runner-types.d.ts +321 -0
- package/dist/types/test-runner-types.d.ts.map +1 -0
- package/dist/types/test-runner-types.js +3 -0
- package/dist/types/test-runner-types.js.map +1 -0
- package/package.json +56 -4
- package/runner/demo-client.html +70 -0
- package/runner/index.html +223 -0
- package/runner/package.json +27 -0
- package/src/cli/nwjs-cli-launcher.ts +243 -0
- package/src/config/harness-config-loader.ts +214 -0
- package/src/core/test-runner.ts +553 -0
- package/src/harness/graphical-overlay-view.ts +219 -0
- package/src/harness/nwjs-graphical-test-harness.ts +468 -0
- package/src/harness/test-http-server.ts +162 -0
- package/src/harness/test-log-writer.ts +154 -0
- package/src/harness/webview-test-controller.ts +192 -0
- package/src/index.ts +28 -0
- package/src/matchers/deep-equality-helper.ts +193 -0
- package/src/matchers/expect-matcher.ts +361 -0
- package/src/mock/jest-mock-factory.ts +170 -0
- package/src/tests/NwjsTestRunnerSuites.ts +267 -0
- package/src/types/test-runner-types.ts +335 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { MockFunction, MockState } from '../types/test-runner-types';
|
|
2
|
+
|
|
3
|
+
export class JestMockFactory {
|
|
4
|
+
private static activeMocks = new Set<MockFunction<any, any>>();
|
|
5
|
+
private static activeSpies = new Set<() => void>();
|
|
6
|
+
|
|
7
|
+
public static fn<TArgs extends any[] = any[], TReturn = any>(
|
|
8
|
+
initialImpl?: (...args: TArgs) => TReturn
|
|
9
|
+
): MockFunction<TArgs, TReturn> {
|
|
10
|
+
let defaultImpl: ((...args: TArgs) => TReturn) | undefined = initialImpl;
|
|
11
|
+
const onceImplQueue: Array<(...args: TArgs) => TReturn> = [];
|
|
12
|
+
|
|
13
|
+
const mockState: MockState<TArgs, TReturn> = {
|
|
14
|
+
calls: [],
|
|
15
|
+
results: [],
|
|
16
|
+
instances: [],
|
|
17
|
+
lastCall: undefined
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const mockFn = function (this: any, ...args: TArgs): TReturn {
|
|
21
|
+
mockState.calls.push(args);
|
|
22
|
+
mockState.lastCall = args;
|
|
23
|
+
mockState.instances.push(this);
|
|
24
|
+
|
|
25
|
+
const impl = onceImplQueue.length > 0 ? onceImplQueue.shift()! : defaultImpl;
|
|
26
|
+
if (!impl) {
|
|
27
|
+
mockState.results.push({ type: 'return', value: undefined as any });
|
|
28
|
+
return undefined as any;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const result = impl.apply(this, args);
|
|
33
|
+
mockState.results.push({ type: 'return', value: result });
|
|
34
|
+
return result;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
mockState.results.push({ type: 'throw', value: err as any });
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
} as unknown as MockFunction<TArgs, TReturn>;
|
|
40
|
+
|
|
41
|
+
mockFn._isMockFunction = true;
|
|
42
|
+
mockFn.mock = mockState;
|
|
43
|
+
|
|
44
|
+
mockFn.mockImplementation = (fn: (...args: TArgs) => TReturn) => {
|
|
45
|
+
defaultImpl = fn;
|
|
46
|
+
return mockFn;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
mockFn.mockImplementationOnce = (fn: (...args: TArgs) => TReturn) => {
|
|
50
|
+
onceImplQueue.push(fn);
|
|
51
|
+
return mockFn;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
mockFn.mockReturnValue = (value: TReturn) => {
|
|
55
|
+
defaultImpl = () => value;
|
|
56
|
+
return mockFn;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
mockFn.mockReturnValueOnce = (value: TReturn) => {
|
|
60
|
+
onceImplQueue.push(() => value);
|
|
61
|
+
return mockFn;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
mockFn.mockResolvedValue = (value: Awaited<TReturn>) => {
|
|
65
|
+
defaultImpl = () => Promise.resolve(value) as unknown as TReturn;
|
|
66
|
+
return mockFn;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
mockFn.mockResolvedValueOnce = (value: Awaited<TReturn>) => {
|
|
70
|
+
onceImplQueue.push(() => Promise.resolve(value) as unknown as TReturn);
|
|
71
|
+
return mockFn;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
mockFn.mockRejectedValue = (err: any) => {
|
|
75
|
+
defaultImpl = () => Promise.reject(err) as unknown as TReturn;
|
|
76
|
+
return mockFn;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
mockFn.mockRejectedValueOnce = (err: any) => {
|
|
80
|
+
onceImplQueue.push(() => Promise.reject(err) as unknown as TReturn);
|
|
81
|
+
return mockFn;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
mockFn.mockClear = () => {
|
|
85
|
+
mockState.calls = [];
|
|
86
|
+
mockState.results = [];
|
|
87
|
+
mockState.instances = [];
|
|
88
|
+
mockState.lastCall = undefined;
|
|
89
|
+
return mockFn;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
mockFn.mockReset = () => {
|
|
93
|
+
mockFn.mockClear();
|
|
94
|
+
defaultImpl = undefined;
|
|
95
|
+
onceImplQueue.length = 0;
|
|
96
|
+
return mockFn;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
mockFn.mockRestore = () => {
|
|
100
|
+
mockFn.mockReset();
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
JestMockFactory.activeMocks.add(mockFn);
|
|
104
|
+
return mockFn;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public static spyOn<T extends object, K extends keyof T>(
|
|
108
|
+
target: T,
|
|
109
|
+
methodName: K
|
|
110
|
+
): MockFunction<any[], any> {
|
|
111
|
+
if (!target || (typeof target !== 'object' && typeof target !== 'function')) {
|
|
112
|
+
throw new Error('jest.spyOn() requires a valid target object');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const originalDescriptor = Object.getOwnPropertyDescriptor(target, methodName);
|
|
116
|
+
const originalMethod = target[methodName];
|
|
117
|
+
if (typeof originalMethod !== 'function') {
|
|
118
|
+
throw new Error(`Cannot spyOn('${String(methodName)}'): property is not a function`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const spy = JestMockFactory.fn((...args: any[]) => originalMethod.apply(target, args));
|
|
122
|
+
|
|
123
|
+
const restore = () => {
|
|
124
|
+
if (originalDescriptor) {
|
|
125
|
+
Object.defineProperty(target, methodName, originalDescriptor);
|
|
126
|
+
} else {
|
|
127
|
+
(target as any)[methodName] = originalMethod;
|
|
128
|
+
}
|
|
129
|
+
JestMockFactory.activeSpies.delete(restore);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
spy.mockRestore = restore;
|
|
133
|
+
JestMockFactory.activeSpies.add(restore);
|
|
134
|
+
|
|
135
|
+
(target as any)[methodName] = spy;
|
|
136
|
+
return spy;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public static isMockFunction(fn: any): fn is MockFunction<any, any> {
|
|
140
|
+
return Boolean(fn && fn._isMockFunction === true);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public static clearAllMocks(): void {
|
|
144
|
+
for (const m of JestMockFactory.activeMocks) {
|
|
145
|
+
m.mockClear();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public static resetAllMocks(): void {
|
|
150
|
+
for (const m of JestMockFactory.activeMocks) {
|
|
151
|
+
m.mockReset();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public static restoreAllMocks(): void {
|
|
156
|
+
for (const restore of Array.from(JestMockFactory.activeSpies)) {
|
|
157
|
+
restore();
|
|
158
|
+
}
|
|
159
|
+
JestMockFactory.clearAllMocks();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export const jest = {
|
|
164
|
+
fn: JestMockFactory.fn,
|
|
165
|
+
spyOn: JestMockFactory.spyOn,
|
|
166
|
+
isMockFunction: JestMockFactory.isMockFunction,
|
|
167
|
+
clearAllMocks: JestMockFactory.clearAllMocks,
|
|
168
|
+
resetAllMocks: JestMockFactory.resetAllMocks,
|
|
169
|
+
restoreAllMocks: JestMockFactory.restoreAllMocks
|
|
170
|
+
};
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as http from 'http';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { NwjsCliLauncher } from '../cli/nwjs-cli-launcher';
|
|
6
|
+
import { HarnessConfigLoader } from '../config/harness-config-loader';
|
|
7
|
+
import { TestRunner } from '../core/test-runner';
|
|
8
|
+
import { TestHttpServer } from '../harness/test-http-server';
|
|
9
|
+
import { TestLogWriter } from '../harness/test-log-writer';
|
|
10
|
+
import { DeepEqualityHelper } from '../matchers/deep-equality-helper';
|
|
11
|
+
import { expect } from '../matchers/expect-matcher';
|
|
12
|
+
import { jest } from '../mock/jest-mock-factory';
|
|
13
|
+
|
|
14
|
+
export function registerNwjsTestRunnerSuites(runner: TestRunner): void {
|
|
15
|
+
runner.registerSuite('CoreExpectMatchersAndDeepEquality', (suite) => {
|
|
16
|
+
suite.it('evaluates primitive, object, array, Map, Set, and Buffer equality', () => {
|
|
17
|
+
expect(42).toBe(42);
|
|
18
|
+
expect('nwjs').not.toBe('electron');
|
|
19
|
+
expect(NaN).toBeNaN();
|
|
20
|
+
expect(10).not.toBeNaN();
|
|
21
|
+
expect(null).toBeNull();
|
|
22
|
+
expect(undefined).toBeUndefined();
|
|
23
|
+
expect('defined').toBeDefined();
|
|
24
|
+
expect(true).toBeTruthy();
|
|
25
|
+
expect(0).toBeFalsy();
|
|
26
|
+
|
|
27
|
+
expect({ a: 1, b: [2, 3], c: { nested: true } }).toEqual({
|
|
28
|
+
a: 1,
|
|
29
|
+
b: [2, 3],
|
|
30
|
+
c: { nested: true }
|
|
31
|
+
});
|
|
32
|
+
expect(new Set(['a', 'b'])).toEqual(new Set(['b', 'a']));
|
|
33
|
+
expect(
|
|
34
|
+
new Map([
|
|
35
|
+
['k1', 10],
|
|
36
|
+
['k2', 20]
|
|
37
|
+
])
|
|
38
|
+
).toEqual(
|
|
39
|
+
new Map([
|
|
40
|
+
['k1', 10],
|
|
41
|
+
['k2', 20]
|
|
42
|
+
])
|
|
43
|
+
);
|
|
44
|
+
expect(Buffer.from([1, 2, 3])).toEqual(Buffer.from([1, 2, 3]));
|
|
45
|
+
expect(DeepEqualityHelper.deepEquals({ x: 1, y: undefined }, { x: 1 }, false)).toBe(true);
|
|
46
|
+
expect(DeepEqualityHelper.deepEquals({ x: 1, y: undefined }, { x: 1 }, true)).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
suite.it('evaluates numeric comparisons, collections, properties, regex, and toThrow', () => {
|
|
50
|
+
expect(15).toBeGreaterThan(10);
|
|
51
|
+
expect(10).toBeGreaterThanOrEqual(10);
|
|
52
|
+
expect(5).toBeLessThan(8);
|
|
53
|
+
expect(8).toBeLessThanOrEqual(8);
|
|
54
|
+
expect(0.1 + 0.2).toBeCloseTo(0.3, 5);
|
|
55
|
+
|
|
56
|
+
expect([10, 20, 30]).toContain(20);
|
|
57
|
+
expect('hello nwjs world').toContain('nwjs');
|
|
58
|
+
expect([{ id: 1 }, { id: 2 }]).toContainEqual({ id: 2 });
|
|
59
|
+
expect([1, 2, 3, 4]).toHaveLength(4);
|
|
60
|
+
|
|
61
|
+
const sampleObj = {
|
|
62
|
+
app: {
|
|
63
|
+
window: { width: 1280, height: 850 },
|
|
64
|
+
tags: ['desktop', 'nwjs']
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
expect(sampleObj).toHaveProperty('app.window.width', 1280);
|
|
68
|
+
expect(sampleObj).toMatchObject({ app: { window: { width: 1280 } } });
|
|
69
|
+
expect('nwjstestrunner-v1.0.0').toMatch(/^nwjstestrunner-v\d+/);
|
|
70
|
+
expect(new Error('boom')).toBeInstanceOf(Error);
|
|
71
|
+
|
|
72
|
+
expect(() => {
|
|
73
|
+
throw new TypeError('Invalid canvas dimension');
|
|
74
|
+
}).toThrow('Invalid canvas');
|
|
75
|
+
expect(() => {
|
|
76
|
+
throw new TypeError('Invalid canvas dimension');
|
|
77
|
+
}).toThrow(TypeError);
|
|
78
|
+
expect(() => 'safe').not.toThrow();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
suite.it('supports async .resolves and .rejects matchers and static TestRunner assertions', async () => {
|
|
82
|
+
await expect(Promise.resolve({ status: 'ok', count: 3 })).resolves.toEqual({
|
|
83
|
+
status: 'ok',
|
|
84
|
+
count: 3
|
|
85
|
+
});
|
|
86
|
+
await expect(Promise.reject(new Error('network timeout'))).rejects.toThrow('network timeout');
|
|
87
|
+
|
|
88
|
+
TestRunner.assertEquals(2 + 2, 4);
|
|
89
|
+
TestRunner.assertTrue(true);
|
|
90
|
+
TestRunner.assertFalse(false);
|
|
91
|
+
const nonNull = TestRunner.assertNotNull('value');
|
|
92
|
+
expect(nonNull).toBe('value');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
runner.registerSuite('JestMocksSpiesAndLifecycleHooks', (suite) => {
|
|
97
|
+
const hookEvents: string[] = [];
|
|
98
|
+
|
|
99
|
+
suite.beforeAll(() => {
|
|
100
|
+
hookEvents.push('beforeAll');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
suite.beforeEach(() => {
|
|
104
|
+
hookEvents.push('beforeEach');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
suite.afterEach(() => {
|
|
108
|
+
hookEvents.push('afterEach');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
suite.afterAll(() => {
|
|
112
|
+
hookEvents.push('afterAll');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
suite.it('tracks jest.fn() calls, return values, once queues, and async mocks', async () => {
|
|
116
|
+
const fn = jest.fn((a: number, b: number) => a + b);
|
|
117
|
+
expect(fn(2, 3)).toBe(5);
|
|
118
|
+
expect(fn(10, 20)).toBe(30);
|
|
119
|
+
|
|
120
|
+
expect(fn).toHaveBeenCalled();
|
|
121
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
122
|
+
expect(fn).toHaveBeenCalledWith(2, 3);
|
|
123
|
+
expect(fn).toHaveBeenLastCalledWith(10, 20);
|
|
124
|
+
|
|
125
|
+
fn.mockReturnValueOnce(999).mockReturnValue(100);
|
|
126
|
+
expect(fn(1, 1)).toBe(999);
|
|
127
|
+
expect(fn(1, 1)).toBe(100);
|
|
128
|
+
|
|
129
|
+
const asyncMock = jest.fn().mockResolvedValueOnce('first').mockResolvedValue('default');
|
|
130
|
+
await expect(asyncMock()).resolves.toBe('first');
|
|
131
|
+
await expect(asyncMock()).resolves.toBe('default');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
suite.it('spies on object methods via jest.spyOn() and restores original implementation', () => {
|
|
135
|
+
const calculator = {
|
|
136
|
+
multiply(x: number, y: number): number {
|
|
137
|
+
return x * y;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const spy = jest.spyOn(calculator, 'multiply');
|
|
142
|
+
expect(calculator.multiply(3, 4)).toBe(12);
|
|
143
|
+
expect(spy).toHaveBeenCalledWith(3, 4);
|
|
144
|
+
|
|
145
|
+
spy.mockReturnValue(42);
|
|
146
|
+
expect(calculator.multiply(3, 4)).toBe(42);
|
|
147
|
+
|
|
148
|
+
spy.mockRestore();
|
|
149
|
+
expect(calculator.multiply(3, 4)).toBe(12);
|
|
150
|
+
expect(hookEvents).toEqual(['beforeAll', 'beforeEach', 'afterEach', 'beforeEach']);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
runner.registerSuite('TestLogWriterHttpServerAndConfigLoader', (suite) => {
|
|
155
|
+
suite.it('writes startup interrupted state, progress snapshots, and final summary log', () => {
|
|
156
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nwjs-log-test-'));
|
|
157
|
+
try {
|
|
158
|
+
const logPath = path.join(tmpDir, 'test-results', 'desktop-tests.log');
|
|
159
|
+
const writer = new TestLogWriter(logPath, 'Sample App Test Log');
|
|
160
|
+
|
|
161
|
+
writer.writeInitialInterruptedBanner();
|
|
162
|
+
expect(fs.existsSync(logPath)).toBe(true);
|
|
163
|
+
expect(fs.readFileSync(logPath, 'utf-8')).toContain('Status: Tests interupted');
|
|
164
|
+
|
|
165
|
+
writer.setTotalTests(3);
|
|
166
|
+
writer.setCounts(2, 0);
|
|
167
|
+
writer.writeProgressSnapshot('Tests interupted (Running 2 of 3)');
|
|
168
|
+
expect(fs.readFileSync(logPath, 'utf-8')).toContain('Passed: 2');
|
|
169
|
+
|
|
170
|
+
writer.incrementPassed(1);
|
|
171
|
+
const summary = writer.writeFinalSummary();
|
|
172
|
+
expect(summary.exitCode).toBe(0);
|
|
173
|
+
expect(summary.logText).toContain('SUCCESS: All tests completed cleanly.');
|
|
174
|
+
} finally {
|
|
175
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
suite.it('serves static files and custom HTTP mock routes via TestHttpServer', async () => {
|
|
180
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nwjs-http-test-'));
|
|
181
|
+
fs.writeFileSync(path.join(tmpDir, 'hello.txt'), 'Hello from TestHttpServer', 'utf-8');
|
|
182
|
+
|
|
183
|
+
const notFoundPaths: string[] = [];
|
|
184
|
+
const server = new TestHttpServer({
|
|
185
|
+
projectRootDir: tmpDir,
|
|
186
|
+
onHttp404: (reqPath) => notFoundPaths.push(reqPath),
|
|
187
|
+
routeHandler: (req, res, { reqPath }) => {
|
|
188
|
+
if (req.method === 'GET' && reqPath === '/v1/custom-json') {
|
|
189
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
190
|
+
res.end(JSON.stringify({ custom: true }));
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const port = await server.start();
|
|
199
|
+
expect(port).toBeGreaterThan(0);
|
|
200
|
+
|
|
201
|
+
const fetchText = (urlPath: string): Promise<{ status: number; body: string }> =>
|
|
202
|
+
new Promise((resolve, reject) => {
|
|
203
|
+
http
|
|
204
|
+
.get(`http://127.0.0.1:${port}${urlPath}`, (res) => {
|
|
205
|
+
let body = '';
|
|
206
|
+
res.on('data', (c) => (body += c.toString()));
|
|
207
|
+
res.on('end', () => resolve({ status: res.statusCode || 0, body }));
|
|
208
|
+
})
|
|
209
|
+
.on('error', reject);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const staticRes = await fetchText('/hello.txt');
|
|
213
|
+
expect(staticRes.status).toBe(200);
|
|
214
|
+
expect(staticRes.body).toBe('Hello from TestHttpServer');
|
|
215
|
+
|
|
216
|
+
const apiRes = await fetchText('/v1/custom-json');
|
|
217
|
+
expect(apiRes.status).toBe(200);
|
|
218
|
+
expect(JSON.parse(apiRes.body)).toEqual({ custom: true });
|
|
219
|
+
|
|
220
|
+
const missingRes = await fetchText('/missing-file.js');
|
|
221
|
+
expect(missingRes.status).toBe(404);
|
|
222
|
+
expect(notFoundPaths).toContain('/missing-file.js');
|
|
223
|
+
} finally {
|
|
224
|
+
await server.stop();
|
|
225
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
suite.it('parses CLI flags and resolves glob test file patterns', () => {
|
|
230
|
+
const parsed = NwjsCliLauncher.parseCliArgs([
|
|
231
|
+
'--project-root',
|
|
232
|
+
'./my-app',
|
|
233
|
+
'--config',
|
|
234
|
+
'custom.config.cjs',
|
|
235
|
+
'--filter',
|
|
236
|
+
'Vector',
|
|
237
|
+
'--init-timeout=5000',
|
|
238
|
+
'--timeout=25000',
|
|
239
|
+
'dist/**/*.test.js'
|
|
240
|
+
]);
|
|
241
|
+
expect(parsed.projectRootDir).toBe('./my-app');
|
|
242
|
+
expect(parsed.configPath).toBe('custom.config.cjs');
|
|
243
|
+
expect(parsed.testFilter).toBe('Vector');
|
|
244
|
+
expect(parsed.initTimeoutMs).toBe(5000);
|
|
245
|
+
expect(parsed.globalTimeoutMs).toBe(25000);
|
|
246
|
+
expect(parsed.testFiles).toEqual(['dist/**/*.test.js']);
|
|
247
|
+
|
|
248
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nwjs-glob-test-'));
|
|
249
|
+
try {
|
|
250
|
+
const nestedDir = path.join(tmpDir, 'dist', 'tests');
|
|
251
|
+
fs.mkdirSync(nestedDir, { recursive: true });
|
|
252
|
+
const f1 = path.join(nestedDir, 'alpha.test.js');
|
|
253
|
+
const f2 = path.join(nestedDir, 'beta.spec.js');
|
|
254
|
+
fs.writeFileSync(f1, '// test 1', 'utf-8');
|
|
255
|
+
fs.writeFileSync(f2, '// test 2', 'utf-8');
|
|
256
|
+
|
|
257
|
+
const discovered = HarnessConfigLoader.resolveTestFiles(tmpDir);
|
|
258
|
+
expect(discovered.length).toBe(2);
|
|
259
|
+
|
|
260
|
+
const globMatched = HarnessConfigLoader.resolveTestFiles(tmpDir, ['dist/**/*.test.js']);
|
|
261
|
+
expect(globMatched).toEqual([f1]);
|
|
262
|
+
} finally {
|
|
263
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|