Commit 35023d43 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge branch 'revert-93f16f3d' into 'master'

Revert "Merge branch 'winh-fix-flaky-dashboard_spec' into 'master'"

Closes gitlab-ee#14795

See merge request gitlab-org/gitlab-ce!32753
parents 222d9e62 3d7b3300
...@@ -40,7 +40,6 @@ class CustomEnvironment extends JSDOMEnvironment { ...@@ -40,7 +40,6 @@ class CustomEnvironment extends JSDOMEnvironment {
this.global.fixturesBasePath = `${ROOT_PATH}/tmp/tests/frontend/fixtures${IS_EE ? '-ee' : ''}`; this.global.fixturesBasePath = `${ROOT_PATH}/tmp/tests/frontend/fixtures${IS_EE ? '-ee' : ''}`;
this.global.staticFixturesBasePath = `${ROOT_PATH}/spec/frontend/fixtures`; this.global.staticFixturesBasePath = `${ROOT_PATH}/spec/frontend/fixtures`;
this.global.IS_EE = IS_EE;
// Not yet supported by JSDOM: https://github.com/jsdom/jsdom/issues/317 // Not yet supported by JSDOM: https://github.com/jsdom/jsdom/issues/317
this.global.document.createRange = () => ({ this.global.document.createRange = () => ({
......
...@@ -73,9 +73,6 @@ expect.extend(customMatchers); ...@@ -73,9 +73,6 @@ expect.extend(customMatchers);
// Tech debt issue TBD // Tech debt issue TBD
testUtilsConfig.logModifiedComponents = false; testUtilsConfig.logModifiedComponents = false;
// Stub for URL.createObjectURL
window.URL.createObjectURL = function createObjectURL() {};
// Basic stub for MutationObserver // Basic stub for MutationObserver
global.MutationObserver = () => ({ global.MutationObserver = () => ({
disconnect: () => {}, disconnect: () => {},
......
import Vue from 'vue'; import Vue from 'vue';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlToast, GlDropdownItem } from '@gitlab/ui'; import { GlToast } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue'; import Dashboard from '~/monitoring/components/dashboard.vue';
import GraphGroup from '~/monitoring/components/graph_group.vue'; import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
import EmptyState from '~/monitoring/components/empty_state.vue';
import { timeWindows } from '~/monitoring/constants';
import * as types from '~/monitoring/stores/mutation_types'; import * as types from '~/monitoring/stores/mutation_types';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
// TODO: replace with dynamic fixture
// https://gitlab.com/gitlab-org/gitlab-ce/issues/62785
import MonitoringMock, { import MonitoringMock, {
metricsGroupsAPIResponse, metricsGroupsAPIResponse,
mockApiEndpoint, mockApiEndpoint,
environmentData, environmentData,
singleGroupResponse, singleGroupResponse,
dashboardGitResponse, dashboardGitResponse,
} from '../../../../spec/javascripts/monitoring/mock_data'; } from '../mock_data';
/* eslint-disable no-unused-vars */
/* eslint-disable no-undef */
// see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/32571#note_211860465
function setupComponentStore(component) {
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
metricsGroupsAPIResponse,
);
component.$store.commit(
`monitoringDashboard/${types.SET_QUERY_RESULT}`,
mockedQueryResultPayload,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
}
// Mock imported files while retaining the original behaviour
// See https://github.com/facebook/jest/issues/936#issuecomment-265074320
function mockMonitoringUtils() {
const original = require.requireActual('~/monitoring/utils');
return {
...original, // Pass down all the exported objects
getTimeDiff: jest.spyOn(original, 'getTimeDiff'),
};
}
jest.mock('~/monitoring/utils', () => mockMonitoringUtils());
const monitoringUtils = require.requireMock('~/monitoring/utils');
function mockUrlUtility() {
const original = require.requireActual('~/lib/utils/url_utility');
return {
...original, // Pass down all the exported objects
getParameterValues: jest.spyOn(original, 'getParameterValues'),
};
}
jest.mock('~/lib/utils/url_utility', () => mockUrlUtility());
const urlUtility = require.requireMock('~/lib/utils/url_utility');
const localVue = createLocalVue(); const localVue = createLocalVue();
const propsData = { const propsData = {
...@@ -128,7 +83,7 @@ describe('Dashboard', () => { ...@@ -128,7 +83,7 @@ describe('Dashboard', () => {
}); });
it('shows the environment selector', () => { it('shows the environment selector', () => {
expect(component.$el.querySelector('#monitor-environments-dropdown')).toBeTruthy(); expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
}); });
}); });
...@@ -140,7 +95,7 @@ describe('Dashboard', () => { ...@@ -140,7 +95,7 @@ describe('Dashboard', () => {
store, store,
}); });
expect(component.$el.querySelector('#monitor-environments-dropdown')).toBeTruthy(); expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
}); });
}); });
...@@ -162,27 +117,47 @@ describe('Dashboard', () => { ...@@ -162,27 +117,47 @@ describe('Dashboard', () => {
}); });
}); });
it('hides the legend when showLegend is false', done => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showLegend: false,
},
store,
});
setTimeout(() => {
expect(component.showEmptyState).toEqual(false);
expect(component.$el.querySelector('.legend-group')).toEqual(null);
expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
done();
});
});
it('hides the group panels when showPanels is false', done => { it('hides the group panels when showPanels is false', done => {
const wrapper = shallowMount(DashboardComponent, { component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { propsData: {
...propsData, ...propsData,
hasMetrics: true, hasMetrics: true,
showPanels: false, showPanels: false,
}, },
store, store,
sync: false,
localVue,
}); });
setImmediate(() => {
expect(wrapper.find(EmptyState).exists()).toBe(false); setTimeout(() => {
expect(wrapper.find(GraphGroup).exists()).toBe(true); expect(component.showEmptyState).toEqual(false);
expect(wrapper.find(GraphGroup).props().showPanels).toBe(false); expect(component.$el.querySelector('.prometheus-panel')).toEqual(null);
expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
done(); done();
}); });
}); });
it('renders the environments dropdown with a number of environments', () => { it('renders the environments dropdown with a number of environments', done => {
const wrapper = shallowMount(DashboardComponent, { component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { propsData: {
...propsData, ...propsData,
hasMetrics: true, hasMetrics: true,
...@@ -191,30 +166,38 @@ describe('Dashboard', () => { ...@@ -191,30 +166,38 @@ describe('Dashboard', () => {
store, store,
}); });
store.commit( component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData, environmentData,
); );
store.commit( component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse, singleGroupResponse,
); );
Vue.nextTick(() => { Vue.nextTick()
const dropdownMenuEnvironments = wrapper .then(() => {
.find('.js-environments-dropdown') const dropdownMenuEnvironments = component.$el.querySelectorAll(
.findAll(GlDropdownItem); '.js-environments-dropdown .dropdown-item',
);
expect(environmentData.length).toBeGreaterThan(0); expect(component.environments.length).toEqual(environmentData.length);
expect(dropdownMenuEnvironments.length).toEqual(environmentData.length); expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
dropdownMenuEnvironments.wrappers.forEach((value, index) => {
expect(value.attributes('href')).toEqual(environmentData[index].metrics_path); Array.from(dropdownMenuEnvironments).forEach((value, index) => {
}); if (environmentData[index].metrics_path) {
}); expect(value).toHaveAttr('href', environmentData[index].metrics_path);
}
});
done();
})
.catch(done.fail);
}); });
it('hides the environments dropdown list when there is no environments', () => { it('hides the environments dropdown list when there is no environments', done => {
const wrapper = shallowMount(DashboardComponent, { component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { propsData: {
...propsData, ...propsData,
hasMetrics: true, hasMetrics: true,
...@@ -223,48 +206,54 @@ describe('Dashboard', () => { ...@@ -223,48 +206,54 @@ describe('Dashboard', () => {
store, store,
}); });
const findEnvironmentsDropdownItems = () => wrapper.find('#monitor-environments-wrapper'); component.$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []);
component.$store.commit(
store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []);
store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse, singleGroupResponse,
); );
return Vue.nextTick(() => { Vue.nextTick()
expect(findEnvironmentsDropdownItems(wrapper).exists()).toEqual(false); .then(() => {
}); const dropdownMenuEnvironments = component.$el.querySelectorAll(
'.js-environments-dropdown .dropdown-item',
);
expect(dropdownMenuEnvironments.length).toEqual(0);
done();
})
.catch(done.fail);
}); });
it('renders the environments dropdown with a single active element', () => { it('renders the environments dropdown with a single active element', done => {
const wrapper = shallowMount(DashboardComponent, { component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { propsData: {
...propsData, ...propsData,
hasMetrics: true, hasMetrics: true,
showPanels: false, showPanels: false,
}, },
store, store,
sync: false,
localVue,
}); });
store.commit( component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData, environmentData,
); );
store.commit( component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse, singleGroupResponse,
); );
Vue.nextTick(() => { Vue.nextTick()
const activeDropdownMenuEnvironments = wrapper .then(() => {
.find('#monitor-environments-dropdown') const dropdownItems = component.$el.querySelectorAll(
.findAll(GlDropdownItem) '.js-environments-dropdown .dropdown-item.active',
.filter(item => item.attributes('active') === 'true'); );
expect(activeDropdownMenuEnvironments.length).toEqual(1); expect(dropdownItems.length).toEqual(1);
}); done();
})
.catch(done.fail);
}); });
it('hides the dropdown', done => { it('hides the dropdown', done => {
...@@ -288,40 +277,33 @@ describe('Dashboard', () => { ...@@ -288,40 +277,33 @@ describe('Dashboard', () => {
}); });
it('renders the time window dropdown with a set of options', done => { it('renders the time window dropdown with a set of options', done => {
const wrapper = shallowMount(DashboardComponent, { component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { propsData: {
...propsData, ...propsData,
hasMetrics: true, hasMetrics: true,
showPanels: false, showPanels: false,
sync: false,
}, },
store, store,
}); });
const numberOfTimeWindows = Object.keys(timeWindows).length; const numberOfTimeWindows = Object.keys(timeWindows).length;
setImmediate(() => { setTimeout(() => {
const timeWindowDropdown = wrapper.find('.js-time-window-dropdown'); const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown');
const timeWindowDropdownEls = wrapper const timeWindowDropdownEls = component.$el.querySelectorAll(
.find('.js-time-window-dropdown') '.js-time-window-dropdown .dropdown-item',
.findAll(GlDropdownItem); );
expect(timeWindowDropdown.exists()).toBe(true); expect(timeWindowDropdown).not.toBeNull();
expect(timeWindowDropdownEls.length).toEqual(numberOfTimeWindows); expect(timeWindowDropdownEls.length).toEqual(numberOfTimeWindows);
done(); done();
}); });
}); });
it('fetches the metrics data with proper time window', () => { it('fetches the metrics data with proper time window', done => {
jest.spyOn(store, 'dispatch').mockImplementationOnce(() => {}); component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
shallowMount(DashboardComponent, {
propsData: { propsData: {
...propsData, ...propsData,
hasMetrics: true, hasMetrics: true,
...@@ -330,49 +312,75 @@ describe('Dashboard', () => { ...@@ -330,49 +312,75 @@ describe('Dashboard', () => {
store, store,
}); });
const defaultRange = monitoringUtils.getTimeDiff(); spyOn(component.$store, 'dispatch').and.stub();
return Vue.nextTick().then(() => { const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff').and.callThrough();
expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/fetchData', defaultRange);
}); component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
component.$mount();
Vue.nextTick()
.then(() => {
expect(component.$store.dispatch).toHaveBeenCalled();
expect(getTimeDiffSpy).toHaveBeenCalled();
done();
})
.catch(done.fail);
}); });
it('shows a specific time window selected from the url params', done => { it('shows a specific time window selected from the url params', done => {
const start = 1564439536; const start = 1564439536;
const end = 1564441336; const end = 1564441336;
monitoringUtils.getTimeDiff.mockReturnValueOnce({ spyOnDependency(Dashboard, 'getTimeDiff').and.returnValue({
start, start,
end, end,
}); });
urlUtility.getParameterValues.mockImplementationOnce(param => { spyOnDependency(Dashboard, 'getParameterValues').and.callFake(param => {
if (param === 'start') return [start]; if (param === 'start') return [start];
if (param === 'end') return [end]; if (param === 'end') return [end];
return []; return [];
}); });
const wrapper = shallowMount(DashboardComponent, { component = new DashboardComponent({
propsData: { el: document.querySelector('.prometheus-graphs'),
...propsData, propsData: { ...propsData, hasMetrics: true },
hasMetrics: true,
showPanels: false,
},
store, store,
}); });
setImmediate(() => { setTimeout(() => {
const activeTimeWindowItems = wrapper const selectedTimeWindow = component.$el.querySelector('.js-time-window-dropdown .active');
.find('.js-time-window-dropdown')
.findAll(GlDropdownItem) expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes');
.filter(item => item.attributes('active') === 'true'); done();
});
});
expect(activeTimeWindowItems.length).toEqual(1); it('defaults to the eight hours time window for non valid url parameters', done => {
expect(activeTimeWindowItems.wrappers[0].text().trim()).toEqual('30 minutes'); spyOnDependency(Dashboard, 'getParameterValues').and.returnValue([
'<script>alert("XSS")</script>',
]);
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true },
store,
});
Vue.nextTick(() => {
expect(component.selectedTimeWindowKey).toEqual(timeWindowsKeyNames.eightHours);
done(); done();
}); });
}); });
}); });
describe('link to chart', () => { // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922
// eslint-disable-next-line jasmine/no-disabled-tests
xdescribe('link to chart', () => {
let wrapper; let wrapper;
const currentDashboard = 'TEST_DASHBOARD'; const currentDashboard = 'TEST_DASHBOARD';
localVue.use(GlToast); localVue.use(GlToast);
...@@ -384,13 +392,13 @@ describe('Dashboard', () => { ...@@ -384,13 +392,13 @@ describe('Dashboard', () => {
wrapper = shallowMount(DashboardComponent, { wrapper = shallowMount(DashboardComponent, {
localVue, localVue,
sync: false,
attachToDocument: true,
propsData: { ...propsData, hasMetrics: true, currentDashboard }, propsData: { ...propsData, hasMetrics: true, currentDashboard },
store, store,
}); });
setImmediate(() => { setTimeout(done);
done();
});
}); });
afterEach(() => { afterEach(() => {
...@@ -429,7 +437,7 @@ describe('Dashboard', () => { ...@@ -429,7 +437,7 @@ describe('Dashboard', () => {
}); });
it('creates a toast when clicked', () => { it('creates a toast when clicked', () => {
jest.spyOn(wrapper.vm.$toast, 'show').mockImplementation(() => {}); spyOn(wrapper.vm.$toast, 'show').and.stub();
link().vm.$emit('click'); link().vm.$emit('click');
...@@ -440,6 +448,11 @@ describe('Dashboard', () => { ...@@ -440,6 +448,11 @@ describe('Dashboard', () => {
describe('when the window resizes', () => { describe('when the window resizes', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
jasmine.clock().install();
});
afterEach(() => {
jasmine.clock().uninstall();
}); });
it('sets elWidth to page width when the sidebar is resized', done => { it('sets elWidth to page width when the sidebar is resized', done => {
...@@ -460,7 +473,7 @@ describe('Dashboard', () => { ...@@ -460,7 +473,7 @@ describe('Dashboard', () => {
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
jest.advanceTimersByTime(1000); jasmine.clock().tick(1000);
return Vue.nextTick(); return Vue.nextTick();
}) })
.then(() => { .then(() => {
...@@ -472,12 +485,11 @@ describe('Dashboard', () => { ...@@ -472,12 +485,11 @@ describe('Dashboard', () => {
}); });
describe('external dashboard link', () => { describe('external dashboard link', () => {
let wrapper; beforeEach(() => {
beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
wrapper = shallowMount(DashboardComponent, { component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { propsData: {
...propsData, ...propsData,
hasMetrics: true, hasMetrics: true,
...@@ -486,53 +498,61 @@ describe('Dashboard', () => { ...@@ -486,53 +498,61 @@ describe('Dashboard', () => {
externalDashboardUrl: '/mockUrl', externalDashboardUrl: '/mockUrl',
}, },
store, store,
sync: false,
localVue,
}); });
setImmediate(done);
}); });
it('shows the link', () => { it('shows the link', done => {
expect(wrapper.find('.js-external-dashboard-link').text()).toContain('View full dashboard'); setTimeout(() => {
expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
'View full dashboard',
);
done();
});
}); });
}); });
describe('Dashboard dropdown', () => { describe('Dashboard dropdown', () => {
let wrapper; beforeEach(() => {
beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
wrapper = shallowMount(DashboardComponent, { component = new DashboardComponent({
propsData: { ...propsData, hasMetrics: true, showPanels: false }, el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
},
store, store,
sync: false,
localVue,
}); });
setImmediate(() => { component.$store.dispatch('monitoringDashboard/setFeatureFlags', {
store.dispatch('monitoringDashboard/setFeatureFlags', { prometheusEndpoint: false,
prometheusEndpoint: false, multipleDashboardsEnabled: true,
multipleDashboardsEnabled: true, });
});
store.commit( component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData, environmentData,
); );
store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse,
);
store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, dashboardGitResponse); component.$store.commit(
done(); `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
}); singleGroupResponse,
);
component.$store.commit(
`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
dashboardGitResponse,
);
}); });
it('shows the dashboard dropdown', () => { it('shows the dashboard dropdown', done => {
expect(wrapper.find('.js-dashboards-dropdown').exists()).toEqual(true); setTimeout(() => {
const dashboardDropdown = component.$el.querySelector('.js-dashboards-dropdown');
expect(dashboardDropdown).not.toEqual(null);
done();
});
}); });
}); });
...@@ -557,25 +577,13 @@ describe('Dashboard', () => { ...@@ -557,25 +577,13 @@ describe('Dashboard', () => {
const data = mockGraphData.queries[0].result[0].values; const data = mockGraphData.queries[0].result[0].values;
const firstRow = `${data[0][0]},${data[0][1]}`; const firstRow = `${data[0][0]},${data[0][1]}`;
expect(component.csvText(mockGraphData)).toContain(`${header}\r\n${firstRow}`); expect(component.csvText(mockGraphData)).toMatch(`^${header}\r\n${firstRow}`);
}); });
}); });
describe('downloadCsv', () => { describe('downloadCsv', () => {
let spy; it('produces a link with a Blob', () => {
expect(component.downloadCsv(mockGraphData)).toContain(`blob:`);
beforeEach(() => {
spy = jest.spyOn(window.URL, 'createObjectURL');
});
afterEach(() => {
spy.mockRestore();
});
it('creates a string containing a URL that represents the object', () => {
component.downloadCsv(mockGraphData);
expect(spy).toHaveBeenCalled();
}); });
}); });
}); });
......
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