Commit 0ff24c10 authored by Tim Zallmann's avatar Tim Zallmann

Merge branch 'ee-make-ide-ce' into 'master'

EE port of make-ide-ce

See merge request gitlab-org/gitlab-ee!5045
parents 586c6506 212936ab
<script>
import { mapState } from 'vuex';
import { sprintf, __ } from '~/locale';
import * as consts from 'ee/ide/stores/modules/commit/constants';
import * as consts from '../../stores/modules/commit/constants';
import RadioGroup from './radio_group.vue';
export default {
......
<script>
import { mapActions } from 'vuex';
import router from 'ee/ide/ide_router';
import icon from '~/vue_shared/components/icon.vue';
import router from '../../ide_router';
export default {
components: {
......
......@@ -5,9 +5,8 @@ import icon from '~/vue_shared/components/icon.vue';
import modal from '~/vue_shared/components/modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import commitFilesList from './commit_sidebar/list.vue';
import * as consts from 'ee/ide/stores/modules/commit/constants'; // eslint-disable-line import/first
import Actions from 'ee/ide/components/commit_sidebar/actions.vue'; // eslint-disable-line import/first
import * as consts from '../stores/modules/commit/constants';
import Actions from './commit_sidebar/actions.vue';
export default {
components: {
......
......@@ -3,9 +3,8 @@
import fileIcon from '~/vue_shared/components/file_icon.vue';
import icon from '~/vue_shared/components/icon.vue';
import fileStatusIcon from 'ee/ide/components/repo_file_status_icon.vue';
import changedFileIcon from 'ee/ide/components/changed_file_icon.vue';
import fileStatusIcon from './repo_file_status_icon.vue';
import changedFileIcon from './changed_file_icon.vue';
export default {
components: {
......
/* global monaco */
import Disposable from './disposable';
import eventHub from 'ee/ide/eventhub'; // eslint-disable-line import/first
import eventHub from '../../eventhub';
export default class Model {
constructor(monaco, file) {
......@@ -11,16 +10,16 @@ export default class Model {
this.content = file.content !== '' ? file.content : file.raw;
this.disposable.add(
this.originalModel = this.monaco.editor.createModel(
(this.originalModel = this.monaco.editor.createModel(
this.file.raw,
undefined,
new this.monaco.Uri(null, null, `original/${this.file.path}`),
),
this.model = this.monaco.editor.createModel(
)),
(this.model = this.monaco.editor.createModel(
this.content,
undefined,
new this.monaco.Uri(null, null, this.file.path),
),
)),
);
this.events = new Map();
......@@ -29,7 +28,10 @@ export default class Model {
this.dispose = this.dispose.bind(this);
eventHub.$on(`editor.update.model.dispose.${this.file.path}`, this.dispose);
eventHub.$on(`editor.update.model.content.${this.file.path}`, this.updateContent);
eventHub.$on(
`editor.update.model.content.${this.file.path}`,
this.updateContent,
);
}
get url() {
......@@ -63,9 +65,7 @@ export default class Model {
onChange(cb) {
this.events.set(
this.path,
this.disposable.add(
this.model.onDidChangeContent(e => cb(this, e)),
),
this.disposable.add(this.model.onDidChangeContent(e => cb(this, e))),
);
}
......@@ -78,7 +78,13 @@ export default class Model {
this.disposable.dispose();
this.events.clear();
eventHub.$off(`editor.update.model.dispose.${this.file.path}`, this.dispose);
eventHub.$off(`editor.update.model.content.${this.file.path}`, this.updateContent);
eventHub.$off(
`editor.update.model.dispose.${this.file.path}`,
this.dispose,
);
eventHub.$off(
`editor.update.model.content.${this.file.path}`,
this.updateContent,
);
}
}
import eventHub from 'ee/ide/eventhub';
import eventHub from '../../eventhub';
import Disposable from './disposable';
import Model from './model';
......@@ -26,7 +26,10 @@ export default class ModelManager {
this.models.set(model.path, model);
this.disposable.add(model);
eventHub.$on(`editor.update.model.dispose.${file.path}`, this.removeCachedModel.bind(this, file));
eventHub.$on(
`editor.update.model.dispose.${file.path}`,
this.removeCachedModel.bind(this, file),
);
return model;
}
......@@ -34,7 +37,10 @@ export default class ModelManager {
removeCachedModel(file) {
this.models.delete(file.path);
eventHub.$off(`editor.update.model.dispose.${file.path}`, this.removeCachedModel);
eventHub.$off(
`editor.update.model.dispose.${file.path}`,
this.removeCachedModel,
);
}
dispose() {
......
import { normalizeHeaders } from '~/lib/utils/common_utils';
import flash from '~/flash';
import eventHub from 'ee/ide/eventhub';
import eventHub from '../../eventhub';
import service from '../../services';
import * as types from '../mutation_types';
import router from '../../ide_router';
......
......@@ -4,8 +4,7 @@ import state from './state';
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
import commitModule from 'ee/ide/stores/modules/commit'; // eslint-disable-line import/first
import commitModule from './modules/commit';
Vue.use(Vuex);
......
import $ from 'jquery';
import { sprintf, __ } from '~/locale';
import * as rootTypes from 'ee/ide/stores/mutation_types';
import { createCommitPayload, createNewMergeRequestUrl } from 'ee/ide/stores/utils';
import router from 'ee/ide/ide_router';
import service from 'ee/ide/services';
import flash from '~/flash';
import { stripHtml } from '~/lib/utils/text_utility';
import * as rootTypes from '../../mutation_types';
import { createCommitPayload, createNewMergeRequestUrl } from '../../utils';
import router from '../../../ide_router';
import service from '../../../services';
import * as types from './mutation_types';
import * as consts from './constants';
import eventHub from 'ee/ide/eventhub'; // eslint-disable-line import/first
import eventHub from '../../../eventhub';
export const updateCommitMessage = ({ commit }, message) => {
commit(types.UPDATE_COMMIT_MESSAGE, message);
......@@ -29,16 +28,18 @@ export const updateBranchName = ({ commit }, branchName) => {
export const setLastCommitMessage = ({ rootState, commit }, data) => {
const currentProject = rootState.projects[rootState.currentProjectId];
const commitStats = data.stats ?
sprintf(
__('with %{additions} additions, %{deletions} deletions.'),
{ additions: data.stats.additions, deletions: data.stats.deletions },
)
const commitStats = data.stats
? sprintf(__('with %{additions} additions, %{deletions} deletions.'), {
additions: data.stats.additions, // eslint-disable-line indent
deletions: data.stats.deletions, // eslint-disable-line indent
}) // eslint-disable-line indent
: '';
const commitMsg = sprintf(
__('Your changes have been committed. Commit %{commitId} %{commitStats}'),
{
commitId: `<a href="${currentProject.web_url}/commit/${data.short_id}" class="commit-sha">${data.short_id}</a>`,
commitId: `<a href="${currentProject.web_url}/commit/${
data.short_id
}" class="commit-sha">${data.short_id}</a>`,
commitStats,
},
false,
......@@ -53,7 +54,9 @@ export const checkCommitStatus = ({ rootState }) =>
.then(({ data }) => {
const { id } = data.commit;
const selectedBranch =
rootState.projects[rootState.currentProjectId].branches[rootState.currentBranchId];
rootState.projects[rootState.currentProjectId].branches[
rootState.currentBranchId
];
if (selectedBranch.workingReference !== id) {
return true;
......@@ -61,7 +64,16 @@ export const checkCommitStatus = ({ rootState }) =>
return false;
})
.catch(() => flash(__('Error checking branch data. Please try again.'), 'alert', document, null, false, true));
.catch(() =>
flash(
__('Error checking branch data. Please try again.'),
'alert',
document,
null,
false,
true,
),
);
export const updateFilesAfterCommit = (
{ commit, dispatch, state, rootState, rootGetters },
......@@ -78,89 +90,129 @@ export const updateFilesAfterCommit = (
},
};
commit(rootTypes.SET_BRANCH_WORKING_REFERENCE, {
projectId: rootState.currentProjectId,
branchId: rootState.currentBranchId,
reference: data.id,
}, { root: true });
commit(
rootTypes.SET_BRANCH_WORKING_REFERENCE,
{
projectId: rootState.currentProjectId,
branchId: rootState.currentBranchId,
reference: data.id,
},
{ root: true },
);
rootState.changedFiles.forEach((entry) => {
commit(rootTypes.SET_LAST_COMMIT_DATA, {
entry,
lastCommit,
}, { root: true });
rootState.changedFiles.forEach(entry => {
commit(
rootTypes.SET_LAST_COMMIT_DATA,
{
entry,
lastCommit,
},
{ root: true },
);
eventHub.$emit(`editor.update.model.content.${entry.path}`, entry.content);
commit(rootTypes.SET_FILE_RAW_DATA, {
file: entry,
raw: entry.content,
}, { root: true });
commit(rootTypes.TOGGLE_FILE_CHANGED, {
file: entry,
changed: false,
}, { root: true });
commit(
rootTypes.SET_FILE_RAW_DATA,
{
file: entry,
raw: entry.content,
},
{ root: true },
);
commit(
rootTypes.TOGGLE_FILE_CHANGED,
{
file: entry,
changed: false,
},
{ root: true },
);
});
commit(rootTypes.REMOVE_ALL_CHANGES_FILES, null, { root: true });
if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH) {
router.push(`/project/${rootState.currentProjectId}/blob/${branch}/${rootGetters.activeFile.path}`);
router.push(
`/project/${rootState.currentProjectId}/blob/${branch}/${
rootGetters.activeFile.path
}`,
);
}
dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH);
};
export const commitChanges = ({ commit, state, getters, dispatch, rootState }) => {
export const commitChanges = ({
commit,
state,
getters,
dispatch,
rootState,
}) => {
const newBranch = state.commitAction !== consts.COMMIT_TO_CURRENT_BRANCH;
const payload = createCommitPayload(getters.branchName, newBranch, state, rootState);
const getCommitStatus = newBranch ? Promise.resolve(false) : dispatch('checkCommitStatus');
const payload = createCommitPayload(
getters.branchName,
newBranch,
state,
rootState,
);
const getCommitStatus = newBranch
? Promise.resolve(false)
: dispatch('checkCommitStatus');
commit(types.UPDATE_LOADING, true);
return getCommitStatus.then(branchChanged => new Promise((resolve) => {
if (branchChanged) {
// show the modal with a Bootstrap call
$('#ide-create-branch-modal').modal('show');
} else {
resolve();
}
}))
.then(() => service.commit(rootState.currentProjectId, payload))
.then(({ data }) => {
commit(types.UPDATE_LOADING, false);
if (!data.short_id) {
flash(data.message, 'alert', document, null, false, true);
return;
}
dispatch('setLastCommitMessage', data);
dispatch('updateCommitMessage', '');
if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR) {
dispatch(
'redirectToUrl',
createNewMergeRequestUrl(
rootState.projects[rootState.currentProjectId].web_url,
getters.branchName,
rootState.currentBranchId,
),
{ root: true },
);
} else {
dispatch('updateFilesAfterCommit', { data, branch: getters.branchName });
}
})
.catch((err) => {
let errMsg = __('Error committing changes. Please try again.');
if (err.response.data && err.response.data.message) {
errMsg += ` (${stripHtml(err.response.data.message)})`;
}
flash(errMsg, 'alert', document, null, false, true);
window.dispatchEvent(new Event('resize'));
commit(types.UPDATE_LOADING, false);
});
return getCommitStatus
.then(
branchChanged =>
new Promise(resolve => {
if (branchChanged) {
// show the modal with a Bootstrap call
$('#ide-create-branch-modal').modal('show');
} else {
resolve();
}
}),
)
.then(() => service.commit(rootState.currentProjectId, payload))
.then(({ data }) => {
commit(types.UPDATE_LOADING, false);
if (!data.short_id) {
flash(data.message, 'alert', document, null, false, true);
return;
}
dispatch('setLastCommitMessage', data);
dispatch('updateCommitMessage', '');
if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR) {
dispatch(
'redirectToUrl',
createNewMergeRequestUrl(
rootState.projects[rootState.currentProjectId].web_url,
getters.branchName,
rootState.currentBranchId,
),
{ root: true },
);
} else {
dispatch('updateFilesAfterCommit', {
data,
branch: getters.branchName,
});
}
})
.catch(err => {
let errMsg = __('Error committing changes. Please try again.');
if (err.response.data && err.response.data.message) {
errMsg += ` (${stripHtml(err.response.data.message)})`;
}
flash(errMsg, 'alert', document, null, false, true);
window.dispatchEvent(new Event('resize'));
commit(types.UPDATE_LOADING, false);
});
};
/* eslint-disable comma-dangle, no-unused-vars, class-methods-use-this, quotes, consistent-return, func-names, prefer-arrow-callback, space-before-function-paren, max-len */
import $ from 'jquery';
import Cookies from 'js-cookie';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import flash from '../flash';
......@@ -10,7 +9,6 @@ export default class Profile {
constructor({ form } = {}) {
this.onSubmitForm = this.onSubmitForm.bind(this);
this.form = form || $('.edit-user');
this.newRepoActivated = Cookies.get('new_repo');
this.setRepoRadio();
this.bindEvents();
this.initAvatarGlCrop();
......@@ -23,21 +21,28 @@ export default class Profile {
modalCrop: '.modal-profile-crop',
pickImageEl: '.js-choose-user-avatar-button',
uploadImageBtn: '.js-upload-user-avatar',
modalCropImg: '.modal-profile-crop-image'
modalCropImg: '.modal-profile-crop-image',
};
this.avatarGlCrop = $('.js-user-avatar-input').glCrop(cropOpts).data('glcrop');
this.avatarGlCrop = $('.js-user-avatar-input')
.glCrop(cropOpts)
.data('glcrop');
}
bindEvents() {
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
$('input[name="user[multi_file]"]').on('change', this.setNewRepoCookie);
$('.js-preferences-form').on(
'change.preference',
'input[type=radio]',
this.submitForm,
);
$('#user_notification_email').on('change', this.submitForm);
$('#user_notified_of_own_activity').on('change', this.submitForm);
this.form.on('submit', this.onSubmitForm);
}
submitForm() {
return $(this).parents('form').submit();
return $(this)
.parents('form')
.submit();
}
onSubmitForm(e) {
......@@ -59,21 +64,13 @@ export default class Profile {
url: this.form.attr('action'),
data: formData,
})
.then(({ data }) => flash(data.message, 'notice'))
.then(() => {
window.scrollTo(0, 0);
// Enable submit button after requests ends
self.form.find(':input[disabled]').enable();
})
.catch(error => flash(error.message));
}
setNewRepoCookie() {
if (this.value === 'off') {
Cookies.remove('new_repo');
} else {
Cookies.set('new_repo', true, { expires_in: 365 });
}
.then(({ data }) => flash(data.message, 'notice'))
.then(() => {
window.scrollTo(0, 0);
// Enable submit button after requests ends
self.form.find(':input[disabled]').enable();
})
.catch(error => flash(error.message));
}
setRepoRadio() {
......
class IdeController < ApplicationController
layout 'nav_only'
before_action :check_ide_available!
def index
end
private
def check_ide_available!
render_404 unless License.feature_available?(:ide)
end
end
......@@ -33,6 +33,17 @@ module BlobHelper
ref)
end
def ide_edit_button(project = @project, ref = @ref, path = @path, options = {})
return unless blob = readable_blob(options, path, project, ref)
edit_button_tag(blob,
'btn btn-default',
_('Web IDE'),
ide_edit_path(project, ref, path, options),
project,
ref)
end
def modify_file_button(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:)
return unless current_user
......
......@@ -2,8 +2,6 @@
- @content_class = "limit-container-width" unless fluid_layout
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
= render "profiles/preferences/ide", f: f
.col-lg-4.application-theme
%h4.prepend-top-0
GitLab navigation theme
......
......@@ -14,11 +14,7 @@
= lock_file_link(html_options: { class: 'btn btn-sm path-lock' })
= edit_blob_button
-# EE-specific
= ide_edit_button
-# EE-specific
- if current_user
= replace_blob_link
= delete_blob_link
......
......@@ -78,11 +78,8 @@
= render 'projects/find_file_link'
-## EE-specific
- if show_new_ide?(@project)
= succeed " " do
= link_to ide_edit_path(@project, @id), class: 'btn btn-default' do
= _('Web IDE')
-## EE-specific
= succeed " " do
= link_to ide_edit_path(@project, @id), class: 'btn btn-default' do
= _('Web IDE')
= render 'projects/buttons/download', project: @project, ref: @ref
......@@ -77,10 +77,8 @@ Rails.application.routes.draw do
# UserCallouts
resources :user_callouts, only: [:create]
## EE-specific
get 'ide' => 'ide#index'
get 'ide/*vueroute' => 'ide#index', format: false
## EE-specific
end
# Koding route
......
This diff is collapsed.
module IdeHelper
def show_new_ide?(project)
cookies["new_repo"] == "true" && project.feature_available?(:ide)
end
def ide_edit_button(project = @project, ref = @ref, path = @path, options = {})
return unless show_new_ide?(project)
return unless blob = readable_blob(options, path, project, ref)
common_classes = "btn js-edit-ide #{options[:extra_class]}"
edit_button_tag(blob,
common_classes,
_('Web IDE'),
ide_edit_path(project, ref, path, options),
project,
ref)
end
end
- if License.feature_available?(:ide)
.col-lg-4
%h4.prepend-top-0
Web IDE (Beta)
%p
Enable the new web IDE on this device to make it possible to open and edit multiple files with a single commit.
- if Gitlab::CurrentSettings.should_check_namespace_plan?
Available for public GitLab.com projects or those using Gold.
.col-lg-8.multi-file-editor-options
= label_tag do
.preview.append-bottom-10= image_tag "multi-editor-off.png"
= f.radio_button :multi_file, "off", checked: true
Off
= label_tag do
.preview.append-bottom-10= image_tag "multi-editor-on.png"
= f.radio_button :multi_file, "on", checked: false
On
.col-sm-12
%hr
......@@ -5,13 +5,9 @@ feature 'Multi-file editor new directory', :js do
let(:project) { create(:project, :repository) }
before do
stub_licensed_features(ide: true)
project.add_master(user)
sign_in(user)
set_cookie('new_repo', 'true')
visit project_tree_path(project, :master)
wait_for_requests
......
......@@ -5,13 +5,9 @@ feature 'Multi-file editor new file', :js do
let(:project) { create(:project, :repository) }
before do
stub_licensed_features(ide: true)
project.add_master(user)
sign_in(user)
set_cookie('new_repo', 'true')
visit project_path(project)
wait_for_requests
......
......@@ -7,13 +7,9 @@ feature 'Multi-file editor upload file', :js do
let(:img_file) { File.join(Rails.root, 'spec', 'fixtures', 'dk.png') }
before do
stub_licensed_features(ide: true)
project.add_master(user)
sign_in(user)
set_cookie('new_repo', 'true')
visit project_tree_path(project, :master)
wait_for_requests
......
import Vue from 'vue';
import changedFileIcon from 'ee/ide/components/changed_file_icon.vue';
import changedFileIcon from '~/ide/components/changed_file_icon.vue';
import createComponent from 'spec/helpers/vue_mount_component_helper';
describe('IDE changed file icon', () => {
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import commitActions from 'ee/ide/components/commit_sidebar/actions.vue';
import store from '~/ide/stores';
import commitActions from '~/ide/components/commit_sidebar/actions.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from 'spec/ide/helpers';
describe('IDE commit sidebar actions', () => {
let vm;
beforeEach((done) => {
beforeEach(done => {
const Component = Vue.extend(commitActions);
vm = createComponentWithStore(Component, store);
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import listCollapsed from 'ee/ide/components/commit_sidebar/list_collapsed.vue';
import store from '~/ide/stores';
import listCollapsed from '~/ide/components/commit_sidebar/list_collapsed.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file } from '../../helpers';
......
import Vue from 'vue';
import listItem from 'ee/ide/components/commit_sidebar/list_item.vue';
import router from 'ee/ide/ide_router';
import listItem from '~/ide/components/commit_sidebar/list_item.vue';
import router from '~/ide/ide_router';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { file } from '../../helpers';
......@@ -23,7 +23,9 @@ describe('Multi-file editor commit sidebar list item', () => {
});
it('renders file path', () => {
expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path);
expect(
vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim(),
).toBe(f.path);
});
it('calls discardFileChanges when clicking discard button', () => {
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import commitSidebarList from 'ee/ide/components/commit_sidebar/list.vue';
import store from '~/ide/stores';
import commitSidebarList from '~/ide/components/commit_sidebar/list.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file } from '../../helpers';
......@@ -25,7 +25,7 @@ describe('Multi-file editor commit sidebar list', () => {
});
describe('with a list of files', () => {
beforeEach((done) => {
beforeEach(done => {
const f = file('file name');
f.changed = true;
vm.fileList.push(f);
......@@ -39,7 +39,7 @@ describe('Multi-file editor commit sidebar list', () => {
});
describe('collapsed', () => {
beforeEach((done) => {
beforeEach(done => {
vm.$store.state.rightPanelCollapsed = true;
Vue.nextTick(done);
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import radioGroup from 'ee/ide/components/commit_sidebar/radio_group.vue';
import store from '~/ide/stores';
import radioGroup from '~/ide/components/commit_sidebar/radio_group.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from 'spec/ide/helpers';
describe('IDE commit sidebar radio group', () => {
let vm;
beforeEach((done) => {
beforeEach(done => {
const Component = Vue.extend(radioGroup);
store.state.commit.commitAction = '2';
......@@ -33,7 +33,7 @@ describe('IDE commit sidebar radio group', () => {
expect(vm.$el.textContent).toContain('test');
});
it('uses slot if label is not present', (done) => {
it('uses slot if label is not present', done => {
vm.$destroy();
vm = new Vue({
......@@ -59,7 +59,7 @@ describe('IDE commit sidebar radio group', () => {
});
});
it('updates store when changing radio button', (done) => {
it('updates store when changing radio button', done => {
vm.$el.querySelector('input').dispatchEvent(new Event('change'));
Vue.nextTick(() => {
......@@ -69,7 +69,7 @@ describe('IDE commit sidebar radio group', () => {
});
});
it('renders helpText tooltip', (done) => {
it('renders helpText tooltip', done => {
vm.helpText = 'help text';
Vue.nextTick(() => {
......@@ -83,7 +83,7 @@ describe('IDE commit sidebar radio group', () => {
});
describe('with input', () => {
beforeEach((done) => {
beforeEach(done => {
vm.$destroy();
const Component = Vue.extend(radioGroup);
......@@ -106,7 +106,7 @@ describe('IDE commit sidebar radio group', () => {
expect(vm.$el.querySelector('.form-control')).not.toBeNull();
});
it('hides input when commitAction doesnt match value', (done) => {
it('hides input when commitAction doesnt match value', done => {
store.state.commit.commitAction = '2';
Vue.nextTick(() => {
......@@ -115,7 +115,7 @@ describe('IDE commit sidebar radio group', () => {
});
});
it('updates branch name in store on input', (done) => {
it('updates branch name in store on input', done => {
const input = vm.$el.querySelector('.form-control');
input.value = 'testing-123';
input.dispatchEvent(new Event('input'));
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import ideContextBar from 'ee/ide/components/ide_context_bar.vue';
import store from '~/ide/stores';
import ideContextBar from '~/ide/components/ide_context_bar.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
describe('Multi-file editor right context bar', () => {
......@@ -24,7 +24,7 @@ describe('Multi-file editor right context bar', () => {
});
describe('collapsed', () => {
beforeEach((done) => {
beforeEach(done => {
vm.$store.state.rightPanelCollapsed = true;
Vue.nextTick(done);
......
import Vue from 'vue';
import ideExternalLinks from 'ee/ide/components/ide_external_links.vue';
import ideExternalLinks from '~/ide/components/ide_external_links.vue';
import createComponent from 'spec/helpers/vue_mount_component_helper';
describe('ide external links component', () => {
......
import Vue from 'vue';
import ideRepoTree from 'ee/ide/components/ide_repo_tree.vue';
import ideRepoTree from '~/ide/components/ide_repo_tree.vue';
import createComponent from '../../helpers/vue_mount_component_helper';
import { file } from '../helpers';
......@@ -29,11 +29,13 @@ describe('IdeRepoTree', () => {
expect(vm.$el.querySelector('.file')).not.toBeNull();
});
it('renders 3 loading files if tree is loading', (done) => {
it('renders 3 loading files if tree is loading', done => {
tree.loading = true;
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toEqual(3);
expect(
vm.$el.querySelectorAll('.multi-file-loading-container').length,
).toEqual(3);
done();
});
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import ideSidebar from 'ee/ide/components/ide_side_bar.vue';
import store from '~/ide/stores';
import ideSidebar from '~/ide/components/ide_side_bar.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../helpers';
......@@ -20,15 +20,21 @@ describe('IdeSidebar', () => {
});
it('renders a sidebar', () => {
expect(vm.$el.querySelector('.multi-file-commit-panel-inner')).not.toBeNull();
expect(
vm.$el.querySelector('.multi-file-commit-panel-inner'),
).not.toBeNull();
});
it('renders loading icon component', (done) => {
it('renders loading icon component', done => {
vm.$store.state.loading = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.multi-file-loading-container')).not.toBeNull();
expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3);
expect(
vm.$el.querySelector('.multi-file-loading-container'),
).not.toBeNull();
expect(
vm.$el.querySelectorAll('.multi-file-loading-container').length,
).toBe(3);
done();
});
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import ide from 'ee/ide/components/ide.vue';
import store from '~/ide/stores';
import ide from '~/ide/components/ide.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file, resetStore } from '../helpers';
......@@ -27,7 +27,7 @@ describe('ide component', () => {
expect(vm.$el.querySelector('.panel-right')).toBeNull();
});
it('renders panel right when files are open', (done) => {
it('renders panel right when files are open', done => {
vm.$store.state.trees['abcproject/mybranch'] = {
tree: [file()],
};
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import newDropdown from 'ee/ide/components/new_dropdown/index.vue';
import store from '~/ide/stores';
import newDropdown from '~/ide/components/new_dropdown/index.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../../helpers';
......
import Vue from 'vue';
import modal from 'ee/ide/components/new_dropdown/modal.vue';
import modal from '~/ide/components/new_dropdown/modal.vue';
import createComponent from 'spec/helpers/vue_mount_component_helper';
describe('new file modal component', () => {
......@@ -10,7 +10,7 @@ describe('new file modal component', () => {
vm.$destroy();
});
['tree', 'blob'].forEach((type) => {
['tree', 'blob'].forEach(type => {
describe(type, () => {
beforeEach(() => {
vm = createComponent(Component, {
......@@ -25,19 +25,25 @@ describe('new file modal component', () => {
it(`sets modal title as ${type}`, () => {
const title = type === 'tree' ? 'directory' : 'file';
expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(`Create new ${title}`);
expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(
`Create new ${title}`,
);
});
it(`sets button label as ${type}`, () => {
const title = type === 'tree' ? 'directory' : 'file';
expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(`Create ${title}`);
expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(
`Create ${title}`,
);
});
it(`sets form label as ${type}`, () => {
const title = type === 'tree' ? 'Directory' : 'File';
expect(vm.$el.querySelector('.label-light').textContent.trim()).toBe(`${title} name`);
expect(vm.$el.querySelector('.label-light').textContent.trim()).toBe(
`${title} name`,
);
});
describe('createEntryInStore', () => {
......@@ -59,11 +65,15 @@ describe('new file modal component', () => {
it('focuses field on mount', () => {
document.body.innerHTML += '<div class="js-test"></div>';
vm = createComponent(Component, {
type: 'tree',
branchId: 'master',
path: '',
}, '.js-test');
vm = createComponent(
Component,
{
type: 'tree',
branchId: 'master',
path: '',
},
'.js-test',
);
expect(document.activeElement).toBe(vm.$refs.fieldName);
......
import Vue from 'vue';
import upload from 'ee/ide/components/new_dropdown/upload.vue';
import upload from '~/ide/components/new_dropdown/upload.vue';
import createComponent from 'spec/helpers/vue_mount_component_helper';
describe('new dropdown upload', () => {
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import service from 'ee/ide/services';
import repoCommitSection from 'ee/ide/components/repo_commit_section.vue';
import store from '~/ide/stores';
import service from '~/ide/services';
import repoCommitSection from '~/ide/components/repo_commit_section.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import { file, resetStore } from '../helpers';
......@@ -31,30 +31,35 @@ describe('RepoCommitSection', () => {
vm.$store.state.rightPanelCollapsed = false;
vm.$store.state.currentBranch = 'master';
vm.$store.state.changedFiles = [file('file1'), file('file2')];
vm.$store.state.changedFiles.forEach(f => Object.assign(f, {
changed: true,
content: 'testing',
}));
vm.$store.state.changedFiles.forEach(f =>
Object.assign(f, {
changed: true,
content: 'testing',
}),
);
return vm.$mount();
}
beforeEach((done) => {
beforeEach(done => {
vm = createComponent();
spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({
headers: {
'page-title': 'test',
},
json: () => Promise.resolve({
last_commit_path: 'last_commit_path',
parent_tree_url: 'parent_tree_url',
path: '/',
trees: [{ name: 'tree' }],
blobs: [{ name: 'blob' }],
submodules: [{ name: 'submodule' }],
spyOn(service, 'getTreeData').and.returnValue(
Promise.resolve({
headers: {
'page-title': 'test',
},
json: () =>
Promise.resolve({
last_commit_path: 'last_commit_path',
parent_tree_url: 'parent_tree_url',
path: '/',
trees: [{ name: 'tree' }],
blobs: [{ name: 'blob' }],
submodules: [{ name: 'submodule' }],
}),
}),
}));
);
Vue.nextTick(done);
});
......@@ -75,27 +80,35 @@ describe('RepoCommitSection', () => {
committedStateSvgPath: 'svg',
}).$mount();
expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toContain('No changes');
expect(vm.$el.querySelector('.js-empty-state img').getAttribute('src')).toBe('nochangessvg');
expect(
vm.$el.querySelector('.js-empty-state').textContent.trim(),
).toContain('No changes');
expect(
vm.$el.querySelector('.js-empty-state img').getAttribute('src'),
).toBe('nochangessvg');
});
});
it('renders a commit section', () => {
const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
const changedFileElements = [
...vm.$el.querySelectorAll('.multi-file-commit-list li'),
];
const submitCommit = vm.$el.querySelector('form .btn');
expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
expect(changedFileElements.length).toEqual(2);
changedFileElements.forEach((changedFile, i) => {
expect(changedFile.textContent.trim()).toContain(vm.$store.state.changedFiles[i].path);
expect(changedFile.textContent.trim()).toContain(
vm.$store.state.changedFiles[i].path,
);
});
expect(submitCommit.disabled).toBeTruthy();
expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeNull();
});
it('updates commitMessage in store on input', (done) => {
it('updates commitMessage in store on input', done => {
const textarea = vm.$el.querySelector('textarea');
textarea.value = 'testing commit message';
......@@ -104,7 +117,9 @@ describe('RepoCommitSection', () => {
getSetTimeoutPromise()
.then(() => {
expect(vm.$store.state.commit.commitMessage).toBe('testing commit message');
expect(vm.$store.state.commit.commitMessage).toBe(
'testing commit message',
);
})
.then(done)
.catch(done.fail);
......@@ -112,10 +127,12 @@ describe('RepoCommitSection', () => {
describe('discard draft button', () => {
it('hidden when commitMessage is empty', () => {
expect(vm.$el.querySelector('.multi-file-commit-form .btn-default')).toBeNull();
expect(
vm.$el.querySelector('.multi-file-commit-form .btn-default'),
).toBeNull();
});
it('resets commitMessage when clicking discard button', (done) => {
it('resets commitMessage when clicking discard button', done => {
vm.$store.state.commit.commitMessage = 'testing commit message';
getSetTimeoutPromise()
......@@ -124,7 +141,9 @@ describe('RepoCommitSection', () => {
})
.then(Vue.nextTick)
.then(() => {
expect(vm.$store.state.commit.commitMessage).not.toBe('testing commit message');
expect(vm.$store.state.commit.commitMessage).not.toBe(
'testing commit message',
);
})
.then(done)
.catch(done.fail);
......@@ -136,7 +155,7 @@ describe('RepoCommitSection', () => {
spyOn(vm, 'commitChanges');
});
it('calls commitChanges', (done) => {
it('calls commitChanges', done => {
vm.$store.state.commit.commitMessage = 'testing commit message';
getSetTimeoutPromise()
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import repoEditor from 'ee/ide/components/repo_editor.vue';
import monacoLoader from 'ee/ide/monaco_loader';
import Editor from 'ee/ide/lib/editor';
import store from '~/ide/stores';
import repoEditor from '~/ide/components/repo_editor.vue';
import monacoLoader from '~/ide/monaco_loader';
import Editor from '~/ide/lib/editor';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
import { file, resetStore } from '../helpers';
describe('RepoEditor', () => {
let vm;
beforeEach((done) => {
beforeEach(done => {
const f = file();
const RepoEditor = Vue.extend(repoEditor);
......@@ -39,7 +39,7 @@ describe('RepoEditor', () => {
Editor.editorInstance.modelManager.dispose();
});
it('renders an ide container', (done) => {
it('renders an ide container', done => {
Vue.nextTick(() => {
expect(vm.shouldHideEditor).toBeFalsy();
......@@ -48,7 +48,7 @@ describe('RepoEditor', () => {
});
describe('when open file is binary and not raw', () => {
beforeEach((done) => {
beforeEach(done => {
vm.file.binary = true;
vm.$nextTick(done);
......@@ -64,7 +64,7 @@ describe('RepoEditor', () => {
});
describe('createEditorInstance', () => {
it('calls createInstance when viewer is editor', (done) => {
it('calls createInstance when viewer is editor', done => {
spyOn(vm.editor, 'createInstance');
vm.createEditorInstance();
......@@ -76,7 +76,7 @@ describe('RepoEditor', () => {
});
});
it('calls createDiffInstance when viewer is diff', (done) => {
it('calls createDiffInstance when viewer is diff', done => {
vm.$store.state.viewer = 'diff';
spyOn(vm.editor, 'createDiffInstance');
......@@ -124,7 +124,7 @@ describe('RepoEditor', () => {
expect(vm.model.events.size).toBe(1);
});
it('updates state when model content changed', (done) => {
it('updates state when model content changed', done => {
vm.model.setValue('testing 123');
setTimeout(() => {
......
import Vue from 'vue';
import repoFileButtons from 'ee/ide/components/repo_file_buttons.vue';
import repoFileButtons from '~/ide/components/repo_file_buttons.vue';
import createVueComponent from '../../helpers/vue_mount_component_helper';
import { file } from '../helpers';
......@@ -23,7 +23,7 @@ describe('RepoFileButtons', () => {
vm.$destroy();
});
it('renders Raw, Blame, History, Permalink and Preview toggle', (done) => {
it('renders Raw, Blame, History, Permalink and Preview toggle', done => {
vm = createComponent();
vm.$nextTick(() => {
......@@ -37,7 +37,9 @@ describe('RepoFileButtons', () => {
expect(blame.textContent.trim()).toEqual('Blame');
expect(history.href).toMatch(`/${activeFile.commitsPath}`);
expect(history.textContent.trim()).toEqual('History');
expect(vm.$el.querySelector('.permalink').textContent.trim()).toEqual('Permalink');
expect(vm.$el.querySelector('.permalink').textContent.trim()).toEqual(
'Permalink',
);
done();
});
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import repoFile from 'ee/ide/components/repo_file.vue';
import router from 'ee/ide/ide_router';
import store from '~/ide/stores';
import repoFile from '~/ide/components/repo_file.vue';
import router from '~/ide/ide_router';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
import { file } from '../helpers';
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import repoLoadingFile from 'ee/ide/components/repo_loading_file.vue';
import store from '~/ide/stores';
import repoLoadingFile from '~/ide/components/repo_loading_file.vue';
import { resetStore } from '../helpers';
describe('RepoLoadingFile', () => {
......@@ -22,7 +22,7 @@ describe('RepoLoadingFile', () => {
}
function assertColumns(columns) {
columns.forEach((column) => {
columns.forEach(column => {
const container = column.querySelector('.animation-container');
const lines = [...container.querySelectorAll(':scope > div')];
......@@ -46,7 +46,7 @@ describe('RepoLoadingFile', () => {
assertColumns(columns);
});
it('renders 1 column of animated LoC if isMini', (done) => {
it('renders 1 column of animated LoC if isMini', done => {
vm = createComponent();
vm.$store.state.leftPanelCollapsed = true;
vm.$store.state.openFiles.push('test');
......
import Vue from 'vue';
import store from 'ee/ide/stores';
import repoTab from 'ee/ide/components/repo_tab.vue';
import router from 'ee/ide/ide_router';
import store from '~/ide/stores';
import repoTab from '~/ide/components/repo_tab.vue';
import router from '~/ide/ide_router';
import { file, resetStore } from '../helpers';
describe('RepoTab', () => {
......@@ -62,7 +62,7 @@ describe('RepoTab', () => {
expect(vm.closeFile).toHaveBeenCalledWith(vm.tab.path);
});
it('changes icon on hover', (done) => {
it('changes icon on hover', done => {
const tab = file();
tab.changed = true;
vm = createComponent({
......@@ -112,13 +112,15 @@ describe('RepoTab', () => {
});
it('renders a tooltip', () => {
expect(vm.$el.querySelector('span:nth-child(2)').dataset.originalTitle).toContain('Locked by testuser');
expect(
vm.$el.querySelector('span:nth-child(2)').dataset.originalTitle,
).toContain('Locked by testuser');
});
});
describe('methods', () => {
describe('closeTab', () => {
it('closes tab if file has changed', (done) => {
it('closes tab if file has changed', done => {
const tab = file();
tab.changed = true;
tab.opened = true;
......@@ -140,7 +142,7 @@ describe('RepoTab', () => {
});
});
it('closes tab when clicking close btn', (done) => {
it('closes tab when clicking close btn', done => {
const tab = file('lose');
tab.opened = true;
vm = createComponent({
......
import Vue from 'vue';
import repoTabs from 'ee/ide/components/repo_tabs.vue';
import repoTabs from '~/ide/components/repo_tabs.vue';
import createComponent from '../../helpers/vue_mount_component_helper';
import { file } from '../helpers';
......
import { decorateData } from 'ee/ide/stores/utils';
import state from 'ee/ide/stores/state';
import commitState from 'ee/ide/stores/modules/commit/state';
import { decorateData } from '~/ide/stores/utils';
import state from '~/ide/stores/state';
import commitState from '~/ide/stores/modules/commit/state';
export const resetStore = (store) => {
export const resetStore = store => {
const newState = {
...state(),
commit: commitState(),
......@@ -10,12 +10,13 @@ export const resetStore = (store) => {
store.replaceState(newState);
};
export const file = (name = 'name', id = name, type = '') => decorateData({
id,
type,
icon: 'icon',
url: 'url',
name,
path: name,
lastCommit: {},
});
export const file = (name = 'name', id = name, type = '') =>
decorateData({
id,
type,
icon: 'icon',
url: 'url',
name,
path: name,
lastCommit: {},
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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