diff --git a/frontend/cypress/e2e/task/date-display.spec.ts b/frontend/cypress/e2e/task/date-display.spec.ts new file mode 100644 index 000000000..fa9f85427 --- /dev/null +++ b/frontend/cypress/e2e/task/date-display.spec.ts @@ -0,0 +1,57 @@ +import {UserFactory} from '../../factories/user' +import {ProjectFactory} from '../../factories/project' +import {TaskFactory} from '../../factories/task' +import {login} from '../../support/authenticateUser' +import {DATE_DISPLAY} from '../../../src/constants/dateDisplay' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' + +dayjs.extend(relativeTime) + +const createdDate = new Date(Date.UTC(2022, 6, 25, 12)) +const now = new Date(Date.UTC(2022, 6, 30, 12)) + +const expectedFormats = { + [DATE_DISPLAY.RELATIVE]: dayjs(createdDate).from(now), + [DATE_DISPLAY.MM_DD_YYYY]: dayjs(createdDate).format('MM-DD-YYYY'), + [DATE_DISPLAY.DD_MM_YYYY]: dayjs(createdDate).format('DD-MM-YYYY'), + [DATE_DISPLAY.YYYY_MM_DD]: dayjs(createdDate).format('YYYY-MM-DD'), + [DATE_DISPLAY.MM_SLASH_DD_YYYY]: dayjs(createdDate).format('MM/DD/YYYY'), + [DATE_DISPLAY.DD_SLASH_MM_YYYY]: dayjs(createdDate).format('DD/MM/YYYY'), + [DATE_DISPLAY.YYYY_SLASH_MM_DD]: dayjs(createdDate).format('YYYY/MM/DD'), + [DATE_DISPLAY.DAY_MONTH_YEAR]: new Intl.DateTimeFormat('en', { + day: 'numeric', + month: 'long', + year: 'numeric', + }).format(createdDate), + [DATE_DISPLAY.WEEKDAY_DAY_MONTH_YEAR]: new Intl.DateTimeFormat('en', { + weekday: 'long', + day: 'numeric', + month: 'long', + year: 'numeric', + }).format(createdDate), +} + +describe('Date display setting', () => { + Object.entries(expectedFormats).forEach(([format, expected]) => { + it(`shows ${format}`, () => { + const user = UserFactory.create(1, { + frontend_settings: JSON.stringify({dateDisplay: format}), + })[0] + const project = ProjectFactory.create(1, {owner_id: user.id})[0] + TaskFactory.truncate() + const task = TaskFactory.create(1, { + id: 1, + project_id: project.id, + created_by_id: user.id, + created: createdDate.toISOString(), + updated: createdDate.toISOString(), + })[0] + + cy.clock(now, ['Date']) + login(user) + cy.visit(`/tasks/${task.id}`) + cy.get('.task-view .created time span').should('contain', expected) + }) + }) +}) diff --git a/frontend/cypress/e2e/task/task.spec.ts b/frontend/cypress/e2e/task/task.spec.ts index b0029600d..f36fa4814 100644 --- a/frontend/cypress/e2e/task/task.spec.ts +++ b/frontend/cypress/e2e/task/task.spec.ts @@ -1,5 +1,10 @@ import {createFakeUserAndLogin} from '../../support/authenticateUser' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' + +dayjs.extend(relativeTime) + import {TaskFactory} from '../../factories/task' import {ProjectFactory} from '../../factories/project' import {TaskCommentFactory} from '../../factories/task_comment' @@ -629,10 +634,9 @@ describe('Task', () => { .click() const today = new Date() - const day = today.toLocaleString('default', {day: 'numeric'}) - const month = today.toLocaleString('default', {month: 'short'}) - const year = today.toLocaleString('default', {year: 'numeric'}) - const date = `${month} ${day}, ${year} 12:00 PM` + today.setHours(12) + today.setMinutes(0) + today.setSeconds(0) cy.get('.task-view .columns.details .column') .contains('Due Date') .get('.date-input .datepicker-popup') @@ -640,7 +644,7 @@ describe('Task', () => { cy.get('.task-view .columns.details .column') .contains('Due Date') .get('.date-input') - .should('contain.text', date) + .should('contain.text', dayjs(today).fromNow()) cy.get('.global-notification') .should('contain', 'Success') }) @@ -658,6 +662,9 @@ describe('Task', () => { }) const today = new Date(2025, 2, 5) + today.setHours(12) + today.setMinutes(0) + today.setSeconds(0) cy.visit(`/tasks/${tasks[0].id}`) @@ -674,10 +681,6 @@ describe('Task', () => { .contains('Confirm') .click() - const day = today.toLocaleString('default', {day: 'numeric'}) - const month = today.toLocaleString('default', {month: 'short'}) - const year = today.toLocaleString('default', {year: 'numeric'}) - const date = `${month} ${day}, ${year} 12:00 PM` cy.get('.task-view .columns.details .column') .contains('Due Date') .get('.date-input .datepicker-popup') @@ -685,7 +688,7 @@ describe('Task', () => { cy.get('.task-view .columns.details .column') .contains('Due Date') .get('.date-input') - .should('contain.text', date) + .should('contain.text', dayjs(today).fromNow()) cy.get('.global-notification') .should('contain', 'Success') }) diff --git a/frontend/src/components/input/Datepicker.vue b/frontend/src/components/input/Datepicker.vue index ae52be833..7ea252261 100644 --- a/frontend/src/components/input/Datepicker.vue +++ b/frontend/src/components/input/Datepicker.vue @@ -5,7 +5,7 @@ :disabled="disabled || undefined" @click.stop="toggleDatePopup" > - {{ date === null ? chooseDateLabel : formatDateShort(date) }} + {{ date === null ? chooseDateLabel : formatDisplayDate(date) }} @@ -39,7 +39,7 @@ import CustomTransition from '@/components/misc/CustomTransition.vue' import DatepickerInline from '@/components/input/DatepickerInline.vue' import SimpleButton from '@/components/input/SimpleButton.vue' -import {formatDateShort} from '@/helpers/time/formatDate' +import {formatDisplayDate} from '@/helpers/time/formatDate' import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside' import {createDateFromString} from '@/helpers/time/createDateFromString' import {useI18n} from 'vue-i18n' diff --git a/frontend/src/components/notifications/Notifications.vue b/frontend/src/components/notifications/Notifications.vue index 6147a227f..44a67a31e 100644 --- a/frontend/src/components/notifications/Notifications.vue +++ b/frontend/src/components/notifications/Notifications.vue @@ -59,7 +59,7 @@ v-tooltip="formatDateLong(n.created)" class="created" > - {{ formatDateSince(n.created) }} + {{ formatDisplayDate(n.created) }} @@ -95,7 +95,7 @@ import CustomTransition from '@/components/misc/CustomTransition.vue' import User from '@/components/misc/User.vue' import { NOTIFICATION_NAMES as names, type INotification} from '@/modelTypes/INotification' import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside' -import {formatDateLong, formatDateSince} from '@/helpers/time/formatDate' +import {formatDateLong, formatDisplayDate} from '@/helpers/time/formatDate' import {getDisplayName} from '@/models/user' import {useAuthStore} from '@/stores/auth' import XButton from '@/components/input/Button.vue' diff --git a/frontend/src/components/tasks/partials/Attachments.vue b/frontend/src/components/tasks/partials/Attachments.vue index 53b37dc65..bf27c1d21 100644 --- a/frontend/src/components/tasks/partials/Attachments.vue +++ b/frontend/src/components/tasks/partials/Attachments.vue @@ -56,7 +56,7 @@ scope="global" > - {{ formatDateSince(a.created) }} + {{ formatDisplayDate(a.created) }} - {{ formatDateSince(c.created) }} + {{ formatDisplayDate(c.created) }} - · {{ $t('task.comment.edited', {date: formatDateSince(c.updated)}) }} + · {{ $t('task.comment.edited', {date: formatDisplayDate(c.updated)}) }} - {{ formatDateSince(task.created) }} + {{ formatDisplayDate(task.created) }} {{ getDisplayName(task.createdBy) }} @@ -46,7 +46,7 @@ diff --git a/frontend/src/components/tasks/partials/DateTableCell.vue b/frontend/src/components/tasks/partials/DateTableCell.vue index 01114518d..3cb2ce5b1 100644 --- a/frontend/src/components/tasks/partials/DateTableCell.vue +++ b/frontend/src/components/tasks/partials/DateTableCell.vue @@ -1,13 +1,13 @@