dashboard_spec.js 12.5 KB
Newer Older
1
import Vue from 'vue';
2
import MockAdapter from 'axios-mock-adapter';
3
import Dashboard from '~/monitoring/components/dashboard.vue';
4
import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
5 6
import * as types from '~/monitoring/stores/mutation_types';
import { createStore } from '~/monitoring/stores';
7
import axios from '~/lib/utils/axios_utils';
8 9 10 11 12 13
import {
  metricsGroupsAPIResponse,
  mockApiEndpoint,
  environmentData,
  singleGroupResponse,
} from './mock_data';
14

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
const propsData = {
  hasMetrics: false,
  documentationPath: '/path/to/docs',
  settingsPath: '/path/to/settings',
  clustersPath: '/path/to/clusters',
  tagsPath: '/path/to/tags',
  projectPath: '/path/to/project',
  metricsEndpoint: mockApiEndpoint,
  deploymentEndpoint: null,
  emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
  emptyLoadingSvgPath: '/path/to/loading.svg',
  emptyNoDataSvgPath: '/path/to/no-data.svg',
  emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
  environmentsEndpoint: '/root/hello-prometheus/environments/35',
  currentEnvironmentName: 'production',
30 31 32
  customMetricsAvailable: false,
  customMetricsPath: '',
  validateQueryPath: '',
33 34 35 36
};

export default propsData;

37 38
describe('Dashboard', () => {
  let DashboardComponent;
39
  let mock;
40
  let store;
41

42
  beforeEach(() => {
43 44
    setFixtures(`
      <div class="prometheus-graphs"></div>
45
      <div class="layout-page"></div>
46
    `);
47

48 49 50
    window.gon = {
      ...window.gon,
      ee: false,
51 52 53
      features: {
        grafanaDashboardLink: true,
      },
54 55
    };

56
    store = createStore();
57
    mock = new MockAdapter(axios);
58
    DashboardComponent = Vue.extend(Dashboard);
59 60
  });

61 62 63 64
  afterEach(() => {
    mock.restore();
  });

65 66
  describe('no metrics are available yet', () => {
    it('shows a getting started empty state when no metrics are present', () => {
67
      const component = new DashboardComponent({
68
        el: document.querySelector('.prometheus-graphs'),
69
        propsData: { ...propsData, showTimeWindowDropdown: false },
70
        store,
71 72
      });

73
      expect(component.$el.querySelector('.prometheus-graphs')).toBe(null);
74
      expect(component.emptyState).toEqual('gettingStarted');
75 76 77 78 79
    });
  });

  describe('requests information to the server', () => {
    beforeEach(() => {
80
      mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
81 82
    });

83
    it('shows up a loading state', done => {
84
      const component = new DashboardComponent({
85
        el: document.querySelector('.prometheus-graphs'),
86
        propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: false },
87
        store,
88
      });
89

90
      Vue.nextTick(() => {
91
        expect(component.emptyState).toEqual('loading');
92 93 94
        done();
      });
    });
95

96
    it('hides the legend when showLegend is false', done => {
97
      const component = new DashboardComponent({
98
        el: document.querySelector('.prometheus-graphs'),
99 100 101 102 103 104
        propsData: {
          ...propsData,
          hasMetrics: true,
          showLegend: false,
          showTimeWindowDropdown: false,
        },
105
        store,
106 107 108 109
      });

      setTimeout(() => {
        expect(component.showEmptyState).toEqual(false);
110
        expect(component.$el.querySelector('.legend-group')).toEqual(null);
111 112 113 114 115
        expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
        done();
      });
    });

116
    it('hides the group panels when showPanels is false', done => {
117
      const component = new DashboardComponent({
118
        el: document.querySelector('.prometheus-graphs'),
119 120 121 122 123 124
        propsData: {
          ...propsData,
          hasMetrics: true,
          showPanels: false,
          showTimeWindowDropdown: false,
        },
125
        store,
126 127 128 129
      });

      setTimeout(() => {
        expect(component.showEmptyState).toEqual(false);
130
        expect(component.$el.querySelector('.prometheus-panel')).toEqual(null);
131 132 133 134
        expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
        done();
      });
    });
135

136
    it('renders the environments dropdown with a number of environments', done => {
137 138
      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
139 140 141 142 143 144
        propsData: {
          ...propsData,
          hasMetrics: true,
          showPanels: false,
          showTimeWindowDropdown: false,
        },
145
        store,
146 147
      });

148 149 150 151 152 153 154 155
      component.$store.commit(
        `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
        environmentData,
      );
      component.$store.commit(
        `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
        singleGroupResponse,
      );
156 157

      setTimeout(() => {
158 159 160
        const dropdownMenuEnvironments = component.$el.querySelectorAll(
          '.js-environments-dropdown .dropdown-item',
        );
161

162
        expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
163 164 165 166
        done();
      });
    });

167
    it('hides the environments dropdown list when there is no environments', done => {
168 169
      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
170 171 172 173 174 175
        propsData: {
          ...propsData,
          hasMetrics: true,
          showPanels: false,
          showTimeWindowDropdown: false,
        },
176
        store,
177 178
      });

179 180 181 182 183
      component.$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []);
      component.$store.commit(
        `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
        singleGroupResponse,
      );
184

185 186 187 188 189
      Vue.nextTick()
        .then(() => {
          const dropdownMenuEnvironments = component.$el.querySelectorAll(
            '.js-environments-dropdown .dropdown-item',
          );
190

191 192 193 194
          expect(dropdownMenuEnvironments.length).toEqual(0);
          done();
        })
        .catch(done.fail);
195 196
    });

197
    it('renders the environments dropdown with a single active element', done => {
198 199
      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
200 201 202 203 204 205
        propsData: {
          ...propsData,
          hasMetrics: true,
          showPanels: false,
          showTimeWindowDropdown: false,
        },
206
        store,
207 208
      });

209 210 211 212 213 214 215 216
      component.$store.commit(
        `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
        environmentData,
      );
      component.$store.commit(
        `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
        singleGroupResponse,
      );
217

218 219 220 221 222
      Vue.nextTick()
        .then(() => {
          const dropdownItems = component.$el.querySelectorAll(
            '.js-environments-dropdown .dropdown-item[active="true"]',
          );
223

224 225 226 227
          expect(dropdownItems.length).toEqual(1);
          done();
        })
        .catch(done.fail);
228 229 230 231 232 233 234 235 236 237
    });

    it('hides the dropdown', done => {
      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
        propsData: {
          ...propsData,
          hasMetrics: true,
          showPanels: false,
          environmentsEndpoint: '',
238
          showTimeWindowDropdown: false,
239
        },
240
        store,
241 242 243 244 245 246
      });

      Vue.nextTick(() => {
        const dropdownIsActiveElement = component.$el.querySelectorAll('.environments');

        expect(dropdownIsActiveElement.length).toEqual(0);
247 248 249
        done();
      });
    });
250

251
    it('does not show the time window dropdown when the feature flag is not set', done => {
252 253
      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
254 255 256 257 258 259
        propsData: {
          ...propsData,
          hasMetrics: true,
          showPanels: false,
          showTimeWindowDropdown: false,
        },
260
        store,
261 262 263 264 265 266 267 268 269 270 271
      });

      setTimeout(() => {
        const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown');

        expect(timeWindowDropdown).toBeNull();

        done();
      });
    });

272
    it('renders the time window dropdown with a set of options', done => {
273 274
      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
275 276 277 278 279 280
        propsData: {
          ...propsData,
          hasMetrics: true,
          showPanels: false,
          showTimeWindowDropdown: true,
        },
281
        store,
282 283 284 285
      });
      const numberOfTimeWindows = Object.keys(timeWindows).length;

      setTimeout(() => {
Jose Vargas's avatar
Jose Vargas committed
286
        const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown');
287
        const timeWindowDropdownEls = component.$el.querySelectorAll(
Jose Vargas's avatar
Jose Vargas committed
288
          '.js-time-window-dropdown .dropdown-item',
289 290 291 292 293 294 295 296
        );

        expect(timeWindowDropdown).not.toBeNull();
        expect(timeWindowDropdownEls.length).toEqual(numberOfTimeWindows);

        done();
      });
    });
297 298 299 300 301 302 303

    it('shows a specific time window selected from the url params', done => {
      spyOnDependency(Dashboard, 'getParameterValues').and.returnValue(['thirtyMinutes']);

      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
        propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: true },
304
        store,
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
      });

      setTimeout(() => {
        const selectedTimeWindow = component.$el.querySelector(
          '.js-time-window-dropdown [active="true"]',
        );

        expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes');
        done();
      });
    });

    it('defaults to the eight hours time window for non valid url parameters', done => {
      spyOnDependency(Dashboard, 'getParameterValues').and.returnValue([
        '<script>alert("XSS")</script>',
      ]);

      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
        propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: true },
325
        store,
326 327 328 329 330 331 332 333
      });

      Vue.nextTick(() => {
        expect(component.selectedTimeWindowKey).toEqual(timeWindowsKeyNames.eightHours);

        done();
      });
    });
334
  });
335 336 337 338 339 340 341 342 343 344 345

  describe('when the window resizes', () => {
    beforeEach(() => {
      mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
      jasmine.clock().install();
    });

    afterEach(() => {
      jasmine.clock().uninstall();
    });

346
    it('sets elWidth to page width when the sidebar is resized', done => {
347 348
      const component = new DashboardComponent({
        el: document.querySelector('.prometheus-graphs'),
349 350 351 352 353 354
        propsData: {
          ...propsData,
          hasMetrics: true,
          showPanels: false,
          showTimeWindowDropdown: false,
        },
355
        store,
356 357
      });

358
      expect(component.elWidth).toEqual(0);
359

360 361
      const pageLayoutEl = document.querySelector('.layout-page');
      pageLayoutEl.classList.add('page-with-icon-sidebar');
362 363 364 365 366 367 368

      Vue.nextTick()
        .then(() => {
          jasmine.clock().tick(1000);
          return Vue.nextTick();
        })
        .then(() => {
369
          expect(component.elWidth).toEqual(pageLayoutEl.clientWidth);
370 371 372 373 374
          done();
        })
        .catch(done.fail);
    });
  });
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395

  describe('external dashboard link', () => {
    let component;

    beforeEach(() => {
      mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
    });

    afterEach(() => {
      component.$destroy();
    });

    describe('with feature flag enabled', () => {
      beforeEach(() => {
        component = new DashboardComponent({
          el: document.querySelector('.prometheus-graphs'),
          propsData: {
            ...propsData,
            hasMetrics: true,
            showPanels: false,
            showTimeWindowDropdown: false,
396
            externalDashboardUrl: '/mockUrl',
397
          },
398
          store,
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
        });
      });

      it('shows the link', done => {
        setTimeout(() => {
          expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
            'View full dashboard',
          );
          done();
        });
      });
    });

    describe('without feature flage enabled', () => {
      beforeEach(() => {
        window.gon.features.grafanaDashboardLink = false;
        component = new DashboardComponent({
          el: document.querySelector('.prometheus-graphs'),
          propsData: {
            ...propsData,
            hasMetrics: true,
            showPanels: false,
            showTimeWindowDropdown: false,
422
            externalDashboardUrl: '',
423
          },
424
          store,
425 426 427 428 429 430 431 432 433 434 435
        });
      });

      it('does not show the link', done => {
        setTimeout(() => {
          expect(component.$el.querySelector('.js-external-dashboard-link')).toBe(null);
          done();
        });
      });
    });
  });
436
});