diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 13:37:47 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-20 13:37:47 +0000 |
commit | aee0a117a889461ce8ced6fcf73207fe017f1d99 (patch) | |
tree | 891d9ef189227a8445d83f35c1b0fc99573f4380 /spec/frontend/ide | |
parent | 8d46af3258650d305f53b819eabf7ab18d22f59e (diff) | |
download | gitlab-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.js | 13 | ||||
-rw-r--r-- | spec/frontend/ide/components/pipelines/__snapshots__/list_spec.js.snap | 10 | ||||
-rw-r--r-- | spec/frontend/ide/components/pipelines/empty_state_spec.js | 44 | ||||
-rw-r--r-- | spec/frontend/ide/components/pipelines/list_spec.js | 8 | ||||
-rw-r--r-- | spec/frontend/ide/components/repo_editor_spec.js | 85 | ||||
-rw-r--r-- | spec/frontend/ide/ide_router_spec.js | 61 | ||||
-rw-r--r-- | spec/frontend/ide/services/index_spec.js | 59 | ||||
-rw-r--r-- | spec/frontend/ide/stores/actions/project_spec.js | 93 | ||||
-rw-r--r-- | spec/frontend/ide/stores/mutations/project_spec.js | 37 |
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, + }); + } }); }); }); |