Commit 433460ce authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'ek-extend-total-time-component' into 'master'

Add approximate duration function

See merge request gitlab-org/gitlab!21803
parents aa12ca7b c280c279
...@@ -470,7 +470,7 @@ export const pikadayToString = date => { ...@@ -470,7 +470,7 @@ export const pikadayToString = date => {
*/ */
export const parseSeconds = ( export const parseSeconds = (
seconds, seconds,
{ daysPerWeek = 5, hoursPerDay = 8, limitToHours = false } = {}, { daysPerWeek = 5, hoursPerDay = 8, limitToHours = false, limitToDays = false } = {},
) => { ) => {
const DAYS_PER_WEEK = daysPerWeek; const DAYS_PER_WEEK = daysPerWeek;
const HOURS_PER_DAY = hoursPerDay; const HOURS_PER_DAY = hoursPerDay;
...@@ -486,8 +486,11 @@ export const parseSeconds = ( ...@@ -486,8 +486,11 @@ export const parseSeconds = (
minutes: 1, minutes: 1,
}; };
if (limitToHours) { if (limitToDays || limitToHours) {
timePeriodConstraints.weeks = 0; timePeriodConstraints.weeks = 0;
}
if (limitToHours) {
timePeriodConstraints.days = 0; timePeriodConstraints.days = 0;
} }
...@@ -612,3 +615,44 @@ export const secondsToDays = seconds => Math.round(seconds / 86400); ...@@ -612,3 +615,44 @@ export const secondsToDays = seconds => Math.round(seconds / 86400);
* @return {Date} the date following the date provided * @return {Date} the date following the date provided
*/ */
export const dayAfter = date => new Date(newDate(date).setDate(date.getDate() + 1)); export const dayAfter = date => new Date(newDate(date).setDate(date.getDate() + 1));
/**
* Mimics the behaviour of the rails distance_of_time_in_words function
* https://api.rubyonrails.org/v6.0.1/classes/ActionView/Helpers/DateHelper.html#method-i-distance_of_time_in_words
* 0 < -> 29 secs => less than a minute
* 30 secs < -> 1 min, 29 secs => 1 minute
* 1 min, 30 secs < -> 44 mins, 29 secs => [2..44] minutes
* 44 mins, 30 secs < -> 89 mins, 29 secs => about 1 hour
* 89 mins, 30 secs < -> 23 hrs, 59 mins, 29 secs => about[2..24]hours
* 23 hrs, 59 mins, 30 secs < -> 41 hrs, 59 mins, 29 secs => 1 day
* 41 hrs, 59 mins, 30 secs => x days
*
* @param {Number} seconds
* @return {String} approximated time
*/
export const approximateDuration = (seconds = 0) => {
if (!_.isNumber(seconds) || seconds < 0) {
return '';
}
const ONE_MINUTE_LIMIT = 90; // 1 minute 30s
const MINUTES_LIMIT = 2670; // 44 minutes 30s
const ONE_HOUR_LIMIT = 5370; // 89 minutes 30s
const HOURS_LIMIT = 86370; // 23 hours 59 minutes 30s
const ONE_DAY_LIMIT = 151170; // 41 hours 59 minutes 30s
const { days = 0, hours = 0, minutes = 0 } = parseSeconds(seconds, {
daysPerWeek: 7,
hoursPerDay: 24,
limitToDays: true,
});
if (seconds < 30) {
return __('less than a minute');
} else if (seconds < MINUTES_LIMIT) {
return n__('1 minute', '%d minutes', seconds < ONE_MINUTE_LIMIT ? 1 : minutes);
} else if (seconds < HOURS_LIMIT) {
return n__('about 1 hour', 'about %d hours', seconds < ONE_HOUR_LIMIT ? 1 : hours);
}
return n__('1 day', '%d days', seconds < ONE_DAY_LIMIT ? 1 : days);
};
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TotalTimeComponent with a blank object to render -- 1`] = `
"<span class=\\"total-time\\">
--
</span>"
`;
exports[`TotalTimeComponent with a valid time object with {"days": 3, "mins": 47, "seconds": 3} 1`] = `
"<span class=\\"total-time\\">
3 <span> days </span></span>"
`;
exports[`TotalTimeComponent with a valid time object with {"hours": 7, "mins": 20, "seconds": 10} 1`] = `
"<span class=\\"total-time\\">
7 <span> hrs </span></span>"
`;
exports[`TotalTimeComponent with a valid time object with {"hours": 23, "mins": 10} 1`] = `
"<span class=\\"total-time\\">
23 <span> hrs </span></span>"
`;
exports[`TotalTimeComponent with a valid time object with {"mins": 47, "seconds": 3} 1`] = `
"<span class=\\"total-time\\">
47 <span> mins </span></span>"
`;
exports[`TotalTimeComponent with a valid time object with {"seconds": 35} 1`] = `
"<span class=\\"total-time\\">
35 <span> s </span></span>"
`;
import { mount } from '@vue/test-utils';
import TotalTimeComponent from 'ee/analytics/cycle_analytics/components/total_time_component.vue';
describe('TotalTimeComponent', () => {
function createComponent(propsData) {
return mount(TotalTimeComponent, {
propsData,
});
}
let wrapper = null;
afterEach(() => {
wrapper.destroy();
});
describe('with a valid time object', () => {
it.each`
time
${{ seconds: 35 }}
${{ mins: 47, seconds: 3 }}
${{ days: 3, mins: 47, seconds: 3 }}
${{ hours: 23, mins: 10 }}
${{ hours: 7, mins: 20, seconds: 10 }}
`('with $time', ({ time }) => {
wrapper = createComponent({
time,
});
expect(wrapper.html()).toMatchSnapshot();
});
});
describe('with a blank object', () => {
beforeEach(() => {
wrapper = createComponent({
time: {},
});
});
it('to render --', () => {
expect(wrapper.html()).toMatchSnapshot();
});
});
});
...@@ -548,7 +548,9 @@ msgstr[0] "" ...@@ -548,7 +548,9 @@ msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "1 day" msgid "1 day"
msgstr "" msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
msgid "1 group" msgid "1 group"
msgid_plural "%d groups" msgid_plural "%d groups"
...@@ -560,6 +562,11 @@ msgid_plural "%{merge_requests} merged merge requests" ...@@ -560,6 +562,11 @@ msgid_plural "%{merge_requests} merged merge requests"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "1 minute"
msgid_plural "%d minutes"
msgstr[0] ""
msgstr[1] ""
msgid "1 open issue" msgid "1 open issue"
msgid_plural "%{issues} open issues" msgid_plural "%{issues} open issues"
msgstr[0] "" msgstr[0] ""
...@@ -21123,6 +21130,11 @@ msgstr "" ...@@ -21123,6 +21130,11 @@ msgstr ""
msgid "a design" msgid "a design"
msgstr "" msgstr ""
msgid "about 1 hour"
msgid_plural "about %d hours"
msgstr[0] ""
msgstr[1] ""
msgid "added %{created_at_timeago}" msgid "added %{created_at_timeago}"
msgstr "" msgstr ""
...@@ -21713,6 +21725,9 @@ msgstr "" ...@@ -21713,6 +21725,9 @@ msgstr ""
msgid "leave %{group_name}" msgid "leave %{group_name}"
msgstr "" msgstr ""
msgid "less than a minute"
msgstr ""
msgid "limit of %{project_limit} reached" msgid "limit of %{project_limit} reached"
msgstr "" msgstr ""
......
...@@ -341,6 +341,16 @@ describe('prettyTime methods', () => { ...@@ -341,6 +341,16 @@ describe('prettyTime methods', () => {
assertTimeUnits(twoDays, 3, 48, 0, 0); assertTimeUnits(twoDays, 3, 48, 0, 0);
}); });
it('should correctly parse values when limitedToDays is true', () => {
const sevenDays = datetimeUtility.parseSeconds(648750, {
hoursPerDay: 24,
daysPerWeek: 7,
limitToDays: true,
});
assertTimeUnits(sevenDays, 12, 12, 7, 0);
});
}); });
describe('stringifyTime', () => { describe('stringifyTime', () => {
...@@ -507,3 +517,32 @@ describe('secondsToDays', () => { ...@@ -507,3 +517,32 @@ describe('secondsToDays', () => {
expect(datetimeUtility.secondsToDays(270000)).toBe(3); expect(datetimeUtility.secondsToDays(270000)).toBe(3);
}); });
}); });
describe('approximateDuration', () => {
it.each`
seconds
${null}
${{}}
${[]}
${-1}
`('returns a blank string for seconds=$seconds', ({ seconds }) => {
expect(datetimeUtility.approximateDuration(seconds)).toBe('');
});
it.each`
seconds | approximation
${0} | ${'less than a minute'}
${25} | ${'less than a minute'}
${45} | ${'1 minute'}
${90} | ${'1 minute'}
${100} | ${'1 minute'}
${150} | ${'2 minutes'}
${220} | ${'3 minutes'}
${3000} | ${'about 1 hour'}
${30000} | ${'about 8 hours'}
${100000} | ${'1 day'}
${180000} | ${'2 days'}
`('converts $seconds seconds to $approximation', ({ seconds, approximation }) => {
expect(datetimeUtility.approximateDuration(seconds)).toBe(approximation);
});
});
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