Skip to content

Instantly share code, notes, and snippets.

@johnsonjo4531
Last active November 9, 2021 19:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save johnsonjo4531/d751d7b63576a50878c9772ad9b12a01 to your computer and use it in GitHub Desktop.
Save johnsonjo4531/d751d7b63576a50878c9772ad9b12a01 to your computer and use it in GitHub Desktop.
Tiny Testing Framework that allows async functions and test groups.
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,html,css,json,md,vue,scss,ejs,ts,tsx,jsx,graphql}]
charset = utf-8
indent_style = tab
indent_size = 2
[makefile]
indent_style = tab
indent_size = 4
# .prettierrc or .prettierrc.yaml
trailingComma: "es5"
tabWidth: 2
useTabs: true
semi: true
singleQuote: false

Addition to Ryan Florence's tiny testing framework

Ryan Florence tweeted a tiny testing framework

I added console.groups and an AsyncLock so that the grouping isn't messed up when multiple async tests are run at once.

// https://codereview.stackexchange.com/questions/177935/asynclock-implementation-for-js
export default function AsyncLock() {
const p = () => new Promise(next => (nextIter = next));
var nextIter,
next = p();
const nextP = () => {
const result = next;
next = result.then(() => p());
return result;
};
nextIter();
return Object.assign(
{},
{
async *[Symbol.asyncIterator]() {
try {
yield nextP();
} finally {
nextIter();
}
}
}
);
}
<h1>Check your console</h1>
<script type="module" src="./test.js"></script>
Copyright 2019 johnsonjo4531
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import { test, assert } from "./tiny-test.js";
const timeout = ms =>
new Promise(res => {
console.info(`waiting for ${Number(ms)}ms`);
setTimeout(() => res(), ms);
});
test("simple math", async () => {
assert(2 + 2 == 4, "2 + 2 is 4");
await timeout(1000);
assert(2 * 2 == 18, "2 * 2 is 18");
});
test("more math", async () => {
assert(3 + 2 == 5, "3 + 2 is 5");
await timeout(500);
assert(3 * 3 == 10, "3 * 3 is 10");
});
test("5 is great", async done => {
console.info("timing out for 400ms");
setTimeout(() => {
assert(5 > 2, "5 is greater than 2");
done();
}, 400);
});
// Ryan Florence's Basic Testing Framework modified to support async functions
// https://twitter.com/ryanflorence/status/1162792430422200320
import AsyncLock from "./AsyncLock.js";
const lock = AsyncLock();
var startTime = Date.now();
let totalTestsFailed = 0;
let totalTestsPassed = 0;
// we can share this data between test and assert since only
// one test runs at a time.
let currentTestData;
export const test = async (name, fn) => {
// we have to lock, so that console.groups are grouped together when
// async functions are used. This also makes it so only one test runs at a time.
for await (const _ of lock) {
currentTestData = {
passes: 0,
fails: 0
};
console.group(name);
if (fn.length > 0) {
let timeout;
await new Promise((resolve, reject) => {
try {
fn(resolve);
timeout = setTimeout(() => {
console.info(
"Test is taking a while to finish. Are you sure you called done?"
);
}, 5000);
} catch (err) {
reject(err);
}
});
clearTimeout(timeout);
} else {
await fn();
}
console.info("group assertions passed: ", currentTestData.passes);
console.info("group assertions failed: ", currentTestData.fails);
console.groupEnd();
if (currentTestData.fails > 0) {
totalTestsFailed++;
} else {
totalTestsPassed++;
}
}
};
// setTimeout will run after all the async functions.
setTimeout(async () => {
for await (const _ of lock) {
console.info("Tests finished in " + (Date.now() - startTime) + "ms");
console.info(`Tests passed: `, totalTestsPassed);
console.info(`Tests failed: `, totalTestsFailed);
}
}, 1);
class AssertionError extends Error {
constructor(message) {
super(message);
this.message = "Assertion Failed: " + message;
}
}
export const assert = (condition, description) => {
if (condition) {
console.log("%c✔️", "font-size: 18px; color: green", description);
currentTestData.passes++;
} else {
console.assert(condition, description);
currentTestData.fails++;
}
};
@johnsonjo4531
Copy link
Author

Here's a bl.ocks.org preview (you'll need to check your console to see the tests run.)

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