summaryrefslogtreecommitdiff
path: root/spec/frontend/diffs/components/inline_diff_table_row_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/diffs/components/inline_diff_table_row_spec.js')
-rw-r--r--spec/frontend/diffs/components/inline_diff_table_row_spec.js355
1 files changed, 279 insertions, 76 deletions
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 f929f97b598..951b3f6258b 100644
--- a/spec/frontend/diffs/components/inline_diff_table_row_spec.js
+++ b/spec/frontend/diffs/components/inline_diff_table_row_spec.js
@@ -1,114 +1,317 @@
import { shallowMount } from '@vue/test-utils';
+import { TEST_HOST } from 'helpers/test_constants';
import { createStore } from '~/mr_notes/stores';
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';
+
+const TEST_USER_ID = 'abc123';
+const TEST_USER = { id: TEST_USER_ID };
describe('InlineDiffTableRow', () => {
let wrapper;
- let vm;
+ let store;
const thisLine = diffFileMockData.highlighted_diff_lines[0];
- beforeEach(() => {
+ const createComponent = (props = {}, propsStore = store) => {
wrapper = shallowMount(InlineDiffTableRow, {
- store: createStore(),
+ store: propsStore,
propsData: {
line: thisLine,
fileHash: diffFileMockData.file_hash,
filePath: diffFileMockData.file_path,
contextLinesPath: 'contextLinesPath',
isHighlighted: false,
+ ...props,
},
});
- vm = wrapper.vm;
+ };
+
+ const setWindowLocation = value => {
+ Object.defineProperty(window, 'location', {
+ writable: true,
+ value,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ store.state.notes.userData = TEST_USER;
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
});
- it('does not add hll class to line content when line does not match highlighted row', done => {
- vm.$nextTick()
- .then(() => {
- expect(wrapper.find('.line_content').classes('hll')).toBe(false);
- })
- .then(done)
- .catch(done.fail);
+ it('does not add hll class to line content when line does not match highlighted row', () => {
+ createComponent();
+ expect(wrapper.find('.line_content').classes('hll')).toBe(false);
});
- it('adds hll class to lineContent when line is the highlighted row', done => {
- vm.$nextTick()
- .then(() => {
- vm.$store.state.diffs.highlightedRow = thisLine.line_code;
-
- return vm.$nextTick();
- })
- .then(() => {
- expect(wrapper.find('.line_content').classes('hll')).toBe(true);
- })
- .then(done)
- .catch(done.fail);
+ it('adds hll class to lineContent when line is the highlighted row', () => {
+ store.state.diffs.highlightedRow = thisLine.line_code;
+ createComponent({}, store);
+ expect(wrapper.find('.line_content').classes('hll')).toBe(true);
});
it('adds hll class to lineContent when line is part of a multiline comment', () => {
- wrapper.setProps({ isCommented: true });
- return vm.$nextTick().then(() => {
- expect(wrapper.find('.line_content').classes('hll')).toBe(true);
- });
+ createComponent({ isCommented: true });
+ expect(wrapper.find('.line_content').classes('hll')).toBe(true);
});
describe('sets coverage title and class', () => {
- it('for lines with coverage', done => {
- vm.$nextTick()
- .then(() => {
- const name = diffFileMockData.file_path;
- const line = thisLine.new_line;
-
- vm.$store.state.diffs.coverageFiles = { files: { [name]: { [line]: 5 } } };
-
- return vm.$nextTick();
- })
- .then(() => {
- const coverage = wrapper.find('.line-coverage');
-
- expect(coverage.attributes('title')).toContain('Test coverage: 5 hits');
- expect(coverage.classes('coverage')).toBe(true);
- })
- .then(done)
- .catch(done.fail);
+ it('for lines with coverage', () => {
+ const name = diffFileMockData.file_path;
+ const line = thisLine.new_line;
+
+ store.state.diffs.coverageFiles = { files: { [name]: { [line]: 5 } } };
+ createComponent({}, store);
+ const coverage = wrapper.find('.line-coverage');
+
+ expect(coverage.attributes('title')).toContain('Test coverage: 5 hits');
+ expect(coverage.classes('coverage')).toBe(true);
+ });
+
+ it('for lines without coverage', () => {
+ const name = diffFileMockData.file_path;
+ const line = thisLine.new_line;
+
+ store.state.diffs.coverageFiles = { files: { [name]: { [line]: 0 } } };
+ createComponent({}, store);
+ const coverage = wrapper.find('.line-coverage');
+
+ expect(coverage.attributes('title')).toContain('No test coverage');
+ expect(coverage.classes('no-coverage')).toBe(true);
+ });
+
+ it('for unknown lines', () => {
+ store.state.diffs.coverageFiles = {};
+ createComponent({}, store);
+
+ const coverage = wrapper.find('.line-coverage');
+
+ expect(coverage.attributes('title')).toBeUndefined();
+ expect(coverage.classes('coverage')).toBe(false);
+ expect(coverage.classes('no-coverage')).toBe(false);
+ });
+ });
+
+ describe('Table Cells', () => {
+ const findNewTd = () => wrapper.find({ ref: 'newTd' });
+ const findOldTd = () => wrapper.find({ ref: 'oldTd' });
+
+ describe('td', () => {
+ it('highlights when isHighlighted true', () => {
+ store.state.diffs.highlightedRow = thisLine.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: 'addDiffNoteButton' });
+
+ it.each`
+ userData | query | mergeRefHeadComments | expectation
+ ${TEST_USER} | ${'diff_head=false'} | ${false} | ${true}
+ ${TEST_USER} | ${'diff_head=true'} | ${true} | ${true}
+ ${TEST_USER} | ${'diff_head=true'} | ${false} | ${false}
+ ${null} | ${''} | ${true} | ${false}
+ `(
+ 'exists is $expectation - with userData ($userData) query ($query)',
+ ({ userData, query, mergeRefHeadComments, expectation }) => {
+ store.state.notes.userData = userData;
+ gon.features = { mergeRefHeadComments };
+ setWindowLocation({ href: `${TEST_HOST}?${query}` });
+ createComponent({}, store);
+
+ expect(findNoteButton().exists()).toBe(expectation);
+ },
+ );
+
+ it.each`
+ isHover | line | expectation
+ ${true} | ${{ ...thisLine, discussions: [] }} | ${true}
+ ${false} | ${{ ...thisLine, discussions: [] }} | ${false}
+ ${true} | ${{ ...thisLine, type: 'context', discussions: [] }} | ${false}
+ ${true} | ${{ ...thisLine, type: 'old-nonewline', discussions: [] }} | ${false}
+ ${true} | ${{ ...thisLine, discussions: [{}] }} | ${false}
+ `('visible is $expectation - line ($line)', ({ isHover, line, expectation }) => {
+ createComponent({ line });
+ wrapper.setData({ isHover });
+
+ return wrapper.vm.$nextTick().then(() => {
+ 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 }) => {
+ createComponent({
+ line: { ...thisLine, commentsDisabled },
+ });
+
+ wrapper.setData({ isHover: true });
+
+ return wrapper.vm.$nextTick().then(() => {
+ 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: 'addNoteTooltip' });
+
+ 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 }) => {
+ createComponent({
+ line: { ...thisLine, commentsDisabled },
+ });
+
+ wrapper.setData({ isHover: true });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findTooltip().attributes('title')).toBe(tooltip);
+ });
+ },
+ );
});
- it('for lines without coverage', done => {
- vm.$nextTick()
- .then(() => {
- const name = diffFileMockData.file_path;
- const line = thisLine.new_line;
+ 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(false);
+ expect(findLineNumberNew().exists()).toBe(true);
+ });
- vm.$store.state.diffs.coverageFiles = { files: { [name]: { [line]: 0 } } };
+ describe('with lineNumber prop', () => {
+ const TEST_LINE_CODE = 'LC_42';
+ const TEST_LINE_NUMBER = 1;
- return vm.$nextTick();
- })
- .then(() => {
- const coverage = wrapper.find('.line-coverage');
+ 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}
+ ${{ line_code: undefined, left: { line_code: TEST_LINE_CODE }, old_line: TEST_LINE_NUMBER }} | ${findLineNumberOld} | ${'#'} | ${TEST_LINE_CODE}
+ ${{ line_code: undefined, right: { line_code: TEST_LINE_CODE }, new_line: TEST_LINE_NUMBER }} | ${findLineNumberNew} | ${'#'} | ${TEST_LINE_CODE}
+ `(
+ 'with line ($lineProps)',
+ ({ lineProps, findLineNumber, expectedHref, expectedClickArg }) => {
+ beforeEach(() => {
+ jest.spyOn(store, 'dispatch').mockImplementation();
+ createComponent({
+ line: { ...thisLine, ...lineProps },
+ });
+ });
- expect(coverage.attributes('title')).toContain('No test coverage');
- expect(coverage.classes('no-coverage')).toBe(true);
- })
- .then(done)
- .catch(done.fail);
+ 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);
+ });
+ },
+ );
+ });
});
- it('for unknown lines', done => {
- vm.$nextTick()
- .then(() => {
- vm.$store.state.diffs.coverageFiles = {};
-
- return vm.$nextTick();
- })
- .then(() => {
- const coverage = wrapper.find('.line-coverage');
-
- expect(coverage.attributes('title')).toBeUndefined();
- expect(coverage.classes('coverage')).toBe(false);
- expect(coverage.classes('no-coverage')).toBe(false);
- })
- .then(done)
- .catch(done.fail);
+ 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 = {
+ 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.discussions,
+ discussionsExpanded: line.discussionsExpanded,
+ });
+ });
+
+ it('does notrender if line has no discussions', () => {
+ line.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.discussionsExpanded,
+ });
+ });
+ });
});
});
});