summaryrefslogtreecommitdiff
path: root/spec/frontend/ide/components/commit_sidebar/form_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ide/components/commit_sidebar/form_spec.js')
-rw-r--r--spec/frontend/ide/components/commit_sidebar/form_spec.js395
1 files changed, 207 insertions, 188 deletions
diff --git a/spec/frontend/ide/components/commit_sidebar/form_spec.js b/spec/frontend/ide/components/commit_sidebar/form_spec.js
index abd7e3bb8fc..2b567816ce8 100644
--- a/spec/frontend/ide/components/commit_sidebar/form_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/form_spec.js
@@ -1,11 +1,12 @@
+import { GlModal } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
-import { getByText } from '@testing-library/dom';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import { projectData } from 'jest/ide/mock_data';
+import { stubComponent } from 'helpers/stub_component';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import waitForPromises from 'helpers/wait_for_promises';
-import { createStore } from '~/ide/stores';
-import consts from '~/ide/stores/modules/commit/constants';
+import { projectData } from 'jest/ide/mock_data';
import CommitForm from '~/ide/components/commit_sidebar/form.vue';
+import CommitMessageField from '~/ide/components/commit_sidebar/message_field.vue';
import { leftSidebarViews } from '~/ide/constants';
import {
createCodeownersCommitError,
@@ -13,258 +14,287 @@ import {
createBranchChangedCommitError,
branchAlreadyExistsCommitError,
} from '~/ide/lib/errors';
+import { createStore } from '~/ide/stores';
+import { COMMIT_TO_NEW_BRANCH } from '~/ide/stores/modules/commit/constants';
describe('IDE commit form', () => {
- const Component = Vue.extend(CommitForm);
- let vm;
+ let wrapper;
let store;
- const beginCommitButton = () => vm.$el.querySelector('[data-testid="begin-commit-button"]');
+ const createComponent = () => {
+ wrapper = shallowMount(CommitForm, {
+ store,
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ stubs: {
+ GlModal: stubComponent(GlModal),
+ },
+ });
+ };
+
+ const setLastCommitMessage = (msg) => {
+ store.state.lastCommitMsg = msg;
+ };
+ const goToCommitView = () => {
+ store.state.currentActivityView = leftSidebarViews.commit.name;
+ };
+ const goToEditView = () => {
+ store.state.currentActivityView = leftSidebarViews.edit.name;
+ };
+ const findBeginCommitButton = () => wrapper.find('[data-testid="begin-commit-button"]');
+ const findBeginCommitButtonTooltip = () =>
+ wrapper.find('[data-testid="begin-commit-button-tooltip"]');
+ const findBeginCommitButtonData = () => ({
+ disabled: findBeginCommitButton().props('disabled'),
+ tooltip: getBinding(findBeginCommitButtonTooltip().element, 'gl-tooltip').value.title,
+ });
+ const findCommitButton = () => wrapper.find('[data-testid="commit-button"]');
+ const findCommitButtonTooltip = () => wrapper.find('[data-testid="commit-button-tooltip"]');
+ const findCommitButtonData = () => ({
+ disabled: findCommitButton().props('disabled'),
+ tooltip: getBinding(findCommitButtonTooltip().element, 'gl-tooltip').value.title,
+ });
+ const clickCommitButton = () => findCommitButton().vm.$emit('click');
+ const findForm = () => wrapper.find('form');
+ const submitForm = () => findForm().trigger('submit');
+ const findCommitMessageInput = () => wrapper.find(CommitMessageField);
+ const setCommitMessageInput = (val) => findCommitMessageInput().vm.$emit('input', val);
+ const findDiscardDraftButton = () => wrapper.find('[data-testid="discard-draft"]');
beforeEach(() => {
store = createStore();
- store.state.changedFiles.push('test');
+ store.state.stagedFiles.push('test');
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'master';
- Vue.set(store.state.projects, 'abcproject', { ...projectData });
-
- vm = createComponentWithStore(Component, store).$mount();
+ Vue.set(store.state.projects, 'abcproject', {
+ ...projectData,
+ userPermissions: { pushCode: true },
+ });
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
- it('enables begin commit button when there are changes', () => {
- expect(beginCommitButton()).not.toHaveAttr('disabled');
- });
+ // Notes:
+ // - When there are no changes, there is no commit button so there's nothing to test :)
+ describe.each`
+ desc | stagedFiles | userPermissions | viewFn | buttonFn | disabled | tooltip
+ ${'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}
+ `('$desc', ({ stagedFiles, userPermissions, viewFn, buttonFn, disabled, tooltip }) => {
+ beforeEach(async () => {
+ store.state.stagedFiles = stagedFiles;
+ store.state.projects.abcproject.userPermissions = userPermissions;
+
+ createComponent();
+ });
- it('disables begin commit button when there are no changes', async () => {
- store.state.changedFiles = [];
- await vm.$nextTick();
+ it(`at view=${viewFn.name}, ${buttonFn.name} has disabled=${disabled} tooltip=${tooltip}`, async () => {
+ viewFn();
- expect(beginCommitButton()).toHaveAttr('disabled');
+ await wrapper.vm.$nextTick();
+
+ expect(buttonFn()).toEqual({
+ disabled,
+ tooltip,
+ });
+ });
});
- describe('compact', () => {
- beforeEach(() => {
- vm.isCompact = true;
+ describe('on edit tab', () => {
+ beforeEach(async () => {
+ // Test that we react to switching to compact view.
+ goToCommitView();
+
+ createComponent();
- return vm.$nextTick();
+ goToEditView();
+
+ await wrapper.vm.$nextTick();
});
it('renders commit button in compact mode', () => {
- expect(beginCommitButton()).not.toBeNull();
- expect(beginCommitButton().textContent).toContain('Commit');
+ expect(findBeginCommitButton().exists()).toBe(true);
+ expect(findBeginCommitButton().text()).toBe('Commit…');
});
it('does not render form', () => {
- expect(vm.$el.querySelector('form')).toBeNull();
+ expect(findForm().exists()).toBe(false);
});
it('renders overview text', () => {
- vm.$store.state.stagedFiles.push('test');
-
- return vm.$nextTick(() => {
- expect(vm.$el.querySelector('p').textContent).toContain('1 changed file');
- });
+ expect(wrapper.find('p').text()).toBe('1 changed file');
});
- it('shows form when clicking commit button', () => {
- beginCommitButton().click();
-
- return vm.$nextTick(() => {
- expect(vm.$el.querySelector('form')).not.toBeNull();
- });
- });
+ it('when begin commit button is clicked, shows form', async () => {
+ findBeginCommitButton().vm.$emit('click');
- it('toggles activity bar view when clicking commit button', () => {
- beginCommitButton().click();
+ await wrapper.vm.$nextTick();
- return vm.$nextTick(() => {
- expect(store.state.currentActivityView).toBe(leftSidebarViews.commit.name);
- });
+ expect(findForm().exists()).toBe(true);
});
- it('collapses if lastCommitMsg is set to empty and current view is not commit view', async () => {
- store.state.lastCommitMsg = 'abc';
- store.state.currentActivityView = leftSidebarViews.edit.name;
- await vm.$nextTick();
-
- // if commit message is set, form is uncollapsed
- expect(vm.isCompact).toBe(false);
+ it('when begin commit button is clicked, sets activity view', async () => {
+ findBeginCommitButton().vm.$emit('click');
- store.state.lastCommitMsg = '';
- await vm.$nextTick();
+ await wrapper.vm.$nextTick();
- // collapsed when set to empty
- expect(vm.isCompact).toBe(true);
+ expect(store.state.currentActivityView).toBe(leftSidebarViews.commit.name);
});
- it('collapses if in commit view but there are no changes and vice versa', async () => {
- store.state.currentActivityView = leftSidebarViews.commit.name;
- await vm.$nextTick();
+ it('collapses if lastCommitMsg is set to empty and current view is not commit view', async () => {
+ // Test that it expands when lastCommitMsg is set
+ setLastCommitMessage('test');
+ goToEditView();
- // expanded by default if there are changes
- expect(vm.isCompact).toBe(false);
+ await wrapper.vm.$nextTick();
- store.state.changedFiles = [];
- await vm.$nextTick();
+ expect(findForm().exists()).toBe(true);
- expect(vm.isCompact).toBe(true);
+ // Now test that it collapses when lastCommitMsg is cleared
+ setLastCommitMessage('');
- store.state.changedFiles.push('test');
- await vm.$nextTick();
+ await wrapper.vm.$nextTick();
- // uncollapsed once again
- expect(vm.isCompact).toBe(false);
+ expect(findForm().exists()).toBe(false);
});
+ });
- it('collapses if switched from commit view to edit view and vice versa', async () => {
- store.state.currentActivityView = leftSidebarViews.edit.name;
- await vm.$nextTick();
-
- expect(vm.isCompact).toBe(true);
+ describe('on commit tab when window height is less than MAX_WINDOW_HEIGHT', () => {
+ let oldHeight;
- store.state.currentActivityView = leftSidebarViews.commit.name;
- await vm.$nextTick();
+ beforeEach(async () => {
+ oldHeight = window.innerHeight;
+ window.innerHeight = 700;
- expect(vm.isCompact).toBe(false);
+ createComponent();
- store.state.currentActivityView = leftSidebarViews.edit.name;
- await vm.$nextTick();
+ goToCommitView();
- expect(vm.isCompact).toBe(true);
+ await wrapper.vm.$nextTick();
});
- describe('when window height is less than MAX_WINDOW_HEIGHT', () => {
- let oldHeight;
-
- beforeEach(() => {
- oldHeight = window.innerHeight;
- window.innerHeight = 700;
- });
+ afterEach(() => {
+ window.innerHeight = oldHeight;
+ });
- afterEach(() => {
- window.innerHeight = oldHeight;
- });
+ it('stays collapsed if changes are added or removed', async () => {
+ expect(findForm().exists()).toBe(false);
- it('stays collapsed when switching from edit view to commit view and back', async () => {
- store.state.currentActivityView = leftSidebarViews.edit.name;
- await vm.$nextTick();
+ store.state.stagedFiles = [];
+ await wrapper.vm.$nextTick();
- expect(vm.isCompact).toBe(true);
+ expect(findForm().exists()).toBe(false);
- store.state.currentActivityView = leftSidebarViews.commit.name;
- await vm.$nextTick();
+ store.state.stagedFiles.push('test');
+ await wrapper.vm.$nextTick();
- expect(vm.isCompact).toBe(true);
+ expect(findForm().exists()).toBe(false);
+ });
+ });
- store.state.currentActivityView = leftSidebarViews.edit.name;
- await vm.$nextTick();
+ describe('on commit tab', () => {
+ beforeEach(async () => {
+ // Test that the component reacts to switching to full view
+ goToEditView();
- expect(vm.isCompact).toBe(true);
- });
+ createComponent();
- it('stays uncollapsed if changes are added or removed', async () => {
- store.state.currentActivityView = leftSidebarViews.commit.name;
- await vm.$nextTick();
+ goToCommitView();
- expect(vm.isCompact).toBe(true);
+ await wrapper.vm.$nextTick();
+ });
- store.state.changedFiles = [];
- await vm.$nextTick();
+ it('shows form', () => {
+ expect(findForm().exists()).toBe(true);
+ });
- expect(vm.isCompact).toBe(true);
+ it('hides begin commit button', () => {
+ expect(findBeginCommitButton().exists()).toBe(false);
+ });
- store.state.changedFiles.push('test');
- await vm.$nextTick();
+ describe('when no changed files', () => {
+ beforeEach(async () => {
+ store.state.stagedFiles = [];
+ await wrapper.vm.$nextTick();
+ });
- expect(vm.isCompact).toBe(true);
+ it('hides form', () => {
+ expect(findForm().exists()).toBe(false);
});
- it('uncollapses when clicked on Commit button in the edit view', async () => {
- store.state.currentActivityView = leftSidebarViews.edit.name;
- beginCommitButton().click();
- await waitForPromises();
+ it('expands again when staged files are added', async () => {
+ store.state.stagedFiles.push('test');
+ await wrapper.vm.$nextTick();
- expect(vm.isCompact).toBe(false);
+ expect(findForm().exists()).toBe(true);
});
});
- });
- describe('full', () => {
- beforeEach(() => {
- vm.isCompact = false;
+ it('updates commitMessage in store on input', async () => {
+ setCommitMessageInput('testing commit message');
- return vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ expect(store.state.commit.commitMessage).toBe('testing commit message');
});
- it('updates commitMessage in store on input', () => {
- const textarea = vm.$el.querySelector('textarea');
+ describe('discard draft button', () => {
+ it('hidden when commitMessage is empty', () => {
+ expect(findDiscardDraftButton().exists()).toBe(false);
+ });
- textarea.value = 'testing commit message';
+ it('resets commitMessage when clicking discard button', async () => {
+ setCommitMessageInput('testing commit message');
- textarea.dispatchEvent(new Event('input'));
+ await wrapper.vm.$nextTick();
- return vm.$nextTick().then(() => {
- expect(vm.$store.state.commit.commitMessage).toBe('testing commit message');
- });
- });
+ expect(findCommitMessageInput().props('text')).toBe('testing commit message');
- it('updating currentActivityView not to commit view sets compact mode', () => {
- store.state.currentActivityView = 'a';
+ // Test that commitMessage is cleared on click
+ findDiscardDraftButton().vm.$emit('click');
- return vm.$nextTick(() => {
- expect(vm.isCompact).toBe(true);
+ await wrapper.vm.$nextTick();
+
+ expect(findCommitMessageInput().props('text')).toBe('');
});
});
- it('always opens itself in full view current activity view is not commit view when clicking commit button', () => {
- beginCommitButton().click();
+ describe('when submitting', () => {
+ beforeEach(async () => {
+ goToEditView();
- return vm.$nextTick(() => {
- expect(store.state.currentActivityView).toBe(leftSidebarViews.commit.name);
- expect(vm.isCompact).toBe(false);
- });
- });
+ createComponent();
- describe('discard draft button', () => {
- it('hidden when commitMessage is empty', () => {
- expect(vm.$el.querySelector('.btn-default').textContent).toContain('Collapse');
- });
+ goToCommitView();
+
+ await wrapper.vm.$nextTick();
- it('resets commitMessage when clicking discard button', () => {
- vm.$store.state.commit.commitMessage = 'testing commit message';
-
- return vm
- .$nextTick()
- .then(() => {
- vm.$el.querySelector('.btn-default').click();
- })
- .then(() => vm.$nextTick())
- .then(() => {
- expect(vm.$store.state.commit.commitMessage).not.toBe('testing commit message');
- });
+ setCommitMessageInput('testing commit message');
+
+ await wrapper.vm.$nextTick();
+
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
});
- });
- describe('when submitting', () => {
- beforeEach(() => {
- jest.spyOn(vm, 'commitChanges');
+ it.each([clickCommitButton, submitForm])('when %p, commits changes', (fn) => {
+ fn();
- vm.$store.state.stagedFiles.push('test');
- vm.$store.state.commit.commitMessage = 'testing commit message';
+ expect(store.dispatch).toHaveBeenCalledWith('commit/commitChanges', undefined);
});
- it('calls commitChanges', () => {
- vm.commitChanges.mockResolvedValue({ success: true });
+ it('when cannot push code, submitting does nothing', async () => {
+ store.state.projects.abcproject.userPermissions.pushCode = false;
+ await wrapper.vm.$nextTick();
- return vm.$nextTick().then(() => {
- vm.$el.querySelector('.btn-success').click();
+ submitForm();
- expect(vm.commitChanges).toHaveBeenCalled();
- });
+ expect(store.dispatch).not.toHaveBeenCalled();
});
it.each`
@@ -272,31 +302,32 @@ describe('IDE commit form', () => {
${() => createCodeownersCommitError('test message')} | ${{ actionPrimary: { text: 'Create new branch' } }}
${createUnexpectedCommitError} | ${{ actionPrimary: null }}
`('opens error modal if commitError with $error', async ({ createError, props }) => {
- jest.spyOn(vm.$refs.commitErrorModal, 'show');
+ const modal = wrapper.find(GlModal);
+ modal.vm.show = jest.fn();
const error = createError();
store.state.commit.commitError = error;
- await vm.$nextTick();
+ await wrapper.vm.$nextTick();
- expect(vm.$refs.commitErrorModal.show).toHaveBeenCalled();
- expect(vm.$refs.commitErrorModal).toMatchObject({
+ expect(modal.vm.show).toHaveBeenCalled();
+ expect(modal.props()).toMatchObject({
actionCancel: { text: 'Cancel' },
...props,
});
// Because of the legacy 'mountComponent' approach here, the only way to
// test the text of the modal is by viewing the content of the modal added to the document.
- expect(document.body).toHaveText(error.messageHTML);
+ expect(modal.html()).toContain(error.messageHTML);
});
});
describe('with error modal with primary', () => {
beforeEach(() => {
- jest.spyOn(vm.$store, 'dispatch').mockReturnValue(Promise.resolve());
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
});
const commitActions = [
- ['commit/updateCommitAction', consts.COMMIT_TO_NEW_BRANCH],
+ ['commit/updateCommitAction', COMMIT_TO_NEW_BRANCH],
['commit/commitChanges'],
];
@@ -310,27 +341,15 @@ describe('IDE commit form', () => {
async ({ commitError, expectedActions }) => {
store.state.commit.commitError = commitError('test message');
- await vm.$nextTick();
+ await wrapper.vm.$nextTick();
- getByText(document.body, 'Create new branch').click();
+ wrapper.find(GlModal).vm.$emit('ok');
await waitForPromises();
- expect(vm.$store.dispatch.mock.calls).toEqual(expectedActions);
+ expect(store.dispatch.mock.calls).toEqual(expectedActions);
},
);
});
});
-
- describe('commitButtonText', () => {
- it('returns commit text when staged files exist', () => {
- vm.$store.state.stagedFiles.push('testing');
-
- expect(vm.commitButtonText).toBe('Commit');
- });
-
- it('returns stage & commit text when staged files do not exist', () => {
- expect(vm.commitButtonText).toBe('Stage & Commit');
- });
- });
});