A tiny testing framework to use with trycf.com
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<style> | |
.tinyTest {background-color: black; color:white; font-family:monospace} | |
.tinyTest div {margin-left: 1em} | |
.tinyTest .pass {color:green;} | |
.tinyTest .fail {color:red;} | |
.tinyTest .error {background-color:red; color:black} | |
</style> | |
<cfscript> | |
tinyTest = { | |
results = { | |
pass = 0, | |
fail = 0, | |
error = 0 | |
}, | |
runTests = () => { | |
writeOutput('<div class="tinyTest">') | |
try { | |
run() | |
} finally { | |
tinyTest.handlers.keyExists("afterAllHandler") ? tinyTest.handlers.afterAllHandler() : false | |
} | |
tinyTest.showResults() | |
writeOutput('</div>') | |
}, | |
showResults = () => { | |
savecontent variable="local.results" { | |
writeOutput("<div>Results:") | |
writeOutput(" [Pass: #tinyTest.results.pass#]") | |
writeOutput(" [Fail: #tinyTest.results.fail#]") | |
writeOutput(" [Error: #tinyTest.results.error#]") | |
writeOutput(" [Total: #tinyTest.results.pass + tinyTest.results.fail + tinyTest.results.error#]") | |
writeOutput("</div>") | |
} | |
writeOutput(local.results) | |
}, | |
contexts = [], | |
handlers = {}, | |
describe = (string label, function testGroup) => { | |
tinyTest.handlers.keyExists("beforeAllHandler") ? tinyTest.handlers.beforeAllHandler() : false | |
tinyTest.inDiv(() => { | |
try { | |
writeOutput("#label#<br>") | |
tinyTest.contexts.push({}) | |
testGroup() | |
} catch (any e) { | |
writeOutput("Error: #e.message#<br>") | |
} finally { | |
tinyTest.contexts.pop() | |
} | |
}) | |
}, | |
beforeEach = (callback) => { | |
tinyTest.contexts.last().beforeEachHandler = callback | |
}, | |
afterEach = (callback) => { | |
tinyTest.contexts.last().afterEachHandler = callback | |
}, | |
aroundEach = (callback) => { | |
tinyTest.contexts.last().aroundEachHandler = callback | |
}, | |
beforeAll = (callback) => { | |
tinyTest.handlers.beforeAllHandler = () => { | |
callback() | |
structDelete(tinyTest.handlers, "beforeAllHandler") | |
} | |
}, | |
afterAll = (callback) => { | |
tinyTest.handlers.afterAllHandler = callback | |
}, | |
it = (string label, function implementation) => { | |
tinyTest.inDiv(() => { | |
try { | |
writeOutput("It #label#: ") | |
tinyTest.contexts | |
.filter((context) => context.keyExists("beforeEachHandler")) | |
.each((context) => context.beforeEachHandler()) | |
decoratedImplementation = tinyTest.contexts | |
.filter((context) => context.keyExists("aroundEachHandler")) | |
.reduce((reversed, context) => reversed.prepend(context), []) | |
.reduce((decorated, context) => () => context.aroundEachHandler(decorated), () => { | |
try { | |
implementation() | |
tinyTest.testResult = true | |
} catch (any e) { | |
tinyTest.testResult = e | |
} | |
}) | |
try { | |
decoratedImplementation() | |
if (!tinyTest.testResult.equals(true)) { | |
throw(object=tinyTest.testResult) | |
} | |
} finally { | |
tinyTest.contexts | |
.filter((context) => context.keyExists("afterEachHandler")) | |
.reduce((reversedContexts, context) => reversedContexts.prepend(context), []) | |
.each((context) => context.afterEachHandler()) | |
} | |
tinyTest.handlePass() | |
} catch (TinyTest e) { | |
tinyTest.handleFail() | |
} catch (any e) { | |
tinyTest.handleError(e) | |
} | |
}) | |
}, | |
expect = (any actual) => { | |
var proxy.actual = arguments?.actual | |
return { | |
toBe = (expected) => tinyTest.matchers.toBe(expected, actual), | |
toBeTrue = () => tinyTest.matchers.toBe(true, actual), | |
toBeFalse = () => tinyTest.matchers.toBe(false, actual), | |
toBeNull = () => tinyTest.matchers.toBeNull(proxy?.actual), | |
toThrow = () => tinyTest.matchers.toThrow(actual), | |
toInclude = (needle) => tinyTest.matchers.toInclude(needle, actual), | |
toHaveKey = (key) => tinyTest.matchers.toHaveKey(key, actual), | |
toHaveLength = (length) => tinyTest.matchers.toHaveLength(length, actual), | |
notToBe = (expected) => tinyTest.matchers.not((expected) => tinyTest.matchers.toBe(expected, actual)), | |
notToBeTrue = (expected) => tinyTest.matchers.not((expected) => tinyTest.matchers.toBe(true, actual)), | |
notToBeFalse = (expected) => tinyTest.matchers.not((expected) => tinyTest.matchers.toBe(false, actual)), | |
notToBeNull = () => tinyTest.matchers.not(() => tinyTest.matchers.toBeNull(proxy?.actual)), | |
notToThrow = () => tinyTest.matchers.not(() => tinyTest.matchers.toThrow(actual)), | |
notToInclude = (needle) => tinyTest.matchers.not((needle) => tinyTest.matchers.toInclude(needle, actual)), | |
notToHaveKey = (key) => tinyTest.matchers.not((key) => tinyTest.matchers.toHaveKey(key, actual)), | |
notToHaveLength = (length) => tinyTest.matchers.not((length) => tinyTest.matchers.toHaveLength(length, actual)) | |
} | |
}, | |
fail = () => { | |
throw(type="TinyTest.FailureException") | |
}, | |
matchers = { | |
toBe = (expected, actual) => { | |
if (actual.equals(expected)) { | |
return true | |
} | |
throw(type="TinyTest.TestFailedException") | |
}, | |
toBeNull = (actual) => { | |
if (isNull(actual)) { | |
return true | |
} | |
throw(type="TinyTest.TestFailedException") | |
}, | |
toThrow = (callback) => { | |
try { | |
callback() | |
throw(type="TinyTest.TestFailedException") | |
} catch (TinyTest.TestFailedException e) { | |
rethrow | |
} catch (any e) { | |
return true | |
} | |
}, | |
toInclude = (needle, haystack) => { | |
return tinyTest.matchers.toBe(true, haystack.findNoCase(needle) > 0) | |
}, | |
toHaveKey = (key, struct) => { | |
return tinyTest.matchers.toBe(true, struct.keyExists(key)) | |
}, | |
toHaveLength = (length, object) => { | |
return tinyTest.matchers.toBe(true, tinyTest.resolveLength(object) == length) | |
}, | |
"not" = (callback) => { | |
try { | |
callback() | |
throw(type="TinyTest.NotTestFailedException") | |
} catch (TinyTest.NotTestFailedException e) { | |
throw(type="TinyTest.TestFailedException") | |
} catch (TinyTest.TestFailedException e) { | |
return true | |
} catch (any e) { | |
rethrow | |
} | |
} | |
}, | |
resolveLength = (object) => { | |
if (isStruct(object)) { | |
return object.count() | |
} | |
if (isArray(object)) { | |
return object.len() | |
} | |
if (isQuery(object)) { | |
return object.recordCount | |
} | |
if (isSimpleValue(object)) { | |
return object.toString().len() | |
} | |
throw(type="TinyTest.UnsupportedTypeException", message="Cannot resolve length of #object.getClass().getName()#") | |
}, | |
inDiv = (callback) => { | |
writeOutput("<div>") | |
callBack() | |
writeOutput("</div>") | |
}, | |
handlePass = () => { | |
tinyTest.results.pass++ | |
writeOutput('<span class="pass">OK</span>') | |
}, | |
handleFail = () => { | |
tinyTest.results.fail++ | |
writeOutput('<span class="fail"><em>Failed</em></span>') | |
}, | |
handleError = (e) => { | |
tinyTest.results.error++ | |
var message = "Error: [#e.message#][#e.detail#]" | |
writeOutput('<span class="error"><strong>#message#</strong></span>') | |
} | |
} | |
describe = tinyTest.describe | |
it = tinyTest.it | |
expect = tinyTest.expect | |
fail = tinyTest.fail | |
beforeEach = tinyTest.beforeEach | |
afterEach = tinyTest.afterEach | |
aroundEach = tinyTest.aroundEach | |
beforeAll = tinyTest.beforeAll | |
afterAll = tinyTest.afterAll | |
</cfscript> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment