Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue18
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue10
-rw-r--r--app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker.vue29
-rw-r--r--app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_input.vue10
-rw-r--r--app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_lib.js112
-rw-r--r--app/assets/javascripts/monitoring/components/embed.vue4
-rw-r--r--app/assets/javascripts/monitoring/constants.js64
-rw-r--r--app/assets/javascripts/monitoring/utils.js65
-rw-r--r--changelogs/unreleased/197412.yml5
-rw-r--r--doc/ci/yaml/README.md3
-rw-r--r--locale/gitlab.pot9
-rw-r--r--spec/frontend/monitoring/components/dashboard_time_window_spec.js3
-rw-r--r--spec/frontend/monitoring/components/date_time_picker/date_time_picker_lib_spec.js264
-rw-r--r--spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js93
-rw-r--r--spec/frontend/monitoring/utils_spec.js274
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js23
16 files changed, 570 insertions, 416 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 5d27c6eb865..ee10a1e92fc 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -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" />
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index f6f119d4463..211990f3d7c 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -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"
/>
diff --git a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker.vue b/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker.vue
index 0aa710b1b3a..3a18a494cad 100644
--- a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker.vue
+++ b/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker.vue
@@ -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>
diff --git a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_input.vue b/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_input.vue
index c3beae18726..b27a379c46a 100644
--- a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_input.vue
+++ b/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_input.vue
@@ -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 {
diff --git a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_lib.js b/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_lib.js
new file mode 100644
index 00000000000..604b17baab9
--- /dev/null
+++ b/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_lib.js
@@ -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 {};
diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue
index 2f562071764..10c081070d7 100644
--- a/app/assets/javascripts/monitoring/components/embed.vue
+++ b/app/assets/javascripts/monitoring/components/embed.vue
@@ -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;
diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js
index 398b45b9012..2d5361a5029 100644
--- a/app/assets/javascripts/monitoring/constants.js
+++ b/app/assets/javascripts/monitoring/constants.js
@@ -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 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 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 timeWindowsKeyNames = Object.keys(secondsIn).reduce(
- (otherTimeWindows, timeWindow) => ({
- ...otherTimeWindows,
- [timeWindow]: timeWindow,
- }),
- {},
-);
diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js
index c824d6d4ddb..3847d885f9a 100644
--- a/app/assets/javascripts/monitoring/utils.js
+++ b/app/assets/javascripts/monitoring/utils.js
@@ -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
diff --git a/changelogs/unreleased/197412.yml b/changelogs/unreleased/197412.yml
new file mode 100644
index 00000000000..5118cbdd2a2
--- /dev/null
+++ b/changelogs/unreleased/197412.yml
@@ -0,0 +1,5 @@
+---
+title: Track usage of merge request file header buttons
+merge_request:
+author: Oregand
+type: other
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 4a79fc33da3..c6389ee5093 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -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
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e06754b9db5..65f2a948f20 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/spec/frontend/monitoring/components/dashboard_time_window_spec.js b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
index 8e2f52a3183..29cca695093 100644
--- a/spec/frontend/monitoring/components/dashboard_time_window_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
@@ -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(),
);
diff --git a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_lib_spec.js b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_lib_spec.js
new file mode 100644
index 00000000000..9c0f66427ae
--- /dev/null
+++ b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_lib_spec.js
@@ -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);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js
index 340143a6b53..3b37da5bcd6 100644
--- a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js
+++ b/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js
@@ -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();
+ });
+ });
+ });
});
diff --git a/spec/frontend/monitoring/utils_spec.js b/spec/frontend/monitoring/utils_spec.js
index 9b1a331e3b5..9df48eb0ad3 100644
--- a/spec/frontend/monitoring/utils_spec.js
+++ b/spec/frontend/monitoring/utils_spec.js
@@ -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;
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index b4425b8e8a2..eab4f4fb17f 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -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);