Commit 3d1c2c26 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'ce-to-ee-2018-10-26' into 'master'

CE upstream - 2018-10-26 16:28 UTC

Closes gitlab-qa#59

See merge request gitlab-org/gitlab-ee!8111
parents 057b4ce9 8ad1d303
......@@ -81,6 +81,9 @@ Naming/FileName:
- 'locale/unfound_translations.rb'
- 'ee/locale/unfound_translations.rb'
- 'ee/lib/generators/**/*'
- 'qa/qa/scenario/test/integration/ldap_no_tls.rb'
- 'qa/qa/scenario/test/integration/ldap_tls.rb'
IgnoreExecutableScripts: true
AllowedAcronyms:
- EE
......
......@@ -95,29 +95,20 @@ export default {
return awardList.filter(award => award.user.id === this.getUserData.id).length;
},
awardTitle(awardsList) {
const hasReactionByCurrentUser = this.hasReactionByCurrentUser(
awardsList,
);
const hasReactionByCurrentUser = this.hasReactionByCurrentUser(awardsList);
const TOOLTIP_NAME_COUNT = hasReactionByCurrentUser ? 9 : 10;
let awardList = awardsList;
// Filter myself from list if I am awarded.
if (hasReactionByCurrentUser) {
awardList = awardList.filter(
award => award.user.id !== this.getUserData.id,
);
awardList = awardList.filter(award => award.user.id !== this.getUserData.id);
}
// Get only 9-10 usernames to show in tooltip text.
const namesToShow = awardList
.slice(0, TOOLTIP_NAME_COUNT)
.map(award => award.user.name);
const namesToShow = awardList.slice(0, TOOLTIP_NAME_COUNT).map(award => award.user.name);
// Get the remaining list to use in `and x more` text.
const remainingAwardList = awardList.slice(
TOOLTIP_NAME_COUNT,
awardList.length,
);
const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length);
// Add myself to the begining of the list so title will start with You.
if (hasReactionByCurrentUser) {
......@@ -128,9 +119,7 @@ export default {
// We have 10+ awarded user, join them with comma and add `and x more`.
if (remainingAwardList.length) {
title = `${namesToShow.join(', ')}, and ${
remainingAwardList.length
} more.`;
title = `${namesToShow.join(', ')}, and ${remainingAwardList.length} more.`;
} else if (namesToShow.length > 1) {
// Join all names with comma but not the last one, it will be added with and text.
title = namesToShow.slice(0, namesToShow.length - 1).join(', ');
......@@ -170,9 +159,7 @@ export default {
awardName: parsedName,
};
this.toggleAwardRequest(data).catch(() =>
Flash('Something went wrong on our end.'),
);
this.toggleAwardRequest(data).catch(() => Flash('Something went wrong on our end.'));
},
},
};
......
......@@ -68,10 +68,7 @@ export const collapseSystemNotes = notes => {
lastDescriptionSystemNote = note;
lastDescriptionSystemNoteIndex = acc.length;
} else if (lastDescriptionSystemNote) {
const timeDifferenceMinutes = getTimeDifferenceMinutes(
lastDescriptionSystemNote,
note,
);
const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note);
// are they less than 10 minutes appart?
if (timeDifferenceMinutes > 10) {
......
......@@ -4,5 +4,4 @@ import notesModule from './modules';
Vue.use(Vuex);
export default () =>
new Vuex.Store(notesModule());
export default () => new Vuex.Store(notesModule());
<script>
export default {
export default {
props: {
currentRequest: {
type: Object,
......@@ -23,7 +23,7 @@
);
},
},
};
};
</script>
<template>
<div
......
......@@ -9,8 +9,7 @@ export default ({ container }) =>
performanceBarApp: () => import('./components/performance_bar_app.vue'),
},
data() {
const performanceBarData = document.querySelector(this.$options.el)
.dataset;
const performanceBarData = document.querySelector(this.$options.el).dataset;
const store = new PerformanceBarStore();
return {
......
......@@ -11,8 +11,10 @@ export default class PerformanceBarService {
static registerInterceptor(peekUrl, callback) {
const interceptor = response => {
const [fireCallback, requestId, requestUrl] =
PerformanceBarService.callbackParams(response, peekUrl);
const [fireCallback, requestId, requestUrl] = PerformanceBarService.callbackParams(
response,
peekUrl,
);
if (fireCallback) {
callback(requestId, requestUrl);
......@@ -30,10 +32,7 @@ export default class PerformanceBarService {
static removeInterceptor(interceptor) {
axios.interceptors.response.eject(interceptor);
Vue.http.interceptors = _.without(
Vue.http.interceptors,
vueResourceInterceptor,
);
Vue.http.interceptors = _.without(Vue.http.interceptors, vueResourceInterceptor);
}
static callbackParams(response, peekUrl) {
......
......@@ -32,8 +32,6 @@ export default class PerformanceBarStore {
}
canTrackRequest(requestUrl) {
return (
this.requests.filter(request => request.url === requestUrl).length < 2
);
return this.requests.filter(request => request.url === requestUrl).length < 2;
}
}
<script>
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { __, s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { __, s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
export default {
export default {
components: {
DeprecatedModal,
},
......@@ -72,7 +72,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
this.$refs.form.submit();
},
},
};
};
</script>
<template>
......
......@@ -4,20 +4,35 @@ import $ from 'jquery';
import 'cropper';
import _ from 'underscore';
((global) => {
(global => {
// Matches everything but the file name
const FILENAMEREGEX = /^.*[\\\/]/;
class GitLabCrop {
constructor(input, { filename, previewImage, modalCrop, pickImageEl, uploadImageBtn, modalCropImg,
exportWidth = 200, exportHeight = 200, cropBoxWidth = 200, cropBoxHeight = 200 } = {}) {
constructor(
input,
{
filename,
previewImage,
modalCrop,
pickImageEl,
uploadImageBtn,
modalCropImg,
exportWidth = 200,
exportHeight = 200,
cropBoxWidth = 200,
cropBoxHeight = 200,
} = {},
) {
this.onUploadImageBtnClick = this.onUploadImageBtnClick.bind(this);
this.onModalHide = this.onModalHide.bind(this);
this.onModalShow = this.onModalShow.bind(this);
this.onPickImageClick = this.onPickImageClick.bind(this);
this.fileInput = $(input);
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `${this.fileInput.attr('id')}-trigger`);
this.fileInput
.attr('name', `${this.fileInput.attr('name')}-trigger`)
.attr('id', `${this.fileInput.attr('id')}-trigger`);
this.exportWidth = exportWidth;
this.exportHeight = exportHeight;
this.cropBoxWidth = cropBoxWidth;
......@@ -59,7 +74,7 @@ import _ from 'underscore';
btn = this;
return _this.onActionBtnClick(btn);
});
return this.croppedImageBlob = null;
return (this.croppedImageBlob = null);
}
onPickImageClick() {
......@@ -94,9 +109,9 @@ import _ from 'underscore';
width: cropBoxWidth,
height: cropBoxHeight,
left: (container.width - cropBoxWidth) / 2,
top: (container.height - cropBoxHeight) / 2
top: (container.height - cropBoxHeight) / 2,
});
}
},
});
}
......@@ -116,7 +131,7 @@ import _ from 'underscore';
var data, result;
data = $(btn).data();
if (this.modalCropImg.data('cropper') && data.method) {
return result = this.modalCropImg.cropper(data.method, data.option);
return (result = this.modalCropImg.cropper(data.method, data.option));
}
}
......@@ -127,7 +142,7 @@ import _ from 'underscore';
readFile(input) {
var _this, reader;
_this = this;
reader = new FileReader;
reader = new FileReader();
reader.onload = () => {
_this.modalCropImg.attr('src', reader.result);
return _this.modalCrop.modal('show');
......@@ -145,7 +160,7 @@ import _ from 'underscore';
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
type: 'image/png'
type: 'image/png',
});
}
......@@ -157,11 +172,13 @@ import _ from 'underscore';
}
setBlob() {
this.dataURL = this.modalCropImg.cropper('getCroppedCanvas', {
this.dataURL = this.modalCropImg
.cropper('getCroppedCanvas', {
width: 200,
height: 200
}).toDataURL('image/png');
return this.croppedImageBlob = this.dataURLtoBlob(this.dataURL);
height: 200,
})
.toDataURL('image/png');
return (this.croppedImageBlob = this.dataURLtoBlob(this.dataURL));
}
getBlob() {
......
......@@ -26,11 +26,7 @@ export default class Profile {
}
bindEvents() {
$('.js-preferences-form').on(
'change.preference',
'input[type=radio]',
this.submitForm,
);
$('.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);
......
......@@ -46,8 +46,12 @@ export default class ProtectedBranchCreate {
onSelect() {
// Enable submit button
const $branchInput = this.$form.find('input[name="protected_branch[name]"]');
const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]');
const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]');
const $allowedToMergeInput = this.$form.find(
'input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]',
);
const $allowedToPushInput = this.$form.find(
'input[name="protected_branch[push_access_levels_attributes][0][access_level]"]',
);
const completedForm = !(
$branchInput.val() &&
$allowedToMergeInput.length &&
......
......@@ -29,8 +29,12 @@ export default class ProtectedBranchEdit {
}
onSelect() {
const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
const $allowedToMergeInput = this.$wrap.find(
`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`,
);
const $allowedToPushInput = this.$wrap.find(
`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`,
);
// Do not update if one dropdown has not selected any option
if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
......@@ -38,25 +42,36 @@ export default class ProtectedBranchEdit {
this.$allowedToMergeDropdown.disable();
this.$allowedToPushDropdown.disable();
axios.patch(this.$wrap.data('url'), {
axios
.patch(this.$wrap.data('url'), {
protected_branch: {
merge_access_levels_attributes: [{
merge_access_levels_attributes: [
{
id: this.$allowedToMergeDropdown.data('accessLevelId'),
access_level: $allowedToMergeInput.val(),
}],
push_access_levels_attributes: [{
},
],
push_access_levels_attributes: [
{
id: this.$allowedToPushDropdown.data('accessLevelId'),
access_level: $allowedToPushInput.val(),
}],
},
}).then(() => {
],
},
})
.then(() => {
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
}).catch(() => {
})
.catch(() => {
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
flash('Failed to update branch!', 'alert', document.querySelector('.js-protected-branches-list'));
flash(
'Failed to update branch!',
'alert',
document.querySelector('.js-protected-branches-list'),
);
});
}
}
......@@ -40,7 +40,9 @@ export default class ProtectedTagCreate {
const $tagInput = this.$form.find('input[name="protected_tag[name]"]');
const $allowedToCreateInput = this.$form.find('#create_access_levels_attributes');
this.$form.find('input[type="submit"]').prop('disabled', !($tagInput.val() && $allowedToCreateInput.length));
this.$form
.find('input[type="submit"]')
.prop('disabled', !($tagInput.val() && $allowedToCreateInput.length));
}
static getProtectedTags(term, callback) {
......
......@@ -21,23 +21,30 @@ export default class ProtectedTagEdit {
}
onSelect() {
const $allowedToCreateInput = this.$wrap.find(`input[name="${this.$allowedToCreateDropdownButton.data('fieldName')}"]`);
const $allowedToCreateInput = this.$wrap.find(
`input[name="${this.$allowedToCreateDropdownButton.data('fieldName')}"]`,
);
// Do not update if one dropdown has not selected any option
if (!$allowedToCreateInput.length) return;
this.$allowedToCreateDropdownButton.disable();
axios.patch(this.$wrap.data('url'), {
axios
.patch(this.$wrap.data('url'), {
protected_tag: {
create_access_levels_attributes: [{
create_access_levels_attributes: [
{
id: this.$allowedToCreateDropdownButton.data('accessLevelId'),
access_level: $allowedToCreateInput.val(),
}],
},
}).then(() => {
],
},
})
.then(() => {
this.$allowedToCreateDropdownButton.enable();
}).catch(() => {
})
.catch(() => {
this.$allowedToCreateDropdownButton.enable();
flash('Failed to update tag!', 'alert', document.querySelector('.js-protected-tags-list'));
......
<script>
import { mapGetters, mapActions } from 'vuex';
import Flash from '../../flash';
import store from '../stores';
import collapsibleContainer from './collapsible_container.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
import { mapGetters, mapActions } from 'vuex';
import Flash from '../../flash';
import store from '../stores';
import collapsibleContainer from './collapsible_container.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
export default {
export default {
name: 'RegistryListApp',
components: {
collapsibleContainer,
......@@ -18,25 +18,18 @@
},
store,
computed: {
...mapGetters([
'isLoading',
'repos',
]),
...mapGetters(['isLoading', 'repos']),
},
created() {
this.setMainEndpoint(this.endpoint);
},
mounted() {
this.fetchRepos()
.catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS]));
this.fetchRepos().catch(() => Flash(errorMessages[errorMessagesTypes.FETCH_REPOS]));
},
methods: {
...mapActions([
'setMainEndpoint',
'fetchRepos',
]),
...mapActions(['setMainEndpoint', 'fetchRepos']),
},
};
};
</script>
<template>
<div>
......
<script>
import { mapActions } from 'vuex';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import tableRegistry from './table_registry.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
import { __ } from '../../locale';
import { mapActions } from 'vuex';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import tableRegistry from './table_registry.vue';
import { errorMessages, errorMessagesTypes } from '../constants';
import { __ } from '../../locale';
export default {
export default {
name: 'CollapsibeContainerRegisty',
components: {
clipboardButton,
......@@ -28,18 +28,15 @@
};
},
methods: {
...mapActions([
'fetchRepos',
'fetchList',
'deleteRepo',
]),
...mapActions(['fetchRepos', 'fetchList', 'deleteRepo']),
toggleRepo() {
this.isOpen = !this.isOpen;
if (this.isOpen) {
this.fetchList({ repo: this.repo })
.catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY));
this.fetchList({ repo: this.repo }).catch(() =>
this.showError(errorMessagesTypes.FETCH_REGISTRY),
);
}
},
......@@ -56,7 +53,7 @@
Flash(errorMessages[message]);
},
},
};
};
</script>
<template>
......
<script>
import { mapActions } from 'vuex';
import { n__ } from '../../locale';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import { errorMessages, errorMessagesTypes } from '../constants';
import { numberToHumanSize } from '../../lib/utils/number_utils';
import { mapActions } from 'vuex';
import { n__ } from '../../locale';
import Flash from '../../flash';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import { errorMessages, errorMessagesTypes } from '../constants';
import { numberToHumanSize } from '../../lib/utils/number_utils';
export default {
export default {
components: {
clipboardButton,
tablePagination,
......@@ -17,9 +17,7 @@
directives: {
tooltip,
},
mixins: [
timeagoMixin,
],
mixins: [timeagoMixin],
props: {
repo: {
type: Object,
......@@ -32,10 +30,7 @@
},
},
methods: {
...mapActions([
'fetchList',
'deleteRegistry',
]),
...mapActions(['fetchList', 'deleteRegistry']),
layers(item) {
return item.layers ? n__('%d layer', '%d layers', item.layers) : '';
......@@ -52,15 +47,16 @@
},
onPageChange(pageNumber) {
this.fetchList({ repo: this.repo, page: pageNumber })
.catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY));
this.fetchList({ repo: this.repo, page: pageNumber }).catch(() =>
this.showError(errorMessagesTypes.FETCH_REGISTRY),
);
},
showError(message) {
Flash(errorMessages[message]);
},
},
};
};
</script>
<template>
<div>
......
......@@ -4,7 +4,8 @@ import Translate from '../vue_shared/translate';
Vue.use(Translate);
export default () => new Vue({
export default () =>
new Vue({
el: '#js-vue-registry-images',
components: {
registryApp,
......@@ -22,4 +23,4 @@ export default () => new Vue({
},
});
},
});
});
......@@ -2,7 +2,6 @@ import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '../../lib/utils/common_utils';
export default {
[types.SET_MAIN_ENDPOINT](state, endpoint) {
Object.assign(state, { endpoint });
},
......
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { componentNames } from './issue_body';
import ReportSection from './report_section.vue';
import SummaryRow from './summary_row.vue';
import IssuesList from './issues_list.vue';
import Modal from './modal.vue';
import createStore from '../store';
import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils';
import { mapActions, mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { componentNames } from './issue_body';
import ReportSection from './report_section.vue';
import SummaryRow from './summary_row.vue';
import IssuesList from './issues_list.vue';
import Modal from './modal.vue';
import createStore from '../store';
import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils';
export default {
export default {
name: 'GroupedTestReportsApp',
store: createStore(),
components: {
......@@ -26,19 +26,12 @@
},
componentNames,
computed: {
...mapState([
'reports',
'isLoading',
'hasError',
'summary',
]),
...mapState(['reports', 'isLoading', 'hasError', 'summary']),
...mapState({
modalTitle: state => state.modal.title || '',
modalData: state => state.modal.data || {},
}),
...mapGetters([
'summaryStatus',
]),
...mapGetters(['summaryStatus']),
groupedSummaryText() {
if (this.isLoading) {
return s__('Reports|Test summary results are being parsed');
......@@ -73,7 +66,7 @@
);
},
},
};
};
</script>
<template>
<report-section
......
<script>
import Icon from '~/vue_shared/components/icon.vue';
import {
STATUS_FAILED,
STATUS_NEUTRAL,
STATUS_SUCCESS,
} from '../constants';
import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '../constants';
export default {
name: 'IssueStatusIcon',
......
<script>
import IssuesBlock from '~/reports/components/report_issues.vue';
import {
STATUS_SUCCESS,
STATUS_FAILED,
STATUS_NEUTRAL,
} from '~/reports/constants';
import { STATUS_SUCCESS, STATUS_FAILED, STATUS_NEUTRAL } from '~/reports/constants';
/**
* Renders block of issues
......
<script>
import Modal from '~/vue_shared/components/gl_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import { fieldTypes } from '../constants';
import Modal from '~/vue_shared/components/gl_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import { fieldTypes } from '../constants';
export default {
export default {
components: {
Modal,
LoadingButton,
......@@ -21,7 +21,7 @@
},
},
fieldTypes,
};
};
</script>
<template>
<modal
......
<script>
import { mapActions } from 'vuex';
import { mapActions } from 'vuex';
export default {
export default {
name: 'TestIssueBody',
props: {
issue: {
......@@ -22,7 +22,7 @@
methods: {
...mapActions(['openModal']),
},
};
};
</script>
<template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
......
......@@ -43,8 +43,10 @@ export const fetchReports = ({ state, dispatch }) => {
},
data: state.endpoint,
method: 'getReports',
successCallback: ({ data, status }) => dispatch('receiveReportsSuccess', {
data, status,
successCallback: ({ data, status }) =>
dispatch('receiveReportsSuccess', {
data,
status,
}),
errorCallback: () => dispatch('receiveReportsError'),
});
......
......@@ -7,9 +7,10 @@ import state from './state';
Vue.use(Vuex);
export default () => new Vuex.Store({
export default () =>
new Vuex.Store({
actions,
mutations,
getters,
state: state(),
});
});
......@@ -4,4 +4,3 @@ export const REQUEST_REPORTS = 'REQUEST_REPORTS';
export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS';
export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR';
export const SET_ISSUE_MODAL_DATA = 'SET_ISSUE_MODAL_DATA';
......@@ -19,7 +19,6 @@ export default {
state.status = response.status;
state.reports = response.suites;
},
[types.RECEIVE_REPORTS_ERROR](state) {
state.isLoading = false;
......@@ -36,7 +35,7 @@ export default {
[types.SET_ISSUE_MODAL_DATA](state, payload) {
state.modal.title = payload.issue.name;
Object.keys(payload.issue).forEach((key) => {
Object.keys(payload.issue).forEach(key => {
if (Object.prototype.hasOwnProperty.call(state.modal.data, key)) {
state.modal.data[key] = {
...state.modal.data[key],
......
......@@ -57,5 +57,4 @@ export default () => ({
},
},
},
});
......@@ -69,7 +69,8 @@ export default {
this.loading = false;
}
this.mediator.saveAssignees(this.field)
this.mediator
.saveAssignees(this.field)
.then(setLoadingFalse.bind(this))
.catch(() => {
setLoadingFalse();
......
......@@ -56,11 +56,7 @@ export default {
.update('issue', { confidential })
.then(() => window.location.reload())
.catch(() => {
Flash(
__(
'Something went wrong trying to change the confidentiality of this issue',
),
);
Flash(__('Something went wrong trying to change the confidentiality of this issue'));
});
},
},
......
......@@ -34,11 +34,7 @@ export default {
required: true,
type: Object,
validator(mediatorObject) {
return (
mediatorObject.service &&
mediatorObject.service.update &&
mediatorObject.store
);
return mediatorObject.service && mediatorObject.service.update && mediatorObject.store;
},
},
},
......@@ -67,8 +63,7 @@ export default {
methods: {
toggleForm() {
this.mediator.store.isLockDialogOpen = !this.mediator.store
.isLockDialogOpen;
this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen;
},
updateLockedAttribute(locked) {
......@@ -79,9 +74,14 @@ export default {
.then(() => window.location.reload())
.catch(() =>
Flash(
sprintf(__('Something went wrong trying to change the locked state of this %{issuableDisplayName}'), {
sprintf(
__(
'Something went wrong trying to change the locked state of this %{issuableDisplayName}',
),
{
issuableDisplayName: this.issuableDisplayName,
}),
},
),
),
);
},
......
<script>
import { __, n__, sprintf } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import { __, n__, sprintf } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
export default {
export default {
directives: {
tooltip,
},
......@@ -72,7 +72,7 @@
this.$emit('toggleSidebar');
},
},
};
};
</script>
<template>
......
<script>
import Store from '../../stores/sidebar_store';
import participants from './participants.vue';
import Store from '../../stores/sidebar_store';
import participants from './participants.vue';
export default {
export default {
components: {
participants,
},
......@@ -17,7 +17,7 @@
store: new Store(),
};
},
};
};
</script>
<template>
......
......@@ -21,8 +21,7 @@ export default {
},
methods: {
onToggleSubscription() {
this.mediator.toggleSubscription()
.catch(() => {
this.mediator.toggleSubscription().catch(() => {
Flash(__('Error occurred when toggling the notification subscription'));
});
},
......
......@@ -15,16 +15,22 @@ export default {
},
estimateText() {
return sprintf(
s__('estimateCommand|%{slash_command} will update the estimated time with the latest command.'), {
s__(
'estimateCommand|%{slash_command} will update the estimated time with the latest command.',
),
{
slash_command: '<code>/estimate</code>',
}, false,
},
false,
);
},
spendText() {
return sprintf(
s__('spendCommand|%{slash_command} will update the sum of the time spent.'), {
s__('spendCommand|%{slash_command} will update the sum of the time spent.'),
{
slash_command: '<code>/spend</code>',
}, false,
},
false,
);
},
},
......
......@@ -26,7 +26,7 @@ export default {
methods: {
listenForQuickActions() {
$(document).on('ajax:success', '.gfm-form', this.quickActionListened);
eventHub.$on('timeTrackingUpdated', (data) => {
eventHub.$on('timeTrackingUpdated', data => {
this.quickActionListened(null, data);
});
},
......@@ -34,9 +34,7 @@ export default {
const subscribedCommands = ['spend_time', 'time_estimate'];
let changedCommands;
if (data !== undefined) {
changedCommands = data.commands_changes
? Object.keys(data.commands_changes)
: [];
changedCommands = data.commands_changes ? Object.keys(data.commands_changes) : [];
} else {
changedCommands = [];
}
......
......@@ -41,9 +41,9 @@ export default {
},
computed: {
buttonClasses() {
return this.collapsed ?
'btn-blank btn-todo sidebar-collapsed-icon dont-change-state' :
'btn btn-default btn-todo issuable-header-btn float-right';
return this.collapsed
? 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state'
: 'btn btn-default btn-todo issuable-header-btn float-right';
},
buttonLabel() {
return this.isTodo ? MARK_TEXT : TODO_TEXT;
......
......@@ -37,7 +37,8 @@ class SidebarMoveIssue {
// Keep the dropdown open after selecting an option
shouldPropagate: false,
data: (searchTerm, callback) => {
this.mediator.fetchAutocompleteProjects(searchTerm)
this.mediator
.fetchAutocompleteProjects(searchTerm)
.then(callback)
.catch(() => new window.Flash('An error occurred while fetching projects autocomplete.'));
},
......@@ -48,7 +49,7 @@ class SidebarMoveIssue {
</a>
</li>
`,
clicked: (options) => {
clicked: options => {
const project = options.selectedObj;
const selectedProjectId = options.isMarking ? project.id : 0;
this.mediator.setMoveToProjectId(selectedProjectId);
......@@ -68,16 +69,11 @@ class SidebarMoveIssue {
onConfirmClicked() {
if (isValidProjectId(this.mediator.store.moveToProjectId)) {
this.$confirmButton
.disable()
.addClass('is-loading');
this.$confirmButton.disable().addClass('is-loading');
this.mediator.moveIssue()
.catch(() => {
this.mediator.moveIssue().catch(() => {
window.Flash('An error occurred while moving the issue.');
this.$confirmButton
.enable()
.removeClass('is-loading');
this.$confirmButton.enable().removeClass('is-loading');
});
}
}
......
......@@ -15,7 +15,8 @@ export default class SidebarMilestone {
components: {
timeTracker,
},
render: createElement => createElement('timeTracker', {
render: createElement =>
createElement('timeTracker', {
props: {
timeEstimate: parseInt(timeEstimate, 10),
timeSpent: parseInt(timeSpent, 10),
......
......@@ -22,7 +22,8 @@ function mountAssigneesComponent(mediator) {
components: {
SidebarAssignees,
},
render: createElement => createElement('sidebar-assignees', {
render: createElement =>
createElement('sidebar-assignees', {
props: {
mediator,
field: el.dataset.field,
......@@ -83,7 +84,8 @@ function mountParticipantsComponent(mediator) {
components: {
sidebarParticipants,
},
render: createElement => createElement('sidebar-participants', {
render: createElement =>
createElement('sidebar-participants', {
props: {
mediator,
},
......@@ -102,7 +104,8 @@ function mountSubscriptionsComponent(mediator) {
components: {
sidebarSubscriptions,
},
render: createElement => createElement('sidebar-subscriptions', {
render: createElement =>
createElement('sidebar-subscriptions', {
props: {
mediator,
},
......
......@@ -22,11 +22,15 @@ export default class SidebarService {
}
update(key, data) {
return Vue.http.put(this.endpoint, {
return Vue.http.put(
this.endpoint,
{
[key]: data,
}, {
},
{
emulateJSON: true,
});
},
);
}
getProjectsAutocomplete(searchTerm) {
......
# Review apps
We currently have review apps available as a manual job in EE pipelines. Here is
[the first implementation](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6259).
That said, [the Quality team is working](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6665)
on making Review Apps automatically deployed by each pipeline, both in CE and EE.
Review Apps are automatically deployed by each pipeline, both in
[CE](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22010) and
[EE](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6665).
## How does it work?
1. On every EE [pipeline][gitlab-pipeline] during the `test` stage, you can
start the [`review` job][review-job]
1. On every [pipeline][gitlab-pipeline] during the `test` stage, the
[`review` job][review-job] is automatically started.
1. The `review` job [triggers a pipeline][cng-pipeline] in the
[`CNG-mirror`][cng-mirror] [^1] project
1. The `CNG-mirror` pipeline creates the Docker images of each component (e.g. `gitlab-rails-ee`,
......@@ -39,6 +37,9 @@ on making Review Apps automatically deployed by each pipeline, both in CE and EE
review app manually, and is also started by GitLab once a branch is deleted
- [TBD] Review apps are cleaned up regularly using a pipeline schedule that runs
the [`scripts/review_apps/automated_cleanup.rb`][automated_cleanup.rb] script
- If you're unable to log in using the `root` username and password the
deployment may have failed. Stop the review app via the `stop_review`
manual job and then retry the `review` job to redeploy the review app.
[^1]: We use the `CNG-mirror` project so that the `CNG`, (**C**loud **N**ative **G**itLab), project's registry is
not overloaded with a lot of transient Docker images.
......
......@@ -99,7 +99,8 @@ module QA
module Integration
autoload :Github, 'qa/scenario/test/integration/github'
autoload :LDAP, 'qa/scenario/test/integration/ldap'
autoload :LDAPNoTLS, 'qa/scenario/test/integration/ldap_no_tls'
autoload :LDAPTLS, 'qa/scenario/test/integration/ldap_tls'
autoload :InstanceSAML, 'qa/scenario/test/integration/instance_saml'
autoload :Kubernetes, 'qa/scenario/test/integration/kubernetes'
autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
......
# frozen_string_literal: true
module QA
module Scenario
module Test
module Integration
class LDAPNoTLS < Test::Instance::All
tags :ldap_no_tls
end
end
end
end
end
# frozen_string_literal: true
module QA
module Scenario
module Test
module Integration
class LDAP < Test::Instance::All
tags :ldap
class LDAPTLS < Test::Instance::All
tags :ldap_tls
end
end
end
......
# frozen_string_literal: true
module QA
context 'Manage', :orchestrated, :ldap do
context 'Manage', :orchestrated, :ldap_no_tls, :ldap_tls do
describe 'LDAP login' do
it 'user logs into GitLab using LDAP credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
......
......@@ -22,7 +22,7 @@ module QA
end
end
context 'Manage', :orchestrated, :ldap, :skip_signup_disabled do
context 'Manage', :orchestrated, :ldap_no_tls, :skip_signup_disabled do
describe 'while LDAP is enabled' do
it_behaves_like 'registration and login'
end
......
......@@ -2,7 +2,7 @@
module QA
context 'Create' do
describe 'Git clone over HTTP', :ldap do
describe 'Git clone over HTTP', :ldap_no_tls do
let(:location) do
Page::Project::Show.act do
choose_repository_clone_http
......
......@@ -2,7 +2,7 @@
module QA
context 'Create' do
describe 'Git push over HTTP', :ldap do
describe 'Git push over HTTP', :ldap_no_tls do
it 'user pushes code to the repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
......
......@@ -2,7 +2,7 @@
module QA
context 'Create' do
describe 'Protected branch support', :ldap do
describe 'Protected branch support', :ldap_no_tls do
let(:branch_name) { 'protected-branch' }
let(:commit_message) { 'Protected push commit message' }
let(:project) do
......
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::LDAP do
describe QA::Scenario::Test::Integration::LDAPNoTLS do
context '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap] }
let(:tags) { [:ldap_no_tls] }
end
end
end
describe QA::Scenario::Test::Integration::LDAPTLS do
context '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap_tls] }
end
end
end
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