summaryrefslogtreecommitdiff
path: root/spec/frontend/ide
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /spec/frontend/ide
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
downloadgitlab-ce-aee0a117a889461ce8ced6fcf73207fe017f1d99.tar.gz
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'spec/frontend/ide')
-rw-r--r--spec/frontend/ide/components/ide_tree_list_spec.js13
-rw-r--r--spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap10
-rw-r--r--spec/frontend/ide/components/pipelines/empty_state_spec.js44
-rw-r--r--spec/frontend/ide/components/pipelines/list_spec.js8
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js85
-rw-r--r--spec/frontend/ide/ide_router_spec.js61
-rw-r--r--spec/frontend/ide/services/index_spec.js59
-rw-r--r--spec/frontend/ide/stores/actions/project_spec.js93
-rw-r--r--spec/frontend/ide/stores/mutations/project_spec.js37
9 files changed, 298 insertions, 112 deletions
diff --git a/spec/frontend/ide/components/ide_tree_list_spec.js b/spec/frontend/ide/components/ide_tree_list_spec.js
index 85d9feb0c09..ace51204374 100644
--- a/spec/frontend/ide/components/ide_tree_list_spec.js
+++ b/spec/frontend/ide/components/ide_tree_list_spec.js
@@ -38,9 +38,16 @@ describe('IDE tree list', () => {
beforeEach(() => {
bootstrapWithTree();
+ jest.spyOn(vm, '$emit').mockImplementation(() => {});
+
vm.$mount();
});
+ it('emits tree-ready event', () => {
+ expect(vm.$emit).toHaveBeenCalledTimes(1);
+ expect(vm.$emit).toHaveBeenCalledWith('tree-ready');
+ });
+
it('renders loading indicator', (done) => {
store.state.trees['abcproject/main'].loading = true;
@@ -61,9 +68,15 @@ describe('IDE tree list', () => {
beforeEach(() => {
bootstrapWithTree(emptyBranchTree);
+ jest.spyOn(vm, '$emit').mockImplementation(() => {});
+
vm.$mount();
});
+ it('still emits tree-ready event', () => {
+ expect(vm.$emit).toHaveBeenCalledWith('tree-ready');
+ });
+
it('does not load files if the branch is empty', () => {
expect(vm.$el.textContent).not.toContain('fileName');
expect(vm.$el.textContent).toContain('No files');
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 47e3a56e83d..069b6927bac 100644
--- a/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap
+++ b/spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap
@@ -6,10 +6,10 @@ exports[`IDE pipelines list when loaded renders empty state when no latestPipeli
>
<!---->
- <empty-state-stub
- cansetci="true"
- class="gl-p-5"
- emptystatesvgpath="http://test.host"
- />
+ <div
+ class="gl-h-full gl-display-flex gl-flex-direction-column gl-justify-content-center"
+ >
+ <empty-state-stub />
+ </div>
</div>
`;
diff --git a/spec/frontend/ide/components/pipelines/empty_state_spec.js b/spec/frontend/ide/components/pipelines/empty_state_spec.js
new file mode 100644
index 00000000000..f7409fc36be
--- /dev/null
+++ b/spec/frontend/ide/components/pipelines/empty_state_spec.js
@@ -0,0 +1,44 @@
+import { GlEmptyState } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import EmptyState from '~/ide/components/pipelines/empty_state.vue';
+import { createStore } from '~/ide/stores';
+
+const TEST_PIPELINES_EMPTY_STATE_SVG_PATH = 'illustrations/test/pipelines.svg';
+
+describe('~/ide/components/pipelines/empty_state.vue', () => {
+ let store;
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(EmptyState, {
+ store,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ store.dispatch('setEmptyStateSvgs', {
+ pipelinesEmptyStateSvgPath: TEST_PIPELINES_EMPTY_STATE_SVG_PATH,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('default', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders empty state', () => {
+ expect(wrapper.find(GlEmptyState).props()).toMatchObject({
+ title: EmptyState.i18n.title,
+ description: EmptyState.i18n.description,
+ primaryButtonText: EmptyState.i18n.primaryButtonText,
+ primaryButtonLink: '/help/ci/quick_start/index.md',
+ svgPath: TEST_PIPELINES_EMPTY_STATE_SVG_PATH,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ide/components/pipelines/list_spec.js b/spec/frontend/ide/components/pipelines/list_spec.js
index a917f4c0230..8a3606e27eb 100644
--- a/spec/frontend/ide/components/pipelines/list_spec.js
+++ b/spec/frontend/ide/components/pipelines/list_spec.js
@@ -2,10 +2,10 @@ import { GlLoadingIcon, GlTab } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
-import { TEST_HOST } from 'helpers/test_constants';
import { pipelines } from 'jest/ide/mock_data';
import JobsList from '~/ide/components/jobs/list.vue';
import List from '~/ide/components/pipelines/list.vue';
+import EmptyState from '~/ide/components/pipelines/empty_state.vue';
import IDEServices from '~/ide/services';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
@@ -18,9 +18,6 @@ jest.mock('~/ide/services', () => ({
describe('IDE pipelines list', () => {
let wrapper;
- const defaultState = {
- pipelinesEmptyStateSvgPath: TEST_HOST,
- };
const defaultPipelinesState = {
stages: [],
failedStages: [],
@@ -38,7 +35,6 @@ describe('IDE pipelines list', () => {
currentProject: () => ({ web_url: 'some/url ', path_with_namespace: fakeProjectPath }),
},
state: {
- ...defaultState,
...rootState,
},
modules: {
@@ -131,6 +127,8 @@ describe('IDE pipelines list', () => {
it('renders empty state when no latestPipeline', () => {
createComponent({}, { ...defaultPipelinesLoadedState, latestPipeline: null });
+
+ expect(wrapper.find(EmptyState).exists()).toBe(true);
expect(wrapper.element).toMatchSnapshot();
});
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index c2212eea849..c957c64aa10 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -9,7 +9,7 @@ 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 { EditorMarkdownExtension } from '~/editor/extensions/source_editor_markdown_ext';
-import { EditorWebIdeExtension } from '~/editor/extensions/source_editor_webide_ext';
+import { EditorMarkdownPreviewExtension } from '~/editor/extensions/source_editor_markdown_livepreview_ext';
import SourceEditor from '~/editor/source_editor';
import RepoEditor from '~/ide/components/repo_editor.vue';
import {
@@ -23,6 +23,8 @@ 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 SourceEditorInstance from '~/editor/source_editor_instance';
+import { spyOnApi } from 'jest/editor/helpers';
import { file } from '../helpers';
const PREVIEW_MARKDOWN_PATH = '/foo/bar/preview_markdown';
@@ -101,6 +103,7 @@ describe('RepoEditor', () => {
let createDiffInstanceSpy;
let createModelSpy;
let applyExtensionSpy;
+ let extensionsStore;
const waitForEditorSetup = () =>
new Promise((resolve) => {
@@ -120,6 +123,7 @@ describe('RepoEditor', () => {
});
await waitForPromises();
vm = wrapper.vm;
+ extensionsStore = wrapper.vm.globalEditor.extensionsStore;
jest.spyOn(vm, 'getFileData').mockResolvedValue();
jest.spyOn(vm, 'getRawFileData').mockResolvedValue();
};
@@ -127,28 +131,12 @@ describe('RepoEditor', () => {
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"]');
- const expectEditorMarkdownExtension = (shouldHaveExtension) => {
- if (shouldHaveExtension) {
- expect(applyExtensionSpy).toHaveBeenCalledWith(
- wrapper.vm.editor,
- expect.any(EditorMarkdownExtension),
- );
- // TODO: spying on extensions causes Jest to blow up, so we have to assert on
- // the public property the extension adds, as opposed to the args passed to the ctor
- expect(wrapper.vm.editor.previewMarkdownPath).toBe(PREVIEW_MARKDOWN_PATH);
- } else {
- expect(applyExtensionSpy).not.toHaveBeenCalledWith(
- wrapper.vm.editor,
- expect.any(EditorMarkdownExtension),
- );
- }
- };
beforeEach(() => {
createInstanceSpy = jest.spyOn(SourceEditor.prototype, EDITOR_CODE_INSTANCE_FN);
createDiffInstanceSpy = jest.spyOn(SourceEditor.prototype, EDITOR_DIFF_INSTANCE_FN);
createModelSpy = jest.spyOn(monacoEditor, 'createModel');
- applyExtensionSpy = jest.spyOn(SourceEditor, 'instanceApplyExtension');
+ applyExtensionSpy = jest.spyOn(SourceEditorInstance.prototype, 'use');
jest.spyOn(service, 'getFileData').mockResolvedValue();
jest.spyOn(service, 'getRawFileData').mockResolvedValue();
});
@@ -275,14 +263,13 @@ describe('RepoEditor', () => {
);
it('installs the WebIDE extension', async () => {
- const extensionSpy = jest.spyOn(SourceEditor, 'instanceApplyExtension');
await createComponent();
- expect(extensionSpy).toHaveBeenCalled();
- Reflect.ownKeys(EditorWebIdeExtension.prototype)
- .filter((fn) => fn !== 'constructor')
- .forEach((fn) => {
- expect(vm.editor[fn]).toBe(EditorWebIdeExtension.prototype[fn]);
- });
+ expect(applyExtensionSpy).toHaveBeenCalled();
+ const ideExtensionApi = extensionsStore.get('EditorWebIde').api;
+ Reflect.ownKeys(ideExtensionApi).forEach((fn) => {
+ expect(vm.editor[fn]).toBeDefined();
+ expect(vm.editor.methods[fn]).toBe('EditorWebIde');
+ });
});
it.each`
@@ -301,7 +288,20 @@ describe('RepoEditor', () => {
async ({ activeFile, viewer, shouldHaveMarkdownExtension } = {}) => {
await createComponent({ state: { viewer }, activeFile });
- expectEditorMarkdownExtension(shouldHaveMarkdownExtension);
+ if (shouldHaveMarkdownExtension) {
+ expect(applyExtensionSpy).toHaveBeenCalledWith({
+ definition: EditorMarkdownPreviewExtension,
+ setupOptions: { previewMarkdownPath: PREVIEW_MARKDOWN_PATH },
+ });
+ // TODO: spying on extensions causes Jest to blow up, so we have to assert on
+ // the public property the extension adds, as opposed to the args passed to the ctor
+ expect(wrapper.vm.editor.markdownPreview.path).toBe(PREVIEW_MARKDOWN_PATH);
+ } else {
+ expect(applyExtensionSpy).not.toHaveBeenCalledWith(
+ wrapper.vm.editor,
+ expect.any(EditorMarkdownExtension),
+ );
+ }
},
);
});
@@ -329,18 +329,6 @@ describe('RepoEditor', () => {
expect(vm.model).toBe(existingModel);
});
- it('adds callback methods', () => {
- jest.spyOn(vm.editor, 'onPositionChange');
- jest.spyOn(vm.model, 'onChange');
- jest.spyOn(vm.model, 'updateOptions');
-
- vm.setupEditor();
-
- expect(vm.editor.onPositionChange).toHaveBeenCalledTimes(1);
- expect(vm.model.onChange).toHaveBeenCalledTimes(1);
- expect(vm.model.updateOptions).toHaveBeenCalledWith(vm.rules);
- });
-
it('updates state with the value of the model', () => {
const newContent = 'As Gregor Samsa\n awoke one morning\n';
vm.model.setValue(newContent);
@@ -366,53 +354,48 @@ describe('RepoEditor', () => {
describe('editor updateDimensions', () => {
let updateDimensionsSpy;
- let updateDiffViewSpy;
beforeEach(async () => {
await createComponent();
- updateDimensionsSpy = jest.spyOn(vm.editor, 'updateDimensions');
- updateDiffViewSpy = jest.spyOn(vm.editor, 'updateDiffView').mockImplementation();
+ const ext = extensionsStore.get('EditorWebIde');
+ updateDimensionsSpy = jest.fn();
+ spyOnApi(ext, {
+ updateDimensions: updateDimensionsSpy,
+ });
});
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
vm.$store.state.panelResizing = true;
await vm.$nextTick();
expect(updateDimensionsSpy).not.toHaveBeenCalled();
- expect(updateDiffViewSpy).not.toHaveBeenCalled();
vm.$store.state.panelResizing = false;
await vm.$nextTick();
expect(updateDimensionsSpy).toHaveBeenCalledTimes(1);
- expect(updateDiffViewSpy).toHaveBeenCalledTimes(1);
vm.$store.state.panelResizing = true;
await vm.$nextTick();
expect(updateDimensionsSpy).toHaveBeenCalledTimes(1);
- expect(updateDiffViewSpy).toHaveBeenCalledTimes(1);
});
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
vm.$store.state.rightPane.isOpen = true;
await vm.$nextTick();
expect(updateDimensionsSpy).toHaveBeenCalledTimes(1);
- expect(updateDiffViewSpy).toHaveBeenCalledTimes(1);
vm.$store.state.rightPane.isOpen = false;
await vm.$nextTick();
expect(updateDimensionsSpy).toHaveBeenCalledTimes(2);
- expect(updateDiffViewSpy).toHaveBeenCalledTimes(2);
});
});
@@ -447,7 +430,11 @@ describe('RepoEditor', () => {
activeFile: dummyFile.markdown,
});
- updateDimensionsSpy = jest.spyOn(vm.editor, 'updateDimensions');
+ const ext = extensionsStore.get('EditorWebIde');
+ updateDimensionsSpy = jest.fn();
+ spyOnApi(ext, {
+ updateDimensions: updateDimensionsSpy,
+ });
changeViewMode(FILE_VIEW_MODE_PREVIEW);
await vm.$nextTick();
diff --git a/spec/frontend/ide/ide_router_spec.js b/spec/frontend/ide/ide_router_spec.js
index 3fb7781b176..cd10812f8ea 100644
--- a/spec/frontend/ide/ide_router_spec.js
+++ b/spec/frontend/ide/ide_router_spec.js
@@ -6,6 +6,7 @@ describe('IDE router', () => {
const PROJECT_NAMESPACE = 'my-group/sub-group';
const PROJECT_NAME = 'my-project';
const TEST_PATH = `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/merge_requests/2`;
+ const DEFAULT_BRANCH = 'default-main';
let store;
let router;
@@ -13,34 +14,46 @@ describe('IDE router', () => {
beforeEach(() => {
window.history.replaceState({}, '', '/');
store = createStore();
- router = createRouter(store);
+ router = createRouter(store, DEFAULT_BRANCH);
jest.spyOn(store, 'dispatch').mockReturnValue(new Promise(() => {}));
});
- [
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/blob/`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/tree/`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/weird:branch/name-123/-/src/tree/`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/edit`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/merge_requests/2`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/blob/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit/blob/-/src/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/merge_requests/2`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit`,
- `/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}`,
- ].forEach((route) => {
- it(`finds project path when route is "${route}"`, () => {
- router.push(route);
-
- expect(store.dispatch).toHaveBeenCalledWith('getProjectData', {
- namespace: PROJECT_NAMESPACE,
- projectId: PROJECT_NAME,
- });
+ it.each`
+ route | expectedBranchId | expectedBasePath
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/blob/`} | ${'main'} | ${'src/blob/'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/blob`} | ${'main'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob/-/src/blob`} | ${'blob'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/main/-/src/tree/`} | ${'main'} | ${'src/tree/'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/weird:branch/name-123/-/src/tree/`} | ${'weird:branch/name-123'} | ${'src/tree/'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/blob`} | ${'main'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/edit`} | ${'main'} | ${'src/edit'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/main/-/src/merge_requests/2`} | ${'main'} | ${'src/merge_requests/2'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/blob/blob/-/src/blob`} | ${'blob'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit/blob/-/src/blob`} | ${'blob'} | ${'src/blob'}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/tree/blob`} | ${'blob'} | ${''}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/edit`} | ${DEFAULT_BRANCH} | ${''}
+ ${`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}`} | ${DEFAULT_BRANCH} | ${''}
+ `('correctly opens Web IDE for $route', ({ route, expectedBranchId, expectedBasePath } = {}) => {
+ router.push(route);
+
+ expect(store.dispatch).toHaveBeenCalledWith('openBranch', {
+ projectId: `${PROJECT_NAMESPACE}/${PROJECT_NAME}`,
+ branchId: expectedBranchId,
+ basePath: expectedBasePath,
+ });
+ });
+
+ it('correctly opens an MR', () => {
+ const expectedId = '2';
+
+ router.push(`/project/${PROJECT_NAMESPACE}/${PROJECT_NAME}/merge_requests/${expectedId}`);
+
+ expect(store.dispatch).toHaveBeenCalledWith('openMergeRequest', {
+ projectId: `${PROJECT_NAMESPACE}/${PROJECT_NAME}`,
+ mergeRequestId: expectedId,
+ targetProjectId: undefined,
});
+ expect(store.dispatch).not.toHaveBeenCalledWith('openBranch');
});
it('keeps router in sync when store changes', async () => {
diff --git a/spec/frontend/ide/services/index_spec.js b/spec/frontend/ide/services/index_spec.js
index eacf1244d55..0fab828dfb3 100644
--- a/spec/frontend/ide/services/index_spec.js
+++ b/spec/frontend/ide/services/index_spec.js
@@ -6,7 +6,7 @@ import dismissUserCallout from '~/graphql_shared/mutations/dismiss_user_callout.
import services from '~/ide/services';
import { query, mutate } from '~/ide/services/gql';
import { escapeFileUrl } from '~/lib/utils/url_utility';
-import ciConfig from '~/pipeline_editor/graphql/queries/ci_config.graphql';
+import ciConfig from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
import { projectData } from '../mock_data';
jest.mock('~/api');
@@ -216,29 +216,6 @@ describe('IDE services', () => {
);
});
- describe('getProjectData', () => {
- it('combines gql and API requests', () => {
- const gqlProjectData = {
- userPermissions: {
- bogus: true,
- },
- };
- Api.project.mockReturnValue(Promise.resolve({ data: { ...projectData } }));
- query.mockReturnValue(Promise.resolve({ data: { project: gqlProjectData } }));
-
- return services.getProjectData(TEST_NAMESPACE, TEST_PROJECT).then((response) => {
- expect(response).toEqual({ data: { ...projectData, ...gqlProjectData } });
- expect(Api.project).toHaveBeenCalledWith(TEST_PROJECT_ID);
- expect(query).toHaveBeenCalledWith({
- query: getIdeProject,
- variables: {
- projectPath: TEST_PROJECT_ID,
- },
- });
- });
- });
- });
-
describe('getFiles', () => {
let mock;
let relativeUrlRoot;
@@ -330,4 +307,38 @@ describe('IDE services', () => {
});
});
});
+
+ describe('getProjectPermissionsData', () => {
+ const TEST_PROJECT_PATH = 'foo/bar';
+
+ it('queries for the project permissions', () => {
+ const result = { data: { project: projectData } };
+ query.mockResolvedValue(result);
+
+ return services.getProjectPermissionsData(TEST_PROJECT_PATH).then((data) => {
+ expect(data).toEqual(result.data.project);
+ expect(query).toHaveBeenCalledWith(
+ expect.objectContaining({
+ query: getIdeProject,
+ variables: { projectPath: TEST_PROJECT_PATH },
+ }),
+ );
+ });
+ });
+
+ it('converts the returned GraphQL id to the regular ID number', () => {
+ const projectId = 2;
+ const gqlProjectData = {
+ id: `gid://gitlab/Project/${projectId}`,
+ userPermissions: {
+ bogus: true,
+ },
+ };
+
+ query.mockResolvedValue({ data: { project: gqlProjectData } });
+ return services.getProjectPermissionsData(TEST_PROJECT_PATH).then((data) => {
+ expect(data.id).toBe(projectId);
+ });
+ });
+ });
});
diff --git a/spec/frontend/ide/stores/actions/project_spec.js b/spec/frontend/ide/stores/actions/project_spec.js
index ca6f7169059..e07dcf22860 100644
--- a/spec/frontend/ide/stores/actions/project_spec.js
+++ b/spec/frontend/ide/stores/actions/project_spec.js
@@ -2,9 +2,12 @@ import MockAdapter from 'axios-mock-adapter';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import testAction from 'helpers/vuex_action_helper';
import api from '~/api';
+import createFlash from '~/flash';
import service from '~/ide/services';
import { createStore } from '~/ide/stores';
import {
+ setProject,
+ fetchProjectPermissions,
refreshLastCommitData,
showBranchNotFoundError,
createNewBranchFromDefault,
@@ -13,8 +16,12 @@ import {
loadFile,
loadBranch,
} from '~/ide/stores/actions';
+import { logError } from '~/lib/logger';
import axios from '~/lib/utils/axios_utils';
+jest.mock('~/flash');
+jest.mock('~/lib/logger');
+
const TEST_PROJECT_ID = 'abc/def';
describe('IDE store project actions', () => {
@@ -34,6 +41,92 @@ describe('IDE store project actions', () => {
mock.restore();
});
+ describe('setProject', () => {
+ const project = { id: 'foo', path_with_namespace: TEST_PROJECT_ID };
+ const baseMutations = [
+ {
+ type: 'SET_PROJECT',
+ payload: {
+ projectPath: TEST_PROJECT_ID,
+ project,
+ },
+ },
+ {
+ type: 'SET_CURRENT_PROJECT',
+ payload: TEST_PROJECT_ID,
+ },
+ ];
+
+ it.each`
+ desc | payload | expectedMutations
+ ${'does not commit any action if project is not passed'} | ${undefined} | ${[]}
+ ${'commits correct actions in the correct order by default'} | ${{ project }} | ${[...baseMutations]}
+ `('$desc', async ({ payload, expectedMutations } = {}) => {
+ await testAction({
+ action: setProject,
+ payload,
+ state: store.state,
+ expectedMutations,
+ expectedActions: [],
+ });
+ });
+ });
+
+ describe('fetchProjectPermissions', () => {
+ const permissionsData = {
+ userPermissions: {
+ bogus: true,
+ },
+ };
+ const permissionsMutations = [
+ {
+ type: 'UPDATE_PROJECT',
+ payload: {
+ projectPath: TEST_PROJECT_ID,
+ props: {
+ ...permissionsData,
+ },
+ },
+ },
+ ];
+
+ let spy;
+
+ beforeEach(() => {
+ spy = jest.spyOn(service, 'getProjectPermissionsData');
+ });
+
+ afterEach(() => {
+ createFlash.mockRestore();
+ });
+
+ it.each`
+ desc | projectPath | responseSuccess | expectedMutations
+ ${'does not fetch permissions if project does not exist'} | ${undefined} | ${true} | ${[]}
+ ${'fetches permission when project is specified'} | ${TEST_PROJECT_ID} | ${true} | ${[...permissionsMutations]}
+ ${'flashes an error if the request fails'} | ${TEST_PROJECT_ID} | ${false} | ${[]}
+ `('$desc', async ({ projectPath, expectedMutations, responseSuccess } = {}) => {
+ store.state.currentProjectId = projectPath;
+ if (responseSuccess) {
+ spy.mockResolvedValue(permissionsData);
+ } else {
+ spy.mockRejectedValue();
+ }
+
+ await testAction({
+ action: fetchProjectPermissions,
+ state: store.state,
+ expectedMutations,
+ expectedActions: [],
+ });
+
+ if (!responseSuccess) {
+ expect(logError).toHaveBeenCalled();
+ expect(createFlash).toHaveBeenCalled();
+ }
+ });
+ });
+
describe('refreshLastCommitData', () => {
beforeEach(() => {
store.state.currentProjectId = 'abc/def';
diff --git a/spec/frontend/ide/stores/mutations/project_spec.js b/spec/frontend/ide/stores/mutations/project_spec.js
index b3ce39c33d2..0fdd7798f00 100644
--- a/spec/frontend/ide/stores/mutations/project_spec.js
+++ b/spec/frontend/ide/stores/mutations/project_spec.js
@@ -3,21 +3,48 @@ import state from '~/ide/stores/state';
describe('Multi-file store branch mutations', () => {
let localState;
+ const nonExistentProj = 'nonexistent';
+ const existingProj = 'abcproject';
beforeEach(() => {
localState = state();
- localState.projects = { abcproject: { empty_repo: true } };
+ localState.projects = { [existingProj]: { empty_repo: true } };
});
describe('TOGGLE_EMPTY_STATE', () => {
it('sets empty_repo for project to passed value', () => {
- mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: 'abcproject', value: false });
+ mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: existingProj, value: false });
- expect(localState.projects.abcproject.empty_repo).toBe(false);
+ expect(localState.projects[existingProj].empty_repo).toBe(false);
- mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: 'abcproject', value: true });
+ mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: existingProj, value: true });
- expect(localState.projects.abcproject.empty_repo).toBe(true);
+ expect(localState.projects[existingProj].empty_repo).toBe(true);
+ });
+ });
+
+ describe('UPDATE_PROJECT', () => {
+ it.each`
+ desc | projectPath | props | expectedProps
+ ${'extends existing project with the passed props'} | ${existingProj} | ${{ foo1: 'bar' }} | ${{ foo1: 'bar' }}
+ ${'overrides existing props on the exsiting project'} | ${existingProj} | ${{ empty_repo: false }} | ${{ empty_repo: false }}
+ ${'does nothing if the project does not exist'} | ${nonExistentProj} | ${{ foo2: 'bar' }} | ${undefined}
+ ${'does nothing if project is not passed'} | ${undefined} | ${{ foo3: 'bar' }} | ${undefined}
+ ${'does nothing if the props are not passed'} | ${existingProj} | ${undefined} | ${{}}
+ ${'does nothing if the props are empty'} | ${existingProj} | ${{}} | ${{}}
+ `('$desc', ({ projectPath, props, expectedProps } = {}) => {
+ const origProject = localState.projects[projectPath];
+
+ mutations.UPDATE_PROJECT(localState, { projectPath, props });
+
+ if (!expectedProps) {
+ expect(localState.projects[projectPath]).toBeUndefined();
+ } else {
+ expect(localState.projects[projectPath]).toEqual({
+ ...origProject,
+ ...expectedProps,
+ });
+ }
});
});
});