Commit 78e9efaf authored by Justin Boyson's avatar Justin Boyson Committed by Phil Hughes

Add drag to select lines

Updates mr diffs to use draggable events to select lines for comment
parent b674bce0
......@@ -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
......@@ -1627,6 +1627,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