New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

tst

Package Overview
Dependencies
Maintainers
3
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tst - npm Package Compare versions

Comparing version 1.3.2 to 2.0.0

.travis.yml

735

index.js

@@ -1,668 +0,149 @@

/**
* Simple node/browser test runner
*
* @module tst
*/
import * as assert from './assert.js'
var chalk = require('chalk');
var isBrowser = require('is-browser');
var now = require('performance-now');
var elegantSpinner = require('elegant-spinner');
var logUpdate = require('log-update');
var ansi = require('ansi-escapes');
var inherits = require('inherits');
var Emitter = require('events');
var extend = require('xtend/mutable');
var inspect = require('util-inspect')
let ondone
// Error.stackTraceLimit = 10;
export const done = new Promise((resolve) => {
ondone = resolve
})
function start () {
if (!running) {
running = true
//default indentation
test.INDENT = ' ';
//whether we run the only test, forcefully
test.ONLY_MODE = false;
//default timeout for async tests
test.TIMEOUT = 2000;
//max timeout
test.MAX_TIMEOUT = 10e5;
//chain of nested test calls
var tests = [];
var testCount = 0;
//planned tests to run
var testQueue = [];
//flag indicating that since some time tests are run in deferred fashion
//i.e. lost their stack in browser :(
var DEFERRED = false;
//indicate whether we are in only-detection mode (tests are just planned, not run)
//or we are in a forced full-bundle run. Unlikely user will ever touch this flag.
test.DETECT_ONLY = true;
//detect whether at least one test failed
test.ERROR = false;
//end with error, if any
process.on('exit', function () {
if (test.ERROR) process.exit(1);
});
//run execution after all sync tests are registered
if (test.DETECT_ONLY) {
setTimeout(function () {
//if only detection mode wasn’t changed by user
//which means sync tests are run already - run the thing
if (test.DETECT_ONLY) {
run();
Promise.resolve().then(() => {
const hasOnly = tests.some((test) => test.only)
tests.forEach((test) => {
test.shouldRun = true
if (test.skip) {
test.shouldRun = false
} else if (hasOnly) {
test.shouldRun = test.only
}
});
}
})
/**
* Test enqueuer
*/
function test (message, fn, only) {
//if run in exclusive mode - allow only `test.only` calls
if (test.ONLY_MODE && !only) {
//but if test is run within the parent - allow it
if (!tests.length) return test;
}
//ignore bad args
if (!message) return test;
//init test object params
var testObj = new Test({
id: testCount++,
title: message,
//pending, success, error, group
status: null,
//test function
fn: fn,
//nested tests
children: [],
//whether test should be resolved
async: undefined,
//whether the test is last child within the group
last: false,
//timeout for the async
_timeout: test.TIMEOUT,
//whether the test is the only to run (launched via .only method)
only: !!only,
//whether the test was started in deferred fashion
//it can be sync, but launched after async
deferred: DEFERRED
});
//handle args
if (!fn) {
//if only message passed - do skip
if (!fn && typeof message === 'string') {
testObj.status = 'skip';
}
else {
//detect test name
testObj.fn = message;
message = message.name;
if (!message) message = 'Test #' + testObj.id;
//update test title
testObj.title = message;
}
}
//clean title
testObj.title = testObj.title.trim();
testObj.name = testObj.title;
//detect async as at least one function argument
//NOTE: tests returning promise will set async flag here
if (testObj.async == null) {
testObj.async = !!(testObj.fn && testObj.fn.length);
}
//also detect promise, if passed one
if (testObj.fn && testObj.fn.then) {
//also that means that the test is run already
//and tests within the promise executor are already detected it’s parent wrongly
//nothing we can do. Redefining parent is not an option -
//we don’t know which tests were of this parent, which were not.
testObj.promise = testObj.fn;
testObj.async = true;
testObj.time = now();
}
//nested tests are detected here
//because calls to `.test` from children happen only when some test is active
testObj.parent = tests[tests.length - 1];
//register children - supposed that parent will run all the children after fin
if (testObj.parent) {
testObj.parent.children.push(testObj);
}
//if test has no parent - plan it's separate run
else {
testQueue.push(testObj);
}
//if detecion only mode - ignore execution
//if ONLY_MODE - execute it at instant
if (!test.DETECT_ONLY || test.ONLY_MODE) {
run();
}
return testObj;
dequeue()
})
}
}
/**
* Tests queue runner
*/
var currentTest;
function run () {
//ignore active run
if (currentTest) return;
//get the planned test
currentTest = testQueue.shift();
//if the queue is empty - return
if (!currentTest) return;
//ignore test if it is not the only run
if (test.ONLY_MODE && !currentTest.only) {
return planRun();
}
//exec it, the promise will be formed
currentTest.exec();
//at the moment test is run, we know all it’s children
//push all the children to the queue, after the current test
//FIXME: this guy erases good stacktrace :< Maybe call asyncs last?
var children = currentTest.children;
//mind the case if no only children test is selected - run them all instead of none
if (children.every(function (child) {return !child.only})) {
children.forEach(function (child) {
child.only = true;
});
}
for (var i = children.length; i--;){
testQueue.unshift(children[i]);
}
//mark last kid
if (children.length) {
children[children.length - 1].last = true;
}
//if test is not async - run results at instant to avoid losing stacktrace
if (!currentTest.async) {
currentTest = null;
run();
}
//plan running next test after the promise
else {
DEFERRED = true;
currentTest.promise.then(planRun, planRun);
}
function planRun () {
currentTest = null;
run();
}
export default function test (name, fn) {
tests.push({ name, fn, skip: false, only: false, shouldRun: false })
start()
}
Object.assign(test, {
skip (name, fn) {
tests.push({ name, fn, skip: true, only: false, shouldRun: null })
start()
},
only (name, fn) {
tests.push({ name, fn, skip: false, only: true, shouldRun: null })
start()
}
})
/**
* A test object constructor
*/
function Test (opts) {
extend(this, opts);
}
let testIndex = 0
let assertIndex = 0
let running = false
inherits(Test, Emitter);
const tests = []
let passed = 0
let failed = 0
let skipped = 0
const isNode = typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]'
/**
* Call before exec
*/
Test.prototype.after = function (cb) {
this.once('after', cb);
return this;
};
/**
* Call after exec
*/
Test.prototype.before = function (cb) {
this.once('before', cb);
return this;
};
export function log (ok, operator, msg, info = {}) {
assertIndex += 1
if (ok) {
console.log(`ok ${assertIndex} — ${msg}`)
passed += 1
} else {
console.log(`not ok ${assertIndex} — ${msg}`)
failed += 1
/**
* Bind promise-like
*/
Test.prototype.then = function (resolve, reject) {
this.once('success', resolve);
this.once('error', reject);
return this;
};
console.log(' ---')
console.log(` operator: ${operator}`)
/**
* Mocha-compat timeout setter
*/
Test.prototype.timeout = function (value) {
if (value == null) return this._timeout;
if (value === false) this._timeout = test.MAX_TIMEOUT;
else if (value === Infinity) this._timeout = test.MAX_TIMEOUT;
else this._timeout = value;
return this;
}
/**
* Prototype props
*
* @True {[type]}
*/
extend(Test.prototype, {
id: testCount,
title: 'Undefined test',
//pending, success, error, group
status: null,
//test function
fn: null,
//nested tests
children: [],
//whether test should be resolved
async: undefined,
//whether the test is last child within the group
last: false,
//timeout for the async
_timeout: test.TIMEOUT,
//whether the test is the only to run (launched via .only method)
only: false,
//whether the test was started in deferred fashion
//it can be sync, but launched after async
deferred: DEFERRED
});
/**
* Execute main test function
*/
Test.prototype.exec = function () {
var self = this;
//ignore skipping test
if (self.status === 'skip') {
self.promise = Promise.resolve();
self.print();
return self;
if ('expected' in info) {
console.log(` expected:`, info.expected)
}
//save test to the chain
tests.push(self);
//display title of the test
self.printTitle();
//timeout promise timeout id
var toId;
//prepare test
self.emit('before');
//exec sync test
if (!self.async) {
self.promise = Promise.resolve();
var time;
try {
self.time = now();
var result = self.fn.call(self);
time = now() - self.time;
} catch (e) {
self.fail(e);
}
//if the result is promise - whoops, we need to run async
if (result && result.then) {
self.async = true;
self.promise = result;
//FIXME: this guy violates the order of nesting
//because so far it was thought as sync
self.execAsync();
}
//if result is not error - do finish
else {
self.time = time;
self.emit('after');
if (!self.error) {
if (!self.status !== 'group') self.status = 'success';
self.emit('success');
self.print();
}
}
if ('actual' in info) {
console.log(` actual:`, info.actual)
}
else {
self.execAsync();
let {actual, expected, ...rest} = info
for (let prop in rest) {
console.log(` ${prop}:`, rest[prop])
}
//after promise’s executor, but before promise then’s
//so user can’t create tests asynchronously, they should be created at once
tests.pop();
// find where the error occurred, and try to clean it up
let err = new Error()
Error.captureStackTrace(err, log);
let lines = err.stack.split('\n').slice(3)
return self;
};
let cwd = ''
if (isNode) {
cwd = process.cwd()
if (/[/\\]/.test(cwd[0])) cwd += cwd[0]
/*
* Exec async test - it should be run in promise
* sorry about the stacktrace, nothing I can do...
*/
Test.prototype.execAsync = function () {
var self = this;
const dirname = typeof __dirname === 'string' && __dirname.replace(/dist$/, '')
//if promise is already created (by user) - race with timeout
//FIXME: add time measure
if (self.promise) {
self.promise = Promise.race([
self.promise,
new Promise(execTimeout)
]);
}
//else - invoke function
else {
self.promise = Promise.race([
new Promise(function (resolve, reject) {
self.status = 'pending';
self.time = now();
return self.fn.call(self, resolve);
}),
new Promise(execTimeout)
])
}
self.promise.then(function () {
self.time = now() - self.time;
clearTimeout(toId);
if (self.status !== 'group') self.status = 'success';
self.emit('after');
self.emit('success');
self.print();
}, function (e) {
self.fail(e)
});
function execTimeout (resolve, reject) {
toId = setTimeout(function () {
reject(new Error('Timeout ' + self._timeout + 'ms reached. Please fix the test or set `this.timeout(' + (self._timeout + 1000) + ');`.'));
}, self._timeout);
}
};
/**
* Resolve to error (error handler)
*/
Test.prototype.fail = function (e) {
var self = this;
if (typeof e !== 'object') e = Error(e);
//grab stack (the most actual is here, further is mystically lost)
self.stack = e.stack;
//set flag that bundle is failed
test.ERROR = true;
var parent = self.parent;
while (parent) {
parent.status = 'group';
parent = parent.parent;
}
//update test status
self.status = 'error';
self.error = e;
self.emit('fail', e);
self.print();
};
Test.prototype.printTitle = function () {
var self = this;
if (!isBrowser) {
var frame = elegantSpinner();
//print title (indicator of started, now current test)
updateTitle();
self.titleInterval = setInterval(updateTitle, 50);
//update title frame
function updateTitle () {
//FIXME: this is the most undestructive for logs way of rendering, but crappy
process.stdout.write(ansi.cursorLeft);
process.stdout.write(ansi.eraseEndLine);
process.stdout.write(chalk.white(indent(self) + ' ' + frame() + ' ' + self.title) + test.INDENT);
// logUpdate(chalk.white(indent(test.indent) + ' ' + frame() + ' ' + test.title));
for (let i = 0; i < lines.length; i += 1) {
if (lines[i].indexOf(dirname) !== -1) {
lines = lines.slice(0, i)
break
}
}
}
}
//clear printed title (node)
Test.prototype.clearTitle = function () {
if (!isBrowser && this.titleInterval) {
clearInterval(this.titleInterval);
process.stdout.write(ansi.cursorLeft);
process.stdout.write(ansi.eraseEndLine);
}
}
//universal printer dependent on resolved test
Test.prototype.print = function () {
var self = this;
const stack = lines
.map((line) => ` ${line.replace(cwd, '').trim()}`)
.join('\n')
this.clearTitle();
var single = self.children && self.children.length ? false : true;
if (self.status === 'error') {
self.printError();
}
else if (self.status === 'group') {
self.printGroup(single);
}
else if (self.status === 'success') {
self.printSuccess(single);
}
else if (self.status === 'skip') {
self.printSkip(single);
}
//last child should close parent’s group in browser
if (self.last) {
if (isBrowser) {
//if truly last - create as many groups as many last parents
if (!self.children.length) {
console.groupEnd();
var parent = self.parent;
while (parent && parent.last) {
console.groupEnd();
parent = parent.parent;
}
}
} else {
//create padding
if (!self.children.length) console.log();
}
}
console.log(` stack: \n${stack}`)
console.log(` ...`)
}
}
//print pure red error
Test.prototype.printError = function () {
var self = this;
async function dequeue () {
const test = tests[testIndex++]
//browser shows errors better
if (isBrowser) {
console.group('%c× ' + self.title, 'color: red; font-weight: normal');
if (self.error) {
if (self.error.name === 'AssertionError') {
if (typeof self.error.expected !== 'object') {
var msg = '%cAssertionError:\n%c' + self.error.expected + '\n' + '%c' + self.error.operator + '\n' + '%c' + self.error.actual;
console.groupCollapsed(msg, 'color: red; font-weight: normal', 'color: green; font-weight: normal', 'color: gray; font-weight: normal', 'color: red; font-weight: normal');
}
else {
var msg = '%cAssertionError: ' + self.error.message;
console.groupCollapsed(msg, 'color: red; font-weight: normal');
}
console.error(self.stack);
console.groupEnd();
}
else {
var msg = typeof self.error === 'string' ? self.error : self.error.message;
console.groupCollapsed('%c' + msg, 'color: red; font-weight: normal');
console.error(self.stack);
console.groupEnd();
}
}
console.groupEnd();
if (test) {
if (!test.shouldRun) {
if (test.skip) {
// Useless info
// console.log(`# skip ${test.name}`)
}
skipped += 1
dequeue()
return
}
else {
console.log(chalk.red(indent(self) + ' × ') + chalk.red(self.title));
if (self.error.stack) {
if (self.error.name === 'AssertionError') {
var expected = inspect(self.error.expected)
var actual = inspect(self.error.actual)
console.error(chalk.gray('AssertionError: ') + chalk.green(expected) + '\n' + chalk.gray(self.error.operator) + '\n' + chalk.red(actual));
} else {
//NOTE: node prints e.stack along with e.message
var stack = self.error.stack.replace(/^\s*/gm, indent(self) + ' ');
console.error(chalk.gray(stack));
}
}
}
}
console.log(`# ${test.name}`)
//print green success
Test.prototype.printSuccess = function (single) {
var self = this;
if (isBrowser) {
if (single) {
console.log('%c√ ' + self.title + '%c ' + self.time.toFixed(2) + 'ms', 'color: green; font-weight: normal', 'color:rgb(150,150,150); font-size:0.9em');
} else {
self.printGroup();
}
try {
await test.fn(assert)
} catch (err) {
failed += 1
console.log(`not ok ${assertIndex} — ${err.message}`)
console.error(` ${err.stack.replace(/^\s+/gm, ' ')}`)
}
else {
if (!single) {
self.printGroup();
}
else {
console.log(chalk.green(indent(self) + ' √ ') + chalk.green.dim(self.title) + chalk.gray(' ' + self.time.toFixed(2) + 'ms'));
}
}
}
//print yellow warning (not all tests are passed or it is container)
Test.prototype.printGroup = function () {
var self = this;
dequeue()
} else {
// summarise
const total = passed + failed + skipped
console.log(`\n1..${total}`)
console.log(`# tests ${total}`)
if (passed) console.log(`# pass ${passed}`)
if (failed) console.log(`# fail ${failed}`)
if (skipped) console.log(`# skip ${skipped}`)
if (isBrowser) {
console.group('%c+ ' + self.title + '%c ' + self.time.toFixed(2) + 'ms', 'color: orange; font-weight: normal', 'color:rgb(150,150,150); font-size:0.9em');
}
else {
console.log();
console.log(indent(self) +' ' + chalk.yellow('+') + ' ' + chalk.yellow(self.title) + chalk.gray(' ' + self.time.toFixed(2) + 'ms'));
}
ondone()
if (isNode) process.exit(failed ? 1 : 0)
}
}
//print blue skip
Test.prototype.printSkip = function (single) {
var self = this;
if (isBrowser) {
console[single ? 'log' : 'group']('%c- ' + self.title, 'color: blue');
}
else {
console.log(chalk.cyan(indent(self) + ' - ') + chalk.cyan.dim(self.title));
}
}
//return indentation of for a test, based on nestedness
function indent (testObj) {
var parent = testObj.parent;
var str = '';
while (parent) {
str += test.INDENT;
parent = parent.parent;
}
return str;
}
//skip alias
test.skip = function skip (message) {
return test(message);
};
//only alias
test.only = function only (message, fn) {
//indicate that only is detected, except for the case of intentional run
if (fn) test.DETECT_ONLY = false;
//change only mode to true
test.ONLY_MODE = true;
var result = test(message, fn, true);
return result;
}
//more obvious chain
test.test = test;
module.exports = test;
{
"name": "tst",
"version": "1.3.2",
"description": "Minimalistic test runner for node/browser",
"description": "Testing tool",
"version": "2.0.0",
"repository": "dy/tst",
"author": "Dmitry Yv",
"main": "index.js",
"files": [
"index.js"
],
"module": "index.js",
"license": "MIT",
"scripts": {
"test": "node test.js",
"test:browser": "budo test.js"
"test": "node -r esm test.js"
},
"repository": {
"type": "git",
"url": "https://github.com/dfcreative/tst"
},
"keywords": [
"testing",
"mocha",
"ava",
"tap",
"tape",
"tdd",
"bdd",
"async",
"karma",
"test",
"runner",
"light-mocha"
],
"author": "grahamlyons <graham@grahamlyons.com>",
"contributors": [
"Dmitry Yvanow <dfcreative@gmail.com>"
],
"license": "BSD",
"bugs": {
"url": "https://github.com/dfcreative/tst/issues"
},
"dependencies": {
"ansi-escapes": "^1.1.0",
"chalk": "^1.1.1",
"elegant-spinner": "^1.0.1",
"inherits": "^2.0.1",
"is-browser": "^2.0.1",
"log-update": "^1.0.2",
"performance-now": "^0.2.0",
"util-inspect": "^0.1.8",
"xtend": "^4.0.1"
"devDependencies": {
"esm": "^3.2.25"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc