Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0f8c2334f0
commit
f47c768fad
16 changed files with 571 additions and 417 deletions
|
@ -210,6 +210,9 @@ export default {
|
|||
:text="diffFile.file_path"
|
||||
:gfm="gfmCopyText"
|
||||
css-class="btn-default btn-transparent btn-clipboard"
|
||||
data-track-event="click_copy_file_button"
|
||||
data-track-label="diff_copy_file_path_button"
|
||||
data-track-property="diff_copy_file"
|
||||
/>
|
||||
|
||||
<small v-if="isModeChanged" ref="fileMode" class="mr-1">
|
||||
|
@ -233,6 +236,9 @@ export default {
|
|||
:class="{ active: diffHasExpandedDiscussions(diffFile) }"
|
||||
class="js-btn-vue-toggle-comments btn"
|
||||
data-qa-selector="toggle_comments_button"
|
||||
data-track-event="click_toggle_comments_button"
|
||||
data-track-label="diff_toggle_comments_button"
|
||||
data-track-property="diff_toggle_comments"
|
||||
type="button"
|
||||
@click="toggleFileDiscussionWrappers(diffFile)"
|
||||
>
|
||||
|
@ -245,6 +251,9 @@ export default {
|
|||
:can-current-user-fork="canCurrentUserFork"
|
||||
:edit-path="diffFile.edit_path"
|
||||
:can-modify-blob="diffFile.can_modify_blob"
|
||||
data-track-event="click_toggle_edit_button"
|
||||
data-track-label="diff_toggle_edit_button"
|
||||
data-track-property="diff_toggle_edit"
|
||||
@showForkMessage="showForkMessage"
|
||||
/>
|
||||
</template>
|
||||
|
@ -263,6 +272,9 @@ export default {
|
|||
v-gl-tooltip.hover
|
||||
:title="expandDiffToFullFileTitle"
|
||||
class="expand-file"
|
||||
data-track-event="click_toggle_view_full_button"
|
||||
data-track-label="diff_toggle_view_full_button"
|
||||
data-track-property="diff_toggle_view_full"
|
||||
@click="toggleFullDiff(diffFile.file_path)"
|
||||
>
|
||||
<gl-loading-icon v-if="diffFile.isLoadingFullFile" color="dark" inline />
|
||||
|
@ -275,6 +287,9 @@ export default {
|
|||
:href="diffFile.view_path"
|
||||
target="blank"
|
||||
class="view-file"
|
||||
data-track-event="click_toggle_view_sha_button"
|
||||
data-track-label="diff_toggle_view_sha_button"
|
||||
data-track-property="diff_toggle_view_sha"
|
||||
:title="viewFileButtonText"
|
||||
>
|
||||
<icon name="doc-text" />
|
||||
|
@ -288,6 +303,9 @@ export default {
|
|||
:title="`View on ${diffFile.formatted_external_url}`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
data-track-event="click_toggle_external_button"
|
||||
data-track-label="diff_toggle_external_button"
|
||||
data-track-property="diff_toggle_external"
|
||||
class="btn btn-file-option"
|
||||
>
|
||||
<icon name="external-link" />
|
||||
|
|
|
@ -27,10 +27,12 @@ import GroupEmptyState from './group_empty_state.vue';
|
|||
import DashboardsDropdown from './dashboards_dropdown.vue';
|
||||
|
||||
import TrackEventDirective from '~/vue_shared/directives/track_event';
|
||||
import { getTimeDiff, getAddMetricTrackingOptions } from '../utils';
|
||||
import { metricStates } from '../constants';
|
||||
import { getAddMetricTrackingOptions } from '../utils';
|
||||
import { getTimeRange } from './date_time_picker/date_time_picker_lib';
|
||||
|
||||
const defaultTimeDiff = getTimeDiff();
|
||||
import { datePickerTimeWindows, metricStates } from '../constants';
|
||||
|
||||
const defaultTimeDiff = getTimeRange();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -191,6 +193,7 @@ export default {
|
|||
startDate: getParameterValues('start')[0] || defaultTimeDiff.start,
|
||||
endDate: getParameterValues('end')[0] || defaultTimeDiff.end,
|
||||
hasValidDates: true,
|
||||
datePickerTimeWindows,
|
||||
isRearrangingPanels: false,
|
||||
};
|
||||
},
|
||||
|
@ -426,6 +429,7 @@ export default {
|
|||
<date-time-picker
|
||||
:start="startDate"
|
||||
:end="endDate"
|
||||
:time-windows="datePickerTimeWindows"
|
||||
@apply="onDateTimePickerApply"
|
||||
@invalid="onDateTimePickerInvalid"
|
||||
/>
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
<script>
|
||||
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 DateTimePickerInput from './date_time_picker_input.vue';
|
||||
import {
|
||||
getTimeDiff,
|
||||
defaultTimeWindows,
|
||||
isValidDate,
|
||||
getTimeWindow,
|
||||
getTimeRange,
|
||||
getTimeWindowKey,
|
||||
stringToISODate,
|
||||
ISODateToString,
|
||||
truncateZerosInDateTime,
|
||||
isDateTimePickerInputValid,
|
||||
} from '~/monitoring/utils';
|
||||
|
||||
import { timeWindows } from '~/monitoring/constants';
|
||||
} from './date_time_picker_lib';
|
||||
|
||||
const events = {
|
||||
apply: 'apply',
|
||||
|
@ -41,7 +40,7 @@ export default {
|
|||
timeWindows: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => timeWindows,
|
||||
default: () => defaultTimeWindows,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
@ -81,11 +80,11 @@ export default {
|
|||
},
|
||||
|
||||
timeWindowText() {
|
||||
const timeWindow = getTimeWindow({ start: this.start, end: this.end });
|
||||
const timeWindow = getTimeWindowKey({ start: this.start, end: this.end }, this.timeWindows);
|
||||
if (timeWindow) {
|
||||
return this.timeWindows[timeWindow];
|
||||
return this.timeWindows[timeWindow].label;
|
||||
} else if (isValidDate(this.start) && isValidDate(this.end)) {
|
||||
return sprintf(s__('%{start} to %{end}'), {
|
||||
return sprintf(__('%{start} to %{end}'), {
|
||||
start: this.formatDate(this.start),
|
||||
end: this.formatDate(this.end),
|
||||
});
|
||||
|
@ -104,7 +103,7 @@ export default {
|
|||
return truncateZerosInDateTime(ISODateToString(date));
|
||||
},
|
||||
setTimeWindow(key) {
|
||||
const { start, end } = getTimeDiff(key);
|
||||
const { start, end } = getTimeRange(key, this.timeWindows);
|
||||
this.startDate = start;
|
||||
this.endDate = end;
|
||||
|
||||
|
@ -161,18 +160,18 @@ export default {
|
|||
class="col-md-4 p-0 m-0"
|
||||
>
|
||||
<gl-dropdown-item
|
||||
v-for="(value, key) in timeWindows"
|
||||
v-for="(timeWindow, key) in timeWindows"
|
||||
:key="key"
|
||||
:active="value === timeWindowText"
|
||||
:active="timeWindow.label === timeWindowText"
|
||||
active-class="active"
|
||||
@click="setTimeWindow(key)"
|
||||
>
|
||||
<icon
|
||||
name="mobile-issue-close"
|
||||
class="align-bottom"
|
||||
:class="{ invisible: value !== timeWindowText }"
|
||||
:class="{ invisible: timeWindow.label !== timeWindowText }"
|
||||
/>
|
||||
{{ value }}
|
||||
{{ timeWindow.label }}
|
||||
</gl-dropdown-item>
|
||||
</gl-form-group>
|
||||
</div>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import { GlFormGroup, GlFormInput } from '@gitlab/ui';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { dateFormats } from '~/monitoring/constants';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { dateFormats } from './date_time_picker_lib';
|
||||
|
||||
const inputGroupText = {
|
||||
invalidFeedback: sprintf(s__('Format: %{dateFormat}'), {
|
||||
dateFormat: dateFormats.dateTimePicker.format,
|
||||
invalidFeedback: sprintf(__('Format: %{dateFormat}'), {
|
||||
dateFormat: dateFormats.stringDate,
|
||||
}),
|
||||
placeholder: dateFormats.dateTimePicker.format,
|
||||
placeholder: dateFormats.stringDate,
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
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';
|
|||
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
|
||||
import { getParameterValues, removeParams } from '~/lib/utils/url_utility';
|
||||
import { sidebarAnimationDuration } from '../constants';
|
||||
import { getTimeDiff } from '../utils';
|
||||
import { getTimeRange } from './date_time_picker/date_time_picker_lib';
|
||||
|
||||
let sidebarMutationObserver;
|
||||
|
||||
|
@ -18,7 +18,7 @@ export default {
|
|||
},
|
||||
},
|
||||
data() {
|
||||
const defaultRange = getTimeDiff();
|
||||
const defaultRange = getTimeRange();
|
||||
const start = getParameterValues('start', this.dashboardUrl)[0] || defaultRange.start;
|
||||
const end = getParameterValues('end', this.dashboardUrl)[0] || defaultRange.end;
|
||||
|
||||
|
|
|
@ -50,11 +50,6 @@ export const metricStates = {
|
|||
export const sidebarAnimationDuration = 300; // milliseconds.
|
||||
|
||||
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 = {
|
||||
deploymentData: 'scatter',
|
||||
|
@ -83,38 +78,39 @@ export const lineWidths = {
|
|||
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 = {
|
||||
timeOfDay: 'h:MM TT',
|
||||
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 datePickerTimeWindows = {
|
||||
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,
|
||||
},
|
||||
oneWeek: {
|
||||
label: __('1 week'),
|
||||
seconds: 60 * 60 * 24 * 7 * 1,
|
||||
},
|
||||
twoWeeks: {
|
||||
label: __('2 weeks'),
|
||||
seconds: 60 * 60 * 24 * 7 * 2,
|
||||
},
|
||||
};
|
||||
|
||||
export const secondsIn = {
|
||||
thirtyMinutes: 60 * 30,
|
||||
threeHours: 60 * 60 * 3,
|
||||
eightHours: 60 * 60 * 8,
|
||||
oneDay: 60 * 60 * 24 * 1,
|
||||
threeDays: 60 * 60 * 24 * 3,
|
||||
oneWeek: 60 * 60 * 24 * 7 * 1,
|
||||
};
|
||||
|
||||
export const timeWindowsKeyNames = Object.keys(secondsIn).reduce(
|
||||
(otherTimeWindows, timeWindow) => ({
|
||||
...otherTimeWindows,
|
||||
[timeWindow]: timeWindow,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
|
|
@ -1,68 +1,3 @@
|
|||
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
|
||||
* that needs a time series as a response from a prometheus query (query_range) is
|
||||
|
|
5
changelogs/unreleased/197412.yml
Normal file
5
changelogs/unreleased/197412.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Track usage of merge request file header buttons
|
||||
merge_request:
|
||||
author: Oregand
|
||||
type: other
|
|
@ -1342,8 +1342,7 @@ In its simplest form, the `environment` keyword can be defined like:
|
|||
deploy to production:
|
||||
stage: deploy
|
||||
script: git push production HEAD:master
|
||||
environment:
|
||||
name: production
|
||||
environment: production
|
||||
```
|
||||
|
||||
In the above example, the `deploy to production` job will be marked as doing a
|
||||
|
|
|
@ -6801,6 +6801,9 @@ msgstr ""
|
|||
msgid "Email address"
|
||||
msgstr ""
|
||||
|
||||
msgid "Email display name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Email domain is not editable in subgroups. Value inherited from top-level parent group."
|
||||
msgstr ""
|
||||
|
||||
|
@ -6846,6 +6849,9 @@ msgstr ""
|
|||
msgid "Emails"
|
||||
msgstr ""
|
||||
|
||||
msgid "Emails sent from Service Desk will have this name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Emails separated by comma"
|
||||
msgstr ""
|
||||
|
||||
|
@ -8918,6 +8924,9 @@ msgstr ""
|
|||
msgid "GitLab Shared Runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com)."
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Support Bot"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab User"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ describe('dashboard time window', () => {
|
|||
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);
|
||||
|
||||
createComponentWrapperMounted({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
|
||||
|
@ -55,6 +55,7 @@ describe('dashboard time window', () => {
|
|||
const timeWindowDropdownItems = wrapper
|
||||
.find('.js-time-window-dropdown')
|
||||
.findAll(GlDropdownItem);
|
||||
|
||||
const activeItem = timeWindowDropdownItems.wrappers.filter(itemWrapper =>
|
||||
itemWrapper.find('.active').exists(),
|
||||
);
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
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 end = '2019-10-13T07:00:00.000Z';
|
||||
const selectedTimeWindowText = `3 days`;
|
||||
|
@ -13,6 +13,7 @@ describe('DateTimePicker', () => {
|
|||
const dropdownToggle = () => dateTimePicker.find('.dropdown-toggle');
|
||||
const dropdownMenu = () => dateTimePicker.find('.dropdown-menu');
|
||||
const applyButtonElement = () => dateTimePicker.find('button.btn-success').element;
|
||||
const findQuickRangeItems = () => dateTimePicker.findAll('.dropdown-item');
|
||||
const cancelButtonElement = () => dateTimePicker.find('button.btn-secondary').element;
|
||||
const fillInputAndBlur = (input, val) => {
|
||||
dateTimePicker.find(input).setValue(val);
|
||||
|
@ -25,7 +26,6 @@ describe('DateTimePicker', () => {
|
|||
const createComponent = props => {
|
||||
dateTimePicker = mount(DateTimePicker, {
|
||||
propsData: {
|
||||
timeWindows,
|
||||
start,
|
||||
end,
|
||||
...props,
|
||||
|
@ -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 => {
|
||||
createComponent({
|
||||
start: '2019-10-10T00:00:00.000Z',
|
||||
|
@ -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();
|
||||
dropdownToggle().trigger('click');
|
||||
dateTimePicker.vm.$nextTick(() => {
|
||||
expect(dateTimePicker.findAll('.dropdown-item').length).toBe(timeWindowsCount);
|
||||
expect(findQuickRangeItems().length).toBe(timeWindowsCount);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as monitoringUtils from '~/monitoring/utils';
|
||||
import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
|
||||
import {
|
||||
graphDataPrometheusQuery,
|
||||
graphDataPrometheusQueryRange,
|
||||
|
@ -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', () => {
|
||||
/*
|
||||
* When dealing with a metric using the query format, e.g.
|
||||
|
@ -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', () => {
|
||||
let oneMetric;
|
||||
let threeMetrics;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Vue from 'vue';
|
||||
import { createStore } from 'ee_else_ce/mr_notes/stores';
|
||||
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
||||
import { mockTracking, triggerEvent } from 'spec/helpers/tracking_helper';
|
||||
import DiffFileComponent from '~/diffs/components/diff_file.vue';
|
||||
import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
|
||||
import diffFileMockDataReadable from '../mock_data/diff_file';
|
||||
|
@ -8,12 +9,14 @@ import diffFileMockDataUnreadable from '../mock_data/diff_file_unreadable';
|
|||
|
||||
describe('DiffFile', () => {
|
||||
let vm;
|
||||
let trackingSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
vm = createComponentWithStore(Vue.extend(DiffFileComponent), createStore(), {
|
||||
file: JSON.parse(JSON.stringify(diffFileMockDataReadable)),
|
||||
canCurrentUserFork: false,
|
||||
}).$mount();
|
||||
trackingSpy = mockTracking('_category_', vm.$el, spyOn);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -30,6 +33,7 @@ describe('DiffFile', () => {
|
|||
|
||||
expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
|
||||
expect(el.querySelector('.js-file-title')).toBeDefined();
|
||||
expect(el.querySelector('.btn-clipboard')).toBeDefined();
|
||||
expect(el.querySelector('.file-title-name').innerText.indexOf(file_path)).toBeGreaterThan(-1);
|
||||
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
|
||||
|
||||
|
@ -39,6 +43,25 @@ describe('DiffFile', () => {
|
|||
.then(() => {
|
||||
expect(el.querySelectorAll('.line_content').length).toBe(5);
|
||||
expect(el.querySelectorAll('.js-line-expansion-content').length).toBe(1);
|
||||
triggerEvent('.btn-clipboard');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('should track a click event on copy to clip board button', done => {
|
||||
const el = vm.$el;
|
||||
|
||||
expect(el.querySelector('.btn-clipboard')).toBeDefined();
|
||||
vm.file.renderIt = true;
|
||||
vm.$nextTick()
|
||||
.then(() => {
|
||||
triggerEvent('.btn-clipboard');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_copy_file_button', {
|
||||
label: 'diff_copy_file_path_button',
|
||||
property: 'diff_copy_file',
|
||||
});
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
|
|
Loading…
Reference in a new issue