Commit 120f4aae authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 729e3765
import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () => {
const callout = document.querySelector('.js-admin-integrations-moved');
PersistentUserCallout.factory(callout);
});
...@@ -3,7 +3,7 @@ import ZenMode from '~/zen_mode'; ...@@ -3,7 +3,7 @@ import ZenMode from '~/zen_mode';
import LineHighlighter from '~/line_highlighter'; import LineHighlighter from '~/line_highlighter';
import BlobViewer from '~/blob/viewer'; import BlobViewer from '~/blob/viewer';
import snippetEmbed from '~/snippet/snippet_embed'; import snippetEmbed from '~/snippet/snippet_embed';
import initSnippetsApp from '~/snippets'; import { SnippetShowInit } from '~/snippets';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
if (!gon.features.snippetsVue) { if (!gon.features.snippetsVue) {
...@@ -13,7 +13,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -13,7 +13,7 @@ document.addEventListener('DOMContentLoaded', () => {
new ZenMode(); // eslint-disable-line no-new new ZenMode(); // eslint-disable-line no-new
snippetEmbed(); snippetEmbed();
} else { } else {
initSnippetsApp(); SnippetShowInit();
initNotes(); initNotes();
} }
}); });
...@@ -3,7 +3,7 @@ import BlobViewer from '~/blob/viewer'; ...@@ -3,7 +3,7 @@ import BlobViewer from '~/blob/viewer';
import ZenMode from '~/zen_mode'; import ZenMode from '~/zen_mode';
import initNotes from '~/init_notes'; import initNotes from '~/init_notes';
import snippetEmbed from '~/snippet/snippet_embed'; import snippetEmbed from '~/snippet/snippet_embed';
import initSnippetsApp from '~/snippets'; import { SnippetShowInit } from '~/snippets';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
if (!gon.features.snippetsVue) { if (!gon.features.snippetsVue) {
...@@ -13,7 +13,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -13,7 +13,7 @@ document.addEventListener('DOMContentLoaded', () => {
new ZenMode(); // eslint-disable-line no-new new ZenMode(); // eslint-disable-line no-new
snippetEmbed(); snippetEmbed();
} else { } else {
initSnippetsApp(); SnippetShowInit();
initNotes(); initNotes();
} }
}); });
...@@ -58,7 +58,11 @@ export default { ...@@ -58,7 +58,11 @@ export default {
</script> </script>
<template> <template>
<div class="table-section section-10 d-none d-sm-none d-md-block pipeline-tags"> <div class="table-section section-10 d-none d-sm-none d-md-block pipeline-tags">
<gl-link :href="pipeline.path" class="js-pipeline-url-link js-onboarding-pipeline-item"> <gl-link
:href="pipeline.path"
class="js-pipeline-url-link js-onboarding-pipeline-item"
data-qa-selector="pipeline_url_link"
>
<span class="pipeline-id">#{{ pipeline.id }}</span> <span class="pipeline-id">#{{ pipeline.id }}</span>
</gl-link> </gl-link>
<div class="label-container"> <div class="label-container">
......
...@@ -3,19 +3,16 @@ import Translate from '~/vue_shared/translate'; ...@@ -3,19 +3,16 @@ import Translate from '~/vue_shared/translate';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import SnippetsApp from './components/app.vue'; import SnippetsApp from './components/show.vue';
Vue.use(VueApollo); Vue.use(VueApollo);
Vue.use(Translate); Vue.use(Translate);
export default () => { function appFactory(el, Component) {
const el = document.getElementById('js-snippet-view');
if (!el) { if (!el) {
return false; return false;
} }
const { snippetGid } = el.dataset;
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(),
}); });
...@@ -24,11 +21,17 @@ export default () => { ...@@ -24,11 +21,17 @@ export default () => {
el, el,
apolloProvider, apolloProvider,
render(createElement) { render(createElement) {
return createElement(SnippetsApp, { return createElement(Component, {
props: { props: {
snippetGid, ...el.dataset,
}, },
}); });
}, },
}); });
}
export const SnippetShowInit = () => {
appFactory(document.getElementById('js-snippet-view'), SnippetsApp);
}; };
export default () => {};
...@@ -81,3 +81,8 @@ ...@@ -81,3 +81,8 @@
.gl-text-green-700 { @include gl-text-green-700; } .gl-text-green-700 { @include gl-text-green-700; }
.gl-align-items-center { @include gl-align-items-center; } .gl-align-items-center { @include gl-align-items-center; }
.d-sm-table-column {
@include media-breakpoint-up(sm) {
display: table-column !important;
}
}
...@@ -27,6 +27,16 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -27,6 +27,16 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
define_method(action) { perform_update if submitted? } define_method(action) { perform_update if submitted? }
end end
def integrations
if Feature.enabled?(:instance_level_integrations)
# TODO: Update this with actual integrations
# To be fixed with https://gitlab.com/gitlab-org/gitlab/-/issues/199388
@integrations = []
end
perform_update if submitted?
end
def update def update
perform_update perform_update
end end
......
...@@ -48,7 +48,7 @@ module BroadcastMessagesHelper ...@@ -48,7 +48,7 @@ module BroadcastMessagesHelper
def render_broadcast_message(broadcast_message) def render_broadcast_message(broadcast_message)
if Feature.enabled?(:broadcast_message_placeholders) if Feature.enabled?(:broadcast_message_placeholders)
Banzai.render_and_post_process(broadcast_message.message, { Banzai.render_field_and_post_process(broadcast_message, :message, {
current_user: current_user, current_user: current_user,
skip_project_check: true, skip_project_check: true,
broadcast_message_placeholders: true broadcast_message_placeholders: true
......
...@@ -62,6 +62,10 @@ module ServicesHelper ...@@ -62,6 +62,10 @@ module ServicesHelper
!current_controller?("admin/services") && service.deprecated? !current_controller?("admin/services") && service.deprecated?
end end
def edit_integration_path(integration)
edit_admin_application_settings_integration_path(integration)
end
extend self extend self
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module UserCalloutsHelper module UserCalloutsHelper
ADMIN_INTEGRATIONS_MOVED = 'admin_integrations_moved'
GKE_CLUSTER_INTEGRATION = 'gke_cluster_integration' GKE_CLUSTER_INTEGRATION = 'gke_cluster_integration'
GCP_SIGNUP_OFFER = 'gcp_signup_offer' GCP_SIGNUP_OFFER = 'gcp_signup_offer'
SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed' SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed'
TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight' TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight'
WEBHOOKS_MOVED = 'webhooks_moved' WEBHOOKS_MOVED = 'webhooks_moved'
def show_admin_integrations_moved?
!user_dismissed?(ADMIN_INTEGRATIONS_MOVED)
end
def show_gke_cluster_integration_callout?(project) def show_gke_cluster_integration_callout?(project)
can?(current_user, :create_cluster, project) && can?(current_user, :create_cluster, project) &&
!user_dismissed?(GKE_CLUSTER_INTEGRATION) !user_dismissed?(GKE_CLUSTER_INTEGRATION)
......
...@@ -4,7 +4,7 @@ module OptionallySearch ...@@ -4,7 +4,7 @@ module OptionallySearch
extend ActiveSupport::Concern extend ActiveSupport::Concern
class_methods do class_methods do
def search(*) def search(query, **options)
raise( raise(
NotImplementedError, NotImplementedError,
'Your model must implement the "search" class method' 'Your model must implement the "search" class method'
......
...@@ -138,7 +138,7 @@ class Label < ApplicationRecord ...@@ -138,7 +138,7 @@ class Label < ApplicationRecord
# query - The search query as a String. # query - The search query as a String.
# #
# Returns an ActiveRecord::Relation. # Returns an ActiveRecord::Relation.
def self.search(query) def self.search(query, **options)
fuzzy_search(query, [:title, :description]) fuzzy_search(query, [:title, :description])
end end
......
...@@ -274,7 +274,7 @@ class Namespace < ApplicationRecord ...@@ -274,7 +274,7 @@ class Namespace < ApplicationRecord
end end
def has_parent? def has_parent?
parent_id.present? || parent.present? parent.present?
end end
def root_ancestor def root_ancestor
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require "discordrb/webhooks" require "discordrb/webhooks"
class DiscordService < ChatNotificationService class DiscordService < ChatNotificationService
ATTACHMENT_REGEX = /: (?<entry>.*?)\n - (?<name>.*)\n*/.freeze
def title def title
s_("DiscordService|Discord Notifications") s_("DiscordService|Discord Notifications")
end end
...@@ -52,7 +54,10 @@ class DiscordService < ChatNotificationService ...@@ -52,7 +54,10 @@ class DiscordService < ChatNotificationService
client = Discordrb::Webhooks::Client.new(url: webhook) client = Discordrb::Webhooks::Client.new(url: webhook)
client.execute do |builder| client.execute do |builder|
builder.content = message.pretext builder.add_embed do |embed|
embed.author = Discordrb::Webhooks::EmbedAuthor.new(name: message.user_name, icon_url: message.user_avatar)
embed.description = (message.pretext + "\n" + Array.wrap(message.attachments).join("\n")).gsub(ATTACHMENT_REGEX, " \\k<entry> - \\k<name>\n")
end
end end
end end
......
...@@ -511,7 +511,7 @@ class User < ApplicationRecord ...@@ -511,7 +511,7 @@ class User < ApplicationRecord
# query - The search query as a String # query - The search query as a String
# #
# Returns an ActiveRecord::Relation. # Returns an ActiveRecord::Relation.
def search(query) def search(query, **options)
query = query&.delete_prefix('@') query = query&.delete_prefix('@')
return none if query.blank? return none if query.blank?
......
...@@ -16,7 +16,8 @@ module UserCalloutEnums ...@@ -16,7 +16,8 @@ module UserCalloutEnums
cluster_security_warning: 3, cluster_security_warning: 3,
suggest_popover_dismissed: 9, suggest_popover_dismissed: 9,
tabs_position_highlight: 10, tabs_position_highlight: 10,
webhooks_moved: 13 webhooks_moved: 13,
admin_integrations_moved: 15
} }
end end
end end
......
...@@ -103,3 +103,12 @@ ...@@ -103,3 +103,12 @@
= s_('IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation.') = s_('IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox client side evaluation.')
= f.submit _('Save changes'), class: "btn btn-success" = f.submit _('Save changes'), class: "btn btn-success"
- if Feature.enabled?(:instance_level_integrations)
= render_if_exists 'admin/application_settings/elasticsearch_form'
= render 'admin/application_settings/plantuml'
= render 'admin/application_settings/sourcegraph'
= render_if_exists 'admin/application_settings/slack'
= render 'admin/application_settings/third_party_offers'
= render 'admin/application_settings/snowplow'
= render 'admin/application_settings/eks'
- breadcrumb_title _("Integrations") - breadcrumb_title _('Integrations')
- page_title _("Integrations") - page_title _('Integrations')
- @content_class = "limit-container-width" unless fluid_layout - @content_class = 'limit-container-width' unless fluid_layout
= render_if_exists 'admin/application_settings/elasticsearch_form' - if Feature.enabled?(:instance_level_integrations)
= render 'admin/application_settings/plantuml' - if show_admin_integrations_moved?
= render 'admin/application_settings/sourcegraph' .gl-alert.gl-alert-info.js-admin-integrations-moved.mt-3{ role: 'alert', data: { feature_id: UserCalloutsHelper::ADMIN_INTEGRATIONS_MOVED, dismiss_endpoint: user_callouts_path } }
= render_if_exists 'admin/application_settings/slack' = sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
= render 'admin/application_settings/third_party_offers' %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
= render 'admin/application_settings/snowplow' = sprite_icon('close', size: 16, css_class: 'gl-icon')
= render 'admin/application_settings/eks' .gl-alert-body
%h4.gl-alert-title= s_('AdminSettings|Some settings have moved')
= s_('AdminSettings|Elasticsearch, PlantUML, Slack application, Third party offers, Snowplow, Amazon EKS have moved to Settings > General.')
.gl-alert-actions
= link_to s_('AdminSettings|Go to General Settings'), admin_application_settings_path, class: 'btn gl-alert-action btn-info new-gl-button'
%h4= s_('AdminSettings|Apply integration settings to all Projects')
%p
= s_('AdminSettings|Integrations configured here will automatically apply to all projects on this instance.')
= link_to _('Learn more'), '#'
= render 'projects/services/integrations'
- else
= render_if_exists 'admin/application_settings/elasticsearch_form'
= render 'admin/application_settings/plantuml'
= render 'admin/application_settings/sourcegraph'
= render_if_exists 'admin/application_settings/slack'
= render 'admin/application_settings/third_party_offers'
= render 'admin/application_settings/snowplow'
= render 'admin/application_settings/eks'
- add_to_breadcrumbs _('Integrations'), admin_application_settings_integration_path - add_to_breadcrumbs _('Integrations'), integrations_admin_application_settings_path
- breadcrumb_title @service.title - breadcrumb_title @service.title
- page_title @service.title, _('Integrations') - page_title @service.title, _('Integrations')
......
%table.table.b-table.gl-table.mt-3{ role: 'table', 'aria-busy': false, 'aria-colcount': 4 }
%colgroup
%col
%col
%col.d-none.d-sm-table-column
%col{ width: 120 }
%thead{ role: 'rowgroup' }
%tr{ role: 'row' }
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 1 }
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 2 }= _('Integration')
%th.d-none.d-sm-block{ role: 'columnheader', scope: 'col', 'aria-colindex': 3 }= _('Description')
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 4 }= _('Last updated')
%tbody{ role: 'rowgroup' }
- @integrations&.each do |integration|
%tr{ role: 'row' }
%td{ role: 'cell', 'aria-colindex': 1 }
= boolean_to_icon integration.activated?
%td{ role: 'cell', 'aria-colindex': 2 }
= link_to edit_integration_path(integration) do
%strong= integration.title
%td.d-none.d-sm-block{ role: 'cell', 'aria-colindex': 3 }
= integration.description
%td{ role: 'cell', 'aria-colindex': 4 }
- if integration.updated_at.present?
= time_ago_with_tooltip integration.updated_at
...@@ -928,7 +928,7 @@ ...@@ -928,7 +928,7 @@
:weight: 2 :weight: 2
:idempotent: true :idempotent: true
- :name: background_migration - :name: background_migration
:feature_category: :not_owned :feature_category: :database
:has_external_dependencies: :has_external_dependencies:
:urgency: :low :urgency: :low
:resource_boundary: :unknown :resource_boundary: :unknown
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class BackgroundMigrationWorker # rubocop:disable Scalability/IdempotentWorker class BackgroundMigrationWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker include ApplicationWorker
feature_category_not_owned! feature_category :database
# The minimum amount of time between processing two jobs of the same migration # The minimum amount of time between processing two jobs of the same migration
# class. # class.
......
---
title: Optimize usage ping queries by using batch counting
merge_request: 27455
author:
type: performance
---
title: Update discord notifications to be a single embed and include log messages
merge_request: 27812
author: Sam Bingner
type: fixed
---
title: Use id instead of cve where possible when parsing remediations
merge_request: 27815
author:
type: other
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
--- ---
- accessibility_testing - accessibility_testing
- analysis - analysis
- api
- attack_emulation - attack_emulation
- audit_events - audit_events
- audit_reports - audit_reports
...@@ -24,14 +25,16 @@ ...@@ -24,14 +25,16 @@
- code_analytics - code_analytics
- code_quality - code_quality
- code_review - code_review
- code_testing
- collection - collection
- compliance_management - compliance_management
- container_behavior_analytics
- container_network_security - container_network_security
- container_registry - container_registry
- container_scanning - container_scanning
- continuous_delivery - continuous_delivery
- continuous_integration - continuous_integration
- ddos_protection - database
- dependency_firewall - dependency_firewall
- dependency_proxy - dependency_proxy
- dependency_scanning - dependency_scanning
...@@ -43,8 +46,8 @@ ...@@ -43,8 +46,8 @@
- epics - epics
- error_tracking - error_tracking
- feature_flags - feature_flags
- frontend_foundation - foundations
- fuzzing - fuzz-testing
- gdk - gdk
- geo_replication - geo_replication
- git_lfs - git_lfs
...@@ -72,6 +75,7 @@ ...@@ -72,6 +75,7 @@
- load_testing - load_testing
- logging - logging
- malware_scanning - malware_scanning
- merge_trains
- metrics - metrics
- omnibus_package - omnibus_package
- package_registry - package_registry
...@@ -87,8 +91,6 @@ ...@@ -87,8 +91,6 @@
- roadmaps - roadmaps
- runbooks - runbooks
- runner - runner
- runtime_application_self_protection
- sdk
- secret_detection - secret_detection
- secrets_management - secrets_management
- serverless - serverless
...@@ -100,10 +102,8 @@ ...@@ -100,10 +102,8 @@
- status_page - status_page
- subgroups - subgroups
- templates - templates
- threat_detection
- time_tracking - time_tracking
- tracing - tracing
- unit_testing
- usability_testing - usability_testing
- users - users
- value_stream_management - value_stream_management
......
...@@ -8,6 +8,10 @@ module Banzai ...@@ -8,6 +8,10 @@ module Banzai
post_process(render(text, context), context) post_process(render(text, context), context)
end end
def self.render_field_and_post_process(object, field, context = {})
post_process(render_field(object, field, context), context)
end
def self.render(text, context = {}) def self.render(text, context = {})
Renderer.render(text, context) Renderer.render(text, context)
end end
......
...@@ -13,7 +13,12 @@ module Gitlab ...@@ -13,7 +13,12 @@ module Gitlab
VALIDATION_REQUEST_TIMEOUT = 5 VALIDATION_REQUEST_TIMEOUT = 5
def perform! def perform!
error('External validation failed', drop_reason: :external_validation_failure) unless validate_external pipeline_authorized = validate_external
log_message = pipeline_authorized ? 'authorized' : 'not authorized'
Gitlab::AppLogger.info(message: "Pipeline #{log_message}", project_id: @pipeline.project.id, user_id: @pipeline.user.id)
error('External validation failed', drop_reason: :external_validation_failure) unless pipeline_authorized
end end
def break? def break?
......
...@@ -218,9 +218,9 @@ module Gitlab ...@@ -218,9 +218,9 @@ module Gitlab
results[:projects_jira_server_active] += counts[:server].count if counts[:server] results[:projects_jira_server_active] += counts[:server].count if counts[:server]
results[:projects_jira_cloud_active] += counts[:cloud].count if counts[:cloud] results[:projects_jira_cloud_active] += counts[:cloud].count if counts[:cloud]
if results[:projects_jira_active] == -1 if results[:projects_jira_active] == -1
results[:projects_jira_active] = count(services, batch: false) results[:projects_jira_active] = services.size
else else
results[:projects_jira_active] += count(services, batch: false) results[:projects_jira_active] += services.size
end end
end end
......
...@@ -1346,15 +1346,27 @@ msgstr "" ...@@ -1346,15 +1346,27 @@ msgstr ""
msgid "AdminProjects|Delete project" msgid "AdminProjects|Delete project"
msgstr "" msgstr ""
msgid "AdminSettings|Apply integration settings to all Projects"
msgstr ""
msgid "AdminSettings|Auto DevOps domain" msgid "AdminSettings|Auto DevOps domain"
msgstr "" msgstr ""
msgid "AdminSettings|Elasticsearch, PlantUML, Slack application, Third party offers, Snowplow, Amazon EKS have moved to Settings > General."
msgstr ""
msgid "AdminSettings|Enable shared runners for new projects" msgid "AdminSettings|Enable shared runners for new projects"
msgstr "" msgstr ""
msgid "AdminSettings|Environment variables are protected by default" msgid "AdminSettings|Environment variables are protected by default"
msgstr "" msgstr ""
msgid "AdminSettings|Go to General Settings"
msgstr ""
msgid "AdminSettings|Integrations configured here will automatically apply to all projects on this instance."
msgstr ""
msgid "AdminSettings|No required pipeline" msgid "AdminSettings|No required pipeline"
msgstr "" msgstr ""
...@@ -1370,6 +1382,9 @@ msgstr "" ...@@ -1370,6 +1382,9 @@ msgstr ""
msgid "AdminSettings|Set an instance-wide auto included %{link_start}pipeline configuration%{link_end}. This pipeline configuration will be run after the project's own configuration." msgid "AdminSettings|Set an instance-wide auto included %{link_start}pipeline configuration%{link_end}. This pipeline configuration will be run after the project's own configuration."
msgstr "" msgstr ""
msgid "AdminSettings|Some settings have moved"
msgstr ""
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages." msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
msgstr "" msgstr ""
...@@ -8458,6 +8473,9 @@ msgstr "" ...@@ -8458,6 +8473,9 @@ msgstr ""
msgid "Failed to load errors from Sentry. Error message: %{errorMessage}" msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr "" msgstr ""
msgid "Failed to load group activity metrics. Please try again."
msgstr ""
msgid "Failed to load groups & users." msgid "Failed to load groups & users."
msgstr "" msgstr ""
...@@ -10015,6 +10033,15 @@ msgstr "" ...@@ -10015,6 +10033,15 @@ msgstr ""
msgid "Group: %{name}" msgid "Group: %{name}"
msgstr "" msgstr ""
msgid "GroupActivyMetrics|Issues created"
msgstr ""
msgid "GroupActivyMetrics|Merge Requests created"
msgstr ""
msgid "GroupActivyMetrics|Recent activity (last 90 days)"
msgstr ""
msgid "GroupRoadmap|%{dateWord} – No end date" msgid "GroupRoadmap|%{dateWord} – No end date"
msgstr "" msgstr ""
...@@ -10943,6 +10970,9 @@ msgstr "" ...@@ -10943,6 +10970,9 @@ msgstr ""
msgid "Instance license" msgid "Instance license"
msgstr "" msgstr ""
msgid "Integration"
msgstr ""
msgid "Integration Settings" msgid "Integration Settings"
msgstr "" msgstr ""
......
...@@ -478,7 +478,7 @@ module QA ...@@ -478,7 +478,7 @@ module QA
autoload :Configure, 'qa/vendor/jenkins/page/configure' autoload :Configure, 'qa/vendor/jenkins/page/configure'
autoload :NewCredentials, 'qa/vendor/jenkins/page/new_credentials' autoload :NewCredentials, 'qa/vendor/jenkins/page/new_credentials'
autoload :NewJob, 'qa/vendor/jenkins/page/new_job' autoload :NewJob, 'qa/vendor/jenkins/page/new_job'
autoload :Job, 'qa/vendor/jenkins/page/job' autoload :LastJobConsole, 'qa/vendor/jenkins/page/last_job_console'
autoload :ConfigureJob, 'qa/vendor/jenkins/page/configure_job' autoload :ConfigureJob, 'qa/vendor/jenkins/page/configure_job'
end end
end end
......
...@@ -4,7 +4,7 @@ module QA::Page ...@@ -4,7 +4,7 @@ module QA::Page
module Project::Pipeline module Project::Pipeline
class Index < QA::Page::Base class Index < QA::Page::Base
view 'app/assets/javascripts/pipelines/components/pipeline_url.vue' do view 'app/assets/javascripts/pipelines/components/pipeline_url.vue' do
element :pipeline_link, 'class="js-pipeline-url-link' # rubocop:disable QA/ElementWithPattern element :pipeline_url_link
end end
view 'app/assets/javascripts/pipelines/components/pipelines_table_row.vue' do view 'app/assets/javascripts/pipelines/components/pipelines_table_row.vue' do
...@@ -13,9 +13,7 @@ module QA::Page ...@@ -13,9 +13,7 @@ module QA::Page
end end
def click_on_latest_pipeline def click_on_latest_pipeline
css = '.js-pipeline-url-link' all_elements(:pipeline_url_link, minimum: 1, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).first.click
first(css, wait: 60).click
end end
def wait_for_latest_pipeline_success def wait_for_latest_pipeline_success
......
...@@ -18,6 +18,7 @@ module QA ...@@ -18,6 +18,7 @@ module QA
click_build_when_change_is_pushed_to_gitlab click_build_when_change_is_pushed_to_gitlab
set_publish_status_to_gitlab set_publish_status_to_gitlab
click_save click_save
wait_for_configuration_to_save
end end
private private
...@@ -55,6 +56,12 @@ module QA ...@@ -55,6 +56,12 @@ module QA
def select_publish_build_status_to_gitlab def select_publish_build_status_to_gitlab
click_link "Publish build status to GitLab" click_link "Publish build status to GitLab"
end end
def wait_for_configuration_to_save
QA::Support::Waiter.wait_until(sleep_interval: 1.0) do
!page.current_url.include?(@path)
end
end
end end
end end
end end
......
...@@ -6,15 +6,19 @@ module QA ...@@ -6,15 +6,19 @@ module QA
module Vendor module Vendor
module Jenkins module Jenkins
module Page module Page
class Job < Page::Base class LastJobConsole < Page::Base
attr_accessor :job_name attr_accessor :job_name
def path def path
"/job/#{@job_name}" "/job/#{@job_name}/lastBuild/console"
end end
def has_successful_build? def has_successful_build?
page.has_text?("Last successful build") page.has_text?('Finished: SUCCESS')
end
def no_failed_status_update?
page.has_no_text?('Failed to update Gitlab commit status')
end end
end end
end end
......
...@@ -3,5 +3,6 @@ ...@@ -3,5 +3,6 @@
FactoryBot.define do FactoryBot.define do
factory :application_setting do factory :application_setting do
default_projects_limit { 42 } default_projects_limit { 42 }
import_sources { [] }
end end
end end
...@@ -194,12 +194,6 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc ...@@ -194,12 +194,6 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
expect(page).to have_content "Application settings saved successfully" expect(page).to have_content "Application settings saved successfully"
expect(current_settings.terminal_max_session_time).to eq(15) expect(current_settings.terminal_max_session_time).to eq(15)
end end
end
context 'Integrations page' do
before do
visit integrations_admin_application_settings_path
end
it 'Enable hiding third party offers' do it 'Enable hiding third party offers' do
page.within('.as-third-party-offers') do page.within('.as-third-party-offers') do
...@@ -241,6 +235,25 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc ...@@ -241,6 +235,25 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
end end
end end
context 'Integration page', :js do
before do
visit integrations_admin_application_settings_path
end
it 'allows user to dismiss deprecation notice' do
expect(page).to have_content('Some settings have moved')
click_button 'Dismiss'
wait_for_requests
expect(page).not_to have_content('Some settings have moved')
visit integrations_admin_application_settings_path
expect(page).not_to have_content('Some settings have moved')
end
end
context 'CI/CD page' do context 'CI/CD page' do
it 'Change CI/CD settings' do it 'Change CI/CD settings' do
visit ci_cd_admin_application_settings_path visit ci_cd_admin_application_settings_path
......
...@@ -68,4 +68,16 @@ describe 'Broadcast Messages' do ...@@ -68,4 +68,16 @@ describe 'Broadcast Messages' do
expect(page).to have_content "Hi #{user.name}" expect(page).to have_content "Hi #{user.name}"
end end
it 'renders broadcast message with placeholders and styled links' do
create(:broadcast_message, broadcast_type: 'notification', message: "Hi {{name}} <a href='gitlab.com' style='color: purple'>click</a>")
user = create(:user)
sign_in(user)
visit root_path
expected_html = "<p>Hi #{user.name} <a href=\"gitlab.com\" style=\"color: purple\">click</a></p>"
expect(page.body).to include(expected_html)
end
end end
...@@ -208,7 +208,7 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do ...@@ -208,7 +208,7 @@ describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin) sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin) gitlab_enable_admin_mode_sign_in(admin)
visit integrations_admin_application_settings_path visit general_admin_application_settings_path
end end
it 'user does not see the offer' do it 'user does not see the offer' do
......
import SnippetApp from '~/snippets/components/app.vue'; import SnippetApp from '~/snippets/components/show.vue';
import SnippetHeader from '~/snippets/components/snippet_header.vue'; import SnippetHeader from '~/snippets/components/snippet_header.vue';
import SnippetTitle from '~/snippets/components/snippet_title.vue'; import SnippetTitle from '~/snippets/components/snippet_title.vue';
import SnippetBlob from '~/snippets/components/snippet_blob_view.vue'; import SnippetBlob from '~/snippets/components/snippet_blob_view.vue';
......
...@@ -47,6 +47,26 @@ describe UserCalloutsHelper do ...@@ -47,6 +47,26 @@ describe UserCalloutsHelper do
end end
end end
describe '.show_admin_integrations_moved?' do
subject { helper.show_admin_integrations_moved? }
context 'when user has not dismissed' do
before do
allow(helper).to receive(:user_dismissed?).with(described_class::ADMIN_INTEGRATIONS_MOVED) { false }
end
it { is_expected.to be true }
end
context 'when user dismissed' do
before do
allow(helper).to receive(:user_dismissed?).with(described_class::ADMIN_INTEGRATIONS_MOVED) { true }
end
it { is_expected.to be false }
end
end
describe '.render_flash_user_callout' do describe '.render_flash_user_callout' do
it 'renders the flash_user_callout partial' do it 'renders the flash_user_callout partial' do
expect(helper).to receive(:render) expect(helper).to receive(:render)
......
...@@ -67,6 +67,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::External do ...@@ -67,6 +67,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::External do
expect(step.break?).to be false expect(step.break?).to be false
end end
it 'logs the authorization' do
expect(Gitlab::AppLogger).to receive(:info).with(message: 'Pipeline authorized', project_id: project.id, user_id: user.id)
perform!
end
end end
context 'when validation return false' do context 'when validation return false' do
...@@ -86,6 +92,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::External do ...@@ -86,6 +92,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::External do
expect(step.break?).to be true expect(step.break?).to be true
end end
it 'logs the authorization' do
expect(Gitlab::AppLogger).to receive(:info).with(message: 'Pipeline not authorized', project_id: project.id, user_id: user.id)
perform!
end
end end
end end
......
...@@ -3,28 +3,39 @@ ...@@ -3,28 +3,39 @@
require 'spec_helper' require 'spec_helper'
describe OptionallySearch do describe OptionallySearch do
let(:model) do describe '.search' do
Class.new(ActiveRecord::Base) do let(:model) do
self.table_name = 'users' Class.new do
include OptionallySearch
include OptionallySearch end
end end
end
describe '.search' do
it 'raises NotImplementedError' do it 'raises NotImplementedError' do
expect { model.search('foo') }.to raise_error(NotImplementedError) expect { model.search('foo') }.to raise_error(NotImplementedError)
end end
end end
describe '.optionally_search' do describe '.optionally_search' do
let(:model) do
Class.new(ActiveRecord::Base) do
self.table_name = 'users'
include OptionallySearch
def self.search(query, **options)
[query, options]
end
end
end
context 'when a query is given' do context 'when a query is given' do
it 'delegates to the search method' do it 'delegates to the search method' do
expect(model) expect(model)
.to receive(:search) .to receive(:search)
.with('foo', {}) .with('foo', {})
.and_call_original
model.optionally_search('foo') expect(model.optionally_search('foo')).to eq(['foo', {}])
end end
end end
...@@ -33,8 +44,9 @@ describe OptionallySearch do ...@@ -33,8 +44,9 @@ describe OptionallySearch do
expect(model) expect(model)
.to receive(:search) .to receive(:search)
.with('foo', some_option: true) .with('foo', some_option: true)
.and_call_original
model.optionally_search('foo', some_option: true) expect(model.optionally_search('foo', some_option: true)).to eq(['foo', { some_option: true }])
end end
end end
......
...@@ -31,6 +31,24 @@ describe DiscordService do ...@@ -31,6 +31,24 @@ describe DiscordService do
WebMock.stub_request(:post, webhook_url) WebMock.stub_request(:post, webhook_url)
end end
it 'uses the right embed parameters' do
builder = Discordrb::Webhooks::Builder.new
allow_next_instance_of(Discordrb::Webhooks::Client) do |client|
allow(client).to receive(:execute).and_yield(builder)
end
subject.execute(sample_data)
expect(builder.to_json_hash[:embeds].first).to include(
description: start_with("#{user.name} pushed to branch [master](http://localhost/#{project.namespace.path}/#{project.path}/commits/master) of"),
author: hash_including(
icon_url: start_with('https://www.gravatar.com/avatar/'),
name: user.name
)
)
end
context 'DNS rebind to local address' do context 'DNS rebind to local address' do
before do before do
stub_dns(webhook_url, ip_address: '192.168.2.120') stub_dns(webhook_url, ip_address: '192.168.2.120')
......
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
require 'spec_helper' require 'spec_helper'
describe 'admin/application_settings/integrations.html.haml' do describe 'admin/application_settings/general.html.haml' do
let(:app_settings) { build(:application_setting) } let(:app_settings) { build(:application_setting) }
let(:user) { create(:admin) }
describe 'sourcegraph integration' do describe 'sourcegraph integration' do
let(:sourcegraph_flag) { true } let(:sourcegraph_flag) { true }
...@@ -11,6 +12,7 @@ describe 'admin/application_settings/integrations.html.haml' do ...@@ -11,6 +12,7 @@ describe 'admin/application_settings/integrations.html.haml' do
before do before do
assign(:application_setting, app_settings) assign(:application_setting, app_settings)
allow(Gitlab::Sourcegraph).to receive(:feature_available?).and_return(sourcegraph_flag) allow(Gitlab::Sourcegraph).to receive(:feature_available?).and_return(sourcegraph_flag)
allow(view).to receive(:current_user).and_return(user)
end end
context 'when sourcegraph feature is enabled' do context 'when sourcegraph feature is enabled' do
......
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment