Awesome
Tupe
<!-- Badges -->Features | Motivations | Configuration | API | Examples | FAQ
A generic unit-testing runner for front-end.➰
Features
- 🚀 Blazing fast: Tupe use the great Parcel as bundler, and Puppeteer as runtime environment under the hook, making it fast on both bundle-time and runtime.
- 🌓 Isolation & 🎎 Concurrency: Each test file is bundled and run as a isolated new tab on puppeteer concurrently.
- 🎨 Descriptive assertion: bonus with Power-Assert builtin.
- 🐦 Configuration-Free: almost no configuration, except for some basic babel setup, which might agrees to your project setup already
- ❄️ Simplicity. use the standard assert interface.
Motivations
There are already tons of unit-testing runner/framework for JavaScript.
- some requires a bit configuration, like karma, not simple enough.
- when it comes to front-end code testing that requires a browser environment, many of them utilize jsdom, which is a subset emulation of a web browser for use in nodejs environment. That's awesome, but would it be great and more reliable if our code runs in a real-world browser?
- features like concurrency and isolation are great. You should definitely try out AVA and Jest, they are awesome. But again, they don't seems to bundle for a real-world browser? Correct me if I'm wrong, thanks~
- Puppeteer is born, which seems to be a perfect fit for browser-side JavaScript testing. Maybe we can try it out and learn how to build a unit test runner, And have all the fun along the way, that's the whole point..
Install
yarn add --dev tupe
CLI
tupe <files...>
Run Tests
Positionals:
files path or glob for test files [string]
Options:
-h, --help Show help [boolean]
-v, --verbose [default: false]
--watch watch mode [default: false]
--port server port [default: 1234]
--tmpdir temporary directory [default: ".tmp"]
--fail-fast exit on first fail [boolean] [default: true]
Configuration
You can configure Tupe by specifying a tupe
field at your project's package.json
files
: file or glob for test filestmpdir
: this is a temporary folder used by Parcel for compiled filesfail-fast
: in a test file, globalbefore
orafter
hooks failure will always terminate the runner as failure; For each individual case, if abeforeEach
hook failed, this case itself and itsafterEach
hooks will not be executed, a case-failure event will araise and the runner will continue to the next case. However, by setting this option totrue
, any case failure will terminate the runner of that test file.coverage
: options for istanbul, defaults to this
{
"tupe": {
"files": [
"test/**/*.js"
],
"tmpdir": ".tmp",
"fail-fast": false,
"coverage": {
"dir": "test/.coverage",
"reporters": ["text"],
"check": {
}
}
}
}
.babelrc
- to enable power-assert feature, add
power-assert
preset and set patterns forbabel-plugin-espower
- to enable code coverage, add
istanbul
plugin, recommend only set for a test env
{
"presets": ["@babel/env", "power-assert"],
"plugins": [
["babel-plugin-espower", {
"patterns": [
"t(value, [message])",
"t.ok(value, [message])",
"t.equal(actual, expected, [message])",
"t.deepEqual(actual, expected, [message])",
"t.notEqual(actual, expected, [message])",
"t.strictEqual(actual, expected, [message])",
"t.notStrictEqual(actual, expected, [message])",
"t.notDeepEqual(actual, expected, [message])",
"t.deepStrictEqual(actual, expected, [message])",
"t.notDeepStrictEqual(actual, expected, [message])",
"t.throws(block, [error], [message])"
]
}]
],
"env": {
"test": {
"plugins": [
["istanbul", {
"include": [
"src/**/*.js"
]
}]
]
}
}
}
API
test.before([title], fn(t, [callback]))
test.beforeEach([title], fn(t, [callback]))
test.afterEach([title], fn(t, [callback]))
test.after([title], fn(t, [callback]))
test(title, fn(t, [callback]))
t
is a enhenced assert function, added with a additionalt.context
abject as a isolated context for each case, live throughbeforeEach
dueafterEach
hook
Examples
import test from 'tupe';
test('hello tupe!', t => {
const who = { name: 'Tupe!' };
t(who.name === 'Tupe!');
});
test('async waiting...', async t => {
const answer = true;
await new Promise(r => setTimeout(r, 1500));
t(answer === true);
});
test('misspell my name ?', t => {
const who = { name: 'Hello Tupe!' };
t(who.name === 'Hel' + 'o Tupe~');
});
test('use callback ?', (t, done) => {
setTimeout(() => {
t('dump' === 'dump');
done();
}, 1500);
});
test('array assert', t => {
const runners = ['AVA', 'Tupe'];
t(runners.indexOf('Tupe') === 0);
});