Skip to content

Instantly share code, notes, and snippets.

@IlyaGershman
Last active October 14, 2024 02:07
Show Gist options
  • Save IlyaGershman/17cb1771bcc48ab392c8320c312ada36 to your computer and use it in GitHub Desktop.
Save IlyaGershman/17cb1771bcc48ab392c8320c312ada36 to your computer and use it in GitHub Desktop.
This is a small set of functions to help you write simple tests without frameworks
type Config = {
logPerformance?: boolean;
timeout?: number; // Timeout in milliseconds
};
function createTestRunner(initialConfig?: Config) {
let globalConfig: Config = {
logPerformance: false,
timeout: 5000,
...initialConfig,
};
const assert = {
condition: (condition: boolean, message?: string) => {
if (!condition) {
throw new Error(message || "Assertion failed: condition is false");
}
},
isDeepEqual: (a: any, b: any, message?: string) => {
const stringify1 = JSON.stringify(a);
const stringify2 = JSON.stringify(b);
if (stringify1 !== stringify2) {
throw new Error(
message ||
`Assertion failed: values are not equal ${stringify1} !== ${stringify2}`
);
}
},
shouldThrow: (fn: Function) => {
const message = "Assertion failed: the function hasn't thrown";
try {
fn();
throw new Error(message);
} catch (e) {
if (e instanceof Error && e.message === message) {
throw e;
}
return true;
}
},
};
function setConfig(config: Config) {
globalConfig = { ...globalConfig, ...config };
}
function it(
desc: string,
fn: (done: (error?: any) => void) => void | Promise<void>,
config?: Config
) {
const { logPerformance, timeout } = { ...globalConfig, ...config };
const startTime = Date.now();
const testPromise = executeTestFunction(fn, timeout);
handleTestResult(testPromise, desc, startTime, logPerformance);
}
function executeTestFunction(fn: Function, timeout?: number): Promise<void> {
return new Promise<void>((resolve, reject) => {
let doneCalled = false;
const done = (error?: any) => {
if (doneCalled) {
reject(new Error("done() called multiple times"));
return;
}
doneCalled = true;
if (error) {
reject(error);
} else {
resolve();
}
};
try {
const result = fn.length > 0 ? fn(done) : fn();
if (result instanceof Promise) {
result.then(resolve).catch(reject);
} else if (fn.length === 0 && result === undefined) {
// Synchronous test passed
resolve();
}
if (fn.length > 0 && result === undefined) {
const timeoutDuration = timeout ?? globalConfig.timeout ?? 5000;
setTimeout(() => {
if (!doneCalled) {
reject(new Error("Test timed out: done() was not called"));
}
}, timeoutDuration);
}
} catch (error) {
reject(error);
}
});
}
function handleTestResult(
testPromise: Promise<void>,
desc: string,
startTime: number,
logPerformance?: boolean
) {
testPromise
.then(() => {
logTestSuccess(desc, startTime, logPerformance);
})
.catch((error) => {
logTestFailure(desc, startTime, error, logPerformance);
});
}
function logTestSuccess(
desc: string,
startTime: number,
logPerformance?: boolean
) {
const endTime = Date.now();
let message = `\x1b[32m\u2714 ${desc}\x1b[0m`;
if (logPerformance) {
const duration = endTime - startTime;
message += ` (Duration: ${duration} ms)`;
}
console.log(message);
}
function logTestFailure(
desc: string,
startTime: number,
error: any,
logPerformance?: boolean
) {
const endTime = Date.now();
let message = `\n\x1b[31m\u2718 ${desc}\x1b[0m`;
if (logPerformance) {
const duration = endTime - startTime;
message += ` (Duration: ${duration} ms)`;
}
console.log(message);
console.error(error);
}
// Return the methods
return { it, assert, setConfig };
}
// Usage example
const { it, assert, setConfig } = createTestRunner({
logPerformance: true,
timeout: 5000,
});
// Synchronous test
it("should add numbers correctly", () => {
const result = 1 + 1;
assert.condition(result === 2, "1 + 1 should equal 2");
});
// Promise-based asynchronous test
it("should resolve after 1 second", () => {
return new Promise<void>((resolve) => {
setTimeout(() => {
assert.condition(true);
resolve();
}, 1000);
});
});
// Callback-based asynchronous test with custom timeout
it(
"should call done after async operation",
(done) => {
setTimeout(() => {
assert.condition(true);
done();
}, 3000);
},
{
timeout: 4000,
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment