Prepare first release
This commit is contained in:
parent
cc34409439
commit
653e54c846
15 changed files with 1215 additions and 2 deletions
64
cli.mjs
Normal file
64
cli.mjs
Normal file
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Get arguments
|
||||
const [,, ...args] = process.argv
|
||||
|
||||
import c from './lib/casette.mjs'
|
||||
import { CLI, printError } from './lib/cli.mjs'
|
||||
|
||||
c.begin()
|
||||
|
||||
const cli = new CLI(c)
|
||||
cli.parseOptions(args)
|
||||
|
||||
if (cli.errored) {
|
||||
PrintHelp()
|
||||
}
|
||||
|
||||
function PrintHelp() {
|
||||
console.log('')
|
||||
console.log('Usage: casette <options> <files>')
|
||||
console.log('')
|
||||
console.log('where <files> can either be a single file or a simple glob pattern.')
|
||||
console.log('where <options> can be any of the following:')
|
||||
console.log(' -r, --reporter - Specify the reporter to use.')
|
||||
console.log(' Supported reporters: list, dot')
|
||||
console.log('')
|
||||
console.log('casette test/mytest.mjs')
|
||||
console.log('casette dot test/*.mjs')
|
||||
console.log('casette -r dot test/**/*.test.mjs')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
cli.processTargets().then(function() {
|
||||
if (!cli.files.length) {
|
||||
console.log('')
|
||||
console.log('No files were found with pattern', cli.targets.join(','))
|
||||
PrintHelp()
|
||||
}
|
||||
return cli.loadFiles()
|
||||
.then(function() {
|
||||
c.reporter = cli.reporter
|
||||
|
||||
return c.run()
|
||||
.catch(function(err) {
|
||||
console.log('')
|
||||
console.error('\x1b[31mUnknown error occured while running the tests\x1b[0m')
|
||||
printError(err)
|
||||
process.exit(1)
|
||||
})
|
||||
}, function(err) {
|
||||
console.log('')
|
||||
console.error('\x1b[31mUnknown error while opening files\x1b[0m')
|
||||
printError(err)
|
||||
process.exit(1)
|
||||
})
|
||||
}, function(err) {
|
||||
console.log('')
|
||||
console.error('\x1b[31mUnknown error while processing arguments\x1b[0m')
|
||||
printError(err)
|
||||
process.exit(1)
|
||||
})
|
||||
.then(function() {
|
||||
process.exit(0)
|
||||
})
|
7
index.mjs
Normal file
7
index.mjs
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Casette from './lib/casette.mjs'
|
||||
import assert from './lib/assert.mjs'
|
||||
|
||||
export {
|
||||
Casette,
|
||||
assert,
|
||||
}
|
83
lib/assert.mjs
Normal file
83
lib/assert.mjs
Normal file
|
@ -0,0 +1,83 @@
|
|||
import assert from 'assert'
|
||||
import util from 'util'
|
||||
|
||||
const fail = assert.fail;
|
||||
|
||||
function truncate(s, n) {
|
||||
return s.length < n ? s : s.slice(0, n) + '...';
|
||||
}
|
||||
|
||||
function stringifyObject(data) {
|
||||
if (typeof(data) !== 'string') {
|
||||
data = util.inspect(
|
||||
data,
|
||||
{ depth: 1 }
|
||||
)
|
||||
.replace(/\n /g, '');
|
||||
}
|
||||
return truncate(data, 64);
|
||||
}
|
||||
|
||||
assert.notOk = (value, message) => {
|
||||
if (Boolean(value)) {
|
||||
assert.equal(value, false, message)
|
||||
}
|
||||
}
|
||||
|
||||
assert.match = (value, test, message) => {
|
||||
let result = value.match(test);
|
||||
if (result) return;
|
||||
|
||||
let m = message
|
||||
if (!m) {
|
||||
m = `${value} did not match ${test}`
|
||||
}
|
||||
|
||||
fail(m);
|
||||
}
|
||||
|
||||
assert.notMatch = (value, test, message) => {
|
||||
let result = value.match(test);
|
||||
if (!result) return;
|
||||
|
||||
let m = message
|
||||
if (!m) {
|
||||
m = `${value} matched ${test}`
|
||||
}
|
||||
|
||||
fail(m);
|
||||
}
|
||||
|
||||
assert.isFulfilled = (promise, message) => {
|
||||
return Promise.resolve(true)
|
||||
.then(() => promise)
|
||||
.catch((err) => {
|
||||
let m = message
|
||||
if (!m) {
|
||||
m = `promise failed with ${err.message || stringifyObject(err)}`;
|
||||
}
|
||||
fail(m);
|
||||
});
|
||||
}
|
||||
|
||||
assert.isRejected = (promise, message) => {
|
||||
let hasFailed = false;
|
||||
|
||||
return Promise.resolve(true)
|
||||
.then(() => promise)
|
||||
.catch((data) => {
|
||||
hasFailed = true;
|
||||
return data;
|
||||
})
|
||||
.then((data) => {
|
||||
if (hasFailed) return data;
|
||||
let m = message
|
||||
if (!m) {
|
||||
m = `promise was fulfilled with ${stringifyObject(data)}`;
|
||||
}
|
||||
|
||||
fail(m);
|
||||
});
|
||||
}
|
||||
|
||||
export default assert
|
263
lib/casette.mjs
Normal file
263
lib/casette.mjs
Normal file
|
@ -0,0 +1,263 @@
|
|||
import { printError } from './cli.mjs'
|
||||
|
||||
function Group(name) {
|
||||
this.name = name
|
||||
this.tests = []
|
||||
}
|
||||
|
||||
function Test(name, func) {
|
||||
this.skipTest = false
|
||||
this.customTimeout = null
|
||||
this.name = name
|
||||
this.func = func
|
||||
this.group = null
|
||||
this.error = null
|
||||
}
|
||||
|
||||
Test.prototype.timeout = function(time) {
|
||||
this.customTimeout = time
|
||||
}
|
||||
|
||||
Test.prototype.skip = function() {
|
||||
this.skipTest = true
|
||||
}
|
||||
|
||||
function Casette() {
|
||||
this.__timeout = 2000
|
||||
this.reporter = 'list'
|
||||
this.Casette = Casette
|
||||
this.groups = new Map()
|
||||
this.groupsFlat = []
|
||||
this.tests = []
|
||||
this.failedTests = []
|
||||
this.hasTests = false
|
||||
this.starting = false
|
||||
this.filename = ''
|
||||
this.prefix = ''
|
||||
}
|
||||
|
||||
Casette.prototype.begin = function() {
|
||||
if (this.starting) {
|
||||
console.warn('WARNING: Multiple calls to Casette.begin were done.')
|
||||
return
|
||||
}
|
||||
this.hasTests = false
|
||||
this.starting = true
|
||||
this.filename = ''
|
||||
this.prefix = ''
|
||||
this.groups.clear()
|
||||
this.tests.splice(0, this.tests.length)
|
||||
}
|
||||
|
||||
Casette.prototype.__runTest = async function(stats, test) {
|
||||
if (this.reporter === 'list') {
|
||||
process.stdout.write(' \x1b[90m? ' + test.name + '\x1b[0m')
|
||||
}
|
||||
|
||||
if (!test.skipTest) {
|
||||
await new Promise((resolve, reject) => {
|
||||
// Flag to check if we finished
|
||||
let finished = false
|
||||
let timeout = test.customTimeout || this.__timeout
|
||||
|
||||
// Timeout timer in case test times out
|
||||
let timer = setTimeout(function() {
|
||||
if (finished === true) return
|
||||
reject(new Error('timeout of ' + timeout + 'ms exceeded. Ensure the done() callback is being called in this test.'))
|
||||
}, timeout)
|
||||
|
||||
// start the test runner
|
||||
try {
|
||||
// Does it accept a callback
|
||||
let checkIsCallback = (test.func.toString()).match(/^(function)? *\([^\)]+\)/)
|
||||
let promise
|
||||
|
||||
// If the test requires callback, wrap it in a promise where callback
|
||||
// either resolves or rejects that promise
|
||||
if (checkIsCallback) {
|
||||
promise = new Promise(function(res, rej) {
|
||||
test.func(function(err) {
|
||||
if (err) {
|
||||
return rej(err)
|
||||
}
|
||||
res()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// Function doesn't require a callback, run it directly
|
||||
promise = test.func()
|
||||
}
|
||||
|
||||
// Check if the function we ran returned a promise
|
||||
if (promise && promise.then && typeof(promise.then === 'function')) {
|
||||
// If the promise from the function succeeded, resolve our promise.
|
||||
// Otherwise reject it
|
||||
promise.then(function() {
|
||||
// check if our test had already finished and if so, do nothing
|
||||
if (finished === true) return
|
||||
|
||||
finished = true
|
||||
clearTimeout(timer)
|
||||
resolve()
|
||||
}, function(err) {
|
||||
// check if our test had already finished and if so, do nothing
|
||||
if (finished === true) return
|
||||
|
||||
finished = true
|
||||
clearTimeout(timer)
|
||||
reject(err)
|
||||
})
|
||||
} else {
|
||||
// check if our test had already finished and if so, do nothing
|
||||
if (finished === true) return
|
||||
|
||||
// Possible this was a synchronous test, pass immediately
|
||||
finished = true
|
||||
clearTimeout(timer)
|
||||
resolve()
|
||||
}
|
||||
} catch (err) {
|
||||
// check if our test had already finished and if so, do nothing
|
||||
if (finished === true) return
|
||||
|
||||
// An error occured while running function. Possible exception
|
||||
// during a synchronous test or something else.
|
||||
finished = true
|
||||
clearTimeout(timer)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
stats.passed++
|
||||
}, function(err) {
|
||||
test.error = err
|
||||
stats.failed++
|
||||
}
|
||||
)
|
||||
} else {
|
||||
stats.skipped++
|
||||
}
|
||||
|
||||
if (test.error) {
|
||||
this.failedTests.push(test)
|
||||
}
|
||||
|
||||
if (this.reporter === 'list') {
|
||||
process.stdout.clearLine();
|
||||
process.stdout.cursorTo(0);
|
||||
if (test.skipTest) {
|
||||
process.stdout.write(' \x1b[94m- ' + test.name + '\x1b[0m\n')
|
||||
} else if (!test.error) {
|
||||
process.stdout.write(' \x1b[32m√\x1b[90m ' + test.name + '\x1b[0m\n')
|
||||
} else {
|
||||
process.stdout.write(' \x1b[31m' + this.failedTests.length + ') ' + test.name + '\x1b[0m\n')
|
||||
}
|
||||
} else if (this.reporter === 'dot') {
|
||||
if (test.skipTest) {
|
||||
process.stdout.write('\x1b[94m.\x1b[0m')
|
||||
} else if (!test.error) {
|
||||
process.stdout.write('\x1b[32m.\x1b[0m')
|
||||
} else {
|
||||
process.stdout.write('\x1b[31m.\x1b[0m')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Casette.prototype.run = async function() {
|
||||
if (this.reporter) {
|
||||
console.log('')
|
||||
console.log('')
|
||||
}
|
||||
|
||||
let stats = {
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
skipped: 0,
|
||||
}
|
||||
|
||||
let start = process.hrtime()
|
||||
for (let i = 0; i < this.groupsFlat.length; i++) {
|
||||
let g = this.groupsFlat[i];
|
||||
|
||||
if (this.reporter === 'list') {
|
||||
console.log(' ' + g.name)
|
||||
}
|
||||
for (let x = 0; x < g.tests.length; x++) {
|
||||
await this.__runTest(stats, g.tests[x])
|
||||
}
|
||||
}
|
||||
|
||||
for (let x = 0; x < this.tests.length; x++) {
|
||||
await this.__runTest(stats, this.tests[x])
|
||||
}
|
||||
let end = process.hrtime(start)
|
||||
|
||||
if (this.reporter) {
|
||||
console.log('')
|
||||
console.log('')
|
||||
if (stats.passed) {
|
||||
console.log(' \x1b[32m' + stats.passed + ' passing \x1b[90m(' + (end[0] * 1000 + Math.round(end[1] / 1000000)) + 'ms)\x1b[0m')
|
||||
}
|
||||
if (stats.failed) {
|
||||
console.log(' \x1b[31m' + stats.failed + ' failing\x1b[0m')
|
||||
}
|
||||
if (stats.skipped) {
|
||||
console.log(' \x1b[94m' + stats.skipped + ' pending\x1b[0m')
|
||||
}
|
||||
console.log('')
|
||||
|
||||
if (this.failedTests.length) {
|
||||
for (let x = 0; x < this.failedTests.length; x++) {
|
||||
let test = this.failedTests[x];
|
||||
console.log(' ' + (x + 1) + ') '
|
||||
+ (test.group ? test.group.name + ': ' : '' )
|
||||
+ test.name + ':'
|
||||
)
|
||||
printError(test.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Casette.prototype.setFilename = function(filename) {
|
||||
this.filename = filename
|
||||
}
|
||||
|
||||
Casette.prototype.resetFilename = function() {
|
||||
this.filename = ''
|
||||
}
|
||||
|
||||
Casette.prototype.describe = function(name, func) {
|
||||
let before = this.prefix
|
||||
if (before) {
|
||||
this.prefix = before + ' ' + name
|
||||
} else {
|
||||
this.prefix = name
|
||||
}
|
||||
func()
|
||||
this.prefix = before
|
||||
}
|
||||
|
||||
Casette.prototype.test = function(name, func) {
|
||||
let targetName = name
|
||||
if (this.prefix) {
|
||||
targetName = this.prefix + ' ' + name
|
||||
}
|
||||
this.hasTests = true
|
||||
|
||||
let group = this
|
||||
if (this.filename) {
|
||||
if (!this.groups.has(this.filename)) {
|
||||
let g = new Group(this.filename)
|
||||
this.groupsFlat.push(g)
|
||||
this.groups.set(this.filename, g)
|
||||
}
|
||||
group = this.groups.get(this.filename)
|
||||
}
|
||||
let test = new Test(targetName, func)
|
||||
test.group = group
|
||||
group.tests.push(test)
|
||||
return test
|
||||
}
|
||||
|
||||
export default new Casette()
|
169
lib/cli.mjs
Normal file
169
lib/cli.mjs
Normal file
|
@ -0,0 +1,169 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
export function CLI(c) {
|
||||
this.c = c
|
||||
this.reporter = 'list'
|
||||
this.targets = ['test/**']
|
||||
this.files = []
|
||||
this.errored = false
|
||||
}
|
||||
|
||||
CLI.prototype.parseOptions = function(args) {
|
||||
if (!args || !args.length) {
|
||||
this.targets.push('test/**')
|
||||
this.errored = false
|
||||
return
|
||||
}
|
||||
|
||||
this.errored = false
|
||||
this.targets.splice(0, this.targets.length)
|
||||
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '-r' || args[i] === '--reporter') {
|
||||
if (!args[i + 1] || (args[i + 1] !== 'list' && args[i + 1] !== 'dot')) {
|
||||
this.errored = true
|
||||
return
|
||||
}
|
||||
this.reporter = args[i + 1]
|
||||
i++
|
||||
} else {
|
||||
this.targets.push(args[i])
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.targets.length) {
|
||||
this.targets.push('test/**')
|
||||
}
|
||||
}
|
||||
|
||||
CLI.prototype.processTargets = function() {
|
||||
this.files.splice(0, this.files.length)
|
||||
|
||||
if (!this.targets.length) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return Promise.all(this.targets.map((target) => {
|
||||
return getFiles(this.files, target)
|
||||
})).then(() => {
|
||||
if (!this.files.length) {
|
||||
this.errored = 'empty'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
CLI.prototype.loadFiles = async function() {
|
||||
let cwd = process.cwd()
|
||||
|
||||
for (let i = 0; i < this.files.length; i++) {
|
||||
if (this.files[i].endsWith('.mjs') || this.files[i].endsWith('.js')) {
|
||||
this.c.setFilename(this.files[i])
|
||||
await import('file:///' + path.join(cwd, this.files[i]))
|
||||
this.c.resetFilename()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function traverseFolder(files, curr, match, insidePath, grabAll, insideStar, includeFiles) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
return fs.readdir(curr, function(err, data) {
|
||||
if (err) return reject(new Error('unable to read directory ' + curr + ': ' + err.message))
|
||||
|
||||
resolve(Promise.all(data.map(function(file) {
|
||||
return new Promise(function(res, rej) {
|
||||
fs.lstat(path.join(curr, file), function(err, stat) {
|
||||
if (err) return rej(new Error('unable to read file or directory ' + path.join(curr, file) + ': ' + err.message))
|
||||
|
||||
if ((includeFiles || grabAll) && stat.isFile()) {
|
||||
if (!match || fileMatches(file, match)) {
|
||||
files.push(path.join(insidePath, file).replace(/\\/g, '/'))
|
||||
return res(files)
|
||||
}
|
||||
}
|
||||
if (stat.isDirectory() && grabAll) {
|
||||
return res(traverseFolder(files, path.join(curr, file), match, path.join(insidePath, file), grabAll, insideStar, includeFiles))
|
||||
} else if (stat.isDirectory() && match) {
|
||||
return res(getFiles(files, match, path.join(insidePath, file), grabAll, insideStar))
|
||||
}
|
||||
res(null)
|
||||
})
|
||||
})
|
||||
})).then(function() { return files }))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function fileMatches(filename, match) {
|
||||
return Boolean(filename.match(new RegExp(match.replace(/\./, '\\.').replace(/\*/, '.*'))))
|
||||
}
|
||||
|
||||
export function getFiles(files, match, insidePath, grabAll, insideStar) {
|
||||
let isGrabbingAll = grabAll || false
|
||||
let isStarred = insideStar || false
|
||||
let cwd = process.cwd()
|
||||
let currPath = insidePath || ''
|
||||
let curr = path.join(cwd, currPath || '')
|
||||
|
||||
return new Promise(function(res, rej) {
|
||||
let start = 0
|
||||
let splitted = match.split('/')
|
||||
if (splitted[start] === '.') {
|
||||
start++
|
||||
}
|
||||
if (splitted[splitted.length - 1] === '') {
|
||||
splitted[splitted.length - 1] === '*'
|
||||
}
|
||||
|
||||
let first = splitted[start]
|
||||
|
||||
if (splitted.length > start + 1) {
|
||||
if (first === '**') {
|
||||
isGrabbingAll = isStarred = true
|
||||
return traverseFolder(files, curr, splitted.slice(start + 1).join('/'), currPath, isGrabbingAll, isStarred, false)
|
||||
.then(res, rej)
|
||||
} else if (first === '*') {
|
||||
isStarred = true
|
||||
return traverseFolder(files, curr, splitted.slice(start + 1).join('/'), currPath, isGrabbingAll, isStarred, false)
|
||||
.then(res, rej)
|
||||
}
|
||||
return getFiles(files, splitted.slice(start + 1).join('/'), path.join(currPath, first), grabAll, isStarred)
|
||||
.then(res, rej)
|
||||
} else if (first.indexOf('*') >= 0) {
|
||||
if (first === '**') {
|
||||
isGrabbingAll = isStarred = true
|
||||
}
|
||||
return traverseFolder(files, curr, first === '*' || first === '**'? '' : first, currPath, isGrabbingAll, isStarred, true)
|
||||
.then(res, rej)
|
||||
}
|
||||
|
||||
fs.lstat(path.join(curr, first), function(err, stat) {
|
||||
if (err) {
|
||||
// If we're inside a star, we ignore files we cannot find
|
||||
if (isStarred) {
|
||||
return res(files)
|
||||
}
|
||||
return rej(new Error('file ' + path.join(insidePath, first) + ' could not be found: ' + err.message))
|
||||
}
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
return traverseFolder(files, path.join(curr, first), '', path.join(currPath, first), true, true, true)
|
||||
.then(res, rej)
|
||||
}
|
||||
|
||||
files.push(path.join(currPath, match).replace(/\\/g, '/'))
|
||||
return res(files)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function printError(err, msg) {
|
||||
let before = msg || ''
|
||||
console.error('')
|
||||
console.error('\x1b[31m '
|
||||
+ before + err.toString()
|
||||
+ '\x1b[0m\n \x1b[90m'
|
||||
+ err.stack.replace(err.toString(), ''))
|
||||
console.error('\x1b[0m')
|
||||
}
|
10
package.json
10
package.json
|
@ -2,7 +2,7 @@
|
|||
"name": "casette",
|
||||
"version": "0.9.0",
|
||||
"description": "No-dependancy test framework for node",
|
||||
"main": "index.js",
|
||||
"main": "index.mjs",
|
||||
"scripts": {
|
||||
"test": "node cli.mjs test/**/*.test.mjs"
|
||||
},
|
||||
|
@ -25,5 +25,11 @@
|
|||
"homepage": "https://github.com/TheThing/node-casette#readme",
|
||||
"bin": {
|
||||
"casette": "./cli.mjs"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"index.mjs",
|
||||
"cli.mjs",
|
||||
"README.md",
|
||||
"lib"
|
||||
]
|
||||
}
|
||||
|
|
154
test/assert.test.mjs
Normal file
154
test/assert.test.mjs
Normal file
|
@ -0,0 +1,154 @@
|
|||
import util from 'util'
|
||||
import assert from 'assert'
|
||||
import assertExtended from '../lib/assert.mjs'
|
||||
|
||||
import c from '../lib/casette.mjs'
|
||||
|
||||
const testLongObject = {
|
||||
a: 1, b:2, c:3, d:4,
|
||||
e: {herp: 51, derp: 23},
|
||||
f: 'asdfgagwegawegawegawegawe',
|
||||
g: '32ghaiwugb23 238023'
|
||||
}
|
||||
|
||||
c.describe('#notOk()', function() {
|
||||
c.test('should exist', function() {
|
||||
assertExtended.ok(assertExtended.notOk)
|
||||
})
|
||||
|
||||
c.test('should throw for true values', function() {
|
||||
assertExtended.throws(function() {
|
||||
assertExtended.notOk(true)
|
||||
}, assertExtended.AssertionError)
|
||||
})
|
||||
|
||||
c.test('should pass for false values', function() {
|
||||
assertExtended.notOk(false)
|
||||
assertExtended.notOk(null)
|
||||
assertExtended.notOk(0)
|
||||
})
|
||||
})
|
||||
|
||||
c.describe('#isFulfilled()', function() {
|
||||
c.test('should exist', function() {
|
||||
assertExtended.ok(assertExtended.isFulfilled)
|
||||
})
|
||||
|
||||
c.test('should throw for rejected promises', function() {
|
||||
return assertExtended.isFulfilled(Promise.reject({}))
|
||||
.catch((err) => {
|
||||
assertExtended.ok(err.message.match(/promise fail/))
|
||||
})
|
||||
})
|
||||
|
||||
c.test('should properly parse rejected object response', function() {
|
||||
let assertMessage = util.inspect(testLongObject, {depth: 1}).replace(/\n /g, '')
|
||||
assertMessage = assertMessage.slice(0, 64) + '...'
|
||||
|
||||
return assertExtended.isFulfilled(Promise.reject(testLongObject))
|
||||
.catch((err) =>
|
||||
assertExtended.notStrictEqual(err.message.indexOf(assertMessage), -1)
|
||||
)
|
||||
})
|
||||
|
||||
c.test('should include error message if error', function() {
|
||||
const assertMessage = 'something something dark side'
|
||||
return assertExtended.isFulfilled(Promise.reject(new Error(assertMessage)))
|
||||
.catch((err) => {
|
||||
assertExtended.ok(err.message.match(new RegExp('with ' + assertMessage)))
|
||||
})
|
||||
})
|
||||
|
||||
c.test('should pass for resolved promises', function() {
|
||||
return assertExtended.isFulfilled(Promise.resolve())
|
||||
})
|
||||
|
||||
c.test('should support custom message', function() {
|
||||
const assertMessage = 'something something dark side'
|
||||
return assertExtended.isFulfilled(Promise.reject({}), assertMessage)
|
||||
.catch((err) => {
|
||||
assertExtended.ok(err.message.match(assertMessage))
|
||||
})
|
||||
})
|
||||
|
||||
c.test('should return result for the resolved promise', function() {
|
||||
const assertResult = {a: 1}
|
||||
|
||||
return assertExtended.isFulfilled(Promise.resolve(assertResult))
|
||||
.then((data) => assertExtended.strictEqual(data, assertResult))
|
||||
})
|
||||
})
|
||||
|
||||
c.describe('#isRejected()', function() {
|
||||
c.test('should exist', function() {
|
||||
assertExtended.ok(assertExtended.isRejected)
|
||||
})
|
||||
|
||||
c.test('should throw for resolved promises', function() {
|
||||
let hasFailed = false
|
||||
|
||||
return assertExtended.isRejected(Promise.resolve({}))
|
||||
.catch((err) => {
|
||||
hasFailed = true
|
||||
assertExtended.ok(err.message.match(/fulfilled with/))
|
||||
})
|
||||
.then(function() {
|
||||
assertExtended.strictEqual(hasFailed, true)
|
||||
})
|
||||
})
|
||||
|
||||
c.test('should properly stringify objects', function() {
|
||||
let assertMessage = util.inspect(testLongObject, {depth: 1}).replace(/\n /g, '')
|
||||
assertMessage = assertMessage.slice(0, 64) + '...'
|
||||
|
||||
return assertExtended.isRejected(Promise.resolve(testLongObject))
|
||||
.catch((err) =>
|
||||
assertExtended.notStrictEqual(err.message.indexOf(assertMessage), -1)
|
||||
)
|
||||
})
|
||||
|
||||
c.test('should support custom message', function() {
|
||||
const assertMessage = 'something something dark side'
|
||||
return assertExtended.isRejected(Promise.resolve({}), assertMessage)
|
||||
.catch((err) => assertExtended.ok(err.message.match(assertMessage)))
|
||||
})
|
||||
|
||||
c.test('should return result for the unresolved promise', function() {
|
||||
const assertResult = {a: 1}
|
||||
|
||||
return assertExtended.isRejected(Promise.reject(assertResult))
|
||||
.then((data) => assertExtended.strictEqual(data, assertResult))
|
||||
})
|
||||
})
|
||||
|
||||
c.describe('#match()', function() {
|
||||
c.test('should exist', function() {
|
||||
assertExtended.ok(assertExtended.match);
|
||||
});
|
||||
|
||||
c.test('should throw if no match', function() {
|
||||
assertExtended.throws(function() {
|
||||
assertExtended.match('a', /b/);
|
||||
}, assertExtended.AssertionError);
|
||||
});
|
||||
|
||||
c.test('should pass if matches', function() {
|
||||
assertExtended.match('a', /a/);
|
||||
});
|
||||
})
|
||||
|
||||
c.describe('#notMatch()', function() {
|
||||
c.test('should exist', function() {
|
||||
assertExtended.ok(assertExtended.notMatch);
|
||||
});
|
||||
|
||||
c.test('should throw if match', function() {
|
||||
assertExtended.throws(function() {
|
||||
assertExtended.notMatch('a', /a/);
|
||||
}, assertExtended.AssertionError);
|
||||
});
|
||||
|
||||
c.test('should pass if not matches', function() {
|
||||
assertExtended.notMatch('a', /b/);
|
||||
});
|
||||
})
|
192
test/casette.test.mjs
Normal file
192
test/casette.test.mjs
Normal file
|
@ -0,0 +1,192 @@
|
|||
import c from '../lib/casette.mjs'
|
||||
import assert from '../lib/assert.mjs'
|
||||
import { printError } from '../lib/cli.mjs'
|
||||
|
||||
let testsWereRun = false
|
||||
|
||||
function CreateT() {
|
||||
const t = new c.Casette()
|
||||
t.reporter = ''
|
||||
return t
|
||||
}
|
||||
|
||||
c.test('Casette describe should add prefix to the group tests', async function() {
|
||||
testsWereRun = true
|
||||
const assertPrefix = 'something'
|
||||
const assertName = 'blabla'
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.setFilename('test')
|
||||
t.describe(assertPrefix, function() {
|
||||
t.test(assertName, function() {})
|
||||
})
|
||||
|
||||
assert.strictEqual(t.groupsFlat.length, 1)
|
||||
assert.strictEqual(t.groupsFlat[0].tests.length, 1)
|
||||
assert.strictEqual(t.groupsFlat[0].tests[0].name, assertPrefix + ' ' + assertName)
|
||||
})
|
||||
|
||||
c.test('Casette describe should add prefix to individual tests', async function() {
|
||||
testsWereRun = true
|
||||
const assertPrefix = 'something'
|
||||
const assertName = 'blabla'
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe(assertPrefix, function() {
|
||||
t.test(assertName, function() {})
|
||||
})
|
||||
|
||||
assert.strictEqual(t.tests.length, 1)
|
||||
assert.strictEqual(t.tests[0].name, assertPrefix + ' ' + assertName)
|
||||
})
|
||||
|
||||
c.test('Casette describe should support multiple describe', async function() {
|
||||
testsWereRun = true
|
||||
const assertPrefix = 'something'
|
||||
const assertPrefix2 = 'else'
|
||||
const assertName = 'blabla'
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe(assertPrefix, function() {
|
||||
t.describe(assertPrefix2, function() {
|
||||
t.test(assertName, function() {})
|
||||
})
|
||||
})
|
||||
|
||||
assert.strictEqual(t.tests.length, 1)
|
||||
assert.strictEqual(t.tests[0].name, assertPrefix + ' ' + assertPrefix2 + ' ' + assertName)
|
||||
})
|
||||
|
||||
c.test('Casette should run test', async function() {
|
||||
testsWereRun = true
|
||||
let assertIsTrue = false
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function() {
|
||||
assertIsTrue = true
|
||||
})
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(assertIsTrue, true)
|
||||
})
|
||||
|
||||
c.test('Casette should run promised test', async function() {
|
||||
testsWereRun = true
|
||||
let assertIsTrue = false
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function() {
|
||||
return new Promise(function(res) {
|
||||
assertIsTrue = true
|
||||
res()
|
||||
})
|
||||
})
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(assertIsTrue, true)
|
||||
})
|
||||
|
||||
c.test('Casette should support callback', async function() {
|
||||
testsWereRun = true
|
||||
let assertIsTrue = false
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function(cb) {
|
||||
setTimeout(function() {
|
||||
assertIsTrue = true
|
||||
cb()
|
||||
}, 50)
|
||||
})
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(assertIsTrue, true)
|
||||
})
|
||||
|
||||
c.test('Casette should support directly thrown errors', async function() {
|
||||
testsWereRun = true
|
||||
const assertError = new Error()
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function() {
|
||||
throw assertError
|
||||
})
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 1)
|
||||
assert.strictEqual(t.failedTests[0].error, assertError)
|
||||
})
|
||||
|
||||
c.test('Casette should support promise rejected errors', async function() {
|
||||
testsWereRun = true
|
||||
const assertError = new Error()
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function() {
|
||||
return new Promise(function(res, rej) {
|
||||
rej(assertError)
|
||||
})
|
||||
})
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 1)
|
||||
assert.strictEqual(t.failedTests[0].error, assertError)
|
||||
})
|
||||
|
||||
c.test('Casette should support callback rejected errors', async function() {
|
||||
testsWereRun = true
|
||||
const assertError = new Error()
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function(cb) {
|
||||
cb(assertError)
|
||||
})
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 1)
|
||||
assert.strictEqual(t.failedTests[0].error, assertError)
|
||||
})
|
||||
|
||||
c.test('Casette should support timing out tests', async function() {
|
||||
testsWereRun = true
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function(cb) { }).timeout(50)
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 1)
|
||||
assert.ok(t.failedTests[0].error)
|
||||
assert.match(t.failedTests[0].error.message, /50ms/)
|
||||
})
|
||||
|
||||
c.test('Casette should support timed out tests on late tests', async function() {
|
||||
testsWereRun = true
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function(cb) {
|
||||
setTimeout(function() {
|
||||
cb()
|
||||
}, 100)
|
||||
}).timeout(50)
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 1)
|
||||
assert.ok(t.failedTests[0].error)
|
||||
assert.match(t.failedTests[0].error.message, /50ms/)
|
||||
})
|
||||
|
||||
c.test('Casette should support skipped tests', async function() {
|
||||
testsWereRun = true
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.test('', function() {
|
||||
throw new Error('Should not be called')
|
||||
}).skip()
|
||||
await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
})
|
||||
|
||||
// Extra testing to make sure tests were run at all
|
||||
process.on('exit', function(e) {
|
||||
try {
|
||||
assert.strictEqual(testsWereRun, true)
|
||||
} catch(err) {
|
||||
printError(err)
|
||||
process.exit(1)
|
||||
}
|
||||
process.exit(e)
|
||||
})
|
265
test/cli.test.mjs
Normal file
265
test/cli.test.mjs
Normal file
|
@ -0,0 +1,265 @@
|
|||
import c from '../lib/casette.mjs'
|
||||
import assert from '../lib/assert.mjs'
|
||||
import { CLI, getFiles, fileMatches } from '../lib/cli.mjs'
|
||||
|
||||
c.describe('CLI', function() {
|
||||
let cli = new CLI()
|
||||
|
||||
c.test('#constructor() give default options', function() {
|
||||
assert.strictEqual(cli.reporter, 'list')
|
||||
assert.deepEqual(cli.targets, ['test/**'])
|
||||
assert.deepEqual(cli.files, [])
|
||||
assert.notOk(cli.errored)
|
||||
})
|
||||
|
||||
/*****************************************
|
||||
* #parseOptions()
|
||||
*****************************************/
|
||||
|
||||
c.describe('#parseOptions()', function() {
|
||||
c.test('should not do anything if no options', function() {
|
||||
cli.reporter = 'list'
|
||||
cli.parseOptions([])
|
||||
assert.strictEqual(cli.reporter, 'list')
|
||||
assert.notOk(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should support overriding reporter with shorthand option', function() {
|
||||
cli.reporter = 'list'
|
||||
cli.parseOptions(['-r', 'dot'])
|
||||
assert.strictEqual(cli.reporter, 'dot')
|
||||
assert.notOk(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should support overriding reporter with long option', function() {
|
||||
cli.reporter = 'list'
|
||||
cli.parseOptions(['--reporter', 'dot'])
|
||||
assert.strictEqual(cli.reporter, 'dot')
|
||||
assert.notOk(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should support reporter list', function() {
|
||||
cli.reporter = 'list'
|
||||
cli.parseOptions(['-r', 'list'])
|
||||
assert.strictEqual(cli.reporter, 'list')
|
||||
assert.notOk(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should mark errored if missing reporter', function() {
|
||||
cli.parseOptions(['--reporter'])
|
||||
assert.ok(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should mark errored if invalid reporter', function() {
|
||||
cli.parseOptions(['--reporter', 'test'])
|
||||
assert.ok(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should add file to targets', function() {
|
||||
cli.parseOptions(['test'])
|
||||
assert.deepEqual(cli.targets, ['test'])
|
||||
assert.notOk(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should add file to targets no matter where it is', function() {
|
||||
cli.parseOptions(['test', '-r', 'list', 'test2'])
|
||||
assert.deepEqual(cli.targets, ['test', 'test2'])
|
||||
assert.notOk(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should default add test to target if no target', function() {
|
||||
cli.parseOptions(['-r', 'list'])
|
||||
assert.deepEqual(cli.targets, ['test/**'])
|
||||
assert.notOk(cli.errored)
|
||||
})
|
||||
})
|
||||
|
||||
/*****************************************
|
||||
* #processTargets()
|
||||
*****************************************/
|
||||
|
||||
c.describe('#processTargets()', function() {
|
||||
c.test('should mark errored if empty', async function() {
|
||||
cli.targets = ['test/folder1/*.txt']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.strictEqual(cli.files.length, 0)
|
||||
assert.ok(cli.errored)
|
||||
})
|
||||
|
||||
c.test('should support direct file path if exists', async function() {
|
||||
cli.targets = ['test/folder1/sampletest1.temp.mjs']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.strictEqual(cli.files.length, 1)
|
||||
assert.strictEqual(cli.files[0], 'test/folder1/sampletest1.temp.mjs')
|
||||
})
|
||||
|
||||
c.test('should return all files in a directory', async function() {
|
||||
cli.targets = ['test/folder1/']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.strictEqual(cli.files.length, 2)
|
||||
assert.strictEqual(cli.files[0], 'test/folder1/sampletest1.temp.mjs')
|
||||
assert.strictEqual(cli.files[1], 'test/folder1/sampletest2.temp.mjs')
|
||||
})
|
||||
|
||||
c.test('should support start as folder substitute', async function() {
|
||||
cli.targets = ['*/folder1/']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.strictEqual(cli.files.length, 2)
|
||||
assert.strictEqual(cli.files[0], 'test/folder1/sampletest1.temp.mjs')
|
||||
assert.strictEqual(cli.files[1], 'test/folder1/sampletest2.temp.mjs')
|
||||
})
|
||||
|
||||
c.test('should support grabbing only files in folder', async function() {
|
||||
cli.targets = ['test/*']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.ok(cli.files.length)
|
||||
for (let i = 0; i < cli.files.length; i++) {
|
||||
assert.notOk(cli.files[i].match(/\/folder1\//))
|
||||
assert.notOk(cli.files[i].match(/\/folder2\//))
|
||||
}
|
||||
})
|
||||
|
||||
c.test('should support grabbing only pattern files in folder', async function() {
|
||||
cli.targets = ['test/*.test.mjs']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.ok(cli.files.length)
|
||||
for (let i = 0; i < cli.files.length; i++) {
|
||||
assert.notOk(cli.files[i].match(/\/folder1\//))
|
||||
assert.notOk(cli.files[i].match(/\/folder2\//))
|
||||
}
|
||||
})
|
||||
|
||||
c.test('should support multiple star pattern', async function() {
|
||||
cli.targets = ['test/*/*.mjs']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.strictEqual(cli.files.length, 4)
|
||||
cli.files.sort()
|
||||
assert.deepEqual(cli.files, [
|
||||
'test/folder1/sampletest1.temp.mjs',
|
||||
'test/folder1/sampletest2.temp.mjs',
|
||||
'test/folder2/sampletest3.temp.mjs',
|
||||
'test/folder2/sampletest4.temp.mjs',
|
||||
])
|
||||
|
||||
cli.targets = ['test/*/sampletest*.mjs']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.strictEqual(cli.files.length, 4)
|
||||
cli.files.sort()
|
||||
assert.deepEqual(cli.files, [
|
||||
'test/folder1/sampletest1.temp.mjs',
|
||||
'test/folder1/sampletest2.temp.mjs',
|
||||
'test/folder2/sampletest3.temp.mjs',
|
||||
'test/folder2/sampletest4.temp.mjs',
|
||||
])
|
||||
})
|
||||
|
||||
c.test('should support double star pattern', async function() {
|
||||
cli.targets = ['test/**/*.mjs']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.ok(cli.files.length)
|
||||
|
||||
let found = {
|
||||
sampletest1: false,
|
||||
sampletest2: false,
|
||||
sampletest3: false,
|
||||
sampletest4: false,
|
||||
sampletest5: false,
|
||||
cli: false
|
||||
}
|
||||
|
||||
for (let i = 0; i < cli.files.length; i++) {
|
||||
if (cli.files[i] === 'test/folder1/sampletest1.temp.mjs') {
|
||||
found.sampletest1 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/folder1/sampletest2.temp.mjs') {
|
||||
found.sampletest2 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/folder2/sampletest3.temp.mjs') {
|
||||
found.sampletest3 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/folder2/sampletest4.temp.mjs') {
|
||||
found.sampletest4 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/folder2/sampletest5.temp.txt') {
|
||||
found.sampletest5 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/cli.test.mjs') {
|
||||
found.cli = true
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepEqual(found, {
|
||||
sampletest1: true,
|
||||
sampletest2: true,
|
||||
sampletest3: true,
|
||||
sampletest4: true,
|
||||
sampletest5: false,
|
||||
cli: true
|
||||
})
|
||||
})
|
||||
|
||||
c.test('should support double star pattern end', async function() {
|
||||
cli.targets = ['test/**']
|
||||
await cli.processTargets()
|
||||
|
||||
assert.ok(cli.files.length)
|
||||
|
||||
let found = {
|
||||
sampletest1: false,
|
||||
sampletest2: false,
|
||||
sampletest3: false,
|
||||
sampletest4: false,
|
||||
sampletest5: false,
|
||||
cli: false
|
||||
}
|
||||
|
||||
for (let i = 0; i < cli.files.length; i++) {
|
||||
if (cli.files[i] === 'test/folder1/sampletest1.temp.mjs') {
|
||||
found.sampletest1 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/folder1/sampletest2.temp.mjs') {
|
||||
found.sampletest2 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/folder2/sampletest3.temp.mjs') {
|
||||
found.sampletest3 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/folder2/sampletest4.temp.mjs') {
|
||||
found.sampletest4 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/folder2/sampletest5.temp.txt') {
|
||||
found.sampletest5 = true
|
||||
}
|
||||
if (cli.files[i] === 'test/cli.test.mjs') {
|
||||
found.cli = true
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepEqual(found, {
|
||||
sampletest1: true,
|
||||
sampletest2: true,
|
||||
sampletest3: true,
|
||||
sampletest4: true,
|
||||
sampletest5: true,
|
||||
cli: true
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
c.test('#fileMatches() should support filename matching with glob pattern', async function() {
|
||||
assert.ok(fileMatches('bla.test.mjs', '*.mjs'))
|
||||
assert.ok(fileMatches('bla.test.mjs', '*test.mjs'))
|
||||
assert.ok(fileMatches('bla.test.mjs', 'bla*.mjs'))
|
||||
assert.notOk(fileMatches('bla.test.mjs', 'bla*.js'))
|
||||
assert.notOk(fileMatches('bla.test.mjs', '*.js'))
|
||||
assert.notOk(fileMatches('bla.test.mjs', 'blas*.js'))
|
||||
})
|
0
test/folder1/sampletest1.temp.mjs
Normal file
0
test/folder1/sampletest1.temp.mjs
Normal file
0
test/folder1/sampletest2.temp.mjs
Normal file
0
test/folder1/sampletest2.temp.mjs
Normal file
0
test/folder2/sampletest3.temp.mjs
Normal file
0
test/folder2/sampletest3.temp.mjs
Normal file
0
test/folder2/sampletest4.temp.mjs
Normal file
0
test/folder2/sampletest4.temp.mjs
Normal file
1
test/folder2/sampletest5.temp.txt
Normal file
1
test/folder2/sampletest5.temp.txt
Normal file
|
@ -0,0 +1 @@
|
|||
keep
|
9
test/test.mjs
Normal file
9
test/test.mjs
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { Casette as c, assert} from '../index.mjs'
|
||||
|
||||
c.describe('Array', function() {
|
||||
c.describe('#indexOf()', function() {
|
||||
c.test('should return -1 when value is not present', function() {
|
||||
assert.equal([1,2,3].indexOf(4), -1)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue