Extract discussion actions into separate component
- Created DiscussionActions component - Updated NoteableDiscussion component accordingly - Wrote Jest tests for DiscussionActions - Updated Jest config to enable emojis aliases mock - Updated qa specs to reflect changes in NoteableDiscussions
This commit is contained in:
parent
dfdcd8b429
commit
e555c8f480
|
@ -0,0 +1,58 @@
|
|||
<script>
|
||||
import ReplyPlaceholder from './discussion_reply_placeholder.vue';
|
||||
import ResolveDiscussionButton from './discussion_resolve_button.vue';
|
||||
import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue';
|
||||
import JumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
|
||||
|
||||
export default {
|
||||
name: 'DiscussionActions',
|
||||
components: {
|
||||
ReplyPlaceholder,
|
||||
ResolveDiscussionButton,
|
||||
ResolveWithIssueButton,
|
||||
JumpToNextDiscussionButton,
|
||||
},
|
||||
props: {
|
||||
discussion: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
isResolving: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
resolveButtonTitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
resolveWithIssuePath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
shouldShowJumpToNextDiscussion: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="discussion-with-resolve-btn">
|
||||
<reply-placeholder class="qa-discussion-reply" @onClick="$emit('showReplyForm')" />
|
||||
<resolve-discussion-button
|
||||
v-if="discussion.resolvable"
|
||||
:is-resolving="isResolving"
|
||||
:button-title="resolveButtonTitle"
|
||||
@onClick="$emit('resolve')"
|
||||
/>
|
||||
<div v-if="discussion.resolvable" class="btn-group discussion-actions ml-sm-2" role="group">
|
||||
<resolve-with-issue-button v-if="resolveWithIssuePath" :url="resolveWithIssuePath" />
|
||||
<jump-to-next-discussion-button
|
||||
v-if="shouldShowJumpToNextDiscussion"
|
||||
@onClick="$emit('jumpToNextDiscussion')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -14,7 +14,6 @@ import { SYSTEM_NOTE } from '../constants';
|
|||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import noteableNote from './noteable_note.vue';
|
||||
import noteHeader from './note_header.vue';
|
||||
import resolveDiscussionButton from './discussion_resolve_button.vue';
|
||||
import toggleRepliesWidget from './toggle_replies_widget.vue';
|
||||
import noteSignedOutWidget from './note_signed_out_widget.vue';
|
||||
import noteEditedText from './note_edited_text.vue';
|
||||
|
@ -25,10 +24,8 @@ import placeholderSystemNote from '../../vue_shared/components/notes/placeholder
|
|||
import noteable from '../mixins/noteable';
|
||||
import resolvable from '../mixins/resolvable';
|
||||
import discussionNavigation from '../mixins/discussion_navigation';
|
||||
import ReplyPlaceholder from './discussion_reply_placeholder.vue';
|
||||
import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue';
|
||||
import jumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
|
||||
import eventHub from '../event_hub';
|
||||
import DiscussionActions from './discussion_actions.vue';
|
||||
|
||||
export default {
|
||||
name: 'NoteableDiscussion',
|
||||
|
@ -40,16 +37,13 @@ export default {
|
|||
noteSignedOutWidget,
|
||||
noteEditedText,
|
||||
noteForm,
|
||||
resolveDiscussionButton,
|
||||
jumpToNextDiscussionButton,
|
||||
toggleRepliesWidget,
|
||||
ReplyPlaceholder,
|
||||
placeholderNote,
|
||||
placeholderSystemNote,
|
||||
ResolveWithIssueButton,
|
||||
systemNote,
|
||||
DraftNote: () => import('ee_component/batch_comments/components/draft_note.vue'),
|
||||
TimelineEntryItem,
|
||||
DiscussionActions,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
@ -465,31 +459,17 @@ Please check your network connection and try again.`;
|
|||
:class="{ 'is-replying': isReplying }"
|
||||
class="discussion-reply-holder"
|
||||
>
|
||||
<template v-if="!isReplying && canReply">
|
||||
<div class="discussion-with-resolve-btn">
|
||||
<reply-placeholder class="qa-discussion-reply" @onClick="showReplyForm" />
|
||||
<resolve-discussion-button
|
||||
v-if="discussion.resolvable"
|
||||
:is-resolving="isResolving"
|
||||
:button-title="resolveButtonTitle"
|
||||
@onClick="resolveHandler"
|
||||
/>
|
||||
<div
|
||||
v-if="discussion.resolvable"
|
||||
class="btn-group discussion-actions ml-sm-2"
|
||||
role="group"
|
||||
>
|
||||
<resolve-with-issue-button
|
||||
v-if="resolveWithIssuePath"
|
||||
:url="resolveWithIssuePath"
|
||||
/>
|
||||
<jump-to-next-discussion-button
|
||||
v-if="shouldShowJumpToNextDiscussion"
|
||||
@onClick="jumpToNextDiscussion"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<discussion-actions
|
||||
v-if="!isReplying && canReply"
|
||||
:discussion="discussion"
|
||||
:is-resolving="isResolving"
|
||||
:resolve-button-title="resolveButtonTitle"
|
||||
:resolve-with-issue-path="resolveWithIssuePath"
|
||||
:should-show-jump-to-next-discussion="shouldShowJumpToNextDiscussion"
|
||||
@showReplyForm="showReplyForm"
|
||||
@resolve="resolveHandler"
|
||||
@jumpToNextDiscussion="jumpToNextDiscussion"
|
||||
/>
|
||||
<note-form
|
||||
v-if="isReplying"
|
||||
ref="noteForm"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Extract DiscussionActions component from NoteableDiscussion
|
||||
merge_request: 27227
|
||||
author:
|
||||
type: other
|
|
@ -15,7 +15,7 @@ module QA
|
|||
element :reply_comment_button
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/notes/components/noteable_discussion.vue' do
|
||||
base.view 'app/assets/javascripts/notes/components/discussion_actions.vue' do
|
||||
element :discussion_reply
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
import createStore from '~/notes/stores';
|
||||
import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
|
||||
import { discussionMock } from '../../../javascripts/notes/mock_data';
|
||||
import DiscussionActions from '~/notes/components/discussion_actions.vue';
|
||||
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
|
||||
import ResolveDiscussionButton from '~/notes/components/discussion_resolve_button.vue';
|
||||
import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_issue_button.vue';
|
||||
import JumpToNextDiscussionButton from '~/notes/components/discussion_jump_to_next_button.vue';
|
||||
|
||||
describe('DiscussionActions', () => {
|
||||
let wrapper;
|
||||
const createComponentFactory = (shallow = true) => props => {
|
||||
const localVue = createLocalVue();
|
||||
const store = createStore();
|
||||
const mountFn = shallow ? shallowMount : mount;
|
||||
|
||||
wrapper = mountFn(DiscussionActions, {
|
||||
localVue,
|
||||
store,
|
||||
propsData: {
|
||||
discussion: discussionMock,
|
||||
isResolving: false,
|
||||
resolveButtonTitle: 'Resolve discussion',
|
||||
resolveWithIssuePath: '/some/issue/path',
|
||||
shouldShowJumpToNextDiscussion: true,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
const createComponent = createComponentFactory();
|
||||
|
||||
it('renders reply placeholder, resolve discussion button, resolve with issue button and jump to next discussion button', () => {
|
||||
createComponent();
|
||||
expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true);
|
||||
expect(wrapper.find(ResolveDiscussionButton).exists()).toBe(true);
|
||||
expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(true);
|
||||
expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('only renders reply placholder if disccusion is not resolvable', () => {
|
||||
const discussion = { ...discussionMock };
|
||||
discussion.resolvable = false;
|
||||
createComponent({ discussion });
|
||||
|
||||
expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true);
|
||||
expect(wrapper.find(ResolveDiscussionButton).exists()).toBe(false);
|
||||
expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(false);
|
||||
expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not render resolve with issue button if resolveWithIssuePath is falsy', () => {
|
||||
createComponent({ resolveWithIssuePath: '' });
|
||||
|
||||
expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not render jump to next discussion button if shouldShowJumpToNextDiscussion is false', () => {
|
||||
createComponent({ shouldShowJumpToNextDiscussion: false });
|
||||
|
||||
expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('events handling', () => {
|
||||
const createComponent = createComponentFactory(false);
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('emits showReplyForm event when clicking on reply placeholder', () => {
|
||||
jest.spyOn(wrapper.vm, '$emit');
|
||||
wrapper
|
||||
.find(ReplyPlaceholder)
|
||||
.find('button')
|
||||
.trigger('click');
|
||||
expect(wrapper.vm.$emit).toHaveBeenCalledWith('showReplyForm');
|
||||
});
|
||||
|
||||
it('emits resolve event when clicking on resolve button', () => {
|
||||
jest.spyOn(wrapper.vm, '$emit');
|
||||
wrapper
|
||||
.find(ResolveDiscussionButton)
|
||||
.find('button')
|
||||
.trigger('click');
|
||||
expect(wrapper.vm.$emit).toHaveBeenCalledWith('resolve');
|
||||
});
|
||||
|
||||
it('emits jumpToNextDiscussion event when clicking on jump to next discussion button', () => {
|
||||
jest.spyOn(wrapper.vm, '$emit');
|
||||
wrapper
|
||||
.find(JumpToNextDiscussionButton)
|
||||
.find('button')
|
||||
.trigger('click');
|
||||
expect(wrapper.vm.$emit).toHaveBeenCalledWith('jumpToNextDiscussion');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue