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')
-rw-r--r--spec/frontend/boards/board_card_spec.js2
-rw-r--r--spec/frontend/cycle_analytics/stage_nav_item_spec.js3
-rw-r--r--spec/frontend/deploy_keys/components/action_btn_spec.js2
-rw-r--r--spec/frontend/deploy_keys/components/app_spec.js6
-rw-r--r--spec/frontend/groups/components/app_spec.js8
-rw-r--r--spec/frontend/groups/components/groups_spec.js5
-rw-r--r--spec/frontend/groups/components/item_actions_spec.js5
-rw-r--r--spec/frontend/helpers/wait_using_real_timer.js7
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js118
-rw-r--r--spec/frontend/ide/stores/actions/file_spec.js54
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js3
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js12
12 files changed, 177 insertions, 48 deletions
diff --git a/spec/frontend/boards/board_card_spec.js b/spec/frontend/boards/board_card_spec.js
index 959c71d05ca..6bfbc7cb12f 100644
--- a/spec/frontend/boards/board_card_spec.js
+++ b/spec/frontend/boards/board_card_spec.js
@@ -196,7 +196,7 @@ describe('Board card', () => {
wrapper.trigger('mousedown');
wrapper.trigger('mouseup');
- expect(eventHub.$emit).toHaveBeenCalledWith('newDetailIssue', wrapper.vm.issue, undefined);
+ expect(eventHub.$emit).toHaveBeenCalledWith('newDetailIssue', [wrapper.vm.issue, undefined]);
expect(boardsStore.detail.list).toEqual(wrapper.vm.list);
});
diff --git a/spec/frontend/cycle_analytics/stage_nav_item_spec.js b/spec/frontend/cycle_analytics/stage_nav_item_spec.js
index 480bb756731..1fe80d3b1ce 100644
--- a/spec/frontend/cycle_analytics/stage_nav_item_spec.js
+++ b/spec/frontend/cycle_analytics/stage_nav_item_spec.js
@@ -10,7 +10,6 @@ describe('StageNavItem', () => {
const func = shallow ? shallowMount : mount;
return func(StageNavItem, {
propsData: {
- canEdit: false,
isActive: false,
isUserAllowed: false,
isDefaultStage: true,
@@ -125,7 +124,7 @@ describe('StageNavItem', () => {
describe('User can edit stages', () => {
beforeEach(() => {
- wrapper = createComponent({ canEdit: true, isUserAllowed: true }, false);
+ wrapper = createComponent({ isUserAllowed: true }, false);
});
afterEach(() => {
diff --git a/spec/frontend/deploy_keys/components/action_btn_spec.js b/spec/frontend/deploy_keys/components/action_btn_spec.js
index b8211b02464..78312751429 100644
--- a/spec/frontend/deploy_keys/components/action_btn_spec.js
+++ b/spec/frontend/deploy_keys/components/action_btn_spec.js
@@ -32,7 +32,7 @@ describe('Deploy keys action btn', () => {
wrapper.trigger('click');
return wrapper.vm.$nextTick().then(() => {
- expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', deployKey, expect.anything());
+ expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', [deployKey, expect.any(Function)]);
});
});
diff --git a/spec/frontend/deploy_keys/components/app_spec.js b/spec/frontend/deploy_keys/components/app_spec.js
index 291502c9ed7..cf54b7e60aa 100644
--- a/spec/frontend/deploy_keys/components/app_spec.js
+++ b/spec/frontend/deploy_keys/components/app_spec.js
@@ -88,7 +88,7 @@ describe('Deploy keys app component', () => {
jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
jest.spyOn(wrapper.vm.service, 'enableKey').mockImplementation(() => Promise.resolve());
- eventHub.$emit('enable.key', key);
+ eventHub.$emit('enable.key', [key]);
return wrapper.vm.$nextTick();
})
@@ -106,7 +106,7 @@ describe('Deploy keys app component', () => {
jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve());
- eventHub.$emit('disable.key', key);
+ eventHub.$emit('disable.key', [key]);
return wrapper.vm.$nextTick();
})
@@ -124,7 +124,7 @@ describe('Deploy keys app component', () => {
jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve());
- eventHub.$emit('remove.key', key);
+ eventHub.$emit('remove.key', [key]);
return wrapper.vm.$nextTick();
})
diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js
index 35eda21e047..3c04cac7402 100644
--- a/spec/frontend/groups/components/app_spec.js
+++ b/spec/frontend/groups/components/app_spec.js
@@ -184,7 +184,7 @@ describe('AppComponent', () => {
jest.spyOn(window.history, 'replaceState').mockImplementation(() => {});
jest.spyOn($, 'scrollTo').mockImplementation(() => {});
- const fetchPagePromise = vm.fetchPage(2, null, null, true);
+ const fetchPagePromise = vm.fetchPage([2, null, null, true]);
expect(vm.isLoading).toBe(true);
expect(vm.fetchGroups).toHaveBeenCalledWith({
@@ -275,7 +275,7 @@ describe('AppComponent', () => {
expect(vm.targetGroup).toBe(null);
expect(vm.targetParentGroup).toBe(null);
- vm.showLeaveGroupModal(group, mockParentGroupItem);
+ vm.showLeaveGroupModal([group, mockParentGroupItem]);
expect(vm.targetGroup).not.toBe(null);
expect(vm.targetParentGroup).not.toBe(null);
@@ -286,7 +286,7 @@ describe('AppComponent', () => {
expect(vm.showModal).toBe(false);
expect(vm.groupLeaveConfirmationMessage).toBe('');
- vm.showLeaveGroupModal(group, mockParentGroupItem);
+ vm.showLeaveGroupModal([group, mockParentGroupItem]);
expect(vm.showModal).toBe(true);
expect(vm.groupLeaveConfirmationMessage).toBe(
@@ -298,7 +298,7 @@ describe('AppComponent', () => {
describe('hideLeaveGroupModal', () => {
it('hides modal confirmation which is shown before leaving the group', () => {
const group = { ...mockParentGroupItem };
- vm.showLeaveGroupModal(group, mockParentGroupItem);
+ vm.showLeaveGroupModal([group, mockParentGroupItem]);
expect(vm.showModal).toBe(true);
vm.hideLeaveGroupModal();
diff --git a/spec/frontend/groups/components/groups_spec.js b/spec/frontend/groups/components/groups_spec.js
index 6205400eb03..42c3c813941 100644
--- a/spec/frontend/groups/components/groups_spec.js
+++ b/spec/frontend/groups/components/groups_spec.js
@@ -41,13 +41,12 @@ describe('GroupsComponent', () => {
vm.change(2);
- expect(eventHub.$emit).toHaveBeenCalledWith(
- 'fetchPage',
+ expect(eventHub.$emit).toHaveBeenCalledWith('fetchPage', [
2,
expect.any(Object),
expect.any(Object),
expect.any(Object),
- );
+ ]);
});
});
});
diff --git a/spec/frontend/groups/components/item_actions_spec.js b/spec/frontend/groups/components/item_actions_spec.js
index c0dc1a816e6..1ca42f28c7f 100644
--- a/spec/frontend/groups/components/item_actions_spec.js
+++ b/spec/frontend/groups/components/item_actions_spec.js
@@ -31,11 +31,10 @@ describe('ItemActionsComponent', () => {
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
vm.onLeaveGroup();
- expect(eventHub.$emit).toHaveBeenCalledWith(
- 'showLeaveGroupModal',
+ expect(eventHub.$emit).toHaveBeenCalledWith('showLeaveGroupModal', [
vm.group,
vm.parentGroup,
- );
+ ]);
});
});
});
diff --git a/spec/frontend/helpers/wait_using_real_timer.js b/spec/frontend/helpers/wait_using_real_timer.js
new file mode 100644
index 00000000000..ddf23cd97b4
--- /dev/null
+++ b/spec/frontend/helpers/wait_using_real_timer.js
@@ -0,0 +1,7 @@
+/* useful for timing promises when jest fakeTimers are not reliable enough */
+export default timeout =>
+ new Promise(resolve => {
+ jest.useRealTimers();
+ setTimeout(resolve, timeout);
+ jest.useFakeTimers();
+ });
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 6187ed8cd8b..0ba58561f08 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -4,19 +4,25 @@ import MockAdapter from 'axios-mock-adapter';
import '~/behaviors/markdown/render_gfm';
import { Range } from 'monaco-editor';
import axios from '~/lib/utils/axios_utils';
+import service from '~/ide/services';
import { createStoreOptions } from '~/ide/stores';
import RepoEditor from '~/ide/components/repo_editor.vue';
import Editor from '~/ide/lib/editor';
-import { leftSidebarViews, FILE_VIEW_MODE_EDITOR, FILE_VIEW_MODE_PREVIEW } from '~/ide/constants';
+import {
+ leftSidebarViews,
+ FILE_VIEW_MODE_EDITOR,
+ FILE_VIEW_MODE_PREVIEW,
+ viewerTypes,
+} from '~/ide/constants';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { file } from '../helpers';
import { exampleConfigs, exampleFiles } from '../lib/editorconfig/mock_data';
+import waitUsingRealTimer from 'helpers/wait_using_real_timer';
describe('RepoEditor', () => {
let vm;
let store;
- let mockActions;
const waitForEditorSetup = () =>
new Promise(resolve => {
@@ -30,6 +36,10 @@ describe('RepoEditor', () => {
vm = createComponentWithStore(Vue.extend(RepoEditor), store, {
file: store.state.openFiles[0],
});
+
+ jest.spyOn(vm, 'getFileData').mockResolvedValue();
+ jest.spyOn(vm, 'getRawFileData').mockResolvedValue();
+
vm.$mount();
};
@@ -43,21 +53,12 @@ describe('RepoEditor', () => {
};
beforeEach(() => {
- mockActions = {
- getFileData: jest.fn().mockResolvedValue(),
- getRawFileData: jest.fn().mockResolvedValue(),
- };
-
const f = {
...file(),
viewMode: FILE_VIEW_MODE_EDITOR,
};
const storeOptions = createStoreOptions();
- storeOptions.actions = {
- ...storeOptions.actions,
- ...mockActions,
- };
store = new Vuex.Store(storeOptions);
f.active = true;
@@ -438,7 +439,7 @@ describe('RepoEditor', () => {
vm.initEditor();
vm.$nextTick()
.then(() => {
- expect(mockActions.getFileData).not.toHaveBeenCalled();
+ expect(vm.getFileData).not.toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
@@ -449,10 +450,11 @@ describe('RepoEditor', () => {
vm.file.raw = '';
vm.initEditor();
+
vm.$nextTick()
.then(() => {
- expect(mockActions.getFileData).toHaveBeenCalled();
- expect(mockActions.getRawFileData).toHaveBeenCalled();
+ expect(vm.getFileData).toHaveBeenCalled();
+ expect(vm.getRawFileData).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
@@ -464,8 +466,8 @@ describe('RepoEditor', () => {
vm.initEditor();
vm.$nextTick()
.then(() => {
- expect(mockActions.getFileData).not.toHaveBeenCalled();
- expect(mockActions.getRawFileData).not.toHaveBeenCalled();
+ expect(vm.getFileData).not.toHaveBeenCalled();
+ expect(vm.getRawFileData).not.toHaveBeenCalled();
expect(vm.editor.createInstance).not.toHaveBeenCalled();
})
.then(done)
@@ -526,6 +528,65 @@ describe('RepoEditor', () => {
});
});
+ describe('populates editor with the fetched content', () => {
+ beforeEach(() => {
+ vm.getRawFileData.mockRestore();
+ });
+
+ const createRemoteFile = name => ({
+ ...file(name),
+ tmpFile: false,
+ });
+
+ 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;
+
+ // we delay returning the file to make sure editor doesn't initialize before we fetch file content
+ await waitUsingRealTimer(10);
+ return 'rawFileData123\n';
+ });
+
+ const f = createRemoteFile('newFile');
+ Vue.set(store.state.entries, f.path, f);
+
+ vm.file = f;
+
+ // use the real timer to accurately simulate the race condition
+ await waitUsingRealTimer(20);
+ 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(10);
+ return 'fileB-rawContent\n';
+ });
+
+ vm.file = fileA;
+
+ // use the real timer to accurately simulate the race condition
+ await waitUsingRealTimer(20);
+ expect(vm.model.getModel().getValue()).toBe('fileB-rawContent\n');
+ });
+ });
+
describe('onPaste', () => {
const setFileName = name => {
Vue.set(vm, 'file', {
@@ -557,6 +618,11 @@ describe('RepoEditor', () => {
});
});
+ // 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');
@@ -576,13 +642,10 @@ describe('RepoEditor', () => {
});
});
- // The following test is flaky
- // see https://gitlab.com/gitlab-org/gitlab/-/issues/221039
- // eslint-disable-next-line jest/no-disabled-tests
- it.skip('adds an image entry to the same folder for a pasted image in a markdown file', () => {
+ it('adds an image entry to the same folder for a pasted image in a markdown file', () => {
pasteImage();
- return waitForPromises().then(() => {
+ return waitForFileContentChange().then(() => {
expect(vm.$store.state.entries['foo/foo.png']).toMatchObject({
path: 'foo/foo.png',
type: 'blob',
@@ -596,10 +659,7 @@ describe('RepoEditor', () => {
it("adds a markdown image tag to the file's contents", () => {
pasteImage();
- // 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
- return watchState(s => s.entries['foo/bar.md'].content).then(() => {
+ return waitForFileContentChange().then(() => {
expect(vm.file.content).toBe('hello world\n![foo.png](./foo.png)');
});
});
@@ -632,8 +692,8 @@ describe('RepoEditor', () => {
return waitForEditorSetup().then(() => {
expect(vm.rules).toEqual(monacoRules);
expect(vm.model.options).toMatchObject(monacoRules);
- expect(mockActions.getFileData).not.toHaveBeenCalled();
- expect(mockActions.getRawFileData).not.toHaveBeenCalled();
+ expect(vm.getFileData).not.toHaveBeenCalled();
+ expect(vm.getRawFileData).not.toHaveBeenCalled();
});
},
);
@@ -649,13 +709,13 @@ describe('RepoEditor', () => {
createComponent();
return waitForEditorSetup().then(() => {
- expect(mockActions.getFileData.mock.calls.map(([, args]) => args)).toEqual([
+ 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(mockActions.getRawFileData.mock.calls.map(([, args]) => args)).toEqual([
+ expect(vm.getRawFileData.mock.calls.map(([args]) => args)).toEqual([
{ path: 'foo/bar/baz/.editorconfig' },
{ path: 'foo/bar/.editorconfig' },
{ path: 'foo/.editorconfig' },
diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js
index e2dc7626c67..827759c4901 100644
--- a/spec/frontend/ide/stores/actions/file_spec.js
+++ b/spec/frontend/ide/stores/actions/file_spec.js
@@ -446,6 +446,54 @@ describe('IDE store file actions', () => {
})
.catch(done.fail);
});
+
+ describe('sets file loading to true', () => {
+ let loadingWhenGettingRawData;
+ let loadingWhenGettingBaseRawData;
+
+ beforeEach(() => {
+ loadingWhenGettingRawData = undefined;
+ loadingWhenGettingBaseRawData = undefined;
+
+ jest.spyOn(service, 'getRawFileData').mockImplementation(f => {
+ loadingWhenGettingRawData = f.loading;
+ return Promise.resolve('raw');
+ });
+ jest.spyOn(service, 'getBaseRawFileData').mockImplementation(f => {
+ loadingWhenGettingBaseRawData = f.loading;
+ return Promise.resolve('rawBase');
+ });
+ });
+
+ it('when getting raw file data', async () => {
+ expect(tmpFile.loading).toBe(false);
+
+ await store.dispatch('getRawFileData', { path: tmpFile.path });
+
+ expect(loadingWhenGettingRawData).toBe(true);
+ expect(tmpFile.loading).toBe(false);
+ });
+
+ it('when getting base raw file data', async () => {
+ tmpFile.mrChange = { new_file: false };
+
+ expect(tmpFile.loading).toBe(false);
+
+ await store.dispatch('getRawFileData', { path: tmpFile.path });
+
+ expect(loadingWhenGettingBaseRawData).toBe(true);
+ expect(tmpFile.loading).toBe(false);
+ });
+
+ it('when file was already loading', async () => {
+ tmpFile.loading = true;
+
+ await store.dispatch('getRawFileData', { path: tmpFile.path });
+
+ expect(loadingWhenGettingRawData).toBe(true);
+ expect(tmpFile.loading).toBe(false);
+ });
+ });
});
describe('return JSON', () => {
@@ -489,6 +537,12 @@ describe('IDE store file actions', () => {
});
});
});
+
+ it('toggles loading off after error', async () => {
+ await expect(store.dispatch('getRawFileData', { path: tmpFile.path })).rejects.toThrow();
+
+ expect(tmpFile.loading).toBe(false);
+ });
});
});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js
index 8b0253dc01a..b77305277ea 100644
--- a/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
-import { GlLink } from '@gitlab/ui';
+import { GlLink, GlSprintf } from '@gitlab/ui';
import suggestPipelineComponent from '~/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue';
import stubChildren from 'helpers/stub_children';
import PipelineTourState from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue';
@@ -18,6 +18,7 @@ describe('MRWidgetHeader', () => {
propsData: { pipelinePath, pipelineSvgPath, humanAccess },
stubs: {
...stubChildren(PipelineTourState),
+ GlSprintf,
},
});
});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js
index e8f95e099cc..acb7316ae27 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { GlPopover } from '@gitlab/ui';
+import { GlPopover, GlLink, GlSprintf } from '@gitlab/ui';
import Cookies from 'js-cookie';
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
import pipelineTourState from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue';
@@ -51,6 +51,9 @@ describe('MRWidgetPipelineTour', () => {
Cookies.remove(cookieKey);
wrapper = shallowMount(pipelineTourState, {
propsData: popoverProps,
+ stubs: {
+ GlSprintf,
+ },
});
});
@@ -60,6 +63,13 @@ describe('MRWidgetPipelineTour', () => {
expect(popover.exists()).toBe(true);
});
+ it('renders the help link', () => {
+ const link = wrapper.find(GlLink);
+
+ expect(link.exists()).toBe(true);
+ expect(link.attributes('href')).toBe(wrapper.vm.$options.helpURL);
+ });
+
it('renders the show me how button', () => {
const button = findOkBtn();