Skip to content

Instantly share code, notes, and snippets.

@adamcameron
Last active November 6, 2023 09:13
Show Gist options
  • Save adamcameron/816ce84fd991c2682df612dbaf1cad11 to your computer and use it in GitHub Desktop.
Save adamcameron/816ce84fd991c2682df612dbaf1cad11 to your computer and use it in GitHub Desktop.
A tiny testing framework to use with trycf.com
<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