Compare commits

...

2 commits

Author SHA1 Message Date
a67479f4bc assert: Add equalWithMargin for floating and other number comparison with error margin
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
2024-09-20 19:33:50 +00:00
17d7bb862c expose stub calls and remove logging on before/after code
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
2024-09-20 19:09:20 +00:00
7 changed files with 180 additions and 22 deletions

View file

@ -111,9 +111,11 @@ $ eltro --watch my_watch_name --npm build
Not only does eltro allow you to use any assertion library of your own choosing, it also comes with it's own assertion library based on node's default [assert](https://nodejs.org/api/assert.html) with a few extra methods: Not only does eltro allow you to use any assertion library of your own choosing, it also comes with it's own assertion library based on node's default [assert](https://nodejs.org/api/assert.html) with a few extra methods:
* `assert.equalWithMargin(value, test, margin, [message])`: Check if number value is equal to test with error margin.
* `assert.notOk(value, [message])`: Assert value is not ok. * `assert.notOk(value, [message])`: Assert value is not ok.
* `assert.match(value, test, [message])`: Check if value matches RegExp test. * `assert.match(value, test, [message])`: Check if value matches RegExp test.
* `assert.notMatch(value, [message])`: Check if value does not match RegExp test. * `assert.notMatch(value, [message])`: Check if value does not match RegExp test.
* `assert.throwsAndCatch(fn, [message])`: Checks if function fn throws and returns the thrown error.
* `assert.isFulfilled(promise, [message])`: Assert the promise resolves. * `assert.isFulfilled(promise, [message])`: Assert the promise resolves.
* `assert.isRejected(promise, [message])`: Assert the promise gets rejects. * `assert.isRejected(promise, [message])`: Assert the promise gets rejects.

View file

@ -57,6 +57,14 @@ assert.throwsAndCatch = (fn, message) => {
return err return err
} }
assert.equalWithMargin = (value, test, margin = 0.005, message) => {
assert.strictEqual(typeof value, 'number', 'Value should be number')
assert.strictEqual(typeof test, 'number', 'Test should be number')
let difference = Math.abs(value - test)
assert.ok(difference <= margin, message || `${value} ± ${margin} != ${test} (difference of ${difference} > ${margin})`)
}
assert.isFulfilled = (promise, message) => { assert.isFulfilled = (promise, message) => {
return Promise.resolve(true) return Promise.resolve(true)
.then(() => promise) .then(() => promise)

View file

@ -117,6 +117,7 @@ process.on('uncaughtException', function(err) {
}) })
function Eltro() { function Eltro() {
this.process = process
this.__timeout = 2000 this.__timeout = 2000
this.hasExclusive = false this.hasExclusive = false
this.reporter = 'list' this.reporter = 'list'
@ -159,8 +160,8 @@ Eltro.prototype.begin = function() {
} }
Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test', child = null) { Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test', child = null) {
if (this.reporter === 'list') { if (this.reporter === 'list' && prefix === 'Test') {
process.stdout.write(' \x1b[90m? ' + test.name + '\x1b[0m') this.process.stdout.write(' \x1b[90m? ' + test.name + '\x1b[0m')
} }
let markRealTest = child || test let markRealTest = child || test
@ -279,24 +280,24 @@ Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test', child =
this.captureOutsideExceptions = null this.captureOutsideExceptions = null
if (this.reporter === 'list') { if (this.reporter === 'list') {
readline.clearLine(process.stdout, 0) readline.clearLine(this.process.stdout, 0)
readline.cursorTo(process.stdout, 0, null) readline.cursorTo(this.process.stdout, 0, null)
if (markRealTest.skipTest) { if (markRealTest.skipTest) {
process.stdout.write(' \x1b[94m- ' + markRealTest.name + '\x1b[0m\n') this.process.stdout.write(' \x1b[94m- ' + markRealTest.name + '\x1b[0m\n')
} else if (!markRealTest.error) { } else if (!markRealTest.error) {
if (!test.name.startsWith('~')) { if (!test.name.startsWith('~')) {
process.stdout.write(' \x1b[32m√\x1b[90m ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n') this.process.stdout.write(' \x1b[32m√\x1b[90m ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n')
} }
} else if (prefix === 'Test') { } else if (prefix === 'Test') {
process.stdout.write(' \x1b[31m' + this.failedTests.length + ') ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n') this.process.stdout.write(' \x1b[31m' + this.failedTests.length + ') ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n')
} }
} else if (this.reporter === 'dot') { } else if (this.reporter === 'dot') {
if (markRealTest.skipTest) { if (markRealTest.skipTest) {
process.stdout.write('\x1b[94m.\x1b[0m') this.process.stdout.write('\x1b[94m.\x1b[0m')
} else if (!markRealTest.error) { } else if (markRealTest.error) {
process.stdout.write('\x1b[32m.\x1b[0m') this.process.stdout.write('\x1b[31m.\x1b[0m')
} else if (prefix === 'Test') { } else if (prefix === 'Test') {
process.stdout.write('\x1b[31m.\x1b[0m') this.process.stdout.write('\x1b[32m.\x1b[0m')
} }
} }
} }
@ -304,7 +305,7 @@ Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test', child =
Eltro.prototype.__runGroup = async function(g, stats) { Eltro.prototype.__runGroup = async function(g, stats) {
if (g.tests.length) { if (g.tests.length) {
if (this.reporter === 'list') { if (this.reporter === 'list') {
console.log(' ' + g.name) this.process.stdout.write(' ' + g.name + '\n')
} }
} }
if (g.before) { if (g.before) {
@ -351,8 +352,8 @@ Eltro.prototype.__runGroup = async function(g, stats) {
Eltro.prototype.run = async function() { Eltro.prototype.run = async function() {
if (this.reporter && this.reporter !== 'test') { if (this.reporter && this.reporter !== 'test') {
console.log('') this.process.stdout.write('' + '\n')
console.log('') this.process.stdout.write('' + '\n')
} }
captureUnknownErrors(this) captureUnknownErrors(this)
@ -381,23 +382,23 @@ Eltro.prototype.run = async function() {
} }
} }
} else if (this.reporter) { } else if (this.reporter) {
console.log('') this.process.stdout.write('' + '\n')
console.log('') this.process.stdout.write('' + '\n')
if (stats.passed) { if (stats.passed) {
console.log(' \x1b[32m' + stats.passed + ' passing \x1b[90m(' + (end[0] * 1000 + Math.round(end[1] / 1000000)) + 'ms)\x1b[0m') this.process.stdout.write(' \x1b[32m' + stats.passed + ' passing \x1b[90m(' + (end[0] * 1000 + Math.round(end[1] / 1000000)) + 'ms)\x1b[0m' + '\n')
} }
if (stats.failed) { if (stats.failed) {
console.log(' \x1b[31m' + stats.failed + ' failing\x1b[0m') this.process.stdout.write(' \x1b[31m' + stats.failed + ' failing\x1b[0m' + '\n')
} }
if (stats.skipped) { if (stats.skipped) {
console.log(' \x1b[94m' + stats.skipped + ' pending\x1b[0m') this.process.stdout.write(' \x1b[94m' + stats.skipped + ' pending\x1b[0m' + '\n')
} }
console.log('') this.process.stdout.write('' + '\n')
if (this.failedTests.length) { if (this.failedTests.length) {
for (let x = 0; x < this.failedTests.length; x++) { for (let x = 0; x < this.failedTests.length; x++) {
let test = this.failedTests[x]; let test = this.failedTests[x];
console.log(' ' + (x + 1) + ') ' + test.name + ':') this.process.stdout.write(' ' + (x + 1) + ') ' + test.name + ':' + '\n')
printError(test.error) printError(test.error)
} }
} }

View file

@ -34,6 +34,7 @@ export function stub(returnFunc = null) {
func.lastCall = null func.lastCall = null
func.called = false func.called = false
func.callCount = 0 func.callCount = 0
func.calls = calls
func.findCall = function(fn) { func.findCall = function(fn) {
for (let call of calls) { for (let call of calls) {

View file

@ -1,6 +1,6 @@
{ {
"name": "eltro", "name": "eltro",
"version": "1.4.5", "version": "1.5.0",
"description": "Eltro is a tiny no-dependancy test framework for node", "description": "Eltro is a tiny no-dependancy test framework for node",
"main": "index.mjs", "main": "index.mjs",
"scripts": { "scripts": {

View file

@ -29,6 +29,41 @@ t.describe('#notOk()', function() {
}) })
}) })
t.describe('#equalWithMargin()', function() {
t.test('should support default margin for floating point math', function() {
let check = 0.1 + 0.2
assertExtended.throws(function() {
assertExtended.strictEqual(check, 0.3)
}, assertExtended.AssertionError)
assertExtended.equalWithMargin(check, 0.3)
})
t.test('should support custom margin', function() {
assertExtended.equalWithMargin(1, 2, 1)
})
t.test('should fail if margin is too small', function() {
assertExtended.throws(function() {
assertExtended.equalWithMargin(1, 2, 0.9)
}, assertExtended.AssertionError)
})
t.test('should support custom message', function () {
const assertMessage = 'Hello world'
let error = null
try {
assertExtended.equalWithMargin(1, 2, 0, assertMessage)
} catch (err) {
error = err
}
assert.ok(error)
assert.match(error.message, new RegExp(assertMessage))
})
})
t.describe('#throwAndCatch()', function() { t.describe('#throwAndCatch()', function() {
t.test('should work and return the original error', function() { t.test('should work and return the original error', function() {
const assertError = new Error('Speed') const assertError = new Error('Speed')

View file

@ -8,6 +8,9 @@ function CreateT() {
t.logger = { t.logger = {
log: stub() log: stub()
} }
t.process = {
stdout: { write: stub() }
}
return t return t
} }
@ -284,6 +287,60 @@ e.describe('#beforeEach()', function() {
assert.match(t.logger.log.getCallN(4)[0], /DDDD/) assert.match(t.logger.log.getCallN(4)[0], /DDDD/)
assert.match(t.logger.log.getCallN(4)[0], /BBBB/) assert.match(t.logger.log.getCallN(4)[0], /BBBB/)
}) })
e.describe('reporter', function() {
e.test('should not log before each with reporter list', async function() {
const t = CreateT()
t.reporter = 'list'
t.begin()
t.describe('BBBB', function() {
t.beforeEach(function() {})
t.describe('CCCC', function() {
t.test('c1', function() { })
t.test('c2', function() { })
})
t.describe('DDDD', function() {
t.test('d1', function() { })
})
t.test('AAAA', function() { })
})
await t.run()
for (let row of t.process.stdout.write.calls) {
assert.notMatch(row.filter(x => x).join(' '), /before each/i)
}
})
e.test('should not log success before each with reporter dot', async function() {
const t = CreateT()
t.reporter = 'dot'
t.begin()
t.describe('BBBB', function() {
t.beforeEach(function() {})
t.describe('CCCC', function() {
t.test('c1', function() { })
t.test('c2', function() { })
})
t.describe('DDDD', function() {
t.test('d1', function() { })
})
t.test('AAAA', function() { })
})
await t.run()
let total = 0
for (let row of t.process.stdout.write.calls) {
if (row.filter(x => x).join(' ').match(/\[32m\./)) {
total++
}
}
assert.strictEqual(total, 4)
})
})
}) })
e.describe('#after()', function() { e.describe('#after()', function() {
@ -679,6 +736,60 @@ e.describe('#afterEach()', function() {
assert.match(t.logger.log.getCallN(5)[0], /JJJJ/) assert.match(t.logger.log.getCallN(5)[0], /JJJJ/)
assert.match(t.logger.log.getCallN(5)[0], /YYYY/) assert.match(t.logger.log.getCallN(5)[0], /YYYY/)
}) })
e.describe('reporter', function() {
e.test('should not log before each with reporter list', async function() {
const t = CreateT()
t.reporter = 'list'
t.begin()
t.describe('BBBB', function() {
t.afterEach(function() {})
t.describe('CCCC', function() {
t.test('c1', function() { })
t.test('c2', function() { })
})
t.describe('DDDD', function() {
t.test('d1', function() { })
})
t.test('AAAA', function() { })
})
await t.run()
for (let row of t.process.stdout.write.calls) {
assert.notMatch(row.filter(x => x).join(' '), /after each/i)
}
})
e.test('should not log success before each with reporter dot', async function() {
const t = CreateT()
t.reporter = 'dot'
t.begin()
t.describe('BBBB', function() {
t.afterEach(function() {})
t.describe('CCCC', function() {
t.test('c1', function() { })
t.test('c2', function() { })
})
t.describe('DDDD', function() {
t.test('d1', function() { })
})
t.test('AAAA', function() { })
})
await t.run()
let total = 0
for (let row of t.process.stdout.write.calls) {
if (row.filter(x => x).join(' ').match(/\[32m\./)) {
total++
}
}
assert.strictEqual(total, 4)
})
})
}) })
let commonBeforeTests = ['before', 'beforeEach'] let commonBeforeTests = ['before', 'beforeEach']