422 lines
14 KiB
JavaScript
422 lines
14 KiB
JavaScript
import { shallowMount } from '@vue/test-utils';
|
|
import Vue from 'vue';
|
|
import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
|
|
import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
|
|
import { mapParallel } from '~/diffs/components/diff_row_utils';
|
|
import ParallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue';
|
|
import { createStore } from '~/mr_notes/stores';
|
|
import discussionsMockData from '../mock_data/diff_discussions';
|
|
import diffFileMockData from '../mock_data/diff_file';
|
|
|
|
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;
|
|
const thisLine = diffFileMockData.parallel_diff_lines[0];
|
|
const rightLine = diffFileMockData.parallel_diff_lines[0].right;
|
|
|
|
beforeEach(() => {
|
|
wrapper = shallowMount(ParallelDiffTableRow, {
|
|
store: createStore(),
|
|
propsData: {
|
|
line: applyMap(thisLine),
|
|
fileHash: diffFileMockData.file_hash,
|
|
filePath: diffFileMockData.file_path,
|
|
contextLinesPath: 'contextLinesPath',
|
|
isHighlighted: false,
|
|
},
|
|
});
|
|
|
|
vm = wrapper.vm;
|
|
});
|
|
|
|
it('does not highlight non empty line content when line does not match highlighted row', (done) => {
|
|
vm.$nextTick()
|
|
.then(() => {
|
|
expect(vm.$el.querySelector('.line_content.right-side').classList).not.toContain('hll');
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('highlights nonempty line content when line is the highlighted row', (done) => {
|
|
vm.$nextTick()
|
|
.then(() => {
|
|
vm.$store.state.diffs.highlightedRow = rightLine.line_code;
|
|
|
|
return vm.$nextTick();
|
|
})
|
|
.then(() => {
|
|
expect(vm.$el.querySelector('.line_content.right-side').classList).toContain('hll');
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('highlights nonempty line content when line is part of a multiline comment', () => {
|
|
wrapper.setProps({ isCommented: true });
|
|
return vm.$nextTick().then(() => {
|
|
expect(vm.$el.querySelector('.line_content.right-side').classList).toContain('hll');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when both sides have content', () => {
|
|
let vm;
|
|
const thisLine = diffFileMockData.parallel_diff_lines[2];
|
|
const rightLine = diffFileMockData.parallel_diff_lines[2].right;
|
|
|
|
beforeEach(() => {
|
|
vm = createComponentWithStore(Vue.extend(ParallelDiffTableRow), createStore(), {
|
|
line: applyMap(thisLine),
|
|
fileHash: diffFileMockData.file_hash,
|
|
filePath: diffFileMockData.file_path,
|
|
contextLinesPath: 'contextLinesPath',
|
|
isHighlighted: false,
|
|
}).$mount();
|
|
});
|
|
|
|
it('does not highlight either line when line does not match highlighted row', (done) => {
|
|
vm.$nextTick()
|
|
.then(() => {
|
|
expect(vm.$el.querySelector('.line_content.right-side').classList).not.toContain('hll');
|
|
expect(vm.$el.querySelector('.line_content.left-side').classList).not.toContain('hll');
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('adds hll class to lineContent when line is the highlighted row', (done) => {
|
|
vm.$nextTick()
|
|
.then(() => {
|
|
vm.$store.state.diffs.highlightedRow = rightLine.line_code;
|
|
|
|
return vm.$nextTick();
|
|
})
|
|
.then(() => {
|
|
expect(vm.$el.querySelector('.line_content.right-side').classList).toContain('hll');
|
|
expect(vm.$el.querySelector('.line_content.left-side').classList).toContain('hll');
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
|
|
describe('sets coverage title and class', () => {
|
|
it('for lines with coverage', (done) => {
|
|
vm.$nextTick()
|
|
.then(() => {
|
|
const name = diffFileMockData.file_path;
|
|
const line = rightLine.new_line;
|
|
|
|
vm.$store.state.diffs.coverageFiles = { files: { [name]: { [line]: 5 } } };
|
|
|
|
return vm.$nextTick();
|
|
})
|
|
.then(() => {
|
|
const coverage = vm.$el.querySelector('.line-coverage.right-side');
|
|
|
|
expect(coverage.title).toContain('Test coverage: 5 hits');
|
|
expect(coverage.classList).toContain('coverage');
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('for lines without coverage', (done) => {
|
|
vm.$nextTick()
|
|
.then(() => {
|
|
const name = diffFileMockData.file_path;
|
|
const line = rightLine.new_line;
|
|
|
|
vm.$store.state.diffs.coverageFiles = { files: { [name]: { [line]: 0 } } };
|
|
|
|
return vm.$nextTick();
|
|
})
|
|
.then(() => {
|
|
const coverage = vm.$el.querySelector('.line-coverage.right-side');
|
|
|
|
expect(coverage.title).toContain('No test coverage');
|
|
expect(coverage.classList).toContain('no-coverage');
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('for unknown lines', (done) => {
|
|
vm.$nextTick()
|
|
.then(() => {
|
|
vm.$store.state.diffs.coverageFiles = {};
|
|
|
|
return vm.$nextTick();
|
|
})
|
|
.then(() => {
|
|
const coverage = vm.$el.querySelector('.line-coverage.right-side');
|
|
|
|
expect(coverage.title).not.toContain('Coverage');
|
|
expect(coverage.classList).not.toContain('coverage');
|
|
expect(coverage.classList).not.toContain('no-coverage');
|
|
})
|
|
.then(done)
|
|
.catch(done.fail);
|
|
});
|
|
});
|
|
});
|
|
|
|
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;
|
|
},
|
|
});
|
|
};
|
|
|
|
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 | expectation
|
|
${true} | ${{}} | ${TEST_USER} | ${true}
|
|
${true} | ${{ line: { left: null } }} | ${TEST_USER} | ${false}
|
|
${true} | ${{}} | ${null} | ${false}
|
|
${false} | ${{}} | ${TEST_USER} | ${false}
|
|
`(
|
|
'exists is $expectation - with userData ($userData)',
|
|
async ({ hover, line, userData, expectation }) => {
|
|
store.state.notes.userData = userData;
|
|
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: applyMap(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: applyMap({ ...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 = applyMap({
|
|
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: applyMap(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,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|