|
| 1 | +import type { ReplOptions, REPLServer } from 'repl'; |
| 2 | +import { start } from 'repl'; |
| 3 | +import type { Readable, Writable } from 'stream'; |
| 4 | +import { PassThrough } from 'stream'; |
| 5 | +import { tick } from '../test/repl-helpers'; |
| 6 | +import { installPasteSupport } from './repl-paste-support'; |
| 7 | +import { expect } from 'chai'; |
| 8 | + |
| 9 | +function createTerminalRepl(extraOpts: Partial<ReplOptions> = {}): { |
| 10 | + input: Writable; |
| 11 | + output: Readable; |
| 12 | + repl: REPLServer; |
| 13 | +} { |
| 14 | + const input = new PassThrough(); |
| 15 | + const output = new PassThrough({ encoding: 'utf8' }); |
| 16 | + |
| 17 | + const repl = start({ |
| 18 | + input: input, |
| 19 | + output: output, |
| 20 | + prompt: '> ', |
| 21 | + terminal: true, |
| 22 | + useColors: false, |
| 23 | + ...extraOpts, |
| 24 | + }); |
| 25 | + return { input, output, repl }; |
| 26 | +} |
| 27 | + |
| 28 | +describe('installPasteSupport', function () { |
| 29 | + it('does nothing for non-terminal REPL instances', async function () { |
| 30 | + const { repl, output } = createTerminalRepl({ terminal: false }); |
| 31 | + const onFinish = installPasteSupport(repl); |
| 32 | + await tick(); |
| 33 | + expect(output.read()).to.equal('> '); |
| 34 | + expect(onFinish).to.equal(''); |
| 35 | + }); |
| 36 | + |
| 37 | + it('prints a control character sequence that indicates support for bracketed paste', async function () { |
| 38 | + const { repl, output } = createTerminalRepl(); |
| 39 | + const onFinish = installPasteSupport(repl); |
| 40 | + await tick(); |
| 41 | + expect(output.read()).to.include('\x1B[?2004h'); |
| 42 | + expect(onFinish).to.include('\x1B[?2004l'); |
| 43 | + }); |
| 44 | + |
| 45 | + it('echoes back control characters in the input by default', async function () { |
| 46 | + const { repl, input, output } = createTerminalRepl(); |
| 47 | + installPasteSupport(repl); |
| 48 | + await tick(); |
| 49 | + output.read(); // Ignore prompt etc. |
| 50 | + input.write('foo\x1b[Dbar'); // ESC[D = 1 character to the left |
| 51 | + await tick(); |
| 52 | + expect(output.read()).to.equal( |
| 53 | + 'foo\x1B[1D\x1B[1G\x1B[0J> fobo\x1B[6G\x1B[1G\x1B[0J> fobao\x1B[7G\x1B[1G\x1B[0J> fobaro\x1B[8G' |
| 54 | + ); |
| 55 | + }); |
| 56 | + |
| 57 | + it('ignores control characters in the input while pasting', async function () { |
| 58 | + const { repl, input, output } = createTerminalRepl(); |
| 59 | + installPasteSupport(repl); |
| 60 | + await tick(); |
| 61 | + output.read(); // Ignore prompt etc. |
| 62 | + input.write('\x1b[200~foo\x1b[Dbar\x1b[201~'); // ESC[D = 1 character to the left |
| 63 | + await tick(); |
| 64 | + expect(output.read()).to.equal('foobar'); |
| 65 | + }); |
| 66 | + |
| 67 | + it('resets to accepting control characters in the input after pasting', async function () { |
| 68 | + const { repl, input, output } = createTerminalRepl(); |
| 69 | + installPasteSupport(repl); |
| 70 | + await tick(); |
| 71 | + output.read(); |
| 72 | + input.write('\x1b[200~foo\x1b[Dbar\x1b[201~'); // ESC[D = 1 character to the left |
| 73 | + await tick(); |
| 74 | + output.read(); |
| 75 | + input.write('foo\x1b[Dbar'); |
| 76 | + await tick(); |
| 77 | + expect(output.read()).to.equal( |
| 78 | + 'foo\x1B[1D\x1B[1G\x1B[0J> foobarfobo\x1B[12G\x1B[1G\x1B[0J> foobarfobao\x1B[13G\x1B[1G\x1B[0J> foobarfobaro\x1B[14G' |
| 79 | + ); |
| 80 | + }); |
| 81 | + |
| 82 | + it('allows a few special characters while pasting', async function () { |
| 83 | + const { repl, input, output } = createTerminalRepl(); |
| 84 | + installPasteSupport(repl); |
| 85 | + await tick(); |
| 86 | + output.read(); |
| 87 | + input.write('\x1b[200~12*34\n_*_\n\x1b[201~'); |
| 88 | + await tick(); |
| 89 | + expect(output.read()).to.include((12 * 34) ** 2); |
| 90 | + }); |
| 91 | +}); |
0 commit comments