diff options
Diffstat (limited to 'spec/frontend/editor/editor_lite_spec.js')
-rw-r--r-- | spec/frontend/editor/editor_lite_spec.js | 159 |
1 files changed, 123 insertions, 36 deletions
diff --git a/spec/frontend/editor/editor_lite_spec.js b/spec/frontend/editor/editor_lite_spec.js index 2968984df01..3a7680f6d17 100644 --- a/spec/frontend/editor/editor_lite_spec.js +++ b/spec/frontend/editor/editor_lite_spec.js @@ -1,6 +1,8 @@ +/* eslint-disable max-classes-per-file */ import { editor as monacoEditor, languages as monacoLanguages, Uri } from 'monaco-editor'; import waitForPromises from 'helpers/wait_for_promises'; import Editor from '~/editor/editor_lite'; +import { EditorLiteExtension } from '~/editor/editor_lite_extension_base'; import { DEFAULT_THEME, themes } from '~/ide/lib/themes'; import { EDITOR_LITE_INSTANCE_ERROR_NO_EL, URI_PREFIX } from '~/editor/constants'; @@ -242,17 +244,53 @@ describe('Base editor', () => { describe('extensions', () => { let instance; - const foo1 = jest.fn(); - const foo2 = jest.fn(); - const bar = jest.fn(); - const MyExt1 = { - foo: foo1, + const alphaRes = jest.fn(); + const betaRes = jest.fn(); + const fooRes = jest.fn(); + const barRes = jest.fn(); + class AlphaClass { + constructor() { + this.res = alphaRes; + } + alpha() { + return this?.nonExistentProp || alphaRes; + } + } + class BetaClass { + beta() { + return this?.nonExistentProp || betaRes; + } + } + class WithStaticMethod { + constructor({ instance: inst, ...options } = {}) { + Object.assign(inst, options); + } + static computeBoo(a) { + return a + 1; + } + boo() { + return WithStaticMethod.computeBoo(this.base); + } + } + class WithStaticMethodExtended extends EditorLiteExtension { + static computeBoo(a) { + return a + 1; + } + boo() { + return WithStaticMethodExtended.computeBoo(this.base); + } + } + const AlphaExt = new AlphaClass(); + const BetaExt = new BetaClass(); + const FooObjExt = { + foo() { + return fooRes; + }, }; - const MyExt2 = { - bar, - }; - const MyExt3 = { - foo: foo2, + const BarObjExt = { + bar() { + return barRes; + }, }; describe('basic functionality', () => { @@ -260,13 +298,6 @@ describe('Base editor', () => { instance = editor.createInstance({ el: editorEl, blobPath, blobContent }); }); - it('is extensible with the extensions', () => { - expect(instance.foo).toBeUndefined(); - - instance.use(MyExt1); - expect(instance.foo).toEqual(foo1); - }); - it('does not fail if no extensions supplied', () => { const spy = jest.spyOn(global.console, 'error'); instance.use(); @@ -274,24 +305,80 @@ describe('Base editor', () => { expect(spy).not.toHaveBeenCalled(); }); - it('is extensible with multiple extensions', () => { - expect(instance.foo).toBeUndefined(); - expect(instance.bar).toBeUndefined(); + it("does not extend instance with extension's constructor", () => { + expect(instance.constructor).toBeDefined(); + const { constructor } = instance; + + expect(AlphaExt.constructor).toBeDefined(); + expect(AlphaExt.constructor).not.toEqual(constructor); + + instance.use(AlphaExt); + expect(instance.constructor).toBe(constructor); + }); + + it.each` + type | extensions | methods | expectations + ${'ES6 classes'} | ${AlphaExt} | ${['alpha']} | ${[alphaRes]} + ${'multiple ES6 classes'} | ${[AlphaExt, BetaExt]} | ${['alpha', 'beta']} | ${[alphaRes, betaRes]} + ${'simple objects'} | ${FooObjExt} | ${['foo']} | ${[fooRes]} + ${'multiple simple objects'} | ${[FooObjExt, BarObjExt]} | ${['foo', 'bar']} | ${[fooRes, barRes]} + ${'combination of ES6 classes and objects'} | ${[AlphaExt, BarObjExt]} | ${['alpha', 'bar']} | ${[alphaRes, barRes]} + `('is extensible with $type', ({ extensions, methods, expectations } = {}) => { + methods.forEach(method => { + expect(instance[method]).toBeUndefined(); + }); - instance.use([MyExt1, MyExt2]); + instance.use(extensions); - expect(instance.foo).toEqual(foo1); - expect(instance.bar).toEqual(bar); + methods.forEach(method => { + expect(instance[method]).toBeDefined(); + }); + + expectations.forEach((expectation, i) => { + expect(instance[methods[i]].call()).toEqual(expectation); + }); }); + it('does not extend instance with private data of an extension', () => { + const ext = new WithStaticMethod({ instance }); + ext.staticMethod = () => { + return 'foo'; + }; + ext.staticProp = 'bar'; + + expect(instance.boo).toBeUndefined(); + expect(instance.staticMethod).toBeUndefined(); + expect(instance.staticProp).toBeUndefined(); + + instance.use(ext); + + expect(instance.boo).toBeDefined(); + expect(instance.staticMethod).toBeUndefined(); + expect(instance.staticProp).toBeUndefined(); + }); + + it.each([WithStaticMethod, WithStaticMethodExtended])( + 'properly resolves data for an extension with private data', + ExtClass => { + const base = 1; + expect(instance.base).toBeUndefined(); + expect(instance.boo).toBeUndefined(); + + const ext = new ExtClass({ instance, base }); + + instance.use(ext); + expect(instance.base).toBe(1); + expect(instance.boo()).toBe(2); + }, + ); + it('uses the last definition of a method in case of an overlap', () => { - instance.use([MyExt1, MyExt2, MyExt3]); - expect(instance).toEqual( - expect.objectContaining({ - foo: foo2, - bar, - }), - ); + const FooObjExt2 = { foo: 'foo2' }; + instance.use([FooObjExt, BarObjExt, FooObjExt2]); + expect(instance).toMatchObject({ + foo: 'foo2', + ...BarObjExt, + }); }); it('correctly resolves references withing extensions', () => { @@ -396,15 +483,15 @@ describe('Base editor', () => { }); it('extends all instances if no specific instance is passed', () => { - editor.use(MyExt1); - expect(inst1.foo).toEqual(foo1); - expect(inst2.foo).toEqual(foo1); + editor.use(AlphaExt); + expect(inst1.alpha()).toEqual(alphaRes); + expect(inst2.alpha()).toEqual(alphaRes); }); it('extends specific instance if it has been passed', () => { - editor.use(MyExt1, inst2); - expect(inst1.foo).toBeUndefined(); - expect(inst2.foo).toEqual(foo1); + editor.use(AlphaExt, inst2); + expect(inst1.alpha).toBeUndefined(); + expect(inst2.alpha()).toEqual(alphaRes); }); }); }); |