Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
db061f4432
commit
22e60f1c61
39 changed files with 252 additions and 215 deletions
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -8,6 +8,8 @@ export const ListType = {
|
|||
blank: 'blank',
|
||||
};
|
||||
|
||||
export const inactiveListId = 0;
|
||||
|
||||
export default {
|
||||
ListType,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -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'));
|
||||
});
|
|
@ -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}',
|
||||
)
|
||||
"
|
||||
>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix an issue where the Search dropdown results would not be clickable.
|
||||
merge_request: 30087
|
||||
author: mbergeron
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Web IDE handling of deleting newly added files
|
||||
merge_request: 29783
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update the example regex in the image expiration policy UI
|
||||
merge_request: 29348
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Service Templates missing Active toggle
|
||||
merge_request: 29936
|
||||
author:
|
||||
type: fixed
|
|
@ -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) |
|
||||
|
|
|
@ -18,6 +18,7 @@ Feature.enable(:metrics_dashboard_annotations)
|
|||
|
||||
```plaintext
|
||||
POST /environments/:id/metrics_dashboard/annotations/
|
||||
POST /clusters/:id/metrics_dashboard/annotations/
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 31 KiB |
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -50,7 +50,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def basename
|
||||
(directory? && !blank_node?) ? name + '/' : name
|
||||
directory? && !blank_node? ? name + '/' : name
|
||||
end
|
||||
|
||||
def name
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -67,7 +67,7 @@ describe API::PipelineSchedules do
|
|||
end
|
||||
|
||||
def active?(str)
|
||||
(str == 'active') ? true : false
|
||||
str == 'active'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,14 +110,14 @@ module WebIdeSpecHelpers
|
|||
evaluate_script("monaco.editor.getModel('#{uri}').getValue()")
|
||||
end
|
||||
|
||||
def ide_commit
|
||||
ide_switch_mode('commit')
|
||||
|
||||
commit_to_current_branch
|
||||
def ide_commit_tab_selector
|
||||
ide_tab_selector('commit')
|
||||
end
|
||||
|
||||
def ide_switch_mode(mode)
|
||||
find(".js-ide-#{mode}-mode").click
|
||||
def ide_commit
|
||||
find(ide_commit_tab_selector).click
|
||||
|
||||
commit_to_current_branch
|
||||
end
|
||||
|
||||
private
|
||||
|
|
Loading…
Reference in a new issue