From 78687b63ff0ba5cfafe4c82957ab41d18fe57975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Tue, 18 Aug 2026 16:57:11 -0600 Subject: [PATCH] Move formatRelativeDate() to formatText util, add util tests --- app/components/contribution-photo.gjs | 27 +---------- app/helpers/format-relative-date.js | 6 +++ app/utils/format-text.js | 20 ++++++++ tests/unit/utils/format-text-test.js | 66 +++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 app/helpers/format-relative-date.js create mode 100644 tests/unit/utils/format-text-test.js diff --git a/app/components/contribution-photo.gjs b/app/components/contribution-photo.gjs index c2a9ac6..80b488b 100644 --- a/app/components/contribution-photo.gjs +++ b/app/components/contribution-photo.gjs @@ -2,26 +2,7 @@ import Component from '@glimmer/component'; import { on } from '@ember/modifier'; import { fn } from '@ember/helper'; import or from 'ember-truth-helpers/helpers/or'; - -function formatRelativeDate(timestamp) { - if (!timestamp) return ''; - const date = new Date(timestamp * 1000); - const now = new Date(); - const diffMs = now - date; - const diffMin = Math.floor(diffMs / 60000); - const diffHr = Math.floor(diffMin / 60); - const diffDay = Math.floor(diffHr / 24); - - if (diffMin < 1) return 'just now'; - if (diffMin < 60) return `${diffMin} min ago`; - if (diffHr < 24) return `${diffHr} hr ago`; - if (diffDay < 7) return `${diffDay} day${diffDay === 1 ? '' : 's'} ago`; - return date.toLocaleDateString(undefined, { - year: 'numeric', - month: 'short', - day: 'numeric', - }); -} +import formatRelativeDate from '../helpers/format-relative-date'; export default class ContributionPhoto extends Component { get item() { @@ -44,10 +25,6 @@ export default class ContributionPhoto extends Component { return this.item?.placeNameLoading; } - get relativeDate() { - return formatRelativeDate(this.item?.createdAt); - } - get tags() { // Collect unique tags across all photos in the contribution group const all = (this.item?.photos || []).flatMap((p) => p.tags || []); @@ -86,7 +63,7 @@ export default class ContributionPhoto extends Component { {{/if}}
- {{this.relativeDate}} + {{formatRelativeDate @item.createdAt}} {{#if (gt this.item.photos.length 1)}} ยท {{this.item.photos.length}} diff --git a/app/helpers/format-relative-date.js b/app/helpers/format-relative-date.js new file mode 100644 index 0000000..6b435b9 --- /dev/null +++ b/app/helpers/format-relative-date.js @@ -0,0 +1,6 @@ +import { helper } from '@ember/component/helper'; +import { formatRelativeDate as format } from '../utils/format-text'; + +export default helper(function formatRelativeDate([timestamp]) { + return format(timestamp); +}); diff --git a/app/utils/format-text.js b/app/utils/format-text.js index a08f349..f6d7564 100644 --- a/app/utils/format-text.js +++ b/app/utils/format-text.js @@ -12,3 +12,23 @@ export function capitalize(text) { if (typeof text !== 'string' || !text) return ''; return text.charAt(0).toUpperCase() + text.slice(1); } + +export function formatRelativeDate(timestamp) { + if (!timestamp) return ''; + const date = new Date(timestamp * 1000); + const now = new Date(); + const diffMs = now - date; + const diffMin = Math.floor(diffMs / 60000); + const diffHr = Math.floor(diffMin / 60); + const diffDay = Math.floor(diffHr / 24); + + if (diffMin < 1) return 'just now'; + if (diffMin < 60) return `${diffMin} min ago`; + if (diffHr < 24) return `${diffHr} hr ago`; + if (diffDay < 7) return `${diffDay} day${diffDay === 1 ? '' : 's'} ago`; + return date.toLocaleDateString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric', + }); +} diff --git a/tests/unit/utils/format-text-test.js b/tests/unit/utils/format-text-test.js new file mode 100644 index 0000000..be47463 --- /dev/null +++ b/tests/unit/utils/format-text-test.js @@ -0,0 +1,66 @@ +import { module, test } from 'qunit'; +import { + formatRelativeDate, + humanizeOsmTag, + capitalize, +} from 'marco/utils/format-text'; + +module('Unit | Utility | format-text', function () { + test('humanizeOsmTag replaces underscores and dashes with spaces and title-cases', function (assert) { + assert.strictEqual(humanizeOsmTag('atm'), 'Atm'); + assert.strictEqual(humanizeOsmTag('fast_food'), 'Fast Food'); + assert.strictEqual(humanizeOsmTag('phone-repair'), 'Phone Repair'); + assert.strictEqual(humanizeOsmTag(''), ''); + assert.strictEqual(humanizeOsmTag(null), ''); + }); + + test('capitalize uppercases the first letter', function (assert) { + assert.strictEqual(capitalize('hello'), 'Hello'); + assert.strictEqual(capitalize(''), ''); + assert.strictEqual(capitalize(null), ''); + }); + + test('formatRelativeDate returns empty string for falsy timestamp', function (assert) { + assert.strictEqual(formatRelativeDate(null), ''); + assert.strictEqual(formatRelativeDate(0), ''); + assert.strictEqual(formatRelativeDate(undefined), ''); + }); + + test('formatRelativeDate returns "just now" for timestamps within the last minute', function (assert) { + const now = Math.floor(Date.now() / 1000); + assert.strictEqual(formatRelativeDate(now), 'just now'); + assert.strictEqual(formatRelativeDate(now - 10), 'just now'); + }); + + test('formatRelativeDate returns "min ago" for timestamps within the last hour', function (assert) { + const now = Math.floor(Date.now() / 1000); + assert.strictEqual(formatRelativeDate(now - 60), '1 min ago'); + assert.strictEqual(formatRelativeDate(now - 300), '5 min ago'); + assert.strictEqual(formatRelativeDate(now - 3599), '59 min ago'); + }); + + test('formatRelativeDate returns "hr ago" for timestamps within the last day', function (assert) { + const now = Math.floor(Date.now() / 1000); + assert.strictEqual(formatRelativeDate(now - 3600), '1 hr ago'); + assert.strictEqual(formatRelativeDate(now - 7200), '2 hr ago'); + assert.strictEqual(formatRelativeDate(now - 86399), '23 hr ago'); + }); + + test('formatRelativeDate returns "day(s) ago" for timestamps within the last week', function (assert) { + const now = Math.floor(Date.now() / 1000); + assert.strictEqual(formatRelativeDate(now - 86400), '1 day ago'); + assert.strictEqual(formatRelativeDate(now - 86400 * 2), '2 days ago'); + assert.strictEqual(formatRelativeDate(now - 86400 * 6), '6 days ago'); + }); + + test('formatRelativeDate returns an absolute date for timestamps older than a week', function (assert) { + const now = Math.floor(Date.now() / 1000); + const oldTimestamp = now - 86400 * 30; // 30 days ago + const result = formatRelativeDate(oldTimestamp); + // The exact format depends on the locale, but it should contain a year + assert.ok( + result.includes(String(new Date().getFullYear())), + 'Contains the year' + ); + }); +});