diff --git a/bin/doctest b/bin/doctest index 4e1a7ba..59e0475 100755 --- a/bin/doctest +++ b/bin/doctest @@ -13,6 +13,7 @@ require ('child_process') process.execPath, ['--experimental-import-meta-resolve', '--experimental-vm-modules', + '--no-warnings', ...(flags ? args[idx + 1].split (/\s+/) : []), '--', path.resolve (__dirname, '..', 'lib', 'command.js'), diff --git a/lib/Effect.js b/lib/Effect.js index 86c8fa9..616c96d 100644 --- a/lib/Effect.js +++ b/lib/Effect.js @@ -1,27 +1,30 @@ +import {Result} from './Output.js'; + + // Failure :: Any -> Effect const Failure = exception => ({ tag: 'Failure', exception, }); -// Success :: Any -> Effect -const Success = value => ({ +// Success :: Output -> Effect +const Success = output => ({ tag: 'Success', - value, + output, }); -// effect :: (Any -> a) -> (Any -> a) -> Effect -> a +// effect :: (Any -> a) -> (Output -> a) -> Effect -> a const effect = failure => success => effect => { switch (effect.tag) { case 'Failure': return failure (effect.exception); - case 'Success': return success (effect.value); + case 'Success': return success (effect.output); } }; // encase :: AnyFunction -> ...Any -> Effect const encase = f => (...args) => { try { - return Success (f (...args)); + return Success (Result (f (...args))); } catch (exception) { return Failure (exception); } diff --git a/lib/Output.js b/lib/Output.js new file mode 100644 index 0000000..1b91c2a --- /dev/null +++ b/lib/Output.js @@ -0,0 +1,8 @@ +// Channel :: String -> Any -> Output +const Channel = channel => value => ({channel, value}); + +// Result :: Any -> Output +const Result = Channel (null); + + +export {Channel, Result}; diff --git a/lib/command.js b/lib/command.js index 608525b..29d47ee 100644 --- a/lib/command.js +++ b/lib/command.js @@ -6,10 +6,25 @@ import doctest from './doctest.js'; import program from './program.js'; +const bold = text => '\u001B[1m' + text + '\u001B[22m'; +const invert = text => '\u001B[7m' + text + '\u001B[0m'; +const blue = text => '\u001B[34m' + text + '\u001B[0m'; +const purple = text => '\u001B[35m' + text + '\u001B[0m'; + +// formatLine :: (String -> String) -> Array Line -> String +const formatLine = colour => line => `${ + invert (` ${`${line.number}`.padStart (4)} `) +} ${ + colour (line.text) +}`; + // formatEffect :: Effect -> String const formatEffect = ( - effect (x => `! ${x}`) - (show) + effect (e => `${bold ('throw')} ${show (e)}`) + (output => + output.channel == null + ? show (output.value) + : `${bold (output.channel)} (${show (output.value)})`) ); (async () => { @@ -27,25 +42,27 @@ const formatEffect = ( process.exit (0); } - const messages = output.map (test => + const failures = output.flatMap (test => comparison (actual => expected => [ - `FAIL: expected ${ - formatEffect (expected) - } on line ${ - test.lines.output[0].number - } (got ${ - formatEffect (actual) - })\n`, + [[''], + test.lines.input.map (formatLine (blue)), + test.lines.output.map (formatLine (purple)), + [''], + actual.map (a => blue (formatEffect (a))), + expected.map (e => purple (formatEffect (e))), + ['']] + .flat () + .join ('\n'), ]) (_ => []) (test.comparison) ); - const failures = messages.flat (); if (!program.silent) { - process.stdout.write (`running doctests in ${path}... -${(messages.map (m => m.length === 0 ? '.' : 'x')).join ('')} -${failures.join ('')}`); + process.stdout.write (` +Running doctests in ${bold (path)}... +${failures.join ('')} +`); } process.exit (failures.length > 0 ? 1 : 0); diff --git a/lib/doctest.js b/lib/doctest.js index 6431c7b..26ae981 100644 --- a/lib/doctest.js +++ b/lib/doctest.js @@ -19,91 +19,125 @@ import show from 'sanctuary-show'; import Z from 'sanctuary-type-classes'; import {Incorrect, Correct} from './Comparison.js'; -import {Failure, Success, effect, encase} from './Effect.js'; +import {Success, effect, encase} from './Effect.js'; import {Line} from './Line.js'; +import {Channel} from './Output.js'; import require from './require.js'; -// indentN :: Integer -> String -> String -const indentN = n => s => s.replace (/^(?!$)/gm, ' '.repeat (n)); - -// formatLines :: Integer -> NonEmpty (Array Line) -> String -const formatLines = indent => lines => ( - lines - .map (line => `{number: ${show (line.number)}, text: ${show (line.text)}},`) - .join ('\n' + ' '.repeat (indent)) -); - -// formatInput :: Integer -> NonEmpty (Array Line) -> String -const formatInput = indent => lines => ( - lines - .map (line => line.text.replace (/^\s*([>]|[.]+)[ ]?/, '')) - .join ('\n' + ' '.repeat (indent)) -); - -// formatOutput :: Integer -> NonEmpty (Array Line) -> String -const formatOutput = indent => lines => { - const [head, ...tail] = lines.map (line => line.text.replace (/^\s*/, '')); - const match = /^![ ]?([^:]*)(?::[ ]?(.*))?$/.exec (head); - return [ - `${head.startsWith ('!') ? 'throw' : 'return'} (`, - ` ${match == null ? head : `new ${match[1]}(${show (match[2] ?? '')})`}`, - ...(tail.map (text => ' ' + text.replace (/^[.]+[ ]?/, ''))), - ')', - ].join ('\n' + ' '.repeat (indent)); -}; +const wrapJs = sourceType => logFunctions => ({input, outputs}) => { + let source = ''; + for (const {text} of input.lines) { + source += text.replace (/^\s*([>]|[.]+)[ ]?/, '') + '\n'; + } -const wrapJs = sourceType => test => { - const source = formatInput (0) (test.input.lines); - const ast = acorn.parse ( - source.startsWith ('{') && source.endsWith ('}') ? `(${source})` : source, - {ecmaVersion: 2023, sourceType} - ); - const {type} = ast.body[0]; - if (type !== 'ExpressionStatement') return source; + if ( + acorn.parse ( + source.replace (/^[{].*[}]\n$/s, '($&)'), + {ecmaVersion: 2023, sourceType} + ) + .body[0].type !== 'ExpressionStatement' + ) return source; - return ` -__doctest.enqueue({ - input: { - lines: [ - ${formatLines (6) (test.input.lines)} - ], - thunk: () => { - return ( - ${formatInput (8) (test.input.lines)} + source = ''; + source += '\n'; + source += '__doctest.enqueue({\n'; + source += ' input: {\n'; + source += ' lines: [\n'; + for (const {number, text} of input.lines) { + source += ` {number: ${show (number)}, text: ${show (text)}},\n`; + } + source += ' ],\n'; + source += ` thunk: ([${logFunctions.join (', ')}]) => {\n`; + source += ' return (\n'; + for (const {text} of input.lines) { + source += ` ${text.replace (/^\s*([>]|[.]+)[ ]?/, '')}\n`; + } + source += ' );\n'; + source += ' },\n'; + source += ' },\n'; + source += ' outputs: [\n'; + for (const {lines, channel} of outputs) { + source += ' {\n'; + source += ' lines: [\n'; + for (const {number, text} of lines) { + source += ` {number: ${show (number)}, text: ${show (text)}},\n`; + } + source += ' ],\n'; + source += ` channel: ${show (channel)},\n`; + source += ` thunk: ([${logFunctions.join (', ')}]) => {\n`; + { + const parts = lines.map (line => + line.text.replace (/^\s*([.]+[ ]?)?/, '') ); - }, - }, - output: ${test.output && `{ - lines: [ - ${formatLines (6) (test.output.lines)} - ], - thunk: () => { - ${formatOutput (6) (test.output.lines)}; - }, - }`}, -}); -`; + const options = { + ecmaVersion: 2023, + allowReturnOutsideFunction: true, + }; + let isExpression = false; + try { + isExpression = ( + (acorn.parse (parts.join ('\n'), options)).body[0].type + !== 'ExpressionStatement' + ); + } catch {} + if (isExpression) { + for (const part of parts) { + source += ` ${part}\n`; + } + } else { + source += ' return (\n'; + for (const part of parts) { + source += ` ${part}\n`; + } + source += ' );\n'; + } + } + source += ' },\n'; + source += ' },\n'; + } + source += ' ],\n'; + source += '});\n'; + return source; }; -const wrapCoffee = test => ` -__doctest.enqueue { - input: { - lines: [ - ${formatLines (6) (test.input.lines)} - ] - thunk: -> - ${formatInput (6) (test.input.lines)} +const wrapCoffee = logFunctions => ({input, outputs, indent}) => { + let source = ''; + source += `${indent}__doctest.enqueue {\n`; + source += `${indent} input: {\n`; + source += `${indent} lines: [\n`; + for (let {number, text} of input.lines) { + number = show (number); + text = show (text); + source += `${indent} {number: ${number}, text: ${text}}\n`; + } + source += `${indent} ]\n`; + source += `${indent} thunk: ([${logFunctions.join (', ')}]) ->\n`; + for (const {text} of input.lines) { + source += `${indent} ${text.replace (/^\s*([>]|[.]+)[ ]?/, '')}\n`; } - output: ${test.output && `{ - lines: [ - ${formatLines (6) (test.output.lines)} - ] - thunk: -> - ${formatOutput (6) (test.output.lines)} - }`} -} -`; + source += `${indent} }\n`; + source += `${indent} outputs: [\n`; + for (const {lines, channel} of outputs) { + source += `${indent} {\n`; + source += `${indent} lines: [\n`; + for (let {number, text} of lines) { + number = show (number); + text = show (text); + source += `${indent} {number: ${number}, text: ${text}}\n`; + } + source += `${indent} ]\n`; + source += `${indent} channel: ${show (channel)}\n`; + source += `${indent} thunk: ([${logFunctions.join (', ')}]) ->\n`; + for (const {text} of lines) { + source += `${indent} ${text.replace (/^\s*([.]+[ ]?)?/, '')}\n`; + } + source += `${indent} }\n`; + } + source += `${indent} ]\n`; + source += `${indent}}\n`; + return source; +}; // contiguous :: Line -> NonEmpty (Array Line) -> Boolean const contiguous = line => lines => ( @@ -114,6 +148,7 @@ const rewriteJs = sourceType => ({ prefix, openingDelimiter, closingDelimiter, + logFunctions, }) => input => { const comments = []; acorn.parse (input, { @@ -166,7 +201,7 @@ const rewriteJs = sourceType => ({ .lines ) .reduce ( - (accum, {value, start, end, number}) => { + (accum, {value, start, number}) => { if (value.startsWith (prefix)) { const text = value .slice (prefix.length) @@ -177,16 +212,29 @@ const rewriteJs = sourceType => ({ } else if (text === closingDelimiter) { accum.state = 'closed'; } else if (text.startsWith ('>')) { - accum.tests.push ({[accum.state = 'input']: {lines: [line], start}}); + accum.state = 'input'; + accum.tests.push ({ + input: {channel: null, lines: [line], start}, + outputs: [], + }); } else if (text.startsWith ('.')) { - accum.tests[accum.tests.length - 1][accum.state].lines.push (line); - } else if (accum.state === 'input') { - // A comment immediately following an input line is an output - // line if and only if it contains non-whitespace characters. - const {lines} = accum.tests[accum.tests.length - 1].input; - if (contiguous (line) (lines) && text !== '') { - accum.tests[accum.tests.length - 1][accum.state = 'output'] = - {lines: [line], start}; + const {input, outputs} = accum.tests[accum.tests.length - 1]; + if (accum.state === 'input') { + input.lines.push (line); + } else { + outputs[outputs.length - 1].lines.push (line); + } + } else if (accum.state === 'input' || accum.state === 'outputs') { + const {input, outputs} = accum.tests[accum.tests.length - 1]; + if ( + // A comment immediately following an input line is an output + // line if and only if it contains non-whitespace characters. + contiguous (line) + (input.lines.concat (outputs.flatMap (o => o.lines))) && + text !== '' + ) { + accum.state = 'outputs'; + outputs.push ({channel: null, lines: [line], start}); } else { accum.state = 'open'; } @@ -197,7 +245,7 @@ const rewriteJs = sourceType => ({ {tests: [], state: openingDelimiter == null ? 'open' : 'closed'} ) .tests - .map (test => [test.input.start, wrapJs (sourceType) (test)]); + .map (test => [test.input.start, wrapJs (sourceType) (logFunctions) (test)]); return Z.sort (Z.concat (literalChunks, commentChunks)) .map (([, text]) => text) @@ -208,6 +256,7 @@ const rewriteCoffee = ({ prefix, openingDelimiter, closingDelimiter, + logFunctions, }) => input => { const lines = input.match (/^.*(?=\n)/gm); const chunks = lines.reduce ((accum, text, idx) => { @@ -227,8 +276,8 @@ const rewriteCoffee = ({ isComment: false, }); - const testChunks = chunks.commentChunks.map (commentChunk => { - const result = commentChunk.reduce ((accum, {number, text}) => { + const testChunks = chunks.commentChunks.map (commentChunk => + commentChunk.reduce ((accum, {number, text}) => { const [, indent, uncommented] = text.match (/^([ \t]*)#(.*)$/); if (uncommented.startsWith (prefix)) { const unprefixed = uncommented @@ -240,36 +289,43 @@ const rewriteCoffee = ({ } else if (unprefixed === closingDelimiter) { accum.state = 'closed'; } else if (unprefixed.startsWith ('>')) { + accum.state = 'input'; accum.tests.push ({ indent, - [accum.state = 'input']: { + input: { lines: [line], }, + outputs: [], }); } else if (unprefixed.startsWith ('.')) { - accum.tests[accum.tests.length - 1][accum.state].lines.push ( - line - ); - } else if (accum.state === 'input') { - // A comment immediately following an input line is an output - // line if and only if it contains non-whitespace characters. - const {lines} = accum.tests[accum.tests.length - 1].input; - if (contiguous (line) (lines) && unprefixed !== '') { - accum.tests[accum.tests.length - 1][accum.state = 'output'] = { - lines: [line], - }; + const {input, outputs} = accum.tests[accum.tests.length - 1]; + if (accum.state === 'input') { + input.lines.push (line); + } else { + outputs[outputs.length - 1].lines.push (line); + } + } else if (accum.state === 'input' || accum.state === 'outputs') { + const {input, outputs} = accum.tests[accum.tests.length - 1]; + if ( + // A comment immediately following an input line is an output + // line if and only if it contains non-whitespace characters. + contiguous (line) + (input.lines + .concat (outputs.flatMap (o => o.lines))) && + unprefixed !== '' + ) { + accum.state = 'outputs'; + outputs.push ({channel: null, lines: [line]}); } else { accum.state = 'open'; } } } return accum; - }, {state: openingDelimiter == null ? 'open' : 'closed', tests: []}); - - return result.tests.map ( - test => indentN (test.indent.length) (wrapCoffee (test)) - ); - }); + }, {state: openingDelimiter == null ? 'open' : 'closed', tests: []}) + .tests + .map (wrapCoffee (logFunctions)) + ); return CoffeeScript.compile ( chunks.literalChunks.reduce ( @@ -283,26 +339,7 @@ const rewriteCoffee = ({ ); }; -const run = queue => - queue.flatMap (({input, output}) => { - const i = encase (input.thunk) (); - if (output == null) return []; - const o = encase (output.thunk) (); - const comparison = ( - effect (o => effect (i => i.name === o.name && - i.message === (o.message || i.message) ? - Correct (Failure (i)) : - Incorrect (Failure (i)) (Failure (o))) - (i => Incorrect (Success (i)) (Failure (o)))) - (o => effect (i => Incorrect (Failure (i)) (Success (o))) - (i => Z.equals (i, o) ? - Correct (Success (i)) : - Incorrect (Success (i)) (Success (o)))) - (o) - (i) - ); - return [{lines: {input: input.lines, output: output.lines}, comparison}]; - }); +const sentinel = {}; const readSourceFile = async path => ( (await fs.readFile (path, 'utf8')) @@ -332,7 +369,7 @@ const evaluateModule = moduleUrl => async source => { return module; }); await module.evaluate (); - return run (queue); + return queue; }; const evaluateScript = context => async source => { @@ -340,7 +377,77 @@ const evaluateScript = context => async source => { const enqueue = io => { queue.push (io); }; const __doctest = {enqueue, require}; vm.runInNewContext (source, {...global, ...context, __doctest}); - return run (queue); + return queue; +}; + +const run = channels => timeout => async queue => { + const tests = []; + for (const {input, outputs} of queue) { + const actuals = await new Promise (res => { + const actuals = []; + let open = true; + let job; + + const done = () => { + open = false; + clearTimeout (job); + res (actuals); + }; + + const logFunctions = channels.map (channel => value => { + if (open) { + actuals.push (Success (Channel (channel) (value))); + clearTimeout (job); + job = setTimeout (done, timeout); + } + }); + + actuals.push (encase (input.thunk) (logFunctions)); + + job = setTimeout (done, timeout); + }); + + if (outputs.length === 0) continue; + + const expecteds = []; + const logFunctions = channels.map (channel => value => (( + expecteds.push (Success (Channel (channel) (value))), + sentinel + ))); + for (const output of outputs) { + const expected = encase (output.thunk) (logFunctions); + if (effect (_ => true) (({value}) => value !== sentinel) (expected)) { + expecteds.push (expected); + } + } + const isError = x => ( + Object.prototype.toString.call (x) === '[object Error]' + ); + const test = { + lines: { + input: input.lines, + output: outputs.flatMap (output => output.lines), + }, + comparison: ( + actuals.length === expecteds.length && + actuals.every ((a, idx) => + effect (a => effect (e => isError (a) && isError (e) + ? a.name === e.name && + a.message === (e.message || a.message) + : Z.equals (a, e)) + (_ => false)) + (a => effect (_ => false) + (e => Z.equals (a, e))) + (a) + (expecteds[idx]) + ) + ? Correct (actuals) + : Incorrect (actuals) (expecteds) + ), + }; + tests.push (test); + } + return tests; }; export default ({ @@ -349,17 +456,21 @@ export default ({ prefix = '', openingDelimiter, closingDelimiter, + logFunctions = [], + timeout = logFunctions.length > 0 ? 100 : 0, print = false, }) => async path => { const __filename = resolve (process.cwd (), path); let context = {}; - const options = {prefix, openingDelimiter, closingDelimiter}; + const options = {prefix, openingDelimiter, closingDelimiter, logFunctions}; switch (module) { case 'esm': { const rewrite = rewriteJs ('module'); const source = rewrite (options) (await readSourceFile (path)); if (print) return source; - return evaluateModule (url.pathToFileURL (__filename)) (source); + const moduleUrl = url.pathToFileURL (__filename); + const queue = await evaluateModule (moduleUrl) (source); + return run (logFunctions) (timeout) (queue); } case 'commonjs': { const exports = {}; @@ -371,7 +482,8 @@ export default ({ const rewrite = coffee ? rewriteCoffee : rewriteJs ('script'); const source = rewrite (options) (await readSourceFile (path)); if (print) return source; - return evaluateScript (context) (source); + const queue = await evaluateScript (context) (source); + return run (logFunctions) (timeout) (queue); } default: { throw new Error (`Invalid module ${show (module)}`); diff --git a/lib/program.js b/lib/program.js index 42ae7b4..5d0ba74 100644 --- a/lib/program.js +++ b/lib/program.js @@ -1,4 +1,5 @@ import program from 'commander'; +import Z from 'sanctuary-type-classes'; import require from './require.js'; @@ -20,10 +21,20 @@ program 'specify line preceding doctest block (e.g. "```javascript")') .option (' --closing-delimiter ', 'specify line following doctest block (e.g. "```")') +.option (' --log-function ', + 'expose a log function with the given name to your doctests' + + ' (can be specified multiple times)', + Z.append, + []) +.option (' --timeout ', + 'specify an alternative log timeout time (defaults to 100)') .option ('-p, --print', 'output the rewritten source without running tests') .option ('-s, --silent', 'suppress output') .parse (process.argv); +program.logFunctions = program.logFunction; +delete program.logFunction; + export default program; diff --git a/test/bin/results.js b/test/bin/results.js index 59b5892..5f54bdc 100644 --- a/test/bin/results.js +++ b/test/bin/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('executable without file extension') ([Line (3) ('> identity(42)')]) ([Line (4) ('42')]) - (Correct (Success (42))), + (Correct ([Success (Result (42))])), ]; diff --git a/test/commonjs/__dirname/results.coffee.js b/test/commonjs/__dirname/results.coffee.js index 7fc2e57..02f2558 100644 --- a/test/commonjs/__dirname/results.coffee.js +++ b/test/commonjs/__dirname/results.coffee.js @@ -1,18 +1,18 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('__dirname is defined') ([Line (3) ('> typeof __dirname')]) ([Line (4) ("'string'")]) - (Correct (Success ('string'))), + (Correct ([Success (Result ('string'))])), Test ('__dirname is absolute') ([Line (8) ('> path.isAbsolute __dirname')]) ([Line (9) ('true')]) - (Correct (Success (true))), + (Correct ([Success (Result (true))])), Test ('__dirname is correct') ([Line (11) ('> path.relative process.cwd(), __dirname')]) ([Line (12) ("'test/commonjs/__dirname'")]) - (Correct (Success ('test/commonjs/__dirname'))), + (Correct ([Success (Result ('test/commonjs/__dirname'))])), ]; diff --git a/test/commonjs/__dirname/results.js b/test/commonjs/__dirname/results.js index 1c10de9..90a1790 100644 --- a/test/commonjs/__dirname/results.js +++ b/test/commonjs/__dirname/results.js @@ -1,18 +1,18 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('__dirname is defined') ([Line (3) ('> typeof __dirname')]) ([Line (4) ("'string'")]) - (Correct (Success ('string'))), + (Correct ([Success (Result ('string'))])), Test ('__dirname is absolute') ([Line (8) ('> path.isAbsolute (__dirname)')]) ([Line (9) ('true')]) - (Correct (Success (true))), + (Correct ([Success (Result (true))])), Test ('__dirname is correct') ([Line (11) ('> path.relative (process.cwd (), __dirname)')]) ([Line (12) ("'test/commonjs/__dirname'")]) - (Correct (Success ('test/commonjs/__dirname'))), + (Correct ([Success (Result ('test/commonjs/__dirname'))])), ]; diff --git a/test/commonjs/__doctest.require/results.js b/test/commonjs/__doctest.require/results.js index 2c3f78b..0026af8 100644 --- a/test/commonjs/__doctest.require/results.js +++ b/test/commonjs/__doctest.require/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('__doctest.require') ([Line (5) ("> (new url.URL ('https://sanctuary.js.org/')).hostname")]) ([Line (6) ("'sanctuary.js.org'")]) - (Correct (Success ('sanctuary.js.org'))), + (Correct ([Success (Result ('sanctuary.js.org'))])), ]; diff --git a/test/commonjs/__filename/results.coffee.js b/test/commonjs/__filename/results.coffee.js index 236155f..cb85d41 100644 --- a/test/commonjs/__filename/results.coffee.js +++ b/test/commonjs/__filename/results.coffee.js @@ -1,18 +1,18 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('__filename is defined') ([Line (3) ('> typeof __filename')]) ([Line (4) ("'string'")]) - (Correct (Success ('string'))), + (Correct ([Success (Result ('string'))])), Test ('__filename is absolute') ([Line (8) ('> path.isAbsolute __filename')]) ([Line (9) ('true')]) - (Correct (Success (true))), + (Correct ([Success (Result (true))])), Test ('__filename is correct') ([Line (11) ('> path.relative process.cwd(), __filename')]) ([Line (12) ("'test/commonjs/__filename/index.coffee'")]) - (Correct (Success ('test/commonjs/__filename/index.coffee'))), + (Correct ([Success (Result ('test/commonjs/__filename/index.coffee'))])), ]; diff --git a/test/commonjs/__filename/results.js b/test/commonjs/__filename/results.js index 18e35cb..3451781 100644 --- a/test/commonjs/__filename/results.js +++ b/test/commonjs/__filename/results.js @@ -1,18 +1,18 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('__filename is defined') ([Line (3) ('> typeof __filename')]) ([Line (4) ("'string'")]) - (Correct (Success ('string'))), + (Correct ([Success (Result ('string'))])), Test ('__filename is absolute') ([Line (8) ('> path.isAbsolute (__filename)')]) ([Line (9) ('true')]) - (Correct (Success (true))), + (Correct ([Success (Result (true))])), Test ('__filename is correct') ([Line (11) ('> path.relative (process.cwd (), __filename)')]) ([Line (12) ("'test/commonjs/__filename/index.js'")]) - (Correct (Success ('test/commonjs/__filename/index.js'))), + (Correct ([Success (Result ('test/commonjs/__filename/index.js'))])), ]; diff --git a/test/commonjs/exports/results.js b/test/commonjs/exports/results.js index eba1f37..3c8c083 100644 --- a/test/commonjs/exports/results.js +++ b/test/commonjs/exports/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('exports') ([Line (1) ('> exports.identity(42)')]) ([Line (2) ('42')]) - (Correct (Success (42))), + (Correct ([Success (Result (42))])), ]; diff --git a/test/commonjs/module.exports/results.js b/test/commonjs/module.exports/results.js index c06f84b..5b9b2b7 100644 --- a/test/commonjs/module.exports/results.js +++ b/test/commonjs/module.exports/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('module.exports') ([Line (1) ('> module.exports(42)')]) ([Line (2) ('42')]) - (Correct (Success (42))), + (Correct ([Success (Result (42))])), ]; diff --git a/test/commonjs/require/results.js b/test/commonjs/require/results.js index a0085fa..16edd5e 100644 --- a/test/commonjs/require/results.js +++ b/test/commonjs/require/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('require another CommonJS module') ([Line (1) ('> typeof $require("assert")')]) ([Line (2) ('"function"')]) - (Correct (Success ('function'))), + (Correct ([Success (Result ('function'))])), ]; diff --git a/test/commonjs/strict/results.js b/test/commonjs/strict/results.js index 7477891..fcba63c 100644 --- a/test/commonjs/strict/results.js +++ b/test/commonjs/strict/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ("preserves 'use strict' directive") ([Line (3) ('> (function() { return this; }())')]) ([Line (4) ('undefined')]) - (Correct (Success (undefined))), + (Correct ([Success (Result (undefined))])), ]; diff --git a/test/contiguity/results.js b/test/contiguity/results.js index c8a8f8d..04b9b5e 100644 --- a/test/contiguity/results.js +++ b/test/contiguity/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('output line immediately following input line') ([Line (15) ('> zero (42)')]) ([Line (16) ('0')]) - (Correct (Success (0))), + (Correct ([Success (Result (0))])), ]; diff --git a/test/es2015/results.js b/test/es2015/results.js index 1f3cb57..4ffbc20 100644 --- a/test/es2015/results.js +++ b/test/es2015/results.js @@ -1,28 +1,28 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('seq.next().value') ([Line (12) ('> seq.next().value')]) ([Line (13) ('1')]) - (Correct (Success (1))), + (Correct ([Success (Result (1))])), Test ('seq.next().value') ([Line (14) ('> seq.next().value')]) ([Line (15) ('1')]) - (Correct (Success (1))), + (Correct ([Success (Result (1))])), Test ('seq.next().value') ([Line (16) ('> seq.next().value')]) ([Line (17) ('2')]) - (Correct (Success (2))), + (Correct ([Success (Result (2))])), Test ('seq.next().value') ([Line (18) ('> seq.next().value')]) ([Line (19) ('3')]) - (Correct (Success (3))), + (Correct ([Success (Result (3))])), Test ('seq.next().value') ([Line (20) ('> seq.next().value')]) ([Line (21) ('5')]) - (Correct (Success (5))), + (Correct ([Success (Result (5))])), ]; diff --git a/test/es2018/results.js b/test/es2018/results.js index 990762f..01149ad 100644 --- a/test/es2018/results.js +++ b/test/es2018/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('object spread syntax') ([Line (1) ('> {x: 0, ...{x: 1, y: 2, z: 3}, z: 4}')]) ([Line (2) ('{x: 1, y: 2, z: 4}')]) - (Correct (Success ({x: 1, y: 2, z: 4}))), + (Correct ([Success (Result ({x: 1, y: 2, z: 4}))])), ]; diff --git a/test/es2020/results.js b/test/es2020/results.js index 9bde95c..b02071a 100644 --- a/test/es2020/results.js +++ b/test/es2020/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('nullish coalescing operator') ([Line (1) ("> null ?? 'default'")]) ([Line (2) ("'default'")]) - (Correct (Success ('default'))), + (Correct ([Success (Result ('default'))])), ]; diff --git a/test/esm/async.js b/test/esm/async.js new file mode 100644 index 0000000..da6f215 --- /dev/null +++ b/test/esm/async.js @@ -0,0 +1,7 @@ +// > ( stdout (1) +// . , setTimeout (stdout, 1, 2) +// . , stderr (3) ) +// stdout (1) +// stderr (3) +// undefined +// stdout (2) diff --git a/test/esm/globals/results.js b/test/esm/globals/results.js index 103f345..d6a928c 100644 --- a/test/esm/globals/results.js +++ b/test/esm/globals/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('setTimeout is defined') ([Line (1) ('> typeof setTimeout')]) ([Line (2) ("'function'")]) - (Correct (Success ('function'))), + (Correct ([Success (Result ('function'))])), ]; diff --git a/test/exceptions/index.js b/test/exceptions/index.js index d56a5f8..1afa864 100644 --- a/test/exceptions/index.js +++ b/test/exceptions/index.js @@ -12,26 +12,29 @@ // new Error('XXX') // > new Error('Invalid value') -// ! Error: Invalid value +// throw new Error('Invalid value') // > sqrt(-1) // new Error('Invalid value') // > 0..toString(1) -// ! RangeError +// throw new RangeError // > 0..toString(1) -// ! Error +// throw new Error // > sqrt(-1) -// ! Error: Invalid value +// throw new Error('Invalid value') // > sqrt(-1) -// ! Error: XXX +// throw new Error('XXX') // > 'foo' + 'bar' // foobar +// > (() => { throw [1, 2, 3] })() +// throw [1, 2, 3] + var sqrt = function(n) { if (n >= 0) { return Math.sqrt(n); diff --git a/test/exceptions/results.js b/test/exceptions/results.js index e073d86..530cb05 100644 --- a/test/exceptions/results.js +++ b/test/exceptions/results.js @@ -1,66 +1,71 @@ -export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ +export default ({Test, Line, Incorrect, Correct, Failure, Success, Result}) => [ Test ('input evaluates to Error with expected message') ([Line (2) ("> new Error('Invalid value')")]) ([Line (3) ("new Error('Invalid value')")]) - (Correct (Success (new Error ('Invalid value')))), + (Correct ([Success (Result (new Error ('Invalid value')))])), Test ('input evaluates to Error without expected message') ([Line (5) ('> new Error()')]) ([Line (6) ("new Error('Invalid value')")]) - (Incorrect (Success (new Error ())) - (Success (new Error ('Invalid value')))), + (Incorrect ([Success (Result (new Error ()))]) + ([Success (Result (new Error ('Invalid value')))])), Test ('input evaluates to Error with unexpected message') ([Line (8) ("> new Error('Invalid value')")]) ([Line (9) ('new Error()')]) - (Incorrect (Success (new Error ('Invalid value'))) - (Success (new Error ()))), + (Incorrect ([Success (Result (new Error ('Invalid value')))]) + ([Success (Result (new Error ()))])), Test ('input evaluates to Error with unexpected message') ([Line (11) ("> new Error('Invalid value')")]) ([Line (12) ("new Error('XXX')")]) - (Incorrect (Success (new Error ('Invalid value'))) - (Success (new Error ('XXX')))), + (Incorrect ([Success (Result (new Error ('Invalid value')))]) + ([Success (Result (new Error ('XXX')))])), Test ('evaluating input does not throw expected exception') ([Line (14) ("> new Error('Invalid value')")]) - ([Line (15) ('! Error: Invalid value')]) - (Incorrect (Success (new Error ('Invalid value'))) - (Failure (new Error ('Invalid value')))), + ([Line (15) ("throw new Error('Invalid value')")]) + (Incorrect ([Success (Result (new Error ('Invalid value')))]) + ([Failure (new Error ('Invalid value'))])), Test ('evaluating input throws unexpected exception') ([Line (17) ('> sqrt(-1)')]) ([Line (18) ("new Error('Invalid value')")]) - (Incorrect (Failure (new Error ('Invalid value'))) - (Success (new Error ('Invalid value')))), + (Incorrect ([Failure (new Error ('Invalid value'))]) + ([Success (Result (new Error ('Invalid value')))])), Test ('evaluating input throws exception as expected, of expected type') ([Line (20) ('> 0..toString(1)')]) - ([Line (21) ('! RangeError')]) - (Correct (Failure (new RangeError ('toString() radix argument must be between 2 and 36')))), + ([Line (21) ('throw new RangeError')]) + (Correct ([Failure (new RangeError ('toString() radix argument must be between 2 and 36'))])), Test ('evaluating input throws exception as expected, of unexpected type') ([Line (23) ('> 0..toString(1)')]) - ([Line (24) ('! Error')]) - (Incorrect (Failure (new RangeError ('toString() radix argument must be between 2 and 36'))) - (Failure (new Error ()))), + ([Line (24) ('throw new Error')]) + (Incorrect ([Failure (new RangeError ('toString() radix argument must be between 2 and 36'))]) + ([Failure (new Error ())])), Test ('evaluating input throws exception as expected, with expected message') ([Line (26) ('> sqrt(-1)')]) - ([Line (27) ('! Error: Invalid value')]) - (Correct (Failure (new Error ('Invalid value')))), + ([Line (27) ("throw new Error('Invalid value')")]) + (Correct ([Failure (new Error ('Invalid value'))])), Test ('evaluating input throws exception as expected, with unexpected message') ([Line (29) ('> sqrt(-1)')]) - ([Line (30) ('! Error: XXX')]) - (Incorrect (Failure (new Error ('Invalid value'))) - (Failure (new Error ('XXX')))), + ([Line (30) ("throw new Error('XXX')")]) + (Incorrect ([Failure (new Error ('Invalid value'))]) + ([Failure (new Error ('XXX'))])), Test ('evaluating output throws unexpected exception') ([Line (32) ("> 'foo' + 'bar'")]) ([Line (33) ('foobar')]) - (Incorrect (Success ('foobar')) - (Failure (new ReferenceError ('foobar is not defined')))), + (Incorrect ([Success (Result ('foobar'))]) + ([Failure (new ReferenceError ('foobar is not defined'))])), + + Test ('evaluating input throws non-Error exception as expected') + ([Line (35) ('> (() => { throw [1, 2, 3] })()')]) + ([Line (36) ('throw [1, 2, 3]')]) + (Correct ([Failure ([1, 2, 3])])), ]; diff --git a/test/fantasy-land/results.js b/test/fantasy-land/results.js index e2dc03c..5ab1233 100644 --- a/test/fantasy-land/results.js +++ b/test/fantasy-land/results.js @@ -1,11 +1,11 @@ import Absolute from './index.js'; -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('uses Z.equals for equality checks') ([Line (1) ('> Absolute(-1)')]) ([Line (2) ('Absolute(1)')]) - (Correct (Success (Absolute (-1)))), + (Correct ([Success (Result (Absolute (-1)))])), ]; diff --git a/test/index.js b/test/index.js index 544153d..4e7021e 100644 --- a/test/index.js +++ b/test/index.js @@ -9,6 +9,7 @@ import Z from 'sanctuary-type-classes'; import {Incorrect, Correct} from '../lib/Comparison.js'; import {Failure, Success} from '../lib/Effect.js'; import {Line} from '../lib/Line.js'; +import {Result, Channel} from '../lib/Output.js'; import doctest from '../lib/doctest.js'; import resultsBin from './bin/results.js'; @@ -29,12 +30,18 @@ import resultsEsmGlobals from './esm/globals/results.js'; import resultsExceptions from './exceptions/results.js'; import resultsFantasyLand from './fantasy-land/results.js'; import resultsLineEndings from './line-endings/results.js'; +import resultsLogging from './logging/results.js'; import resultsSharedCoffee from './shared/results.coffee.js'; import resultsSharedJs from './shared/results.js'; import resultsStatements from './statements/results.js'; import resultsTranscribe from './transcribe/results.js'; +const style = ([chunk, ...chunks], ...args) => chunks.reduce ( + (text, chunk, idx) => `${text}\u001B[${args[idx]}m${chunk}`, + chunk +); + const eq = actual => expected => { strictEqual (show (actual), show (expected)); strictEqual (Z.equals (actual, expected), true); @@ -66,6 +73,8 @@ const dependencies = { Correct, Failure, Success, + Result, + Channel, }; const testModule = (module, path, options) => { @@ -218,6 +227,10 @@ testModule (resultsContiguity, 'test/contiguity/index.coffee', { coffee: true, }); +testModule (resultsLogging, 'test/logging/index.js', { + logFunctions: ['stdout', 'stderr'], +}); + { const options = {module: 'esm', print: true}; const path = 'test/esm/index.js'; @@ -231,22 +244,25 @@ __doctest.enqueue({ lines: [ {number: 3, text: "> toFahrenheit (0)"}, ], - thunk: () => { + thunk: ([]) => { return ( toFahrenheit (0) ); }, }, - output: { - lines: [ - {number: 4, text: "32"}, - ], - thunk: () => { - return ( - 32 - ); + outputs: [ + { + lines: [ + {number: 4, text: "32"}, + ], + channel: null, + thunk: ([]) => { + return ( + 32 + ); + }, }, - }, + ], }); @@ -260,118 +276,334 @@ export function toFahrenheit(degreesCelsius) { testCommand ('bin/doctest', { status: 0, - stdout: '', - stderr: '', + stdout: style``, + stderr: style``, }); testCommand ('bin/doctest --xxx', { status: 1, - stdout: '', - stderr: `error: unknown option \`--xxx' + stdout: style``, + stderr: style`error: unknown option \`--xxx' `, }); testCommand ('bin/doctest test/shared/index.js', { status: 1, - stdout: `running doctests in test/shared/index.js... -......x.x...........x........x -FAIL: expected 5 on line 31 (got 4) -FAIL: expected ! TypeError on line 38 (got 0) -FAIL: expected 9.5 on line 97 (got 5) -FAIL: expected "on automatic semicolon insertion" on line 155 (got "the rewriter should not rely") + stdout: style` +Running doctests in ${1}test/shared/index.js${22}... + +${7} 30 ${0} ${34}> two + two${0} +${7} 31 ${0} ${35}5${0} + +${34}4${0} +${35}5${0} + +${7} 37 ${0} ${34}> [].length${0} +${7} 38 ${0} ${35}throw new TypeError${0} + +${34}0${0} +${35}${1}throw${22} new TypeError ("")${0} + +${7} 95 ${0} ${34}>10 -${0} +${7} 96 ${0} ${34}..5${0} +${7} 97 ${0} ${35}9.5${0} + +${34}5${0} +${35}9.5${0} + +${7} 154 ${0} ${34}> "the rewriter should not rely"${0} +${7} 155 ${0} ${35}"on automatic semicolon insertion"${0} + +${34}"the rewriter should not rely"${0} +${35}"on automatic semicolon insertion"${0} + `, - stderr: '', + stderr: style``, }); testCommand ('bin/doctest --coffee test/shared/index.coffee', { status: 1, - stdout: `running doctests in test/shared/index.coffee... -......x.x...........x..... -FAIL: expected 5 on line 31 (got 4) -FAIL: expected ! TypeError on line 38 (got 0) -FAIL: expected 9.5 on line 97 (got 5) + stdout: style` +Running doctests in ${1}test/shared/index.coffee${22}... + +${7} 30 ${0} ${34}> two + two${0} +${7} 31 ${0} ${35}5${0} + +${34}4${0} +${35}5${0} + +${7} 37 ${0} ${34}> [].length${0} +${7} 38 ${0} ${35}throw new TypeError${0} + +${34}0${0} +${35}${1}throw${22} new TypeError ("")${0} + +${7} 95 ${0} ${34}>10 -${0} +${7} 96 ${0} ${34}..5${0} +${7} 97 ${0} ${35}9.5${0} + +${34}5${0} +${35}9.5${0} + +`, + stderr: style``, +}); + +testCommand ('bin/doctest test/exceptions/index.js', { + status: 1, + stdout: style` +Running doctests in ${1}test/exceptions/index.js${22}... + +${7} 5 ${0} ${34}> new Error()${0} +${7} 6 ${0} ${35}new Error('Invalid value')${0} + +${34}new Error ("")${0} +${35}new Error ("Invalid value")${0} + +${7} 8 ${0} ${34}> new Error('Invalid value')${0} +${7} 9 ${0} ${35}new Error()${0} + +${34}new Error ("Invalid value")${0} +${35}new Error ("")${0} + +${7} 11 ${0} ${34}> new Error('Invalid value')${0} +${7} 12 ${0} ${35}new Error('XXX')${0} + +${34}new Error ("Invalid value")${0} +${35}new Error ("XXX")${0} + +${7} 14 ${0} ${34}> new Error('Invalid value')${0} +${7} 15 ${0} ${35}throw new Error('Invalid value')${0} + +${34}new Error ("Invalid value")${0} +${35}${1}throw${22} new Error ("Invalid value")${0} + +${7} 17 ${0} ${34}> sqrt(-1)${0} +${7} 18 ${0} ${35}new Error('Invalid value')${0} + +${34}${1}throw${22} new Error ("Invalid value")${0} +${35}new Error ("Invalid value")${0} + +${7} 23 ${0} ${34}> 0..toString(1)${0} +${7} 24 ${0} ${35}throw new Error${0} + +${34}${1}throw${22} new RangeError ("toString() radix argument must be between 2 and 36")${0} +${35}${1}throw${22} new Error ("")${0} + +${7} 29 ${0} ${34}> sqrt(-1)${0} +${7} 30 ${0} ${35}throw new Error('XXX')${0} + +${34}${1}throw${22} new Error ("Invalid value")${0} +${35}${1}throw${22} new Error ("XXX")${0} + +${7} 32 ${0} ${34}> 'foo' + 'bar'${0} +${7} 33 ${0} ${35}foobar${0} + +${34}"foobar"${0} +${35}${1}throw${22} new ReferenceError ("foobar is not defined")${0} + `, - stderr: '', + stderr: style``, }); testCommand ('bin/doctest --silent test/shared/index.js', { status: 1, - stdout: '', - stderr: '', + stdout: style``, + stderr: style``, +}); + +testCommand ('bin/doctest --module xxx file.js', { + status: 1, + stdout: style``, + stderr: style`Error: Invalid module "xxx" +`, }); testCommand ('bin/doctest test/bin/executable', { status: 0, - stdout: `running doctests in test/bin/executable... -. + stdout: style` +Running doctests in ${1}test/bin/executable${22}... + `, - stderr: '', + stderr: style``, }); -testCommand ('bin/doctest --module xxx file.js', { +testCommand ("bin/doctest --prefix . --opening-delimiter '```javascript' --closing-delimiter '```' test/transcribe/index.js", { + status: 0, + stdout: style` +Running doctests in ${1}test/transcribe/index.js${22}... + +`, + stderr: style``, +}); + +testCommand ("bin/doctest --coffee --prefix . --opening-delimiter '```coffee' --closing-delimiter '```' test/transcribe/index.coffee", { + status: 0, + stdout: style` +Running doctests in ${1}test/transcribe/index.coffee${22}... + +`, + stderr: style``, +}); + +testCommand ('bin/doctest --log-function stdout --log-function stderr test/shared/async.js', { + status: 0, + stdout: style` +Running doctests in ${1}test/shared/async.js${22}... + +`, + stderr: style``, +}); + +testCommand ('bin/doctest --module commonjs --log-function stdout --log-function stderr test/shared/async.js', { + status: 0, + stdout: style` +Running doctests in ${1}test/shared/async.js${22}... + +`, + stderr: style``, +}); + +testCommand ('bin/doctest --coffee --log-function stdout --log-function stderr test/shared/async.coffee', { + status: 0, + stdout: style` +Running doctests in ${1}test/shared/async.coffee${22}... + +`, + stderr: style``, +}); + +testCommand ('bin/doctest --log-function stdout --log-function stderr test/logging/index.js', { status: 1, - stdout: '', - stderr: `Error: Invalid module "xxx" + stdout: style` +Running doctests in ${1}test/logging/index.js${22}... + +${7} 29 ${0} ${34}> (stdout (1), 3)${0} +${7} 30 ${0} ${35}stdout (1)${0} +${7} 31 ${0} ${35}stdout (2)${0} +${7} 32 ${0} ${35}return 3${0} + +${34}${1}stdout${22} (1)${0} +${34}3${0} +${35}${1}stdout${22} (1)${0} +${35}${1}stdout${22} (2)${0} +${35}3${0} + +${7} 36 ${0} ${34}> (stdout (1), stdout (2), 3)${0} +${7} 37 ${0} ${35}stdout (1)${0} +${7} 38 ${0} ${35}return 3${0} + +${34}${1}stdout${22} (1)${0} +${34}${1}stdout${22} (2)${0} +${34}3${0} +${35}${1}stdout${22} (1)${0} +${35}3${0} + +${7} 42 ${0} ${34}> (stdout (1), stdout (2), 3)${0} +${7} 43 ${0} ${35}stdout (2)${0} +${7} 44 ${0} ${35}stdout (1)${0} +${7} 45 ${0} ${35}return 3${0} + +${34}${1}stdout${22} (1)${0} +${34}${1}stdout${22} (2)${0} +${34}3${0} +${35}${1}stdout${22} (2)${0} +${35}${1}stdout${22} (1)${0} +${35}3${0} + +${7} 49 ${0} ${34}> (stdout (1), stdout (2), 3)${0} +${7} 50 ${0} ${35}stdout (1)${0} +${7} 51 ${0} ${35}stderr (2)${0} +${7} 52 ${0} ${35}return 3${0} + +${34}${1}stdout${22} (1)${0} +${34}${1}stdout${22} (2)${0} +${34}3${0} +${35}${1}stdout${22} (1)${0} +${35}${1}stderr${22} (2)${0} +${35}3${0} + +${7} 56 ${0} ${34}> (setTimeout (stdout, 125, 1), 2)${0} +${7} 57 ${0} ${35}return 2${0} +${7} 58 ${0} ${35}stdout (1)${0} + +${34}2${0} +${35}2${0} +${35}${1}stdout${22} (1)${0} + `, + stderr: style``, }); testCommand ('bin/doctest --module esm lib/doctest.js', { status: 0, - stdout: `running doctests in lib/doctest.js... + stdout: style` +Running doctests in ${1}lib/doctest.js${22}... `, - stderr: '', + stderr: style``, }); testCommand ('bin/doctest --module esm test/esm/index.js', { status: 0, - stdout: `running doctests in test/esm/index.js... -. + stdout: style` +Running doctests in ${1}test/esm/index.js${22}... + `, - stderr: '', + stderr: style``, }); testCommand ('bin/doctest --module esm test/esm/dependencies.js', { status: 0, - stdout: `running doctests in test/esm/dependencies.js... -. + stdout: style` +Running doctests in ${1}test/esm/dependencies.js${22}... + `, - stderr: '', + stderr: style``, }); testCommand ('bin/doctest --module esm test/esm/incorrect.js', { status: 1, - stdout: `running doctests in test/esm/incorrect.js... -x -FAIL: expected 32 on line 4 (got "0°F") + stdout: style` +Running doctests in ${1}test/esm/incorrect.js${22}... + +${7} 3 ${0} ${34}> toFahrenheit (0)${0} +${7} 4 ${0} ${35}32${0} + +${34}"0°F"${0} +${35}32${0} + `, - stderr: '', + stderr: style``, }); testCommand ('bin/doctest --print test/commonjs/exports/index.js', { status: 0, - stdout: ` + stdout: style` __doctest.enqueue({ input: { lines: [ {number: 1, text: "> exports.identity(42)"}, ], - thunk: () => { + thunk: ([]) => { return ( exports.identity(42) ); }, }, - output: { - lines: [ - {number: 2, text: "42"}, - ], - thunk: () => { - return ( - 42 - ); + outputs: [ + { + lines: [ + {number: 2, text: "42"}, + ], + channel: null, + thunk: ([]) => { + return ( + 42 + ); + }, }, - }, + ], }); @@ -379,33 +611,36 @@ exports.identity = function(x) { return x; }; `, - stderr: '', + stderr: style``, }); testCommand ('bin/doctest --print --module commonjs test/commonjs/exports/index.js', { status: 0, - stdout: ` + stdout: style` __doctest.enqueue({ input: { lines: [ {number: 1, text: "> exports.identity(42)"}, ], - thunk: () => { + thunk: ([]) => { return ( exports.identity(42) ); }, }, - output: { - lines: [ - {number: 2, text: "42"}, - ], - thunk: () => { - return ( - 42 - ); + outputs: [ + { + lines: [ + {number: 2, text: "42"}, + ], + channel: null, + thunk: ([]) => { + return ( + 42 + ); + }, }, - }, + ], }); @@ -413,5 +648,5 @@ exports.identity = function(x) { return x; }; `, - stderr: '', + stderr: style``, }); diff --git a/test/line-endings/results.js b/test/line-endings/results.js index 1c5a3db..5051386 100644 --- a/test/line-endings/results.js +++ b/test/line-endings/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('correct line number reported irrespective of line endings') ([Line (1) ('> 2 * 3 * 7')]) ([Line (2) ('42')]) - (Correct (Success (42))), + (Correct ([Success (Result (42))])), ]; diff --git a/test/logging/index.js b/test/logging/index.js new file mode 100644 index 0000000..9257484 --- /dev/null +++ b/test/logging/index.js @@ -0,0 +1,64 @@ +// > function crash() { throw new Error ('') } + +// Synchronous log with output +// +// > (stdout (1), 2) +// stdout (1) +// return 2 + +// Synchronous log with exception +// +// > (stdout (1), crash ()) +// stdout (1) +// throw new Error ('') + +// Asynchronous log with output +// +// > (setImmediate (stdout, 1), 2) +// return 2 +// stdout (1) + +// Asynchronous log with exception +// +// > (setImmediate (stdout, 1), crash ()) +// throw new Error ('') +// stdout (1) + +// Failure due to not enough output +// +// > (stdout (1), 3) +// stdout (1) +// stdout (2) +// return 3 + +// Failure due to too much output +// +// > (stdout (1), stdout (2), 3) +// stdout (1) +// return 3 + +// Failure due to incorrectly ordered output +// +// > (stdout (1), stdout (2), 3) +// stdout (2) +// stdout (1) +// return 3 + +// Failure due to output on the wrong channel +// +// > (stdout (1), stdout (2), 3) +// stdout (1) +// stderr (2) +// return 3 + +// Failure due to timing out +// +// > (setTimeout (stdout, 125, 1), 2) +// return 2 +// stdout (1) + +// Success after a previous timeout +// +// > (setTimeout (stdout, 75, 1), 2) +// return 2 +// stdout (1) diff --git a/test/logging/results.js b/test/logging/results.js new file mode 100644 index 0000000..ed47097 --- /dev/null +++ b/test/logging/results.js @@ -0,0 +1,91 @@ +export default ({Test, Line, Incorrect, Correct, Failure, Success, Result, Channel}) => [ + + Test ('synchronous log with output') + ([Line (5) ('> (stdout (1), 2)')]) + ([Line (6) ('stdout (1)'), + Line (7) ('return 2')]) + (Correct ([Success (Channel ('stdout') (1)), + Success (Result (2))])), + + Test ('synchronous log with exception') + ([Line (11) ('> (stdout (1), crash ())')]) + ([Line (12) ('stdout (1)'), + Line (13) ("throw new Error ('')")]) + (Correct ([Success (Channel ('stdout') (1)), + Failure (new Error (''))])), + + Test ('asynchronous log with output') + ([Line (17) ('> (setImmediate (stdout, 1), 2)')]) + ([Line (18) ('return 2'), + Line (19) ('stdout (1)')]) + (Correct ([Success (Result (2)), + Success (Channel ('stdout') (1))])), + + Test ('asynchronous log with exception') + ([Line (23) ('> (setImmediate (stdout, 1), crash ())')]) + ([Line (24) ("throw new Error ('')"), + Line (25) ('stdout (1)')]) + (Correct ([Failure (new Error ('')), + Success (Channel ('stdout') (1))])), + + Test ('failure due to not enough output') + ([Line (29) ('> (stdout (1), 3)')]) + ([Line (30) ('stdout (1)'), + Line (31) ('stdout (2)'), + Line (32) ('return 3')]) + (Incorrect ([Success (Channel ('stdout') (1)), + Success (Result (3))]) + ([Success (Channel ('stdout') (1)), + Success (Channel ('stdout') (2)), + Success (Result (3))])), + + Test ('failure due to too much output') + ([Line (36) ('> (stdout (1), stdout (2), 3)')]) + ([Line (37) ('stdout (1)'), + Line (38) ('return 3')]) + (Incorrect ([Success (Channel ('stdout') (1)), + Success (Channel ('stdout') (2)), + Success (Result (3))]) + ([Success (Channel ('stdout') (1)), + Success (Result (3))])), + + Test ('failure due to incorrectly ordered output') + ([Line (42) ('> (stdout (1), stdout (2), 3)')]) + ([Line (43) ('stdout (2)'), + Line (44) ('stdout (1)'), + Line (45) ('return 3')]) + (Incorrect ([Success (Channel ('stdout') (1)), + Success (Channel ('stdout') (2)), + Success (Result (3))]) + ([Success (Channel ('stdout') (2)), + Success (Channel ('stdout') (1)), + Success (Result (3))])), + + Test ('failure due to output on the wrong channel') + ([Line (49) ('> (stdout (1), stdout (2), 3)')]) + ([Line (50) ('stdout (1)'), + Line (51) ('stderr (2)'), + Line (52) ('return 3')]) + (Incorrect ([Success (Channel ('stdout') (1)), + Success (Channel ('stdout') (2)), + Success (Result (3))]) + ([Success (Channel ('stdout') (1)), + Success (Channel ('stderr') (2)), + Success (Result (3))])), + + Test ('failure due to timing out') + ([Line (56) ('> (setTimeout (stdout, 125, 1), 2)')]) + ([Line (57) ('return 2'), + Line (58) ('stdout (1)')]) + (Incorrect ([Success (Result (2))]) + ([Success (Result (2)), + Success (Channel ('stdout') (1))])), + + Test ('success after a previous timeout') + ([Line (62) ('> (setTimeout (stdout, 75, 1), 2)')]) + ([Line (63) ('return 2'), + Line (64) ('stdout (1)')]) + (Correct ([Success (Result (2)), + Success (Channel ('stdout') (1))])), + +]; diff --git a/test/shared/async.coffee b/test/shared/async.coffee new file mode 100644 index 0000000..209a1d4 --- /dev/null +++ b/test/shared/async.coffee @@ -0,0 +1,7 @@ +# > stdout 1; +# . setTimeout stdout, 50, 2; +# . stderr 3; +# stdout 1 +# stderr 3 +# undefined +# stdout 2 diff --git a/test/shared/async.js b/test/shared/async.js new file mode 100644 index 0000000..a5e0a51 --- /dev/null +++ b/test/shared/async.js @@ -0,0 +1,7 @@ +// > ( stdout (1) +// . , setTimeout (stdout, 50, 2) +// . , stderr (3) ) +// stdout (1) +// stderr (3) +// undefined +// stdout (2) diff --git a/test/shared/index.coffee b/test/shared/index.coffee index dca6d05..c8b328e 100644 --- a/test/shared/index.coffee +++ b/test/shared/index.coffee @@ -32,10 +32,10 @@ do -> 8: 'RangeError captured and reported' # > 0.toString 1 - # ! RangeError + # throw new RangeError 9: 'TypeError expected but not reported' # > [].length - # ! TypeError + # throw new TypeError 10: 'function accessible before declaration' # > double(6) diff --git a/test/shared/index.js b/test/shared/index.js index cad5eb1..7cec857 100644 --- a/test/shared/index.js +++ b/test/shared/index.js @@ -32,10 +32,10 @@ global = 'global' 8, 'RangeError captured and reported' // > 0..toString(1) - // ! RangeError + // throw new RangeError 9, 'TypeError expected but not reported' // > [].length - // ! TypeError + // throw new TypeError 10, 'function accessible before declaration' // > double(6) @@ -64,7 +64,7 @@ global = 'global' // . 7,8,9] // [1,2,3,4,5,6,7,8,9] 14, 'multiline assignment' - // > string = "input " + + // > var string = "input " + // . "may span many " + // . "lines" // > string diff --git a/test/shared/results.coffee.js b/test/shared/results.coffee.js index 86b9414..9508d6e 100644 --- a/test/shared/results.coffee.js +++ b/test/shared/results.coffee.js @@ -1,115 +1,115 @@ -export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ +export default ({Test, Line, Incorrect, Correct, Failure, Success, Result}) => [ Test ('global variable accessible in outer scope') ([Line (2) ('> global')]) ([Line (3) ('"global"')]) - (Correct (Success ('global'))), + (Correct ([Success (Result ('global'))])), Test ('global variable accessible in inner scope') ([Line (9) ('> global')]) ([Line (10) ('"global"')]) - (Correct (Success ('global'))), + (Correct ([Success (Result ('global'))])), Test ('local variable referenced, not shadowed global') ([Line (13) ('> global')]) ([Line (14) ('"shadowed"')]) - (Correct (Success ('shadowed'))), + (Correct ([Success (Result ('shadowed'))])), Test ('local variable accessible before declaration') ([Line (19) ('> one * two')]) ([Line (20) ('2')]) - (Correct (Success (2))), + (Correct ([Success (Result (2))])), Test ('assignment is an expression') ([Line (24) ('> @three = one + two')]) ([Line (25) ('3')]) - (Correct (Success (3))), + (Correct ([Success (Result (3))])), Test ('variable declared in doctest remains accessible') ([Line (27) ('> [one, two, three]')]) ([Line (28) ('[1, 2, 3]')]) - (Correct (Success ([1, 2, 3]))), + (Correct ([Success (Result ([1, 2, 3]))])), Test ('arithmetic error reported') ([Line (30) ('> two + two')]) ([Line (31) ('5')]) - (Incorrect (Success (4)) - (Success (5))), + (Incorrect ([Success (Result (4))]) + ([Success (Result (5))])), Test ('RangeError captured and reported') ([Line (34) ('> 0.toString 1')]) - ([Line (35) ('! RangeError')]) - (Correct (Failure (new RangeError ('toString() radix argument must be between 2 and 36')))), + ([Line (35) ('throw new RangeError')]) + (Correct ([Failure (new RangeError ('toString() radix argument must be between 2 and 36'))])), Test ('TypeError expected but not reported') ([Line (37) ('> [].length')]) - ([Line (38) ('! TypeError')]) - (Incorrect (Success (0)) - (Failure (new TypeError ()))), + ([Line (38) ('throw new TypeError')]) + (Incorrect ([Success (Result (0))]) + ([Failure (new TypeError ())])), Test ('function accessible before declaration') ([Line (41) ('> double(6)')]) ([Line (42) ('12')]) - (Correct (Success (12))), + (Correct ([Success (Result (12))])), Test ('NaN can be used as expected result') ([Line (44) ('> double()')]) ([Line (45) ('NaN')]) - (Correct (Success (NaN))), + (Correct ([Success (Result (NaN))])), Test ('function accessible after declaration') ([Line (52) ('> double.call(null, 2)')]) ([Line (53) ('4')]) - (Correct (Success (4))), + (Correct ([Success (Result (4))])), Test ('multiline input') ([Line (62) ('> [1,2,3,'), Line (63) ('. 4,5,6,'), Line (64) ('. 7,8,9]')]) ([Line (65) ('[1,2,3,4,5,6,7,8,9]')]) - (Correct (Success ([1, 2, 3, 4, 5, 6, 7, 8, 9]))), + (Correct ([Success (Result ([1, 2, 3, 4, 5, 6, 7, 8, 9]))])), Test ('multiline assignment') ([Line (70) ('> string')]) ([Line (71) ('"input may span many lines"')]) - (Correct (Success ('input may span many lines'))), + (Correct ([Success (Result ('input may span many lines'))])), Test ("spaces following '#' and '>' are optional") ([Line (74) ('>"no spaces"')]) ([Line (75) ('"no spaces"')]) - (Correct (Success ('no spaces'))), + (Correct ([Success (Result ('no spaces'))])), Test ('indented doctest') ([Line (77) ('> "Docco-compatible whitespace"')]) ([Line (78) ('"Docco-compatible whitespace"')]) - (Correct (Success ('Docco-compatible whitespace'))), + (Correct ([Success (Result ('Docco-compatible whitespace'))])), Test ("'>' in doctest") ([Line (80) ('> 2 > 1')]) ([Line (81) ('true')]) - (Correct (Success (true))), + (Correct ([Success (Result (true))])), Test ('comment on input line') ([Line (84) ('> "foo" + "bar" # comment')]) ([Line (85) ('"foobar"')]) - (Correct (Success ('foobar'))), + (Correct ([Success (Result ('foobar'))])), Test ('comment on output line') ([Line (87) ('> 5 * 5')]) ([Line (88) ('25 # comment')]) - (Correct (Success (25))), + (Correct ([Success (Result (25))])), Test ('variable in creation context is not accessible') ([Line (91) ('> typeof Z')]) ([Line (92) ('"undefined"')]) - (Correct (Success ('undefined'))), + (Correct ([Success (Result ('undefined'))])), Test ("'.' should not follow leading '.' in multiline expressions") ([Line (95) ('>10 -'), Line (96) ('..5')]) ([Line (97) ('9.5')]) - (Incorrect (Success (5)) - (Success (9.5))), + (Incorrect ([Success (Result (5))]) + ([Success (Result (9.5))])), Test ("wrapped lines may begin with more than one '.'") ([Line (100) ('> 1000 +'), @@ -118,12 +118,12 @@ export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ Line (103) ('.... 4 +'), Line (104) ('..... .5')]) ([Line (105) ('1234.5')]) - (Correct (Success (1234.5))), + (Correct ([Success (Result (1234.5))])), Test ('multiline comment') ([Line (109) ('> 3 ** 3 - 2 ** 2')]) ([Line (110) ('23')]) - (Correct (Success (23))), + (Correct ([Success (Result (23))])), Test ('multiline comment with wrapped input') ([Line (115) ('> (["foo", "bar", "baz"]'), @@ -131,14 +131,14 @@ export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ Line (117) ('. .join(" ")'), Line (118) ('. .toUpperCase())')]) ([Line (119) ('"FOO BAR"')]) - (Correct (Success ('FOO BAR'))), + (Correct ([Success (Result ('FOO BAR'))])), Test ('multiline output') ([Line (139) ('> ["foo", "bar", "baz"]')]) ([Line (140) ('[ "foo"'), Line (141) ('. "bar"'), Line (142) ('. "baz" ]')]) - (Correct (Success (['foo', 'bar', 'baz']))), + (Correct ([Success (Result (['foo', 'bar', 'baz']))])), Test ('multiline input with multiline output') ([Line (145) ('> ["foo", "bar", "baz"]'), @@ -148,6 +148,6 @@ export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ ([Line (149) ('[ "FOO"'), Line (150) ('. "BAR"'), Line (151) ('. "BAZ" ]')]) - (Correct (Success (['FOO', 'BAR', 'BAZ']))), + (Correct ([Success (Result (['FOO', 'BAR', 'BAZ']))])), ]; diff --git a/test/shared/results.js b/test/shared/results.js index 7fca7e7..380cab8 100644 --- a/test/shared/results.js +++ b/test/shared/results.js @@ -1,115 +1,115 @@ -export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ +export default ({Test, Line, Incorrect, Correct, Failure, Success, Result}) => [ Test ('global variable accessible in outer scope') ([Line (2) ('> global')]) ([Line (3) ('"global"')]) - (Correct (Success ('global'))), + (Correct ([Success (Result ('global'))])), Test ('global variable accessible in inner scope') ([Line (9) ('> global')]) ([Line (10) ('"global"')]) - (Correct (Success ('global'))), + (Correct ([Success (Result ('global'))])), Test ('local variable referenced, not shadowed global') ([Line (13) ('> global')]) ([Line (14) ('"shadowed"')]) - (Correct (Success ('shadowed'))), + (Correct ([Success (Result ('shadowed'))])), Test ('local variable accessible before declaration') ([Line (19) ('> one * two')]) ([Line (20) ('2')]) - (Correct (Success (2))), + (Correct ([Success (Result (2))])), Test ('assignment is an expression') ([Line (24) ('> three = one + two')]) ([Line (25) ('3')]) - (Correct (Success (3))), + (Correct ([Success (Result (3))])), Test ('variable declared in doctest remains accessible') ([Line (27) ('> [one, two, three]')]) ([Line (28) ('[1, 2, 3]')]) - (Correct (Success ([1, 2, 3]))), + (Correct ([Success (Result ([1, 2, 3]))])), Test ('arithmetic error reported') ([Line (30) ('> two + two')]) ([Line (31) ('5')]) - (Incorrect (Success (4)) - (Success (5))), + (Incorrect ([Success (Result (4))]) + ([Success (Result (5))])), Test ('RangeError captured and reported') ([Line (34) ('> 0..toString(1)')]) - ([Line (35) ('! RangeError')]) - (Correct (Failure (new RangeError ('toString() radix argument must be between 2 and 36')))), + ([Line (35) ('throw new RangeError')]) + (Correct ([Failure (new RangeError ('toString() radix argument must be between 2 and 36'))])), Test ('TypeError expected but not reported') ([Line (37) ('> [].length')]) - ([Line (38) ('! TypeError')]) - (Incorrect (Success (0)) - (Failure (new TypeError ()))), + ([Line (38) ('throw new TypeError')]) + (Incorrect ([Success (Result (0))]) + ([Failure (new TypeError ())])), Test ('function accessible before declaration') ([Line (41) ('> double(6)')]) ([Line (42) ('12')]) - (Correct (Success (12))), + (Correct ([Success (Result (12))])), Test ('NaN can be used as expected result') ([Line (44) ('> double()')]) ([Line (45) ('NaN')]) - (Correct (Success (NaN))), + (Correct ([Success (Result (NaN))])), Test ('function accessible after declaration') ([Line (52) ('> double.call(null, 2)')]) ([Line (53) ('4')]) - (Correct (Success (4))), + (Correct ([Success (Result (4))])), Test ('multiline input') ([Line (62) ('> [1,2,3,'), Line (63) ('. 4,5,6,'), Line (64) ('. 7,8,9]')]) ([Line (65) ('[1,2,3,4,5,6,7,8,9]')]) - (Correct (Success ([1, 2, 3, 4, 5, 6, 7, 8, 9]))), + (Correct ([Success (Result ([1, 2, 3, 4, 5, 6, 7, 8, 9]))])), Test ('multiline assignment') ([Line (70) ('> string')]) ([Line (71) ('"input may span many lines"')]) - (Correct (Success ('input may span many lines'))), + (Correct ([Success (Result ('input may span many lines'))])), Test ("spaces following '//' and '>' are optional") ([Line (74) ('>"no spaces"')]) ([Line (75) ('"no spaces"')]) - (Correct (Success ('no spaces'))), + (Correct ([Success (Result ('no spaces'))])), Test ('indented doctest') ([Line (77) ('> "Docco-compatible whitespace"')]) ([Line (78) ('"Docco-compatible whitespace"')]) - (Correct (Success ('Docco-compatible whitespace'))), + (Correct ([Success (Result ('Docco-compatible whitespace'))])), Test ("'>' in doctest") ([Line (80) ('> 2 > 1')]) ([Line (81) ('true')]) - (Correct (Success (true))), + (Correct ([Success (Result (true))])), Test ('comment on input line') ([Line (84) ('> "foo" + "bar" // comment')]) ([Line (85) ('"foobar"')]) - (Correct (Success ('foobar'))), + (Correct ([Success (Result ('foobar'))])), Test ('comment on output line') ([Line (87) ('> 5 * 5')]) ([Line (88) ('25 // comment')]) - (Correct (Success (25))), + (Correct ([Success (Result (25))])), Test ('variable in creation context is not accessible') ([Line (91) ('> typeof Z')]) ([Line (92) ('"undefined"')]) - (Correct (Success ('undefined'))), + (Correct ([Success (Result ('undefined'))])), Test ("'.' should not follow leading '.' in multiline expressions") ([Line (95) ('>10 -'), Line (96) ('..5')]) ([Line (97) ('9.5')]) - (Incorrect (Success (5)) - (Success (9.5))), + (Incorrect ([Success (Result (5))]) + ([Success (Result (9.5))])), Test ("wrapped lines may begin with more than one '.'") ([Line (100) ('> 1000 +'), @@ -118,12 +118,12 @@ export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ Line (103) ('.... 4 +'), Line (104) ('..... .5')]) ([Line (105) ('1234.5')]) - (Correct (Success (1234.5))), + (Correct ([Success (Result (1234.5))])), Test ('multiline comment') ([Line (109) ('> Math.pow(3, 3) - Math.pow(2, 2)')]) ([Line (110) ('23')]) - (Correct (Success (23))), + (Correct ([Success (Result (23))])), Test ('multiline comment with wrapped input') ([Line (115) ('> ["foo", "bar", "baz"]'), @@ -131,31 +131,31 @@ export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ Line (117) ('. .join(" ")'), Line (118) ('. .toUpperCase()')]) ([Line (119) ('"FOO BAR"')]) - (Correct (Success ('FOO BAR'))), + (Correct ([Success (Result ('FOO BAR'))])), Test ('multiline comment with leading asterisks') ([Line (124) ('> 1 + 2 * 3 * 4')]) ([Line (125) ('25')]) - (Correct (Success (25))), + (Correct ([Success (Result (25))])), Test ('multiline comment with leading asterisks') ([Line (126) ('> 1 * 2 + 3 + 4 * 5')]) ([Line (127) ('25')]) - (Correct (Success (25))), + (Correct ([Success (Result (25))])), Test ('multiline comment with leading asterisks and wrapped input') ([Line (132) ('> (function fib(n) {'), Line (133) ('. return n == 0 || n == 1 ? n : fib(n - 2) + fib(n - 1);'), Line (134) ('. })(10)')]) ([Line (135) ('55')]) - (Correct (Success (55))), + (Correct ([Success (Result (55))])), Test ('multiline output') ([Line (139) ('> ["foo", "bar", "baz"]')]) ([Line (140) ('[ "foo",'), Line (141) ('. "bar",'), Line (142) ('. "baz" ]')]) - (Correct (Success (['foo', 'bar', 'baz']))), + (Correct ([Success (Result (['foo', 'bar', 'baz']))])), Test ('multiline input with multiline output') ([Line (145) ('> ["foo", "bar", "baz"]'), @@ -165,12 +165,12 @@ export default ({Test, Line, Incorrect, Correct, Failure, Success}) => [ ([Line (149) ('[ "FOO",'), Line (150) ('. "BAR",'), Line (151) ('. "BAZ" ]')]) - (Correct (Success (['FOO', 'BAR', 'BAZ']))), + (Correct ([Success (Result (['FOO', 'BAR', 'BAZ']))])), Test ('the rewriter should not rely on automatic semicolon insertion') ([Line (154) ('> "the rewriter should not rely"')]) ([Line (155) ('"on automatic semicolon insertion"')]) - (Incorrect (Success ('the rewriter should not rely')) - (Success ('on automatic semicolon insertion'))), + (Incorrect ([Success (Result ('the rewriter should not rely'))]) + ([Success (Result ('on automatic semicolon insertion'))])), ]; diff --git a/test/statements/results.js b/test/statements/results.js index 63c55fd..90f8e0a 100644 --- a/test/statements/results.js +++ b/test/statements/results.js @@ -1,18 +1,18 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('var') ([Line (3) ('> Math.sqrt(x)')]) ([Line (4) ('8')]) - (Correct (Success (8))), + (Correct ([Success (Result (8))])), Test ('let') ([Line (8) ('> Math.abs(y)')]) ([Line (9) ('1')]) - (Correct (Success (1))), + (Correct ([Success (Result (1))])), Test ('function declaration') ([Line (13) ('> fib(10)')]) ([Line (14) ('55')]) - (Correct (Success (55))), + (Correct ([Success (Result (55))])), ]; diff --git a/test/transcribe/results.js b/test/transcribe/results.js index 71e9ac3..4411033 100644 --- a/test/transcribe/results.js +++ b/test/transcribe/results.js @@ -1,8 +1,8 @@ -export default ({Test, Line, Correct, Success}) => [ +export default ({Test, Line, Correct, Success, Result}) => [ Test ('accepts Transcribe-style prefix') ([Line (11) ('> map(Math.sqrt)([1, 4, 9])')]) ([Line (12) ('[1, 2, 3]')]) - (Correct (Success ([1, 2, 3]))), + (Correct ([Success (Result ([1, 2, 3]))])), ];