Commit 50e00489 authored by Miguel Rincon's avatar Miguel Rincon

Merge branch 'astoicescu/actions_menu_update' into 'master'

Add more actions to and change UI of dashboard actions menu

See merge request gitlab-org/gitlab!38946
parents 8269fe74 21c48641
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import {
GlDeprecatedButton,
GlNewDropdown,
GlNewDropdownDivider,
GlNewDropdownItem,
GlModal,
GlIcon,
GlModalDirective,
GlTooltipDirective,
} from '@gitlab/ui';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import DuplicateDashboardModal from './duplicate_dashboard_modal.vue';
import CreateDashboardModal from './create_dashboard_modal.vue';
import { s__ } from '~/locale';
import invalidUrl from '~/lib/utils/invalid_url';
import { redirectTo } from '~/lib/utils/url_utility';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { getAddMetricTrackingOptions } from '../utils';
export default {
components: {
GlDeprecatedButton,
GlNewDropdown,
GlNewDropdownDivider,
GlNewDropdownItem,
GlModal,
GlIcon,
DuplicateDashboardModal,
CreateDashboardModal,
CustomMetricsFormFields,
},
directives: {
GlModal: GlModalDirective,
GlTooltip: GlTooltipDirective,
TrackEvent: TrackEventDirective,
},
props: {
addingMetricsAvailable: {
type: Boolean,
required: false,
default: false,
},
customMetricsPath: {
type: String,
required: false,
default: invalidUrl,
},
validateQueryPath: {
type: String,
required: false,
default: invalidUrl,
},
defaultBranch: {
type: String,
required: true,
},
},
data() {
return { customMetricsFormIsValid: null };
},
computed: {
...mapState('monitoringDashboard', [
'projectPath',
'isUpdatingStarredValue',
'addDashboardDocumentationPath',
]),
...mapGetters('monitoringDashboard', ['selectedDashboard']),
isOutOfTheBoxDashboard() {
return this.selectedDashboard?.out_of_the_box_dashboard;
},
isMenuItemEnabled() {
return {
createDashboard: Boolean(this.projectPath),
editDashboard: this.selectedDashboard?.can_edit,
};
},
isMenuItemShown() {
return {
duplicateDashboard: this.isOutOfTheBoxDashboard,
};
},
},
methods: {
...mapActions('monitoringDashboard', ['toggleStarredValue']),
setFormValidity(isValid) {
this.customMetricsFormIsValid = isValid;
},
hideAddMetricModal() {
this.$refs.addMetricModal.hide();
},
getAddMetricTrackingOptions,
submitCustomMetricsForm() {
this.$refs.customMetricsForm.submit();
},
selectDashboard(dashboard) {
// Once the sidebar See metrics link is updated to the new URL,
// this sort of hardcoding will not be necessary.
// https://gitlab.com/gitlab-org/gitlab/-/issues/229277
const baseURL = `${this.projectPath}/-/metrics`;
const dashboardPath = encodeURIComponent(
dashboard.out_of_the_box_dashboard ? dashboard.path : dashboard.display_name,
);
redirectTo(`${baseURL}/${dashboardPath}`);
},
},
modalIds: {
addMetric: 'addMetric',
createDashboard: 'createDashboard',
duplicateDashboard: 'duplicateDashboard',
},
i18n: {
actionsMenu: s__('Metrics|More actions'),
duplicateDashboard: s__('Metrics|Duplicate current dashboard'),
starDashboard: s__('Metrics|Star dashboard'),
unstarDashboard: s__('Metrics|Unstar dashboard'),
addMetric: s__('Metrics|Add metric'),
editDashboardInfo: s__('Metrics|Duplicate this dashboard to edit dashboard YAML'),
editDashboard: s__('Metrics|Edit dashboard YAML'),
createDashboard: s__('Metrics|Create new dashboard'),
},
};
</script>
<template>
<gl-new-dropdown
v-gl-tooltip
data-testid="actions-menu"
data-qa-selector="actions_menu_dropdown"
right
no-caret
toggle-class="gl-px-3!"
:title="$options.i18n.actionsMenu"
>
<template #button-content>
<gl-icon class="gl-mr-0!" name="ellipsis_v" />
</template>
<template v-if="addingMetricsAvailable">
<gl-new-dropdown-item
v-gl-modal="$options.modalIds.addMetric"
data-qa-selector="add_metric_button"
data-testid="add-metric-item"
>
{{ $options.i18n.addMetric }}
</gl-new-dropdown-item>
<gl-modal
ref="addMetricModal"
:modal-id="$options.modalIds.addMetric"
:title="$options.i18n.addMetric"
data-testid="add-metric-modal"
>
<form ref="customMetricsForm" :action="customMetricsPath" method="post">
<custom-metrics-form-fields
:validate-query-path="validateQueryPath"
form-operation="post"
@formValidation="setFormValidity"
/>
</form>
<div slot="modal-footer">
<gl-deprecated-button @click="hideAddMetricModal">
{{ __('Cancel') }}
</gl-deprecated-button>
<gl-deprecated-button
v-track-event="getAddMetricTrackingOptions()"
data-testid="add-metric-modal-submit-button"
:disabled="!customMetricsFormIsValid"
variant="success"
@click="submitCustomMetricsForm"
>
{{ __('Save changes') }}
</gl-deprecated-button>
</div>
</gl-modal>
</template>
<gl-new-dropdown-item
v-if="isMenuItemEnabled.editDashboard"
:href="selectedDashboard ? selectedDashboard.project_blob_path : null"
data-qa-selector="edit_dashboard_button_enabled"
data-testid="edit-dashboard-item-enabled"
>
{{ $options.i18n.editDashboard }}
</gl-new-dropdown-item>
<!--
wrapper for tooltip as button can be `disabled`
https://bootstrap-vue.org/docs/components/tooltip#disabled-elements
-->
<div v-else v-gl-tooltip :title="$options.i18n.editDashboardInfo">
<gl-new-dropdown-item
:alt="$options.i18n.editDashboardInfo"
:href="selectedDashboard ? selectedDashboard.project_blob_path : null"
data-testid="edit-dashboard-item-disabled"
disabled
class="gl-cursor-not-allowed"
>
<span class="gl-text-gray-400">{{ $options.i18n.editDashboard }}</span>
</gl-new-dropdown-item>
</div>
<template v-if="isMenuItemShown.duplicateDashboard">
<gl-new-dropdown-item
v-gl-modal="$options.modalIds.duplicateDashboard"
data-testid="duplicate-dashboard-item"
>
{{ $options.i18n.duplicateDashboard }}
</gl-new-dropdown-item>
<duplicate-dashboard-modal
:default-branch="defaultBranch"
:modal-id="$options.modalIds.duplicateDashboard"
data-testid="duplicate-dashboard-modal"
@dashboardDuplicated="selectDashboard"
/>
</template>
<gl-new-dropdown-item
v-if="selectedDashboard"
data-testid="star-dashboard-item"
:disabled="isUpdatingStarredValue"
@click="toggleStarredValue()"
>
{{ selectedDashboard.starred ? $options.i18n.unstarDashboard : $options.i18n.starDashboard }}
</gl-new-dropdown-item>
<gl-new-dropdown-divider />
<gl-new-dropdown-item
v-gl-modal="$options.modalIds.createDashboard"
data-testid="create-dashboard-item"
:disabled="!isMenuItemEnabled.createDashboard"
:class="{ 'monitoring-actions-item-disabled': !isMenuItemEnabled.createDashboard }"
>
{{ $options.i18n.createDashboard }}
</gl-new-dropdown-item>
<template v-if="isMenuItemEnabled.createDashboard">
<create-dashboard-modal
data-testid="create-dashboard-modal"
:add-dashboard-documentation-path="addDashboardDocumentationPath"
:modal-id="$options.modalIds.createDashboard"
:project-path="projectPath"
/>
</template>
</gl-new-dropdown>
</template>
......@@ -7,17 +7,11 @@ import {
GlDeprecatedDropdownItem,
GlDeprecatedDropdownHeader,
GlDeprecatedDropdownDivider,
GlNewDropdown,
GlNewDropdownDivider,
GlNewDropdownItem,
GlModal,
GlLoadingIcon,
GlSearchBoxByType,
GlModalDirective,
GlTooltipDirective,
} from '@gitlab/ui';
import { s__ } from '~/locale';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url';
import Icon from '~/vue_shared/components/icon.vue';
......@@ -25,11 +19,9 @@ import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_p
import DashboardsDropdown from './dashboards_dropdown.vue';
import RefreshButton from './refresh_button.vue';
import CreateDashboardModal from './create_dashboard_modal.vue';
import DuplicateDashboardModal from './duplicate_dashboard_modal.vue';
import ActionsMenu from './dashboard_actions_menu.vue';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { getAddMetricTrackingOptions, timeRangeToUrl } from '../utils';
import { timeRangeToUrl } from '../utils';
import { timeRanges } from '~/vue_shared/constants';
import { timezones } from '../format_date';
......@@ -42,23 +34,17 @@ export default {
GlDeprecatedDropdownItem,
GlDeprecatedDropdownHeader,
GlDeprecatedDropdownDivider,
GlNewDropdown,
GlNewDropdownDivider,
GlNewDropdownItem,
GlSearchBoxByType,
GlModal,
CustomMetricsFormFields,
DateTimePicker,
DashboardsDropdown,
RefreshButton,
DuplicateDashboardModal,
CreateDashboardModal,
ActionsMenu,
},
directives: {
GlModal: GlModalDirective,
GlTooltip: GlTooltipDirective,
TrackEvent: TrackEventDirective,
},
props: {
defaultBranch: {
......@@ -94,29 +80,19 @@ export default {
required: true,
},
},
data() {
return {
formIsValid: null,
};
},
computed: {
...mapState('monitoringDashboard', [
'emptyState',
'environmentsLoading',
'currentEnvironmentName',
'isUpdatingStarredValue',
'dashboardTimezone',
'projectPath',
'canAccessOperationsSettings',
'operationsSettingsPath',
'currentDashboard',
'addDashboardDocumentationPath',
'externalDashboardUrl',
]),
...mapGetters('monitoringDashboard', ['selectedDashboard', 'filteredEnvironments']),
isOutOfTheBoxDashboard() {
return this.selectedDashboard?.out_of_the_box_dashboard;
},
shouldShowEmptyState() {
return Boolean(this.emptyState);
},
......@@ -130,7 +106,7 @@ export default {
// Custom metrics only avaialble on system dashboards because
// they are stored in the database. This can be improved. See:
// https://gitlab.com/gitlab-org/gitlab/-/issues/28241
this.selectedDashboard?.system_dashboard
this.selectedDashboard?.out_of_the_box_dashboard
);
},
showRearrangePanelsBtn() {
......@@ -139,15 +115,12 @@ export default {
displayUtc() {
return this.dashboardTimezone === timezones.UTC;
},
shouldShowActionsMenu() {
return Boolean(this.projectPath);
},
shouldShowSettingsButton() {
return this.canAccessOperationsSettings && this.operationsSettingsPath;
},
},
methods: {
...mapActions('monitoringDashboard', ['filterEnvironments', 'toggleStarredValue']),
...mapActions('monitoringDashboard', ['filterEnvironments']),
selectDashboard(dashboard) {
// Once the sidebar See metrics link is updated to the new URL,
// this sort of hardcoding will not be necessary.
......@@ -171,16 +144,6 @@ export default {
toggleRearrangingPanels() {
this.$emit('setRearrangingPanels', !this.isRearrangingPanels);
},
setFormValidity(isValid) {
this.formIsValid = isValid;
},
hideAddMetricModal() {
this.$refs.addMetricModal.hide();
},
getAddMetricTrackingOptions,
submitCustomMetricsForm() {
this.$refs.customMetricsForm.submit();
},
getEnvironmentPath(environment) {
// Once the sidebar See metrics link is updated to the new URL,
// this sort of hardcoding will not be necessary.
......@@ -193,16 +156,6 @@ export default {
return mergeUrlParams({ environment }, url);
},
},
modalIds: {
addMetric: 'addMetric',
createDashboard: 'createDashboard',
duplicateDashboard: 'duplicateDashboard',
},
i18n: {
starDashboard: s__('Metrics|Star dashboard'),
unstarDashboard: s__('Metrics|Unstar dashboard'),
addMetric: s__('Metrics|Add metric'),
},
timeRanges,
};
</script>
......@@ -280,29 +233,6 @@ export default {
<div class="flex-grow-1"></div>
<div class="d-sm-flex">
<div v-if="selectedDashboard" class="mb-2 mr-2 d-flex">
<!--
wrapper for tooltip as button can be `disabled`
https://bootstrap-vue.org/docs/components/tooltip#disabled-elements
-->
<div
v-gl-tooltip
class="flex-grow-1"
:title="
selectedDashboard.starred ? $options.i18n.unstarDashboard : $options.i18n.starDashboard
"
>
<gl-button
ref="toggleStarBtn"
class="w-100"
:disabled="isUpdatingStarredValue"
variant="default"
:icon="selectedDashboard.starred ? 'star' : 'star-o'"
@click="toggleStarredValue()"
/>
</div>
</div>
<div v-if="showRearrangePanelsBtn" class="mb-2 mr-2 d-flex">
<gl-button
:pressed="isRearrangingPanels"
......@@ -313,58 +243,6 @@ export default {
{{ __('Arrange charts') }}
</gl-button>
</div>
<div v-if="addingMetricsAvailable" class="mb-2 mr-2 d-flex d-sm-block">
<gl-button
ref="addMetricBtn"
v-gl-modal="$options.modalIds.addMetric"
variant="default"
data-qa-selector="add_metric_button"
class="flex-grow-1"
>
{{ $options.i18n.addMetric }}
</gl-button>
<gl-modal
ref="addMetricModal"
:modal-id="$options.modalIds.addMetric"
:title="$options.i18n.addMetric"
>
<form ref="customMetricsForm" :action="customMetricsPath" method="post">
<custom-metrics-form-fields
:validate-query-path="validateQueryPath"
form-operation="post"
@formValidation="setFormValidity"
/>
</form>
<div slot="modal-footer">
<gl-button @click="hideAddMetricModal">
{{ __('Cancel') }}
</gl-button>
<gl-button
ref="submitCustomMetricsFormBtn"
v-track-event="getAddMetricTrackingOptions()"
:disabled="!formIsValid"
variant="success"
category="primary"
@click="submitCustomMetricsForm"
>
{{ __('Save changes') }}
</gl-button>
</div>
</gl-modal>
</div>
<div
v-if="selectedDashboard && selectedDashboard.can_edit"
class="mb-2 mr-2 d-flex d-sm-block"
>
<gl-button
class="flex-grow-1 js-edit-link"
:href="selectedDashboard.project_blob_path"
data-qa-selector="edit_dashboard_button"
>
{{ __('Edit dashboard') }}
</gl-button>
</div>
<div
v-if="externalDashboardUrl && externalDashboardUrl.length"
......@@ -382,65 +260,28 @@ export default {
</gl-button>
</div>
<!-- This separator should be displayed only if at least one of the action menu or settings button are displayed -->
<span
v-if="shouldShowActionsMenu || shouldShowSettingsButton"
aria-hidden="true"
class="gl-pl-3 border-left gl-mb-3 d-none d-sm-block"
></span>
<div class="gl-mb-3 gl-mr-3 d-flex d-sm-block">
<actions-menu
:adding-metrics-available="addingMetricsAvailable"
:custom-metrics-path="customMetricsPath"
:validate-query-path="validateQueryPath"
:default-branch="defaultBranch"
/>
</div>
<div v-if="shouldShowActionsMenu" class="gl-mb-3 gl-mr-3 d-flex d-sm-block">
<gl-new-dropdown
v-gl-tooltip
right
class="gl-flex-grow-1"
data-testid="actions-menu"
data-qa-selector="actions_menu_dropdown"
:title="s__('Metrics|Create dashboard')"
:icon="'plus-square'"
>
<gl-new-dropdown-item
v-gl-modal="$options.modalIds.createDashboard"
data-testid="action-create-dashboard"
>{{ s__('Metrics|Create new dashboard') }}</gl-new-dropdown-item
>
<template v-if="shouldShowSettingsButton">
<span aria-hidden="true" class="gl-pl-3 border-left gl-mb-3 d-none d-sm-block"></span>
<create-dashboard-modal
data-testid="create-dashboard-modal"
:add-dashboard-documentation-path="addDashboardDocumentationPath"
:modal-id="$options.modalIds.createDashboard"
:project-path="projectPath"
<div class="mb-2 mr-2 d-flex d-sm-block">
<gl-button
v-gl-tooltip
data-testid="metrics-settings-button"
icon="settings"
:href="operationsSettingsPath"
:title="s__('Metrics|Metrics Settings')"
/>
<template v-if="isOutOfTheBoxDashboard">
<gl-new-dropdown-divider />
<gl-new-dropdown-item
ref="duplicateDashboardItem"
v-gl-modal="$options.modalIds.duplicateDashboard"
data-testid="action-duplicate-dashboard"
>
{{ s__('Metrics|Duplicate current dashboard') }}
</gl-new-dropdown-item>
<duplicate-dashboard-modal
:default-branch="defaultBranch"
:modal-id="$options.modalIds.duplicateDashboard"
@dashboardDuplicated="selectDashboard"
/>
</template>
</gl-new-dropdown>
</div>
<div v-if="shouldShowSettingsButton" class="mb-2 mr-2 d-flex d-sm-block">
<gl-button
v-gl-tooltip
data-testid="metrics-settings-button"
icon="settings"
:href="operationsSettingsPath"
:title="s__('Metrics|Metrics Settings')"
/>
</div>
</div>
</template>
</div>
</div>
</template>
---
title: Change UI and add new actions to monitor dashboard actions menu
merge_request: 38946
author:
type: changed
......@@ -20,7 +20,7 @@ The metrics as defined below do not support alerts, unlike
## Add a new dashboard to your project
> UI option [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223204) in GitLab 13.2.
> UI option [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/228856) in GitLab 13.3.
You can configure a custom dashboard by adding a new YAML file into your project's
`.gitlab/dashboards/` directory. For the dashboard to display on your project's **Operations > Metrics** page, the files must have a `.yml`
......@@ -31,9 +31,9 @@ To create a new dashboard from the GitLab user interface:
1. Sign in to GitLab as a user with Maintainer or Owner
[permissions](../../../user/permissions.md#project-members-permissions).
1. Navigate to your dashboard at **Operations > Metrics**.
1. In the top-right corner of your dashboard, click the **{file-addition-solid}** **Actions** menu,
1. In the top-right corner of your dashboard, click the **{{ellipsis_v}}** **More actions** menu,
and select **Create new**:
![Monitoring Dashboard actions menu with create new item](img/actions_menu_create_new_dashboard_v13_2.png)
![Monitoring Dashboard actions menu with create new item](img/actions_menu_create_new_dashboard_v13_3.png)
1. In the modal window, click **Open Repository**, then follow the instructions
for creating a new dashboard from the command line.
......@@ -82,7 +82,7 @@ The resulting `.yml` file can be customized and adapted to your project.
You can decide to save the dashboard `.yml` file in the project's **default** branch or in a
new branch.
1. Click **Duplicate dashboard** in the actions menu.
1. Click **Duplicate current dashboard** in the **{{ellipsis_v}}** **More actions** menu.
NOTE: **Note:**
You can duplicate only GitLab-defined dashboards.
......@@ -105,7 +105,7 @@ To manage the settings for your metrics dashboard:
1. Navigate to your dashboard at **Operations > Metrics**.
1. In the top-right corner of your dashboard, click **Metrics Settings**:
![Monitoring Dashboard actions menu with create new item](img/metrics_settings_button_v13_2.png)
![Monitoring Dashboard actions menu with create new item](img/metrics_settings_button_v13_3.png)
## Chart Context Menu
......
......@@ -25,7 +25,7 @@ To view the metrics dashboard for an environment that has
GitLab displays the default metrics dashboard for the environment, like the
following example:
![Example of metrics dashboard](img/example-dashboard_v13_1.png)
![Example of metrics dashboard](img/example-dashboard_v13_3.png)
The top of the dashboard contains a navigation bar. From left to right, the
navigation bar contains:
......@@ -37,15 +37,19 @@ navigation bar contains:
- **Range** - The time period of data to display.
- **Refresh dashboard** **{retry}** - Reload the dashboard with current data.
- **Set refresh rate** - Set a time frame for refreshing the data displayed.
- **Star dashboard** **{star-o}** - Click to mark a dashboard as a favorite.
- **More actions** **{ellipsis_v}** - More dashboard actions
- **Add metric** - Adds a [custom metric](#adding-custom-metrics). Only available on GitLab-defined dashboards.
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34779) in GitLab 12.5.)
- **Edit dashboard YAML** - Edit the source YAML file of a custom dashboard. Only available on
[custom dashboards](dashboards/index.md).
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34779) in GitLab 12.5.)
- **Duplicate current dashboard** - Save a [complete copy of a dashboard](dashboards/index.md#duplicate-a-gitlab-defined-dashboard). Only available on GitLab-defined dashboards.
- **Star dashboard** **{star-o}** - Click to mark a dashboard as a favorite.
Starred dashboards display a solid star **{star}** button, and display first
in the **Dashboard** dropdown list.
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214582) in GitLab 13.0.)
- **Edit dashboard** - Edit the source YAML file of a custom dashboard. Only available on
[custom dashboards](dashboards/index.md).
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34779) in GitLab 12.5.)
- **Create dashboard** **{file-addition-solid}** - Create a
[new custom dashboard for your project](dashboards/index.md#add-a-new-dashboard-to-your-project).
- **Create new dashboard** - Create a [new custom dashboard for your project](dashboards/index.md#add-a-new-dashboard-to-your-project).
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/228856) in GitLab 13.3.)
- **Metrics settings** - Configure the
[settings for this dashboard](dashboards/index.md#manage-the-metrics-dashboard-settings).
......@@ -70,7 +74,7 @@ helps quickly create a deployment:
1. When the pipeline has run successfully, graphs are available on the
**Operations > Metrics** page.
![Monitoring Dashboard](img/prometheus_monitoring_dashboard_v13_1.png)
![Monitoring Dashboard](img/prometheus_monitoring_dashboard_v13_3.png)
## Customize your metrics dashboard
......
......@@ -8804,9 +8804,6 @@ msgstr ""
msgid "Edit comment"
msgstr ""
msgid "Edit dashboard"
msgstr ""
msgid "Edit description"
msgstr ""
......@@ -15236,9 +15233,6 @@ msgstr ""
msgid "Metrics|Create custom dashboard %{fileName}"
msgstr ""
msgid "Metrics|Create dashboard"
msgstr ""
msgid "Metrics|Create metric"
msgstr ""
......@@ -15272,9 +15266,15 @@ msgstr ""
msgid "Metrics|Duplicate dashboard"
msgstr ""
msgid "Metrics|Duplicate this dashboard to edit dashboard YAML"
msgstr ""
msgid "Metrics|Duplicating..."
msgstr ""
msgid "Metrics|Edit dashboard YAML"
msgstr ""
msgid "Metrics|Edit metric"
msgid_plural "Metrics|Edit metrics"
msgstr[0] ""
......@@ -15316,6 +15316,9 @@ msgstr ""
msgid "Metrics|Min"
msgstr ""
msgid "Metrics|More actions"
msgstr ""
msgid "Metrics|Must be a valid PromQL query."
msgstr ""
......
......@@ -18,9 +18,12 @@ module QA
view 'app/assets/javascripts/monitoring/components/dashboard_header.vue' do
element :dashboards_filter_dropdown
element :environments_dropdown
element :edit_dashboard_button
element :range_picker_dropdown
end
view 'app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue' do
element :actions_menu_dropdown
element :edit_dashboard_button_enabled
end
view 'app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue' do
......@@ -56,7 +59,7 @@ module QA
def has_edit_dashboard_enabled?
within_element :prometheus_graphs do
has_element? :edit_dashboard_button
has_element? :edit_dashboard_button_enabled
end
end
......
......@@ -99,34 +99,19 @@ exports[`Dashboard template matches the default snapshot 1`] = `
<div
class="d-sm-flex"
>
<div
class="mb-2 mr-2 d-flex"
>
<div
class="flex-grow-1"
title="Star dashboard"
>
<gl-button-stub
category="tertiary"
class="w-100"
icon="star-o"
size="medium"
variant="default"
/>
</div>
</div>
<!---->
<!---->
<!---->
<!---->
<!---->
<!---->
<div
class="gl-mb-3 gl-mr-3 d-flex d-sm-block"
>
<actions-menu-stub
custommetricspath="/monitoring/monitor-project/prometheus/metrics"
defaultbranch="master"
validatequerypath="/monitoring/monitor-project/prometheus/metrics/validate_query"
/>
</div>
<!---->
</div>
......
import { shallowMount } from '@vue/test-utils';
import { createStore } from '~/monitoring/stores';
import { setupAllDashboards, setupStoreWithData } from '../store_utils';
import { redirectTo } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import ActionsMenu from '~/monitoring/components/dashboard_actions_menu.vue';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import { dashboardActionsMenuProps, dashboardGitResponse } from '../mock_data';
import * as types from '~/monitoring/stores/mutation_types';
jest.mock('~/lib/utils/url_utility', () => ({
redirectTo: jest.fn(),
queryToObject: jest.fn(),
}));
describe('Actions menu', () => {
const ootbDashboards = [dashboardGitResponse[0], dashboardGitResponse[2]];
const customDashboard = dashboardGitResponse[1];
let store;
let wrapper;
const findAddMetricItem = () => wrapper.find('[data-testid="add-metric-item"]');
const findAddMetricModal = () => wrapper.find('[data-testid="add-metric-modal"]');
const findAddMetricModalSubmitButton = () =>
wrapper.find('[data-testid="add-metric-modal-submit-button"]');
const findStarDashboardItem = () => wrapper.find('[data-testid="star-dashboard-item"]');
const findEditDashboardItemEnabled = () =>
wrapper.find('[data-testid="edit-dashboard-item-enabled"]');
const findEditDashboardItemDisabled = () =>
wrapper.find('[data-testid="edit-dashboard-item-disabled"]');
const findDuplicateDashboardItem = () => wrapper.find('[data-testid="duplicate-dashboard-item"]');
const findDuplicateDashboardModal = () =>
wrapper.find('[data-testid="duplicate-dashboard-modal"]');
const findCreateDashboardItem = () => wrapper.find('[data-testid="create-dashboard-item"]');
const findCreateDashboardModal = () => wrapper.find('[data-testid="create-dashboard-modal"]');
const createShallowWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(ActionsMenu, {
propsData: { ...dashboardActionsMenuProps, ...props },
store,
...options,
});
};
beforeEach(() => {
store = createStore();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('add metric item', () => {
it('is rendered when custom metrics are available', () => {
createShallowWrapper();
return wrapper.vm.$nextTick(() => {
expect(findAddMetricItem().exists()).toBe(true);
});
});
it('is not rendered when custom metrics are not available', () => {
createShallowWrapper({
addingMetricsAvailable: false,
});
return wrapper.vm.$nextTick(() => {
expect(findAddMetricItem().exists()).toBe(false);
});
});
describe('when available', () => {
beforeEach(() => {
createShallowWrapper();
});
it('modal for custom metrics form is rendered', () => {
expect(findAddMetricModal().exists()).toBe(true);
expect(findAddMetricModal().attributes().modalid).toBe('addMetric');
});
it('add metric modal submit button exists', () => {
expect(findAddMetricModalSubmitButton().exists()).toBe(true);
});
it('renders custom metrics form fields', () => {
expect(wrapper.find(CustomMetricsFormFields).exists()).toBe(true);
});
});
describe('when not available', () => {
beforeEach(() => {
createShallowWrapper({ addingMetricsAvailable: false });
});
it('modal for custom metrics form is not rendered', () => {
expect(findAddMetricModal().exists()).toBe(false);
});
});
describe('adding new metric from modal', () => {
let origPage;
beforeEach(done => {
jest.spyOn(Tracking, 'event').mockReturnValue();
createShallowWrapper();
setupStoreWithData(store);
origPage = document.body.dataset.page;
document.body.dataset.page = 'projects:environments:metrics';
wrapper.vm.$nextTick(done);
});
afterEach(() => {
document.body.dataset.page = origPage;
});
it('is tracked', done => {
const submitButton = findAddMetricModalSubmitButton().vm;
wrapper.vm.$nextTick(() => {
submitButton.$el.click();
wrapper.vm.$nextTick(() => {
expect(Tracking.event).toHaveBeenCalledWith(
document.body.dataset.page,
'click_button',
{
label: 'add_new_metric',
property: 'modal',
value: undefined,
},
);
done();
});
});
});
});
});
describe('edit dashboard yml item', () => {
beforeEach(() => {
createShallowWrapper();
});
describe('when current dashboard is custom', () => {
beforeEach(() => {
setupAllDashboards(store, customDashboard.path);
});
it('enabled item is rendered and has falsy disabled attribute', () => {
expect(findEditDashboardItemEnabled().exists()).toBe(true);
expect(findEditDashboardItemEnabled().attributes('disabled')).toBe(undefined);
});
it('enabled item links to their edit path', () => {
expect(findEditDashboardItemEnabled().attributes('href')).toBe(
customDashboard.project_blob_path,
);
});
it('disabled item is not rendered', () => {
expect(findEditDashboardItemDisabled().exists()).toBe(false);
});
});
describe.each(ootbDashboards)('when current dashboard is OOTB', dashboard => {
beforeEach(() => {
setupAllDashboards(store, dashboard.path);
});
it('disabled item is rendered and has disabled attribute set on it', () => {
expect(findEditDashboardItemDisabled().exists()).toBe(true);
expect(findEditDashboardItemDisabled().attributes('disabled')).toBe('');
});
it('enabled item is not rendered', () => {
expect(findEditDashboardItemEnabled().exists()).toBe(false);
});
});
});
describe('duplicate dashboard item', () => {
beforeEach(() => {
createShallowWrapper();
});
describe.each(ootbDashboards)('when current dashboard is OOTB', dashboard => {
beforeEach(() => {
setupAllDashboards(store, dashboard.path);
});
it('is rendered', () => {
expect(findDuplicateDashboardItem().exists()).toBe(true);
});
it('duplicate dashboard modal is rendered', () => {
expect(findDuplicateDashboardModal().exists()).toBe(true);
});
it('clicking on item opens up the duplicate dashboard modal', () => {
const modalId = 'duplicateDashboard';
const modalTrigger = findDuplicateDashboardItem();
const rootEmit = jest.spyOn(wrapper.vm.$root, '$emit');
modalTrigger.trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(rootEmit.mock.calls[0]).toContainEqual(modalId);
});
});
});
describe('when current dashboard is custom', () => {
beforeEach(() => {
setupAllDashboards(store, customDashboard.path);
});
it('is not rendered', () => {
expect(findDuplicateDashboardItem().exists()).toBe(false);
});
it('duplicate dashboard modal is not rendered', () => {
expect(findDuplicateDashboardModal().exists()).toBe(false);
});
});
describe('when no dashboard is set', () => {
it('is not rendered', () => {
expect(findDuplicateDashboardItem().exists()).toBe(false);
});
it('duplicate dashboard modal is not rendered', () => {
expect(findDuplicateDashboardModal().exists()).toBe(false);
});
});
describe('when a dashboard has been duplicated in the duplicate dashboard modal', () => {
beforeEach(() => {
store.state.monitoringDashboard.projectPath = 'root/sandbox';
setupAllDashboards(store, dashboardGitResponse[0].path);
});
it('redirects to the newly created dashboard', () => {
delete window.location;
window.location = new URL('https://localhost');
const newDashboard = dashboardGitResponse[1];
const newDashboardUrl = 'root/sandbox/-/metrics/dashboard.yml';
findDuplicateDashboardModal().vm.$emit('dashboardDuplicated', newDashboard);
return wrapper.vm.$nextTick().then(() => {
expect(redirectTo).toHaveBeenCalled();
expect(redirectTo).toHaveBeenCalledWith(newDashboardUrl);
});
});
});
});
describe('star dashboard item', () => {
beforeEach(() => {
createShallowWrapper();
setupAllDashboards(store);
jest.spyOn(store, 'dispatch').mockResolvedValue();
});
it('is shown', () => {
expect(findStarDashboardItem().exists()).toBe(true);
});
it('is not disabled', () => {
expect(findStarDashboardItem().attributes('disabled')).toBeFalsy();
});
it('is disabled when starring is taking place', () => {
store.commit(`monitoringDashboard/${types.REQUEST_DASHBOARD_STARRING}`);
return wrapper.vm.$nextTick(() => {
expect(findStarDashboardItem().exists()).toBe(true);
expect(findStarDashboardItem().attributes('disabled')).toBe('true');
});
});
it('on click it dispatches a toggle star action', () => {
findStarDashboardItem().vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(store.dispatch).toHaveBeenCalledWith(
'monitoringDashboard/toggleStarredValue',
undefined,
);
});
});
describe('when dashboard is not starred', () => {
beforeEach(() => {
store.commit(`monitoringDashboard/${types.SET_INITIAL_STATE}`, {
currentDashboard: dashboardGitResponse[0].path,
});
return wrapper.vm.$nextTick();
});
it('item text shows "Star dashboard"', () => {
expect(findStarDashboardItem().html()).toMatch(/Star dashboard/);
});
});
describe('when dashboard is starred', () => {
beforeEach(() => {
store.commit(`monitoringDashboard/${types.SET_INITIAL_STATE}`, {
currentDashboard: dashboardGitResponse[1].path,
});
return wrapper.vm.$nextTick();
});
it('item text shows "Unstar dashboard"', () => {
expect(findStarDashboardItem().html()).toMatch(/Unstar dashboard/);
});
});
});
describe('create dashboard item', () => {
beforeEach(() => {
createShallowWrapper();
});
it('is rendered by default but it is disabled', () => {
expect(findCreateDashboardItem().attributes('disabled')).toBe('true');
});
describe('when project path is set', () => {
const mockProjectPath = 'root/sandbox';
const mockAddDashboardDocPath = '/doc/add-dashboard';
beforeEach(() => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
store.state.monitoringDashboard.addDashboardDocumentationPath = mockAddDashboardDocPath;
});
it('is not disabled', () => {
expect(findCreateDashboardItem().attributes('disabled')).toBe(undefined);
});
it('renders a modal for creating a dashboard', () => {
expect(findCreateDashboardModal().exists()).toBe(true);
});
it('clicking opens up the modal', () => {
const modalId = 'createDashboard';
const modalTrigger = findCreateDashboardItem();
const rootEmit = jest.spyOn(wrapper.vm.$root, '$emit');
modalTrigger.trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(rootEmit.mock.calls[0]).toContainEqual(modalId);
});
});
it('modal gets passed correct props', () => {
expect(findCreateDashboardModal().props('projectPath')).toBe(mockProjectPath);
expect(findCreateDashboardModal().props('addDashboardDocumentationPath')).toBe(
mockAddDashboardDocPath,
);
});
});
describe('when project path is not set', () => {
beforeEach(() => {
store.state.monitoringDashboard.projectPath = null;
});
it('is disabled', () => {
expect(findCreateDashboardItem().attributes('disabled')).toBe('true');
});
it('does not render a modal for creating a dashboard', () => {
expect(findCreateDashboardModal().exists()).toBe(false);
});
});
});
});
......@@ -6,8 +6,7 @@ import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_p
import RefreshButton from '~/monitoring/components/refresh_button.vue';
import DashboardHeader from '~/monitoring/components/dashboard_header.vue';
import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue';
import DuplicateDashboardModal from '~/monitoring/components/duplicate_dashboard_modal.vue';
import CreateDashboardModal from '~/monitoring/components/create_dashboard_modal.vue';
import ActionsMenu from '~/monitoring/components/dashboard_actions_menu.vue';
import { setupAllDashboards, setupStoreWithDashboard, setupStoreWithData } from '../store_utils';
import {
environmentData,
......@@ -18,7 +17,6 @@ import {
import { redirectTo } from '~/lib/utils/url_utility';
const mockProjectPath = 'https://path/to/project';
const mockAddDashboardDocPath = '/doc/add-dashboard';
jest.mock('~/lib/utils/url_utility', () => ({
redirectTo: jest.fn(),
......@@ -41,13 +39,7 @@ describe('Dashboard header', () => {
const findDateTimePicker = () => wrapper.find(DateTimePicker);
const findRefreshButton = () => wrapper.find(RefreshButton);
const findActionsMenu = () => wrapper.find('[data-testid="actions-menu"]');
const findCreateDashboardMenuItem = () =>
findActionsMenu().find('[data-testid="action-create-dashboard"]');
const findCreateDashboardDuplicateItem = () =>
findActionsMenu().find('[data-testid="action-duplicate-dashboard"]');
const findDuplicateDashboardModal = () => wrapper.find(DuplicateDashboardModal);
const findCreateDashboardModal = () => wrapper.find('[data-testid="create-dashboard-modal"]');
const findActionsMenu = () => wrapper.find(ActionsMenu);
const setSearchTerm = searchTerm => {
store.commit(`monitoringDashboard/${types.SET_ENVIRONMENTS_FILTER}`, searchTerm);
......@@ -264,31 +256,6 @@ describe('Dashboard header', () => {
});
});
describe('when a dashboard has been duplicated in the duplicate dashboard modal', () => {
beforeEach(() => {
store.state.monitoringDashboard.projectPath = 'root/sandbox';
setupAllDashboards(store, dashboardGitResponse[0].path);
});
it('redirects to the newly created dashboard', () => {
delete window.location;
window.location = new URL('https://localhost');
const newDashboard = dashboardGitResponse[1];
createShallowWrapper();
const newDashboardUrl = 'root/sandbox/-/metrics/dashboard.yml';
findDuplicateDashboardModal().vm.$emit('dashboardDuplicated', newDashboard);
return wrapper.vm.$nextTick().then(() => {
expect(redirectTo).toHaveBeenCalled();
expect(redirectTo).toHaveBeenCalledWith(newDashboardUrl);
});
});
});
describe('external dashboard link', () => {
beforeEach(() => {
store.state.monitoringDashboard.externalDashboardUrl = '/mockUrl';
......@@ -307,113 +274,97 @@ describe('Dashboard header', () => {
});
describe('actions menu', () => {
beforeEach(() => {
store.state.monitoringDashboard.projectPath = '';
createShallowWrapper();
});
const ootbDashboards = [
dashboardGitResponse[0].path,
selfMonitoringDashboardGitResponse[0].path,
];
const customDashboards = [
dashboardGitResponse[1].path,
selfMonitoringDashboardGitResponse[1].path,
];
it('is rendered if projectPath is set in store', () => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
it('is rendered', () => {
createShallowWrapper();
return wrapper.vm.$nextTick().then(() => {
expect(findActionsMenu().exists()).toBe(true);
});
expect(findActionsMenu().exists()).toBe(true);
});
it('is not rendered if projectPath is not set in store', () => {
expect(findActionsMenu().exists()).toBe(false);
});
describe('adding metrics prop', () => {
it.each(ootbDashboards)('gets passed true if current dashboard is OOTB', dashboardPath => {
createShallowWrapper({ customMetricsAvailable: true });
it('contains the create dashboard modal', () => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
store.state.monitoringDashboard.emptyState = false;
setupAllDashboards(store, dashboardPath);
return wrapper.vm.$nextTick().then(() => {
expect(findActionsMenu().contains(CreateDashboardModal)).toBe(true);
return wrapper.vm.$nextTick().then(() => {
expect(findActionsMenu().props('addingMetricsAvailable')).toBe(true);
});
});
});
const duplicableCases = [
null, // When no path is specified, it uses the overview dashboard path.
dashboardGitResponse[0].path,
dashboardGitResponse[2].path,
selfMonitoringDashboardGitResponse[0].path,
];
it.each(customDashboards)(
'gets passed false if current dashboard is custom',
dashboardPath => {
createShallowWrapper({ customMetricsAvailable: true });
describe.each(duplicableCases)(
'when the selected dashboard can be duplicated',
dashboardPath => {
it('contains menu items for "Create New", "Duplicate Dashboard" and a modal for duplicating dashboards', () => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
store.state.monitoringDashboard.emptyState = false;
setupAllDashboards(store, dashboardPath);
return wrapper.vm.$nextTick().then(() => {
expect(findCreateDashboardMenuItem().exists()).toBe(true);
expect(findCreateDashboardDuplicateItem().exists()).toBe(true);
expect(findDuplicateDashboardModal().exists()).toBe(true);
expect(findActionsMenu().props('addingMetricsAvailable')).toBe(false);
});
});
},
);
},
);
const nonDuplicableCases = [
dashboardGitResponse[1].path,
selfMonitoringDashboardGitResponse[1].path,
];
it('gets passed false if empty state is shown', () => {
createShallowWrapper({ customMetricsAvailable: true });
describe.each(nonDuplicableCases)(
'when the selected dashboard cannot be duplicated',
dashboardPath => {
it('contains a "Create New" menu item, but no "Duplicate Dashboard" menu item and modal', () => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
setupAllDashboards(store, dashboardPath);
store.state.monitoringDashboard.emptyState = true;
setupAllDashboards(store, ootbDashboards[0]);
return wrapper.vm.$nextTick().then(() => {
expect(findCreateDashboardMenuItem().exists()).toBe(true);
expect(findCreateDashboardDuplicateItem().exists()).toBe(false);
expect(findDuplicateDashboardModal().exists()).toBe(false);
});
return wrapper.vm.$nextTick().then(() => {
expect(findActionsMenu().props('addingMetricsAvailable')).toBe(false);
});
},
);
});
});
describe('actions menu modals', () => {
beforeEach(() => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
store.state.monitoringDashboard.addDashboardDocumentationPath = mockAddDashboardDocPath;
setupAllDashboards(store);
it('gets passed false if custom metrics are not available', () => {
createShallowWrapper({ customMetricsAvailable: false });
createShallowWrapper();
store.state.monitoringDashboard.emptyState = false;
setupAllDashboards(store, ootbDashboards[0]);
return wrapper.vm.$nextTick().then(() => {
expect(findActionsMenu().props('addingMetricsAvailable')).toBe(false);
});
});
});
it('Clicking on "Create New" opens up a modal', () => {
const modalId = 'createDashboard';
const modalTrigger = findCreateDashboardMenuItem();
const rootEmit = jest.spyOn(wrapper.vm.$root, '$emit');
it('custom metrics path gets passed', () => {
const path = 'https://path/to/customMetrics';
modalTrigger.trigger('click');
createShallowWrapper({ customMetricsPath: path });
return wrapper.vm.$nextTick().then(() => {
expect(rootEmit.mock.calls[0]).toContainEqual(modalId);
expect(findActionsMenu().props('customMetricsPath')).toBe(path);
});
});
it('"Create new dashboard" modal contains correct buttons', () => {
expect(findCreateDashboardModal().props('projectPath')).toBe(mockProjectPath);
expect(findCreateDashboardModal().props('addDashboardDocumentationPath')).toBe(
mockAddDashboardDocPath,
);
it('validate query path gets passed', () => {
const path = 'https://path/to/validateQuery';
createShallowWrapper({ validateQueryPath: path });
return wrapper.vm.$nextTick().then(() => {
expect(findActionsMenu().props('validateQueryPath')).toBe(path);
});
});
it('"Duplicate Dashboard" opens up a modal', () => {
const modalId = 'duplicateDashboard';
const modalTrigger = findCreateDashboardDuplicateItem();
const rootEmit = jest.spyOn(wrapper.vm.$root, '$emit');
it('default branch gets passed', () => {
const branch = 'branchName';
modalTrigger.trigger('click');
createShallowWrapper({ defaultBranch: branch });
return wrapper.vm.$nextTick().then(() => {
expect(rootEmit.mock.calls[0]).toContainEqual(modalId);
expect(findActionsMenu().props('defaultBranch')).toBe(branch);
});
});
});
......@@ -465,72 +416,4 @@ describe('Dashboard header', () => {
});
});
});
describe('Add metric button', () => {
const findAddMetricButton = () => wrapper.find('[data-qa-selector="add_metric_button"]');
it('is not rendered when custom metrics are not available', () => {
store.state.monitoringDashboard.emptyState = false;
createShallowWrapper({
customMetricsAvailable: false,
});
setupAllDashboards(store, dashboardGitResponse[0].path);
return wrapper.vm.$nextTick(() => {
expect(findAddMetricButton().exists()).toBe(false);
});
});
it('is not rendered when displaying empty state', () => {
store.state.monitoringDashboard.emptyState = true;
createShallowWrapper({
customMetricsAvailable: true,
});
setupAllDashboards(store, dashboardGitResponse[0].path);
return wrapper.vm.$nextTick(() => {
expect(findAddMetricButton().exists()).toBe(false);
});
});
describe('system dashboards', () => {
const systemDashboards = [
dashboardGitResponse[0].path,
selfMonitoringDashboardGitResponse[0].path,
];
const nonSystemDashboards = [
dashboardGitResponse[1].path,
dashboardGitResponse[2].path,
selfMonitoringDashboardGitResponse[1].path,
];
beforeEach(() => {
store.state.monitoringDashboard.emptyState = false;
createShallowWrapper({
customMetricsAvailable: true,
});
});
it.each(systemDashboards)('is rendered for system dashboards', dashboardPath => {
setupAllDashboards(store, dashboardPath);
return wrapper.vm.$nextTick(() => {
expect(findAddMetricButton().exists()).toBe(true);
});
});
it.each(nonSystemDashboards)('is not rendered for non-system dashboards', dashboardPath => {
setupAllDashboards(store, dashboardPath);
return wrapper.vm.$nextTick(() => {
expect(findAddMetricButton().exists()).toBe(false);
});
});
});
});
});
import { shallowMount, mount } from '@vue/test-utils';
import Tracking from '~/tracking';
import { ESC_KEY, ESC_KEY_IE11 } from '~/lib/utils/keys';
import { GlModal } from '@gitlab/ui';
import { objectToQuery } from '~/lib/utils/url_utility';
import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter';
......@@ -10,7 +8,6 @@ import { dashboardEmptyStates, metricStates } from '~/monitoring/constants';
import Dashboard from '~/monitoring/components/dashboard.vue';
import DashboardHeader from '~/monitoring/components/dashboard_header.vue';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import EmptyState from '~/monitoring/components/empty_state.vue';
import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import DashboardPanel from '~/monitoring/components/dashboard_panel.vue';
......@@ -42,8 +39,6 @@ describe('Dashboard', () => {
let wrapper;
let mock;
const findDashboardHeader = () => wrapper.find(DashboardHeader);
const createShallowWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(Dashboard, {
propsData: { ...dashboardProps, ...props },
......@@ -446,84 +441,6 @@ describe('Dashboard', () => {
});
});
describe('star dashboards', () => {
const findToggleStar = () => findDashboardHeader().find({ ref: 'toggleStarBtn' });
beforeEach(() => {
createShallowWrapper();
setupAllDashboards(store);
});
it('toggle star button is shown', () => {
expect(findToggleStar().exists()).toBe(true);
expect(findToggleStar().props('disabled')).toBe(false);
});
it('toggle star button is disabled when starring is taking place', () => {
store.commit(`monitoringDashboard/${types.REQUEST_DASHBOARD_STARRING}`);
return wrapper.vm.$nextTick(() => {
expect(findToggleStar().exists()).toBe(true);
expect(findToggleStar().props('disabled')).toBe(true);
});
});
describe('when the dashboard list is loaded', () => {
// Tooltip element should wrap directly
const getToggleTooltip = () => findToggleStar().element.parentElement.getAttribute('title');
beforeEach(() => {
setupAllDashboards(store);
jest.spyOn(store, 'dispatch');
});
it('dispatches a toggle star action', () => {
findToggleStar().vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(store.dispatch).toHaveBeenCalledWith(
'monitoringDashboard/toggleStarredValue',
undefined,
);
});
});
describe('when dashboard is not starred', () => {
beforeEach(() => {
store.commit(`monitoringDashboard/${types.SET_INITIAL_STATE}`, {
currentDashboard: dashboardGitResponse[0].path,
});
return wrapper.vm.$nextTick();
});
it('toggle star button shows "Star dashboard"', () => {
expect(getToggleTooltip()).toBe('Star dashboard');
});
it('toggle star button shows an unstarred state', () => {
expect(findToggleStar().attributes('icon')).toBe('star-o');
});
});
describe('when dashboard is starred', () => {
beforeEach(() => {
store.commit(`monitoringDashboard/${types.SET_INITIAL_STATE}`, {
currentDashboard: dashboardGitResponse[1].path,
});
return wrapper.vm.$nextTick();
});
it('toggle star button shows "Star dashboard"', () => {
expect(getToggleTooltip()).toBe('Unstar dashboard');
});
it('toggle star button shows a starred state', () => {
expect(findToggleStar().attributes('icon')).toBe('star');
});
});
});
});
describe('variables section', () => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
......@@ -800,33 +717,6 @@ describe('Dashboard', () => {
});
});
describe('dashboard edit link', () => {
const findEditLink = () => wrapper.find('.js-edit-link');
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
setupAllDashboards(store);
return wrapper.vm.$nextTick();
});
it('is not present for the overview dashboard', () => {
expect(findEditLink().exists()).toBe(false);
});
it('is present for a custom dashboard, and links to its edit_path', () => {
const dashboard = dashboardGitResponse[1];
store.commit(`monitoringDashboard/${types.SET_INITIAL_STATE}`, {
currentDashboard: dashboard.path,
});
return wrapper.vm.$nextTick().then(() => {
expect(findEditLink().exists()).toBe(true);
expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path);
});
});
});
describe('document title', () => {
const originalTitle = 'Original Title';
const overviewDashboardName = dashboardGitResponse[0].display_name;
......@@ -940,74 +830,4 @@ describe('Dashboard', () => {
expect(dashboardPanel.exists()).toBe(true);
});
});
describe('add custom metrics', () => {
const findAddMetricButton = () => findDashboardHeader().find({ ref: 'addMetricBtn' });
describe('when not available', () => {
beforeEach(() => {
createShallowWrapper({
hasMetrics: true,
customMetricsPath: '/endpoint',
});
});
it('does not render add button on the dashboard', () => {
expect(findAddMetricButton().exists()).toBe(false);
});
});
describe('when available', () => {
let origPage;
beforeEach(done => {
jest.spyOn(Tracking, 'event').mockReturnValue();
createShallowWrapper({
hasMetrics: true,
customMetricsPath: '/endpoint',
customMetricsAvailable: true,
});
setupStoreWithData(store);
origPage = document.body.dataset.page;
document.body.dataset.page = 'projects:environments:metrics';
wrapper.vm.$nextTick(done);
});
afterEach(() => {
document.body.dataset.page = origPage;
});
it('renders add button on the dashboard', () => {
expect(findAddMetricButton()).toBeDefined();
});
it('uses modal for custom metrics form', () => {
expect(wrapper.find(GlModal).exists()).toBe(true);
expect(wrapper.find(GlModal).attributes().modalid).toBe('addMetric');
});
it('adding new metric is tracked', done => {
const submitButton = wrapper
.find(DashboardHeader)
.find({ ref: 'submitCustomMetricsFormBtn' }).vm;
wrapper.vm.$nextTick(() => {
submitButton.$el.click();
wrapper.vm.$nextTick(() => {
expect(Tracking.event).toHaveBeenCalledWith(
document.body.dataset.page,
'click_button',
{
label: 'add_new_metric',
property: 'modal',
value: undefined,
},
);
done();
});
});
});
it('renders custom metrics form fields', () => {
expect(wrapper.find(CustomMetricsFormFields).exists()).toBe(true);
});
});
});
});
......@@ -622,3 +622,10 @@ export const dashboardHeaderProps = {
end: '2020-01-01T01:00:00.000Z',
},
};
export const dashboardActionsMenuProps = {
defaultBranch: 'master',
addingMetricsAvailable: true,
customMetricsPath: 'https://path/to/customMetrics',
validateQueryPath: 'https://path/to/validateQuery',
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment