UNPKG

pxt-common-packages

Version:
137 lines (120 loc) 3.87 kB
/** * Various test event in the execution cycle */ enum TestEvent { //% block="run setup" RunSetUp = 0, //% block="run teardown" RunTearDown = 1, //% block="test setup" TestSetUp = 2, //% block="test teardown" TestTearDown = 3 } /** * A Unit tests framework */ //% weight=100 color=#0fbc11 icon="" namespace tests { class Test { name: string; handler: () => void; errors: string[]; constructor(name: string, handler: () => void) { this.name = name; this.handler = handler; this.errors = []; } run() { // clear state if (_runSetup) _runSetup(); console.log(`> ${this.name}`) this.handler() if (this.errors.length) console.log('') // ensure clean state after test if (_runTearDown) _runTearDown(); } } let _tests: Test[] = undefined; let _currentTest: Test = undefined; let _runSetup: () => void = undefined; let _runTearDown: () => void = undefined; let _testSetUp: () => void = undefined; let _testTearDown: () => void = undefined; function run() { if (!_tests) return; if (_testSetUp) _testSetUp(); const start = control.millis(); console.log(`${_tests.length} tests found`) console.log(` `) for (let i = 0; i < _tests.length; ++i) { const t = _currentTest = _tests[i]; t.run(); _currentTest = undefined; } if (_testTearDown) _testTearDown(); console.log(` `) console.log(`${_tests.length} tests, ${_tests.map(t => t.errors.length).reduce((p, c) => p + c, 0)} errs in ${Math.ceil((control.millis() - start) / 1000)}s`) } /** * Registers a test to run */ //% blockId=testtest block="test %name" //% weight=100 export function test(name: string, handler: () => void): void { if (!name || !handler) return; if (!_tests) { _tests = []; control.runInParallel(function () { // should run after on start pause(100) run() }) } _tests.push(new Test(name, handler)); } /** * Checks a boolean condition */ //% blockId=testAssert block="assert %message|%condition" //% weight=80 //% blockGap=8 export function assert(message: string, condition: boolean) { if (!condition) { console.log(`!!! ${message || ''}`) if (_currentTest) _currentTest.errors.push(message); } } /** * Checks that 2 values are close to each other * @param expected what the value should be * @param actual what the value was * @param tolerance the acceptable error margin, eg: 5 */ //% blockId=testAssertClose block="assert %message|%expected|close to %actual|by %tolerance" //% weight=79 //% inlineInputMode=inline export function assertClose(name: string, expected: number, actual: number, tolerance: number) { assert(`${name} ${expected} != ${actual} +-${tolerance}`, Math.abs(expected - actual) <= tolerance); } /** * Registers code to be called at various points in the test execution * @param handler */ //% blockGap=8 //% weight=10 export function onEvent(event: TestEvent, handler: () => void) { switch(event) { case TestEvent.RunSetUp: _runSetup = handler; break; case TestEvent.RunTearDown: _runTearDown = handler; break; case TestEvent.TestSetUp: _testSetUp = handler; break; case TestEvent.TestTearDown: _testTearDown = handler; break; } } }