264 lines
7.4 KiB
JavaScript
264 lines
7.4 KiB
JavaScript
import { shallowMount } from '@vue/test-utils';
|
|
import Vue, { nextTick } from 'vue';
|
|
import Vuex from 'vuex';
|
|
import { setHTMLFixture } from 'helpers/fixtures';
|
|
import createEventHub from '~/helpers/event_hub_factory';
|
|
import * as utils from '~/lib/utils/common_utils';
|
|
import eventHub from '~/notes/event_hub';
|
|
import discussionNavigation from '~/notes/mixins/discussion_navigation';
|
|
import notesModule from '~/notes/stores/modules';
|
|
|
|
let scrollToFile;
|
|
const discussion = (id, index) => ({
|
|
id,
|
|
resolvable: index % 2 === 0,
|
|
active: true,
|
|
notes: [{}],
|
|
diff_discussion: true,
|
|
position: { new_line: 1, old_line: 1 },
|
|
diff_file: { file_path: 'test.js' },
|
|
});
|
|
const createDiscussions = () => [...'abcde'].map(discussion);
|
|
const createComponent = () => ({
|
|
mixins: [discussionNavigation],
|
|
render() {
|
|
return this.$slots.default;
|
|
},
|
|
});
|
|
|
|
describe('Discussion navigation mixin', () => {
|
|
Vue.use(Vuex);
|
|
|
|
let wrapper;
|
|
let store;
|
|
let expandDiscussion;
|
|
|
|
beforeEach(() => {
|
|
setHTMLFixture(
|
|
[...'abcde']
|
|
.map(
|
|
(id) =>
|
|
`<ul class="notes" data-discussion-id="${id}"></ul>
|
|
<div class="discussion" data-discussion-id="${id}"></div>`,
|
|
)
|
|
.join(''),
|
|
);
|
|
|
|
jest.spyOn(utils, 'scrollToElementWithContext');
|
|
jest.spyOn(utils, 'scrollToElement');
|
|
|
|
expandDiscussion = jest.fn();
|
|
scrollToFile = jest.fn();
|
|
const { actions, ...notesRest } = notesModule();
|
|
store = new Vuex.Store({
|
|
modules: {
|
|
notes: {
|
|
...notesRest,
|
|
actions: { ...actions, expandDiscussion },
|
|
},
|
|
diffs: {
|
|
namespaced: true,
|
|
actions: { scrollToFile },
|
|
},
|
|
},
|
|
});
|
|
store.state.notes.discussions = createDiscussions();
|
|
|
|
wrapper = shallowMount(createComponent(), { store });
|
|
});
|
|
|
|
afterEach(() => {
|
|
wrapper.vm.$destroy();
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
const findDiscussion = (selector, 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', async () => {
|
|
store.dispatch.mockResolvedValue();
|
|
|
|
vm.jumpToFirstUnresolvedDiscussion();
|
|
|
|
await nextTick();
|
|
expect(vm.jumpToNextDiscussion).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('cycle through discussions', () => {
|
|
beforeEach(() => {
|
|
window.mrTabs = { eventHub: createEventHub(), tabShown: jest.fn() };
|
|
});
|
|
|
|
describe.each`
|
|
fn | args | currentId | expected
|
|
${'jumpToNextDiscussion'} | ${[]} | ${null} | ${'a'}
|
|
${'jumpToNextDiscussion'} | ${[]} | ${'a'} | ${'c'}
|
|
${'jumpToNextDiscussion'} | ${[]} | ${'e'} | ${'a'}
|
|
${'jumpToPreviousDiscussion'} | ${[]} | ${null} | ${'e'}
|
|
${'jumpToPreviousDiscussion'} | ${[]} | ${'e'} | ${'c'}
|
|
${'jumpToPreviousDiscussion'} | ${[]} | ${'c'} | ${'a'}
|
|
${'jumpToNextRelativeDiscussion'} | ${[null]} | ${null} | ${'a'}
|
|
${'jumpToNextRelativeDiscussion'} | ${['a']} | ${null} | ${'c'}
|
|
${'jumpToNextRelativeDiscussion'} | ${['e']} | ${'c'} | ${'a'}
|
|
`('$fn (args = $args, currentId = $currentId)', ({ fn, args, currentId, expected }) => {
|
|
beforeEach(() => {
|
|
store.state.notes.currentDiscussionId = currentId;
|
|
});
|
|
|
|
describe('on `show` active tab', () => {
|
|
beforeEach(async () => {
|
|
window.mrTabs.currentAction = 'show';
|
|
wrapper.vm[fn](...args);
|
|
|
|
await nextTick();
|
|
});
|
|
|
|
it('sets current discussion', () => {
|
|
expect(store.state.notes.currentDiscussionId).toEqual(expected);
|
|
});
|
|
|
|
it('expands discussion', () => {
|
|
expect(expandDiscussion).toHaveBeenCalled();
|
|
});
|
|
|
|
it('scrolls to element', () => {
|
|
expect(utils.scrollToElement).toHaveBeenCalledWith(
|
|
findDiscussion('div.discussion', expected),
|
|
{ behavior: 'auto' },
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('on `diffs` active tab', () => {
|
|
beforeEach(async () => {
|
|
window.mrTabs.currentAction = 'diffs';
|
|
wrapper.vm[fn](...args);
|
|
|
|
await nextTick();
|
|
});
|
|
|
|
it('sets current discussion', () => {
|
|
expect(store.state.notes.currentDiscussionId).toEqual(expected);
|
|
});
|
|
|
|
it('expands discussion', () => {
|
|
expect(expandDiscussion).toHaveBeenCalled();
|
|
});
|
|
|
|
it('scrolls when scrollToDiscussion is emitted', () => {
|
|
expect(utils.scrollToElementWithContext).not.toHaveBeenCalled();
|
|
|
|
eventHub.$emit('scrollToDiscussion');
|
|
|
|
expect(utils.scrollToElementWithContext).toHaveBeenCalledWith(
|
|
findDiscussion('ul.notes', expected),
|
|
{ behavior: 'auto' },
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('on `other` active tab', () => {
|
|
beforeEach(async () => {
|
|
window.mrTabs.currentAction = 'other';
|
|
wrapper.vm[fn](...args);
|
|
|
|
await nextTick();
|
|
});
|
|
|
|
it('sets current discussion', () => {
|
|
expect(store.state.notes.currentDiscussionId).toEqual(expected);
|
|
});
|
|
|
|
it('does not expand discussion yet', () => {
|
|
expect(expandDiscussion).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('shows mrTabs', () => {
|
|
expect(window.mrTabs.tabShown).toHaveBeenCalledWith('show');
|
|
});
|
|
|
|
describe('when tab is changed', () => {
|
|
beforeEach(() => {
|
|
window.mrTabs.eventHub.$emit('MergeRequestTabChange');
|
|
|
|
jest.runAllTimers();
|
|
});
|
|
|
|
it('expands discussion', () => {
|
|
expect(expandDiscussion).toHaveBeenCalledWith(expect.anything(), {
|
|
discussionId: expected,
|
|
});
|
|
});
|
|
|
|
it('scrolls to discussion', () => {
|
|
expect(utils.scrollToElement).toHaveBeenCalledWith(
|
|
findDiscussion('div.discussion', expected),
|
|
{ behavior: 'auto' },
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('virtual scrolling feature', () => {
|
|
beforeEach(() => {
|
|
jest.spyOn(store, 'dispatch');
|
|
|
|
store.state.notes.currentDiscussionId = 'a';
|
|
window.location.hash = 'test';
|
|
});
|
|
|
|
afterEach(() => {
|
|
window.gon = {};
|
|
window.location.hash = '';
|
|
});
|
|
|
|
it('resets location hash', async () => {
|
|
wrapper.vm.jumpToNextDiscussion();
|
|
|
|
await nextTick();
|
|
|
|
expect(window.location.hash).toBe('');
|
|
});
|
|
|
|
it.each`
|
|
tabValue
|
|
${'diffs'}
|
|
${'show'}
|
|
${'other'}
|
|
`(
|
|
'calls scrollToFile with setHash as $hashValue when the tab is $tabValue',
|
|
async ({ tabValue }) => {
|
|
window.mrTabs.currentAction = tabValue;
|
|
|
|
wrapper.vm.jumpToNextDiscussion();
|
|
|
|
await nextTick();
|
|
|
|
expect(store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', {
|
|
path: 'test.js',
|
|
});
|
|
},
|
|
);
|
|
});
|
|
});
|
|
});
|