Commit c300249c authored by Phil Hughes's avatar Phil Hughes

Merge branch 'jdb/drag-select-comment-v2' into 'master'

Implement click and drag (single action) multi-line comments

See merge request gitlab-org/gitlab!49875
parents 241536b3 78e9efaf
......@@ -56,10 +56,11 @@ export default {
},
computed: {
...mapState({
noteableData: (state) => state.notes.noteableData,
diffViewType: (state) => state.diffs.diffViewType,
diffViewType: ({ diffs }) => diffs.diffViewType,
showSuggestPopover: ({ diffs }) => diffs.showSuggestPopover,
noteableData: ({ notes }) => notes.noteableData,
selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition,
}),
...mapState('diffs', ['showSuggestPopover']),
...mapGetters('diffs', ['getDiffFileByHash', 'diffLines']),
...mapGetters([
'isLoggedIn',
......@@ -126,6 +127,10 @@ export default {
this.initAutoSave(this.noteableData, keys);
}
if (this.selectedCommentPosition) {
this.commentLineStart = this.selectedCommentPosition.start;
}
},
methods: {
...mapActions('diffs', [
......
......@@ -10,6 +10,7 @@ import {
CONFLICT_THEIR,
CONFLICT_MARKER,
} from '../constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
import * as utils from './diff_row_utils';
......@@ -22,6 +23,7 @@ export default {
GlTooltip: GlTooltipDirective,
SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
props: {
fileHash: {
type: String,
......@@ -45,6 +47,15 @@ export default {
required: false,
default: false,
},
index: {
type: Number,
required: true,
},
},
data() {
return {
dragging: false,
};
},
computed: {
...mapGetters('diffs', ['fileLineCoverage']),
......@@ -52,26 +63,35 @@ export default {
...mapState({
isHighlighted(state) {
const line = this.line.left?.line_code ? this.line.left : this.line.right;
return utils.isHighlighted(state, line, this.isCommented);
return utils.isHighlighted(state, line, false);
},
}),
classNameMap() {
return {
[CONTEXT_LINE_CLASS_NAME]: this.line.isContextLineLeft,
[PARALLEL_DIFF_VIEW_TYPE]: !this.inline,
commented: this.isCommented,
};
},
parallelViewLeftLineType() {
return utils.parallelViewLeftLineType(this.line, this.isHighlighted);
return utils.parallelViewLeftLineType(this.line, this.isHighlighted || this.isCommented);
},
coverageState() {
return this.fileLineCoverage(this.filePath, this.line.right.new_line);
},
classNameMapCellLeft() {
return utils.classNameMapCell(this.line.left, this.isHighlighted, this.isLoggedIn);
return utils.classNameMapCell({
line: this.line.left,
hll: this.isHighlighted || this.isCommented,
isLoggedIn: this.isLoggedIn,
});
},
classNameMapCellRight() {
return utils.classNameMapCell(this.line.right, this.isHighlighted, this.isLoggedIn);
return utils.classNameMapCell({
line: this.line.right,
hll: this.isHighlighted || this.isCommented,
isLoggedIn: this.isLoggedIn,
});
},
addCommentTooltipLeft() {
return utils.addCommentTooltip(this.line.left);
......@@ -131,6 +151,22 @@ export default {
? this.$options.THEIR_CHANGES
: this.$options.OUR_CHANGES;
},
onDragEnd() {
this.dragging = false;
if (!this.glFeatures.dragCommentSelection) return;
this.$emit('stopdragging');
},
onDragEnter(line, index) {
if (!this.glFeatures.dragCommentSelection) return;
this.$emit('enterdragging', { ...line, index });
},
onDragStart(line) {
this.$root.$emit('bv::hide::tooltip');
this.dragging = true;
this.$emit('startdragging', line);
},
},
OUR_CHANGES: 'HEAD//our changes',
THEIR_CHANGES: 'origin//their changes',
......@@ -143,7 +179,13 @@ export default {
<template>
<div :class="classNameMap" class="diff-grid-row diff-tr line_holder">
<div class="diff-grid-left left-side">
<div
data-testid="left-side"
class="diff-grid-left left-side"
@dragover.prevent
@dragenter="onDragEnter(line.left, index)"
@dragend="onDragEnd"
>
<template v-if="line.left && line.left.type !== $options.CONFLICT_MARKER">
<div
:class="classNameMapCellLeft"
......@@ -159,10 +201,13 @@ export default {
:title="addCommentTooltipLeft"
>
<button
:draggable="glFeatures.dragCommentSelection"
type="button"
class="add-diff-note note-button js-add-diff-note-button qa-diff-comment"
:class="{ 'gl-cursor-grab': dragging }"
:disabled="line.left.commentsDisabled"
@click="handleCommentButton(line.left)"
@dragstart="onDragStart({ ...line.left, index })"
>
<gl-icon :size="12" name="comment" />
</button>
......@@ -234,7 +279,14 @@ export default {
></div>
</template>
</div>
<div v-if="!inline" class="diff-grid-right right-side">
<div
v-if="!inline"
data-testid="right-side"
class="diff-grid-right right-side"
@dragover.prevent
@dragenter="onDragEnter(line.right, index)"
@dragend="onDragEnd"
>
<template v-if="line.right">
<div :class="classNameMapCellRight" class="diff-td diff-line-num new_line">
<template v-if="line.right.type !== $options.CONFLICT_MARKER_THEIR">
......@@ -246,10 +298,13 @@ export default {
:title="addCommentTooltipRight"
>
<button
:draggable="glFeatures.dragCommentSelection"
type="button"
class="add-diff-note note-button js-add-diff-note-button qa-diff-comment"
:class="{ 'gl-cursor-grab': dragging }"
:disabled="line.right.commentsDisabled"
@click="handleCommentButton(line.right)"
@dragstart="onDragStart({ ...line.right, index })"
>
<gl-icon :size="12" name="comment" />
</button>
......@@ -279,13 +334,21 @@ export default {
<div
v-gl-tooltip.hover
:title="coverageState.text"
:class="[line.right.type, coverageState.class, { hll: isHighlighted }]"
:class="[line.right.type, coverageState.class, { hll: isHighlighted, hll: isCommented }]"
class="diff-td line-coverage right-side"
></div>
<div
:id="line.right.line_code"
:key="line.right.rich_text"
:class="[line.right.type, { hll: isHighlighted, parallel: !inline }]"
v-safe-html="line.right.rich_text"
:class="[
line.right.type,
{
hll: isHighlighted,
hll: isCommented,
parallel: !inline,
},
]"
class="diff-td line_content with-coverage right-side"
@mousedown="handleParallelLineMouseDown"
>
......
......@@ -35,7 +35,7 @@ export const lineCode = (line) => {
return line.line_code || line.left?.line_code || line.right?.line_code;
};
export const classNameMapCell = (line, hll, isLoggedIn, isHover) => {
export const classNameMapCell = ({ line, hll, isLoggedIn, isHover }) => {
if (!line) return [];
const { type } = line;
......@@ -54,7 +54,9 @@ export const addCommentTooltip = (line) => {
let tooltip;
if (!line) return tooltip;
tooltip = __('Add a comment to this line');
tooltip = gon.drag_comment_selection
? __('Add a comment to this line or drag for multiple lines')
: __('Add a comment to this line');
const brokenSymlinks = line.commentsDisabled;
if (brokenSymlinks) {
......
<script>
import { mapGetters, mapState } from 'vuex';
import { mapGetters, mapState, mapActions } from 'vuex';
import draftCommentsMixin from '~/diffs/mixins/draft_comments';
import DraftNote from '~/batch_comments/components/draft_note.vue';
import DiffRow from './diff_row.vue';
......@@ -35,6 +35,12 @@ export default {
default: false,
},
},
data() {
return {
dragStart: null,
updatedLineRange: null,
};
},
computed: {
...mapGetters('diffs', ['commitId']),
...mapState({
......@@ -52,12 +58,39 @@ export default {
},
},
methods: {
...mapActions(['setSelectedCommentPosition']),
...mapActions('diffs', ['showCommentForm']),
showCommentLeft(line) {
return !this.inline || line.left;
},
showCommentRight(line) {
return !this.inline || (line.right && !line.left);
},
onStartDragging(line) {
this.dragStart = line;
},
onDragOver(line) {
if (line.chunk !== this.dragStart.chunk) return;
let start = this.dragStart;
let end = line;
if (this.dragStart.index >= line.index) {
start = line;
end = this.dragStart;
}
this.updatedLineRange = { start, end };
this.setSelectedCommentPosition(this.updatedLineRange);
},
onStopDragging() {
this.showCommentForm({
lineCode: this.updatedLineRange?.end?.line_code,
fileHash: this.diffFile.file_hash,
});
this.dragStart = null;
},
},
userColorScheme: window.gon.user_color_scheme,
};
......@@ -94,6 +127,10 @@ export default {
:is-bottom="index + 1 === diffLinesLength"
:is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
:inline="inline"
:index="index"
@enterdragging="onDragOver"
@startdragging="onStartDragging"
@stopdragging="onStopDragging"
/>
<div
v-if="line.renderCommentRow"
......
......@@ -72,7 +72,12 @@ export default {
return this.fileLineCoverage(this.filePath, this.line.new_line);
},
classNameMapCell() {
return classNameMapCell(this.line, this.isHighlighted, this.isLoggedIn, this.isHover);
return classNameMapCell({
line: this.line,
hll: this.isHighlighted,
isLoggedIn: this.isLoggedIn,
isHover: this.isHover,
});
},
addCommentTooltip() {
return addCommentTooltip(this.line);
......
......@@ -68,20 +68,20 @@ export default {
return this.fileLineCoverage(this.filePath, this.line.right.new_line);
},
classNameMapCellLeft() {
return utils.classNameMapCell(
this.line.left,
this.isHighlighted,
this.isLoggedIn,
this.isLeftHover,
);
return utils.classNameMapCell({
line: this.line.left,
hll: this.isHighlighted,
isLoggedIn: this.isLoggedIn,
isHover: this.isLeftHover,
});
},
classNameMapCellRight() {
return utils.classNameMapCell(
this.line.right,
this.isHighlighted,
this.isLoggedIn,
this.isRightHover,
);
return utils.classNameMapCell({
line: this.line.right,
hll: this.isHighlighted,
isLoggedIn: this.isLoggedIn,
isHover: this.isRightHover,
});
},
addCommentTooltipLeft() {
return utils.addCommentTooltip(this.line.left);
......
......@@ -55,8 +55,17 @@ export const parallelizeDiffLines = (diffLines, inline) => {
let conflictStartIndex = -1;
const lines = [];
// `chunk` is used for dragging to select diff lines
// we are restricting commenting to only lines that appear between
// "expansion rows". Here equal chunks are lines grouped together
// inbetween expansion rows.
let chunk = 0;
for (let i = 0, diffLinesLength = diffLines.length, index = 0; i < diffLinesLength; i += 1) {
const line = diffLines[i];
line.chunk = chunk;
if (isMeta(line)) chunk += 1;
if (isRemoved(line) || isConflictOur(line) || inline) {
lines.push({
......
<script>
import { mapActions } from 'vuex';
import { mapActions, mapState } from 'vuex';
import { GlFormSelect, GlSprintf } from '@gitlab/ui';
import { getSymbol, getLineClasses } from './multiline_comment_utils';
......@@ -27,12 +27,13 @@ export default {
};
},
computed: {
...mapState({ selectedCommentPosition: ({ notes }) => notes.selectedCommentPosition }),
lineNumber() {
return this.commentLineOptions[this.commentLineOptions.length - 1].text;
},
},
created() {
const line = this.lineRange?.start || this.line;
const line = this.selectedCommentPosition?.start || this.lineRange?.start || this.line;
this.commentLineStart = {
line_code: line.line_code,
......@@ -40,6 +41,8 @@ export default {
old_line: line.old_line,
new_line: line.new_line,
};
if (this.selectedCommentPosition) return;
this.highlightSelection();
},
destroyed() {
......
......@@ -15,7 +15,7 @@ export default () => ({
batchSuggestionsInfo: [],
currentlyFetchingDiscussions: false,
/**
* selectedCommentPosition & selectedCommentPosition structures are the same as `position.line_range`:
* selectedCommentPosition & selectedCommentPositionHover structures are the same as `position.line_range`:
* {
* start: { line_code: string, new_line: number, old_line:number, type: string },
* end: { line_code: string, new_line: number, old_line:number, type: string },
......
......@@ -516,9 +516,12 @@ $line-removed-dark-transparent: rgba(246, 53, 85, 0.2);
$line-number-old: #f9d7dc;
$line-number-new: #ddfbe6;
$line-number-select: #fbf2da;
$line-number-commented: #dae5fb;
$line-target-blue: $blue-50;
$line-select-yellow: #fcf8e7;
$line-select-yellow-dark: #f0e2bd;
$line-commented-blue: #e8effc;
$line-commented-blue-dark: #bccef0;
$dark-diff-match-bg: rgba($white, 0.3);
$dark-diff-match-color: rgba($white, 0.1);
$diff-image-info-color: #808080;
......
......@@ -33,6 +33,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:batch_suggestions, @project, default_enabled: true)
push_frontend_feature_flag(:approvals_commented_by, @project, default_enabled: true)
push_frontend_feature_flag(:merge_request_widget_graphql, @project)
push_frontend_feature_flag(:drag_comment_selection, @project, default_enabled: true)
push_frontend_feature_flag(:unified_diff_components, @project, default_enabled: true)
push_frontend_feature_flag(:default_merge_ref_for_diffs, @project)
push_frontend_feature_flag(:core_security_mr_widget, @project, default_enabled: true)
......
---
name: drag_comment_selection
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49875
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/293945
milestone: '13.7'
type: development
group: group::source code
default_enabled: true
......@@ -1630,6 +1630,9 @@ msgstr ""
msgid "Add a comment to this line"
msgstr ""
msgid "Add a comment to this line or drag for multiple lines"
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
msgstr ""
......
......@@ -11,14 +11,16 @@ describe('DiffLineNoteForm', () => {
let diffLines;
const getDiffFileMock = () => ({ ...diffFileMockData });
beforeEach(() => {
const createComponent = (args = {}) => {
diffFile = getDiffFileMock();
diffLines = diffFile.highlighted_diff_lines;
const store = createStore();
store.state.notes.userData.id = 1;
store.state.notes.noteableData = noteableDataMock;
wrapper = shallowMount(DiffLineNoteForm, {
store.replaceState({ ...store.state, ...args.state });
return shallowMount(DiffLineNoteForm, {
store,
propsData: {
diffFileHash: diffFile.file_hash,
......@@ -27,9 +29,13 @@ describe('DiffLineNoteForm', () => {
noteTargetLine: diffLines[0],
},
});
});
};
describe('methods', () => {
beforeEach(() => {
wrapper = createComponent();
});
describe('handleCancelCommentForm', () => {
it('should ask for confirmation when shouldConfirm and isDirty passed as truthy', () => {
jest.spyOn(window, 'confirm').mockReturnValue(false);
......@@ -114,14 +120,39 @@ describe('DiffLineNoteForm', () => {
describe('mounted', () => {
it('should init autosave', () => {
const key = 'autosave/Note/Issue/98//DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1';
wrapper = createComponent();
expect(wrapper.vm.autosave).toBeDefined();
expect(wrapper.vm.autosave.key).toEqual(key);
});
it('should set selectedCommentPosition', () => {
wrapper = createComponent();
let startLineCode = wrapper.vm.commentLineStart.line_code;
let lineCode = wrapper.vm.line.line_code;
expect(startLineCode).toEqual(lineCode);
wrapper.destroy();
const state = {
notes: {
selectedCommentPosition: {
start: {
line_code: 'test',
},
},
},
};
wrapper = createComponent({ state });
startLineCode = wrapper.vm.commentLineStart.line_code;
lineCode = state.notes.selectedCommentPosition.start.line_code;
expect(startLineCode).toEqual(lineCode);
});
});
describe('template', () => {
it('should have note form', () => {
wrapper = createComponent();
expect(wrapper.find(NoteForm).exists()).toBe(true);
});
});
......
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { getByTestId, fireEvent } from '@testing-library/dom';
import Vuex from 'vuex';
import diffsModule from '~/diffs/store/modules';
import DiffRow from '~/diffs/components/diff_row.vue';
......@@ -42,16 +43,16 @@ describe('DiffRow', () => {
fileHash: 'abc',
filePath: 'abc',
line: {},
index: 0,
...props,
};
return shallowMount(DiffRow, { propsData, localVue, store });
};
it('isHighlighted returns true if isCommented is true', () => {
const props = { isCommented: true };
const wrapper = createWrapper({ props });
expect(wrapper.vm.isHighlighted).toBe(true);
});
const provide = {
glFeatures: { dragCommentSelection: true },
};
return shallowMount(DiffRow, { propsData, localVue, store, provide });
};
it('isHighlighted returns true given line.left', () => {
const props = {
......@@ -124,4 +125,36 @@ describe('DiffRow', () => {
const lineNumber = testLines[0].right.new_line;
expect(wrapper.find(`[data-linenumber="${lineNumber}"]`).exists()).toBe(true);
});
describe('drag operations', () => {
let line;
beforeEach(() => {
line = { ...testLines[0] };
});
it.each`
side
${'left'}
${'right'}
`('emits `enterdragging` onDragEnter $side side', ({ side }) => {
const expectation = { ...line[side], index: 0 };
const wrapper = createWrapper({ props: { line } });
fireEvent.dragEnter(getByTestId(wrapper.element, `${side}-side`));
expect(wrapper.emitted().enterdragging).toBeTruthy();
expect(wrapper.emitted().enterdragging[0]).toEqual([expectation]);
});
it.each`
side
${'left'}
${'right'}
`('emits `stopdragging` onDrop $side side', ({ side }) => {
const wrapper = createWrapper({ props: { line } });
fireEvent.dragEnd(getByTestId(wrapper.element, `${side}-side`));
expect(wrapper.emitted().stopdragging).toBeTruthy();
});
});
});
......@@ -126,14 +126,14 @@ describe('lineCode', () => {
describe('classNameMapCell', () => {
it.each`
line | hll | loggedIn | hovered | expectation
${undefined} | ${true} | ${true} | ${true} | ${[]}
${{ type: 'new' }} | ${false} | ${false} | ${false} | ${['new', { hll: false, 'is-over': false, new_line: true, old_line: false }]}
${{ type: 'new' }} | ${true} | ${true} | ${false} | ${['new', { hll: true, 'is-over': false, new_line: true, old_line: false }]}
${{ type: 'new' }} | ${true} | ${false} | ${true} | ${['new', { hll: true, 'is-over': false, new_line: true, old_line: false }]}
${{ type: 'new' }} | ${true} | ${true} | ${true} | ${['new', { hll: true, 'is-over': true, new_line: true, old_line: false }]}
`('should return $expectation', ({ line, hll, loggedIn, hovered, expectation }) => {
const classes = utils.classNameMapCell(line, hll, loggedIn, hovered);
line | hll | isLoggedIn | isHover | expectation
${undefined} | ${true} | ${true} | ${true} | ${[]}
${{ type: 'new' }} | ${false} | ${false} | ${false} | ${['new', { hll: false, 'is-over': false, new_line: true, old_line: false }]}
${{ type: 'new' }} | ${true} | ${true} | ${false} | ${['new', { hll: true, 'is-over': false, new_line: true, old_line: false }]}
${{ type: 'new' }} | ${true} | ${false} | ${true} | ${['new', { hll: true, 'is-over': false, new_line: true, old_line: false }]}
${{ type: 'new' }} | ${true} | ${true} | ${true} | ${['new', { hll: true, 'is-over': true, new_line: true, old_line: false }]}
`('should return $expectation', ({ line, hll, isLoggedIn, isHover, expectation }) => {
const classes = utils.classNameMapCell({ line, hll, isLoggedIn, isHover });
expect(classes).toEqual(expectation);
});
});
......
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vue from 'vue';
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import DiffView from '~/diffs/components/diff_view.vue';
// import DraftNote from '~/batch_comments/components/draft_note.vue';
// import DiffRow from '~/diffs/components/diff_row.vue';
// import DiffCommentCell from '~/diffs/components/diff_comment_cell.vue';
// import DiffExpansionCell from '~/diffs/components/diff_expansion_cell.vue';
describe('DiffView', () => {
const DiffExpansionCell = { template: `<div/>` };
const DiffRow = { template: `<div/>` };
const DiffCommentCell = { template: `<div/>` };
const DraftNote = { template: `<div/>` };
const showCommentForm = jest.fn();
const setSelectedCommentPosition = jest.fn();
const getDiffRow = (wrapper) => wrapper.findComponent(DiffRow).vm;
const createWrapper = (props) => {
const localVue = createLocalVue();
localVue.use(Vuex);
Vue.use(Vuex);
const batchComments = {
getters: {
......@@ -26,8 +26,13 @@ describe('DiffView', () => {
},
namespaced: true,
};
const diffs = { getters: { commitId: () => 'abc123' }, namespaced: true };
const diffs = {
actions: { showCommentForm },
getters: { commitId: () => 'abc123' },
namespaced: true,
};
const notes = {
actions: { setSelectedCommentPosition },
state: { selectedCommentPosition: null, selectedCommentPositionHover: null },
};
......@@ -41,7 +46,7 @@ describe('DiffView', () => {
...props,
};
const stubs = { DiffExpansionCell, DiffRow, DiffCommentCell, DraftNote };
return shallowMount(DiffView, { propsData, store, localVue, stubs });
return shallowMount(DiffView, { propsData, store, stubs });
};
it('renders a match line', () => {
......@@ -74,4 +79,55 @@ describe('DiffView', () => {
});
expect(wrapper.find(DraftNote).exists()).toBe(true);
});
describe('drag operations', () => {
it('sets `dragStart` onStartDragging', () => {
const wrapper = createWrapper({ diffLines: [{}] });
wrapper.findComponent(DiffRow).vm.$emit('startdragging', { test: true });
expect(wrapper.vm.dragStart).toEqual({ test: true });
});
it('does not call `setSelectedCommentPosition` on different chunks onDragOver', () => {
const wrapper = createWrapper({ diffLines: [{}] });
const diffRow = getDiffRow(wrapper);
diffRow.$emit('startdragging', { chunk: 0 });
diffRow.$emit('enterdragging', { chunk: 1 });
expect(setSelectedCommentPosition).not.toHaveBeenCalled();
});
it.each`
start | end | expectation
${1} | ${2} | ${{ start: { index: 1 }, end: { index: 2 } }}
${2} | ${1} | ${{ start: { index: 1 }, end: { index: 2 } }}
${1} | ${1} | ${{ start: { index: 1 }, end: { index: 1 } }}
`(
'calls `setSelectedCommentPosition` with correct `updatedLineRange`',
({ start, end, expectation }) => {
const wrapper = createWrapper({ diffLines: [{}] });
const diffRow = getDiffRow(wrapper);
diffRow.$emit('startdragging', { chunk: 1, index: start });
diffRow.$emit('enterdragging', { chunk: 1, index: end });
const arg = setSelectedCommentPosition.mock.calls[0][1];
expect(arg).toMatchObject(expectation);
},
);
it('sets `dragStart` to null onStopDragging', () => {
const wrapper = createWrapper({ diffLines: [{}] });
const diffRow = getDiffRow(wrapper);
diffRow.$emit('startdragging', { test: true });
expect(wrapper.vm.dragStart).toEqual({ test: true });
diffRow.$emit('stopdragging');
expect(wrapper.vm.dragStart).toBeNull();
expect(showCommentForm).toHaveBeenCalled();
});
});
});
......@@ -1159,7 +1159,7 @@ describe('DiffsStoreUtils', () => {
it('converts inline diff lines to parallel diff lines', () => {
const file = getDiffFileMock();
expect(utils.parallelizeDiffLines(file[INLINE_DIFF_LINES_KEY])).toEqual(
expect(utils.parallelizeDiffLines(file[INLINE_DIFF_LINES_KEY])).toMatchObject(
file.parallel_diff_lines,
);
});
......@@ -1178,16 +1178,17 @@ describe('DiffsStoreUtils', () => {
{
left: null,
right: {
chunk: 0,
type: 'new',
},
},
{
left: { type: 'conflict_marker_our' },
right: { type: 'conflict_marker_their' },
left: { chunk: 0, type: 'conflict_marker_our' },
right: { chunk: 0, type: 'conflict_marker_their' },
},
{
left: { type: 'conflict_our' },
right: { type: 'conflict_their' },
left: { chunk: 0, type: 'conflict_our' },
right: { chunk: 0, type: 'conflict_their' },
},
]);
});
......@@ -1196,9 +1197,9 @@ describe('DiffsStoreUtils', () => {
const file = getDiffFileMock();
const files = utils.parallelizeDiffLines(file.highlighted_diff_lines, true);
expect(files[5].left).toEqual(file.parallel_diff_lines[5].left);
expect(files[5].left).toMatchObject(file.parallel_diff_lines[5].left);
expect(files[5].right).toBeNull();
expect(files[6].left).toEqual(file.parallel_diff_lines[5].right);
expect(files[6].left).toMatchObject(file.parallel_diff_lines[5].right);
expect(files[6].right).toBeNull();
});
});
......
import Vue from 'vue';
import Vuex from 'vuex';
import { mount } from '@vue/test-utils';
import { GlFormSelect } from '@gitlab/ui';
import MultilineCommentForm from '~/notes/components/multiline_comment_form.vue';
import notesModule from '~/notes/stores/modules';
describe('MultilineCommentForm', () => {
Vue.use(Vuex);
const setSelectedCommentPosition = jest.fn();
const testLine = {
line_code: 'test',
type: 'test',
old_line: 'test',
new_line: 'test',
};
const createWrapper = (props = {}, state) => {
setSelectedCommentPosition.mockReset();
const store = new Vuex.Store({
modules: { notes: notesModule() },
actions: { setSelectedCommentPosition },
});
if (state) store.replaceState({ ...store.state, ...state });
const propsData = {
line: { ...testLine },
commentLineOptions: [{ text: '1' }],
...props,
};
return mount(MultilineCommentForm, { propsData, store });
};
describe('created', () => {
it('sets commentLineStart to line', () => {
const line = { ...testLine };
const wrapper = createWrapper({ line });
expect(wrapper.vm.commentLineStart).toEqual(line);
expect(setSelectedCommentPosition).toHaveBeenCalled();
});
it('sets commentLineStart to lineRange', () => {
const lineRange = {
start: { ...testLine },
};
const wrapper = createWrapper({ lineRange });
expect(wrapper.vm.commentLineStart).toEqual(lineRange.start);
expect(setSelectedCommentPosition).toHaveBeenCalled();
});
it('sets commentLineStart to selectedCommentPosition', () => {
const notes = {
selectedCommentPosition: {
start: { ...testLine },
},
};
const wrapper = createWrapper({}, { notes });
expect(wrapper.vm.commentLineStart).toEqual(wrapper.vm.selectedCommentPosition.start);
expect(setSelectedCommentPosition).not.toHaveBeenCalled();
});
});
describe('destroyed', () => {
it('calls setSelectedCommentPosition', () => {
const wrapper = createWrapper();
wrapper.destroy();
// Once during created, once during destroyed
expect(setSelectedCommentPosition).toHaveBeenCalledTimes(2);
});
});
it('handles changing the start line', () => {
const line = { ...testLine };
const wrapper = createWrapper({ line });
const glSelect = wrapper.findComponent(GlFormSelect);
glSelect.vm.$emit('change', { ...testLine });
expect(wrapper.vm.commentLineStart).toEqual(line);
expect(wrapper.emitted('input')).toBeTruthy();
// Once during created, once during updateCommentLineStart
expect(setSelectedCommentPosition).toHaveBeenCalledTimes(2);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment