Commit 6a4dd162 authored by Thomas Randolph's avatar Thomas Randolph

Test new interactive jump to first unresolved thread button

- Updates the RSpec tests with the new UI text
- Tests the Discussion Navigation for the
    new behavior
- Tests the unresolved discussions component
    to ensure it's triggering the correct hub event
parent 1ba1348c
...@@ -8,10 +8,14 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j ...@@ -8,10 +8,14 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion } let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
def resolve_all_discussions_link_selector def resolve_all_discussions_link_selector(title: "")
text = "Resolve all threads in new issue"
url = new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) url = new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
%Q{a[title="#{text}"][href="#{url}"]}
if title.empty?
%Q{a[href="#{url}"]}
else
%Q{a[title="#{title}"][href="#{url}"]}
end
end end
describe 'as a user with access to the project' do describe 'as a user with access to the project' do
...@@ -23,7 +27,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j ...@@ -23,7 +27,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
it 'shows a button to resolve all threads by creating a new issue' do it 'shows a button to resolve all threads by creating a new issue' do
within('.line-resolve-all-container') do within('.line-resolve-all-container') do
expect(page).to have_selector resolve_all_discussions_link_selector expect(page).to have_selector resolve_all_discussions_link_selector( title: "Resolve all threads in new issue" )
end end
end end
...@@ -34,6 +38,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j ...@@ -34,6 +38,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
it 'hides the link for creating a new issue' do it 'hides the link for creating a new issue' do
expect(page).not_to have_selector resolve_all_discussions_link_selector expect(page).not_to have_selector resolve_all_discussions_link_selector
expect(page).not_to have_content "Resolve all threads in new issue"
end end
end end
...@@ -57,7 +62,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j ...@@ -57,7 +62,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
end end
it 'does not show a link to create a new issue' do it 'does not show a link to create a new issue' do
expect(page).not_to have_link 'Create an issue to resolve them later' expect(page).not_to have_link 'Resolve all threads in new issue'
end end
end end
...@@ -67,18 +72,20 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j ...@@ -67,18 +72,20 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
end end
it 'shows a warning that the merge request contains unresolved threads' do it 'shows a warning that the merge request contains unresolved threads' do
expect(page).to have_content 'There are unresolved threads.' expect(page).to have_content 'Before this can be merged,'
end end
it 'has a link to resolve all threads by creating an issue' do it 'has a link to resolve all threads by creating an issue' do
page.within '.mr-widget-body' do page.within '.mr-widget-body' do
expect(page).to have_link 'Create an issue to resolve them later', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) expect(page).to have_link 'Resolve all threads in new issue', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
end end
end end
context 'creating an issue for threads' do context 'creating an issue for threads' do
before do before do
page.click_link 'Create an issue to resolve them later', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) page.within '.mr-widget-body' do
page.click_link 'Resolve all threads in new issue', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
end
end end
it_behaves_like 'creating an issue for a thread' it_behaves_like 'creating an issue for a thread'
......
...@@ -21,7 +21,7 @@ RSpec.describe 'Merge request > User sees merge button depending on unresolved t ...@@ -21,7 +21,7 @@ RSpec.describe 'Merge request > User sees merge button depending on unresolved t
context 'with unresolved threads' do context 'with unresolved threads' do
it 'does not allow to merge' do it 'does not allow to merge' do
expect(page).not_to have_button 'Merge' expect(page).not_to have_button 'Merge'
expect(page).to have_content('There are unresolved threads.') expect(page).to have_content('Before this can be merged,')
end end
end end
......
/* global Mousetrap */ /* global Mousetrap */
import 'mousetrap'; import 'mousetrap';
import Vue from 'vue';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import DiscussionKeyboardNavigator from '~/notes/components/discussion_keyboard_navigator.vue'; import DiscussionKeyboardNavigator from '~/notes/components/discussion_keyboard_navigator.vue';
import eventHub from '~/notes/event_hub';
describe('notes/components/discussion_keyboard_navigator', () => { describe('notes/components/discussion_keyboard_navigator', () => {
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -29,10 +31,29 @@ describe('notes/components/discussion_keyboard_navigator', () => { ...@@ -29,10 +31,29 @@ describe('notes/components/discussion_keyboard_navigator', () => {
}); });
afterEach(() => { afterEach(() => {
wrapper.destroy(); if (wrapper) {
wrapper.destroy();
}
wrapper = null; wrapper = null;
}); });
describe('on create', () => {
let onSpy;
let vm;
beforeEach(() => {
onSpy = jest.spyOn(eventHub, '$on');
vm = new (Vue.extend(DiscussionKeyboardNavigator))();
});
it('listens for jumpToFirstUnresolvedDiscussion events', () => {
expect(onSpy).toHaveBeenCalledWith(
'jumpToFirstUnresolvedDiscussion',
vm.jumpToFirstUnresolvedDiscussion,
);
});
});
describe('on mount', () => { describe('on mount', () => {
beforeEach(() => { beforeEach(() => {
createComponent(); createComponent();
...@@ -52,11 +73,16 @@ describe('notes/components/discussion_keyboard_navigator', () => { ...@@ -52,11 +73,16 @@ describe('notes/components/discussion_keyboard_navigator', () => {
}); });
describe('on destroy', () => { describe('on destroy', () => {
let jumpFn;
beforeEach(() => { beforeEach(() => {
jest.spyOn(Mousetrap, 'unbind'); jest.spyOn(Mousetrap, 'unbind');
jest.spyOn(eventHub, '$off');
createComponent(); createComponent();
jumpFn = wrapper.vm.jumpToFirstUnresolvedDiscussion;
wrapper.destroy(); wrapper.destroy();
}); });
...@@ -65,6 +91,10 @@ describe('notes/components/discussion_keyboard_navigator', () => { ...@@ -65,6 +91,10 @@ describe('notes/components/discussion_keyboard_navigator', () => {
expect(Mousetrap.unbind).toHaveBeenCalledWith('p'); expect(Mousetrap.unbind).toHaveBeenCalledWith('p');
}); });
it('unbinds event hub listeners', () => {
expect(eventHub.$off).toHaveBeenCalledWith('jumpToFirstUnresolvedDiscussion', jumpFn);
});
it('does not call jumpToNextDiscussion when pressing `n`', () => { it('does not call jumpToNextDiscussion when pressing `n`', () => {
Mousetrap.trigger('n'); Mousetrap.trigger('n');
......
...@@ -66,6 +66,35 @@ describe('Discussion navigation mixin', () => { ...@@ -66,6 +66,35 @@ describe('Discussion navigation mixin', () => {
const findDiscussion = (selector, id) => const findDiscussion = (selector, id) =>
document.querySelector(`${selector}[data-discussion-id="${id}"]`); document.querySelector(`${selector}[data-discussion-id="${id}"]`);
describe('jumpToFirstUnresolvedDiscussion method', () => {
let vm;
beforeEach(() => {
createComponent();
({ vm } = wrapper);
jest.spyOn(store, 'dispatch');
jest.spyOn(vm, 'jumpToNextDiscussion');
});
it('triggers the setCurrentDiscussionId action with null as the value', () => {
vm.jumpToFirstUnresolvedDiscussion();
expect(store.dispatch).toHaveBeenCalledWith('setCurrentDiscussionId', null);
});
it('triggers the jumpToNextDiscussion action when the previous store action succeeds', () => {
store.dispatch.mockResolvedValue();
vm.jumpToFirstUnresolvedDiscussion();
return vm.$nextTick().then(() => {
expect(vm.jumpToNextDiscussion).toHaveBeenCalled();
});
});
});
describe('cycle through discussions', () => { describe('cycle through discussions', () => {
beforeEach(() => { beforeEach(() => {
window.mrTabs = { eventHub: createEventHub(), tabShown: jest.fn() }; window.mrTabs = { eventHub: createEventHub(), tabShown: jest.fn() };
......
import Vue from 'vue'; import { mount } from '@vue/test-utils';
import mountComponent from 'helpers/vue_mount_component_helper';
import UnresolvedDiscussions from '~/vue_merge_request_widget/components/states/unresolved_discussions.vue'; import UnresolvedDiscussions from '~/vue_merge_request_widget/components/states/unresolved_discussions.vue';
import notesEventHub from '~/notes/event_hub';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
function createComponent({ path = '' } = {}) {
return mount(UnresolvedDiscussions, {
propsData: {
mr: {
createIssueToResolveDiscussionsPath: path,
},
},
});
}
describe('UnresolvedDiscussions', () => { describe('UnresolvedDiscussions', () => {
const Component = Vue.extend(UnresolvedDiscussions); let wrapper;
let vm;
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
});
it('triggers the correct notes event when the jump to first unresolved discussion button is clicked', () => {
jest.spyOn(notesEventHub, '$emit');
wrapper.find('[data-testid="jump-to-first"]').trigger('click');
expect(notesEventHub.$emit).toHaveBeenCalledWith('jumpToFirstUnresolvedDiscussion');
}); });
describe('with threads path', () => { describe('with threads path', () => {
beforeEach(() => { beforeEach(() => {
vm = mountComponent(Component, { wrapper = createComponent({ path: TEST_HOST });
mr: { });
createIssueToResolveDiscussionsPath: TEST_HOST,
}, afterEach(() => {
}); wrapper.destroy();
}); });
it('should have correct elements', () => { it('should have correct elements', () => {
expect(vm.$el.innerText).toContain( expect(wrapper.element.innerText).toContain(
'There are unresolved threads. Please resolve these threads', `Before this can be merged, one or more threads must be resolved.`,
); );
expect(vm.$el.innerText).toContain('Create an issue to resolve them later'); expect(wrapper.element.innerText).toContain('Jump to first unresolved thread');
expect(vm.$el.querySelector('.js-create-issue').getAttribute('href')).toEqual(TEST_HOST); expect(wrapper.element.innerText).toContain('Resolve all threads in new issue');
expect(wrapper.element.querySelector('.js-create-issue').getAttribute('href')).toEqual(
TEST_HOST,
);
}); });
}); });
describe('without threads path', () => { describe('without threads path', () => {
beforeEach(() => {
vm = mountComponent(Component, { mr: {} });
});
it('should not show create issue link if user cannot create issue', () => { it('should not show create issue link if user cannot create issue', () => {
expect(vm.$el.innerText).toContain( expect(wrapper.element.innerText).toContain(
'There are unresolved threads. Please resolve these threads', `Before this can be merged, one or more threads must be resolved.`,
); );
expect(vm.$el.querySelector('.js-create-issue')).toEqual(null); expect(wrapper.element.innerText).toContain('Jump to first unresolved thread');
expect(wrapper.element.innerText).not.toContain('Resolve all threads in new issue');
expect(wrapper.element.querySelector('.js-create-issue')).toEqual(null);
}); });
}); });
}); });
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