Resolve ""Jump to first/next unresolved discussion" jumps to resolved discussions"
This commit is contained in:
		
							parent
							
								
									9b01b293ce
								
							
						
					
					
						commit
						f2f8ddf4cc
					
				|  | @ -30,6 +30,7 @@ export default { | ||||||
|           :render-header="false" |           :render-header="false" | ||||||
|           :render-diff-file="false" |           :render-diff-file="false" | ||||||
|           :always-expanded="true" |           :always-expanded="true" | ||||||
|  |           :discussions-by-diff-order="true" | ||||||
|         /> |         /> | ||||||
|       </ul> |       </ul> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  | @ -5,19 +5,20 @@ import resolvedSvg from 'icons/_icon_status_success_solid.svg'; | ||||||
| import mrIssueSvg from 'icons/_icon_mr_issue.svg'; | import mrIssueSvg from 'icons/_icon_mr_issue.svg'; | ||||||
| import nextDiscussionSvg from 'icons/_next_discussion.svg'; | import nextDiscussionSvg from 'icons/_next_discussion.svg'; | ||||||
| import { pluralize } from '../../lib/utils/text_utility'; | import { pluralize } from '../../lib/utils/text_utility'; | ||||||
| import { scrollToElement } from '../../lib/utils/common_utils'; | import discussionNavigation from '../mixins/discussion_navigation'; | ||||||
| import tooltip from '../../vue_shared/directives/tooltip'; | import tooltip from '../../vue_shared/directives/tooltip'; | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
|   directives: { |   directives: { | ||||||
|     tooltip, |     tooltip, | ||||||
|   }, |   }, | ||||||
|  |   mixins: [discussionNavigation], | ||||||
|   computed: { |   computed: { | ||||||
|     ...mapGetters([ |     ...mapGetters([ | ||||||
|       'getUserData', |       'getUserData', | ||||||
|       'getNoteableData', |       'getNoteableData', | ||||||
|       'discussionCount', |       'discussionCount', | ||||||
|       'unresolvedDiscussions', |       'firstUnresolvedDiscussionId', | ||||||
|       'resolvedDiscussionCount', |       'resolvedDiscussionCount', | ||||||
|     ]), |     ]), | ||||||
|     isLoggedIn() { |     isLoggedIn() { | ||||||
|  | @ -35,11 +36,6 @@ export default { | ||||||
|     resolveAllDiscussionsIssuePath() { |     resolveAllDiscussionsIssuePath() { | ||||||
|       return this.getNoteableData.create_issue_to_resolve_discussions_path; |       return this.getNoteableData.create_issue_to_resolve_discussions_path; | ||||||
|     }, |     }, | ||||||
|     firstUnresolvedDiscussionId() { |  | ||||||
|       const item = this.unresolvedDiscussions[0] || {}; |  | ||||||
| 
 |  | ||||||
|       return item.id; |  | ||||||
|     }, |  | ||||||
|   }, |   }, | ||||||
|   created() { |   created() { | ||||||
|     this.resolveSvg = resolveSvg; |     this.resolveSvg = resolveSvg; | ||||||
|  | @ -50,22 +46,10 @@ export default { | ||||||
|   methods: { |   methods: { | ||||||
|     ...mapActions(['expandDiscussion']), |     ...mapActions(['expandDiscussion']), | ||||||
|     jumpToFirstUnresolvedDiscussion() { |     jumpToFirstUnresolvedDiscussion() { | ||||||
|       const discussionId = this.firstUnresolvedDiscussionId; |       const diffTab = window.mrTabs.currentAction === 'diffs'; | ||||||
|       if (!discussionId) { |       const discussionId = this.firstUnresolvedDiscussionId(diffTab); | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       const el = document.querySelector(`[data-discussion-id="${discussionId}"]`); |       this.jumpToDiscussion(discussionId); | ||||||
|       const activeTab = window.mrTabs.currentAction; |  | ||||||
| 
 |  | ||||||
|       if (activeTab === 'commits' || activeTab === 'pipelines') { |  | ||||||
|         window.mrTabs.activateTab('show'); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (el) { |  | ||||||
|         this.expandDiscussion({ discussionId }); |  | ||||||
|         scrollToElement(el); |  | ||||||
|       } |  | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1,9 +1,8 @@ | ||||||
| <script> | <script> | ||||||
| import _ from 'underscore'; |  | ||||||
| import { mapActions, mapGetters } from 'vuex'; | import { mapActions, mapGetters } from 'vuex'; | ||||||
| import resolveDiscussionsSvg from 'icons/_icon_mr_issue.svg'; | import resolveDiscussionsSvg from 'icons/_icon_mr_issue.svg'; | ||||||
| import nextDiscussionsSvg from 'icons/_next_discussion.svg'; | import nextDiscussionsSvg from 'icons/_next_discussion.svg'; | ||||||
| import { convertObjectPropsToCamelCase, scrollToElement } from '~/lib/utils/common_utils'; | import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||||
| import { truncateSha } from '~/lib/utils/text_utility'; | import { truncateSha } from '~/lib/utils/text_utility'; | ||||||
| import systemNote from '~/vue_shared/components/notes/system_note.vue'; | import systemNote from '~/vue_shared/components/notes/system_note.vue'; | ||||||
| import { s__ } from '~/locale'; | import { s__ } from '~/locale'; | ||||||
|  | @ -21,6 +20,7 @@ import placeholderSystemNote from '../../vue_shared/components/notes/placeholder | ||||||
| import autosave from '../mixins/autosave'; | import autosave from '../mixins/autosave'; | ||||||
| import noteable from '../mixins/noteable'; | import noteable from '../mixins/noteable'; | ||||||
| import resolvable from '../mixins/resolvable'; | import resolvable from '../mixins/resolvable'; | ||||||
|  | import discussionNavigation from '../mixins/discussion_navigation'; | ||||||
| import tooltip from '../../vue_shared/directives/tooltip'; | import tooltip from '../../vue_shared/directives/tooltip'; | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
|  | @ -40,7 +40,7 @@ export default { | ||||||
|   directives: { |   directives: { | ||||||
|     tooltip, |     tooltip, | ||||||
|   }, |   }, | ||||||
|   mixins: [autosave, noteable, resolvable], |   mixins: [autosave, noteable, resolvable, discussionNavigation], | ||||||
|   props: { |   props: { | ||||||
|     discussion: { |     discussion: { | ||||||
|       type: Object, |       type: Object, | ||||||
|  | @ -61,6 +61,11 @@ export default { | ||||||
|       required: false, |       required: false, | ||||||
|       default: false, |       default: false, | ||||||
|     }, |     }, | ||||||
|  |     discussionsByDiffOrder: { | ||||||
|  |       type: Boolean, | ||||||
|  |       required: false, | ||||||
|  |       default: false, | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|  | @ -75,7 +80,12 @@ export default { | ||||||
|       'discussionCount', |       'discussionCount', | ||||||
|       'resolvedDiscussionCount', |       'resolvedDiscussionCount', | ||||||
|       'allDiscussions', |       'allDiscussions', | ||||||
|  |       'unresolvedDiscussionsIdsByDiff', | ||||||
|  |       'unresolvedDiscussionsIdsByDate', | ||||||
|       'unresolvedDiscussions', |       'unresolvedDiscussions', | ||||||
|  |       'unresolvedDiscussionsIdsOrdered', | ||||||
|  |       'nextUnresolvedDiscussionId', | ||||||
|  |       'isLastUnresolvedDiscussion', | ||||||
|     ]), |     ]), | ||||||
|     transformedDiscussion() { |     transformedDiscussion() { | ||||||
|       return { |       return { | ||||||
|  | @ -126,6 +136,10 @@ export default { | ||||||
|     hasMultipleUnresolvedDiscussions() { |     hasMultipleUnresolvedDiscussions() { | ||||||
|       return this.unresolvedDiscussions.length > 1; |       return this.unresolvedDiscussions.length > 1; | ||||||
|     }, |     }, | ||||||
|  |     showJumpToNextDiscussion() { | ||||||
|  |       return this.hasMultipleUnresolvedDiscussions && | ||||||
|  |         !this.isLastUnresolvedDiscussion(this.discussion.id, this.discussionsByDiffOrder); | ||||||
|  |     }, | ||||||
|     shouldRenderDiffs() { |     shouldRenderDiffs() { | ||||||
|       const { diffDiscussion, diffFile } = this.transformedDiscussion; |       const { diffDiscussion, diffFile } = this.transformedDiscussion; | ||||||
| 
 | 
 | ||||||
|  | @ -242,21 +256,10 @@ Please check your network connection and try again.`; | ||||||
|         }); |         }); | ||||||
|     }, |     }, | ||||||
|     jumpToNextDiscussion() { |     jumpToNextDiscussion() { | ||||||
|       const discussionIds = this.allDiscussions.map(d => d.id); |       const nextId = | ||||||
|       const unresolvedIds = this.unresolvedDiscussions.map(d => d.id); |         this.nextUnresolvedDiscussionId(this.discussion.id, this.discussionsByDiffOrder); | ||||||
|       const currentIndex = discussionIds.indexOf(this.discussion.id); |  | ||||||
|       const remainingAfterCurrent = discussionIds.slice(currentIndex + 1); |  | ||||||
|       const nextIndex = _.findIndex(remainingAfterCurrent, id => unresolvedIds.indexOf(id) > -1); |  | ||||||
| 
 | 
 | ||||||
|       if (nextIndex > -1) { |       this.jumpToDiscussion(nextId); | ||||||
|         const nextId = remainingAfterCurrent[nextIndex]; |  | ||||||
|         const el = document.querySelector(`[data-discussion-id="${nextId}"]`); |  | ||||||
| 
 |  | ||||||
|         if (el) { |  | ||||||
|           this.expandDiscussion({ discussionId: nextId }); |  | ||||||
|           scrollToElement(el); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|  | @ -398,7 +401,7 @@ Please check your network connection and try again.`; | ||||||
|                           </a> |                           </a> | ||||||
|                         </div> |                         </div> | ||||||
|                         <div |                         <div | ||||||
|                           v-if="hasMultipleUnresolvedDiscussions" |                           v-if="showJumpToNextDiscussion" | ||||||
|                           class="btn-group" |                           class="btn-group" | ||||||
|                           role="group"> |                           role="group"> | ||||||
|                           <button |                           <button | ||||||
|  |  | ||||||
|  | @ -0,0 +1,29 @@ | ||||||
|  | import { scrollToElement } from '~/lib/utils/common_utils'; | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   methods: { | ||||||
|  |     jumpToDiscussion(id) { | ||||||
|  |       if (id) { | ||||||
|  |         const activeTab = window.mrTabs.currentAction; | ||||||
|  |         const selector = | ||||||
|  |           activeTab === 'diffs' | ||||||
|  |             ? `ul.notes[data-discussion-id="${id}"]` | ||||||
|  |             : `div.discussion[data-discussion-id="${id}"]`; | ||||||
|  |         const el = document.querySelector(selector); | ||||||
|  | 
 | ||||||
|  |         if (activeTab === 'commits' || activeTab === 'pipelines') { | ||||||
|  |           window.mrTabs.activateTab('show'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (el) { | ||||||
|  |           this.expandDiscussion({ discussionId: id }); | ||||||
|  | 
 | ||||||
|  |           scrollToElement(el); | ||||||
|  |           return true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       return false; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | @ -70,6 +70,9 @@ export const allDiscussions = (state, getters) => { | ||||||
|   return Object.values(resolved).concat(unresolved); |   return Object.values(resolved).concat(unresolved); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export const allResolvableDiscussions = (state, getters) => | ||||||
|  |   getters.allDiscussions.filter(d => !d.individual_note && d.resolvable); | ||||||
|  | 
 | ||||||
| export const resolvedDiscussionsById = state => { | export const resolvedDiscussionsById = state => { | ||||||
|   const map = {}; |   const map = {}; | ||||||
| 
 | 
 | ||||||
|  | @ -86,6 +89,51 @@ export const resolvedDiscussionsById = state => { | ||||||
|   return map; |   return map; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // Gets Discussions IDs ordered by the date of their initial note
 | ||||||
|  | export const unresolvedDiscussionsIdsByDate = (state, getters) => | ||||||
|  |   getters.allResolvableDiscussions | ||||||
|  |     .filter(d => !d.resolved) | ||||||
|  |     .sort((a, b) => { | ||||||
|  |       const aDate = new Date(a.notes[0].created_at); | ||||||
|  |       const bDate = new Date(b.notes[0].created_at); | ||||||
|  | 
 | ||||||
|  |       if (aDate < bDate) { | ||||||
|  |         return -1; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       return aDate === bDate ? 0 : 1; | ||||||
|  |     }) | ||||||
|  |     .map(d => d.id); | ||||||
|  | 
 | ||||||
|  | // Gets Discussions IDs ordered by their position in the diff
 | ||||||
|  | //
 | ||||||
|  | // Sorts the array of resolvable yet unresolved discussions by
 | ||||||
|  | // comparing file names first. If file names are the same, compares
 | ||||||
|  | // line numbers.
 | ||||||
|  | export const unresolvedDiscussionsIdsByDiff = (state, getters) => | ||||||
|  |   getters.allResolvableDiscussions | ||||||
|  |     .filter(d => !d.resolved) | ||||||
|  |     .sort((a, b) => { | ||||||
|  |       if (!a.diff_file || !b.diff_file) { | ||||||
|  |         return 0; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Get file names comparison result
 | ||||||
|  |       const filenameComparison = a.diff_file.file_path.localeCompare(b.diff_file.file_path); | ||||||
|  | 
 | ||||||
|  |       // Get the line numbers, to compare within the same file
 | ||||||
|  |       const aLines = [a.position.formatter.new_line, a.position.formatter.old_line]; | ||||||
|  |       const bLines = [b.position.formatter.new_line, b.position.formatter.old_line]; | ||||||
|  | 
 | ||||||
|  |       return filenameComparison < 0 || | ||||||
|  |         (filenameComparison === 0 && | ||||||
|  |           // .max() because one of them might be zero (if removed/added)
 | ||||||
|  |           Math.max(aLines[0], aLines[1]) < Math.max(bLines[0], bLines[1])) | ||||||
|  |         ? -1 | ||||||
|  |         : 1; | ||||||
|  |     }) | ||||||
|  |     .map(d => d.id); | ||||||
|  | 
 | ||||||
| export const resolvedDiscussionCount = (state, getters) => { | export const resolvedDiscussionCount = (state, getters) => { | ||||||
|   const resolvedMap = getters.resolvedDiscussionsById; |   const resolvedMap = getters.resolvedDiscussionsById; | ||||||
| 
 | 
 | ||||||
|  | @ -102,5 +150,42 @@ export const discussionTabCounter = state => { | ||||||
|   return all.length; |   return all.length; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // Returns the list of discussion IDs ordered according to given parameter
 | ||||||
|  | // @param {Boolean} diffOrder - is ordered by diff?
 | ||||||
|  | export const unresolvedDiscussionsIdsOrdered = (state, getters) => diffOrder => { | ||||||
|  |   if (diffOrder) { | ||||||
|  |     return getters.unresolvedDiscussionsIdsByDiff; | ||||||
|  |   } | ||||||
|  |   return getters.unresolvedDiscussionsIdsByDate; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Checks if a given discussion is the last in the current order (diff or date)
 | ||||||
|  | // @param {Boolean} discussionId - id of the discussion
 | ||||||
|  | // @param {Boolean} diffOrder - is ordered by diff?
 | ||||||
|  | export const isLastUnresolvedDiscussion = (state, getters) => (discussionId, diffOrder) => { | ||||||
|  |   const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder); | ||||||
|  |   const lastDiscussionId = idsOrdered[idsOrdered.length - 1]; | ||||||
|  | 
 | ||||||
|  |   return lastDiscussionId === discussionId; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Gets the ID of the discussion following the one provided, respecting order (diff or date)
 | ||||||
|  | // @param {Boolean} discussionId - id of the current discussion
 | ||||||
|  | // @param {Boolean} diffOrder - is ordered by diff?
 | ||||||
|  | export const nextUnresolvedDiscussionId = (state, getters) => (discussionId, diffOrder) => { | ||||||
|  |   const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder); | ||||||
|  |   const currentIndex = idsOrdered.indexOf(discussionId); | ||||||
|  | 
 | ||||||
|  |   return idsOrdered.slice(currentIndex + 1, currentIndex + 2)[0]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // @param {Boolean} diffOrder - is ordered by diff?
 | ||||||
|  | export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => { | ||||||
|  |   if (diffOrder) { | ||||||
|  |     return getters.unresolvedDiscussionsIdsByDiff[0]; | ||||||
|  |   } | ||||||
|  |   return getters.unresolvedDiscussionsIdsByDate[0]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| // prevent babel-plugin-rewire from generating an invalid default during karma tests
 | // prevent babel-plugin-rewire from generating an invalid default during karma tests
 | ||||||
| export default () => {}; | export default () => {}; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | --- | ||||||
|  | title: Fix navigation to First and Next discussion on MR Changes tab | ||||||
|  | merge_request: 20434 | ||||||
|  | author: | ||||||
|  | type: fixed | ||||||
|  | @ -342,8 +342,9 @@ describe 'Merge request > User resolves diff notes and discussions', :js do | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       it 'shows jump to next discussion button' do |       it 'shows jump to next discussion button, apart from the last one' do | ||||||
|         expect(page.all('.discussion-reply-holder', count: 2)).to all(have_selector('.discussion-next-btn')) |         expect(page).to have_selector('.discussion-reply-holder', count: 2) | ||||||
|  |         expect(page).to have_selector('.discussion-reply-holder .discussion-next-btn', count: 1) | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       it 'displays next discussion even if hidden' do |       it 'displays next discussion even if hidden' do | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ describe('DiscussionCounter component', () => { | ||||||
|           discussions, |           discussions, | ||||||
|         }); |         }); | ||||||
|         setFixtures(` |         setFixtures(` | ||||||
|           <div data-discussion-id="${firstDiscussionId}"></div> |           <div class="discussion" data-discussion-id="${firstDiscussionId}"></div> | ||||||
|         `);
 |         `);
 | ||||||
| 
 | 
 | ||||||
|         vm.jumpToFirstUnresolvedDiscussion(); |         vm.jumpToFirstUnresolvedDiscussion(); | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ describe('noteable_discussion component', () => { | ||||||
|   preloadFixtures(discussionWithTwoUnresolvedNotes); |   preloadFixtures(discussionWithTwoUnresolvedNotes); | ||||||
| 
 | 
 | ||||||
|   beforeEach(() => { |   beforeEach(() => { | ||||||
|  |     window.mrTabs = {}; | ||||||
|     store = createStore(); |     store = createStore(); | ||||||
|     store.dispatch('setNoteableData', noteableDataMock); |     store.dispatch('setNoteableData', noteableDataMock); | ||||||
|     store.dispatch('setNotesData', notesDataMock); |     store.dispatch('setNotesData', notesDataMock); | ||||||
|  | @ -106,33 +107,29 @@ describe('noteable_discussion component', () => { | ||||||
| 
 | 
 | ||||||
|   describe('methods', () => { |   describe('methods', () => { | ||||||
|     describe('jumpToNextDiscussion', () => { |     describe('jumpToNextDiscussion', () => { | ||||||
|       it('expands next unresolved discussion', () => { |       it('expands next unresolved discussion', done => { | ||||||
|  |         const discussion2 = getJSONFixture(discussionWithTwoUnresolvedNotes)[0]; | ||||||
|  |         discussion2.resolved = false; | ||||||
|  |         discussion2.id = 'next'; // prepare this for being identified as next one (to be jumped to)
 | ||||||
|  |         vm.$store.dispatch('setInitialNotes', [discussionMock, discussion2]); | ||||||
|  |         window.mrTabs.currentAction = 'show'; | ||||||
|  | 
 | ||||||
|  |         Vue.nextTick() | ||||||
|  |           .then(() => { | ||||||
|             spyOn(vm, 'expandDiscussion').and.stub(); |             spyOn(vm, 'expandDiscussion').and.stub(); | ||||||
|         const discussions = [ | 
 | ||||||
|           discussionMock, |             const nextDiscussionId = discussion2.id; | ||||||
|           { | 
 | ||||||
|             ...discussionMock, |  | ||||||
|             id: discussionMock.id + 1, |  | ||||||
|             notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: true }], |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             ...discussionMock, |  | ||||||
|             id: discussionMock.id + 2, |  | ||||||
|             notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: false }], |  | ||||||
|           }, |  | ||||||
|         ]; |  | ||||||
|         const nextDiscussionId = discussionMock.id + 2; |  | ||||||
|         store.replaceState({ |  | ||||||
|           ...store.state, |  | ||||||
|           discussions, |  | ||||||
|         }); |  | ||||||
|             setFixtures(` |             setFixtures(` | ||||||
|           <div data-discussion-id="${nextDiscussionId}"></div> |               <div class="discussion" data-discussion-id="${nextDiscussionId}"></div> | ||||||
|             `);
 |             `);
 | ||||||
| 
 | 
 | ||||||
|             vm.jumpToNextDiscussion(); |             vm.jumpToNextDiscussion(); | ||||||
| 
 | 
 | ||||||
|             expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: nextDiscussionId }); |             expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: nextDiscussionId }); | ||||||
|  |           }) | ||||||
|  |           .then(done) | ||||||
|  |           .catch(done.fail); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  | @ -1168,3 +1168,87 @@ export const collapsedSystemNotes = [ | ||||||
|     diff_discussion: false, |     diff_discussion: false, | ||||||
|   }, |   }, | ||||||
| ]; | ]; | ||||||
|  | 
 | ||||||
|  | export const discussion1 = { | ||||||
|  |   id: 'abc1', | ||||||
|  |   resolvable: true, | ||||||
|  |   resolved: false, | ||||||
|  |   diff_file: { | ||||||
|  |     file_path: 'about.md', | ||||||
|  |   }, | ||||||
|  |   position: { | ||||||
|  |     formatter: { | ||||||
|  |       new_line: 50, | ||||||
|  |       old_line: null, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   notes: [ | ||||||
|  |     { | ||||||
|  |       created_at: '2018-07-04T16:25:41.749Z', | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const resolvedDiscussion1 = { | ||||||
|  |   id: 'abc1', | ||||||
|  |   resolvable: true, | ||||||
|  |   resolved: true, | ||||||
|  |   diff_file: { | ||||||
|  |     file_path: 'about.md', | ||||||
|  |   }, | ||||||
|  |   position: { | ||||||
|  |     formatter: { | ||||||
|  |       new_line: 50, | ||||||
|  |       old_line: null, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   notes: [ | ||||||
|  |     { | ||||||
|  |       created_at: '2018-07-04T16:25:41.749Z', | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const discussion2 = { | ||||||
|  |   id: 'abc2', | ||||||
|  |   resolvable: true, | ||||||
|  |   resolved: false, | ||||||
|  |   diff_file: { | ||||||
|  |     file_path: 'README.md', | ||||||
|  |   }, | ||||||
|  |   position: { | ||||||
|  |     formatter: { | ||||||
|  |       new_line: null, | ||||||
|  |       old_line: 20, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   notes: [ | ||||||
|  |     { | ||||||
|  |       created_at: '2018-07-04T12:05:41.749Z', | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const discussion3 = { | ||||||
|  |   id: 'abc3', | ||||||
|  |   resolvable: true, | ||||||
|  |   resolved: false, | ||||||
|  |   diff_file: { | ||||||
|  |     file_path: 'README.md', | ||||||
|  |   }, | ||||||
|  |   position: { | ||||||
|  |     formatter: { | ||||||
|  |       new_line: 21, | ||||||
|  |       old_line: null, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   notes: [ | ||||||
|  |     { | ||||||
|  |       created_at: '2018-07-05T17:25:41.749Z', | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const unresolvableDiscussion = { | ||||||
|  |   resolvable: false, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @ -5,6 +5,11 @@ import { | ||||||
|   noteableDataMock, |   noteableDataMock, | ||||||
|   individualNote, |   individualNote, | ||||||
|   collapseNotesMock, |   collapseNotesMock, | ||||||
|  |   discussion1, | ||||||
|  |   discussion2, | ||||||
|  |   discussion3, | ||||||
|  |   resolvedDiscussion1, | ||||||
|  |   unresolvableDiscussion, | ||||||
| } from '../mock_data'; | } from '../mock_data'; | ||||||
| 
 | 
 | ||||||
| const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json'; | const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json'; | ||||||
|  | @ -109,4 +114,154 @@ describe('Getters Notes Store', () => { | ||||||
|       expect(getters.isNotesFetched(state)).toBeFalsy(); |       expect(getters.isNotesFetched(state)).toBeFalsy(); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   describe('allResolvableDiscussions', () => { | ||||||
|  |     it('should return only resolvable discussions in same order', () => { | ||||||
|  |       const localGetters = { | ||||||
|  |         allDiscussions: [ | ||||||
|  |           discussion3, | ||||||
|  |           unresolvableDiscussion, | ||||||
|  |           discussion1, | ||||||
|  |           unresolvableDiscussion, | ||||||
|  |           discussion2, | ||||||
|  |         ], | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       expect(getters.allResolvableDiscussions(state, localGetters)).toEqual([ | ||||||
|  |         discussion3, | ||||||
|  |         discussion1, | ||||||
|  |         discussion2, | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return empty array if there are no resolvable discussions', () => { | ||||||
|  |       const localGetters = { | ||||||
|  |         allDiscussions: [unresolvableDiscussion, unresolvableDiscussion], | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       expect(getters.allResolvableDiscussions(state, localGetters)).toEqual([]); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('unresolvedDiscussionsIdsByDiff', () => { | ||||||
|  |     it('should return all discussions IDs in diff order', () => { | ||||||
|  |       const localGetters = { | ||||||
|  |         allResolvableDiscussions: [discussion3, discussion1, discussion2], | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       expect(getters.unresolvedDiscussionsIdsByDiff(state, localGetters)).toEqual([ | ||||||
|  |         'abc1', | ||||||
|  |         'abc2', | ||||||
|  |         'abc3', | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return empty array if all discussions have been resolved', () => { | ||||||
|  |       const localGetters = { | ||||||
|  |         allResolvableDiscussions: [resolvedDiscussion1], | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       expect(getters.unresolvedDiscussionsIdsByDiff(state, localGetters)).toEqual([]); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('unresolvedDiscussionsIdsByDate', () => { | ||||||
|  |     it('should return all discussions in date ascending order', () => { | ||||||
|  |       const localGetters = { | ||||||
|  |         allResolvableDiscussions: [discussion3, discussion1, discussion2], | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       expect(getters.unresolvedDiscussionsIdsByDate(state, localGetters)).toEqual([ | ||||||
|  |         'abc2', | ||||||
|  |         'abc1', | ||||||
|  |         'abc3', | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return empty array if all discussions have been resolved', () => { | ||||||
|  |       const localGetters = { | ||||||
|  |         allResolvableDiscussions: [resolvedDiscussion1], | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       expect(getters.unresolvedDiscussionsIdsByDate(state, localGetters)).toEqual([]); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('unresolvedDiscussionsIdsOrdered', () => { | ||||||
|  |     const localGetters = { | ||||||
|  |       unresolvedDiscussionsIdsByDate: ['123', '456'], | ||||||
|  |       unresolvedDiscussionsIdsByDiff: ['abc', 'def'], | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     it('should return IDs ordered by diff when diffOrder param is true', () => { | ||||||
|  |       expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(true)).toEqual([ | ||||||
|  |         'abc', | ||||||
|  |         'def', | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return IDs ordered by date when diffOrder param is not true', () => { | ||||||
|  |       expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(false)).toEqual([ | ||||||
|  |         '123', | ||||||
|  |         '456', | ||||||
|  |       ]); | ||||||
|  |       expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(undefined)).toEqual([ | ||||||
|  |         '123', | ||||||
|  |         '456', | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('isLastUnresolvedDiscussion', () => { | ||||||
|  |     const localGetters = { | ||||||
|  |       unresolvedDiscussionsIdsOrdered: () => ['123', '456', '789'], | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     it('should return true if the discussion id provided is the last', () => { | ||||||
|  |       expect(getters.isLastUnresolvedDiscussion(state, localGetters)('789')).toBe(true); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return false if the discussion id provided is not the last', () => { | ||||||
|  |       expect(getters.isLastUnresolvedDiscussion(state, localGetters)('123')).toBe(false); | ||||||
|  |       expect(getters.isLastUnresolvedDiscussion(state, localGetters)('456')).toBe(false); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('nextUnresolvedDiscussionId', () => { | ||||||
|  |     const localGetters = { | ||||||
|  |       unresolvedDiscussionsIdsOrdered: () => ['123', '456', '789'], | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     it('should return the ID of the discussion after the ID provided', () => { | ||||||
|  |       expect(getters.nextUnresolvedDiscussionId(state, localGetters)('123')).toBe('456'); | ||||||
|  |       expect(getters.nextUnresolvedDiscussionId(state, localGetters)('456')).toBe('789'); | ||||||
|  |       expect(getters.nextUnresolvedDiscussionId(state, localGetters)('789')).toBe(undefined); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('firstUnresolvedDiscussionId', () => { | ||||||
|  |     const localGetters = { | ||||||
|  |       unresolvedDiscussionsIdsByDate: ['123', '456'], | ||||||
|  |       unresolvedDiscussionsIdsByDiff: ['abc', 'def'], | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     it('should return the first discussion id by diff when diffOrder param is true', () => { | ||||||
|  |       expect(getters.firstUnresolvedDiscussionId(state, localGetters)(true)).toBe('abc'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should return the first discussion id by date when diffOrder param is not true', () => { | ||||||
|  |       expect(getters.firstUnresolvedDiscussionId(state, localGetters)(false)).toBe('123'); | ||||||
|  |       expect(getters.firstUnresolvedDiscussionId(state, localGetters)(undefined)).toBe('123'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should be falsy if all discussions are resolved', () => { | ||||||
|  |       const localGettersFalsy = { | ||||||
|  |         unresolvedDiscussionsIdsByDiff: [], | ||||||
|  |         unresolvedDiscussionsIdsByDate: [], | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(true)).toBeFalsy(); | ||||||
|  |       expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(false)).toBeFalsy(); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue