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 {
? sprintf(__("%{username}'s avatar"), { username: this.author.username })
: null;
},
createdTime() {
const now = new Date();
const isFuture = now < new Date(this.releasedAt);
return isFuture ? __('Will be created') : __('Created');
},
},
};
</script>
......@@ -86,7 +91,7 @@ export default {
v-if="releasedAt || author"
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">
<span
v-gl-tooltip.bottom
......
......@@ -38,9 +38,12 @@ export default {
return Boolean(this.author);
},
releasedTimeAgo() {
return sprintf(__('released %{time}'), {
time: this.timeFormatted(this.release.releasedAt),
});
const now = new Date();
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() {
return Boolean(this.release.milestones?.length);
......@@ -74,7 +77,11 @@ export default {
<div class="append-right-4">
&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 }}
</span>
</div>
......
---
title: Resolve Text for future Release date grammatically incorrect
merge_request: 28075
author:
type: fixed
......@@ -24036,6 +24036,9 @@ msgstr ""
msgid "Wiki|Wiki Pages"
msgstr ""
msgid "Will be created"
msgstr ""
msgid "Will deploy to"
msgstr ""
......@@ -26032,6 +26035,9 @@ msgstr ""
msgid "wiki page"
msgstr ""
msgid "will be released %{time}"
msgstr ""
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
......
......@@ -3,13 +3,17 @@ import { GlLink } from '@gitlab/ui';
import { trimText } from 'helpers/text_helper';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.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 { cloneDeep } from 'lodash';
const mockFutureDate = new Date(9999, 0, 0).toISOString();
let mockIsFutureRelease = false;
jest.mock('~/vue_shared/mixins/timeago', () => ({
methods: {
timeFormatted() {
return '7 fortnights ago';
return mockIsFutureRelease ? 'in 1 month' : '7 fortnights ago';
},
tooltipTitle() {
return 'February 30, 2401';
......@@ -19,12 +23,12 @@ jest.mock('~/vue_shared/mixins/timeago', () => ({
describe('Release block footer', () => {
let wrapper;
let releaseClone;
let release;
const factory = (props = {}) => {
wrapper = mount(ReleaseBlockFooter, {
propsData: {
...convertObjectPropsToCamelCase(releaseClone, { deep: true }),
...convertObjectPropsToCamelCase(release, { deep: true }),
...props,
},
});
......@@ -33,11 +37,13 @@ describe('Release block footer', () => {
};
beforeEach(() => {
releaseClone = JSON.parse(JSON.stringify(release));
release = cloneDeep(originalRelease);
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
mockIsFutureRelease = false;
});
const commitInfoSection = () => wrapper.find('.js-commit-info');
......@@ -60,8 +66,8 @@ describe('Release block footer', () => {
const commitLink = commitInfoSectionLink();
expect(commitLink.exists()).toBe(true);
expect(commitLink.text()).toBe(releaseClone.commit.short_id);
expect(commitLink.attributes('href')).toBe(releaseClone.commit_path);
expect(commitLink.text()).toBe(release.commit.short_id);
expect(commitLink.attributes('href')).toBe(release.commit_path);
});
it('renders the tag icon', () => {
......@@ -75,28 +81,60 @@ describe('Release block footer', () => {
const commitLink = tagInfoSection().find(GlLink);
expect(commitLink.exists()).toBe(true);
expect(commitLink.text()).toBe(releaseClone.tag_name);
expect(commitLink.attributes('href')).toBe(releaseClone.tag_path);
expect(commitLink.text()).toBe(release.tag_name);
expect(commitLink.attributes('href')).toBe(release.tag_path);
});
it('renders the author and creation time info', () => {
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", () => {
const avatarImg = authorDateInfoSection().find('img');
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", () => {
const authorLink = authorDateInfoSection().find(GlLink);
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', () => {
it('renders the commit SHA as plain text (instead of a link)', () => {
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', () => {
it('renders the tag name as plain text (instead of a link)', () => {
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', () => {
beforeEach(() => factory({ author: undefined }));
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', () => {
it('renders the author name without the release date', () => {
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