diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-23 00:10:00 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-23 00:10:00 +0300 |
commit | 22e60f1c61443a7efd8a2334f61556d19d6630be (patch) | |
tree | 57ffc5cda535d1d664f84689c1d42855334fb410 | |
parent | db061f44328ca45f713eaf22d92aae8e76148fda (diff) |
Add latest changes from gitlab-org/gitlab@master
39 files changed, 251 insertions, 214 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 344a880760e..bd0f9184cd6 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -613,18 +613,6 @@ Style/StringLiteralsInInterpolation: Style/SymbolProc: Enabled: false -# Offense count: 7 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, AllowSafeAssignment. -# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex -Style/TernaryParentheses: - Exclude: - - 'app/finders/projects_finder.rb' - - 'app/helpers/namespaces_helper.rb' - - 'lib/gitlab/ci/build/artifacts/metadata/entry.rb' - - 'spec/requests/api/pipeline_schedules_spec.rb' - - 'spec/support/capybara.rb' - # Offense count: 99 # Cop supports --auto-correct. Style/UnneededInterpolation: diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue index 10c855675db..faecd77d598 100644 --- a/app/assets/javascripts/boards/components/board_column.vue +++ b/app/assets/javascripts/boards/components/board_column.vue @@ -353,7 +353,7 @@ export default { v-if="isSettingsShown" ref="settingsBtn" :aria-label="__(`List settings`)" - class="no-drag rounded-right" + class="no-drag rounded-right js-board-settings-button" title="List settings" type="button" @click="openSidebarSettings" diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js index dcecfe5e1bb..40f79a44b51 100644 --- a/app/assets/javascripts/boards/constants.js +++ b/app/assets/javascripts/boards/constants.js @@ -8,6 +8,8 @@ export const ListType = { blank: 'blank', }; +export const inactiveListId = 0; + export default { ListType, }; diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index 49485f4d575..5d567d9b169 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -216,7 +216,12 @@ export default { if (entry.type === 'blob') { if (tempFile) { + // Since we only support one list of file changes, it's safe to just remove from both + // changed and staged. Otherwise, we'd need to somehow evaluate the difference between + // changed and HEAD. + // https://gitlab.com/gitlab-org/create-stage/-/issues/12669 state.changedFiles = state.changedFiles.filter(f => f.path !== path); + state.stagedFiles = state.stagedFiles.filter(f => f.path !== path); } else { state.changedFiles = state.changedFiles.concat(entry); } diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 85306023d7d..571da30675d 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -14,7 +14,7 @@ import { GlModalDirective, GlTooltipDirective, } from '@gitlab/ui'; -import PanelType from './panel_type_with_alerts.vue'; +import DashboardPanel from './dashboard_panel.vue'; import { s__ } from '~/locale'; import createFlash from '~/flash'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; @@ -37,7 +37,7 @@ import { defaultTimeRange, timeRanges } from '~/vue_shared/constants'; export default { components: { VueDraggable, - PanelType, + DashboardPanel, Icon, GlDeprecatedButton, GlDropdown, @@ -558,7 +558,7 @@ export default { > <div v-for="(graphData, graphIndex) in groupData.panels" - :key="`panel-type-${graphIndex}`" + :key="`dashboard-panel-${graphIndex}`" class="col-12 col-lg-6 px-2 mb-2 draggable" :class="{ 'draggable-enabled': isRearrangingPanels }" > @@ -573,7 +573,7 @@ export default { </a> </div> - <panel-type + <dashboard-panel :clipboard-text="generateLink(groupData.group, graphData.title, graphData.y_label)" :graph-data="graphData" :alerts-endpoint="alertsEndpoint" diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/dashboard_panel.vue index eed41b94cd3..7b09c78aae2 100644 --- a/app/assets/javascripts/monitoring/components/panel_type.vue +++ b/app/assets/javascripts/monitoring/components/dashboard_panel.vue @@ -26,6 +26,7 @@ import MonitorBarChart from './charts/bar.vue'; import MonitorStackedColumnChart from './charts/stacked_column.vue'; import TrackEventDirective from '~/vue_shared/directives/track_event'; +import AlertWidget from './alert_widget.vue'; import { timeRangeToUrl, downloadCSVOptions, generateLinkToChartOptions } from '../utils'; const events = { @@ -40,6 +41,7 @@ export default { MonitorColumnChart, MonitorBarChart, MonitorStackedColumnChart, + AlertWidget, GlIcon, GlLoadingIcon, GlTooltip, @@ -78,11 +80,22 @@ export default { required: false, default: 'monitoringDashboard', }, + alertsEndpoint: { + type: String, + required: false, + default: null, + }, + prometheusAlertsAvailable: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { showTitleTooltip: false, zoomedTimeRange: null, + allAlerts: {}, }; }, computed: { @@ -104,14 +117,13 @@ export default { timeRange(state) { return state[this.namespace].timeRange; }, + metricsSavedToDb(state, getters) { + return getters[`${this.namespace}/metricsSavedToDb`]; + }, }), title() { return this.graphData.title || ''; }, - alertWidgetAvailable() { - // This method is extended by ee functionality - return false; - }, graphDataHasResult() { return ( this.graphData.metrics && @@ -165,6 +177,18 @@ export default { editCustomMetricLinkText() { return n__('Metrics|Edit metric', 'Metrics|Edit metrics', this.graphData.metrics.length); }, + hasMetricsInDb() { + const { metrics = [] } = this.graphData; + return metrics.some(({ metricId }) => this.metricsSavedToDb.includes(metricId)); + }, + alertWidgetAvailable() { + return ( + this.prometheusAlertsAvailable && + this.alertsEndpoint && + this.graphData && + this.hasMetricsInDb + ); + }, }, mounted() { this.refreshTitleTooltip(); @@ -200,6 +224,13 @@ export default { this.zoomedTimeRange = { start, end }; this.$emit(events.timeRangeZoom, { start, end }); }, + setAlerts(alertPath, alertAttributes) { + if (alertAttributes) { + this.$set(this.allAlerts, alertPath, alertAttributes); + } else { + this.$delete(this.allAlerts, alertPath); + } + }, }, panelTypes, }; diff --git a/app/assets/javascripts/monitoring/components/embeds/metric_embed.vue b/app/assets/javascripts/monitoring/components/embeds/metric_embed.vue index 129de6cc2f6..1557a49137e 100644 --- a/app/assets/javascripts/monitoring/components/embeds/metric_embed.vue +++ b/app/assets/javascripts/monitoring/components/embeds/metric_embed.vue @@ -1,6 +1,6 @@ <script> import { mapState, mapActions } from 'vuex'; -import PanelType from '~/monitoring/components/panel_type_with_alerts.vue'; +import DashboardPanel from '~/monitoring/components/dashboard_panel.vue'; import { convertToFixedRange } from '~/lib/utils/datetime_range'; import { defaultTimeRange } from '~/vue_shared/constants'; import { timeRangeFromUrl, removeTimeRangeParams } from '../../utils'; @@ -10,7 +10,7 @@ let sidebarMutationObserver; export default { components: { - PanelType, + DashboardPanel, }, props: { containerClass: { @@ -113,9 +113,9 @@ export default { </script> <template> <div class="metrics-embed p-0 d-flex flex-wrap" :class="embedClass"> - <panel-type + <dashboard-panel v-for="(graphData, graphIndex) in charts" - :key="`panel-type-${graphIndex}`" + :key="`dashboard-panel-${graphIndex}`" :class="panelClass" :graph-data="graphData" :group-id="dashboardUrl" diff --git a/app/assets/javascripts/monitoring/components/panel_type_with_alerts.vue b/app/assets/javascripts/monitoring/components/panel_type_with_alerts.vue deleted file mode 100644 index ca81242af2e..00000000000 --- a/app/assets/javascripts/monitoring/components/panel_type_with_alerts.vue +++ /dev/null @@ -1,55 +0,0 @@ -<script> -import { mapGetters } from 'vuex'; -import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue'; -import CePanelType from '~/monitoring/components/panel_type.vue'; -import AlertWidget from './alert_widget.vue'; - -export default { - components: { - AlertWidget, - CustomMetricsFormFields, - }, - extends: CePanelType, - props: { - alertsEndpoint: { - type: String, - required: false, - default: null, - }, - prometheusAlertsAvailable: { - type: Boolean, - required: false, - default: false, - }, - }, - data() { - return { - allAlerts: {}, - }; - }, - computed: { - ...mapGetters('monitoringDashboard', ['metricsSavedToDb']), - hasMetricsInDb() { - const { metrics = [] } = this.graphData; - return metrics.some(({ metricId }) => this.metricsSavedToDb.includes(metricId)); - }, - alertWidgetAvailable() { - return ( - this.prometheusAlertsAvailable && - this.alertsEndpoint && - this.graphData && - this.hasMetricsInDb - ); - }, - }, - methods: { - setAlerts(alertPath, alertAttributes) { - if (alertAttributes) { - this.$set(this.allAlerts, alertPath, alertAttributes); - } else { - this.$delete(this.allAlerts, alertPath); - } - }, - }, -}; -</script> diff --git a/app/assets/javascripts/pages/admin/services/edit/index.js b/app/assets/javascripts/pages/admin/services/edit/index.js new file mode 100644 index 00000000000..e5e80d2f566 --- /dev/null +++ b/app/assets/javascripts/pages/admin/services/edit/index.js @@ -0,0 +1,9 @@ +import IntegrationSettingsForm from '~/integrations/integration_settings_form'; +import initAlertsSettings from '~/alerts_service_settings'; + +document.addEventListener('DOMContentLoaded', () => { + const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); + integrationSettingsForm.init(); + + initAlertsSettings(document.querySelector('.js-alerts-service-settings')); +}); diff --git a/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue b/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue index 3e212f09e35..26dfd11b55c 100644 --- a/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue +++ b/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue @@ -182,7 +182,7 @@ export default { <gl-sprintf :message=" s__( - 'ContainerRegistry|Wildcards such as %{codeStart}.*-stable%{codeEnd} or %{codeStart}production/.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}', + 'ContainerRegistry|Regular expressions such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}', ) " > diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index cacaa585b5d..d8eb981c106 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -407,7 +407,7 @@ export class SearchAutocomplete { disableAutocomplete() { if (!this.searchInput.hasClass('js-autocomplete-disabled') && this.dropdown.hasClass('show')) { this.searchInput.addClass('js-autocomplete-disabled'); - this.dropdown.dropdown('toggle'); + this.dropdownToggle.dropdown('toggle'); this.restoreMenu(); } } diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index 3a84600b09f..8846ff54eb2 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -151,11 +151,11 @@ class ProjectsFinder < UnionFinder end def by_personal(items) - (params[:personal].present? && current_user) ? items.personal(current_user) : items + params[:personal].present? && current_user ? items.personal(current_user) : items end def by_starred(items) - (params[:starred].present? && current_user) ? items.starred_by(current_user) : items + params[:starred].present? && current_user ? items.starred_by(current_user) : items end def by_trending(items) diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index 9de28fb3ed9..228dc2cc27f 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -80,8 +80,8 @@ module NamespacesHelper visibility_level: n.visibility_level_value, visibility: n.visibility, name: n.name, - show_path: (type == 'group') ? group_path(n) : user_path(n), - edit_path: (type == 'group') ? edit_group_path(n) : nil + show_path: type == 'group' ? group_path(n) : user_path(n), + edit_path: type == 'group' ? edit_group_path(n) : nil }] end diff --git a/changelogs/unreleased/208171-clicking-on-search-results-does-not-follow-link.yml b/changelogs/unreleased/208171-clicking-on-search-results-does-not-follow-link.yml new file mode 100644 index 00000000000..0ef8666e10e --- /dev/null +++ b/changelogs/unreleased/208171-clicking-on-search-results-does-not-follow-link.yml @@ -0,0 +1,5 @@ +--- +title: Fix an issue where the Search dropdown results would not be clickable. +merge_request: 30087 +author: mbergeron +type: fixed diff --git a/changelogs/unreleased/213239-fix-newly-added-deleted-files-in-ide.yml b/changelogs/unreleased/213239-fix-newly-added-deleted-files-in-ide.yml new file mode 100644 index 00000000000..ae346e7f507 --- /dev/null +++ b/changelogs/unreleased/213239-fix-newly-added-deleted-files-in-ide.yml @@ -0,0 +1,5 @@ +--- +title: Fix Web IDE handling of deleting newly added files +merge_request: 29783 +author: +type: fixed diff --git a/changelogs/unreleased/214007-example-expiration-regex.yml b/changelogs/unreleased/214007-example-expiration-regex.yml new file mode 100644 index 00000000000..43db738ed0f --- /dev/null +++ b/changelogs/unreleased/214007-example-expiration-regex.yml @@ -0,0 +1,5 @@ +--- +title: Update the example regex in the image expiration policy UI +merge_request: 29348 +author: +type: changed diff --git a/changelogs/unreleased/215041-active-checkbox-not-showing-on-service-templates-form.yml b/changelogs/unreleased/215041-active-checkbox-not-showing-on-service-templates-form.yml new file mode 100644 index 00000000000..84ff3d7b68d --- /dev/null +++ b/changelogs/unreleased/215041-active-checkbox-not-showing-on-service-templates-form.yml @@ -0,0 +1,5 @@ +--- +title: Fix Service Templates missing Active toggle +merge_request: 29936 +author: +type: fixed diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md index c871e57666f..34b4bf934bc 100644 --- a/doc/api/api_resources.md +++ b/doc/api/api_resources.md @@ -132,7 +132,7 @@ The following API resources are available outside of project and group contexts | [License](license.md) **(CORE ONLY)** | `/license` | | [Markdown](markdown.md) | `/markdown` | | [Merge requests](merge_requests.md) | `/merge_requests` (also available for groups and projects) | -| [Metrics dashboard annotations](metrics_dashboard_annotations.md) | `/environments/:id/metrics_dashboard/annotations` | +| [Metrics dashboard annotations](metrics_dashboard_annotations.md) | `/environments/:id/metrics_dashboard/annotations`, `/clusters/:id/metrics_dashboard/annotations` | | [Namespaces](namespaces.md) | `/namespaces` | | [Notification settings](notification_settings.md) | `/notification_settings` (also available for groups and projects) | | [Pages domains](pages_domains.md) | `/pages/domains` (also available for projects) | diff --git a/doc/api/metrics_dashboard_annotations.md b/doc/api/metrics_dashboard_annotations.md index d8a018fe6c3..8cfd4dc8eed 100644 --- a/doc/api/metrics_dashboard_annotations.md +++ b/doc/api/metrics_dashboard_annotations.md @@ -18,6 +18,7 @@ Feature.enable(:metrics_dashboard_annotations) ```plaintext POST /environments/:id/metrics_dashboard/annotations/ +POST /clusters/:id/metrics_dashboard/annotations/ ``` Parameters: diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 9da1224a9b9..f7d9430433e 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -107,7 +107,7 @@ The following table lists available parameters for jobs: | [`when`](#when) | When to run job. Also available: `when:manual` and `when:delayed`. | | [`environment`](#environment) | Name of an environment to which the job deploys. Also available: `environment:name`, `environment:url`, `environment:on_stop`, `environment:auto_stop_in` and `environment:action`. | | [`cache`](#cache) | List of files that should be cached between subsequent runs. Also available: `cache:paths`, `cache:key`, `cache:untracked`, and `cache:policy`. | -| [`artifacts`](#artifacts) | List of files and directories to attach to a job on success. Also available: `artifacts:paths`, `artifacts:expose_as`, `artifacts:name`, `artifacts:untracked`, `artifacts:when`, `artifacts:expire_in`, `artifacts:reports`, `artifacts:reports:junit`, and `artifacts:reports:cobertura`.<br><br>In GitLab [Enterprise Edition](https://about.gitlab.com/pricing/), these are available: `artifacts:reports:codequality`, `artifacts:reports:sast`, `artifacts:reports:dependency_scanning`, `artifacts:reports:container_scanning`, `artifacts:reports:dast`, `artifacts:reports:license_management`, `artifacts:reports:performance` and `artifacts:reports:metrics`. | +| [`artifacts`](#artifacts) | List of files and directories to attach to a job on success. Also available: `artifacts:paths`, `artifacts:expose_as`, `artifacts:name`, `artifacts:untracked`, `artifacts:when`, `artifacts:expire_in`, `artifacts:reports`, `artifacts:reports:junit`, `artifacts:reports:cobertura`, and `artifacts:reports:terraform`.<br><br>In GitLab [Enterprise Edition](https://about.gitlab.com/pricing/), these are available: `artifacts:reports:codequality`, `artifacts:reports:sast`, `artifacts:reports:dependency_scanning`, `artifacts:reports:container_scanning`, `artifacts:reports:dast`, `artifacts:reports:license_management`, `artifacts:reports:performance` and `artifacts:reports:metrics`. | | [`dependencies`](#dependencies) | Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. | | [`coverage`](#coverage) | Code coverage settings for a given job. | | [`retry`](#retry) | When and how many times a job can be auto-retried in case of a failure. | @@ -3004,6 +3004,14 @@ and will be automatically shown in merge requests. Cobertura was originally developed for Java, but there are many third party ports for other languages like JavaScript, Python, Ruby, etc. +##### `artifacts:reports:terraform` + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/207527) in GitLab 12.10. Requires [GitLab Runner](https://docs.gitlab.com/runner/) 11.5 and above. + +The `terraform` report collects Terraform `tfplan.json` files. The +collected Terraform plan reports will be uploaded to GitLab as +artifacts and will be automatically shown in merge requests. + ##### `artifacts:reports:codequality` **(STARTER)** > Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above. diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index 3431456363b..d6f4289e4b5 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -300,6 +300,9 @@ as even native users of English might misunderstand them. - Instead of "i.e.," use "that is." - Instead of "e.g.," use "for example," "such as," "for instance," or "like." - Instead of "etc.," either use "and so on" or consider editing it out, since it can be vague. +- Avoid using the word *Currently* when talking about the product or its + features. The documentation describes the product as it is, and not as it + will be at some indeterminate point in the future. ### Contractions diff --git a/doc/user/packages/container_registry/img/expiration-policy-app.png b/doc/user/packages/container_registry/img/expiration-policy-app.png Binary files differdeleted file mode 100644 index e2d3d668e38..00000000000 --- a/doc/user/packages/container_registry/img/expiration-policy-app.png +++ /dev/null diff --git a/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png b/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png Binary files differnew file mode 100644 index 00000000000..a38014a2b9b --- /dev/null +++ b/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md index 5505a4503ca..9d50c23b780 100644 --- a/doc/user/packages/container_registry/index.md +++ b/doc/user/packages/container_registry/index.md @@ -511,7 +511,7 @@ then goes through a process of excluding tags from it until only the ones to be To manage project expiration policy, navigate to **{settings}** **Settings > CI/CD > Container Registry tag expiration policy**. -![Expiration Policy App](img/expiration-policy-app.png) +![Expiration Policy App](img/expiration_policy_app_v13_0.png) The UI allows you to configure the following: diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index 82e2dfaaca2..6b8ec3c4abd 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -123,6 +123,24 @@ to integrate with. 1. Provide the domain name or IP address of your server, for example `http://thanos.example.com/` or `http://192.0.2.1/`. 1. Click **Save changes**. +### Precedence with multiple Prometheus configurations + +Although you can enable both a [manual configuration](#manual-configuration-of-prometheus) +and [auto configuration](#managed-prometheus-on-kubernetes) of Prometheus, only +one of them will be used: + +- If you have enabled a + [Prometheus manual configuration](#manual-configuration-of-prometheus) + and a [managed Prometheus on Kubernetes](#managed-prometheus-on-kubernetes), + the manual configuration takes precedence and is used to run queries from + [dashboards](#defining-custom-dashboards-per-project) and [custom metrics](#adding-custom-metrics). +- If you have managed Prometheus applications installed on Kubernetes clusters + at **different** levels (project, group, instance), the order of precedence is described in + [Cluster precedence](../../instance/clusters/index.md#cluster-precedence). +- If you have managed Prometheus applications installed on multiple Kubernetes + clusters at the **same** level, the Prometheus application of a cluster with a + matching [environment scope](../../../ci/environments.md#scoping-environments-with-specs) is used. + ## Monitoring CI/CD Environments Once configured, GitLab will attempt to retrieve performance metrics for any diff --git a/lib/gitlab/ci/build/artifacts/metadata/entry.rb b/lib/gitlab/ci/build/artifacts/metadata/entry.rb index 80e69cdcc95..ef354832e8e 100644 --- a/lib/gitlab/ci/build/artifacts/metadata/entry.rb +++ b/lib/gitlab/ci/build/artifacts/metadata/entry.rb @@ -50,7 +50,7 @@ module Gitlab end def basename - (directory? && !blank_node?) ? name + '/' : name + directory? && !blank_node? ? name + '/' : name end def name diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 17227e32548..11e0b54f4eb 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5600,6 +5600,9 @@ msgstr "" msgid "ContainerRegistry|Quick Start" msgstr "" +msgid "ContainerRegistry|Regular expressions such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}" +msgstr "" + msgid "ContainerRegistry|Remove repository" msgstr "" @@ -5686,9 +5689,6 @@ msgstr "" msgid "ContainerRegistry|We are having trouble connecting to Docker, which could be due to an issue with your project name or path. %{docLinkStart}More Information%{docLinkEnd}" msgstr "" -msgid "ContainerRegistry|Wildcards such as %{codeStart}.*-stable%{codeEnd} or %{codeStart}production/.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}" -msgstr "" - msgid "ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. %{docLinkStart}More Information%{docLinkEnd}" msgstr "" diff --git a/qa/qa/page/project/operations/metrics/show.rb b/qa/qa/page/project/operations/metrics/show.rb index 020a3a1d5f8..d44b31aebf9 100644 --- a/qa/qa/page/project/operations/metrics/show.rb +++ b/qa/qa/page/project/operations/metrics/show.rb @@ -21,7 +21,7 @@ module QA element :duplicate_dashboard_filename_field end - view 'app/assets/javascripts/monitoring/components/panel_type.vue' do + view 'app/assets/javascripts/monitoring/components/dashboard_panel.vue' do element :prometheus_graph_widgets element :prometheus_widgets_dropdown element :alert_widget_menu_item diff --git a/spec/features/ide/user_commits_changes_spec.rb b/spec/features/ide/user_commits_changes_spec.rb index f53abde1523..56f2c6b8afc 100644 --- a/spec/features/ide/user_commits_changes_spec.rb +++ b/spec/features/ide/user_commits_changes_spec.rb @@ -30,4 +30,14 @@ describe 'IDE user commits changes', :js do expect(project.repository.blob_at('master', 'foo/bar/.gitkeep')).to be_nil expect(project.repository.blob_at('master', 'foo/bar/lorem_ipsum.md').data).to eql(content) end + + it 'user adds then deletes new file' do + ide_create_new_file('foo/bar/lorem_ipsum.md') + + expect(page).to have_selector(ide_commit_tab_selector) + + ide_delete_file('foo/bar/lorem_ipsum.md') + + expect(page).not_to have_selector(ide_commit_tab_selector) + end end diff --git a/spec/frontend/ide/stores/mutations_spec.js b/spec/frontend/ide/stores/mutations_spec.js index d9ce59ad378..5d0fe35a10e 100644 --- a/spec/frontend/ide/stores/mutations_spec.js +++ b/spec/frontend/ide/stores/mutations_spec.js @@ -273,7 +273,7 @@ describe('Multi-file store mutations', () => { expect(localState.changedFiles).toEqual([]); }); - it('removes tempFile from changedFiles when deleted', () => { + it('removes tempFile from changedFiles and stagedFiles when deleted', () => { localState.entries.filePath = { path: 'filePath', deleted: false, @@ -282,10 +282,12 @@ describe('Multi-file store mutations', () => { }; localState.changedFiles.push({ ...localState.entries.filePath }); + localState.stagedFiles.push({ ...localState.entries.filePath }); mutations.DELETE_ENTRY(localState, 'filePath'); expect(localState.changedFiles).toEqual([]); + expect(localState.stagedFiles).toEqual([]); }); it('bursts unused seal', () => { diff --git a/spec/frontend/monitoring/components/panel_type_spec.js b/spec/frontend/monitoring/components/dashboard_panel_spec.js index 753d5caba69..3c0292e016d 100644 --- a/spec/frontend/monitoring/components/panel_type_spec.js +++ b/spec/frontend/monitoring/components/dashboard_panel_spec.js @@ -1,10 +1,13 @@ +import Vuex from 'vuex'; import { shallowMount } from '@vue/test-utils'; import AxiosMockAdapter from 'axios-mock-adapter'; import { setTestTimeout } from 'helpers/timeout'; import invalidUrl from '~/lib/utils/invalid_url'; import axios from '~/lib/utils/axios_utils'; +import { GlDropdownItem } from '@gitlab/ui'; +import AlertWidget from '~/monitoring/components/alert_widget.vue'; -import PanelType from '~/monitoring/components/panel_type.vue'; +import DashboardPanel from '~/monitoring/components/dashboard_panel.vue'; import { anomalyMockGraphData, mockLogsHref, @@ -40,7 +43,7 @@ const mocks = { }, }; -describe('Panel Type component', () => { +describe('Dashboard Panel', () => { let axiosMock; let store; let state; @@ -54,7 +57,7 @@ describe('Panel Type component', () => { const findContextualMenu = () => wrapper.find({ ref: 'contextualMenu' }); const createWrapper = props => { - wrapper = shallowMount(PanelType, { + wrapper = shallowMount(DashboardPanel, { propsData: { graphData, ...props, @@ -343,7 +346,7 @@ describe('Panel Type component', () => { describe('when downloading metrics data as CSV', () => { beforeEach(() => { - wrapper = shallowMount(PanelType, { + wrapper = shallowMount(DashboardPanel, { propsData: { clipboardText: exampleText, graphData: { @@ -392,7 +395,7 @@ describe('Panel Type component', () => { store.registerModule(mockNamespace, monitoringDashboard); store.state.embedGroup.modules.push(mockNamespace); - wrapper = shallowMount(PanelType, { + wrapper = shallowMount(DashboardPanel, { propsData: { graphData, namespace: mockNamespace, @@ -432,4 +435,52 @@ describe('Panel Type component', () => { expect(wrapper.find(MonitorTimeSeriesChart).exists()).toBe(true); }); }); + + describe('panel alerts', () => { + const setMetricsSavedToDb = val => + monitoringDashboard.getters.metricsSavedToDb.mockReturnValue(val); + const findAlertsWidget = () => wrapper.find(AlertWidget); + const findMenuItemAlert = () => + wrapper.findAll(GlDropdownItem).filter(i => i.text() === 'Alerts'); + + beforeEach(() => { + jest.spyOn(monitoringDashboard.getters, 'metricsSavedToDb').mockReturnValue([]); + + store = new Vuex.Store({ + modules: { + monitoringDashboard, + }, + }); + + createWrapper(); + }); + + describe.each` + desc | metricsSavedToDb | propsData | isShown + ${'with permission and no metrics in db'} | ${[]} | ${{}} | ${false} + ${'with permission and related metrics in db'} | ${[graphData.metrics[0].metricId]} | ${{}} | ${true} + ${'without permission and related metrics in db'} | ${[graphData.metrics[0].metricId]} | ${{ prometheusAlertsAvailable: false }} | ${false} + ${'with permission and unrelated metrics in db'} | ${['another_metric_id']} | ${{}} | ${false} + `('$desc', ({ metricsSavedToDb, isShown, propsData }) => { + const showsDesc = isShown ? 'shows' : 'does not show'; + + beforeEach(() => { + setMetricsSavedToDb(metricsSavedToDb); + createWrapper({ + alertsEndpoint: '/endpoint', + prometheusAlertsAvailable: true, + ...propsData, + }); + return wrapper.vm.$nextTick(); + }); + + it(`${showsDesc} alert widget`, () => { + expect(findAlertsWidget().exists()).toBe(isShown); + }); + + it(`${showsDesc} alert configuration`, () => { + expect(findMenuItemAlert().exists()).toBe(isShown); + }); + }); + }); }); diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js index ad48874014e..d8c9af59f90 100644 --- a/spec/frontend/monitoring/components/dashboard_spec.js +++ b/spec/frontend/monitoring/components/dashboard_spec.js @@ -12,7 +12,7 @@ import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_p import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue'; import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue'; import GroupEmptyState from '~/monitoring/components/group_empty_state.vue'; -import PanelType from '~/monitoring/components/panel_type_with_alerts.vue'; +import DashboardPanel from '~/monitoring/components/dashboard_panel.vue'; import { createStore } from '~/monitoring/stores'; import * as types from '~/monitoring/stores/mutation_types'; import { setupStoreWithDashboard, setMetricResult, setupStoreWithData } from '../store_utils'; @@ -48,6 +48,7 @@ describe('Dashboard', () => { fetchData: jest.fn(), }, store, + stubs: ['graph-group', 'dashboard-panel'], ...options, }); }; @@ -126,10 +127,7 @@ describe('Dashboard', () => { }); it('hides the group panels when showPanels is false', () => { - createMountedWrapper( - { hasMetrics: true, showPanels: false }, - { stubs: ['graph-group', 'panel-type'] }, - ); + createMountedWrapper({ hasMetrics: true, showPanels: false }); setupStoreWithData(wrapper.vm.$store); @@ -142,7 +140,7 @@ describe('Dashboard', () => { it('fetches the metrics data with proper time window', () => { jest.spyOn(store, 'dispatch'); - createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] }); + createMountedWrapper({ hasMetrics: true }); wrapper.vm.$store.commit( `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, @@ -157,7 +155,7 @@ describe('Dashboard', () => { describe('when all requests have been commited by the store', () => { beforeEach(() => { - createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] }); + createMountedWrapper({ hasMetrics: true }); setupStoreWithData(wrapper.vm.$store); @@ -186,7 +184,7 @@ describe('Dashboard', () => { }); it('hides the environments dropdown list when there is no environments', () => { - createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] }); + createMountedWrapper({ hasMetrics: true }); setupStoreWithDashboard(wrapper.vm.$store); @@ -196,7 +194,7 @@ describe('Dashboard', () => { }); it('renders the datetimepicker dropdown', () => { - createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] }); + createMountedWrapper({ hasMetrics: true }); setupStoreWithData(wrapper.vm.$store); @@ -206,7 +204,7 @@ describe('Dashboard', () => { }); it('renders the refresh dashboard button', () => { - createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] }); + createMountedWrapper({ hasMetrics: true }); setupStoreWithData(wrapper.vm.$store); @@ -249,13 +247,7 @@ describe('Dashboard', () => { describe('searchable environments dropdown', () => { beforeEach(() => { - createMountedWrapper( - { hasMetrics: true }, - { - attachToDocument: true, - stubs: ['graph-group', 'panel-type'], - }, - ); + createMountedWrapper({ hasMetrics: true }, { attachToDocument: true }); setupStoreWithData(wrapper.vm.$store); @@ -465,7 +457,7 @@ describe('Dashboard', () => { describe('Dashboard dropdown', () => { beforeEach(() => { - createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] }); + createMountedWrapper({ hasMetrics: true }); wrapper.vm.$store.commit( `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, @@ -484,15 +476,12 @@ describe('Dashboard', () => { describe('external dashboard link', () => { beforeEach(() => { - createMountedWrapper( - { - hasMetrics: true, - showPanels: false, - showTimeWindowDropdown: false, - externalDashboardUrl: '/mockUrl', - }, - { stubs: ['graph-group', 'panel-type'] }, - ); + createMountedWrapper({ + hasMetrics: true, + showPanels: false, + showTimeWindowDropdown: false, + externalDashboardUrl: '/mockUrl', + }); return wrapper.vm.$nextTick(); }); @@ -511,7 +500,7 @@ describe('Dashboard', () => { const getClipboardTextAt = i => wrapper - .findAll(PanelType) + .findAll(DashboardPanel) .at(i) .props('clipboardText'); diff --git a/spec/frontend/monitoring/components/dashboard_url_time_spec.js b/spec/frontend/monitoring/components/dashboard_url_time_spec.js index 65e9d036d1a..9bba5280007 100644 --- a/spec/frontend/monitoring/components/dashboard_url_time_spec.js +++ b/spec/frontend/monitoring/components/dashboard_url_time_spec.js @@ -27,7 +27,7 @@ describe('dashboard invalid url parameters', () => { wrapper = mount(Dashboard, { propsData: { ...propsData, ...props }, store, - stubs: ['graph-group', 'panel-type'], + stubs: ['graph-group', 'dashboard-panel'], ...options, }); }; diff --git a/spec/frontend/monitoring/components/embeds/metric_embed_spec.js b/spec/frontend/monitoring/components/embeds/metric_embed_spec.js index b6734ede63e..f23823ccad6 100644 --- a/spec/frontend/monitoring/components/embeds/metric_embed_spec.js +++ b/spec/frontend/monitoring/components/embeds/metric_embed_spec.js @@ -1,6 +1,6 @@ import { createLocalVue, shallowMount } from '@vue/test-utils'; import Vuex from 'vuex'; -import PanelType from '~/monitoring/components/panel_type_with_alerts.vue'; +import DashboardPanel from '~/monitoring/components/dashboard_panel.vue'; import { TEST_HOST } from 'helpers/test_constants'; import MetricEmbed from '~/monitoring/components/embeds/metric_embed.vue'; import { groups, initialState, metricsData, metricsWithData } from './mock_data'; @@ -62,7 +62,7 @@ describe('MetricEmbed', () => { it('shows an empty state when no metrics are present', () => { expect(wrapper.find('.metrics-embed').exists()).toBe(true); - expect(wrapper.find(PanelType).exists()).toBe(false); + expect(wrapper.find(DashboardPanel).exists()).toBe(false); }); }); @@ -90,12 +90,12 @@ describe('MetricEmbed', () => { it('shows a chart when metrics are present', () => { expect(wrapper.find('.metrics-embed').exists()).toBe(true); - expect(wrapper.find(PanelType).exists()).toBe(true); - expect(wrapper.findAll(PanelType).length).toBe(2); + expect(wrapper.find(DashboardPanel).exists()).toBe(true); + expect(wrapper.findAll(DashboardPanel).length).toBe(2); }); it('includes groupId with dashboardUrl', () => { - expect(wrapper.find(PanelType).props('groupId')).toBe(TEST_HOST); + expect(wrapper.find(DashboardPanel).props('groupId')).toBe(TEST_HOST); }); }); }); diff --git a/spec/frontend/monitoring/components/panel_type_with_alerts_spec.js b/spec/frontend/monitoring/components/panel_type_with_alerts_spec.js deleted file mode 100644 index 3374fe4b55f..00000000000 --- a/spec/frontend/monitoring/components/panel_type_with_alerts_spec.js +++ /dev/null @@ -1,73 +0,0 @@ -import Vuex from 'vuex'; -import { shallowMount } from '@vue/test-utils'; -import { GlDropdownItem } from '@gitlab/ui'; -import { monitoringDashboard } from '~/monitoring/stores'; -import PanelType from '~/monitoring/components/panel_type_with_alerts.vue'; -import AlertWidget from '~/monitoring/components/alert_widget.vue'; -import { graphData } from 'jest/monitoring/fixture_data'; - -global.URL.createObjectURL = jest.fn(); - -describe('Panel Type', () => { - let store; - let wrapper; - - const setMetricsSavedToDb = val => - monitoringDashboard.getters.metricsSavedToDb.mockReturnValue(val); - const findAlertsWidget = () => wrapper.find(AlertWidget); - const findMenuItemAlert = () => - wrapper.findAll(GlDropdownItem).filter(i => i.text() === 'Alerts'); - - const mockPropsData = { - graphData, - clipboardText: 'example_text', - alertsEndpoint: '/endpoint', - prometheusAlertsAvailable: true, - }; - - const createWrapper = propsData => { - wrapper = shallowMount(PanelType, { - propsData: { - ...mockPropsData, - ...propsData, - }, - store, - }); - }; - - beforeEach(() => { - jest.spyOn(monitoringDashboard.getters, 'metricsSavedToDb').mockReturnValue([]); - - store = new Vuex.Store({ - modules: { - monitoringDashboard, - }, - }); - }); - - describe('panel type alerts', () => { - describe.each` - desc | metricsSavedToDb | propsData | isShown - ${'with license and no metrics in db'} | ${[]} | ${{}} | ${false} - ${'with license and related metrics in db'} | ${[graphData.metrics[0].metricId]} | ${{}} | ${true} - ${'without license and related metrics in db'} | ${[graphData.metrics[0].metricId]} | ${{ prometheusAlertsAvailable: false }} | ${false} - ${'with license and unrelated metrics in db'} | ${['another_metric_id']} | ${{}} | ${false} - `('$desc', ({ metricsSavedToDb, isShown, propsData }) => { - const showsDesc = isShown ? 'shows' : 'does not show'; - - beforeEach(() => { - setMetricsSavedToDb(metricsSavedToDb); - createWrapper(propsData); - return wrapper.vm.$nextTick(); - }); - - it(`${showsDesc} alert widget`, () => { - expect(findAlertsWidget().exists()).toBe(isShown); - }); - - it(`${showsDesc} alert configuration`, () => { - expect(findMenuItemAlert().exists()).toBe(isShown); - }); - }); - }); -}); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index e9bc1fc51e8..4f42d4880e8 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -188,4 +188,28 @@ describe('Search autocomplete dropdown', () => { // example) on JavaScript-created keypresses. expect(submitSpy).not.toHaveBeenTriggered(); }); + + describe('disableAutocomplete', function() { + beforeEach(function() { + widget.enableAutocomplete(); + }); + + it('should close the Dropdown', function() { + const toggleSpy = spyOn(widget.dropdownToggle, 'dropdown'); + + widget.dropdown.addClass('show'); + widget.disableAutocomplete(); + + expect(toggleSpy).toHaveBeenCalledWith('toggle'); + }); + }); + + describe('enableAutocomplete', function() { + it('should open the Dropdown', function() { + const toggleSpy = spyOn(widget.dropdownToggle, 'dropdown'); + widget.enableAutocomplete(); + + expect(toggleSpy).toHaveBeenCalledWith('toggle'); + }); + }); }); diff --git a/spec/requests/api/pipeline_schedules_spec.rb b/spec/requests/api/pipeline_schedules_spec.rb index 14b292db045..98eaf36b14e 100644 --- a/spec/requests/api/pipeline_schedules_spec.rb +++ b/spec/requests/api/pipeline_schedules_spec.rb @@ -67,7 +67,7 @@ describe API::PipelineSchedules do end def active?(str) - (str == 'active') ? true : false + str == 'active' end end end diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index 90adfb1a2ee..e80b69e4fd5 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -7,7 +7,7 @@ require 'capybara-screenshot/rspec' require 'selenium-webdriver' # Give CI some extra time -timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30 +timeout = ENV['CI'] || ENV['CI_SERVER'] ? 60 : 30 # Define an error class for JS console messages JSConsoleError = Class.new(StandardError) diff --git a/spec/support/helpers/features/web_ide_spec_helpers.rb b/spec/support/helpers/features/web_ide_spec_helpers.rb index 37c8345a4e5..123bd9b5070 100644 --- a/spec/support/helpers/features/web_ide_spec_helpers.rb +++ b/spec/support/helpers/features/web_ide_spec_helpers.rb @@ -32,6 +32,10 @@ module WebIdeSpecHelpers page.find('.ide-tree-actions') end + def ide_tab_selector(mode) + ".js-ide-#{mode}-mode" + end + def ide_file_row_open?(row) row.matches_css?('.is-open') end @@ -106,16 +110,16 @@ module WebIdeSpecHelpers evaluate_script("monaco.editor.getModel('#{uri}').getValue()") end + def ide_commit_tab_selector + ide_tab_selector('commit') + end + def ide_commit - ide_switch_mode('commit') + find(ide_commit_tab_selector).click commit_to_current_branch end - def ide_switch_mode(mode) - find(".js-ide-#{mode}-mode").click - end - private def file_row_container(row) |