First implementation of the metaltest

master
Frédéric Matte 2022-11-08 22:29:58 -05:00
parent 16f2281e77
commit f953f607bb
8 changed files with 241 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

59
consolereporter.js Normal file
View File

@ -0,0 +1,59 @@
import chalk from 'chalk'
const log = (...args) => process.stdout.write(...args)
const consolereporter = () => {
const report = {
start: (title) => {
log(chalk.cyanBright(title) + ' ')
},
success: (test) => {
log(chalk.greenBright('✓ '))
},
fail: (test, e) => {
log(chalk.redBright('✗ '))
},
end: (stats) => {
const { success, fail, total, testFail } = stats
log(' ' + chalk.redBright(fail))
log(' ' + chalk.greenBright(success))
log(' ' + chalk.gray('total:', total) + '\n')
for (const test of testFail) {
log('\n' + chalk.redBright(test.name) + '\n')
prettyError(test.error)
log('\n')
}
}
}
return report
}
const prettyError = (error) => {
if (typeof error == 'string') {
log(chalk.yellowBright(error))
log('\n')
return
}
const msg = error.stack
const i = msg.indexOf('\n')
const title = msg.slice(0, i)
log(chalk.yellowBright(title) + '\n')
const at = msg.indexOf(' at ')
const diff = msg.slice(i + 1, at - 1)
if (diff != '\n ')
log(chalk.white(diff))
const atEnd = msg.slice(at).indexOf('\n')
const where = msg.slice(at + 3, at + atEnd)
if (where != '')
log(chalk.gray(where) + '\n')
}
export { consolereporter }

70
index.js Normal file
View File

@ -0,0 +1,70 @@
import { consolereporter } from './consolereporter.js'
import { totalreporter } from './totalreporter.js'
import { runifmain } from './runifmain.js'
const noreporter = {
start: (title) => { },
before: () => { },
success: (test) => { },
fail: (test, e) => { },
after: () => { },
end: (stats) => { },
}
const metaltest = (title) => {
const suite = []
const only = []
const before = []
const after = []
let success = 0
let fail = 0
const testSuccess = []
const testFail = []
const runner = (name, fn) => {
suite.push({ name, fn })
}
runner.only = (fn) => { only.push(fn) }
runner.before = (fn) => { before.push(fn) }
runner.after = (fn) => { after.push(fn) }
runner.run = async (...reporters) => {
const rs = reporters.map(r => Object.assign({}, noreporter, r))
rs.forEach(r => r.start(title))
rs.forEach(r => r.before())
const tests = only.length ? only : suite
for (const test of tests) {
try {
for (const fn of before) await fn()
await test.fn()
success++
rs.forEach(r => r.success(test))
}
catch (e) {
fail++
testFail.push(Object.assign({}, test, { error: e }))
rs.forEach(r => r.fail(test, e))
}
}
for (const fn of after) await fn()
rs.forEach(r => r.after())
const stats = { title, success, fail, total: success + fail, testSuccess, testFail }
rs.forEach(r => r.end(stats))
return stats
}
return runner
}
export { metaltest, consolereporter, totalreporter, runifmain }
export default metaltest

34
package-lock.json generated Normal file
View File

@ -0,0 +1,34 @@
{
"name": "metaltest",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "metaltest",
"version": "1.0.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"chalk": "^5.1.2"
}
},
"node_modules/chalk": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz",
"integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
}
},
"dependencies": {
"chalk": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz",
"integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ=="
}
}
}

19
package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "metaltest",
"version": "1.0.0",
"description": "Clone of baretest with reporting",
"type": "module",
"exports": "./index.js",
"scripts": {
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "https://git.tranche.ca/fmatte/metaltest"
},
"author": "Frédéric Matte <fmatte@gmail.com>",
"license": "AGPL-3.0-or-later",
"dependencies": {
"chalk": "^5.1.2"
}
}

15
runifmain.js Normal file
View File

@ -0,0 +1,15 @@
import { fileURLToPath } from 'node:url'
const isMain = (meta) => {
const path = fileURLToPath(meta.url)
return path.includes(process.argv[1])
}
const runifmain = async (meta, fn) => {
if (isMain(meta)) {
await fn()
}
}
export { runifmain }

26
test.js Normal file
View File

@ -0,0 +1,26 @@
import assert from 'node:assert/strict'
import { metaltest, consolereporter } from './index.js'
const test = metaltest('Metaltest')
let count = 0
test.before(() => count++)
test('success', () => new Promise((s, f) => setTimeout(s, 500)))
test('not equal', () => assert.notEqual({ a: 1 }, { a: 2 }))
test('fail with the message', () => new Promise((s, f) => setTimeout(s, 500)))
test('throw', () => {
return assert.rejects(async () => {
throw new Error('message')
}, { name: 'Error', message: 'message' })
})
export { test }
import { runifmain } from './runifmain.js'
await runifmain(import.meta, async () => {
const stats = await test.run(consolereporter())
assert.equal(count, 4)
assert.equal(stats.success, 4)
assert.equal(stats.fail, 0)
})

17
totalreporter.js Normal file
View File

@ -0,0 +1,17 @@
import chalk from 'chalk'
const totalreporter = () => {
let totalSuccess = 0, totalFail = 0
return {
success: (test) => { totalSuccess++ },
fail: (test, e) => { totalFail++ },
msg: () => `
${chalk.redBright(`Total fail: ${totalFail}`)}
${chalk.greenBright(`Total sucess: ${totalSuccess}`)}
${chalk.yellowBright(`Ratio ${(totalSuccess / (totalSuccess + totalFail)) * 100}%`)}
`
}
}
export { totalreporter }