summaryrefslogtreecommitdiff
path: root/spec/frontend
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-03 09:07:54 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-03 09:07:54 +0000
commit87ef501eacd66d7166183d20d84e33de022f7002 (patch)
treefa4e0f41e00a4b6aeb035530be4b5473f51b7a3d /spec/frontend
parentf321e51f46bcb628c3e96a44b5ebf3bb1c4033ab (diff)
downloadgitlab-ce-87ef501eacd66d7166183d20d84e33de022f7002.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/boards/components/boards_selector_spec.js155
-rw-r--r--spec/frontend/error_tracking/components/error_details_spec.js29
-rw-r--r--spec/frontend/error_tracking/components/error_tracking_list_spec.js6
-rw-r--r--spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap17
-rw-r--r--spec/frontend/monitoring/components/charts/options_spec.js60
-rw-r--r--spec/frontend/monitoring/components/charts/time_series_spec.js13
-rw-r--r--spec/frontend/monitoring/components/dashboard_spec.js13
-rw-r--r--spec/frontend/monitoring/mock_data.js7
-rw-r--r--spec/frontend/monitoring/store/utils_spec.js98
9 files changed, 374 insertions, 24 deletions
diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js
new file mode 100644
index 00000000000..7723af07d8c
--- /dev/null
+++ b/spec/frontend/boards/components/boards_selector_spec.js
@@ -0,0 +1,155 @@
+import Vue from 'vue';
+import { mount } from '@vue/test-utils';
+import { GlDropdown } from '@gitlab/ui';
+import { TEST_HOST } from 'spec/test_constants';
+import BoardsSelector from '~/boards/components/boards_selector.vue';
+import boardsStore from '~/boards/stores/boards_store';
+
+const throttleDuration = 1;
+
+function boardGenerator(n) {
+ return new Array(n).fill().map((board, id) => {
+ const name = `board${id}`;
+
+ return {
+ id,
+ name,
+ };
+ });
+}
+
+describe('BoardsSelector', () => {
+ let wrapper;
+ let allBoardsResponse;
+ let recentBoardsResponse;
+ const boards = boardGenerator(20);
+ const recentBoards = boardGenerator(5);
+
+ const fillSearchBox = filterTerm => {
+ const searchBox = wrapper.find({ ref: 'searchBox' });
+ const searchBoxInput = searchBox.find('input');
+ searchBoxInput.setValue(filterTerm);
+ searchBoxInput.trigger('input');
+ };
+
+ const getDropdownItems = () => wrapper.findAll('.js-dropdown-item');
+ const getDropdownHeaders = () => wrapper.findAll('.dropdown-bold-header');
+
+ beforeEach(() => {
+ boardsStore.setEndpoints({
+ boardsEndpoint: '',
+ recentBoardsEndpoint: '',
+ listsEndpoint: '',
+ bulkUpdatePath: '',
+ boardId: '',
+ });
+
+ allBoardsResponse = Promise.resolve({
+ data: boards,
+ });
+ recentBoardsResponse = Promise.resolve({
+ data: recentBoards,
+ });
+
+ boardsStore.allBoards = jest.fn(() => allBoardsResponse);
+ boardsStore.recentBoards = jest.fn(() => recentBoardsResponse);
+
+ const Component = Vue.extend(BoardsSelector);
+ wrapper = mount(Component, {
+ propsData: {
+ throttleDuration,
+ currentBoard: {
+ id: 1,
+ name: 'Development',
+ milestone_id: null,
+ weight: null,
+ assignee_id: null,
+ labels: [],
+ },
+ milestonePath: `${TEST_HOST}/milestone/path`,
+ boardBaseUrl: `${TEST_HOST}/board/base/url`,
+ hasMissingBoards: false,
+ canAdminBoard: true,
+ multipleIssueBoardsAvailable: true,
+ labelsPath: `${TEST_HOST}/labels/path`,
+ projectId: 42,
+ groupId: 19,
+ scopedIssueBoardFeatureEnabled: true,
+ weights: [],
+ },
+ attachToDocument: true,
+ });
+
+ // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
+ wrapper.find(GlDropdown).vm.$emit('show');
+
+ return Promise.all([allBoardsResponse, recentBoardsResponse]).then(() => Vue.nextTick());
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('filtering', () => {
+ it('shows all boards without filtering', () => {
+ expect(getDropdownItems().length).toBe(boards.length + recentBoards.length);
+ });
+
+ it('shows only matching boards when filtering', () => {
+ const filterTerm = 'board1';
+ const expectedCount = boards.filter(board => board.name.includes(filterTerm)).length;
+
+ fillSearchBox(filterTerm);
+
+ return Vue.nextTick().then(() => {
+ expect(getDropdownItems().length).toBe(expectedCount);
+ });
+ });
+
+ it('shows message if there are no matching boards', () => {
+ fillSearchBox('does not exist');
+
+ return Vue.nextTick().then(() => {
+ expect(getDropdownItems().length).toBe(0);
+ expect(wrapper.text().includes('No matching boards found')).toBe(true);
+ });
+ });
+ });
+
+ describe('recent boards section', () => {
+ it('shows only when boards are greater than 10', () => {
+ const expectedCount = 2; // Recent + All
+
+ expect(getDropdownHeaders().length).toBe(expectedCount);
+ });
+
+ it('does not show when boards are less than 10', () => {
+ wrapper.setData({
+ boards: boards.slice(0, 5),
+ });
+
+ return Vue.nextTick().then(() => {
+ expect(getDropdownHeaders().length).toBe(0);
+ });
+ });
+
+ it('does not show when recentBoards api returns empty array', () => {
+ wrapper.setData({
+ recentBoards: [],
+ });
+
+ return Vue.nextTick().then(() => {
+ expect(getDropdownHeaders().length).toBe(0);
+ });
+ });
+
+ it('does not show when search is active', () => {
+ fillSearchBox('Random string');
+
+ return Vue.nextTick().then(() => {
+ expect(getDropdownHeaders().length).toBe(0);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/error_tracking/components/error_details_spec.js b/spec/frontend/error_tracking/components/error_details_spec.js
index 94bf0189c91..e43f9569ffc 100644
--- a/spec/frontend/error_tracking/components/error_details_spec.js
+++ b/spec/frontend/error_tracking/components/error_details_spec.js
@@ -1,8 +1,15 @@
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { __ } from '~/locale';
-import { GlLoadingIcon, GlLink, GlBadge, GlFormInput, GlAlert, GlSprintf } from '@gitlab/ui';
-import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import {
+ GlButton,
+ GlLoadingIcon,
+ GlLink,
+ GlBadge,
+ GlFormInput,
+ GlAlert,
+ GlSprintf,
+} from '@gitlab/ui';
import Stacktrace from '~/error_tracking/components/stacktrace.vue';
import ErrorDetails from '~/error_tracking/components/error_details.vue';
import {
@@ -28,7 +35,7 @@ describe('ErrorDetails', () => {
function mountComponent() {
wrapper = shallowMount(ErrorDetails, {
- stubs: { LoadingButton, GlSprintf },
+ stubs: { GlButton, GlSprintf },
localVue,
store,
mocks,
@@ -127,7 +134,7 @@ describe('ErrorDetails', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.find(Stacktrace).exists()).toBe(false);
expect(wrapper.find(GlBadge).exists()).toBe(false);
- expect(wrapper.findAll('button').length).toBe(3);
+ expect(wrapper.findAll(GlButton).length).toBe(3);
});
describe('Badges', () => {
@@ -226,7 +233,7 @@ describe('ErrorDetails', () => {
it('should submit the form', () => {
window.HTMLFormElement.prototype.submit = () => {};
const submitSpy = jest.spyOn(wrapper.vm.$refs.sentryIssueForm, 'submit');
- wrapper.find('[data-qa-selector="create_issue_button"]').trigger('click');
+ wrapper.find('[data-qa-selector="create_issue_button"]').vm.$emit('click');
expect(submitSpy).toHaveBeenCalled();
submitSpy.mockRestore();
});
@@ -255,14 +262,14 @@ describe('ErrorDetails', () => {
});
it('marks error as ignored when ignore button is clicked', () => {
- findUpdateIgnoreStatusButton().trigger('click');
+ findUpdateIgnoreStatusButton().vm.$emit('click');
expect(actions.updateIgnoreStatus.mock.calls[0][1]).toEqual(
expect.objectContaining({ status: errorStatus.IGNORED }),
);
});
it('marks error as resolved when resolve button is clicked', () => {
- findUpdateResolveStatusButton().trigger('click');
+ findUpdateResolveStatusButton().vm.$emit('click');
expect(actions.updateResolveStatus.mock.calls[0][1]).toEqual(
expect.objectContaining({ status: errorStatus.RESOLVED }),
);
@@ -281,14 +288,14 @@ describe('ErrorDetails', () => {
});
it('marks error as unresolved when ignore button is clicked', () => {
- findUpdateIgnoreStatusButton().trigger('click');
+ findUpdateIgnoreStatusButton().vm.$emit('click');
expect(actions.updateIgnoreStatus.mock.calls[0][1]).toEqual(
expect.objectContaining({ status: errorStatus.UNRESOLVED }),
);
});
it('marks error as resolved when resolve button is clicked', () => {
- findUpdateResolveStatusButton().trigger('click');
+ findUpdateResolveStatusButton().vm.$emit('click');
expect(actions.updateResolveStatus.mock.calls[0][1]).toEqual(
expect.objectContaining({ status: errorStatus.RESOLVED }),
);
@@ -307,14 +314,14 @@ describe('ErrorDetails', () => {
});
it('marks error as ignored when ignore button is clicked', () => {
- findUpdateIgnoreStatusButton().trigger('click');
+ findUpdateIgnoreStatusButton().vm.$emit('click');
expect(actions.updateIgnoreStatus.mock.calls[0][1]).toEqual(
expect.objectContaining({ status: errorStatus.IGNORED }),
);
});
it('marks error as unresolved when unresolve button is clicked', () => {
- findUpdateResolveStatusButton().trigger('click');
+ findUpdateResolveStatusButton().vm.$emit('click');
expect(actions.updateResolveStatus.mock.calls[0][1]).toEqual(
expect.objectContaining({ status: errorStatus.UNRESOLVED }),
);
diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
index b632b461eb9..f852a3091aa 100644
--- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js
+++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
@@ -42,9 +42,6 @@ describe('ErrorTrackingList', () => {
...stubChildren(ErrorTrackingList),
...stubs,
},
- data() {
- return { errorSearchQuery: 'search' };
- },
});
}
@@ -164,8 +161,9 @@ describe('ErrorTrackingList', () => {
});
it('it searches by query', () => {
+ findSearchBox().vm.$emit('input', 'search');
findSearchBox().trigger('keyup.enter');
- expect(actions.searchByQuery.mock.calls[0][1]).toEqual(wrapper.vm.errorSearchQuery);
+ expect(actions.searchByQuery.mock.calls[0][1]).toBe('search');
});
it('it sorts by fields', () => {
diff --git a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap
index c705270343b..77f7f2e0609 100644
--- a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap
+++ b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap
@@ -72,7 +72,7 @@ exports[`Dashboard template matches the default snapshot 1`] = `
</gl-form-group-stub>
<gl-form-group-stub
- class="col-sm-6 col-md-6 col-lg-4"
+ class="col-sm-auto col-md-auto col-lg-auto"
label="Show last"
label-for="monitor-time-window-dropdown"
label-size="sm"
@@ -83,6 +83,21 @@ exports[`Dashboard template matches the default snapshot 1`] = `
/>
</gl-form-group-stub>
+ <gl-form-group-stub
+ class="col-sm-2 col-md-2 col-lg-1 refresh-dashboard-button"
+ >
+ <gl-button-stub
+ size="md"
+ title="Reload this page"
+ variant="default"
+ >
+ <icon-stub
+ name="repeat"
+ size="16"
+ />
+ </gl-button-stub>
+ </gl-form-group-stub>
+
<!---->
</div>
</div>
diff --git a/spec/frontend/monitoring/components/charts/options_spec.js b/spec/frontend/monitoring/components/charts/options_spec.js
new file mode 100644
index 00000000000..d219a6627bf
--- /dev/null
+++ b/spec/frontend/monitoring/components/charts/options_spec.js
@@ -0,0 +1,60 @@
+import { SUPPORTED_FORMATS } from '~/lib/utils/unit_format';
+import { getYAxisOptions, getTooltipFormatter } from '~/monitoring/components/charts/options';
+
+describe('options spec', () => {
+ describe('getYAxisOptions', () => {
+ it('default options', () => {
+ const options = getYAxisOptions();
+
+ expect(options).toMatchObject({
+ name: expect.any(String),
+ axisLabel: {
+ formatter: expect.any(Function),
+ },
+ scale: true,
+ boundaryGap: [expect.any(Number), expect.any(Number)],
+ });
+
+ expect(options.name).not.toHaveLength(0);
+ });
+
+ it('name options', () => {
+ const yAxisName = 'My axis values';
+ const options = getYAxisOptions({
+ name: yAxisName,
+ });
+
+ expect(options).toMatchObject({
+ name: yAxisName,
+ nameLocation: 'center',
+ nameGap: expect.any(Number),
+ });
+ });
+
+ it('formatter options', () => {
+ const options = getYAxisOptions({
+ format: SUPPORTED_FORMATS.bytes,
+ });
+
+ expect(options.axisLabel.formatter).toEqual(expect.any(Function));
+ expect(options.axisLabel.formatter(1)).toBe('1.00B');
+ });
+ });
+
+ describe('getTooltipFormatter', () => {
+ it('default format', () => {
+ const formatter = getTooltipFormatter();
+
+ expect(formatter).toEqual(expect.any(Function));
+ expect(formatter(1)).toBe('1.000');
+ });
+
+ it('defined format', () => {
+ const formatter = getTooltipFormatter({
+ format: SUPPORTED_FORMATS.bytes,
+ });
+
+ expect(formatter(1)).toBe('1.000B');
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/components/charts/time_series_spec.js b/spec/frontend/monitoring/components/charts/time_series_spec.js
index e9322d6b5a9..8dcb54e3fd9 100644
--- a/spec/frontend/monitoring/components/charts/time_series_spec.js
+++ b/spec/frontend/monitoring/components/charts/time_series_spec.js
@@ -190,7 +190,8 @@ describe('Time series component', () => {
it('formats tooltip content', () => {
const name = 'Total';
- const value = '5.556';
+ const value = '5.556MB';
+
const dataIndex = 0;
const seriesLabel = timeSeriesChart.find(GlChartSeriesLabel);
@@ -348,9 +349,9 @@ describe('Time series component', () => {
});
});
- it('additional y axis data', () => {
+ it('additional y-axis data', () => {
const mockCustomYAxisOption = {
- name: 'Custom y axis label',
+ name: 'Custom y-axis label',
axisLabel: {
formatter: jest.fn(),
},
@@ -397,8 +398,8 @@ describe('Time series component', () => {
deploymentFormatter = getChartOptions().yAxis[1].axisLabel.formatter;
});
- it('rounds to 3 decimal places', () => {
- expect(dataFormatter(0.88888)).toBe('0.889');
+ it('formats and rounds to 2 decimal places', () => {
+ expect(dataFormatter(0.88888)).toBe('0.89MB');
});
it('deployment formatter is set as is required to display a tooltip', () => {
@@ -421,7 +422,7 @@ describe('Time series component', () => {
});
describe('yAxisLabel', () => {
- it('y axis is configured correctly', () => {
+ it('y-axis is configured correctly', () => {
const { yAxis } = getChartOptions();
expect(yAxis).toHaveLength(2);
diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js
index 6f05207204e..bec22b28a5c 100644
--- a/spec/frontend/monitoring/components/dashboard_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_spec.js
@@ -214,6 +214,19 @@ describe('Dashboard', () => {
});
});
+ it('renders the refresh dashboard button', () => {
+ createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
+
+ setupComponentStore(wrapper);
+
+ return wrapper.vm.$nextTick().then(() => {
+ const refreshBtn = wrapper.findAll({ ref: 'refreshDashboardBtn' });
+
+ expect(refreshBtn).toHaveLength(1);
+ expect(refreshBtn.is(GlButton)).toBe(true);
+ });
+ });
+
describe('when one of the metrics is missing', () => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
diff --git a/spec/frontend/monitoring/mock_data.js b/spec/frontend/monitoring/mock_data.js
index 32daf990ad3..60b1510973d 100644
--- a/spec/frontend/monitoring/mock_data.js
+++ b/spec/frontend/monitoring/mock_data.js
@@ -393,13 +393,16 @@ export const metricsDashboardPayload = {
type: 'area-chart',
y_label: 'Total Memory Used',
weight: 4,
+ y_axis: {
+ format: 'megabytes',
+ },
metrics: [
{
id: 'system_metrics_kubernetes_container_memory_total',
query_range:
- 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024',
+ 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1000/1000',
label: 'Total',
- unit: 'GB',
+ unit: 'MB',
metric_id: 12,
prometheus_endpoint_path: 'http://test',
},
diff --git a/spec/frontend/monitoring/store/utils_spec.js b/spec/frontend/monitoring/store/utils_spec.js
index 57418e90470..2bd8af9b7d5 100644
--- a/spec/frontend/monitoring/store/utils_spec.js
+++ b/spec/frontend/monitoring/store/utils_spec.js
@@ -1,3 +1,4 @@
+import { SUPPORTED_FORMATS } from '~/lib/utils/unit_format';
import {
uniqMetricsId,
parseEnvironmentsResponse,
@@ -44,6 +45,11 @@ describe('mapToDashboardViewModel', () => {
title: 'Title A',
type: 'chart-type',
y_label: 'Y Label A',
+ yAxis: {
+ name: 'Y Label A',
+ format: 'number',
+ precision: 2,
+ },
metrics: [],
},
],
@@ -90,6 +96,98 @@ describe('mapToDashboardViewModel', () => {
});
});
+ describe('panel mapping', () => {
+ const panelTitle = 'Panel Title';
+ const yAxisName = 'Y Axis Name';
+
+ let dashboard;
+
+ const setupWithPanel = panel => {
+ dashboard = {
+ panel_groups: [
+ {
+ panels: [panel],
+ },
+ ],
+ };
+ };
+
+ const getMappedPanel = () => mapToDashboardViewModel(dashboard).panelGroups[0].panels[0];
+
+ it('group y_axis defaults', () => {
+ setupWithPanel({
+ title: panelTitle,
+ });
+
+ expect(getMappedPanel()).toEqual({
+ title: panelTitle,
+ y_label: '',
+ yAxis: {
+ name: '',
+ format: SUPPORTED_FORMATS.number,
+ precision: 2,
+ },
+ metrics: [],
+ });
+ });
+
+ it('panel with y_axis.name', () => {
+ setupWithPanel({
+ y_axis: {
+ name: yAxisName,
+ },
+ });
+
+ expect(getMappedPanel().y_label).toBe(yAxisName);
+ expect(getMappedPanel().yAxis.name).toBe(yAxisName);
+ });
+
+ it('panel with y_axis.name and y_label, displays y_axis.name', () => {
+ setupWithPanel({
+ y_label: 'Ignored Y Label',
+ y_axis: {
+ name: yAxisName,
+ },
+ });
+
+ expect(getMappedPanel().y_label).toBe(yAxisName);
+ expect(getMappedPanel().yAxis.name).toBe(yAxisName);
+ });
+
+ it('group y_label', () => {
+ setupWithPanel({
+ y_label: yAxisName,
+ });
+
+ expect(getMappedPanel().y_label).toBe(yAxisName);
+ expect(getMappedPanel().yAxis.name).toBe(yAxisName);
+ });
+
+ it('group y_axis format and precision', () => {
+ setupWithPanel({
+ title: panelTitle,
+ y_axis: {
+ precision: 0,
+ format: SUPPORTED_FORMATS.bytes,
+ },
+ });
+
+ expect(getMappedPanel().yAxis.format).toBe(SUPPORTED_FORMATS.bytes);
+ expect(getMappedPanel().yAxis.precision).toBe(0);
+ });
+
+ it('group y_axis unsupported format defaults to number', () => {
+ setupWithPanel({
+ title: panelTitle,
+ y_axis: {
+ format: 'invalid_format',
+ },
+ });
+
+ expect(getMappedPanel().yAxis.format).toBe(SUPPORTED_FORMATS.number);
+ });
+ });
+
describe('metrics mapping', () => {
const defaultLabel = 'Panel Label';
const dashboardWithMetric = (metric, label = defaultLabel) => ({