I've created a Vue component that displays an Xterm.js terminal.
Terminal.vue
<template>
<div id="terminal"></div>
</template>
<script>
import Vue from 'vue';
import { Terminal } from 'xterm/lib/public/Terminal';
import { ITerminalOptions, ITheme } from 'xterm';
export default Vue.extend({
data() {
return {};
},
mounted() {
Terminal.applyAddon(fit);
this.term = new Terminal(opts);
this.term.open(document.getElementById('terminal'));
},
</script>
I'm looking to run tests on this component.
Terminal.test.js
import Terminal from 'components/Terminal'
import { mount } from '@vue/test-utils';
describe('test', ()=>{
const wrapper = mount(App);
});
When running jest
on the test file, I encounter the following error:
TypeError: Cannot set property 'globalCompositeOperation' of null
45 | this.term = new Terminal(opts);
> 46 | this.term.open(document.getElementById('terminal'));
Upon investigating the stack trace, I found that it's related to Xterm's ColorManager.
at new ColorManager (node_modules/xterm/src/renderer/ColorManager.ts:94:39)
at new Renderer (node_modules/xterm/src/renderer/Renderer.ts:41:25)
Examining their code revealed an interesting issue:
constructor(document: Document, public allowTransparency: boolean) {
const canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('Could not get rendering context');
}
this._ctx = ctx;
this._ctx.globalCompositeOperation = 'copy';
this._litmusColor = this._ctx.createLinearGradient(0, 0, 1, 1);
this.colors = {
foreground: DEFAULT_FOREGROUND,
background: DEFAULT_BACKGROUND,
cursor: DEFAULT_CURSOR,
cursorAccent: DEFAULT_CURSOR_ACCENT,
selection: DEFAULT_SELECTION,
ansi: DEFAULT_ANSI_COLORS.slice()
};
}
The issue seems to revolve around the behavior of canvas.getContext
, which returned a valid context initially but caused issues later on.
To conduct proper testing, Xterm uses fake DOM setup in their testing files with jsdom
:
beforeEach(() => {
dom = new jsdom.JSDOM('');
window = dom.window;
document = window.document;
(<any>window).HTMLCanvasElement.prototype.getContext = () => ({
createLinearGradient(): any {
return null;
},
fillRect(): void { },
getImageData(): any {
return {data: [0, 0, 0, 0xFF]};
}
});
cm = new ColorManager(document, false);
});
Considering that vue-test-utils
also employs jsdom
under the hood, and its mount
function handles both attachment and rendering of components, I'm puzzled about how to effectively simulate and test a Vue component integrated with Xterm using Jest.
Creates a Wrapper that contains the mounted and rendered Vue component.
https://vue-test-utils.vuejs.org/api/#mount
What steps can I take to properly mock a DOM for testing a Vue component utilizing Xterm.js within Jest?