From 47bd05e9a00a293ed69e6ea099b68bb86d97cf6e Mon Sep 17 00:00:00 2001 From: Alon Salant Date: Mon, 21 Jan 2013 10:02:04 -0800 Subject: [PATCH] REPL tests based on direct interaction with input and output stream. Includes multiline tests. --- Cakefile | 1 + lib/coffee-script/repl.js | 2 +- src/repl.coffee | 2 +- test/repl.coffee | 106 +++++++++++++++++++++++++------------- 4 files changed, 74 insertions(+), 37 deletions(-) diff --git a/Cakefile b/Cakefile index b148ee54..3f3020ef 100644 --- a/Cakefile +++ b/Cakefile @@ -172,6 +172,7 @@ runTests = (CoffeeScript) -> # Convenience aliases. global.CoffeeScript = CoffeeScript + global.Repl = require './lib/coffee-script/repl' # Our test helper function for delimiting different test cases. global.test = (description, fn) -> diff --git a/lib/coffee-script/repl.js b/lib/coffee-script/repl.js index 2d6d5cd8..f3057827 100644 --- a/lib/coffee-script/repl.js +++ b/lib/coffee-script/repl.js @@ -67,7 +67,7 @@ rli.prompt(true); return; } - if (!rli.line.match(/^\s*$/)) { + if (!((rli.line == null) || rli.line.match(/^\s*$/))) { return; } multiline.enabled = !multiline.enabled; diff --git a/src/repl.coffee b/src/repl.coffee index 52c7628f..0b3766eb 100644 --- a/src/repl.coffee +++ b/src/repl.coffee @@ -54,7 +54,7 @@ addMultilineHandler = (repl) -> rli.prompt true return # no-op unless the current line is empty - return unless rli.line.match /^\s*$/ + return unless not rli.line? or rli.line.match /^\s*$/ # eval, print, loop multiline.enabled = not multiline.enabled rli.line = '' diff --git a/test/repl.coffee b/test/repl.coffee index d5051d8b..940651aa 100644 --- a/test/repl.coffee +++ b/test/repl.coffee @@ -1,46 +1,82 @@ # REPL # ---- +Stream = require 'stream' -# TODO: add more tests -{spawn} = require 'child_process' -PROMPT = 'coffee> ' +class MockInputStream extends Stream + constructor: () -> + @readable = true -testOutput = (expected, actual) -> - eq expected, actual.slice(0, expected.length) - actual.substr expected.length + resume: -> -testCommands = (input, expectedOutput) -> - input = [input] if typeof input is 'string' - expectedOutput = [expectedOutput] if typeof expectedOutput in ['string', 'undefined'] - output = '' - coffee = spawn 'bin/coffee' - input.push 'process.exit()' + emitLine: (val) -> + @emit 'data', new Buffer("#{val}\n") - coffee.stdout.on 'data', (data) -> - output += data.toString().replace(/\u001b\[\d{0,2}m/g, '') - coffee.stdin.write "#{input.shift()}\n" +class MockOutputStream extends Stream + constructor: () -> + @writable = true + @written = [] - coffee.on 'exit', -> - output = testOutput PROMPT, output - while expectedOutput.length > 0 - output = testOutput "#{expectedOutput.shift()}\n#{PROMPT}", output - eq '', output + write: (data) -> + #console.log 'output write', arguments + @written.push data -test "comments are ignored", -> - testCommands "1 + 1 #foo", "2" + lastWrite: (fromEnd=-1) -> + @written[@written.length - 1 + fromEnd].replace /\n$/, '' -test "output in inspect mode", -> - testCommands '"1 + 1\\n"', "'1 + 1\\n'" +testRepl = (desc, fn) -> + input = new MockInputStream() + output = new MockOutputStream() + Repl.start {input, output} + fn input, output -test "variables are saved", -> - input = [ - "foo = 'foo'" - 'foobar = "#{foo}bar"' - ] - testCommands input, [ - "'foo'" - "'foobar'" - ] +assertEqual = (expected, value) -> + eq expected, value, "Expected '#{value}' to equal '#{expected}'" -test "empty command evaluates to undefined", -> - testCommands "", undefined + +testRepl "starts with coffee prompt", (input, output) -> + assertEqual 'coffee> ', output.lastWrite(0) + +testRepl "writes eval to output", (input, output) -> + input.emitLine '1+1' + assertEqual '2', output.lastWrite() + +testRepl "comments are ignored", (input, output) -> + input.emitLine '1 + 1 #foo' + assertEqual '2', output.lastWrite() + +testRepl "output in inspect mode", (input, output) -> + input.emitLine '"1 + 1\\n"' + assertEqual "'1 + 1\\n'", output.lastWrite() + +testRepl "variables are saved", (input, output) -> + input.emitLine "foo = 'foo'" + input.emitLine 'foobar = "#{foo}bar"' + assertEqual "'foobar'", output.lastWrite() + +testRepl "empty command evaluates to undefined", (input, output) -> + input.emitLine '' + assertEqual 'undefined', output.lastWrite() + +ctrlV = { ctrl: true, name: 'v'} +testRepl "ctrl-v toggles multiline prompt", (input, output) -> + input.emit 'keypress', null, ctrlV + assertEqual '------> ', output.lastWrite(0) + input.emit 'keypress', null, ctrlV + assertEqual 'coffee> ', output.lastWrite(0) + +testRepl "multiline continuation changes prompt", (input, output) -> + input.emit 'keypress', null, ctrlV + input.emitLine '' + assertEqual '....... ', output.lastWrite(0) + +testRepl "evaluates multiline", (input, output) -> + # Stubs. Could assert on their use. + output.cursorTo = (pos) -> + output.clearLine = -> + + input.emit 'keypress', null, ctrlV + input.emitLine '(->' + input.emitLine ' 1 + 1' + input.emitLine ')()' + input.emit 'keypress', null, ctrlV + assertEqual '2', output.lastWrite()