Mike Frysinger | 598e801 | 2022-09-07 08:38:34 -0400 | [diff] [blame] | 1 | // Copyright 2022 The ChromiumOS Authors |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | /** |
| 6 | * @fileoverview Unit tests for terminal_emulator.js. |
| 7 | */ |
| 8 | |
Mike Frysinger | 75895da | 2022-10-04 00:42:28 +0545 | [diff] [blame] | 9 | import {lib} from './deps_local.concat.js'; |
| 10 | |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 11 | import {sleep} from './terminal_common.js'; |
Jason Lin | a8adea5 | 2022-10-25 13:14:14 +1100 | [diff] [blame] | 12 | import {A11yButtons, Modifier, XtermTerminal, XtermTerminalTestParams, |
| 13 | encodeKeyCombo} from './terminal_emulator.js'; |
Jason Lin | 5690e75 | 2022-08-30 15:36:45 +1000 | [diff] [blame] | 14 | import {MockFunction, MockObject} from './terminal_test_mocks.js'; |
Jason Lin | a8adea5 | 2022-10-25 13:14:14 +1100 | [diff] [blame] | 15 | import {Terminal} from './xterm.js'; |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 16 | |
| 17 | describe('terminal_emulator_tests.js', function() { |
| 18 | describe('XtermTerminal', function() { |
| 19 | beforeEach(async function() { |
| 20 | this.mocks = { |
Jason Lin | e9231bc | 2022-09-01 13:54:02 +1000 | [diff] [blame] | 21 | term: new MockObject({ |
| 22 | options: {}, |
| 23 | parser: { |
| 24 | registerOscHandler: () => {}, |
| 25 | }, |
| 26 | }), |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 27 | fontManager: new MockObject(), |
Jason Lin | 2649da2 | 2022-10-12 10:16:44 +1100 | [diff] [blame] | 28 | xtermInternal: new MockObject({ |
| 29 | getActualCellDimensions: () => ({width: 9, height: 22}), |
| 30 | }), |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 31 | }; |
| 32 | const testParams = {}; |
| 33 | for (const prop in this.mocks) { |
| 34 | testParams[prop] = this.mocks[prop].proxy; |
| 35 | } |
| 36 | |
| 37 | this.terminal = new XtermTerminal({ |
| 38 | storage: new lib.Storage.Memory(), |
| 39 | profileId: 'test', |
| 40 | enableWebGL: true, |
| 41 | testParams: /** @type {!XtermTerminalTestParams} */(testParams), |
| 42 | }); |
Jason Lin | c2504ae | 2022-09-02 13:03:31 +1000 | [diff] [blame] | 43 | |
| 44 | // Some hacking because we don't run the decorate() function. Maybe we |
| 45 | // should just run it. |
| 46 | this.terminal.container_ = /** @type {!Element} */({ |
| 47 | offsetWidth: 1000, |
| 48 | offsetHeight: 500, |
| 49 | }); |
Jason Lin | 8de3d28 | 2022-09-01 21:29:05 +1000 | [diff] [blame] | 50 | this.terminal.inited_ = true; |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 51 | }); |
| 52 | |
| 53 | describe('updateFont_()', function() { |
| 54 | it('updates font', async function() { |
| 55 | const updateFontPromise = this.terminal.updateFont_('font one'); |
| 56 | assert.deepEqual( |
| 57 | await this.mocks.fontManager.whenCalled('loadFont'), |
| 58 | [['font one']]); |
| 59 | assert.equal(this.mocks.term.baseObj.options.fontFamily, undefined); |
| 60 | assert.isNotNull(this.terminal.pendingFont_); |
Jason Lin | c2504ae | 2022-09-02 13:03:31 +1000 | [diff] [blame] | 61 | assert.equal(this.mocks.term.popMethodHistory('resize').length, 0); |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 62 | |
| 63 | await updateFontPromise; |
| 64 | assert.equal(this.mocks.term.baseObj.options.fontFamily, 'font one'); |
| 65 | assert.isNull(this.terminal.pendingFont_); |
| 66 | await sleep(0); |
Jason Lin | c2504ae | 2022-09-02 13:03:31 +1000 | [diff] [blame] | 67 | assert.equal(this.mocks.term.popMethodHistory('resize').length, 1); |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 68 | }); |
| 69 | |
| 70 | it('refresh font when the font is the same', async function() { |
| 71 | this.mocks.term.baseObj.options.fontFamily = 'font one'; |
| 72 | const updateFontPromise = this.terminal.updateFont_('font one'); |
| 73 | assert.deepEqual( |
| 74 | await this.mocks.fontManager.whenCalled('loadFont'), |
| 75 | [['font one']]); |
| 76 | assert.equal(this.mocks.term.baseObj.options.fontFamily, 'font one'); |
| 77 | assert.isNotNull(this.terminal.pendingFont_); |
Jason Lin | c2504ae | 2022-09-02 13:03:31 +1000 | [diff] [blame] | 78 | assert.equal(this.mocks.term.popMethodHistory('resize').length, 0); |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 79 | |
| 80 | await updateFontPromise; |
| 81 | // Note the extra space at the end. |
| 82 | assert.equal(this.mocks.term.baseObj.options.fontFamily, 'font one '); |
| 83 | assert.isNull(this.terminal.pendingFont_); |
| 84 | await sleep(0); |
Jason Lin | c2504ae | 2022-09-02 13:03:31 +1000 | [diff] [blame] | 85 | assert.equal(this.mocks.term.popMethodHistory('resize').length, 1); |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 86 | }); |
| 87 | |
| 88 | it('aborts if pendingFont_ was changed', async function() { |
| 89 | const updateFontPromise = this.terminal.updateFont_('font one'); |
| 90 | assert.deepEqual( |
| 91 | await this.mocks.fontManager.whenCalled('loadFont'), |
| 92 | [['font one']]); |
| 93 | assert.equal(this.mocks.term.baseObj.options.fontFamily, undefined); |
| 94 | assert.isNotNull(this.terminal.pendingFont_); |
Jason Lin | c2504ae | 2022-09-02 13:03:31 +1000 | [diff] [blame] | 95 | assert.equal(this.mocks.term.popMethodHistory('resize').length, 0); |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 96 | |
| 97 | this.terminal.pendingFont_ = 'font two'; |
| 98 | |
| 99 | await updateFontPromise; |
| 100 | assert.equal(this.mocks.term.baseObj.options.fontFamily, undefined); |
| 101 | assert.equal(this.terminal.pendingFont_, 'font two'); |
| 102 | await sleep(0); |
Jason Lin | c2504ae | 2022-09-02 13:03:31 +1000 | [diff] [blame] | 103 | assert.equal(this.mocks.term.popMethodHistory('resize').length, 0); |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 104 | }); |
| 105 | }); |
Jason Lin | 5690e75 | 2022-08-30 15:36:45 +1000 | [diff] [blame] | 106 | |
| 107 | it('customKeyEventHandler_', async function() { |
| 108 | const mockHandler = new MockFunction(); |
| 109 | const fakeEvent = { |
| 110 | type: 'keydown', |
| 111 | keyCode: 65, |
| 112 | ctrlKey: true, |
| 113 | }; |
| 114 | this.terminal.keyDownHandlers_.set(encodeKeyCombo(Modifier.Ctrl, 65), |
| 115 | mockHandler.proxy); |
| 116 | assert.isFalse(this.terminal.customKeyEventHandler_(fakeEvent)); |
| 117 | const history = mockHandler.popHistory(); |
| 118 | assert.equal(history.length, 1); |
| 119 | assert.equal(history[0][0], fakeEvent); |
| 120 | |
| 121 | assert.isFalse(this.terminal.customKeyEventHandler_({...fakeEvent, |
| 122 | type: 'keypress'})); |
| 123 | assert.isEmpty(mockHandler.popHistory()); |
| 124 | |
| 125 | assert.isTrue(this.terminal.customKeyEventHandler_({...fakeEvent, |
| 126 | shiftKey: true})); |
| 127 | assert.isEmpty(mockHandler.popHistory()); |
| 128 | |
| 129 | assert.isTrue(this.terminal.customKeyEventHandler_({...fakeEvent, |
| 130 | keyCode: 66})); |
| 131 | assert.isEmpty(mockHandler.popHistory()); |
| 132 | |
| 133 | assert.isTrue(this.terminal.customKeyEventHandler_({...fakeEvent, |
| 134 | ctrlKey: false})); |
| 135 | assert.isEmpty(mockHandler.popHistory()); |
| 136 | }); |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 137 | }); |
Jason Lin | a8adea5 | 2022-10-25 13:14:14 +1100 | [diff] [blame] | 138 | |
| 139 | describe('A11yButtons', () => { |
| 140 | const ROWS = 5; |
| 141 | |
| 142 | beforeEach(function() { |
| 143 | this.elem = document.createElement('div'); |
| 144 | this.elem.style.height = '500px'; |
| 145 | this.elem.style.width = '500px'; |
| 146 | document.body.appendChild(this.elem); |
| 147 | |
| 148 | this.terminal = new Terminal({cols: 80, rows: ROWS, |
| 149 | allowProposedApi: true}); |
| 150 | this.htermA11yReaderMock = new MockObject(); |
| 151 | this.a11yButtons = new A11yButtons(this.terminal, this.elem, |
| 152 | /** @type {!hterm.AccessibilityReader} */( |
| 153 | this.htermA11yReaderMock.proxy)); |
| 154 | |
| 155 | this.write = async (content) => { |
| 156 | return new Promise((resolve) => this.terminal.write(content, resolve)); |
| 157 | }; |
| 158 | }); |
| 159 | |
| 160 | afterEach(function() { |
| 161 | this.terminal.dispose(); |
| 162 | document.body.removeChild(this.elem); |
| 163 | }); |
| 164 | |
| 165 | it('announceScreenContent_', async function() { |
| 166 | this.a11yButtons.announceScreenContent_(); |
| 167 | assert.deepEqual( |
| 168 | this.htermA11yReaderMock.popMethodHistory('assertiveAnnounce'), |
| 169 | [['100% scrolled,']]); |
| 170 | |
| 171 | await this.write('hello'); |
| 172 | this.a11yButtons.announceScreenContent_(); |
| 173 | assert.deepEqual( |
| 174 | this.htermA11yReaderMock.popMethodHistory('assertiveAnnounce'), |
| 175 | [['100% scrolled,\nhello']]); |
| 176 | |
| 177 | await this.write('\r\nworld'); |
| 178 | this.a11yButtons.announceScreenContent_(); |
| 179 | assert.deepEqual( |
| 180 | this.htermA11yReaderMock.popMethodHistory('assertiveAnnounce'), |
| 181 | [['100% scrolled,\nhello\nworld']]); |
| 182 | |
| 183 | for (let i = 0; i < ROWS; ++i) { |
| 184 | await this.write(`\r\n${i}`); |
| 185 | } |
| 186 | this.a11yButtons.announceScreenContent_(); |
| 187 | assert.deepEqual( |
| 188 | this.htermA11yReaderMock.popMethodHistory('assertiveAnnounce'), |
| 189 | [['100% scrolled,\n0\n1\n2\n3\n4']]); |
| 190 | |
| 191 | this.terminal.scrollLines(-1); |
| 192 | this.a11yButtons.announceScreenContent_(); |
| 193 | assert.deepEqual( |
| 194 | this.htermA11yReaderMock.popMethodHistory('assertiveAnnounce'), |
| 195 | [['50% scrolled,\nworld\n0\n1\n2\n3']]); |
| 196 | |
| 197 | this.terminal.scrollLines(-1); |
| 198 | this.a11yButtons.announceScreenContent_(); |
| 199 | assert.deepEqual( |
| 200 | this.htermA11yReaderMock.popMethodHistory('assertiveAnnounce'), |
| 201 | [['0% scrolled,\nhello\nworld\n0\n1\n2']]); |
| 202 | }); |
| 203 | }); |
Jason Lin | abad756 | 2022-08-22 14:49:05 +1000 | [diff] [blame] | 204 | }); |