Socket
Socket
Sign inDemoInstall

tap-arc

Package Overview
Dependencies
Maintainers
3
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tap-arc - npm Package Compare versions

Comparing version 0.3.5 to 0.3.6-RC.0

src/_help-text.js

290

index.js
#!/usr/bin/env node
import minimist from 'minimist'
const JSON5 = require('json5')
const minimist = require('minimist')
const Parser = require('tap-parser')
const stripAnsi = require('strip-ansi')
const through = require('through2')
const { strict } = require('tcompare')
const { blue, bold, dim, green, italic, red, underline, yellow } = require(
'picocolors',
)
import helpText from './src/_help-text.js'
import tapArc from './src/arc-tap.js'
// Log test-group name
const RESULT_COMMENTS = [
'tests ', 'pass ', 'skip', 'todo', 'fail ', 'failed ', 'ok',
]
const alias = {

@@ -32,274 +21,11 @@ help: [ 'h', 'help' ],

if (options.help) {
console.log(
`
Usage:
tap-arc <options>
Parses TAP data from stdin, and outputs a "spec-like" formatted result.
Options:
-v | --verbose
Output full stack trace
-p | --pessimistic | --bail
Immediately exit upon encountering a failure
example: tap-arc -p
--no-color
Output without ANSI escape sequences for colors
example: tap-arc --no-color`,
)
console.log(helpText)
process.exit()
}
const parser = new Parser({ bail: options.pessimistic })
const tapArc = through()
const cwd = process.cwd()
const start = Date.now()
const OKAY = green('✔')
const FAIL = red('✖')
function pad (count = 1, char = ' ') {
return dim(char).repeat(count)
}
function makeDiff (lhs, rhs) {
const msg = []
let isJson = true
let pLhs = lhs
let pRhs = rhs
try {
pLhs = JSON5.parse(lhs)
pRhs = JSON5.parse(rhs)
}
catch (e) {
isJson = false
}
if (isJson) {
lhs = pLhs
rhs = pRhs
}
const compared = strict(lhs, rhs, {
includeEnumerable: true,
includeGetters: true,
pretty: true,
sort: true,
})
if (!compared.match) {
// remove leading header lines
let diff = compared.diff.split('\n')
diff = diff.slice(2, diff.length - 1)
for (const line of diff) {
const char0 = line.charAt(0)
if (char0 === '-') {
msg.push(red(line))
}
else if (char0 === '+') {
msg.push(green(line))
}
else if (char0 === '@') {
msg.push(italic(dim(line)))
}
else {
msg.push(line)
}
}
}
else {
msg.push(`${red('Expected')} did not match ${green('actual')}.`)
}
return msg
}
function print (msg) {
tapArc.push(options.color ? msg : stripAnsi(msg))
}
function prettyMs (start) {
const ms = Date.now() - start
return ms < 1000 ? `${ms} ms` : `${ms / 1000} s`
}
parser.on('pass', (pass) => {
print(`${pad(2)}${OKAY} ${dim(pass.name)}\n`)
const parser = tapArc(options, (error, result) => {
process.exit(result.ok ? 0 : 1)
// process.exit(result.ok && result.count > 0 ? 0 : 1)
})
parser.on('skip', (skip) => {
print(`${pad(2)}${dim(`SKIP ${skip.name}`)}\n`)
})
parser.on('extra', (extra) => {
const stripped = stripAnsi(extra).trim()
const justAnsi = stripped.length === 0 && extra.length > 0
if (!justAnsi) {
print(`${pad(2)}${extra}`)
}
})
parser.on('comment', (comment) => {
if (!RESULT_COMMENTS.some((c) => comment.startsWith(c, 2))) {
print(`\n${pad()}${underline(comment.trimEnd().replace(/^(# )/, ''))}\n`)
}
})
parser.on('todo', (todo) => {
if (todo.ok) {
print(`${pad(2)}${yellow('TODO')} ${dim(todo.name)}\n`)
}
else {
print(`${pad(2)}${red('TODO')} ${dim(todo.name)}\n`)
}
})
parser.on('fail', (fail) => {
print(`${pad(2)}${FAIL} ${dim(`${fail.id})`)} ${red(fail.name)}\n`)
if (fail.diag) {
const { actual, at, expected, operator, stack } = fail.diag
let msg = [] // individual lines of output
if ([ 'equal', 'deepEqual' ].includes(operator)) {
if (typeof expected === 'string' && typeof actual === 'string') {
msg = [ ...msg, ...makeDiff(actual, expected) ]
}
else if (typeof expected === 'object' && typeof actual === 'object') {
// probably an array
msg = [ ...msg, ...makeDiff(actual, expected) ]
}
else if (typeof expected === 'number' || typeof actual === 'number') {
msg.push(`Expected ${red(expected)} but got ${green(actual)}`)
}
else {
// mixed types
msg.push(`operator: ${operator}`)
msg.push(`expected: ${red(`- ${expected}`)} <${typeof expected}>`)
msg.push(`actual: ${green(`+ ${actual}`)} <${typeof actual}>`)
}
}
else if ([ 'notEqual', 'notDeepEqual' ].includes(operator)) {
msg.push('Expected values to differ')
}
else if (operator === 'ok') {
msg.push(`Expected ${blue('truthy')} but got ${green(actual)}`)
}
else if (operator === 'match') {
msg.push(`Expected "${actual}" to match ${blue(expected)}`)
}
else if (operator === 'doesNotMatch') {
msg.push(`Expected "${actual}" to not match ${blue(expected)}`)
}
else if (
operator === 'throws' &&
actual &&
actual !== 'undefined' &&
expected &&
expected !== 'undefined'
) {
// this combination is throws with expected/assertion
msg.push(
`Expected ${red(expected)} to match "${green(
actual.message || actual,
)}"`,
)
}
else if (operator === 'throws' && actual && actual !== 'undefined') {
// this combination is ~doesNotThrow
msg.push(`Expected to not throw, received "${green(actual)}"`)
}
else if (operator === 'throws') {
msg.push('Expected to throw')
}
else if (operator === 'error') {
msg.push(`Expected error to be ${blue('falsy')}`)
}
else if (expected && !actual) {
msg.push(`Expected ${red(operator)} but got nothing`)
}
else if (actual && !expected) {
msg.push(`Expected ${blue('falsy')} but got ${green(actual)}`)
}
else if (expected && actual) {
msg.push(`Expected ${red(expected)} but got ${green(actual)}`)
}
else if (operator === 'fail') {
msg.push('Explicit fail')
}
else if (!expected && !actual) {
msg.push(`operator: ${yellow(operator)}`)
}
else {
// unlikely
msg.push(`operator: ${yellow(operator)}`)
msg.push(`expected: ${green(expected)}`)
msg.push(`actual: ${red(actual)}`)
}
if (at) {
msg.push(`${dim(`At: ${at.replace(cwd, '')}`)}`)
}
if (options.verbose && stack) {
msg.push('')
stack.split('\n').forEach((s) => {
msg.push(dim(s.trim().replace(cwd, '')))
})
}
msg.push('')
// final formatting, each entry must be a single line
msg = msg.map((line) => `${pad(3)}${line}\n`)
print(msg.join(''))
}
})
parser.on('complete', (result) => {
if (!result.ok) {
let failureSummary = '\n'
failureSummary += `${pad()}${red('Failed tests:')}`
failureSummary += ` There ${result.fail > 1 ? 'were' : 'was'} `
failureSummary += red(result.fail)
failureSummary += ` failure${result.fail > 1 ? 's' : ''}\n\n`
print(failureSummary)
for (const fail of result.failures) {
print(`${pad(2)}${FAIL} ${dim(`${fail.id})`)} ${fail.name}\n`)
}
}
print(`\n${pad()}total: ${result.count}\n`)
if (result.pass > 0) {
print(green(`${pad()}passing: ${result.pass}\n`))
}
if (result.fail > 0) {
print(red(`${pad()}failing: ${result.fail}\n`))
}
if (result.skip > 0) {
print(`${pad()}skipped: ${result.skip}\n`)
}
if (result.todo > 0) {
print(`${pad()}todo: ${result.todo}\n`)
}
if (result.bailout) {
print(`${pad()}${bold(underline(red('BAILED!')))}\n`)
}
tapArc.end(`${dim(`${pad()}${prettyMs(start)}`)}\n\n`)
process.exit(result.ok && result.count > 0 ? 0 : 1)
})
process.stdin
.pipe(parser)
.pipe(tapArc)
.pipe(process.stdout)
process.stdin.pipe(parser)

@@ -5,4 +5,5 @@ {

"author": "tbeseda",
"version": "0.3.5",
"version": "0.3.6-RC.0",
"license": "Apache-2.0",
"type": "module",
"main": "index.js",

@@ -13,3 +14,4 @@ "bin": {

"files": [
"index.js"
"index.js",
"src/"
],

@@ -21,3 +23,3 @@ "repository": {

"engines": {
"node": ">=14"
"node": ">=16"
},

@@ -35,4 +37,2 @@ "keywords": [

"lint:check": "eslint .",
"make-snapshots": "node scripts/make-snapshots.js",
"slow": "npm run --silent tape:slow-pass | ./index.js",
"tap-arc:diff:-v": "npm run --silent tape:diff | ./index.js -v",

@@ -48,30 +48,31 @@ "tap-arc:diff": "npm run --silent tape:diff | ./index.js",

"tap-arc:throws": "npm run --silent tape:throws | ./index.js",
"tape:diff": "tape test/create-diff-tap.js",
"tape:empty": "tape test/create-empty-tap.js",
"tape:mixed": "tape test/create-mixed-tap.js",
"tape:upstream-error": "tape test/create-upstream-error-tap.js",
"tape:passing": "tape test/create-passing-tap.js",
"tape:simple": "tape test/create-simple-tap.js",
"tape:slow-pass": "tape test/create-slow-passing-tap.js",
"tape:throws": "tape test/create-throws-tap.js",
"test": "npm run lint:check && tape test/index.js | ./index.js"
"tape:diff": "tape test/create-diff-tap.cjs",
"tape:empty": "tape test/create-empty-tap.cjs",
"tape:mixed": "tape test/create-mixed-tap.cjs",
"tape:upstream-error": "tape test/create-upstream-error-tap.cjs",
"tape:passing": "tape test/create-passing-tap.cjs",
"tape:simple": "tape test/create-simple-tap.cjs",
"tape:throws": "tape test/create-throws-tap.cjs",
"test": "npm run lint:check && tape test/index.cjs | ./index.js"
},
"dependencies": {
"json5": "^2.2.1",
"minimist": "^1.2.6",
"picocolors": "^1.0.0",
"chalk": "^5.2.0",
"json5": "^2.2.3",
"minimist": "^1.2.8",
"strip-ansi": "6.0.1",
"tap-parser": "^11.0.1",
"tcompare": "^5.0.7",
"through2": "^4.0.2"
"tap-parser": "^13.0.0",
"tcompare": "^6.0.0"
},
"devDependencies": {
"@architect/eslint-config": "^2.0.1",
"@architect/eslint-config": "^2.1.1",
"@types/node": "^18.0.03",
"eslint": "^8.19.0",
"tape": "5.5.3"
"eslint": "^8.39.0",
"tape": "5.6.3"
},
"eslintConfig": {
"extends": "@architect/eslint-config"
"extends": "@architect/eslint-config",
"parserOptions": {
"sourceType": "module"
}
}
}
# `tap-arc`
> A small (~23kB) [TAP](https://testanything.org/) reporter with spec-like output, streaming, and failure diffing.
> A small (~25kB) [TAP](https://testanything.org/) reporter with spec-like output, streaming, and failure diffing.

@@ -8,3 +8,3 @@ ## Objectives

- minimal, informative spec-like output for all assertions
- minimal, maintained dependencies -- can't be shipping React to CI
- minimal, maintained dependencies
- streaming in and out

@@ -17,5 +17,5 @@ - helpful diffing for failures

> Compatible with Node.js 12+.
> Compatible with Node.js 14+.
For a JavaScript project, save `tap-arc` as a development dependency:
Save `tap-arc` as a development dependency:

@@ -68,4 +68,2 @@ ```sh

The entirety of the reporter lives in `./index.js`.
When building `tap-arc`, it's helpful to try various TAP outputs. See `package.json` `"scripts"` for useful "tap-arc:*" commands to test passing and failing TAP.

@@ -77,12 +75,10 @@

### Snapshot tests
### Tests
The main library is snapshot tested (`npm test` loads all snapshots to compare to current output). Create snapshots with the `npm run make-snapshots` commands.
`tap-arc` is tested to output the correct exit code based on your test suite's TAP output. In the process, the boundaries of tap-arc's process are also tested by creating and parsing several types of TAP output.
The snapshots are versioned by Node.js' major version, ie. `node14` and `node16`. But snapshots may vary between minor and patch versions of Node. (Line numbers of Node internals shift, causing changes in stack traces.) GitHub's Actions are set to use the latest Node.js 14.x and 16.x, so when testing and creating snapshots locally, do the same.
Snapshot testing became too arduous to maintain for the sake of aesthetics.
This is also why `tape` is pinned as a development dependency. Update as needed, but recreate snapshots.
Testing could be improved by unit testing the printer and diff maker.
Request: please exclude updated snapshots from commits if the _only_ change is to the duration line. This variance is accounted for in the tests.
## Credit & Inspiration

@@ -89,0 +85,0 @@

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