Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c222aa0890
commit
b9e3013993
|
@ -1 +1 @@
|
|||
36aaf4e475fdcc4ae89f14772662fa89125d7716
|
||||
e302aa4a8caf07caad38c236d610fea49a41aa2f
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 596 B |
|
@ -1,13 +1,5 @@
|
|||
<script>
|
||||
import {
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlModalDirective,
|
||||
GlTooltipDirective,
|
||||
GlDropdownDivider,
|
||||
GlDropdownSectionHeader,
|
||||
} from '@gitlab/ui';
|
||||
import { GlDropdown, GlDropdownItem, GlModalDirective, GlTooltip } from '@gitlab/ui';
|
||||
|
||||
import { INSTALL_AGENT_MODAL_ID, CLUSTERS_ACTIONS } from '../constants';
|
||||
|
||||
|
@ -15,84 +7,91 @@ export default {
|
|||
i18n: CLUSTERS_ACTIONS,
|
||||
INSTALL_AGENT_MODAL_ID,
|
||||
components: {
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlDropdownSectionHeader,
|
||||
GlTooltip,
|
||||
},
|
||||
directives: {
|
||||
GlModalDirective,
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
inject: [
|
||||
'newClusterPath',
|
||||
'addClusterPath',
|
||||
'newClusterDocsPath',
|
||||
'canAddCluster',
|
||||
'displayClusterAgents',
|
||||
'certificateBasedClustersEnabled',
|
||||
],
|
||||
computed: {
|
||||
tooltip() {
|
||||
const { connectWithAgent, connectExistingCluster, dropdownDisabledHint } = this.$options.i18n;
|
||||
|
||||
if (!this.canAddCluster) {
|
||||
return dropdownDisabledHint;
|
||||
} else if (this.displayClusterAgents) {
|
||||
return connectWithAgent;
|
||||
}
|
||||
|
||||
return connectExistingCluster;
|
||||
},
|
||||
shouldTriggerModal() {
|
||||
return this.canAddCluster && this.displayClusterAgents;
|
||||
},
|
||||
defaultActionText() {
|
||||
const { connectCluster, connectWithAgent, connectClusterDeprecated } = this.$options.i18n;
|
||||
|
||||
if (!this.displayClusterAgents) {
|
||||
return connectClusterDeprecated;
|
||||
} else if (!this.certificateBasedClustersEnabled) {
|
||||
return connectCluster;
|
||||
}
|
||||
return connectWithAgent;
|
||||
},
|
||||
defaultActionUrl() {
|
||||
if (this.displayClusterAgents) {
|
||||
return null;
|
||||
}
|
||||
return this.addClusterPath;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="nav-controls gl-ml-auto">
|
||||
<gl-tooltip
|
||||
v-if="!canAddCluster"
|
||||
:target="() => $refs.dropdown.$el"
|
||||
:title="$options.i18n.dropdownDisabledHint"
|
||||
/>
|
||||
|
||||
<gl-dropdown
|
||||
v-if="certificateBasedClustersEnabled"
|
||||
ref="dropdown"
|
||||
v-gl-modal-directive="shouldTriggerModal && $options.INSTALL_AGENT_MODAL_ID"
|
||||
v-gl-tooltip="tooltip"
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
:text="$options.i18n.actionsButton"
|
||||
:text="defaultActionText"
|
||||
:disabled="!canAddCluster"
|
||||
:split="displayClusterAgents"
|
||||
:split-href="defaultActionUrl"
|
||||
split
|
||||
right
|
||||
>
|
||||
<template v-if="displayClusterAgents">
|
||||
<gl-dropdown-section-header>{{ $options.i18n.agent }}</gl-dropdown-section-header>
|
||||
<gl-dropdown-item
|
||||
v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID"
|
||||
data-testid="connect-new-agent-link"
|
||||
>
|
||||
{{ $options.i18n.connectWithAgent }}
|
||||
<gl-dropdown-item
|
||||
v-if="displayClusterAgents"
|
||||
:href="newClusterDocsPath"
|
||||
data-testid="create-cluster-link"
|
||||
@click.stop
|
||||
>
|
||||
{{ $options.i18n.createCluster }}
|
||||
</gl-dropdown-item>
|
||||
|
||||
<template v-if="displayClusterAgents && certificateBasedClustersEnabled">
|
||||
<gl-dropdown-item :href="newClusterPath" data-testid="new-cluster-link" @click.stop>
|
||||
{{ $options.i18n.createClusterCertificate }}
|
||||
</gl-dropdown-item>
|
||||
|
||||
<gl-dropdown-item :href="addClusterPath" data-testid="connect-cluster-link" @click.stop>
|
||||
{{ $options.i18n.connectClusterCertificate }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-section-header>{{ $options.i18n.certificate }}</gl-dropdown-section-header>
|
||||
</template>
|
||||
|
||||
<gl-dropdown-item :href="newClusterPath" data-testid="new-cluster-link" @click.stop>
|
||||
{{ $options.i18n.createNewCluster }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item :href="addClusterPath" data-testid="connect-cluster-link" @click.stop>
|
||||
{{ $options.i18n.connectExistingCluster }}
|
||||
<gl-dropdown-item
|
||||
v-if="certificateBasedClustersEnabled && !displayClusterAgents"
|
||||
:href="newClusterPath"
|
||||
data-testid="new-cluster-link"
|
||||
@click.stop
|
||||
>
|
||||
{{ $options.i18n.createClusterDeprecated }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
<gl-button
|
||||
v-else
|
||||
v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID"
|
||||
v-gl-tooltip="tooltip"
|
||||
:disabled="!canAddCluster"
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
>
|
||||
{{ $options.i18n.connectWithAgent }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -252,12 +252,13 @@ export const CERTIFICATE_TAB = {
|
|||
export const CLUSTERS_TABS = [ALL_TAB, AGENT_TAB, CERTIFICATE_TAB];
|
||||
|
||||
export const CLUSTERS_ACTIONS = {
|
||||
actionsButton: s__('ClusterAgents|Actions'),
|
||||
createNewCluster: s__('ClusterAgents|Create a new cluster'),
|
||||
connectWithAgent: s__('ClusterAgents|Connect with an agent'),
|
||||
connectExistingCluster: s__('ClusterAgents|Connect with a certificate'),
|
||||
agent: s__('ClusterAgents|Agent'),
|
||||
certificate: s__('ClusterAgents|Certificate'),
|
||||
connectCluster: s__('ClusterAgents|Connect a cluster'),
|
||||
connectWithAgent: s__('ClusterAgents|Connect a cluster (agent)'),
|
||||
connectClusterDeprecated: s__('ClusterAgents|Connect a cluster (deprecated)'),
|
||||
createClusterDeprecated: s__('ClusterAgents|Create a cluster (deprecated)'),
|
||||
createCluster: s__('ClusterAgents|Create a cluster'),
|
||||
createClusterCertificate: s__('ClusterAgents|Create a cluster (certificate - deprecated)'),
|
||||
connectClusterCertificate: s__('ClusterAgents|Connect a cluster (certificate - deprecated)'),
|
||||
dropdownDisabledHint: s__(
|
||||
'ClusterAgents|Requires a Maintainer or greater role to perform these actions',
|
||||
),
|
||||
|
|
|
@ -25,6 +25,7 @@ export default () => {
|
|||
kasAddress,
|
||||
newClusterPath,
|
||||
addClusterPath,
|
||||
newClusterDocsPath,
|
||||
emptyStateHelpText,
|
||||
clustersEmptyStateImage,
|
||||
canAddCluster,
|
||||
|
@ -43,6 +44,7 @@ export default () => {
|
|||
kasAddress,
|
||||
newClusterPath,
|
||||
addClusterPath,
|
||||
newClusterDocsPath,
|
||||
emptyStateHelpText,
|
||||
clustersEmptyStateImage,
|
||||
canAddCluster: parseBoolean(canAddCluster),
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
|
||||
import CodeBlockHighlight from './code_block_highlight';
|
||||
|
||||
export default CodeBlockHighlight.extend({
|
||||
name: 'diagram',
|
||||
|
||||
isolating: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
language: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
return element.dataset.diagram;
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
priority: PARSE_HTML_PRIORITY_HIGHEST,
|
||||
tag: '[data-diagram]',
|
||||
getContent(element, schema) {
|
||||
const source = atob(element.dataset.diagramSrc.replace('data:text/plain;base64,', ''));
|
||||
const node = schema.node('paragraph', {}, [schema.text(source)]);
|
||||
return node.content;
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes: { language, ...HTMLAttributes } }) {
|
||||
return [
|
||||
'div',
|
||||
[
|
||||
'pre',
|
||||
{
|
||||
language,
|
||||
class: `content-editor-code-block code highlight`,
|
||||
...HTMLAttributes,
|
||||
},
|
||||
['code', {}, 0],
|
||||
],
|
||||
];
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {};
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [];
|
||||
},
|
||||
});
|
|
@ -15,6 +15,7 @@ import DescriptionItem from '../extensions/description_item';
|
|||
import DescriptionList from '../extensions/description_list';
|
||||
import Details from '../extensions/details';
|
||||
import DetailsContent from '../extensions/details_content';
|
||||
import Diagram from '../extensions/diagram';
|
||||
import Division from '../extensions/division';
|
||||
import Document from '../extensions/document';
|
||||
import Dropcursor from '../extensions/dropcursor';
|
||||
|
@ -100,6 +101,7 @@ export const createContentEditor = ({
|
|||
Details,
|
||||
DetailsContent,
|
||||
Document,
|
||||
Diagram,
|
||||
Division,
|
||||
Dropcursor,
|
||||
Emoji,
|
||||
|
|
|
@ -13,6 +13,7 @@ import DescriptionList from '../extensions/description_list';
|
|||
import Details from '../extensions/details';
|
||||
import DetailsContent from '../extensions/details_content';
|
||||
import Division from '../extensions/division';
|
||||
import Diagram from '../extensions/diagram';
|
||||
import Emoji from '../extensions/emoji';
|
||||
import Figure from '../extensions/figure';
|
||||
import FigureCaption from '../extensions/figure_caption';
|
||||
|
@ -48,6 +49,7 @@ import Video from '../extensions/video';
|
|||
import WordBreak from '../extensions/word_break';
|
||||
import {
|
||||
isPlainURL,
|
||||
renderCodeBlock,
|
||||
renderHardBreak,
|
||||
renderTable,
|
||||
renderTableCell,
|
||||
|
@ -130,13 +132,8 @@ const defaultSerializerConfig = {
|
|||
}
|
||||
},
|
||||
[BulletList.name]: defaultMarkdownSerializer.nodes.bullet_list,
|
||||
[CodeBlockHighlight.name]: (state, node) => {
|
||||
state.write(`\`\`\`${node.attrs.language || ''}\n`);
|
||||
state.text(node.textContent, false);
|
||||
state.ensureNewLine();
|
||||
state.write('```');
|
||||
state.closeBlock(node);
|
||||
},
|
||||
[CodeBlockHighlight.name]: renderCodeBlock,
|
||||
[Diagram.name]: renderCodeBlock,
|
||||
[Division.name]: (state, node) => {
|
||||
if (node.attrs.className?.includes('js-markdown-code')) {
|
||||
state.renderInline(node);
|
||||
|
|
|
@ -341,3 +341,11 @@ export function renderImage(state, node) {
|
|||
export function renderPlayable(state, node) {
|
||||
renderImage(state, node);
|
||||
}
|
||||
|
||||
export function renderCodeBlock(state, node) {
|
||||
state.write(`\`\`\`${node.attrs.language || ''}\n`);
|
||||
state.text(node.textContent, false);
|
||||
state.ensureNewLine();
|
||||
state.write('```');
|
||||
state.closeBlock(node);
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ function deferredInitialisation() {
|
|||
// In case the user started searching before we bootstrapped, let's pass the search along.
|
||||
const initialSearchValue = searchInputBox.value;
|
||||
await initHeaderSearchApp(initialSearchValue);
|
||||
searchInputBox.focus();
|
||||
document.querySelector('#search').focus();
|
||||
})
|
||||
.catch(() => {});
|
||||
} else {
|
||||
|
|
|
@ -192,10 +192,12 @@ export default {
|
|||
@keydown.enter.prevent="onSearchBoxEnter"
|
||||
/>
|
||||
|
||||
<gl-dropdown-item @click="selectNoMilestone()">
|
||||
<span :class="{ 'gl-pl-6': true, 'selected-item': selectedMilestones.length === 0 }">
|
||||
{{ $options.translations.noMilestone }}
|
||||
</span>
|
||||
<gl-dropdown-item
|
||||
:is-checked="selectedMilestones.length === 0"
|
||||
is-check-item
|
||||
@click="selectNoMilestone()"
|
||||
>
|
||||
{{ $options.translations.noMilestone }}
|
||||
</gl-dropdown-item>
|
||||
|
||||
<gl-dropdown-divider />
|
||||
|
@ -241,9 +243,10 @@ export default {
|
|||
v-for="(item, idx) in extraLinks"
|
||||
:key="idx"
|
||||
:href="item.url"
|
||||
:is-check-item="true"
|
||||
data-testid="milestone-combobox-extra-links"
|
||||
>
|
||||
<span class="gl-pl-6">{{ item.text }}</span>
|
||||
{{ item.text }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
@ -77,10 +77,14 @@ export default {
|
|||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<gl-dropdown-item v-for="{ title } in items" :key="title" @click="$emit('selected', title)">
|
||||
<span class="gl-pl-6" :class="{ 'selected-item': isSelectedMilestone(title) }">
|
||||
{{ title }}
|
||||
</span>
|
||||
<gl-dropdown-item
|
||||
v-for="{ title } in items"
|
||||
:key="title"
|
||||
:is-checked="isSelectedMilestone(title)"
|
||||
is-check-item
|
||||
@click="$emit('selected', title)"
|
||||
>
|
||||
{{ title }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-divider />
|
||||
</template>
|
||||
|
|
|
@ -1,13 +1,4 @@
|
|||
.milestone-combobox {
|
||||
.selected-item {
|
||||
/* stylelint-disable-next-line function-url-quotes */
|
||||
background: url(asset_path('checkmark.png')) no-repeat 0 2px;
|
||||
}
|
||||
|
||||
.dropdown-item-space {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.dropdown-menu.show {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class Clusters::ClustersController < Clusters::BaseController
|
|||
before_action :authorize_create_cluster!, only: [:new, :connect, :authorize_aws_role]
|
||||
before_action :authorize_update_cluster!, only: [:update]
|
||||
before_action :update_applications_status, only: [:cluster_status]
|
||||
before_action :ensure_feature_enabled!, except: :index
|
||||
before_action :ensure_feature_enabled!, except: [:index, :new_cluster_docs]
|
||||
|
||||
helper_method :token_in_session
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ module ClustersHelper
|
|||
empty_state_help_text: clusterable.empty_state_help_text,
|
||||
new_cluster_path: clusterable.new_path,
|
||||
add_cluster_path: clusterable.connect_path,
|
||||
new_cluster_docs_path: clusterable.new_cluster_docs_path,
|
||||
can_add_cluster: clusterable.can_add_cluster?.to_s,
|
||||
can_admin_cluster: clusterable.can_admin_cluster?.to_s,
|
||||
display_cluster_agents: display_cluster_agents?(clusterable).to_s,
|
||||
|
|
|
@ -36,6 +36,10 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
|
|||
polymorphic_path([clusterable, :clusters], action: :connect)
|
||||
end
|
||||
|
||||
def new_cluster_docs_path
|
||||
polymorphic_path([clusterable, :clusters], action: :new_cluster_docs)
|
||||
end
|
||||
|
||||
def authorize_aws_role_path
|
||||
polymorphic_path([clusterable, :clusters], action: :authorize_aws_role)
|
||||
end
|
||||
|
|
|
@ -43,6 +43,11 @@ class InstanceClusterablePresenter < ClusterablePresenter
|
|||
connect_admin_clusters_path
|
||||
end
|
||||
|
||||
override :new_cluster_docs_path
|
||||
def new_cluster_docs_path
|
||||
nil
|
||||
end
|
||||
|
||||
override :create_user_clusters_path
|
||||
def create_user_clusters_path
|
||||
create_user_admin_clusters_path
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
- is_connect_page = local_assigns.fetch(:is_connect_page, false)
|
||||
- docs_mode = local_assigns.fetch(:docs_mode, false)
|
||||
- title = is_connect_page ? s_('ClusterIntegration|Connect a Kubernetes cluster') : s_('ClusterIntegration|Create a Kubernetes cluster')
|
||||
|
||||
%h3
|
||||
= s_('ClusterIntegration|Connect a Kubernetes cluster')
|
||||
= title
|
||||
%p
|
||||
= clusterable.sidebar_text
|
||||
%p
|
||||
= clusterable.learn_more_link
|
||||
|
||||
= render 'clusters/clusters/multiple_clusters_message'
|
||||
- if !docs_mode
|
||||
%p
|
||||
= clusterable.learn_more_link
|
||||
|
||||
= render 'clusters/clusters/multiple_clusters_message'
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
- provider = local_assigns.fetch(:provider)
|
||||
- is_current_provider = provider == params[:provider]
|
||||
- logo_path = local_assigns.fetch(:logo_path)
|
||||
- help_path = local_assigns.fetch(:help_path)
|
||||
- label = local_assigns.fetch(:label)
|
||||
- last = local_assigns.fetch(:last, false)
|
||||
- classes = ["btn btn-confirm gl-button btn-confirm-secondary gl-flex-direction-column gl-w-half js-create-#{provider}-cluster-button"]
|
||||
- conditional_classes = [('gl-mr-5' unless last), ('active' if is_current_provider)]
|
||||
- docs_mode = local_assigns.fetch(:docs_mode, false)
|
||||
- classes = ["btn btn-confirm gl-button btn-confirm-secondary gl-flex-direction-column gl-w-half"]
|
||||
- conditional_classes = [("gl-mr-5" unless last), ("active" if is_current_provider && !docs_mode), ("js-create-#{provider}-cluster-button" if !docs_mode)]
|
||||
- link = docs_mode ? help_path : clusterable.new_path(provider: provider)
|
||||
|
||||
= link_to clusterable.new_path(provider: provider), class: classes + conditional_classes do
|
||||
.svg-content.gl-p-3= image_tag logo_path, alt: label, class: 'gl-w-64 gl-h-64'
|
||||
= link_to link, class: classes + conditional_classes do
|
||||
.svg-content.gl-p-3= image_tag logo_path, alt: label, class: "gl-w-64 gl-h-64"
|
||||
%span
|
||||
= label
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
- gke_label = s_('ClusterIntegration|Google GKE')
|
||||
- eks_label = s_('ClusterIntegration|Amazon EKS')
|
||||
- create_cluster_label = s_('ClusterIntegration|Where do you want to create a cluster?')
|
||||
- eks_help_path = help_page_path('user/infrastructure/clusters/connect/new_eks_cluster')
|
||||
- gke_help_path = help_page_path('user/infrastructure/clusters/connect/new_gke_cluster')
|
||||
- docs_mode = local_assigns.fetch(:docs_mode, false)
|
||||
|
||||
.gl-p-5
|
||||
%h4.gl-mb-5
|
||||
= create_cluster_label
|
||||
.gl-display-flex
|
||||
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
|
||||
locals: { provider: 'aws', label: eks_label, logo_path: 'illustrations/logos/amazon_eks.svg' }
|
||||
locals: { provider: 'aws', label: eks_label, logo_path: 'illustrations/logos/amazon_eks.svg', help_path: eks_help_path, docs_mode: docs_mode }
|
||||
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
|
||||
locals: { provider: 'gcp', label: gke_label, logo_path: 'illustrations/logos/google_gke.svg', last: true }
|
||||
locals: { provider: 'gcp', label: gke_label, logo_path: 'illustrations/logos/google_gke.svg', help_path: gke_help_path, docs_mode: docs_mode, last: true }
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
- breadcrumb_title _('Connect a cluster')
|
||||
- page_title _('Connect a Kubernetes Cluster')
|
||||
|
||||
.row.gl-mt-3
|
||||
.col-md-3
|
||||
= render 'sidebar'
|
||||
.col-md-9
|
||||
.gl-md-display-flex.gl-mt-3
|
||||
.gl-w-quarter.gl-xs-w-full.gl-flex-shrink-0.gl-md-mr-5
|
||||
= render 'sidebar', is_connect_page: true
|
||||
.gl-w-full
|
||||
#js-cluster-new{ data: js_cluster_new }
|
||||
= render 'clusters/clusters/user/form'
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
= render_gcp_signup_offer
|
||||
|
||||
.row.gl-mt-3
|
||||
.col-md-3
|
||||
= render 'sidebar'
|
||||
.col-md-9
|
||||
.gl-md-display-flex.gl-mt-3
|
||||
.gl-w-quarter.gl-xs-w-full.gl-flex-shrink-0.gl-md-mr-5
|
||||
= render 'sidebar', is_connect_page: false
|
||||
.gl-w-full
|
||||
= render 'clusters/clusters/cloud_providers/cloud_provider_selector'
|
||||
|
||||
- if ['aws', 'gcp'].include?(provider)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
- @content_class = 'limit-container-width' unless fluid_layout
|
||||
- add_to_breadcrumbs _('Kubernetes Clusters'), clusterable.index_path
|
||||
- breadcrumb_title _('Create a cluster')
|
||||
- page_title _('Create a Kubernetes cluster')
|
||||
- docs_mode = true
|
||||
|
||||
= render_gcp_signup_offer
|
||||
|
||||
.gl-md-display-flex.gl-mt-3
|
||||
.gl-w-quarter.gl-xs-w-full.gl-flex-shrink-0.gl-md-mr-5
|
||||
= render 'sidebar', docs_mode: docs_mode, is_connect_page: false
|
||||
.gl-w-full
|
||||
= render 'clusters/clusters/cloud_providers/cloud_provider_selector', docs_mode: docs_mode
|
|
@ -40,7 +40,7 @@
|
|||
- search_menu_item = top_nav_search_menu_item_attrs
|
||||
%li.nav-item.header-search-new.d-none.d-lg-block.m-auto
|
||||
- unless current_controller?(:search)
|
||||
- if Feature.enabled?(:new_header_search)
|
||||
- if Feature.enabled?(:new_header_search, default_enabled: :yaml)
|
||||
= render 'layouts/header_search'
|
||||
- else
|
||||
= render 'layouts/search'
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339348
|
|||
milestone: '14.3'
|
||||
type: development
|
||||
group: group::global search
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -237,6 +237,7 @@ Rails.application.routes.draw do
|
|||
resources :clusters, only: [:index, :new, :show, :update, :destroy] do
|
||||
collection do
|
||||
get :connect
|
||||
get :new_cluster_docs
|
||||
post :create_user
|
||||
post :create_gcp
|
||||
post :create_aws
|
||||
|
|
|
@ -39,6 +39,9 @@ module Banzai
|
|||
allowlist[:attributes][:all].delete('name')
|
||||
allowlist[:attributes]['a'].push('name')
|
||||
|
||||
allowlist[:attributes]['img'].push('data-diagram')
|
||||
allowlist[:attributes]['img'].push('data-diagram-src')
|
||||
|
||||
# Allow any protocol in `a` elements
|
||||
# and then remove links with unsafe protocols
|
||||
allowlist[:protocols].delete('a')
|
||||
|
|
|
@ -27,6 +27,13 @@ module Banzai
|
|||
# make sure the original non-proxied src carries over to the link
|
||||
link['data-canonical-src'] = img['data-canonical-src'] if img['data-canonical-src']
|
||||
|
||||
if img['data-diagram'] && img['data-diagram-src']
|
||||
link['data-diagram'] = img['data-diagram']
|
||||
link['data-diagram-src'] = img['data-diagram-src']
|
||||
img.remove_attribute('data-diagram')
|
||||
img.remove_attribute('data-diagram-src')
|
||||
end
|
||||
|
||||
link.children = if link_replaces_image
|
||||
img['alt'] || img['data-src'] || img['src']
|
||||
else
|
||||
|
|
|
@ -22,7 +22,14 @@ module Banzai
|
|||
doc.xpath(xpath).each do |node|
|
||||
diagram_type = node.parent['lang']
|
||||
img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{create_image_src(diagram_type, diagram_format, node.content)}"/>))
|
||||
node.parent.replace(img_tag)
|
||||
img_tag = img_tag.children.first
|
||||
|
||||
unless img_tag.nil?
|
||||
img_tag.set_attribute('data-diagram', node.parent['lang'])
|
||||
img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
|
||||
|
||||
node.parent.replace(img_tag)
|
||||
end
|
||||
end
|
||||
|
||||
doc
|
||||
|
|
|
@ -15,8 +15,14 @@ module Banzai
|
|||
|
||||
doc.xpath(lang_tag).each do |node|
|
||||
img_tag = Nokogiri::HTML::DocumentFragment.parse(
|
||||
Asciidoctor::PlantUml::Processor.plantuml_content(node.content, {}))
|
||||
node.parent.replace(img_tag)
|
||||
Asciidoctor::PlantUml::Processor.plantuml_content(node.content, {})).css('img').first
|
||||
|
||||
unless img_tag.nil?
|
||||
img_tag.set_attribute('data-diagram', 'plantuml')
|
||||
img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
|
||||
|
||||
node.parent.replace(img_tag)
|
||||
end
|
||||
end
|
||||
|
||||
doc
|
||||
|
|
|
@ -12,6 +12,7 @@ module Banzai
|
|||
def self.filters
|
||||
@filters ||= FilterArray[
|
||||
Filter::PlantumlFilter,
|
||||
Filter::KrokiFilter,
|
||||
# Must always be before the SanitizationFilter to prevent XSS attacks
|
||||
Filter::SpacedLinkFilter,
|
||||
Filter::SanitizationFilter,
|
||||
|
@ -19,7 +20,6 @@ module Banzai
|
|||
Filter::SyntaxHighlightFilter,
|
||||
Filter::MathFilter,
|
||||
Filter::ColorFilter,
|
||||
Filter::KrokiFilter,
|
||||
Filter::MermaidFilter,
|
||||
Filter::VideoLinkFilter,
|
||||
Filter::AudioLinkFilter,
|
||||
|
|
|
@ -7822,9 +7822,6 @@ msgstr ""
|
|||
msgid "ClusterAgents|Access tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Actions"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Add an agent configuration file to %{linkStart}this repository%{linkEnd} and select it, or create a new one to register with GitLab:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7879,18 +7876,24 @@ msgstr ""
|
|||
msgid "ClusterAgents|Configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect a cluster"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect a cluster (agent)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect a cluster (certificate - deprecated)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect a cluster (deprecated)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect a cluster through an agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect existing cluster"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect with a certificate"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect with an agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Connect with the GitLab Agent"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7906,7 +7909,13 @@ msgstr ""
|
|||
msgid "ClusterAgents|Copy token"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Create a new cluster"
|
||||
msgid "ClusterAgents|Create a cluster"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Create a cluster (certificate - deprecated)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Create a cluster (deprecated)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Create agent access token"
|
||||
|
@ -8289,6 +8298,9 @@ msgstr ""
|
|||
msgid "ClusterIntegration|Create Kubernetes cluster"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Create a Kubernetes cluster"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterIntegration|Creating Kubernetes cluster"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ const createMainOutput = ({ outFile, cssKeys, type }) => ({
|
|||
htmlPaths: [
|
||||
path.join(FIXTURES_ROOT, `startup_css/project-${type}.html`),
|
||||
path.join(FIXTURES_ROOT, `startup_css/project-${type}-signed-out.html`),
|
||||
path.join(FIXTURES_ROOT, `startup_css/project-${type}-search-ff-on.html`),
|
||||
path.join(FIXTURES_ROOT, `startup_css/project-${type}-search-ff-off.html`),
|
||||
],
|
||||
cssKeys,
|
||||
purgeOptions: {
|
||||
|
|
|
@ -15,8 +15,8 @@ RSpec.describe 'Instance-level AWS EKS Cluster', :js do
|
|||
before do
|
||||
visit admin_clusters_path
|
||||
|
||||
click_button 'Actions'
|
||||
click_link 'Create a new cluster'
|
||||
click_button(class: 'dropdown-toggle-split')
|
||||
click_link 'Create a cluster (deprecated)'
|
||||
end
|
||||
|
||||
context 'when user creates a cluster on AWS EKS' do
|
||||
|
|
|
@ -25,7 +25,7 @@ RSpec.describe 'Cluster agent registration', :js do
|
|||
end
|
||||
|
||||
it 'allows the user to select an agent to install, and displays the resulting agent token' do
|
||||
click_button('Actions')
|
||||
click_button('Connect a cluster')
|
||||
expect(page).to have_content('Register')
|
||||
|
||||
click_button('Select an agent')
|
||||
|
|
|
@ -30,7 +30,7 @@ RSpec.describe 'When a user searches for Sentry errors', :js, :use_clean_rails_m
|
|||
expect(results.count).to be(3)
|
||||
end
|
||||
|
||||
find('.gl-form-input').set('NotFound').native.send_keys(:return)
|
||||
find('.filtered-search-input-container .gl-form-input').set('NotFound').native.send_keys(:return)
|
||||
|
||||
page.within(find('.gl-table')) do
|
||||
results = page.all('.table-row')
|
||||
|
|
|
@ -20,8 +20,8 @@ RSpec.describe 'Group AWS EKS Cluster', :js do
|
|||
before do
|
||||
visit group_clusters_path(group)
|
||||
|
||||
click_button 'Actions'
|
||||
click_link 'Create a new cluster'
|
||||
click_button(class: 'dropdown-toggle-split')
|
||||
click_link 'Create a cluster (deprecated)'
|
||||
end
|
||||
|
||||
context 'when user creates a cluster on AWS EKS' do
|
||||
|
|
|
@ -20,7 +20,7 @@ RSpec.describe 'AWS EKS Cluster', :js do
|
|||
visit project_clusters_path(project)
|
||||
|
||||
click_button(class: 'dropdown-toggle-split')
|
||||
click_link 'Create a new cluster'
|
||||
click_link 'Create a cluster (certificate - deprecated)'
|
||||
end
|
||||
|
||||
context 'when user creates a cluster on AWS EKS' do
|
||||
|
|
|
@ -135,7 +135,7 @@ RSpec.describe 'Gcp Cluster', :js do
|
|||
visit project_clusters_path(project)
|
||||
|
||||
click_button(class: 'dropdown-toggle-split')
|
||||
click_link 'Connect with a certificate'
|
||||
click_link 'Connect a cluster (certificate - deprecated)'
|
||||
end
|
||||
|
||||
it 'user sees the "Environment scope" field' do
|
||||
|
@ -220,6 +220,6 @@ RSpec.describe 'Gcp Cluster', :js do
|
|||
|
||||
def visit_create_cluster_page
|
||||
click_button(class: 'dropdown-toggle-split')
|
||||
click_link 'Create a new cluster'
|
||||
click_link 'Create a cluster (certificate - deprecated)'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -222,11 +222,11 @@ RSpec.describe 'Clusters', :js do
|
|||
visit project_clusters_path(project)
|
||||
|
||||
click_button(class: 'dropdown-toggle-split')
|
||||
click_link 'Create a new cluster'
|
||||
click_link 'Create a cluster (certificate - deprecated)'
|
||||
end
|
||||
|
||||
def visit_connect_cluster_page
|
||||
click_button(class: 'dropdown-toggle-split')
|
||||
click_link 'Connect with a certificate'
|
||||
click_link 'Connect a cluster (certificate - deprecated)'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,8 @@ RSpec.describe 'User searches for projects', :js do
|
|||
context 'when signed out' do
|
||||
context 'when block_anonymous_global_searches is disabled' do
|
||||
before do
|
||||
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000)
|
||||
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000)
|
||||
stub_feature_flags(block_anonymous_global_searches: false)
|
||||
end
|
||||
|
||||
|
|
|
@ -17,12 +17,15 @@ RSpec.describe 'User uses header search field', :js do
|
|||
end
|
||||
|
||||
before do
|
||||
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000)
|
||||
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
shared_examples 'search field examples' do
|
||||
before do
|
||||
visit(url)
|
||||
wait_for_all_requests
|
||||
end
|
||||
|
||||
it 'starts searching by pressing the enter key' do
|
||||
|
@ -37,7 +40,6 @@ RSpec.describe 'User uses header search field', :js do
|
|||
before do
|
||||
find('#search')
|
||||
find('body').native.send_keys('s')
|
||||
|
||||
wait_for_all_requests
|
||||
end
|
||||
|
||||
|
@ -49,6 +51,7 @@ RSpec.describe 'User uses header search field', :js do
|
|||
context 'when clicking the search field' do
|
||||
before do
|
||||
page.find('#search').click
|
||||
wait_for_all_requests
|
||||
end
|
||||
|
||||
it 'shows category search dropdown', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/250285' do
|
||||
|
@ -59,7 +62,7 @@ RSpec.describe 'User uses header search field', :js do
|
|||
let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
|
||||
|
||||
it 'shows assigned issues' do
|
||||
find('.search-input-container .dropdown-menu').click_link('Issues assigned to me')
|
||||
find('[data-testid="header-search-dropdown-menu"]').click_link('Issues assigned to me')
|
||||
|
||||
expect(page).to have_selector('.issues-list .issue')
|
||||
expect_tokens([assignee_token(user.name)])
|
||||
|
@ -67,7 +70,7 @@ RSpec.describe 'User uses header search field', :js do
|
|||
end
|
||||
|
||||
it 'shows created issues' do
|
||||
find('.search-input-container .dropdown-menu').click_link("Issues I've created")
|
||||
find('[data-testid="header-search-dropdown-menu"]').click_link("Issues I've created")
|
||||
|
||||
expect(page).to have_selector('.issues-list .issue')
|
||||
expect_tokens([author_token(user.name)])
|
||||
|
@ -79,7 +82,7 @@ RSpec.describe 'User uses header search field', :js do
|
|||
let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignees: [user]) }
|
||||
|
||||
it 'shows assigned merge requests' do
|
||||
find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me')
|
||||
find('[data-testid="header-search-dropdown-menu"]').click_link('Merge requests assigned to me')
|
||||
|
||||
expect(page).to have_selector('.mr-list .merge-request')
|
||||
expect_tokens([assignee_token(user.name)])
|
||||
|
@ -87,7 +90,7 @@ RSpec.describe 'User uses header search field', :js do
|
|||
end
|
||||
|
||||
it 'shows created merge requests' do
|
||||
find('.search-input-container .dropdown-menu').click_link("Merge requests I've created")
|
||||
find('[data-testid="header-search-dropdown-menu"]').click_link("Merge requests I've created")
|
||||
|
||||
expect(page).to have_selector('.mr-list .merge-request')
|
||||
expect_tokens([author_token(user.name)])
|
||||
|
@ -150,10 +153,9 @@ RSpec.describe 'User uses header search field', :js do
|
|||
|
||||
it 'displays search options' do
|
||||
fill_in_search('test')
|
||||
|
||||
expect(page).to have_selector(scoped_search_link('test'))
|
||||
expect(page).to have_selector(scoped_search_link('test', group_id: group.id))
|
||||
expect(page).to have_selector(scoped_search_link('test', project_id: project.id, group_id: group.id))
|
||||
expect(page).to have_selector(scoped_search_link('test', search_code: true))
|
||||
expect(page).to have_selector(scoped_search_link('test', group_id: group.id, search_code: true))
|
||||
expect(page).to have_selector(scoped_search_link('test', project_id: project.id, group_id: group.id, search_code: true))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -165,10 +167,9 @@ RSpec.describe 'User uses header search field', :js do
|
|||
|
||||
it 'displays search options' do
|
||||
fill_in_search('test')
|
||||
|
||||
expect(page).to have_selector(scoped_search_link('test'))
|
||||
expect(page).not_to have_selector(scoped_search_link('test', group_id: project.namespace_id))
|
||||
expect(page).to have_selector(scoped_search_link('test', project_id: project.id))
|
||||
expect(page).to have_selector(scoped_search_link('test', search_code: true, repository_ref: 'master'))
|
||||
expect(page).not_to have_selector(scoped_search_link('test', search_code: true, group_id: project.namespace_id, repository_ref: 'master'))
|
||||
expect(page).to have_selector(scoped_search_link('test', search_code: true, project_id: project.id, repository_ref: 'master'))
|
||||
end
|
||||
|
||||
it 'displays a link to project merge requests' do
|
||||
|
@ -217,7 +218,6 @@ RSpec.describe 'User uses header search field', :js do
|
|||
|
||||
it 'displays search options' do
|
||||
fill_in_search('test')
|
||||
|
||||
expect(page).to have_selector(scoped_search_link('test'))
|
||||
expect(page).to have_selector(scoped_search_link('test', group_id: group.id))
|
||||
expect(page).not_to have_selector(scoped_search_link('test', project_id: project.id))
|
||||
|
@ -248,18 +248,20 @@ RSpec.describe 'User uses header search field', :js do
|
|||
end
|
||||
end
|
||||
|
||||
def scoped_search_link(term, project_id: nil, group_id: nil)
|
||||
def scoped_search_link(term, project_id: nil, group_id: nil, search_code: nil, repository_ref: nil)
|
||||
# search_path will accept group_id and project_id but the order does not match
|
||||
# what is expected in the href, so the variable must be built manually
|
||||
href = search_path(search: term)
|
||||
href.concat("&nav_source=navbar")
|
||||
href.concat("&project_id=#{project_id}") if project_id
|
||||
href.concat("&group_id=#{group_id}") if group_id
|
||||
href.concat("&nav_source=navbar")
|
||||
href.concat("&search_code=true") if search_code
|
||||
href.concat("&repository_ref=#{repository_ref}") if repository_ref
|
||||
|
||||
".dropdown a[href='#{href}']"
|
||||
"[data-testid='header-search-dropdown-menu'] a[href='#{href}']"
|
||||
end
|
||||
|
||||
def dashboard_search_options_popup_menu
|
||||
"div[data-testid='dashboard-search-options']"
|
||||
"[data-testid='header-search-dropdown-menu'] .header-search-dropdown-content"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -377,6 +377,34 @@
|
|||
</ol>
|
||||
</details>
|
||||
|
||||
- name: diagram_kroki_nomnoml
|
||||
markdown: |-
|
||||
```nomnoml
|
||||
#stroke: #a86128
|
||||
[<frame>Decorator pattern|
|
||||
[<abstract>Component||+ operation()]
|
||||
[Client] depends --> [Component]
|
||||
[Decorator|- next: Component]
|
||||
[Decorator] decorates -- [ConcreteComponent]
|
||||
[Component] <:- [Decorator]
|
||||
[Component] <:- [ConcreteComponent]
|
||||
]
|
||||
```
|
||||
html: |-
|
||||
<a class="no-attachment-icon" href="http://localhost:8000/nomnoml/svg/eNp1jbsOwjAMRfd-haUuIJQBBlRFVZb2L1CGkBqpgtpR6oEhH0_CW6hsts-9xwD1LJHPqKF2zX67ayqAQ3uKbkLTo-fohCMEJ4KRUoYFu2MuOS-m4ykwIUlKG-CAOT0yrdb2EewuY2YWBgxIwwxKmXx8dZ6h95ekgPAqGv4miuk-YnEVFfmIgr-Fzw6tVt-CZb7osdUNUAReJA==" target="_blank" rel="noopener noreferrer" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,ICAjc3Ryb2tlOiAjYTg2MTI4CiAgWzxmcmFtZT5EZWNvcmF0b3IgcGF0dGVybnwKICAgIFs8YWJzdHJhY3Q+Q29tcG9uZW50fHwrIG9wZXJhdGlvbigpXQogICAgW0NsaWVudF0gZGVwZW5kcyAtLT4gW0NvbXBvbmVudF0KICAgIFtEZWNvcmF0b3J8LSBuZXh0OiBDb21wb25lbnRdCiAgICBbRGVjb3JhdG9yXSBkZWNvcmF0ZXMgLS0gW0NvbmNyZXRlQ29tcG9uZW50XQogICAgW0NvbXBvbmVudF0gPDotIFtEZWNvcmF0b3JdCiAgICBbQ29tcG9uZW50XSA8Oi0gW0NvbmNyZXRlQ29tcG9uZW50XQogIF0K"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" class="lazy" data-src="http://localhost:8000/nomnoml/svg/eNp1jbsOwjAMRfd-haUuIJQBBlRFVZb2L1CGkBqpgtpR6oEhH0_CW6hsts-9xwD1LJHPqKF2zX67ayqAQ3uKbkLTo-fohCMEJ4KRUoYFu2MuOS-m4ykwIUlKG-CAOT0yrdb2EewuY2YWBgxIwwxKmXx8dZ6h95ekgPAqGv4miuk-YnEVFfmIgr-Fzw6tVt-CZb7osdUNUAReJA=="></a>
|
||||
|
||||
- name: diagram_plantuml
|
||||
markdown: |-
|
||||
```plantuml
|
||||
Alice -> Bob: Authentication Request
|
||||
Bob --> Alice: Authentication Response
|
||||
|
||||
Alice -> Bob: Another authentication Request
|
||||
Alice <-- Bob: Another authentication Response
|
||||
```
|
||||
html: |-
|
||||
<a class="no-attachment-icon" href="http://localhost:8080/png/U9nJK73CoKnELT2rKt3AJx9IS2mjoKZDAybCJYp9pCzJ24ejB4qjBk5I0Cagw09LWPLZKLTSa9zNdCe5L8bcO5u-K6MHGY8kWo7ARNHr2QY7MW00AeWxTG00" target="_blank" rel="noopener noreferrer" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,ICBBbGljZSAtPiBCb2I6IEF1dGhlbnRpY2F0aW9uIFJlcXVlc3QKICBCb2IgLS0+IEFsaWNlOiBBdXRoZW50aWNhdGlvbiBSZXNwb25zZQoKICBBbGljZSAtPiBCb2I6IEFub3RoZXIgYXV0aGVudGljYXRpb24gUmVxdWVzdAogIEFsaWNlIDwtLSBCb2I6IEFub3RoZXIgYXV0aGVudGljYXRpb24gUmVzcG9uc2UK"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" class="lazy" data-src="http://localhost:8080/png/U9nJK73CoKnELT2rKt3AJx9IS2mjoKZDAybCJYp9pCzJ24ejB4qjBk5I0Cagw09LWPLZKLTSa9zNdCe5L8bcO5u-K6MHGY8kWo7ARNHr2QY7MW00AeWxTG00"></a>
|
||||
|
||||
- name: div
|
||||
markdown: |-
|
||||
<div>plain text</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { GlDropdown, GlDropdownItem, GlButton } from '@gitlab/ui';
|
||||
import { GlDropdown, GlDropdownItem, GlTooltip } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import ClustersActions from '~/clusters_list/components/clusters_actions.vue';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
|
@ -7,12 +7,14 @@ import { INSTALL_AGENT_MODAL_ID, CLUSTERS_ACTIONS } from '~/clusters_list/consta
|
|||
describe('ClustersActionsComponent', () => {
|
||||
let wrapper;
|
||||
|
||||
const newClusterPath = 'path/to/create/cluster';
|
||||
const newClusterPath = 'path/to/add/cluster';
|
||||
const addClusterPath = 'path/to/connect/existing/cluster';
|
||||
const newClusterDocsPath = 'path/to/create/new/cluster';
|
||||
|
||||
const defaultProvide = {
|
||||
newClusterPath,
|
||||
addClusterPath,
|
||||
newClusterDocsPath,
|
||||
canAddCluster: true,
|
||||
displayClusterAgents: true,
|
||||
certificateBasedClustersEnabled: true,
|
||||
|
@ -20,12 +22,13 @@ describe('ClustersActionsComponent', () => {
|
|||
|
||||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
|
||||
const findTooltip = () => wrapper.findComponent(GlTooltip);
|
||||
const findDropdownItemIds = () =>
|
||||
findDropdownItems().wrappers.map((x) => x.attributes('data-testid'));
|
||||
const findDropdownItemTexts = () => findDropdownItems().wrappers.map((x) => x.text());
|
||||
const findNewClusterLink = () => wrapper.findByTestId('new-cluster-link');
|
||||
const findNewClusterDocsLink = () => wrapper.findByTestId('create-cluster-link');
|
||||
const findConnectClusterLink = () => wrapper.findByTestId('connect-cluster-link');
|
||||
const findConnectNewAgentLink = () => wrapper.findByTestId('connect-new-agent-link');
|
||||
const findConnectWithAgentButton = () => wrapper.findComponent(GlButton);
|
||||
|
||||
const createWrapper = (provideData = {}) => {
|
||||
wrapper = shallowMountExtended(ClustersActions, {
|
||||
|
@ -35,7 +38,6 @@ describe('ClustersActionsComponent', () => {
|
|||
},
|
||||
directives: {
|
||||
GlModalDirective: createMockDirective(),
|
||||
GlTooltip: createMockDirective(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -49,12 +51,15 @@ describe('ClustersActionsComponent', () => {
|
|||
});
|
||||
describe('when the certificate based clusters are enabled', () => {
|
||||
it('renders actions menu', () => {
|
||||
expect(findDropdown().props('text')).toBe(CLUSTERS_ACTIONS.actionsButton);
|
||||
expect(findDropdown().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders correct href attributes for the links', () => {
|
||||
expect(findNewClusterLink().attributes('href')).toBe(newClusterPath);
|
||||
expect(findConnectClusterLink().attributes('href')).toBe(addClusterPath);
|
||||
it('shows split button in dropdown', () => {
|
||||
expect(findDropdown().props('split')).toBe(true);
|
||||
});
|
||||
|
||||
it("doesn't show the tooltip", () => {
|
||||
expect(findTooltip().exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('when user cannot add clusters', () => {
|
||||
|
@ -67,8 +72,7 @@ describe('ClustersActionsComponent', () => {
|
|||
});
|
||||
|
||||
it('shows tooltip explaining why dropdown is disabled', () => {
|
||||
const tooltip = getBinding(findDropdown().element, 'gl-tooltip');
|
||||
expect(tooltip.value).toBe(CLUSTERS_ACTIONS.dropdownDisabledHint);
|
||||
expect(findTooltip().attributes('title')).toBe(CLUSTERS_ACTIONS.dropdownDisabledHint);
|
||||
});
|
||||
|
||||
it('does not bind split dropdown button', () => {
|
||||
|
@ -79,33 +83,36 @@ describe('ClustersActionsComponent', () => {
|
|||
});
|
||||
|
||||
describe('when on project level', () => {
|
||||
it(`displays default action as ${CLUSTERS_ACTIONS.connectWithAgent}`, () => {
|
||||
expect(findDropdown().props('text')).toBe(CLUSTERS_ACTIONS.connectWithAgent);
|
||||
});
|
||||
|
||||
it('renders correct modal id for the default action', () => {
|
||||
const binding = getBinding(findDropdown().element, 'gl-modal-directive');
|
||||
|
||||
expect(binding.value).toBe(INSTALL_AGENT_MODAL_ID);
|
||||
});
|
||||
|
||||
it('renders a dropdown with 3 actions items', () => {
|
||||
expect(findDropdownItemIds()).toEqual([
|
||||
'connect-new-agent-link',
|
||||
'create-cluster-link',
|
||||
'new-cluster-link',
|
||||
'connect-cluster-link',
|
||||
]);
|
||||
});
|
||||
|
||||
it('renders correct modal id for the agent link', () => {
|
||||
const binding = getBinding(findConnectNewAgentLink().element, 'gl-modal-directive');
|
||||
|
||||
expect(binding.value).toBe(INSTALL_AGENT_MODAL_ID);
|
||||
it('renders correct texts for the dropdown items', () => {
|
||||
expect(findDropdownItemTexts()).toEqual([
|
||||
CLUSTERS_ACTIONS.createCluster,
|
||||
CLUSTERS_ACTIONS.createClusterCertificate,
|
||||
CLUSTERS_ACTIONS.connectClusterCertificate,
|
||||
]);
|
||||
});
|
||||
|
||||
it('shows tooltip', () => {
|
||||
const tooltip = getBinding(findDropdown().element, 'gl-tooltip');
|
||||
expect(tooltip.value).toBe(CLUSTERS_ACTIONS.connectWithAgent);
|
||||
});
|
||||
|
||||
it('shows split button in dropdown', () => {
|
||||
expect(findDropdown().props('split')).toBe(true);
|
||||
});
|
||||
|
||||
it('binds split button with modal id', () => {
|
||||
const binding = getBinding(findDropdown().element, 'gl-modal-directive');
|
||||
|
||||
expect(binding.value).toBe(INSTALL_AGENT_MODAL_ID);
|
||||
it('renders correct href attributes for the links', () => {
|
||||
expect(findNewClusterDocsLink().attributes('href')).toBe(newClusterDocsPath);
|
||||
expect(findNewClusterLink().attributes('href')).toBe(newClusterPath);
|
||||
expect(findConnectClusterLink().attributes('href')).toBe(addClusterPath);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -114,17 +121,20 @@ describe('ClustersActionsComponent', () => {
|
|||
createWrapper({ displayClusterAgents: false });
|
||||
});
|
||||
|
||||
it('renders a dropdown with 2 actions items', () => {
|
||||
expect(findDropdownItemIds()).toEqual(['new-cluster-link', 'connect-cluster-link']);
|
||||
it(`displays default action as ${CLUSTERS_ACTIONS.connectClusterDeprecated}`, () => {
|
||||
expect(findDropdown().props('text')).toBe(CLUSTERS_ACTIONS.connectClusterDeprecated);
|
||||
});
|
||||
|
||||
it('shows tooltip', () => {
|
||||
const tooltip = getBinding(findDropdown().element, 'gl-tooltip');
|
||||
expect(tooltip.value).toBe(CLUSTERS_ACTIONS.connectExistingCluster);
|
||||
it('renders a dropdown with 1 action item', () => {
|
||||
expect(findDropdownItemIds()).toEqual(['new-cluster-link']);
|
||||
});
|
||||
|
||||
it('does not show split button in dropdown', () => {
|
||||
expect(findDropdown().props('split')).toBe(false);
|
||||
it('renders correct text for the dropdown item', () => {
|
||||
expect(findDropdownItemTexts()).toEqual([CLUSTERS_ACTIONS.createClusterDeprecated]);
|
||||
});
|
||||
|
||||
it('renders correct href attributes for the links', () => {
|
||||
expect(findNewClusterLink().attributes('href')).toBe(newClusterPath);
|
||||
});
|
||||
|
||||
it('does not bind dropdown button to modal', () => {
|
||||
|
@ -140,17 +150,26 @@ describe('ClustersActionsComponent', () => {
|
|||
createWrapper({ certificateBasedClustersEnabled: false });
|
||||
});
|
||||
|
||||
it('it does not show the the dropdown', () => {
|
||||
expect(findDropdown().exists()).toBe(false);
|
||||
it(`displays default action as ${CLUSTERS_ACTIONS.connectCluster}`, () => {
|
||||
expect(findDropdown().props('text')).toBe(CLUSTERS_ACTIONS.connectCluster);
|
||||
});
|
||||
|
||||
it('shows the connect with agent button', () => {
|
||||
expect(findConnectWithAgentButton().props()).toMatchObject({
|
||||
disabled: !defaultProvide.canAddCluster,
|
||||
category: 'primary',
|
||||
variant: 'confirm',
|
||||
});
|
||||
expect(findConnectWithAgentButton().text()).toBe(CLUSTERS_ACTIONS.connectWithAgent);
|
||||
it('renders correct modal id for the default action', () => {
|
||||
const binding = getBinding(findDropdown().element, 'gl-modal-directive');
|
||||
|
||||
expect(binding.value).toBe(INSTALL_AGENT_MODAL_ID);
|
||||
});
|
||||
|
||||
it('renders a dropdown with 1 action item', () => {
|
||||
expect(findDropdownItemIds()).toEqual(['create-cluster-link']);
|
||||
});
|
||||
|
||||
it('renders correct text for the dropdown item', () => {
|
||||
expect(findDropdownItemTexts()).toEqual([CLUSTERS_ACTIONS.createCluster]);
|
||||
});
|
||||
|
||||
it('renders correct href attributes for the links', () => {
|
||||
expect(findNewClusterDocsLink().attributes('href')).toBe(newClusterDocsPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -41,12 +41,12 @@ RSpec.describe 'Startup CSS fixtures', type: :controller do
|
|||
expect(response).to be_successful
|
||||
end
|
||||
|
||||
# This Feature Flag is off by default
|
||||
# This Feature Flag is on by default
|
||||
# This ensures that the correct css is generated
|
||||
# When the feature flag is off, the general startup will capture it
|
||||
# When the feature flag is on, the general startup will capture it
|
||||
# This will be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/339348
|
||||
it "startup_css/project-#{type}-search-ff-on.html" do
|
||||
stub_feature_flags(new_header_search: true)
|
||||
it "startup_css/project-#{type}-search-ff-off.html" do
|
||||
stub_feature_flags(new_header_search: false)
|
||||
|
||||
get :show, params: {
|
||||
namespace_id: project.namespace.to_param,
|
||||
|
|
|
@ -340,7 +340,9 @@ describe('Milestone combobox component', () => {
|
|||
await nextTick();
|
||||
|
||||
expect(
|
||||
findFirstProjectMilestonesDropdownItem().find('span').classes('selected-item'),
|
||||
findFirstProjectMilestonesDropdownItem()
|
||||
.find('svg')
|
||||
.classes('gl-new-dropdown-item-check-icon'),
|
||||
).toBe(true);
|
||||
|
||||
selectFirstProjectMilestone();
|
||||
|
@ -348,8 +350,8 @@ describe('Milestone combobox component', () => {
|
|||
await nextTick();
|
||||
|
||||
expect(
|
||||
findFirstProjectMilestonesDropdownItem().find('span').classes('selected-item'),
|
||||
).toBe(false);
|
||||
findFirstProjectMilestonesDropdownItem().find('svg').classes('gl-visibility-hidden'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
describe('when a project milestones is selected', () => {
|
||||
|
@ -464,17 +466,19 @@ describe('Milestone combobox component', () => {
|
|||
|
||||
await nextTick();
|
||||
|
||||
expect(findFirstGroupMilestonesDropdownItem().find('span').classes('selected-item')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
findFirstGroupMilestonesDropdownItem()
|
||||
.find('svg')
|
||||
.classes('gl-new-dropdown-item-check-icon'),
|
||||
).toBe(true);
|
||||
|
||||
selectFirstGroupMilestone();
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(findFirstGroupMilestonesDropdownItem().find('span').classes('selected-item')).toBe(
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
findFirstGroupMilestonesDropdownItem().find('svg').classes('gl-visibility-hidden'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
describe('when a group milestones is selected', () => {
|
||||
|
|
|
@ -74,6 +74,10 @@ RSpec.describe ClustersHelper do
|
|||
expect(subject[:add_cluster_path]).to eq("#{project_path(project)}/-/clusters/connect")
|
||||
end
|
||||
|
||||
it 'displays create cluster path' do
|
||||
expect(subject[:new_cluster_docs_path]).to eq("#{project_path(project)}/-/clusters/new_cluster_docs")
|
||||
end
|
||||
|
||||
it 'displays project default branch' do
|
||||
expect(subject[:default_branch_name]).to eq(project.default_branch)
|
||||
end
|
||||
|
|
|
@ -46,6 +46,16 @@ RSpec.describe Banzai::Filter::ImageLinkFilter do
|
|||
expect(doc.at_css('img')['data-canonical-src']).to eq doc.at_css('a')['data-canonical-src']
|
||||
end
|
||||
|
||||
it 'moves the data-diagram* attributes' do
|
||||
doc = filter(%q(<img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">), context)
|
||||
|
||||
expect(doc.at_css('a')['data-diagram']).to eq "plantuml"
|
||||
expect(doc.at_css('a')['data-diagram-src']).to eq "data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw=="
|
||||
|
||||
expect(doc.at_css('a img')['data-diagram']).to be_nil
|
||||
expect(doc.at_css('a img')['data-diagram-src']).to be_nil
|
||||
end
|
||||
|
||||
it 'adds no-attachment icon class to the link' do
|
||||
doc = filter(image(path), context)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ RSpec.describe Banzai::Filter::KrokiFilter do
|
|||
stub_application_setting(kroki_enabled: true, kroki_url: "http://localhost:8000")
|
||||
doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>")
|
||||
|
||||
expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==">'
|
||||
expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDogSW50fHJhaWQoKTtwaWxsYWdlKCl8CiAgW2JlYXJkXS0tW3BhcnJvdF0KICBbYmVhcmRdLTo+W2ZvdWwgbW91dGhdCl0=">'
|
||||
end
|
||||
|
||||
it 'replaces nomnoml pre tag with img tag if both kroki and plantuml are enabled' do
|
||||
|
@ -19,7 +19,7 @@ RSpec.describe Banzai::Filter::KrokiFilter do
|
|||
plantuml_url: "http://localhost:8080")
|
||||
doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>")
|
||||
|
||||
expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==">'
|
||||
expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDogSW50fHJhaWQoKTtwaWxsYWdlKCl8CiAgW2JlYXJkXS0tW3BhcnJvdF0KICBbYmVhcmRdLTo+W2ZvdWwgbW91dGhdCl0=">'
|
||||
end
|
||||
|
||||
it 'does not replace nomnoml pre tag with img tag if kroki is disabled' do
|
||||
|
|
|
@ -9,7 +9,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter do
|
|||
stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080")
|
||||
|
||||
input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
output = '<div class="imageblock"><div class="content"><img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq"></div></div>'
|
||||
output = '<img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
|
@ -29,7 +29,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter do
|
|||
stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid")
|
||||
|
||||
input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
output = '<div class="listingblock"><div class="content"><pre class="plantuml plantuml-error"> Error: cannot connect to PlantUML server at "invalid"</pre></div></div>'
|
||||
output = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
|
|
|
@ -49,6 +49,12 @@ RSpec.describe ProjectClusterablePresenter do
|
|||
it { is_expected.to eq(connect_project_clusters_path(project)) }
|
||||
end
|
||||
|
||||
describe '#new_cluster_docs_path' do
|
||||
subject { presenter.new_cluster_docs_path }
|
||||
|
||||
it { is_expected.to eq(new_cluster_docs_project_clusters_path(project)) }
|
||||
end
|
||||
|
||||
describe '#authorize_aws_role_path' do
|
||||
subject { presenter.authorize_aws_role_path }
|
||||
|
||||
|
|
|
@ -329,11 +329,6 @@ RSpec.configure do |config|
|
|||
stub_feature_flags(disable_anonymous_search: false)
|
||||
stub_feature_flags(disable_anonymous_project_search: false)
|
||||
|
||||
# Disable the refactored top nav search until there is functionality
|
||||
# Can be removed once all existing functionality has been replicated
|
||||
# For more information check https://gitlab.com/gitlab-org/gitlab/-/issues/339348
|
||||
stub_feature_flags(new_header_search: false)
|
||||
|
||||
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged)
|
||||
else
|
||||
unstub_all_feature_flags
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
module SearchHelpers
|
||||
def fill_in_search(text)
|
||||
page.within('.search-input-wrap') do
|
||||
# Once the `new_header_search` feature flag has been removed
|
||||
# We can remove the `.search-input-wrap` selector
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/339348
|
||||
page.within('.header-search-new') do
|
||||
find('#search').click
|
||||
fill_in('search', with: text)
|
||||
fill_in 'search', with: text
|
||||
end
|
||||
|
||||
wait_for_all_requests
|
||||
|
|
|
@ -270,7 +270,7 @@ module MarkdownMatchers
|
|||
set_default_markdown_messages
|
||||
|
||||
match do |actual|
|
||||
expect(actual).to have_link(href: 'http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==')
|
||||
expect(actual).to have_link(href: 'http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjliuUCAE_tHdw=')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,6 +64,9 @@ RSpec.shared_context 'API::Markdown Golden Master shared context' do |markdown_y
|
|||
let(:substitutions) { markdown_example.fetch(:substitutions, {}) }
|
||||
|
||||
it "verifies conversion of GFM to HTML", :unlimited_max_formatted_output_length do
|
||||
stub_application_setting(plantuml_enabled: true, plantuml_url: 'http://localhost:8080')
|
||||
stub_application_setting(kroki_enabled: true, kroki_url: 'http://localhost:8000')
|
||||
|
||||
pending pending_reason if pending_reason
|
||||
|
||||
normalized_example_html = normalize_html(example_html, substitutions)
|
||||
|
|
|
@ -11,6 +11,7 @@ RSpec.shared_examples 'search timeouts' do |scope|
|
|||
end
|
||||
|
||||
it 'renders timeout information' do
|
||||
# expect(page).to have_content('This endpoint has been requested too many times.')
|
||||
expect(page).to have_content('Your search timed out')
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue