Commit 6a4264e1 authored by Nathan Friend's avatar Nathan Friend

Merge branch '199046-text-for-future-release-date-grammatically-incorrect' into 'master'

Resolve "Text for future Release date grammatically incorrect"

Closes #199046

See merge request gitlab-org/gitlab!28075
parents e6719bbd 590ec9e3
...@@ -57,6 +57,11 @@ export default { ...@@ -57,6 +57,11 @@ export default {
? sprintf(__("%{username}'s avatar"), { username: this.author.username }) ? sprintf(__("%{username}'s avatar"), { username: this.author.username })
: null; : null;
}, },
createdTime() {
const now = new Date();
const isFuture = now < new Date(this.releasedAt);
return isFuture ? __('Will be created') : __('Created');
},
}, },
}; };
</script> </script>
...@@ -86,7 +91,7 @@ export default { ...@@ -86,7 +91,7 @@ export default {
v-if="releasedAt || author" v-if="releasedAt || author"
class="float-left d-flex align-items-center js-author-date-info" class="float-left d-flex align-items-center js-author-date-info"
> >
<span class="text-secondary">{{ __('Created') }}&nbsp;</span> <span class="text-secondary">{{ createdTime }}&nbsp;</span>
<template v-if="releasedAt"> <template v-if="releasedAt">
<span <span
v-gl-tooltip.bottom v-gl-tooltip.bottom
......
...@@ -38,9 +38,12 @@ export default { ...@@ -38,9 +38,12 @@ export default {
return Boolean(this.author); return Boolean(this.author);
}, },
releasedTimeAgo() { releasedTimeAgo() {
return sprintf(__('released %{time}'), { const now = new Date();
time: this.timeFormatted(this.release.releasedAt), const isFuture = now < new Date(this.release.releasedAt);
}); const time = this.timeFormatted(this.release.releasedAt);
return isFuture
? sprintf(__('will be released %{time}'), { time })
: sprintf(__('released %{time}'), { time });
}, },
shouldRenderMilestones() { shouldRenderMilestones() {
return Boolean(this.release.milestones?.length); return Boolean(this.release.milestones?.length);
...@@ -74,7 +77,11 @@ export default { ...@@ -74,7 +77,11 @@ export default {
<div class="append-right-4"> <div class="append-right-4">
&bull; &bull;
<span v-gl-tooltip.bottom :title="tooltipTitle(release.releasedAt)"> <span
v-gl-tooltip.bottom
class="js-release-date-info"
:title="tooltipTitle(release.releasedAt)"
>
{{ releasedTimeAgo }} {{ releasedTimeAgo }}
</span> </span>
</div> </div>
......
---
title: Resolve Text for future Release date grammatically incorrect
merge_request: 28075
author:
type: fixed
...@@ -24285,6 +24285,9 @@ msgstr "" ...@@ -24285,6 +24285,9 @@ msgstr ""
msgid "Wiki|Wiki Pages" msgid "Wiki|Wiki Pages"
msgstr "" msgstr ""
msgid "Will be created"
msgstr ""
msgid "Will deploy to" msgid "Will deploy to"
msgstr "" msgstr ""
...@@ -26287,6 +26290,9 @@ msgstr "" ...@@ -26287,6 +26290,9 @@ msgstr ""
msgid "wiki page" msgid "wiki page"
msgstr "" msgstr ""
msgid "will be released %{time}"
msgstr ""
msgid "with %{additions} additions, %{deletions} deletions." msgid "with %{additions} additions, %{deletions} deletions."
msgstr "" msgstr ""
......
...@@ -3,13 +3,17 @@ import { GlLink } from '@gitlab/ui'; ...@@ -3,13 +3,17 @@ import { GlLink } from '@gitlab/ui';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue'; import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { release } from '../mock_data'; import { release as originalRelease } from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { cloneDeep } from 'lodash';
const mockFutureDate = new Date(9999, 0, 0).toISOString();
let mockIsFutureRelease = false;
jest.mock('~/vue_shared/mixins/timeago', () => ({ jest.mock('~/vue_shared/mixins/timeago', () => ({
methods: { methods: {
timeFormatted() { timeFormatted() {
return '7 fortnights ago'; return mockIsFutureRelease ? 'in 1 month' : '7 fortnights ago';
}, },
tooltipTitle() { tooltipTitle() {
return 'February 30, 2401'; return 'February 30, 2401';
...@@ -19,12 +23,12 @@ jest.mock('~/vue_shared/mixins/timeago', () => ({ ...@@ -19,12 +23,12 @@ jest.mock('~/vue_shared/mixins/timeago', () => ({
describe('Release block footer', () => { describe('Release block footer', () => {
let wrapper; let wrapper;
let releaseClone; let release;
const factory = (props = {}) => { const factory = (props = {}) => {
wrapper = mount(ReleaseBlockFooter, { wrapper = mount(ReleaseBlockFooter, {
propsData: { propsData: {
...convertObjectPropsToCamelCase(releaseClone, { deep: true }), ...convertObjectPropsToCamelCase(release, { deep: true }),
...props, ...props,
}, },
}); });
...@@ -33,11 +37,13 @@ describe('Release block footer', () => { ...@@ -33,11 +37,13 @@ describe('Release block footer', () => {
}; };
beforeEach(() => { beforeEach(() => {
releaseClone = JSON.parse(JSON.stringify(release)); release = cloneDeep(originalRelease);
}); });
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null;
mockIsFutureRelease = false;
}); });
const commitInfoSection = () => wrapper.find('.js-commit-info'); const commitInfoSection = () => wrapper.find('.js-commit-info');
...@@ -60,8 +66,8 @@ describe('Release block footer', () => { ...@@ -60,8 +66,8 @@ describe('Release block footer', () => {
const commitLink = commitInfoSectionLink(); const commitLink = commitInfoSectionLink();
expect(commitLink.exists()).toBe(true); expect(commitLink.exists()).toBe(true);
expect(commitLink.text()).toBe(releaseClone.commit.short_id); expect(commitLink.text()).toBe(release.commit.short_id);
expect(commitLink.attributes('href')).toBe(releaseClone.commit_path); expect(commitLink.attributes('href')).toBe(release.commit_path);
}); });
it('renders the tag icon', () => { it('renders the tag icon', () => {
...@@ -75,28 +81,60 @@ describe('Release block footer', () => { ...@@ -75,28 +81,60 @@ describe('Release block footer', () => {
const commitLink = tagInfoSection().find(GlLink); const commitLink = tagInfoSection().find(GlLink);
expect(commitLink.exists()).toBe(true); expect(commitLink.exists()).toBe(true);
expect(commitLink.text()).toBe(releaseClone.tag_name); expect(commitLink.text()).toBe(release.tag_name);
expect(commitLink.attributes('href')).toBe(releaseClone.tag_path); expect(commitLink.attributes('href')).toBe(release.tag_path);
}); });
it('renders the author and creation time info', () => { it('renders the author and creation time info', () => {
expect(trimText(authorDateInfoSection().text())).toBe( expect(trimText(authorDateInfoSection().text())).toBe(
`Created 7 fortnights ago by ${releaseClone.author.username}`, `Created 7 fortnights ago by ${release.author.username}`,
); );
}); });
describe('when the release date is in the past', () => {
it('prefixes the creation info with "Created"', () => {
expect(trimText(authorDateInfoSection().text())).toEqual(expect.stringMatching(/^Created/));
});
});
describe('renders the author and creation time info with future release date', () => {
beforeEach(() => {
mockIsFutureRelease = true;
factory({ releasedAt: mockFutureDate });
});
it('renders the release date without the author name', () => {
expect(trimText(authorDateInfoSection().text())).toBe(
`Will be created in 1 month by ${release.author.username}`,
);
});
});
describe('when the release date is in the future', () => {
beforeEach(() => {
mockIsFutureRelease = true;
factory({ releasedAt: mockFutureDate });
});
it('prefixes the creation info with "Will be created"', () => {
expect(trimText(authorDateInfoSection().text())).toEqual(
expect.stringMatching(/^Will be created/),
);
});
});
it("renders the author's avatar image", () => { it("renders the author's avatar image", () => {
const avatarImg = authorDateInfoSection().find('img'); const avatarImg = authorDateInfoSection().find('img');
expect(avatarImg.exists()).toBe(true); expect(avatarImg.exists()).toBe(true);
expect(avatarImg.attributes('src')).toBe(releaseClone.author.avatar_url); expect(avatarImg.attributes('src')).toBe(release.author.avatar_url);
}); });
it("renders a link to the author's profile", () => { it("renders a link to the author's profile", () => {
const authorLink = authorDateInfoSection().find(GlLink); const authorLink = authorDateInfoSection().find(GlLink);
expect(authorLink.exists()).toBe(true); expect(authorLink.exists()).toBe(true);
expect(authorLink.attributes('href')).toBe(releaseClone.author.web_url); expect(authorLink.attributes('href')).toBe(release.author.web_url);
}); });
}); });
...@@ -113,7 +151,7 @@ describe('Release block footer', () => { ...@@ -113,7 +151,7 @@ describe('Release block footer', () => {
it('renders the commit SHA as plain text (instead of a link)', () => { it('renders the commit SHA as plain text (instead of a link)', () => {
expect(commitInfoSectionLink().exists()).toBe(false); expect(commitInfoSectionLink().exists()).toBe(false);
expect(commitInfoSection().text()).toBe(releaseClone.commit.short_id); expect(commitInfoSection().text()).toBe(release.commit.short_id);
}); });
}); });
...@@ -130,7 +168,7 @@ describe('Release block footer', () => { ...@@ -130,7 +168,7 @@ describe('Release block footer', () => {
it('renders the tag name as plain text (instead of a link)', () => { it('renders the tag name as plain text (instead of a link)', () => {
expect(tagInfoSectionLink().exists()).toBe(false); expect(tagInfoSectionLink().exists()).toBe(false);
expect(tagInfoSection().text()).toBe(releaseClone.tag_name); expect(tagInfoSection().text()).toBe(release.tag_name);
}); });
}); });
...@@ -138,7 +176,18 @@ describe('Release block footer', () => { ...@@ -138,7 +176,18 @@ describe('Release block footer', () => {
beforeEach(() => factory({ author: undefined })); beforeEach(() => factory({ author: undefined }));
it('renders the release date without the author name', () => { it('renders the release date without the author name', () => {
expect(trimText(authorDateInfoSection().text())).toBe('Created 7 fortnights ago'); expect(trimText(authorDateInfoSection().text())).toBe(`Created 7 fortnights ago`);
});
});
describe('future release without any author info', () => {
beforeEach(() => {
mockIsFutureRelease = true;
factory({ author: undefined, releasedAt: mockFutureDate });
});
it('renders the release date without the author name', () => {
expect(trimText(authorDateInfoSection().text())).toBe(`Will be created in 1 month`);
}); });
}); });
...@@ -147,7 +196,7 @@ describe('Release block footer', () => { ...@@ -147,7 +196,7 @@ describe('Release block footer', () => {
it('renders the author name without the release date', () => { it('renders the author name without the release date', () => {
expect(trimText(authorDateInfoSection().text())).toBe( expect(trimText(authorDateInfoSection().text())).toBe(
`Created by ${releaseClone.author.username}`, `Created by ${release.author.username}`,
); );
}); });
}); });
......
import { mount } from '@vue/test-utils';
import { trimText } from 'helpers/text_helper';
import ReleaseBlockMetadata from '~/releases/components/release_block_metadata.vue';
import { release as originalRelease } from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { cloneDeep } from 'lodash';
const mockFutureDate = new Date(9999, 0, 0).toISOString();
let mockIsFutureRelease = false;
jest.mock('~/vue_shared/mixins/timeago', () => ({
methods: {
timeFormatted() {
return mockIsFutureRelease ? 'in 1 month' : '7 fortnights ago';
},
tooltipTitle() {
return 'February 30, 2401';
},
},
}));
describe('Release block metadata', () => {
let wrapper;
let release;
const factory = (releaseUpdates = {}) => {
wrapper = mount(ReleaseBlockMetadata, {
propsData: {
release: {
...convertObjectPropsToCamelCase(release, { deep: true }),
...releaseUpdates,
},
},
});
};
beforeEach(() => {
release = cloneDeep(originalRelease);
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
mockIsFutureRelease = false;
});
const findReleaseDateInfo = () => wrapper.find('.js-release-date-info');
describe('with all props provided', () => {
beforeEach(() => factory());
it('renders the release time info', () => {
expect(trimText(findReleaseDateInfo().text())).toBe(`released 7 fortnights ago`);
});
});
describe('with a future release date', () => {
beforeEach(() => {
mockIsFutureRelease = true;
factory({ releasedAt: mockFutureDate });
});
it('renders the release date without the author name', () => {
expect(trimText(findReleaseDateInfo().text())).toBe(`will be released in 1 month`);
});
});
});
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