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
|
||||
* arbitrary existing container.
|
||||
*
|
||||
* This alert is always dismissible.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* 1. Render a new alert
|
||||
* arbitrary existing container. This alert is always dismissible.
|
||||
*
|
||||
* @example
|
||||
* // Render a new alert
|
||||
* import { createAlert, VARIANT_WARNING } from '~/flash';
|
||||
*
|
||||
* createAlert({ message: 'My error message' });
|
||||
* createAlert({ message: 'My warning message', variant: VARIANT_WARNING });
|
||||
*
|
||||
* 2. Dismiss this alert programmatically
|
||||
*
|
||||
* @example
|
||||
* // Dismiss this alert programmatically
|
||||
* const alert = createAlert({ message: 'Message' });
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* 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 {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 {Object?} options.parent Reference to parent element under which alert needs to appear. Defaults to `document`.
|
||||
* @param {Function?} options.onDismiss Handler to call when this alert is dismissed.
|
||||
* @param {Object?} options.containerSelector Selector for the container of the alert
|
||||
* @param {Object?} options.primaryButton Object describing primary button of alert
|
||||
* @param {String?} link Href of primary button
|
||||
* @param {String?} text Text of primary button
|
||||
* @param {Function?} clickHandler Handler to call when primary button is clicked on. The click event is sent as an argument.
|
||||
* @param {Object?} options.secondaryButton Object describing secondary button of alert
|
||||
* @param {String?} link Href of secondary button
|
||||
* @param {String?} text Text of secondary button
|
||||
* @param {Function?} clickHandler Handler to call when secondary button is clicked on. The click event is sent as an argument.
|
||||
* @param {Boolean?} options.captureError Whether to send error to Sentry
|
||||
* @param {Object} options.error Error to be captured in Sentry
|
||||
* @returns
|
||||
* @param {object} options - Options to control the flash message
|
||||
* @param {string} options.message - Alert message text
|
||||
* @param {VARIANT_SUCCESS|VARIANT_WARNING|VARIANT_DANGER|VARIANT_INFO|VARIANT_TIP} [options.variant] - Which GlAlert variant to use; it defaults to VARIANT_DANGER.
|
||||
* @param {object} [options.parent] - Reference to parent element under which alert needs to appear. Defaults to `document`.
|
||||
* @param {Function} [options.onDismiss] - Handler to call when this alert is dismissed.
|
||||
* @param {string} [options.containerSelector] - Selector for the container of the alert
|
||||
* @param {object} [options.primaryButton] - Object describing primary button of alert
|
||||
* @param {string} [options.primaryButton.link] - Href of primary button
|
||||
* @param {string} [options.primaryButton.text] - Text 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 {object} [options.secondaryButton] - Object describing secondary button of alert
|
||||
* @param {string} [options.secondaryButton.link] - Href of secondary button
|
||||
* @param {string} [options.secondaryButton.text] - Text 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 {boolean} [options.captureError] - Whether to send error to Sentry
|
||||
* @param {object} [options.error] - Error to be captured in Sentry
|
||||
*/
|
||||
const createAlert = function createAlert({
|
||||
message,
|
||||
|
@ -208,22 +203,24 @@ const createAlert = function createAlert({
|
|||
};
|
||||
|
||||
/**
|
||||
* Flash banner supports different types of Flash configurations
|
||||
* along with ability to provide actionConfig which can be used to show
|
||||
* additional action or link on banner next to message
|
||||
* @deprecated use `createAlert` instead
|
||||
*
|
||||
* @param {Object} options Options to control the flash message
|
||||
* @param {String} options.message Flash message text
|
||||
* @param {String} options.type Type of Flash, it can be `notice`, `success`, `warning` or `alert` (default)
|
||||
* @param {Object} 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 {String} href URL to which action config should point to (default: '#')
|
||||
* @param {String} title Title of action
|
||||
* @param {Function} 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.captureError Boolean to determine whether to send error to Sentry
|
||||
* @param {Object} options.error Error to be captured in Sentry
|
||||
* @deprecated Use `createAlert` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/362334.
|
||||
* Flash banner supports different types of Flash configurations
|
||||
* along with ability to provide actionConfig which can be used to show
|
||||
* additional action or link on banner next to message
|
||||
*
|
||||
* @param {object} options - Options to control the flash message
|
||||
* @param {string} options.message - Flash message text
|
||||
* @param {'alert'|'notice'|'success'|'warning'} [options.type] - Type of Flash; it defaults to 'alert'
|
||||
* @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 {string} [options.actionConfig.href] - URL to which action config should point to (default: '#')
|
||||
* @param {string} [options.actionConfig.title] - Title of action
|
||||
* @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.addBodyClass] - Adds `flash-shown` class to the `body` element
|
||||
* @param {boolean} [options.captureError] - Boolean to determine whether to send error to Sentry
|
||||
* @param {object} [options.error] - Error to be captured in Sentry
|
||||
*/
|
||||
const createFlash = function createFlash({
|
||||
message,
|
||||
|
|
|
@ -195,7 +195,11 @@ $body.on('click', 'a[href^="#"]', function clickHashLinkCallback() {
|
|||
* Quick fix: Get rid of jQuery for this implementation
|
||||
*/
|
||||
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 $layoutPage = $('.layout-page');
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* eslint-disable no-new, class-methods-use-this */
|
||||
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
|
||||
import $ from 'jquery';
|
||||
import Vue from 'vue';
|
||||
import { getCookie, isMetaClick, parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
|
||||
|
@ -176,6 +175,8 @@ export default class MergeRequestTabs {
|
|||
: null;
|
||||
this.navbar = document.querySelector('.navbar-gitlab');
|
||||
this.peek = document.getElementById('js-peek');
|
||||
this.sidebar = document.querySelector('.js-right-sidebar');
|
||||
this.pageLayout = document.querySelector('.layout-page');
|
||||
this.paddingTop = 16;
|
||||
|
||||
this.scrollPositions = {};
|
||||
|
@ -282,7 +283,7 @@ export default class MergeRequestTabs {
|
|||
|
||||
if (action === 'commits') {
|
||||
this.loadCommits(href);
|
||||
this.expandView();
|
||||
// this.hideSidebar();
|
||||
this.resetViewContainer();
|
||||
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
|
||||
} else if (action === 'new') {
|
||||
|
@ -301,13 +302,12 @@ export default class MergeRequestTabs {
|
|||
*/
|
||||
this.loadDiff(href);
|
||||
}
|
||||
if (bp.getBreakpointSize() !== 'xl') {
|
||||
this.shrinkView();
|
||||
}
|
||||
// this.hideSidebar();
|
||||
this.expandViewContainer();
|
||||
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
|
||||
this.commitsTab.classList.remove('active');
|
||||
} else if (action === 'pipelines') {
|
||||
// this.hideSidebar();
|
||||
this.resetViewContainer();
|
||||
this.mountPipelinesView();
|
||||
} else {
|
||||
|
@ -320,9 +320,7 @@ export default class MergeRequestTabs {
|
|||
notesTab.classList.add('active');
|
||||
}
|
||||
|
||||
if (bp.getBreakpointSize() !== 'xs') {
|
||||
this.expandView();
|
||||
}
|
||||
// this.showSidebar();
|
||||
this.resetViewContainer();
|
||||
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
|
||||
expandView() {
|
||||
if (parseBoolean(getCookie('collapsed_gutter'))) {
|
||||
|
@ -538,4 +523,24 @@ export default class MergeRequestTabs {
|
|||
}
|
||||
}, 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 { 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() {
|
||||
this.toggleTodo = this.toggleTodo.bind(this);
|
||||
this.sidebar = $('aside');
|
||||
|
@ -42,13 +54,22 @@ Sidebar.prototype.addEventListeners = function () {
|
|||
this.sidebar.on('hiddenGlDropdown', this, this.onSidebarDropdownHidden);
|
||||
|
||||
$document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
|
||||
return $(document)
|
||||
.off('click', '.js-issuable-todo')
|
||||
.on('click', '.js-issuable-todo', this.toggleTodo);
|
||||
$(document).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) {
|
||||
const $this = $(this);
|
||||
|
||||
if ($this.hasClass('right-sidebar-merge-requests')) return;
|
||||
|
||||
const $collapseIcon = $('.js-sidebar-collapse');
|
||||
const $expandIcon = $('.js-sidebar-expand');
|
||||
const $toggleContainer = $('.js-sidebar-toggle-container');
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
||||
import { n__, __ } from '~/locale';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
export default {
|
||||
name: 'AssigneeTitle',
|
||||
|
@ -8,6 +9,7 @@ export default {
|
|||
GlLoadingIcon,
|
||||
GlIcon,
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
|
@ -63,6 +65,7 @@ export default {
|
|||
v-if="showToggle"
|
||||
:aria-label="__('Toggle sidebar')"
|
||||
class="gutter-toggle float-right js-sidebar-toggle"
|
||||
:class="{ 'gl-display-block gl-md-display-none!': glFeatures.movedMrSidebar }"
|
||||
href="#"
|
||||
role="button"
|
||||
>
|
||||
|
|
|
@ -111,12 +111,6 @@ export default {
|
|||
/>
|
||||
|
||||
<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 }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -103,7 +103,7 @@ export default {
|
|||
</div>
|
||||
<div
|
||||
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" />
|
||||
{{ participantLabel }}
|
||||
|
|
|
@ -98,7 +98,7 @@ export default {
|
|||
'gl-mb-3': index !== users.length - 1,
|
||||
'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"
|
||||
>
|
||||
<reviewer-avatar-link
|
||||
|
|
|
@ -5,6 +5,7 @@ import { IssuableType } from '~/issues/constants';
|
|||
import { isLoggedIn } from '~/lib/utils/common_utils';
|
||||
import { __, sprintf } from '~/locale';
|
||||
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';
|
||||
|
||||
const ICON_ON = 'notifications';
|
||||
|
@ -25,6 +26,7 @@ export default {
|
|||
GlToggle,
|
||||
SidebarEditableItem,
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
props: {
|
||||
iid: {
|
||||
type: String,
|
||||
|
@ -82,6 +84,9 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
isMergeRequest() {
|
||||
return this.issuableType === IssuableType.MergeRequest && this.glFeatures.movedMrSidebar;
|
||||
},
|
||||
isLoading() {
|
||||
return this.$apollo.queries?.subscribed?.loading || this.loading;
|
||||
},
|
||||
|
@ -171,7 +176,20 @@ export default {
|
|||
</script>
|
||||
|
||||
<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
|
||||
v-else
|
||||
ref="editable"
|
||||
:title="$options.i18n.notifications"
|
||||
:tracking="$options.tracking"
|
||||
|
|
|
@ -204,7 +204,7 @@ export default {
|
|||
:time-estimate-human-readable="humanTimeEstimate"
|
||||
/>
|
||||
<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') }}
|
||||
<gl-loading-icon v-if="isTimeTrackingInfoLoading" size="sm" class="gl-ml-2" inline />
|
||||
|
|
|
@ -19,13 +19,23 @@
|
|||
.right-sidebar-collapsed {
|
||||
padding-right: 0;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
|
||||
padding-right: $gutter-collapsed-width;
|
||||
}
|
||||
&:not(.is-merge-request) {
|
||||
@include media-breakpoint-up(sm) {
|
||||
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
|
||||
padding-right: $gutter-collapsed-width;
|
||||
}
|
||||
|
||||
.merge-request-tabs-holder.affix {
|
||||
right: $gutter-collapsed-width;
|
||||
.merge-request-tabs-holder.affix {
|
||||
right: $gutter-collapsed-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-merge-request {
|
||||
@include media-breakpoint-up(md) {
|
||||
.content-wrapper {
|
||||
padding-right: $gutter-collapsed-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +59,18 @@
|
|||
padding-right: 0;
|
||||
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) {
|
||||
&:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
|
||||
padding-right: $gutter-collapsed-width;
|
||||
|
@ -73,10 +95,18 @@
|
|||
.right-sidebar {
|
||||
border-left: 1px solid $gray-50;
|
||||
|
||||
.sidebar-container,
|
||||
.issuable-sidebar {
|
||||
// Add 100px so that potentially visible vertical scroll bar is hidden
|
||||
width: calc(100% + 100px);
|
||||
&.right-sidebar-merge-requests {
|
||||
@include media-breakpoint-up(md) {
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.right-sidebar-merge-requests) {
|
||||
.sidebar-container,
|
||||
.issuable-sidebar {
|
||||
// Add 100px so that potentially visible vertical scroll bar is hidden
|
||||
width: calc(100% + 100px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,6 +261,10 @@
|
|||
margin-right: -$gl-spacing-scale-2;
|
||||
}
|
||||
|
||||
.issuable-sidebar.is-merge-request .edit-link {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.assignee-grid {
|
||||
grid-template-areas: ' attention user';
|
||||
grid-template-columns: min-content 1fr;
|
||||
|
|
|
@ -735,3 +735,11 @@ $tabs-holder-z-index: 250;
|
|||
.attention-request-sidebar-popover {
|
||||
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;
|
||||
top: $header-height;
|
||||
// Default value for CSS var must contain a unit
|
||||
|
@ -125,6 +125,18 @@
|
|||
background-color: $white;
|
||||
z-index: 200;
|
||||
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) {
|
||||
z-index: 251;
|
||||
|
@ -135,10 +147,6 @@
|
|||
|
||||
&:hover {
|
||||
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,
|
||||
.sidebar-contained-width,
|
||||
.issuable-sidebar-header {
|
||||
|
@ -224,9 +247,20 @@
|
|||
|
||||
.issuable-sidebar {
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
&:not(.is-merge-request) {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
-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 {
|
||||
|
@ -234,7 +268,9 @@
|
|||
}
|
||||
|
||||
&.right-sidebar-expanded {
|
||||
width: $gutter-width;
|
||||
&:not(.right-sidebar-merge-requests) {
|
||||
width: $gutter-width;
|
||||
}
|
||||
|
||||
.value {
|
||||
line-height: 1;
|
||||
|
@ -242,6 +278,12 @@
|
|||
|
||||
.issuable-sidebar {
|
||||
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]) {
|
||||
|
@ -284,8 +326,17 @@
|
|||
/* Extra small devices (phones, less than 768px) */
|
||||
display: none;
|
||||
/* Small devices (tablets, 768px and up) */
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: block;
|
||||
|
||||
&:not(.right-sidebar-merge-requests) {
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&.right-sidebar-merge-requests {
|
||||
@include media-breakpoint-up(md) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
width: $gutter-collapsed-width;
|
||||
|
|
|
@ -233,8 +233,7 @@ $tabs-holder-z-index: 250;
|
|||
top: calc(#{$header-height} + #{$system-header-height} + #{$performance-bar-height});
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
position: -webkit-sticky;
|
||||
@include media-breakpoint-up(md) {
|
||||
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(:updated_mr_header, project)
|
||||
push_frontend_feature_flag(:remove_diff_header_icons, project)
|
||||
push_frontend_feature_flag(:moved_mr_sidebar, project)
|
||||
end
|
||||
|
||||
before_action do
|
||||
|
|
|
@ -66,5 +66,3 @@ module AlertManagement
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
AlertManagement::AlertsFinder.prepend_mod_with('AlertManagement::AlertsFinder')
|
||||
|
|
|
@ -41,6 +41,8 @@ module Types
|
|||
field :architecture_name, GraphQL::Types::String, null: true,
|
||||
description: 'Architecture provided by the the runner.',
|
||||
method: :architecture
|
||||
field :maintenance_note, GraphQL::Types::String, null: true,
|
||||
description: 'Runner\'s maintenance notes.'
|
||||
field :groups, ::Types::GroupType.connection_type, null: true,
|
||||
description: 'Groups the runner is associated with. For group runners only.'
|
||||
field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
|
||||
|
|
|
@ -13,6 +13,8 @@ module IssuablesHelper
|
|||
end
|
||||
|
||||
def sidebar_gutter_collapsed_class
|
||||
return "right-sidebar-expanded" if moved_mr_sidebar_enabled?
|
||||
|
||||
"right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
|
||||
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 }
|
||||
end
|
||||
|
||||
def moved_mr_sidebar_enabled?
|
||||
Feature.enabled?(:moved_mr_sidebar, @project) && defined?(@merge_request)
|
||||
end
|
||||
end
|
||||
|
||||
MergeRequestsHelper.prepend_mod_with('MergeRequestsHelper')
|
||||
|
|
|
@ -19,11 +19,13 @@ module NavHelper
|
|||
end
|
||||
|
||||
def page_gutter_class
|
||||
moved_sidebar_enabled = current_controller?('merge_requests') && moved_mr_sidebar_enabled?
|
||||
|
||||
if page_has_markdown?
|
||||
if cookies[:collapsed_gutter] == 'true'
|
||||
%w[page-gutter right-sidebar-collapsed]
|
||||
["page-gutter", "#{'right-sidebar-collapsed' unless moved_sidebar_enabled}"]
|
||||
else
|
||||
%w[page-gutter right-sidebar-expanded]
|
||||
["page-gutter", "#{'right-sidebar-expanded' unless moved_sidebar_enabled}"]
|
||||
end
|
||||
elsif current_path?('jobs#show')
|
||||
%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 :not_resolved, -> { without_status(:resolved) }
|
||||
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
|
||||
scope :with_threat_monitoring_alerts, -> { where(domain: :threat_monitoring ) }
|
||||
scope :with_operations_alerts, -> { where(domain: :operations) }
|
||||
|
||||
scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
|
||||
|
|
|
@ -18,7 +18,6 @@ module Clusters
|
|||
default_value_for :version, VERSION
|
||||
|
||||
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,
|
||||
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 :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 :managed, -> { where(managed: true) }
|
||||
|
|
|
@ -5,7 +5,11 @@ class DeployToken < ApplicationRecord
|
|||
include TokenAuthenticatable
|
||||
include PolicyActor
|
||||
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
|
||||
read_package_registry write_package_registry).freeze
|
||||
|
|
|
@ -30,7 +30,6 @@ module Integrations
|
|||
after_create_commit :create_default_alerts
|
||||
|
||||
scope :preload_project, -> { preload(:project) }
|
||||
scope :with_clusters_with_cilium, -> { joins(project: [:clusters]).merge(Clusters::Cluster.with_available_cilium) }
|
||||
|
||||
def show_active_box?
|
||||
false
|
||||
|
|
|
@ -14,7 +14,5 @@ module Clusters
|
|||
GITLAB_CROSSPLANE_DATABASE_ROLE_BINDING_NAME = 'gitlab-crossplane-database-rolebinding'
|
||||
KNATIVE_SERVING_NAMESPACE = 'knative-serving'
|
||||
ISTIO_SYSTEM_NAMESPACE = 'istio-system'
|
||||
GITLAB_CILIUM_ROLE_NAME = 'gitlab-cilium-role'
|
||||
GITLAB_CILIUM_ROLE_BINDING_NAME = 'gitlab-cilium-rolebinding'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,8 +53,6 @@ module Clusters
|
|||
create_or_update_knative_serving_role_binding
|
||||
create_or_update_crossplane_database_role
|
||||
create_or_update_crossplane_database_role_binding
|
||||
create_or_update_cilium_role
|
||||
create_or_update_cilium_role_binding
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -99,14 +97,6 @@ module Clusters
|
|||
kubeclient.update_role_binding(crossplane_database_role_binding_resource)
|
||||
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
|
||||
Gitlab::Kubernetes::ServiceAccount.new(
|
||||
service_account_name,
|
||||
|
@ -185,28 +175,6 @@ module Clusters
|
|||
service_account_name: service_account_name
|
||||
).generate
|
||||
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
|
||||
|
|
|
@ -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
|
||||
.gl-new-dropdown-item-text-wrapper
|
||||
= _('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)
|
||||
|
||||
%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')
|
||||
|
||||
.detail-page-header-actions.js-issuable-actions{ class: "#{'gl-align-self-start is-merge-request' if updated_mr_header_enabled}" }
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
- @gfm_form = true
|
||||
- @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}"
|
||||
- unless moved_mr_sidebar_enabled?
|
||||
- @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}"
|
||||
- add_to_breadcrumbs _("Merge requests"), project_merge_requests_path(@project)
|
||||
- breadcrumb_title @merge_request.to_reference
|
||||
- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", _("Merge requests")
|
||||
|
@ -47,8 +48,8 @@
|
|||
#js-diff-file-finder
|
||||
#js-code-navigation
|
||||
= render "projects/merge_requests/tabs/pane", id: "notes", class: "notes voting_notes" do
|
||||
.row
|
||||
%section.col-md-12
|
||||
%div{ class: "#{'merge-request-overview' if moved_mr_sidebar_enabled?}" }
|
||||
%section
|
||||
.issuable-discussion.js-vue-notes-event
|
||||
- if @merge_request.description.present?
|
||||
.detail-page-description
|
||||
|
@ -70,6 +71,8 @@
|
|||
help_page_path: suggest_changes_help_path,
|
||||
current_user_data: @current_user_data,
|
||||
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
|
||||
-# This tab is always loaded via AJAX
|
||||
|
@ -83,7 +86,8 @@
|
|||
.loading.hide
|
||||
= gl_loading_icon(size: 'lg')
|
||||
|
||||
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
|
||||
- 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
|
||||
|
||||
- if @merge_request.can_be_reverted?(current_user)
|
||||
= render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
- add_page_startup_api_call "#{issuable_sidebar[:issuable_json_path]}?serializer=sidebar_extras"
|
||||
- reviewers = local_assigns.fetch(:reviewers, nil)
|
||||
- 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 }
|
||||
.issuable-sidebar
|
||||
.issuable-sidebar-header.gl-py-3
|
||||
%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' } }
|
||||
%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{ class: "#{'is-merge-request' if moved_sidebar_enabled}" }
|
||||
.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", 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
|
||||
- if signed_in
|
||||
.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
|
||||
#js-lock-entry-point
|
||||
|
||||
- if signed_in
|
||||
- if signed_in && !moved_sidebar_enabled
|
||||
.js-sidebar-subscriptions-entry-point
|
||||
|
||||
.js-sidebar-participants-entry-point
|
||||
|
||||
.block.with-sub-blocks
|
||||
#js-reference-entry-point
|
||||
- if issuable_type == 'merge_request'
|
||||
- if issuable_type == 'merge_request' && !moved_sidebar_enabled
|
||||
.sub-block.js-sidebar-source-branch
|
||||
.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')
|
||||
|
|
|
@ -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="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="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="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. |
|
||||
|
|
|
@ -625,6 +625,11 @@ Use lowercase for **merge requests**. If you use **MR** as the acronym, spell it
|
|||
|
||||
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
|
||||
|
||||
Do not use **navigate**. Use **go** instead. For example:
|
||||
|
|
|
@ -26,7 +26,7 @@ used for the build.
|
|||
|
||||
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.
|
||||
|
||||
### Custom buildpacks with Herokuish
|
||||
|
|
|
@ -4,8 +4,7 @@ module Gitlab
|
|||
module AlertManagement
|
||||
module Payload
|
||||
MONITORING_TOOLS = {
|
||||
prometheus: 'Prometheus',
|
||||
cilium: 'Cilium'
|
||||
prometheus: 'Prometheus'
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
|
@ -48,5 +47,3 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::AlertManagement::Payload.prepend_mod_with('Gitlab::AlertManagement::Payload')
|
||||
|
|
|
@ -23,13 +23,11 @@ module Gitlab::Ci
|
|||
MIN_MEDIUM_DEFAULT = 75
|
||||
|
||||
def initialize(badge)
|
||||
@entity = badge.entity
|
||||
@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_acceptable = badge.customization.dig(:min_acceptable)
|
||||
@min_medium = badge.customization.dig(:min_medium)
|
||||
super
|
||||
end
|
||||
|
||||
def value_text
|
||||
|
|
|
@ -22,10 +22,8 @@ module Gitlab::Ci
|
|||
}.freeze
|
||||
|
||||
def initialize(badge)
|
||||
@entity = badge.entity
|
||||
@status = badge.status
|
||||
@key_text = badge.customization.dig(:key_text)
|
||||
@key_width = badge.customization.dig(:key_width)
|
||||
super
|
||||
end
|
||||
|
||||
def value_text
|
||||
|
|
|
@ -13,10 +13,8 @@ module Gitlab::Ci
|
|||
VALUE_WIDTH_DEFAULT = 54
|
||||
|
||||
def initialize(badge)
|
||||
@entity = badge.entity
|
||||
@tag = badge.tag || "none"
|
||||
@key_width = badge.customization.dig(:key_width)
|
||||
@key_text = badge.customization.dig(:key_text)
|
||||
super
|
||||
end
|
||||
|
||||
def key_text
|
||||
|
|
|
@ -12,7 +12,8 @@ module Gitlab::Ci
|
|||
|
||||
def initialize(badge)
|
||||
@entity = badge.entity
|
||||
@status = badge.status
|
||||
@key_text = badge.customization.dig(:key_text)
|
||||
@key_width = badge.customization.dig(:key_width)
|
||||
end
|
||||
|
||||
def key_text
|
||||
|
|
|
@ -63,12 +63,14 @@ module Gitlab
|
|||
|
||||
GEO_NODES_LOAD = 'SELECT 1 AS one FROM "geo_nodes" LIMIT 1'
|
||||
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
|
||||
# (i.e. only requested occasionally and otherwise cached).
|
||||
def ignorable?(sql)
|
||||
return true if sql&.include?(GEO_NODES_LOAD)
|
||||
return true if sql&.include?(LICENSES_LOAD)
|
||||
return true if ATTR_INTROSPECTION =~ sql
|
||||
|
||||
false
|
||||
end
|
||||
|
|
|
@ -40027,9 +40027,15 @@ msgstr ""
|
|||
msgid "Turn off"
|
||||
msgstr ""
|
||||
|
||||
msgid "Turn off notifications"
|
||||
msgstr ""
|
||||
|
||||
msgid "Turn on"
|
||||
msgstr ""
|
||||
|
||||
msgid "Turn on notifications"
|
||||
msgstr ""
|
||||
|
||||
msgid "Twitter"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -5,13 +5,30 @@ module Gitlab
|
|||
module Group
|
||||
module Settings
|
||||
class Billing < Chemlab::Page
|
||||
# TODO: Supplant with data-qa-selectors
|
||||
h4 :billing_plan_header, css: 'div.billing-plan-header h4'
|
||||
|
||||
h4 :billing_plan_header
|
||||
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'
|
||||
link :upgrade_to_ultimate, css: '[data-testid="plan-card-ultimate"] a.billing-cta-purchase-new'
|
||||
# Subscription details
|
||||
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
|
||||
|
|
|
@ -100,6 +100,222 @@ module Gitlab
|
|||
def upgrade_to_ultimate?
|
||||
# This is a stub, used for indexing. The method is dynamically generated.
|
||||
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
|
||||
|
|
|
@ -12,7 +12,8 @@ module QA
|
|||
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.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
|
||||
|
||||
|
|
|
@ -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'
|
||||
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')
|
||||
- echo $token
|
||||
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")'
|
||||
- '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
|
||||
|
||||
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
|
||||
with_incident
|
||||
with_assignee
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
FactoryBot.define do
|
||||
factory :deploy_token do
|
||||
token { nil }
|
||||
token_encrypted { Gitlab::CryptoHelper.aes256_gcm_encrypt(SecureRandom.hex(50)) }
|
||||
sequence(:name) { |n| "PDT #{n}" }
|
||||
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(:path) { issues_group_path(group) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vue_issues_list: true)
|
||||
end
|
||||
|
||||
context 'with shared examples', :js do
|
||||
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 }
|
||||
|
||||
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(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name)
|
||||
expect_suggestion(user.name)
|
||||
expect_no_suggestion(user2.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -76,23 +80,9 @@ RSpec.describe 'Group issues page' do
|
|||
it 'returns all group and subgroup issues' do
|
||||
visit issues_group_path(group)
|
||||
|
||||
page.within('.issuable-list') do
|
||||
expect(page).to have_selector('li.issue', count: 2)
|
||||
expect(page).to have_content('root group issue')
|
||||
expect(page).to have_content('subgroup issue')
|
||||
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')
|
||||
expect(page).to have_selector('li.issue', count: 2)
|
||||
expect(page).to have_content('root group issue')
|
||||
expect(page).to have_content('subgroup issue')
|
||||
end
|
||||
|
||||
context 'when project is archived' do
|
||||
|
@ -115,7 +105,6 @@ RSpec.describe 'Group issues page' do
|
|||
let!(:subgroup_issue) { create(:issue, project: subgroup_project) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vue_issues_list: true)
|
||||
visit issues_group_path(group_with_no_issues)
|
||||
end
|
||||
|
||||
|
@ -135,14 +124,10 @@ RSpec.describe 'Group issues page' do
|
|||
end
|
||||
|
||||
it 'shows projects only with issues feature enabled', :js do
|
||||
within '.empty-state' do
|
||||
click_button 'Toggle project select'
|
||||
end
|
||||
click_button 'Toggle project select'
|
||||
|
||||
page.within('.select2-results') do
|
||||
expect(page).to have_content(project.full_name)
|
||||
expect(page).not_to have_content(project_with_issues_disabled.full_name)
|
||||
end
|
||||
expect(page).to have_button project.full_name
|
||||
expect(page).not_to have_button project_with_issues_disabled.full_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -155,15 +140,15 @@ RSpec.describe 'Group issues page' do
|
|||
let!(:issue3) { create(:issue, project: project, title: 'Issue #3', relative_position: 3) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vue_issues_list: false)
|
||||
|
||||
sign_in(user_in_group)
|
||||
end
|
||||
|
||||
it 'displays all issues' do
|
||||
visit issues_group_path(group, sort: 'relative_position')
|
||||
|
||||
page.within('.issues-list') do
|
||||
expect(page).to have_selector('li.issue', count: 3)
|
||||
end
|
||||
expect(page).to have_selector('li.issue', count: 3)
|
||||
end
|
||||
|
||||
it 'has manual-ordering css applied' do
|
||||
|
@ -218,11 +203,9 @@ RSpec.describe 'Group issues page' do
|
|||
end
|
||||
|
||||
def check_issue_order
|
||||
page.within('.manual-ordering') do
|
||||
expect(find('.issue:nth-child(1) .title')).to have_content('Issue #2')
|
||||
expect(find('.issue:nth-child(2) .title')).to have_content('Issue #3')
|
||||
expect(find('.issue:nth-child(3) .title')).to have_content('Issue #1')
|
||||
end
|
||||
expect(page).to have_css('.issue:nth-child(1) .title', text: 'Issue #2')
|
||||
expect(page).to have_css('.issue:nth-child(2) .title', text: 'Issue #3')
|
||||
expect(page).to have_css('.issue:nth-child(3) .title', text: 'Issue #1')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -239,14 +222,8 @@ RSpec.describe 'Group issues page' do
|
|||
end
|
||||
|
||||
it 'shows the pagination' do
|
||||
expect(page).to have_link 'Prev'
|
||||
expect(page).to have_link 'Next'
|
||||
end
|
||||
|
||||
it 'first pagination item is active' do
|
||||
page.within('.gl-pagination') do
|
||||
expect(find('li.active')).to have_content('1')
|
||||
end
|
||||
expect(page).to have_button 'Prev', disabled: true
|
||||
expect(page).to have_button 'Next'
|
||||
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') }
|
||||
|
||||
before do
|
||||
stub_feature_flags(vue_issues_list: true)
|
||||
|
||||
grandparent.add_owner(user)
|
||||
|
||||
sign_in(user)
|
||||
|
@ -34,8 +36,6 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
click_on 'Close'
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_selector('.gl-label', text: label.title)
|
||||
end
|
||||
end
|
||||
|
@ -44,8 +44,6 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
page.within('.block.labels') do
|
||||
click_on 'Edit'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).not_to have_text(child_group_label.title)
|
||||
end
|
||||
end
|
||||
|
@ -54,15 +52,21 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
shared_examples 'filtering by ancestor labels for projects' do |board = false|
|
||||
it 'filters by ancestor labels' do
|
||||
[grandparent_group_label, parent_group_label, project_label_1].each do |label|
|
||||
select_label_on_dropdown(label.title)
|
||||
|
||||
wait_for_requests
|
||||
|
||||
if board
|
||||
select_label_on_dropdown(label.title)
|
||||
|
||||
expect(page).to have_selector('.board-card-title') do |card|
|
||||
expect(card).to have_selector('a', text: labeled_issue.title)
|
||||
end
|
||||
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(page).to have_selector('.issue-title', text: labeled_issue.title)
|
||||
end
|
||||
|
@ -70,9 +74,11 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
end
|
||||
|
||||
it 'does not filter by descendant group labels' do
|
||||
filtered_search.set("label=")
|
||||
|
||||
wait_for_requests
|
||||
if board
|
||||
filtered_search.set("label=")
|
||||
else
|
||||
select_tokens 'Label', '='
|
||||
end
|
||||
|
||||
expect(page).not_to have_link child_group_label.title
|
||||
end
|
||||
|
@ -93,11 +99,9 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
|
||||
it 'filters by ancestors and current group labels' do
|
||||
[grandparent_group_label, parent_group_label].each do |label|
|
||||
select_label_on_dropdown(label.title)
|
||||
|
||||
wait_for_requests
|
||||
|
||||
if board
|
||||
select_label_on_dropdown(label.title)
|
||||
|
||||
expect(page).to have_selector('.board-card-title') do |card|
|
||||
expect(card).to have_selector('a', text: labeled_issue.title)
|
||||
end
|
||||
|
@ -106,6 +110,14 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
expect(card).to have_selector('a', text: labeled_issue_2.title)
|
||||
end
|
||||
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(page).to have_selector('.issue-title', text: labeled_issue.title)
|
||||
expect(page).to have_selector('.issue-title', text: labeled_issue_2.title)
|
||||
|
@ -115,11 +127,9 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
end
|
||||
|
||||
it 'filters by descendant group labels' do
|
||||
wait_for_requests
|
||||
|
||||
select_label_on_dropdown(group_label_3.title)
|
||||
|
||||
if board
|
||||
select_label_on_dropdown(group_label_3.title)
|
||||
|
||||
expect(page).to have_selector('.board-card-title') do |card|
|
||||
expect(card).not_to have_selector('a', text: labeled_issue_2.title)
|
||||
end
|
||||
|
@ -128,17 +138,23 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
expect(card).to have_selector('a', text: labeled_issue_3.title)
|
||||
end
|
||||
else
|
||||
select_tokens 'Label', '=', group_label_3.title, submit: true
|
||||
|
||||
expect_issues_list_count(1)
|
||||
expect(page).to have_selector('.issue-title', text: labeled_issue_3.title)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not filter by descendant group project labels' do
|
||||
filtered_search.set("label=")
|
||||
if board
|
||||
filtered_search.set("label=")
|
||||
|
||||
wait_for_requests
|
||||
expect(page).not_to have_selector('.btn-link', text: project_label_3.title)
|
||||
else
|
||||
select_tokens 'Label', '='
|
||||
|
||||
expect(page).not_to have_selector('.btn-link', text: project_label_3.title)
|
||||
expect(page).not_to have_link project_label_3.title
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -195,9 +211,7 @@ RSpec.describe 'Labels Hierarchy', :js do
|
|||
it_behaves_like 'filtering by ancestor labels for projects'
|
||||
|
||||
it 'does not filter by descendant group labels' do
|
||||
filtered_search.set("label=")
|
||||
|
||||
wait_for_requests
|
||||
select_tokens 'Label', '='
|
||||
|
||||
expect(page).not_to have_link child_group_label.title
|
||||
end
|
||||
|
|
|
@ -59,7 +59,6 @@ RSpec.describe 'Batch diffs', :js do
|
|||
|
||||
# Confirm scrolled to correct UI element
|
||||
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
|
||||
|
||||
|
|
|
@ -6,29 +6,60 @@ RSpec.describe 'User manages subscription', :js do
|
|||
let(:project) { create(:project, :public, :repository) }
|
||||
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let(:user) { create(:user) }
|
||||
let(:moved_mr_sidebar_enabled) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(moved_mr_sidebar: moved_mr_sidebar_enabled)
|
||||
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
visit(merge_request_path(merge_request))
|
||||
end
|
||||
|
||||
it 'toggles subscription' do
|
||||
page.within('[data-testid="subscription-toggle"]') do
|
||||
context 'moved sidebar flag disabled' do
|
||||
it 'toggles subscription' do
|
||||
page.within('[data-testid="subscription-toggle"]') do
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css 'button:not(.is-checked)'
|
||||
find('button:not(.is-checked)').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css 'button.is-checked'
|
||||
find('button.is-checked').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css 'button:not(.is-checked)'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'moved sidebar flag enabled' do
|
||||
let(:moved_mr_sidebar_enabled) { true }
|
||||
|
||||
it 'toggles subscription' do
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css 'button:not(.is-checked)'
|
||||
find('button:not(.is-checked)').click
|
||||
click_button 'Toggle dropdown'
|
||||
|
||||
expect(page).to have_content('Turn on notifications')
|
||||
click_button 'Turn on notifications'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css 'button.is-checked'
|
||||
find('button.is-checked').click
|
||||
click_button 'Toggle dropdown'
|
||||
|
||||
expect(page).to have_content('Turn off notifications')
|
||||
click_button 'Turn off notifications'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css 'button:not(.is-checked)'
|
||||
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>" }
|
||||
|
||||
before do
|
||||
stub_feature_flags(moved_mr_sidebar: false)
|
||||
|
||||
project.repository.create_branch(source_branch, "master")
|
||||
|
||||
mr = create(:merge_request, source_project: project, target_project: project, source_branch: source_branch)
|
||||
|
|
|
@ -1,300 +1,422 @@
|
|||
{
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"category": "dependency_scanning",
|
||||
"name": "Vulnerabilities in libxml2",
|
||||
"message": "Vulnerabilities in libxml2 in nokogiri",
|
||||
"description": "",
|
||||
"cve": "CVE-1020",
|
||||
"severity": "High",
|
||||
"solution": "Upgrade to latest version.",
|
||||
"scanner": {
|
||||
"id": "gemnasium",
|
||||
"name": "Gemnasium"
|
||||
},
|
||||
"evidence": {
|
||||
"source": {
|
||||
"id": "assert:CORS - Bad 'Origin' value",
|
||||
"name": "CORS - Bad 'Origin' value"
|
||||
},
|
||||
"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": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}
|
||||
],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
},
|
||||
"response": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Server",
|
||||
"value": "TwistedWeb/20.3.0"
|
||||
}
|
||||
],
|
||||
"reason_phrase": "OK",
|
||||
"status_code": 200,
|
||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||
},
|
||||
"supporting_messages": [
|
||||
{
|
||||
"name": "Origional",
|
||||
"request": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}
|
||||
],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Recorded",
|
||||
"request": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}
|
||||
],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
},
|
||||
"response": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Server",
|
||||
"value": "TwistedWeb/20.3.0"
|
||||
}
|
||||
],
|
||||
"reason_phrase": "OK",
|
||||
"status_code": 200,
|
||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"location": {},
|
||||
"identifiers": [
|
||||
{
|
||||
"type": "GitLab",
|
||||
"name": "Foo vulnerability",
|
||||
"value": "foo"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020"
|
||||
}
|
||||
],
|
||||
"details": {
|
||||
"commit": {
|
||||
"name": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "The Commit"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "Commit where the vulnerability was identified"
|
||||
}
|
||||
],
|
||||
"type": "commit",
|
||||
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3",
|
||||
"category": "dependency_scanning",
|
||||
"name": "Regular Expression Denial of Service",
|
||||
"message": "Regular Expression Denial of Service in debug",
|
||||
"description": "",
|
||||
"cve": "CVE-1030",
|
||||
"severity": "Unknown",
|
||||
"solution": "Upgrade to latest versions.",
|
||||
"scanner": {
|
||||
"id": "gemnasium",
|
||||
"name": "Gemnasium"
|
||||
},
|
||||
"evidence": {
|
||||
"source": {
|
||||
"id": "assert:CORS - Bad 'Origin' value",
|
||||
"name": "CORS - Bad 'Origin' value"
|
||||
},
|
||||
"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": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}
|
||||
],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
},
|
||||
"response": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Server",
|
||||
"value": "TwistedWeb/20.3.0"
|
||||
}
|
||||
],
|
||||
"reason_phrase": "OK",
|
||||
"status_code": 200,
|
||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||
},
|
||||
"supporting_messages": [
|
||||
{
|
||||
"name": "Origional",
|
||||
"request": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}
|
||||
],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Recorded",
|
||||
"request": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}
|
||||
],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
},
|
||||
"response": {
|
||||
"headers": [
|
||||
{
|
||||
"name": "Server",
|
||||
"value": "TwistedWeb/20.3.0"
|
||||
}
|
||||
],
|
||||
"reason_phrase": "OK",
|
||||
"status_code": 200,
|
||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"location": {},
|
||||
"identifiers": [
|
||||
{
|
||||
"type": "GitLab",
|
||||
"name": "Bar vulnerability",
|
||||
"value": "bar"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"name": "CVE-1030",
|
||||
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "dependency_scanning",
|
||||
"name": "Authentication bypass via incorrect DOM traversal and canonicalization",
|
||||
"message": "Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js",
|
||||
"description": "",
|
||||
"cve": "yarn/yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98",
|
||||
"severity": "Unknown",
|
||||
"solution": "Upgrade to fixed version.\r\n",
|
||||
"scanner": {
|
||||
"id": "gemnasium",
|
||||
"name": "Gemnasium"
|
||||
},
|
||||
"location": {},
|
||||
"identifiers": [],
|
||||
"links": [
|
||||
]
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"remediations": [
|
||||
{
|
||||
"fixes": [
|
||||
{
|
||||
"cve": "CVE-1020"
|
||||
}
|
||||
],
|
||||
"summary": "",
|
||||
"diff": ""
|
||||
},
|
||||
{
|
||||
"fixes": [
|
||||
{
|
||||
"cve": "CVE",
|
||||
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
|
||||
}
|
||||
],
|
||||
"summary": "",
|
||||
"diff": ""
|
||||
},
|
||||
{
|
||||
"fixes": [
|
||||
{
|
||||
"cve": "CVE",
|
||||
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
|
||||
}
|
||||
],
|
||||
"summary": "",
|
||||
"diff": ""
|
||||
},
|
||||
{
|
||||
"fixes": [
|
||||
{
|
||||
"id": "2134",
|
||||
"cve": "CVE-1"
|
||||
}
|
||||
],
|
||||
"summary": "",
|
||||
"diff": ""
|
||||
}
|
||||
],
|
||||
"dependency_files": [],
|
||||
"scan": {
|
||||
"analyzer": {
|
||||
"id": "common-analyzer",
|
||||
"name": "Common Analyzer",
|
||||
"url": "https://site.com/analyzer/common",
|
||||
"version": "2.0.1",
|
||||
"vendor": {
|
||||
"name": "Common"
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"id": "gemnasium",
|
||||
"name": "Gemnasium",
|
||||
"url": "https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-maven",
|
||||
"vendor": {
|
||||
"name": "GitLab"
|
||||
},
|
||||
"version": "2.18.0"
|
||||
},
|
||||
"type": "dependency_scanning",
|
||||
"start_time": "placeholder-value",
|
||||
"end_time": "placeholder-value",
|
||||
"status": "success"
|
||||
}
|
||||
},
|
||||
"version": "14.0.2"
|
||||
{
|
||||
"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",
|
||||
"name": "Vulnerabilities in libxml2",
|
||||
"message": "Vulnerabilities in libxml2 in nokogiri",
|
||||
"description": "",
|
||||
"cve": "CVE-1020",
|
||||
"severity": "High",
|
||||
"solution": "Upgrade to latest version.",
|
||||
"scanner": {
|
||||
"id": "gemnasium",
|
||||
"name": "Gemnasium"
|
||||
},
|
||||
"evidence": {
|
||||
"source": {
|
||||
"id": "assert:CORS - Bad 'Origin' value",
|
||||
"name": "CORS - Bad 'Origin' value"
|
||||
},
|
||||
"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": {
|
||||
"headers": [{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
},
|
||||
"response": {
|
||||
"headers": [{
|
||||
"name": "Server",
|
||||
"value": "TwistedWeb/20.3.0"
|
||||
}],
|
||||
"reason_phrase": "OK",
|
||||
"status_code": 200,
|
||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||
},
|
||||
"supporting_messages": [{
|
||||
"name": "Origional",
|
||||
"request": {
|
||||
"headers": [{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Recorded",
|
||||
"request": {
|
||||
"headers": [{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
},
|
||||
"response": {
|
||||
"headers": [{
|
||||
"name": "Server",
|
||||
"value": "TwistedWeb/20.3.0"
|
||||
}],
|
||||
"reason_phrase": "OK",
|
||||
"status_code": 200,
|
||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"location": {},
|
||||
"identifiers": [{
|
||||
"type": "GitLab",
|
||||
"name": "Foo vulnerability",
|
||||
"value": "foo"
|
||||
}],
|
||||
"links": [{
|
||||
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020"
|
||||
}],
|
||||
"details": {
|
||||
"commit": {
|
||||
"name": [{
|
||||
"lang": "en",
|
||||
"value": "The Commit"
|
||||
}],
|
||||
"description": [{
|
||||
"lang": "en",
|
||||
"value": "Commit where the vulnerability was identified"
|
||||
}],
|
||||
"type": "commit",
|
||||
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3",
|
||||
"category": "dependency_scanning",
|
||||
"name": "Regular Expression Denial of Service",
|
||||
"message": "Regular Expression Denial of Service in debug",
|
||||
"description": "",
|
||||
"cve": "CVE-1030",
|
||||
"severity": "Unknown",
|
||||
"solution": "Upgrade to latest versions.",
|
||||
"scanner": {
|
||||
"id": "gemnasium",
|
||||
"name": "Gemnasium"
|
||||
},
|
||||
"evidence": {
|
||||
"source": {
|
||||
"id": "assert:CORS - Bad 'Origin' value",
|
||||
"name": "CORS - Bad 'Origin' value"
|
||||
},
|
||||
"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": {
|
||||
"headers": [{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
},
|
||||
"response": {
|
||||
"headers": [{
|
||||
"name": "Server",
|
||||
"value": "TwistedWeb/20.3.0"
|
||||
}],
|
||||
"reason_phrase": "OK",
|
||||
"status_code": 200,
|
||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||
},
|
||||
"supporting_messages": [{
|
||||
"name": "Origional",
|
||||
"request": {
|
||||
"headers": [{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Recorded",
|
||||
"request": {
|
||||
"headers": [{
|
||||
"name": "Host",
|
||||
"value": "127.0.0.1:7777"
|
||||
}],
|
||||
"method": "GET",
|
||||
"url": "http://127.0.0.1:7777/api/users",
|
||||
"body": ""
|
||||
},
|
||||
"response": {
|
||||
"headers": [{
|
||||
"name": "Server",
|
||||
"value": "TwistedWeb/20.3.0"
|
||||
}],
|
||||
"reason_phrase": "OK",
|
||||
"status_code": 200,
|
||||
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"location": {},
|
||||
"identifiers": [{
|
||||
"type": "GitLab",
|
||||
"name": "Bar vulnerability",
|
||||
"value": "bar"
|
||||
}],
|
||||
"links": [{
|
||||
"name": "CVE-1030",
|
||||
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"category": "dependency_scanning",
|
||||
"name": "Authentication bypass via incorrect DOM traversal and canonicalization",
|
||||
"message": "Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js",
|
||||
"description": "",
|
||||
"cve": "yarn/yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98",
|
||||
"severity": "Unknown",
|
||||
"solution": "Upgrade to fixed version.\r\n",
|
||||
"scanner": {
|
||||
"id": "gemnasium",
|
||||
"name": "Gemnasium"
|
||||
},
|
||||
"location": {},
|
||||
"identifiers": [],
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"remediations": [{
|
||||
"fixes": [{
|
||||
"cve": "CVE-2137"
|
||||
}],
|
||||
"summary": "this remediates CVE-2137",
|
||||
"diff": "dG90YWxseSBsZWdpdCBkaWZm"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}],
|
||||
"summary": "",
|
||||
"diff": ""
|
||||
},
|
||||
{
|
||||
"fixes": [{
|
||||
"cve": "CVE",
|
||||
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
|
||||
}],
|
||||
"summary": "",
|
||||
"diff": ""
|
||||
},
|
||||
{
|
||||
"fixes": [{
|
||||
"cve": "CVE",
|
||||
"id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
|
||||
}],
|
||||
"summary": "",
|
||||
"diff": ""
|
||||
},
|
||||
{
|
||||
"fixes": [{
|
||||
"id": "2134",
|
||||
"cve": "CVE-1"
|
||||
}],
|
||||
"summary": "",
|
||||
"diff": ""
|
||||
}
|
||||
],
|
||||
"dependency_files": [],
|
||||
"scan": {
|
||||
"analyzer": {
|
||||
"id": "common-analyzer",
|
||||
"name": "Common Analyzer",
|
||||
"url": "https://site.com/analyzer/common",
|
||||
"version": "2.0.1",
|
||||
"vendor": {
|
||||
"name": "Common"
|
||||
}
|
||||
},
|
||||
"scanner": {
|
||||
"id": "gemnasium",
|
||||
"name": "Gemnasium",
|
||||
"url": "https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-maven",
|
||||
"vendor": {
|
||||
"name": "GitLab"
|
||||
},
|
||||
"version": "2.18.0"
|
||||
},
|
||||
"type": "dependency_scanning",
|
||||
"start_time": "placeholder-value",
|
||||
"end_time": "placeholder-value",
|
||||
"status": "success"
|
||||
},
|
||||
"version": "14.0.2"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import $ from 'jquery';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import initCopyAsGFM, { CopyAsGFM } from '~/behaviors/markdown/copy_as_gfm';
|
||||
|
@ -13,7 +12,6 @@ jest.mock('~/lib/utils/common_utils', () => ({
|
|||
|
||||
describe('ShortcutsIssuable', () => {
|
||||
const snippetShowFixtureName = 'snippets/show.html';
|
||||
const mrShowFixtureName = 'merge_requests/merge_request_of_current_user.html';
|
||||
|
||||
beforeAll(() => {
|
||||
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
|
||||
|
||||
context 'filtering by domain' do
|
||||
let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
|
||||
let_it_be(:alert2) { 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: 'other', domain: :threat_monitoring) }
|
||||
let_it_be(:alert3) { create(:alert_management_alert, project: project, monitoring_tool: 'generic') }
|
||||
|
||||
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
|
||||
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
|
||||
groups projects jobs token_expires_at
|
||||
maintenance_note groups projects jobs token_expires_at
|
||||
]
|
||||
|
||||
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(&:start_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)
|
||||
end
|
||||
|
||||
|
@ -348,22 +348,29 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
|
|||
it 'returns links object for each finding', :aggregate_failures do
|
||||
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(&:name)).to match_array([nil, 'CVE-1030'])
|
||||
expect(links.size).to eq(2)
|
||||
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',
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138",
|
||||
"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)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'parsing evidence' do
|
||||
it 'returns evidence object for each finding', :aggregate_failures do
|
||||
evidences = report.findings.map(&:evidence)
|
||||
RSpec::Matchers.define_negated_matcher :have_values, :be_empty
|
||||
|
||||
expect(evidences.first.data).not_to be_empty
|
||||
expect(evidences.first.data["summary"]).to match(/The Origin header was changed/)
|
||||
expect(evidences.size).to eq(3)
|
||||
expect(evidences.compact.size).to eq(2)
|
||||
expect(evidences.first).to be_a(::Gitlab::Ci::Reports::Security::Evidence)
|
||||
it 'returns evidence object for each finding', :aggregate_failures do
|
||||
all_evidences = report.findings.map(&:evidence)
|
||||
evidences = all_evidences.compact
|
||||
data = evidences.map(&:data)
|
||||
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
|
||||
|
||||
|
|
|
@ -85,6 +85,12 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do
|
|||
expect do
|
||||
transaction.increment(described_class::GEO_NODES_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
|
||||
end
|
||||
|
|
|
@ -73,10 +73,10 @@ RSpec.describe DeployToken do
|
|||
|
||||
describe '#ensure_token' do
|
||||
it 'ensures a token' do
|
||||
deploy_token.token = nil
|
||||
deploy_token.token_encrypted = nil
|
||||
deploy_token.save!
|
||||
|
||||
expect(deploy_token.token).not_to be_empty
|
||||
expect(deploy_token.token_encrypted).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ RSpec.describe 'Query.runner(id)' do
|
|||
let_it_be(:active_instance_runner) do
|
||||
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,
|
||||
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
|
||||
|
||||
let_it_be(:inactive_instance_runner) do
|
||||
|
@ -64,6 +65,7 @@ RSpec.describe 'Query.runner(id)' do
|
|||
'executorName' => runner.executor_type&.dasherize,
|
||||
'architectureName' => runner.architecture,
|
||||
'platformName' => runner.platform,
|
||||
'maintenanceNote' => runner.maintenance_note,
|
||||
'jobCount' => 0,
|
||||
'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything),
|
||||
'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(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(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(
|
||||
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(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(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
|
||||
|
||||
it 'creates a namespace object' do
|
||||
|
@ -245,47 +243,6 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
|
|||
)
|
||||
)
|
||||
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
|
||||
|
|
|
@ -274,6 +274,10 @@ module FilteredSearchHelpers
|
|||
expect(page).to have_css '.gl-filtered-search-token', text: "Milestone != %#{value}"
|
||||
end
|
||||
|
||||
def expect_epic_token(value)
|
||||
expect(page).to have_css '.gl-filtered-search-token', text: "Epic = #{value}"
|
||||
end
|
||||
|
||||
def expect_search_term(value)
|
||||
value.split(' ').each do |term|
|
||||
expect(page).to have_css '.gl-filtered-search-term', text: term
|
||||
|
|
Loading…
Reference in New Issue