From 4437a4f57bca289d9ab6213c544ee42d0acb369d Mon Sep 17 00:00:00 2001 From: Bryan Liao Date: Mon, 14 Jul 2025 18:30:31 -0700 Subject: [PATCH] Updated validation method and related test cases --- .../epoch-converter.e2e.spec.ts | 56 ++++++++++++++++--- .../epoch-converter.service.test.ts | 0 .../epoch-converter.service.ts | 15 ----- src/tools/epoch-converter/epoch-converter.vue | 13 ++++- 4 files changed, 59 insertions(+), 25 deletions(-) rename src/tools/epoch-converter/{__test__ => __test}/epoch-converter.e2e.spec.ts (66%) rename src/tools/epoch-converter/{__test__ => __test}/epoch-converter.service.test.ts (100%) diff --git a/src/tools/epoch-converter/__test__/epoch-converter.e2e.spec.ts b/src/tools/epoch-converter/__test/epoch-converter.e2e.spec.ts similarity index 66% rename from src/tools/epoch-converter/__test__/epoch-converter.e2e.spec.ts rename to src/tools/epoch-converter/__test/epoch-converter.e2e.spec.ts index 2d32e38d..a2633169 100644 --- a/src/tools/epoch-converter/__test__/epoch-converter.e2e.spec.ts +++ b/src/tools/epoch-converter/__test/epoch-converter.e2e.spec.ts @@ -17,7 +17,6 @@ test.describe('Tool - Epoch converter', () => { const localLine = page.locator('text=Local Time:'); await expect(utcLine).toContainText('GMT (UTC): Fri, 20 Jun 2025 20:48:00 GMT'); - // await expect(localLine).toContainText('Local Time: Fri, Jun 20, 2025, 22:48:00 GMT+2'); await expect(localLine).toContainText('Local Time:'); await expect(localLine).toContainText('Jun 20, 2025'); await expect(localLine).toContainText('22:48:00'); @@ -34,24 +33,24 @@ test.describe('Tool - Epoch converter', () => { await page.getByRole('button', { name: 'Convert to Epoch (Local)' }).click(); - await expect(page.getByText('1750420080')).toBeVisible({ timeout: 50000 }); + await expect(page.getByText('1750420080')).toBeVisible({ timeout: 5000 }); await page.getByRole('button', { name: 'Convert to Epoch (UTC)' }).click(); - await expect(page.getByText('1750427280')).toBeVisible({ timeout: 50000 }); + await expect(page.getByText('1750427280')).toBeVisible({ timeout: 5000 }); }); test('Shows error if year is not 4 digits', async ({ page }) => { await page.getByPlaceholder('YYYY').fill('99'); await page.getByRole('button', { name: 'Convert to Epoch (Local)' }).click(); - await expect(page.getByText('Year must be 4 digits')).toBeVisible({ timeout: 50000 }); + await expect(page.getByText('Year must be 4 digits')).toBeVisible({ timeout: 5000 }); }); test('Shows error if month is invalid', async ({ page }) => { await page.getByPlaceholder('YYYY').fill('1999'); await page.getByPlaceholder('MM').first().fill('00'); await page.getByRole('button', { name: 'Convert to Epoch (Local)' }).click(); - await expect(page.getByText('Month must be between 1 and 12')).toBeVisible({ timeout: 50000 }); + await expect(page.getByText('Month must be between 1 and 12')).toBeVisible({ timeout: 5000 }); }); test('Shows error if day is invalid', async ({ page }) => { @@ -59,7 +58,7 @@ test.describe('Tool - Epoch converter', () => { await page.getByPlaceholder('MM').first().fill('01'); await page.getByPlaceholder('DD').fill('0'); await page.getByRole('button', { name: 'Convert to Epoch (UTC)' }).click(); - await expect(page.getByText('Day must be between 1 and 31')).toBeVisible({ timeout: 50000 }); + await expect(page.getByText('Day must be between 1 and 31')).toBeVisible({ timeout: 5000 }); }); test('Shows error if hour is invalid', async ({ page }) => { @@ -68,7 +67,7 @@ test.describe('Tool - Epoch converter', () => { await page.getByPlaceholder('DD').fill('1'); await page.getByPlaceholder('HH').fill('24'); await page.getByRole('button', { name: 'Convert to Epoch (Local)' }).click(); - await expect(page.getByText('Hour must be between 0 and 23')).toBeVisible({ timeout: 50000 }); + await expect(page.getByText('Hour must be between 0 and 23')).toBeVisible({ timeout: 5000 }); }); test('Shows error if minute is invalid', async ({ page }) => { @@ -78,7 +77,7 @@ test.describe('Tool - Epoch converter', () => { await page.getByPlaceholder('HH').fill('23'); await page.getByPlaceholder('MM').nth(1).fill('60'); await page.getByRole('button', { name: 'Convert to Epoch (UTC)' }).click(); - await expect(page.getByText('Minute must be between 0 and 59')).toBeVisible({ timeout: 50000 }); + await expect(page.getByText('Minute must be between 0 and 59')).toBeVisible({ timeout: 5000 }); }); test('Shows error if second is invalid', async ({ page }) => { @@ -89,6 +88,45 @@ test.describe('Tool - Epoch converter', () => { await page.getByPlaceholder('MM').nth(1).fill('59'); await page.getByPlaceholder('SS').fill('99'); await page.getByRole('button', { name: 'Convert to Epoch (UTC)' }).click(); - await expect(page.getByText('Second must be between 0 and 59')).toBeVisible({ timeout: 50000 }); + await expect(page.getByText('Second must be between 0 and 59')).toBeVisible({ timeout: 5000 }); + }); + + test('Shows error for invalid combination like February 31', async ({ page }) => { + await page.getByPlaceholder('YYYY').fill('2024'); + await page.getByPlaceholder('MM').first().fill('02'); + await page.getByPlaceholder('DD').fill('31'); + await page.getByPlaceholder('HH').fill('12'); + await page.getByPlaceholder('MM').nth(1).fill('00'); + await page.getByPlaceholder('SS').fill('00'); + await page.getByRole('button', { name: 'Convert to Epoch (Local)' }).click(); + + await expect(page.getByText('Invalid date string')).toBeVisible({ timeout: 30000 }); + }); + + test('Shows error for invalid date like April 31', async ({ page }) => { + await page.getByPlaceholder('YYYY').fill('2024'); + await page.getByPlaceholder('MM').first().fill('04'); + await page.getByPlaceholder('DD').fill('31'); + await page.getByPlaceholder('HH').fill('12'); + await page.getByPlaceholder('MM').nth(1).fill('00'); + await page.getByPlaceholder('SS').fill('00'); + await page.getByRole('button', { name: 'Convert to Epoch (UTC)' }).click(); + await expect(page.locator('.c-alert--icon')).toBeVisible({ timeout: 60000 }); + + await expect(page.getByText('Invalid date string')).toBeVisible({ timeout: 60000 }); + }); + + test('Allows valid leap year date: February 29, 2024', async ({ page }) => { + await page.getByPlaceholder('YYYY').fill('2024'); + await page.getByPlaceholder('MM').first().fill('02'); + await page.getByPlaceholder('DD').fill('29'); + await page.getByPlaceholder('HH').fill('12'); + await page.getByPlaceholder('MM').nth(1).fill('00'); + await page.getByPlaceholder('SS').fill('00'); + + await page.getByRole('button', { name: 'Convert to Epoch (UTC)' }).click(); + + // Should not show any error alert + await expect(page.locator('.c-alert')).not.toBeVisible(); }); }); diff --git a/src/tools/epoch-converter/__test__/epoch-converter.service.test.ts b/src/tools/epoch-converter/__test/epoch-converter.service.test.ts similarity index 100% rename from src/tools/epoch-converter/__test__/epoch-converter.service.test.ts rename to src/tools/epoch-converter/__test/epoch-converter.service.test.ts diff --git a/src/tools/epoch-converter/epoch-converter.service.ts b/src/tools/epoch-converter/epoch-converter.service.ts index 5f800747..17367b04 100644 --- a/src/tools/epoch-converter/epoch-converter.service.ts +++ b/src/tools/epoch-converter/epoch-converter.service.ts @@ -81,23 +81,8 @@ export function dateToEpoch( if (parseAsUTC && !dateString.endsWith('Z')) { normalizedDateString = `${dateString}Z`; } - // eslint-disable-next-line no-console - console.log(`test = ${normalizedDateString}`); - // else { - // // Manually compute local timezone offset and append it - // const tempDate = new Date(`${dateString}`); - // const offsetMinutes = tempDate.getTimezoneOffset(); - // const sign = offsetMinutes <= 0 ? '+' : '-'; - // const absOffset = Math.abs(offsetMinutes); - // const hours = String(Math.floor(absOffset / 60)).padStart(2, '0'); - // const minutes = String(absOffset % 60).padStart(2, '0'); - // const offset = `${sign}${hours}:${minutes}`; - // normalizedDateString = `${dateString}${offset}`; - // } const date = new Date(normalizedDateString); - // eslint-disable-next-line no-console - console.log(date); if (Number.isNaN(date.getTime())) { throw new TypeError('Invalid date string'); } diff --git a/src/tools/epoch-converter/epoch-converter.vue b/src/tools/epoch-converter/epoch-converter.vue index 71fba89d..a33dcc11 100644 --- a/src/tools/epoch-converter/epoch-converter.vue +++ b/src/tools/epoch-converter/epoch-converter.vue @@ -87,6 +87,17 @@ function validateDateParts(parts: DateParts): void { if (+second < 0 || +second > 59) { throw new Error('Second must be between 0 and 59'); } + + // Cross-check full date validity + const y = +year; + const m = +month - 1; + const d = +day; + + const constructed = new Date(y, m, d); + + if (constructed.getUTCFullYear() !== y || constructed.getUTCMonth() !== m || constructed.getUTCDate() !== m) { + throw new TypeError('Invalid date string'); + } } // Clear errors on input @@ -183,7 +194,7 @@ watch(dateParts, () => { - + {{ dateInputError }}