Commit 590ec9e3 authored by jakeburden's avatar jakeburden

Fix grammar for future releases

Future releases are prefixed with “will be”
Past releases are prefixed with “created in” or “released in”
parent ccd3b3fe
...@@ -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
...@@ -24036,6 +24036,9 @@ msgstr "" ...@@ -24036,6 +24036,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 ""
...@@ -26032,6 +26035,9 @@ msgstr "" ...@@ -26032,6 +26035,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