项目文件夹

文件
wehub-resource-sync 98e40dac97
CLI Smoke Test / smoke-test-linux (20) (push) Has been cancelled
CLI Smoke Test / smoke-test-linux (24) (push) Has been cancelled
CLI Smoke Test / smoke-test-windows (20) (push) Has been cancelled
CLI Smoke Test / smoke-test-windows (24) (push) Has been cancelled
Expo App TypeScript typecheck / typecheck (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:40:49 +08:00

277 行
8.1 KiB
TypeScript

interface TestResult {
name: string;
passed: boolean;
error?: Error;
duration: number;
}
interface TestSuite {
name: string;
tests: TestResult[];
}
class TestRunner {
private suites: Map<string, TestSuite> = new Map();
private currentSuite: string | null = null;
private currentTests: Array<{ name: string; fn: () => void | Promise<void> }> = [];
describe(name: string, fn: () => void) {
this.currentSuite = name;
this.currentTests = [];
// Execute the describe block to collect tests
fn();
// Store the collected tests
this.suites.set(name, {
name,
tests: []
});
// Store test functions for later execution
const suite = this.suites.get(name)!;
suite.tests = this.currentTests.map(test => ({
name: test.name,
passed: false,
duration: 0
}));
// Store the test functions separately for execution
(suite as any).testFunctions = this.currentTests;
this.currentSuite = null;
}
it(name: string, fn: () => void | Promise<void>) {
if (!this.currentSuite) {
throw new Error('it() must be called inside describe()');
}
this.currentTests.push({ name, fn });
}
async runAll(): Promise<TestSuite[]> {
const results: TestSuite[] = [];
for (const [suiteName, suite] of this.suites) {
const testFunctions = (suite as any).testFunctions as Array<{ name: string; fn: () => void | Promise<void> }>;
const suiteResult: TestSuite = {
name: suiteName,
tests: []
};
for (let i = 0; i < testFunctions.length; i++) {
const test = testFunctions[i];
const startTime = Date.now();
try {
await test.fn();
suiteResult.tests.push({
name: test.name,
passed: true,
duration: Date.now() - startTime
});
} catch (error) {
suiteResult.tests.push({
name: test.name,
passed: false,
error: error as Error,
duration: Date.now() - startTime
});
}
}
results.push(suiteResult);
}
return results;
}
async runSuite(suiteName: string): Promise<TestSuite | null> {
const suite = this.suites.get(suiteName);
if (!suite) return null;
const testFunctions = (suite as any).testFunctions as Array<{ name: string; fn: () => void | Promise<void> }>;
const suiteResult: TestSuite = {
name: suiteName,
tests: []
};
for (const test of testFunctions) {
const startTime = Date.now();
try {
await test.fn();
suiteResult.tests.push({
name: test.name,
passed: true,
duration: Date.now() - startTime
});
} catch (error) {
suiteResult.tests.push({
name: test.name,
passed: false,
error: error as Error,
duration: Date.now() - startTime
});
}
}
return suiteResult;
}
getSuites(): string[] {
return Array.from(this.suites.keys());
}
clear() {
this.suites.clear();
this.currentSuite = null;
this.currentTests = [];
}
}
// Simple expect implementation
class Expect<T> {
private negated = false;
constructor(private actual: T) {}
get not(): Expect<T> {
const newExpect = new Expect(this.actual);
newExpect.negated = true;
return newExpect;
}
toEqual(expected: T) {
const isEqual = this.deepEqual(this.actual, expected);
if (this.negated) {
if (isEqual) {
throw new Error(`Expected ${JSON.stringify(expected)} to not equal ${JSON.stringify(this.actual)}`);
}
} else {
if (!isEqual) {
throw new Error(`Expected ${JSON.stringify(expected)} but got ${JSON.stringify(this.actual)}`);
}
}
}
toBe(expected: T) {
const isEqual = this.actual === expected;
if (this.negated) {
if (isEqual) {
throw new Error(`Expected ${expected} to not be ${this.actual}`);
}
} else {
if (!isEqual) {
throw new Error(`Expected ${expected} but got ${this.actual}`);
}
}
}
toBeTruthy() {
if (this.negated) {
if (this.actual) {
throw new Error(`Expected falsy value but got ${this.actual}`);
}
} else {
if (!this.actual) {
throw new Error(`Expected truthy value but got ${this.actual}`);
}
}
}
toBeFalsy() {
if (this.negated) {
if (!this.actual) {
throw new Error(`Expected truthy value but got ${this.actual}`);
}
} else {
if (this.actual) {
throw new Error(`Expected falsy value but got ${this.actual}`);
}
}
}
toBeGreaterThan(expected: number) {
const actualNum = this.actual as unknown as number;
if (this.negated) {
if (actualNum > expected) {
throw new Error(`Expected ${actualNum} to not be greater than ${expected}`);
}
} else {
if (!(actualNum > expected)) {
throw new Error(`Expected ${actualNum} to be greater than ${expected}`);
}
}
}
toThrow(message?: string) {
if (typeof this.actual !== 'function') {
throw new Error('toThrow can only be used with functions');
}
let didThrow = false;
let thrownError: any;
try {
(this.actual as any)();
} catch (error: any) {
didThrow = true;
thrownError = error;
}
if (this.negated) {
if (didThrow) {
throw new Error('Expected function to not throw but it did');
}
} else {
if (!didThrow) {
throw new Error('Expected function to throw but it did not');
}
if (message && thrownError && !thrownError.message.includes(message)) {
throw new Error(`Expected error message to include "${message}" but got "${thrownError.message}"`);
}
}
}
private deepEqual(a: any, b: any): boolean {
if (a === b) return true;
if (a instanceof Uint8Array && b instanceof Uint8Array) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
if (a && b && typeof a === 'object' && typeof b === 'object') {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
for (const key of keysA) {
if (!keysB.includes(key)) return false;
if (!this.deepEqual(a[key], b[key])) return false;
}
return true;
}
return false;
}
}
// Global test runner instance
export const testRunner = new TestRunner();
// Export test functions
export const describe = (name: string, fn: () => void) => testRunner.describe(name, fn);
export const it = (name: string, fn: () => void | Promise<void>) => testRunner.it(name, fn);
export const expect = <T>(actual: T) => new Expect(actual);
export type { TestResult, TestSuite };