Skip to content

Commit 09f5884

Browse files
committed
updated other test runners
1 parent 8e164ef commit 09f5884

File tree

12 files changed

+5217
-7975
lines changed

12 files changed

+5217
-7975
lines changed

@bellatrix/core/package.build.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"jasmine-core": "^5.1.1",
4545
"mocha": "^10.2.0",
4646
"reflect-metadata": "^0.2.1",
47-
"vitest": "^1.2.1"
47+
"vitest": "^3.0.5"
4848
},
4949
"devDependencies": {
5050
"fix-esm-import-path": "^1.5.0"

@bellatrix/core/src/test/jasmine.ts

Lines changed: 121 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import inspector from 'inspector';
2+
3+
let isDebuggerAttached = false;
4+
if (inspector.url() !== undefined && new URL(inspector.url()!).port === '12016' && !isDebuggerAttached) {
5+
inspector.waitForDebugger();
6+
isDebuggerAttached = true;
7+
}
8+
9+
// WARNING: do not install @types/jasmine as it will flood the global scope with types that are not needed
110
// @ts-expect-error - jasmine-core does not have a declaration file
211
import jasmineModule from 'jasmine-core';
312
import 'reflect-metadata';
@@ -13,6 +22,7 @@ import type { ConfigureFn, Method, MethodNames, ParameterlessCtor, TestFn } from
1322
const nativeLibrary: JasmineModule = jasmineModule.noGlobals();
1423
const BaseTest = ServiceLocator.resolveType(BellatrixTest);
1524
const testSettings = BellatrixSettings.get().frameworkSettings.testSettings;
25+
const testFilters = JSON.parse(process.env.BELLATRIX_TEST_FILTER!);
1626

1727
function getSymbolMethods<T extends BellatrixTest>(type: ParameterlessCtor<T>) {
1828
return {
@@ -46,6 +56,66 @@ export function SuiteDecorator<T extends BellatrixTest>(target: ParameterlessCto
4656
const testMethods = Object.getOwnPropertyNames(testClass).filter(method => typeof testClass[method] === 'function' && Reflect.hasMetadata(Symbols.TEST, testClass[method]));
4757
const title = target.name; // or passed as @Suite('title') or similar
4858

59+
const tests: Map<string, unknown> = new Map;
60+
for (const testMethod of testMethods) {
61+
const testMetadata = getTestMetadata(testClass[testMethod], testClass);
62+
63+
for (const [filterKey, filterValue] of Object.entries(testFilters)) {
64+
if (filterKey == 'suiteName') {
65+
if (Array.isArray(filterValue)) {
66+
throw new Error('no more than one --suiteName argument allowed as it equals to AND operator, use regex');
67+
}
68+
69+
if (!(new RegExp(String(filterValue), 'i').test(testMetadata[filterKey]))) {
70+
testMetadata.shouldSkip = true;
71+
break;
72+
}
73+
}
74+
75+
if (filterKey == 'testName') {
76+
if (Array.isArray(filterValue)) {
77+
throw new Error('no more than one --testName argument allowed as it equals to AND operator, use regex');
78+
}
79+
80+
if (!(new RegExp(String(filterValue), 'i').test(testMetadata[filterKey]))) {
81+
testMetadata.shouldSkip = true;
82+
break;
83+
}
84+
} else {
85+
if (Array.isArray(filterValue)) {
86+
let remainingMatches = filterValue.length;
87+
filterValue.forEach(singleFilterValue => {
88+
if (new RegExp(String(singleFilterValue), 'i').test(String(testMetadata.customData.get(filterKey)))) {
89+
remainingMatches--;
90+
}
91+
});
92+
93+
if (remainingMatches > 0) {
94+
testMetadata.shouldSkip = true;
95+
break;
96+
}
97+
} else if (!(new RegExp(String(filterValue), 'i').test(String(testMetadata.customData.get(filterKey))))) {
98+
testMetadata.shouldSkip = true;
99+
break;
100+
}
101+
}
102+
}
103+
104+
const currentTest = async () => {
105+
try {
106+
await testClass[testMethod].call(testClassInstance);
107+
} catch (error) {
108+
if (error instanceof Error) {
109+
testMetadata.error = error;
110+
throw error;
111+
}
112+
}
113+
};
114+
115+
Object.defineProperty(currentTest, 'name', { value: testMethod }); // !!! Important
116+
tests.set(testMethod, currentTest);
117+
}
118+
49119
nativeLibrary.describe(title, () => {
50120
nativeLibrary.beforeAll(async () => await testClassSymbolMethods.beforeAll.call(testClassInstance));
51121

@@ -63,18 +133,16 @@ export function SuiteDecorator<T extends BellatrixTest>(target: ParameterlessCto
63133

64134
nativeLibrary.afterAll(async () => await testClassSymbolMethods.afterAll.call(testClassInstance));
65135

66-
for (const testMethod of testMethods) {
67-
nativeLibrary.it(testMethod, async () => {
68-
try {
69-
await testClass[testMethod].call(testClassInstance);
70-
} catch (error) {
71-
if (error instanceof Error) {
72-
getTestMetadata(testClass[testMethod], testClass).error = error;
73-
throw error;
74-
}
75-
}
76-
}, testSettings.testTimeout!);
77-
}
136+
tests.forEach((testFunction, testName) => {
137+
const testMetadata = getTestMetadata(testClass[(testFunction as Function).name], testClass);
138+
if (testMetadata.shouldSkip) {
139+
nativeLibrary.xit(testName, testFunction as never, testSettings.testTimeout);
140+
} else if (testMetadata.only) {
141+
nativeLibrary.fit(testName, testFunction as never, testSettings.testTimeout);
142+
} else {
143+
nativeLibrary.it(testName, testFunction as never, testSettings.testTimeout);
144+
}
145+
});
78146
});
79147
}
80148

@@ -172,85 +240,64 @@ export {
172240

173241
type JasmineModule = {
174242
/**
175-
* Run some shared setup once before all of the specs in the {@link describe} are run.
176-
*
177-
* _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
178-
* @name beforeAll
179-
* @since 2.1.0
180-
* @function
181-
* @global
182-
* @param {implementationCallback} [function] Function that contains the code to setup your specs.
183-
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll.
184-
* @see async
243+
* Run some shared setup once before all of the specs in the describe are run.
244+
* Note: Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
245+
* @param action Function that contains the code to setup your specs.
246+
* @param timeout Custom timeout for an async beforeAll.
185247
*/
186-
beforeAll: (fn: () => void, timeout?: number) => void;
248+
beforeAll: (action: () => void, timeout?: number) => void;
187249

188250
/**
189-
* Run some shared teardown once after all of the specs in the {@link describe} are run.
190-
*
191-
* _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
192-
* @name afterAll
193-
* @since 2.1.0
194-
* @function
195-
* @global
196-
* @param {implementationCallback} [function] Function that contains the code to teardown your specs.
197-
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll.
198-
* @see async
251+
* Run some shared teardown once after all of the specs in the describe are run.
252+
* Note: Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
253+
* @param action Function that contains the code to teardown your specs.
254+
* @param timeout Custom timeout for an async afterAll
199255
*/
200-
afterAll: (fn: () => void, timeout?: number) => void;
256+
afterAll: (action: () => void, timeout?: number) => void;
201257

202258
/**
203259
* Create a group of specs (often called a suite).
204-
*
205-
* Calls to `describe` can be nested within other calls to compose your suite as a tree.
206-
* @name describe
207-
* @since 1.3.0
208-
* @function
209-
* @global
210-
* @param {String} description Textual description of the group
211-
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs
260+
* @param description Textual description of the group
261+
* @param specDefinitions Function for Jasmine to invoke that will define inner suites a specs
212262
*/
213263
describe: (description: string, specDefinitions: () => void) => void;
214264

215265
/**
216-
* Run some shared setup before each of the specs in the {@link describe} in which it is called.
217-
* @name beforeEach
218-
* @since 1.3.0
219-
* @function
220-
* @global
221-
* @param {implementationCallback} [function] Function that contains the code to setup your specs.
222-
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach.
223-
* @see async
266+
* Run some shared setup before each of the specs in the describe in which it is called.
267+
* @param action Function that contains the code to setup your specs.
268+
* @param timeout Custom timeout for an async beforeEach.
224269
*/
225-
beforeEach: (fn: () => void, timeout?: number) => void;
270+
beforeEach: (action: () => void, timeout?: number) => void;
226271

227272
/**
228-
* Run some shared teardown after each of the specs in the {@link describe} in which it is called.
229-
* @name afterEach
230-
* @since 1.3.0
231-
* @function
232-
* @global
233-
* @param {implementationCallback} [function] Function that contains the code to teardown your specs.
234-
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach.
235-
* @see async
273+
* Run some shared teardown after each of the specs in the describe in which it is called.
274+
* @param action Function that contains the code to teardown your specs.
275+
* @param timeout Custom timeout for an async afterEach.
236276
*/
237-
afterEach: (fn: () => void, timeout?: number) => void;
277+
afterEach: (action: () => void, timeout?: number) => void;
238278

239279
/**
240-
* Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code.
241-
*
280+
* Define a single spec. A spec should contain one or more expectations that test the state of the code.
242281
* A spec whose expectations all succeed will be passing and a spec with any failures will fail.
243-
* The name `it` is a pronoun for the test target, not an abbreviation of anything. It makes the
244-
* spec more readable by connecting the function name `it` and the argument `description` as a
245-
* complete sentence.
246-
* @name it
247-
* @since 1.3.0
248-
* @function
249-
* @global
250-
* @param {String} description Textual description of what this spec is checking
251-
* @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`.
252-
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
253-
* @see async
282+
* @param expectation Textual description of what this spec is checking
283+
* @param assertion Function that contains the code of your test. If not provided the test will be pending.
284+
* @param timeout Custom timeout for an async spec.
285+
*/
286+
it: (expectation: string, assertion?: () => void, timeout?: number) => void;
287+
288+
/**
289+
* A focused `it`. If suites or specs are focused, only those that are focused will be executed.
290+
* @param expectation Textual description of what this spec is checking
291+
* @param assertion Function that contains the code of your test. If not provided the test will be pending.
292+
* @param timeout Custom timeout for an async spec.
293+
*/
294+
fit: (expectation: string, assertion?: () => void, timeout?: number) => void;
295+
296+
/**
297+
* A temporarily disabled `it`. The spec will report as pending and will not be executed.
298+
* @param expectation Textual description of what this spec is checking
299+
* @param assertion Function that contains the code of your test. If not provided the test will be pending.
300+
* @param timeout Custom timeout for an async spec.
254301
*/
255-
it: (description: string, testFunction?: () => void, timeout?: number) => void;
302+
xit: (expectation: string, assertion?: () => void, timeout?: number) => void;
256303
};

@bellatrix/core/src/test/jest.ts

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
import inspector from 'inspector';
2+
3+
let isDebuggerAttached = false;
4+
if (inspector.url() !== undefined && new URL(inspector.url()!).port === '12016' && !isDebuggerAttached) {
5+
inspector.waitForDebugger();
6+
isDebuggerAttached = true;
7+
}
8+
19
import * as nativeLibrary from '@jest/globals';
210
import 'reflect-metadata';
311

@@ -11,6 +19,7 @@ import type { ConfigureFn, Method, MethodNames, ParameterlessCtor, TestFn } from
1119

1220
const BaseTest = ServiceLocator.resolveType(BellatrixTest);
1321
const testSettings = BellatrixSettings.get().frameworkSettings.testSettings;
22+
const testFilters = JSON.parse(process.env.BELLATRIX_TEST_FILTER!);
1423

1524
function getSymbolMethods<T extends BellatrixTest>(type: ParameterlessCtor<T>) {
1625
return {
@@ -33,6 +42,66 @@ export function SuiteDecorator<T extends BellatrixTest>(target: ParameterlessCto
3342
const testMethods = Object.getOwnPropertyNames(testClass).filter(method => typeof testClass[method] === 'function' && Reflect.hasMetadata(Symbols.TEST, testClass[method]));
3443
const title = target.name; // or passed as @Suite('title') or similar
3544

45+
const tests: Map<string, unknown> = new Map;
46+
for (const testMethod of testMethods) {
47+
const testMetadata = getTestMetadata(testClass[testMethod], testClass);
48+
49+
for (const [filterKey, filterValue] of Object.entries(testFilters)) {
50+
if (filterKey == 'suiteName') {
51+
if (Array.isArray(filterValue)) {
52+
throw new Error('no more than one --suiteName argument allowed as it equals to AND operator, use regex');
53+
}
54+
55+
if (!(new RegExp(String(filterValue), 'i').test(testMetadata[filterKey]))) {
56+
testMetadata.shouldSkip = true;
57+
break;
58+
}
59+
}
60+
61+
if (filterKey == 'testName') {
62+
if (Array.isArray(filterValue)) {
63+
throw new Error('no more than one --testName argument allowed as it equals to AND operator, use regex');
64+
}
65+
66+
if (!(new RegExp(String(filterValue), 'i').test(testMetadata[filterKey]))) {
67+
testMetadata.shouldSkip = true;
68+
break;
69+
}
70+
} else {
71+
if (Array.isArray(filterValue)) {
72+
let remainingMatches = filterValue.length;
73+
filterValue.forEach(singleFilterValue => {
74+
if (new RegExp(String(singleFilterValue), 'i').test(String(testMetadata.customData.get(filterKey)))) {
75+
remainingMatches--;
76+
}
77+
});
78+
79+
if (remainingMatches > 0) {
80+
testMetadata.shouldSkip = true;
81+
break;
82+
}
83+
} else if (!(new RegExp(String(filterValue), 'i').test(String(testMetadata.customData.get(filterKey))))) {
84+
testMetadata.shouldSkip = true;
85+
break;
86+
}
87+
}
88+
}
89+
90+
const currentTest = async () => {
91+
try {
92+
await testClass[testMethod].call(testClassInstance);
93+
} catch (error) {
94+
if (error instanceof Error) {
95+
testMetadata.error = error;
96+
throw error;
97+
}
98+
}
99+
};
100+
101+
Object.defineProperty(currentTest, 'name', { value: testMethod }); // !!! Important
102+
tests.set(testMethod, currentTest);
103+
}
104+
36105
nativeLibrary.jest.setTimeout(testSettings.testTimeout);
37106
nativeLibrary.describe(title, () => {
38107
nativeLibrary.beforeAll(async () => await testClassSymbolMethods.beforeAll.call(testClassInstance));
@@ -51,26 +120,24 @@ export function SuiteDecorator<T extends BellatrixTest>(target: ParameterlessCto
51120

52121
nativeLibrary.afterAll(async () => await testClassSymbolMethods.afterAll.call(testClassInstance));
53122

54-
for (const testMethod of testMethods) {
55-
nativeLibrary.test(testMethod, async () => {
56-
try {
57-
await testClass[testMethod].call(testClassInstance);
58-
} catch (error) {
59-
if (error instanceof Error) {
60-
getTestMetadata(testClass[testMethod], testClass).error = error;
61-
throw error;
62-
}
63-
}
64-
});
65-
}
123+
tests.forEach((testFunction, testName) => {
124+
const testMetadata = getTestMetadata(testClass[(testFunction as Function).name], testClass);
125+
if (testMetadata.shouldSkip) {
126+
nativeLibrary.test.skip(testName, testFunction as never);
127+
} else if (testMetadata.only) {
128+
nativeLibrary.test.only(testName, testFunction as never);
129+
} else {
130+
nativeLibrary.test(testName, testFunction as never);
131+
}
132+
});
66133
});
67134
}
68135

69136
function test<T extends BellatrixTest, K extends string>(target: T, key: K extends MethodNames<BellatrixTest> ? never : K): void;
70137
function test(name: string, fn: TestFn<TestProps>): void;
71-
function test<T extends BellatrixTest, K extends string>(name: unknown, fn: unknown): void {
72-
if (name instanceof BellatrixTest) {
73-
const target = name as T;
138+
function test<T extends BellatrixTest, K extends string>(nameOrTarget: unknown, fn: unknown): void {
139+
if (nameOrTarget instanceof BellatrixTest) {
140+
const target = nameOrTarget as T;
74141
const key = fn as K extends MethodNames<BellatrixTest> ? never : K;
75142
defineTestMetadata(target[key as keyof T] as (...args: unknown[]) => (Promise<void> | void), target.constructor as ParameterlessCtor<T>);
76143
return;
@@ -84,9 +151,9 @@ function test<T extends BellatrixTest, K extends string>(name: unknown, fn: unkn
84151
}
85152

86153
const testFn = async () => await (fn as TestFn<TestProps>)(ServiceLocator.resolve(TestProps));
87-
Object.defineProperty(testFn, 'name', { value: name });
88-
currentTestClass.constructor.prototype[name as keyof T] = testFn;
89-
test(currentTestClass, name as string);
154+
Object.defineProperty(testFn, 'name', { value: nameOrTarget });
155+
currentTestClass.constructor.prototype[nameOrTarget as keyof T] = testFn;
156+
test(currentTestClass, nameOrTarget as string);
90157
}
91158

92159
function describe(title: string, fn: () => void): void {

0 commit comments

Comments
 (0)