Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7ff36fc6e9
commit
cffe2c2c34
|
@ -86,48 +86,43 @@ const addDismissFlashClickListener = (flashEl, fadeTransition) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render an alert at the top of the page, or, optionally an
|
* Render an alert at the top of the page, or, optionally an
|
||||||
* arbitrary existing container.
|
* arbitrary existing container. This alert is always dismissible.
|
||||||
*
|
|
||||||
* This alert is always dismissible.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
*
|
|
||||||
* 1. Render a new alert
|
|
||||||
*
|
*
|
||||||
|
* @example
|
||||||
|
* // Render a new alert
|
||||||
* import { createAlert, VARIANT_WARNING } from '~/flash';
|
* import { createAlert, VARIANT_WARNING } from '~/flash';
|
||||||
*
|
*
|
||||||
* createAlert({ message: 'My error message' });
|
* createAlert({ message: 'My error message' });
|
||||||
* createAlert({ message: 'My warning message', variant: VARIANT_WARNING });
|
* createAlert({ message: 'My warning message', variant: VARIANT_WARNING });
|
||||||
*
|
*
|
||||||
* 2. Dismiss this alert programmatically
|
* @example
|
||||||
*
|
* // Dismiss this alert programmatically
|
||||||
* const alert = createAlert({ message: 'Message' });
|
* const alert = createAlert({ message: 'Message' });
|
||||||
*
|
*
|
||||||
* // ...
|
* // ...
|
||||||
*
|
*
|
||||||
* alert.dismiss();
|
* alert.dismiss();
|
||||||
*
|
*
|
||||||
* 3. Respond to the alert being dismissed
|
* @example
|
||||||
|
* // Respond to the alert being dismissed
|
||||||
|
* createAlert({ message: 'Message', onDismiss: () => {} });
|
||||||
*
|
*
|
||||||
* createAlert({ message: 'Message', onDismiss: () => { ... }});
|
* @param {object} options - Options to control the flash message
|
||||||
*
|
* @param {string} options.message - Alert message text
|
||||||
* @param {Object} options Options to control the flash message
|
* @param {VARIANT_SUCCESS|VARIANT_WARNING|VARIANT_DANGER|VARIANT_INFO|VARIANT_TIP} [options.variant] - Which GlAlert variant to use; it defaults to VARIANT_DANGER.
|
||||||
* @param {String} options.message Alert message text
|
* @param {object} [options.parent] - Reference to parent element under which alert needs to appear. Defaults to `document`.
|
||||||
* @param {String?} options.variant Which GlAlert variant to use, should be VARIANT_SUCCESS, VARIANT_WARNING, VARIANT_DANGER, VARIANT_INFO or VARIANT_TIP. Defaults to VARIANT_DANGER.
|
* @param {Function} [options.onDismiss] - Handler to call when this alert is dismissed.
|
||||||
* @param {Object?} options.parent Reference to parent element under which alert needs to appear. Defaults to `document`.
|
* @param {string} [options.containerSelector] - Selector for the container of the alert
|
||||||
* @param {Function?} options.onDismiss Handler to call when this alert is dismissed.
|
* @param {object} [options.primaryButton] - Object describing primary button of alert
|
||||||
* @param {Object?} options.containerSelector Selector for the container of the alert
|
* @param {string} [options.primaryButton.link] - Href of primary button
|
||||||
* @param {Object?} options.primaryButton Object describing primary button of alert
|
* @param {string} [options.primaryButton.text] - Text of primary button
|
||||||
* @param {String?} link Href of primary button
|
* @param {Function} [options.primaryButton.clickHandler] - Handler to call when primary button is clicked on. The click event is sent as an argument.
|
||||||
* @param {String?} text Text of primary button
|
* @param {object} [options.secondaryButton] - Object describing secondary button of alert
|
||||||
* @param {Function?} clickHandler Handler to call when primary button is clicked on. The click event is sent as an argument.
|
* @param {string} [options.secondaryButton.link] - Href of secondary button
|
||||||
* @param {Object?} options.secondaryButton Object describing secondary button of alert
|
* @param {string} [options.secondaryButton.text] - Text of secondary button
|
||||||
* @param {String?} link Href of secondary button
|
* @param {Function} [options.secondaryButton.clickHandler] - Handler to call when secondary button is clicked on. The click event is sent as an argument.
|
||||||
* @param {String?} text Text of secondary button
|
* @param {boolean} [options.captureError] - Whether to send error to Sentry
|
||||||
* @param {Function?} clickHandler Handler to call when secondary button is clicked on. The click event is sent as an argument.
|
* @param {object} [options.error] - Error to be captured in Sentry
|
||||||
* @param {Boolean?} options.captureError Whether to send error to Sentry
|
|
||||||
* @param {Object} options.error Error to be captured in Sentry
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
const createAlert = function createAlert({
|
const createAlert = function createAlert({
|
||||||
message,
|
message,
|
||||||
|
@ -208,22 +203,24 @@ const createAlert = function createAlert({
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated use `createAlert` instead
|
||||||
|
*
|
||||||
* Flash banner supports different types of Flash configurations
|
* Flash banner supports different types of Flash configurations
|
||||||
* along with ability to provide actionConfig which can be used to show
|
* along with ability to provide actionConfig which can be used to show
|
||||||
* additional action or link on banner next to message
|
* additional action or link on banner next to message
|
||||||
*
|
*
|
||||||
* @param {Object} options Options to control the flash message
|
* @param {object} options - Options to control the flash message
|
||||||
* @param {String} options.message Flash message text
|
* @param {string} options.message - Flash message text
|
||||||
* @param {String} options.type Type of Flash, it can be `notice`, `success`, `warning` or `alert` (default)
|
* @param {'alert'|'notice'|'success'|'warning'} [options.type] - Type of Flash; it defaults to 'alert'
|
||||||
* @param {Object} options.parent Reference to parent element under which Flash needs to appear
|
* @param {Element|Document} [options.parent] - Reference to parent element under which Flash needs to appear
|
||||||
* @param {Object} options.actionConfig Map of config to show action on banner
|
* @param {object} [options.actionConfig] - Map of config to show action on banner
|
||||||
* @param {String} href URL to which action config should point to (default: '#')
|
* @param {string} [options.actionConfig.href] - URL to which action config should point to (default: '#')
|
||||||
* @param {String} title Title of action
|
* @param {string} [options.actionConfig.title] - Title of action
|
||||||
* @param {Function} clickHandler Method to call when action is clicked on
|
* @param {Function} [options.actionConfig.clickHandler] - Method to call when action is clicked on
|
||||||
* @param {Boolean} options.fadeTransition Boolean to determine whether to fade the alert out
|
* @param {boolean} [options.fadeTransition] - Boolean to determine whether to fade the alert out
|
||||||
* @param {Boolean} options.captureError Boolean to determine whether to send error to Sentry
|
* @param {boolean} [options.addBodyClass] - Adds `flash-shown` class to the `body` element
|
||||||
* @param {Object} options.error Error to be captured in Sentry
|
* @param {boolean} [options.captureError] - Boolean to determine whether to send error to Sentry
|
||||||
* @deprecated Use `createAlert` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/362334.
|
* @param {object} [options.error] - Error to be captured in Sentry
|
||||||
*/
|
*/
|
||||||
const createFlash = function createFlash({
|
const createFlash = function createFlash({
|
||||||
message,
|
message,
|
||||||
|
|
|
@ -195,7 +195,11 @@ $body.on('click', 'a[href^="#"]', function clickHashLinkCallback() {
|
||||||
* Quick fix: Get rid of jQuery for this implementation
|
* Quick fix: Get rid of jQuery for this implementation
|
||||||
*/
|
*/
|
||||||
const isBoardsPage = /(projects|groups):boards:show/.test(document.body.dataset.page);
|
const isBoardsPage = /(projects|groups):boards:show/.test(document.body.dataset.page);
|
||||||
if (!isBoardsPage && (bootstrapBreakpoint === 'sm' || bootstrapBreakpoint === 'xs')) {
|
if (
|
||||||
|
!isBoardsPage &&
|
||||||
|
!window.gon?.features?.movedMrSidebar &&
|
||||||
|
(bootstrapBreakpoint === 'sm' || bootstrapBreakpoint === 'xs')
|
||||||
|
) {
|
||||||
const $rightSidebar = $('aside.right-sidebar');
|
const $rightSidebar = $('aside.right-sidebar');
|
||||||
const $layoutPage = $('.layout-page');
|
const $layoutPage = $('.layout-page');
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
/* eslint-disable no-new, class-methods-use-this */
|
/* eslint-disable no-new, class-methods-use-this */
|
||||||
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { getCookie, isMetaClick, parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
|
import { getCookie, isMetaClick, parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
|
||||||
|
@ -176,6 +175,8 @@ export default class MergeRequestTabs {
|
||||||
: null;
|
: null;
|
||||||
this.navbar = document.querySelector('.navbar-gitlab');
|
this.navbar = document.querySelector('.navbar-gitlab');
|
||||||
this.peek = document.getElementById('js-peek');
|
this.peek = document.getElementById('js-peek');
|
||||||
|
this.sidebar = document.querySelector('.js-right-sidebar');
|
||||||
|
this.pageLayout = document.querySelector('.layout-page');
|
||||||
this.paddingTop = 16;
|
this.paddingTop = 16;
|
||||||
|
|
||||||
this.scrollPositions = {};
|
this.scrollPositions = {};
|
||||||
|
@ -282,7 +283,7 @@ export default class MergeRequestTabs {
|
||||||
|
|
||||||
if (action === 'commits') {
|
if (action === 'commits') {
|
||||||
this.loadCommits(href);
|
this.loadCommits(href);
|
||||||
this.expandView();
|
// this.hideSidebar();
|
||||||
this.resetViewContainer();
|
this.resetViewContainer();
|
||||||
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
|
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
|
||||||
} else if (action === 'new') {
|
} else if (action === 'new') {
|
||||||
|
@ -301,13 +302,12 @@ export default class MergeRequestTabs {
|
||||||
*/
|
*/
|
||||||
this.loadDiff(href);
|
this.loadDiff(href);
|
||||||
}
|
}
|
||||||
if (bp.getBreakpointSize() !== 'xl') {
|
// this.hideSidebar();
|
||||||
this.shrinkView();
|
|
||||||
}
|
|
||||||
this.expandViewContainer();
|
this.expandViewContainer();
|
||||||
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
|
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
|
||||||
this.commitsTab.classList.remove('active');
|
this.commitsTab.classList.remove('active');
|
||||||
} else if (action === 'pipelines') {
|
} else if (action === 'pipelines') {
|
||||||
|
// this.hideSidebar();
|
||||||
this.resetViewContainer();
|
this.resetViewContainer();
|
||||||
this.mountPipelinesView();
|
this.mountPipelinesView();
|
||||||
} else {
|
} else {
|
||||||
|
@ -320,9 +320,7 @@ export default class MergeRequestTabs {
|
||||||
notesTab.classList.add('active');
|
notesTab.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bp.getBreakpointSize() !== 'xs') {
|
// this.showSidebar();
|
||||||
this.expandView();
|
|
||||||
}
|
|
||||||
this.resetViewContainer();
|
this.resetViewContainer();
|
||||||
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
|
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
|
||||||
}
|
}
|
||||||
|
@ -509,19 +507,6 @@ export default class MergeRequestTabs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shrinkView() {
|
|
||||||
const $gutterBtn = $('.js-sidebar-toggle:visible');
|
|
||||||
const $expandSvg = $gutterBtn.find('.js-sidebar-expand');
|
|
||||||
|
|
||||||
// Wait until listeners are set
|
|
||||||
setTimeout(() => {
|
|
||||||
// Only when sidebar is expanded
|
|
||||||
if ($expandSvg.length && $expandSvg.hasClass('hidden')) {
|
|
||||||
$gutterBtn.trigger('click', [true]);
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand the issuable sidebar unless the user explicitly collapsed it
|
// Expand the issuable sidebar unless the user explicitly collapsed it
|
||||||
expandView() {
|
expandView() {
|
||||||
if (parseBoolean(getCookie('collapsed_gutter'))) {
|
if (parseBoolean(getCookie('collapsed_gutter'))) {
|
||||||
|
@ -538,4 +523,24 @@ export default class MergeRequestTabs {
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideSidebar() {
|
||||||
|
if (!isInVueNoteablePage() || this.cachedPageLayoutClasses) return;
|
||||||
|
|
||||||
|
this.cachedPageLayoutClasses = this.pageLayout.className;
|
||||||
|
this.pageLayout.classList.remove(
|
||||||
|
'right-sidebar-collapsed',
|
||||||
|
'right-sidebar-expanded',
|
||||||
|
'page-with-icon-sidebar',
|
||||||
|
);
|
||||||
|
this.sidebar.style.width = '0px';
|
||||||
|
}
|
||||||
|
|
||||||
|
showSidebar() {
|
||||||
|
if (!isInVueNoteablePage() || !this.cachedPageLayoutClasses) return;
|
||||||
|
|
||||||
|
this.pageLayout.className = this.cachedPageLayoutClasses;
|
||||||
|
this.sidebar.style.width = '';
|
||||||
|
delete this.cachedPageLayoutClasses;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,18 @@ import createFlash from './flash';
|
||||||
import axios from './lib/utils/axios_utils';
|
import axios from './lib/utils/axios_utils';
|
||||||
import { sprintf, s__, __ } from './locale';
|
import { sprintf, s__, __ } from './locale';
|
||||||
|
|
||||||
|
const updateSidebarClasses = (layoutPage, rightSidebar) => {
|
||||||
|
if (window.innerWidth >= 768) {
|
||||||
|
layoutPage.classList.remove('right-sidebar-expanded', 'right-sidebar-collapsed');
|
||||||
|
rightSidebar.classList.remove('right-sidebar-collapsed');
|
||||||
|
rightSidebar.classList.add('right-sidebar-expanded');
|
||||||
|
} else {
|
||||||
|
layoutPage.classList.add('right-sidebar-collapsed', 'is-merge-request');
|
||||||
|
rightSidebar.classList.add('right-sidebar-collapsed');
|
||||||
|
rightSidebar.classList.remove('right-sidebar-expanded');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function Sidebar() {
|
function Sidebar() {
|
||||||
this.toggleTodo = this.toggleTodo.bind(this);
|
this.toggleTodo = this.toggleTodo.bind(this);
|
||||||
this.sidebar = $('aside');
|
this.sidebar = $('aside');
|
||||||
|
@ -42,13 +54,22 @@ Sidebar.prototype.addEventListeners = function () {
|
||||||
this.sidebar.on('hiddenGlDropdown', this, this.onSidebarDropdownHidden);
|
this.sidebar.on('hiddenGlDropdown', this, this.onSidebarDropdownHidden);
|
||||||
|
|
||||||
$document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
|
$document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
|
||||||
return $(document)
|
$(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
|
||||||
.off('click', '.js-issuable-todo')
|
|
||||||
.on('click', '.js-issuable-todo', this.toggleTodo);
|
if (window.gon?.features?.movedMrSidebar) {
|
||||||
|
const layoutPage = document.querySelector('.layout-page');
|
||||||
|
const rightSidebar = document.querySelector('.js-right-sidebar');
|
||||||
|
|
||||||
|
updateSidebarClasses(layoutPage, rightSidebar);
|
||||||
|
window.addEventListener('resize', () => updateSidebarClasses(layoutPage, rightSidebar));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
|
Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
|
||||||
const $this = $(this);
|
const $this = $(this);
|
||||||
|
|
||||||
|
if ($this.hasClass('right-sidebar-merge-requests')) return;
|
||||||
|
|
||||||
const $collapseIcon = $('.js-sidebar-collapse');
|
const $collapseIcon = $('.js-sidebar-collapse');
|
||||||
const $expandIcon = $('.js-sidebar-expand');
|
const $expandIcon = $('.js-sidebar-expand');
|
||||||
const $toggleContainer = $('.js-sidebar-toggle-container');
|
const $toggleContainer = $('.js-sidebar-toggle-container');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
||||||
import { n__, __ } from '~/locale';
|
import { n__, __ } from '~/locale';
|
||||||
|
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AssigneeTitle',
|
name: 'AssigneeTitle',
|
||||||
|
@ -8,6 +9,7 @@ export default {
|
||||||
GlLoadingIcon,
|
GlLoadingIcon,
|
||||||
GlIcon,
|
GlIcon,
|
||||||
},
|
},
|
||||||
|
mixins: [glFeatureFlagMixin()],
|
||||||
props: {
|
props: {
|
||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -63,6 +65,7 @@ export default {
|
||||||
v-if="showToggle"
|
v-if="showToggle"
|
||||||
:aria-label="__('Toggle sidebar')"
|
:aria-label="__('Toggle sidebar')"
|
||||||
class="gutter-toggle float-right js-sidebar-toggle"
|
class="gutter-toggle float-right js-sidebar-toggle"
|
||||||
|
:class="{ 'gl-display-block gl-md-display-none!': glFeatures.movedMrSidebar }"
|
||||||
href="#"
|
href="#"
|
||||||
role="button"
|
role="button"
|
||||||
>
|
>
|
||||||
|
|
|
@ -111,12 +111,6 @@ export default {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div data-testid="lock-status" class="sidebar-item-value" :class="lockStatus.class">
|
<div data-testid="lock-status" class="sidebar-item-value" :class="lockStatus.class">
|
||||||
<gl-icon
|
|
||||||
:size="16"
|
|
||||||
:name="lockStatus.icon"
|
|
||||||
class="sidebar-item-icon"
|
|
||||||
:class="lockStatus.iconClass"
|
|
||||||
/>
|
|
||||||
{{ lockStatus.displayText }}
|
{{ lockStatus.displayText }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -103,7 +103,7 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="showParticipantLabel"
|
v-if="showParticipantLabel"
|
||||||
class="title hide-collapsed gl-mb-2 gl-line-height-20 gl-font-weight-bold"
|
class="title hide-collapsed gl-mb-2! gl-line-height-20 gl-font-weight-bold"
|
||||||
>
|
>
|
||||||
<gl-loading-icon v-if="loading" size="sm" :inline="true" />
|
<gl-loading-icon v-if="loading" size="sm" :inline="true" />
|
||||||
{{ participantLabel }}
|
{{ participantLabel }}
|
||||||
|
|
|
@ -98,7 +98,7 @@ export default {
|
||||||
'gl-mb-3': index !== users.length - 1,
|
'gl-mb-3': index !== users.length - 1,
|
||||||
'attention-requests': glFeatures.mrAttentionRequests,
|
'attention-requests': glFeatures.mrAttentionRequests,
|
||||||
}"
|
}"
|
||||||
class="gl-display-grid gl-align-items-center reviewer-grid"
|
class="gl-display-grid gl-align-items-center reviewer-grid gl-mr-2"
|
||||||
data-testid="reviewer"
|
data-testid="reviewer"
|
||||||
>
|
>
|
||||||
<reviewer-avatar-link
|
<reviewer-avatar-link
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { IssuableType } from '~/issues/constants';
|
||||||
import { isLoggedIn } from '~/lib/utils/common_utils';
|
import { isLoggedIn } from '~/lib/utils/common_utils';
|
||||||
import { __, sprintf } from '~/locale';
|
import { __, sprintf } from '~/locale';
|
||||||
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
|
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
|
||||||
|
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||||
import { subscribedQueries, Tracking } from '~/sidebar/constants';
|
import { subscribedQueries, Tracking } from '~/sidebar/constants';
|
||||||
|
|
||||||
const ICON_ON = 'notifications';
|
const ICON_ON = 'notifications';
|
||||||
|
@ -25,6 +26,7 @@ export default {
|
||||||
GlToggle,
|
GlToggle,
|
||||||
SidebarEditableItem,
|
SidebarEditableItem,
|
||||||
},
|
},
|
||||||
|
mixins: [glFeatureFlagMixin()],
|
||||||
props: {
|
props: {
|
||||||
iid: {
|
iid: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -82,6 +84,9 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isMergeRequest() {
|
||||||
|
return this.issuableType === IssuableType.MergeRequest && this.glFeatures.movedMrSidebar;
|
||||||
|
},
|
||||||
isLoading() {
|
isLoading() {
|
||||||
return this.$apollo.queries?.subscribed?.loading || this.loading;
|
return this.$apollo.queries?.subscribed?.loading || this.loading;
|
||||||
},
|
},
|
||||||
|
@ -171,7 +176,20 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<li v-if="isMergeRequest" class="gl-new-dropdown-item">
|
||||||
|
<button type="button" class="dropdown-item" @click="toggleSubscribed">
|
||||||
|
<span class="gl-new-dropdown-item-text-wrapper">
|
||||||
|
<template v-if="subscribed">
|
||||||
|
{{ __('Turn off notifications') }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ __('Turn on notifications') }}
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<sidebar-editable-item
|
<sidebar-editable-item
|
||||||
|
v-else
|
||||||
ref="editable"
|
ref="editable"
|
||||||
:title="$options.i18n.notifications"
|
:title="$options.i18n.notifications"
|
||||||
:tracking="$options.tracking"
|
:tracking="$options.tracking"
|
||||||
|
|
|
@ -204,7 +204,7 @@ export default {
|
||||||
:time-estimate-human-readable="humanTimeEstimate"
|
:time-estimate-human-readable="humanTimeEstimate"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold"
|
class="hide-collapsed gl-line-height-20 gl-text-gray-900 gl-display-flex gl-align-items-center gl-font-weight-bold gl-mr-3"
|
||||||
>
|
>
|
||||||
{{ __('Time tracking') }}
|
{{ __('Time tracking') }}
|
||||||
<gl-loading-icon v-if="isTimeTrackingInfoLoading" size="sm" class="gl-ml-2" inline />
|
<gl-loading-icon v-if="isTimeTrackingInfoLoading" size="sm" class="gl-ml-2" inline />
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
.right-sidebar-collapsed {
|
.right-sidebar-collapsed {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
|
||||||
|
&:not(.is-merge-request) {
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
|
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
|
||||||
padding-right: $gutter-collapsed-width;
|
padding-right: $gutter-collapsed-width;
|
||||||
|
@ -28,6 +29,15 @@
|
||||||
right: $gutter-collapsed-width;
|
right: $gutter-collapsed-width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-merge-request {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
.content-wrapper {
|
||||||
|
padding-right: $gutter-collapsed-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-collapsed-icon {
|
.sidebar-collapsed-icon {
|
||||||
.btn {
|
.btn {
|
||||||
|
@ -49,6 +59,18 @@
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
z-index: $zindex-dropdown-menu;
|
z-index: $zindex-dropdown-menu;
|
||||||
|
|
||||||
|
&.right-sidebar-merge-requests {
|
||||||
|
width: 270px;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
z-index: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-sidebar-dropdown-toggle {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include media-breakpoint-only(sm) {
|
@include media-breakpoint-only(sm) {
|
||||||
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
|
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
|
||||||
padding-right: $gutter-collapsed-width;
|
padding-right: $gutter-collapsed-width;
|
||||||
|
@ -73,12 +95,20 @@
|
||||||
.right-sidebar {
|
.right-sidebar {
|
||||||
border-left: 1px solid $gray-50;
|
border-left: 1px solid $gray-50;
|
||||||
|
|
||||||
|
&.right-sidebar-merge-requests {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.right-sidebar-merge-requests) {
|
||||||
.sidebar-container,
|
.sidebar-container,
|
||||||
.issuable-sidebar {
|
.issuable-sidebar {
|
||||||
// Add 100px so that potentially visible vertical scroll bar is hidden
|
// Add 100px so that potentially visible vertical scroll bar is hidden
|
||||||
width: calc(100% + 100px);
|
width: calc(100% + 100px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.with-performance-bar .right-sidebar.affix {
|
.with-performance-bar .right-sidebar.affix {
|
||||||
top: calc(#{$header-height} + #{$performance-bar-height});
|
top: calc(#{$header-height} + #{$performance-bar-height});
|
||||||
|
@ -231,6 +261,10 @@
|
||||||
margin-right: -$gl-spacing-scale-2;
|
margin-right: -$gl-spacing-scale-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issuable-sidebar.is-merge-request .edit-link {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.assignee-grid {
|
.assignee-grid {
|
||||||
grid-template-areas: ' attention user';
|
grid-template-areas: ' attention user';
|
||||||
grid-template-columns: min-content 1fr;
|
grid-template-columns: min-content 1fr;
|
||||||
|
|
|
@ -735,3 +735,11 @@ $tabs-holder-z-index: 250;
|
||||||
.attention-request-sidebar-popover {
|
.attention-request-sidebar-popover {
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.merge-request-overview {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 270px;
|
||||||
|
grid-gap: 5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-sidebar {
|
@mixin right-sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: $header-height;
|
top: $header-height;
|
||||||
// Default value for CSS var must contain a unit
|
// Default value for CSS var must contain a unit
|
||||||
|
@ -125,6 +125,18 @@
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-sidebar {
|
||||||
|
&:not(.right-sidebar-merge-requests) {
|
||||||
|
@include right-sidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right-sidebar-merge-requests {
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
@include right-sidebar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include media-breakpoint-down(sm) {
|
@include media-breakpoint-down(sm) {
|
||||||
z-index: 251;
|
z-index: 251;
|
||||||
|
@ -135,10 +147,6 @@
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $blue-800;
|
color: $blue-800;
|
||||||
|
|
||||||
.avatar {
|
|
||||||
border-color: rgba($gray-normal, 0.2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +176,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.right-sidebar-merge-requests {
|
||||||
|
.block,
|
||||||
|
.sidebar-contained-width,
|
||||||
|
.issuable-sidebar-header {
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: $gl-spacing-scale-5 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.block,
|
.block,
|
||||||
.sidebar-contained-width,
|
.sidebar-contained-width,
|
||||||
.issuable-sidebar-header {
|
.issuable-sidebar-header {
|
||||||
|
@ -224,17 +247,30 @@
|
||||||
|
|
||||||
.issuable-sidebar {
|
.issuable-sidebar {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
&:not(.is-merge-request) {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-merge-request {
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.affix-top .issuable-sidebar {
|
&.affix-top .issuable-sidebar {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.right-sidebar-expanded {
|
&.right-sidebar-expanded {
|
||||||
|
&:not(.right-sidebar-merge-requests) {
|
||||||
width: $gutter-width;
|
width: $gutter-width;
|
||||||
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
@ -242,6 +278,12 @@
|
||||||
|
|
||||||
.issuable-sidebar {
|
.issuable-sidebar {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
|
||||||
|
&.is-merge-request {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
|
&:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
|
||||||
|
@ -284,9 +326,18 @@
|
||||||
/* Extra small devices (phones, less than 768px) */
|
/* Extra small devices (phones, less than 768px) */
|
||||||
display: none;
|
display: none;
|
||||||
/* Small devices (tablets, 768px and up) */
|
/* Small devices (tablets, 768px and up) */
|
||||||
|
|
||||||
|
&:not(.right-sidebar-merge-requests) {
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right-sidebar-merge-requests {
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
width: $gutter-collapsed-width;
|
width: $gutter-collapsed-width;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -233,8 +233,7 @@ $tabs-holder-z-index: 250;
|
||||||
top: calc(#{$header-height} + #{$system-header-height} + #{$performance-bar-height});
|
top: calc(#{$header-height} + #{$system-header-height} + #{$performance-bar-height});
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(md) {
|
||||||
position: -webkit-sticky;
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
||||||
push_frontend_feature_flag(:mr_attention_requests, current_user)
|
push_frontend_feature_flag(:mr_attention_requests, current_user)
|
||||||
push_frontend_feature_flag(:updated_mr_header, project)
|
push_frontend_feature_flag(:updated_mr_header, project)
|
||||||
push_frontend_feature_flag(:remove_diff_header_icons, project)
|
push_frontend_feature_flag(:remove_diff_header_icons, project)
|
||||||
|
push_frontend_feature_flag(:moved_mr_sidebar, project)
|
||||||
end
|
end
|
||||||
|
|
||||||
before_action do
|
before_action do
|
||||||
|
|
|
@ -66,5 +66,3 @@ module AlertManagement
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
AlertManagement::AlertsFinder.prepend_mod_with('AlertManagement::AlertsFinder')
|
|
||||||
|
|
|
@ -41,6 +41,8 @@ module Types
|
||||||
field :architecture_name, GraphQL::Types::String, null: true,
|
field :architecture_name, GraphQL::Types::String, null: true,
|
||||||
description: 'Architecture provided by the the runner.',
|
description: 'Architecture provided by the the runner.',
|
||||||
method: :architecture
|
method: :architecture
|
||||||
|
field :maintenance_note, GraphQL::Types::String, null: true,
|
||||||
|
description: 'Runner\'s maintenance notes.'
|
||||||
field :groups, ::Types::GroupType.connection_type, null: true,
|
field :groups, ::Types::GroupType.connection_type, null: true,
|
||||||
description: 'Groups the runner is associated with. For group runners only.'
|
description: 'Groups the runner is associated with. For group runners only.'
|
||||||
field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
|
field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
|
||||||
|
|
|
@ -13,6 +13,8 @@ module IssuablesHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def sidebar_gutter_collapsed_class
|
def sidebar_gutter_collapsed_class
|
||||||
|
return "right-sidebar-expanded" if moved_mr_sidebar_enabled?
|
||||||
|
|
||||||
"right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
|
"right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -256,6 +256,10 @@ module MergeRequestsHelper
|
||||||
|
|
||||||
_('%{author} requested to merge %{source_branch} %{copy_button} into %{target_branch} %{created_at}').html_safe % { author: link_to_author.html_safe, source_branch: merge_request_source_branch(merge_request).html_safe, copy_button: copy_button.html_safe, target_branch: target_branch.html_safe, created_at: time_ago_with_tooltip(merge_request.created_at, html_class: 'gl-display-inline-block').html_safe }
|
_('%{author} requested to merge %{source_branch} %{copy_button} into %{target_branch} %{created_at}').html_safe % { author: link_to_author.html_safe, source_branch: merge_request_source_branch(merge_request).html_safe, copy_button: copy_button.html_safe, target_branch: target_branch.html_safe, created_at: time_ago_with_tooltip(merge_request.created_at, html_class: 'gl-display-inline-block').html_safe }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def moved_mr_sidebar_enabled?
|
||||||
|
Feature.enabled?(:moved_mr_sidebar, @project) && defined?(@merge_request)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
MergeRequestsHelper.prepend_mod_with('MergeRequestsHelper')
|
MergeRequestsHelper.prepend_mod_with('MergeRequestsHelper')
|
||||||
|
|
|
@ -19,11 +19,13 @@ module NavHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def page_gutter_class
|
def page_gutter_class
|
||||||
|
moved_sidebar_enabled = current_controller?('merge_requests') && moved_mr_sidebar_enabled?
|
||||||
|
|
||||||
if page_has_markdown?
|
if page_has_markdown?
|
||||||
if cookies[:collapsed_gutter] == 'true'
|
if cookies[:collapsed_gutter] == 'true'
|
||||||
%w[page-gutter right-sidebar-collapsed]
|
["page-gutter", "#{'right-sidebar-collapsed' unless moved_sidebar_enabled}"]
|
||||||
else
|
else
|
||||||
%w[page-gutter right-sidebar-expanded]
|
["page-gutter", "#{'right-sidebar-expanded' unless moved_sidebar_enabled}"]
|
||||||
end
|
end
|
||||||
elsif current_path?('jobs#show')
|
elsif current_path?('jobs#show')
|
||||||
%w[page-gutter build-sidebar right-sidebar-expanded]
|
%w[page-gutter build-sidebar right-sidebar-expanded]
|
||||||
|
|
|
@ -81,7 +81,6 @@ module AlertManagement
|
||||||
scope :search, -> (query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
|
scope :search, -> (query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
|
||||||
scope :not_resolved, -> { without_status(:resolved) }
|
scope :not_resolved, -> { without_status(:resolved) }
|
||||||
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
|
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
|
||||||
scope :with_threat_monitoring_alerts, -> { where(domain: :threat_monitoring ) }
|
|
||||||
scope :with_operations_alerts, -> { where(domain: :operations) }
|
scope :with_operations_alerts, -> { where(domain: :operations) }
|
||||||
|
|
||||||
scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
|
scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
|
||||||
|
|
|
@ -18,7 +18,6 @@ module Clusters
|
||||||
default_value_for :version, VERSION
|
default_value_for :version, VERSION
|
||||||
|
|
||||||
scope :preload_cluster_platform, -> { preload(cluster: [:platform_kubernetes]) }
|
scope :preload_cluster_platform, -> { preload(cluster: [:platform_kubernetes]) }
|
||||||
scope :with_clusters_with_cilium, -> { joins(:cluster).merge(Clusters::Cluster.with_available_cilium) }
|
|
||||||
|
|
||||||
attr_encrypted :alert_manager_token,
|
attr_encrypted :alert_manager_token,
|
||||||
mode: :per_attribute_iv,
|
mode: :per_attribute_iv,
|
||||||
|
|
|
@ -137,7 +137,6 @@ module Clusters
|
||||||
scope :aws_installed, -> { aws_provided.joins(:provider_aws).merge(Clusters::Providers::Aws.with_status(:created)) }
|
scope :aws_installed, -> { aws_provided.joins(:provider_aws).merge(Clusters::Providers::Aws.with_status(:created)) }
|
||||||
|
|
||||||
scope :with_available_elasticstack, -> { joins(:application_elastic_stack).merge(::Clusters::Applications::ElasticStack.available) }
|
scope :with_available_elasticstack, -> { joins(:application_elastic_stack).merge(::Clusters::Applications::ElasticStack.available) }
|
||||||
scope :with_available_cilium, -> { joins(:application_cilium).merge(::Clusters::Applications::Cilium.available) }
|
|
||||||
scope :distinct_with_deployed_environments, -> { joins(:environments).merge(::Deployment.success).distinct }
|
scope :distinct_with_deployed_environments, -> { joins(:environments).merge(::Deployment.success).distinct }
|
||||||
|
|
||||||
scope :managed, -> { where(managed: true) }
|
scope :managed, -> { where(managed: true) }
|
||||||
|
|
|
@ -5,7 +5,11 @@ class DeployToken < ApplicationRecord
|
||||||
include TokenAuthenticatable
|
include TokenAuthenticatable
|
||||||
include PolicyActor
|
include PolicyActor
|
||||||
include Gitlab::Utils::StrongMemoize
|
include Gitlab::Utils::StrongMemoize
|
||||||
add_authentication_token_field :token, encrypted: :optional
|
include IgnorableColumns
|
||||||
|
|
||||||
|
ignore_column :token, remove_with: '15.2', remove_after: '2022-07-22'
|
||||||
|
|
||||||
|
add_authentication_token_field :token, encrypted: :required
|
||||||
|
|
||||||
AVAILABLE_SCOPES = %i(read_repository read_registry write_registry
|
AVAILABLE_SCOPES = %i(read_repository read_registry write_registry
|
||||||
read_package_registry write_package_registry).freeze
|
read_package_registry write_package_registry).freeze
|
||||||
|
|
|
@ -30,7 +30,6 @@ module Integrations
|
||||||
after_create_commit :create_default_alerts
|
after_create_commit :create_default_alerts
|
||||||
|
|
||||||
scope :preload_project, -> { preload(:project) }
|
scope :preload_project, -> { preload(:project) }
|
||||||
scope :with_clusters_with_cilium, -> { joins(project: [:clusters]).merge(Clusters::Cluster.with_available_cilium) }
|
|
||||||
|
|
||||||
def show_active_box?
|
def show_active_box?
|
||||||
false
|
false
|
||||||
|
|
|
@ -14,7 +14,5 @@ module Clusters
|
||||||
GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME = 'gitlab-crossplane-database-rolebinding'
|
GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME = 'gitlab-crossplane-database-rolebinding'
|
||||||
KNATIVE_SERVING_NAMESPACE = 'knative-serving'
|
KNATIVE_SERVING_NAMESPACE = 'knative-serving'
|
||||||
ISTIO_SYSTEM_NAMESPACE = 'istio-system'
|
ISTIO_SYSTEM_NAMESPACE = 'istio-system'
|
||||||
GITLAB_CILIUM_ROLE_NAME = 'gitlab-cilium-role'
|
|
||||||
GITLAB_CILIUM_ROLE_BINDING_NAME = 'gitlab-cilium-rolebinding'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -53,8 +53,6 @@ module Clusters
|
||||||
create_or_update_knative_serving_role_binding
|
create_or_update_knative_serving_role_binding
|
||||||
create_or_update_crossplane_database_role
|
create_or_update_crossplane_database_role
|
||||||
create_or_update_crossplane_database_role_binding
|
create_or_update_crossplane_database_role_binding
|
||||||
create_or_update_cilium_role
|
|
||||||
create_or_update_cilium_role_binding
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -99,14 +97,6 @@ module Clusters
|
||||||
kubeclient.update_role_binding(crossplane_database_role_binding_resource)
|
kubeclient.update_role_binding(crossplane_database_role_binding_resource)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_or_update_cilium_role
|
|
||||||
kubeclient.update_role(cilium_role_resource)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_or_update_cilium_role_binding
|
|
||||||
kubeclient.update_role_binding(cilium_role_binding_resource)
|
|
||||||
end
|
|
||||||
|
|
||||||
def service_account_resource
|
def service_account_resource
|
||||||
Gitlab::Kubernetes::ServiceAccount.new(
|
Gitlab::Kubernetes::ServiceAccount.new(
|
||||||
service_account_name,
|
service_account_name,
|
||||||
|
@ -185,28 +175,6 @@ module Clusters
|
||||||
service_account_name: service_account_name
|
service_account_name: service_account_name
|
||||||
).generate
|
).generate
|
||||||
end
|
end
|
||||||
|
|
||||||
def cilium_role_resource
|
|
||||||
Gitlab::Kubernetes::Role.new(
|
|
||||||
name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME,
|
|
||||||
namespace: service_account_namespace,
|
|
||||||
rules: [{
|
|
||||||
apiGroups: %w(cilium.io),
|
|
||||||
resources: %w(ciliumnetworkpolicies),
|
|
||||||
verbs: %w(get list create update patch)
|
|
||||||
}]
|
|
||||||
).generate
|
|
||||||
end
|
|
||||||
|
|
||||||
def cilium_role_binding_resource
|
|
||||||
Gitlab::Kubernetes::RoleBinding.new(
|
|
||||||
name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME,
|
|
||||||
role_name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME,
|
|
||||||
role_kind: :Role,
|
|
||||||
namespace: service_account_namespace,
|
|
||||||
service_account_name: service_account_name
|
|
||||||
).generate
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,3 +38,7 @@
|
||||||
= link_to new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)), class: 'dropdown-item' do
|
= link_to new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)), class: 'dropdown-item' do
|
||||||
.gl-new-dropdown-item-text-wrapper
|
.gl-new-dropdown-item-text-wrapper
|
||||||
= _('Report abuse')
|
= _('Report abuse')
|
||||||
|
- if current_user && moved_mr_sidebar_enabled?
|
||||||
|
%li.gl-new-dropdown-divider
|
||||||
|
%hr.dropdown-divider
|
||||||
|
%li.gl-new-dropdown-item.js-sidebar-subscriptions-entry-point
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
= issuable_meta(@merge_request, @project)
|
= issuable_meta(@merge_request, @project)
|
||||||
|
|
||||||
%div
|
%div
|
||||||
%button.gl-button.btn.btn-default.btn-icon.float-right.d-block.d-sm-none.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ type: 'button' }
|
%button.gl-button.btn.btn-default.btn-icon.float-right.gl-display-block.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ type: 'button', class: "#{'gl-md-display-none!' if moved_mr_sidebar_enabled? } #{'gl-sm-display-none!' unless moved_mr_sidebar_enabled?}" }
|
||||||
= sprite_icon('chevron-double-lg-left')
|
= sprite_icon('chevron-double-lg-left')
|
||||||
|
|
||||||
.detail-page-header-actions.js-issuable-actions{ class: "#{'gl-align-self-start is-merge-request' if updated_mr_header_enabled}" }
|
.detail-page-header-actions.js-issuable-actions{ class: "#{'gl-align-self-start is-merge-request' if updated_mr_header_enabled}" }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
- @gfm_form = true
|
- @gfm_form = true
|
||||||
|
- unless moved_mr_sidebar_enabled?
|
||||||
- @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}"
|
- @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}"
|
||||||
- add_to_breadcrumbs _("Merge requests"), project_merge_requests_path(@project)
|
- add_to_breadcrumbs _("Merge requests"), project_merge_requests_path(@project)
|
||||||
- breadcrumb_title @merge_request.to_reference
|
- breadcrumb_title @merge_request.to_reference
|
||||||
|
@ -47,8 +48,8 @@
|
||||||
#js-diff-file-finder
|
#js-diff-file-finder
|
||||||
#js-code-navigation
|
#js-code-navigation
|
||||||
= render "projects/merge_requests/tabs/pane", id: "notes", class: "notes voting_notes" do
|
= render "projects/merge_requests/tabs/pane", id: "notes", class: "notes voting_notes" do
|
||||||
.row
|
%div{ class: "#{'merge-request-overview' if moved_mr_sidebar_enabled?}" }
|
||||||
%section.col-md-12
|
%section
|
||||||
.issuable-discussion.js-vue-notes-event
|
.issuable-discussion.js-vue-notes-event
|
||||||
- if @merge_request.description.present?
|
- if @merge_request.description.present?
|
||||||
.detail-page-description
|
.detail-page-description
|
||||||
|
@ -70,6 +71,8 @@
|
||||||
help_page_path: suggest_changes_help_path,
|
help_page_path: suggest_changes_help_path,
|
||||||
current_user_data: @current_user_data,
|
current_user_data: @current_user_data,
|
||||||
is_locked: @merge_request.discussion_locked.to_s } }
|
is_locked: @merge_request.discussion_locked.to_s } }
|
||||||
|
- if moved_mr_sidebar_enabled?
|
||||||
|
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
|
||||||
|
|
||||||
= render "projects/merge_requests/tabs/pane", name: "commits", id: "commits", class: "commits" do
|
= render "projects/merge_requests/tabs/pane", name: "commits", id: "commits", class: "commits" do
|
||||||
-# This tab is always loaded via AJAX
|
-# This tab is always loaded via AJAX
|
||||||
|
@ -83,6 +86,7 @@
|
||||||
.loading.hide
|
.loading.hide
|
||||||
= gl_loading_icon(size: 'lg')
|
= gl_loading_icon(size: 'lg')
|
||||||
|
|
||||||
|
- unless moved_mr_sidebar_enabled?
|
||||||
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
|
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
|
||||||
|
|
||||||
- if @merge_request.can_be_reverted?(current_user)
|
- if @merge_request.can_be_reverted?(current_user)
|
||||||
|
|
|
@ -8,11 +8,12 @@
|
||||||
- add_page_startup_api_call "#{issuable_sidebar[:issuable_json_path]}?serializer=sidebar_extras"
|
- add_page_startup_api_call "#{issuable_sidebar[:issuable_json_path]}?serializer=sidebar_extras"
|
||||||
- reviewers = local_assigns.fetch(:reviewers, nil)
|
- reviewers = local_assigns.fetch(:reviewers, nil)
|
||||||
- in_group_context_with_iterations = @project.group.present? && issuable_sidebar[:supports_iterations]
|
- in_group_context_with_iterations = @project.group.present? && issuable_sidebar[:supports_iterations]
|
||||||
|
- moved_sidebar_enabled = moved_mr_sidebar_enabled? && issuable_type === 'merge_request'
|
||||||
|
|
||||||
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type }
|
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: "#{sidebar_gutter_collapsed_class} #{'right-sidebar-merge-requests' if moved_sidebar_enabled}", 'aria-live' => 'polite', 'aria-label': issuable_type }
|
||||||
.issuable-sidebar
|
.issuable-sidebar{ class: "#{'is-merge-request' if moved_sidebar_enabled}" }
|
||||||
.issuable-sidebar-header.gl-py-3
|
.issuable-sidebar-header{ class: "#{'gl-pb-2! gl-md-display-flex gl-justify-content-end' if moved_sidebar_enabled}" }
|
||||||
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
|
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", class: "#{'gl-display-block gl-md-display-none!' if moved_sidebar_enabled}", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
|
||||||
= sidebar_gutter_toggle_icon
|
= sidebar_gutter_toggle_icon
|
||||||
- if signed_in
|
- if signed_in
|
||||||
.js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
|
.js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
|
||||||
|
@ -76,14 +77,14 @@
|
||||||
%script#js-lock-issue-data{ type: "application/json" }= { is_locked: !!issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe
|
%script#js-lock-issue-data{ type: "application/json" }= { is_locked: !!issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe
|
||||||
#js-lock-entry-point
|
#js-lock-entry-point
|
||||||
|
|
||||||
- if signed_in
|
- if signed_in && !moved_sidebar_enabled
|
||||||
.js-sidebar-subscriptions-entry-point
|
.js-sidebar-subscriptions-entry-point
|
||||||
|
|
||||||
.js-sidebar-participants-entry-point
|
.js-sidebar-participants-entry-point
|
||||||
|
|
||||||
.block.with-sub-blocks
|
.block.with-sub-blocks
|
||||||
#js-reference-entry-point
|
#js-reference-entry-point
|
||||||
- if issuable_type == 'merge_request'
|
- if issuable_type == 'merge_request' && !moved_sidebar_enabled
|
||||||
.sub-block.js-sidebar-source-branch
|
.sub-block.js-sidebar-source-branch
|
||||||
.sidebar-collapsed-icon.js-dont-change-state
|
.sidebar-collapsed-icon.js-dont-change-state
|
||||||
= clipboard_button(text: source_branch, title: _('Copy branch name'), placement: "left", boundary: 'viewport', class: 'btn-clipboard gl-button btn-default-tertiary btn-icon btn-sm js-source-branch-copy')
|
= clipboard_button(text: source_branch, title: _('Copy branch name'), placement: "left", boundary: 'viewport', class: 'btn-clipboard gl-button btn-default-tertiary btn-icon btn-sm js-source-branch-copy')
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: moved_mr_sidebar
|
||||||
|
introduced_by_url:
|
||||||
|
rollout_issue_url:
|
||||||
|
milestone: '14.10'
|
||||||
|
type: development
|
||||||
|
group: group::code review
|
||||||
|
default_enabled: false
|
|
@ -9567,6 +9567,7 @@ Represents the total number of issues and their weights for a particular day.
|
||||||
| <a id="cirunneripaddress"></a>`ipAddress` | [`String`](#string) | IP address of the runner. |
|
| <a id="cirunneripaddress"></a>`ipAddress` | [`String`](#string) | IP address of the runner. |
|
||||||
| <a id="cirunnerjobcount"></a>`jobCount` | [`Int`](#int) | Number of jobs processed by the runner (limited to 1000, plus one to indicate that more items exist). |
|
| <a id="cirunnerjobcount"></a>`jobCount` | [`Int`](#int) | Number of jobs processed by the runner (limited to 1000, plus one to indicate that more items exist). |
|
||||||
| <a id="cirunnerlocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
|
| <a id="cirunnerlocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
|
||||||
|
| <a id="cirunnermaintenancenote"></a>`maintenanceNote` | [`String`](#string) | Runner's maintenance notes. |
|
||||||
| <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
|
| <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
|
||||||
| <a id="cirunnerpaused"></a>`paused` | [`Boolean!`](#boolean) | Indicates the runner is paused and not available to run jobs. |
|
| <a id="cirunnerpaused"></a>`paused` | [`Boolean!`](#boolean) | Indicates the runner is paused and not available to run jobs. |
|
||||||
| <a id="cirunnerplatformname"></a>`platformName` | [`String`](#string) | Platform provided by the runner. |
|
| <a id="cirunnerplatformname"></a>`platformName` | [`String`](#string) | Platform provided by the runner. |
|
||||||
|
|
|
@ -625,6 +625,11 @@ Use lowercase for **merge requests**. If you use **MR** as the acronym, spell it
|
||||||
|
|
||||||
Use lowercase for **milestones**.
|
Use lowercase for **milestones**.
|
||||||
|
|
||||||
|
## n/a, N/A, not applicable
|
||||||
|
|
||||||
|
When possible, use **not applicable**. Spelling out the phrase helps non-English speaking users and avoids
|
||||||
|
capitalization inconsistencies.
|
||||||
|
|
||||||
## navigate
|
## navigate
|
||||||
|
|
||||||
Do not use **navigate**. Use **go** instead. For example:
|
Do not use **navigate**. Use **go** instead. For example:
|
||||||
|
|
|
@ -26,7 +26,7 @@ used for the build.
|
||||||
|
|
||||||
Specify either:
|
Specify either:
|
||||||
|
|
||||||
- The CI/CD variable `BUILDPACK_URL` according to [`pack`'s specifications](https://buildpacks.io/docs/app-developer-guide/specify-buildpacks/).
|
- The CI/CD variable `BUILDPACK_URL` with any of [`pack`'s URI specification formats](https://buildpacks.io/docs/app-developer-guide/specify-buildpacks/).
|
||||||
- A [`project.toml` project descriptor](https://buildpacks.io/docs/app-developer-guide/using-project-descriptor/) with the buildpacks you would like to include.
|
- A [`project.toml` project descriptor](https://buildpacks.io/docs/app-developer-guide/using-project-descriptor/) with the buildpacks you would like to include.
|
||||||
|
|
||||||
### Custom buildpacks with Herokuish
|
### Custom buildpacks with Herokuish
|
||||||
|
|
|
@ -4,8 +4,7 @@ module Gitlab
|
||||||
module AlertManagement
|
module AlertManagement
|
||||||
module Payload
|
module Payload
|
||||||
MONITORING_TOOLS = {
|
MONITORING_TOOLS = {
|
||||||
prometheus: 'Prometheus',
|
prometheus: 'Prometheus'
|
||||||
cilium: 'Cilium'
|
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
@ -48,5 +47,3 @@ module Gitlab
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Gitlab::AlertManagement::Payload.prepend_mod_with('Gitlab::AlertManagement::Payload')
|
|
||||||
|
|
|
@ -23,13 +23,11 @@ module Gitlab::Ci
|
||||||
MIN_MEDIUM_DEFAULT = 75
|
MIN_MEDIUM_DEFAULT = 75
|
||||||
|
|
||||||
def initialize(badge)
|
def initialize(badge)
|
||||||
@entity = badge.entity
|
|
||||||
@status = badge.status
|
@status = badge.status
|
||||||
@key_text = badge.customization.dig(:key_text)
|
|
||||||
@key_width = badge.customization.dig(:key_width)
|
|
||||||
@min_good = badge.customization.dig(:min_good)
|
@min_good = badge.customization.dig(:min_good)
|
||||||
@min_acceptable = badge.customization.dig(:min_acceptable)
|
@min_acceptable = badge.customization.dig(:min_acceptable)
|
||||||
@min_medium = badge.customization.dig(:min_medium)
|
@min_medium = badge.customization.dig(:min_medium)
|
||||||
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def value_text
|
def value_text
|
||||||
|
|
|
@ -22,10 +22,8 @@ module Gitlab::Ci
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def initialize(badge)
|
def initialize(badge)
|
||||||
@entity = badge.entity
|
|
||||||
@status = badge.status
|
@status = badge.status
|
||||||
@key_text = badge.customization.dig(:key_text)
|
super
|
||||||
@key_width = badge.customization.dig(:key_width)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def value_text
|
def value_text
|
||||||
|
|
|
@ -13,10 +13,8 @@ module Gitlab::Ci
|
||||||
VALUE_WIDTH_DEFAULT = 54
|
VALUE_WIDTH_DEFAULT = 54
|
||||||
|
|
||||||
def initialize(badge)
|
def initialize(badge)
|
||||||
@entity = badge.entity
|
|
||||||
@tag = badge.tag || "none"
|
@tag = badge.tag || "none"
|
||||||
@key_width = badge.customization.dig(:key_width)
|
super
|
||||||
@key_text = badge.customization.dig(:key_text)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def key_text
|
def key_text
|
||||||
|
|
|
@ -12,7 +12,8 @@ module Gitlab::Ci
|
||||||
|
|
||||||
def initialize(badge)
|
def initialize(badge)
|
||||||
@entity = badge.entity
|
@entity = badge.entity
|
||||||
@status = badge.status
|
@key_text = badge.customization.dig(:key_text)
|
||||||
|
@key_width = badge.customization.dig(:key_width)
|
||||||
end
|
end
|
||||||
|
|
||||||
def key_text
|
def key_text
|
||||||
|
|
|
@ -63,12 +63,14 @@ module Gitlab
|
||||||
|
|
||||||
GEO_NODES_LOAD = 'SELECT 1 AS one FROM "geo_nodes" LIMIT 1'
|
GEO_NODES_LOAD = 'SELECT 1 AS one FROM "geo_nodes" LIMIT 1'
|
||||||
LICENSES_LOAD = 'SELECT "licenses".* FROM "licenses" ORDER BY "licenses"."id"'
|
LICENSES_LOAD = 'SELECT "licenses".* FROM "licenses" ORDER BY "licenses"."id"'
|
||||||
|
ATTR_INTROSPECTION = %r/SELECT .*\ba.attname\b.* (FROM|JOIN) pg_attribute a/m.freeze
|
||||||
|
|
||||||
# queries can be safely ignored if they are amoritized in regular usage
|
# queries can be safely ignored if they are amoritized in regular usage
|
||||||
# (i.e. only requested occasionally and otherwise cached).
|
# (i.e. only requested occasionally and otherwise cached).
|
||||||
def ignorable?(sql)
|
def ignorable?(sql)
|
||||||
return true if sql&.include?(GEO_NODES_LOAD)
|
return true if sql&.include?(GEO_NODES_LOAD)
|
||||||
return true if sql&.include?(LICENSES_LOAD)
|
return true if sql&.include?(LICENSES_LOAD)
|
||||||
|
return true if ATTR_INTROSPECTION =~ sql
|
||||||
|
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
|
@ -40027,9 +40027,15 @@ msgstr ""
|
||||||
msgid "Turn off"
|
msgid "Turn off"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Turn off notifications"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Turn on"
|
msgid "Turn on"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Turn on notifications"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Twitter"
|
msgid "Twitter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,30 @@ module Gitlab
|
||||||
module Group
|
module Group
|
||||||
module Settings
|
module Settings
|
||||||
class Billing < Chemlab::Page
|
class Billing < Chemlab::Page
|
||||||
# TODO: Supplant with data-qa-selectors
|
h4 :billing_plan_header
|
||||||
h4 :billing_plan_header, css: 'div.billing-plan-header h4'
|
|
||||||
|
|
||||||
link :start_your_free_trial
|
link :start_your_free_trial
|
||||||
|
link :upgrade_to_premium
|
||||||
|
link :upgrade_to_ultimate
|
||||||
|
|
||||||
link :upgrade_to_premium, css: '[data-testid="plan-card-premium"] a.billing-cta-purchase-new'
|
# Subscription details
|
||||||
link :upgrade_to_ultimate, css: '[data-testid="plan-card-ultimate"] a.billing-cta-purchase-new'
|
strong :subscription_header
|
||||||
|
button :refresh_seats
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
p :seats_in_subscription
|
||||||
|
p :seats_currently_in_use
|
||||||
|
link :see_seats_usage
|
||||||
|
p :max_seats_used
|
||||||
|
p :seats_owed
|
||||||
|
|
||||||
|
# Billing
|
||||||
|
p :subscription_start_date
|
||||||
|
p :subscription_end_date
|
||||||
|
|
||||||
|
def refresh_subscription_seats
|
||||||
|
refresh_seats
|
||||||
|
::QA::Support::WaitForRequests.wait_for_requests
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -100,6 +100,222 @@ module Gitlab
|
||||||
def upgrade_to_ultimate?
|
def upgrade_to_ultimate?
|
||||||
# This is a stub, used for indexing. The method is dynamically generated.
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @note Defined as +strong :subscription_header+
|
||||||
|
# @return [String] The text content or value of +subscription_header+
|
||||||
|
def subscription_header
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.subscription_header_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::Strong] The raw +Strong+ element
|
||||||
|
def subscription_header_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_subscription_header
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +subscription_header+ element is present on the page
|
||||||
|
def subscription_header?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @note Defined as +button :refresh_seats+
|
||||||
|
# Clicks +refresh_seats+
|
||||||
|
def refresh_seats
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.refresh_seats_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::Button] The raw +Button+ element
|
||||||
|
def refresh_seats_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_refresh_seats
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +refresh_seats+ element is present on the page
|
||||||
|
def refresh_seats?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @note Defined as +p :seats_in_subscription+
|
||||||
|
# @return [String] The text content or value of +seats_in_subscription+
|
||||||
|
def seats_in_subscription
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.seats_in_subscription_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::P] The raw +P+ element
|
||||||
|
def seats_in_subscription_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_seats_in_subscription
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +seats_in_subscription+ element is present on the page
|
||||||
|
def seats_in_subscription?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @note Defined as +p :seats_currently_in_use+
|
||||||
|
# @return [String] The text content or value of +seats_currently_in_use+
|
||||||
|
def seats_currently_in_use
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.seats_currently_in_use_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::P] The raw +P+ element
|
||||||
|
def seats_currently_in_use_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_seats_currently_in_use
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +seats_currently_in_use+ element is present on the page
|
||||||
|
def seats_currently_in_use?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @note Defined as +link :see_seats_usage+
|
||||||
|
# Clicks +see_seats_usage+
|
||||||
|
def see_seats_usage
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.see_seats_usage_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::Link] The raw +Link+ element
|
||||||
|
def see_seats_usage_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_see_seats_usage
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +see_seats_usage+ element is present on the page
|
||||||
|
def see_seats_usage?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @note Defined as +p :max_seats_used+
|
||||||
|
# @return [String] The text content or value of +max_seats_used+
|
||||||
|
def max_seats_used
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.max_seats_used_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::P] The raw +P+ element
|
||||||
|
def max_seats_used_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_max_seats_used
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +max_seats_used+ element is present on the page
|
||||||
|
def max_seats_used?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @note Defined as +p :seats_owed+
|
||||||
|
# @return [String] The text content or value of +seats_owed+
|
||||||
|
def seats_owed
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.seats_owed_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::P] The raw +P+ element
|
||||||
|
def seats_owed_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_seats_owed
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +seats_owed+ element is present on the page
|
||||||
|
def seats_owed?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @note Defined as +p :subscription_start_date+
|
||||||
|
# @return [String] The text content or value of +subscription_start_date+
|
||||||
|
def subscription_start_date
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.subscription_start_date_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::P] The raw +P+ element
|
||||||
|
def subscription_start_date_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_subscription_start_date
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +subscription_start_date+ element is present on the page
|
||||||
|
def subscription_start_date?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @note Defined as +p :subscription_end_date+
|
||||||
|
# @return [String] The text content or value of +subscription_end_date+
|
||||||
|
def subscription_end_date
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing.subscription_end_date_element).to exist
|
||||||
|
# end
|
||||||
|
# @return [Watir::P] The raw +P+ element
|
||||||
|
def subscription_end_date_element
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
|
|
||||||
|
# @example
|
||||||
|
# Gitlab::Page::Group::Settings::Billing.perform do |billing|
|
||||||
|
# expect(billing).to be_subscription_end_date
|
||||||
|
# end
|
||||||
|
# @return [Boolean] true if the +subscription_end_date+ element is present on the page
|
||||||
|
def subscription_end_date?
|
||||||
|
# This is a stub, used for indexing. The method is dynamically generated.
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,8 @@ module QA
|
||||||
QA::Runtime::Logger.debug(%Q[Adding user #{user.username} to #{full_path} #{self.class.name}])
|
QA::Runtime::Logger.debug(%Q[Adding user #{user.username} to #{full_path} #{self.class.name}])
|
||||||
|
|
||||||
response = post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
|
response = post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
|
||||||
response.code == QA::Support::API::HTTP_STATUS_CREATED
|
break true if response.code == QA::Support::API::HTTP_STATUS_CREATED
|
||||||
|
break true if response.body.include?('Member already exists')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module QA
|
|
||||||
module Service
|
|
||||||
module ClusterProvider
|
|
||||||
class K3sCilium < K3s
|
|
||||||
def setup
|
|
||||||
@k3s = Service::DockerRun::K3s.new.tap do |k3s|
|
|
||||||
k3s.remove!
|
|
||||||
k3s.cni_enabled = true
|
|
||||||
k3s.register!
|
|
||||||
|
|
||||||
shell "kubectl config set-cluster k3s --server https://#{k3s.host_name}:6443 --insecure-skip-tls-verify"
|
|
||||||
shell 'kubectl config set-credentials default --username=node --password=some-secret'
|
|
||||||
shell 'kubectl config set-context k3s --cluster=k3s --user=default'
|
|
||||||
shell 'kubectl config use-context k3s'
|
|
||||||
|
|
||||||
wait_for_server(k3s.host_name) do
|
|
||||||
shell 'kubectl version'
|
|
||||||
# install local storage
|
|
||||||
shell 'kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml'
|
|
||||||
|
|
||||||
# patch local storage
|
|
||||||
shell %(kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}')
|
|
||||||
shell 'kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.8/install/kubernetes/quick-install.yaml'
|
|
||||||
|
|
||||||
wait_for_namespaces do
|
|
||||||
wait_for_cilium
|
|
||||||
wait_for_coredns do
|
|
||||||
shell 'kubectl create -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.31.0/deploy/static/provider/cloud/deploy.yaml'
|
|
||||||
wait_for_ingress
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def wait_for_cilium
|
|
||||||
QA::Runtime::Logger.info 'Waiting for Cilium pod to be initialized'
|
|
||||||
|
|
||||||
60.times do
|
|
||||||
if service_available?('kubectl get pods --all-namespaces -l k8s-app=cilium --no-headers=true | grep -o "cilium-.*1/1"')
|
|
||||||
return yield if block_given?
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
QA::Runtime::Logger.info '.'
|
|
||||||
end
|
|
||||||
|
|
||||||
raise 'Cilium pod has not initialized correctly'
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_for_coredns
|
|
||||||
QA::Runtime::Logger.info 'Waiting for CoreDNS pod to be initialized'
|
|
||||||
|
|
||||||
60.times do
|
|
||||||
if service_available?('kubectl get pods --all-namespaces --no-headers=true | grep -o "coredns.*1/1"')
|
|
||||||
return yield if block_given?
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
QA::Runtime::Logger.info '.'
|
|
||||||
end
|
|
||||||
|
|
||||||
raise 'CoreDNS pod has not been initialized correctly'
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_for_ingress
|
|
||||||
QA::Runtime::Logger.info 'Waiting for Ingress controller pod to be initialized'
|
|
||||||
|
|
||||||
60.times do
|
|
||||||
if service_available?('kubectl get pods --all-namespaces -l app.kubernetes.io/component=controller | grep -o "ingress-nginx-controller.*1/1"')
|
|
||||||
return yield if block_given?
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
QA::Runtime::Logger.info '.'
|
|
||||||
end
|
|
||||||
|
|
||||||
raise 'Ingress pod has not been initialized correctly'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -57,7 +57,6 @@ module QA
|
||||||
MEDIA_TYPE: 'application/vnd.docker.distribution.manifest.v2+json'
|
MEDIA_TYPE: 'application/vnd.docker.distribution.manifest.v2+json'
|
||||||
before_script:
|
before_script:
|
||||||
- token=$(curl -u "$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" "https://$CI_SERVER_HOST/jwt/auth?service=container_registry&scope=repository:$CI_PROJECT_PATH:pull,push,delete" | jq -r '.token')
|
- token=$(curl -u "$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" "https://$CI_SERVER_HOST/jwt/auth?service=container_registry&scope=repository:$CI_PROJECT_PATH:pull,push,delete" | jq -r '.token')
|
||||||
- echo $token
|
|
||||||
script:
|
script:
|
||||||
- 'digest=$(curl -L -H "Authorization: Bearer $token" -H "Accept: $MEDIA_TYPE" "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH/manifests/master" | jq -r ".layers[0].digest")'
|
- 'digest=$(curl -L -H "Authorization: Bearer $token" -H "Accept: $MEDIA_TYPE" "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH/manifests/master" | jq -r ".layers[0].digest")'
|
||||||
- 'curl -L -X DELETE -H "Authorization: Bearer $token" -H "Accept: $MEDIA_TYPE" "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH/blobs/$digest"'
|
- 'curl -L -X DELETE -H "Authorization: Bearer $token" -H "Accept: $MEDIA_TYPE" "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH/blobs/$digest"'
|
||||||
|
|
|
@ -113,20 +113,6 @@ FactoryBot.define do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
trait :cilium do
|
|
||||||
monitoring_tool { Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:cilium] }
|
|
||||||
payload do
|
|
||||||
{
|
|
||||||
annotations: {
|
|
||||||
title: 'This is a cilium alert',
|
|
||||||
summary: 'Summary of the alert',
|
|
||||||
description: 'Description of the alert'
|
|
||||||
},
|
|
||||||
startsAt: started_at
|
|
||||||
}.with_indifferent_access
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
trait :all_fields do
|
trait :all_fields do
|
||||||
with_incident
|
with_incident
|
||||||
with_assignee
|
with_assignee
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :deploy_token do
|
factory :deploy_token do
|
||||||
token { nil }
|
|
||||||
token_encrypted { Gitlab::CryptoHelper.aes256_gcm_encrypt(SecureRandom.hex(50)) }
|
token_encrypted { Gitlab::CryptoHelper.aes256_gcm_encrypt(SecureRandom.hex(50)) }
|
||||||
sequence(:name) { |n| "PDT #{n}" }
|
sequence(:name) { |n| "PDT #{n}" }
|
||||||
read_repository { true }
|
read_repository { true }
|
||||||
|
|
|
@ -11,6 +11,10 @@ RSpec.describe 'Group issues page' do
|
||||||
let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) }
|
let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) }
|
||||||
let(:path) { issues_group_path(group) }
|
let(:path) { issues_group_path(group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_feature_flags(vue_issues_list: true)
|
||||||
|
end
|
||||||
|
|
||||||
context 'with shared examples', :js do
|
context 'with shared examples', :js do
|
||||||
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
|
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
|
||||||
|
|
||||||
|
@ -58,10 +62,10 @@ RSpec.describe 'Group issues page' do
|
||||||
let(:user2) { user_outside_group }
|
let(:user2) { user_outside_group }
|
||||||
|
|
||||||
it 'filters by only group users' do
|
it 'filters by only group users' do
|
||||||
filtered_search.set('assignee:=')
|
select_tokens 'Assignee', '='
|
||||||
|
|
||||||
expect(find('#js-dropdown-assignee .filter-dropdown')).to have_content(user.name)
|
expect_suggestion(user.name)
|
||||||
expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name)
|
expect_no_suggestion(user2.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -76,24 +80,10 @@ RSpec.describe 'Group issues page' do
|
||||||
it 'returns all group and subgroup issues' do
|
it 'returns all group and subgroup issues' do
|
||||||
visit issues_group_path(group)
|
visit issues_group_path(group)
|
||||||
|
|
||||||
page.within('.issuable-list') do
|
|
||||||
expect(page).to have_selector('li.issue', count: 2)
|
expect(page).to have_selector('li.issue', count: 2)
|
||||||
expect(page).to have_content('root group issue')
|
expect(page).to have_content('root group issue')
|
||||||
expect(page).to have_content('subgroup issue')
|
expect(page).to have_content('subgroup issue')
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
it 'truncates issue counts if over the threshold', :clean_gitlab_redis_cache do
|
|
||||||
allow(Rails.cache).to receive(:read).and_call_original
|
|
||||||
allow(Rails.cache).to receive(:read).with(
|
|
||||||
['group', group.id, 'issues'],
|
|
||||||
{ expires_in: Gitlab::IssuablesCountForState::CACHE_EXPIRES_IN }
|
|
||||||
).and_return({ opened: 1050, closed: 500, all: 1550 })
|
|
||||||
|
|
||||||
visit issues_group_path(group)
|
|
||||||
|
|
||||||
expect(page).to have_text('Open 1.1k Closed 500 All 1.6k')
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when project is archived' do
|
context 'when project is archived' do
|
||||||
before do
|
before do
|
||||||
|
@ -115,7 +105,6 @@ RSpec.describe 'Group issues page' do
|
||||||
let!(:subgroup_issue) { create(:issue, project: subgroup_project) }
|
let!(:subgroup_issue) { create(:issue, project: subgroup_project) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(vue_issues_list: true)
|
|
||||||
visit issues_group_path(group_with_no_issues)
|
visit issues_group_path(group_with_no_issues)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -135,14 +124,10 @@ RSpec.describe 'Group issues page' do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows projects only with issues feature enabled', :js do
|
it 'shows projects only with issues feature enabled', :js do
|
||||||
within '.empty-state' do
|
|
||||||
click_button 'Toggle project select'
|
click_button 'Toggle project select'
|
||||||
end
|
|
||||||
|
|
||||||
page.within('.select2-results') do
|
expect(page).to have_button project.full_name
|
||||||
expect(page).to have_content(project.full_name)
|
expect(page).not_to have_button project_with_issues_disabled.full_name
|
||||||
expect(page).not_to have_content(project_with_issues_disabled.full_name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -155,16 +140,16 @@ RSpec.describe 'Group issues page' do
|
||||||
let!(:issue3) { create(:issue, project: project, title: 'Issue #3', relative_position: 3) }
|
let!(:issue3) { create(:issue, project: project, title: 'Issue #3', relative_position: 3) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_issues_list: false)
|
||||||
|
|
||||||
sign_in(user_in_group)
|
sign_in(user_in_group)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays all issues' do
|
it 'displays all issues' do
|
||||||
visit issues_group_path(group, sort: 'relative_position')
|
visit issues_group_path(group, sort: 'relative_position')
|
||||||
|
|
||||||
page.within('.issues-list') do
|
|
||||||
expect(page).to have_selector('li.issue', count: 3)
|
expect(page).to have_selector('li.issue', count: 3)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
it 'has manual-ordering css applied' do
|
it 'has manual-ordering css applied' do
|
||||||
visit issues_group_path(group, sort: 'relative_position')
|
visit issues_group_path(group, sort: 'relative_position')
|
||||||
|
@ -218,11 +203,9 @@ RSpec.describe 'Group issues page' do
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_issue_order
|
def check_issue_order
|
||||||
page.within('.manual-ordering') do
|
expect(page).to have_css('.issue:nth-child(1) .title', text: 'Issue #2')
|
||||||
expect(find('.issue:nth-child(1) .title')).to have_content('Issue #2')
|
expect(page).to have_css('.issue:nth-child(2) .title', text: 'Issue #3')
|
||||||
expect(find('.issue:nth-child(2) .title')).to have_content('Issue #3')
|
expect(page).to have_css('.issue:nth-child(3) .title', text: 'Issue #1')
|
||||||
expect(find('.issue:nth-child(3) .title')).to have_content('Issue #1')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -239,14 +222,8 @@ RSpec.describe 'Group issues page' do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows the pagination' do
|
it 'shows the pagination' do
|
||||||
expect(page).to have_link 'Prev'
|
expect(page).to have_button 'Prev', disabled: true
|
||||||
expect(page).to have_link 'Next'
|
expect(page).to have_button 'Next'
|
||||||
end
|
|
||||||
|
|
||||||
it 'first pagination item is active' do
|
|
||||||
page.within('.gl-pagination') do
|
|
||||||
expect(find('li.active')).to have_content('1')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,8 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') }
|
let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_issues_list: true)
|
||||||
|
|
||||||
grandparent.add_owner(user)
|
grandparent.add_owner(user)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
@ -34,8 +36,6 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
click_on 'Close'
|
click_on 'Close'
|
||||||
end
|
end
|
||||||
|
|
||||||
wait_for_requests
|
|
||||||
|
|
||||||
expect(page).to have_selector('.gl-label', text: label.title)
|
expect(page).to have_selector('.gl-label', text: label.title)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -44,8 +44,6 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
page.within('.block.labels') do
|
page.within('.block.labels') do
|
||||||
click_on 'Edit'
|
click_on 'Edit'
|
||||||
|
|
||||||
wait_for_requests
|
|
||||||
|
|
||||||
expect(page).not_to have_text(child_group_label.title)
|
expect(page).not_to have_text(child_group_label.title)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -54,15 +52,21 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
shared_examples 'filtering by ancestor labels for projects' do |board = false|
|
shared_examples 'filtering by ancestor labels for projects' do |board = false|
|
||||||
it 'filters by ancestor labels' do
|
it 'filters by ancestor labels' do
|
||||||
[grandparent_group_label, parent_group_label, project_label_1].each do |label|
|
[grandparent_group_label, parent_group_label, project_label_1].each do |label|
|
||||||
|
if board
|
||||||
select_label_on_dropdown(label.title)
|
select_label_on_dropdown(label.title)
|
||||||
|
|
||||||
wait_for_requests
|
|
||||||
|
|
||||||
if board
|
|
||||||
expect(page).to have_selector('.board-card-title') do |card|
|
expect(page).to have_selector('.board-card-title') do |card|
|
||||||
expect(card).to have_selector('a', text: labeled_issue.title)
|
expect(card).to have_selector('a', text: labeled_issue.title)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
within '[data-testid="filtered-search-input"]' do
|
||||||
|
click_filtered_search_bar
|
||||||
|
click_on 'Label'
|
||||||
|
click_on '= is'
|
||||||
|
click_on label.title
|
||||||
|
send_keys :enter
|
||||||
|
end
|
||||||
|
|
||||||
expect_issues_list_count(1)
|
expect_issues_list_count(1)
|
||||||
expect(page).to have_selector('.issue-title', text: labeled_issue.title)
|
expect(page).to have_selector('.issue-title', text: labeled_issue.title)
|
||||||
end
|
end
|
||||||
|
@ -70,9 +74,11 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not filter by descendant group labels' do
|
it 'does not filter by descendant group labels' do
|
||||||
|
if board
|
||||||
filtered_search.set("label=")
|
filtered_search.set("label=")
|
||||||
|
else
|
||||||
wait_for_requests
|
select_tokens 'Label', '='
|
||||||
|
end
|
||||||
|
|
||||||
expect(page).not_to have_link child_group_label.title
|
expect(page).not_to have_link child_group_label.title
|
||||||
end
|
end
|
||||||
|
@ -93,11 +99,9 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
|
|
||||||
it 'filters by ancestors and current group labels' do
|
it 'filters by ancestors and current group labels' do
|
||||||
[grandparent_group_label, parent_group_label].each do |label|
|
[grandparent_group_label, parent_group_label].each do |label|
|
||||||
|
if board
|
||||||
select_label_on_dropdown(label.title)
|
select_label_on_dropdown(label.title)
|
||||||
|
|
||||||
wait_for_requests
|
|
||||||
|
|
||||||
if board
|
|
||||||
expect(page).to have_selector('.board-card-title') do |card|
|
expect(page).to have_selector('.board-card-title') do |card|
|
||||||
expect(card).to have_selector('a', text: labeled_issue.title)
|
expect(card).to have_selector('a', text: labeled_issue.title)
|
||||||
end
|
end
|
||||||
|
@ -106,6 +110,14 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
expect(card).to have_selector('a', text: labeled_issue_2.title)
|
expect(card).to have_selector('a', text: labeled_issue_2.title)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
within '[data-testid="filtered-search-input"]' do
|
||||||
|
click_filtered_search_bar
|
||||||
|
click_on 'Label'
|
||||||
|
click_on '= is'
|
||||||
|
click_on label.title
|
||||||
|
send_keys :enter
|
||||||
|
end
|
||||||
|
|
||||||
expect_issues_list_count(3)
|
expect_issues_list_count(3)
|
||||||
expect(page).to have_selector('.issue-title', text: labeled_issue.title)
|
expect(page).to have_selector('.issue-title', text: labeled_issue.title)
|
||||||
expect(page).to have_selector('.issue-title', text: labeled_issue_2.title)
|
expect(page).to have_selector('.issue-title', text: labeled_issue_2.title)
|
||||||
|
@ -115,11 +127,9 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'filters by descendant group labels' do
|
it 'filters by descendant group labels' do
|
||||||
wait_for_requests
|
if board
|
||||||
|
|
||||||
select_label_on_dropdown(group_label_3.title)
|
select_label_on_dropdown(group_label_3.title)
|
||||||
|
|
||||||
if board
|
|
||||||
expect(page).to have_selector('.board-card-title') do |card|
|
expect(page).to have_selector('.board-card-title') do |card|
|
||||||
expect(card).not_to have_selector('a', text: labeled_issue_2.title)
|
expect(card).not_to have_selector('a', text: labeled_issue_2.title)
|
||||||
end
|
end
|
||||||
|
@ -128,17 +138,23 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
expect(card).to have_selector('a', text: labeled_issue_3.title)
|
expect(card).to have_selector('a', text: labeled_issue_3.title)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
select_tokens 'Label', '=', group_label_3.title, submit: true
|
||||||
|
|
||||||
expect_issues_list_count(1)
|
expect_issues_list_count(1)
|
||||||
expect(page).to have_selector('.issue-title', text: labeled_issue_3.title)
|
expect(page).to have_selector('.issue-title', text: labeled_issue_3.title)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not filter by descendant group project labels' do
|
it 'does not filter by descendant group project labels' do
|
||||||
|
if board
|
||||||
filtered_search.set("label=")
|
filtered_search.set("label=")
|
||||||
|
|
||||||
wait_for_requests
|
|
||||||
|
|
||||||
expect(page).not_to have_selector('.btn-link', text: project_label_3.title)
|
expect(page).not_to have_selector('.btn-link', text: project_label_3.title)
|
||||||
|
else
|
||||||
|
select_tokens 'Label', '='
|
||||||
|
|
||||||
|
expect(page).not_to have_link project_label_3.title
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -195,9 +211,7 @@ RSpec.describe 'Labels Hierarchy', :js do
|
||||||
it_behaves_like 'filtering by ancestor labels for projects'
|
it_behaves_like 'filtering by ancestor labels for projects'
|
||||||
|
|
||||||
it 'does not filter by descendant group labels' do
|
it 'does not filter by descendant group labels' do
|
||||||
filtered_search.set("label=")
|
select_tokens 'Label', '='
|
||||||
|
|
||||||
wait_for_requests
|
|
||||||
|
|
||||||
expect(page).not_to have_link child_group_label.title
|
expect(page).not_to have_link child_group_label.title
|
||||||
end
|
end
|
||||||
|
|
|
@ -59,7 +59,6 @@ RSpec.describe 'Batch diffs', :js do
|
||||||
|
|
||||||
# Confirm scrolled to correct UI element
|
# Confirm scrolled to correct UI element
|
||||||
expect(get_first_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
|
expect(get_first_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
|
||||||
expect(get_second_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,18 @@ RSpec.describe 'User manages subscription', :js do
|
||||||
let(:project) { create(:project, :public, :repository) }
|
let(:project) { create(:project, :public, :repository) }
|
||||||
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
let(:moved_mr_sidebar_enabled) { false }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(moved_mr_sidebar: moved_mr_sidebar_enabled)
|
||||||
|
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
visit(merge_request_path(merge_request))
|
visit(merge_request_path(merge_request))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'moved sidebar flag disabled' do
|
||||||
it 'toggles subscription' do
|
it 'toggles subscription' do
|
||||||
page.within('[data-testid="subscription-toggle"]') do
|
page.within('[data-testid="subscription-toggle"]') do
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
|
@ -32,3 +36,30 @@ RSpec.describe 'User manages subscription', :js do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'moved sidebar flag enabled' do
|
||||||
|
let(:moved_mr_sidebar_enabled) { true }
|
||||||
|
|
||||||
|
it 'toggles subscription' do
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
click_button 'Toggle dropdown'
|
||||||
|
|
||||||
|
expect(page).to have_content('Turn on notifications')
|
||||||
|
click_button 'Turn on notifications'
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
click_button 'Toggle dropdown'
|
||||||
|
|
||||||
|
expect(page).to have_content('Turn off notifications')
|
||||||
|
click_button 'Turn off notifications'
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
click_button 'Toggle dropdown'
|
||||||
|
|
||||||
|
expect(page).to have_content('Turn on notifications')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -119,6 +119,8 @@ RSpec.describe 'User views an open merge request' do
|
||||||
let(:source_branch) { "'><iframe/srcdoc=''></iframe>" }
|
let(:source_branch) { "'><iframe/srcdoc=''></iframe>" }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(moved_mr_sidebar: false)
|
||||||
|
|
||||||
project.repository.create_branch(source_branch, "master")
|
project.repository.create_branch(source_branch, "master")
|
||||||
|
|
||||||
mr = create(:merge_request, source_project: project, target_project: project, source_branch: source_branch)
|
mr = create(:merge_request, source_project: project, target_project: project, source_branch: source_branch)
|
||||||
|
|
|
@ -1,5 +1,148 @@
|
||||||
{
|
{
|
||||||
"vulnerabilities": [
|
"vulnerabilities": [{
|
||||||
|
"category": "dependency_scanning",
|
||||||
|
"name": "Vulnerability for remediation testing 1",
|
||||||
|
"message": "This vulnerability should have ONE remediation",
|
||||||
|
"description": "",
|
||||||
|
"cve": "CVE-2137",
|
||||||
|
"severity": "High",
|
||||||
|
"solution": "Upgrade to latest version.",
|
||||||
|
"scanner": {
|
||||||
|
"id": "gemnasium",
|
||||||
|
"name": "Gemnasium"
|
||||||
|
},
|
||||||
|
"location": {},
|
||||||
|
"identifiers": [{
|
||||||
|
"type": "GitLab",
|
||||||
|
"name": "Foo vulnerability",
|
||||||
|
"value": "foo"
|
||||||
|
}],
|
||||||
|
"links": [{
|
||||||
|
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137"
|
||||||
|
}],
|
||||||
|
"details": {
|
||||||
|
"commit": {
|
||||||
|
"name": [{
|
||||||
|
"lang": "en",
|
||||||
|
"value": "The Commit"
|
||||||
|
}],
|
||||||
|
"description": [{
|
||||||
|
"lang": "en",
|
||||||
|
"value": "Commit where the vulnerability was identified"
|
||||||
|
}],
|
||||||
|
"type": "commit",
|
||||||
|
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "dependency_scanning",
|
||||||
|
"name": "Vulnerability for remediation testing 2",
|
||||||
|
"message": "This vulnerability should have ONE remediation",
|
||||||
|
"description": "",
|
||||||
|
"cve": "CVE-2138",
|
||||||
|
"severity": "High",
|
||||||
|
"solution": "Upgrade to latest version.",
|
||||||
|
"scanner": {
|
||||||
|
"id": "gemnasium",
|
||||||
|
"name": "Gemnasium"
|
||||||
|
},
|
||||||
|
"location": {},
|
||||||
|
"identifiers": [{
|
||||||
|
"type": "GitLab",
|
||||||
|
"name": "Foo vulnerability",
|
||||||
|
"value": "foo"
|
||||||
|
}],
|
||||||
|
"links": [{
|
||||||
|
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138"
|
||||||
|
}],
|
||||||
|
"details": {
|
||||||
|
"commit": {
|
||||||
|
"name": [{
|
||||||
|
"lang": "en",
|
||||||
|
"value": "The Commit"
|
||||||
|
}],
|
||||||
|
"description": [{
|
||||||
|
"lang": "en",
|
||||||
|
"value": "Commit where the vulnerability was identified"
|
||||||
|
}],
|
||||||
|
"type": "commit",
|
||||||
|
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "dependency_scanning",
|
||||||
|
"name": "Vulnerability for remediation testing 3",
|
||||||
|
"message": "Remediation for this vulnerability should remediate CVE-2140 as well",
|
||||||
|
"description": "",
|
||||||
|
"cve": "CVE-2139",
|
||||||
|
"severity": "High",
|
||||||
|
"solution": "Upgrade to latest version.",
|
||||||
|
"scanner": {
|
||||||
|
"id": "gemnasium",
|
||||||
|
"name": "Gemnasium"
|
||||||
|
},
|
||||||
|
"location": {},
|
||||||
|
"identifiers": [{
|
||||||
|
"type": "GitLab",
|
||||||
|
"name": "Foo vulnerability",
|
||||||
|
"value": "foo"
|
||||||
|
}],
|
||||||
|
"links": [{
|
||||||
|
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139"
|
||||||
|
}],
|
||||||
|
"details": {
|
||||||
|
"commit": {
|
||||||
|
"name": [{
|
||||||
|
"lang": "en",
|
||||||
|
"value": "The Commit"
|
||||||
|
}],
|
||||||
|
"description": [{
|
||||||
|
"lang": "en",
|
||||||
|
"value": "Commit where the vulnerability was identified"
|
||||||
|
}],
|
||||||
|
"type": "commit",
|
||||||
|
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "dependency_scanning",
|
||||||
|
"name": "Vulnerability for remediation testing 4",
|
||||||
|
"message": "Remediation for this vulnerability should remediate CVE-2139 as well",
|
||||||
|
"description": "",
|
||||||
|
"cve": "CVE-2140",
|
||||||
|
"severity": "High",
|
||||||
|
"solution": "Upgrade to latest version.",
|
||||||
|
"scanner": {
|
||||||
|
"id": "gemnasium",
|
||||||
|
"name": "Gemnasium"
|
||||||
|
},
|
||||||
|
"location": {},
|
||||||
|
"identifiers": [{
|
||||||
|
"type": "GitLab",
|
||||||
|
"name": "Foo vulnerability",
|
||||||
|
"value": "foo"
|
||||||
|
}],
|
||||||
|
"links": [{
|
||||||
|
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140"
|
||||||
|
}],
|
||||||
|
"details": {
|
||||||
|
"commit": {
|
||||||
|
"name": [{
|
||||||
|
"lang": "en",
|
||||||
|
"value": "The Commit"
|
||||||
|
}],
|
||||||
|
"description": [{
|
||||||
|
"lang": "en",
|
||||||
|
"value": "Commit where the vulnerability was identified"
|
||||||
|
}],
|
||||||
|
"type": "commit",
|
||||||
|
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "dependency_scanning",
|
"category": "dependency_scanning",
|
||||||
"name": "Vulnerabilities in libxml2",
|
"name": "Vulnerabilities in libxml2",
|
||||||
|
@ -19,37 +162,30 @@
|
||||||
},
|
},
|
||||||
"summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
|
"summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
|
||||||
"request": {
|
"request": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Host",
|
"name": "Host",
|
||||||
"value": "127.0.0.1:7777"
|
"value": "127.0.0.1:7777"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://127.0.0.1:7777/api/users",
|
"url": "http://127.0.0.1:7777/api/users",
|
||||||
"body": ""
|
"body": ""
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Server",
|
"name": "Server",
|
||||||
"value": "TwistedWeb/20.3.0"
|
"value": "TwistedWeb/20.3.0"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"reason_phrase": "OK",
|
"reason_phrase": "OK",
|
||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||||
},
|
},
|
||||||
"supporting_messages": [
|
"supporting_messages": [{
|
||||||
{
|
|
||||||
"name": "Origional",
|
"name": "Origional",
|
||||||
"request": {
|
"request": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Host",
|
"name": "Host",
|
||||||
"value": "127.0.0.1:7777"
|
"value": "127.0.0.1:7777"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://127.0.0.1:7777/api/users",
|
"url": "http://127.0.0.1:7777/api/users",
|
||||||
"body": ""
|
"body": ""
|
||||||
|
@ -58,23 +194,19 @@
|
||||||
{
|
{
|
||||||
"name": "Recorded",
|
"name": "Recorded",
|
||||||
"request": {
|
"request": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Host",
|
"name": "Host",
|
||||||
"value": "127.0.0.1:7777"
|
"value": "127.0.0.1:7777"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://127.0.0.1:7777/api/users",
|
"url": "http://127.0.0.1:7777/api/users",
|
||||||
"body": ""
|
"body": ""
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Server",
|
"name": "Server",
|
||||||
"value": "TwistedWeb/20.3.0"
|
"value": "TwistedWeb/20.3.0"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"reason_phrase": "OK",
|
"reason_phrase": "OK",
|
||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||||
|
@ -83,32 +215,24 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"location": {},
|
"location": {},
|
||||||
"identifiers": [
|
"identifiers": [{
|
||||||
{
|
|
||||||
"type": "GitLab",
|
"type": "GitLab",
|
||||||
"name": "Foo vulnerability",
|
"name": "Foo vulnerability",
|
||||||
"value": "foo"
|
"value": "foo"
|
||||||
}
|
}],
|
||||||
],
|
"links": [{
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020"
|
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"details": {
|
"details": {
|
||||||
"commit": {
|
"commit": {
|
||||||
"name": [
|
"name": [{
|
||||||
{
|
|
||||||
"lang": "en",
|
"lang": "en",
|
||||||
"value": "The Commit"
|
"value": "The Commit"
|
||||||
}
|
}],
|
||||||
],
|
"description": [{
|
||||||
"description": [
|
|
||||||
{
|
|
||||||
"lang": "en",
|
"lang": "en",
|
||||||
"value": "Commit where the vulnerability was identified"
|
"value": "Commit where the vulnerability was identified"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"type": "commit",
|
"type": "commit",
|
||||||
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
|
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
|
||||||
}
|
}
|
||||||
|
@ -134,37 +258,30 @@
|
||||||
},
|
},
|
||||||
"summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
|
"summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
|
||||||
"request": {
|
"request": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Host",
|
"name": "Host",
|
||||||
"value": "127.0.0.1:7777"
|
"value": "127.0.0.1:7777"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://127.0.0.1:7777/api/users",
|
"url": "http://127.0.0.1:7777/api/users",
|
||||||
"body": ""
|
"body": ""
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Server",
|
"name": "Server",
|
||||||
"value": "TwistedWeb/20.3.0"
|
"value": "TwistedWeb/20.3.0"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"reason_phrase": "OK",
|
"reason_phrase": "OK",
|
||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||||
},
|
},
|
||||||
"supporting_messages": [
|
"supporting_messages": [{
|
||||||
{
|
|
||||||
"name": "Origional",
|
"name": "Origional",
|
||||||
"request": {
|
"request": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Host",
|
"name": "Host",
|
||||||
"value": "127.0.0.1:7777"
|
"value": "127.0.0.1:7777"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://127.0.0.1:7777/api/users",
|
"url": "http://127.0.0.1:7777/api/users",
|
||||||
"body": ""
|
"body": ""
|
||||||
|
@ -173,23 +290,19 @@
|
||||||
{
|
{
|
||||||
"name": "Recorded",
|
"name": "Recorded",
|
||||||
"request": {
|
"request": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Host",
|
"name": "Host",
|
||||||
"value": "127.0.0.1:7777"
|
"value": "127.0.0.1:7777"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://127.0.0.1:7777/api/users",
|
"url": "http://127.0.0.1:7777/api/users",
|
||||||
"body": ""
|
"body": ""
|
||||||
},
|
},
|
||||||
"response": {
|
"response": {
|
||||||
"headers": [
|
"headers": [{
|
||||||
{
|
|
||||||
"name": "Server",
|
"name": "Server",
|
||||||
"value": "TwistedWeb/20.3.0"
|
"value": "TwistedWeb/20.3.0"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"reason_phrase": "OK",
|
"reason_phrase": "OK",
|
||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||||
|
@ -198,19 +311,15 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"location": {},
|
"location": {},
|
||||||
"identifiers": [
|
"identifiers": [{
|
||||||
{
|
|
||||||
"type": "GitLab",
|
"type": "GitLab",
|
||||||
"name": "Bar vulnerability",
|
"name": "Bar vulnerability",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}],
|
||||||
],
|
"links": [{
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"name": "CVE-1030",
|
"name": "CVE-1030",
|
||||||
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030"
|
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030"
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "dependency_scanning",
|
"category": "dependency_scanning",
|
||||||
|
@ -226,47 +335,60 @@
|
||||||
},
|
},
|
||||||
"location": {},
|
"location": {},
|
||||||
"identifiers": [],
|
"identifiers": [],
|
||||||
"links": [
|
"links": []
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"remediations": [
|
"remediations": [{
|
||||||
|
"fixes": [{
|
||||||
|
"cve": "CVE-2137"
|
||||||
|
}],
|
||||||
|
"summary": "this remediates CVE-2137",
|
||||||
|
"diff": "dG90YWxseSBsZWdpdCBkaWZm"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fixes": [
|
"fixes": [{
|
||||||
|
"cve": "CVE-2138"
|
||||||
|
}],
|
||||||
|
"summary": "this remediates CVE-2138",
|
||||||
|
"diff": "dG90YWxseSBsZWdpdCBkaWZm"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
|
"fixes": [{
|
||||||
|
"cve": "CVE-2139"
|
||||||
|
}, {
|
||||||
|
"cve": "CVE-2140"
|
||||||
|
}],
|
||||||
|
"summary": "this remediates CVE-2139 and CVE-2140",
|
||||||
|
"diff": "dG90YWxseSBsZWdpdGltYXRlIGRpZmYsIDEwLzEwIHdvdWxkIGFwcGx5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixes": [{
|
||||||
"cve": "CVE-1020"
|
"cve": "CVE-1020"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"diff": ""
|
"diff": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fixes": [
|
"fixes": [{
|
||||||
{
|
|
||||||
"cve": "CVE",
|
"cve": "CVE",
|
||||||
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
|
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"diff": ""
|
"diff": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fixes": [
|
"fixes": [{
|
||||||
{
|
|
||||||
"cve": "CVE",
|
"cve": "CVE",
|
||||||
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
|
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"diff": ""
|
"diff": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fixes": [
|
"fixes": [{
|
||||||
{
|
|
||||||
"id": "2134",
|
"id": "2134",
|
||||||
"cve": "CVE-1"
|
"cve": "CVE-1"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"diff": ""
|
"diff": ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import Mousetrap from 'mousetrap';
|
|
||||||
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
||||||
import waitForPromises from 'helpers/wait_for_promises';
|
import waitForPromises from 'helpers/wait_for_promises';
|
||||||
import initCopyAsGFM, { CopyAsGFM } from '~/behaviors/markdown/copy_as_gfm';
|
import initCopyAsGFM, { CopyAsGFM } from '~/behaviors/markdown/copy_as_gfm';
|
||||||
|
@ -13,7 +12,6 @@ jest.mock('~/lib/utils/common_utils', () => ({
|
||||||
|
|
||||||
describe('ShortcutsIssuable', () => {
|
describe('ShortcutsIssuable', () => {
|
||||||
const snippetShowFixtureName = 'snippets/show.html';
|
const snippetShowFixtureName = 'snippets/show.html';
|
||||||
const mrShowFixtureName = 'merge_requests/merge_request_of_current_user.html';
|
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
initCopyAsGFM();
|
initCopyAsGFM();
|
||||||
|
@ -282,40 +280,4 @@ describe('ShortcutsIssuable', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('copyBranchName', () => {
|
|
||||||
let sidebarCollapsedBtn;
|
|
||||||
let sidebarExpandedBtn;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
loadHTMLFixture(mrShowFixtureName);
|
|
||||||
|
|
||||||
window.shortcut = new ShortcutsIssuable();
|
|
||||||
|
|
||||||
[sidebarCollapsedBtn, sidebarExpandedBtn] = document.querySelectorAll(
|
|
||||||
'.js-source-branch-copy',
|
|
||||||
);
|
|
||||||
|
|
||||||
[sidebarCollapsedBtn, sidebarExpandedBtn].forEach((btn) => jest.spyOn(btn, 'click'));
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
delete window.shortcut;
|
|
||||||
resetHTMLFixture();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the sidebar is expanded', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
// simulate the applied CSS styles when the
|
|
||||||
// sidebar is expanded
|
|
||||||
sidebarCollapsedBtn.style.display = 'none';
|
|
||||||
|
|
||||||
Mousetrap.trigger('b');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('clicks the "expanded" version of the copy source branch button', () => {
|
|
||||||
expect(sidebarExpandedBtn.click).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,8 +39,8 @@ RSpec.describe Resolvers::AlertManagement::AlertResolver do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'filtering by domain' do
|
context 'filtering by domain' do
|
||||||
let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
|
let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'other', domain: :threat_monitoring) }
|
||||||
let_it_be(:alert2) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
|
let_it_be(:alert2) { create(:alert_management_alert, project: project, monitoring_tool: 'other', domain: :threat_monitoring) }
|
||||||
let_it_be(:alert3) { create(:alert_management_alert, project: project, monitoring_tool: 'generic') }
|
let_it_be(:alert3) { create(:alert_management_alert, project: project, monitoring_tool: 'generic') }
|
||||||
|
|
||||||
let(:args) { { domain: 'operations' } }
|
let(:args) { { domain: 'operations' } }
|
||||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do
|
||||||
id description created_at contacted_at maximum_timeout access_level active paused status
|
id description created_at contacted_at maximum_timeout access_level active paused status
|
||||||
version short_sha revision locked run_untagged ip_address runner_type tag_list
|
version short_sha revision locked run_untagged ip_address runner_type tag_list
|
||||||
project_count job_count admin_url edit_admin_url user_permissions executor_name architecture_name platform_name
|
project_count job_count admin_url edit_admin_url user_permissions executor_name architecture_name platform_name
|
||||||
groups projects jobs token_expires_at
|
maintenance_note groups projects jobs token_expires_at
|
||||||
]
|
]
|
||||||
|
|
||||||
expect(described_class).to include_graphql_fields(*expected_fields)
|
expect(described_class).to include_graphql_fields(*expected_fields)
|
||||||
|
|
|
@ -292,7 +292,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
|
||||||
expect(scans.map(&:status).all?('success')).to be(true)
|
expect(scans.map(&:status).all?('success')).to be(true)
|
||||||
expect(scans.map(&:start_time).all?('placeholder-value')).to be(true)
|
expect(scans.map(&:start_time).all?('placeholder-value')).to be(true)
|
||||||
expect(scans.map(&:end_time).all?('placeholder-value')).to be(true)
|
expect(scans.map(&:end_time).all?('placeholder-value')).to be(true)
|
||||||
expect(scans.size).to eq(3)
|
expect(scans.size).to eq(7)
|
||||||
expect(scans.first).to be_a(::Gitlab::Ci::Reports::Security::Scan)
|
expect(scans.first).to be_a(::Gitlab::Ci::Reports::Security::Scan)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -348,22 +348,29 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
|
||||||
it 'returns links object for each finding', :aggregate_failures do
|
it 'returns links object for each finding', :aggregate_failures do
|
||||||
links = report.findings.flat_map(&:links)
|
links = report.findings.flat_map(&:links)
|
||||||
|
|
||||||
expect(links.map(&:url)).to match_array(['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030'])
|
expect(links.map(&:url)).to match_array(['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030',
|
||||||
expect(links.map(&:name)).to match_array([nil, 'CVE-1030'])
|
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138",
|
||||||
expect(links.size).to eq(2)
|
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140"])
|
||||||
|
expect(links.map(&:name)).to match_array([nil, nil, nil, nil, nil, 'CVE-1030'])
|
||||||
|
expect(links.size).to eq(6)
|
||||||
expect(links.first).to be_a(::Gitlab::Ci::Reports::Security::Link)
|
expect(links.first).to be_a(::Gitlab::Ci::Reports::Security::Link)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'parsing evidence' do
|
describe 'parsing evidence' do
|
||||||
it 'returns evidence object for each finding', :aggregate_failures do
|
RSpec::Matchers.define_negated_matcher :have_values, :be_empty
|
||||||
evidences = report.findings.map(&:evidence)
|
|
||||||
|
|
||||||
expect(evidences.first.data).not_to be_empty
|
it 'returns evidence object for each finding', :aggregate_failures do
|
||||||
expect(evidences.first.data["summary"]).to match(/The Origin header was changed/)
|
all_evidences = report.findings.map(&:evidence)
|
||||||
expect(evidences.size).to eq(3)
|
evidences = all_evidences.compact
|
||||||
expect(evidences.compact.size).to eq(2)
|
data = evidences.map(&:data)
|
||||||
expect(evidences.first).to be_a(::Gitlab::Ci::Reports::Security::Evidence)
|
summaries = evidences.map { |e| e.data["summary"] }
|
||||||
|
|
||||||
|
expect(all_evidences.size).to eq(7)
|
||||||
|
expect(evidences.size).to eq(2)
|
||||||
|
expect(evidences).to all( be_a(::Gitlab::Ci::Reports::Security::Evidence) )
|
||||||
|
expect(data).to all( have_values )
|
||||||
|
expect(summaries).to all( match(/The Origin header was changed/) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,12 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do
|
||||||
expect do
|
expect do
|
||||||
transaction.increment(described_class::GEO_NODES_LOAD)
|
transaction.increment(described_class::GEO_NODES_LOAD)
|
||||||
transaction.increment(described_class::LICENSES_LOAD)
|
transaction.increment(described_class::LICENSES_LOAD)
|
||||||
|
transaction.increment('SELECT a.attname, a.other_column FROM pg_attribute a')
|
||||||
|
transaction.increment('SELECT x.foo, a.attname FROM some_table x JOIN pg_attribute a')
|
||||||
|
transaction.increment(<<-SQL)
|
||||||
|
SELECT a.attname, a.other_column
|
||||||
|
FROM pg_attribute a
|
||||||
|
SQL
|
||||||
end.not_to change(transaction, :count)
|
end.not_to change(transaction, :count)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -73,10 +73,10 @@ RSpec.describe DeployToken do
|
||||||
|
|
||||||
describe '#ensure_token' do
|
describe '#ensure_token' do
|
||||||
it 'ensures a token' do
|
it 'ensures a token' do
|
||||||
deploy_token.token = nil
|
deploy_token.token_encrypted = nil
|
||||||
deploy_token.save!
|
deploy_token.save!
|
||||||
|
|
||||||
expect(deploy_token.token).not_to be_empty
|
expect(deploy_token.token_encrypted).not_to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ RSpec.describe 'Query.runner(id)' do
|
||||||
let_it_be(:active_instance_runner) do
|
let_it_be(:active_instance_runner) do
|
||||||
create(:ci_runner, :instance, description: 'Runner 1', contacted_at: 2.hours.ago,
|
create(:ci_runner, :instance, description: 'Runner 1', contacted_at: 2.hours.ago,
|
||||||
active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
|
active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
|
||||||
access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom)
|
access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom,
|
||||||
|
maintenance_note: 'Test maintenance note')
|
||||||
end
|
end
|
||||||
|
|
||||||
let_it_be(:inactive_instance_runner) do
|
let_it_be(:inactive_instance_runner) do
|
||||||
|
@ -64,6 +65,7 @@ RSpec.describe 'Query.runner(id)' do
|
||||||
'executorName' => runner.executor_type&.dasherize,
|
'executorName' => runner.executor_type&.dasherize,
|
||||||
'architectureName' => runner.architecture,
|
'architectureName' => runner.architecture,
|
||||||
'platformName' => runner.platform,
|
'platformName' => runner.platform,
|
||||||
|
'maintenanceNote' => runner.maintenance_note,
|
||||||
'jobCount' => 0,
|
'jobCount' => 0,
|
||||||
'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything),
|
'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything),
|
||||||
'projectCount' => nil,
|
'projectCount' => nil,
|
||||||
|
|
|
@ -39,8 +39,6 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute'
|
||||||
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
|
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
|
||||||
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_NAME, namespace: namespace)
|
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_NAME, namespace: namespace)
|
||||||
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME, namespace: namespace)
|
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME, namespace: namespace)
|
||||||
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME, namespace: namespace)
|
|
||||||
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME, namespace: namespace)
|
|
||||||
|
|
||||||
stub_kubeclient_get_secret(
|
stub_kubeclient_get_secret(
|
||||||
api_url,
|
api_url,
|
||||||
|
|
|
@ -147,8 +147,6 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
|
||||||
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
|
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
|
||||||
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_NAME, namespace: namespace)
|
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_NAME, namespace: namespace)
|
||||||
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME, namespace: namespace)
|
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME, namespace: namespace)
|
||||||
stub_kubeclient_put_role(api_url, Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME, namespace: namespace)
|
|
||||||
stub_kubeclient_put_role_binding(api_url, Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME, namespace: namespace)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a namespace object' do
|
it 'creates a namespace object' do
|
||||||
|
@ -245,47 +243,6 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a role granting cilium permissions to the service account' do
|
|
||||||
subject
|
|
||||||
|
|
||||||
expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME}").with(
|
|
||||||
body: hash_including(
|
|
||||||
metadata: {
|
|
||||||
name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME,
|
|
||||||
namespace: namespace
|
|
||||||
},
|
|
||||||
rules: [{
|
|
||||||
apiGroups: %w(cilium.io),
|
|
||||||
resources: %w(ciliumnetworkpolicies),
|
|
||||||
verbs: %w(get list create update patch)
|
|
||||||
}]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a role binding granting cilium permissions to the service account' do
|
|
||||||
subject
|
|
||||||
|
|
||||||
expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME}").with(
|
|
||||||
body: hash_including(
|
|
||||||
metadata: {
|
|
||||||
name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_BINDING_NAME,
|
|
||||||
namespace: namespace
|
|
||||||
},
|
|
||||||
roleRef: {
|
|
||||||
apiGroup: 'rbac.authorization.k8s.io',
|
|
||||||
kind: 'Role',
|
|
||||||
name: Clusters::Kubernetes::GITLAB_CILIUM_ROLE_NAME
|
|
||||||
},
|
|
||||||
subjects: [{
|
|
||||||
kind: 'ServiceAccount',
|
|
||||||
name: service_account_name,
|
|
||||||
namespace: namespace
|
|
||||||
}]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -274,6 +274,10 @@ module FilteredSearchHelpers
|
||||||
expect(page).to have_css '.gl-filtered-search-token', text: "Milestone != %#{value}"
|
expect(page).to have_css '.gl-filtered-search-token', text: "Milestone != %#{value}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expect_epic_token(value)
|
||||||
|
expect(page).to have_css '.gl-filtered-search-token', text: "Epic = #{value}"
|
||||||
|
end
|
||||||
|
|
||||||
def expect_search_term(value)
|
def expect_search_term(value)
|
||||||
value.split(' ').each do |term|
|
value.split(' ').each do |term|
|
||||||
expect(page).to have_css '.gl-filtered-search-term', text: term
|
expect(page).to have_css '.gl-filtered-search-term', text: term
|
||||||
|
|
Loading…
Reference in New Issue