diff --git a/src/renderer/components/output.tsx b/src/renderer/components/output.tsx index adb306255c..5248ae5a8c 100644 --- a/src/renderer/components/output.tsx +++ b/src/renderer/components/output.tsx @@ -1,4 +1,4 @@ -import { autorun } from 'mobx'; +import { autorun, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as MonacoType from 'monaco-editor'; import * as React from 'react'; @@ -35,16 +35,23 @@ export class Output extends React.Component { public language = 'consoleOutputLanguage'; private outputRef = React.createRef(); + private readonly model: MonacoType.editor.ITextModel; constructor(props: CommandsProps) { super(props); + const { monaco } = this.props; this.language = 'consoleOutputLanguage'; + this.model = monaco.editor.createModel('', this.language); + this.updateModel(); + reaction( + () => props.appState.output.length, + () => this.updateModel(), + ); } public async componentDidMount() { autorun(async () => { - this.destroyMonacoEditor(); await this.initMonaco(); this.toggleConsole(); }); @@ -58,13 +65,6 @@ export class Output extends React.Component { this.toggleConsole(); } - /** - * Set Monaco Editor's value. - */ - public async UNSAFE_componentWillReceiveProps(newProps: CommandsProps) { - await this.setContent(newProps.appState.output); - } - /** * Initialize Monaco. */ @@ -79,11 +79,9 @@ export class Output extends React.Component { readOnly: true, contextmenu: false, automaticLayout: true, - model: null, + model: this.model, ...monacoOptions, }); - - await this.setContent(this.props.appState.output); } } @@ -152,8 +150,8 @@ export class Output extends React.Component { * @private * @memberof Output */ - private async setContent(output: OutputEntry[]) { - this.setEditorText(this.getOutputLines(output)); + private async updateModel() { + this.model.setValue(this.getOutputLines(this.props.appState.output)); // have terminal always scroll to the bottom this.editor?.revealLine(this.editor?.getScrollHeight()); } @@ -182,22 +180,6 @@ export class Output extends React.Component { return lines.join('\n'); } - /** - * Create a model and attach it to the editor - * - * @private - * @param {string} value - */ - private setEditorText(value: string) { - const { monaco } = this.props; - const model = monaco.editor.createModel(value, this.language); - model.updateOptions({ - tabSize: 2, - }); - - this.editor?.setModel(model); - } - public render(): JSX.Element | null { const { isConsoleShowing } = this.props.appState; diff --git a/tests/renderer/components/output-spec.tsx b/tests/renderer/components/output-spec.tsx index 5d83e8d1eb..2441278da3 100644 --- a/tests/renderer/components/output-spec.tsx +++ b/tests/renderer/components/output-spec.tsx @@ -55,22 +55,6 @@ describe('Output component', () => { expect(monaco.editor.create).toHaveBeenCalled(); expect(monaco.editor.createModel).toHaveBeenCalled(); }); - - it('initializes with a fixed tab size', async () => { - const wrapper = shallow( - , - ); - const instance: any = wrapper.instance(); - - instance.outputRef.current = 'ref'; - await instance.initMonaco(); - - expect(monaco.latestModel.updateOptions).toHaveBeenCalledWith( - expect.objectContaining({ - tabSize: 2, - }), - ); - }); }); it('componentWillUnmount() attempts to dispose the editor', async () => { @@ -120,7 +104,7 @@ describe('Output component', () => { expect(wrapper.html()).not.toBe(null); }); - it('setContent updates model with correct values', async () => { + it('updateModel updates model with correct values', async () => { store.output = [ { timestamp: 1532704073130, @@ -140,21 +124,47 @@ describe('Output component', () => { instance.outputRef.current = 'ref'; await instance.initMonaco(); + instance.updateModel(); - instance.editor.setContent(store.output); - const expectedFormattedOutput = - new Date(store.output[0].timestamp).toLocaleTimeString() + - ` Hi!\n` + - new Date(store.output[1].timestamp).toLocaleTimeString() + - ' Hi!'; - // makes sure setContent() is called with the right values - expect(monaco.editor.createModel).toHaveBeenCalledWith( - expectedFormattedOutput, - 'consoleOutputLanguage', - ); + expect(monaco.editor.createModel).toHaveBeenCalled(); expect(instance.editor.revealLine).toHaveBeenCalled(); }); + it('updateModel correctly observes and gets called when output is updated', async () => { + store.output = [ + { + timestamp: 1532704073130, + text: 'Hi!', + }, + ]; + + const wrapper = shallow( + , + ); + + const instance: any = wrapper.instance(); + const spy = jest.spyOn(instance, 'updateModel'); + + instance.outputRef.current = 'ref'; + await instance.initMonaco(); + + instance.updateModel(); + + // new output + store.output = [ + { + timestamp: 1532704073130, + text: 'Hi!', + }, + { + timestamp: 1532704073130, + text: 'Hi!', + isNotPre: true, + }, + ]; + expect(spy).toHaveBeenCalledTimes(2); + }); + it('handles componentDidUpdate', async () => { // set up component const wrapper = shallow(