diff options
Diffstat (limited to 'spec/frontend/editor/source_editor_extension_base_spec.js')
-rw-r--r-- | spec/frontend/editor/source_editor_extension_base_spec.js | 161 |
1 files changed, 53 insertions, 108 deletions
diff --git a/spec/frontend/editor/source_editor_extension_base_spec.js b/spec/frontend/editor/source_editor_extension_base_spec.js index a0fb1178b3b..6606557fd1f 100644 --- a/spec/frontend/editor/source_editor_extension_base_spec.js +++ b/spec/frontend/editor/source_editor_extension_base_spec.js @@ -2,40 +2,25 @@ import { Range } from 'monaco-editor'; import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame'; import setWindowLocation from 'helpers/set_window_location_helper'; import { - ERROR_INSTANCE_REQUIRED_FOR_EXTENSION, EDITOR_TYPE_CODE, EDITOR_TYPE_DIFF, + EXTENSION_BASE_LINE_LINK_ANCHOR_CLASS, + EXTENSION_BASE_LINE_NUMBERS_CLASS, } from '~/editor/constants'; import { SourceEditorExtension } from '~/editor/extensions/source_editor_extension_base'; - -jest.mock('~/helpers/startup_css_helper', () => { - return { - waitForCSSLoaded: jest.fn().mockImplementation((cb) => { - // We have to artificially put the callback's execution - // to the end of the current call stack to be able to - // test that the callback is called after waitForCSSLoaded. - // setTimeout with 0 delay does exactly that. - // Otherwise we might end up with false positive results - setTimeout(() => { - cb.apply(); - }, 0); - }), - }; -}); +import EditorInstance from '~/editor/source_editor_instance'; describe('The basis for an Source Editor extension', () => { const defaultLine = 3; - let ext; let event; - const defaultOptions = { foo: 'bar' }; const findLine = (num) => { - return document.querySelector(`.line-numbers:nth-child(${num})`); + return document.querySelector(`.${EXTENSION_BASE_LINE_NUMBERS_CLASS}:nth-child(${num})`); }; const generateLines = () => { let res = ''; for (let line = 1, lines = 5; line <= lines; line += 1) { - res += `<div class="line-numbers">${line}</div>`; + res += `<div class="${EXTENSION_BASE_LINE_NUMBERS_CLASS}">${line}</div>`; } return res; }; @@ -49,6 +34,9 @@ describe('The basis for an Source Editor extension', () => { }, }; }; + const createInstance = (baseInstance = {}) => { + return new EditorInstance(baseInstance); + }; beforeEach(() => { setFixtures(generateLines()); @@ -59,95 +47,47 @@ describe('The basis for an Source Editor extension', () => { jest.clearAllMocks(); }); - describe('constructor', () => { - it('resets the layout in waitForCSSLoaded callback', async () => { - const instance = { - layout: jest.fn(), - }; - ext = new SourceEditorExtension({ instance }); - expect(instance.layout).not.toHaveBeenCalled(); - - // We're waiting for the waitForCSSLoaded mock to kick in - await jest.runOnlyPendingTimers(); + describe('onUse callback', () => { + it('initializes the line highlighting', () => { + const instance = createInstance(); + const spy = jest.spyOn(SourceEditorExtension, 'highlightLines'); - expect(instance.layout).toHaveBeenCalled(); + instance.use({ definition: SourceEditorExtension }); + expect(spy).toHaveBeenCalled(); }); 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 } = {}) => { - SourceEditorExtension.deferRerender = jest.fn(); - const originalInstance = { ...instance }; - - if (instance) { - if (options) { - Object.entries(options).forEach((prop) => { - expect(instance[prop]).toBeUndefined(); - }); - // Both instance and options are passed - ext = new SourceEditorExtension({ instance, ...options }); - Object.entries(options).forEach(([prop, value]) => { - expect(ext[prop]).toBeUndefined(); - expect(instance[prop]).toBe(value); - }); + description | instanceType | shouldBeCalled + ${'Sets up'} | ${EDITOR_TYPE_CODE} | ${true} + ${'Does not set up'} | ${EDITOR_TYPE_DIFF} | ${false} + `( + '$description the line linking for $instanceType instance', + ({ instanceType, shouldBeCalled }) => { + const instance = createInstance({ + getEditorType: jest.fn().mockReturnValue(instanceType), + onMouseMove: jest.fn(), + onMouseDown: jest.fn(), + }); + const spy = jest.spyOn(SourceEditorExtension, 'setupLineLinking'); + + instance.use({ definition: SourceEditorExtension }); + if (shouldBeCalled) { + expect(spy).toHaveBeenCalledWith(instance); } else { - ext = new SourceEditorExtension({ instance }); - expect(instance).toEqual(originalInstance); + expect(spy).not.toHaveBeenCalled(); } - } else if (options) { - // Options are passed without instance - expect(() => { - ext = new SourceEditorExtension({ ...options }); - }).toThrow(ERROR_INSTANCE_REQUIRED_FOR_EXTENSION); - } else { - // Neither options nor instance are passed - expect(() => { - ext = new SourceEditorExtension(); - }).not.toThrow(); - } - }); - - it('initializes the line highlighting', () => { - SourceEditorExtension.deferRerender = jest.fn(); - const spy = jest.spyOn(SourceEditorExtension, 'highlightLines'); - ext = new SourceEditorExtension({ instance: {} }); - expect(spy).toHaveBeenCalled(); - }); - - it('sets up the line linking for code instance', () => { - SourceEditorExtension.deferRerender = jest.fn(); - const spy = jest.spyOn(SourceEditorExtension, 'setupLineLinking'); - const instance = { - getEditorType: jest.fn().mockReturnValue(EDITOR_TYPE_CODE), - onMouseMove: jest.fn(), - onMouseDown: jest.fn(), - }; - ext = new SourceEditorExtension({ instance }); - expect(spy).toHaveBeenCalledWith(instance); - }); - - it('does not set up the line linking for diff instance', () => { - SourceEditorExtension.deferRerender = jest.fn(); - const spy = jest.spyOn(SourceEditorExtension, 'setupLineLinking'); - const instance = { - getEditorType: jest.fn().mockReturnValue(EDITOR_TYPE_DIFF), - }; - ext = new SourceEditorExtension({ instance }); - expect(spy).not.toHaveBeenCalled(); - }); + }, + ); }); describe('highlightLines', () => { const revealSpy = jest.fn(); const decorationsSpy = jest.fn(); - const instance = { + const instance = createInstance({ revealLineInCenter: revealSpy, deltaDecorations: decorationsSpy, - }; + }); + instance.use({ definition: SourceEditorExtension }); const defaultDecorationOptions = { isWholeLine: true, className: 'active-line-text', @@ -175,7 +115,7 @@ describe('The basis for an Source Editor extension', () => { ${'uses bounds if both hash and bounds exist'} | ${'#L7-42'} | ${[3, 5]} | ${true} | ${[3, 1, 5, 1]} `('$desc', ({ hash, bounds, shouldReveal, expectedRange } = {}) => { window.location.hash = hash; - SourceEditorExtension.highlightLines(instance, bounds); + instance.highlightLines(bounds); if (!shouldReveal) { expect(revealSpy).not.toHaveBeenCalled(); expect(decorationsSpy).not.toHaveBeenCalled(); @@ -193,11 +133,11 @@ describe('The basis for an Source Editor extension', () => { } }); - it('stores the line decorations on the instance', () => { + it('stores the line decorations on the instance', () => { decorationsSpy.mockReturnValue('foo'); window.location.hash = '#L10'; expect(instance.lineDecorations).toBeUndefined(); - SourceEditorExtension.highlightLines(instance); + instance.highlightLines(); expect(instance.lineDecorations).toBe('foo'); }); @@ -215,7 +155,7 @@ describe('The basis for an Source Editor extension', () => { }, ]; instance.lineDecorations = oldLineDecorations; - SourceEditorExtension.highlightLines(instance, [7, 10]); + instance.highlightLines([7, 10]); expect(decorationsSpy).toHaveBeenCalledWith(oldLineDecorations, newLineDecorations); }); }); @@ -228,13 +168,18 @@ describe('The basis for an Source Editor extension', () => { options: { isWholeLine: true, className: 'active-line-text' }, }, ]; - const instance = { - deltaDecorations: decorationsSpy, - lineDecorations, - }; + let instance; + + beforeEach(() => { + instance = createInstance({ + deltaDecorations: decorationsSpy, + lineDecorations, + }); + instance.use({ definition: SourceEditorExtension }); + }); it('removes all existing decorations', () => { - SourceEditorExtension.removeHighlights(instance); + instance.removeHighlights(); expect(decorationsSpy).toHaveBeenCalledWith(lineDecorations, []); }); }); @@ -261,9 +206,9 @@ describe('The basis for an Source Editor extension', () => { }); it.each` - desc | eventTrigger | shouldRemove - ${'does not remove the line decorations if the event is triggered on a wrong node'} | ${null} | ${false} - ${'removes existing line decorations when clicking a line number'} | ${'.link-anchor'} | ${true} + desc | eventTrigger | shouldRemove + ${'does not remove the line decorations if the event is triggered on a wrong node'} | ${null} | ${false} + ${'removes existing line decorations when clicking a line number'} | ${`.${EXTENSION_BASE_LINE_LINK_ANCHOR_CLASS}`} | ${true} `('$desc', ({ eventTrigger, shouldRemove } = {}) => { event = generateEventMock({ el: eventTrigger ? document.querySelector(eventTrigger) : null }); instance.onMouseDown.mockImplementation((fn) => { |