Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
190b76ae
Commit
190b76ae
authored
May 01, 2019
by
Adriel Santiago
Committed by
Clement Ho
May 01, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move custom metrics form to its own component
Allow for reuse of the custom metric form fields
parent
ceff1529
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
499 additions
and
209 deletions
+499
-209
ee/app/assets/javascripts/custom_metrics/components/custom_metrics_form.vue
...scripts/custom_metrics/components/custom_metrics_form.vue
+17
-193
ee/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
.../custom_metrics/components/custom_metrics_form_fields.vue
+234
-0
ee/app/assets/javascripts/custom_metrics/constants.js
ee/app/assets/javascripts/custom_metrics/constants.js
+8
-1
ee/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js
...tom_metrics/components/custom_metrics_form_fields_spec.js
+228
-0
ee/spec/javascripts/custom_metrics/custom_metrics_form_spec.js
...ec/javascripts/custom_metrics/custom_metrics_form_spec.js
+0
-15
locale/gitlab.pot
locale/gitlab.pot
+12
-0
No files found.
ee/app/assets/javascripts/custom_metrics/components/custom_metrics_form.vue
View file @
190b76ae
<
script
>
import
{
GlFormInput
,
GlButton
,
GlLink
,
GlFormGroup
,
GlFormRadioGroup
}
from
'
@gitlab/ui
'
;
import
_
from
'
underscore
'
;
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
csrf
from
'
~/lib/utils/csrf
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
CustomMetricsFormFields
from
'
./custom_metrics_form_fields.vue
'
;
import
DeleteCustomMetricModal
from
'
./delete_custom_metric_modal.vue
'
;
import
QueryTypes
from
'
../constants
'
;
import
{
formDataValidator
}
from
'
../constants
'
;
export
default
{
components
:
{
CustomMetricsFormFields
,
DeleteCustomMetricModal
,
GlFormInput
,
GlButton
,
GlLink
,
GlFormGroup
,
GlFormRadioGroup
,
Icon
,
},
props
:
{
customMetricsPath
:
{
...
...
@@ -39,220 +33,50 @@ export default {
formData
:
{
type
:
Object
,
required
:
true
,
validator
:
val
=>
{
const
fieldNames
=
Object
.
keys
(
val
);
const
requiredFields
=
[
'
title
'
,
'
query
'
,
'
yLabel
'
,
'
unit
'
,
'
group
'
,
'
legend
'
];
return
requiredFields
.
every
(
name
=>
fieldNames
.
includes
(
name
));
},
validator
:
formDataValidator
,
},
},
data
()
{
return
{
validCustomQuery
:
null
,
formIsValid
:
null
,
errorMessage
:
''
,
formGroupOptions
:
[
{
text
:
__
(
'
Business
'
),
value
:
QueryTypes
.
business
},
{
text
:
__
(
'
Response
'
),
value
:
QueryTypes
.
response
},
{
text
:
__
(
'
System
'
),
value
:
QueryTypes
.
system
},
],
};
},
computed
:
{
disabledForm
()
{
return
this
.
validCustomQuery
;
},
saveButtonText
()
{
return
this
.
metricPersisted
?
__
(
'
Save Changes
'
)
:
s__
(
'
Metrics|Create metric
'
);
},
titleText
()
{
return
this
.
metricPersisted
?
s__
(
'
Metrics|Edit metric
'
)
:
s__
(
'
Metrics|New metric
'
);
},
validQueryMsg
()
{
return
this
.
validCustomQuery
?
s__
(
'
Metrics|PromQL query is valid
'
)
:
''
;
},
invalidQueryMsg
()
{
return
!
this
.
validCustomQuery
?
this
.
errorMessage
:
''
;
},
},
created
()
{
this
.
csrf
=
csrf
.
token
!=
null
?
csrf
.
token
:
''
;
this
.
formOperation
=
this
.
metricPersisted
?
'
patch
'
:
'
post
'
;
this
.
formData
.
group
=
this
.
formData
.
group
.
length
?
this
.
formData
.
group
:
QueryTypes
.
business
;
if
(
this
.
metricPersisted
)
{
this
.
validate
();
}
},
methods
:
{
formValidation
(
isValid
)
{
this
.
formIsValid
=
isValid
;
},
submit
()
{
this
.
$refs
.
form
.
submit
();
},
validate
()
{
this
.
requestValidation
()
.
then
(
res
=>
{
const
response
=
res
.
data
;
const
{
valid
,
error
}
=
response
.
query
;
if
(
response
.
success
)
{
this
.
errorMessage
=
valid
?
''
:
error
;
this
.
validCustomQuery
=
valid
;
}
else
{
throw
new
Error
(
'
There was an error trying to validate your query
'
);
}
})
.
catch
(()
=>
{
this
.
errorMessage
=
s__
(
'
Metrics|There was an error trying to validate your query
'
);
this
.
validCustomQuery
=
false
;
});
},
validateQuery
:
_
.
debounce
(
function
debounceValidateQuery
()
{
this
.
validate
();
},
500
),
requestValidation
()
{
return
axios
.
post
(
this
.
validateQueryPath
,
{
query
:
this
.
formData
.
query
,
});
},
},
QueryTypes
,
};
</
script
>
<
template
>
<div
class=
"row my-3"
>
<h4
class=
"text-center prepend-top-0"
>
{{
titleText
}}
</h4>
<form
ref=
"form"
class=
"col-lg-8 offset-lg-2"
:action=
"customMetricsPath"
method=
"post"
>
<input
ref=
"method"
type=
"hidden"
name=
"_method"
:value=
"formOperation"
/>
<input
:value=
"csrf"
type=
"hidden"
name=
"authenticity_token"
/>
<gl-form-group
:label=
"__('Name')"
label-for=
"prometheus_metric_title"
label-class=
"label-bold"
>
<gl-form-input
id=
"prometheus_metric_title"
v-model=
"formData.title"
:value=
"formData.title"
name=
"prometheus_metric[title]"
class=
"form-control"
:placeholder=
"s__('Metrics|e.g. Throughput')"
required
/>
<span
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|Used as a title for the chart
'
)
}}
</span>
</gl-form-group>
<gl-form-group
:label=
"__('Type')"
label-for=
"prometheus_metric_group"
label-class=
"label-bold"
>
<gl-form-radio-group
id=
"metric-group"
v-model=
"formData.group"
:options=
"formGroupOptions"
:checked=
"formData.group"
name=
"prometheus_metric[group]"
/>
<span
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|For grouping similar metrics
'
)
}}
</span>
</gl-form-group>
<gl-form-group
:label=
"__('Query')"
label-for=
"prometheus_metric_query"
label-class=
"label-bold"
:state=
"validCustomQuery"
>
<gl-form-input
id=
"prometheus_metric_query"
v-model=
"formData.query"
:value=
"formData.query"
name=
"prometheus_metric[query]"
class=
"form-control"
placeholder=
"e.g. rate(http_requests_total[5m])"
required
:state=
"validCustomQuery"
@
input=
"validateQuery"
/>
<slot
name=
"valid-feedback"
>
<span
class=
"form-text cgreen"
>
{{
validQueryMsg
}}
</span>
</slot>
<slot
name=
"invalid-feedback"
>
<span
class=
"form-text cred"
>
{{
invalidQueryMsg
}}
</span>
</slot>
<span
v-show=
"formData.query.length === 0"
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|Must be a valid PromQL query.
'
)
}}
<gl-link
href=
"https://prometheus.io/docs/prometheus/latest/querying/basics/"
tabindex=
"-1"
>
{{
s__
(
'
Metrics|Prometheus Query Documentation
'
)
}}
<icon
name=
"external-link"
:size=
"12"
/>
</gl-link>
</span>
</gl-form-group>
<gl-form-group
:label=
"s__('Metrics|Y-axis label')"
label-for=
"prometheus_metric_y_label"
label-class=
"label-bold"
>
<gl-form-input
id=
"prometheus_metric_y_label"
v-model=
"formData.yLabel"
:value=
"formData.yLabel"
name=
"prometheus_metric[y_label]"
class=
"form-control"
placeholder=
"e.g. Requests/second"
required
/>
<span
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|Label of the y-axis (usually the unit). The x-axis always represents time.
'
,
)
}}
</span>
</gl-form-group>
<gl-form-group
:label=
"s__('Metrics|Unit label')"
label-for=
"prometheus_metric_unit"
label-class=
"label-bold"
>
<gl-form-input
id=
"prometheus_metric_unit"
v-model=
"formData.unit"
:value=
"formData.unit"
name=
"prometheus_metric[unit]"
class=
"form-control"
placeholder=
"e.g. req/sec"
required
/>
</gl-form-group>
<gl-form-group
:label=
"s__('Metrics|Legend label (optional)')"
label-for=
"prometheus_metric_legend"
label-class=
"label-bold"
>
<gl-form-input
id=
"prometheus_metric_legend"
v-model=
"formData.legend"
:value=
"formData.legend"
name=
"prometheus_metric[legend]"
class=
"form-control"
placeholder=
"e.g. HTTP requests"
required
/>
<span
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response.
'
,
)
}}
</span>
</gl-form-group>
<custom-metrics-form-fields
:form-operation=
"formOperation"
:form-data=
"formData"
:metric-persisted=
"metricPersisted"
:validate-query-path=
"validateQueryPath"
@
formValidation=
"formValidation"
/>
<div
class=
"form-actions"
>
<gl-button
variant=
"success"
:disabled=
"!
disabledForm
"
@
click=
"submit"
>
<gl-button
variant=
"success"
:disabled=
"!
formIsValid
"
@
click=
"submit"
>
{{
saveButtonText
}}
</gl-button>
<gl-button
variant=
"secondary"
class=
"float-right"
:href=
"editProjectServicePath"
>
{{
...
...
ee/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
0 → 100644
View file @
190b76ae
<
script
>
import
{
GlFormInput
,
GlButton
,
GlLink
,
GlFormGroup
,
GlFormRadioGroup
}
from
'
@gitlab/ui
'
;
import
{
debounce
}
from
'
underscore
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
csrf
from
'
~/lib/utils/csrf
'
;
import
{
queryTypes
,
formDataValidator
}
from
'
../constants
'
;
export
default
{
components
:
{
GlFormInput
,
GlButton
,
GlLink
,
GlFormGroup
,
GlFormRadioGroup
,
Icon
,
},
props
:
{
formOperation
:
{
type
:
String
,
required
:
true
,
},
formData
:
{
type
:
Object
,
required
:
false
,
default
:
()
=>
({
title
:
''
,
yLabel
:
''
,
query
:
''
,
unit
:
''
,
group
:
''
,
legend
:
''
,
}),
validator
:
formDataValidator
,
},
metricPersisted
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
validateQueryPath
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
const
group
=
this
.
formData
.
group
.
length
?
this
.
formData
.
group
:
queryTypes
.
business
;
return
{
queryIsValid
:
null
,
...
this
.
formData
,
group
,
};
},
computed
:
{
formIsValid
()
{
return
!!
(
this
.
queryIsValid
&&
this
.
title
.
length
&&
this
.
yLabel
.
length
&&
this
.
unit
.
length
&&
this
.
group
.
length
);
},
validQueryMsg
()
{
return
this
.
queryIsValid
?
s__
(
'
Metrics|PromQL query is valid
'
)
:
''
;
},
invalidQueryMsg
()
{
return
!
this
.
queryIsValid
?
this
.
errorMessage
:
''
;
},
},
watch
:
{
formIsValid
(
value
)
{
this
.
$emit
(
'
formValidation
'
,
value
);
},
},
beforeMount
()
{
if
(
this
.
metricPersisted
)
{
this
.
validateQuery
();
}
},
methods
:
{
requestValidation
()
{
return
axios
.
post
(
this
.
validateQueryPath
,
{
query
:
this
.
query
,
});
},
validateQuery
()
{
this
.
requestValidation
()
.
then
(
res
=>
{
const
response
=
res
.
data
;
const
{
valid
,
error
}
=
response
.
query
;
if
(
response
.
success
)
{
this
.
errorMessage
=
valid
?
''
:
error
;
this
.
queryIsValid
=
valid
;
}
else
{
throw
new
Error
(
'
There was an error trying to validate your query
'
);
}
})
.
catch
(()
=>
{
this
.
errorMessage
=
s__
(
'
Metrics|There was an error trying to validate your query
'
);
this
.
queryIsValid
=
false
;
});
},
debouncedValidateQuery
:
debounce
(
function
checkQuery
()
{
this
.
validateQuery
();
},
500
),
},
csrfToken
:
csrf
.
token
||
''
,
formGroupOptions
:
[
{
text
:
__
(
'
Business
'
),
value
:
queryTypes
.
business
},
{
text
:
__
(
'
Response
'
),
value
:
queryTypes
.
response
},
{
text
:
__
(
'
System
'
),
value
:
queryTypes
.
system
},
],
};
</
script
>
<
template
>
<div>
<input
ref=
"method"
type=
"hidden"
name=
"_method"
:value=
"formOperation"
/>
<input
:value=
"$options.csrfToken"
type=
"hidden"
name=
"authenticity_token"
/>
<gl-form-group
:label=
"__('Name')"
label-for=
"prometheus_metric_title"
label-class=
"label-bold"
>
<gl-form-input
id=
"prometheus_metric_title"
v-model=
"title"
name=
"prometheus_metric[title]"
class=
"form-control"
:placeholder=
"s__('Metrics|e.g. Throughput')"
required
/>
<span
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|Used as a title for the chart
'
)
}}
</span>
</gl-form-group>
<gl-form-group
:label=
"__('Type')"
label-for=
"prometheus_metric_group"
label-class=
"label-bold"
>
<gl-form-radio-group
id=
"metric-group"
v-model=
"group"
:options=
"$options.formGroupOptions"
:checked=
"group"
name=
"prometheus_metric[group]"
/>
<span
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|For grouping similar metrics
'
)
}}
</span>
</gl-form-group>
<gl-form-group
:label=
"__('Query')"
label-for=
"prometheus_metric_query"
label-class=
"label-bold"
:state=
"queryIsValid"
>
<gl-form-input
id=
"prometheus_metric_query"
v-model=
"query"
name=
"prometheus_metric[query]"
class=
"form-control"
:placeholder=
"s__('Metrics|e.g. rate(http_requests_total[5m])')"
required
:state=
"queryIsValid"
@
input=
"debouncedValidateQuery"
/>
<slot
name=
"valid-feedback"
>
<span
class=
"form-text cgreen"
>
{{
validQueryMsg
}}
</span>
</slot>
<slot
name=
"invalid-feedback"
>
<span
class=
"form-text cred"
>
{{
invalidQueryMsg
}}
</span>
</slot>
<span
v-show=
"query.length === 0"
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|Must be a valid PromQL query.
'
)
}}
<gl-link
href=
"https://prometheus.io/docs/prometheus/latest/querying/basics/"
tabindex=
"-1"
>
{{
s__
(
'
Metrics|Prometheus Query Documentation
'
)
}}
<icon
name=
"external-link"
:size=
"12"
/>
</gl-link>
</span>
</gl-form-group>
<gl-form-group
:label=
"s__('Metrics|Y-axis label')"
label-for=
"prometheus_metric_y_label"
label-class=
"label-bold"
>
<gl-form-input
id=
"prometheus_metric_y_label"
v-model=
"yLabel"
name=
"prometheus_metric[y_label]"
class=
"form-control"
:placeholder=
"s__('Metrics|e.g. Requests/second')"
required
/>
<span
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|Label of the y-axis (usually the unit). The x-axis always represents time.
'
)
}}
</span>
</gl-form-group>
<gl-form-group
:label=
"s__('Metrics|Unit label')"
label-for=
"prometheus_metric_unit"
label-class=
"label-bold"
>
<gl-form-input
id=
"prometheus_metric_unit"
v-model=
"unit"
name=
"prometheus_metric[unit]"
class=
"form-control"
:placeholder=
"s__('Metrics|e.g. req/sec')"
required
/>
</gl-form-group>
<gl-form-group
:label=
"s__('Metrics|Legend label (optional)')"
label-for=
"prometheus_metric_legend"
label-class=
"label-bold"
>
<gl-form-input
id=
"prometheus_metric_legend"
v-model=
"legend"
name=
"prometheus_metric[legend]"
class=
"form-control"
:placeholder=
"s__('Metrics|e.g. HTTP requests')"
required
/>
<span
class=
"form-text text-muted"
>
{{
s__
(
'
Metrics|Used if the query returns a single series. If it returns multiple series, their legend labels will be picked up from the response.
'
,
)
}}
</span>
</gl-form-group>
</div>
</
template
>
ee/app/assets/javascripts/custom_metrics/constants.js
View file @
190b76ae
export
default
{
export
const
queryTypes
=
{
business
:
'
business
'
,
response
:
'
response
'
,
system
:
'
system
'
,
};
export
const
formDataValidator
=
val
=>
{
const
fieldNames
=
Object
.
keys
(
val
);
const
requiredFields
=
[
'
title
'
,
'
query
'
,
'
yLabel
'
,
'
unit
'
,
'
group
'
,
'
legend
'
];
return
requiredFields
.
every
(
name
=>
fieldNames
.
includes
(
name
));
};
ee/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js
0 → 100644
View file @
190b76ae
import
{
mount
}
from
'
@vue/test-utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
CustomMetricsFormFields
from
'
ee/custom_metrics/components/custom_metrics_form_fields.vue
'
;
jest
.
mock
(
'
~/lib/utils/axios_utils
'
);
describe
(
'
custom metrics form fields component
'
,
()
=>
{
let
component
;
const
getNamedInput
=
name
=>
component
.
element
.
querySelector
(
`input[name="
${
name
}
"]`
);
const
validateQueryPath
=
`
${
TEST_HOST
}
/mock/path`
;
const
validQueryResponse
=
{
data
:
{
success
:
true
,
query
:
{
valid
:
true
,
error
:
''
}
}
};
const
csrfToken
=
'
mockToken
'
;
const
formOperation
=
'
post
'
;
const
makeFormData
=
(
data
=
{})
=>
({
formData
:
{
title
:
''
,
yLabel
:
''
,
query
:
''
,
unit
:
''
,
group
:
''
,
legend
:
''
,
...
data
,
},
});
const
mountComponent
=
props
=>
{
component
=
mount
(
CustomMetricsFormFields
,
{
propsData
:
{
formOperation
,
validateQueryPath
,
...
props
,
},
csrfToken
,
sync
:
false
,
});
};
beforeEach
(()
=>
{
axios
.
post
.
mockRestore
();
axios
.
post
.
mockResolvedValue
(
validQueryResponse
);
});
afterEach
(()
=>
{
component
.
destroy
();
});
it
(
'
checks form validity
'
,
done
=>
{
mountComponent
({
metricPersisted
:
true
,
...
makeFormData
({
title
:
'
title
'
,
yLabel
:
'
yLabel
'
,
unit
:
'
unit
'
,
group
:
'
group
'
,
}),
});
component
.
vm
.
$nextTick
(()
=>
{
expect
(
component
.
vm
.
formIsValid
).
toBe
(
true
);
done
();
});
});
describe
(
'
hidden inputs
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
specifies form operation _method
'
,
()
=>
{
expect
(
getNamedInput
(
'
_method
'
,
'
input
'
).
value
).
toBe
(
'
post
'
);
});
it
(
'
specifies authenticity token
'
,
()
=>
{
expect
(
getNamedInput
(
'
authenticity_token
'
,
'
input
'
).
value
).
toBe
(
csrfToken
);
});
});
describe
(
'
name input
'
,
()
=>
{
const
name
=
'
prometheus_metric[title]
'
;
it
(
'
is empty by default
'
,
()
=>
{
mountComponent
();
expect
(
getNamedInput
(
name
).
value
).
toBe
(
''
);
});
it
(
'
receives a persisted value
'
,
()
=>
{
const
title
=
'
mockTitle
'
;
mountComponent
(
makeFormData
({
title
}));
expect
(
getNamedInput
(
name
).
value
).
toBe
(
title
);
});
});
describe
(
'
group input
'
,
()
=>
{
it
(
'
has a default value
'
,
()
=>
{
mountComponent
();
expect
(
getNamedInput
(
'
prometheus_metric[group]
'
,
'
glformradiogroup-stub
'
).
value
).
toBe
(
'
business
'
,
);
});
});
describe
(
'
query input
'
,
()
=>
{
const
name
=
'
prometheus_metric[query]
'
;
it
(
'
is empty by default
'
,
()
=>
{
mountComponent
();
expect
(
getNamedInput
(
name
).
value
).
toBe
(
''
);
});
it
(
'
receives and validates a persisted value
'
,
()
=>
{
const
query
=
'
persistedQuery
'
;
mountComponent
({
metricPersisted
:
true
,
...
makeFormData
({
query
})
});
expect
(
axios
.
post
).
toHaveBeenCalledWith
(
validateQueryPath
,
{
query
});
expect
(
getNamedInput
(
name
).
value
).
toBe
(
query
);
jest
.
runAllTimers
();
});
it
(
'
checks validity on user input
'
,
done
=>
{
const
query
=
'
changedQuery
'
;
mountComponent
();
const
spy
=
jest
.
spyOn
(
component
.
vm
,
'
debouncedValidateQuery
'
);
const
queryInput
=
getNamedInput
(
name
);
queryInput
.
value
=
query
;
queryInput
.
dispatchEvent
(
new
Event
(
'
input
'
));
component
.
vm
.
$nextTick
(()
=>
{
expect
(
spy
).
toHaveBeenCalledWith
(
query
);
done
();
});
});
describe
(
'
when query is invalid
'
,
()
=>
{
const
errorMessage
=
'
mockErrorMessage
'
;
const
invalidQueryResponse
=
{
data
:
{
success
:
true
,
query
:
{
valid
:
false
,
error
:
errorMessage
}
},
};
beforeEach
(()
=>
{
axios
.
post
.
mockResolvedValue
(
invalidQueryResponse
);
mountComponent
({
metricPersisted
:
true
,
...
makeFormData
({
query
:
'
invalidQuery
'
})
});
});
it
(
'
sets queryIsValid to false
'
,
done
=>
{
component
.
vm
.
$nextTick
(()
=>
{
expect
(
component
.
vm
.
queryIsValid
).
toBe
(
false
);
done
();
});
});
it
(
'
shows invalid query message
'
,
()
=>
{
expect
(
component
.
text
()).
toContain
(
errorMessage
);
});
});
describe
(
'
when query is valid
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
metricPersisted
:
true
,
...
makeFormData
({
query
:
'
validQuery
'
})
});
});
it
(
'
sets queryIsValid to true when query is valid
'
,
done
=>
{
component
.
vm
.
$nextTick
(()
=>
{
expect
(
component
.
vm
.
queryIsValid
).
toBe
(
true
);
done
();
});
});
it
(
'
shows valid query message
'
,
()
=>
{
expect
(
component
.
text
()).
toContain
(
'
PromQL query is valid
'
);
});
});
});
describe
(
'
yLabel input
'
,
()
=>
{
const
name
=
'
prometheus_metric[y_label]
'
;
it
(
'
is empty by default
'
,
()
=>
{
mountComponent
();
expect
(
getNamedInput
(
name
).
value
).
toBe
(
''
);
});
it
(
'
receives a persisted value
'
,
()
=>
{
const
yLabel
=
'
mockYLabel
'
;
mountComponent
(
makeFormData
({
yLabel
}));
expect
(
getNamedInput
(
name
).
value
).
toBe
(
yLabel
);
});
});
describe
(
'
unit input
'
,
()
=>
{
const
name
=
'
prometheus_metric[unit]
'
;
it
(
'
is empty by default
'
,
()
=>
{
mountComponent
();
expect
(
getNamedInput
(
name
).
value
).
toBe
(
''
);
});
it
(
'
receives a persisted value
'
,
()
=>
{
const
unit
=
'
mockUnit
'
;
mountComponent
(
makeFormData
({
unit
}));
expect
(
getNamedInput
(
name
).
value
).
toBe
(
unit
);
});
});
describe
(
'
legend input
'
,
()
=>
{
const
name
=
'
prometheus_metric[legend]
'
;
it
(
'
is empty by default
'
,
()
=>
{
mountComponent
();
expect
(
getNamedInput
(
name
).
value
).
toBe
(
''
);
});
it
(
'
receives a persisted value
'
,
()
=>
{
const
legend
=
'
mockLegend
'
;
mountComponent
(
makeFormData
({
legend
}));
expect
(
getNamedInput
(
name
).
value
).
toBe
(
legend
);
});
});
});
ee/spec/javascripts/custom_metrics/custom_metrics_form_spec.js
View file @
190b76ae
...
...
@@ -50,20 +50,5 @@ describe('CustomMetricsForm', () => {
expect
(
wrapper
.
vm
.
saveButtonText
).
toEqual
(
'
Create metric
'
);
expect
(
wrapper
.
vm
.
titleText
).
toEqual
(
'
New metric
'
);
});
it
(
'
Shows a correct validation message for a valid custom query
'
,
()
=>
{
mountComponent
({
metricPersisted
:
false
});
wrapper
.
vm
.
validCustomQuery
=
true
;
expect
(
wrapper
.
vm
.
validQueryMsg
).
toEqual
(
'
PromQL query is valid
'
);
});
it
(
'
Shows an incorrect validation message for an invalid custom query
'
,
()
=>
{
mountComponent
({
metricPersisted
:
false
});
wrapper
.
vm
.
validCustomQuery
=
false
;
wrapper
.
vm
.
errorMessage
=
'
parse error at char...
'
;
expect
(
wrapper
.
vm
.
invalidQueryMsg
).
toEqual
(
wrapper
.
vm
.
errorMessage
);
});
});
});
locale/gitlab.pot
View file @
190b76ae
...
...
@@ -7631,9 +7631,21 @@ msgstr ""
msgid "Metrics|You're about to permanently delete this metric. This cannot be undone."
msgstr ""
msgid "Metrics|e.g. HTTP requests"
msgstr ""
msgid "Metrics|e.g. Requests/second"
msgstr ""
msgid "Metrics|e.g. Throughput"
msgstr ""
msgid "Metrics|e.g. rate(http_requests_total[5m])"
msgstr ""
msgid "Metrics|e.g. req/sec"
msgstr ""
msgid "Migrated %{success_count}/%{total_count} files."
msgstr ""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment