Commit 24bb2839 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'xanf-vtu-v1-custom-metrics-form' into 'master'

Upgrading VTU to v1: Refactor custom_metrics_form_fields_spec

See merge request gitlab-org/gitlab!50505
parents 0bcefe43 2d5c32b1
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants'; 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 CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
const { CancelToken } = axios; const { CancelToken } = axios;
describe('custom metrics form fields component', () => { describe('custom metrics form fields component', () => {
let component; let wrapper;
let mockAxios; let mockAxios;
const getNamedInput = (name) => component.element.querySelector(`input[name="${name}"]`); const getNamedInput = (name) => wrapper.element.querySelector(`input[name="${name}"]`);
const validateQueryPath = `${TEST_HOST}/mock/path`; const validateQueryPath = `${TEST_HOST}/mock/path`;
const validQueryResponse = { data: { success: true, query: { valid: true, error: '' } } }; const validQueryResponse = { success: true, query: { valid: true, error: '' } };
const csrfToken = 'mockToken'; const csrfToken = 'mockToken';
const formOperation = 'post'; const formOperation = 'post';
const debouncedValidateQueryMock = jest.fn();
const makeFormData = (data = {}) => ({ const makeFormData = (data = {}) => ({
formData: { formData: {
title: '', title: '',
...@@ -28,43 +26,42 @@ describe('custom metrics form fields component', () => { ...@@ -28,43 +26,42 @@ describe('custom metrics form fields component', () => {
...data, ...data,
}, },
}); });
const mountComponent = (props, methods = {}) => { const mountComponent = (props) => {
component = mount(CustomMetricsFormFields, { wrapper = mount(CustomMetricsFormFields, {
propsData: { propsData: {
formOperation, formOperation,
validateQueryPath, validateQueryPath,
...props, ...props,
}, },
csrfToken, csrfToken,
methods,
}); });
}; };
beforeEach(() => { beforeEach(() => {
mockAxios = new MockAdapter(axios); mockAxios = new MockAdapter(axios);
mockAxios.onPost(validateQueryPath).reply(validQueryResponse);
}); });
afterEach(() => { afterEach(() => {
component.destroy(); wrapper.destroy();
mockAxios.restore(); mockAxios.restore();
}); });
it('checks form validity', (done) => { it('checks form validity', async () => {
mockAxios.onPost(validateQueryPath).reply(200, validQueryResponse);
mountComponent({ mountComponent({
metricPersisted: true, metricPersisted: true,
...makeFormData({ ...makeFormData({
title: 'title', title: 'title-old',
yLabel: 'yLabel', yLabel: 'yLabel',
unit: 'unit', unit: 'unit',
group: 'group', group: 'group',
}), }),
}); });
component.vm.$nextTick(() => { wrapper.find(`input[name="prometheus_metric[query]"]`).setValue('query');
expect(component.vm.formIsValid).toBe(false); await axios.waitForAll();
done();
}); expect(wrapper.emitted('formValidation')).toStrictEqual([[true]]);
}); });
describe('hidden inputs', () => { describe('hidden inputs', () => {
...@@ -110,9 +107,6 @@ describe('custom metrics form fields component', () => { ...@@ -110,9 +107,6 @@ describe('custom metrics form fields component', () => {
describe('query input', () => { describe('query input', () => {
const queryInputName = 'prometheus_metric[query]'; const queryInputName = 'prometheus_metric[query]';
beforeEach(() => {
mockAxios.onPost(validateQueryPath).reply(validQueryResponse);
});
it('is empty by default', () => { it('is empty by default', () => {
mountComponent(); mountComponent();
...@@ -135,147 +129,65 @@ describe('custom metrics form fields component', () => { ...@@ -135,147 +129,65 @@ describe('custom metrics form fields component', () => {
jest.runAllTimers(); jest.runAllTimers();
}); });
it('checks validity on user input', () => { it('checks validity on user input', async () => {
const query = 'changedQuery'; const query = 'changedQuery';
mountComponent( mountComponent();
{},
{ expect(mockAxios.history.post).toHaveLength(0);
debouncedValidateQuery: debouncedValidateQueryMock, const queryInput = wrapper.find(`input[name="${queryInputName}"]`);
},
);
const queryInput = component.find(`input[name="${queryInputName}"]`);
queryInput.setValue(query); queryInput.setValue(query);
queryInput.trigger('input');
expect(debouncedValidateQueryMock).toHaveBeenCalledWith(query); await axios.waitForAll();
expect(mockAxios.history.post).toHaveLength(1);
}); });
describe('when query validation is in flight', () => { describe('when query validation is in flight', () => {
beforeEach(() => { beforeEach(() => {
mountComponent( mountComponent({ metricPersisted: true, ...makeFormData({ query: 'validQuery' }) });
{ metricPersisted: true, ...makeFormData({ query: 'validQuery' }) }, mockAxios.onPost(validateQueryPath).reply(200, validQueryResponse);
{
requestValidation: jest.fn().mockImplementation(
() =>
new Promise((resolve) =>
setTimeout(() => {
resolve(validQueryResponse);
}, 4000),
),
),
},
);
}); });
afterEach(() => { it('expect loading message to display', async () => {
jest.clearAllTimers(); const queryInput = wrapper.find(`input[name="${queryInputName}"]`);
});
it('expect queryValidateInFlight is in flight', (done) => {
const queryInput = component.find(`input[name="${queryInputName}"]`);
queryInput.setValue('query'); 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) => { expect(wrapper.text()).toContain('Validating query');
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) => { it('expect loading message to disappear', async () => {
const queryInput = component.find(`input[name="${queryInputName}"]`); const queryInput = wrapper.find(`input[name="${queryInputName}"]`);
queryInput.setValue('query'); queryInput.setValue('query');
queryInput.trigger('input');
component.vm.$nextTick(() => { await axios.waitForAll();
jest.runOnlyPendingTimers(); expect(wrapper.text()).not.toContain('Validating query');
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', () => { describe('when query is invalid', () => {
const errorMessage = 'mockErrorMessage'; const errorMessage = 'mockErrorMessage';
const invalidQueryResponse = { const invalidQueryResponse = { success: true, query: { valid: false, error: errorMessage } };
data: { success: true, query: { valid: false, error: errorMessage } },
};
beforeEach(() => { beforeEach(() => {
mountComponent( mockAxios.onPost(validateQueryPath).reply(200, invalidQueryResponse);
{ metricPersisted: true, ...makeFormData({ query: 'invalidQuery' }) }, mountComponent({ metricPersisted: true, ...makeFormData({ query: 'invalidQuery' }) });
{ return axios.waitForAll();
requestValidation: jest
.fn()
.mockImplementation(() => Promise.resolve(invalidQueryResponse)),
},
);
}); });
it('sets queryIsValid to false', (done) => { it('shows invalid query message', async () => {
component.vm.$nextTick(() => { expect(wrapper.text()).toContain(errorMessage);
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', () => { describe('when query is valid', () => {
beforeEach(() => { beforeEach(() => {
mountComponent( mockAxios.onPost(validateQueryPath).reply(200, validQueryResponse);
{ metricPersisted: true, ...makeFormData({ query: 'validQuery' }) }, mountComponent({ metricPersisted: true, ...makeFormData({ query: 'validQuery' }) });
{
requestValidation: jest
.fn()
.mockImplementation(() => Promise.resolve(validQueryResponse)),
},
);
}); });
it('sets queryIsValid to true when query is valid', (done) => { it('shows valid query message', async () => {
component.vm.$nextTick(() => { await axios.waitForAll();
expect(component.vm.queryIsValid).toBe(true);
done();
});
});
it('shows valid query message', () => { expect(wrapper.text()).toContain('PromQL query is valid');
expect(component.text()).toContain('PromQL query is valid');
}); });
}); });
}); });
......
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