summaryrefslogtreecommitdiff
path: root/spec/frontend/custom_metrics
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-08 21:09:50 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-08 21:09:50 +0000
commit76358aee81a471a5e71eaf3e8c2d91b7c9a0a5a9 (patch)
treedf9ba3dcc09eb404de31e0d79cb8f0b77812e655 /spec/frontend/custom_metrics
parent80e9fdc9682cfbcfb9202a2733605a6a6bd23f05 (diff)
downloadgitlab-ce-76358aee81a471a5e71eaf3e8c2d91b7c9a0a5a9.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/custom_metrics')
-rw-r--r--spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js334
-rw-r--r--spec/frontend/custom_metrics/components/custom_metrics_form_spec.js48
2 files changed, 382 insertions, 0 deletions
diff --git a/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js b/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js
new file mode 100644
index 00000000000..61cbef0c557
--- /dev/null
+++ b/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js
@@ -0,0 +1,334 @@
+import { mount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import { TEST_HOST } from 'helpers/test_constants';
+import waitForPromises from 'helpers/wait_for_promises';
+import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
+import axios from '~/lib/utils/axios_utils';
+
+const { CancelToken } = axios;
+
+describe('custom metrics form fields component', () => {
+ let component;
+ let mockAxios;
+
+ 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 debouncedValidateQueryMock = jest.fn();
+ const makeFormData = (data = {}) => ({
+ formData: {
+ title: '',
+ yLabel: '',
+ query: '',
+ unit: '',
+ group: '',
+ legend: '',
+ ...data,
+ },
+ });
+ const mountComponent = (props, methods = {}) => {
+ component = mount(CustomMetricsFormFields, {
+ propsData: {
+ formOperation,
+ validateQueryPath,
+ ...props,
+ },
+ csrfToken,
+ methods,
+ });
+ };
+
+ beforeEach(() => {
+ mockAxios = new MockAdapter(axios);
+ mockAxios.onPost(validateQueryPath).reply(validQueryResponse);
+ });
+
+ afterEach(() => {
+ component.destroy();
+ mockAxios.restore();
+ });
+
+ 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(false);
+ 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 queryInputName = 'prometheus_metric[query]';
+ beforeEach(() => {
+ mockAxios.onPost(validateQueryPath).reply(validQueryResponse);
+ });
+
+ it('is empty by default', () => {
+ mountComponent();
+
+ expect(getNamedInput(queryInputName).value).toBe('');
+ });
+
+ it('receives and validates a persisted value', () => {
+ const query = 'persistedQuery';
+ const axiosPost = jest.spyOn(axios, 'post');
+ const source = CancelToken.source();
+ mountComponent({ metricPersisted: true, ...makeFormData({ query }) });
+
+ expect(axiosPost).toHaveBeenCalledWith(
+ validateQueryPath,
+ { query },
+ { cancelToken: source.token },
+ );
+ expect(getNamedInput(queryInputName).value).toBe(query);
+ jest.runAllTimers();
+ });
+
+ it('checks validity on user input', () => {
+ const query = 'changedQuery';
+ mountComponent(
+ {},
+ {
+ debouncedValidateQuery: debouncedValidateQueryMock,
+ },
+ );
+ const queryInput = component.find(`input[name="${queryInputName}"]`);
+ queryInput.setValue(query);
+ queryInput.trigger('input');
+
+ expect(debouncedValidateQueryMock).toHaveBeenCalledWith(query);
+ });
+
+ describe('when query validation is in flight', () => {
+ beforeEach(() => {
+ jest.useFakeTimers();
+ mountComponent(
+ { metricPersisted: true, ...makeFormData({ query: 'validQuery' }) },
+ {
+ requestValidation: jest.fn().mockImplementation(
+ () =>
+ new Promise(resolve =>
+ setTimeout(() => {
+ resolve(validQueryResponse);
+ }, 4000),
+ ),
+ ),
+ },
+ );
+ });
+
+ afterEach(() => {
+ jest.clearAllTimers();
+ });
+
+ it('expect queryValidateInFlight is in flight', done => {
+ const queryInput = component.find(`input[name="${queryInputName}"]`);
+ queryInput.setValue('query');
+ queryInput.trigger('input');
+
+ component.vm.$nextTick(() => {
+ expect(component.vm.queryValidateInFlight).toBe(true);
+ jest.runOnlyPendingTimers();
+ waitForPromises()
+ .then(() => {
+ component.vm.$nextTick(() => {
+ expect(component.vm.queryValidateInFlight).toBe(false);
+ expect(component.vm.queryIsValid).toBe(true);
+ done();
+ });
+ })
+ .catch(done.fail);
+ });
+ });
+
+ it('expect loading message to display', done => {
+ const queryInput = component.find(`input[name="${queryInputName}"]`);
+ queryInput.setValue('query');
+ queryInput.trigger('input');
+ component.vm.$nextTick(() => {
+ expect(component.text()).toContain('Validating query');
+ jest.runOnlyPendingTimers();
+ done();
+ });
+ });
+
+ it('expect loading message to disappear', done => {
+ const queryInput = component.find(`input[name="${queryInputName}"]`);
+ queryInput.setValue('query');
+ queryInput.trigger('input');
+ component.vm.$nextTick(() => {
+ jest.runOnlyPendingTimers();
+ waitForPromises()
+ .then(() => {
+ component.vm.$nextTick(() => {
+ expect(component.vm.queryValidateInFlight).toBe(false);
+ expect(component.vm.queryIsValid).toBe(true);
+ expect(component.vm.errorMessage).toBe('');
+ done();
+ });
+ })
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('when query is invalid', () => {
+ const errorMessage = 'mockErrorMessage';
+ const invalidQueryResponse = {
+ data: { success: true, query: { valid: false, error: errorMessage } },
+ };
+
+ beforeEach(() => {
+ mountComponent(
+ { metricPersisted: true, ...makeFormData({ query: 'invalidQuery' }) },
+ {
+ requestValidation: jest
+ .fn()
+ .mockImplementation(() => Promise.resolve(invalidQueryResponse)),
+ },
+ );
+ });
+
+ it('sets queryIsValid to false', done => {
+ component.vm.$nextTick(() => {
+ expect(component.vm.queryValidateInFlight).toBe(false);
+ expect(component.vm.queryIsValid).toBe(false);
+ done();
+ });
+ });
+
+ it('shows invalid query message', done => {
+ component.vm.$nextTick(() => {
+ expect(component.text()).toContain(errorMessage);
+ done();
+ });
+ });
+ });
+
+ describe('when query is valid', () => {
+ beforeEach(() => {
+ mountComponent(
+ { metricPersisted: true, ...makeFormData({ query: 'validQuery' }) },
+ {
+ requestValidation: jest
+ .fn()
+ .mockImplementation(() => Promise.resolve(validQueryResponse)),
+ },
+ );
+ });
+
+ 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);
+ });
+ });
+});
diff --git a/spec/frontend/custom_metrics/components/custom_metrics_form_spec.js b/spec/frontend/custom_metrics/components/custom_metrics_form_spec.js
new file mode 100644
index 00000000000..384d6699150
--- /dev/null
+++ b/spec/frontend/custom_metrics/components/custom_metrics_form_spec.js
@@ -0,0 +1,48 @@
+import { shallowMount } from '@vue/test-utils';
+import CustomMetricsForm from '~/custom_metrics/components/custom_metrics_form.vue';
+
+describe('CustomMetricsForm', () => {
+ let wrapper;
+
+ function mountComponent({
+ metricPersisted = false,
+ formData = {
+ title: '',
+ query: '',
+ yLabel: '',
+ unit: '',
+ group: '',
+ legend: '',
+ },
+ }) {
+ wrapper = shallowMount(CustomMetricsForm, {
+ propsData: {
+ customMetricsPath: '',
+ editProjectServicePath: '',
+ metricPersisted,
+ validateQueryPath: '',
+ formData,
+ },
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('Computed', () => {
+ it('Form button and title text indicate the custom metric is being edited', () => {
+ mountComponent({ metricPersisted: true });
+
+ expect(wrapper.vm.saveButtonText).toBe('Save Changes');
+ expect(wrapper.vm.titleText).toBe('Edit metric');
+ });
+
+ it('Form button and title text indicate the custom metric is being created', () => {
+ mountComponent({ metricPersisted: false });
+
+ expect(wrapper.vm.saveButtonText).toBe('Create metric');
+ expect(wrapper.vm.titleText).toBe('New metric');
+ });
+ });
+});