Compare commits
9 Commits
05cc68d84c
...
ee6ef75765
Author | SHA1 | Date |
---|---|---|
|
ee6ef75765 | |
|
d9786a78d0 | |
|
1b53b9854b | |
|
b9a01eed3f | |
|
cdd8e8c8aa | |
|
ccd64a4367 | |
|
46282ac834 | |
|
4fa6686a74 | |
|
1df35e971a |
|
@ -2,8 +2,9 @@
|
|||
|
||||
import { suite } from 'metaltest'
|
||||
|
||||
const run = async () => {
|
||||
await suite(process.cwd())
|
||||
const run = async (folder) => {
|
||||
if (!folder) folder = process.cwd()
|
||||
await suite(folder)
|
||||
}
|
||||
|
||||
const [, , folder] = process.argv
|
1
index.js
1
index.js
|
@ -1,4 +1,5 @@
|
|||
export * from './reporter/index.js'
|
||||
export * from './lib/mock.js'
|
||||
export { suite } from './lib/suite.js'
|
||||
|
||||
import { metaltest } from './lib/metaltest.js'
|
||||
|
|
|
@ -40,11 +40,11 @@ const metaltest = (title) => {
|
|||
}
|
||||
|
||||
const runner = (name, fn) => {
|
||||
suite.push({ name, fn })
|
||||
suite.push({ name, fn, at: getAt() })
|
||||
}
|
||||
|
||||
runner.only = (name, fn) => { only.push({ name, fn }) }
|
||||
runner.skip = (name, fn) => { skipped.push({ name, fn }) }
|
||||
runner.only = (name, fn) => { only.push({ name, fn, at: getAt() }) }
|
||||
runner.skip = (name, fn) => { skipped.push({ name, fn, at: getAt() }) }
|
||||
runner.before = (fn) => { before.push(fn) }
|
||||
runner.after = (fn) => { after.push(fn) }
|
||||
runner.end = (name, fn) => { end.push({ name, fn }) }
|
||||
|
@ -74,7 +74,7 @@ const metaltest = (title) => {
|
|||
for (const fn of before) await fn()
|
||||
await test.fn(test)
|
||||
stats('success', test)
|
||||
notify(reporters, 'success', test)
|
||||
await notify(reporters, 'success', test)
|
||||
}
|
||||
catch (error) {
|
||||
stats('fail', test, error)
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
const capture = (actions, name, fn) => async (...args) => {
|
||||
const count = actions.reduce((count, curr) => {
|
||||
if (curr.name === name) return count + 1
|
||||
|
||||
return count
|
||||
}, 1)
|
||||
|
||||
try {
|
||||
const result = await fn(...args, { count })
|
||||
actions.push({ name, args, result })
|
||||
return result
|
||||
} catch (error) {
|
||||
actions.push({ name, args, result: error })
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const mock = async (mocks, fn) => {
|
||||
const olds = {}
|
||||
const actions = []
|
||||
|
||||
for (const mock of mocks) {
|
||||
if (mock.package in olds === false) olds[mock.package] = {}
|
||||
const old = olds[mock.package]
|
||||
|
||||
for (const name in mock.funcs) {
|
||||
//Save
|
||||
old[name] = mock.package[name]
|
||||
|
||||
//Capture
|
||||
mock.package[name] = capture(actions, name, mock.funcs[name])
|
||||
}
|
||||
}
|
||||
|
||||
//Restore even when an error occurs
|
||||
//Like with a failed assert
|
||||
try {
|
||||
await fn()
|
||||
} catch (error) {
|
||||
throw error
|
||||
} finally {
|
||||
for (const mock of mocks) {
|
||||
const old = olds[mock.package]
|
||||
|
||||
for (const name in mock.funcs) {
|
||||
//Restore
|
||||
mock.package[name] = old[name]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
export { mock }
|
|
@ -1,9 +1,13 @@
|
|||
const regAssertParser = /(?<title>.*?)\n+(?<diff>.*?)\n+(?<stacktrace> (?<where>at.*?file:\/\/(?<file>.*?):(?<line>\d+):(?<column>\d+)\)?)\n.*)/s
|
||||
|
||||
const regStack = /(?<=^ )at.*/gm
|
||||
|
||||
const stackParser = (stack) => {
|
||||
const m = stack.match(regAssertParser)
|
||||
|
||||
return m.groups
|
||||
const stacks = stack.match(regStack)
|
||||
|
||||
return { ...m.groups, stacks }
|
||||
}
|
||||
|
||||
export { stackParser }
|
||||
export { stackParser }
|
||||
|
|
43
lib/suite.js
43
lib/suite.js
|
@ -1,47 +1,17 @@
|
|||
import { readdir } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
const walkDir = async function* (dir = '.', exclude = []) {
|
||||
const files = await readdir(dir, { withFileTypes: true })
|
||||
import path from 'node:path'
|
||||
import { walkDir } from './walkDir.js'
|
||||
import { summaryreporter, totalreporter, totalerrorreporter } from 'metaltest'
|
||||
|
||||
for (const file of files) {
|
||||
if (exclude.includes(file.name)) continue
|
||||
|
||||
if (file.isDirectory()) {
|
||||
yield* walkDir(join(dir, file.name))
|
||||
} else if (file.isSymbolicLink()) {
|
||||
yield* walkDir(join(dir, file.name))
|
||||
} else {
|
||||
yield join(dir, file.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dirname } from 'node:path'
|
||||
const getStackAbsolutePaths = () => {
|
||||
const old = Error.prepareStackTrace
|
||||
Error.prepareStackTrace = (_, stack) => stack
|
||||
const stack = new Error().stack
|
||||
Error.prepareStackTrace = old
|
||||
|
||||
return stack
|
||||
.map(cs => cs.getFileName())
|
||||
.filter(filename => filename?.startsWith('file://'))
|
||||
.map(filename => dirname(fileURLToPath(filename)))
|
||||
}
|
||||
|
||||
import { summaryreporter, errorreporter, totalreporter } from 'metaltest'
|
||||
const suite = async (folder = '.') => {
|
||||
const absolutePaths = getStackAbsolutePaths()
|
||||
const absolutePath = absolutePaths[2]
|
||||
const fileIgnored = []
|
||||
|
||||
const total = totalreporter()
|
||||
const error = totalerrorreporter()
|
||||
|
||||
for await (const file of walkDir(folder, ['node_modules', '.git'])) {
|
||||
if (!file.endsWith('.test.js')) continue
|
||||
|
||||
const module = join(absolutePath, file)
|
||||
const module = path.resolve(folder, file)
|
||||
const { test } = await import(module)
|
||||
|
||||
if (test === undefined) {
|
||||
|
@ -49,13 +19,14 @@ const suite = async (folder = '.') => {
|
|||
continue
|
||||
}
|
||||
|
||||
await test.run(summaryreporter(), errorreporter(), total)
|
||||
await test.run(summaryreporter(), error, total)
|
||||
}
|
||||
|
||||
for (const file of fileIgnored) {
|
||||
console.log(`The file ${file} doesn't export the metaltest object`)
|
||||
}
|
||||
|
||||
error.msg()
|
||||
console.log(total.msg())
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { readdir, stat } from 'fs/promises'
|
||||
import { join } from 'path/posix'
|
||||
|
||||
const walkDir = async function* (dir = '.', exclude = []) {
|
||||
const files = await readdir(dir)
|
||||
|
||||
for await (const file of files) {
|
||||
if (exclude.includes(file)) continue
|
||||
|
||||
try {
|
||||
const stats = await stat(join(dir, file))
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
yield* walkDir(join(dir, file))
|
||||
} else {
|
||||
yield join(dir, file)
|
||||
}
|
||||
}
|
||||
catch (err) { } //Like file not found when symlink to a file that do not exist
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { walkDir }
|
|
@ -4,6 +4,7 @@
|
|||
"description": "Clone of baretest with reporting",
|
||||
"type": "module",
|
||||
"exports": "./index.js",
|
||||
"bin": "./cli/suite.js",
|
||||
"scripts": {
|
||||
"test": "node suite.js"
|
||||
},
|
||||
|
|
|
@ -1,37 +1,14 @@
|
|||
import chalk from 'chalk'
|
||||
import { log } from '../lib/log.js'
|
||||
import { showError } from './showError.js'
|
||||
|
||||
const errorreporter = () => {
|
||||
const report = {
|
||||
end: (stats) => {
|
||||
const { testFail } = stats
|
||||
|
||||
for (const test of testFail) {
|
||||
log('\n' + chalk.redBright(test.name) + '\n')
|
||||
prettyError(test.error)
|
||||
}
|
||||
showError(testFail)
|
||||
}
|
||||
}
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
import { stackParser } from '../lib/stackparser.js'
|
||||
const prettyError = (error) => {
|
||||
if (typeof error == 'string') {
|
||||
log(chalk.yellowBright(error))
|
||||
log('\n')
|
||||
return
|
||||
}
|
||||
|
||||
const stack = stackParser(error.stack)
|
||||
log(chalk.yellowBright(stack.title) + '\n')
|
||||
|
||||
if (stack.diff != '')
|
||||
log(chalk.white(stack.diff) + '\n')
|
||||
|
||||
if (stack.where != '')
|
||||
log(chalk.gray(stack.where) + '\n')
|
||||
}
|
||||
|
||||
export { errorreporter }
|
|
@ -2,4 +2,5 @@ export { summaryreporter } from './summaryreporter.js'
|
|||
export { linereporter } from './linereporter.js'
|
||||
export { totalreporter } from './totalreporter.js'
|
||||
export { errorreporter } from './errorreporter.js'
|
||||
export { totalerrorreporter } from './totalerrorreporter.js'
|
||||
export { endreporter } from './endreporter.js'
|
||||
|
|
|
@ -15,7 +15,7 @@ const linereporter = () => {
|
|||
fail: (test, e) => {
|
||||
const line = e.stack.match(/at.*:(.*):/)[1]
|
||||
lines.push(line)
|
||||
log(chalk.redBright('✗'), line, test.name)
|
||||
log(chalk.redBright('✗'), test.name, `(line ${line})`)
|
||||
},
|
||||
end: (stats) => {
|
||||
const { success, fail, testFail } = stats
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import chalk from 'chalk'
|
||||
import { log } from '../lib/log.js'
|
||||
import { stackParser } from '../lib/stackparser.js'
|
||||
|
||||
const showError = (testsFail) => {
|
||||
for (const test of testsFail) {
|
||||
log('\n' + chalk.redBright(test.name) + '\n')
|
||||
prettyError(test)
|
||||
}
|
||||
}
|
||||
|
||||
const prettyError = (test) => {
|
||||
const { error } = test
|
||||
if (typeof error == 'string') {
|
||||
log(chalk.yellowBright(error))
|
||||
log('\n')
|
||||
return
|
||||
}
|
||||
|
||||
const stack = stackParser(error.stack)
|
||||
log(chalk.yellowBright(stack.title) + '\n')
|
||||
|
||||
if (stack.diff != '')
|
||||
log(chalk.white(stack.diff) + '\n')
|
||||
|
||||
// if (stack.where != '')
|
||||
// log(chalk.gray(stack.where) + '\n')
|
||||
|
||||
log(chalk.gray(test.at.summary) + '\n')
|
||||
log(chalk.gray(stack.stacks.join('\n')) + '\n')
|
||||
}
|
||||
|
||||
export { showError }
|
|
@ -0,0 +1,16 @@
|
|||
import { showError } from "./showError.js"
|
||||
|
||||
const totalerrorreporter = () => {
|
||||
const testFails = []
|
||||
|
||||
const report = {
|
||||
end: (stats) => testFails.push(...stats.testFail),
|
||||
msg: () => {
|
||||
showError(testFails)
|
||||
},
|
||||
}
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
export { totalerrorreporter }
|
Loading…
Reference in New Issue