Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
aa33f5d5bb
commit
28811a419e
|
@ -101,12 +101,12 @@ export default {
|
|||
@keydown.esc.native="$emit('hide-dropdown')"
|
||||
@hide="$emit('hide-dropdown')"
|
||||
>
|
||||
<div v-if="isSidebar" class="dropdown-title text-center">
|
||||
<span class="alert-title">{{ s__('AlertManagement|Assign status') }}</span>
|
||||
<div v-if="isSidebar" class="dropdown-title gl-display-flex">
|
||||
<span class="alert-title gl-ml-auto">{{ s__('AlertManagement|Assign status') }}</span>
|
||||
<gl-button
|
||||
:aria-label="__('Close')"
|
||||
variant="link"
|
||||
class="dropdown-title-button dropdown-menu-close"
|
||||
class="dropdown-title-button dropdown-menu-close gl-ml-auto gl-text-black-normal!"
|
||||
icon="close"
|
||||
@click="$emit('hide-dropdown')"
|
||||
/>
|
||||
|
|
|
@ -225,12 +225,12 @@ export default {
|
|||
@keydown.esc.native="hideDropdown"
|
||||
@hide="hideDropdown"
|
||||
>
|
||||
<div class="dropdown-title">
|
||||
<span class="alert-title">{{ __('Assign To') }}</span>
|
||||
<div class="dropdown-title gl-display-flex">
|
||||
<span class="alert-title gl-ml-auto">{{ __('Assign To') }}</span>
|
||||
<gl-button
|
||||
:aria-label="__('Close')"
|
||||
variant="link"
|
||||
class="dropdown-title-button dropdown-menu-close"
|
||||
class="dropdown-title-button dropdown-menu-close gl-ml-auto gl-text-black-normal!"
|
||||
icon="close"
|
||||
@click="hideDropdown"
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import { GlLoadingIcon, GlButton, GlIcon } from '@gitlab/ui';
|
||||
import { GlButton, GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
||||
import { sprintf, n__ } from '~/locale';
|
||||
import DraftsCount from './drafts_count.vue';
|
||||
import PublishButton from './publish_button.vue';
|
||||
|
@ -8,8 +8,8 @@ import PreviewItem from './preview_item.vue';
|
|||
|
||||
export default {
|
||||
components: {
|
||||
GlLoadingIcon,
|
||||
GlButton,
|
||||
GlLoadingIcon,
|
||||
GlIcon,
|
||||
DraftsCount,
|
||||
PublishButton,
|
||||
|
@ -81,16 +81,17 @@ export default {
|
|||
show: showPreviewDropdown,
|
||||
}"
|
||||
>
|
||||
<div class="dropdown-title">
|
||||
{{ dropdownTitle }}
|
||||
<button
|
||||
<div class="dropdown-title gl-display-flex gl-align-items-center">
|
||||
<span class="gl-ml-auto">{{ dropdownTitle }}</span>
|
||||
<gl-button
|
||||
:aria-label="__('Close')"
|
||||
type="button"
|
||||
class="dropdown-title-button dropdown-menu-close"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
class="dropdown-title-button gl-ml-auto gl-p-0!"
|
||||
icon="close"
|
||||
@click="toggleReviewDropdown"
|
||||
>
|
||||
<gl-icon name="close" />
|
||||
</button>
|
||||
/>
|
||||
</div>
|
||||
<div class="dropdown-content">
|
||||
<ul v-if="isNotesFetched">
|
||||
|
|
|
@ -199,10 +199,10 @@ export default {
|
|||
);
|
||||
},
|
||||
hasDiscussionsLeft() {
|
||||
return this.line.left.discussions && this.line.left.discussions.length > 0;
|
||||
return this.line.left?.discussions?.length > 0;
|
||||
},
|
||||
hasDiscussionsRight() {
|
||||
return this.line.right.discussions && this.line.right.discussions.length > 0;
|
||||
return this.line.right?.discussions?.length > 0;
|
||||
},
|
||||
lineHrefOld() {
|
||||
return `#${this.line.left.line_code || ''}`;
|
||||
|
@ -217,14 +217,14 @@ export default {
|
|||
);
|
||||
},
|
||||
isMetaLineLeft() {
|
||||
const { type } = this.line.left;
|
||||
const type = this.line.left?.type;
|
||||
|
||||
return (
|
||||
type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
|
||||
);
|
||||
},
|
||||
isMetaLineRight() {
|
||||
const { type } = this.line.right;
|
||||
const type = this.line.right?.type;
|
||||
|
||||
return (
|
||||
type === OLD_NO_NEW_LINE_TYPE || type === NEW_NO_NEW_LINE_TYPE || type === EMPTY_CELL_TYPE
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
<script>
|
||||
/* eslint-disable @gitlab/vue-require-i18n-strings */
|
||||
import $ from 'jquery';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import IssuableTemplateSelectors from '../../../templates/issuable_template_selectors';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
},
|
||||
props: {
|
||||
formState: {
|
||||
type: Object,
|
||||
|
@ -61,14 +65,14 @@ export default {
|
|||
<i aria-hidden="true" class="fa fa-chevron-down"> </i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-select">
|
||||
<div class="dropdown-title">
|
||||
Choose a template
|
||||
<div class="dropdown-title gl-display-flex gl-justify-content-center">
|
||||
<span class="gl-ml-auto">Choose a template</span>
|
||||
<button
|
||||
class="dropdown-title-button dropdown-menu-close"
|
||||
class="dropdown-title-button dropdown-menu-close gl-ml-auto"
|
||||
:aria-label="__('Close')"
|
||||
type="button"
|
||||
>
|
||||
<i aria-hidden="true" class="fa fa-times dropdown-menu-close-icon"> </i>
|
||||
<gl-icon name="close" class="dropdown-menu-close-icon" :aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-input">
|
||||
|
@ -79,12 +83,11 @@ export default {
|
|||
autocomplete="off"
|
||||
/>
|
||||
<i aria-hidden="true" class="fa fa-search dropdown-input-search"> </i>
|
||||
<i
|
||||
role="button"
|
||||
<gl-icon
|
||||
name="close"
|
||||
class="dropdown-input-clear js-dropdown-input-clear"
|
||||
:aria-label="__('Clear templates search input')"
|
||||
class="fa fa-times dropdown-input-clear js-dropdown-input-clear"
|
||||
>
|
||||
</i>
|
||||
/>
|
||||
</div>
|
||||
<div class="dropdown-content"></div>
|
||||
<div class="dropdown-footer">
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import { toArray } from 'lodash';
|
||||
import jQuery from 'jquery';
|
||||
import { toArray, isFunction } from 'lodash';
|
||||
import Tooltips from './components/tooltips.vue';
|
||||
|
||||
let app;
|
||||
|
@ -53,26 +54,66 @@ const handleTooltipEvent = (rootTarget, e, selector, config = {}) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const initTooltips = (selector, config = {}) => {
|
||||
const triggers = config?.triggers || DEFAULT_TRIGGER;
|
||||
const events = triggers.split(' ').map(trigger => EVENTS_MAP[trigger]);
|
||||
const applyToElements = (elements, handler) => toArray(elements).forEach(handler);
|
||||
|
||||
events.forEach(event => {
|
||||
document.addEventListener(event, e => handleTooltipEvent(document, e, selector, config), true);
|
||||
});
|
||||
|
||||
return tooltipsApp();
|
||||
const invokeBootstrapApi = (elements, method) => {
|
||||
if (isFunction(elements.tooltip)) {
|
||||
jQuery(elements).tooltip(method);
|
||||
}
|
||||
};
|
||||
|
||||
const elementsIterator = handler => elements => toArray(elements).forEach(handler);
|
||||
const isGlTooltipsEnabled = () => Boolean(window.gon.glTooltipsEnabled);
|
||||
|
||||
export const dispose = elementsIterator(element => tooltipsApp().dispose(element));
|
||||
export const fixTitle = elementsIterator(element => tooltipsApp().fixTitle(element));
|
||||
export const enable = elementsIterator(element => tooltipsApp().triggerEvent(element, 'enable'));
|
||||
export const disable = elementsIterator(element => tooltipsApp().triggerEvent(element, 'disable'));
|
||||
export const hide = elementsIterator(element => tooltipsApp().triggerEvent(element, 'close'));
|
||||
export const show = elementsIterator(element => tooltipsApp().triggerEvent(element, 'open'));
|
||||
const tooltipApiInvoker = ({ glHandler, bsHandler }) => (elements, ...params) => {
|
||||
if (isGlTooltipsEnabled()) {
|
||||
applyToElements(elements, glHandler);
|
||||
} else {
|
||||
bsHandler(elements, ...params);
|
||||
}
|
||||
};
|
||||
|
||||
export const initTooltips = (config = {}) => {
|
||||
if (isGlTooltipsEnabled()) {
|
||||
const triggers = config?.triggers || DEFAULT_TRIGGER;
|
||||
const events = triggers.split(' ').map(trigger => EVENTS_MAP[trigger]);
|
||||
|
||||
events.forEach(event => {
|
||||
document.addEventListener(
|
||||
event,
|
||||
e => handleTooltipEvent(document, e, config.selector, config),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
return tooltipsApp();
|
||||
}
|
||||
|
||||
return invokeBootstrapApi(document.body, config);
|
||||
};
|
||||
export const dispose = tooltipApiInvoker({
|
||||
glHandler: element => tooltipsApp().dispose(element),
|
||||
bsHandler: elements => invokeBootstrapApi(elements, 'dispose'),
|
||||
});
|
||||
export const fixTitle = tooltipApiInvoker({
|
||||
glHandler: element => tooltipsApp().fixTitle(element),
|
||||
bsHandler: elements => invokeBootstrapApi(elements, '_fixTitle'),
|
||||
});
|
||||
export const enable = tooltipApiInvoker({
|
||||
glHandler: element => tooltipsApp().triggerEvent(element, 'enable'),
|
||||
bsHandler: elements => invokeBootstrapApi(elements, 'enable'),
|
||||
});
|
||||
export const disable = tooltipApiInvoker({
|
||||
glHandler: element => tooltipsApp().triggerEvent(element, 'disable'),
|
||||
bsHandler: elements => invokeBootstrapApi(elements, 'disable'),
|
||||
});
|
||||
export const hide = tooltipApiInvoker({
|
||||
glHandler: element => tooltipsApp().triggerEvent(element, 'close'),
|
||||
bsHandler: elements => invokeBootstrapApi(elements, 'hide'),
|
||||
});
|
||||
export const show = tooltipApiInvoker({
|
||||
glHandler: element => tooltipsApp().triggerEvent(element, 'open'),
|
||||
bsHandler: elements => invokeBootstrapApi(elements, 'show'),
|
||||
});
|
||||
export const destroy = () => {
|
||||
tooltipsApp().$destroy();
|
||||
app = null;
|
||||
|
|
|
@ -41,12 +41,5 @@ export default {
|
|||
autocomplete="off"
|
||||
/>
|
||||
<i class="fa fa-search dropdown-input-search" aria-hidden="true" data-hidden="true"> </i>
|
||||
<i
|
||||
class="fa fa-times dropdown-input-clear js-dropdown-input-clear"
|
||||
aria-hidden="true"
|
||||
data-hidden="true"
|
||||
role="button"
|
||||
>
|
||||
</i>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
<script>
|
||||
export default {};
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown-title">
|
||||
<span>{{ __('Assign labels') }}</span>
|
||||
<div class="dropdown-title gl-display-flex gl-justify-content-center">
|
||||
<span class="gl-ml-auto">{{ __('Assign labels') }}</span>
|
||||
<button
|
||||
:aria-label="__('Close')"
|
||||
type="button"
|
||||
class="dropdown-title-button dropdown-menu-close"
|
||||
class="dropdown-title-button dropdown-menu-close gl-ml-auto"
|
||||
>
|
||||
<i aria-hidden="true" class="fa fa-times dropdown-menu-close-icon" data-hidden="true"> </i>
|
||||
<gl-icon name="close" aria-hidden="true" class="dropdown-menu-close-icon" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -596,8 +596,6 @@
|
|||
}
|
||||
|
||||
.dropdown-title-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
color: $dropdown-title-btn-color;
|
||||
border: 0;
|
||||
|
@ -609,17 +607,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-close {
|
||||
top: $gl-padding-6;
|
||||
right: $gl-padding-8;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.dropdown-menu-close-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.dropdown-menu-back {
|
||||
left: 10px;
|
||||
top: $gl-padding-8;
|
||||
|
@ -632,6 +619,7 @@
|
|||
|
||||
.fa,
|
||||
.input-icon,
|
||||
.dropdown-input-clear,
|
||||
.dropdown-input-search {
|
||||
position: absolute;
|
||||
top: $gl-padding-8;
|
||||
|
@ -682,13 +670,15 @@
|
|||
border-color: $blue-300;
|
||||
box-shadow: 0 0 4px $dropdown-input-focus-shadow;
|
||||
|
||||
~ .fa {
|
||||
~ .fa,
|
||||
~ .dropdown-input-clear {
|
||||
color: $gray-700;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
~ .fa {
|
||||
~ .fa,
|
||||
~ .dropdown-input-clear {
|
||||
color: $gray-700;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,20 +62,37 @@ module DropdownsHelper
|
|||
end
|
||||
|
||||
def dropdown_title(title, options: {})
|
||||
content_tag :div, class: "dropdown-title" do
|
||||
has_back = options.fetch(:back, false)
|
||||
has_close = options.fetch(:close, true)
|
||||
|
||||
container_class = %w[dropdown-title gl-display-flex]
|
||||
margin_class = []
|
||||
|
||||
if has_back && has_close
|
||||
container_class << 'gl-justify-content-space-between'
|
||||
elsif has_back
|
||||
margin_class << 'gl-mr-auto'
|
||||
elsif has_close
|
||||
margin_class << 'gl-ml-auto'
|
||||
end
|
||||
|
||||
container_class = container_class.join(' ')
|
||||
margin_class = margin_class.join(' ')
|
||||
|
||||
content_tag :div, class: container_class do
|
||||
title_output = []
|
||||
|
||||
if options.fetch(:back, false)
|
||||
title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-back", aria: { label: "Go back" }, type: "button") do
|
||||
if has_back
|
||||
title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-back " + margin_class, aria: { label: "Go back" }, type: "button") do
|
||||
sprite_icon('arrow-left')
|
||||
end
|
||||
end
|
||||
|
||||
title_output << content_tag(:span, title)
|
||||
title_output << content_tag(:span, title, class: margin_class)
|
||||
|
||||
if options.fetch(:close, true)
|
||||
title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close", aria: { label: "Close" }, type: "button") do
|
||||
sprite_icon('close', css_class: 'dropdown-menu-close-icon')
|
||||
if has_close
|
||||
title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close " + margin_class, aria: { label: "Close" }, type: "button") do
|
||||
sprite_icon('close', size: 16, css_class: 'dropdown-menu-close-icon')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -83,20 +100,11 @@ module DropdownsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def dropdown_input(placeholder, input_id: nil)
|
||||
content_tag :div, class: "dropdown-input" do
|
||||
filter_output = text_field_tag input_id, nil, class: "dropdown-input-field dropdown-no-filter", placeholder: placeholder, autocomplete: 'off'
|
||||
filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
|
||||
|
||||
filter_output.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def dropdown_filter(placeholder, search_id: nil)
|
||||
content_tag :div, class: "dropdown-input" do
|
||||
filter_output = search_field_tag search_id, nil, class: "dropdown-input-field qa-dropdown-input-field", placeholder: placeholder, autocomplete: 'off'
|
||||
filter_output << icon('search', class: "dropdown-input-search")
|
||||
filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
|
||||
filter_output << sprite_icon('close', size: 16, css_class: 'dropdown-input-clear js-dropdown-input-clear')
|
||||
|
||||
filter_output.html_safe
|
||||
end
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Enums
|
||||
module UserCallout
|
||||
# Returns the `Hash` to use for the `feature_name` enum in the `UserCallout`
|
||||
# model.
|
||||
#
|
||||
# This method is separate from the `UserCallout` model so that it can be
|
||||
# extended by EE.
|
||||
#
|
||||
# If you are going to add new items to this hash, check that you're not going
|
||||
# to conflict with EE-only values: https://gitlab.com/gitlab-org/gitlab/blob/master/ee/app/models/concerns/ee/enums/user_callout.rb
|
||||
def self.feature_names
|
||||
{
|
||||
gke_cluster_integration: 1,
|
||||
gcp_signup_offer: 2,
|
||||
cluster_security_warning: 3,
|
||||
suggest_popover_dismissed: 9,
|
||||
tabs_position_highlight: 10,
|
||||
webhooks_moved: 13,
|
||||
service_templates_deprecated: 14,
|
||||
admin_integrations_moved: 15,
|
||||
web_ide_alert_dismissed: 16,
|
||||
personal_access_token_expiry: 21, # EE-only
|
||||
suggest_pipeline: 22,
|
||||
customize_homepage: 23,
|
||||
feature_flags_new_version: 24
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Enums::UserCallout.prepend_if_ee('EE::Enums::UserCallout')
|
|
@ -3,9 +3,30 @@
|
|||
class UserCallout < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
# We use `Enums::UserCallout.feature_names` here so that EE can more easily
|
||||
# extend this `Hash` with new values.
|
||||
enum feature_name: Enums::UserCallout.feature_names
|
||||
enum feature_name: {
|
||||
gke_cluster_integration: 1,
|
||||
gcp_signup_offer: 2,
|
||||
cluster_security_warning: 3,
|
||||
gold_trial: 4, # EE-only
|
||||
geo_enable_hashed_storage: 5, # EE-only
|
||||
geo_migrate_hashed_storage: 6, # EE-only
|
||||
canary_deployment: 7, # EE-only
|
||||
gold_trial_billings: 8, # EE-only
|
||||
suggest_popover_dismissed: 9,
|
||||
tabs_position_highlight: 10,
|
||||
threat_monitoring_info: 11, # EE-only
|
||||
account_recovery_regular_check: 12, # EE-only
|
||||
webhooks_moved: 13,
|
||||
service_templates_deprecated: 14,
|
||||
admin_integrations_moved: 15,
|
||||
web_ide_alert_dismissed: 16,
|
||||
active_user_count_threshold: 18, # EE-only
|
||||
buy_pipeline_minutes_notification_dot: 19, # EE-only
|
||||
personal_access_token_expiry: 21, # EE-only
|
||||
suggest_pipeline: 22,
|
||||
customize_homepage: 23,
|
||||
feature_flags_new_version: 24
|
||||
}
|
||||
|
||||
validates :user, presence: true
|
||||
validates :feature_name,
|
||||
|
|
|
@ -52,67 +52,126 @@
|
|||
{
|
||||
"name": "brakeman",
|
||||
"label": "Brakeman",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "Ruby on Rails",
|
||||
"variables": [
|
||||
{
|
||||
"field" : "SAST_BRAKEMAN_LEVEL",
|
||||
"label" : "Brakeman confidence level.",
|
||||
"type": "string",
|
||||
"default_value": "1",
|
||||
"value": "",
|
||||
"size": "SMALL",
|
||||
"description": "Ignore Brakeman vulnerabilities under given confidence level. Integer, 1=Low, 2=Medium, 3=High."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bandit",
|
||||
"label": "Bandit",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "Python",
|
||||
"variables": [
|
||||
{
|
||||
"field" : "SAST_BANDIT_EXCLUDED_PATHS",
|
||||
"label" : "Paths to exclude from scan.",
|
||||
"type": "string",
|
||||
"default_value": "",
|
||||
"value": "",
|
||||
"size": "SMALL",
|
||||
"description": "Comma-separated list of paths to exclude from scan. Uses Python’s 'fnmatch' syntax; For example: '*/tests/*, */venv/*'"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eslint",
|
||||
"label": "ESLint",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "JavaScript, TypeScript, React",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "flawfinder",
|
||||
"label": "Flawfinder",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "C, C++",
|
||||
"variables": [
|
||||
{
|
||||
"field" : "SAST_FLAWFINDER_LEVEL",
|
||||
"label" : "Flawfinder risk level",
|
||||
"type": "string",
|
||||
"default_value": "1",
|
||||
"value": "",
|
||||
"size": "SMALL",
|
||||
"description": "Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "kubesec",
|
||||
"label": "kubesec",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "Kubernetes manifests, Helm Charts",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "nodejsscan",
|
||||
"name": "nodejs-scan",
|
||||
"label": "Node.js Scan",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "Node.js",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "gosec",
|
||||
"label": "Golang Security Checker",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "Go",
|
||||
"variables": [
|
||||
{
|
||||
"field" : "SAST_GOSEC_LEVEL",
|
||||
"label" : "Gosec confidence level",
|
||||
"type": "string",
|
||||
"default_value": "0",
|
||||
"value": "",
|
||||
"size": "SMALL",
|
||||
"description": "Ignore Gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "phpcs-security-audit",
|
||||
"label": "PHP Security Audit",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "PHP",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "pmd-apex",
|
||||
"label": "PMD APEX",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "Apex (Salesforce)",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "security-code-scan",
|
||||
"label": "Security Code Scan",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": ".NET Core, .NET Framework",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "sobelow",
|
||||
"label": "Sobelow",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "Elixir (Phoenix)",
|
||||
"variables": []
|
||||
},
|
||||
{
|
||||
"name": "spotbugs",
|
||||
"label": "Spotbugs",
|
||||
"enabled" : true
|
||||
},
|
||||
{
|
||||
"name": "secrets",
|
||||
"label": "Secrets",
|
||||
"enabled" : true
|
||||
"enabled" : true,
|
||||
"description": "Groovy, Java, Scala",
|
||||
"variables": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
= issuable.issue_type.capitalize || _("Select type")
|
||||
= icon('chevron-down')
|
||||
.dropdown-menu.dropdown-menu-selectable.dropdown-select
|
||||
.dropdown-title
|
||||
= _("Select type")
|
||||
%button.dropdown-title-button.dropdown-menu-close.gl-mt-3
|
||||
= icon('times', class: 'dropdown-menu-close-icon', 'aria-hidden' => 'true')
|
||||
.dropdown-title.gl-display-flex
|
||||
%span.gl-ml-auto
|
||||
= _("Select type")
|
||||
%button.dropdown-title-button.dropdown-menu-close.gl-ml-auto{ "aria-label" => _('Close') }
|
||||
= sprite_icon('close', size: 16, css_class: 'dropdown-menu-close-icon')
|
||||
.dropdown-content
|
||||
%ul
|
||||
%li.js-filter-issuable-type
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace fa-times with GitLab SVG close icon in dropdowns
|
||||
merge_request: 40585
|
||||
author:
|
||||
type: performance
|
|
@ -284,6 +284,7 @@ namespaces
|
|||
namespacing
|
||||
namespacings
|
||||
Nanoc
|
||||
Netlify
|
||||
NGINX
|
||||
Nokogiri
|
||||
npm
|
||||
|
|
|
@ -235,6 +235,7 @@ control over how the Pages daemon runs and serves content in your environment.
|
|||
| `pages_path` | The directory on disk where pages are stored, defaults to `GITLAB-RAILS/shared/pages`.
|
||||
| `pages_nginx[]` | |
|
||||
| `enable` | Include a virtual host `server{}` block for Pages inside NGINX. Needed for NGINX to proxy traffic back to the Pages daemon. Set to `false` if the Pages daemon should directly receive all requests, for example, when using [custom domains](index.md#custom-domains).
|
||||
| `FF_ENABLE_REDIRECTS` | Feature flag to enable redirects. See the [redirects documentation](../../user/project/pages/redirects.md#enable-or-disable-redirects) for more info. |
|
||||
|
||||
---
|
||||
|
||||
|
@ -424,6 +425,10 @@ Authority (CA) in the system certificate store.
|
|||
|
||||
For Omnibus, this is fixed by [installing a custom CA in Omnibus GitLab](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
|
||||
|
||||
## Enable redirects
|
||||
|
||||
In GitLab Pages, you can [enable the redirects feature](../../user/project/pages/redirects.md#enable-or-disable-redirects) to configure rules to forward one URL to another using HTTP redirects.
|
||||
|
||||
## Activate verbose logging for daemon
|
||||
|
||||
Verbose logging was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/2533) in
|
||||
|
|
|
@ -347,6 +347,10 @@ world. Custom domains and TLS are supported.
|
|||
1. Restart NGINX
|
||||
1. [Restart GitLab](../restart_gitlab.md#installations-from-source)
|
||||
|
||||
## Enable redirects
|
||||
|
||||
In GitLab Pages, you can [enable the redirects feature](../../user/project/pages/redirects.md#enable-or-disable-redirects) to configure rules to forward one URL to another using HTTP redirects.
|
||||
|
||||
## NGINX caveats
|
||||
|
||||
NOTE: **Note:**
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Admin Sidekiq queues API
|
||||
# Sidekiq queues administration API **(CORE ONLY)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25998) in GitLab 12.9
|
||||
|
||||
|
@ -15,7 +15,7 @@ The response has three fields:
|
|||
delete further jobs (including those added after the first request
|
||||
was issued).
|
||||
|
||||
This API endpoint is only available to admin users.
|
||||
This API endpoint is only available to administrators.
|
||||
|
||||
```plaintext
|
||||
DELETE /admin/sidekiq/queues/:queue_name
|
||||
|
|
|
@ -116,7 +116,7 @@ The following API resources are available outside of project and group contexts
|
|||
| Resource | Available endpoints |
|
||||
|:---------------------------------------------------|:------------------------------------------------------------------------|
|
||||
| [Instance-level CI/CD variables](instance_level_ci_variables.md) | `/admin/ci/variables` |
|
||||
| [Admin Sidekiq queues](admin_sidekiq_queues.md) | `/admin/sidekiq/queues/:queue_name` |
|
||||
| [Sidekiq queues administration](admin_sidekiq_queues.md) **(CORE ONLY)** | `/admin/sidekiq/queues/:queue_name` |
|
||||
| [Appearance](appearance.md) **(CORE ONLY)** | `/application/appearance` |
|
||||
| [Applications](applications.md) | `/applications` |
|
||||
| [Audit Events](audit_events.md) **(PREMIUM ONLY)** | `/audit_events` |
|
||||
|
@ -148,7 +148,7 @@ The following API resources are available outside of project and group contexts
|
|||
| [Search](search.md) | `/search` (also available for groups and projects) |
|
||||
| [Settings](settings.md) **(CORE ONLY)** | `/application/settings` |
|
||||
| [Statistics](statistics.md) | `/application/statistics` |
|
||||
| [Sidekiq metrics](sidekiq_metrics.md) | `/sidekiq` |
|
||||
| [Sidekiq metrics](sidekiq_metrics.md) **(CORE ONLY)** | `/sidekiq` |
|
||||
| [Suggestions](suggestions.md) | `/suggestions` |
|
||||
| [System hooks](system_hooks.md) | `/hooks` |
|
||||
| [To-dos](todos.md) | `/todos` |
|
||||
|
|
|
@ -15186,24 +15186,49 @@ Represents an analyzer entity in SAST CI configuration
|
|||
"""
|
||||
type SastCiConfigurationAnalyzersEntity {
|
||||
"""
|
||||
Analyzer description that is displayed on the form.
|
||||
Analyzer description that is displayed on the form
|
||||
"""
|
||||
description: String
|
||||
|
||||
"""
|
||||
Indicates whether an analyzer is enabled.
|
||||
Indicates whether an analyzer is enabled
|
||||
"""
|
||||
enabled: Boolean
|
||||
|
||||
"""
|
||||
Analyzer label used in the config UI.
|
||||
Analyzer label used in the config UI
|
||||
"""
|
||||
label: String
|
||||
|
||||
"""
|
||||
Name of the analyzer.
|
||||
Name of the analyzer
|
||||
"""
|
||||
name: String
|
||||
|
||||
"""
|
||||
List of supported variables
|
||||
"""
|
||||
variables(
|
||||
"""
|
||||
Returns the elements in the list that come after the specified cursor.
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""
|
||||
Returns the elements in the list that come before the specified cursor.
|
||||
"""
|
||||
before: String
|
||||
|
||||
"""
|
||||
Returns the first _n_ elements from the list.
|
||||
"""
|
||||
first: Int
|
||||
|
||||
"""
|
||||
Returns the last _n_ elements from the list.
|
||||
"""
|
||||
last: Int
|
||||
): SastCiConfigurationEntityConnection
|
||||
}
|
||||
|
||||
"""
|
||||
|
|
|
@ -44190,6 +44190,59 @@
|
|||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "variables",
|
||||
"description": "List of supported variables",
|
||||
"args": [
|
||||
{
|
||||
"name": "after",
|
||||
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
"description": "Returns the elements in the list that come before the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "first",
|
||||
"description": "Returns the first _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "last",
|
||||
"description": "Returns the last _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "SastCiConfigurationEntityConnection",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
|
@ -44206,7 +44259,7 @@
|
|||
"fields": [
|
||||
{
|
||||
"name": "description",
|
||||
"description": "Analyzer description that is displayed on the form.",
|
||||
"description": "Analyzer description that is displayed on the form",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -44220,7 +44273,7 @@
|
|||
},
|
||||
{
|
||||
"name": "enabled",
|
||||
"description": "Indicates whether an analyzer is enabled.",
|
||||
"description": "Indicates whether an analyzer is enabled",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -44234,7 +44287,7 @@
|
|||
},
|
||||
{
|
||||
"name": "label",
|
||||
"description": "Analyzer label used in the config UI.",
|
||||
"description": "Analyzer label used in the config UI",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -44248,7 +44301,7 @@
|
|||
},
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the analyzer.",
|
||||
"description": "Name of the analyzer",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
|
|
@ -2107,10 +2107,10 @@ Represents an analyzer entity in SAST CI configuration.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String | Analyzer description that is displayed on the form. |
|
||||
| `enabled` | Boolean | Indicates whether an analyzer is enabled. |
|
||||
| `label` | String | Analyzer label used in the config UI. |
|
||||
| `name` | String | Name of the analyzer. |
|
||||
| `description` | String | Analyzer description that is displayed on the form |
|
||||
| `enabled` | Boolean | Indicates whether an analyzer is enabled |
|
||||
| `label` | String | Analyzer label used in the config UI |
|
||||
| `name` | String | Name of the analyzer |
|
||||
|
||||
### SastCiConfigurationEntity
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Sidekiq Metrics API
|
||||
# Sidekiq Metrics API **(CORE ONLY)**
|
||||
|
||||
> Introduced in GitLab 8.9.
|
||||
|
||||
|
|
|
@ -166,17 +166,17 @@ instances](#indexing-large-instances) below.
|
|||
To enable Elasticsearch, you need to have admin access to GitLab:
|
||||
|
||||
1. Navigate to **Admin Area** (wrench icon), then **Settings > General**
|
||||
and expand the **Elasticsearch** section.
|
||||
and expand the **Advanced Search** section.
|
||||
|
||||
NOTE: **Note:**
|
||||
To see the Elasticsearch section, you need an active Starter
|
||||
To see the Advanced Search section, you need an active Starter
|
||||
[license](../user/admin_area/license.md).
|
||||
|
||||
1. Configure the [Elasticsearch settings](#elasticsearch-configuration) for
|
||||
your Elasticsearch cluster. Do not enable **Elasticsearch indexing** or
|
||||
**Search with Elasticsearch** yet.
|
||||
**Search with Elasticsearch enabled** yet.
|
||||
1. Click **Save changes** for the changes to take effect.
|
||||
1. Before enabling Elasticsearch indexing you need to create an index by
|
||||
1. Before enabling **Elasticsearch indexing** you need to create an index by
|
||||
running the Rake task:
|
||||
|
||||
```shell
|
||||
|
@ -188,7 +188,7 @@ To enable Elasticsearch, you need to have admin access to GitLab:
|
|||
```
|
||||
|
||||
1. Now enable `Elasticsearch indexing` in **Admin Area > Settings >
|
||||
General > Elasticsearch** and click **Save changes**.
|
||||
General > Advanced Search** and click **Save changes**.
|
||||
1. Click **Index all projects**.
|
||||
1. Click **Check progress** in the confirmation message to see the status of
|
||||
the background jobs.
|
||||
|
@ -202,8 +202,8 @@ To enable Elasticsearch, you need to have admin access to GitLab:
|
|||
bundle exec rake gitlab:elastic:index_snippets RAILS_ENV=production
|
||||
```
|
||||
|
||||
1. After the indexing has completed, enable **Search with Elasticsearch** in
|
||||
**Admin Area > Settings > General > Elasticsearch** and click **Save
|
||||
1. After the indexing has completed, enable **Search with Elasticsearch enabled** in
|
||||
**Admin Area > Settings > General > Advanced Search** and click **Save
|
||||
changes**.
|
||||
|
||||
### Elasticsearch configuration
|
||||
|
@ -213,7 +213,7 @@ The following Elasticsearch settings are available:
|
|||
| Parameter | Description |
|
||||
|-------------------------------------------------------|-------------|
|
||||
| `Elasticsearch indexing` | Enables or disables Elasticsearch indexing. You may want to enable indexing but disable search in order to give the index time to be fully completed, for example. Also, keep in mind that this option doesn't have any impact on existing data, this only enables/disables the background indexer which tracks data changes and ensures new data is indexed. |
|
||||
| `Elasticsearch pause indexing` | Enables or disables temporary indexing pause. This is useful for cluster migration/reindexing. All changes are still tracked, but they are not committed to the Elasticsearch index until unpaused. |
|
||||
| `Pause Elasticsearch indexing` | Enables or disables temporary indexing pause. This is useful for cluster migration/reindexing. All changes are still tracked, but they are not committed to the Elasticsearch index until unpaused. |
|
||||
| `Search with Elasticsearch enabled` | Enables or disables using Elasticsearch in search. |
|
||||
| `URL` | The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., `http://host1, https://host2:9200`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (e.g., `http://<username>:<password>@<elastic_host>:9200/`). |
|
||||
| `Number of Elasticsearch shards` | Elasticsearch indexes are split into multiple shards for performance reasons. In general, larger indexes need to have more shards. Changes to this value do not take effect until the index is recreated. You can read more about tradeoffs in the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/scalability.html). |
|
||||
|
@ -258,7 +258,7 @@ from the Elasticsearch index as expected.
|
|||
To disable the Elasticsearch integration:
|
||||
|
||||
1. Navigate to the **Admin Area** (wrench icon), then **Settings > General**.
|
||||
1. Expand the **Elasticsearch** section and uncheck **Elasticsearch indexing**
|
||||
1. Expand the **Advanced Search** section and uncheck **Elasticsearch indexing**
|
||||
and **Search with Elasticsearch enabled**.
|
||||
1. Click **Save changes** for the changes to take effect.
|
||||
1. (Optional) Delete the existing index:
|
||||
|
@ -287,7 +287,7 @@ used by the GitLab Elasticsearch integration.
|
|||
|
||||
### Pause the indexing
|
||||
|
||||
In the **Admin Area > Settings > General > Elasticsearch** section, select the
|
||||
In the **Admin Area > Settings > General > Advanced Search** section, select the
|
||||
**Pause Elasticsearch Indexing** setting, and then save your change.
|
||||
|
||||
With this, all updates that should happen on your Elasticsearch index will be
|
||||
|
@ -297,13 +297,13 @@ buffered and caught up once unpaused.
|
|||
|
||||
TIP: **Tip:**
|
||||
If your index was created with GitLab 13.0 or greater, you can directly
|
||||
[trigger the reindex](#trigger-the-reindex-via-the-elasticsearch-administration).
|
||||
[trigger the reindex](#trigger-the-reindex-via-the-advanced-search-administration).
|
||||
|
||||
This process involves several shell commands and curl invocations, so a good
|
||||
initial setup will help for later:
|
||||
|
||||
```shell
|
||||
# You can find this value under Admin Area > Settings > General > Elasticsearch > URL
|
||||
# You can find this value under Admin Area > Settings > General > Advanced Search > URL
|
||||
export CLUSTER_URL="http://localhost:9200"
|
||||
export PRIMARY_INDEX="gitlab-production"
|
||||
export SECONDARY_INDEX="gitlab-production-$(date +%s)"
|
||||
|
@ -400,18 +400,18 @@ To trigger the re-index from `primary` index:
|
|||
$CLUSTER_URL/_aliases
|
||||
```
|
||||
|
||||
The reindexing is now completed. Your GitLab instance is now ready to use the [automated in-cluster reindexing](#trigger-the-reindex-via-the-elasticsearch-administration) feature for future reindexing.
|
||||
The reindexing is now completed. Your GitLab instance is now ready to use the [automated in-cluster reindexing](#trigger-the-reindex-via-the-advanced-search-administration) feature for future reindexing.
|
||||
|
||||
1. Unpause the indexing
|
||||
|
||||
Under **Admin Area > Settings > General > Elasticsearch**, uncheck the **Pause Elasticsearch Indexing** setting and save.
|
||||
Under **Admin Area > Settings > General > Advanced Search**, uncheck the **Pause Elasticsearch Indexing** setting and save.
|
||||
|
||||
### Trigger the reindex via the Elasticsearch administration
|
||||
### Trigger the reindex via the Advanced Search administration
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34069) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.2.
|
||||
> - A scheduled index deletion and the ability to cancel it was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38914) in GitLab Starter 13.3.
|
||||
|
||||
Under **Admin Area > Settings > General > Elasticsearch > Elasticsearch zero-downtime reindexing**, click on **Trigger cluster reindexing**.
|
||||
Under **Admin Area > Settings > General > Advanced Search > Elasticsearch zero-downtime reindexing**, click on **Trigger cluster reindexing**.
|
||||
|
||||
NOTE: **Note:**
|
||||
Reindexing can be a lengthy process depending on the size of your Elasticsearch cluster.
|
||||
|
@ -433,7 +433,7 @@ The following are some available Rake tasks:
|
|||
|
||||
| Task | Description |
|
||||
|:--------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`sudo gitlab-rake gitlab:elastic:index`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Enables Elasticsearch Indexing and run `gitlab:elastic:create_empty_index`, `gitlab:elastic:clear_index_status`, `gitlab:elastic:index_projects`, and `gitlab:elastic:index_snippets`. |
|
||||
| [`sudo gitlab-rake gitlab:elastic:index`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Enables Elasticsearch indexing and run `gitlab:elastic:create_empty_index`, `gitlab:elastic:clear_index_status`, `gitlab:elastic:index_projects`, and `gitlab:elastic:index_snippets`. |
|
||||
| [`sudo gitlab-rake gitlab:elastic:index_projects`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Iterates over all projects and queues Sidekiq jobs to index them in the background. |
|
||||
| [`sudo gitlab-rake gitlab:elastic:index_projects_status`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Determines the overall status of the indexing. It is done by counting the total number of indexed projects, dividing by a count of the total number of projects, then multiplying by 100. |
|
||||
| [`sudo gitlab-rake gitlab:elastic:clear_index_status`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Deletes all instances of IndexStatus for all projects. |
|
||||
|
@ -664,7 +664,7 @@ Sidekiq processes](../administration/operations/extra_sidekiq_processes.md).
|
|||
} }'
|
||||
```
|
||||
|
||||
1. After the indexing has completed, enable [**Search with Elasticsearch**](#enabling-elasticsearch).
|
||||
1. After the indexing has completed, enable [**Search with Elasticsearch enabled**](#enabling-elasticsearch).
|
||||
|
||||
### Deleted documents
|
||||
|
||||
|
@ -848,7 +848,7 @@ There is a [more structured, lower-level troubleshooting document](../administra
|
|||
|
||||
Improvements to the `code_analyzer` pattern and filters is being discussed in [epic 3621](https://gitlab.com/groups/gitlab-org/-/epics/3621).
|
||||
|
||||
### Reverting to basic search
|
||||
### Reverting to Basic Search
|
||||
|
||||
Sometimes there may be issues with your Elasticsearch index data and as such
|
||||
GitLab will allow you to revert to "basic search" when there are no search
|
||||
|
|
|
@ -59,6 +59,7 @@ To update a GitLab Pages website:
|
|||
| [Explore GitLab Pages](introduction.md) | Requirements, technical aspects, specific GitLab CI/CD configuration options, Access Control, custom 404 pages, limitations, FAQ. |
|
||||
| [Custom domains and SSL/TLS Certificates](custom_domains_ssl_tls_certification/index.md) | Custom domains and subdomains, DNS records, and SSL/TLS certificates. |
|
||||
| [Let's Encrypt integration](custom_domains_ssl_tls_certification/lets_encrypt_integration.md) | Secure your Pages sites with Let's Encrypt certificates, which are automatically obtained and renewed by GitLab. |
|
||||
| [Redirects](redirects.md) | Set up HTTP redirects to forward one page to another. |
|
||||
|
||||
Learn more and see examples:
|
||||
|
||||
|
|
|
@ -60,14 +60,8 @@ If the case of `404.html`, there are different scenarios. For example:
|
|||
|
||||
## Redirects in GitLab Pages
|
||||
|
||||
Since you cannot use any custom server configuration files, like `.htaccess` or
|
||||
any `.conf` file, if you want to redirect a page to another
|
||||
location, you can use the [HTTP meta refresh tag](https://en.wikipedia.org/wiki/Meta_refresh).
|
||||
|
||||
Some static site generators provide plugins for that functionality so that you
|
||||
don't have to create and edit HTML files manually. For example, Jekyll has the
|
||||
[redirect-from plugin](https://github.com/jekyll/jekyll-redirect-from), Sphinx
|
||||
has the [sphinx-reredirects](https://gitlab.com/documatt/sphinx-reredirects) extension.
|
||||
You can configure redirects for your site using a `_redirects` file. To learn more, read
|
||||
the [redirects documentation](redirects.md).
|
||||
|
||||
## GitLab Pages Access Control **(CORE)**
|
||||
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
stage: Release
|
||||
group: Release Management
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Create redirects for GitLab Pages
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/24) in GitLab Pages 1.25.0 and GitLab 13.4.
|
||||
> - It's [deployed behind a feature flag](#enable-or-disable-redirects), disabled by default.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-redirects).
|
||||
|
||||
CAUTION: **Warning:**
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
In GitLab Pages, you can [enable](#enable-or-disable-redirects) the redirects feature to configure rules to forward one URL to another using HTTP redirects. GitLab Pages uses
|
||||
[Netlify style redirects](https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file).
|
||||
|
||||
## Supported features
|
||||
|
||||
GitLab Pages only supports the
|
||||
[`_redirects` plain text file syntax](https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file),
|
||||
and `.toml` files are not supported.
|
||||
|
||||
Redirects are only supported at a basic level, and GitLab Pages doesn't support all
|
||||
[special options offered by Netlify](https://docs.netlify.com/routing/redirects/redirect-options/):
|
||||
|
||||
| Feature | Supported | Example |
|
||||
| ------- | --------- | ------- |
|
||||
| Redirects (`301`, `302`) | **{check-circle}** Yes | `/wardrobe.html /narnia.html 302`
|
||||
| Rewrites (other status codes) | **{dotted-circle}** No | `/en/* /en/404.html 404` |
|
||||
| [Splats](https://docs.netlify.com/routing/redirects/redirect-options/#splats) | **{dotted-circle}** No | `/news/* /blog/:splat` |
|
||||
| Placeholders | **{dotted-circle}** No | `/news/:year/:month/:date/:slug /blog/:year/:month/:date/:slug` |
|
||||
| Query parameters | **{dotted-circle}** No | `/store id=:id /blog/:id 301` |
|
||||
| Force ([shadowing](https://docs.netlify.com/routing/redirects/rewrites-proxies/#shadowing)) | **{dotted-circle}** No | `/app/ /app/index.html 200!` |
|
||||
| Domain-level redirects | **{dotted-circle}** No | `http://blog.example.com/* https://www.example.com/blog/:splat 301` |
|
||||
| Redirect by country or language | **{dotted-circle}** No | `/ /anz 302 Country=au,nz` |
|
||||
| Redirect by role | **{dotted-circle}** No | `/admin/* 200! Role=admin` |
|
||||
|
||||
NOTE: **Note:**
|
||||
Supported paths must start with a forward slash `/`.
|
||||
|
||||
## Create redirects
|
||||
|
||||
To create redirects after [enabling](#enable-or-disable-redirects) the feature,
|
||||
create a configuration file named `_redirects` in the `public/` directory of your
|
||||
GitLab Pages site.
|
||||
|
||||
If your GitLab Pages site uses the default domain name (such as
|
||||
`namespace.gitlab.io/projectname`) you must prefix every rule with the project name:
|
||||
|
||||
```plaintext
|
||||
/projectname/redirect-portal.html /projectname/magic-land.html 301
|
||||
/projectname/cake-portal.html /projectname/still-alive.html 302
|
||||
/projectname/wardrobe.html /projectname/narnia.html 302
|
||||
/projectname/pit.html /projectname/spikes.html 302
|
||||
```
|
||||
|
||||
If your GitLab Pages site uses [custom domains](custom_domains_ssl_tls_certification/index.md),
|
||||
no project name prefix is needed. For example, if your custom domain is `example.com`,
|
||||
your `_redirect` file would look like:
|
||||
|
||||
```plaintext
|
||||
/redirect-portal.html /magic-land.html 301
|
||||
/cake-portal.html /still-alive.html 302
|
||||
/wardrobe.html /narnia.html 302
|
||||
/pit.html /spikes.html 302
|
||||
```
|
||||
|
||||
## Files override redirects
|
||||
|
||||
Files take priority over redirects. If a file exists on disk, GitLab Pages serves
|
||||
the file instead of your redirect. For example, if the files `hello.html` and
|
||||
`world.html` exist, and the `_redirects` file contains the following line, the redirect
|
||||
is ignored because `hello.html` exists:
|
||||
|
||||
```plaintext
|
||||
/projectname/hello.html /projectname/world.html 302
|
||||
```
|
||||
|
||||
NOTE: **Note:**
|
||||
GitLab does not support Netlify's
|
||||
[force option](https://docs.netlify.com/routing/redirects/rewrites-proxies/#shadowing)
|
||||
to change this behavior.
|
||||
|
||||
## Debug redirect rules
|
||||
|
||||
If a redirect isn't working as expected, or you want to check your redirect syntax, visit
|
||||
`https://[namespace.gitlab.io]/projectname/_redirects`, replacing `[namespace.gitlab.io]` with
|
||||
your domain name. The `_redirects` file isn't served directly, but your browser
|
||||
displays a numbered list of your redirect rules, and whether the rule is valid or invalid:
|
||||
|
||||
```plaintext
|
||||
11 rules
|
||||
rule 1: valid
|
||||
rule 2: valid
|
||||
rule 3: error: splats are not supported
|
||||
rule 4: valid
|
||||
rule 5: error: placeholders are not supported
|
||||
rule 6: valid
|
||||
rule 7: error: no domain-level redirects to outside sites
|
||||
rule 8: error: url path must start with forward slash /
|
||||
rule 9: error: no domain-level redirects to outside sites
|
||||
rule 10: valid
|
||||
rule 11: valid
|
||||
```
|
||||
|
||||
## Enable or disable redirects
|
||||
|
||||
Redirects in GitLab Pages is under development and not ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
|
||||
For [Omnibus installations](../../../administration/pages/index.md), define the
|
||||
`FF_ENABLE_REDIRECTS` environment variable in the
|
||||
[global settings](../../../administration/pages/index.md#global-settings).
|
||||
Add the following line to `/etc/gitlab/gitlab.rb` and
|
||||
[reconfigure the instance](../../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
|
||||
```ruby
|
||||
gitlab_pages['env']['FF_ENABLE_REDIRECTS'] = 'true'
|
||||
```
|
||||
|
||||
For [source installations](../../../administration/pages/source.md), define the
|
||||
`FF_ENABLE_REDIRECTS` environment variable, then
|
||||
[restart GitLab](../../../administration/restart_gitlab.md#installations-from-source):
|
||||
|
||||
```shell
|
||||
export FF_ENABLE_REDIRECTS="true"
|
||||
/path/to/pages/bin/gitlab-pages -config gitlab-pages.conf
|
||||
```
|
|
@ -100,7 +100,7 @@ To edit the details of a release:
|
|||
1. Click **Save changes**.
|
||||
|
||||
You can edit the release title, notes, associated milestones, and asset links.
|
||||
To change other release information, such as the tag or release date, use the
|
||||
To change the release date use the
|
||||
[Releases API](../../../api/releases/index.md#update-a-release).
|
||||
|
||||
## Add release notes to Git tags
|
||||
|
|
|
@ -2139,6 +2139,12 @@ msgstr ""
|
|||
msgid "Advanced"
|
||||
msgstr ""
|
||||
|
||||
msgid "Advanced Search"
|
||||
msgstr ""
|
||||
|
||||
msgid "Advanced Search with Elasticsearch"
|
||||
msgstr ""
|
||||
|
||||
msgid "Advanced Settings"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9273,9 +9279,6 @@ msgstr ""
|
|||
msgid "Editing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Elasticsearch"
|
||||
msgstr ""
|
||||
|
||||
msgid "Elasticsearch AWS IAM credentials"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9288,9 +9291,6 @@ msgstr ""
|
|||
msgid "Elasticsearch indexing started"
|
||||
msgstr ""
|
||||
|
||||
msgid "Elasticsearch integration. Elasticsearch AWS IAM."
|
||||
msgstr ""
|
||||
|
||||
msgid "Elasticsearch reindexing is already in progress"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -2,7 +2,13 @@
|
|||
|
||||
FactoryBot.define do
|
||||
factory :merge_request_diff do
|
||||
association :merge_request, :without_merge_request_diff
|
||||
merge_request do
|
||||
build(:merge_request) do |merge_request|
|
||||
# MergeRequest should not create a MergeRequestDiff in the callback
|
||||
allow(merge_request).to receive(:ensure_merge_request_diff)
|
||||
end
|
||||
end
|
||||
|
||||
state { :collected }
|
||||
commits_count { 1 }
|
||||
|
||||
|
|
|
@ -92,16 +92,6 @@ FactoryBot.define do
|
|||
target_branch { "feature_two" }
|
||||
end
|
||||
|
||||
trait(:without_merge_request_diff) do
|
||||
after(:build) do |_|
|
||||
MergeRequest.skip_callback(:create, :after, :ensure_merge_request_diff)
|
||||
end
|
||||
|
||||
after(:create) do |_|
|
||||
MergeRequest.set_callback(:create, :after, :ensure_merge_request_diff)
|
||||
end
|
||||
end
|
||||
|
||||
trait :locked do
|
||||
state_id { MergeRequest.available_states[:locked] }
|
||||
end
|
||||
|
|
|
@ -228,19 +228,20 @@ describe('ParallelDiffTableRow', () => {
|
|||
const findNoteButton = () => wrapper.find({ ref: 'addDiffNoteButtonLeft' });
|
||||
|
||||
it.each`
|
||||
hover | userData | query | mergeRefHeadComments | expectation
|
||||
${true} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${true}
|
||||
${true} | ${TEST_USER} | ${'diff_head=true'} | ${true} | ${true}
|
||||
${true} | ${TEST_USER} | ${'diff_head=true'} | ${false} | ${false}
|
||||
${true} | ${null} | ${''} | ${true} | ${false}
|
||||
${false} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${false}
|
||||
hover | line | userData | query | mergeRefHeadComments | expectation
|
||||
${true} | ${{}} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${true}
|
||||
${true} | ${{ line: { left: null } }} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${false}
|
||||
${true} | ${{}} | ${TEST_USER} | ${'diff_head=true'} | ${true} | ${true}
|
||||
${true} | ${{}} | ${TEST_USER} | ${'diff_head=true'} | ${false} | ${false}
|
||||
${true} | ${{}} | ${null} | ${''} | ${true} | ${false}
|
||||
${false} | ${{}} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${false}
|
||||
`(
|
||||
'exists is $expectation - with userData ($userData) query ($query)',
|
||||
async ({ hover, userData, query, mergeRefHeadComments, expectation }) => {
|
||||
async ({ hover, line, userData, query, mergeRefHeadComments, expectation }) => {
|
||||
store.state.notes.userData = userData;
|
||||
gon.features = { mergeRefHeadComments };
|
||||
setWindowLocation({ href: `${TEST_HOST}?${query}` });
|
||||
createComponent({}, store);
|
||||
createComponent(line, store);
|
||||
if (hover) await wrapper.find('.line_holder').trigger('mouseover');
|
||||
|
||||
expect(findNoteButton().exists()).toBe(expectation);
|
||||
|
|
|
@ -1,26 +1,39 @@
|
|||
<div>
|
||||
<div class="dropdown inline">
|
||||
<button class="dropdown-menu-toggle" data-toggle="dropdown" id="js-project-dropdown" type="button">
|
||||
<div class="dropdown-toggle-text">
|
||||
Projects
|
||||
</div>
|
||||
<i class="fa fa-chevron-down dropdown-toggle-caret js-projects-dropdown-toggle"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-select dropdown-menu-selectable">
|
||||
<div class="dropdown-title">
|
||||
<span>Go to project</span>
|
||||
<button aria="{:label=>"Close"}" class="dropdown-title-button dropdown-menu-close">
|
||||
<i class="fa fa-times dropdown-menu-close-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-input">
|
||||
<input class="dropdown-input-field" placeholder="Filter results" type="search">
|
||||
<i class="fa fa-search dropdown-input-search"></i>
|
||||
</div>
|
||||
<div class="dropdown-content"></div>
|
||||
<div class="dropdown-loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown inline">
|
||||
<button
|
||||
class="dropdown-menu-toggle"
|
||||
data-toggle="dropdown"
|
||||
id="js-project-dropdown"
|
||||
type="button"
|
||||
>
|
||||
<div class="dropdown-toggle-text">
|
||||
Projects
|
||||
</div>
|
||||
<i class="fa fa-chevron-down dropdown-toggle-caret js-projects-dropdown-toggle"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-select dropdown-menu-selectable">
|
||||
<div class="dropdown-title gl-display-flex gl-align-items-center">
|
||||
<span class="gl-ml-auto">Go to project</span>
|
||||
<button
|
||||
aria-label="Close"
|
||||
type="button"
|
||||
class="btn dropdown-title-button dropdown-menu-close gl-ml-auto btn-default btn-sm gl-button btn-default-tertiary btn-icon"
|
||||
>
|
||||
<svg data-testid="close-icon" class="gl-icon s16 dropdown-menu-close-icon">
|
||||
<use
|
||||
href="/assets/icons-795a2ef2fd636a0538bbef3b8d2787dd90927b42d7617fdda8620930016b333d.svg#close"
|
||||
></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-input">
|
||||
<input class="dropdown-input-field" placeholder="Filter results" type="search" />
|
||||
<i class="fa fa-search dropdown-input-search"></i>
|
||||
</div>
|
||||
<div class="dropdown-content"></div>
|
||||
<div class="dropdown-loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import jQuery from 'jquery';
|
||||
import { initTooltips, dispose, destroy, hide, show, enable, disable, fixTitle } from '~/tooltips';
|
||||
|
||||
describe('tooltips/index.js', () => {
|
||||
|
@ -21,7 +22,7 @@ describe('tooltips/index.js', () => {
|
|||
};
|
||||
|
||||
const buildTooltipsApp = () => {
|
||||
tooltipsApp = initTooltips('.has-tooltip');
|
||||
tooltipsApp = initTooltips({ selector: '.has-tooltip' });
|
||||
};
|
||||
|
||||
const triggerEvent = (target, eventName = 'mouseenter') => {
|
||||
|
@ -30,6 +31,10 @@ describe('tooltips/index.js', () => {
|
|||
target.dispatchEvent(event);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
window.gon.glTooltipsEnabled = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.childNodes.forEach(node => node.remove());
|
||||
destroy();
|
||||
|
@ -117,4 +122,28 @@ describe('tooltips/index.js', () => {
|
|||
|
||||
expect(tooltipsApp.fixTitle).toHaveBeenCalledWith(target);
|
||||
});
|
||||
|
||||
describe('when glTooltipsEnabled feature flag is disabled', () => {
|
||||
beforeEach(() => {
|
||||
window.gon.glTooltipsEnabled = false;
|
||||
});
|
||||
|
||||
it.each`
|
||||
method | methodName | bootstrapParams
|
||||
${dispose} | ${'dispose'} | ${'dispose'}
|
||||
${fixTitle} | ${'fixTitle'} | ${'_fixTitle'}
|
||||
${enable} | ${'enable'} | ${'enable'}
|
||||
${disable} | ${'disable'} | ${'disable'}
|
||||
${hide} | ${'hide'} | ${'hide'}
|
||||
${show} | ${'show'} | ${'show'}
|
||||
`('delegates $methodName to bootstrap tooltip API', ({ method, bootstrapParams }) => {
|
||||
const elements = jQuery(createTooltipTarget());
|
||||
|
||||
jest.spyOn(jQuery.fn, 'tooltip');
|
||||
|
||||
method(elements);
|
||||
|
||||
expect(elements.tooltip).toHaveBeenCalledWith(bootstrapParams);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,12 +32,6 @@ describe('DropdownSearchInputComponent', () => {
|
|||
expect(wrapper.find('.fa-search.dropdown-input-search').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders clear search icon element', () => {
|
||||
expect(wrapper.find('.fa-times.dropdown-input-clear.js-dropdown-input-clear').exists()).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('displays custom placeholder text', () => {
|
||||
expect(findInputEl().attributes('placeholder')).toBe(defaultProps.placeholderText);
|
||||
});
|
||||
|
|
|
@ -33,7 +33,7 @@ describe('DropdownHeaderComponent', () => {
|
|||
);
|
||||
|
||||
expect(closeBtnEl).not.toBeNull();
|
||||
expect(closeBtnEl.querySelector('.fa-times.dropdown-menu-close-icon')).not.toBeNull();
|
||||
expect(closeBtnEl.querySelector('.dropdown-menu-close-icon')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe DropdownsHelper do
|
||||
before do
|
||||
allow(helper).to receive(:sprite_icon).and_return('<span class="icon"></span>'.html_safe)
|
||||
allow(helper).to receive(:icon).and_return('<span class="icon"></span>'.html_safe)
|
||||
end
|
||||
|
||||
shared_examples 'has two icons' do
|
||||
it 'returns two icons' do
|
||||
expect(content.scan('icon').count).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dropdown_tag' do
|
||||
let(:content) { helper.dropdown_tag('toggle', options: { wrapper_class: 'fuz' }) { 'fizzbuzz' } }
|
||||
|
||||
it 'returns the container in the content' do
|
||||
expect(content).to include('dropdown fuz')
|
||||
end
|
||||
|
||||
it 'returns the block in the content' do
|
||||
expect(content).to include('fizzbuzz')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dropdown_toggle' do
|
||||
let(:content) { helper.dropdown_toggle('foo', { default_label: 'foo' }, { toggle_class: 'fuz' }) }
|
||||
|
||||
it 'returns the button' do
|
||||
expect(content).to include('dropdown-menu-toggle fuz')
|
||||
end
|
||||
|
||||
it 'returns the buttons default label data attribute' do
|
||||
expect(content).to include('data-default-label="foo"')
|
||||
end
|
||||
|
||||
it 'returns the dropdown toggle text', :aggregate_failures do
|
||||
expect(content).to include('dropdown-toggle-text is-default')
|
||||
expect(content).to include('foo')
|
||||
end
|
||||
|
||||
it 'returns the button icon in the content' do
|
||||
expect(content.scan('icon').count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dropdown_toggle_link' do
|
||||
let(:content) { dropdown_toggle_link('foo', { data: 'bar' }, { toggle_class: 'fuz' }) }
|
||||
|
||||
it 'returns the link' do
|
||||
expect(content).to include('dropdown-toggle-text fuz')
|
||||
end
|
||||
|
||||
it 'returns the links data attribute' do
|
||||
expect(content).to include('data-data="bar"')
|
||||
end
|
||||
|
||||
it 'returns the link text' do
|
||||
expect(content).to include('foo')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dropdown_title' do
|
||||
shared_examples 'has a back button' do
|
||||
it 'contains the back button' do
|
||||
expect(content).to include('dropdown-title-button dropdown-menu-back')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'does not have a back button' do
|
||||
it 'does not contain the back button' do
|
||||
expect(content).not_to include('dropdown-title-button dropdown-menu-back')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'does not apply the margin class to the back button' do
|
||||
it 'does not contain the back button margin class' do
|
||||
expect(content).not_to include('dropdown-title-button dropdown-menu-back gl-mr-auto')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'has a close button' do
|
||||
it 'contains the close button' do
|
||||
expect(content).to include('dropdown-title-button dropdown-menu-close')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'does not have a close button' do
|
||||
it 'does not contain the close button' do
|
||||
expect(content).not_to include('dropdown-title-button dropdown-menu-close')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'does not apply the margin class to the close button' do
|
||||
it 'does not contain the close button margin class' do
|
||||
expect(content).not_to include('dropdown-title-button dropdown-menu-close gl-ml-auto')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'has the title text' do
|
||||
it 'contains the title text' do
|
||||
expect(content).to include('Foo')
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'has the title margin class' do |margin_class: ''|
|
||||
it 'contains the title margin class' do
|
||||
expect(content).to match(/class="#{margin_class}.*"[^>]*>Foo/)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'does not have the title margin class' do
|
||||
it 'does not have the title margin class' do
|
||||
expect(content).not_to match(/class="gl-m[r|l]-auto.*"[^>]*>Foo/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a back and close button' do
|
||||
let(:content) { helper.dropdown_title('Foo', options: { back: true, close: true }) }
|
||||
|
||||
it 'applies the justification class to the container', :aggregate_failures do
|
||||
expect(content).to match(/"dropdown-title.*gl-justify-content-space-between"/)
|
||||
end
|
||||
|
||||
it_behaves_like 'has a back button'
|
||||
it_behaves_like 'has the title text'
|
||||
it_behaves_like 'has a close button'
|
||||
it_behaves_like 'has two icons'
|
||||
it_behaves_like 'does not have the title margin class'
|
||||
end
|
||||
|
||||
context 'with a back button' do
|
||||
let(:content) { helper.dropdown_title('Foo', options: { back: true, close: false }) }
|
||||
|
||||
it_behaves_like 'has a back button'
|
||||
it_behaves_like 'has the title text'
|
||||
it_behaves_like 'has the title margin class', margin_class: 'gl-mr-auto'
|
||||
it_behaves_like 'does not have a close button'
|
||||
|
||||
it 'returns the back button icon' do
|
||||
expect(content.scan('icon').count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a close button' do
|
||||
let(:content) { helper.dropdown_title('Foo', options: { back: false, close: true }) }
|
||||
|
||||
it_behaves_like 'does not have a back button'
|
||||
it_behaves_like 'has the title text'
|
||||
it_behaves_like 'has the title margin class', margin_class: 'gl-ml-auto'
|
||||
it_behaves_like 'has a close button'
|
||||
|
||||
it 'returns the close button icon' do
|
||||
expect(content.scan('icon').count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without any buttons' do
|
||||
let(:content) { helper.dropdown_title('Foo', options: { back: false, close: false }) }
|
||||
|
||||
it_behaves_like 'does not have a back button'
|
||||
it_behaves_like 'has the title text'
|
||||
it_behaves_like 'does not have the title margin class'
|
||||
it_behaves_like 'does not have a close button'
|
||||
|
||||
it 'returns no button icons' do
|
||||
expect(content.scan('icon').count).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dropdown_filter' do
|
||||
let(:content) { helper.dropdown_filter('foo') }
|
||||
|
||||
it_behaves_like 'has two icons'
|
||||
|
||||
it 'returns the container' do
|
||||
expect(content).to include('dropdown-input')
|
||||
end
|
||||
|
||||
it 'returns the search input', :aggregate_failures do
|
||||
expect(content).to include('dropdown-input-field')
|
||||
expect(content).to include('placeholder="foo"')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dropdown_content' do
|
||||
shared_examples 'contains the container' do
|
||||
it 'returns the container in the content' do
|
||||
expect(content).to include('dropdown-content')
|
||||
end
|
||||
end
|
||||
|
||||
context 'without block' do
|
||||
let(:content) { helper.dropdown_content }
|
||||
|
||||
it_behaves_like 'contains the container'
|
||||
end
|
||||
|
||||
context 'with block' do
|
||||
let(:content) { helper.dropdown_content { 'foo' } }
|
||||
|
||||
it_behaves_like 'contains the container'
|
||||
|
||||
it 'returns the block in the content' do
|
||||
expect(content).to include('foo')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dropdown_footer' do
|
||||
shared_examples 'contains the content' do
|
||||
it 'returns the container in the content' do
|
||||
expect(content).to include('dropdown-footer')
|
||||
end
|
||||
|
||||
it 'returns the block in the content' do
|
||||
expect(content).to include('foo')
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a content class' do
|
||||
let(:content) { helper.dropdown_footer { 'foo' } }
|
||||
|
||||
it_behaves_like 'contains the content'
|
||||
end
|
||||
|
||||
context 'without a content class' do
|
||||
let(:content) { helper.dropdown_footer(add_content_class: true) { 'foo' } }
|
||||
|
||||
it_behaves_like 'contains the content'
|
||||
|
||||
it 'returns the footer in the content' do
|
||||
expect(content).to include('dropdown-footer-content')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#dropdown_loading' do
|
||||
let(:content) { helper.dropdown_loading }
|
||||
|
||||
it 'returns the container in the content' do
|
||||
expect(content).to include('dropdown-loading')
|
||||
end
|
||||
|
||||
it 'returns an icon in the content' do
|
||||
expect(content.scan('icon').count).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,6 +4,7 @@ include:
|
|||
variables:
|
||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers2"
|
||||
SAST_EXCLUDED_PATHS: "spec, executables"
|
||||
SAST_DEFAULT_ANALYZERS: "bandit, gosec"
|
||||
|
||||
stages:
|
||||
- our_custom_security_stage
|
||||
|
@ -11,3 +12,4 @@ sast:
|
|||
stage: our_custom_security_stage
|
||||
variables:
|
||||
SEARCH_MAX_DEPTH: 8
|
||||
SAST_BRAKEMAN_LEVEL: 2
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Shared examples to that test code that creates AwardEmoji also mark Todos
|
||||
# as done.
|
||||
# Shared examples to test that the code that creates AwardEmoji also marks
|
||||
# ToDos as done.
|
||||
#
|
||||
# The examples expect these to be defined in the calling spec:
|
||||
# - `subject` the callable code that executes the creation of an AwardEmoji
|
||||
# - `user`
|
||||
# - `project`
|
||||
#
|
||||
RSpec.shared_examples 'creating award emojis marks Todos as done' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
Loading…
Reference in New Issue