Commit e9d48450 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Natalia Tepluhina

Swap custom store for vuex implementation

- wire vuex in bundle and main app
- remove custom store
parent 8d802266
<script> <script>
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { mapActions } from 'vuex';
import { deprecatedCreateFlash as flash } from '~/flash'; import { deprecatedCreateFlash as flash } from '~/flash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { INTERACTIVE_RESOLVE_MODE } from '../constants';
export default { export default {
props: { props: {
...@@ -10,14 +12,6 @@ export default { ...@@ -10,14 +12,6 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
onCancelDiscardConfirmation: {
type: Function,
required: true,
},
onAcceptDiscardConfirmation: {
type: Function,
required: true,
},
}, },
data() { data() {
return { return {
...@@ -50,6 +44,7 @@ export default { ...@@ -50,6 +44,7 @@ export default {
} }
}, },
methods: { methods: {
...mapActions(['setFileResolveMode', 'setPromptConfirmationState', 'updateFile']),
loadEditor() { loadEditor() {
const EditorPromise = import(/* webpackChunkName: 'EditorLite' */ '~/editor/editor_lite'); const EditorPromise = import(/* webpackChunkName: 'EditorLite' */ '~/editor/editor_lite');
const DataPromise = axios.get(this.file.content_path); const DataPromise = axios.get(this.file.content_path);
...@@ -82,23 +77,24 @@ export default { ...@@ -82,23 +77,24 @@ export default {
saveDiffResolution() { saveDiffResolution() {
this.saved = true; this.saved = true;
// This probably be better placed in the data provider this.updateFile({
/* eslint-disable vue/no-mutating-props */ ...this.file,
this.file.content = this.editor.getValue(); content: this.editor.getValue(),
this.file.resolveEditChanged = this.file.content !== this.originalContent; resolveEditChanged: this.file.content !== this.originalContent,
this.file.promptDiscardConfirmation = false; promptDiscardConfirmation: false,
/* eslint-enable vue/no-mutating-props */ });
}, },
resetEditorContent() { resetEditorContent() {
if (this.fileLoaded) { if (this.fileLoaded) {
this.editor.setValue(this.originalContent); this.editor.setValue(this.originalContent);
} }
}, },
cancelDiscardConfirmation(file) {
this.onCancelDiscardConfirmation(file);
},
acceptDiscardConfirmation(file) { acceptDiscardConfirmation(file) {
this.onAcceptDiscardConfirmation(file); this.setPromptConfirmationState({ file, promptDiscardConfirmation: false });
this.setFileResolveMode({ file, mode: INTERACTIVE_RESOLVE_MODE });
},
cancelDiscardConfirmation(file) {
this.setPromptConfirmationState({ file, promptDiscardConfirmation: false });
}, },
}, },
}; };
......
<script> <script>
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import actionsMixin from '../mixins/line_conflict_actions'; import { mapActions } from 'vuex';
import syntaxHighlight from '~/syntax_highlight';
import { SYNTAX_HIGHLIGHT_CLASS } from '../constants';
import utilsMixin from '../mixins/line_conflict_utils'; import utilsMixin from '../mixins/line_conflict_utils';
export default { export default {
directives: { directives: {
SafeHtml, SafeHtml,
}, },
mixins: [utilsMixin, actionsMixin], mixins: [utilsMixin],
SYNTAX_HIGHLIGHT_CLASS,
props: { props: {
file: { file: {
type: Object, type: Object,
required: true, required: true,
}, },
}, },
mounted() {
syntaxHighlight(document.querySelectorAll(`.${SYNTAX_HIGHLIGHT_CLASS}`));
},
methods: {
...mapActions(['handleSelected']),
},
}; };
</script> </script>
<template> <template>
<table class="diff-wrap-lines code code-commit js-syntax-highlight"> <table :class="['diff-wrap-lines code code-commit', $options.SYNTAX_HIGHLIGHT_CLASS]">
<tr <!-- Unfortunately there isn't a good key for these sections -->
v-for="line in file.inlineLines" <!-- eslint-disable vue/require-v-for-key -->
:key="(line.isHeader ? line.id : line.new_line) + line.richText" <tr v-for="line in file.inlineLines" class="line_holder diff-inline">
class="line_holder diff-inline"
>
<template v-if="line.isHeader"> <template v-if="line.isHeader">
<td :class="lineCssClass(line)" class="diff-line-num header"></td> <td :class="lineCssClass(line)" class="diff-line-num header"></td>
<td :class="lineCssClass(line)" class="diff-line-num header"></td> <td :class="lineCssClass(line)" class="diff-line-num header"></td>
<td :class="lineCssClass(line)" class="line_content header"> <td :class="lineCssClass(line)" class="line_content header">
<strong>{{ line.richText }}</strong> <strong>{{ line.richText }}</strong>
<button class="btn" @click="handleSelected(file, line.id, line.section)"> <button class="btn" @click="handleSelected({ file, line })">
{{ line.buttonTitle }} {{ line.buttonTitle }}
</button> </button>
</td> </td>
......
<script> <script>
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import actionsMixin from '../mixins/line_conflict_actions'; import { mapActions } from 'vuex';
import syntaxHighlight from '~/syntax_highlight';
import { SYNTAX_HIGHLIGHT_CLASS } from '../constants';
import utilsMixin from '../mixins/line_conflict_utils'; import utilsMixin from '../mixins/line_conflict_utils';
export default { export default {
directives: { directives: {
SafeHtml, SafeHtml,
}, },
mixins: [utilsMixin, actionsMixin], mixins: [utilsMixin],
SYNTAX_HIGHLIGHT_CLASS,
props: { props: {
file: { file: {
type: Object, type: Object,
required: true, required: true,
}, },
}, },
mounted() {
syntaxHighlight(document.querySelectorAll(`.${SYNTAX_HIGHLIGHT_CLASS}`));
},
methods: {
...mapActions(['handleSelected']),
},
}; };
</script> </script>
<template> <template>
<!-- Unfortunately there isn't a good key for these sections --> <!-- Unfortunately there isn't a good key for these sections -->
<!-- eslint-disable vue/require-v-for-key --> <!-- eslint-disable vue/require-v-for-key -->
<table class="diff-wrap-lines code js-syntax-highlight"> <table :class="['diff-wrap-lines code', $options.SYNTAX_HIGHLIGHT_CLASS]">
<tr v-for="section in file.parallelLines" class="line_holder parallel"> <tr v-for="section in file.parallelLines" class="line_holder parallel">
<template v-for="line in section"> <template v-for="line in section">
<template v-if="line.isHeader"> <template v-if="line.isHeader">
<td class="diff-line-num header" :class="lineCssClass(line)"></td> <td class="diff-line-num header" :class="lineCssClass(line)"></td>
<td class="line_content header" :class="lineCssClass(line)"> <td class="line_content header" :class="lineCssClass(line)">
<strong>{{ line.richText }}</strong> <strong>{{ line.richText }}</strong>
<button class="btn" @click="handleSelected(file, line.id, line.section)"> <button class="btn" @click="handleSelected({ file, line })">
{{ line.buttonTitle }} {{ line.buttonTitle }}
</button> </button>
</td> </td>
......
...@@ -13,6 +13,7 @@ export const VIEW_TYPES = { ...@@ -13,6 +13,7 @@ export const VIEW_TYPES = {
export const EDIT_RESOLVE_MODE = 'edit'; export const EDIT_RESOLVE_MODE = 'edit';
export const INTERACTIVE_RESOLVE_MODE = 'interactive'; export const INTERACTIVE_RESOLVE_MODE = 'interactive';
export const DEFAULT_RESOLVE_MODE = INTERACTIVE_RESOLVE_MODE; export const DEFAULT_RESOLVE_MODE = INTERACTIVE_RESOLVE_MODE;
export const SYNTAX_HIGHLIGHT_CLASS = 'js-syntax-highlight';
export const HEAD_HEADER_TEXT = s__('MergeConflict|HEAD//our changes'); export const HEAD_HEADER_TEXT = s__('MergeConflict|HEAD//our changes');
export const ORIGIN_HEADER_TEXT = s__('MergeConflict|origin//their changes'); export const ORIGIN_HEADER_TEXT = s__('MergeConflict|origin//their changes');
......
<script> <script>
import { GlSprintf } from '@gitlab/ui'; import { GlSprintf } from '@gitlab/ui';
import { mapGetters, mapState, mapActions } from 'vuex';
import { __ } from '~/locale'; import { __ } from '~/locale';
import FileIcon from '~/vue_shared/components/file_icon.vue'; import FileIcon from '~/vue_shared/components/file_icon.vue';
import DiffFileEditor from './components/diff_file_editor.vue'; import DiffFileEditor from './components/diff_file_editor.vue';
import InlineConflictLines from './components/inline_conflict_lines.vue'; import InlineConflictLines from './components/inline_conflict_lines.vue';
import ParallelConflictLines from './components/parallel_conflict_lines.vue'; import ParallelConflictLines from './components/parallel_conflict_lines.vue';
import { INTERACTIVE_RESOLVE_MODE } from './constants';
/** /**
* NOTE: Most of this component is directly using $root, rather than props or a better data store. * A lot of the classes below should
* This is BAD and one shouldn't copy that behavior. Similarly a lot of the classes below should
* be replaced with GitLab UI components. * be replaced with GitLab UI components.
* *
* We are just doing it temporarily in order to migrate the template from HAML => Vue in an iterative manner * We are just doing it temporarily in order to migrate the template from HAML => Vue in an iterative manner
...@@ -25,60 +26,88 @@ export default { ...@@ -25,60 +26,88 @@ export default {
InlineConflictLines, InlineConflictLines,
ParallelConflictLines, ParallelConflictLines,
}, },
inject: ['mergeRequestPath', 'sourceBranchPath'], inject: ['mergeRequestPath', 'sourceBranchPath', 'resolveConflictsPath'],
i18n: { i18n: {
commitStatSummary: __('Showing %{conflict} between %{sourceBranch} and %{targetBranch}'), commitStatSummary: __('Showing %{conflict} between %{sourceBranch} and %{targetBranch}'),
resolveInfo: __( resolveInfo: __(
'You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}', 'You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}',
), ),
}, },
computed: {
...mapGetters([
'getConflictsCountText',
'isReadyToCommit',
'getCommitButtonText',
'fileTextTypePresent',
]),
...mapState(['isLoading', 'hasError', 'isParallel', 'conflictsData']),
commitMessage: {
get() {
return this.conflictsData.commitMessage;
},
set(value) {
this.updateCommitMessage(value);
},
},
},
methods: {
...mapActions([
'setViewType',
'submitResolvedConflicts',
'setFileResolveMode',
'setPromptConfirmationState',
'updateCommitMessage',
]),
onClickResolveModeButton(file, mode) {
if (mode === INTERACTIVE_RESOLVE_MODE && file.resolveEditChanged) {
this.setPromptConfirmationState({ file, promptDiscardConfirmation: true });
} else {
this.setFileResolveMode({ file, mode });
}
},
},
}; };
</script> </script>
<template> <template>
<div id="conflicts"> <div id="conflicts">
<div v-if="$root.isLoading" class="loading"> <div v-if="isLoading" class="loading">
<div class="spinner spinner-md"></div> <div class="spinner spinner-md"></div>
</div> </div>
<div v-if="$root.hasError" class="nothing-here-block"> <div v-if="hasError" class="nothing-here-block">
{{ $root.conflictsData.errorMessage }} {{ conflictsData.errorMessage }}
</div> </div>
<template v-if="!$root.isLoading && !$root.hasError"> <template v-if="!isLoading && !hasError">
<div class="content-block oneline-block files-changed"> <div class="content-block oneline-block files-changed">
<div v-if="$root.showDiffViewTypeSwitcher" class="inline-parallel-buttons"> <div v-if="fileTextTypePresent" class="inline-parallel-buttons">
<div class="btn-group"> <div class="btn-group">
<button <button
:class="{ active: !$root.isParallel }" :class="{ active: !isParallel }"
class="btn gl-button" class="btn gl-button"
@click="$root.handleViewTypeChange('inline')" @click="setViewType('inline')"
> >
{{ __('Inline') }} {{ __('Inline') }}
</button> </button>
<button <button
:class="{ active: $root.isParallel }" :class="{ active: isParallel }"
class="btn gl-button" class="btn gl-button"
@click="$root.handleViewTypeChange('parallel')" data-testid="side-by-side"
@click="setViewType('parallel')"
> >
{{ __('Side-by-side') }} {{ __('Side-by-side') }}
</button> </button>
</div> </div>
</div> </div>
<div class="js-toggle-container"> <div class="js-toggle-container">
<div class="commit-stat-summary"> <div class="commit-stat-summary" data-testid="conflicts-count">
<gl-sprintf :message="$options.i18n.commitStatSummary"> <gl-sprintf :message="$options.i18n.commitStatSummary">
<template #conflict> <template #conflict>
<strong class="cred"> <strong class="cred">{{ getConflictsCountText }}</strong>
{{ $root.conflictsCountText }}
</strong>
</template> </template>
<template #sourceBranch> <template #sourceBranch>
<strong class="ref-name"> <strong class="ref-name">{{ conflictsData.sourceBranch }}</strong>
{{ $root.conflictsData.sourceBranch }}
</strong>
</template> </template>
<template #targetBranch> <template #targetBranch>
<strong class="ref-name"> <strong class="ref-name">{{ conflictsData.targetBranch }}</strong>
{{ $root.conflictsData.targetBranch }}
</strong>
</template> </template>
</gl-sprintf> </gl-sprintf>
</div> </div>
...@@ -87,12 +116,13 @@ export default { ...@@ -87,12 +116,13 @@ export default {
<div class="files-wrapper"> <div class="files-wrapper">
<div class="files"> <div class="files">
<div <div
v-for="file in $root.conflictsData.files" v-for="file in conflictsData.files"
:key="file.blobPath" :key="file.blobPath"
class="diff-file file-holder conflict" class="diff-file file-holder conflict"
data-testid="files"
> >
<div class="js-file-title file-title file-title-flex-parent cursor-default"> <div class="js-file-title file-title file-title-flex-parent cursor-default">
<div class="file-header-content"> <div class="file-header-content" data-testid="file-name">
<file-icon :file-name="file.filePath" :size="18" css-classes="gl-mr-2" /> <file-icon :file-name="file.filePath" :size="18" css-classes="gl-mr-2" />
<strong class="file-title-name">{{ file.filePath }}</strong> <strong class="file-title-name">{{ file.filePath }}</strong>
</div> </div>
...@@ -102,7 +132,8 @@ export default { ...@@ -102,7 +132,8 @@ export default {
:class="{ active: file.resolveMode === 'interactive' }" :class="{ active: file.resolveMode === 'interactive' }"
class="btn gl-button" class="btn gl-button"
type="button" type="button"
@click="$root.onClickResolveModeButton(file, 'interactive')" data-testid="interactive-button"
@click="onClickResolveModeButton(file, 'interactive')"
> >
{{ __('Interactive mode') }} {{ __('Interactive mode') }}
</button> </button>
...@@ -110,7 +141,8 @@ export default { ...@@ -110,7 +141,8 @@ export default {
:class="{ active: file.resolveMode === 'edit' }" :class="{ active: file.resolveMode === 'edit' }"
class="btn gl-button" class="btn gl-button"
type="button" type="button"
@click="$root.onClickResolveModeButton(file, 'edit')" data-testid="inline-button"
@click="onClickResolveModeButton(file, 'edit')"
> >
{{ __('Edit inline') }} {{ __('Edit inline') }}
</button> </button>
...@@ -118,35 +150,23 @@ export default { ...@@ -118,35 +150,23 @@ export default {
<a :href="file.blobPath" class="btn gl-button view-file"> <a :href="file.blobPath" class="btn gl-button view-file">
<gl-sprintf :message="__('View file @ %{commitSha}')"> <gl-sprintf :message="__('View file @ %{commitSha}')">
<template #commitSha> <template #commitSha>
{{ $root.conflictsData.shortCommitSha }} {{ conflictsData.shortCommitSha }}
</template> </template>
</gl-sprintf> </gl-sprintf>
</a> </a>
</div> </div>
</div> </div>
<div class="diff-content diff-wrap-lines"> <div class="diff-content diff-wrap-lines">
<div <template v-if="file.resolveMode === 'interactive' && file.type === 'text'">
v-show=" <div v-if="!isParallel" class="file-content">
!$root.isParallel && file.resolveMode === 'interactive' && file.type === 'text'
"
class="file-content"
>
<inline-conflict-lines :file="file" /> <inline-conflict-lines :file="file" />
</div> </div>
<div <div v-if="isParallel" class="file-content">
v-show="
$root.isParallel && file.resolveMode === 'interactive' && file.type === 'text'
"
class="file-content"
>
<parallel-conflict-lines :file="file" /> <parallel-conflict-lines :file="file" />
</div> </div>
<div v-show="file.resolveMode === 'edit' || file.type === 'text-editor'"> </template>
<diff-file-editor <div v-if="file.resolveMode === 'edit' || file.type === 'text-editor'">
:file="file" <diff-file-editor :file="file" />
:on-accept-discard-confirmation="$root.acceptDiscardConfirmation"
:on-cancel-discard-confirmation="$root.cancelDiscardConfirmation"
/>
</div> </div>
</div> </div>
</div> </div>
...@@ -169,7 +189,7 @@ export default { ...@@ -169,7 +189,7 @@ export default {
</template> </template>
<template #branch_name> <template #branch_name>
<a class="ref-name" :href="sourceBranchPath"> <a class="ref-name" :href="sourceBranchPath">
{{ $root.conflictsData.sourceBranch }} {{ conflictsData.sourceBranch }}
</a> </a>
</template> </template>
</gl-sprintf> </gl-sprintf>
...@@ -183,7 +203,8 @@ export default { ...@@ -183,7 +203,8 @@ export default {
<div class="max-width-marker"></div> <div class="max-width-marker"></div>
<textarea <textarea
id="commit-message" id="commit-message"
v-model="$root.conflictsData.commitMessage" v-model="commitMessage"
data-testid="commit-message"
class="form-control js-commit-message" class="form-control js-commit-message"
rows="5" rows="5"
></textarea> ></textarea>
...@@ -195,12 +216,12 @@ export default { ...@@ -195,12 +216,12 @@ export default {
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6">
<button <button
:disabled="!$root.readyToCommit" :disabled="!isReadyToCommit"
class="btn gl-button btn-success js-submit-button" class="btn gl-button btn-success js-submit-button"
type="button" type="button"
@click="$root.commit()" @click="submitResolvedConflicts(resolveConflictsPath)"
> >
<span>{{ $root.commitButtonText }}</span> <span>{{ getCommitButtonText }}</span>
</button> </button>
</div> </div>
<div class="col-6 text-right"> <div class="col-6 text-right">
......
import axios from '../lib/utils/axios_utils';
export default class MergeConflictsService {
constructor(options) {
this.conflictsPath = options.conflictsPath;
this.resolveConflictsPath = options.resolveConflictsPath;
}
fetchConflictsData() {
return axios.get(this.conflictsPath);
}
submitResolveConflicts(data) {
return axios.post(this.resolveConflictsPath, data);
}
}
import $ from 'jquery';
import Vue from 'vue'; import Vue from 'vue';
import { __ } from '~/locale';
import { deprecatedCreateFlash as createFlash } from '../flash';
import initIssuableSidebar from '../init_issuable_sidebar'; import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store';
import syntaxHighlight from '../syntax_highlight';
import MergeConflictsResolverApp from './merge_conflict_resolver_app.vue'; import MergeConflictsResolverApp from './merge_conflict_resolver_app.vue';
import MergeConflictsService from './merge_conflict_service'; import { createStore } from './store';
export default function initMergeConflicts() { export default function initMergeConflicts() {
const INTERACTIVE_RESOLVE_MODE = 'interactive';
const conflictsEl = document.querySelector('#conflicts'); const conflictsEl = document.querySelector('#conflicts');
const { mergeConflictsStore } = gl.mergeConflicts;
const mergeConflictsService = new MergeConflictsService({
conflictsPath: conflictsEl.dataset.conflictsPath,
resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath,
});
const { sourceBranchPath, mergeRequestPath } = conflictsEl.dataset; const {
sourceBranchPath,
mergeRequestPath,
conflictsPath,
resolveConflictsPath,
} = conflictsEl.dataset;
initIssuableSidebar(); initIssuableSidebar();
const store = createStore();
return new Vue({ return new Vue({
el: conflictsEl, el: conflictsEl,
store,
provide: { provide: {
sourceBranchPath, sourceBranchPath,
mergeRequestPath, mergeRequestPath,
}, resolveConflictsPath,
data: mergeConflictsStore.state,
computed: {
conflictsCountText() {
return mergeConflictsStore.getConflictsCountText();
},
readyToCommit() {
return mergeConflictsStore.isReadyToCommit();
},
commitButtonText() {
return mergeConflictsStore.getCommitButtonText();
},
showDiffViewTypeSwitcher() {
return mergeConflictsStore.fileTextTypePresent();
},
}, },
created() { created() {
mergeConflictsService store.dispatch('fetchConflictsData', conflictsPath);
.fetchConflictsData()
.then(({ data }) => {
if (data.type === 'error') {
mergeConflictsStore.setFailedRequest(data.message);
} else {
mergeConflictsStore.setConflictsData(data);
}
mergeConflictsStore.setLoadingState(false);
this.$nextTick(() => {
syntaxHighlight($('.js-syntax-highlight'));
});
})
.catch(() => {
mergeConflictsStore.setLoadingState(false);
mergeConflictsStore.setFailedRequest();
});
},
methods: {
handleViewTypeChange(viewType) {
mergeConflictsStore.setViewType(viewType);
},
onClickResolveModeButton(file, mode) {
if (mode === INTERACTIVE_RESOLVE_MODE && file.resolveEditChanged) {
mergeConflictsStore.setPromptConfirmationState(file, true);
return;
}
mergeConflictsStore.setFileResolveMode(file, mode);
},
acceptDiscardConfirmation(file) {
mergeConflictsStore.setPromptConfirmationState(file, false);
mergeConflictsStore.setFileResolveMode(file, INTERACTIVE_RESOLVE_MODE);
},
cancelDiscardConfirmation(file) {
mergeConflictsStore.setPromptConfirmationState(file, false);
},
commit() {
mergeConflictsStore.setSubmitState(true);
mergeConflictsService
.submitResolveConflicts(mergeConflictsStore.getCommitData())
.then(({ data }) => {
window.location.href = data.redirect_to;
})
.catch(() => {
mergeConflictsStore.setSubmitState(false);
createFlash(__('Failed to save merge conflicts resolutions. Please try again!'));
});
},
}, },
render(createElement) { render(createElement) {
return createElement(MergeConflictsResolverApp); return createElement(MergeConflictsResolverApp);
......
export default {
methods: {
handleSelected(file, sectionId, selection) {
gl.mergeConflicts.mergeConflictsStore.handleSelected(file, sectionId, selection);
},
},
};
...@@ -118,3 +118,8 @@ export const handleSelected = ({ commit, state, getters }, { file, line: { id, s ...@@ -118,3 +118,8 @@ export const handleSelected = ({ commit, state, getters }, { file, line: { id, s
commit(types.UPDATE_FILE, { file: updated, index }); commit(types.UPDATE_FILE, { file: updated, index });
}; };
export const updateFile = ({ commit, getters }, file) => {
const index = getters.getFileIndex(file);
commit(types.UPDATE_FILE, { file, index });
};
import { GlSprintf } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import InlineConflictLines from '~/merge_conflicts/components/inline_conflict_lines.vue';
import ParallelConflictLines from '~/merge_conflicts/components/parallel_conflict_lines.vue';
import component from '~/merge_conflicts/merge_conflict_resolver_app.vue';
import { createStore } from '~/merge_conflicts/store';
import { decorateFiles } from '~/merge_conflicts/utils';
import { conflictsMock } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Merge Conflict Resolver App', () => {
let wrapper;
let store;
const decoratedMockFiles = decorateFiles(conflictsMock.files);
const mountComponent = () => {
wrapper = shallowMount(component, {
store,
stubs: { GlSprintf },
provide() {
return {
mergeRequestPath: 'foo',
sourceBranchPath: 'foo',
resolveConflictsPath: 'bar',
};
},
});
};
beforeEach(() => {
store = createStore();
store.commit('SET_LOADING_STATE', false);
store.dispatch('setConflictsData', conflictsMock);
});
afterEach(() => {
wrapper.destroy();
});
const findConflictsCount = () => wrapper.find('[data-testid="conflicts-count"]');
const findFiles = () => wrapper.findAll('[data-testid="files"]');
const findFileHeader = (w = wrapper) => w.find('[data-testid="file-name"]');
const findFileInteractiveButton = (w = wrapper) => w.find('[data-testid="interactive-button"]');
const findFileInlineButton = (w = wrapper) => w.find('[data-testid="inline-button"]');
const findSideBySideButton = () => wrapper.find('[data-testid="side-by-side"]');
const findInlineConflictLines = (w = wrapper) => w.find(InlineConflictLines);
const findParallelConflictLines = (w = wrapper) => w.find(ParallelConflictLines);
const findCommitMessageTextarea = () => wrapper.find('[data-testid="commit-message"]');
it('shows the amount of conflicts', () => {
mountComponent();
const title = findConflictsCount();
expect(title.exists()).toBe(true);
expect(title.text().trim()).toBe('Showing 3 conflicts between test-conflicts and master');
});
describe('files', () => {
it('shows one file area for each file', () => {
mountComponent();
expect(findFiles()).toHaveLength(conflictsMock.files.length);
});
it('has the appropriate file header', () => {
mountComponent();
const fileHeader = findFileHeader(findFiles().at(0));
expect(fileHeader.text()).toBe(decoratedMockFiles[0].filePath);
});
describe('editing', () => {
it('interactive mode is the default', () => {
mountComponent();
const interactiveButton = findFileInteractiveButton(findFiles().at(0));
const inlineButton = findFileInlineButton(findFiles().at(0));
expect(interactiveButton.classes('active')).toBe(true);
expect(inlineButton.classes('active')).toBe(false);
});
it('clicking inline set inline as default', async () => {
mountComponent();
const inlineButton = findFileInlineButton(findFiles().at(0));
expect(inlineButton.classes('active')).toBe(false);
inlineButton.trigger('click');
await wrapper.vm.$nextTick();
expect(inlineButton.classes('active')).toBe(true);
});
it('inline mode shows a inline-conflict-lines', () => {
mountComponent();
const inlineConflictLinesComponent = findInlineConflictLines(findFiles().at(0));
expect(inlineConflictLinesComponent.exists()).toBe(true);
expect(inlineConflictLinesComponent.props('file')).toEqual(decoratedMockFiles[0]);
});
it('parallel mode shows a parallel-conflict-lines', async () => {
mountComponent();
findSideBySideButton().trigger('click');
await wrapper.vm.$nextTick();
const parallelConflictLinesComponent = findParallelConflictLines(findFiles().at(0));
expect(parallelConflictLinesComponent.exists()).toBe(true);
expect(parallelConflictLinesComponent.props('file')).toEqual(decoratedMockFiles[0]);
});
});
});
describe('submit form', () => {
it('contains a commit message textarea', () => {
mountComponent();
expect(findCommitMessageTextarea().exists()).toBe(true);
});
});
});
This diff is collapsed.
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