terminal: fix web font related rendering issue with xterm.js
xterm.js caches text atlas and paints on a canvas, so we need to:
1. Change the font family after the font (or at least the latin part)
has loaded
2. Refresh the rendering if new font is loaded (e.g. new non-latin
character was just printed)
Bug: b/236205389
Change-Id: Ida9709f29518e7861e9b9b97638891112f39f3b7
Reviewed-on: https://chromium-review.googlesource.com/c/apps/libapps/+/3831832
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
new file mode 100644
index 0000000..04b1b8d
--- /dev/null
+++ b/terminal/js/terminal_emulator_tests.js
@@ -0,0 +1,88 @@
+// Copyright 2022 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Unit tests for terminal_emulator.js.
+ */
+
+import {sleep} from './terminal_common.js';
+import {XtermTerminal, XtermTerminalTestParams} from './terminal_emulator.js';
+import {MockObject} from './terminal_test_mocks.js';
+
+describe('terminal_emulator_tests.js', function() {
+ describe('XtermTerminal', function() {
+ beforeEach(async function() {
+ this.mocks = {
+ term: new MockObject({options: {}}),
+ fontManager: new MockObject(),
+ fitAddon: new MockObject(),
+ };
+ const testParams = {};
+ for (const prop in this.mocks) {
+ testParams[prop] = this.mocks[prop].proxy;
+ }
+
+ this.terminal = new XtermTerminal({
+ storage: new lib.Storage.Memory(),
+ profileId: 'test',
+ enableWebGL: true,
+ testParams: /** @type {!XtermTerminalTestParams} */(testParams),
+ });
+ });
+
+ describe('updateFont_()', function() {
+ it('updates font', async function() {
+ const updateFontPromise = this.terminal.updateFont_('font one');
+ assert.deepEqual(
+ await this.mocks.fontManager.whenCalled('loadFont'),
+ [['font one']]);
+ assert.equal(this.mocks.term.baseObj.options.fontFamily, undefined);
+ assert.isNotNull(this.terminal.pendingFont_);
+ assert.deepEqual(this.mocks.fitAddon.getMethodHistory('fit'), []);
+
+ await updateFontPromise;
+ assert.equal(this.mocks.term.baseObj.options.fontFamily, 'font one');
+ assert.isNull(this.terminal.pendingFont_);
+ await sleep(0);
+ assert.deepEqual(this.mocks.fitAddon.getMethodHistory('fit'), [[]]);
+ });
+
+ it('refresh font when the font is the same', async function() {
+ this.mocks.term.baseObj.options.fontFamily = 'font one';
+ const updateFontPromise = this.terminal.updateFont_('font one');
+ assert.deepEqual(
+ await this.mocks.fontManager.whenCalled('loadFont'),
+ [['font one']]);
+ assert.equal(this.mocks.term.baseObj.options.fontFamily, 'font one');
+ assert.isNotNull(this.terminal.pendingFont_);
+ assert.deepEqual(this.mocks.fitAddon.getMethodHistory('fit'), []);
+
+ await updateFontPromise;
+ // Note the extra space at the end.
+ assert.equal(this.mocks.term.baseObj.options.fontFamily, 'font one ');
+ assert.isNull(this.terminal.pendingFont_);
+ await sleep(0);
+ assert.deepEqual(this.mocks.fitAddon.getMethodHistory('fit'), [[]]);
+ });
+
+ it('aborts if pendingFont_ was changed', async function() {
+ const updateFontPromise = this.terminal.updateFont_('font one');
+ assert.deepEqual(
+ await this.mocks.fontManager.whenCalled('loadFont'),
+ [['font one']]);
+ assert.equal(this.mocks.term.baseObj.options.fontFamily, undefined);
+ assert.isNotNull(this.terminal.pendingFont_);
+ assert.deepEqual(this.mocks.fitAddon.getMethodHistory('fit'), []);
+
+ this.terminal.pendingFont_ = 'font two';
+
+ await updateFontPromise;
+ assert.equal(this.mocks.term.baseObj.options.fontFamily, undefined);
+ assert.equal(this.terminal.pendingFont_, 'font two');
+ await sleep(0);
+ assert.deepEqual(this.mocks.fitAddon.getMethodHistory('fit'), []);
+ });
+ });
+ });
+});