terminal: fix arrow and 6-pack keys with modifiers for xterm.js

Fixed: b/271450792
Change-Id: If5142710851d3ee3f4f1069acd982de500a3b4e2
Reviewed-on: https://chromium-review.googlesource.com/c/apps/libapps/+/4310761
Reviewed-by: Joel Hockey <joelhockey@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/terminal/js/terminal_emulator_tests.js b/terminal/js/terminal_emulator_tests.js
index 62bd920..18a0a59 100644
--- a/terminal/js/terminal_emulator_tests.js
+++ b/terminal/js/terminal_emulator_tests.js
@@ -10,7 +10,7 @@
 
 import {sleep} from './terminal_common.js';
 import {A11yButtons, Modifier, XtermTerminal, XtermTerminalTestParams,
-  encodeKeyCombo} from './terminal_emulator.js';
+  encodeKeyCombo, keyCodes} from './terminal_emulator.js';
 import {MockFunction, MockObject} from './terminal_test_mocks.js';
 import {Terminal} from './xterm.js';
 
@@ -28,9 +28,10 @@
         xtermInternal: new MockObject({
           getActualCellDimensions: () => ({width: 9, height: 22}),
         }),
+        onVTKeystroke: new MockFunction(),
       };
       const testParams = {};
-      for (const prop in this.mocks) {
+      for (const prop of ['term', 'fontManager', 'xtermInternal']) {
         testParams[prop] = this.mocks[prop].proxy;
       }
 
@@ -41,6 +42,8 @@
         testParams: /** @type {!XtermTerminalTestParams} */(testParams),
       });
 
+      this.terminal.io.onVTKeystroke = this.mocks.onVTKeystroke.proxy;
+
       // Some hacking because we don't run the decorate() function. Maybe we
       // should just run it.
       this.terminal.container_ = /** @type {!Element} */({
@@ -98,35 +101,88 @@
       });
     });
 
-    it('customKeyEventHandler_', async function() {
-      const mockHandler = new MockFunction();
-      const fakeEvent = {
-        type: 'keydown',
-        keyCode: 65,
-        ctrlKey: true,
-      };
-      this.terminal.keyDownHandlers_.set(encodeKeyCombo(Modifier.Ctrl, 65),
-          mockHandler.proxy);
-      assert.isFalse(this.terminal.customKeyEventHandler_(fakeEvent));
-      const history = mockHandler.popHistory();
-      assert.equal(history.length, 1);
-      assert.equal(history[0][0], fakeEvent);
+    describe('handleKeyEvent_', function() {
+      it('keyDownHandlers_', async function() {
+        const mockHandler = new MockFunction();
+        const fakeEvent = {
+          type: 'keydown',
+          keyCode: 65,
+          ctrlKey: true,
+        };
+        this.terminal.keyDownHandlers_.set(encodeKeyCombo(Modifier.Ctrl, 65),
+            mockHandler.proxy);
+        assert.isTrue(this.terminal.handleKeyEvent_(fakeEvent));
+        const history = mockHandler.popHistory();
+        assert.equal(history.length, 1);
+        assert.equal(history[0][0], fakeEvent);
 
-      assert.isFalse(this.terminal.customKeyEventHandler_({...fakeEvent,
-        type: 'keypress'}));
-      assert.isEmpty(mockHandler.popHistory());
+        assert.isTrue(this.terminal.handleKeyEvent_({...fakeEvent,
+          type: 'keypress'}));
+        assert.isEmpty(mockHandler.popHistory());
 
-      assert.isTrue(this.terminal.customKeyEventHandler_({...fakeEvent,
-        shiftKey: true}));
-      assert.isEmpty(mockHandler.popHistory());
+        assert.isFalse(this.terminal.handleKeyEvent_({...fakeEvent,
+          shiftKey: true}));
+        assert.isEmpty(mockHandler.popHistory());
 
-      assert.isTrue(this.terminal.customKeyEventHandler_({...fakeEvent,
-        keyCode: 66}));
-      assert.isEmpty(mockHandler.popHistory());
+        assert.isFalse(this.terminal.handleKeyEvent_({...fakeEvent,
+          keyCode: 66}));
+        assert.isEmpty(mockHandler.popHistory());
 
-      assert.isTrue(this.terminal.customKeyEventHandler_({...fakeEvent,
-        ctrlKey: false}));
-      assert.isEmpty(mockHandler.popHistory());
+        assert.isFalse(this.terminal.handleKeyEvent_({...fakeEvent,
+          ctrlKey: false}));
+        assert.isEmpty(mockHandler.popHistory());
+      });
+
+      it('arrow keys and 6 pack keys', async function() {
+        const check = (ev, handled, vtKeystroke) => {
+          const mockPreventDefault = new MockFunction();
+          const mockStopPropagation = new MockFunction();
+          assert.equal(this.terminal.handleKeyEvent_({
+            type: 'keydown',
+            preventDefault: mockPreventDefault.proxy,
+            stopPropagation: mockStopPropagation.proxy,
+            ...ev,
+          }), handled);
+          const history = this.mocks.onVTKeystroke.popHistory();
+          if (vtKeystroke) {
+            assert.deepEqual(history, [[vtKeystroke]]);
+          } else {
+            assert.isEmpty(history);
+          }
+          assert.equal(mockPreventDefault.getHistory().length, handled ? 1 : 0);
+          assert.equal(mockStopPropagation.getHistory().length,
+              handled ? 1 : 0);
+        };
+
+        check({keyCode: keyCodes.UP}, false, null);
+        check({keyCode: keyCodes.UP, shiftKey: true}, true, '\x1b[1;2A');
+        check({keyCode: keyCodes.UP, altKey: true}, true, '\x1b[1;3A');
+        check({keyCode: keyCodes.UP, shiftKey: true, altKey: true}, true,
+            '\x1b[1;4A');
+
+        check({keyCode: keyCodes.INSERT}, false, null);
+        check({keyCode: keyCodes.INSERT, altKey: true}, true, '\x1b[2;3~');
+        check({keyCode: keyCodes.INSERT, shiftKey: true, altKey: true}, true,
+            '\x1b[2;4~');
+
+        check({keyCode: keyCodes.HOME}, false, null);
+        check({keyCode: keyCodes.HOME, altKey: true}, true, '\x1b[1;3H');
+        check({keyCode: keyCodes.HOME, shiftKey: true, altKey: true}, true,
+            '\x1b[1;4H');
+
+        // Shift+HOME should scroll the page.
+        assert.equal(this.mocks.term.getMethodHistory('scrollToTop').length, 0);
+        check({keyCode: keyCodes.HOME, shiftKey: true}, true, null);
+        assert.equal(this.mocks.term.getMethodHistory('scrollToTop').length, 1);
+
+        // For non-keydown event, if a modifier key is depressed, we do nothing
+        // but `handleKeyEvent_()` will return true to prevent xterm.js from
+        // handling it.
+        check({type: 'keypress', keyCode: keyCodes.HOME, altKey: true}, true,
+            undefined);
+        // If there is no modifiers, we still pass through it to xterm.js.
+        check({type: 'keypress', keyCode: keyCodes.HOME}, false, null);
+      });
     });
   });