@@ -8,6 +8,7 @@ const REPL = require('internal/repl');
8
8
const assert = require ( 'assert' ) ;
9
9
const fs = require ( 'fs' ) ;
10
10
const path = require ( 'path' ) ;
11
+ const { inspect } = require ( 'util' ) ;
11
12
12
13
const tmpdir = require ( '../common/tmpdir' ) ;
13
14
tmpdir . refresh ( ) ;
@@ -17,6 +18,7 @@ const defaultHistoryPath = path.join(tmpdir.path, '.node_repl_history');
17
18
// Create an input stream specialized for testing an array of actions
18
19
class ActionStream extends stream . Stream {
19
20
run ( data ) {
21
+ let reallyWait = true ;
20
22
const _iter = data [ Symbol . iterator ] ( ) ;
21
23
const doAction = ( ) => {
22
24
const next = _iter . next ( ) ;
@@ -32,24 +34,34 @@ class ActionStream extends stream.Stream {
32
34
} else {
33
35
this . emit ( 'data' , `${ action } ` ) ;
34
36
}
35
- setImmediate ( doAction ) ;
37
+ if ( action === WAIT && reallyWait ) {
38
+ setTimeout ( doAction , common . platformTimeout ( 50 ) ) ;
39
+ reallyWait = false ;
40
+ } else {
41
+ setImmediate ( doAction ) ;
42
+ }
36
43
} ;
37
- setImmediate ( doAction ) ;
44
+ doAction ( ) ;
38
45
}
39
46
resume ( ) { }
40
47
pause ( ) { }
41
48
}
42
49
ActionStream . prototype . readable = true ;
43
50
44
-
45
51
// Mock keys
46
52
const ENTER = { name : 'enter' } ;
47
53
const UP = { name : 'up' } ;
48
54
const DOWN = { name : 'down' } ;
49
55
const LEFT = { name : 'left' } ;
56
+ const RIGHT = { name : 'right' } ;
50
57
const DELETE = { name : 'delete' } ;
58
+ const BACKSPACE = { name : 'backspace' } ;
59
+ const WORD_LEFT = { name : 'left' , ctrl : true } ;
60
+ const WORD_RIGHT = { name : 'right' , ctrl : true } ;
61
+ const GO_TO_END = { name : 'end' } ;
51
62
52
63
const prompt = '> ' ;
64
+ const WAIT = '€' ;
53
65
54
66
const prev = process . features . inspector ;
55
67
@@ -91,6 +103,162 @@ const tests = [
91
103
' 2025, 2116, 2209, ...' ,
92
104
prompt ] . filter ( ( e ) => typeof e === 'string' ) ,
93
105
clean : true
106
+ } ,
107
+ {
108
+ env : { NODE_REPL_HISTORY : defaultHistoryPath } ,
109
+ skip : ! process . features . inspector ,
110
+ test : [
111
+ `const ${ 'veryLongName' . repeat ( 30 ) } = 'I should not be previewed'` ,
112
+ ENTER ,
113
+ 'const e = new RangeError("visible\\ninvisible")' ,
114
+ ENTER ,
115
+ 'e' ,
116
+ ENTER ,
117
+ 'veryLongName' . repeat ( 30 ) ,
118
+ ENTER ,
119
+ `${ '\x1B[90m \x1B[39m' . repeat ( 235 ) } fun` ,
120
+ ENTER ,
121
+ `${ ' ' . repeat ( 236 ) } fun` ,
122
+ ENTER
123
+ ] ,
124
+ expected : [ ] ,
125
+ clean : false
126
+ } ,
127
+ {
128
+ env : { NODE_REPL_HISTORY : defaultHistoryPath } ,
129
+ columns : 250 ,
130
+ skip : ! process . features . inspector ,
131
+ test : [
132
+ UP ,
133
+ UP ,
134
+ UP ,
135
+ UP ,
136
+ BACKSPACE
137
+ ] ,
138
+ expected : [
139
+ prompt ,
140
+ // This exceeds the maximum columns (250):
141
+ // Whitespace + prompt + ' // '.length + 'function'.length
142
+ // 236 + 2 + 4 + 8
143
+ `${ prompt } ${ ' ' . repeat ( 236 ) } fun` ,
144
+ `${ prompt } ${ ' ' . repeat ( 235 ) } fun` ,
145
+ ' // ction' ,
146
+ ' // ction' ,
147
+ `${ prompt } ${ 'veryLongName' . repeat ( 30 ) } ` ,
148
+ `${ prompt } e` ,
149
+ '\n// RangeError: visible' ,
150
+ prompt
151
+ ] ,
152
+ clean : true
153
+ } ,
154
+ {
155
+ env : { NODE_REPL_HISTORY : defaultHistoryPath } ,
156
+ showEscapeCodes : true ,
157
+ skip : ! process . features . inspector ,
158
+ test : [
159
+ 'fun' ,
160
+ RIGHT ,
161
+ BACKSPACE ,
162
+ LEFT ,
163
+ LEFT ,
164
+ 'A' ,
165
+ BACKSPACE ,
166
+ GO_TO_END ,
167
+ BACKSPACE ,
168
+ WORD_LEFT ,
169
+ WORD_RIGHT ,
170
+ ENTER
171
+ ] ,
172
+ // C = Cursor forward
173
+ // D = Cursor back
174
+ // G = Cursor to column n
175
+ // J = Erase in screen
176
+ // K = Erase in line
177
+ expected : [
178
+ // 0.
179
+ // 'f'
180
+ '\x1B[1G' , '\x1B[0J' , prompt , '\x1B[3G' , 'f' ,
181
+ // 'u'
182
+ 'u' , ' // nction' , '\x1B[5G' ,
183
+ // 'n' - Cleanup
184
+ '\x1B[0K' ,
185
+ 'n' , ' // ction' , '\x1B[6G' ,
186
+ // 1. Right. Cleanup
187
+ '\x1B[0K' ,
188
+ 'ction' ,
189
+ // 2. Backspace. Refresh
190
+ '\x1B[1G' , '\x1B[0J' , `${ prompt } functio` , '\x1B[10G' ,
191
+ // Autocomplete and refresh?
192
+ ' // n' , '\x1B[10G' , ' // n' , '\x1B[10G' ,
193
+ // 3. Left. Cleanup
194
+ '\x1B[0K' ,
195
+ '\x1B[1D' , '\x1B[10G' , ' // n' , '\x1B[9G' ,
196
+ // 4. Left. Cleanup
197
+ '\x1B[10G' , '\x1B[0K' , '\x1B[9G' ,
198
+ '\x1B[1D' , '\x1B[10G' , ' // n' , '\x1B[8G' ,
199
+ // 5. 'A' - Cleanup
200
+ '\x1B[10G' , '\x1B[0K' , '\x1B[8G' ,
201
+ // Refresh
202
+ '\x1B[1G' , '\x1B[0J' , `${ prompt } functAio` , '\x1B[9G' ,
203
+ // 6. Backspace. Refresh
204
+ '\x1B[1G' , '\x1B[0J' , `${ prompt } functio` , '\x1B[8G' , '\x1B[10G' , ' // n' ,
205
+ '\x1B[8G' , '\x1B[10G' , ' // n' ,
206
+ '\x1B[8G' , '\x1B[10G' ,
207
+ // 7. Go to end. Cleanup
208
+ '\x1B[0K' , '\x1B[8G' , '\x1B[2C' ,
209
+ 'n' ,
210
+ // 8. Backspace. Refresh
211
+ '\x1B[1G' , '\x1B[0J' , `${ prompt } functio` , '\x1B[10G' ,
212
+ // Autocomplete
213
+ ' // n' , '\x1B[10G' , ' // n' , '\x1B[10G' ,
214
+ // 9. Word left. Cleanup
215
+ '\x1B[0K' , '\x1B[7D' , '\x1B[10G' , ' // n' , '\x1B[3G' , '\x1B[10G' ,
216
+ // 10. Word right. Cleanup
217
+ '\x1B[0K' , '\x1B[3G' , '\x1B[7C' , ' // n' , '\x1B[10G' ,
218
+ '\x1B[0K' ,
219
+ ] ,
220
+ clean : true
221
+ } ,
222
+ {
223
+ // Check that the completer ignores completions that are outdated.
224
+ env : { NODE_REPL_HISTORY : defaultHistoryPath } ,
225
+ completer ( line , callback ) {
226
+ if ( line . endsWith ( WAIT ) ) {
227
+ setTimeout (
228
+ callback ,
229
+ common . platformTimeout ( 40 ) ,
230
+ null ,
231
+ [ [ `${ WAIT } WOW` ] , line ]
232
+ ) ;
233
+ } else {
234
+ callback ( null , [ [ ' Always visible' ] , line ] ) ;
235
+ }
236
+ } ,
237
+ skip : ! process . features . inspector ,
238
+ test : [
239
+ WAIT , // The first call is awaited before new input is triggered!
240
+ BACKSPACE ,
241
+ 's' ,
242
+ BACKSPACE ,
243
+ WAIT , // The second call is not awaited. It won't trigger the preview.
244
+ BACKSPACE ,
245
+ 's' ,
246
+ BACKSPACE
247
+ ] ,
248
+ expected : [
249
+ prompt ,
250
+ WAIT ,
251
+ ' // WOW' ,
252
+ prompt ,
253
+ 's' ,
254
+ ' // Always visible' ,
255
+ prompt ,
256
+ WAIT ,
257
+ prompt ,
258
+ 's' ,
259
+ ' // Always visible' ,
260
+ ] ,
261
+ clean : true
94
262
}
95
263
] ;
96
264
const numtests = tests . length ;
@@ -112,29 +280,47 @@ function runTest() {
112
280
const opts = tests . shift ( ) ;
113
281
if ( ! opts ) return ; // All done
114
282
115
- const env = opts . env ;
116
- const test = opts . test ;
117
- const expected = opts . expected ;
283
+ const { expected, skip } = opts ;
284
+
285
+ // Test unsupported on platform.
286
+ if ( skip ) {
287
+ setImmediate ( runTestWrap , true ) ;
288
+ return ;
289
+ }
290
+
291
+ const lastChunks = [ ] ;
118
292
119
- REPL . createInternalRepl ( env , {
293
+ REPL . createInternalRepl ( opts . env , {
120
294
input : new ActionStream ( ) ,
121
295
output : new stream . Writable ( {
122
296
write ( chunk , _ , next ) {
123
297
const output = chunk . toString ( ) ;
124
298
125
- if ( output . charCodeAt ( 0 ) === 27 || / ^ [ \r \n ] + $ / . test ( output ) ) {
299
+ if ( ! opts . showEscapeCodes &&
300
+ output . charCodeAt ( 0 ) === 27 || / ^ [ \r \n ] + $ / . test ( output ) ) {
126
301
return next ( ) ;
127
302
}
128
303
304
+ lastChunks . push ( inspect ( output ) ) ;
305
+
129
306
if ( expected . length ) {
130
- assert . strictEqual ( output , expected [ 0 ] ) ;
307
+ try {
308
+ assert . strictEqual ( output , expected [ 0 ] ) ;
309
+ } catch ( e ) {
310
+ console . error ( `Failed test # ${ numtests - tests . length } ` ) ;
311
+ console . error ( 'Last outputs: ' + inspect ( lastChunks , {
312
+ breakLength : 5 , colors : true
313
+ } ) ) ;
314
+ throw e ;
315
+ }
131
316
expected . shift ( ) ;
132
317
}
133
318
134
319
next ( ) ;
135
320
}
136
321
} ) ,
137
- prompt : prompt ,
322
+ completer : opts . completer ,
323
+ prompt,
138
324
useColors : false ,
139
325
terminal : true
140
326
} , function ( err , repl ) {
@@ -153,7 +339,13 @@ function runTest() {
153
339
setImmediate ( runTestWrap , true ) ;
154
340
} ) ;
155
341
156
- repl . inputStream . run ( test ) ;
342
+ if ( opts . columns ) {
343
+ Object . defineProperty ( repl , 'columns' , {
344
+ value : opts . columns ,
345
+ enumerable : true
346
+ } ) ;
347
+ }
348
+ repl . inputStream . run ( opts . test ) ;
157
349
} ) ;
158
350
}
159
351
0 commit comments