Commit 5d6cb037 authored by Paul Slaughter's avatar Paul Slaughter

Update form_spec to use shallow mount VTU

- This makes testing for disabled/tooltip on
buttons a lot easier
- We were also able to remove a number of
redundant specs
parent c0e171f0
<script> <script>
import { mapState, mapActions, mapGetters } from 'vuex'; import { mapState, mapActions, mapGetters } from 'vuex';
import { GlModal, GlSafeHtmlDirective, GlButton } from '@gitlab/ui'; import { GlModal, GlSafeHtmlDirective, GlButton } from '@gitlab/ui';
import { n__, __ } from '~/locale'; import { n__ } from '~/locale';
import CommitMessageField from './message_field.vue'; import CommitMessageField from './message_field.vue';
import Actions from './actions.vue'; import Actions from './actions.vue';
import SuccessMessage from './success_message.vue'; import SuccessMessage from './success_message.vue';
...@@ -35,10 +35,6 @@ export default { ...@@ -35,10 +35,6 @@ export default {
overviewText() { overviewText() {
return n__('%d changed file', '%d changed files', this.stagedFiles.length); return n__('%d changed file', '%d changed files', this.stagedFiles.length);
}, },
commitButtonText() {
return this.stagedFiles.length ? __('Commit') : __('Stage & Commit');
},
currentViewIsCommitView() { currentViewIsCommitView() {
return this.currentActivityView === leftSidebarViews.commit.name; return this.currentActivityView === leftSidebarViews.commit.name;
}, },
...@@ -160,13 +156,19 @@ export default { ...@@ -160,13 +156,19 @@ export default {
<gl-button <gl-button
:loading="submitCommitLoading" :loading="submitCommitLoading"
class="float-left qa-commit-button" class="float-left qa-commit-button"
data-testid="commit-button"
category="primary" category="primary"
variant="success" variant="success"
@click="commit" @click="commit"
> >
{{ __('Commit') }} {{ __('Commit') }}
</gl-button> </gl-button>
<gl-button v-if="!discardDraftButtonDisabled" class="float-right" @click="discardDraft"> <gl-button
v-if="!discardDraftButtonDisabled"
class="float-right"
data-testid="discard-draft"
@click="discardDraft"
>
{{ __('Discard draft') }} {{ __('Discard draft') }}
</gl-button> </gl-button>
<gl-button <gl-button
......
...@@ -26818,9 +26818,6 @@ msgstr "" ...@@ -26818,9 +26818,6 @@ msgstr ""
msgid "Stage" msgid "Stage"
msgstr "" msgstr ""
msgid "Stage & Commit"
msgstr ""
msgid "Stage data updated" msgid "Stage data updated"
msgstr "" msgstr ""
......
import Vue from 'vue'; import Vue from 'vue';
import { getByText } from '@testing-library/dom'; import { shallowMount } from '@vue/test-utils';
import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; import { GlModal } from '@gitlab/ui';
import { projectData } from 'jest/ide/mock_data'; import { projectData } from 'jest/ide/mock_data';
import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { createStore } from '~/ide/stores'; import { createStore } from '~/ide/stores';
import consts from '~/ide/stores/modules/commit/constants'; import { COMMIT_TO_NEW_BRANCH } from '~/ide/stores/modules/commit/constants';
import CommitForm from '~/ide/components/commit_sidebar/form.vue'; 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 { leftSidebarViews } from '~/ide/constants';
import { import {
createCodeownersCommitError, createCodeownersCommitError,
...@@ -15,256 +17,245 @@ import { ...@@ -15,256 +17,245 @@ import {
} from '~/ide/lib/errors'; } from '~/ide/lib/errors';
describe('IDE commit form', () => { describe('IDE commit form', () => {
const Component = Vue.extend(CommitForm); let wrapper;
let vm;
let store; let store;
const beginCommitButton = () => vm.$el.querySelector('[data-testid="begin-commit-button"]'); const createComponent = () => {
wrapper = shallowMount(CommitForm, {
store,
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 findCommitButton = () => wrapper.find('[data-testid="commit-button"]');
const findForm = () => wrapper.find('form');
const findCommitMessageInput = () => wrapper.find(CommitMessageField);
const setCommitMessageInput = (val) => findCommitMessageInput().vm.$emit('input', val);
const findDiscardDraftButton = () => wrapper.find('[data-testid="discard-draft"]');
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore();
store.state.changedFiles.push('test'); store.state.stagedFiles.push('test');
store.state.currentProjectId = 'abcproject'; store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'master'; store.state.currentBranchId = 'master';
Vue.set(store.state.projects, 'abcproject', { ...projectData }); Vue.set(store.state.projects, 'abcproject', {
...projectData,
vm = createComponentWithStore(Component, store).$mount(); });
}); });
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
wrapper = null;
}); });
it('enables begin commit button when there are changes', () => { describe.each`
expect(beginCommitButton()).not.toHaveAttr('disabled'); desc | stagedFiles | disabled
}); ${'when there are changes'} | ${['test']} | ${false}
${'when there are no changes'} | ${[]} | ${true}
`('$desc', ({ stagedFiles, disabled }) => {
beforeEach(async () => {
store.state.stagedFiles = stagedFiles;
it('disables begin commit button when there are no changes', async () => { createComponent();
store.state.changedFiles = []; });
await vm.$nextTick();
expect(beginCommitButton()).toHaveAttr('disabled'); it(`begin button disabled=${disabled}`, async () => {
expect(findBeginCommitButton().props('disabled')).toBe(disabled);
});
}); });
describe('compact', () => { describe('on edit tab', () => {
beforeEach(() => { beforeEach(async () => {
vm.isCompact = true; // Test that we react to switching to compact view.
goToCommitView();
createComponent();
goToEditView();
return vm.$nextTick(); await wrapper.vm.$nextTick();
}); });
it('renders commit button in compact mode', () => { it('renders commit button in compact mode', () => {
expect(beginCommitButton()).not.toBeNull(); expect(findBeginCommitButton().exists()).toBe(true);
expect(beginCommitButton().textContent).toContain('Commit'); expect(findBeginCommitButton().text()).toBe('Commit…');
}); });
it('does not render form', () => { it('does not render form', () => {
expect(vm.$el.querySelector('form')).toBeNull(); expect(findForm().exists()).toBe(false);
}); });
it('renders overview text', () => { it('renders overview text', () => {
vm.$store.state.stagedFiles.push('test'); expect(wrapper.find('p').text()).toBe('1 changed file');
return vm.$nextTick(() => {
expect(vm.$el.querySelector('p').textContent).toContain('1 changed file');
});
}); });
it('shows form when clicking commit button', () => { it('when begin commit button is clicked, shows form', async () => {
beginCommitButton().click(); findBeginCommitButton().vm.$emit('click');
return vm.$nextTick(() => { await wrapper.vm.$nextTick();
expect(vm.$el.querySelector('form')).not.toBeNull();
});
});
it('toggles activity bar view when clicking commit button', () => {
beginCommitButton().click();
return vm.$nextTick(() => { expect(findForm().exists()).toBe(true);
expect(store.state.currentActivityView).toBe(leftSidebarViews.commit.name);
});
}); });
it('collapses if lastCommitMsg is set to empty and current view is not commit view', async () => { it('when begin commit button is clicked, sets activity view', async () => {
store.state.lastCommitMsg = 'abc'; findBeginCommitButton().vm.$emit('click');
store.state.currentActivityView = leftSidebarViews.edit.name;
await vm.$nextTick();
// if commit message is set, form is uncollapsed
expect(vm.isCompact).toBe(false);
store.state.lastCommitMsg = ''; await wrapper.vm.$nextTick();
await vm.$nextTick();
// collapsed when set to empty expect(store.state.currentActivityView).toBe(leftSidebarViews.commit.name);
expect(vm.isCompact).toBe(true);
}); });
it('collapses if in commit view but there are no changes and vice versa', async () => { it('collapses if lastCommitMsg is set to empty and current view is not commit view', async () => {
store.state.currentActivityView = leftSidebarViews.commit.name; // Test that it expands when lastCommitMsg is set
await vm.$nextTick(); setLastCommitMessage('test');
goToEditView();
// expanded by default if there are changes await wrapper.vm.$nextTick();
expect(vm.isCompact).toBe(false);
store.state.changedFiles = []; expect(findForm().exists()).toBe(true);
await vm.$nextTick();
expect(vm.isCompact).toBe(true); // Now test that it collapses when lastCommitMsg is cleared
setLastCommitMessage('');
store.state.changedFiles.push('test'); await wrapper.vm.$nextTick();
await vm.$nextTick();
// uncollapsed once again expect(findForm().exists()).toBe(false);
expect(vm.isCompact).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);
store.state.currentActivityView = leftSidebarViews.commit.name;
await vm.$nextTick();
expect(vm.isCompact).toBe(false);
store.state.currentActivityView = leftSidebarViews.edit.name;
await vm.$nextTick();
expect(vm.isCompact).toBe(true);
}); });
describe('when window height is less than MAX_WINDOW_HEIGHT', () => { describe('on commit tab when window height is less than MAX_WINDOW_HEIGHT', () => {
let oldHeight; let oldHeight;
beforeEach(() => { beforeEach(async () => {
oldHeight = window.innerHeight; oldHeight = window.innerHeight;
window.innerHeight = 700; window.innerHeight = 700;
createComponent();
goToCommitView();
await wrapper.vm.$nextTick();
}); });
afterEach(() => { afterEach(() => {
window.innerHeight = oldHeight; window.innerHeight = oldHeight;
}); });
it('stays collapsed when switching from edit view to commit view and back', async () => { it('stays collapsed if changes are added or removed', async () => {
store.state.currentActivityView = leftSidebarViews.edit.name; expect(findForm().exists()).toBe(false);
await vm.$nextTick();
expect(vm.isCompact).toBe(true);
store.state.currentActivityView = leftSidebarViews.commit.name; store.state.stagedFiles = [];
await vm.$nextTick(); await wrapper.vm.$nextTick();
expect(vm.isCompact).toBe(true); expect(findForm().exists()).toBe(false);
store.state.currentActivityView = leftSidebarViews.edit.name; store.state.stagedFiles.push('test');
await vm.$nextTick(); await wrapper.vm.$nextTick();
expect(vm.isCompact).toBe(true); expect(findForm().exists()).toBe(false);
});
}); });
it('stays uncollapsed if changes are added or removed', async () => { describe('on commit tab', () => {
store.state.currentActivityView = leftSidebarViews.commit.name; beforeEach(async () => {
await vm.$nextTick(); // Test that the component reacts to switching to full view
goToEditView();
expect(vm.isCompact).toBe(true);
store.state.changedFiles = [];
await vm.$nextTick();
expect(vm.isCompact).toBe(true); createComponent();
store.state.changedFiles.push('test'); goToCommitView();
await vm.$nextTick();
expect(vm.isCompact).toBe(true); await wrapper.vm.$nextTick();
}); });
it('uncollapses when clicked on Commit button in the edit view', async () => { it('shows form', () => {
store.state.currentActivityView = leftSidebarViews.edit.name; expect(findForm().exists()).toBe(true);
beginCommitButton().click();
await waitForPromises();
expect(vm.isCompact).toBe(false);
});
});
}); });
describe('full', () => { it('hides begin commit button', () => {
beforeEach(() => { expect(findBeginCommitButton().exists()).toBe(false);
vm.isCompact = false;
return vm.$nextTick();
}); });
it('updates commitMessage in store on input', () => { describe('when no changed files', () => {
const textarea = vm.$el.querySelector('textarea'); beforeEach(async () => {
store.state.stagedFiles = [];
textarea.value = 'testing commit message'; await wrapper.vm.$nextTick();
textarea.dispatchEvent(new Event('input'));
return vm.$nextTick().then(() => {
expect(vm.$store.state.commit.commitMessage).toBe('testing commit message');
}); });
it('hides form', () => {
expect(findForm().exists()).toBe(false);
}); });
it('updating currentActivityView not to commit view sets compact mode', () => { it('expands again when staged files are added', async () => {
store.state.currentActivityView = 'a'; store.state.stagedFiles.push('test');
await wrapper.vm.$nextTick();
return vm.$nextTick(() => { expect(findForm().exists()).toBe(true);
expect(vm.isCompact).toBe(true);
}); });
}); });
it('always opens itself in full view current activity view is not commit view when clicking commit button', () => { it('updates commitMessage in store on input', async () => {
beginCommitButton().click(); setCommitMessageInput('testing commit message');
return vm.$nextTick(() => { await wrapper.vm.$nextTick();
expect(store.state.currentActivityView).toBe(leftSidebarViews.commit.name);
expect(vm.isCompact).toBe(false); expect(store.state.commit.commitMessage).toBe('testing commit message');
});
}); });
describe('discard draft button', () => { describe('discard draft button', () => {
it('hidden when commitMessage is empty', () => { it('hidden when commitMessage is empty', () => {
expect(vm.$el.querySelector('.btn-default').textContent).toContain('Collapse'); expect(findDiscardDraftButton().exists()).toBe(false);
}); });
it('resets commitMessage when clicking discard button', () => { it('resets commitMessage when clicking discard button', async () => {
vm.$store.state.commit.commitMessage = 'testing commit message'; setCommitMessageInput('testing commit message');
return vm await wrapper.vm.$nextTick();
.$nextTick()
.then(() => { expect(findCommitMessageInput().props('text')).toBe('testing commit message');
vm.$el.querySelector('.btn-default').click();
}) // Test that commitMessage is cleared on click
.then(() => vm.$nextTick()) findDiscardDraftButton().vm.$emit('click');
.then(() => {
expect(vm.$store.state.commit.commitMessage).not.toBe('testing commit message'); await wrapper.vm.$nextTick();
});
expect(findCommitMessageInput().props('text')).toBe('');
}); });
}); });
describe('when submitting', () => { describe('when submitting', () => {
beforeEach(() => { beforeEach(async () => {
jest.spyOn(vm, 'commitChanges'); goToEditView();
vm.$store.state.stagedFiles.push('test'); createComponent();
vm.$store.state.commit.commitMessage = 'testing commit message';
});
it('calls commitChanges', () => { goToCommitView();
vm.commitChanges.mockResolvedValue({ success: true });
await wrapper.vm.$nextTick();
return vm.$nextTick().then(() => { setCommitMessageInput('testing commit message');
vm.$el.querySelector('.btn-success').click();
expect(vm.commitChanges).toHaveBeenCalled(); await wrapper.vm.$nextTick();
jest.spyOn(store, 'dispatch').mockResolvedValue();
}); });
it('calls commitChanges', () => {
findCommitButton().vm.$emit('click');
expect(store.dispatch).toHaveBeenCalledWith('commit/commitChanges', undefined);
}); });
it.each` it.each`
...@@ -272,31 +263,32 @@ describe('IDE commit form', () => { ...@@ -272,31 +263,32 @@ describe('IDE commit form', () => {
${() => createCodeownersCommitError('test message')} | ${{ actionPrimary: { text: 'Create new branch' } }} ${() => createCodeownersCommitError('test message')} | ${{ actionPrimary: { text: 'Create new branch' } }}
${createUnexpectedCommitError} | ${{ actionPrimary: null }} ${createUnexpectedCommitError} | ${{ actionPrimary: null }}
`('opens error modal if commitError with $error', async ({ createError, props }) => { `('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(); const error = createError();
store.state.commit.commitError = error; store.state.commit.commitError = error;
await vm.$nextTick(); await wrapper.vm.$nextTick();
expect(vm.$refs.commitErrorModal.show).toHaveBeenCalled(); expect(modal.vm.show).toHaveBeenCalled();
expect(vm.$refs.commitErrorModal).toMatchObject({ expect(modal.props()).toMatchObject({
actionCancel: { text: 'Cancel' }, actionCancel: { text: 'Cancel' },
...props, ...props,
}); });
// Because of the legacy 'mountComponent' approach here, the only way to // 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. // 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', () => { describe('with error modal with primary', () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(vm.$store, 'dispatch').mockReturnValue(Promise.resolve()); jest.spyOn(store, 'dispatch').mockResolvedValue();
}); });
const commitActions = [ const commitActions = [
['commit/updateCommitAction', consts.COMMIT_TO_NEW_BRANCH], ['commit/updateCommitAction', COMMIT_TO_NEW_BRANCH],
['commit/commitChanges'], ['commit/commitChanges'],
]; ];
...@@ -310,27 +302,15 @@ describe('IDE commit form', () => { ...@@ -310,27 +302,15 @@ describe('IDE commit form', () => {
async ({ commitError, expectedActions }) => { async ({ commitError, expectedActions }) => {
store.state.commit.commitError = commitError('test message'); 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(); 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');
});
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment