Skip to content

Instantly share code, notes, and snippets.

@azz
Last active February 7, 2016 23:57
Show Gist options
  • Select an option

  • Save azz/6dadf730b724326eec88 to your computer and use it in GitHub Desktop.

Select an option

Save azz/6dadf730b724326eec88 to your computer and use it in GitHub Desktop.
Typescript Test Runner
class TestUtils {
// A factory function
static makeTestDecorator(prop: string, modifierFn?: (...fnArgs) => any) {
// Apply modifierFn if it exists, and
// Flatten single-element arrays to a value
const handle = (...values) => {
if (modifierFn)
return modifierFn(...values);
if (values.length === 1)
return values[0];
return values;
}
// A function that produces our decorator
return (...decorations: any[]) => {
// The actual decorator function
return (target, key?) => {
if (key) { // Target is a method
_.extend(target[key], {[prop]: handle(...decorations)})
} else { // Target is a class
// This __test prop is applied to the class object,
// not the instance object.
target.__test = {
[prop]: handle(...decorations)
};
}
}
}
}
}
const TestDecorators = {
bugs: TestUtils.makeTestDecorator('bugs'),
pull: TestUtils.makeTestDecorator('pull'),
it: TestUtils.makeTestDecorator('it'),
describe: TestUtils.makeTestDecorator('describes'),
repro: TestUtils.makeTestDecorator('repro', (steps) => {
return steps
.split('\n')
.map(l => l.trim())
.filter(l => !!l.length)
.join('\n')
})
}
abstract class TestBase {
test: { describes?: string };
constructor() {
// Copy the __test prop from the class to the instance
this.test = this.constructor['__test'];
}
isTest(name: string) {
return /^test/.test(name) && typeof this[name] === "function";
}
}
// a source file
function add(x, y) {
return x - y; // intentional error
}
// in a test file...
const {bugs, pull, repro, it, describe} = TestDecorators;
@describe("add function")
class TestClass extends TestBase {
// ...
@it("should evaluate 0+0 === 0")
public testZeroPlusZero() {
return add(0, 0) === 0;
}
@repro(`
Call add.
Pass two non-zero numbers.
`)
@bugs(1235, 3444)
@pull(343)
@it("should evaluate 1+1 === 2")
public testICanAdd() {
return add(1, 1) === 2;
}
}
// in the test runner...
class TestRunner {
runTests(testInstance: TestBase) {
console.info('* ', testInstance.test.describes);
for (let testName in testInstance) {
if (!testInstance.isTest(testName))
continue;
// run test
let success = !!testInstance[testName]();
// collect metadata
let results = _.extend({}, testInstance[testName].test, {
testName, success, parent: testInstance.parent
});
console[success ? 'log' : 'warn']('\t- ', results.it, results);
}
}
}
// Main
(() => {
let instance = new TestClass;
let runner = new TestRunner;
console.log('Running tests...');
runner.runTests(instance);
})();
@azz

azz commented Feb 6, 2016

Copy link
Copy Markdown
Author

Running in Chrome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment