summaryrefslogtreecommitdiff
path: root/spec/frontend/ide/components
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ide/components')
-rw-r--r--spec/frontend/ide/components/commit_sidebar/form_spec.js5
-rw-r--r--spec/frontend/ide/components/ide_spec.js3
-rw-r--r--spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap1
-rw-r--r--spec/frontend/ide/components/pipelines/list_spec.js1
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js1106
-rw-r--r--spec/frontend/ide/components/repo_tab_spec.js19
6 files changed, 570 insertions, 565 deletions
diff --git a/spec/frontend/ide/components/commit_sidebar/form_spec.js b/spec/frontend/ide/components/commit_sidebar/form_spec.js
index 2b567816ce8..083a2a73b24 100644
--- a/spec/frontend/ide/components/commit_sidebar/form_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/form_spec.js
@@ -14,6 +14,7 @@ import {
createBranchChangedCommitError,
branchAlreadyExistsCommitError,
} from '~/ide/lib/errors';
+import { MSG_CANNOT_PUSH_CODE_SHORT } from '~/ide/messages';
import { createStore } from '~/ide/stores';
import { COMMIT_TO_NEW_BRANCH } from '~/ide/stores/modules/commit/constants';
@@ -84,8 +85,8 @@ describe('IDE commit form', () => {
${'when there are no changes'} | ${[]} | ${{ pushCode: true }} | ${goToEditView} | ${findBeginCommitButtonData} | ${true} | ${''}
${'when there are changes'} | ${['test']} | ${{ pushCode: true }} | ${goToEditView} | ${findBeginCommitButtonData} | ${false} | ${''}
${'when there are changes'} | ${['test']} | ${{ pushCode: true }} | ${goToCommitView} | ${findCommitButtonData} | ${false} | ${''}
- ${'when user cannot push'} | ${['test']} | ${{ pushCode: false }} | ${goToEditView} | ${findBeginCommitButtonData} | ${true} | ${CommitForm.MSG_CANNOT_PUSH_CODE}
- ${'when user cannot push'} | ${['test']} | ${{ pushCode: false }} | ${goToCommitView} | ${findCommitButtonData} | ${true} | ${CommitForm.MSG_CANNOT_PUSH_CODE}
+ ${'when user cannot push'} | ${['test']} | ${{ pushCode: false }} | ${goToEditView} | ${findBeginCommitButtonData} | ${true} | ${MSG_CANNOT_PUSH_CODE_SHORT}
+ ${'when user cannot push'} | ${['test']} | ${{ pushCode: false }} | ${goToCommitView} | ${findCommitButtonData} | ${true} | ${MSG_CANNOT_PUSH_CODE_SHORT}
`('$desc', ({ stagedFiles, userPermissions, viewFn, buttonFn, disabled, tooltip }) => {
beforeEach(async () => {
store.state.stagedFiles = stagedFiles;
diff --git a/spec/frontend/ide/components/ide_spec.js b/spec/frontend/ide/components/ide_spec.js
index c9d19c18d03..bd251f78654 100644
--- a/spec/frontend/ide/components/ide_spec.js
+++ b/spec/frontend/ide/components/ide_spec.js
@@ -4,6 +4,7 @@ import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import ErrorMessage from '~/ide/components/error_message.vue';
import Ide from '~/ide/components/ide.vue';
+import { MSG_CANNOT_PUSH_CODE } from '~/ide/messages';
import { createStore } from '~/ide/stores';
import { file } from '../helpers';
import { projectData } from '../mock_data';
@@ -158,7 +159,7 @@ describe('WebIDE', () => {
expect(findAlert().props()).toMatchObject({
dismissible: false,
});
- expect(findAlert().text()).toBe(Ide.MSG_CANNOT_PUSH_CODE);
+ expect(findAlert().text()).toBe(MSG_CANNOT_PUSH_CODE);
});
it.each`
diff --git a/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap b/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap
index efa58a4a47b..194a619c4aa 100644
--- a/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap
+++ b/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap
@@ -10,7 +10,6 @@ exports[`IDE pipelines list when loaded renders empty state when no latestPipeli
cansetci="true"
class="mb-auto mt-auto"
emptystatesvgpath="http://test.host"
- helppagepath="http://test.host"
/>
</div>
`;
diff --git a/spec/frontend/ide/components/pipelines/list_spec.js b/spec/frontend/ide/components/pipelines/list_spec.js
index 58d8c0629fb..a917f4c0230 100644
--- a/spec/frontend/ide/components/pipelines/list_spec.js
+++ b/spec/frontend/ide/components/pipelines/list_spec.js
@@ -19,7 +19,6 @@ describe('IDE pipelines list', () => {
let wrapper;
const defaultState = {
- links: { ciHelpPagePath: TEST_HOST },
pipelinesEmptyStateSvgPath: TEST_HOST,
};
const defaultPipelinesState = {
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 1985feb1615..a3b327343e5 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -1,11 +1,15 @@
+import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
-import { Range } from 'monaco-editor';
+import { editor as monacoEditor, Range } from 'monaco-editor';
import Vue from 'vue';
import Vuex from 'vuex';
import '~/behaviors/markdown/render_gfm';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
import waitForPromises from 'helpers/wait_for_promises';
import waitUsingRealTimer from 'helpers/wait_using_real_timer';
+import { exampleConfigs, exampleFiles } from 'jest/ide/lib/editorconfig/mock_data';
+import { EDITOR_CODE_INSTANCE_FN, EDITOR_DIFF_INSTANCE_FN } from '~/editor/constants';
+import EditorLite from '~/editor/editor_lite';
+import { EditorWebIdeExtension } from '~/editor/extensions/editor_lite_webide_ext';
import RepoEditor from '~/ide/components/repo_editor.vue';
import {
leftSidebarViews,
@@ -13,733 +17,723 @@ import {
FILE_VIEW_MODE_PREVIEW,
viewerTypes,
} from '~/ide/constants';
-import Editor from '~/ide/lib/editor';
+import ModelManager from '~/ide/lib/common/model_manager';
import service from '~/ide/services';
import { createStoreOptions } from '~/ide/stores';
import axios from '~/lib/utils/axios_utils';
+import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import { file } from '../helpers';
-import { exampleConfigs, exampleFiles } from '../lib/editorconfig/mock_data';
+
+const defaultFileProps = {
+ ...file('file.txt'),
+ content: 'hello world',
+ active: true,
+ tempFile: true,
+};
+const createActiveFile = (props) => {
+ return {
+ ...defaultFileProps,
+ ...props,
+ };
+};
+
+const dummyFile = {
+ markdown: (() =>
+ createActiveFile({
+ projectId: 'namespace/project',
+ path: 'sample.md',
+ name: 'sample.md',
+ }))(),
+ binary: (() =>
+ createActiveFile({
+ name: 'file.dat',
+ content: '🐱', // non-ascii binary content,
+ }))(),
+ empty: (() =>
+ createActiveFile({
+ tempFile: false,
+ content: '',
+ raw: '',
+ }))(),
+};
+
+const prepareStore = (state, activeFile) => {
+ const localState = {
+ openFiles: [activeFile],
+ projects: {
+ 'gitlab-org/gitlab': {
+ branches: {
+ master: {
+ name: 'master',
+ commit: {
+ id: 'abcdefgh',
+ },
+ },
+ },
+ },
+ },
+ currentProjectId: 'gitlab-org/gitlab',
+ currentBranchId: 'master',
+ entries: {
+ [activeFile.path]: activeFile,
+ },
+ };
+ const storeOptions = createStoreOptions();
+ return new Vuex.Store({
+ ...createStoreOptions(),
+ state: {
+ ...storeOptions.state,
+ ...localState,
+ ...state,
+ },
+ });
+};
describe('RepoEditor', () => {
+ let wrapper;
let vm;
- let store;
+ let createInstanceSpy;
+ let createDiffInstanceSpy;
+ let createModelSpy;
const waitForEditorSetup = () =>
new Promise((resolve) => {
vm.$once('editorSetup', resolve);
});
- const createComponent = () => {
- if (vm) {
- throw new Error('vm already exists');
- }
- vm = createComponentWithStore(Vue.extend(RepoEditor), store, {
- file: store.state.openFiles[0],
+ const createComponent = async ({ state = {}, activeFile = defaultFileProps } = {}) => {
+ const store = prepareStore(state, activeFile);
+ wrapper = shallowMount(RepoEditor, {
+ store,
+ propsData: {
+ file: store.state.openFiles[0],
+ },
+ mocks: {
+ ContentViewer,
+ },
});
-
+ await waitForPromises();
+ vm = wrapper.vm;
jest.spyOn(vm, 'getFileData').mockResolvedValue();
jest.spyOn(vm, 'getRawFileData').mockResolvedValue();
-
- vm.$mount();
};
- const createOpenFile = (path) => {
- const origFile = store.state.openFiles[0];
- const newFile = { ...origFile, path, key: path, name: 'myfile.txt', content: 'hello world' };
-
- store.state.entries[path] = newFile;
-
- store.state.openFiles = [newFile];
- };
+ const findEditor = () => wrapper.find('[data-testid="editor-container"]');
+ const findTabs = () => wrapper.findAll('.ide-mode-tabs .nav-links li');
+ const findPreviewTab = () => wrapper.find('[data-testid="preview-tab"]');
beforeEach(() => {
- const f = {
- ...file('file.txt'),
- content: 'hello world',
- };
-
- const storeOptions = createStoreOptions();
- store = new Vuex.Store(storeOptions);
-
- f.active = true;
- f.tempFile = true;
-
- store.state.openFiles.push(f);
- store.state.projects = {
- 'gitlab-org/gitlab': {
- branches: {
- master: {
- name: 'master',
- commit: {
- id: 'abcdefgh',
- },
- },
- },
- },
- };
- store.state.currentProjectId = 'gitlab-org/gitlab';
- store.state.currentBranchId = 'master';
-
- Vue.set(store.state.entries, f.path, f);
+ createInstanceSpy = jest.spyOn(EditorLite.prototype, EDITOR_CODE_INSTANCE_FN);
+ createDiffInstanceSpy = jest.spyOn(EditorLite.prototype, EDITOR_DIFF_INSTANCE_FN);
+ createModelSpy = jest.spyOn(monacoEditor, 'createModel');
+ jest.spyOn(service, 'getFileData').mockResolvedValue();
+ jest.spyOn(service, 'getRawFileData').mockResolvedValue();
});
afterEach(() => {
- vm.$destroy();
- vm = null;
-
- Editor.editorInstance.dispose();
+ jest.clearAllMocks();
+ // create a new model each time, otherwise tests conflict with each other
+ // because of same model being used in multiple tests
+ // eslint-disable-next-line no-undef
+ monaco.editor.getModels().forEach((model) => model.dispose());
+ wrapper.destroy();
+ wrapper = null;
});
- const findEditor = () => vm.$el.querySelector('.multi-file-editor-holder');
- const changeViewMode = (viewMode) =>
- store.dispatch('editor/updateFileEditor', { path: vm.file.path, data: { viewMode } });
-
describe('default', () => {
- beforeEach(() => {
- createComponent();
-
- return waitForEditorSetup();
+ it.each`
+ boolVal | textVal
+ ${true} | ${'all'}
+ ${false} | ${'none'}
+ `('sets renderWhitespace to "$textVal"', async ({ boolVal, textVal } = {}) => {
+ await createComponent({
+ state: {
+ renderWhitespaceInCode: boolVal,
+ },
+ });
+ expect(vm.editorOptions.renderWhitespace).toEqual(textVal);
});
- it('sets renderWhitespace to `all`', () => {
- vm.$store.state.renderWhitespaceInCode = true;
-
- expect(vm.editorOptions.renderWhitespace).toEqual('all');
+ it('renders an ide container', async () => {
+ await createComponent();
+ expect(findEditor().isVisible()).toBe(true);
});
- it('sets renderWhitespace to `none`', () => {
- vm.$store.state.renderWhitespaceInCode = false;
+ it('renders only an edit tab', async () => {
+ await createComponent();
+ const tabs = findTabs();
- expect(vm.editorOptions.renderWhitespace).toEqual('none');
+ expect(tabs).toHaveLength(1);
+ expect(tabs.at(0).text()).toBe('Edit');
});
+ });
- it('renders an ide container', () => {
- expect(vm.shouldHideEditor).toBeFalsy();
- expect(vm.showEditor).toBe(true);
- expect(findEditor()).not.toHaveCss({ display: 'none' });
- });
+ describe('when file is markdown', () => {
+ let mock;
+ let activeFile;
- it('renders only an edit tab', (done) => {
- Vue.nextTick(() => {
- const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
+ beforeEach(() => {
+ activeFile = dummyFile.markdown;
- expect(tabs.length).toBe(1);
- expect(tabs[0].textContent.trim()).toBe('Edit');
+ mock = new MockAdapter(axios);
- done();
+ mock.onPost(/(.*)\/preview_markdown/).reply(200, {
+ body: `<p>${defaultFileProps.content}</p>`,
});
});
- describe('when file is markdown', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
-
- mock.onPost(/(.*)\/preview_markdown/).reply(200, {
- body: '<p>testing 123</p>',
- });
-
- Vue.set(vm, 'file', {
- ...vm.file,
- projectId: 'namespace/project',
- path: 'sample.md',
- name: 'sample.md',
- content: 'testing 123',
- });
-
- vm.$store.state.entries[vm.file.path] = vm.file;
+ afterEach(() => {
+ mock.restore();
+ });
- return vm.$nextTick();
- });
+ it('renders an Edit and a Preview Tab', async () => {
+ await createComponent({ activeFile });
+ const tabs = findTabs();
- afterEach(() => {
- mock.restore();
- });
+ expect(tabs).toHaveLength(2);
+ expect(tabs.at(0).text()).toBe('Edit');
+ expect(tabs.at(1).text()).toBe('Preview Markdown');
+ });
- it('renders an Edit and a Preview Tab', (done) => {
- Vue.nextTick(() => {
- const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
+ it('renders markdown for tempFile', async () => {
+ // by default files created in the spec are temp: no need for explicitly sending the param
+ await createComponent({ activeFile });
- expect(tabs.length).toBe(2);
- expect(tabs[0].textContent.trim()).toBe('Edit');
- expect(tabs[1].textContent.trim()).toBe('Preview Markdown');
+ findPreviewTab().trigger('click');
+ await waitForPromises();
+ expect(wrapper.find(ContentViewer).html()).toContain(defaultFileProps.content);
+ });
- done();
- });
+ it('shows no tabs when not in Edit mode', async () => {
+ await createComponent({
+ state: {
+ currentActivityView: leftSidebarViews.review.name,
+ },
+ activeFile,
});
+ expect(findTabs()).toHaveLength(0);
+ });
+ });
- it('renders markdown for tempFile', (done) => {
- vm.file.tempFile = true;
-
- vm.$nextTick()
- .then(() => {
- vm.$el.querySelectorAll('.ide-mode-tabs .nav-links a')[1].click();
- })
- .then(waitForPromises)
- .then(() => {
- expect(vm.$el.querySelector('.preview-container').innerHTML).toContain(
- '<p>testing 123</p>',
- );
- })
- .then(done)
- .catch(done.fail);
- });
+ describe('when file is binary and not raw', () => {
+ beforeEach(async () => {
+ const activeFile = dummyFile.binary;
+ await createComponent({ activeFile });
+ });
- describe('when not in edit mode', () => {
- beforeEach(async () => {
- await vm.$nextTick();
+ it('does not render the IDE', () => {
+ expect(findEditor().isVisible()).toBe(false);
+ });
- vm.$store.state.currentActivityView = leftSidebarViews.review.name;
+ it('does not create an instance', () => {
+ expect(createInstanceSpy).not.toHaveBeenCalled();
+ expect(createDiffInstanceSpy).not.toHaveBeenCalled();
+ });
+ });
- return vm.$nextTick();
+ describe('createEditorInstance', () => {
+ it.each`
+ viewer | diffInstance
+ ${viewerTypes.edit} | ${undefined}
+ ${viewerTypes.diff} | ${true}
+ ${viewerTypes.mr} | ${true}
+ `(
+ 'creates instance of correct type when viewer is $viewer',
+ async ({ viewer, diffInstance }) => {
+ await createComponent({
+ state: { viewer },
});
+ const isDiff = () => {
+ return diffInstance ? { isDiff: true } : {};
+ };
+ expect(createInstanceSpy).toHaveBeenCalledWith(expect.objectContaining(isDiff()));
+ expect(createDiffInstanceSpy).toHaveBeenCalledTimes((diffInstance && 1) || 0);
+ },
+ );
- it('shows no tabs', () => {
- expect(vm.$el.querySelectorAll('.ide-mode-tabs .nav-links a')).toHaveLength(0);
+ it('installs the WebIDE extension', async () => {
+ const extensionSpy = jest.spyOn(EditorLite, 'instanceApplyExtension');
+ await createComponent();
+ expect(extensionSpy).toHaveBeenCalled();
+ Reflect.ownKeys(EditorWebIdeExtension.prototype)
+ .filter((fn) => fn !== 'constructor')
+ .forEach((fn) => {
+ expect(vm.editor[fn]).toBe(EditorWebIdeExtension.prototype[fn]);
});
- });
});
+ });
- describe('when open file is binary and not raw', () => {
- beforeEach((done) => {
- vm.file.name = 'file.dat';
- vm.file.content = '🐱'; // non-ascii binary content
- jest.spyOn(vm.editor, 'createInstance').mockImplementation();
- jest.spyOn(vm.editor, 'createDiffInstance').mockImplementation();
-
- vm.$nextTick(done);
- });
-
- it('does not render the IDE', () => {
- expect(vm.shouldHideEditor).toBeTruthy();
- });
-
- it('does not call createInstance', async () => {
- // Mirror the act's in the `createEditorInstance`
- vm.createEditorInstance();
-
- await vm.$nextTick();
+ describe('setupEditor', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
- expect(vm.editor.createInstance).not.toHaveBeenCalled();
- expect(vm.editor.createDiffInstance).not.toHaveBeenCalled();
- });
+ it('creates new model on load', () => {
+ // We always create two models per file to be able to build a diff of changes
+ expect(createModelSpy).toHaveBeenCalledTimes(2);
+ // The model with the most recent changes is the last one
+ const [content] = createModelSpy.mock.calls[1];
+ expect(content).toBe(defaultFileProps.content);
});
- describe('createEditorInstance', () => {
- it('calls createInstance when viewer is editor', (done) => {
- jest.spyOn(vm.editor, 'createInstance').mockImplementation();
+ it('does not create a new model on subsequent calls to setupEditor and re-uses the already-existing model', () => {
+ const existingModel = vm.model;
+ createModelSpy.mockClear();
- vm.createEditorInstance();
+ vm.setupEditor();
- vm.$nextTick(() => {
- expect(vm.editor.createInstance).toHaveBeenCalled();
+ expect(createModelSpy).not.toHaveBeenCalled();
+ expect(vm.model).toBe(existingModel);
+ });
- done();
- });
- });
+ it('adds callback methods', () => {
+ jest.spyOn(vm.editor, 'onPositionChange');
+ jest.spyOn(vm.model, 'onChange');
+ jest.spyOn(vm.model, 'updateOptions');
- it('calls createDiffInstance when viewer is diff', (done) => {
- vm.$store.state.viewer = 'diff';
+ vm.setupEditor();
- jest.spyOn(vm.editor, 'createDiffInstance').mockImplementation();
+ expect(vm.editor.onPositionChange).toHaveBeenCalledTimes(1);
+ expect(vm.model.onChange).toHaveBeenCalledTimes(1);
+ expect(vm.model.updateOptions).toHaveBeenCalledWith(vm.rules);
+ });
- vm.createEditorInstance();
+ it('updates state with the value of the model', () => {
+ const newContent = 'As Gregor Samsa\n awoke one morning\n';
+ vm.model.setValue(newContent);
- vm.$nextTick(() => {
- expect(vm.editor.createDiffInstance).toHaveBeenCalled();
+ vm.setupEditor();
- done();
- });
- });
+ expect(vm.file.content).toBe(newContent);
+ });
- it('calls createDiffInstance when viewer is a merge request diff', (done) => {
- vm.$store.state.viewer = 'mrdiff';
+ it('sets head model as staged file', () => {
+ vm.modelManager.dispose();
+ const addModelSpy = jest.spyOn(ModelManager.prototype, 'addModel');
- jest.spyOn(vm.editor, 'createDiffInstance').mockImplementation();
+ vm.$store.state.stagedFiles.push({ ...vm.file, key: 'staged' });
+ vm.file.staged = true;
+ vm.file.key = `unstaged-${vm.file.key}`;
- vm.createEditorInstance();
+ vm.setupEditor();
- vm.$nextTick(() => {
- expect(vm.editor.createDiffInstance).toHaveBeenCalled();
+ expect(addModelSpy).toHaveBeenCalledWith(vm.file, vm.$store.state.stagedFiles[0]);
+ });
+ });
- done();
- });
- });
+ describe('editor updateDimensions', () => {
+ let updateDimensionsSpy;
+ let updateDiffViewSpy;
+ beforeEach(async () => {
+ await createComponent();
+ updateDimensionsSpy = jest.spyOn(vm.editor, 'updateDimensions');
+ updateDiffViewSpy = jest.spyOn(vm.editor, 'updateDiffView').mockImplementation();
});
- describe('setupEditor', () => {
- it('creates new model', () => {
- jest.spyOn(vm.editor, 'createModel');
+ it('calls updateDimensions only when panelResizing is false', async () => {
+ expect(updateDimensionsSpy).not.toHaveBeenCalled();
+ expect(updateDiffViewSpy).not.toHaveBeenCalled();
+ expect(vm.$store.state.panelResizing).toBe(false); // default value
- Editor.editorInstance.modelManager.dispose();
+ vm.$store.state.panelResizing = true;
+ await vm.$nextTick();
- vm.setupEditor();
+ expect(updateDimensionsSpy).not.toHaveBeenCalled();
+ expect(updateDiffViewSpy).not.toHaveBeenCalled();
- expect(vm.editor.createModel).toHaveBeenCalledWith(vm.file, null);
- expect(vm.model).not.toBeNull();
- });
+ vm.$store.state.panelResizing = false;
+ await vm.$nextTick();
- it('attaches model to editor', () => {
- jest.spyOn(vm.editor, 'attachModel');
+ expect(updateDimensionsSpy).toHaveBeenCalledTimes(1);
+ expect(updateDiffViewSpy).toHaveBeenCalledTimes(1);
- Editor.editorInstance.modelManager.dispose();
+ vm.$store.state.panelResizing = true;
+ await vm.$nextTick();
- vm.setupEditor();
+ expect(updateDimensionsSpy).toHaveBeenCalledTimes(1);
+ expect(updateDiffViewSpy).toHaveBeenCalledTimes(1);
+ });
- expect(vm.editor.attachModel).toHaveBeenCalledWith(vm.model);
- });
+ it('calls updateDimensions when rightPane is toggled', async () => {
+ expect(updateDimensionsSpy).not.toHaveBeenCalled();
+ expect(updateDiffViewSpy).not.toHaveBeenCalled();
+ expect(vm.$store.state.rightPane.isOpen).toBe(false); // default value
- it('attaches model to merge request editor', () => {
- vm.$store.state.viewer = 'mrdiff';
- vm.file.mrChange = true;
- jest.spyOn(vm.editor, 'attachMergeRequestModel').mockImplementation();
+ vm.$store.state.rightPane.isOpen = true;
+ await vm.$nextTick();
- Editor.editorInstance.modelManager.dispose();
+ expect(updateDimensionsSpy).toHaveBeenCalledTimes(1);
+ expect(updateDiffViewSpy).toHaveBeenCalledTimes(1);
- vm.setupEditor();
+ vm.$store.state.rightPane.isOpen = false;
+ await vm.$nextTick();
- expect(vm.editor.attachMergeRequestModel).toHaveBeenCalledWith(vm.model);
- });
+ expect(updateDimensionsSpy).toHaveBeenCalledTimes(2);
+ expect(updateDiffViewSpy).toHaveBeenCalledTimes(2);
+ });
+ });
- it('does not attach model to merge request editor when not a MR change', () => {
- vm.$store.state.viewer = 'mrdiff';
- vm.file.mrChange = false;
- jest.spyOn(vm.editor, 'attachMergeRequestModel').mockImplementation();
+ describe('editor tabs', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
- Editor.editorInstance.modelManager.dispose();
+ it.each`
+ mode | isVisible
+ ${'edit'} | ${true}
+ ${'review'} | ${false}
+ ${'commit'} | ${false}
+ `('tabs in $mode are $isVisible', async ({ mode, isVisible } = {}) => {
+ vm.$store.state.currentActivityView = leftSidebarViews[mode].name;
- vm.setupEditor();
+ await vm.$nextTick();
+ expect(wrapper.find('.nav-links').exists()).toBe(isVisible);
+ });
+ });
- expect(vm.editor.attachMergeRequestModel).not.toHaveBeenCalledWith(vm.model);
+ describe('files in preview mode', () => {
+ let updateDimensionsSpy;
+ const changeViewMode = (viewMode) =>
+ vm.$store.dispatch('editor/updateFileEditor', {
+ path: vm.file.path,
+ data: { viewMode },
});
- it('adds callback methods', () => {
- jest.spyOn(vm.editor, 'onPositionChange');
-
- Editor.editorInstance.modelManager.dispose();
-
- vm.setupEditor();
-
- expect(vm.editor.onPositionChange).toHaveBeenCalled();
- expect(vm.model.events.size).toBe(2);
+ beforeEach(async () => {
+ await createComponent({
+ activeFile: dummyFile.markdown,
});
- it('updates state with the value of the model', () => {
- vm.model.setValue('testing 1234\n');
-
- vm.setupEditor();
-
- expect(vm.file.content).toBe('testing 1234\n');
- });
+ updateDimensionsSpy = jest.spyOn(vm.editor, 'updateDimensions');
- it('sets head model as staged file', () => {
- jest.spyOn(vm.editor, 'createModel');
+ changeViewMode(FILE_VIEW_MODE_PREVIEW);
+ await vm.$nextTick();
+ });
- Editor.editorInstance.modelManager.dispose();
+ it('do not show the editor', () => {
+ expect(vm.showEditor).toBe(false);
+ expect(findEditor().isVisible()).toBe(false);
+ });
- vm.$store.state.stagedFiles.push({ ...vm.file, key: 'staged' });
- vm.file.staged = true;
- vm.file.key = `unstaged-${vm.file.key}`;
+ it('updates dimensions when switching view back to edit', async () => {
+ expect(updateDimensionsSpy).not.toHaveBeenCalled();
- vm.setupEditor();
+ changeViewMode(FILE_VIEW_MODE_EDITOR);
+ await vm.$nextTick();
- expect(vm.editor.createModel).toHaveBeenCalledWith(vm.file, vm.$store.state.stagedFiles[0]);
- });
+ expect(updateDimensionsSpy).toHaveBeenCalled();
});
+ });
- describe('editor updateDimensions', () => {
- beforeEach(() => {
- jest.spyOn(vm.editor, 'updateDimensions');
- jest.spyOn(vm.editor, 'updateDiffView').mockImplementation();
- });
-
- it('calls updateDimensions when panelResizing is false', (done) => {
- vm.$store.state.panelResizing = true;
-
- vm.$nextTick()
- .then(() => {
- vm.$store.state.panelResizing = false;
- })
- .then(vm.$nextTick)
- .then(() => {
- expect(vm.editor.updateDimensions).toHaveBeenCalled();
- expect(vm.editor.updateDiffView).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('does not call updateDimensions when panelResizing is true', (done) => {
- vm.$store.state.panelResizing = true;
+ describe('initEditor', () => {
+ const hideEditorAndRunFn = async () => {
+ jest.clearAllMocks();
+ jest.spyOn(vm, 'shouldHideEditor', 'get').mockReturnValue(true);
- vm.$nextTick(() => {
- expect(vm.editor.updateDimensions).not.toHaveBeenCalled();
- expect(vm.editor.updateDiffView).not.toHaveBeenCalled();
+ vm.initEditor();
+ await vm.$nextTick();
+ };
- done();
- });
+ it('does not fetch file information for temp entries', async () => {
+ await createComponent({
+ activeFile: createActiveFile(),
});
- it('calls updateDimensions when rightPane is opened', (done) => {
- vm.$store.state.rightPane.isOpen = true;
-
- vm.$nextTick(() => {
- expect(vm.editor.updateDimensions).toHaveBeenCalled();
- expect(vm.editor.updateDiffView).toHaveBeenCalled();
-
- done();
- });
- });
+ expect(vm.getFileData).not.toHaveBeenCalled();
});
- describe('show tabs', () => {
- it('shows tabs in edit mode', () => {
- expect(vm.$el.querySelector('.nav-links')).not.toBe(null);
+ it('is being initialised for files without content even if shouldHideEditor is `true`', async () => {
+ await createComponent({
+ activeFile: dummyFile.empty,
});
- it('hides tabs in review mode', (done) => {
- vm.$store.state.currentActivityView = leftSidebarViews.review.name;
+ await hideEditorAndRunFn();
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.nav-links')).toBe(null);
+ expect(vm.getFileData).toHaveBeenCalled();
+ expect(vm.getRawFileData).toHaveBeenCalled();
+ });
- done();
- });
+ it('does not initialize editor for files already with content when shouldHideEditor is `true`', async () => {
+ await createComponent({
+ activeFile: createActiveFile(),
});
- it('hides tabs in commit mode', (done) => {
- vm.$store.state.currentActivityView = leftSidebarViews.commit.name;
+ await hideEditorAndRunFn();
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.nav-links')).toBe(null);
+ expect(vm.getFileData).not.toHaveBeenCalled();
+ expect(vm.getRawFileData).not.toHaveBeenCalled();
+ expect(createInstanceSpy).not.toHaveBeenCalled();
+ });
+ });
- done();
- });
+ describe('updates on file changes', () => {
+ beforeEach(async () => {
+ await createComponent({
+ activeFile: createActiveFile({
+ content: 'foo', // need to prevent full cycle of initEditor
+ }),
});
+ jest.spyOn(vm, 'initEditor').mockImplementation();
});
- describe('when files view mode is preview', () => {
- beforeEach((done) => {
- jest.spyOn(vm.editor, 'updateDimensions').mockImplementation();
- changeViewMode(FILE_VIEW_MODE_PREVIEW);
- vm.file.name = 'myfile.md';
- vm.file.content = 'hello world';
+ it('calls removePendingTab when old file is pending', async () => {
+ jest.spyOn(vm, 'shouldHideEditor', 'get').mockReturnValue(true);
+ jest.spyOn(vm, 'removePendingTab').mockImplementation();
- vm.$nextTick(done);
- });
+ const origFile = vm.file;
+ vm.file.pending = true;
+ await vm.$nextTick();
- it('should hide editor', () => {
- expect(vm.showEditor).toBe(false);
- expect(findEditor()).toHaveCss({ display: 'none' });
+ wrapper.setProps({
+ file: file('testing'),
});
+ vm.file.content = 'foo'; // need to prevent full cycle of initEditor
+ await vm.$nextTick();
- describe('when file view mode changes to editor', () => {
- it('should update dimensions', () => {
- changeViewMode(FILE_VIEW_MODE_EDITOR);
-
- return vm.$nextTick().then(() => {
- expect(vm.editor.updateDimensions).toHaveBeenCalled();
- });
- });
- });
+ expect(vm.removePendingTab).toHaveBeenCalledWith(origFile);
});
- describe('initEditor', () => {
- beforeEach(() => {
- vm.file.tempFile = false;
- jest.spyOn(vm.editor, 'createInstance').mockImplementation();
- jest.spyOn(vm, 'shouldHideEditor', 'get').mockReturnValue(true);
- });
+ it('does not call initEditor if the file did not change', async () => {
+ Vue.set(vm, 'file', vm.file);
+ await vm.$nextTick();
- it('does not fetch file information for temp entries', (done) => {
- vm.file.tempFile = true;
-
- vm.initEditor();
- vm.$nextTick()
- .then(() => {
- expect(vm.getFileData).not.toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('is being initialised for files without content even if shouldHideEditor is `true`', (done) => {
- vm.file.content = '';
- vm.file.raw = '';
+ expect(vm.initEditor).not.toHaveBeenCalled();
+ });
- vm.initEditor();
+ it('calls initEditor when file key is changed', async () => {
+ expect(vm.initEditor).not.toHaveBeenCalled();
- vm.$nextTick()
- .then(() => {
- expect(vm.getFileData).toHaveBeenCalled();
- expect(vm.getRawFileData).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
+ wrapper.setProps({
+ file: {
+ ...vm.file,
+ key: 'new',
+ },
});
+ await vm.$nextTick();
- it('does not initialize editor for files already with content', (done) => {
- vm.file.content = 'foo';
-
- vm.initEditor();
- vm.$nextTick()
- .then(() => {
- expect(vm.getFileData).not.toHaveBeenCalled();
- expect(vm.getRawFileData).not.toHaveBeenCalled();
- expect(vm.editor.createInstance).not.toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
- });
+ expect(vm.initEditor).toHaveBeenCalled();
+ });
+ });
+
+ describe('populates editor with the fetched content', () => {
+ const createRemoteFile = (name) => ({
+ ...file(name),
+ tmpFile: false,
});
- describe('updates on file changes', () => {
- beforeEach(() => {
- jest.spyOn(vm, 'initEditor').mockImplementation();
- });
+ beforeEach(async () => {
+ await createComponent();
+ vm.getRawFileData.mockRestore();
+ });
- it('calls removePendingTab when old file is pending', (done) => {
- jest.spyOn(vm, 'shouldHideEditor', 'get').mockReturnValue(true);
- jest.spyOn(vm, 'removePendingTab').mockImplementation();
+ it('after switching viewer from edit to diff', async () => {
+ const f = createRemoteFile('newFile');
+ Vue.set(vm.$store.state.entries, f.path, f);
- vm.file.pending = true;
+ jest.spyOn(service, 'getRawFileData').mockImplementation(async () => {
+ expect(vm.file.loading).toBe(true);
- vm.$nextTick()
- .then(() => {
- vm.file = file('testing');
- vm.file.content = 'foo'; // need to prevent full cycle of initEditor
+ // switching from edit to diff mode usually triggers editor initialization
+ vm.$store.state.viewer = viewerTypes.diff;
- return vm.$nextTick();
- })
- .then(() => {
- expect(vm.removePendingTab).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
+ // we delay returning the file to make sure editor doesn't initialize before we fetch file content
+ await waitUsingRealTimer(30);
+ return 'rawFileData123\n';
});
- it('does not call initEditor if the file did not change', (done) => {
- Vue.set(vm, 'file', vm.file);
-
- vm.$nextTick()
- .then(() => {
- expect(vm.initEditor).not.toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
+ wrapper.setProps({
+ file: f,
});
- it('calls initEditor when file key is changed', (done) => {
- expect(vm.initEditor).not.toHaveBeenCalled();
+ await waitForEditorSetup();
+ expect(vm.model.getModel().getValue()).toBe('rawFileData123\n');
+ });
- Vue.set(vm, 'file', {
- ...vm.file,
- key: 'new',
+ it('after opening multiple files at the same time', async () => {
+ const fileA = createRemoteFile('fileA');
+ const aContent = 'fileA-rawContent\n';
+ const bContent = 'fileB-rawContent\n';
+ const fileB = createRemoteFile('fileB');
+ Vue.set(vm.$store.state.entries, fileA.path, fileA);
+ Vue.set(vm.$store.state.entries, fileB.path, fileB);
+
+ jest
+ .spyOn(service, 'getRawFileData')
+ .mockImplementation(async () => {
+ // opening fileB while the content of fileA is still being fetched
+ wrapper.setProps({
+ file: fileB,
+ });
+ return aContent;
+ })
+ .mockImplementationOnce(async () => {
+ // we delay returning fileB content to make sure the editor doesn't initialize prematurely
+ await waitUsingRealTimer(30);
+ return bContent;
});
- vm.$nextTick()
- .then(() => {
- expect(vm.initEditor).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
+ wrapper.setProps({
+ file: fileA,
});
- });
- describe('populates editor with the fetched content', () => {
- beforeEach(() => {
- vm.getRawFileData.mockRestore();
- });
+ await waitForEditorSetup();
+ expect(vm.model.getModel().getValue()).toBe(bContent);
+ });
+ });
- const createRemoteFile = (name) => ({
- ...file(name),
- tmpFile: false,
+ describe('onPaste', () => {
+ const setFileName = (name) =>
+ createActiveFile({
+ content: 'hello world\n',
+ name,
+ path: `foo/${name}`,
+ key: 'new',
});
- it('after switching viewer from edit to diff', async () => {
- jest.spyOn(service, 'getRawFileData').mockImplementation(async () => {
- expect(vm.file.loading).toBe(true);
-
- // switching from edit to diff mode usually triggers editor initialization
- store.state.viewer = viewerTypes.diff;
+ const pasteImage = () => {
+ window.dispatchEvent(
+ Object.assign(new Event('paste'), {
+ clipboardData: {
+ files: [new File(['foo'], 'foo.png', { type: 'image/png' })],
+ },
+ }),
+ );
+ };
- // we delay returning the file to make sure editor doesn't initialize before we fetch file content
- await waitUsingRealTimer(30);
- return 'rawFileData123\n';
+ const watchState = (watched) =>
+ new Promise((resolve) => {
+ const unwatch = vm.$store.watch(watched, () => {
+ unwatch();
+ resolve();
});
-
- const f = createRemoteFile('newFile');
- Vue.set(store.state.entries, f.path, f);
-
- vm.file = f;
-
- await waitForEditorSetup();
- expect(vm.model.getModel().getValue()).toBe('rawFileData123\n');
});
- it('after opening multiple files at the same time', async () => {
- const fileA = createRemoteFile('fileA');
- const fileB = createRemoteFile('fileB');
- Vue.set(store.state.entries, fileA.path, fileA);
- Vue.set(store.state.entries, fileB.path, fileB);
-
- jest
- .spyOn(service, 'getRawFileData')
- .mockImplementationOnce(async () => {
- // opening fileB while the content of fileA is still being fetched
- vm.file = fileB;
- return 'fileA-rawContent\n';
- })
- .mockImplementationOnce(async () => {
- // we delay returning fileB content to make sure the editor doesn't initialize prematurely
- await waitUsingRealTimer(30);
- return 'fileB-rawContent\n';
- });
+ // Pasting an image does a lot of things like using the FileReader API,
+ // so, waitForPromises isn't very reliable (and causes a flaky spec)
+ // Read more about state.watch: https://vuex.vuejs.org/api/#watch
+ const waitForFileContentChange = () => watchState((s) => s.entries['foo/bar.md'].content);
- vm.file = fileA;
-
- await waitForEditorSetup();
- expect(vm.model.getModel().getValue()).toBe('fileB-rawContent\n');
+ beforeEach(async () => {
+ await createComponent({
+ state: {
+ trees: {
+ 'gitlab-org/gitlab': { tree: [] },
+ },
+ currentProjectId: 'gitlab-org',
+ currentBranchId: 'gitlab',
+ },
+ activeFile: setFileName('bar.md'),
});
- });
-
- describe('onPaste', () => {
- const setFileName = (name) => {
- Vue.set(vm, 'file', {
- ...vm.file,
- content: 'hello world\n',
- name,
- path: `foo/${name}`,
- key: 'new',
- });
- vm.$store.state.entries[vm.file.path] = vm.file;
- };
+ vm.setupEditor();
- const pasteImage = () => {
- window.dispatchEvent(
- Object.assign(new Event('paste'), {
- clipboardData: {
- files: [new File(['foo'], 'foo.png', { type: 'image/png' })],
- },
- }),
- );
- };
-
- const watchState = (watched) =>
- new Promise((resolve) => {
- const unwatch = vm.$store.watch(watched, () => {
- unwatch();
- resolve();
- });
- });
+ await waitForPromises();
+ // set cursor to line 2, column 1
+ vm.editor.setSelection(new Range(2, 1, 2, 1));
+ vm.editor.focus();
- // Pasting an image does a lot of things like using the FileReader API,
- // so, waitForPromises isn't very reliable (and causes a flaky spec)
- // Read more about state.watch: https://vuex.vuejs.org/api/#watch
- const waitForFileContentChange = () => watchState((s) => s.entries['foo/bar.md'].content);
-
- beforeEach(() => {
- setFileName('bar.md');
-
- vm.$store.state.trees['gitlab-org/gitlab'] = { tree: [] };
- vm.$store.state.currentProjectId = 'gitlab-org';
- vm.$store.state.currentBranchId = 'gitlab';
-
- // create a new model each time, otherwise tests conflict with each other
- // because of same model being used in multiple tests
- Editor.editorInstance.modelManager.dispose();
- vm.setupEditor();
+ jest.spyOn(vm.editor, 'hasTextFocus').mockReturnValue(true);
+ });
- return waitForPromises().then(() => {
- // set cursor to line 2, column 1
- vm.editor.instance.setSelection(new Range(2, 1, 2, 1));
- vm.editor.instance.focus();
+ it('adds an image entry to the same folder for a pasted image in a markdown file', async () => {
+ pasteImage();
- jest.spyOn(vm.editor.instance, 'hasTextFocus').mockReturnValue(true);
- });
+ await waitForFileContentChange();
+ expect(vm.$store.state.entries['foo/foo.png']).toMatchObject({
+ path: 'foo/foo.png',
+ type: 'blob',
+ content: 'Zm9v',
+ rawPath: 'data:image/png;base64,Zm9v',
});
+ });
- it('adds an image entry to the same folder for a pasted image in a markdown file', () => {
- pasteImage();
-
- return waitForFileContentChange().then(() => {
- expect(vm.$store.state.entries['foo/foo.png']).toMatchObject({
- path: 'foo/foo.png',
- type: 'blob',
- content: 'Zm9v',
- rawPath: 'data:image/png;base64,Zm9v',
- });
- });
- });
+ it("adds a markdown image tag to the file's contents", async () => {
+ pasteImage();
- it("adds a markdown image tag to the file's contents", () => {
- pasteImage();
+ await waitForFileContentChange();
+ expect(vm.file.content).toBe('hello world\n![foo.png](./foo.png)');
+ });
- return waitForFileContentChange().then(() => {
- expect(vm.file.content).toBe('hello world\n![foo.png](./foo.png)');
- });
+ it("does not add file to state or set markdown image syntax if the file isn't markdown", async () => {
+ wrapper.setProps({
+ file: setFileName('myfile.txt'),
});
+ pasteImage();
- it("does not add file to state or set markdown image syntax if the file isn't markdown", () => {
- setFileName('myfile.txt');
- pasteImage();
-
- return waitForPromises().then(() => {
- expect(vm.$store.state.entries['foo/foo.png']).toBeUndefined();
- expect(vm.file.content).toBe('hello world\n');
- });
- });
+ await waitForPromises();
+ expect(vm.$store.state.entries['foo/foo.png']).toBeUndefined();
+ expect(vm.file.content).toBe('hello world\n');
});
});
describe('fetchEditorconfigRules', () => {
- beforeEach(() => {
- exampleConfigs.forEach(({ path, content }) => {
- store.state.entries[path] = { ...file(), path, content };
- });
- });
-
it.each(exampleFiles)(
'does not fetch content from remote for .editorconfig files present locally (case %#)',
- ({ path, monacoRules }) => {
- createOpenFile(path);
- createComponent();
-
- return waitForEditorSetup().then(() => {
- expect(vm.rules).toEqual(monacoRules);
- expect(vm.model.options).toMatchObject(monacoRules);
- expect(vm.getFileData).not.toHaveBeenCalled();
- expect(vm.getRawFileData).not.toHaveBeenCalled();
+ async ({ path, monacoRules }) => {
+ await createComponent({
+ state: {
+ entries: (() => {
+ const res = {};
+ exampleConfigs.forEach(({ path: configPath, content }) => {
+ res[configPath] = { ...file(), path: configPath, content };
+ });
+ return res;
+ })(),
+ },
+ activeFile: createActiveFile({
+ path,
+ key: path,
+ name: 'myfile.txt',
+ content: 'hello world',
+ }),
});
+
+ expect(vm.rules).toEqual(monacoRules);
+ expect(vm.model.options).toMatchObject(monacoRules);
+ expect(vm.getFileData).not.toHaveBeenCalled();
+ expect(vm.getRawFileData).not.toHaveBeenCalled();
},
);
- it('fetches content from remote for .editorconfig files not available locally', () => {
- exampleConfigs.forEach(({ path }) => {
- delete store.state.entries[path].content;
- delete store.state.entries[path].raw;
+ it('fetches content from remote for .editorconfig files not available locally', async () => {
+ const activeFile = createActiveFile({
+ path: 'foo/bar/baz/test/my_spec.js',
+ key: 'foo/bar/baz/test/my_spec.js',
+ name: 'myfile.txt',
+ content: 'hello world',
+ });
+
+ const expectations = [
+ 'foo/bar/baz/.editorconfig',
+ 'foo/bar/.editorconfig',
+ 'foo/.editorconfig',
+ '.editorconfig',
+ ];
+
+ await createComponent({
+ state: {
+ entries: (() => {
+ const res = {
+ [activeFile.path]: activeFile,
+ };
+ exampleConfigs.forEach(({ path: configPath }) => {
+ const f = { ...file(), path: configPath };
+ delete f.content;
+ delete f.raw;
+ res[configPath] = f;
+ });
+ return res;
+ })(),
+ },
+ activeFile,
});
- // Include a "test" directory which does not exist in store. This one should be skipped.
- createOpenFile('foo/bar/baz/test/my_spec.js');
- createComponent();
-
- return waitForEditorSetup().then(() => {
- expect(vm.getFileData.mock.calls.map(([args]) => args)).toEqual([
- { makeFileActive: false, path: 'foo/bar/baz/.editorconfig' },
- { makeFileActive: false, path: 'foo/bar/.editorconfig' },
- { makeFileActive: false, path: 'foo/.editorconfig' },
- { makeFileActive: false, path: '.editorconfig' },
- ]);
- expect(vm.getRawFileData.mock.calls.map(([args]) => args)).toEqual([
- { path: 'foo/bar/baz/.editorconfig' },
- { path: 'foo/bar/.editorconfig' },
- { path: 'foo/.editorconfig' },
- { path: '.editorconfig' },
- ]);
- });
+ expect(service.getFileData.mock.calls.map(([args]) => args)).toEqual(
+ expectations.map((expectation) => expect.stringContaining(expectation)),
+ );
+ expect(service.getRawFileData.mock.calls.map(([args]) => args)).toEqual(
+ expectations.map((expectation) => expect.objectContaining({ path: expectation })),
+ );
});
});
});
diff --git a/spec/frontend/ide/components/repo_tab_spec.js b/spec/frontend/ide/components/repo_tab_spec.js
index b39a488b034..95d52e8f7a9 100644
--- a/spec/frontend/ide/components/repo_tab_spec.js
+++ b/spec/frontend/ide/components/repo_tab_spec.js
@@ -1,5 +1,7 @@
+import { GlTab } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
+import { stubComponent } from 'helpers/stub_component';
import RepoTab from '~/ide/components/repo_tab.vue';
import { createRouter } from '~/ide/ide_router';
import { createStore } from '~/ide/stores';
@@ -8,16 +10,25 @@ import { file } from '../helpers';
const localVue = createLocalVue();
localVue.use(Vuex);
+const GlTabStub = stubComponent(GlTab, {
+ template: '<li><slot name="title" /></li>',
+});
+
describe('RepoTab', () => {
let wrapper;
let store;
let router;
+ const findTab = () => wrapper.find(GlTabStub);
+
function createComponent(propsData) {
wrapper = mount(RepoTab, {
localVue,
store,
propsData,
+ stubs: {
+ GlTab: GlTabStub,
+ },
});
}
@@ -55,7 +66,7 @@ describe('RepoTab', () => {
jest.spyOn(wrapper.vm, 'openPendingTab').mockImplementation(() => {});
- await wrapper.trigger('click');
+ await findTab().vm.$emit('click');
expect(wrapper.vm.openPendingTab).not.toHaveBeenCalled();
});
@@ -67,7 +78,7 @@ describe('RepoTab', () => {
jest.spyOn(wrapper.vm, 'clickFile').mockImplementation(() => {});
- wrapper.trigger('click');
+ findTab().vm.$emit('click');
expect(wrapper.vm.clickFile).toHaveBeenCalledWith(wrapper.vm.tab);
});
@@ -91,11 +102,11 @@ describe('RepoTab', () => {
tab,
});
- await wrapper.trigger('mouseover');
+ await findTab().vm.$emit('mouseover');
expect(wrapper.find('.file-modified').exists()).toBe(false);
- await wrapper.trigger('mouseout');
+ await findTab().vm.$emit('mouseout');
expect(wrapper.find('.file-modified').exists()).toBe(true);
});