summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-04 00:09:37 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-04 00:09:37 +0000
commite3bdfa1a13d7e6c92716324c78b5b20c07eeb7c6 (patch)
treee8776263096b027d32d4be5118cccc87b00de2bc
parentc1a50b8195f4e36fda9b233acbde57a449bcf6c3 (diff)
downloadgitlab-ce-e3bdfa1a13d7e6c92716324c78b5b20c07eeb7c6.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js12
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue21
-rw-r--r--app/assets/javascripts/clusters/components/knative_domain_editor.vue105
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js9
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js8
-rw-r--r--app/assets/javascripts/vue_shared/components/clone_dropdown.vue89
-rw-r--r--app/controllers/admin/integrations_controller.rb6
-rw-r--r--app/controllers/clusters/applications_controller.rb2
-rw-r--r--app/controllers/concerns/integrations_actions.rb31
-rw-r--r--app/controllers/groups/settings/integrations_controller.rb6
-rw-r--r--app/models/project_services/jira_service.rb4
-rw-r--r--app/models/project_services/pipelines_email_service.rb2
-rw-r--r--app/models/service.rb4
-rw-r--r--changelogs/unreleased/209035-consume-link-lfs-objects-projects.yml5
-rw-r--r--changelogs/unreleased/212654-can-we-remove-the-index-index_ci_builds_on_name_for_security_repor.yml5
-rw-r--r--changelogs/unreleased/remove-health-status-epic.yml (renamed from remove-health-status-epic.yml)0
-rw-r--r--db/migrate/20200330123739_remove_index_ci_builds_on_name_for_security_reports_values.rb22
-rw-r--r--db/post_migrate/20200319071702_consume_remaining_link_lfs_objects_projects_jobs.rb15
-rw-r--r--db/structure.sql4
-rw-r--r--locale/gitlab.pot12
-rw-r--r--package.json2
-rw-r--r--spec/controllers/admin/integrations_controller_spec.rb34
-rw-r--r--spec/controllers/groups/settings/integrations_controller_spec.rb35
-rw-r--r--spec/frontend/__mocks__/lodash/debounce.js11
-rw-r--r--spec/frontend/clusters/components/applications_spec.js41
-rw-r--r--spec/frontend/clusters/components/knative_domain_editor_spec.js36
-rw-r--r--spec/frontend/clusters/stores/clusters_store_spec.js2
-rw-r--r--spec/frontend/ide/components/branches/search_list_spec.js2
-rw-r--r--spec/frontend/ide/components/merge_requests/list_spec.js2
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js26
-rw-r--r--spec/frontend/test_setup.js2
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap115
-rw-r--r--spec/frontend/vue_shared/components/clone_dropdown_spec.js74
-rw-r--r--spec/models/service_spec.rb18
-rw-r--r--spec/services/metrics/dashboard/update_dashboard_service_spec.rb36
-rw-r--r--yarn.lock8
36 files changed, 618 insertions, 188 deletions
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index da34c5030f9..1b11ec355bb 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -259,7 +259,7 @@ export default class Clusters {
eventHub.$on('installApplication', this.installApplication);
eventHub.$on('updateApplication', data => this.updateApplication(data));
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
- eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
+ eventHub.$on('setKnativeDomain', data => this.setKnativeDomain(data));
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data));
eventHub.$on('setIngressModSecurityEnabled', data => this.setIngressModSecurityEnabled(data));
@@ -275,7 +275,7 @@ export default class Clusters {
eventHub.$off('installApplication', this.installApplication);
eventHub.$off('updateApplication', this.updateApplication);
eventHub.$off('saveKnativeDomain');
- eventHub.$off('setKnativeHostname');
+ eventHub.$off('setKnativeDomain');
eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication');
eventHub.$off('setIngressModSecurityEnabled');
@@ -521,10 +521,10 @@ export default class Clusters {
});
}
- setKnativeHostname(data) {
- const appId = data.id;
- this.store.updateAppProperty(appId, 'isEditingHostName', true);
- this.store.updateAppProperty(appId, 'hostname', data.hostname);
+ setKnativeDomain({ id: appId, domain, domainId }) {
+ this.store.updateAppProperty(appId, 'isEditingDomain', true);
+ this.store.updateAppProperty(appId, 'hostname', domain);
+ this.store.updateAppProperty(appId, 'pagesDomain', domainId ? { id: domainId, domain } : null);
}
setCrossplaneProviderStack(data) {
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 219825b1c01..723030c5b8b 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -240,16 +240,20 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
this.helmInstallIllustration = helmInstallIllustration;
},
methods: {
- saveKnativeDomain(hostname) {
+ saveKnativeDomain() {
eventHub.$emit('saveKnativeDomain', {
id: 'knative',
- params: { hostname },
+ params: {
+ hostname: this.applications.knative.hostname,
+ pages_domain_id: this.applications.knative.pagesDomain?.id,
+ },
});
},
- setKnativeHostname(hostname) {
- eventHub.$emit('setKnativeHostname', {
+ setKnativeDomain({ domainId, domain }) {
+ eventHub.$emit('setKnativeDomain', {
id: 'knative',
- hostname,
+ domainId,
+ domain,
});
},
setCrossplaneProviderStack(stack) {
@@ -591,7 +595,10 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
:request-reason="applications.knative.requestReason"
:installed="applications.knative.installed"
:install-failed="applications.knative.installFailed"
- :install-application-request-params="{ hostname: applications.knative.hostname }"
+ :install-application-request-params="{
+ hostname: applications.knative.hostname,
+ pages_domain_id: applications.knative.pagesDomain && applications.knative.pagesDomain.id,
+ }"
:installed-via="installedVia"
:uninstallable="applications.knative.uninstallable"
:uninstall-successful="applications.knative.uninstallSuccessful"
@@ -628,7 +635,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
:knative="knative"
:ingress-dns-help-path="ingressDnsHelpPath"
@save="saveKnativeDomain"
- @set="setKnativeHostname"
+ @set="setKnativeDomain"
/>
</div>
</application-row>
diff --git a/app/assets/javascripts/clusters/components/knative_domain_editor.vue b/app/assets/javascripts/clusters/components/knative_domain_editor.vue
index 30efbe2e0f7..8136704d13b 100644
--- a/app/assets/javascripts/clusters/components/knative_domain_editor.vue
+++ b/app/assets/javascripts/clusters/components/knative_domain_editor.vue
@@ -1,5 +1,12 @@
<script>
-import { GlLoadingIcon } from '@gitlab/ui';
+import {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlSprintf,
+} from '@gitlab/ui';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
import { __, s__ } from '~/locale';
@@ -13,6 +20,11 @@ export default {
LoadingButton,
ClipboardButton,
GlLoadingIcon,
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlSearchBoxByType,
+ GlSprintf,
},
props: {
knative: {
@@ -25,6 +37,11 @@ export default {
required: false,
},
},
+ data() {
+ return {
+ searchQuery: '',
+ };
+ },
computed: {
saveButtonDisabled() {
return [UNINSTALLING, UPDATING].includes(this.knative.status);
@@ -49,9 +66,22 @@ export default {
return this.knative.hostname;
},
set(hostname) {
- this.$emit('set', hostname);
+ this.selectCustomDomain(hostname);
},
},
+ domainDropdownText() {
+ return this.knativeHostname || s__('ClusterIntegration|Select existing domain or use new');
+ },
+ availableDomains() {
+ return this.knative.availableDomains || [];
+ },
+ filteredDomains() {
+ const query = this.searchQuery.toLowerCase();
+ return this.availableDomains.filter(({ domain }) => domain.toLowerCase().includes(query));
+ },
+ showDomainsDropdown() {
+ return this.availableDomains.length > 0;
+ },
},
watch: {
knativeUpdateSuccessful(updateSuccessful) {
@@ -60,6 +90,14 @@ export default {
}
},
},
+ methods: {
+ selectDomain({ id, domain }) {
+ this.$emit('set', { domain, domainId: id });
+ },
+ selectCustomDomain(domain) {
+ this.$emit('set', { domain, domainId: null });
+ },
+ },
};
</script>
@@ -72,22 +110,55 @@ export default {
{{ s__('ClusterIntegration|Something went wrong while updating Knative domain name.') }}
</div>
- <template>
- <div
- :class="{ 'col-md-6': knativeInstalled, 'col-12': !knativeInstalled }"
- class="form-group col-sm-12 mb-0"
+ <div
+ :class="{ 'col-md-6': knativeInstalled, 'col-12': !knativeInstalled }"
+ class="form-group col-sm-12 mb-0"
+ >
+ <label for="knative-domainname">
+ <strong>{{ s__('ClusterIntegration|Knative Domain Name:') }}</strong>
+ </label>
+
+ <gl-dropdown
+ v-if="showDomainsDropdown"
+ :text="domainDropdownText"
+ toggle-class="dropdown-menu-toggle"
+ class="w-100 mb-2"
>
- <label for="knative-domainname">
- <strong>{{ s__('ClusterIntegration|Knative Domain Name:') }}</strong>
- </label>
- <input
- id="knative-domainname"
- v-model="knativeHostname"
- type="text"
- class="form-control js-knative-domainname"
+ <gl-search-box-by-type
+ v-model.trim="searchQuery"
+ :placeholder="s__('ClusterIntegration|Search domains')"
+ class="m-2"
/>
- </div>
- </template>
+ <gl-dropdown-item
+ v-for="domain in filteredDomains"
+ :key="domain.id"
+ @click="selectDomain(domain)"
+ >
+ <span class="ml-1">{{ domain.domain }}</span>
+ </gl-dropdown-item>
+ <template v-if="searchQuery">
+ <gl-dropdown-divider />
+ <gl-dropdown-item key="custom-domain" @click="selectCustomDomain(searchQuery)">
+ <span class="ml-1">
+ <gl-sprintf :message="s__('ClusterIntegration|Use %{query}')">
+ <template #query>
+ <code>{{ searchQuery }}</code>
+ </template>
+ </gl-sprintf>
+ </span>
+ </gl-dropdown-item>
+ </template>
+ </gl-dropdown>
+
+ <input
+ v-else
+ id="knative-domainname"
+ v-model="knativeHostname"
+ type="text"
+ class="form-control js-knative-domainname"
+ />
+ </div>
+
<template v-if="knativeInstalled">
<div class="form-group col-sm-12 col-md-6 pl-md-0 mb-0 mt-3 mt-md-0">
<label for="knative-endpoint">
@@ -144,7 +215,7 @@ export default {
:loading="saving"
:disabled="saveButtonDisabled"
:label="saveButtonLabel"
- @click="$emit('save', knativeHostname)"
+ @click="$emit('save')"
/>
</template>
</div>
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index 8685e3decc5..b09fd6800b6 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -93,7 +93,7 @@ export default class ClusterStore {
...applicationInitialState,
title: s__('ClusterIntegration|Knative'),
hostname: null,
- isEditingHostName: false,
+ isEditingDomain: false,
externalIp: null,
externalHostname: null,
updateSuccessful: false,
@@ -234,7 +234,12 @@ export default class ClusterStore {
'jupyter',
);
} else if (appId === KNATIVE) {
- if (!this.state.applications.knative.isEditingHostName) {
+ if (serverAppEntry.available_domains) {
+ this.state.applications.knative.availableDomains = serverAppEntry.available_domains;
+ }
+ if (!this.state.applications.knative.isEditingDomain) {
+ this.state.applications.knative.pagesDomain =
+ serverAppEntry.pages_domain || this.state.applications.knative.pagesDomain;
this.state.applications.knative.hostname =
serverAppEntry.hostname || this.state.applications.knative.hostname;
}
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 1ff4f7bab97..a495d2040d3 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -318,3 +318,11 @@ export const setUrlParams = (params, url = window.location.href, clearParams = f
export function urlIsDifferent(url, compare = String(window.location)) {
return url !== compare;
}
+
+export function getHTTPProtocol(url) {
+ if (!url) {
+ return window.location.protocol.slice(0, -1);
+ }
+ const protocol = url.split(':');
+ return protocol.length > 1 ? protocol[0] : undefined;
+}
diff --git a/app/assets/javascripts/vue_shared/components/clone_dropdown.vue b/app/assets/javascripts/vue_shared/components/clone_dropdown.vue
new file mode 100644
index 00000000000..3b9b9f37f52
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/clone_dropdown.vue
@@ -0,0 +1,89 @@
+<script>
+import {
+ GlNewDropdown,
+ GlNewDropdownHeader,
+ GlFormInputGroup,
+ GlNewButton,
+ GlIcon,
+ GlTooltipDirective,
+} from '@gitlab/ui';
+import { __, sprintf } from '~/locale';
+import { getHTTPProtocol } from '~/lib/utils/url_utility';
+
+export default {
+ components: {
+ GlNewDropdown,
+ GlNewDropdownHeader,
+ GlFormInputGroup,
+ GlNewButton,
+ GlIcon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ sshLink: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ httpLink: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ httpLabel() {
+ const protocol = this.httpLink ? getHTTPProtocol(this.httpLink)?.toUpperCase() : '';
+ return sprintf(__('Clone with %{protocol}'), { protocol });
+ },
+ },
+ labels: {
+ defaultLabel: __('Clone'),
+ ssh: __('Clone with SSH'),
+ },
+ copyURLTooltip: __('Copy URL'),
+};
+</script>
+<template>
+ <gl-new-dropdown :text="$options.labels.defaultLabel" category="primary" variant="info">
+ <div class="pb-2 mx-1">
+ <template v-if="sshLink">
+ <gl-new-dropdown-header>{{ $options.labels.ssh }}</gl-new-dropdown-header>
+
+ <div class="mx-3">
+ <gl-form-input-group :value="sshLink" readonly select-on-click>
+ <template #append>
+ <gl-new-button
+ v-gl-tooltip.hover
+ :title="$options.copyURLTooltip"
+ :data-clipboard-text="sshLink"
+ >
+ <gl-icon name="copy-to-clipboard" :title="$options.copyURLTooltip" />
+ </gl-new-button>
+ </template>
+ </gl-form-input-group>
+ </div>
+ </template>
+
+ <template v-if="httpLink">
+ <gl-new-dropdown-header>{{ httpLabel }}</gl-new-dropdown-header>
+
+ <div class="mx-3">
+ <gl-form-input-group :value="httpLink" readonly select-on-click>
+ <template #append>
+ <gl-new-button
+ v-gl-tooltip.hover
+ :title="$options.copyURLTooltip"
+ :data-clipboard-text="httpLink"
+ >
+ <gl-icon name="copy-to-clipboard" :title="$options.copyURLTooltip" />
+ </gl-new-button>
+ </template>
+ </gl-form-input-group>
+ </div>
+ </template>
+ </div>
+ </gl-new-dropdown>
+</template>
diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb
index 0d79032233f..4f3be43d14d 100644
--- a/app/controllers/admin/integrations_controller.rb
+++ b/app/controllers/admin/integrations_controller.rb
@@ -5,6 +5,12 @@ class Admin::IntegrationsController < Admin::ApplicationController
private
+ def find_or_initialize_integration(name)
+ if name.in?(Service.available_services_names)
+ "#{name}_service".camelize.constantize.find_or_initialize_by(instance: true) # rubocop:disable CodeReuse/ActiveRecord
+ end
+ end
+
def integrations_enabled?
Feature.enabled?(:instance_level_integrations)
end
diff --git a/app/controllers/clusters/applications_controller.rb b/app/controllers/clusters/applications_controller.rb
index ba62cfeea7e..3ebd248c29e 100644
--- a/app/controllers/clusters/applications_controller.rb
+++ b/app/controllers/clusters/applications_controller.rb
@@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController
end
def cluster_application_params
- params.permit(:application, :hostname, :email, :stack, :modsecurity_enabled, :modsecurity_mode)
+ params.permit(:application, :hostname, :pages_domain_id, :email, :stack, :modsecurity_enabled, :modsecurity_mode)
end
def cluster_application_destroy_params
diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb
index ffb5d7a8086..4c998055a5d 100644
--- a/app/controllers/concerns/integrations_actions.rb
+++ b/app/controllers/concerns/integrations_actions.rb
@@ -37,11 +37,7 @@ module IntegrationsActions
end
def test
- if integration.can_test?
- render json: service_test_response, status: :ok
- else
- render json: {}, status: :not_found
- end
+ render json: {}, status: :ok
end
private
@@ -50,17 +46,11 @@ module IntegrationsActions
false
end
- # TODO: Use actual integrations on the group / instance level
- # To be completed in https://gitlab.com/groups/gitlab-org/-/epics/2430
- def project
- Project.first
- end
-
def integration
# Using instance variable `@service` still required as it's used in ServiceParams
# and app/views/shared/_service_settings.html.haml. Should be removed once
# those 2 are refactored to use `@integration`.
- @integration = @service ||= project.find_or_initialize_service(params[:id]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ @integration = @service ||= find_or_initialize_integration(params[:id]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def success_message
@@ -74,21 +64,4 @@ module IntegrationsActions
.as_json(only: integration.json_fields)
.merge(errors: integration.errors.as_json)
end
-
- def service_test_response
- unless integration.update(service_params[:service])
- return { error: true, message: _('Validations failed.'), service_response: integration.errors.full_messages.join(','), test_failed: false }
- end
-
- data = integration.test_data(project, current_user)
- outcome = integration.test(data)
-
- unless outcome[:success]
- return { error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
- end
-
- {}
- rescue Gitlab::HTTP::BlockedUrlError => e
- { error: true, message: _('Test failed.'), service_response: e.message, test_failed: true }
- end
end
diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb
index 43f8a7118d4..0ba030f26bc 100644
--- a/app/controllers/groups/settings/integrations_controller.rb
+++ b/app/controllers/groups/settings/integrations_controller.rb
@@ -9,6 +9,12 @@ module Groups
private
+ # TODO: Make this compatible with group-level integration
+ # https://gitlab.com/groups/gitlab-org/-/epics/2543
+ def find_or_initialize_integration(name)
+ Project.first.find_or_initialize_service(name)
+ end
+
def integrations_enabled?
Feature.enabled?(:group_level_integrations, group)
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 2766ba11c97..eaddac9cce3 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -201,8 +201,7 @@ class JiraService < IssueTrackerService
end
# Jira does not need test data.
- # We are requesting the project that belongs to the project key.
- def test_data(user = nil, project = nil)
+ def test_data(_, _)
nil
end
@@ -221,7 +220,6 @@ class JiraService < IssueTrackerService
def test_settings
return unless client_url.present?
- # Test settings by getting the project
jira_request { client.ServerInfo.all.attrs }
end
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
index c3ed958242b..b5e5afb6ea5 100644
--- a/app/models/project_services/pipelines_email_service.rb
+++ b/app/models/project_services/pipelines_email_service.rb
@@ -53,7 +53,7 @@ class PipelinesEmailService < Service
end
def can_test?
- project.ci_pipelines.any?
+ project&.ci_pipelines&.any?
end
def test_data(project, user)
diff --git a/app/models/service.rb b/app/models/service.rb
index 6bb3a94ce81..017c15468a2 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -184,8 +184,10 @@ class Service < ApplicationRecord
{ success: result.present?, result: result }
end
+ # Disable test for instance-level services.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/213138
def can_test?
- true
+ !instance?
end
# Provide convenient accessor methods
diff --git a/changelogs/unreleased/209035-consume-link-lfs-objects-projects.yml b/changelogs/unreleased/209035-consume-link-lfs-objects-projects.yml
new file mode 100644
index 00000000000..4fa4b88e37b
--- /dev/null
+++ b/changelogs/unreleased/209035-consume-link-lfs-objects-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Consume remaining LinkLFsObjectsProjects jobs
+merge_request: 27558
+author:
+type: other
diff --git a/changelogs/unreleased/212654-can-we-remove-the-index-index_ci_builds_on_name_for_security_repor.yml b/changelogs/unreleased/212654-can-we-remove-the-index-index_ci_builds_on_name_for_security_repor.yml
new file mode 100644
index 00000000000..b6d083a3b2e
--- /dev/null
+++ b/changelogs/unreleased/212654-can-we-remove-the-index-index_ci_builds_on_name_for_security_repor.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unnecessary index index_ci_builds_on_name_for_security_reports_values
+merge_request: 28224
+author:
+type: performance
diff --git a/remove-health-status-epic.yml b/changelogs/unreleased/remove-health-status-epic.yml
index c87d84fc5b3..c87d84fc5b3 100644
--- a/remove-health-status-epic.yml
+++ b/changelogs/unreleased/remove-health-status-epic.yml
diff --git a/db/migrate/20200330123739_remove_index_ci_builds_on_name_for_security_reports_values.rb b/db/migrate/20200330123739_remove_index_ci_builds_on_name_for_security_reports_values.rb
new file mode 100644
index 00000000000..7ab57183e18
--- /dev/null
+++ b/db/migrate/20200330123739_remove_index_ci_builds_on_name_for_security_reports_values.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class RemoveIndexCiBuildsOnNameForSecurityReportsValues < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_ci_builds_on_name_for_security_reports_values'
+
+ def up
+ remove_concurrent_index_by_name :ci_builds, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :ci_builds,
+ :name,
+ name: INDEX_NAME,
+ where: "((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('license_scanning'::character varying)::text]))"
+ end
+end
diff --git a/db/post_migrate/20200319071702_consume_remaining_link_lfs_objects_projects_jobs.rb b/db/post_migrate/20200319071702_consume_remaining_link_lfs_objects_projects_jobs.rb
new file mode 100644
index 00000000000..63fa0234a5b
--- /dev/null
+++ b/db/post_migrate/20200319071702_consume_remaining_link_lfs_objects_projects_jobs.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class ConsumeRemainingLinkLfsObjectsProjectsJobs < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ Gitlab::BackgroundMigration.steal('LinkLfsObjectsProjects')
+ end
+
+ def down
+ # no-op as there is no need to do anything if this gets rolled back
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 94d98c68f48..25855aeadc0 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -8672,8 +8672,6 @@ CREATE INDEX index_ci_builds_on_commit_id_and_type_and_ref ON public.ci_builds U
CREATE INDEX index_ci_builds_on_name_and_security_type_eq_ci_build ON public.ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
-CREATE INDEX index_ci_builds_on_name_for_security_reports_values ON public.ci_builds USING btree (name) WHERE ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('license_scanning'::character varying)::text]));
-
CREATE INDEX index_ci_builds_on_project_id_and_id ON public.ci_builds USING btree (project_id, id);
CREATE INDEX index_ci_builds_on_project_id_and_name_and_ref ON public.ci_builds USING btree (project_id, name, ref) WHERE (((type)::text = 'Ci::Build'::text) AND ((status)::text = 'success'::text) AND ((retried = false) OR (retried IS NULL)));
@@ -12902,6 +12900,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200318164448
20200318165448
20200318175008
+20200319071702
20200319123041
20200319203901
20200320112455
@@ -12927,6 +12926,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200326145443
20200330074719
20200330121000
+20200330123739
20200330132913
20200331220930
\.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c385e1e1a91..983a142590f 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -4034,6 +4034,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with %{protocol}"
+msgstr ""
+
msgid "Clone with KRB5"
msgstr ""
@@ -4691,6 +4694,9 @@ msgstr ""
msgid "ClusterIntegration|Search VPCs"
msgstr ""
+msgid "ClusterIntegration|Search domains"
+msgstr ""
+
msgid "ClusterIntegration|Search instance types"
msgstr ""
@@ -4748,6 +4754,9 @@ msgstr ""
msgid "ClusterIntegration|Select a zone to choose a network"
msgstr ""
+msgid "ClusterIntegration|Select existing domain or use new"
+msgstr ""
+
msgid "ClusterIntegration|Select machine type"
msgstr ""
@@ -4868,6 +4877,9 @@ msgstr ""
msgid "ClusterIntegration|Update failed. Please check the logs and try again."
msgstr ""
+msgid "ClusterIntegration|Use %{query}"
+msgstr ""
+
msgid "ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster."
msgstr ""
diff --git a/package.json b/package.json
index 350d63d9296..56356002bd3 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"@babel/preset-env": "^7.8.4",
"@gitlab/at.js": "1.5.5",
"@gitlab/svgs": "1.117.0",
- "@gitlab/ui": "11.0.0",
+ "@gitlab/ui": "11.0.1",
"@gitlab/visual-review-tools": "1.5.1",
"@sentry/browser": "^5.10.2",
"@sourcegraph/code-host-integration": "0.0.34",
diff --git a/spec/controllers/admin/integrations_controller_spec.rb b/spec/controllers/admin/integrations_controller_spec.rb
index 50748918893..8e48ecddd0f 100644
--- a/spec/controllers/admin/integrations_controller_spec.rb
+++ b/spec/controllers/admin/integrations_controller_spec.rb
@@ -3,7 +3,6 @@
require 'spec_helper'
describe Admin::IntegrationsController do
- let_it_be(:project) { create(:project) }
let(:admin) { create(:admin) }
before do
@@ -34,7 +33,7 @@ describe Admin::IntegrationsController do
end
describe '#update' do
- let(:integration) { create(:jira_service, project: project) }
+ let(:integration) { create(:jira_service, :instance) }
before do
put :update, params: { id: integration.class.to_param, service: { url: url } }
@@ -52,34 +51,9 @@ describe Admin::IntegrationsController do
context 'invalid params' do
let(:url) { 'https://jira.localhost' }
- it 'does not update the integration' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template(:edit)
- expect(integration.reload.url).not_to eq(url)
- end
- end
- end
-
- describe '#test' do
- context 'testable' do
- let(:integration) { create(:jira_service, project: project) }
-
- it 'returns ok' do
- allow_any_instance_of(integration.class).to receive(:test) { { success: true } }
-
- put :test, params: { id: integration.class.to_param }
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- context 'not testable' do
- let(:integration) { create(:alerts_service, project: project) }
-
- it 'returns not found' do
- put :test, params: { id: integration.class.to_param }
-
- expect(response).to have_gitlab_http_status(:not_found)
+ it 'updates the integration' do
+ expect(response).to have_gitlab_http_status(:found)
+ expect(integration.reload.url).to eq(url)
end
end
end
diff --git a/spec/controllers/groups/settings/integrations_controller_spec.rb b/spec/controllers/groups/settings/integrations_controller_spec.rb
index bbf215a4bb9..eee65476dab 100644
--- a/spec/controllers/groups/settings/integrations_controller_spec.rb
+++ b/spec/controllers/groups/settings/integrations_controller_spec.rb
@@ -67,40 +67,11 @@ describe Groups::Settings::IntegrationsController do
end
context 'invalid params' do
- let(:url) { 'ftp://jira.localhost' }
+ let(:url) { 'https://jira.localhost' }
it 'does not update the integration' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template(:edit)
- expect(integration.reload.url).not_to eq(url)
- end
- end
- end
-
- describe '#test' do
- context 'testable' do
- let(:integration) { create(:jira_service, project: project) }
-
- before do
- group.add_owner(user)
- end
-
- it 'returns ok' do
- allow_any_instance_of(integration.class).to receive(:test) { { success: true } }
-
- put :test, params: { group_id: group, id: integration.class.to_param }
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- context 'not testable' do
- let(:integration) { create(:alerts_service, project: project) }
-
- it 'returns not found' do
- put :test, params: { group_id: group, id: integration.class.to_param }
-
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(:found)
+ expect(integration.reload.url).to eq(url)
end
end
end
diff --git a/spec/frontend/__mocks__/lodash/debounce.js b/spec/frontend/__mocks__/lodash/debounce.js
new file mode 100644
index 00000000000..97fdb39097a
--- /dev/null
+++ b/spec/frontend/__mocks__/lodash/debounce.js
@@ -0,0 +1,11 @@
+// `lodash/debounce` has a non-trivial implementation which can lead to
+// [flaky spec errors][1]. This mock simply makes `debounce` calls synchronous.
+//
+// In the future we could enhance this by injecting some test values in
+// the function passed to it. See [this issue][2] for more information.
+//
+// [1]: https://gitlab.com/gitlab-org/gitlab/-/issues/212532
+// [2]: https://gitlab.com/gitlab-org/gitlab/-/issues/213378
+// Further reference: https://github.com/facebook/jest/issues/3465
+
+export default fn => fn;
diff --git a/spec/frontend/clusters/components/applications_spec.js b/spec/frontend/clusters/components/applications_spec.js
index db5d5c84820..782e5215ad8 100644
--- a/spec/frontend/clusters/components/applications_spec.js
+++ b/spec/frontend/clusters/components/applications_spec.js
@@ -400,6 +400,10 @@ describe('Applications', () => {
});
describe('Knative application', () => {
+ const availableDomain = {
+ id: 4,
+ domain: 'newhostname.com',
+ };
const propsData = {
applications: {
...APPLICATIONS_MOCK_STATE,
@@ -409,10 +413,11 @@ describe('Applications', () => {
status: 'installed',
externalIp: '1.1.1.1',
installed: true,
+ availableDomains: [availableDomain],
+ pagesDomain: null,
},
},
};
- const newHostname = 'newhostname.com';
let wrapper;
let knativeDomainEditor;
@@ -428,20 +433,44 @@ describe('Applications', () => {
});
it('emits saveKnativeDomain event when knative domain editor emits save event', () => {
- knativeDomainEditor.vm.$emit('save', newHostname);
+ propsData.applications.knative.hostname = availableDomain.domain;
+ propsData.applications.knative.pagesDomain = availableDomain;
+ knativeDomainEditor.vm.$emit('save');
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', {
+ id: 'knative',
+ params: {
+ hostname: availableDomain.domain,
+ pages_domain_id: availableDomain.id,
+ },
+ });
+ });
+
+ it('emits saveKnativeDomain event when knative domain editor emits save event with custom domain', () => {
+ const newHostName = 'someothernewhostname.com';
+ propsData.applications.knative.hostname = newHostName;
+ propsData.applications.knative.pagesDomain = null;
+ knativeDomainEditor.vm.$emit('save');
expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', {
id: 'knative',
- params: { hostname: newHostname },
+ params: {
+ hostname: newHostName,
+ pages_domain_id: undefined,
+ },
});
});
it('emits setKnativeHostname event when knative domain editor emits change event', () => {
- wrapper.find(KnativeDomainEditor).vm.$emit('set', newHostname);
+ wrapper.find(KnativeDomainEditor).vm.$emit('set', {
+ domain: availableDomain.domain,
+ domainId: availableDomain.id,
+ });
- expect(eventHub.$emit).toHaveBeenCalledWith('setKnativeHostname', {
+ expect(eventHub.$emit).toHaveBeenCalledWith('setKnativeDomain', {
id: 'knative',
- hostname: newHostname,
+ domain: availableDomain.domain,
+ domainId: availableDomain.id,
});
});
});
diff --git a/spec/frontend/clusters/components/knative_domain_editor_spec.js b/spec/frontend/clusters/components/knative_domain_editor_spec.js
index 6514d883c0d..2de04f7da1f 100644
--- a/spec/frontend/clusters/components/knative_domain_editor_spec.js
+++ b/spec/frontend/clusters/components/knative_domain_editor_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import { GlDropdownItem } from '@gitlab/ui';
import KnativeDomainEditor from '~/clusters/components/knative_domain_editor.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { APPLICATION_STATUS } from '~/clusters/constants';
@@ -80,7 +81,7 @@ describe('KnativeDomainEditor', () => {
it('triggers save event and pass current knative hostname', () => {
wrapper.find(LoadingButton).vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('save')[0]).toEqual([knative.hostname]);
+ expect(wrapper.emitted('save').length).toEqual(1);
});
});
});
@@ -104,14 +105,43 @@ describe('KnativeDomainEditor', () => {
describe('when knative domain name input changes', () => {
it('emits "set" event with updated domain name', () => {
- createComponent({ knative });
+ const newDomain = {
+ id: 4,
+ domain: 'newhostname.com',
+ };
+
+ createComponent({ knative: { ...knative, availableDomains: [newDomain] } });
+ jest.spyOn(wrapper.vm, 'selectDomain');
+
+ wrapper.find(GlDropdownItem).vm.$emit('click');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.selectDomain).toHaveBeenCalledWith(newDomain);
+ expect(wrapper.emitted('set')[0]).toEqual([
+ {
+ domain: newDomain.domain,
+ domainId: newDomain.id,
+ },
+ ]);
+ });
+ });
+
+ it('emits "set" event with updated custom domain name', () => {
const newHostname = 'newhostname.com';
+ createComponent({ knative });
+ jest.spyOn(wrapper.vm, 'selectCustomDomain');
+
wrapper.setData({ knativeHostname: newHostname });
return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('set')[0]).toEqual([newHostname]);
+ expect(wrapper.vm.selectCustomDomain).toHaveBeenCalledWith(newHostname);
+ expect(wrapper.emitted('set')[0]).toEqual([
+ {
+ domain: newHostname,
+ domainId: null,
+ },
+ ]);
});
});
});
diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js
index 0207fda84c4..9fafc688af9 100644
--- a/spec/frontend/clusters/stores/clusters_store_spec.js
+++ b/spec/frontend/clusters/stores/clusters_store_spec.js
@@ -140,7 +140,7 @@ describe('Clusters Store', () => {
statusReason: mockResponseData.applications[5].status_reason,
requestReason: null,
hostname: null,
- isEditingHostName: false,
+ isEditingDomain: false,
externalIp: null,
externalHostname: null,
installed: false,
diff --git a/spec/frontend/ide/components/branches/search_list_spec.js b/spec/frontend/ide/components/branches/search_list_spec.js
index 826d51b24f1..fe142d70698 100644
--- a/spec/frontend/ide/components/branches/search_list_spec.js
+++ b/spec/frontend/ide/components/branches/search_list_spec.js
@@ -9,8 +9,6 @@ import { branches } from '../../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
-jest.mock('lodash/debounce', () => jest.fn);
-
describe('IDE branches search list', () => {
let wrapper;
const fetchBranchesMock = jest.fn();
diff --git a/spec/frontend/ide/components/merge_requests/list_spec.js b/spec/frontend/ide/components/merge_requests/list_spec.js
index ae94ee4efa7..e2c6ac49e07 100644
--- a/spec/frontend/ide/components/merge_requests/list_spec.js
+++ b/spec/frontend/ide/components/merge_requests/list_spec.js
@@ -134,9 +134,7 @@ describe('IDE merge requests list', () => {
createComponent(defaultStateWithMergeRequests);
const input = findTokenedInput();
input.vm.$emit('input', 'something');
- fetchMergeRequestsMock.mockClear();
- jest.runAllTimers();
return wrapper.vm.$nextTick().then(() => {
expect(fetchMergeRequestsMock).toHaveBeenCalledWith(
expect.any(Object),
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index d0abf2c03a9..4960895890f 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -485,4 +485,30 @@ describe('URL utility', () => {
);
});
});
+
+ describe('getHTTPProtocol', () => {
+ const httpProtocol = 'http:';
+ const httpsProtocol = 'https:';
+
+ it.each([[httpProtocol], [httpsProtocol]])(
+ 'when no url passed, returns correct protocol for %i from window location',
+ protocol => {
+ setWindowLocation({
+ protocol,
+ });
+ expect(urlUtils.getHTTPProtocol()).toBe(protocol.slice(0, -1));
+ },
+ );
+
+ it.each`
+ url | expectation
+ ${'not-a-url'} | ${undefined}
+ ${'wss://example.com'} | ${'wss'}
+ ${'https://foo.bar'} | ${'https'}
+ ${'http://foo.bar'} | ${'http'}
+ ${'http://foo.bar:8080'} | ${'http'}
+ `('returns correct protocol for $url', ({ url, expectation }) => {
+ expect(urlUtils.getHTTPProtocol(url)).toBe(expectation);
+ });
+ });
});
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index fff76f158dd..e216f49630f 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -20,7 +20,7 @@ afterEach(() =>
// give Promises a bit more time so they fail the right test
new Promise(setImmediate).then(() => {
// wait for pending setTimeout()s
- jest.runAllTimers();
+ jest.runOnlyPendingTimers();
}),
);
diff --git a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
new file mode 100644
index 00000000000..d837c793784
--- /dev/null
+++ b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
@@ -0,0 +1,115 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
+<gl-new-dropdown-stub
+ category="primary"
+ headertext=""
+ size="medium"
+ text="Clone"
+ variant="info"
+>
+ <div
+ class="pb-2 mx-1"
+ >
+ <gl-new-dropdown-header-stub>
+ Clone with SSH
+ </gl-new-dropdown-header-stub>
+
+ <div
+ class="mx-3"
+ >
+ <div
+ readonly="readonly"
+ >
+ <b-input-group-stub
+ tag="div"
+ >
+ <b-input-group-prepend-stub
+ tag="div"
+ >
+
+ <!---->
+ </b-input-group-prepend-stub>
+
+ <b-form-input-stub
+ class="gl-form-input"
+ debounce="0"
+ readonly="true"
+ type="text"
+ value="ssh://foo.bar"
+ />
+
+ <b-input-group-append-stub
+ tag="div"
+ >
+ <gl-new-button-stub
+ category="tertiary"
+ data-clipboard-text="ssh://foo.bar"
+ icon=""
+ size="medium"
+ title="Copy URL"
+ variant="default"
+ >
+ <gl-icon-stub
+ name="copy-to-clipboard"
+ size="16"
+ title="Copy URL"
+ />
+ </gl-new-button-stub>
+ </b-input-group-append-stub>
+ </b-input-group-stub>
+ </div>
+ </div>
+
+ <gl-new-dropdown-header-stub>
+ Clone with HTTP
+ </gl-new-dropdown-header-stub>
+
+ <div
+ class="mx-3"
+ >
+ <div
+ readonly="readonly"
+ >
+ <b-input-group-stub
+ tag="div"
+ >
+ <b-input-group-prepend-stub
+ tag="div"
+ >
+
+ <!---->
+ </b-input-group-prepend-stub>
+
+ <b-form-input-stub
+ class="gl-form-input"
+ debounce="0"
+ readonly="true"
+ type="text"
+ value="http://foo.bar"
+ />
+
+ <b-input-group-append-stub
+ tag="div"
+ >
+ <gl-new-button-stub
+ category="tertiary"
+ data-clipboard-text="http://foo.bar"
+ icon=""
+ size="medium"
+ title="Copy URL"
+ variant="default"
+ >
+ <gl-icon-stub
+ name="copy-to-clipboard"
+ size="16"
+ title="Copy URL"
+ />
+ </gl-new-button-stub>
+ </b-input-group-append-stub>
+ </b-input-group-stub>
+ </div>
+ </div>
+ </div>
+</gl-new-dropdown-stub>
+`;
diff --git a/spec/frontend/vue_shared/components/clone_dropdown_spec.js b/spec/frontend/vue_shared/components/clone_dropdown_spec.js
new file mode 100644
index 00000000000..38e0cadfe83
--- /dev/null
+++ b/spec/frontend/vue_shared/components/clone_dropdown_spec.js
@@ -0,0 +1,74 @@
+import CloneDropdown from '~/vue_shared/components/clone_dropdown.vue';
+import { shallowMount } from '@vue/test-utils';
+import { GlFormInputGroup, GlNewDropdownHeader } from '@gitlab/ui';
+
+describe('Clone Dropdown Button', () => {
+ let wrapper;
+ const sshLink = 'ssh://foo.bar';
+ const httpLink = 'http://foo.bar';
+ const httpsLink = 'https://foo.bar';
+ const defaultPropsData = {
+ sshLink,
+ httpLink,
+ };
+
+ const createComponent = (propsData = defaultPropsData) => {
+ wrapper = shallowMount(CloneDropdown, {
+ propsData,
+ stubs: {
+ 'gl-form-input-group': GlFormInputGroup,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('rendering', () => {
+ it('matches the snapshot', () => {
+ createComponent();
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ it.each`
+ name | index | value
+ ${'SSH'} | ${0} | ${sshLink}
+ ${'HTTP'} | ${1} | ${httpLink}
+ `('renders correct link and a copy-button for $name', ({ index, value }) => {
+ createComponent();
+ const group = wrapper.findAll(GlFormInputGroup).at(index);
+ expect(group.props('value')).toBe(value);
+ expect(group.contains(GlFormInputGroup)).toBe(true);
+ });
+
+ it.each`
+ name | value
+ ${'sshLink'} | ${sshLink}
+ ${'httpLink'} | ${httpLink}
+ `('does not fail if only $name is set', ({ name, value }) => {
+ createComponent({ [name]: value });
+
+ expect(wrapper.find(GlFormInputGroup).props('value')).toBe(value);
+ expect(wrapper.findAll(GlNewDropdownHeader).length).toBe(1);
+ });
+ });
+
+ describe('functionality', () => {
+ it.each`
+ name | value
+ ${'sshLink'} | ${null}
+ ${'httpLink'} | ${null}
+ `('allows null values for the props', ({ name, value }) => {
+ createComponent({ ...defaultPropsData, [name]: value });
+
+ expect(wrapper.findAll(GlNewDropdownHeader).length).toBe(1);
+ });
+
+ it('correctly calculates httpLabel for HTTPS protocol', () => {
+ createComponent({ httpLink: httpsLink });
+ expect(wrapper.find(GlNewDropdownHeader).text()).toContain('HTTPS');
+ });
+ });
+});
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index d1a4924c07e..cb8122b6573 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -104,21 +104,29 @@ describe Service do
describe "Test Button" do
describe '#can_test?' do
+ subject { service.can_test? }
+
let(:service) { create(:service, project: project) }
context 'when repository is not empty' do
let(:project) { create(:project, :repository) }
- it 'returns true' do
- expect(service.can_test?).to be true
- end
+ it { is_expected.to be true }
end
context 'when repository is empty' do
let(:project) { create(:project) }
- it 'returns true' do
- expect(service.can_test?).to be true
+ it { is_expected.to be true }
+ end
+
+ context 'when instance-level service' do
+ Service.available_services_types.each do |service_type|
+ let(:service) do
+ service_type.constantize.new(instance: true)
+ end
+
+ it { is_expected.to be_falsey }
end
end
end
diff --git a/spec/services/metrics/dashboard/update_dashboard_service_spec.rb b/spec/services/metrics/dashboard/update_dashboard_service_spec.rb
index 72d36959bce..fce027688d9 100644
--- a/spec/services/metrics/dashboard/update_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/update_dashboard_service_spec.rb
@@ -27,14 +27,7 @@ describe Metrics::Dashboard::UpdateDashboardService, :use_clean_rails_memory_sto
end
context 'user does not have push right to repository' do
- it 'returns an appropriate message and status code', :aggregate_failures do
- result = service_call
-
- expect(result.keys).to contain_exactly(:message, :http_status, :status, :last_step)
- expect(result[:status]).to eq(:error)
- expect(result[:http_status]).to eq(:forbidden)
- expect(result[:message]).to eq("You are not allowed to push into this branch. Create another branch or open a merge request.")
- end
+ it_behaves_like 'misconfigured dashboard service response with stepable', :forbidden, 'You are not allowed to push into this branch. Create another branch or open a merge request.'
end
context 'with rights to push to the repository' do
@@ -46,27 +39,13 @@ describe Metrics::Dashboard::UpdateDashboardService, :use_clean_rails_memory_sto
context 'with a yml extension' do
let(:file_name) { 'config/prometheus/../database.yml' }
- it 'returns an appropriate message and status code', :aggregate_failures do
- result = service_call
-
- expect(result.keys).to contain_exactly(:message, :http_status, :status, :last_step)
- expect(result[:status]).to eq(:error)
- expect(result[:http_status]).to eq(:bad_request)
- expect(result[:message]).to eq("A file with this name doesn't exist")
- end
+ it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, "A file with this name doesn't exist"
end
context 'without a yml extension' do
let(:file_name) { '../../..../etc/passwd' }
- it 'returns an appropriate message and status code', :aggregate_failures do
- result = service_call
-
- expect(result.keys).to contain_exactly(:message, :http_status, :status, :last_step)
- expect(result[:status]).to eq(:error)
- expect(result[:http_status]).to eq(:bad_request)
- expect(result[:message]).to eq("The file name should have a .yml extension")
- end
+ it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, 'The file name should have a .yml extension'
end
end
@@ -81,14 +60,7 @@ describe Metrics::Dashboard::UpdateDashboardService, :use_clean_rails_memory_sto
project.repository.add_branch(user, branch, 'master')
end
- it 'returns an appropriate message and status code', :aggregate_failures do
- result = service_call
-
- expect(result.keys).to contain_exactly(:message, :http_status, :status, :last_step)
- expect(result[:status]).to eq(:error)
- expect(result[:http_status]).to eq(:bad_request)
- expect(result[:message]).to eq("There was an error updating the dashboard, branch named: existing_branch already exists.")
- end
+ it_behaves_like 'misconfigured dashboard service response with stepable', :bad_request, 'There was an error updating the dashboard, branch named: existing_branch already exists.'
end
context 'Files::UpdateService success' do
diff --git a/yarn.lock b/yarn.lock
index 2a75187b06a..79a8892a499 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -786,10 +786,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.117.0.tgz#05239ddcf529c62ca29e1ec1a25a7e24efb98207"
integrity sha512-dGy/VWuRAFCTZX3Yqu1+RnAHTSUWafteIk/RMfUCN9B/EMbYzjhYsNy0NLVoZ23Rj/KGv1bUGHvyQCoPP6VzpA==
-"@gitlab/ui@11.0.0":
- version "11.0.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-11.0.0.tgz#2dc6c4e92201911b80c1c24ff4ea6c5247810451"
- integrity sha512-Cu50RQMbNGxEfMIxr3iv6i09hNs3deRS01CnkxNEdxmmokyKr86a2TItHwrAKyacjkb8IEfkn0Q/yoBMTpfPAw==
+"@gitlab/ui@11.0.1":
+ version "11.0.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-11.0.1.tgz#7d9fdb823590c72c232b7dee06b86c3e8766ba28"
+ integrity sha512-JlZULrpmm2jELsVHfcMpE0uiam+hA+5tL4+xZxiHoG+i9UlTQCAteMHOgJVT7pQYvjPAoSnw9XzTATEEcHVcOw==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"