Commit 68985a90 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-10-26

# Conflicts:
#	app/assets/javascripts/monitoring/components/dashboard.vue
#	app/assets/javascripts/vue_merge_request_widget/ee_switch_mr_widget_options.js
#	app/assets/javascripts/vue_merge_request_widget/services/ee_switch_mr_widget_service.js
#	app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_get_state_key.js
#	app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_mr_widget_store.js
#	app/assets/javascripts/vue_merge_request_widget/stores/ee_switch_state_maps.js

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