debian-mirror-gitlab/spec/frontend/notes/components/note_form_spec.js

345 lines
9.3 KiB
JavaScript
Raw Normal View History

2021-12-11 22:18:48 +05:30
import { GlLink } from '@gitlab/ui';
2021-03-08 18:12:59 +05:30
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
2020-06-23 00:09:42 +05:30
import batchComments from '~/batch_comments/stores/modules/batch_comments';
2021-03-11 19:13:27 +05:30
import { getDraft, updateDraft } from '~/lib/utils/autosave';
import NoteForm from '~/notes/components/note_form.vue';
import createStore from '~/notes/stores';
2019-07-07 11:18:12 +05:30
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
2020-06-23 00:09:42 +05:30
import { noteableDataMock, notesDataMock, discussionMock } from '../mock_data';
2018-03-17 18:26:18 +05:30
2020-04-22 19:07:51 +05:30
jest.mock('~/lib/utils/autosave');
2018-03-17 18:26:18 +05:30
describe('issue_note_form component', () => {
2019-07-07 11:18:12 +05:30
const dummyAutosaveKey = 'some-autosave-key';
const dummyDraft = 'dummy draft content';
2018-11-08 19:23:39 +05:30
let store;
2019-07-07 11:18:12 +05:30
let wrapper;
2018-03-17 18:26:18 +05:30
let props;
2019-07-07 11:18:12 +05:30
const createComponentWrapper = () => {
2021-03-08 18:12:59 +05:30
return mount(NoteForm, {
2019-07-07 11:18:12 +05:30
store,
propsData: props,
});
};
2021-03-11 19:13:27 +05:30
const findCancelButton = () => wrapper.find('[data-testid="cancel"]');
2018-03-17 18:26:18 +05:30
beforeEach(() => {
2021-03-08 18:12:59 +05:30
getDraft.mockImplementation((key) => {
2019-07-07 11:18:12 +05:30
if (key === dummyAutosaveKey) {
return dummyDraft;
}
return null;
});
2018-03-17 18:26:18 +05:30
2018-11-08 19:23:39 +05:30
store = createStore();
2018-03-17 18:26:18 +05:30
store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
props = {
isEditing: false,
noteBody: 'Magni suscipit eius consectetur enim et ex et commodi.',
2018-11-20 20:47:30 +05:30
noteId: '545',
2018-03-17 18:26:18 +05:30
};
2022-04-04 11:22:00 +05:30
gon.features = { markdownContinueLists: true };
2018-03-17 18:26:18 +05:30
});
afterEach(() => {
2019-07-07 11:18:12 +05:30
wrapper.destroy();
2018-03-17 18:26:18 +05:30
});
2018-11-20 20:47:30 +05:30
describe('noteHash', () => {
2019-07-07 11:18:12 +05:30
beforeEach(() => {
wrapper = createComponentWrapper();
});
2018-11-20 20:47:30 +05:30
it('returns note hash string based on `noteId`', () => {
2019-07-07 11:18:12 +05:30
expect(wrapper.vm.noteHash).toBe(`#note_${props.noteId}`);
2018-11-20 20:47:30 +05:30
});
2021-03-08 18:12:59 +05:30
it('return note hash as `#` when `noteId` is empty', async () => {
2019-07-07 11:18:12 +05:30
wrapper.setProps({
...props,
noteId: '',
});
2021-03-08 18:12:59 +05:30
await nextTick();
2019-07-07 11:18:12 +05:30
2021-03-08 18:12:59 +05:30
expect(wrapper.vm.noteHash).toBe('#');
2018-11-20 20:47:30 +05:30
});
});
2018-03-17 18:26:18 +05:30
describe('conflicts editing', () => {
2019-07-07 11:18:12 +05:30
beforeEach(() => {
wrapper = createComponentWrapper();
});
2021-03-08 18:12:59 +05:30
it('should show conflict message if note changes outside the component', async () => {
2019-07-07 11:18:12 +05:30
wrapper.setProps({
...props,
isEditing: true,
noteBody: 'Foo',
});
2018-11-08 19:23:39 +05:30
const message =
2021-04-17 20:07:23 +05:30
'This comment changed after you started editing it. Review the updated comment to ensure information is not lost.';
2018-03-17 18:26:18 +05:30
2021-03-08 18:12:59 +05:30
await nextTick();
2020-04-22 19:07:51 +05:30
2021-03-08 18:12:59 +05:30
const conflictWarning = wrapper.find('.js-conflict-edit-warning');
expect(conflictWarning.exists()).toBe(true);
expect(conflictWarning.text().replace(/\s+/g, ' ').trim()).toBe(message);
2021-12-11 22:18:48 +05:30
expect(conflictWarning.find(GlLink).attributes('href')).toBe('#note_545');
2018-03-17 18:26:18 +05:30
});
});
describe('form', () => {
2019-07-07 11:18:12 +05:30
beforeEach(() => {
wrapper = createComponentWrapper();
});
2018-03-17 18:26:18 +05:30
it('should render text area with placeholder', () => {
2019-07-07 11:18:12 +05:30
const textarea = wrapper.find('textarea');
expect(textarea.attributes('placeholder')).toEqual(
2018-11-08 19:23:39 +05:30
'Write a comment or drag your files here…',
);
2018-03-17 18:26:18 +05:30
});
it('should link to markdown docs', () => {
const { markdownDocsPath } = notesDataMock;
2019-07-07 11:18:12 +05:30
const markdownField = wrapper.find(MarkdownField);
const markdownFieldProps = markdownField.props();
2018-12-13 13:39:08 +05:30
2019-07-07 11:18:12 +05:30
expect(markdownFieldProps.markdownDocsPath).toBe(markdownDocsPath);
2018-03-17 18:26:18 +05:30
});
describe('keyboard events', () => {
2019-07-07 11:18:12 +05:30
let textarea;
beforeEach(() => {
textarea = wrapper.find('textarea');
textarea.setValue('Foo');
});
2018-03-17 18:26:18 +05:30
describe('up', () => {
it('should ender edit mode', () => {
2019-07-07 11:18:12 +05:30
// TODO: do not spy on vm
2020-04-22 19:07:51 +05:30
jest.spyOn(wrapper.vm, 'editMyLastNote');
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
textarea.trigger('keydown.up');
expect(wrapper.vm.editMyLastNote).toHaveBeenCalled();
2018-03-17 18:26:18 +05:30
});
});
describe('enter', () => {
it('should save note when cmd+enter is pressed', () => {
2019-07-07 11:18:12 +05:30
textarea.trigger('keydown.enter', { metaKey: true });
const { handleFormUpdate } = wrapper.emitted();
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
expect(handleFormUpdate.length).toBe(1);
2018-03-17 18:26:18 +05:30
});
2018-12-13 13:39:08 +05:30
2018-03-17 18:26:18 +05:30
it('should save note when ctrl+enter is pressed', () => {
2019-07-07 11:18:12 +05:30
textarea.trigger('keydown.enter', { ctrlKey: true });
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
const { handleFormUpdate } = wrapper.emitted();
expect(handleFormUpdate.length).toBe(1);
2018-03-17 18:26:18 +05:30
});
2021-11-18 22:05:49 +05:30
it('should disable textarea when ctrl+enter is pressed', async () => {
textarea.trigger('keydown.enter', { ctrlKey: true });
expect(textarea.attributes('disabled')).toBeUndefined();
await nextTick();
expect(textarea.attributes('disabled')).toBe('disabled');
});
2018-03-17 18:26:18 +05:30
});
});
describe('actions', () => {
2021-03-08 18:12:59 +05:30
it('should be possible to cancel', async () => {
2019-07-07 11:18:12 +05:30
wrapper.setProps({
...props,
isEditing: true,
});
2021-03-08 18:12:59 +05:30
await nextTick();
2018-03-17 18:26:18 +05:30
2021-03-11 19:13:27 +05:30
const cancelButton = findCancelButton();
cancelButton.vm.$emit('click');
2021-03-08 18:12:59 +05:30
await nextTick();
2018-03-17 18:26:18 +05:30
2021-03-08 18:12:59 +05:30
expect(wrapper.emitted().cancelForm).toHaveLength(1);
2019-05-18 00:54:41 +05:30
});
2021-03-08 18:12:59 +05:30
it('should be possible to update the note', async () => {
2019-07-07 11:18:12 +05:30
wrapper.setProps({
...props,
isEditing: true,
});
2021-03-08 18:12:59 +05:30
await nextTick();
2019-05-18 00:54:41 +05:30
2021-03-08 18:12:59 +05:30
const textarea = wrapper.find('textarea');
textarea.setValue('Foo');
const saveButton = wrapper.find('.js-vue-issue-save');
2021-03-11 19:13:27 +05:30
saveButton.vm.$emit('click');
2020-04-22 19:07:51 +05:30
2021-03-08 18:12:59 +05:30
expect(wrapper.vm.isSubmitting).toBe(true);
2019-07-07 11:18:12 +05:30
});
});
});
describe('with autosaveKey', () => {
describe('with draft', () => {
2020-04-22 19:07:51 +05:30
beforeEach(() => {
2019-07-07 11:18:12 +05:30
Object.assign(props, {
noteBody: '',
autosaveKey: dummyAutosaveKey,
2019-05-18 00:54:41 +05:30
});
2019-07-07 11:18:12 +05:30
wrapper = createComponentWrapper();
2021-03-08 18:12:59 +05:30
return nextTick();
2019-05-18 00:54:41 +05:30
});
2019-07-07 11:18:12 +05:30
it('displays the draft in textarea', () => {
const textarea = wrapper.find('textarea');
expect(textarea.element.value).toBe(dummyDraft);
});
});
describe('without draft', () => {
2020-04-22 19:07:51 +05:30
beforeEach(() => {
2019-07-07 11:18:12 +05:30
Object.assign(props, {
noteBody: '',
autosaveKey: 'some key without draft',
});
wrapper = createComponentWrapper();
2021-03-08 18:12:59 +05:30
return nextTick();
2019-07-07 11:18:12 +05:30
});
it('leaves the textarea empty', () => {
const textarea = wrapper.find('textarea');
expect(textarea.element.value).toBe('');
});
});
it('updates the draft if textarea content changes', () => {
Object.assign(props, {
noteBody: '',
autosaveKey: dummyAutosaveKey,
});
wrapper = createComponentWrapper();
const textarea = wrapper.find('textarea');
const dummyContent = 'some new content';
textarea.setValue(dummyContent);
2020-04-22 19:07:51 +05:30
expect(updateDraft).toHaveBeenCalledWith(dummyAutosaveKey, dummyContent);
2019-05-18 00:54:41 +05:30
});
2020-07-28 23:09:34 +05:30
it('does not save draft when ctrl+enter is pressed', () => {
const options = {
noteBody: '',
autosaveKey: dummyAutosaveKey,
};
props = { ...props, ...options };
wrapper = createComponentWrapper();
2022-03-02 08:16:31 +05:30
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
2020-07-28 23:09:34 +05:30
wrapper.setData({ isSubmittingWithKeydown: true });
const textarea = wrapper.find('textarea');
textarea.setValue('some content');
textarea.trigger('keydown.enter', { metaKey: true });
expect(updateDraft).not.toHaveBeenCalled();
});
2018-03-17 18:26:18 +05:30
});
2020-06-23 00:09:42 +05:30
describe('with batch comments', () => {
beforeEach(() => {
store.registerModule('batchComments', batchComments());
wrapper = createComponentWrapper();
wrapper.setProps({
...props,
2021-01-29 00:20:46 +05:30
isDraft: true,
2020-06-23 00:09:42 +05:30
noteId: '',
discussion: { ...discussionMock, for_commit: false },
});
});
2021-03-08 18:12:59 +05:30
it('should be possible to cancel', async () => {
2020-06-23 00:09:42 +05:30
jest.spyOn(wrapper.vm, 'cancelHandler');
2021-03-08 18:12:59 +05:30
await nextTick();
const cancelButton = wrapper.find('[data-testid="cancelBatchCommentsEnabled"]');
2021-03-11 19:13:27 +05:30
cancelButton.vm.$emit('click');
2020-06-23 00:09:42 +05:30
2021-03-08 18:12:59 +05:30
expect(wrapper.vm.cancelHandler).toHaveBeenCalledWith(true);
2020-06-23 00:09:42 +05:30
});
it('shows resolve checkbox', () => {
expect(wrapper.find('.js-resolve-checkbox').exists()).toBe(true);
});
2021-01-29 00:20:46 +05:30
it('hides resolve checkbox', async () => {
wrapper.setProps({
isDraft: false,
discussion: {
...discussionMock,
notes: [
2021-03-08 18:12:59 +05:30
...discussionMock.notes.map((n) => ({
2021-01-29 00:20:46 +05:30
...n,
resolvable: true,
current_user: { ...n.current_user, can_resolve_discussion: false },
})),
],
for_commit: false,
},
});
2021-03-08 18:12:59 +05:30
await nextTick();
2021-01-29 00:20:46 +05:30
expect(wrapper.find('.js-resolve-checkbox').exists()).toBe(false);
});
2021-03-11 19:13:27 +05:30
it('hides actions for commits', async () => {
2020-06-23 00:09:42 +05:30
wrapper.setProps({ discussion: { for_commit: true } });
2021-03-11 19:13:27 +05:30
await nextTick();
expect(wrapper.find('.note-form-actions').text()).not.toContain('Start a review');
2020-06-23 00:09:42 +05:30
});
describe('on enter', () => {
2021-03-11 19:13:27 +05:30
it('should start review or add to review when cmd+enter is pressed', async () => {
2020-06-23 00:09:42 +05:30
const textarea = wrapper.find('textarea');
jest.spyOn(wrapper.vm, 'handleAddToReview');
textarea.setValue('Foo');
textarea.trigger('keydown.enter', { metaKey: true });
2021-03-11 19:13:27 +05:30
await nextTick();
expect(wrapper.vm.handleAddToReview).toHaveBeenCalled();
2020-06-23 00:09:42 +05:30
});
});
});
2018-03-17 18:26:18 +05:30
});