Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

18
static/js/ketcher2/node_modules/tap-parser/LICENSE generated vendored Normal file
View File

@ -0,0 +1,18 @@
This software is released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

160
static/js/ketcher2/node_modules/tap-parser/bin/cmd.js generated vendored Executable file
View File

@ -0,0 +1,160 @@
#!/usr/bin/env node
var Parser = require('../')
var etoa = require('events-to-array')
var util = require('util')
var args = process.argv.slice(2)
var json = null
var bail = false
var preserveWhitespace = true
var omitVersion = false
function version () {
console.log(require('../package.json').version)
process.exit(0)
}
args.forEach(function (arg, i) {
if (arg === '-j') {
json = args[i + 1] || 2
} else {
var m = arg.match(/^--json(?:=([0-9]+))$/)
if (m)
json = +m[1] || args[i + 1] || 2
}
if (arg === '-v' || arg === '--version')
version()
else if (arg === '-o' || arg === '--omit-version')
omitVersion = true
else if (arg === '-w' || arg === '--ignore-all-whitespace')
preserveWhitespace = false
else if (arg === '-b' || arg === '--bail')
bail = true
else if (arg === '-t' || arg === '--tap')
json = 'tap'
else if (arg === '-l' || arg === '--lines')
json = 'lines'
else if (arg === '-h' || arg === '--help')
usage()
else
console.error('Unrecognized arg: %j', arg)
if (arg === '-v' || arg === '--version')
console.log(require('../package.json').version)
})
function usage () {
console.log(function () {/*
Usage:
tap-parser <options>
Parses TAP data from stdin, and outputs the parsed result
in the format specified by the options. Default output is
uses node's `util.format()` method.
Options:
-j [<indent>] | --json[=indent]
Output event data as JSON with the specified indentation (default=2)
-t | --tap
Output data as reconstituted TAP based on parsed results
-l | --lines
Output each parsed line as it is recognized by the parser
-b | --bail
Emit a `Bail out!` at the first failed test point encountered
-w | --ignore-all-whitespace
Skip over blank lines outside of YAML blocks
-o | --omit-version
Ignore the `TAP version 13` line at the start of tests
*/}.toString().split('\n').slice(1, -1).join('\n'))
if (!process.stdin.isTTY)
process.stdin.resume()
process.exit()
}
var yaml = require('js-yaml')
function tapFormat (msg, indent) {
return indent + msg.map(function (item) {
switch (item[0]) {
case 'child':
var comment = item[1][0]
var child = item[1].slice(1)
return tapFormat([comment], '') + tapFormat(child, ' ')
case 'version':
return 'TAP version ' + item[1] + '\n'
case 'plan':
var p = item[1].start + '..' + item[1].end
if (item[1].comment)
p += ' # ' + item[1].comment
return p + '\n'
case 'pragma':
return 'pragma ' + (item[2] ? '+' : '-') + item[1] + '\n'
case 'bailout':
var r = item[1] === true ? '' : (' ' + item[1])
return 'Bail out!' + r + '\n'
case 'assert':
var res = item[1]
return (res.ok ? '' : 'not ') + 'ok ' + res.id +
(res.name ? ' - ' + res.name.replace(/ \{$/, '') : '') +
(res.skip ? ' # SKIP' +
(res.skip === true ? '' : ' ' + res.skip) : '') +
(res.todo ? ' # TODO' +
(res.todo === true ? '' : ' ' + res.todo) : '') +
(res.time ? ' # time=' + res.time + 'ms' : '') +
'\n' +
(res.diag ?
' ---\n ' +
yaml.safeDump(res.diag).split('\n').join('\n ').trim() +
'\n ...\n'
: '')
case 'extra':
case 'comment':
return item[1]
}
}).join('').split('\n').join('\n' + indent).trim() + '\n'
}
function format (msg) {
if (json === 'tap')
return tapFormat(msg, '')
else if (json !== null)
return JSON.stringify(msg, null, +json)
else
return util.inspect(events, null, Infinity)
}
var options = {
bail: bail,
preserveWhitespace: preserveWhitespace,
omitVersion: omitVersion
}
var parser = new Parser(options)
var events = etoa(parser, [ 'pipe', 'unpipe', 'prefinish', 'finish', 'line' ])
process.stdin.pipe(parser)
if (json === 'lines')
parser.on('line', function (l) {
process.stdout.write(l)
})
else
process.on('exit', function () {
console.log(format(events))
if (!parser.ok)
process.exit(1)
})

View File

@ -0,0 +1,15 @@
usage: tap-parser OPTIONS
Parse TAP from INPUT. If there are any failures, exits with
a non-zero status code.
OPTIONS are:
-i, --input Read from INPUT. Default: stdin.
-o, --output Write to OUTPUT. Default: stdout.
-r, --results Print results as json. Otherwise pass INPUT
through to OUTPUT.
-h, --help Show this help message.
-v, --version Print the current version of tap-parser.

972
static/js/ketcher2/node_modules/tap-parser/index.js generated vendored Normal file
View File

@ -0,0 +1,972 @@
// Transforms a stream of TAP into a stream of result objects
// and string comments. Emits "results" event with summary.
var Writable = require('stream').Writable
/* istanbul ignore if */
if (!Writable) {
try {
Writable = require('readable-stream').Writable
} catch (er) {
throw new Error('Please install "readable-stream" to use this module ' +
'with Node.js v0.8 and before')
}
}
var yaml = require('js-yaml')
var util = require('util')
var assert = require('assert')
util.inherits(Parser, Writable)
module.exports = Parser
// every line outside of a yaml block is one of these things, or
// a comment, or garbage.
var lineTypes = {
testPoint: /^(not )?ok(?: ([0-9]+))?(?:(?: -)?( .*?))?(\{?)\n$/,
pragma: /^pragma ([+-])([a-z]+)\n$/,
bailout: /^bail out!(.*)\n$/i,
version: /^TAP version ([0-9]+)\n$/i,
childVersion: /^( )+TAP version ([0-9]+)\n$/i,
plan: /^([0-9]+)\.\.([0-9]+)(?:\s+(?:#\s*(.*)))?\n$/,
subtest: /^# Subtest(?:: (.*))?\n$/,
subtestIndent: /^ # Subtest(?:: (.*))?\n$/,
comment: /^\s*#.*\n$/
}
var lineTypeNames = Object.keys(lineTypes)
function lineType (line) {
for (var t in lineTypes) {
var match = line.match(lineTypes[t])
if (match)
return [t, match]
}
return null
}
function parseDirective (line) {
if (!line.trim())
return false
line = line.replace(/\{\s*$/, '').trim()
var time = line.match(/^time=((?:[1-9][0-9]*|0)(?:\.[0-9]+)?)(ms|s)$/i)
if (time) {
var n = +time[1]
if (time[2] === 's') {
// JS does weird things with floats. Round it off a bit.
n *= 1000000
n = Math.round(n)
n /= 1000
}
return [ 'time', n ]
}
var type = line.match(/^(todo|skip)\b/i)
if (!type)
return false
return [ type[1].toLowerCase(), line.substr(type[1].length).trim() || true ]
}
function Result (parsed, count) {
var ok = !parsed[1]
var id = +(parsed[2] || count + 1)
var buffered = parsed[4]
this.ok = ok
this.id = id
var rest = parsed[3] || ''
var name
rest = rest.replace(/([^\\]|^)((?:\\\\)*)#/g, '$1\n$2').split('\n')
name = rest.shift()
rest = rest.filter(function (r) { return r.trim() }).join('#')
// now, let's see if there's a directive in there.
var dir = parseDirective(rest.trim())
if (!dir)
name += (rest ? '#' + rest : '') + buffered
else {
// handle buffered subtests with todo/skip on them, like
// ok 1 - bar # todo foo {\n
var dirKey = dir[0]
var dirValue = dir[1]
this[dirKey] = dirValue
}
if (/\{\s*$/.test(name)) {
name = name.replace(/\{\s*$/, '')
buffered = '{'
}
if (buffered === '{')
this.buffered = true
if (name)
this.name = name.trim()
return this
}
function Parser (options, onComplete) {
if (typeof options === 'function') {
onComplete = options
options = {}
}
if (!(this instanceof Parser))
return new Parser(options, onComplete)
options = options || {}
if (onComplete)
this.on('complete', onComplete)
this.comments = []
this.results = null
this.braceLevel = null
this.parent = options.parent || null
this.failures = []
if (options.passes)
this.passes = []
this.level = options.level || 0
Writable.call(this)
this.buffer = ''
this.bail = !!options.bail
this.bailingOut = false
this.bailedOut = false
this.syntheticBailout = false
this.syntheticPlan = false
this.omitVersion = !!options.omitVersion
this.planStart = -1
this.planEnd = -1
this.planComment = ''
this.yamlish = ''
this.yind = ''
this.child = null
this.current = null
this.maybeSubtest = null
this.extraQueue = []
this.buffered = options.buffered || null
this.aborted = false
this.preserveWhitespace = options.preserveWhitespace || false
this.count = 0
this.pass = 0
this.fail = 0
this.todo = 0
this.skip = 0
this.ok = true
this.strict = options.strict || false
this.pragmas = { strict: this.strict }
this.postPlan = false
}
Parser.prototype.tapError = function (error, line) {
if (line)
this.emit('line', line)
this.ok = false
this.fail ++
if (typeof error === 'string') {
error = {
tapError: error
}
}
this.failures.push(error)
}
Parser.prototype.parseTestPoint = function (testPoint, line) {
this.emitResult()
if (this.bailedOut)
return
this.emit('line', line)
var res = new Result(testPoint, this.count)
if (this.planStart !== -1) {
var lessThanStart = +res.id < this.planStart
var greaterThanEnd = +res.id > this.planEnd
if (lessThanStart || greaterThanEnd) {
if (lessThanStart)
res.tapError = 'id less than plan start'
else
res.tapError = 'id greater than plan end'
res.plan = { start: this.planStart, end: this.planEnd }
this.tapError(res)
}
}
if (res.id) {
if (!this.first || res.id < this.first)
this.first = res.id
if (!this.last || res.id > this.last)
this.last = res.id
}
if (!res.skip && !res.todo)
this.ok = this.ok && res.ok
// hold onto it, because we might get yamlish diagnostics
this.current = res
}
Parser.prototype.nonTap = function (data, didLine) {
if (this.bailingOut && /^( {4})*\}\n$/.test(data))
return
if (this.strict) {
var err = {
tapError: 'Non-TAP data encountered in strict mode',
data: data
}
this.tapError(err)
if (this.parent)
this.parent.tapError(err)
}
// emit each line, then the extra as a whole
if (!didLine)
data.split('\n').slice(0, -1).forEach(function (line) {
line += '\n'
if (this.current || this.extraQueue.length)
this.extraQueue.push(['line', line])
else
this.emit('line', line)
}, this)
if (this.current || this.extraQueue.length)
this.extraQueue.push(['extra', data])
else
this.emit('extra', data)
}
Parser.prototype.plan = function (start, end, comment, line) {
// not allowed to have more than one plan
if (this.planStart !== -1) {
this.nonTap(line)
return
}
// can't put a plan in a child.
if (this.child || this.yind) {
this.nonTap(line)
return
}
this.emitResult()
if (this.bailedOut)
return
// 1..0 is a special case. Otherwise, end must be >= start
if (end < start && end !== 0 && start !== 1) {
if (this.strict)
this.tapError({
tapError: 'plan end cannot be less than plan start',
plan: {
start: start,
end: end
}
}, line)
else
this.nonTap(line)
return
}
this.planStart = start
this.planEnd = end
var p = { start: start, end: end }
if (comment)
this.planComment = p.comment = comment
// This means that the plan is coming at the END of all the tests
// Plans MUST be either at the beginning or the very end. We treat
// plans like '1..0' the same, since they indicate that no tests
// will be coming.
if (this.count !== 0 || this.planEnd === 0)
this.postPlan = true
this.emit('line', line)
this.emit('plan', p)
}
Parser.prototype.resetYamlish = function () {
this.yind = ''
this.yamlish = ''
}
// that moment when you realize it's not what you thought it was
Parser.prototype.yamlGarbage = function () {
var yamlGarbage = this.yind + '---\n' + this.yamlish
this.emitResult()
if (this.bailedOut)
return
this.nonTap(yamlGarbage, true)
}
Parser.prototype.yamlishLine = function (line) {
if (line === this.yind + '...\n') {
// end the yaml block
this.processYamlish()
} else {
this.yamlish += line
}
}
Parser.prototype.processYamlish = function () {
var yamlish = this.yamlish
this.resetYamlish()
try {
var diags = yaml.safeLoad(yamlish)
} catch (er) {
this.nonTap(this.yind + '---\n' + yamlish + this.yind + '...\n', true)
return
}
this.current.diag = diags
// we still don't emit the result here yet, to support diags
// that come ahead of buffered subtests.
}
Parser.prototype.write = function (chunk, encoding, cb) {
if (this.aborted)
return
if (typeof encoding === 'string' && encoding !== 'utf8')
chunk = new Buffer(chunk, encoding)
if (Buffer.isBuffer(chunk))
chunk += ''
if (typeof encoding === 'function') {
cb = encoding
encoding = null
}
this.buffer += chunk
do {
var match = this.buffer.match(/^.*\r?\n/)
if (!match)
break
this.buffer = this.buffer.substr(match[0].length)
this.parse(match[0])
} while (this.buffer.length)
if (cb)
process.nextTick(cb)
return true
}
Parser.prototype.end = function (chunk, encoding, cb) {
if (chunk) {
if (typeof encoding === 'function') {
cb = encoding
encoding = null
}
this.write(chunk, encoding)
}
if (this.buffer)
this.write('\n')
// if we have yamlish, means we didn't finish with a ...
if (this.yamlish)
this.yamlGarbage()
this.emitResult()
if (this.syntheticBailout && this.level === 0) {
var reason = this.bailedOut
if (reason === true)
reason = ''
else
reason = ' ' + reason
this.emit('line', 'Bail out!' + reason + '\n')
}
var skipAll
if (this.planEnd === 0 && this.planStart === 1) {
skipAll = true
if (this.count === 0) {
this.ok = true
} else {
this.tapError('Plan of 1..0, but test points encountered')
}
} else if (!this.bailedOut && this.planStart === -1) {
if (this.count === 0 && !this.syntheticPlan) {
this.syntheticPlan = true
this.plan(1, 0, 'no tests found', '1..0 # no tests found\n')
skipAll = true
} else {
this.tapError('no plan')
}
} else if (this.ok && this.count !== (this.planEnd - this.planStart + 1)) {
this.tapError('incorrect number of tests')
}
if (this.ok && !skipAll && this.first !== this.planStart) {
this.tapError('first test id does not match plan start')
}
if (this.ok && !skipAll && this.last !== this.planEnd) {
this.tapError('last test id does not match plan end')
}
Writable.prototype.end.call(this, null, null, cb)
this.emitComplete(skipAll)
}
Parser.prototype.emitComplete = function (skipAll) {
if (!this.results) {
var res = this.results = new FinalResults(!!skipAll, this)
if (!res.bailout) {
// comment a bit at the end so we know what happened.
// but don't repeat these comments if they're already present.
if (res.plan.end !== res.count)
this.emitComment('test count(' + res.count +
') != plan(' + res.plan.end + ')', false, true)
if (res.fail > 0 && !res.ok)
this.emitComment('failed ' + res.fail +
(res.count > 1 ? ' of ' + res.count + ' tests'
: ' test'),
false, true)
if (res.todo > 0)
this.emitComment('todo: ' + res.todo, false, true)
if (res.skip > 0)
this.emitComment('skip: ' + res.skip, false, true)
}
this.emit('complete', this.results)
}
}
function FinalResults (skipAll, self) {
this.ok = self.ok
this.count = self.count
this.pass = self.pass
this.fail = self.fail || 0
this.bailout = self.bailedOut || false
this.todo = self.todo || 0
this.skip = skipAll ? self.count : self.skip || 0
this.plan = new FinalPlan(skipAll, self)
this.failures = self.failures
if (self.passes)
this.passes = self.passes
}
function FinalPlan (skipAll, self) {
this.start = self.planStart === -1 ? null : self.planStart
this.end = self.planStart === -1 ? null : self.planEnd
this.skipAll = skipAll
this.skipReason = skipAll ? self.planComment : ''
this.comment = self.planComment || ''
}
Parser.prototype.version = function (version, line) {
// If version is specified, must be at the very beginning.
if (version >= 13 &&
this.planStart === -1 &&
this.count === 0 &&
!this.current) {
this.emit('line', line)
this.emit('version', version)
} else
this.nonTap(line)
}
Parser.prototype.pragma = function (key, value, line) {
// can't put a pragma in a child or yaml block
if (this.child) {
this.nonTap(line)
return
}
this.emitResult()
if (this.bailedOut)
return
// only the 'strict' pragma is currently relevant
if (key === 'strict') {
this.strict = value
}
this.pragmas[key] = value
this.emit('line', line)
this.emit('pragma', key, value)
}
Parser.prototype.bailout = function (reason, synthetic) {
this.syntheticBailout = synthetic
if (this.bailingOut)
return
// Guard because emitting a result can trigger a forced bailout
// if the harness decides that failures should be bailouts.
this.bailingOut = reason || true
if (!synthetic)
this.emitResult()
else
this.current = null
this.bailedOut = this.bailingOut
this.ok = false
if (!synthetic) {
// synthetic bailouts get emitted on end
var line = 'Bail out!'
if (reason)
line += ' ' + reason
this.emit('line', line + '\n')
}
this.emit('bailout', reason)
if (this.parent) {
this.end()
this.parent.bailout(reason, true)
}
}
Parser.prototype.clearExtraQueue = function () {
for (var c = 0; c < this.extraQueue.length; c++) {
this.emit(this.extraQueue[c][0], this.extraQueue[c][1])
}
this.extraQueue.length = 0
}
Parser.prototype.endChild = function () {
if (this.child) {
this.child.end()
this.child = null
}
}
Parser.prototype.emitResult = function () {
if (this.bailedOut)
return
this.endChild()
this.resetYamlish()
if (!this.current)
return this.clearExtraQueue()
var res = this.current
this.current = null
this.count++
if (res.ok) {
this.pass++
if (this.passes)
this.passes.push(res)
} else {
this.fail++
if (!res.todo && !res.skip) {
this.ok = false
this.failures.push(res)
}
}
if (res.skip)
this.skip++
if (res.todo)
this.todo++
this.emit('assert', res)
if (this.bail && !res.ok && !res.todo && !res.skip && !this.bailingOut) {
this.maybeChild = null
var ind = new Array(this.level + 1).join(' ')
for (var p = this; p.parent; p = p.parent);
var bailName = res.name ? ' # ' + res.name : ''
p.parse(ind + 'Bail out!' + bailName + '\n')
}
this.clearExtraQueue()
}
// TODO: We COULD say that any "relevant tap" line that's indented
// by 4 spaces starts a child test, and just call it 'unnamed' if
// it does not have a prefix comment. In that case, any number of
// 4-space indents can be plucked off to try to find a relevant
// TAP line type, and if so, start the unnamed child.
Parser.prototype.startChild = function (line) {
var maybeBuffered = this.current && this.current.buffered
var unindentStream = !maybeBuffered && this.maybeChild
var indentStream = !maybeBuffered && !unindentStream &&
lineTypes.subtestIndent.test(line)
var unnamed = !maybeBuffered && !unindentStream && !indentStream
// If we have any other result waiting in the wings, we need to emit
// that now. A buffered test emits its test point at the *end* of
// the child subtest block, so as to match streamed test semantics.
if (!maybeBuffered)
this.emitResult()
if (this.bailedOut)
return
this.child = new Parser({
bail: this.bail,
parent: this,
level: this.level + 1,
buffered: maybeBuffered,
preserveWhitespace: this.preserveWhitespace,
omitVersion: true,
strict: this.strict
})
var self = this
this.child.on('complete', function (results) {
if (!results.ok)
self.ok = false
})
this.child.on('line', function (l) {
if (this.syntheticPlan)
return
if (l.trim() || self.preserveWhitespace)
l = ' ' + l
self.emit('line', l)
})
// Canonicalize the parsing result of any kind of subtest
// if it's a buffered subtest or a non-indented Subtest directive,
// then synthetically emit the Subtest comment
line = line.substr(4)
var subtestComment
if (indentStream) {
subtestComment = line
line = null
} else if (maybeBuffered) {
subtestComment = '# Subtest: ' + this.current.name + '\n'
} else {
subtestComment = this.maybeChild || '# Subtest: (anonymous)\n'
}
this.maybeChild = null
this.child.name = subtestComment.substr('# Subtest: '.length).trim()
// at some point, we may wish to move 100% to preferring
// the Subtest comment on the parent level. If so, uncomment
// this line, and remove the child.emitComment below.
// this.emit('comment', subtestComment)
if (!this.child.buffered)
this.emit('line', subtestComment)
this.emit('child', this.child)
this.child.emitComment(subtestComment, true)
if (line)
this.child.parse(line)
}
Parser.prototype.abort = function (message, extra) {
if (this.child) {
var b = this.child.buffered
this.child.abort(message, extra)
extra = null
if (b)
this.write('\n}\n')
}
var dump
if (extra && Object.keys(extra).length) {
try {
dump = yaml.safeDump(extra).trimRight()
} catch (er) {}
}
var y
if (dump)
y = ' ---\n ' + dump.split('\n').join('\n ') + '\n ...\n'
else
y = '\n'
var n = (this.count || 0) + 1
if (this.current)
n += 1
if (this.planEnd !== -1 && this.planEnd < n && this.parent) {
// skip it, let the parent do this.
this.aborted = true
return
}
var ind = '' // new Array(this.level + 1).join(' ')
message = message.replace(/[\n\r\s\t]/g, ' ')
var point = '\nnot ok ' + n + ' - ' + message + '\n' + y
if (this.planEnd === -1)
point += '1..' + n + '\n'
this.write(point)
this.aborted = true
this.end()
}
Parser.prototype.emitComment = function (line, skipLine, noDuplicate) {
if (line.trim().charAt(0) !== '#')
line = '# ' + line
if (line.slice(-1) !== '\n')
line += '\n'
if (noDuplicate && this.comments.indexOf(line) !== -1)
return
this.comments.push(line)
if (this.current || this.extraQueue.length) {
// no way to get here with skipLine being true
this.extraQueue.push(['line', line])
this.extraQueue.push(['comment', line])
} else {
if (!skipLine)
this.emit('line', line)
this.emit('comment', line)
}
}
Parser.prototype.parse = function (line) {
// normalize line endings
line = line.replace(/\r\n$/, '\n')
// sometimes empty lines get trimmed, but are still part of
// a subtest or a yaml block. Otherwise, nothing to parse!
if (line === '\n') {
if (this.child)
line = ' ' + line
else if (this.yind)
line = this.yind + line
}
// If we're bailing out, then the only thing we want to see is the
// end of a buffered child test. Anything else should be ignored.
// But! if we're bailing out a nested child, and ANOTHER nested child
// comes after that one, then we don't want the second child's } to
// also show up, or it looks weird.
if (this.bailingOut) {
if (!/^\s*}\n$/.test(line))
return
else if (!this.braceLevel || line.length < this.braceLevel)
this.braceLevel = line.length
else
return
}
// This allows omitting even parsing the version if the test is
// an indented child test. Several parsers get upset when they
// see an indented version field.
if (this.omitVersion && lineTypes.version.test(line) && !this.yind)
return
// check to see if the line is indented.
// if it is, then it's either a subtest, yaml, or garbage.
var indent = line.match(/^[ \t]*/)[0]
if (indent) {
this.parseIndent(line, indent)
return
}
// In any case where we're going to emitResult, that can trigger
// a bailout, so we need to only emit the line once we know that
// isn't happening, to prevent cases where there's a bailout, and
// then one more line of output. That'll also prevent the case
// where the test point is emitted AFTER the line that follows it.
// buffered subtests must end with a }
if (this.child && this.child.buffered && line === '}\n') {
this.endChild()
this.emit('line', line)
this.emitResult()
return
}
// just a \n, emit only if we care about whitespace
var validLine = this.preserveWhitespace || line.trim() || this.yind
if (line === '\n')
return validLine && this.emit('line', line)
// buffered subtest with diagnostics
if (this.current && line === '{\n' &&
!this.current.buffered &&
!this.child) {
this.emit('line', line)
this.current.buffered = true
return
}
// now we know it's not indented, so if it's either valid tap
// or garbage. Get the type of line.
var type = lineType(line)
if (!type) {
this.nonTap(line)
return
}
if (type[0] === 'comment') {
this.emitComment(line)
return
}
// if we have any yamlish, it's garbage now. We tolerate non-TAP and
// comments in the midst of yaml (though, perhaps, that's questionable
// behavior), but any actual TAP means that the yaml block was just
// not valid.
if (this.yind)
this.yamlGarbage()
// If it's anything other than a comment or garbage, then any
// maybeChild is just an unsatisfied promise.
if (this.maybeChild) {
this.emitComment(this.maybeChild)
this.maybeChild = null
}
// nothing but comments can come after a trailing plan
if (this.postPlan) {
this.nonTap(line)
return
}
// ok, now it's maybe a thing
if (type[0] === 'bailout') {
this.bailout(type[1][1].trim(), false)
return
}
if (type[0] === 'pragma') {
var pragma = type[1]
this.pragma(pragma[2], pragma[1] === '+', line)
return
}
if (type[0] === 'version') {
var version = type[1]
this.version(parseInt(version[1], 10), line)
return
}
if (type[0] === 'plan') {
var plan = type[1]
this.plan(+plan[1], +plan[2], (plan[3] || '').trim(), line)
return
}
// streamed subtests will end when this test point is emitted
if (type[0] === 'testPoint') {
// note: it's weird, but possible, to have a testpoint ending in
// { before a streamed subtest which ends with a test point
// instead of a }. In this case, the parser gets confused, but
// also, even beginning to handle that means doing a much more
// involved multi-line parse. By that point, the subtest block
// has already been emitted as a 'child' event, so it's too late
// to really do the optimal thing. The only way around would be
// to buffer up everything and do a multi-line parse. This is
// rare and weird, and a multi-line parse would be a bigger
// rewrite, so I'm allowing it as it currently is.
this.parseTestPoint(type[1], line)
return
}
// We already detected nontap up above, so the only case left
// should be a `# Subtest:` comment. Ignore for coverage, but
// include the error here just for good measure.
/* istanbul ignore else */
if (type[0] === 'subtest') {
// this is potentially a subtest. Not indented.
// hold until later.
this.maybeChild = line
} else {
throw new Error('Unhandled case: ' + type[0])
}
}
Parser.prototype.parseIndent = function (line, indent) {
// still belongs to the child, so pass it along.
if (this.child && line.substr(0, 4) === ' ') {
line = line.substr(4)
this.child.write(line)
return
}
// one of:
// - continuing yaml block
// - starting yaml block
// - ending yaml block
// - body of a new child subtest that was previously introduced
// - An indented subtest directive
// - A comment, or garbage
// continuing/ending yaml block
if (this.yind) {
if (line.indexOf(this.yind) === 0) {
this.emit('line', line)
this.yamlishLine(line)
return
} else {
// oops! that was not actually yamlish, I guess.
// this is a case where the indent is shortened mid-yamlish block
// treat existing yaml as garbage, continue parsing this line
this.yamlGarbage()
}
}
// start a yaml block under a test point
if (this.current && !this.yind && line === indent + '---\n') {
this.yind = indent
this.emit('line', line)
return
}
// at this point, not yamlish, and not an existing child test.
// We may have already seen an unindented Subtest directive, or
// a test point that ended in { indicating a buffered subtest
// Child tests are always indented 4 spaces.
if (line.substr(0, 4) === ' ') {
if (this.maybeChild ||
this.current && this.current.buffered ||
lineTypes.subtestIndent.test(line)) {
this.startChild(line)
return
}
// It's _something_ indented, if the indentation is divisible by
// 4 spaces, and the result is actual TAP of some sort, then do
// a child subtest for it as well.
//
// This will lead to some ambiguity in cases where there are multiple
// levels of non-signaled subtests, but a Subtest comment in the
// middle of them, which may or may not be considered "indented"
// See the subtest-no-comment-mid-comment fixture for an example
// of this. As it happens, the preference is towards an indented
// Subtest comment as the interpretation, which is the only possible
// way to resolve this, since otherwise there's no way to distinguish
// between an anonymous subtest with a non-indented Subtest comment,
// and an indented Subtest comment.
var s = line.match(/( {4})+(.*\n)$/)
if (s[2].charAt(0) !== ' ') {
// integer number of indentations.
var type = lineType(s[2])
if (type) {
if (type[0] === 'comment') {
this.emit('line', line)
this.emitComment(line)
} else {
// it's relevant! start as an "unnamed" child subtest
this.startChild(line)
}
return
}
}
}
// at this point, it's either a non-subtest comment, or garbage.
if (lineTypes.comment.test(line)) {
this.emitComment(line)
return
}
this.nonTap(line)
}

View File

@ -0,0 +1,90 @@
{
"_from": "tap-parser@^5.3.1",
"_id": "tap-parser@5.4.0",
"_inBundle": false,
"_integrity": "sha512-BIsIaGqv7uTQgTW1KLTMNPSEQf4zDDPgYOBRdgOfuB+JFOLRBfEu6cLa/KvMvmqggu1FKXDfitjLwsq4827RvA==",
"_location": "/tap-parser",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "tap-parser@^5.3.1",
"name": "tap-parser",
"escapedName": "tap-parser",
"rawSpec": "^5.3.1",
"saveSpec": null,
"fetchSpec": "^5.3.1"
},
"_requiredBy": [
"/tap",
"/tap-mocha-reporter"
],
"_resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-5.4.0.tgz",
"_shasum": "6907e89725d7b7fa6ae41ee2c464c3db43188aec",
"_spec": "tap-parser@^5.3.1",
"_where": "/home/manfred/enviPath/ketcher2/ketcher/node_modules/tap",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"bin": {
"tap-parser": "bin/cmd.js"
},
"bugs": {
"url": "https://github.com/substack/tap-parser/issues"
},
"bundleDependencies": false,
"dependencies": {
"events-to-array": "^1.0.1",
"js-yaml": "^3.2.7",
"readable-stream": "^2"
},
"deprecated": false,
"description": "parse the test anything protocol",
"devDependencies": {
"glob": "^7.0.5",
"tap": "^10.3.3"
},
"files": [
"index.js",
"bin/cmd.js",
"bin/usage.txt"
],
"homepage": "https://github.com/substack/tap-parser",
"keywords": [
"tap",
"test",
"parser"
],
"license": "MIT",
"main": "index.js",
"name": "tap-parser",
"optionalDependencies": {
"readable-stream": "^2"
},
"repository": {
"type": "git",
"url": "git://github.com/substack/tap-parser.git"
},
"scripts": {
"postpublish": "git push origin --all; git push origin --tags",
"postversion": "npm publish",
"preversion": "npm test",
"regen-fixtures": "node scripts/generate-test.js test/fixtures/*.tap",
"test": "tap test/*.js --100 -J"
},
"testling": {
"files": "test/*.js",
"browsers": [
"ie/6..latest",
"chrome/10",
"chrome/latest",
"firefox/3.5",
"firefox/latest",
"opera/latest",
"safari/latest"
]
},
"version": "5.4.0"
}

View File

@ -0,0 +1,329 @@
# tap-parser
parse the [test anything protocol](http://testanything.org/)
[![build status](https://secure.travis-ci.org/tapjs/tap-parser.png)](http://travis-ci.org/tapjs/tap-parser)
[![browser support](http://ci.testling.com/substack/tap-parser.png)](http://ci.testling.com/substack/tap-parser)
[![coverage status](https://coveralls.io/repos/tapjs/tap-parser/badge.svg?branch=master&service=github)](https://coveralls.io/github/tapjs/tap-parser?branch=master)
# example
``` js
var parser = require('tap-parser');
var p = parser(function (results) {
console.dir(results);
});
process.stdin.pipe(p);
```
given some [TAP](http://testanything.org/)-formatted input:
```
$ node test.js
TAP version 13
# beep
ok 1 should be equal
ok 2 should be equivalent
# boop
ok 3 should be equal
ok 4 (unnamed assert)
1..4
# tests 4
# pass 4
# ok
```
parse the output:
```
$ node test.js | node parse.js
{ ok: true, count: 4, pass: 4, plan: { start: 1, end: 4 } }
```
# usage
This package also has a `tap-parser` command.
```
Usage:
tap-parser <options>
Parses TAP data from stdin, and outputs the parsed result
in the format specified by the options. Default output is
uses node's `util.format()` method.
Options:
-j [<indent>] | --json[=indent]
Output event data as JSON with the specified indentation (default=2)
-t | --tap
Output data as reconstituted TAP based on parsed results
-l | --lines
Output each parsed line as it is recognized by the parser
-b | --bail
Emit a `Bail out!` at the first failed test point encountered
-w | --ignore-all-whitespace
Skip over blank lines outside of YAML blocks
-o | --omit-version
Ignore the `TAP version 13` line at the start of tests
```
# methods
``` js
var parser = require('tap-parser')
```
## var p = parser(options, cb)
Return a writable stream `p` that emits parse events.
If `cb` is given it will listen for the `'complete'` event.
If `options` is given, it may contain the following flags:
- `preserveWhitespace` boolean which is `false` by default and will
cause the parser to emit `line` events even for lines containing
only whitespace. (Whitespace lines in yaml blocks are always
emitted, because whitespace is semantically relevant for yaml.)
- `strict` boolean which is `false` by default and causes the parser
to treat non-TAP input as a failure. Strictness is heritable to
child subtests. You can also turn strictness on or off by using the
`pragma +strict` line in the TAP data to turn strictness on, or
`pragma -strict` to turn strictness off.
- `bail` boolean which is `false` by default and will cause the parser
to bail out (including emitting a synthetic `Bail out!` line)
whenever a failed test point is encountered.
- `omitVersion` boolean which is `false` by default and will cause the
parser to ignore `TAP version 13` lines. Version lines in subtests
cause problems with some parsers, so they are always ignored.
- `passes` boolean which is false by default and will add "passes" property for that contains the result of all passed tests
The `parent`, `level` and `buffered` options are reserved for internal
use.
# events
## p.on('complete', function (results) {})
The `results` object contains a summary of the number of tests
skipped, failed, passed, etc., as well as a boolean `ok` member which
is true if and only if the planned test were all found, and either
"ok" or marked as "TODO".
## p.on('line', function (line) {})
As each line of input is parsed, a `line` event is emitted.
"Synthetic" line events will be emitted to support the `bail`
behavior, and to inject `1..0` plan lines in subtests that have no
test points. They can be used as a sort of "passthrough stream" to
sanitize and filter a TAP stream, with the caveat that, while `line`
events will be semantically equivalent to the TAP input, they will not
be a perfect replica of the input.
## p.on('assert', function (assert) {})
Every `/^(not )?ok\b/` line will emit an `'assert'` event.
Every `assert` object has these keys:
* `assert.ok` - true if the assertion succeeded, false if failed
* `assert.id` - the assertion number
* `assert.name` - optional short description of the assertion
and may also have
* `assert.todo` - optional description of why the assertion failure is
not a problem. (Boolean `true` if no explaination provided)
* `assert.skip` - optional description of why this assertion was
skipped (boolean `true` if no explanation provided)
* `assert.diag` - a diagnostic object with additional information
about the test point.
## p.on('comment', function (comment) {})
Every `/^# (.+)/` line will emit the string contents of `comment`.
## p.on('plan', function (plan) {})
Every `/^\d+\.\.\d+/` line emits a `'plan'` event for the test numbers
`plan.start` through `plan.end`, inclusive.
If the test is [completely
skipped](http://podwiki.hexten.net/TAP/TAP.html?page=TAP#Skippingeverything)
the result will look like
```
{ ok: true,
count: 0,
pass: 0,
plan:
{ start: 1,
end: 0,
skipAll: true,
skipReason: 'This code has no seat belt' } }
```
## p.on('version', function (version) {})
A `/^TAP version (\d+)/` line emits a `'version'` event with a version
number or string.
## p.on('bailout', function (reason) {})
A `bail out!` line will cause the parser to completely stop doing
anything. Child parser bailouts will bail out their parents as well.
## p.on('child', function (childParser) {})
If a child test set is embedded in the stream like this:
```
TAP Version 13
1..2
# nesting
1..2
ok 1 - true is ok
ok 2 - doag is also okay
ok 1 - nesting
ok 2 - second
```
then the child stream will be parsed and events will be raised on the
`childParser` object.
Since TAP streams with child tests *must* follow child test sets
with a pass or fail assert based on the child test's results, failing
to handle child tests should always result in the same end result.
However, additional information from those child tests will obviously
be lost.
See `Subtests` below for more information on which sorts of subtest
formats are supported by this parser.
## p.on('extra', function (extra) {})
All other lines will trigger an `'extra'` event with the line text.
# install
With [npm](https://npmjs.org) do:
```
npm install tap-parser
```
You can use [browserify](http://browserify.org) to `require('tap-parser')` in
the browser.
# license
MIT
# subtests
5 flavors of Subtests are suppored by this parser.
1. Unadorned.
Indented TAP data with no comment, followed by a test
point at the parent level.
```
ok 1
1..1
ok 1 - child test
1..1
```
2. Indented comment.
An indented `# Subtest: <name>` comment, followed by indented TAP
data, and then a not-indented test point with a matching name.
The summary test point may have yaml diagnostics.
```
# Subtest: child test
ok 1
1..1
ok 1 - child test
1..1
```
3. Unindented comment.
A not-indented `# Subtest: <name>` comment, followed by indented TAP
content, followed by a test point with a matching name.
The summary test point may have yaml diagnostics.
```
# Subtest: child test
ok 1
1..1
ok 1 - child test
1..1
```
4. Buffered, without diagnostics.
A test point line ending in {, followed by indented TAP content, ended
with a } to close the block. todo/skip directives may come *either*
before or after the `{` character. Yaml diagnostics are not allowed.
```
ok 1 - child test {
ok 1
1..1
}
1..1
```
5. Buffered, with diagnostics.
A test point line with yaml diagnostics, followed by `{` alone on a
line, indented TAP data, and then a `}`.
```
ok 1 - child test
---
some: diagnostic
data: true
...
{
ok 1
1..1
}
```
In all cases, the parsed behavior is identical:
1. The parent emits a `child` event with the `childParser` as an
argument.
2. The `childParser` emits a `comment` with `# Subtest: <name>` (or
`(anonymous)` for Unadorned subtests.)
3. When the test is over, the closing test point is emitted on parent
test.
That is, buffered and nonindented/indented comment subtests are parsed
as if they are identical input, since their semantics are the same. This
simplifies implementation of test harness and reporter modules.
Since unadorned subtests have no introduction, a child event is not
emitted until the first "relevant tap" line is encountered. This can
cause confusion if the test output contains a spurious " 1..2" line
or something, but such cases are rare.
Similarly, this means that a test point ending in `{` needs to wait to
emit *either* the 'assert' or 'child' events until an indented line is
encountered. *Any* test point with yaml diagnostics needs to wait to
see if it will be followed by a `{` indicating a subtest.