Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-03-28 09:07:06 +00:00
parent c222aa0890
commit b9e3013993
56 changed files with 432 additions and 220 deletions

View File

@ -1 +1 @@
36aaf4e475fdcc4ae89f14772662fa89125d7716
e302aa4a8caf07caad38c236d610fea49a41aa2f

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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', () => {

View File

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

View File

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

View File

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

View File

@ -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 -&gt; Sara : Hello</code></pre>'
doc = filter(input)
expect(doc.to_s).to eq output

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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