Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/editor')
-rw-r--r--spec/frontend/editor/editor_lite_extension_base_spec.js44
-rw-r--r--spec/frontend/editor/editor_lite_spec.js159
-rw-r--r--spec/frontend/editor/editor_markdown_ext_spec.js4
3 files changed, 169 insertions, 38 deletions
diff --git a/spec/frontend/editor/editor_lite_extension_base_spec.js b/spec/frontend/editor/editor_lite_extension_base_spec.js
new file mode 100644
index 00000000000..ff53640b096
--- /dev/null
+++ b/spec/frontend/editor/editor_lite_extension_base_spec.js
@@ -0,0 +1,44 @@
+import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION } from '~/editor/constants';
+import { EditorLiteExtension } from '~/editor/editor_lite_extension_base';
+
+describe('The basis for an Editor Lite extension', () => {
+ let ext;
+ const defaultOptions = { foo: 'bar' };
+
+ it.each`
+ description | instance | options
+ ${'accepts configuration options and instance'} | ${{}} | ${defaultOptions}
+ ${'leaves instance intact if no options are passed'} | ${{}} | ${undefined}
+ ${'does not fail if both instance and the options are omitted'} | ${undefined} | ${undefined}
+ ${'throws if only options are passed'} | ${undefined} | ${defaultOptions}
+ `('$description', ({ instance, options } = {}) => {
+ const originalInstance = { ...instance };
+
+ if (instance) {
+ if (options) {
+ Object.entries(options).forEach(prop => {
+ expect(instance[prop]).toBeUndefined();
+ });
+ // Both instance and options are passed
+ ext = new EditorLiteExtension({ instance, ...options });
+ Object.entries(options).forEach(([prop, value]) => {
+ expect(ext[prop]).toBeUndefined();
+ expect(instance[prop]).toBe(value);
+ });
+ } else {
+ ext = new EditorLiteExtension({ instance });
+ expect(instance).toEqual(originalInstance);
+ }
+ } else if (options) {
+ // Options are passed without instance
+ expect(() => {
+ ext = new EditorLiteExtension({ ...options });
+ }).toThrow(ERROR_INSTANCE_REQUIRED_FOR_EXTENSION);
+ } else {
+ // Neither options nor instance are passed
+ expect(() => {
+ ext = new EditorLiteExtension();
+ }).not.toThrow();
+ }
+ });
+});
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);
});
});
});
diff --git a/spec/frontend/editor/editor_markdown_ext_spec.js b/spec/frontend/editor/editor_markdown_ext_spec.js
index 30ab29aad35..b432d4d66ad 100644
--- a/spec/frontend/editor/editor_markdown_ext_spec.js
+++ b/spec/frontend/editor/editor_markdown_ext_spec.js
@@ -1,6 +1,6 @@
import { Range, Position } from 'monaco-editor';
import EditorLite from '~/editor/editor_lite';
-import EditorMarkdownExtension from '~/editor/editor_markdown_ext';
+import { EditorMarkdownExtension } from '~/editor/editor_markdown_ext';
describe('Markdown Extension for Editor Lite', () => {
let editor;
@@ -31,7 +31,7 @@ describe('Markdown Extension for Editor Lite', () => {
blobPath: filePath,
blobContent: text,
});
- editor.use(EditorMarkdownExtension);
+ editor.use(new EditorMarkdownExtension());
});
afterEach(() => {