diff options
Diffstat (limited to 'spec/frontend/diffs/components/parallel_diff_table_row_spec.js')
-rw-r--r-- | spec/frontend/diffs/components/parallel_diff_table_row_spec.js | 259 |
1 files changed, 259 insertions, 0 deletions
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 339352943a9..13c4ce06f18 100644 --- a/spec/frontend/diffs/components/parallel_diff_table_row_spec.js +++ b/spec/frontend/diffs/components/parallel_diff_table_row_spec.js @@ -1,9 +1,12 @@ import Vue from 'vue'; import { shallowMount } from '@vue/test-utils'; import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { TEST_HOST } from 'helpers/test_constants'; import { createStore } from '~/mr_notes/stores'; import ParallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue'; 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', () => { describe('when one side is empty', () => { @@ -158,4 +161,260 @@ describe('ParallelDiffTableRow', () => { }); }); }); + + describe('Table Cells', () => { + let wrapper; + let store; + let thisLine; + const TEST_USER_ID = 'abc123'; + const TEST_USER = { id: TEST_USER_ID }; + + const createComponent = (props = {}, propsStore = store, data = {}) => { + wrapper = shallowMount(ParallelDiffTableRow, { + store: propsStore, + propsData: { + line: thisLine, + fileHash: diffFileMockData.file_hash, + filePath: diffFileMockData.file_path, + contextLinesPath: 'contextLinesPath', + isHighlighted: false, + ...props, + }, + data() { + return data; + }, + }); + }; + + const setWindowLocation = value => { + Object.defineProperty(window, 'location', { + writable: true, + value, + }); + }; + + beforeEach(() => { + // eslint-disable-next-line prefer-destructuring + thisLine = diffFileMockData.parallel_diff_lines[2]; + store = createStore(); + store.state.notes.userData = TEST_USER; + }); + + afterEach(() => { + wrapper.destroy(); + }); + + const findNewTd = () => wrapper.find({ ref: 'newTd' }); + const findOldTd = () => wrapper.find({ ref: 'oldTd' }); + + describe('td', () => { + it('highlights when isHighlighted true', () => { + store.state.diffs.highlightedRow = thisLine.left.line_code; + createComponent({}, store); + + expect(findNewTd().classes()).toContain('hll'); + expect(findOldTd().classes()).toContain('hll'); + }); + + it('does not highlight when isHighlighted false', () => { + createComponent(); + + expect(findNewTd().classes()).not.toContain('hll'); + expect(findOldTd().classes()).not.toContain('hll'); + }); + }); + + describe('comment button', () => { + const findNoteButton = () => wrapper.find({ ref: 'addDiffNoteButtonLeft' }); + + it.each` + hover | line | userData | query | mergeRefHeadComments | expectation + ${true} | ${{}} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${true} + ${true} | ${{ line: { left: null } }} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${false} + ${true} | ${{}} | ${TEST_USER} | ${'diff_head=true'} | ${true} | ${true} + ${true} | ${{}} | ${TEST_USER} | ${'diff_head=true'} | ${false} | ${false} + ${true} | ${{}} | ${null} | ${''} | ${true} | ${false} + ${false} | ${{}} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${false} + `( + 'exists is $expectation - with userData ($userData) query ($query)', + async ({ hover, line, userData, query, mergeRefHeadComments, expectation }) => { + store.state.notes.userData = userData; + gon.features = { mergeRefHeadComments }; + setWindowLocation({ href: `${TEST_HOST}?${query}` }); + createComponent(line, store); + if (hover) await wrapper.find('.line_holder').trigger('mouseover'); + + expect(findNoteButton().exists()).toBe(expectation); + }, + ); + + it.each` + line | expectation + ${{ ...thisLine, left: { discussions: [] } }} | ${true} + ${{ ...thisLine, left: { type: 'context', discussions: [] } }} | ${false} + ${{ ...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 }); + + expect(findNoteButton().isVisible()).toBe(expectation); + }); + + it.each` + disabled | commentsDisabled + ${'disabled'} | ${true} + ${undefined} | ${false} + `( + 'has attribute disabled=$disabled when the outer component has prop commentsDisabled=$commentsDisabled', + ({ disabled, commentsDisabled }) => { + thisLine.left.commentsDisabled = commentsDisabled; + createComponent({ line: { ...thisLine } }, store, { + isLeftHover: true, + isCommentButtonRendered: true, + }); + + expect(findNoteButton().attributes('disabled')).toBe(disabled); + }, + ); + + const symlinkishFileTooltip = + 'Commenting on symbolic links that replace or are replaced by files is currently not supported.'; + const realishFileTooltip = + 'Commenting on files that replace or are replaced by symbolic links is currently not supported.'; + const otherFileTooltip = 'Add a comment to this line'; + const findTooltip = () => wrapper.find({ ref: 'addNoteTooltipLeft' }); + + it.each` + tooltip | commentsDisabled + ${symlinkishFileTooltip} | ${{ wasSymbolic: true }} + ${symlinkishFileTooltip} | ${{ isSymbolic: true }} + ${realishFileTooltip} | ${{ wasReal: true }} + ${realishFileTooltip} | ${{ isReal: true }} + ${otherFileTooltip} | ${false} + `( + 'has the correct tooltip when commentsDisabled=$commentsDisabled', + ({ tooltip, commentsDisabled }) => { + thisLine.left.commentsDisabled = commentsDisabled; + createComponent({ line: { ...thisLine } }, store, { + isLeftHover: true, + isCommentButtonRendered: true, + }); + + expect(findTooltip().attributes('title')).toBe(tooltip); + }, + ); + }); + + describe('line number', () => { + const findLineNumberOld = () => wrapper.find({ ref: 'lineNumberRefOld' }); + const findLineNumberNew = () => wrapper.find({ ref: 'lineNumberRefNew' }); + + it('renders line numbers in correct cells', () => { + createComponent(); + + expect(findLineNumberOld().exists()).toBe(true); + expect(findLineNumberNew().exists()).toBe(true); + }); + + describe('with lineNumber prop', () => { + const TEST_LINE_CODE = 'LC_42'; + const TEST_LINE_NUMBER = 1; + + describe.each` + lineProps | findLineNumber | expectedHref | expectedClickArg + ${{ line_code: TEST_LINE_CODE, old_line: TEST_LINE_NUMBER }} | ${findLineNumberOld} | ${`#${TEST_LINE_CODE}`} | ${TEST_LINE_CODE} + ${{ line_code: undefined, old_line: TEST_LINE_NUMBER }} | ${findLineNumberOld} | ${'#'} | ${undefined} + `( + 'with line ($lineProps)', + ({ lineProps, findLineNumber, expectedHref, expectedClickArg }) => { + beforeEach(() => { + jest.spyOn(store, 'dispatch').mockImplementation(); + Object.assign(thisLine.left, lineProps); + Object.assign(thisLine.right, lineProps); + createComponent({ + line: { ...thisLine }, + }); + }); + + it('renders', () => { + expect(findLineNumber().exists()).toBe(true); + expect(findLineNumber().attributes()).toEqual({ + href: expectedHref, + 'data-linenumber': TEST_LINE_NUMBER.toString(), + }); + }); + + it('on click, dispatches setHighlightedRow', () => { + expect(store.dispatch).toHaveBeenCalledTimes(1); + + findLineNumber().trigger('click'); + + expect(store.dispatch).toHaveBeenCalledWith( + 'diffs/setHighlightedRow', + expectedClickArg, + ); + expect(store.dispatch).toHaveBeenCalledTimes(2); + }); + }, + ); + }); + }); + + describe('diff-gutter-avatars', () => { + const TEST_LINE_CODE = 'LC_42'; + const TEST_FILE_HASH = diffFileMockData.file_hash; + const findAvatars = () => wrapper.find(DiffGutterAvatars); + let line; + + beforeEach(() => { + jest.spyOn(store, 'dispatch').mockImplementation(); + + line = { + left: { + line_code: TEST_LINE_CODE, + type: 'new', + old_line: null, + new_line: 1, + discussions: [{ ...discussionsMockData }], + discussionsExpanded: true, + text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + meta_data: null, + }, + }; + }); + + describe('with showCommentButton', () => { + it('renders if line has discussions', () => { + createComponent({ line }); + + expect(findAvatars().props()).toEqual({ + discussions: line.left.discussions, + discussionsExpanded: line.left.discussionsExpanded, + }); + }); + + it('does notrender if line has no discussions', () => { + line.left.discussions = []; + createComponent({ line }); + + expect(findAvatars().exists()).toEqual(false); + }); + + it('toggles line discussion', () => { + createComponent({ line }); + + expect(store.dispatch).toHaveBeenCalledTimes(1); + + findAvatars().vm.$emit('toggleLineDiscussions'); + + expect(store.dispatch).toHaveBeenCalledWith('diffs/toggleLineDiscussions', { + lineCode: TEST_LINE_CODE, + fileHash: TEST_FILE_HASH, + expanded: !line.left.discussionsExpanded, + }); + }); + }); + }); + }); }); |