Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-09-17 00:09:34 +00:00
parent aa33f5d5bb
commit 28811a419e
40 changed files with 847 additions and 250 deletions

View File

@ -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')"
/>

View File

@ -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"
/>

View File

@ -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">

View File

@ -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

View File

@ -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">

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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

View File

@ -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')

View File

@ -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,

View File

@ -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 Pythons '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": []
}
]
}

View File

@ -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

View File

@ -0,0 +1,5 @@
---
title: Replace fa-times with GitLab SVG close icon in dropdowns
merge_request: 40585
author:
type: performance

View File

@ -284,6 +284,7 @@ namespaces
namespacing
namespacings
Nanoc
Netlify
NGINX
Nokogiri
npm

View File

@ -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

View File

@ -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:**

View File

@ -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

View File

@ -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` |

View File

@ -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
}
"""

View File

@ -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": [
],

View File

@ -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

View File

@ -1,4 +1,4 @@
# Sidekiq Metrics API
# Sidekiq Metrics API **(CORE ONLY)**
> Introduced in GitLab 8.9.

View File

@ -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

View File

@ -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:

View File

@ -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)**

View File

@ -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
```

View File

@ -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

View File

@ -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 ""

View File

@ -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 }

View File

@ -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

View File

@ -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);

View File

@ -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=&gt;&quot;Close&quot;}" 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>

View File

@ -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);
});
});
});

View File

@ -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);
});

View File

@ -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();
});
});
});

View File

@ -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

View File

@ -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

View File

@ -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