// Copyright Joyent, Inc. and other Node contributors. // // 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. var assert = require('assert'); var readline = require('readline'); var EventEmitter = require('events').EventEmitter; var inherits = require('util').inherits; function FakeInput() { EventEmitter.call(this); } inherits(FakeInput, EventEmitter); FakeInput.prototype.resume = function() {}; FakeInput.prototype.pause = function() {}; FakeInput.prototype.write = function() {}; FakeInput.prototype.end = function() {}; [ true, false ].forEach(function(terminal) { var fi; var rli; var called; // sending a full line fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: terminal }); called = false; rli.on('line', function(line) { called = true; assert.equal(line, 'asdf'); }); fi.emit('data', 'asdf\n'); assert.ok(called); // sending a blank line fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: terminal }); called = false; rli.on('line', function(line) { called = true; assert.equal(line, ''); }); fi.emit('data', '\n'); assert.ok(called); // sending a single character with no newline fi = new FakeInput(); rli = new readline.Interface(fi, {}); called = false; rli.on('line', function(line) { called = true; }); fi.emit('data', 'a'); assert.ok(!called); rli.close(); // sending a single character with no newline and then a newline fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: terminal }); called = false; rli.on('line', function(line) { called = true; assert.equal(line, 'a'); }); fi.emit('data', 'a'); assert.ok(!called); fi.emit('data', '\n'); assert.ok(called); rli.close(); // sending multiple newlines at once fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: terminal }); var expectedLines = ['foo', 'bar', 'baz']; var callCount = 0; rli.on('line', function(line) { assert.equal(line, expectedLines[callCount]); callCount++; }); fi.emit('data', expectedLines.join('\n') + '\n'); assert.equal(callCount, expectedLines.length); rli.close(); // sending multiple newlines at once that does not end with a new line fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: terminal }); expectedLines = ['foo', 'bar', 'baz', 'bat']; callCount = 0; rli.on('line', function(line) { assert.equal(line, expectedLines[callCount]); callCount++; }); fi.emit('data', expectedLines.join('\n')); assert.equal(callCount, expectedLines.length - 1); rli.close(); // \r\n should emit one line event, not two fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: terminal }); expectedLines = ['foo', 'bar', 'baz', 'bat']; callCount = 0; rli.on('line', function(line) { assert.equal(line, expectedLines[callCount]); callCount++; }); fi.emit('data', expectedLines.join('\r\n')); assert.equal(callCount, expectedLines.length - 1); rli.close(); // \r\n should emit one line event when split across multiple writes. fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: terminal }); expectedLines = ['foo', 'bar', 'baz', 'bat']; callCount = 0; rli.on('line', function(line) { assert.equal(line, expectedLines[callCount]); callCount++; }); expectedLines.forEach(function(line) { fi.emit('data', line + '\r'); fi.emit('data', '\n'); }); assert.equal(callCount, expectedLines.length); rli.close(); // \r should behave like \n when alone fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: true }); expectedLines = ['foo', 'bar', 'baz', 'bat']; callCount = 0; rli.on('line', function(line) { assert.equal(line, expectedLines[callCount]); callCount++; }); fi.emit('data', expectedLines.join('\r')); assert.equal(callCount, expectedLines.length - 1); rli.close(); // \r at start of input should output blank line fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: true }); expectedLines = ['', 'foo' ]; callCount = 0; rli.on('line', function(line) { assert.equal(line, expectedLines[callCount]); callCount++; }); fi.emit('data', '\rfoo\r'); assert.equal(callCount, expectedLines.length); rli.close(); // sending a multi-byte utf8 char over multiple writes var buf = Buffer('☮', 'utf8'); fi = new FakeInput(); rli = new readline.Interface({ input: fi, output: fi, terminal: terminal }); callCount = 0; rli.on('line', function(line) { callCount++; assert.equal(line, buf.toString('utf8')); }); [].forEach.call(buf, function(i) { fi.emit('data', Buffer([i])); }); assert.equal(callCount, 0); fi.emit('data', '\n'); assert.equal(callCount, 1); rli.close(); assert.deepEqual(fi.listeners('end'), []); assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []); });