diff options
Diffstat (limited to 'spec/javascripts/diffs/store')
-rw-r--r-- | spec/javascripts/diffs/store/actions_spec.js | 489 | ||||
-rw-r--r-- | spec/javascripts/diffs/store/getters_spec.js | 105 | ||||
-rw-r--r-- | spec/javascripts/diffs/store/mutations_spec.js | 244 | ||||
-rw-r--r-- | spec/javascripts/diffs/store/utils_spec.js | 327 |
4 files changed, 1088 insertions, 77 deletions
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js index c1560dac1a0..85c1926fcb1 100644 --- a/spec/javascripts/diffs/store/actions_spec.js +++ b/spec/javascripts/diffs/store/actions_spec.js @@ -5,19 +5,59 @@ import { INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE, } from '~/diffs/constants'; -import * as actions from '~/diffs/store/actions'; +import actions, { + setBaseConfig, + fetchDiffFiles, + assignDiscussionsToDiff, + removeDiscussionsFromDiff, + startRenderDiffsQueue, + setInlineDiffViewType, + setParallelDiffViewType, + showCommentForm, + cancelCommentForm, + loadMoreLines, + scrollToLineIfNeededInline, + scrollToLineIfNeededParallel, + loadCollapsedDiff, + expandAllFiles, + toggleFileDiscussions, + saveDiffDiscussion, + toggleTreeOpen, + scrollToFile, + toggleShowTreeList, +} from '~/diffs/store/actions'; import * as types from '~/diffs/store/mutation_types'; +import { reduceDiscussionsToLineCodes } from '~/notes/stores/utils'; import axios from '~/lib/utils/axios_utils'; import testAction from '../../helpers/vuex_action_helper'; describe('DiffsStoreActions', () => { + const originalMethods = { + requestAnimationFrame: global.requestAnimationFrame, + requestIdleCallback: global.requestIdleCallback, + }; + + beforeEach(() => { + ['requestAnimationFrame', 'requestIdleCallback'].forEach(method => { + global[method] = cb => { + cb(); + }; + }); + }); + + afterEach(() => { + ['requestAnimationFrame', 'requestIdleCallback'].forEach(method => { + global[method] = originalMethods[method]; + }); + }); + describe('setBaseConfig', () => { it('should set given endpoint and project path', done => { const endpoint = '/diffs/set/endpoint'; const projectPath = '/root/project'; testAction( - actions.setBaseConfig, + setBaseConfig, { endpoint, projectPath }, { endpoint: '', projectPath: '' }, [{ type: types.SET_BASE_CONFIG, payload: { endpoint, projectPath } }], @@ -35,7 +75,7 @@ describe('DiffsStoreActions', () => { mock.onGet(endpoint).reply(200, res); testAction( - actions.fetchDiffFiles, + fetchDiffFiles, {}, { endpoint }, [ @@ -53,10 +93,193 @@ describe('DiffsStoreActions', () => { }); }); + describe('assignDiscussionsToDiff', () => { + it('should merge discussions into diffs', done => { + const state = { + diffFiles: [ + { + fileHash: 'ABC', + parallelDiffLines: [ + { + left: { + lineCode: 'ABC_1_1', + discussions: [], + }, + right: { + lineCode: 'ABC_1_1', + discussions: [], + }, + }, + ], + highlightedDiffLines: [ + { + lineCode: 'ABC_1_1', + discussions: [], + oldLine: 5, + newLine: null, + }, + ], + diffRefs: { + baseSha: 'abc', + headSha: 'def', + startSha: 'ghi', + }, + newPath: 'file1', + oldPath: 'file2', + }, + ], + }; + + const diffPosition = { + baseSha: 'abc', + headSha: 'def', + startSha: 'ghi', + newLine: null, + newPath: 'file1', + oldLine: 5, + oldPath: 'file2', + }; + + const singleDiscussion = { + line_code: 'ABC_1_1', + diff_discussion: {}, + diff_file: { + file_hash: 'ABC', + }, + fileHash: 'ABC', + resolvable: true, + position: diffPosition, + original_position: diffPosition, + }; + + const discussions = reduceDiscussionsToLineCodes([singleDiscussion]); + + testAction( + assignDiscussionsToDiff, + discussions, + state, + [ + { + type: types.SET_LINE_DISCUSSIONS_FOR_FILE, + payload: { + fileHash: 'ABC', + discussions: [singleDiscussion], + diffPositionByLineCode: { + ABC_1_1: { + baseSha: 'abc', + headSha: 'def', + startSha: 'ghi', + newLine: null, + newPath: 'file1', + oldLine: 5, + oldPath: 'file2', + lineCode: 'ABC_1_1', + positionType: 'text', + }, + }, + }, + }, + ], + [], + () => { + done(); + }, + ); + }); + }); + + describe('removeDiscussionsFromDiff', () => { + it('should remove discussions from diffs', done => { + const state = { + diffFiles: [ + { + fileHash: 'ABC', + parallelDiffLines: [ + { + left: { + lineCode: 'ABC_1_1', + discussions: [ + { + id: 1, + }, + ], + }, + right: { + lineCode: 'ABC_1_1', + discussions: [], + }, + }, + ], + highlightedDiffLines: [ + { + lineCode: 'ABC_1_1', + discussions: [], + }, + ], + }, + ], + }; + const singleDiscussion = { + fileHash: 'ABC', + line_code: 'ABC_1_1', + }; + + testAction( + removeDiscussionsFromDiff, + singleDiscussion, + state, + [ + { + type: types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, + payload: { + fileHash: 'ABC', + lineCode: 'ABC_1_1', + }, + }, + ], + [], + () => { + done(); + }, + ); + }); + }); + + describe('startRenderDiffsQueue', () => { + it('should set all files to RENDER_FILE', () => { + const state = { + diffFiles: [ + { + id: 1, + renderIt: false, + collapsed: false, + }, + { + id: 2, + renderIt: false, + collapsed: false, + }, + ], + }; + + const pseudoCommit = (commitType, file) => { + expect(commitType).toBe(types.RENDER_FILE); + Object.assign(file, { + renderIt: true, + }); + }; + + startRenderDiffsQueue({ state, commit: pseudoCommit }); + + expect(state.diffFiles[0].renderIt).toBe(true); + expect(state.diffFiles[1].renderIt).toBe(true); + }); + }); + describe('setInlineDiffViewType', () => { it('should set diff view type to inline and also set the cookie properly', done => { testAction( - actions.setInlineDiffViewType, + setInlineDiffViewType, null, {}, [{ type: types.SET_DIFF_VIEW_TYPE, payload: INLINE_DIFF_VIEW_TYPE }], @@ -74,7 +297,7 @@ describe('DiffsStoreActions', () => { describe('setParallelDiffViewType', () => { it('should set diff view type to parallel and also set the cookie properly', done => { testAction( - actions.setParallelDiffViewType, + setParallelDiffViewType, null, {}, [{ type: types.SET_DIFF_VIEW_TYPE, payload: PARALLEL_DIFF_VIEW_TYPE }], @@ -94,7 +317,7 @@ describe('DiffsStoreActions', () => { const payload = { lineCode: 'lineCode' }; testAction( - actions.showCommentForm, + showCommentForm, payload, {}, [{ type: types.ADD_COMMENT_FORM_LINE, payload }], @@ -109,7 +332,7 @@ describe('DiffsStoreActions', () => { const payload = { lineCode: 'lineCode' }; testAction( - actions.cancelCommentForm, + cancelCommentForm, payload, {}, [{ type: types.REMOVE_COMMENT_FORM_LINE, payload }], @@ -131,7 +354,7 @@ describe('DiffsStoreActions', () => { mock.onGet(endpoint).reply(200, contextLines); testAction( - actions.loadMoreLines, + loadMoreLines, options, {}, [ @@ -157,7 +380,7 @@ describe('DiffsStoreActions', () => { mock.onGet(file.loadCollapsedDiffUrl).reply(200, data); testAction( - actions.loadCollapsedDiff, + loadCollapsedDiff, file, {}, [ @@ -178,7 +401,7 @@ describe('DiffsStoreActions', () => { describe('expandAllFiles', () => { it('should change the collapsed prop from the diffFiles', done => { testAction( - actions.expandAllFiles, + expandAllFiles, null, {}, [ @@ -202,9 +425,13 @@ describe('DiffsStoreActions', () => { const dispatch = jasmine.createSpy('dispatch'); - actions.toggleFileDiscussions({ getters, dispatch }); + toggleFileDiscussions({ getters, dispatch }); - expect(dispatch).toHaveBeenCalledWith('collapseDiscussion', { discussionId: 1 }, { root: true }); + expect(dispatch).toHaveBeenCalledWith( + 'collapseDiscussion', + { discussionId: 1 }, + { root: true }, + ); }); it('should dispatch expandDiscussion when all discussions are collapsed', () => { @@ -216,9 +443,13 @@ describe('DiffsStoreActions', () => { const dispatch = jasmine.createSpy(); - actions.toggleFileDiscussions({ getters, dispatch }); + toggleFileDiscussions({ getters, dispatch }); - expect(dispatch).toHaveBeenCalledWith('expandDiscussion', { discussionId: 1 }, { root: true }); + expect(dispatch).toHaveBeenCalledWith( + 'expandDiscussion', + { discussionId: 1 }, + { root: true }, + ); }); it('should dispatch expandDiscussion when some discussions are collapsed and others are expanded for the collapsed discussion', () => { @@ -230,9 +461,235 @@ describe('DiffsStoreActions', () => { const dispatch = jasmine.createSpy(); - actions.toggleFileDiscussions({ getters, dispatch }); + toggleFileDiscussions({ getters, dispatch }); + + expect(dispatch).toHaveBeenCalledWith( + 'expandDiscussion', + { discussionId: 1 }, + { root: true }, + ); + }); + }); + + describe('scrollToLineIfNeededInline', () => { + const lineMock = { + lineCode: 'ABC_123', + }; + + it('should not call handleLocationHash when there is not hash', () => { + window.location.hash = ''; + + const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); + + scrollToLineIfNeededInline({}, lineMock); + + expect(handleLocationHashSpy).not.toHaveBeenCalled(); + }); + + it('should not call handleLocationHash when the hash does not match any line', () => { + window.location.hash = 'XYZ_456'; + + const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); + + scrollToLineIfNeededInline({}, lineMock); + + expect(handleLocationHashSpy).not.toHaveBeenCalled(); + }); + + it('should call handleLocationHash only when the hash matches a line', () => { + window.location.hash = 'ABC_123'; + + const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); + + scrollToLineIfNeededInline( + {}, + { + lineCode: 'ABC_456', + }, + ); + scrollToLineIfNeededInline({}, lineMock); + scrollToLineIfNeededInline( + {}, + { + lineCode: 'XYZ_456', + }, + ); + + expect(handleLocationHashSpy).toHaveBeenCalled(); + expect(handleLocationHashSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe('scrollToLineIfNeededParallel', () => { + const lineMock = { + left: null, + right: { + lineCode: 'ABC_123', + }, + }; + + it('should not call handleLocationHash when there is not hash', () => { + window.location.hash = ''; + + const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); + + scrollToLineIfNeededParallel({}, lineMock); + + expect(handleLocationHashSpy).not.toHaveBeenCalled(); + }); + + it('should not call handleLocationHash when the hash does not match any line', () => { + window.location.hash = 'XYZ_456'; + + const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); + + scrollToLineIfNeededParallel({}, lineMock); + + expect(handleLocationHashSpy).not.toHaveBeenCalled(); + }); + + it('should call handleLocationHash only when the hash matches a line', () => { + window.location.hash = 'ABC_123'; + + const handleLocationHashSpy = spyOnDependency(actions, 'handleLocationHash').and.stub(); + + scrollToLineIfNeededParallel( + {}, + { + left: null, + right: { + lineCode: 'ABC_456', + }, + }, + ); + scrollToLineIfNeededParallel({}, lineMock); + scrollToLineIfNeededParallel( + {}, + { + left: null, + right: { + lineCode: 'XYZ_456', + }, + }, + ); + + expect(handleLocationHashSpy).toHaveBeenCalled(); + expect(handleLocationHashSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe('saveDiffDiscussion', () => { + beforeEach(() => { + spyOnDependency(actions, 'getNoteFormData').and.returnValue('testData'); + spyOnDependency(actions, 'reduceDiscussionsToLineCodes').and.returnValue('discussions'); + }); + + it('dispatches actions', done => { + const dispatch = jasmine.createSpy('dispatch').and.callFake(name => { + switch (name) { + case 'saveNote': + return Promise.resolve({ + discussion: 'test', + }); + case 'updateDiscussion': + return Promise.resolve('discussion'); + default: + return Promise.resolve({}); + } + }); + + saveDiffDiscussion({ dispatch }, { note: {}, formData: {} }) + .then(() => { + expect(dispatch.calls.argsFor(0)).toEqual(['saveNote', 'testData', { root: true }]); + expect(dispatch.calls.argsFor(1)).toEqual(['updateDiscussion', 'test', { root: true }]); + expect(dispatch.calls.argsFor(2)).toEqual(['assignDiscussionsToDiff', 'discussions']); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('toggleTreeOpen', () => { + it('commits TOGGLE_FOLDER_OPEN', done => { + testAction( + toggleTreeOpen, + 'path', + {}, + [{ type: types.TOGGLE_FOLDER_OPEN, payload: 'path' }], + [], + done, + ); + }); + }); + + describe('scrollToFile', () => { + let commit; + + beforeEach(() => { + commit = jasmine.createSpy(); + jasmine.clock().install(); + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); + + it('updates location hash', () => { + const state = { + treeEntries: { + path: { + fileHash: 'test', + }, + }, + }; + + scrollToFile({ state, commit }, 'path'); + + expect(document.location.hash).toBe('#test'); + }); + + it('commits UPDATE_CURRENT_DIFF_FILE_ID', () => { + const state = { + treeEntries: { + path: { + fileHash: 'test', + }, + }, + }; + + scrollToFile({ state, commit }, 'path'); + + expect(commit).toHaveBeenCalledWith(types.UPDATE_CURRENT_DIFF_FILE_ID, 'test'); + }); + + it('resets currentDiffId after timeout', () => { + const state = { + treeEntries: { + path: { + fileHash: 'test', + }, + }, + }; + + scrollToFile({ state, commit }, 'path'); + + jasmine.clock().tick(1000); + + expect(commit.calls.argsFor(1)).toEqual([types.UPDATE_CURRENT_DIFF_FILE_ID, '']); + }); + }); + + describe('toggleShowTreeList', () => { + it('commits toggle', done => { + testAction(toggleShowTreeList, null, {}, [{ type: types.TOGGLE_SHOW_TREE_LIST }], [], done); + }); + + it('updates localStorage', () => { + spyOn(localStorage, 'setItem'); + + toggleShowTreeList({ commit() {}, state: { showTreeList: true } }); - expect(dispatch).toHaveBeenCalledWith('expandDiscussion', { discussionId: 1 }, { root: true }); + expect(localStorage.setItem).toHaveBeenCalledWith('mr_tree_show', true); }); }); }); diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js index a59b26b2634..cfeaaec6980 100644 --- a/spec/javascripts/diffs/store/getters_spec.js +++ b/spec/javascripts/diffs/store/getters_spec.js @@ -184,101 +184,73 @@ describe('Diffs Module Getters', () => { }); }); - describe('singleDiscussionByLineCode', () => { - it('returns found discussion per line Code', () => { - const discussionsMock = {}; - discussionsMock.ABC = discussionMock; - - expect( - getters.singleDiscussionByLineCode(localState, {}, null, { - discussionsByLineCode: () => discussionsMock, - })('DEF'), - ).toEqual([]); - }); - - it('returns empty array when no discussions match', () => { - expect( - getters.singleDiscussionByLineCode(localState, {}, null, { - discussionsByLineCode: () => {}, - })('DEF'), - ).toEqual([]); - }); - }); - describe('shouldRenderParallelCommentRow', () => { let line; beforeEach(() => { line = {}; + discussionMock.expanded = true; + line.left = { lineCode: 'ABC', + discussions: [discussionMock], }; line.right = { lineCode: 'DEF', + discussions: [discussionMock1], }; }); it('returns true when discussion is expanded', () => { - discussionMock.expanded = true; - - expect( - getters.shouldRenderParallelCommentRow(localState, { - singleDiscussionByLineCode: () => [discussionMock], - })(line), - ).toEqual(true); + expect(getters.shouldRenderParallelCommentRow(localState)(line)).toEqual(true); }); it('returns false when no discussion was found', () => { + line.left.discussions = []; + line.right.discussions = []; + localState.diffLineCommentForms.ABC = false; localState.diffLineCommentForms.DEF = false; - expect( - getters.shouldRenderParallelCommentRow(localState, { - singleDiscussionByLineCode: () => [], - })(line), - ).toEqual(false); + expect(getters.shouldRenderParallelCommentRow(localState)(line)).toEqual(false); }); it('returns true when discussionForm was found', () => { localState.diffLineCommentForms.ABC = {}; - expect( - getters.shouldRenderParallelCommentRow(localState, { - singleDiscussionByLineCode: () => [discussionMock], - })(line), - ).toEqual(true); + expect(getters.shouldRenderParallelCommentRow(localState)(line)).toEqual(true); }); }); describe('shouldRenderInlineCommentRow', () => { + let line; + + beforeEach(() => { + discussionMock.expanded = true; + + line = { + lineCode: 'ABC', + discussions: [discussionMock], + }; + }); + it('returns true when diffLineCommentForms has form', () => { localState.diffLineCommentForms.ABC = {}; - expect( - getters.shouldRenderInlineCommentRow(localState)({ - lineCode: 'ABC', - }), - ).toEqual(true); + expect(getters.shouldRenderInlineCommentRow(localState)(line)).toEqual(true); }); it('returns false when no line discussions were found', () => { - expect( - getters.shouldRenderInlineCommentRow(localState, { - singleDiscussionByLineCode: () => [], - })('DEF'), - ).toEqual(false); + line.discussions = []; + expect(getters.shouldRenderInlineCommentRow(localState)(line)).toEqual(false); }); it('returns true if all found discussions are expanded', () => { discussionMock.expanded = true; - expect( - getters.shouldRenderInlineCommentRow(localState, { - singleDiscussionByLineCode: () => [discussionMock], - })('ABC'), - ).toEqual(true); + expect(getters.shouldRenderInlineCommentRow(localState)(line)).toEqual(true); }); }); @@ -319,4 +291,31 @@ describe('Diffs Module Getters', () => { expect(getters.getDiffFileByHash(localState)('123')).toBeUndefined(); }); }); + + describe('allBlobs', () => { + it('returns an array of blobs', () => { + localState.treeEntries = { + file: { + type: 'blob', + }, + tree: { + type: 'tree', + }, + }; + + expect(getters.allBlobs(localState)).toEqual([ + { + type: 'blob', + }, + ]); + }); + }); + + describe('diffFilesLength', () => { + it('returns length of diff files', () => { + localState.diffFiles.push('test', 'test 2'); + + expect(getters.diffFilesLength(localState)).toBe(2); + }); + }); }); diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js index 8f89984c6e5..0b712055956 100644 --- a/spec/javascripts/diffs/store/mutations_spec.js +++ b/spec/javascripts/diffs/store/mutations_spec.js @@ -1,3 +1,4 @@ +import createState from '~/diffs/store/modules/diff_state'; import mutations from '~/diffs/store/mutations'; import * as types from '~/diffs/store/mutation_types'; import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; @@ -138,10 +139,9 @@ describe('DiffsStoreMutations', () => { const fileHash = 123; const state = { diffFiles: [{}, { fileHash, existingField: 0 }] }; - const file = { fileHash }; const data = { diff_files: [{ file_hash: fileHash, extra_field: 1, existingField: 1 }] }; - mutations[types.ADD_COLLAPSED_DIFFS](state, { file, data }); + mutations[types.ADD_COLLAPSED_DIFFS](state, { file: state.diffFiles[1], data }); expect(spy).toHaveBeenCalledWith(data, { deep: true }); expect(state.diffFiles[1].fileHash).toEqual(fileHash); @@ -149,4 +149,244 @@ describe('DiffsStoreMutations', () => { expect(state.diffFiles[1].extraField).toEqual(1); }); }); + + describe('SET_LINE_DISCUSSIONS_FOR_FILE', () => { + it('should add discussions to the given line', () => { + const diffPosition = { + baseSha: 'ed13df29948c41ba367caa757ab3ec4892509910', + headSha: 'b921914f9a834ac47e6fd9420f78db0f83559130', + newLine: null, + newPath: '500-lines-4.txt', + oldLine: 5, + oldPath: '500-lines-4.txt', + startSha: 'ed13df29948c41ba367caa757ab3ec4892509910', + }; + + const state = { + latestDiff: true, + diffFiles: [ + { + fileHash: 'ABC', + parallelDiffLines: [ + { + left: { + lineCode: 'ABC_1', + discussions: [], + }, + right: { + lineCode: 'ABC_1', + discussions: [], + }, + }, + ], + highlightedDiffLines: [ + { + lineCode: 'ABC_1', + discussions: [], + }, + ], + }, + ], + }; + const discussions = [ + { + id: 1, + line_code: 'ABC_1', + diff_discussion: true, + resolvable: true, + original_position: diffPosition, + position: diffPosition, + }, + { + id: 2, + line_code: 'ABC_1', + diff_discussion: true, + resolvable: true, + original_position: diffPosition, + position: diffPosition, + }, + ]; + + const diffPositionByLineCode = { + ABC_1: diffPosition, + }; + + mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { + fileHash: 'ABC', + discussions, + diffPositionByLineCode, + }); + + expect(state.diffFiles[0].parallelDiffLines[0].left.discussions.length).toEqual(2); + expect(state.diffFiles[0].parallelDiffLines[0].left.discussions[1].id).toEqual(2); + + expect(state.diffFiles[0].highlightedDiffLines[0].discussions.length).toEqual(2); + expect(state.diffFiles[0].highlightedDiffLines[0].discussions[1].id).toEqual(2); + }); + + it('should add legacy discussions to the given line', () => { + const diffPosition = { + baseSha: 'ed13df29948c41ba367caa757ab3ec4892509910', + headSha: 'b921914f9a834ac47e6fd9420f78db0f83559130', + newLine: null, + newPath: '500-lines-4.txt', + oldLine: 5, + oldPath: '500-lines-4.txt', + startSha: 'ed13df29948c41ba367caa757ab3ec4892509910', + lineCode: 'ABC_1', + }; + + const state = { + latestDiff: true, + diffFiles: [ + { + fileHash: 'ABC', + parallelDiffLines: [ + { + left: { + lineCode: 'ABC_1', + discussions: [], + }, + right: { + lineCode: 'ABC_1', + discussions: [], + }, + }, + ], + highlightedDiffLines: [ + { + lineCode: 'ABC_1', + discussions: [], + }, + ], + }, + ], + }; + const discussions = [ + { + id: 1, + line_code: 'ABC_1', + diff_discussion: true, + active: true, + }, + { + id: 2, + line_code: 'ABC_1', + diff_discussion: true, + active: true, + }, + ]; + + const diffPositionByLineCode = { + ABC_1: diffPosition, + }; + + mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { + fileHash: 'ABC', + discussions, + diffPositionByLineCode, + }); + + expect(state.diffFiles[0].parallelDiffLines[0].left.discussions.length).toEqual(2); + expect(state.diffFiles[0].parallelDiffLines[0].left.discussions[1].id).toEqual(2); + + expect(state.diffFiles[0].highlightedDiffLines[0].discussions.length).toEqual(2); + expect(state.diffFiles[0].highlightedDiffLines[0].discussions[1].id).toEqual(2); + }); + }); + + describe('REMOVE_LINE_DISCUSSIONS', () => { + it('should remove the existing discussions on the given line', () => { + const state = { + diffFiles: [ + { + fileHash: 'ABC', + parallelDiffLines: [ + { + left: { + lineCode: 'ABC_1', + discussions: [ + { + id: 1, + line_code: 'ABC_1', + }, + { + id: 2, + line_code: 'ABC_1', + }, + ], + }, + right: { + lineCode: 'ABC_1', + discussions: [], + }, + }, + ], + highlightedDiffLines: [ + { + lineCode: 'ABC_1', + discussions: [ + { + id: 1, + line_code: 'ABC_1', + }, + { + id: 2, + line_code: 'ABC_1', + }, + ], + }, + ], + }, + ], + }; + + mutations[types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { + fileHash: 'ABC', + lineCode: 'ABC_1', + }); + expect(state.diffFiles[0].parallelDiffLines[0].left.discussions.length).toEqual(0); + expect(state.diffFiles[0].highlightedDiffLines[0].discussions.length).toEqual(0); + }); + }); + + describe('TOGGLE_FOLDER_OPEN', () => { + it('toggles entry opened prop', () => { + const state = { + treeEntries: { + path: { + opened: false, + }, + }, + }; + + mutations[types.TOGGLE_FOLDER_OPEN](state, 'path'); + + expect(state.treeEntries.path.opened).toBe(true); + }); + }); + + describe('TOGGLE_SHOW_TREE_LIST', () => { + it('toggles showTreeList', () => { + const state = createState(); + + mutations[types.TOGGLE_SHOW_TREE_LIST](state); + + expect(state.showTreeList).toBe(false, 'Failed to toggle showTreeList to false'); + + mutations[types.TOGGLE_SHOW_TREE_LIST](state); + + expect(state.showTreeList).toBe(true, 'Failed to toggle showTreeList to true'); + }); + }); + + describe('UPDATE_CURRENT_DIFF_FILE_ID', () => { + it('updates currentDiffFileId', () => { + const state = createState(); + + mutations[types.UPDATE_CURRENT_DIFF_FILE_ID](state, 'somefileid'); + + expect(state.currentDiffFileId).toBe('somefileid'); + }); + }); }); diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js index 32136d9ebff..257270a91ec 100644 --- a/spec/javascripts/diffs/store/utils_spec.js +++ b/spec/javascripts/diffs/store/utils_spec.js @@ -3,6 +3,7 @@ import { LINE_POSITION_LEFT, LINE_POSITION_RIGHT, TEXT_DIFF_POSITION_TYPE, + LEGACY_DIFF_NOTE_TYPE, DIFF_NOTE_TYPE, NEW_LINE_TYPE, OLD_LINE_TYPE, @@ -135,6 +136,7 @@ describe('DiffsStoreUtils', () => { note_project_id: '', target_type: options.noteableType, target_id: options.noteableData.id, + return_discussion: true, note: { noteable_type: options.noteableType, noteable_id: options.noteableData.id, @@ -151,6 +153,65 @@ describe('DiffsStoreUtils', () => { data: postData, }); }); + + it('should create legacy note form data', () => { + const diffFile = getDiffFileMock(); + delete diffFile.diffRefs.startSha; + delete diffFile.diffRefs.headSha; + + noteableDataMock.targetType = MERGE_REQUEST_NOTEABLE_TYPE; + + const options = { + note: 'Hello world!', + noteableData: noteableDataMock, + noteableType: MERGE_REQUEST_NOTEABLE_TYPE, + diffFile, + noteTargetLine: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + metaData: null, + newLine: 3, + oldLine: 1, + }, + diffViewType: PARALLEL_DIFF_VIEW_TYPE, + linePosition: LINE_POSITION_LEFT, + }; + + const position = JSON.stringify({ + base_sha: diffFile.diffRefs.baseSha, + start_sha: undefined, + head_sha: undefined, + old_path: diffFile.oldPath, + new_path: diffFile.newPath, + position_type: TEXT_DIFF_POSITION_TYPE, + old_line: options.noteTargetLine.oldLine, + new_line: options.noteTargetLine.newLine, + }); + + const postData = { + view: options.diffViewType, + line_type: options.linePosition === LINE_POSITION_RIGHT ? NEW_LINE_TYPE : OLD_LINE_TYPE, + merge_request_diff_head_sha: undefined, + in_reply_to_discussion_id: '', + note_project_id: '', + target_type: options.noteableType, + target_id: options.noteableData.id, + return_discussion: true, + note: { + noteable_type: options.noteableType, + noteable_id: options.noteableData.id, + commit_id: '', + type: LEGACY_DIFF_NOTE_TYPE, + line_code: options.noteTargetLine.lineCode, + note: options.note, + position, + }, + }; + + expect(utils.getNoteFormData(options)).toEqual({ + endpoint: options.noteableData.create_note_path, + data: postData, + }); + }); }); describe('addLineReferences', () => { @@ -179,32 +240,286 @@ describe('DiffsStoreUtils', () => { describe('trimFirstCharOfLineContent', () => { it('trims the line when it starts with a space', () => { - expect(utils.trimFirstCharOfLineContent({ richText: ' diff' })).toEqual({ richText: 'diff' }); + expect(utils.trimFirstCharOfLineContent({ richText: ' diff' })).toEqual({ + discussions: [], + richText: 'diff', + }); }); it('trims the line when it starts with a +', () => { - expect(utils.trimFirstCharOfLineContent({ richText: '+diff' })).toEqual({ richText: 'diff' }); + expect(utils.trimFirstCharOfLineContent({ richText: '+diff' })).toEqual({ + discussions: [], + richText: 'diff', + }); }); it('trims the line when it starts with a -', () => { - expect(utils.trimFirstCharOfLineContent({ richText: '-diff' })).toEqual({ richText: 'diff' }); + expect(utils.trimFirstCharOfLineContent({ richText: '-diff' })).toEqual({ + discussions: [], + richText: 'diff', + }); }); it('does not trims the line when it starts with a letter', () => { - expect(utils.trimFirstCharOfLineContent({ richText: 'diff' })).toEqual({ richText: 'diff' }); + expect(utils.trimFirstCharOfLineContent({ richText: 'diff' })).toEqual({ + discussions: [], + richText: 'diff', + }); }); it('does not modify the provided object', () => { const lineObj = { + discussions: [], richText: ' diff', }; utils.trimFirstCharOfLineContent(lineObj); - expect(lineObj).toEqual({ richText: ' diff' }); + expect(lineObj).toEqual({ discussions: [], richText: ' diff' }); }); it('handles a undefined or null parameter', () => { - expect(utils.trimFirstCharOfLineContent()).toEqual({}); + expect(utils.trimFirstCharOfLineContent()).toEqual({ discussions: [] }); + }); + }); + + describe('prepareDiffData', () => { + it('sets the renderIt and collapsed attribute on files', () => { + const preparedDiff = { diffFiles: [getDiffFileMock()] }; + utils.prepareDiffData(preparedDiff); + + const firstParallelDiffLine = preparedDiff.diffFiles[0].parallelDiffLines[2]; + expect(firstParallelDiffLine.left.discussions.length).toBe(0); + expect(firstParallelDiffLine.left).not.toHaveAttr('text'); + expect(firstParallelDiffLine.right.discussions.length).toBe(0); + expect(firstParallelDiffLine.right).not.toHaveAttr('text'); + const firstParallelChar = firstParallelDiffLine.right.richText.charAt(0); + expect(firstParallelChar).not.toBe(' '); + expect(firstParallelChar).not.toBe('+'); + expect(firstParallelChar).not.toBe('-'); + + const checkLine = preparedDiff.diffFiles[0].highlightedDiffLines[0]; + expect(checkLine.discussions.length).toBe(0); + expect(checkLine).not.toHaveAttr('text'); + const firstChar = checkLine.richText.charAt(0); + expect(firstChar).not.toBe(' '); + expect(firstChar).not.toBe('+'); + expect(firstChar).not.toBe('-'); + + expect(preparedDiff.diffFiles[0].renderIt).toBeTruthy(); + expect(preparedDiff.diffFiles[0].collapsed).toBeFalsy(); + }); + }); + + describe('isDiscussionApplicableToLine', () => { + const diffPosition = { + baseSha: 'ed13df29948c41ba367caa757ab3ec4892509910', + headSha: 'b921914f9a834ac47e6fd9420f78db0f83559130', + newLine: null, + newPath: '500-lines-4.txt', + oldLine: 5, + oldPath: '500-lines-4.txt', + startSha: 'ed13df29948c41ba367caa757ab3ec4892509910', + }; + + const wrongDiffPosition = { + baseSha: 'wrong', + headSha: 'wrong', + newLine: null, + newPath: '500-lines-4.txt', + oldLine: 5, + oldPath: '500-lines-4.txt', + startSha: 'wrong', + }; + + const discussions = { + upToDateDiscussion1: { + original_position: diffPosition, + position: wrongDiffPosition, + }, + outDatedDiscussion1: { + original_position: wrongDiffPosition, + position: wrongDiffPosition, + }, + }; + + it('returns true when the discussion is up to date', () => { + expect( + utils.isDiscussionApplicableToLine({ + discussion: discussions.upToDateDiscussion1, + diffPosition, + latestDiff: true, + }), + ).toBe(true); + }); + + it('returns false when the discussion is not up to date', () => { + expect( + utils.isDiscussionApplicableToLine({ + discussion: discussions.outDatedDiscussion1, + diffPosition, + latestDiff: true, + }), + ).toBe(false); + }); + + it('returns true when line codes match and discussion does not contain position and is not active', () => { + const discussion = { ...discussions.outDatedDiscussion1, line_code: 'ABC_1', active: false }; + delete discussion.original_position; + delete discussion.position; + + expect( + utils.isDiscussionApplicableToLine({ + discussion, + diffPosition: { + ...diffPosition, + lineCode: 'ABC_1', + }, + latestDiff: true, + }), + ).toBe(false); + }); + + it('returns true when line codes match and discussion does not contain position and is active', () => { + const discussion = { ...discussions.outDatedDiscussion1, line_code: 'ABC_1', active: true }; + delete discussion.original_position; + delete discussion.position; + + expect( + utils.isDiscussionApplicableToLine({ + discussion, + diffPosition: { + ...diffPosition, + lineCode: 'ABC_1', + }, + latestDiff: true, + }), + ).toBe(true); + }); + + it('returns false when not latest diff', () => { + const discussion = { ...discussions.outDatedDiscussion1, line_code: 'ABC_1', active: true }; + delete discussion.original_position; + delete discussion.position; + + expect( + utils.isDiscussionApplicableToLine({ + discussion, + diffPosition: { + ...diffPosition, + lineCode: 'ABC_1', + }, + latestDiff: false, + }), + ).toBe(false); + }); + }); + + describe('generateTreeList', () => { + let files; + + beforeAll(() => { + files = [ + { + newPath: 'app/index.js', + deletedFile: false, + newFile: false, + removedLines: 10, + addedLines: 0, + fileHash: 'test', + }, + { + newPath: 'app/test/index.js', + deletedFile: false, + newFile: true, + removedLines: 0, + addedLines: 0, + fileHash: 'test', + }, + { + newPath: 'package.json', + deletedFile: true, + newFile: false, + removedLines: 0, + addedLines: 0, + fileHash: 'test', + }, + ]; + }); + + it('creates a tree of files', () => { + const { tree } = utils.generateTreeList(files); + + expect(tree).toEqual([ + { + key: 'app', + path: 'app', + name: 'app', + type: 'tree', + tree: [ + { + addedLines: 0, + changed: true, + deleted: false, + fileHash: 'test', + key: 'app/index.js', + name: 'index.js', + path: 'app/index.js', + removedLines: 10, + tempFile: false, + type: 'blob', + tree: [], + }, + { + key: 'app/test', + path: 'app/test', + name: 'test', + type: 'tree', + opened: true, + tree: [ + { + addedLines: 0, + changed: true, + deleted: false, + fileHash: 'test', + key: 'app/test/index.js', + name: 'index.js', + path: 'app/test/index.js', + removedLines: 0, + tempFile: true, + type: 'blob', + tree: [], + }, + ], + }, + ], + opened: true, + }, + { + key: 'package.json', + path: 'package.json', + name: 'package.json', + type: 'blob', + changed: true, + tempFile: false, + deleted: true, + fileHash: 'test', + addedLines: 0, + removedLines: 0, + tree: [], + }, + ]); + }); + + it('creates flat list of blobs & folders', () => { + const { treeEntries } = utils.generateTreeList(files); + + expect(Object.keys(treeEntries)).toEqual([ + 'app', + 'app/index.js', + 'app/test', + 'app/test/index.js', + 'package.json', + ]); }); }); }); |