From 562e166ad14c90687328a616b35c80aff7c461bd Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Fri, 21 Feb 2025 17:47:40 -0800 Subject: [PATCH 1/4] Slight refactor to pluralization util --- app/pages/project/instances/InstancesPage.tsx | 6 +++++- .../project/instances/instance/InstancePage.tsx | 5 ++++- app/util/str.spec.tsx | 15 +++++++++++++++ app/util/str.ts | 5 ++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/pages/project/instances/InstancesPage.tsx b/app/pages/project/instances/InstancesPage.tsx index f3558491f7..c500138e49 100644 --- a/app/pages/project/instances/InstancesPage.tsx +++ b/app/pages/project/instances/InstancesPage.tsx @@ -39,6 +39,7 @@ import { Tooltip } from '~/ui/lib/Tooltip' import { setDiff } from '~/util/array' import { toLocaleTimeString } from '~/util/date' import { pb } from '~/util/path-builder' +import { pluralize } from '~/util/str' import { useMakeInstanceActions } from './actions' import { ResizeInstanceModal } from './instance/InstancePage' @@ -99,7 +100,10 @@ export function Component() { header: 'CPU', cell: (info) => ( <> - {info.getValue()} vCPU + {info.getValue()}{' '} + + {pluralize('vCPU', info.getValue(), true)} + ), }), diff --git a/app/pages/project/instances/instance/InstancePage.tsx b/app/pages/project/instances/instance/InstancePage.tsx index 60d0a50a1d..bf430911c2 100644 --- a/app/pages/project/instances/instance/InstancePage.tsx +++ b/app/pages/project/instances/instance/InstancePage.tsx @@ -52,6 +52,7 @@ import { Spinner } from '~/ui/lib/Spinner' import { Tooltip } from '~/ui/lib/Tooltip' import { truncate } from '~/ui/lib/Truncate' import { pb } from '~/util/path-builder' +import { pluralize } from '~/util/str' import { GiB } from '~/util/units' import { useMakeInstanceActions } from '../actions' @@ -221,7 +222,9 @@ export function InstancePage() { {instance.ncpus} - vCPUs + + {pluralize(' vCPU', instance.ncpus, true)} + {memory.value} diff --git a/app/util/str.spec.tsx b/app/util/str.spec.tsx index 19fc93d56d..919b5c0e12 100644 --- a/app/util/str.spec.tsx +++ b/app/util/str.spec.tsx @@ -14,6 +14,7 @@ import { extractText, kebabCase, normalizeName, + pluralize, titleCase, } from './str' @@ -23,6 +24,20 @@ describe('capitalize', () => { }) }) +describe('pluralize', () => { + it('pluralizes correctly', () => { + expect(pluralize('item', 0)).toBe('0 items') + expect(pluralize('item', 1)).toBe('1 item') + expect(pluralize('item', 2)).toBe('2 items') + }) + + it('can return just the string', () => { + expect(pluralize('item', 0, true)).toBe('items') + expect(pluralize('item', 1, true)).toBe('item') + expect(pluralize('item', 2, true)).toBe('items') + }) +}) + describe('camelCase', () => { it('basic formats to camel case', () => { expect(camelCase('camelCase')).toBe('camelCase') diff --git a/app/util/str.ts b/app/util/str.ts index a7614fc894..deb60fdc58 100644 --- a/app/util/str.ts +++ b/app/util/str.ts @@ -10,7 +10,10 @@ import React, { type ReactElement, type ReactNode } from 'react' export const capitalize = (s: string) => s && s.charAt(0).toUpperCase() + s.slice(1) -export const pluralize = (s: string, n: number) => `${n} ${s}${n === 1 ? '' : 's'}` +export const pluralize = (word: string, count: number, stringOnly = false) => { + const pluralizedWord = count === 1 ? word : `${word}s` + return stringOnly ? pluralizedWord : `${count} ${pluralizedWord}` +} export const camelCase = (s: string) => s From 1fb122c2c8cbba95b3fcee015186b7379fcc1898 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Fri, 21 Feb 2025 21:25:00 -0800 Subject: [PATCH 2/4] Update strings in tests --- test/e2e/instance.e2e.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/e2e/instance.e2e.ts b/test/e2e/instance.e2e.ts index 2e6bab3b7c..c1e61bdbd3 100644 --- a/test/e2e/instance.e2e.ts +++ b/test/e2e/instance.e2e.ts @@ -161,7 +161,7 @@ test('can resize a failed or stopped instance', async ({ page }) => { // resize 'you-fail', currently in a failed state await expectRowVisible(table, { name: 'you-fail', - CPU: '4 vCPU', + CPU: '4 vCPUs', Memory: '6 GiB', state: expect.stringMatching(/^failed\d+s$/), }) @@ -173,7 +173,7 @@ test('can resize a failed or stopped instance', async ({ page }) => { await resizeModal.getByRole('button', { name: 'Resize' }).click() await expectRowVisible(table, { name: 'you-fail', - CPU: '10 vCPU', + CPU: '10 vCPUs', Memory: '20 GiB', state: expect.stringMatching(/^failed\d+s$/), }) @@ -181,7 +181,7 @@ test('can resize a failed or stopped instance', async ({ page }) => { // resize 'db1', which needs to be stopped first await expectRowVisible(table, { name: 'db1', - CPU: '2 vCPU', + CPU: '2 vCPUs', Memory: '4 GiB', state: expect.stringMatching(/^running\d+s$/), }) @@ -200,7 +200,7 @@ test('can resize a failed or stopped instance', async ({ page }) => { await resizeModal.getByRole('button', { name: 'Resize' }).click() await expectRowVisible(table, { name: 'db1', - CPU: '8 vCPU', + CPU: '8 vCPUs', Memory: '16 GiB', state: expect.stringMatching(/^stopped\d+s$/), }) @@ -224,19 +224,19 @@ test('instance table', async ({ page }) => { const table = page.getByRole('table') await expectRowVisible(table, { name: 'db1', - CPU: '2 vCPU', + CPU: '2 vCPUs', Memory: '4 GiB', state: expect.stringMatching(/^running\d+s$/), }) await expectRowVisible(table, { name: 'you-fail', - CPU: '4 vCPU', + CPU: '4 vCPUs', Memory: '6 GiB', state: expect.stringMatching(/^failed\d+s$/), }) await expectRowVisible(table, { name: 'not-there-yet', - CPU: '2 vCPU', + CPU: '2 vCPUs', Memory: '8 GiB', state: expect.stringMatching(/^starting\d+s$/), }) From 63cadf22c35d25ae2e27f245f3360bb0ec327348 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Sat, 22 Feb 2025 14:29:27 -0800 Subject: [PATCH 3/4] Simplify function --- app/pages/project/instances/InstancesPage.tsx | 4 +--- .../project/instances/instance/InstancePage.tsx | 4 +--- app/util/str.spec.tsx | 12 +++--------- app/util/str.ts | 5 +---- 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/app/pages/project/instances/InstancesPage.tsx b/app/pages/project/instances/InstancesPage.tsx index c500138e49..17a18d62c8 100644 --- a/app/pages/project/instances/InstancesPage.tsx +++ b/app/pages/project/instances/InstancesPage.tsx @@ -101,9 +101,7 @@ export function Component() { cell: (info) => ( <> {info.getValue()}{' '} - - {pluralize('vCPU', info.getValue(), true)} - + {pluralize('vCPU', info.getValue())} ), }), diff --git a/app/pages/project/instances/instance/InstancePage.tsx b/app/pages/project/instances/instance/InstancePage.tsx index bf430911c2..2975c7900d 100644 --- a/app/pages/project/instances/instance/InstancePage.tsx +++ b/app/pages/project/instances/instance/InstancePage.tsx @@ -222,9 +222,7 @@ export function InstancePage() { {instance.ncpus} - - {pluralize(' vCPU', instance.ncpus, true)} - + {pluralize(' vCPU', instance.ncpus)} {memory.value} diff --git a/app/util/str.spec.tsx b/app/util/str.spec.tsx index 919b5c0e12..4957882188 100644 --- a/app/util/str.spec.tsx +++ b/app/util/str.spec.tsx @@ -26,15 +26,9 @@ describe('capitalize', () => { describe('pluralize', () => { it('pluralizes correctly', () => { - expect(pluralize('item', 0)).toBe('0 items') - expect(pluralize('item', 1)).toBe('1 item') - expect(pluralize('item', 2)).toBe('2 items') - }) - - it('can return just the string', () => { - expect(pluralize('item', 0, true)).toBe('items') - expect(pluralize('item', 1, true)).toBe('item') - expect(pluralize('item', 2, true)).toBe('items') + expect(pluralize('item', 0)).toBe('items') + expect(pluralize('item', 1)).toBe('item') + expect(pluralize('item', 2)).toBe('items') }) }) diff --git a/app/util/str.ts b/app/util/str.ts index deb60fdc58..1e5b5f6843 100644 --- a/app/util/str.ts +++ b/app/util/str.ts @@ -10,10 +10,7 @@ import React, { type ReactElement, type ReactNode } from 'react' export const capitalize = (s: string) => s && s.charAt(0).toUpperCase() + s.slice(1) -export const pluralize = (word: string, count: number, stringOnly = false) => { - const pluralizedWord = count === 1 ? word : `${word}s` - return stringOnly ? pluralizedWord : `${count} ${pluralizedWord}` -} +export const pluralize = (s: string, n: number) => `${s}${n === 1 ? '' : 's'}` export const camelCase = (s: string) => s From f7a0a7094efc748e94e36a9c35a54ba146e801f7 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Mon, 24 Feb 2025 08:05:23 -0800 Subject: [PATCH 4/4] add test case to unrelated test --- app/util/str.spec.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/util/str.spec.tsx b/app/util/str.spec.tsx index 4957882188..8b774bc9a3 100644 --- a/app/util/str.spec.tsx +++ b/app/util/str.spec.tsx @@ -60,8 +60,9 @@ it('commaSeries', () => { expect(commaSeries([], 'or')).toBe('') expect(commaSeries(['a'], 'or')).toBe('a') expect(commaSeries(['a', 'b'], 'or')).toBe('a or b') - expect(commaSeries(['a', 'b'], 'or')).toBe('a or b') + expect(commaSeries(['a', 'b'], 'and')).toBe('a and b') expect(commaSeries(['a', 'b', 'c'], 'or')).toBe('a, b, or c') + expect(commaSeries(['a', 'b', 'c'], 'and')).toBe('a, b, and c') }) describe('titleCase', () => {