summaryrefslogtreecommitdiff
path: root/spec/frontend/diffs
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/diffs')
-rw-r--r--spec/frontend/diffs/components/app_spec.js14
-rw-r--r--spec/frontend/diffs/components/collapsed_files_warning_spec.js9
-rw-r--r--spec/frontend/diffs/components/diff_comment_cell_spec.js43
-rw-r--r--spec/frontend/diffs/components/diff_content_spec.js22
-rw-r--r--spec/frontend/diffs/components/diff_file_header_spec.js34
-rw-r--r--spec/frontend/diffs/components/diff_file_spec.js530
-rw-r--r--spec/frontend/diffs/components/diff_row_spec.js127
-rw-r--r--spec/frontend/diffs/components/diff_row_utils_spec.js73
-rw-r--r--spec/frontend/diffs/components/diff_view_spec.js82
-rw-r--r--spec/frontend/diffs/components/inline_diff_expansion_row_spec.js32
-rw-r--r--spec/frontend/diffs/components/inline_diff_table_row_spec.js26
-rw-r--r--spec/frontend/diffs/components/inline_diff_view_spec.js61
-rw-r--r--spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js31
-rw-r--r--spec/frontend/diffs/components/parallel_diff_table_row_spec.js28
-rw-r--r--spec/frontend/diffs/components/tree_list_spec.js6
-rw-r--r--spec/frontend/diffs/mock_data/diff_file.js1
-rw-r--r--spec/frontend/diffs/mock_data/diff_file_unreadable.js1
-rw-r--r--spec/frontend/diffs/store/actions_spec.js37
-rw-r--r--spec/frontend/diffs/store/getters_spec.js56
-rw-r--r--spec/frontend/diffs/store/mutations_spec.js15
-rw-r--r--spec/frontend/diffs/store/utils_spec.js21
21 files changed, 887 insertions, 362 deletions
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index 86560470ada..225710eab63 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -697,7 +697,7 @@ describe('diffs/components/app', () => {
});
describe('collapsed files', () => {
- it('should render the collapsed files warning if there are any collapsed files', () => {
+ it('should render the collapsed files warning if there are any automatically collapsed files', () => {
createComponent({}, ({ state }) => {
state.diffs.diffFiles = [{ viewer: { automaticallyCollapsed: true } }];
});
@@ -705,16 +705,14 @@ describe('diffs/components/app', () => {
expect(getCollapsedFilesWarning(wrapper).exists()).toBe(true);
});
- it('should not render the collapsed files warning if the user has dismissed the alert already', async () => {
+ it('should not render the collapsed files warning if there are no automatically collapsed files', () => {
createComponent({}, ({ state }) => {
- state.diffs.diffFiles = [{ viewer: { automaticallyCollapsed: true } }];
+ state.diffs.diffFiles = [
+ { viewer: { automaticallyCollapsed: false, manuallyCollapsed: true } },
+ { viewer: { automaticallyCollapsed: false, manuallyCollapsed: false } },
+ ];
});
- expect(getCollapsedFilesWarning(wrapper).exists()).toBe(true);
-
- wrapper.vm.collapsedWarningDismissed = true;
- await wrapper.vm.$nextTick();
-
expect(getCollapsedFilesWarning(wrapper).exists()).toBe(false);
});
});
diff --git a/spec/frontend/diffs/components/collapsed_files_warning_spec.js b/spec/frontend/diffs/components/collapsed_files_warning_spec.js
index 7bbffb7a1cd..75e76d88b6b 100644
--- a/spec/frontend/diffs/components/collapsed_files_warning_spec.js
+++ b/spec/frontend/diffs/components/collapsed_files_warning_spec.js
@@ -2,7 +2,8 @@ import Vuex from 'vuex';
import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
import createStore from '~/diffs/store/modules';
import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vue';
-import { CENTERED_LIMITED_CONTAINER_CLASSES } from '~/diffs/constants';
+import { CENTERED_LIMITED_CONTAINER_CLASSES, EVT_EXPAND_ALL_FILES } from '~/diffs/constants';
+import eventHub from '~/diffs/event_hub';
const propsData = {
limited: true,
@@ -76,13 +77,13 @@ describe('CollapsedFilesWarning', () => {
expect(wrapper.find('[data-testid="root"]').exists()).toBe(false);
});
- it('triggers the expandAllFiles action when the alert action button is clicked', () => {
+ it(`emits the \`${EVT_EXPAND_ALL_FILES}\` event when the alert action button is clicked`, () => {
createComponent({}, { full: true });
- jest.spyOn(wrapper.vm.$store, 'dispatch').mockReturnValue(undefined);
+ jest.spyOn(eventHub, '$emit');
getAlertActionButton().vm.$emit('click');
- expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('diffs/expandAllFiles', undefined);
+ expect(eventHub.$emit).toHaveBeenCalledWith(EVT_EXPAND_ALL_FILES);
});
});
diff --git a/spec/frontend/diffs/components/diff_comment_cell_spec.js b/spec/frontend/diffs/components/diff_comment_cell_spec.js
new file mode 100644
index 00000000000..d6b68fc52d7
--- /dev/null
+++ b/spec/frontend/diffs/components/diff_comment_cell_spec.js
@@ -0,0 +1,43 @@
+import { shallowMount } from '@vue/test-utils';
+import DiffCommentCell from '~/diffs/components/diff_comment_cell.vue';
+import DiffDiscussions from '~/diffs/components/diff_discussions.vue';
+import DiffDiscussionReply from '~/diffs/components/diff_discussion_reply.vue';
+
+describe('DiffCommentCell', () => {
+ const createWrapper = (props = {}) => {
+ const { renderDiscussion, ...otherProps } = props;
+ const line = {
+ discussions: [],
+ renderDiscussion,
+ };
+ const diffFileHash = 'abc';
+
+ return shallowMount(DiffCommentCell, {
+ propsData: { line, diffFileHash, ...otherProps },
+ });
+ };
+
+ it('renders discussions if line has discussions', () => {
+ const wrapper = createWrapper({ renderDiscussion: true });
+
+ expect(wrapper.find(DiffDiscussions).exists()).toBe(true);
+ });
+
+ it('does not render discussions if line has no discussions', () => {
+ const wrapper = createWrapper();
+
+ expect(wrapper.find(DiffDiscussions).exists()).toBe(false);
+ });
+
+ it('renders discussion reply if line has no draft', () => {
+ const wrapper = createWrapper();
+
+ expect(wrapper.find(DiffDiscussionReply).exists()).toBe(true);
+ });
+
+ it('does not render discussion reply if line has draft', () => {
+ const wrapper = createWrapper({ hasDraft: true });
+
+ expect(wrapper.find(DiffDiscussionReply).exists()).toBe(false);
+ });
+});
diff --git a/spec/frontend/diffs/components/diff_content_spec.js b/spec/frontend/diffs/components/diff_content_spec.js
index 6d0120d888e..e3a6f7f16a9 100644
--- a/spec/frontend/diffs/components/diff_content_spec.js
+++ b/spec/frontend/diffs/components/diff_content_spec.js
@@ -12,6 +12,8 @@ import DiffDiscussions from '~/diffs/components/diff_discussions.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants';
import diffFileMockData from '../mock_data/diff_file';
import { diffViewerModes } from '~/ide/constants';
+import { diffLines } from '~/diffs/store/getters';
+import DiffView from '~/diffs/components/diff_view.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -33,7 +35,7 @@ describe('DiffContent', () => {
diffFile: JSON.parse(JSON.stringify(diffFileMockData)),
};
- const createComponent = ({ props, state } = {}) => {
+ const createComponent = ({ props, state, provide } = {}) => {
const fakeStore = new Vuex.Store({
getters: {
getNoteableData() {
@@ -55,6 +57,10 @@ describe('DiffContent', () => {
namespaced: true,
getters: {
draftsForFile: () => () => true,
+ draftForLine: () => () => true,
+ shouldRenderDraftRow: () => () => true,
+ hasParallelDraftLeft: () => () => true,
+ hasParallelDraftRight: () => () => true,
},
},
diffs: {
@@ -68,6 +74,7 @@ describe('DiffContent', () => {
isInlineView: isInlineViewGetterMock,
isParallelView: isParallelViewGetterMock,
getCommentFormForDiffFile: getCommentFormForDiffFileGetterMock,
+ diffLines,
},
actions: {
saveDiffDiscussion: saveDiffDiscussionMock,
@@ -77,6 +84,8 @@ describe('DiffContent', () => {
},
});
+ const glFeatures = provide ? { ...provide.glFeatures } : {};
+
wrapper = shallowMount(DiffContentComponent, {
propsData: {
...defaultProps,
@@ -84,6 +93,7 @@ describe('DiffContent', () => {
},
localVue,
store: fakeStore,
+ provide: { glFeatures },
});
};
@@ -112,6 +122,16 @@ describe('DiffContent', () => {
expect(wrapper.find(ParallelDiffView).exists()).toBe(true);
});
+ it('should render diff view if `unifiedDiffLines` & `unifiedDiffComponents` are true', () => {
+ isParallelViewGetterMock.mockReturnValue(true);
+ createComponent({
+ props: { diffFile: textDiffFile },
+ provide: { glFeatures: { unifiedDiffLines: true, unifiedDiffComponents: true } },
+ });
+
+ expect(wrapper.find(DiffView).exists()).toBe(true);
+ });
+
it('renders rendering more lines loading icon', () => {
createComponent({ props: { diffFile: { ...textDiffFile, renderingLines: true } } });
diff --git a/spec/frontend/diffs/components/diff_file_header_spec.js b/spec/frontend/diffs/components/diff_file_header_spec.js
index a04486fc5c7..1b41456f2f5 100644
--- a/spec/frontend/diffs/components/diff_file_header_spec.js
+++ b/spec/frontend/diffs/components/diff_file_header_spec.js
@@ -1,8 +1,12 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { cloneDeep } from 'lodash';
+
+import { mockTracking, triggerEvent } from 'helpers/tracking_helper';
+
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
import diffDiscussionsMockData from '../mock_data/diff_discussions';
import { truncateSha } from '~/lib/utils/text_utility';
import { diffViewerModes } from '~/ide/constants';
@@ -136,9 +140,25 @@ describe('DiffFileHeader component', () => {
});
});
- it('displays a copy to clipboard button', () => {
- createComponent();
- expect(wrapper.find(ClipboardButton).exists()).toBe(true);
+ describe('copy to clipboard', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('displays a copy to clipboard button', () => {
+ expect(wrapper.find(ClipboardButton).exists()).toBe(true);
+ });
+
+ it('triggers the copy to clipboard tracking event', () => {
+ const trackingSpy = mockTracking('_category_', wrapper.vm.$el, jest.spyOn);
+
+ triggerEvent('[data-testid="diff-file-copy-clipboard"]');
+
+ expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_copy_file_button', {
+ label: 'diff_copy_file_path_button',
+ property: 'diff_copy_file',
+ });
+ });
});
describe('for submodule', () => {
@@ -188,6 +208,14 @@ describe('DiffFileHeader component', () => {
});
expect(findFileActions().exists()).toBe(false);
});
+
+ it('renders submodule icon', () => {
+ createComponent({
+ diffFile: submoduleDiffFile,
+ });
+
+ expect(wrapper.find(FileIcon).props('submodule')).toBe(true);
+ });
});
describe('for any file', () => {
diff --git a/spec/frontend/diffs/components/diff_file_spec.js b/spec/frontend/diffs/components/diff_file_spec.js
index a6f0d2bf11d..71e0ffd176f 100644
--- a/spec/frontend/diffs/components/diff_file_spec.js
+++ b/spec/frontend/diffs/components/diff_file_spec.js
@@ -1,262 +1,422 @@
-import Vue from 'vue';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import { mockTracking, triggerEvent } from 'helpers/tracking_helper';
-import { createStore } from '~/mr_notes/stores';
-import DiffFileComponent from '~/diffs/components/diff_file.vue';
-import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+
+import createDiffsStore from '~/diffs/store/modules';
+import createNotesStore from '~/notes/stores/modules';
import diffFileMockDataReadable from '../mock_data/diff_file';
import diffFileMockDataUnreadable from '../mock_data/diff_file_unreadable';
-describe('DiffFile', () => {
- let vm;
- let trackingSpy;
+import DiffFileComponent from '~/diffs/components/diff_file.vue';
+import DiffFileHeaderComponent from '~/diffs/components/diff_file_header.vue';
+import DiffContentComponent from '~/diffs/components/diff_content.vue';
- beforeEach(() => {
- vm = createComponentWithStore(Vue.extend(DiffFileComponent), createStore(), {
- file: JSON.parse(JSON.stringify(diffFileMockDataReadable)),
+import eventHub from '~/diffs/event_hub';
+import {
+ EVT_EXPAND_ALL_FILES,
+ EVT_PERF_MARK_DIFF_FILES_END,
+ EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN,
+} from '~/diffs/constants';
+
+import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
+
+function changeViewer(store, index, { automaticallyCollapsed, manuallyCollapsed, name }) {
+ const file = store.state.diffs.diffFiles[index];
+ const newViewer = {
+ ...file.viewer,
+ };
+
+ if (automaticallyCollapsed !== undefined) {
+ newViewer.automaticallyCollapsed = automaticallyCollapsed;
+ }
+
+ if (manuallyCollapsed !== undefined) {
+ newViewer.manuallyCollapsed = manuallyCollapsed;
+ }
+
+ if (name !== undefined) {
+ newViewer.name = name;
+ }
+
+ Object.assign(file, {
+ viewer: newViewer,
+ });
+}
+
+function forceHasDiff({ store, index = 0, inlineLines, parallelLines, readableText }) {
+ const file = store.state.diffs.diffFiles[index];
+
+ Object.assign(file, {
+ highlighted_diff_lines: inlineLines,
+ parallel_diff_lines: parallelLines,
+ blob: {
+ ...file.blob,
+ readable_text: readableText,
+ },
+ });
+}
+
+function markFileToBeRendered(store, index = 0) {
+ const file = store.state.diffs.diffFiles[index];
+
+ Object.assign(file, {
+ renderIt: true,
+ });
+}
+
+function createComponent({ file, first = false, last = false }) {
+ const localVue = createLocalVue();
+
+ localVue.use(Vuex);
+
+ const store = new Vuex.Store({
+ ...createNotesStore(),
+ modules: {
+ diffs: createDiffsStore(),
+ },
+ });
+
+ store.state.diffs.diffFiles = [file];
+
+ const wrapper = shallowMount(DiffFileComponent, {
+ store,
+ localVue,
+ propsData: {
+ file,
canCurrentUserFork: false,
viewDiffsFileByFile: false,
- }).$mount();
- trackingSpy = mockTracking('_category_', vm.$el, jest.spyOn);
+ isFirstFile: first,
+ isLastFile: last,
+ },
+ });
+
+ return {
+ localVue,
+ wrapper,
+ store,
+ };
+}
+
+const findDiffHeader = wrapper => wrapper.find(DiffFileHeaderComponent);
+const findDiffContentArea = wrapper => wrapper.find('[data-testid="content-area"]');
+const findLoader = wrapper => wrapper.find('[data-testid="loader-icon"]');
+const findToggleButton = wrapper => wrapper.find('[data-testid="expand-button"]');
+
+const toggleFile = wrapper => findDiffHeader(wrapper).vm.$emit('toggleFile');
+const isDisplayNone = element => element.style.display === 'none';
+const getReadableFile = () => JSON.parse(JSON.stringify(diffFileMockDataReadable));
+const getUnreadableFile = () => JSON.parse(JSON.stringify(diffFileMockDataUnreadable));
+
+const makeFileAutomaticallyCollapsed = (store, index = 0) =>
+ changeViewer(store, index, { automaticallyCollapsed: true, manuallyCollapsed: null });
+const makeFileOpenByDefault = (store, index = 0) =>
+ changeViewer(store, index, { automaticallyCollapsed: false, manuallyCollapsed: null });
+const makeFileManuallyCollapsed = (store, index = 0) =>
+ changeViewer(store, index, { automaticallyCollapsed: false, manuallyCollapsed: true });
+const changeViewerType = (store, newType, index = 0) =>
+ changeViewer(store, index, { name: diffViewerModes[newType] });
+
+describe('DiffFile', () => {
+ let wrapper;
+ let store;
+
+ beforeEach(() => {
+ ({ wrapper, store } = createComponent({ file: getReadableFile() }));
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
+ wrapper = null;
});
- const findDiffContent = () => vm.$el.querySelector('.diff-content');
- const isVisible = el => el.style.display !== 'none';
+ describe('bus events', () => {
+ beforeEach(() => {
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
+ });
+
+ describe('during mount', () => {
+ it.each`
+ first | last | events | file
+ ${false} | ${false} | ${[]} | ${{ inlineLines: [], parallelLines: [], readableText: true }}
+ ${true} | ${true} | ${[]} | ${{ inlineLines: [], parallelLines: [], readableText: true }}
+ ${true} | ${false} | ${[EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN]} | ${false}
+ ${false} | ${true} | ${[EVT_PERF_MARK_DIFF_FILES_END]} | ${false}
+ ${true} | ${true} | ${[EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN, EVT_PERF_MARK_DIFF_FILES_END]} | ${false}
+ `(
+ 'emits the events $events based on the file and its position ({ first: $first, last: $last }) among all files',
+ async ({ file, first, last, events }) => {
+ if (file) {
+ forceHasDiff({ store, ...file });
+ }
+
+ ({ wrapper, store } = createComponent({
+ file: store.state.diffs.diffFiles[0],
+ first,
+ last,
+ }));
+
+ await wrapper.vm.$nextTick();
+
+ expect(eventHub.$emit).toHaveBeenCalledTimes(events.length);
+ events.forEach(event => {
+ expect(eventHub.$emit).toHaveBeenCalledWith(event);
+ });
+ },
+ );
+ });
+
+ describe('after loading the diff', () => {
+ it('indicates that it loaded the file', async () => {
+ forceHasDiff({ store, inlineLines: [], parallelLines: [], readableText: true });
+ ({ wrapper, store } = createComponent({
+ file: store.state.diffs.diffFiles[0],
+ first: true,
+ last: true,
+ }));
+
+ jest.spyOn(wrapper.vm, 'loadCollapsedDiff').mockResolvedValue(getReadableFile());
+ jest.spyOn(window, 'requestIdleCallback').mockImplementation(fn => fn());
+
+ makeFileAutomaticallyCollapsed(store);
+
+ await wrapper.vm.$nextTick(); // Wait for store updates to flow into the component
+
+ toggleFile(wrapper);
+
+ await wrapper.vm.$nextTick(); // Wait for the load to resolve
+ await wrapper.vm.$nextTick(); // Wait for the idleCallback
+ await wrapper.vm.$nextTick(); // Wait for nextTick inside postRender
+
+ expect(eventHub.$emit).toHaveBeenCalledTimes(2);
+ expect(eventHub.$emit).toHaveBeenCalledWith(EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN);
+ expect(eventHub.$emit).toHaveBeenCalledWith(EVT_PERF_MARK_DIFF_FILES_END);
+ });
+ });
+ });
describe('template', () => {
- it('should render component with file header, file content components', done => {
- const el = vm.$el;
- const { file_hash, file_path } = vm.file;
+ it('should render component with file header, file content components', async () => {
+ const el = wrapper.vm.$el;
+ const { file_hash } = wrapper.vm.file;
expect(el.id).toEqual(file_hash);
expect(el.classList.contains('diff-file')).toEqual(true);
expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
expect(el.querySelector('.js-file-title')).toBeDefined();
- expect(el.querySelector('[data-testid="diff-file-copy-clipboard"]')).toBeDefined();
- expect(el.querySelector('.file-title-name').innerText.indexOf(file_path)).toBeGreaterThan(-1);
+ expect(wrapper.find(DiffFileHeaderComponent).exists()).toBe(true);
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
- vm.file.renderIt = true;
+ markFileToBeRendered(store);
+
+ await wrapper.vm.$nextTick();
- vm.$nextTick()
- .then(() => {
- expect(el.querySelectorAll('.line_content').length).toBe(8);
- expect(el.querySelectorAll('.js-line-expansion-content').length).toBe(1);
- triggerEvent('[data-testid="diff-file-copy-clipboard"]');
- })
- .then(done)
- .catch(done.fail);
+ expect(wrapper.find(DiffContentComponent).exists()).toBe(true);
});
+ });
- it('should track a click event on copy to clip board button', done => {
- const el = vm.$el;
+ describe('collapsing', () => {
+ describe(`\`${EVT_EXPAND_ALL_FILES}\` event`, () => {
+ beforeEach(() => {
+ jest.spyOn(wrapper.vm, 'handleToggle').mockImplementation(() => {});
+ });
- expect(el.querySelector('[data-testid="diff-file-copy-clipboard"]')).toBeDefined();
- vm.file.renderIt = true;
- vm.$nextTick()
- .then(() => {
- triggerEvent('[data-testid="diff-file-copy-clipboard"]');
+ it('performs the normal file toggle when the file is collapsed', async () => {
+ makeFileAutomaticallyCollapsed(store);
- expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_copy_file_button', {
- label: 'diff_copy_file_path_button',
- property: 'diff_copy_file',
- });
- })
- .then(done)
- .catch(done.fail);
- });
+ await wrapper.vm.$nextTick();
- describe('collapsed', () => {
- it('should not have file content', done => {
- expect(isVisible(findDiffContent())).toBe(true);
- expect(vm.isCollapsed).toEqual(false);
- vm.isCollapsed = true;
- vm.file.renderIt = true;
+ eventHub.$emit(EVT_EXPAND_ALL_FILES);
- vm.$nextTick(() => {
- expect(isVisible(findDiffContent())).toBe(false);
-
- done();
- });
+ expect(wrapper.vm.handleToggle).toHaveBeenCalledTimes(1);
});
- it('should have collapsed text and link', done => {
- vm.renderIt = true;
- vm.isCollapsed = true;
+ it('does nothing when the file is not collapsed', async () => {
+ eventHub.$emit(EVT_EXPAND_ALL_FILES);
- vm.$nextTick(() => {
- expect(vm.$el.innerText).toContain('This diff is collapsed');
- expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1);
+ await wrapper.vm.$nextTick();
- done();
- });
+ expect(wrapper.vm.handleToggle).not.toHaveBeenCalled();
});
+ });
- it('should have collapsed text and link even before rendered', done => {
- vm.renderIt = false;
- vm.isCollapsed = true;
+ describe('user collapsed', () => {
+ beforeEach(() => {
+ makeFileManuallyCollapsed(store);
+ });
- vm.$nextTick(() => {
- expect(vm.$el.innerText).toContain('This diff is collapsed');
- expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1);
+ it('should not have any content at all', async () => {
+ await wrapper.vm.$nextTick();
- done();
+ Array.from(findDiffContentArea(wrapper).element.children).forEach(child => {
+ expect(isDisplayNone(child)).toBe(true);
});
});
- it('should be collapsable for unreadable files', done => {
- vm.$destroy();
- vm = createComponentWithStore(Vue.extend(DiffFileComponent), createStore(), {
- file: JSON.parse(JSON.stringify(diffFileMockDataUnreadable)),
- canCurrentUserFork: false,
- viewDiffsFileByFile: false,
- }).$mount();
+ it('should not have the class `has-body` to present the header differently', () => {
+ expect(wrapper.classes('has-body')).toBe(false);
+ });
+ });
- vm.renderIt = false;
- vm.isCollapsed = true;
+ describe('automatically collapsed', () => {
+ beforeEach(() => {
+ makeFileAutomaticallyCollapsed(store);
+ });
- vm.$nextTick(() => {
- expect(vm.$el.innerText).toContain('This diff is collapsed');
- expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1);
+ it('should show the collapsed file warning with expansion button', () => {
+ expect(findDiffContentArea(wrapper).html()).toContain(
+ 'Files with large changes are collapsed by default.',
+ );
+ expect(findToggleButton(wrapper).exists()).toBe(true);
+ });
- done();
- });
+ it('should style the component so that it `.has-body` for layout purposes', () => {
+ expect(wrapper.classes('has-body')).toBe(true);
});
+ });
- it('should be collapsed for renamed files', done => {
- vm.renderIt = true;
- vm.isCollapsed = false;
- vm.file.highlighted_diff_lines = null;
- vm.file.viewer.name = diffViewerModes.renamed;
+ describe('not collapsed', () => {
+ beforeEach(() => {
+ makeFileOpenByDefault(store);
+ markFileToBeRendered(store);
+ });
- vm.$nextTick(() => {
- expect(vm.$el.innerText).not.toContain('This diff is collapsed');
+ it('should have the file content', async () => {
+ expect(wrapper.find(DiffContentComponent).exists()).toBe(true);
+ });
- done();
- });
+ it('should style the component so that it `.has-body` for layout purposes', () => {
+ expect(wrapper.classes('has-body')).toBe(true);
});
+ });
- it('should be collapsed for mode changed files', done => {
- vm.renderIt = true;
- vm.isCollapsed = false;
- vm.file.highlighted_diff_lines = null;
- vm.file.viewer.name = diffViewerModes.mode_changed;
+ describe('toggle', () => {
+ it('should update store state', async () => {
+ jest.spyOn(wrapper.vm.$store, 'dispatch').mockImplementation(() => {});
- vm.$nextTick(() => {
- expect(vm.$el.innerText).not.toContain('This diff is collapsed');
+ toggleFile(wrapper);
- done();
+ expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('diffs/setFileCollapsedByUser', {
+ filePath: wrapper.vm.file.file_path,
+ collapsed: true,
});
});
- it('should have loading icon while loading a collapsed diffs', done => {
- vm.isCollapsed = true;
- vm.isLoadingCollapsedDiff = true;
+ describe('fetch collapsed diff', () => {
+ const prepFile = async (inlineLines, parallelLines, readableText) => {
+ forceHasDiff({
+ store,
+ inlineLines,
+ parallelLines,
+ readableText,
+ });
+
+ await wrapper.vm.$nextTick();
- vm.$nextTick(() => {
- expect(vm.$el.querySelectorAll('.diff-content.loading').length).toEqual(1);
+ toggleFile(wrapper);
+ };
- done();
+ beforeEach(() => {
+ jest.spyOn(wrapper.vm, 'requestDiff').mockImplementation(() => {});
+
+ makeFileAutomaticallyCollapsed(store);
});
- });
- it('should update store state', done => {
- jest.spyOn(vm.$store, 'dispatch').mockImplementation(() => {});
+ it.each`
+ inlineLines | parallelLines | readableText
+ ${[1]} | ${[1]} | ${true}
+ ${[]} | ${[1]} | ${true}
+ ${[1]} | ${[]} | ${true}
+ ${[1]} | ${[1]} | ${false}
+ ${[]} | ${[]} | ${false}
+ `(
+ 'does not make a request to fetch the diff for a diff file like { inline: $inlineLines, parallel: $parallelLines, readableText: $readableText }',
+ async ({ inlineLines, parallelLines, readableText }) => {
+ await prepFile(inlineLines, parallelLines, readableText);
+
+ expect(wrapper.vm.requestDiff).not.toHaveBeenCalled();
+ },
+ );
- vm.isCollapsed = true;
+ it.each`
+ inlineLines | parallelLines | readableText
+ ${[]} | ${[]} | ${true}
+ `(
+ 'makes a request to fetch the diff for a diff file like { inline: $inlineLines, parallel: $parallelLines, readableText: $readableText }',
+ async ({ inlineLines, parallelLines, readableText }) => {
+ await prepFile(inlineLines, parallelLines, readableText);
- vm.$nextTick(() => {
- expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/setFileCollapsed', {
- filePath: vm.file.file_path,
- collapsed: true,
- });
+ expect(wrapper.vm.requestDiff).toHaveBeenCalled();
+ },
+ );
+ });
+ });
- done();
- });
+ describe('loading', () => {
+ it('should have loading icon while loading a collapsed diffs', async () => {
+ makeFileAutomaticallyCollapsed(store);
+ wrapper.vm.isLoadingCollapsedDiff = true;
+
+ await wrapper.vm.$nextTick();
+
+ expect(findLoader(wrapper).exists()).toBe(true);
});
+ });
- it('updates local state when changing file state', done => {
- vm.file.viewer.automaticallyCollapsed = true;
+ describe('general (other) collapsed', () => {
+ it('should be expandable for unreadable files', async () => {
+ ({ wrapper, store } = createComponent({ file: getUnreadableFile() }));
+ makeFileAutomaticallyCollapsed(store);
- vm.$nextTick(() => {
- expect(vm.isCollapsed).toBe(true);
+ await wrapper.vm.$nextTick();
- done();
- });
+ expect(findDiffContentArea(wrapper).html()).toContain(
+ 'Files with large changes are collapsed by default.',
+ );
+ expect(findToggleButton(wrapper).exists()).toBe(true);
});
+
+ it.each`
+ mode
+ ${'renamed'}
+ ${'mode_changed'}
+ `(
+ 'should render the DiffContent component for files whose mode is $mode',
+ async ({ mode }) => {
+ makeFileOpenByDefault(store);
+ markFileToBeRendered(store);
+ changeViewerType(store, mode);
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.classes('has-body')).toBe(true);
+ expect(wrapper.find(DiffContentComponent).exists()).toBe(true);
+ expect(wrapper.find(DiffContentComponent).isVisible()).toBe(true);
+ },
+ );
});
});
describe('too large diff', () => {
- it('should have too large warning and blob link', done => {
+ it('should have too large warning and blob link', async () => {
+ const file = store.state.diffs.diffFiles[0];
const BLOB_LINK = '/file/view/path';
- vm.file.viewer.error = diffViewerErrors.too_large;
- vm.file.viewer.error_message =
- 'This source diff could not be displayed because it is too large';
- vm.file.view_path = BLOB_LINK;
- vm.file.renderIt = true;
-
- vm.$nextTick(() => {
- expect(vm.$el.innerText).toContain(
- 'This source diff could not be displayed because it is too large',
- );
- done();
+ Object.assign(store.state.diffs.diffFiles[0], {
+ ...file,
+ view_path: BLOB_LINK,
+ renderIt: true,
+ viewer: {
+ ...file.viewer,
+ error: diffViewerErrors.too_large,
+ error_message: 'This source diff could not be displayed because it is too large',
+ },
});
- });
- });
- describe('watch collapsed', () => {
- it('calls handleLoadCollapsedDiff if collapsed changed & file has no lines', done => {
- jest.spyOn(vm, 'handleLoadCollapsedDiff').mockImplementation(() => {});
-
- vm.file.highlighted_diff_lines = [];
- vm.file.parallel_diff_lines = [];
- vm.isCollapsed = true;
-
- vm.$nextTick()
- .then(() => {
- vm.isCollapsed = false;
-
- return vm.$nextTick();
- })
- .then(() => {
- expect(vm.handleLoadCollapsedDiff).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
- });
+ await wrapper.vm.$nextTick();
- it('does not call handleLoadCollapsedDiff if collapsed changed & file is unreadable', done => {
- vm.$destroy();
- vm = createComponentWithStore(Vue.extend(DiffFileComponent), createStore(), {
- file: JSON.parse(JSON.stringify(diffFileMockDataUnreadable)),
- canCurrentUserFork: false,
- viewDiffsFileByFile: false,
- }).$mount();
-
- jest.spyOn(vm, 'handleLoadCollapsedDiff').mockImplementation(() => {});
-
- vm.file.highlighted_diff_lines = [];
- vm.file.parallel_diff_lines = undefined;
- vm.isCollapsed = true;
-
- vm.$nextTick()
- .then(() => {
- vm.isCollapsed = false;
-
- return vm.$nextTick();
- })
- .then(() => {
- expect(vm.handleLoadCollapsedDiff).not.toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
+ expect(wrapper.vm.$el.innerText).toContain(
+ 'This source diff could not be displayed because it is too large',
+ );
});
});
});
diff --git a/spec/frontend/diffs/components/diff_row_spec.js b/spec/frontend/diffs/components/diff_row_spec.js
new file mode 100644
index 00000000000..f9e76cf8107
--- /dev/null
+++ b/spec/frontend/diffs/components/diff_row_spec.js
@@ -0,0 +1,127 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import diffsModule from '~/diffs/store/modules';
+import DiffRow from '~/diffs/components/diff_row.vue';
+
+describe('DiffRow', () => {
+ const testLines = [
+ {
+ left: { old_line: 1, discussions: [] },
+ right: { new_line: 1, discussions: [] },
+ hasDiscussionsLeft: true,
+ hasDiscussionsRight: true,
+ },
+ {
+ left: {},
+ right: {},
+ isMatchLineLeft: true,
+ isMatchLineRight: true,
+ },
+ {},
+ {
+ left: { old_line: 1, discussions: [] },
+ right: { new_line: 1, discussions: [] },
+ },
+ ];
+
+ const createWrapper = ({ props, state, isLoggedIn = true }) => {
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+
+ const diffs = diffsModule();
+ diffs.state = { ...diffs.state, ...state };
+
+ const getters = { isLoggedIn: () => isLoggedIn };
+
+ const store = new Vuex.Store({
+ modules: { diffs },
+ getters,
+ });
+
+ const propsData = {
+ fileHash: 'abc',
+ filePath: 'abc',
+ line: {},
+ ...props,
+ };
+ return shallowMount(DiffRow, { propsData, localVue, store });
+ };
+
+ it('isHighlighted returns true if isCommented is true', () => {
+ const props = { isCommented: true };
+ const wrapper = createWrapper({ props });
+ expect(wrapper.vm.isHighlighted).toBe(true);
+ });
+
+ it('isHighlighted returns true given line.left', () => {
+ const props = {
+ line: {
+ left: {
+ line_code: 'abc',
+ },
+ },
+ };
+ const state = { highlightedRow: 'abc' };
+ const wrapper = createWrapper({ props, state });
+ expect(wrapper.vm.isHighlighted).toBe(true);
+ });
+
+ it('isHighlighted returns true given line.right', () => {
+ const props = {
+ line: {
+ right: {
+ line_code: 'abc',
+ },
+ },
+ };
+ const state = { highlightedRow: 'abc' };
+ const wrapper = createWrapper({ props, state });
+ expect(wrapper.vm.isHighlighted).toBe(true);
+ });
+
+ it('isHighlighted returns false given line.left', () => {
+ const props = {
+ line: {
+ left: {
+ line_code: 'abc',
+ },
+ },
+ };
+ const wrapper = createWrapper({ props });
+ expect(wrapper.vm.isHighlighted).toBe(false);
+ });
+
+ describe.each`
+ side
+ ${'left'}
+ ${'right'}
+ `('$side side', ({ side }) => {
+ it(`renders empty cells if ${side} is unavailable`, () => {
+ const wrapper = createWrapper({ props: { line: testLines[2] } });
+ expect(wrapper.find(`[data-testid="${side}LineNumber"]`).exists()).toBe(false);
+ expect(wrapper.find(`[data-testid="${side}EmptyCell"]`).exists()).toBe(true);
+ });
+
+ it('renders comment button', () => {
+ const wrapper = createWrapper({ props: { line: testLines[3] } });
+ expect(wrapper.find(`[data-testid="${side}CommentButton"]`).exists()).toBe(true);
+ });
+
+ it('renders avatars', () => {
+ const wrapper = createWrapper({ props: { line: testLines[0] } });
+ expect(wrapper.find(`[data-testid="${side}Discussions"]`).exists()).toBe(true);
+ });
+ });
+
+ it('renders left line numbers', () => {
+ const wrapper = createWrapper({ props: { line: testLines[0] } });
+ const lineNumber = testLines[0].left.old_line;
+ expect(wrapper.find(`[data-linenumber="${lineNumber}"]`).exists()).toBe(true);
+ });
+
+ it('renders right line numbers', () => {
+ const wrapper = createWrapper({ props: { line: testLines[0] } });
+ const lineNumber = testLines[0].right.new_line;
+ expect(wrapper.find(`[data-linenumber="${lineNumber}"]`).exists()).toBe(true);
+ });
+});
diff --git a/spec/frontend/diffs/components/diff_row_utils_spec.js b/spec/frontend/diffs/components/diff_row_utils_spec.js
index 394b6cb1914..c001857fa49 100644
--- a/spec/frontend/diffs/components/diff_row_utils_spec.js
+++ b/spec/frontend/diffs/components/diff_row_utils_spec.js
@@ -201,3 +201,76 @@ describe('shouldShowCommentButton', () => {
},
);
});
+
+describe('mapParallel', () => {
+ it('should assign computed properties to the line object', () => {
+ const side = {
+ discussions: [{}],
+ discussionsExpanded: true,
+ hasForm: true,
+ };
+ const content = {
+ diffFile: {},
+ hasParallelDraftLeft: () => false,
+ hasParallelDraftRight: () => false,
+ draftForLine: () => ({}),
+ };
+ const line = { left: side, right: side };
+ const expectation = {
+ commentRowClasses: '',
+ draftRowClasses: 'js-temp-notes-holder',
+ hasDiscussionsLeft: true,
+ hasDiscussionsRight: true,
+ isContextLineLeft: false,
+ isContextLineRight: false,
+ isMatchLineLeft: false,
+ isMatchLineRight: false,
+ isMetaLineLeft: false,
+ isMetaLineRight: false,
+ };
+ const leftExpectation = {
+ renderDiscussion: true,
+ hasDraft: false,
+ lineDraft: {},
+ hasCommentForm: true,
+ };
+ const rightExpectation = {
+ renderDiscussion: false,
+ hasDraft: false,
+ lineDraft: {},
+ hasCommentForm: false,
+ };
+ const mapped = utils.mapParallel(content)(line);
+
+ expect(mapped).toMatchObject(expectation);
+ expect(mapped.left).toMatchObject(leftExpectation);
+ expect(mapped.right).toMatchObject(rightExpectation);
+ });
+});
+
+describe('mapInline', () => {
+ it('should assign computed properties to the line object', () => {
+ const content = {
+ diffFile: {},
+ shouldRenderDraftRow: () => false,
+ };
+ const line = {
+ discussions: [{}],
+ discussionsExpanded: true,
+ hasForm: true,
+ };
+ const expectation = {
+ commentRowClasses: '',
+ hasDiscussions: true,
+ isContextLine: false,
+ isMatchLine: false,
+ isMetaLine: false,
+ renderDiscussion: true,
+ hasDraft: false,
+ hasCommentForm: true,
+ };
+ const mapped = utils.mapInline(content)(line);
+
+ expect(mapped).toMatchObject(expectation);
+ });
+});
diff --git a/spec/frontend/diffs/components/diff_view_spec.js b/spec/frontend/diffs/components/diff_view_spec.js
new file mode 100644
index 00000000000..4d90112d8f6
--- /dev/null
+++ b/spec/frontend/diffs/components/diff_view_spec.js
@@ -0,0 +1,82 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import DiffView from '~/diffs/components/diff_view.vue';
+// import DraftNote from '~/batch_comments/components/draft_note.vue';
+// import DiffRow from '~/diffs/components/diff_row.vue';
+// import DiffCommentCell from '~/diffs/components/diff_comment_cell.vue';
+// import DiffExpansionCell from '~/diffs/components/diff_expansion_cell.vue';
+
+describe('DiffView', () => {
+ const DiffExpansionCell = { template: `<div/>` };
+ const DiffRow = { template: `<div/>` };
+ const DiffCommentCell = { template: `<div/>` };
+ const DraftNote = { template: `<div/>` };
+ const createWrapper = props => {
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+
+ const batchComments = {
+ getters: {
+ shouldRenderDraftRow: () => false,
+ shouldRenderParallelDraftRow: () => () => true,
+ draftForLine: () => false,
+ draftsForFile: () => false,
+ hasParallelDraftLeft: () => false,
+ hasParallelDraftRight: () => false,
+ },
+ namespaced: true,
+ };
+ const diffs = { getters: { commitId: () => 'abc123' }, namespaced: true };
+ const notes = {
+ state: { selectedCommentPosition: null, selectedCommentPositionHover: null },
+ };
+
+ const store = new Vuex.Store({
+ modules: { diffs, notes, batchComments },
+ });
+
+ const propsData = {
+ diffFile: {},
+ diffLines: [],
+ ...props,
+ };
+ const stubs = { DiffExpansionCell, DiffRow, DiffCommentCell, DraftNote };
+ return shallowMount(DiffView, { propsData, store, localVue, stubs });
+ };
+
+ it('renders a match line', () => {
+ const wrapper = createWrapper({ diffLines: [{ isMatchLineLeft: true }] });
+ expect(wrapper.find(DiffExpansionCell).exists()).toBe(true);
+ });
+
+ it.each`
+ type | side | container | sides | total
+ ${'parallel'} | ${'left'} | ${'.old'} | ${{ left: { lineDraft: {} }, right: { lineDraft: {} } }} | ${2}
+ ${'parallel'} | ${'right'} | ${'.new'} | ${{ left: { lineDraft: {} }, right: { lineDraft: {} } }} | ${2}
+ ${'inline'} | ${'left'} | ${'.old'} | ${{ left: { lineDraft: {} } }} | ${1}
+ ${'inline'} | ${'right'} | ${'.new'} | ${{ right: { lineDraft: {} } }} | ${1}
+ ${'inline'} | ${'left'} | ${'.old'} | ${{ left: { lineDraft: {} }, right: { lineDraft: {} } }} | ${1}
+ `(
+ 'renders a $type comment row with comment cell on $side',
+ ({ type, container, sides, total }) => {
+ const wrapper = createWrapper({
+ diffLines: [{ renderCommentRow: true, ...sides }],
+ inline: type === 'inline',
+ });
+ expect(wrapper.findAll(DiffCommentCell).length).toBe(total);
+ expect(
+ wrapper
+ .find(container)
+ .find(DiffCommentCell)
+ .exists(),
+ ).toBe(true);
+ },
+ );
+
+ it('renders a draft row', () => {
+ const wrapper = createWrapper({
+ diffLines: [{ renderCommentRow: true, left: { lineDraft: { isDraft: true } } }],
+ });
+ expect(wrapper.find(DraftNote).exists()).toBe(true);
+ });
+});
diff --git a/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js b/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js
deleted file mode 100644
index 81e5403d502..00000000000
--- a/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import Vue from 'vue';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import { createStore } from '~/mr_notes/stores';
-import InlineDiffExpansionRow from '~/diffs/components/inline_diff_expansion_row.vue';
-import diffFileMockData from '../mock_data/diff_file';
-
-describe('InlineDiffExpansionRow', () => {
- const mockData = { ...diffFileMockData };
- const matchLine = mockData.highlighted_diff_lines.pop();
-
- const createComponent = (options = {}) => {
- const cmp = Vue.extend(InlineDiffExpansionRow);
- const defaults = {
- fileHash: mockData.file_hash,
- contextLinesPath: 'contextLinesPath',
- line: matchLine,
- isTop: false,
- isBottom: false,
- };
- const props = { ...defaults, ...options };
-
- return createComponentWithStore(cmp, createStore(), props).$mount();
- };
-
- describe('template', () => {
- it('should render expansion row for match lines', () => {
- const vm = createComponent();
-
- expect(vm.$el.classList.contains('line_expansion')).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/diffs/components/inline_diff_table_row_spec.js b/spec/frontend/diffs/components/inline_diff_table_row_spec.js
index c65a39b9083..21e7d7397a0 100644
--- a/spec/frontend/diffs/components/inline_diff_table_row_spec.js
+++ b/spec/frontend/diffs/components/inline_diff_table_row_spec.js
@@ -4,6 +4,7 @@ import InlineDiffTableRow from '~/diffs/components/inline_diff_table_row.vue';
import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
import diffFileMockData from '../mock_data/diff_file';
import discussionsMockData from '../mock_data/diff_discussions';
+import { mapInline } from '~/diffs/components/diff_row_utils';
const TEST_USER_ID = 'abc123';
const TEST_USER = { id: TEST_USER_ID };
@@ -11,7 +12,16 @@ const TEST_USER = { id: TEST_USER_ID };
describe('InlineDiffTableRow', () => {
let wrapper;
let store;
- const thisLine = diffFileMockData.highlighted_diff_lines[0];
+ const mockDiffContent = {
+ diffFile: diffFileMockData,
+ shouldRenderDraftRow: jest.fn(),
+ hasParallelDraftLeft: jest.fn(),
+ hasParallelDraftRight: jest.fn(),
+ draftForLine: jest.fn(),
+ };
+
+ const applyMap = mapInline(mockDiffContent);
+ const thisLine = applyMap(diffFileMockData.highlighted_diff_lines[0]);
const createComponent = (props = {}, propsStore = store) => {
wrapper = shallowMount(InlineDiffTableRow, {
@@ -132,7 +142,7 @@ describe('InlineDiffTableRow', () => {
${true} | ${{ ...thisLine, type: 'old-nonewline', discussions: [] }} | ${false}
${true} | ${{ ...thisLine, discussions: [{}] }} | ${false}
`('visible is $expectation - line ($line)', ({ isHover, line, expectation }) => {
- createComponent({ line });
+ createComponent({ line: applyMap(line) });
wrapper.setData({ isHover });
return wrapper.vm.$nextTick().then(() => {
@@ -148,7 +158,7 @@ describe('InlineDiffTableRow', () => {
'has attribute disabled=$disabled when the outer component has prop commentsDisabled=$commentsDisabled',
({ disabled, commentsDisabled }) => {
createComponent({
- line: { ...thisLine, commentsDisabled },
+ line: applyMap({ ...thisLine, commentsDisabled }),
});
wrapper.setData({ isHover: true });
@@ -177,7 +187,7 @@ describe('InlineDiffTableRow', () => {
'has the correct tooltip when commentsDisabled=$commentsDisabled',
({ tooltip, commentsDisabled }) => {
createComponent({
- line: { ...thisLine, commentsDisabled },
+ line: applyMap({ ...thisLine, commentsDisabled }),
});
wrapper.setData({ isHover: true });
@@ -216,7 +226,7 @@ describe('InlineDiffTableRow', () => {
beforeEach(() => {
jest.spyOn(store, 'dispatch').mockImplementation();
createComponent({
- line: { ...thisLine, ...lineProps },
+ line: applyMap({ ...thisLine, ...lineProps }),
});
});
@@ -268,7 +278,7 @@ describe('InlineDiffTableRow', () => {
describe('with showCommentButton', () => {
it('renders if line has discussions', () => {
- createComponent({ line });
+ createComponent({ line: applyMap(line) });
expect(findAvatars().props()).toEqual({
discussions: line.discussions,
@@ -278,13 +288,13 @@ describe('InlineDiffTableRow', () => {
it('does notrender if line has no discussions', () => {
line.discussions = [];
- createComponent({ line });
+ createComponent({ line: applyMap(line) });
expect(findAvatars().exists()).toEqual(false);
});
it('toggles line discussion', () => {
- createComponent({ line });
+ createComponent({ line: applyMap(line) });
expect(store.dispatch).toHaveBeenCalledTimes(1);
diff --git a/spec/frontend/diffs/components/inline_diff_view_spec.js b/spec/frontend/diffs/components/inline_diff_view_spec.js
index 39c581e2796..6a1791509fd 100644
--- a/spec/frontend/diffs/components/inline_diff_view_spec.js
+++ b/spec/frontend/diffs/components/inline_diff_view_spec.js
@@ -1,54 +1,57 @@
-import Vue from 'vue';
import '~/behaviors/markdown/render_gfm';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
+import { mount } from '@vue/test-utils';
+import { getByText } from '@testing-library/dom';
import { createStore } from '~/mr_notes/stores';
import InlineDiffView from '~/diffs/components/inline_diff_view.vue';
+import { mapInline } from '~/diffs/components/diff_row_utils';
import diffFileMockData from '../mock_data/diff_file';
import discussionsMockData from '../mock_data/diff_discussions';
describe('InlineDiffView', () => {
- let component;
+ let wrapper;
const getDiffFileMock = () => ({ ...diffFileMockData });
const getDiscussionsMockData = () => [{ ...discussionsMockData }];
const notesLength = getDiscussionsMockData()[0].notes.length;
- beforeEach(done => {
- const diffFile = getDiffFileMock();
+ const setup = (diffFile, diffLines) => {
+ const mockDiffContent = {
+ diffFile,
+ shouldRenderDraftRow: jest.fn(),
+ };
const store = createStore();
store.dispatch('diffs/setInlineDiffViewType');
- component = createComponentWithStore(Vue.extend(InlineDiffView), store, {
- diffFile,
- diffLines: diffFile.highlighted_diff_lines,
- }).$mount();
-
- Vue.nextTick(done);
- });
+ wrapper = mount(InlineDiffView, {
+ store,
+ propsData: {
+ diffFile,
+ diffLines: diffLines.map(mapInline(mockDiffContent)),
+ },
+ });
+ };
describe('template', () => {
it('should have rendered diff lines', () => {
- const el = component.$el;
+ const diffFile = getDiffFileMock();
+ setup(diffFile, diffFile.highlighted_diff_lines);
- expect(el.querySelectorAll('tr.line_holder').length).toEqual(8);
- expect(el.querySelectorAll('tr.line_holder.new').length).toEqual(4);
- expect(el.querySelectorAll('tr.line_expansion.match').length).toEqual(1);
- expect(el.textContent.indexOf('Bad dates')).toBeGreaterThan(-1);
+ expect(wrapper.findAll('tr.line_holder').length).toEqual(8);
+ expect(wrapper.findAll('tr.line_holder.new').length).toEqual(4);
+ expect(wrapper.findAll('tr.line_expansion.match').length).toEqual(1);
+ getByText(wrapper.element, /Bad dates/i);
});
- it('should render discussions', done => {
- const el = component.$el;
- component.diffLines[1].discussions = getDiscussionsMockData();
- component.diffLines[1].discussionsExpanded = true;
-
- Vue.nextTick(() => {
- expect(el.querySelectorAll('.notes_holder').length).toEqual(1);
- expect(el.querySelectorAll('.notes_holder .note').length).toEqual(notesLength + 1);
- expect(el.innerText.indexOf('comment 5')).toBeGreaterThan(-1);
- component.$store.dispatch('setInitialNotes', []);
+ it('should render discussions', () => {
+ const diffFile = getDiffFileMock();
+ diffFile.highlighted_diff_lines[1].discussions = getDiscussionsMockData();
+ diffFile.highlighted_diff_lines[1].discussionsExpanded = true;
+ setup(diffFile, diffFile.highlighted_diff_lines);
- done();
- });
+ expect(wrapper.findAll('.notes_holder').length).toEqual(1);
+ expect(wrapper.findAll('.notes_holder .note').length).toEqual(notesLength + 1);
+ getByText(wrapper.element, 'comment 5');
+ wrapper.vm.$store.dispatch('setInitialNotes', []);
});
});
});
diff --git a/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js b/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js
deleted file mode 100644
index 38112445e8d..00000000000
--- a/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Vue from 'vue';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import { createStore } from '~/mr_notes/stores';
-import ParallelDiffExpansionRow from '~/diffs/components/parallel_diff_expansion_row.vue';
-import diffFileMockData from '../mock_data/diff_file';
-
-describe('ParallelDiffExpansionRow', () => {
- const matchLine = diffFileMockData.highlighted_diff_lines[5];
-
- const createComponent = (options = {}) => {
- const cmp = Vue.extend(ParallelDiffExpansionRow);
- const defaults = {
- fileHash: diffFileMockData.file_hash,
- contextLinesPath: 'contextLinesPath',
- line: matchLine,
- isTop: false,
- isBottom: false,
- };
- const props = { ...defaults, ...options };
-
- return createComponentWithStore(cmp, createStore(), props).$mount();
- };
-
- describe('template', () => {
- it('should render expansion row for match lines', () => {
- const vm = createComponent();
-
- expect(vm.$el.classList.contains('line_expansion')).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/diffs/components/parallel_diff_table_row_spec.js b/spec/frontend/diffs/components/parallel_diff_table_row_spec.js
index 13031bd8b66..57eff177261 100644
--- a/spec/frontend/diffs/components/parallel_diff_table_row_spec.js
+++ b/spec/frontend/diffs/components/parallel_diff_table_row_spec.js
@@ -3,11 +3,22 @@ import { shallowMount } from '@vue/test-utils';
import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
import { createStore } from '~/mr_notes/stores';
import ParallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue';
+import { mapParallel } from '~/diffs/components/diff_row_utils';
import diffFileMockData from '../mock_data/diff_file';
import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
import discussionsMockData from '../mock_data/diff_discussions';
describe('ParallelDiffTableRow', () => {
+ const mockDiffContent = {
+ diffFile: diffFileMockData,
+ shouldRenderDraftRow: jest.fn(),
+ hasParallelDraftLeft: jest.fn(),
+ hasParallelDraftRight: jest.fn(),
+ draftForLine: jest.fn(),
+ };
+
+ const applyMap = mapParallel(mockDiffContent);
+
describe('when one side is empty', () => {
let wrapper;
let vm;
@@ -18,7 +29,7 @@ describe('ParallelDiffTableRow', () => {
wrapper = shallowMount(ParallelDiffTableRow, {
store: createStore(),
propsData: {
- line: thisLine,
+ line: applyMap(thisLine),
fileHash: diffFileMockData.file_hash,
filePath: diffFileMockData.file_path,
contextLinesPath: 'contextLinesPath',
@@ -67,7 +78,7 @@ describe('ParallelDiffTableRow', () => {
beforeEach(() => {
vm = createComponentWithStore(Vue.extend(ParallelDiffTableRow), createStore(), {
- line: thisLine,
+ line: applyMap(thisLine),
fileHash: diffFileMockData.file_hash,
filePath: diffFileMockData.file_path,
contextLinesPath: 'contextLinesPath',
@@ -243,7 +254,10 @@ describe('ParallelDiffTableRow', () => {
${{ ...thisLine, left: { type: 'old-nonewline', discussions: [] } }} | ${false}
${{ ...thisLine, left: { discussions: [{}] } }} | ${false}
`('visible is $expectation - line ($line)', async ({ line, expectation }) => {
- createComponent({ line }, store, { isLeftHover: true, isCommentButtonRendered: true });
+ createComponent({ line: applyMap(line) }, store, {
+ isLeftHover: true,
+ isCommentButtonRendered: true,
+ });
expect(findNoteButton().isVisible()).toBe(expectation);
});
@@ -320,7 +334,7 @@ describe('ParallelDiffTableRow', () => {
Object.assign(thisLine.left, lineProps);
Object.assign(thisLine.right, lineProps);
createComponent({
- line: { ...thisLine },
+ line: applyMap({ ...thisLine }),
});
});
@@ -357,7 +371,7 @@ describe('ParallelDiffTableRow', () => {
beforeEach(() => {
jest.spyOn(store, 'dispatch').mockImplementation();
- line = {
+ line = applyMap({
left: {
line_code: TEST_LINE_CODE,
type: 'new',
@@ -369,7 +383,7 @@ describe('ParallelDiffTableRow', () => {
rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
meta_data: null,
},
- };
+ });
});
describe('with showCommentButton', () => {
@@ -384,7 +398,7 @@ describe('ParallelDiffTableRow', () => {
it('does notrender if line has no discussions', () => {
line.left.discussions = [];
- createComponent({ line });
+ createComponent({ line: applyMap(line) });
expect(findAvatars().exists()).toEqual(false);
});
diff --git a/spec/frontend/diffs/components/tree_list_spec.js b/spec/frontend/diffs/components/tree_list_spec.js
index cc177a81d88..c89403e4869 100644
--- a/spec/frontend/diffs/components/tree_list_spec.js
+++ b/spec/frontend/diffs/components/tree_list_spec.js
@@ -91,12 +91,12 @@ describe('Diffs tree list component', () => {
expect(
getFileRows()
.at(0)
- .text(),
+ .html(),
).toContain('index.js');
expect(
getFileRows()
.at(1)
- .text(),
+ .html(),
).toContain('app');
});
@@ -138,7 +138,7 @@ describe('Diffs tree list component', () => {
wrapper.vm.$store.state.diffs.renderTreeList = false;
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find('.file-row').text()).toContain('index.js');
+ expect(wrapper.find('.file-row').html()).toContain('index.js');
});
});
});
diff --git a/spec/frontend/diffs/mock_data/diff_file.js b/spec/frontend/diffs/mock_data/diff_file.js
index d3886819a91..cef776c885a 100644
--- a/spec/frontend/diffs/mock_data/diff_file.js
+++ b/spec/frontend/diffs/mock_data/diff_file.js
@@ -27,6 +27,7 @@ export default {
name: 'text',
error: null,
automaticallyCollapsed: false,
+ manuallyCollapsed: null,
},
added_lines: 2,
removed_lines: 0,
diff --git a/spec/frontend/diffs/mock_data/diff_file_unreadable.js b/spec/frontend/diffs/mock_data/diff_file_unreadable.js
index f6cdca9950a..2a5d694e3b8 100644
--- a/spec/frontend/diffs/mock_data/diff_file_unreadable.js
+++ b/spec/frontend/diffs/mock_data/diff_file_unreadable.js
@@ -26,6 +26,7 @@ export default {
name: 'text',
error: null,
automaticallyCollapsed: false,
+ manuallyCollapsed: null,
},
added_lines: 0,
removed_lines: 0,
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index c3e4ee9c531..0af5ddd9764 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -27,7 +27,6 @@ import {
scrollToLineIfNeededInline,
scrollToLineIfNeededParallel,
loadCollapsedDiff,
- expandAllFiles,
toggleFileDiscussions,
saveDiffDiscussion,
setHighlightedRow,
@@ -42,7 +41,7 @@ import {
fetchFullDiff,
toggleFullDiff,
switchToFullDiffFromRenamedFile,
- setFileCollapsed,
+ setFileCollapsedByUser,
setExpandedDiffLines,
setSuggestPopoverDismissed,
changeCurrentCommit,
@@ -658,23 +657,6 @@ describe('DiffsStoreActions', () => {
});
});
- describe('expandAllFiles', () => {
- it('should change the collapsed prop from the diffFiles', done => {
- testAction(
- expandAllFiles,
- null,
- {},
- [
- {
- type: types.EXPAND_ALL_FILES,
- },
- ],
- [],
- done,
- );
- });
- });
-
describe('toggleFileDiscussions', () => {
it('should dispatch collapseDiscussion when all discussions are expanded', () => {
const getters = {
@@ -1167,7 +1149,11 @@ describe('DiffsStoreActions', () => {
file_hash: 'testhash',
alternate_viewer: { name: updatedViewerName },
};
- const updatedViewer = { name: updatedViewerName, automaticallyCollapsed: false };
+ const updatedViewer = {
+ name: updatedViewerName,
+ automaticallyCollapsed: false,
+ manuallyCollapsed: false,
+ };
const testData = [{ rich_text: 'test' }, { rich_text: 'file2' }];
let renamedFile;
let mock;
@@ -1216,13 +1202,18 @@ describe('DiffsStoreActions', () => {
});
});
- describe('setFileCollapsed', () => {
+ describe('setFileUserCollapsed', () => {
it('commits SET_FILE_COLLAPSED', done => {
testAction(
- setFileCollapsed,
+ setFileCollapsedByUser,
{ filePath: 'test', collapsed: true },
null,
- [{ type: types.SET_FILE_COLLAPSED, payload: { filePath: 'test', collapsed: true } }],
+ [
+ {
+ type: types.SET_FILE_COLLAPSED,
+ payload: { filePath: 'test', collapsed: true, trigger: 'manual' },
+ },
+ ],
[],
done,
);
diff --git a/spec/frontend/diffs/store/getters_spec.js b/spec/frontend/diffs/store/getters_spec.js
index 0083f1d8b44..7e936c561fc 100644
--- a/spec/frontend/diffs/store/getters_spec.js
+++ b/spec/frontend/diffs/store/getters_spec.js
@@ -49,23 +49,53 @@ describe('Diffs Module Getters', () => {
});
});
- describe('hasCollapsedFile', () => {
- it('returns true when all files are collapsed', () => {
- localState.diffFiles = [
- { viewer: { automaticallyCollapsed: true } },
- { viewer: { automaticallyCollapsed: true } },
- ];
+ describe('whichCollapsedTypes', () => {
+ const autoCollapsedFile = { viewer: { automaticallyCollapsed: true, manuallyCollapsed: null } };
+ const manuallyCollapsedFile = {
+ viewer: { automaticallyCollapsed: false, manuallyCollapsed: true },
+ };
+ const openFile = { viewer: { automaticallyCollapsed: false, manuallyCollapsed: false } };
+
+ it.each`
+ description | value | files
+ ${'all files are automatically collapsed'} | ${true} | ${[{ ...autoCollapsedFile }, { ...autoCollapsedFile }]}
+ ${'all files are manually collapsed'} | ${true} | ${[{ ...manuallyCollapsedFile }, { ...manuallyCollapsedFile }]}
+ ${'no files are collapsed in any way'} | ${false} | ${[{ ...openFile }, { ...openFile }]}
+ ${'some files are collapsed in either way'} | ${true} | ${[{ ...manuallyCollapsedFile }, { ...autoCollapsedFile }, { ...openFile }]}
+ `('`any` is $value when $description', ({ value, files }) => {
+ localState.diffFiles = files;
+
+ const getterResult = getters.whichCollapsedTypes(localState);
+
+ expect(getterResult.any).toEqual(value);
+ });
+
+ it.each`
+ description | value | files
+ ${'all files are automatically collapsed'} | ${true} | ${[{ ...autoCollapsedFile }, { ...autoCollapsedFile }]}
+ ${'all files are manually collapsed'} | ${false} | ${[{ ...manuallyCollapsedFile }, { ...manuallyCollapsedFile }]}
+ ${'no files are collapsed in any way'} | ${false} | ${[{ ...openFile }, { ...openFile }]}
+ ${'some files are collapsed in either way'} | ${true} | ${[{ ...manuallyCollapsedFile }, { ...autoCollapsedFile }, { ...openFile }]}
+ `('`automatic` is $value when $description', ({ value, files }) => {
+ localState.diffFiles = files;
- expect(getters.hasCollapsedFile(localState)).toEqual(true);
+ const getterResult = getters.whichCollapsedTypes(localState);
+
+ expect(getterResult.automatic).toEqual(value);
});
- it('returns true when at least one file is collapsed', () => {
- localState.diffFiles = [
- { viewer: { automaticallyCollapsed: false } },
- { viewer: { automaticallyCollapsed: true } },
- ];
+ it.each`
+ description | value | files
+ ${'all files are automatically collapsed'} | ${false} | ${[{ ...autoCollapsedFile }, { ...autoCollapsedFile }]}
+ ${'all files are manually collapsed'} | ${true} | ${[{ ...manuallyCollapsedFile }, { ...manuallyCollapsedFile }]}
+ ${'no files are collapsed in any way'} | ${false} | ${[{ ...openFile }, { ...openFile }]}
+ ${'some files are collapsed in either way'} | ${true} | ${[{ ...manuallyCollapsedFile }, { ...autoCollapsedFile }, { ...openFile }]}
+ `('`manual` is $value when $description', ({ value, files }) => {
+ localState.diffFiles = files;
+
+ const getterResult = getters.whichCollapsedTypes(localState);
- expect(getters.hasCollapsedFile(localState)).toEqual(true);
+ expect(getterResult.manual).toEqual(value);
});
});
diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js
index a84ad63c695..c0645faf89e 100644
--- a/spec/frontend/diffs/store/mutations_spec.js
+++ b/spec/frontend/diffs/store/mutations_spec.js
@@ -126,21 +126,6 @@ describe('DiffsStoreMutations', () => {
});
});
- describe('EXPAND_ALL_FILES', () => {
- it('should change the collapsed prop from diffFiles', () => {
- const diffFile = {
- viewer: {
- automaticallyCollapsed: true,
- },
- };
- const state = { expandAllFiles: true, diffFiles: [diffFile] };
-
- mutations[types.EXPAND_ALL_FILES](state);
-
- expect(state.diffFiles[0].viewer.automaticallyCollapsed).toEqual(false);
- });
- });
-
describe('ADD_CONTEXT_LINES', () => {
it('should call utils.addContextLines with proper params', () => {
const options = {
diff --git a/spec/frontend/diffs/store/utils_spec.js b/spec/frontend/diffs/store/utils_spec.js
index 39a482c85ae..866be0abd22 100644
--- a/spec/frontend/diffs/store/utils_spec.js
+++ b/spec/frontend/diffs/store/utils_spec.js
@@ -1221,5 +1221,26 @@ describe('DiffsStoreUtils', () => {
file.parallel_diff_lines,
);
});
+
+ /**
+ * What's going on here?
+ *
+ * The inline version of parallelizeDiffLines simply keeps the difflines
+ * in the same order they are received as opposed to shuffling them
+ * to be "side by side".
+ *
+ * This keeps the underlying data structure the same which simplifies
+ * the components, but keeps the changes grouped together as users
+ * expect when viewing changes inline.
+ */
+ it('converts inline diff lines to inline diff lines with a parallel structure', () => {
+ const file = getDiffFileMock();
+ const files = utils.parallelizeDiffLines(file.highlighted_diff_lines, true);
+
+ expect(files[5].left).toEqual(file.parallel_diff_lines[5].left);
+ expect(files[5].right).toBeNull();
+ expect(files[6].left).toBeNull();
+ expect(files[6].right).toEqual(file.parallel_diff_lines[5].right);
+ });
});
});