]> git.proxmox.com Git - mirror_xterm.js.git/blob - src/test/test.js
Add test for scrollback fix
[mirror_xterm.js.git] / src / test / test.js
1 var assert = require('chai').assert;
2 var expect = require('chai').expect;
3 var Terminal = require('../xterm');
4
5 describe('xterm.js', function() {
6 var xterm;
7
8 beforeEach(function () {
9 xterm = new Terminal();
10 xterm.refresh = function(){};
11 xterm.viewport = {
12 syncScrollArea: function(){}
13 };
14 xterm.compositionHelper = {
15 keydown: function(){ return true; }
16 };
17 // Force synchronous writes
18 xterm.write = function(data) {
19 xterm.writeBuffer.push(data);
20 xterm.innerWrite();
21 };
22 xterm.element = {
23 classList: {
24 toggle: function(){},
25 remove: function(){}
26 }
27 };
28 });
29
30 describe('getOption', function() {
31 it('should retrieve the option correctly', function() {
32 // In the `options` namespace.
33 xterm.options.cursorBlink = true;
34 assert.equal(xterm.getOption('cursorBlink'), true);
35
36 // On the Terminal instance
37 delete xterm.options.cursorBlink;
38 xterm.cursorBlink = false;
39 assert.equal(xterm.getOption('cursorBlink'), false);
40 });
41 it('should throw when retrieving a non-existant option', function() {
42 assert.throws(xterm.getOption.bind(xterm, 'fake', true));
43 });
44 });
45
46 describe('setOption', function() {
47 it('should set the option correctly', function() {
48 xterm.setOption('cursorBlink', true);
49 assert.equal(xterm.cursorBlink, true);
50 assert.equal(xterm.options.cursorBlink, true);
51 xterm.setOption('cursorBlink', false);
52 assert.equal(xterm.cursorBlink, false);
53 assert.equal(xterm.options.cursorBlink, false);
54 });
55 it('should throw when setting a non-existant option', function() {
56 assert.throws(xterm.setOption.bind(xterm, 'fake', true));
57 });
58 it('should not allow scrollback less than number of rows', function() {
59 let setOptionCall = xterm.setOption.bind(xterm, 'scrollback', xterm.rows - 1);
60
61 assert.equal(setOptionCall(), false);
62 });
63 });
64
65 describe('clear', function() {
66 it('should clear a buffer equal to rows', function() {
67 var promptLine = xterm.lines.get(xterm.ybase + xterm.y);
68 xterm.clear();
69 assert.equal(xterm.y, 0);
70 assert.equal(xterm.ybase, 0);
71 assert.equal(xterm.ydisp, 0);
72 assert.equal(xterm.lines.length, xterm.rows);
73 assert.deepEqual(xterm.lines.get(0), promptLine);
74 for (var i = 1; i < xterm.rows; i++) {
75 assert.deepEqual(xterm.lines.get(i), xterm.blankLine());
76 }
77 });
78 it('should clear a buffer larger than rows', function() {
79 // Fill the buffer with dummy rows
80 for (var i = 0; i < xterm.rows * 2; i++) {
81 xterm.write('test\n');
82 }
83
84 var promptLine = xterm.lines.get(xterm.ybase + xterm.y);
85 xterm.clear();
86 assert.equal(xterm.y, 0);
87 assert.equal(xterm.ybase, 0);
88 assert.equal(xterm.ydisp, 0);
89 assert.equal(xterm.lines.length, xterm.rows);
90 assert.deepEqual(xterm.lines.get(0), promptLine);
91 for (var i = 1; i < xterm.rows; i++) {
92 assert.deepEqual(xterm.lines.get(i), xterm.blankLine());
93 }
94 });
95 it('should not break the prompt when cleared twice', function() {
96 var promptLine = xterm.lines.get(xterm.ybase + xterm.y);
97 xterm.clear();
98 xterm.clear();
99 assert.equal(xterm.y, 0);
100 assert.equal(xterm.ybase, 0);
101 assert.equal(xterm.ydisp, 0);
102 assert.equal(xterm.lines.length, xterm.rows);
103 assert.deepEqual(xterm.lines.get(0), promptLine);
104 for (var i = 1; i < xterm.rows; i++) {
105 assert.deepEqual(xterm.lines.get(i), xterm.blankLine());
106 }
107 });
108 });
109
110 describe('scroll', function() {
111 describe('scrollDisp', function() {
112 var startYDisp;
113 beforeEach(function() {
114 for (var i = 0; i < xterm.rows * 2; i++) {
115 xterm.writeln('test');
116 }
117 startYDisp = xterm.rows + 1;
118 });
119 it('should scroll a single line', function() {
120 assert.equal(xterm.ydisp, startYDisp);
121 xterm.scrollDisp(-1);
122 assert.equal(xterm.ydisp, startYDisp - 1);
123 xterm.scrollDisp(1);
124 assert.equal(xterm.ydisp, startYDisp);
125 });
126 it('should scroll multiple lines', function() {
127 assert.equal(xterm.ydisp, startYDisp);
128 xterm.scrollDisp(-5);
129 assert.equal(xterm.ydisp, startYDisp - 5);
130 xterm.scrollDisp(5);
131 assert.equal(xterm.ydisp, startYDisp);
132 });
133 it('should not scroll beyond the bounds of the buffer', function() {
134 assert.equal(xterm.ydisp, startYDisp);
135 xterm.scrollDisp(1);
136 assert.equal(xterm.ydisp, startYDisp);
137 for (var i = 0; i < startYDisp; i++) {
138 xterm.scrollDisp(-1);
139 }
140 assert.equal(xterm.ydisp, 0);
141 xterm.scrollDisp(-1);
142 assert.equal(xterm.ydisp, 0);
143 });
144 });
145
146 describe('scrollPages', function() {
147 var startYDisp;
148 beforeEach(function() {
149 for (var i = 0; i < xterm.rows * 3; i++) {
150 xterm.writeln('test');
151 }
152 startYDisp = (xterm.rows * 2) + 1;
153 });
154 it('should scroll a single page', function() {
155 assert.equal(xterm.ydisp, startYDisp);
156 xterm.scrollPages(-1);
157 assert.equal(xterm.ydisp, startYDisp - (xterm.rows - 1));
158 xterm.scrollPages(1);
159 assert.equal(xterm.ydisp, startYDisp);
160 });
161 it('should scroll a multiple pages', function() {
162 assert.equal(xterm.ydisp, startYDisp);
163 xterm.scrollPages(-2);
164 assert.equal(xterm.ydisp, startYDisp - (xterm.rows - 1) * 2);
165 xterm.scrollPages(2);
166 assert.equal(xterm.ydisp, startYDisp);
167 });
168 });
169
170 describe('scrollToTop', function() {
171 beforeEach(function() {
172 for (var i = 0; i < xterm.rows * 3; i++) {
173 xterm.writeln('test');
174 }
175 });
176 it('should scroll to the top', function() {
177 assert.notEqual(xterm.ydisp, 0);
178 xterm.scrollToTop();
179 assert.equal(xterm.ydisp, 0);
180 });
181 });
182
183 describe('scrollToBottom', function() {
184 var startYDisp;
185 beforeEach(function() {
186 for (var i = 0; i < xterm.rows * 3; i++) {
187 xterm.writeln('test');
188 }
189 startYDisp = (xterm.rows * 2) + 1;
190 });
191 it('should scroll to the bottom', function() {
192 xterm.scrollDisp(-1);
193 xterm.scrollToBottom();
194 assert.equal(xterm.ydisp, startYDisp);
195 xterm.scrollPages(-1);
196 xterm.scrollToBottom();
197 assert.equal(xterm.ydisp, startYDisp);
198 xterm.scrollToTop();
199 xterm.scrollToBottom();
200 assert.equal(xterm.ydisp, startYDisp);
201 });
202 });
203
204 describe('keyDown', function () {
205 it('should scroll down, when a key is pressed and terminal is scrolled up', function () {
206 // Override evaluateKeyEscapeSequence to return cancel code
207 xterm.evaluateKeyEscapeSequence = function() {
208 return { key: 'a' };
209 };
210 var event = {
211 type: 'keydown',
212 keyCode: 0,
213 preventDefault: function(){},
214 stopPropagation: function(){}
215 };
216
217 xterm.ydisp = 0;
218 xterm.ybase = 40;
219 assert.notEqual(xterm.ydisp, xterm.ybase);
220 xterm.keyDown(event);
221
222 // Ensure that now the terminal is scrolled to bottom
223 assert.equal(xterm.ydisp, xterm.ybase);
224 });
225
226 it('should not scroll down, when a custom keydown handler prevents the event', function () {
227 // Add some output to the terminal
228 for (var i = 0; i < xterm.rows * 3; i++) {
229 xterm.writeln('test');
230 }
231 var startYDisp = (xterm.rows * 2) + 1;
232 xterm.attachCustomKeydownHandler(function () {
233 return false;
234 });
235
236 assert.equal(xterm.ydisp, startYDisp);
237 xterm.scrollDisp(-1);
238 assert.equal(xterm.ydisp, startYDisp - 1);
239 xterm.keyDown({ keyCode: 0 });
240 assert.equal(xterm.ydisp, startYDisp - 1);
241 });
242 });
243 });
244
245 describe('evaluateKeyEscapeSequence', function() {
246 it('should return the correct escape sequence for unmodified keys', function() {
247 // Backspace
248 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 8 }).key, '\x7f'); // ^?
249 // Tab
250 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 9 }).key, '\t');
251 // Return/enter
252 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 13 }).key, '\r'); // CR
253 // Escape
254 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 27 }).key, '\x1b');
255 // Page up, page down
256 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 33 }).key, '\x1b[5~'); // CSI 5 ~
257 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 34 }).key, '\x1b[6~'); // CSI 6 ~
258 // End, Home
259 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 35 }).key, '\x1b[F'); // SS3 F
260 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 36 }).key, '\x1b[H'); // SS3 H
261 // Left, up, right, down arrows
262 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 37 }).key, '\x1b[D'); // CSI D
263 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 38 }).key, '\x1b[A'); // CSI A
264 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 39 }).key, '\x1b[C'); // CSI C
265 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 40 }).key, '\x1b[B'); // CSI B
266 // Insert
267 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 45 }).key, '\x1b[2~'); // CSI 2 ~
268 // Delete
269 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 46 }).key, '\x1b[3~'); // CSI 3 ~
270 // F1-F12
271 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 112 }).key, '\x1bOP'); // SS3 P
272 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 113 }).key, '\x1bOQ'); // SS3 Q
273 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 114 }).key, '\x1bOR'); // SS3 R
274 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 115 }).key, '\x1bOS'); // SS3 S
275 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 116 }).key, '\x1b[15~'); // CSI 1 5 ~
276 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 117 }).key, '\x1b[17~'); // CSI 1 7 ~
277 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 118 }).key, '\x1b[18~'); // CSI 1 8 ~
278 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 119 }).key, '\x1b[19~'); // CSI 1 9 ~
279 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 120 }).key, '\x1b[20~'); // CSI 2 0 ~
280 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 121 }).key, '\x1b[21~'); // CSI 2 1 ~
281 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 122 }).key, '\x1b[23~'); // CSI 2 3 ~
282 assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 123 }).key, '\x1b[24~'); // CSI 2 4 ~
283 });
284 it('should return \\x1b[3;5~ for ctrl+delete', function() {
285 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 46 }).key, '\x1b[3;5~');
286 });
287 it('should return \\x1b[3;2~ for shift+delete', function() {
288 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 46 }).key, '\x1b[3;2~');
289 });
290 it('should return \\x1b[3;3~ for alt+delete', function() {
291 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 46 }).key, '\x1b[3;3~');
292 });
293 it('should return \\x1b[5D for ctrl+left', function() {
294 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 37 }).key, '\x1b[1;5D'); // CSI 5 D
295 });
296 it('should return \\x1b[5C for ctrl+right', function() {
297 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C
298 });
299 it('should return \\x1b[5A for ctrl+up', function() {
300 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A
301 });
302 it('should return \\x1b[5B for ctrl+down', function() {
303 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B
304 });
305
306 describe('On non-macOS platforms', function() {
307 beforeEach(function() {
308 xterm.browser.isMac = false;
309 });
310 // Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100
311 // http://unix.stackexchange.com/a/108106
312 it('should return \\x1b[5D for alt+left', function() {
313 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 37 }).key, '\x1b[1;5D'); // CSI 5 D
314 });
315 it('should return \\x1b[5C for alt+right', function() {
316 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C
317 });
318 });
319
320 describe('On macOS platforms', function() {
321 beforeEach(function() {
322 xterm.browser.isMac = true;
323 });
324 it('should return \\x1bb for alt+left', function() {
325 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 37 }).key, '\x1bb'); // CSI 5 D
326 });
327 it('should return \\x1bf for alt+right', function() {
328 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 39 }).key, '\x1bf'); // CSI 5 C
329 });
330 });
331
332 it('should return \\x1b[5A for alt+up', function() {
333 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A
334 });
335 it('should return \\x1b[5B for alt+down', function() {
336 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B
337 });
338 it('should return the correct escape sequence for modified F1-F12 keys', function() {
339 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 112 }).key, '\x1b[1;2P');
340 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 113 }).key, '\x1b[1;2Q');
341 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 114 }).key, '\x1b[1;2R');
342 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 115 }).key, '\x1b[1;2S');
343 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 116 }).key, '\x1b[15;2~');
344 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 117 }).key, '\x1b[17;2~');
345 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 118 }).key, '\x1b[18;2~');
346 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 119 }).key, '\x1b[19;2~');
347 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 120 }).key, '\x1b[20;2~');
348 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 121 }).key, '\x1b[21;2~');
349 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 122 }).key, '\x1b[23;2~');
350 assert.equal(xterm.evaluateKeyEscapeSequence({ shiftKey: true, keyCode: 123 }).key, '\x1b[24;2~');
351 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 112 }).key, '\x1b[1;3P');
352 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 113 }).key, '\x1b[1;3Q');
353 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 114 }).key, '\x1b[1;3R');
354 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 115 }).key, '\x1b[1;3S');
355 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 116 }).key, '\x1b[15;3~');
356 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 117 }).key, '\x1b[17;3~');
357 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 118 }).key, '\x1b[18;3~');
358 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 119 }).key, '\x1b[19;3~');
359 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 120 }).key, '\x1b[20;3~');
360 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 121 }).key, '\x1b[21;3~');
361 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 122 }).key, '\x1b[23;3~');
362 assert.equal(xterm.evaluateKeyEscapeSequence({ altKey: true, keyCode: 123 }).key, '\x1b[24;3~');
363
364 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 112 }).key, '\x1b[1;5P');
365 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 113 }).key, '\x1b[1;5Q');
366 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 114 }).key, '\x1b[1;5R');
367 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 115 }).key, '\x1b[1;5S');
368 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 116 }).key, '\x1b[15;5~');
369 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 117 }).key, '\x1b[17;5~');
370 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 118 }).key, '\x1b[18;5~');
371 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 119 }).key, '\x1b[19;5~');
372 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 120 }).key, '\x1b[20;5~');
373 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 121 }).key, '\x1b[21;5~');
374 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 122 }).key, '\x1b[23;5~');
375 assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 123 }).key, '\x1b[24;5~');
376 });
377 });
378
379 describe('attachCustomKeyEventHandler', function () {
380 var evKeyDown = {
381 preventDefault: function() {},
382 stopPropagation: function() {},
383 type: 'keydown'
384 }
385 var evKeyPress = {
386 preventDefault: function() {},
387 stopPropagation: function() {},
388 type: 'keypress'
389 }
390
391 beforeEach(function() {
392 xterm.handler = function() {};
393 xterm.showCursor = function() {};
394 xterm.clearSelection = function() {};
395 xterm.compositionHelper = {
396 keydown: {
397 bind: function() {
398 return function () { return true; }
399 }
400 },
401 keypress: {
402 bind: function() {
403 return function () { return true; }
404 }
405 }
406 }
407 });
408
409 it('should process the keydown/keypress event based on what the handler returns', function () {
410 assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), true);
411 assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), true);
412 xterm.attachCustomKeyEventHandler(function (ev) {
413 return ev.keyCode === 77;
414 });
415 assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), true);
416 assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), true);
417 xterm.attachCustomKeyEventHandler(function (ev) {
418 return ev.keyCode !== 77;
419 });
420 assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false);
421 assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false);
422 });
423
424 it('should alive after reset(ESC c Full Reset (RIS))', function () {
425 xterm.attachCustomKeyEventHandler(function (ev) {
426 return ev.keyCode !== 77;
427 });
428 assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false);
429 assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false);
430 xterm.reset();
431 assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false);
432 assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false);
433 });
434 });
435
436 describe('Third level shift', function() {
437 var evKeyDown = {
438 preventDefault: function() {},
439 stopPropagation: function() {},
440 type: 'keydown'
441 },
442 evKeyPress = {
443 preventDefault: function() {},
444 stopPropagation: function() {},
445 type: 'keypress'
446 };
447
448 beforeEach(function() {
449 xterm.handler = function() {};
450 xterm.showCursor = function() {};
451 xterm.clearSelection = function() {};
452 xterm.compositionHelper = {
453 isComposing: false,
454 keydown: {
455 bind: function() {
456 return function() { return true; };
457 }
458 }
459 };
460 });
461
462 describe('On Mac OS', function() {
463 beforeEach(function() {
464 xterm.browser.isMac = true;
465 });
466
467 it('should not interfere with the alt key on keyDown', function() {
468 assert.equal(
469 xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, keyCode: 81 })),
470 true
471 );
472 assert.equal(
473 xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, keyCode: 192 })),
474 true
475 );
476 });
477
478 it('should interefere with the alt + arrow keys', function() {
479 assert.equal(
480 xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, keyCode: 37 })),
481 false
482 );
483 assert.equal(
484 xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, keyCode: 39 })),
485 false
486 );
487 });
488
489 it('should emit key with alt + key on keyPress', function(done) {
490 var keys = ['@', '@', '\\', '\\', '|', '|'];
491
492 xterm.on('keypress', function(key) {
493 if (key) {
494 var index = keys.indexOf(key);
495 assert(index !== -1, "Emitted wrong key: " + key);
496 keys.splice(index, 1);
497 }
498 if (keys.length === 0) done();
499 });
500
501 xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, keyCode: 64 })); // @
502 // Firefox
503 xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, charCode: 64, keyCode: 0 }));
504 xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, keyCode: 92 })); // \
505 xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, charCode: 92, keyCode: 0 }));
506 xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, keyCode: 124 })); // |
507 xterm.keyPress(Object.assign({}, evKeyPress, { altKey: true, charCode: 124, keyCode: 0 }));
508 });
509 });
510
511 describe('On MS Windows', function() {
512 beforeEach(function() {
513 xterm.browser.isMSWindows = true;
514 });
515
516 it('should not interfere with the alt + ctrl key on keyDown', function() {
517 assert.equal(
518 xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, ctrlKey: true, keyCode: 81 })),
519 true
520 );
521 assert.equal(
522 xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, ctrlKey: true, keyCode: 192 })),
523 true
524 );
525 });
526
527 it('should interefere with the alt + ctrl + arrow keys', function() {
528 assert.equal(
529 xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, ctrlKey: true, keyCode: 37 })),
530 false
531 );
532 assert.equal(
533 xterm.keyDown(Object.assign({}, evKeyDown, { altKey: true, ctrlKey: true, keyCode: 39 })),
534 false
535 );
536 });
537
538 it('should emit key with alt + ctrl + key on keyPress', function(done) {
539 var keys = ['@', '@', '\\', '\\', '|', '|'];
540
541 xterm.on('keypress', function(key) {
542 if (key) {
543 var index = keys.indexOf(key);
544 assert(index !== -1, "Emitted wrong key: " + key);
545 keys.splice(index, 1);
546 }
547 if (keys.length === 0) done();
548 });
549
550 xterm.keyPress(
551 Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, keyCode: 64 })
552 ); // @
553 xterm.keyPress(
554 Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, charCode: 64, keyCode: 0 })
555 );
556 xterm.keyPress(
557 Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, keyCode: 92 })
558 ); // \
559 xterm.keyPress(
560 Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, charCode: 92, keyCode: 0 })
561 );
562 xterm.keyPress(
563 Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, keyCode: 124 })
564 ); // |
565 xterm.keyPress(
566 Object.assign({}, evKeyPress, { altKey: true, ctrlKey: true, charCode: 124, keyCode: 0 })
567 );
568 });
569 });
570 });
571
572 describe('unicode - surrogates', function() {
573 it('2 characters per cell', function () {
574 this.timeout(10000); // This is needed because istanbul patches code and slows it down
575 var high = String.fromCharCode(0xD800);
576 for (var i=0xDC00; i<=0xDCFF; ++i) {
577 xterm.write(high + String.fromCharCode(i));
578 var tchar = xterm.lines.get(0)[0];
579 expect(tchar[1]).eql(high + String.fromCharCode(i));
580 expect(tchar[1].length).eql(2);
581 expect(tchar[2]).eql(1);
582 expect(xterm.lines.get(0)[1][1]).eql(' ');
583 xterm.reset();
584 }
585 });
586 it('2 characters at last cell', function() {
587 var high = String.fromCharCode(0xD800);
588 for (var i=0xDC00; i<=0xDCFF; ++i) {
589 xterm.x = xterm.cols - 1;
590 xterm.write(high + String.fromCharCode(i));
591 expect(xterm.lines.get(0)[xterm.x-1][1]).eql(high + String.fromCharCode(i));
592 expect(xterm.lines.get(0)[xterm.x-1][1].length).eql(2);
593 expect(xterm.lines.get(1)[0][1]).eql(' ');
594 xterm.reset();
595 }
596 });
597 it('2 characters per cell over line end with autowrap', function() {
598 var high = String.fromCharCode(0xD800);
599 for (var i=0xDC00; i<=0xDCFF; ++i) {
600 xterm.x = xterm.cols - 1;
601 xterm.wraparoundMode = true;
602 xterm.write('a' + high + String.fromCharCode(i));
603 expect(xterm.lines.get(0)[xterm.cols-1][1]).eql('a');
604 expect(xterm.lines.get(1)[0][1]).eql(high + String.fromCharCode(i));
605 expect(xterm.lines.get(1)[0][1].length).eql(2);
606 expect(xterm.lines.get(1)[1][1]).eql(' ');
607 xterm.reset();
608 }
609 });
610 it('2 characters per cell over line end without autowrap', function() {
611 var high = String.fromCharCode(0xD800);
612 for (var i=0xDC00; i<=0xDCFF; ++i) {
613 xterm.x = xterm.cols - 1;
614 xterm.wraparoundMode = false;
615 xterm.write('a' + high + String.fromCharCode(i));
616 // auto wraparound mode should cut off the rest of the line
617 expect(xterm.lines.get(0)[xterm.cols-1][1]).eql('a');
618 expect(xterm.lines.get(0)[xterm.cols-1][1].length).eql(1);
619 expect(xterm.lines.get(1)[1][1]).eql(' ');
620 xterm.reset();
621 }
622 });
623 it('splitted surrogates', function() {
624 var high = String.fromCharCode(0xD800);
625 for (var i=0xDC00; i<=0xDCFF; ++i) {
626 xterm.write(high);
627 xterm.write(String.fromCharCode(i));
628 var tchar = xterm.lines.get(0)[0];
629 expect(tchar[1]).eql(high + String.fromCharCode(i));
630 expect(tchar[1].length).eql(2);
631 expect(tchar[2]).eql(1);
632 expect(xterm.lines.get(0)[1][1]).eql(' ');
633 xterm.reset();
634 }
635 });
636 });
637
638 describe('unicode - combining characters', function() {
639 it('café', function () {
640 xterm.write('cafe\u0301');
641 expect(xterm.lines.get(0)[3][1]).eql('e\u0301');
642 expect(xterm.lines.get(0)[3][1].length).eql(2);
643 expect(xterm.lines.get(0)[3][2]).eql(1);
644 });
645 it('café - end of line', function() {
646 xterm.x = xterm.cols - 1 - 3;
647 xterm.write('cafe\u0301');
648 expect(xterm.lines.get(0)[xterm.cols-1][1]).eql('e\u0301');
649 expect(xterm.lines.get(0)[xterm.cols-1][1].length).eql(2);
650 expect(xterm.lines.get(0)[xterm.cols-1][2]).eql(1);
651 expect(xterm.lines.get(0)[1][1]).eql(' ');
652 expect(xterm.lines.get(0)[1][1].length).eql(1);
653 expect(xterm.lines.get(0)[1][2]).eql(1);
654 });
655 it('multiple combined é', function() {
656 xterm.wraparoundMode = true;
657 xterm.write(Array(100).join('e\u0301'));
658 for (var i=0; i<xterm.cols; ++i) {
659 var tchar = xterm.lines.get(0)[i];
660 expect(tchar[1]).eql('e\u0301');
661 expect(tchar[1].length).eql(2);
662 expect(tchar[2]).eql(1);
663 }
664 tchar = xterm.lines.get(1)[0];
665 expect(tchar[1]).eql('e\u0301');
666 expect(tchar[1].length).eql(2);
667 expect(tchar[2]).eql(1);
668 });
669 it('multiple surrogate with combined', function() {
670 xterm.wraparoundMode = true;
671 xterm.write(Array(100).join('\uD800\uDC00\u0301'));
672 for (var i=0; i<xterm.cols; ++i) {
673 var tchar = xterm.lines.get(0)[i];
674 expect(tchar[1]).eql('\uD800\uDC00\u0301');
675 expect(tchar[1].length).eql(3);
676 expect(tchar[2]).eql(1);
677 }
678 tchar = xterm.lines.get(1)[0];
679 expect(tchar[1]).eql('\uD800\uDC00\u0301');
680 expect(tchar[1].length).eql(3);
681 expect(tchar[2]).eql(1);
682 });
683 });
684
685 describe('unicode - fullwidth characters', function() {
686 it('cursor movement even', function() {
687 expect(xterm.x).eql(0);
688 xterm.write('¥');
689 expect(xterm.x).eql(2);
690 });
691 it('cursor movement odd', function() {
692 xterm.x = 1;
693 expect(xterm.x).eql(1);
694 xterm.write('¥');
695 expect(xterm.x).eql(3);
696 });
697 it('line of ¥ even', function() {
698 xterm.wraparoundMode = true;
699 xterm.write(Array(50).join('¥'));
700 for (var i=0; i<xterm.cols; ++i) {
701 var tchar = xterm.lines.get(0)[i];
702 if (i % 2) {
703 expect(tchar[1]).eql('');
704 expect(tchar[1].length).eql(0);
705 expect(tchar[2]).eql(0);
706 } else {
707 expect(tchar[1]).eql('¥');
708 expect(tchar[1].length).eql(1);
709 expect(tchar[2]).eql(2);
710 }
711 }
712 tchar = xterm.lines.get(1)[0];
713 expect(tchar[1]).eql('¥');
714 expect(tchar[1].length).eql(1);
715 expect(tchar[2]).eql(2);
716 });
717 it('line of ¥ odd', function() {
718 xterm.wraparoundMode = true;
719 xterm.x = 1;
720 xterm.write(Array(50).join('¥'));
721 for (var i=1; i<xterm.cols-1; ++i) {
722 var tchar = xterm.lines.get(0)[i];
723 if (!(i % 2)) {
724 expect(tchar[1]).eql('');
725 expect(tchar[1].length).eql(0);
726 expect(tchar[2]).eql(0);
727 } else {
728 expect(tchar[1]).eql('¥');
729 expect(tchar[1].length).eql(1);
730 expect(tchar[2]).eql(2);
731 }
732 }
733 tchar = xterm.lines.get(0)[xterm.cols-1];
734 expect(tchar[1]).eql(' ');
735 expect(tchar[1].length).eql(1);
736 expect(tchar[2]).eql(1);
737 tchar = xterm.lines.get(1)[0];
738 expect(tchar[1]).eql('¥');
739 expect(tchar[1].length).eql(1);
740 expect(tchar[2]).eql(2);
741 });
742 it('line of ¥ with combining odd', function() {
743 xterm.wraparoundMode = true;
744 xterm.x = 1;
745 xterm.write(Array(50).join('¥\u0301'));
746 for (var i=1; i<xterm.cols-1; ++i) {
747 var tchar = xterm.lines.get(0)[i];
748 if (!(i % 2)) {
749 expect(tchar[1]).eql('');
750 expect(tchar[1].length).eql(0);
751 expect(tchar[2]).eql(0);
752 } else {
753 expect(tchar[1]).eql('¥\u0301');
754 expect(tchar[1].length).eql(2);
755 expect(tchar[2]).eql(2);
756 }
757 }
758 tchar = xterm.lines.get(0)[xterm.cols-1];
759 expect(tchar[1]).eql(' ');
760 expect(tchar[1].length).eql(1);
761 expect(tchar[2]).eql(1);
762 tchar = xterm.lines.get(1)[0];
763 expect(tchar[1]).eql('¥\u0301');
764 expect(tchar[1].length).eql(2);
765 expect(tchar[2]).eql(2);
766 });
767 it('line of ¥ with combining even', function() {
768 xterm.wraparoundMode = true;
769 xterm.write(Array(50).join('¥\u0301'));
770 for (var i=0; i<xterm.cols; ++i) {
771 var tchar = xterm.lines.get(0)[i];
772 if (i % 2) {
773 expect(tchar[1]).eql('');
774 expect(tchar[1].length).eql(0);
775 expect(tchar[2]).eql(0);
776 } else {
777 expect(tchar[1]).eql('¥\u0301');
778 expect(tchar[1].length).eql(2);
779 expect(tchar[2]).eql(2);
780 }
781 }
782 tchar = xterm.lines.get(1)[0];
783 expect(tchar[1]).eql('¥\u0301');
784 expect(tchar[1].length).eql(2);
785 expect(tchar[2]).eql(2);
786 });
787 it('line of surrogate fullwidth with combining odd', function() {
788 xterm.wraparoundMode = true;
789 xterm.x = 1;
790 xterm.write(Array(50).join('\ud843\ude6d\u0301'));
791 for (var i=1; i<xterm.cols-1; ++i) {
792 var tchar = xterm.lines.get(0)[i];
793 if (!(i % 2)) {
794 expect(tchar[1]).eql('');
795 expect(tchar[1].length).eql(0);
796 expect(tchar[2]).eql(0);
797 } else {
798 expect(tchar[1]).eql('\ud843\ude6d\u0301');
799 expect(tchar[1].length).eql(3);
800 expect(tchar[2]).eql(2);
801 }
802 }
803 tchar = xterm.lines.get(0)[xterm.cols-1];
804 expect(tchar[1]).eql(' ');
805 expect(tchar[1].length).eql(1);
806 expect(tchar[2]).eql(1);
807 tchar = xterm.lines.get(1)[0];
808 expect(tchar[1]).eql('\ud843\ude6d\u0301');
809 expect(tchar[1].length).eql(3);
810 expect(tchar[2]).eql(2);
811 });
812 it('line of surrogate fullwidth with combining even', function() {
813 xterm.wraparoundMode = true;
814 xterm.write(Array(50).join('\ud843\ude6d\u0301'));
815 for (var i=0; i<xterm.cols; ++i) {
816 var tchar = xterm.lines.get(0)[i];
817 if (i % 2) {
818 expect(tchar[1]).eql('');
819 expect(tchar[1].length).eql(0);
820 expect(tchar[2]).eql(0);
821 } else {
822 expect(tchar[1]).eql('\ud843\ude6d\u0301');
823 expect(tchar[1].length).eql(3);
824 expect(tchar[2]).eql(2);
825 }
826 }
827 tchar = xterm.lines.get(1)[0];
828 expect(tchar[1]).eql('\ud843\ude6d\u0301');
829 expect(tchar[1].length).eql(3);
830 expect(tchar[2]).eql(2);
831 });
832 });
833
834 describe('insert mode', function() {
835 it('halfwidth - all', function () {
836 xterm.write(Array(9).join('0123456789').slice(-80));
837 xterm.x = 10;
838 xterm.y = 0;
839 xterm.insertMode = true;
840 xterm.write('abcde');
841 expect(xterm.lines.get(0).length).eql(xterm.cols);
842 expect(xterm.lines.get(0)[10][1]).eql('a');
843 expect(xterm.lines.get(0)[14][1]).eql('e');
844 expect(xterm.lines.get(0)[15][1]).eql('0');
845 expect(xterm.lines.get(0)[79][1]).eql('4');
846 });
847 it('fullwidth - insert', function() {
848 xterm.write(Array(9).join('0123456789').slice(-80));
849 xterm.x = 10;
850 xterm.y = 0;
851 xterm.insertMode = true;
852 xterm.write('¥¥¥');
853 expect(xterm.lines.get(0).length).eql(xterm.cols);
854 expect(xterm.lines.get(0)[10][1]).eql('¥');
855 expect(xterm.lines.get(0)[11][1]).eql('');
856 expect(xterm.lines.get(0)[14][1]).eql('¥');
857 expect(xterm.lines.get(0)[15][1]).eql('');
858 expect(xterm.lines.get(0)[79][1]).eql('3');
859 });
860 it('fullwidth - right border', function() {
861 xterm.write(Array(41).join('¥'));
862 xterm.x = 10;
863 xterm.y = 0;
864 xterm.insertMode = true;
865 xterm.write('a');
866 expect(xterm.lines.get(0).length).eql(xterm.cols);
867 expect(xterm.lines.get(0)[10][1]).eql('a');
868 expect(xterm.lines.get(0)[11][1]).eql('¥');
869 expect(xterm.lines.get(0)[79][1]).eql(' '); // fullwidth char got replaced
870 xterm.write('b');
871 expect(xterm.lines.get(0).length).eql(xterm.cols);
872 expect(xterm.lines.get(0)[11][1]).eql('b');
873 expect(xterm.lines.get(0)[12][1]).eql('¥');
874 expect(xterm.lines.get(0)[79][1]).eql(''); // empty cell after fullwidth
875 });
876 });
877 });