diff options
Diffstat (limited to 'spec/frontend/editor/editor_lite_spec.js')
-rw-r--r-- | spec/frontend/editor/editor_lite_spec.js | 207 |
1 files changed, 177 insertions, 30 deletions
diff --git a/spec/frontend/editor/editor_lite_spec.js b/spec/frontend/editor/editor_lite_spec.js index e4edeab172b..e566d3a4b38 100644 --- a/spec/frontend/editor/editor_lite_spec.js +++ b/spec/frontend/editor/editor_lite_spec.js @@ -1,8 +1,7 @@ import { editor as monacoEditor, languages as monacoLanguages, Uri } from 'monaco-editor'; import Editor from '~/editor/editor_lite'; import { DEFAULT_THEME, themes } from '~/ide/lib/themes'; - -const URI_PREFIX = 'gitlab'; +import { EDITOR_LITE_INSTANCE_ERROR_NO_EL, URI_PREFIX } from '~/editor/constants'; describe('Base editor', () => { let editorEl; @@ -27,9 +26,7 @@ describe('Base editor', () => { it('initializes Editor with basic properties', () => { expect(editor).toBeDefined(); - expect(editor.editorEl).toBe(null); - expect(editor.blobContent).toEqual(''); - expect(editor.blobPath).toEqual(''); + expect(editor.instances).toEqual([]); }); it('removes `editor-loading` data attribute from the target DOM element', () => { @@ -51,15 +48,14 @@ describe('Base editor', () => { instanceSpy = jest.spyOn(monacoEditor, 'create').mockImplementation(() => ({ setModel, dispose, + onDidDispose: jest.fn(), })); }); - it('does nothing if no dom element is supplied', () => { - editor.createInstance(); - - expect(editor.editorEl).toBe(null); - expect(editor.blobContent).toEqual(''); - expect(editor.blobPath).toEqual(''); + it('throws an error if no dom element is supplied', () => { + expect(() => { + editor.createInstance(); + }).toThrow(EDITOR_LITE_INSTANCE_ERROR_NO_EL); expect(modelSpy).not.toHaveBeenCalled(); expect(instanceSpy).not.toHaveBeenCalled(); @@ -89,15 +85,133 @@ describe('Base editor', () => { createUri(blobGlobalId, blobPath), ); }); + + it('initializes instance with passed properties', () => { + const instanceOptions = { + foo: 'bar', + }; + editor.createInstance({ + el: editorEl, + ...instanceOptions, + }); + expect(instanceSpy).toHaveBeenCalledWith(editorEl, expect.objectContaining(instanceOptions)); + }); + + it('disposes instance when the editor is disposed', () => { + editor.createInstance({ el: editorEl, blobPath, blobContent, blobGlobalId }); + + expect(dispose).not.toHaveBeenCalled(); + + editor.dispose(); + + expect(dispose).toHaveBeenCalled(); + }); + }); + + describe('multiple instances', () => { + let instanceSpy; + let inst1Args; + let inst2Args; + let editorEl1; + let editorEl2; + let inst1; + let inst2; + const readOnlyIndex = '68'; // readOnly option has the internal index of 68 in the editor's options + + beforeEach(() => { + setFixtures('<div id="editor1"></div><div id="editor2"></div>'); + editorEl1 = document.getElementById('editor1'); + editorEl2 = document.getElementById('editor2'); + inst1Args = { + el: editorEl1, + blobGlobalId, + }; + inst2Args = { + el: editorEl2, + blobContent, + blobPath, + blobGlobalId, + }; + + editor = new Editor(); + instanceSpy = jest.spyOn(monacoEditor, 'create'); + }); + + afterEach(() => { + editor.dispose(); + }); + + it('can initialize several instances of the same editor', () => { + editor.createInstance(inst1Args); + expect(editor.instances).toHaveLength(1); + + editor.createInstance(inst2Args); + + expect(instanceSpy).toHaveBeenCalledTimes(2); + expect(editor.instances).toHaveLength(2); + }); + + it('sets independent models on independent instances', () => { + inst1 = editor.createInstance(inst1Args); + inst2 = editor.createInstance(inst2Args); + + const model1 = inst1.getModel(); + const model2 = inst2.getModel(); + + expect(model1).toBeDefined(); + expect(model2).toBeDefined(); + expect(model1).not.toEqual(model2); + }); + + it('shares global editor options among all instances', () => { + editor = new Editor({ + readOnly: true, + }); + + inst1 = editor.createInstance(inst1Args); + expect(inst1.getOption(readOnlyIndex)).toBe(true); + + inst2 = editor.createInstance(inst2Args); + expect(inst2.getOption(readOnlyIndex)).toBe(true); + }); + + it('allows overriding editor options on the instance level', () => { + editor = new Editor({ + readOnly: true, + }); + inst1 = editor.createInstance({ + ...inst1Args, + readOnly: false, + }); + + expect(inst1.getOption(readOnlyIndex)).toBe(false); + }); + + it('disposes instances and relevant models independently from each other', () => { + inst1 = editor.createInstance(inst1Args); + inst2 = editor.createInstance(inst2Args); + + expect(inst1.getModel()).not.toBe(null); + expect(inst2.getModel()).not.toBe(null); + expect(editor.instances).toHaveLength(2); + expect(monacoEditor.getModels()).toHaveLength(2); + + inst1.dispose(); + expect(inst1.getModel()).toBe(null); + expect(inst2.getModel()).not.toBe(null); + expect(editor.instances).toHaveLength(1); + expect(monacoEditor.getModels()).toHaveLength(1); + }); }); describe('implementation', () => { + let instance; beforeEach(() => { - editor.createInstance({ el: editorEl, blobPath, blobContent }); + instance = editor.createInstance({ el: editorEl, blobPath, blobContent }); }); it('correctly proxies value from the model', () => { - expect(editor.getValue()).toEqual(blobContent); + expect(instance.getValue()).toBe(blobContent); }); it('is capable of changing the language of the model', () => { @@ -108,24 +222,25 @@ describe('Base editor', () => { const blobRenamedPath = 'test.js'; - expect(editor.model.getLanguageIdentifier().language).toEqual('markdown'); - editor.updateModelLanguage(blobRenamedPath); + expect(instance.getModel().getLanguageIdentifier().language).toBe('markdown'); + instance.updateModelLanguage(blobRenamedPath); - expect(editor.model.getLanguageIdentifier().language).toEqual('javascript'); + expect(instance.getModel().getLanguageIdentifier().language).toBe('javascript'); }); it('falls back to plaintext if there is no language associated with an extension', () => { const blobRenamedPath = 'test.myext'; const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); - editor.updateModelLanguage(blobRenamedPath); + instance.updateModelLanguage(blobRenamedPath); expect(spy).not.toHaveBeenCalled(); - expect(editor.model.getLanguageIdentifier().language).toEqual('plaintext'); + expect(instance.getModel().getLanguageIdentifier().language).toBe('plaintext'); }); }); describe('extensions', () => { + let instance; const foo1 = jest.fn(); const foo2 = jest.fn(); const bar = jest.fn(); @@ -139,14 +254,14 @@ describe('Base editor', () => { foo: foo2, }; beforeEach(() => { - editor.createInstance({ el: editorEl, blobPath, blobContent }); + instance = editor.createInstance({ el: editorEl, blobPath, blobContent }); }); it('is extensible with the extensions', () => { - expect(editor.foo).toBeUndefined(); + expect(instance.foo).toBeUndefined(); editor.use(MyExt1); - expect(editor.foo).toEqual(foo1); + expect(instance.foo).toEqual(foo1); }); it('does not fail if no extensions supplied', () => { @@ -157,18 +272,18 @@ describe('Base editor', () => { }); it('is extensible with multiple extensions', () => { - expect(editor.foo).toBeUndefined(); - expect(editor.bar).toBeUndefined(); + expect(instance.foo).toBeUndefined(); + expect(instance.bar).toBeUndefined(); editor.use([MyExt1, MyExt2]); - expect(editor.foo).toEqual(foo1); - expect(editor.bar).toEqual(bar); + expect(instance.foo).toEqual(foo1); + expect(instance.bar).toEqual(bar); }); it('uses the last definition of a method in case of an overlap', () => { editor.use([MyExt1, MyExt2, MyExt3]); - expect(editor).toEqual( + expect(instance).toEqual( expect.objectContaining({ foo: foo2, bar, @@ -179,15 +294,47 @@ describe('Base editor', () => { it('correctly resolves references withing extensions', () => { const FunctionExt = { inst() { - return this.instance; + return this; }, mod() { - return this.model; + return this.getModel(); }, }; editor.use(FunctionExt); - expect(editor.inst()).toEqual(editor.instance); - expect(editor.mod()).toEqual(editor.model); + expect(instance.inst()).toEqual(editor.instances[0]); + }); + + describe('multiple instances', () => { + let inst1; + let inst2; + let editorEl1; + let editorEl2; + + beforeEach(() => { + setFixtures('<div id="editor1"></div><div id="editor2"></div>'); + editorEl1 = document.getElementById('editor1'); + editorEl2 = document.getElementById('editor2'); + inst1 = editor.createInstance({ el: editorEl1, blobPath: `foo-${blobPath}` }); + inst2 = editor.createInstance({ el: editorEl2, blobPath: `bar-${blobPath}` }); + }); + + afterEach(() => { + editor.dispose(); + editorEl1.remove(); + editorEl2.remove(); + }); + + it('extends all instances if no specific instance is passed', () => { + editor.use(MyExt1); + expect(inst1.foo).toEqual(foo1); + expect(inst2.foo).toEqual(foo1); + }); + + it('extends specific instance if it has been passed', () => { + editor.use(MyExt1, inst2); + expect(inst1.foo).toBeUndefined(); + expect(inst2.foo).toEqual(foo1); + }); }); }); |