Commit c40faffd authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '197879-add-custom-time-ranges-in-logs-time-filters-logs' into 'master'

Setup to make monitoring date picker component reusable in other stages (1)

See merge request gitlab-org/gitlab!23383
parents bf0f387e 2797e290
...@@ -27,10 +27,12 @@ import GroupEmptyState from './group_empty_state.vue'; ...@@ -27,10 +27,12 @@ import GroupEmptyState from './group_empty_state.vue';
import DashboardsDropdown from './dashboards_dropdown.vue'; import DashboardsDropdown from './dashboards_dropdown.vue';
import TrackEventDirective from '~/vue_shared/directives/track_event'; import TrackEventDirective from '~/vue_shared/directives/track_event';
import { getTimeDiff, getAddMetricTrackingOptions } from '../utils'; import { getAddMetricTrackingOptions } from '../utils';
import { metricStates } from '../constants'; import { getTimeRange } from './date_time_picker/date_time_picker_lib';
const defaultTimeDiff = getTimeDiff(); import { datePickerTimeWindows, metricStates } from '../constants';
const defaultTimeDiff = getTimeRange();
export default { export default {
components: { components: {
...@@ -191,6 +193,7 @@ export default { ...@@ -191,6 +193,7 @@ export default {
startDate: getParameterValues('start')[0] || defaultTimeDiff.start, startDate: getParameterValues('start')[0] || defaultTimeDiff.start,
endDate: getParameterValues('end')[0] || defaultTimeDiff.end, endDate: getParameterValues('end')[0] || defaultTimeDiff.end,
hasValidDates: true, hasValidDates: true,
datePickerTimeWindows,
isRearrangingPanels: false, isRearrangingPanels: false,
}; };
}, },
...@@ -426,6 +429,7 @@ export default { ...@@ -426,6 +429,7 @@ export default {
<date-time-picker <date-time-picker
:start="startDate" :start="startDate"
:end="endDate" :end="endDate"
:time-windows="datePickerTimeWindows"
@apply="onDateTimePickerApply" @apply="onDateTimePickerApply"
@invalid="onDateTimePickerInvalid" @invalid="onDateTimePickerInvalid"
/> />
......
<script> <script>
import { GlButton, GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui'; import { GlButton, GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui';
import { s__, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import DateTimePickerInput from './date_time_picker_input.vue'; import DateTimePickerInput from './date_time_picker_input.vue';
import { import {
getTimeDiff, defaultTimeWindows,
isValidDate, isValidDate,
getTimeWindow, getTimeRange,
getTimeWindowKey,
stringToISODate, stringToISODate,
ISODateToString, ISODateToString,
truncateZerosInDateTime, truncateZerosInDateTime,
isDateTimePickerInputValid, isDateTimePickerInputValid,
} from '~/monitoring/utils'; } from './date_time_picker_lib';
import { timeWindows } from '~/monitoring/constants';
const events = { const events = {
apply: 'apply', apply: 'apply',
...@@ -41,7 +40,7 @@ export default { ...@@ -41,7 +40,7 @@ export default {
timeWindows: { timeWindows: {
type: Object, type: Object,
required: false, required: false,
default: () => timeWindows, default: () => defaultTimeWindows,
}, },
}, },
data() { data() {
...@@ -81,11 +80,11 @@ export default { ...@@ -81,11 +80,11 @@ export default {
}, },
timeWindowText() { timeWindowText() {
const timeWindow = getTimeWindow({ start: this.start, end: this.end }); const timeWindow = getTimeWindowKey({ start: this.start, end: this.end }, this.timeWindows);
if (timeWindow) { if (timeWindow) {
return this.timeWindows[timeWindow]; return this.timeWindows[timeWindow].label;
} else if (isValidDate(this.start) && isValidDate(this.end)) { } else if (isValidDate(this.start) && isValidDate(this.end)) {
return sprintf(s__('%{start} to %{end}'), { return sprintf(__('%{start} to %{end}'), {
start: this.formatDate(this.start), start: this.formatDate(this.start),
end: this.formatDate(this.end), end: this.formatDate(this.end),
}); });
...@@ -104,7 +103,7 @@ export default { ...@@ -104,7 +103,7 @@ export default {
return truncateZerosInDateTime(ISODateToString(date)); return truncateZerosInDateTime(ISODateToString(date));
}, },
setTimeWindow(key) { setTimeWindow(key) {
const { start, end } = getTimeDiff(key); const { start, end } = getTimeRange(key, this.timeWindows);
this.startDate = start; this.startDate = start;
this.endDate = end; this.endDate = end;
...@@ -161,18 +160,18 @@ export default { ...@@ -161,18 +160,18 @@ export default {
class="col-md-4 p-0 m-0" class="col-md-4 p-0 m-0"
> >
<gl-dropdown-item <gl-dropdown-item
v-for="(value, key) in timeWindows" v-for="(timeWindow, key) in timeWindows"
:key="key" :key="key"
:active="value === timeWindowText" :active="timeWindow.label === timeWindowText"
active-class="active" active-class="active"
@click="setTimeWindow(key)" @click="setTimeWindow(key)"
> >
<icon <icon
name="mobile-issue-close" name="mobile-issue-close"
class="align-bottom" class="align-bottom"
:class="{ invisible: value !== timeWindowText }" :class="{ invisible: timeWindow.label !== timeWindowText }"
/> />
{{ value }} {{ timeWindow.label }}
</gl-dropdown-item> </gl-dropdown-item>
</gl-form-group> </gl-form-group>
</div> </div>
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { GlFormGroup, GlFormInput } from '@gitlab/ui'; import { GlFormGroup, GlFormInput } from '@gitlab/ui';
import { s__, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { dateFormats } from '~/monitoring/constants'; import { dateFormats } from './date_time_picker_lib';
const inputGroupText = { const inputGroupText = {
invalidFeedback: sprintf(s__('Format: %{dateFormat}'), { invalidFeedback: sprintf(__('Format: %{dateFormat}'), {
dateFormat: dateFormats.dateTimePicker.format, dateFormat: dateFormats.stringDate,
}), }),
placeholder: dateFormats.dateTimePicker.format, placeholder: dateFormats.stringDate,
}; };
export default { export default {
......
import dateformat from 'dateformat';
import { __ } from '~/locale';
import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
/**
* Valid strings for this regex are
* 2019-10-01 and 2019-10-01 01:02:03
*/
const dateTimePickerRegex = /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])(?: (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))?$/;
export const defaultTimeWindows = {
thirtyMinutes: {
label: __('30 minutes'),
seconds: 60 * 30,
},
threeHours: {
label: __('3 hours'),
seconds: 60 * 60 * 3,
},
eightHours: {
label: __('8 hours'),
seconds: 60 * 60 * 8,
default: true,
},
oneDay: {
label: __('1 day'),
seconds: 60 * 60 * 24 * 1,
},
threeDays: {
label: __('3 days'),
seconds: 60 * 60 * 24 * 3,
},
};
export const dateFormats = {
ISODate: "yyyy-mm-dd'T'HH:MM:ss'Z'",
stringDate: 'yyyy-mm-dd HH:MM:ss',
};
/**
* The URL params start and end need to be validated
* before passing them down to other components.
*
* @param {string} dateString
* @returns true if the string is a valid date, false otherwise
*/
export const isValidDate = dateString => {
try {
// dateformat throws error that can be caught.
// This is better than using `new Date()`
if (dateString && dateString.trim()) {
dateformat(dateString, 'isoDateTime');
return true;
}
return false;
} catch (e) {
return false;
}
};
export const getTimeRange = (timeWindowKey, timeWindows = defaultTimeWindows) => {
let difference;
if (timeWindows[timeWindowKey]) {
difference = timeWindows[timeWindowKey].seconds;
} else {
const [defaultEntry] = Object.entries(timeWindows).filter(
([, timeWindow]) => timeWindow.default,
);
// find default time window
difference = defaultEntry[1].seconds;
}
const end = Math.floor(Date.now() / 1000); // convert milliseconds to seconds
const start = end - difference;
return {
start: new Date(secondsToMilliseconds(start)).toISOString(),
end: new Date(secondsToMilliseconds(end)).toISOString(),
};
};
export const getTimeWindowKey = ({ start, end }, timeWindows = defaultTimeWindows) =>
Object.entries(timeWindows).reduce((acc, [timeWindowKey, timeWindow]) => {
if (new Date(end) - new Date(start) === secondsToMilliseconds(timeWindow.seconds)) {
return timeWindowKey;
}
return acc;
}, null);
/**
* Convert the input in Time picker component to ISO date.
*
* @param {string} val
* @returns {string}
*/
export const stringToISODate = val =>
dateformat(new Date(val.replace(/-/g, '/')), dateFormats.ISODate, true);
/**
* Convert the ISO date received from the URL to string
* for the Time picker component.
*
* @param {Date} date
* @returns {string}
*/
export const ISODateToString = date => dateformat(date, dateFormats.stringDate);
export const truncateZerosInDateTime = datetime => datetime.replace(' 00:00:00', '');
export const isDateTimePickerInputValid = val => dateTimePickerRegex.test(val);
export default {};
...@@ -3,7 +3,7 @@ import { mapActions, mapState, mapGetters } from 'vuex'; ...@@ -3,7 +3,7 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue'; import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { getParameterValues, removeParams } from '~/lib/utils/url_utility'; import { getParameterValues, removeParams } from '~/lib/utils/url_utility';
import { sidebarAnimationDuration } from '../constants'; import { sidebarAnimationDuration } from '../constants';
import { getTimeDiff } from '../utils'; import { getTimeRange } from './date_time_picker/date_time_picker_lib';
let sidebarMutationObserver; let sidebarMutationObserver;
...@@ -18,7 +18,7 @@ export default { ...@@ -18,7 +18,7 @@ export default {
}, },
}, },
data() { data() {
const defaultRange = getTimeDiff(); const defaultRange = getTimeRange();
const start = getParameterValues('start', this.dashboardUrl)[0] || defaultRange.start; const start = getParameterValues('start', this.dashboardUrl)[0] || defaultRange.start;
const end = getParameterValues('end', this.dashboardUrl)[0] || defaultRange.end; const end = getParameterValues('end', this.dashboardUrl)[0] || defaultRange.end;
......
...@@ -50,11 +50,6 @@ export const metricStates = { ...@@ -50,11 +50,6 @@ export const metricStates = {
export const sidebarAnimationDuration = 300; // milliseconds. export const sidebarAnimationDuration = 300; // milliseconds.
export const chartHeight = 300; export const chartHeight = 300;
/**
* Valid strings for this regex are
* 2019-10-01 and 2019-10-01 01:02:03
*/
export const dateTimePickerRegex = /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])(?: (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))?$/;
export const graphTypes = { export const graphTypes = {
deploymentData: 'scatter', deploymentData: 'scatter',
...@@ -83,38 +78,39 @@ export const lineWidths = { ...@@ -83,38 +78,39 @@ export const lineWidths = {
default: 2, default: 2,
}; };
export const timeWindows = {
thirtyMinutes: __('30 minutes'),
threeHours: __('3 hours'),
eightHours: __('8 hours'),
oneDay: __('1 day'),
threeDays: __('3 days'),
oneWeek: __('1 week'),
};
export const dateFormats = { export const dateFormats = {
timeOfDay: 'h:MM TT', timeOfDay: 'h:MM TT',
default: 'dd mmm yyyy, h:MMTT', default: 'dd mmm yyyy, h:MMTT',
dateTimePicker: {
format: 'yyyy-mm-dd hh:mm:ss',
ISODate: "yyyy-mm-dd'T'HH:MM:ss'Z'",
stringDate: 'yyyy-mm-dd HH:MM:ss',
},
}; };
export const secondsIn = { export const datePickerTimeWindows = {
thirtyMinutes: 60 * 30, thirtyMinutes: {
threeHours: 60 * 60 * 3, label: __('30 minutes'),
eightHours: 60 * 60 * 8, seconds: 60 * 30,
oneDay: 60 * 60 * 24 * 1, },
threeDays: 60 * 60 * 24 * 3, threeHours: {
oneWeek: 60 * 60 * 24 * 7 * 1, label: __('3 hours'),
seconds: 60 * 60 * 3,
},
eightHours: {
label: __('8 hours'),
seconds: 60 * 60 * 8,
default: true,
},
oneDay: {
label: __('1 day'),
seconds: 60 * 60 * 24 * 1,
},
threeDays: {
label: __('3 days'),
seconds: 60 * 60 * 24 * 3,
},
oneWeek: {
label: __('1 week'),
seconds: 60 * 60 * 24 * 7 * 1,
},
twoWeeks: {
label: __('2 weeks'),
seconds: 60 * 60 * 24 * 7 * 2,
},
}; };
export const timeWindowsKeyNames = Object.keys(secondsIn).reduce(
(otherTimeWindows, timeWindow) => ({
...otherTimeWindows,
[timeWindow]: timeWindow,
}),
{},
);
import dateformat from 'dateformat';
import { secondsIn, dateTimePickerRegex, dateFormats } from './constants';
import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
export const getTimeDiff = timeWindow => {
const end = Math.floor(Date.now() / 1000); // convert milliseconds to seconds
const difference = secondsIn[timeWindow] || secondsIn.eightHours;
const start = end - difference;
return {
start: new Date(secondsToMilliseconds(start)).toISOString(),
end: new Date(secondsToMilliseconds(end)).toISOString(),
};
};
export const getTimeWindow = ({ start, end }) =>
Object.entries(secondsIn).reduce((acc, [timeRange, value]) => {
if (new Date(end) - new Date(start) === secondsToMilliseconds(value)) {
return timeRange;
}
return acc;
}, null);
export const isDateTimePickerInputValid = val => dateTimePickerRegex.test(val);
export const truncateZerosInDateTime = datetime => datetime.replace(' 00:00:00', '');
/**
* The URL params start and end need to be validated
* before passing them down to other components.
*
* @param {string} dateString
*/
export const isValidDate = dateString => {
try {
// dateformat throws error that can be caught.
// This is better than using `new Date()`
if (dateString && dateString.trim()) {
dateformat(dateString, 'isoDateTime');
return true;
}
return false;
} catch (e) {
return false;
}
};
/**
* Convert the input in Time picker component to ISO date.
*
* @param {string} val
* @returns {string}
*/
export const stringToISODate = val =>
dateformat(new Date(val.replace(/-/g, '/')), dateFormats.dateTimePicker.ISODate, true);
/**
* Convert the ISO date received from the URL to string
* for the Time picker component.
*
* @param {Date} date
* @returns {string}
*/
export const ISODateToString = date => dateformat(date, dateFormats.dateTimePicker.stringDate);
/** /**
* This method is used to validate if the graph data format for a chart component * This method is used to validate if the graph data format for a chart component
* that needs a time series as a response from a prometheus query (query_range) is * that needs a time series as a response from a prometheus query (query_range) is
......
...@@ -42,7 +42,7 @@ describe('dashboard time window', () => { ...@@ -42,7 +42,7 @@ describe('dashboard time window', () => {
mock.restore(); mock.restore();
}); });
it('shows an error message if invalid url parameters are passed', done => { it('shows an active quick range option', done => {
mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsDashboardPayload); mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsDashboardPayload);
createComponentWrapperMounted({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] }); createComponentWrapperMounted({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
...@@ -55,6 +55,7 @@ describe('dashboard time window', () => { ...@@ -55,6 +55,7 @@ describe('dashboard time window', () => {
const timeWindowDropdownItems = wrapper const timeWindowDropdownItems = wrapper
.find('.js-time-window-dropdown') .find('.js-time-window-dropdown')
.findAll(GlDropdownItem); .findAll(GlDropdownItem);
const activeItem = timeWindowDropdownItems.wrappers.filter(itemWrapper => const activeItem = timeWindowDropdownItems.wrappers.filter(itemWrapper =>
itemWrapper.find('.active').exists(), itemWrapper.find('.active').exists(),
); );
......
import * as dateTimePickerLib from '~/monitoring/components/date_time_picker/date_time_picker_lib';
describe('date time picker lib', () => {
describe('isValidDate', () => {
[
{
input: '2019-09-09T00:00:00.000Z',
output: true,
},
{
input: '2019-09-09T000:00.000Z',
output: false,
},
{
input: 'a2019-09-09T000:00.000Z',
output: false,
},
{
input: '2019-09-09T',
output: false,
},
{
input: '2019-09-09',
output: true,
},
{
input: '2019-9-9',
output: true,
},
{
input: '2019-9-',
output: true,
},
{
input: '2019--',
output: false,
},
{
input: '2019',
output: true,
},
{
input: '',
output: false,
},
{
input: null,
output: false,
},
].forEach(({ input, output }) => {
it(`isValidDate return ${output} for ${input}`, () => {
expect(dateTimePickerLib.isValidDate(input)).toBe(output);
});
});
});
describe('getTimeWindow', () => {
[
{
args: [
{
start: '2019-10-01T18:27:47.000Z',
end: '2019-10-01T21:27:47.000Z',
},
dateTimePickerLib.defaultTimeWindows,
],
expected: 'threeHours',
},
{
args: [
{
start: '2019-10-01T28:27:47.000Z',
end: '2019-10-01T21:27:47.000Z',
},
dateTimePickerLib.defaultTimeWindows,
],
expected: null,
},
{
args: [
{
start: '',
end: '',
},
dateTimePickerLib.defaultTimeWindows,
],
expected: null,
},
{
args: [
{
start: null,
end: null,
},
dateTimePickerLib.defaultTimeWindows,
],
expected: null,
},
{
args: [{}, dateTimePickerLib.defaultTimeWindows],
expected: null,
},
].forEach(({ args, expected }) => {
it(`returns "${expected}" with args=${JSON.stringify(args)}`, () => {
expect(dateTimePickerLib.getTimeWindowKey(...args)).toEqual(expected);
});
});
});
describe('getTimeRange', () => {
function secondsBetween({ start, end }) {
return (new Date(end) - new Date(start)) / 1000;
}
function minutesBetween(timeRange) {
return secondsBetween(timeRange) / 60;
}
function hoursBetween(timeRange) {
return minutesBetween(timeRange) / 60;
}
it('defaults to an 8 hour (28800s) difference', () => {
const params = dateTimePickerLib.getTimeRange();
expect(hoursBetween(params)).toEqual(8);
});
it('accepts time window as an argument', () => {
const params = dateTimePickerLib.getTimeRange('thirtyMinutes');
expect(minutesBetween(params)).toEqual(30);
});
it('returns a value for every defined time window', () => {
const nonDefaultWindows = Object.entries(dateTimePickerLib.defaultTimeWindows).filter(
([, timeWindow]) => !timeWindow.default,
);
nonDefaultWindows.forEach(timeWindow => {
const params = dateTimePickerLib.getTimeRange(timeWindow[0]);
// Ensure we're not returning the default
expect(hoursBetween(params)).not.toEqual(8);
});
});
});
describe('stringToISODate', () => {
['', 'null', undefined, 'abc'].forEach(input => {
it(`throws error for invalid input like ${input}`, done => {
try {
dateTimePickerLib.stringToISODate(input);
} catch (e) {
expect(e).toBeDefined();
done();
}
});
});
[
{
input: '2019-09-09 01:01:01',
output: '2019-09-09T01:01:01Z',
},
{
input: '2019-09-09 00:00:00',
output: '2019-09-09T00:00:00Z',
},
{
input: '2019-09-09 23:59:59',
output: '2019-09-09T23:59:59Z',
},
{
input: '2019-09-09',
output: '2019-09-09T00:00:00Z',
},
].forEach(({ input, output }) => {
it(`returns ${output} from ${input}`, () => {
expect(dateTimePickerLib.stringToISODate(input)).toBe(output);
});
});
});
describe('truncateZerosInDateTime', () => {
[
{
input: '',
output: '',
},
{
input: '2019-10-10',
output: '2019-10-10',
},
{
input: '2019-10-10 00:00:01',
output: '2019-10-10 00:00:01',
},
{
input: '2019-10-10 00:00:00',
output: '2019-10-10',
},
].forEach(({ input, output }) => {
it(`truncateZerosInDateTime return ${output} for ${input}`, () => {
expect(dateTimePickerLib.truncateZerosInDateTime(input)).toBe(output);
});
});
});
describe('isDateTimePickerInputValid', () => {
[
{
input: null,
output: false,
},
{
input: '',
output: false,
},
{
input: 'xxxx-xx-xx',
output: false,
},
{
input: '9999-99-19',
output: false,
},
{
input: '2019-19-23',
output: false,
},
{
input: '2019-09-23',
output: true,
},
{
input: '2019-09-23 x',
output: false,
},
{
input: '2019-09-29 0:0:0',
output: false,
},
{
input: '2019-09-29 00:00:00',
output: true,
},
{
input: '2019-09-29 24:24:24',
output: false,
},
{
input: '2019-09-29 23:24:24',
output: true,
},
{
input: '2019-09-29 23:24:24 ',
output: false,
},
].forEach(({ input, output }) => {
it(`returns ${output} for ${input}`, () => {
expect(dateTimePickerLib.isDateTimePickerInputValid(input)).toBe(output);
});
});
});
});
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_picker.vue'; import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_picker.vue';
import { timeWindows } from '~/monitoring/constants'; import { defaultTimeWindows } from '~/monitoring/components/date_time_picker/date_time_picker_lib';
const timeWindowsCount = Object.keys(timeWindows).length; const timeWindowsCount = Object.entries(defaultTimeWindows).length;
const start = '2019-10-10T07:00:00.000Z'; const start = '2019-10-10T07:00:00.000Z';
const end = '2019-10-13T07:00:00.000Z'; const end = '2019-10-13T07:00:00.000Z';
const selectedTimeWindowText = `3 days`; const selectedTimeWindowText = `3 days`;
...@@ -13,6 +13,7 @@ describe('DateTimePicker', () => { ...@@ -13,6 +13,7 @@ describe('DateTimePicker', () => {
const dropdownToggle = () => dateTimePicker.find('.dropdown-toggle'); const dropdownToggle = () => dateTimePicker.find('.dropdown-toggle');
const dropdownMenu = () => dateTimePicker.find('.dropdown-menu'); const dropdownMenu = () => dateTimePicker.find('.dropdown-menu');
const applyButtonElement = () => dateTimePicker.find('button.btn-success').element; const applyButtonElement = () => dateTimePicker.find('button.btn-success').element;
const findQuickRangeItems = () => dateTimePicker.findAll('.dropdown-item');
const cancelButtonElement = () => dateTimePicker.find('button.btn-secondary').element; const cancelButtonElement = () => dateTimePicker.find('button.btn-secondary').element;
const fillInputAndBlur = (input, val) => { const fillInputAndBlur = (input, val) => {
dateTimePicker.find(input).setValue(val); dateTimePicker.find(input).setValue(val);
...@@ -25,7 +26,6 @@ describe('DateTimePicker', () => { ...@@ -25,7 +26,6 @@ describe('DateTimePicker', () => {
const createComponent = props => { const createComponent = props => {
dateTimePicker = mount(DateTimePicker, { dateTimePicker = mount(DateTimePicker, {
propsData: { propsData: {
timeWindows,
start, start,
end, end,
...props, ...props,
...@@ -52,16 +52,6 @@ describe('DateTimePicker', () => { ...@@ -52,16 +52,6 @@ describe('DateTimePicker', () => {
}); });
}); });
it('renders dropdown without a selectedTimeWindow set', done => {
createComponent({
selectedTimeWindow: {},
});
dateTimePicker.vm.$nextTick(() => {
expect(dateTimePicker.findAll('input').length).toBe(2);
done();
});
});
it('renders inputs with h/m/s truncated if its all 0s', done => { it('renders inputs with h/m/s truncated if its all 0s', done => {
createComponent({ createComponent({
start: '2019-10-10T00:00:00.000Z', start: '2019-10-10T00:00:00.000Z',
...@@ -74,11 +64,11 @@ describe('DateTimePicker', () => { ...@@ -74,11 +64,11 @@ describe('DateTimePicker', () => {
}); });
}); });
it(`renders dropdown with ${timeWindowsCount} items in quick range`, done => { it(`renders dropdown with ${timeWindowsCount} (default) items in quick range`, done => {
createComponent(); createComponent();
dropdownToggle().trigger('click'); dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => { dateTimePicker.vm.$nextTick(() => {
expect(dateTimePicker.findAll('.dropdown-item').length).toBe(timeWindowsCount); expect(findQuickRangeItems().length).toBe(timeWindowsCount);
done(); done();
}); });
}); });
...@@ -167,4 +157,77 @@ describe('DateTimePicker', () => { ...@@ -167,4 +157,77 @@ describe('DateTimePicker', () => {
}); });
}); });
}); });
describe('when using non-default time windows', () => {
const otherTimeWindows = {
oneMinute: {
label: '1 minute',
seconds: 60,
},
twoMinutes: {
label: '2 minutes',
seconds: 60 * 2,
default: true,
},
fiveMinutes: {
label: '5 minutes',
seconds: 60 * 5,
},
};
it('renders dropdown with a label in the quick range', done => {
createComponent({
// 2 minutes range
start: '2020-01-21T15:00:00.000Z',
end: '2020-01-21T15:02:00.000Z',
timeWindows: otherTimeWindows,
});
dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => {
expect(dropdownToggle().text()).toBe('2 minutes');
done();
});
});
it('renders dropdown with quick range items', done => {
createComponent({
// 2 minutes range
start: '2020-01-21T15:00:00.000Z',
end: '2020-01-21T15:02:00.000Z',
timeWindows: otherTimeWindows,
});
dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => {
const items = findQuickRangeItems();
expect(items.length).toBe(Object.keys(otherTimeWindows).length);
expect(items.at(0).text()).toBe('1 minute');
expect(items.at(0).is('.active')).toBe(false);
expect(items.at(1).text()).toBe('2 minutes');
expect(items.at(1).is('.active')).toBe(true);
expect(items.at(2).text()).toBe('5 minutes');
expect(items.at(2).is('.active')).toBe(false);
done();
});
});
it('renders dropdown with a label not in the quick range', done => {
createComponent({
// 10 minutes range
start: '2020-01-21T15:00:00.000Z',
end: '2020-01-21T15:10:00.000Z',
timeWindows: otherTimeWindows,
});
dropdownToggle().trigger('click');
dateTimePicker.vm.$nextTick(() => {
expect(dropdownToggle().text()).toBe('2020-01-21 15:00:00 to 2020-01-21 15:10:00');
done();
});
});
});
}); });
import * as monitoringUtils from '~/monitoring/utils'; import * as monitoringUtils from '~/monitoring/utils';
import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
import { import {
graphDataPrometheusQuery, graphDataPrometheusQuery,
graphDataPrometheusQueryRange, graphDataPrometheusQueryRange,
...@@ -58,92 +57,6 @@ describe('monitoring/utils', () => { ...@@ -58,92 +57,6 @@ describe('monitoring/utils', () => {
}); });
}); });
describe('getTimeDiff', () => {
function secondsBetween({ start, end }) {
return (new Date(end) - new Date(start)) / 1000;
}
function minutesBetween(timeRange) {
return secondsBetween(timeRange) / 60;
}
function hoursBetween(timeRange) {
return minutesBetween(timeRange) / 60;
}
it('defaults to an 8 hour (28800s) difference', () => {
const params = monitoringUtils.getTimeDiff();
expect(hoursBetween(params)).toEqual(8);
});
it('accepts time window as an argument', () => {
const params = monitoringUtils.getTimeDiff('thirtyMinutes');
expect(minutesBetween(params)).toEqual(30);
});
it('returns a value for every defined time window', () => {
const nonDefaultWindows = Object.keys(timeWindows).filter(window => window !== 'eightHours');
nonDefaultWindows.forEach(timeWindow => {
const params = monitoringUtils.getTimeDiff(timeWindow);
// Ensure we're not returning the default
expect(hoursBetween(params)).not.toEqual(8);
});
});
});
describe('getTimeWindow', () => {
[
{
args: [
{
start: '2019-10-01T18:27:47.000Z',
end: '2019-10-01T21:27:47.000Z',
},
],
expected: timeWindowsKeyNames.threeHours,
},
{
args: [
{
start: '2019-10-01T28:27:47.000Z',
end: '2019-10-01T21:27:47.000Z',
},
],
expected: null,
},
{
args: [
{
start: '',
end: '',
},
],
expected: null,
},
{
args: [
{
start: null,
end: null,
},
],
expected: null,
},
{
args: [{}],
expected: null,
},
].forEach(({ args, expected }) => {
it(`returns "${expected}" with args=${JSON.stringify(args)}`, () => {
expect(monitoringUtils.getTimeWindow(...args)).toEqual(expected);
});
});
});
describe('graphDataValidatorForValues', () => { describe('graphDataValidatorForValues', () => {
/* /*
* When dealing with a metric using the query format, e.g. * When dealing with a metric using the query format, e.g.
...@@ -174,193 +87,6 @@ describe('monitoring/utils', () => { ...@@ -174,193 +87,6 @@ describe('monitoring/utils', () => {
}); });
}); });
describe('stringToISODate', () => {
['', 'null', undefined, 'abc'].forEach(input => {
it(`throws error for invalid input like ${input}`, done => {
try {
monitoringUtils.stringToISODate(input);
} catch (e) {
expect(e).toBeDefined();
done();
}
});
});
[
{
input: '2019-09-09 01:01:01',
output: '2019-09-09T01:01:01Z',
},
{
input: '2019-09-09 00:00:00',
output: '2019-09-09T00:00:00Z',
},
{
input: '2019-09-09 23:59:59',
output: '2019-09-09T23:59:59Z',
},
{
input: '2019-09-09',
output: '2019-09-09T00:00:00Z',
},
].forEach(({ input, output }) => {
it(`returns ${output} from ${input}`, () => {
expect(monitoringUtils.stringToISODate(input)).toBe(output);
});
});
});
describe('ISODateToString', () => {
[
{
input: new Date('2019-09-09T00:00:00.000Z'),
output: '2019-09-09 00:00:00',
},
{
input: new Date('2019-09-09T07:00:00.000Z'),
output: '2019-09-09 07:00:00',
},
].forEach(({ input, output }) => {
it(`ISODateToString return ${output} for ${input}`, () => {
expect(monitoringUtils.ISODateToString(input)).toBe(output);
});
});
});
describe('truncateZerosInDateTime', () => {
[
{
input: '',
output: '',
},
{
input: '2019-10-10',
output: '2019-10-10',
},
{
input: '2019-10-10 00:00:01',
output: '2019-10-10 00:00:01',
},
{
input: '2019-10-10 00:00:00',
output: '2019-10-10',
},
].forEach(({ input, output }) => {
it(`truncateZerosInDateTime return ${output} for ${input}`, () => {
expect(monitoringUtils.truncateZerosInDateTime(input)).toBe(output);
});
});
});
describe('isValidDate', () => {
[
{
input: '2019-09-09T00:00:00.000Z',
output: true,
},
{
input: '2019-09-09T000:00.000Z',
output: false,
},
{
input: 'a2019-09-09T000:00.000Z',
output: false,
},
{
input: '2019-09-09T',
output: false,
},
{
input: '2019-09-09',
output: true,
},
{
input: '2019-9-9',
output: true,
},
{
input: '2019-9-',
output: true,
},
{
input: '2019--',
output: false,
},
{
input: '2019',
output: true,
},
{
input: '',
output: false,
},
{
input: null,
output: false,
},
].forEach(({ input, output }) => {
it(`isValidDate return ${output} for ${input}`, () => {
expect(monitoringUtils.isValidDate(input)).toBe(output);
});
});
});
describe('isDateTimePickerInputValid', () => {
[
{
input: null,
output: false,
},
{
input: '',
output: false,
},
{
input: 'xxxx-xx-xx',
output: false,
},
{
input: '9999-99-19',
output: false,
},
{
input: '2019-19-23',
output: false,
},
{
input: '2019-09-23',
output: true,
},
{
input: '2019-09-23 x',
output: false,
},
{
input: '2019-09-29 0:0:0',
output: false,
},
{
input: '2019-09-29 00:00:00',
output: true,
},
{
input: '2019-09-29 24:24:24',
output: false,
},
{
input: '2019-09-29 23:24:24',
output: true,
},
{
input: '2019-09-29 23:24:24 ',
output: false,
},
].forEach(({ input, output }) => {
it(`returns ${output} for ${input}`, () => {
expect(monitoringUtils.isDateTimePickerInputValid(input)).toBe(output);
});
});
});
describe('graphDataValidatorForAnomalyValues', () => { describe('graphDataValidatorForAnomalyValues', () => {
let oneMetric; let oneMetric;
let threeMetrics; let threeMetrics;
......
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