-
Notifications
You must be signed in to change notification settings - Fork 319
/
tester
executable file
·113 lines (98 loc) · 3.33 KB
/
tester
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env node
'use strict'
// extend str for colors
'no0.blk30.red31.grn32.ylw33.blu34.mgt35.cyn36.wht37'
.split('.')
.map(str => str.split(/([0-9]+)$/))
.forEach(([ name, value ]) => {
String.prototype[name] = function () { return `\u001b[${value}m${this}\u001b[0m` }
String.prototype[`_${name}`] = function () { return `\u001b[${value};1m${this}\u001b[0m` }
})
const fs = require('fs')
const { performance: { now } } = require('perf_hooks')
const { promisify } = require('util')
const Module = require('module')
const { dirname, join } = require('path')
const assert = require('assert')
const pkg = require('./package.json')
const readFile = promisify(fs.readFile)
const current = []
const ok = ' ✓ '.grn()
const formatPathParts = (value, i) => i
? ':'.mgt() + value.blu()
: value.wht()
const wrapInParen = ((open, close) => str => `${open}${str}${close}`)('('.wht(), ')'.wht())
const prettyPath = p => p.split(':').map(formatPathParts).join('')
const parseStackLine = (line, err) => {
if (!line) return ''
let [ , src, path ] = line.split(/at (.+) \((.+)\)$/)
if (!src) return wrapInParen(prettyPath(line))
if ((src).includes('anonymous')) {
src = err.key ? `assert.${err.key.cyn()}` : (err.name || 'Error').cyn()
}
return wrapInParen(`${src} ${prettyPath(path)}`)
}
const prettyStack = err => err.message.red()
+' '+ parseStackLine(err.stack.split('\n')
.filter(line => line.includes(current.filename))[0], err)
Object.keys(assert).filter(key => key !== 'fail').forEach(key => {
const fn = assert[key]
const last = fn.length - 1
assert[key] = (...args) => {
try {
fn(...args)
current.push(`${ok}${current.length + 1} - ${args[last] || key}`)
} catch (err) {
current.allPass = false
err.key = key
current.push(` ✗`.red() +' '+ (current.length + 1) +' '+ prettyStack(err))
}
}
})
const availableTags = [ ...new Set(pkg.tests // Set is used for uniqueness
.map(ex => ex.tags || (ex.tags = [ ex.tag ])) // normalize tags
.reduce((a, b) => a.concat(b))) ] // .flatten has bad support atm
const args = process.argv.slice(2)
if (args.length) {
const invalidArgs = args.filter(tag => !availableTags.includes(tag))
if (invalidArgs.length) {
console.error('Arguments must be one of', availableTags.join(', '))
process.exit(1)
} else {
pkg.tests = pkg.tests.filter(ex => ex.tags.some(tag => args.includes(tag)))
}
}
Promise.all(pkg.tests.map(async ({ name }) => ({
code: await readFile(join(__dirname, 'exercises', name +'.js'), 'utf-8'),
filename: name + '.js',
name,
}))).then(codes => {
let failedTestCount = 0
for (const { code, filename, name } of codes) {
current.length = 0
current.allPass = true
current.filename = filename
try {
(new Module(filename, module.parent))._compile(code, filename)
if (current.allPass) {
console.log('✓'.grn(), name)
} else {
failedTestCount++
console.log('-'.blu(), name)
console.log(current.join('\n'))
}
} catch (err) {
if (!failedTestCount) {
console.log(`✗`.red(), name, prettyStack(err))
}
failedTestCount++
}
}
if (failedTestCount > 1) {
console.log(`...and ${failedTestCount - 1} more to pass.`.wht())
}
process.exit(failedTestCount)
}).catch(err => {
console.error(err)
process.exit(1)
})