Comparing version 1.4.1 to 2.0.0
60
ospec.js
/* eslint-disable global-require, no-bitwise, no-process-exit */ | ||
"use strict" | ||
module.exports = new function init(name) { | ||
;(function(m) { | ||
if (typeof module !== "undefined") module["exports"] = m() | ||
else window.o = m() | ||
})(function init(name) { | ||
var spec = {}, subjects = [], results, only = null, ctx = spec, start, stack = 0, nextTickish, hasProcess = typeof process === "object", hasOwn = ({}).hasOwnProperty | ||
@@ -9,2 +11,5 @@ | ||
try {throw new Error} catch (e) { | ||
var ospecFileName = e.stack && (/[\/\\](.*?):\d+:\d+/).test(e.stack) ? e.stack.match(/[\/\\](.*?):\d+:\d+/)[1] : null | ||
} | ||
function o(subject, predicate) { | ||
@@ -53,9 +58,31 @@ if (predicate === undefined) { | ||
} | ||
o.cleanStackTrace = function(stack) { | ||
return stack.match(/^(?:(?!Error|[\/\\]ospec[\/\\]ospec\.js).)*$/gm).pop() | ||
o.cleanStackTrace = function(error) { | ||
// For IE 10+ in quirks mode, and IE 9- in any mode, errors don't have a stack | ||
if (error.stack == null) return "" | ||
var i = 0, header = error.message ? error.name + ": " + error.message : error.name, stack | ||
// some environments add the name and message to the stack trace | ||
if (error.stack.indexOf(header) === 0) { | ||
stack = error.stack.slice(header.length).split(/\r?\n/) | ||
stack.shift() // drop the initial empty string | ||
} else { | ||
stack = error.stack.split(/\r?\n/) | ||
} | ||
if (ospecFileName == null) return stack.join("\n") | ||
// skip ospec-related entries on the stack | ||
while (stack[i].indexOf(ospecFileName) !== -1) i++ | ||
// now we're in user code | ||
return stack[i] | ||
} | ||
o.run = function() { | ||
o.run = function(reporter) { | ||
results = [] | ||
start = new Date | ||
test(spec, [], [], report) | ||
test(spec, [], [], function() { | ||
setTimeout(function () { | ||
if (typeof reporter === "function") reporter(results) | ||
else { | ||
var errCount = o.report(results) | ||
if (hasProcess && errCount !== 0) process.exit(1) | ||
} | ||
}) | ||
}) | ||
@@ -97,4 +124,4 @@ function test(spec, pre, post, finalize) { | ||
if (err) { | ||
if (err.message) record(err.message, err) | ||
else record(err) | ||
if (err instanceof Error) record(err.message, err) | ||
else record(String(err)) | ||
subjects.pop() | ||
@@ -226,3 +253,4 @@ next() | ||
result.message = message | ||
result.error = error.stack | ||
result.error = error | ||
} | ||
@@ -238,7 +266,7 @@ results.push(result) | ||
function highlight(message) { | ||
return hasProcess ? "\x1b[31m" + message + "\x1b[0m" : "%c" + message + "%c " | ||
return hasProcess ? (process.stdout.isTTY ? "\x1b[31m" + message + "\x1b[0m" : message) : "%c" + message + "%c " | ||
} | ||
function report() { | ||
var status = 0 | ||
o.report = function (results) { | ||
var errCount = 0 | ||
for (var i = 0, r; r = results[i]; i++) { | ||
@@ -248,3 +276,3 @@ if (!r.pass) { | ||
console.error(r.context + ":\n" + highlight(r.message) + (stackTrace ? "\n\n" + stackTrace + "\n\n" : ""), hasProcess ? "" : "color:red", hasProcess ? "" : "color:black") | ||
status = 1 | ||
errCount++ | ||
} | ||
@@ -257,6 +285,6 @@ } | ||
) | ||
if (hasProcess && status === 1) process.exit(1) | ||
return errCount | ||
} | ||
if(hasProcess) { | ||
if (hasProcess) { | ||
nextTickish = process.nextTick | ||
@@ -271,2 +299,2 @@ } else { | ||
return o | ||
} | ||
}) |
{ | ||
"name": "ospec", | ||
"version": "1.4.1", | ||
"version": "2.0.0", | ||
"description": "Noiseless testing framework", | ||
@@ -15,3 +15,6 @@ "main": "ospec.js", | ||
}, | ||
"repository": "MithrilJS/mithril.js" | ||
"repository": "MithrilJS/mithril.js", | ||
"devDependencies": { | ||
"glob": "^7.1.2" | ||
} | ||
} |
163
README.md
ospec [![NPM Version](https://img.shields.io/npm/v/ospec.svg)](https://www.npmjs.com/package/ospec) [![NPM License](https://img.shields.io/npm/l/ospec.svg)](https://www.npmjs.com/package/ospec) | ||
===== | ||
[About](#about) | [Usage](#usage) | [API](#api) | [Goals](#goals) | ||
[About](#about) | [Usage](#usage) | [CLI](#command-line-interface) | [API](#api) | [Goals](#goals) | ||
@@ -10,3 +10,3 @@ Noiseless testing framework | ||
- ~180 LOC | ||
- ~330 LOC including the CLI runner | ||
- terser and faster test code than with mocha, jasmine or tape | ||
@@ -23,3 +23,3 @@ - test code reads like bullet points | ||
- async tests and hooks | ||
- explicitly disallows test-space configuration to encourage focus on testing, and to provide uniform test suites across projects | ||
- explicitly regulates test-space configuration to encourage focus on testing, and to provide uniform test suites across projects | ||
@@ -293,12 +293,7 @@ ## Usage | ||
### Running the test suite from the command-line | ||
## Command Line Interface | ||
ospec will automatically evaluate all `*.js` files in any folder named `/tests`. | ||
`o.run()` is automatically called by the cli - no need to call it in your test code. | ||
#### Create an npm script in your package: | ||
Create a script in your package.json: | ||
``` | ||
"scripts": { | ||
... | ||
"test": "ospec", | ||
@@ -308,14 +303,55 @@ ... | ||
``` | ||
...and run it from the command line: | ||
``` | ||
$ npm test | ||
$ npm test | ||
``` | ||
#### Direct use from the command line | ||
**NOTE:** `o.run()` is automatically called by the cli - no need to call it in your test code. | ||
Ospec doesn't work when installed globally. Using global scripts is generally a bad idea since you can end up with different, incompatible versions of the same package installed locally and globally. | ||
### CLI Options | ||
To work around this limitation, you can use [`npm-run`](https://www.npmjs.com/package/npm-run) which enables one to run the binaries of locally installed packages. | ||
Running ospec without arguments is equivalent to running `ospec '**/tests/**/*.js'`. In english, this tells ospec to evaluate all `*.js` files in any sub-folder named `tests/` (the `node_modules` folder is always excluded). | ||
If you wish to change this behavior, just provide one or more glob match patterns: | ||
``` | ||
ospec '**/spec/**/*.js' '**/*.spec.js' | ||
``` | ||
You can also provide ignore patterns (note: always add `--ignore` AFTER match patterns): | ||
``` | ||
ospec --ignore 'folder1/**' 'folder2/**' | ||
``` | ||
Finally, you may choose to load files or modules before any tests run (**note:** always add `--require` AFTER match patterns): | ||
``` | ||
ospec --require esm | ||
``` | ||
Here's an example of mixing them all together: | ||
``` | ||
ospec '**/*.test.js' --ignore 'folder1/**' --require esm ./my-file.js | ||
``` | ||
### Run ospec directly from the command line: | ||
ospec comes with an executable named `ospec`. NPM auto-installs local binaries to `./node_modules/.bin/`. You can run ospec by running `./node_modules/.bin/ospec` from your project root, but there are more convenient methods to do so that we will soon describe. | ||
ospec doesn't work when installed globally (`npm install -g`). Using global scripts is generally a bad idea since you can end up with different, incompatible versions of the same package installed locally and globally. | ||
Here are different ways of running ospec from the command line. This knowledge applies to not just ospec, but any locally installed npm binary. | ||
#### npx | ||
If you're using a recent version of npm (v5+), you can use run `npx ospec` from your project folder. | ||
#### npm-run | ||
If you're using an older NPM version, you can use [`npm-run`](https://www.npmjs.com/package/npm-run) which enables one to run the binaries of locally installed packages as npx would. | ||
``` | ||
npm install npm-run -g | ||
@@ -330,2 +366,12 @@ ``` | ||
#### PATH | ||
If you understand how your system's PATH works (e.g. for [OSX](https://coolestguidesontheplanet.com/add-shell-path-osx/)), then you can add the following to your PATH... | ||
``` | ||
export PATH=./node_modules/.bin:$PATH | ||
``` | ||
...and you'll be able to run `ospec` without npx, npm, etc. This one-time setup will also work with other binaries across all your node projects, as long as you run binaries from the root of your projects. | ||
--- | ||
@@ -445,8 +491,23 @@ | ||
### void o.run() | ||
### void o.run([Function reporter]) | ||
Runs the test suite | ||
Runs the test suite. By default passing test results are printed using | ||
`console.log` and failing test results are printed using `console.error`. | ||
If you have custom continuous integration needs then you can use a | ||
reporter to process [test result data](#result-data) yourself. | ||
If running in Node.js, ospec will call `process.exit` after reporting | ||
results by default. If you specify a reporter, ospec will not do this | ||
and allow your reporter to respond to results in its own way. | ||
--- | ||
### Number o.report(results) | ||
The default reporter used by `o.run()` when none are provided. Returns the number of failures, doesn't exit Node.js by itself. It expects an array of [test result data](#result-data) as argument. | ||
--- | ||
### Function o.new() | ||
@@ -466,2 +527,70 @@ | ||
## Result data | ||
Test results are available by reference for integration purposes. You | ||
can use custom reporters in `o.run()` to process these results. | ||
```javascript | ||
o.run(function(results) { | ||
// results is an array | ||
results.forEach(function(result) { | ||
// ... | ||
}) | ||
}) | ||
``` | ||
--- | ||
### Boolean result.pass | ||
True if the test passed. **No other keys will exist on the result if this value is true.** | ||
--- | ||
### Error result.error | ||
The `Error` object explaining the reason behind a failure. | ||
--- | ||
### String result.message | ||
If an exception was thrown inside the corresponding test, this will equal that Error's `message`. Otherwise, this will be a preformatted message in [SVO form](https://en.wikipedia.org/wiki/Subject%E2%80%93verb%E2%80%93object). More specifically, `${subject}\n${verb}\n${object}`. | ||
As an example, the following test's result message will be `"false\nshould equal\ntrue"`. | ||
```javascript | ||
o.spec("message", function() { | ||
o(false).equals(true) | ||
}) | ||
``` | ||
If you specify an assertion description, that description will appear two lines above the subject. | ||
```javascript | ||
o.spec("message", function() { | ||
o(false).equals(true)("Candyland") // result.message === "Candyland\n\nfalse\nshould equal\ntrue" | ||
}) | ||
``` | ||
--- | ||
### String result.context | ||
A `>`-separated string showing the structure of the test specification. | ||
In the below example, `result.context` would be `testing > rocks`. | ||
```javascript | ||
o.spec("testing", function() { | ||
o.spec("rocks", function() { | ||
o(false).equals(true) | ||
}) | ||
}) | ||
``` | ||
--- | ||
## Goals | ||
@@ -472,4 +601,4 @@ | ||
- Disallow ability to pick between API styles (BDD/TDD/Qunit, assert/should/expect, etc) | ||
- Disallow ability to pick between different reporters | ||
- Disallow ability to add custom assertion types | ||
- Provide a default simple reporter | ||
- Make assertion code terse, readable and self-descriptive | ||
@@ -476,0 +605,0 @@ - Have as few assertion types as possible for a workable usage pattern |
@@ -21,2 +21,73 @@ "use strict" | ||
new function(o) { | ||
var clone = o.new() | ||
clone.spec("clone", function() { | ||
clone("fail", function() { | ||
clone(true).equals(false) | ||
}) | ||
clone("pass", function() { | ||
clone(true).equals(true) | ||
}) | ||
}) | ||
// Predicate test passing on clone results | ||
o.spec("reporting", function() { | ||
o("reports per instance", function(done, timeout) { | ||
timeout(100) // Waiting on clone | ||
clone.run(function(results) { | ||
o(typeof results).equals("object") | ||
o("length" in results).equals(true) | ||
o(results.length).equals(2)("Two results") | ||
o("error" in results[0] && "pass" in results[0]).equals(true)("error and pass keys present in failing result") | ||
o(!("error" in results[1]) && "pass" in results[1]).equals(true)("only pass key present in passing result") | ||
o(results[0].pass).equals(false)("Test meant to fail has failed") | ||
o(results[1].pass).equals(true)("Test meant to pass has passed") | ||
done() | ||
}) | ||
}) | ||
o("o.report() returns the number of failures", function () { | ||
var log = console.log, error = console.error | ||
console.log = o.spy() | ||
console.error = o.spy() | ||
function makeError(msg) {try{throw msg ? new Error(msg) : new Error} catch(e){return e}} | ||
try { | ||
var errCount = o.report([{pass: true}, {pass: true}]) | ||
o(errCount).equals(0) | ||
o(console.log.callCount).equals(1) | ||
o(console.error.callCount).equals(0) | ||
errCount = o.report([ | ||
{pass: false, error: makeError("hey"), message: "hey"} | ||
]) | ||
o(errCount).equals(1) | ||
o(console.log.callCount).equals(2) | ||
o(console.error.callCount).equals(1) | ||
errCount = o.report([ | ||
{pass: false, error: makeError("hey"), message: "hey"}, | ||
{pass: true}, | ||
{pass: false, error: makeError("ho"), message: "ho"} | ||
]) | ||
o(errCount).equals(2) | ||
o(console.log.callCount).equals(3) | ||
o(console.error.callCount).equals(3) | ||
} catch (e) { | ||
o(1).equals(0)("Error while testing the reporter") | ||
} | ||
console.log = log | ||
console.error = error | ||
}) | ||
}) | ||
}(o) | ||
o.spec("ospec", function() { | ||
@@ -153,9 +224,9 @@ o.spec("sync", function() { | ||
o.spec('stack trace cleaner', function() { | ||
o('handles line breaks', function() { | ||
o.spec("stack trace cleaner", function() { | ||
o("handles line breaks", function() { | ||
try { | ||
throw new Error('line\nbreak') | ||
throw new Error("line\nbreak") | ||
} catch(error) { | ||
var trace = o.cleanStackTrace(error.stack) | ||
o(trace).notEquals('break') | ||
var trace = o.cleanStackTrace(error) | ||
o(trace).notEquals("break") | ||
o(trace.includes("test-ospec.js")).equals(true) | ||
@@ -162,0 +233,0 @@ } |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
37076
9
502
606
0
1