diff --git a/frontend/__tests__/a11y/components/CalendarButton.a11y.test.tsx b/frontend/__tests__/a11y/components/CalendarButton.a11y.test.tsx index e5b9e45d89..d0e9cdcb81 100644 --- a/frontend/__tests__/a11y/components/CalendarButton.a11y.test.tsx +++ b/frontend/__tests__/a11y/components/CalendarButton.a11y.test.tsx @@ -7,8 +7,8 @@ const mockEvent = { title: 'Test Event', description: 'Test description', location: 'Test Location', - startDate: '2025-12-01', - endDate: '2025-12-02', + startDate: 1764547200, // 2025-12-01 + endDate: 1764633600, // 2025-12-02 } describe('CalendarButton Accessibility', () => { diff --git a/frontend/__tests__/a11y/components/CardDetailsPage.a11y.test.tsx b/frontend/__tests__/a11y/components/CardDetailsPage.a11y.test.tsx index 13e1711bc9..4366dba059 100644 --- a/frontend/__tests__/a11y/components/CardDetailsPage.a11y.test.tsx +++ b/frontend/__tests__/a11y/components/CardDetailsPage.a11y.test.tsx @@ -176,8 +176,8 @@ describe('CardDetailsPage a11y', () => { name: 'Intro to Web', description: 'A beginner friendly module.', experienceLevel: ExperienceLevelEnum.Beginner, - startedAt: '2025-01-01', - endedAt: '2025-03-01', + startedAt: 1735689600, // 2025-01-01 + endedAt: 1740787200, // 2025-03-01 mentors: [ { id: 'mentor-mentor1', diff --git a/frontend/__tests__/a11y/components/Milestones.a11y.test.tsx b/frontend/__tests__/a11y/components/Milestones.a11y.test.tsx index 22711eddab..20f0054274 100644 --- a/frontend/__tests__/a11y/components/Milestones.a11y.test.tsx +++ b/frontend/__tests__/a11y/components/Milestones.a11y.test.tsx @@ -27,7 +27,7 @@ const createMockMilestone = (overrides: Partial = {}): Milestone => ( author: createMockUser(), body: 'Test milestone description', closedIssuesCount: 5, - createdAt: '2023-01-01T00:00:00Z', + createdAt: 1672531200, // 2023-01-01T00:00:00Z openIssuesCount: 3, organizationName: 'test-org', progress: 75, diff --git a/frontend/__tests__/a11y/components/ProgramCard.a11y.test.tsx b/frontend/__tests__/a11y/components/ProgramCard.a11y.test.tsx index bc62ad2085..3a9dabae2c 100644 --- a/frontend/__tests__/a11y/components/ProgramCard.a11y.test.tsx +++ b/frontend/__tests__/a11y/components/ProgramCard.a11y.test.tsx @@ -14,8 +14,8 @@ const baseMockProgram: Program = { name: 'Test Program', description: 'This is a test program description', status: ProgramStatusEnum.Published, - startedAt: '2024-01-01T00:00:00Z', - endedAt: '2024-12-31T12:00:00Z', + startedAt: 1704067200, + endedAt: 1735646400, userRole: 'admin', } diff --git a/frontend/__tests__/a11y/components/RecentPullRequests.a11y.test.tsx b/frontend/__tests__/a11y/components/RecentPullRequests.a11y.test.tsx index 9bb0b89e97..b0b0f4ba51 100644 --- a/frontend/__tests__/a11y/components/RecentPullRequests.a11y.test.tsx +++ b/frontend/__tests__/a11y/components/RecentPullRequests.a11y.test.tsx @@ -47,13 +47,13 @@ const minimalData = [ { id: 'mock-pull-request', author: mockUser, - createdAt: '2024-06-01T12:00:00Z', + createdAt: 1717243200, organizationName: 'test-org', repositoryName: 'test-repo', title: 'Test Pull Request', url: 'https://github.com/test-org/test-repo/pull/1', state: 'open', - mergedAt: '2024-06-02T12:00:00Z', + mergedAt: 1717329600, }, ] diff --git a/frontend/__tests__/a11y/components/SingleModuleCard.a11y.test.tsx b/frontend/__tests__/a11y/components/SingleModuleCard.a11y.test.tsx index 9ed68786c6..dbb692f683 100644 --- a/frontend/__tests__/a11y/components/SingleModuleCard.a11y.test.tsx +++ b/frontend/__tests__/a11y/components/SingleModuleCard.a11y.test.tsx @@ -56,8 +56,8 @@ const mockModule: Module = { avatarUrl: 'https://example.com/avatar2.jpg', }, ], - startedAt: '2024-01-01T00:00:00Z', - endedAt: '2024-12-31T23:59:59Z', + startedAt: 1704067200, + endedAt: 1735689599, domains: ['frontend', 'backend'], tags: ['react', 'nodejs'], labels: ['good first issue', 'bug'], diff --git a/frontend/__tests__/a11y/components/SnapshotCard.a11y.test.tsx b/frontend/__tests__/a11y/components/SnapshotCard.a11y.test.tsx index d77f961516..8b38422cbd 100644 --- a/frontend/__tests__/a11y/components/SnapshotCard.a11y.test.tsx +++ b/frontend/__tests__/a11y/components/SnapshotCard.a11y.test.tsx @@ -6,8 +6,8 @@ const defaultProps = { key: 'test-snapshot-1', title: 'Test Snapshot', button: { label: 'Open', onclick: jest.fn() }, - startAt: '2025-01-01T00:00:00Z', - endAt: '2025-01-10T00:00:00Z', + startAt: 1735689600, + endAt: 1736467200, } describe('SnapshotCard a11y', () => { diff --git a/frontend/__tests__/a11y/pages/MyMentorship.a11y.test.tsx b/frontend/__tests__/a11y/pages/MyMentorship.a11y.test.tsx index 1bef8fbd9b..097d1d1187 100644 --- a/frontend/__tests__/a11y/pages/MyMentorship.a11y.test.tsx +++ b/frontend/__tests__/a11y/pages/MyMentorship.a11y.test.tsx @@ -16,8 +16,8 @@ const mockProgramData = { name: 'Test Program', description: 'Test Description', status: 'draft', - startedAt: '2025-07-28', - endedAt: '2025-08-10', + startedAt: 1753660800, + endedAt: 1754784000, experienceLevels: ['beginner'], menteesLimit: 10, admins: [], diff --git a/frontend/__tests__/e2e/data/mockHomeData.ts b/frontend/__tests__/e2e/data/mockHomeData.ts index ce97242795..823ecc2d3b 100644 --- a/frontend/__tests__/e2e/data/mockHomeData.ts +++ b/frontend/__tests__/e2e/data/mockHomeData.ts @@ -2,7 +2,7 @@ export const mockHomeData = { data: { recentProjects: [ { - createdAt: '2025-03-05T13:49:06+00:00', + createdAt: 1741182546, key: 'project_1', leaders: ['Leader 1', 'Leader 2'], name: 'Project 1', @@ -11,7 +11,7 @@ export const mockHomeData = { type: 'documentation', }, { - createdAt: '2025-03-05T13:45:28+00:00', + createdAt: 1741182328, key: 'eks-goat', leaders: ['Leader 3', 'Leader 4'], name: 'OWASP EKS Goat', @@ -20,7 +20,7 @@ export const mockHomeData = { type: 'code', }, { - createdAt: '2025-03-03T16:23:05+00:00', + createdAt: 1741019585, key: 'dojo-shield', leaders: ['Leader 5', 'Leader 6'], name: 'OWASP Dojo Shield', @@ -33,42 +33,42 @@ export const mockHomeData = { { authorName: 'Author 1', authorImageUrl: 'https://owasp.org/assets/images/people/shruti_kulkarni.jpeg', - publishedAt: '2025-03-06T07:00:00+00:00', + publishedAt: 1741244400, title: 'Post 1', url: 'https://owasp.org/blog/2025/03/06/owasp-education-and-training-committee-update.html', }, { authorName: 'Author 2', authorImageUrl: 'https://owasp.org/assets/images/people/leader_springett.png', - publishedAt: '2025-02-24T07:00:00+00:00', + publishedAt: 1740380400, title: 'Post 2', url: 'https://owasp.org/blog/2025/02/24/advisory-on-implementation-of-software-bill-of-materials-for-vulnerability-management.html', }, { authorName: 'Author 3', authorImageUrl: 'https://owasp.org/assets/images/people/leader_bjoern.jpg', - publishedAt: '2025-01-29T07:00:00+00:00', + publishedAt: 1738130400, title: 'Author 3', url: 'https://owasp.org/blog/2025/01/29/juice-shop-leadership.html', }, ], recentChapters: [ { - createdAt: '2025-03-18T01:03:09+00:00', + createdAt: 1742259789, key: 'chapter_1', leaders: ['Leader 1', 'Leader 3'], name: 'Chapter 1', suggestedLocation: 'Pune, Maharashtra, India', }, { - createdAt: '2025-03-13T00:01:01+00:00', + createdAt: 1741827661, key: 'chapter_2', leaders: ['Leader 1', 'Leader 2'], name: 'Chapter 2', suggestedLocation: 'Location 2', }, { - createdAt: '2025-02-25T02:04:57+00:00', + createdAt: 1740449097, key: 'chapter_3', leaders: ['Leader 1', 'Leader 2'], name: 'Chapter 3', @@ -94,7 +94,7 @@ export const mockHomeData = { ], recentIssues: [ { - createdAt: '2025-03-20T14:03:10+00:00', + createdAt: 1742479390, title: 'Issue 1', repositoryName: 'Dependency-Track', url: 'https://github.com/DependencyTrack/hyades/issues/1724', @@ -105,7 +105,7 @@ export const mockHomeData = { }, }, { - createdAt: '2025-03-20T10:30:33+00:00', + createdAt: 1742466633, title: 'Issue 2', repositoryName: 'BLT', url: 'https://github.com/OWASP-BLT/BLT/issues/4013', @@ -116,7 +116,7 @@ export const mockHomeData = { }, }, { - createdAt: '2025-03-20T09:36:39+00:00', + createdAt: 1742463399, title: 'Troubleshooting the OWASP Dependency-Check Scanner', repositoryName: 'devguard', url: 'https://github.com/l3montree-dev/devguard/issues/336', @@ -135,7 +135,7 @@ export const mockHomeData = { name: '', }, name: 'Release 1', - publishedAt: '2025-03-19T13:51:35+00:00', + publishedAt: 1742392295, repositoryName: 'repo-1', tagName: 'v2.1.0', url: 'https://github.com/CycloneDX/cyclonedx-node-npm/releases/tag/v2.1.0', @@ -147,7 +147,7 @@ export const mockHomeData = { name: 'Arkadii Yakovets', }, name: 'Release 2', - publishedAt: '2025-03-20T01:41:36+00:00', + publishedAt: 1742434896, repositoryName: 'Nest', tagName: '0.1.25', url: 'https://github.com/OWASP/Nest/releases/tag/0.1.25', @@ -159,7 +159,7 @@ export const mockHomeData = { name: 'author 3', }, name: 'v4.14.0', - publishedAt: '2025-03-18T12:24:40+00:00', + publishedAt: 1742300680, repositoryName: 'secureCodeBox', tagName: 'v4.14.0', url: 'https://github.com/secureCodeBox/secureCodeBox/releases/tag/v4.14.0', @@ -177,7 +177,7 @@ export const mockHomeData = { closedIssuesCount: 15, repositoryName: 'Home Repo One', organizationName: 'OWASP', - createdAt: '2025-03-01T10:00:00Z', + createdAt: 1740823200, url: 'https://github.com/OWASP/repo-one/milestone/1', }, ], @@ -214,10 +214,10 @@ export const mockHomeData = { upcomingEvents: [ { category: 'APPSEC_DAYS', - endDate: '2025-04-06', + endDate: 1743897600, key: 'event_1', name: 'Event 1', - startDate: '2025-04-05', + startDate: 1743811200, summary: 'The OWASP Boston Application Security Conference 2025 will take place on April 5, 2025. This event is organized by the Boston chapter of OWASP and features presenters and workshops. The focus will be on the latest trends in application security. It is an opportunity for attendees to learn and engage with experts in the field.', suggestedLocation: 'Boston, USA', @@ -225,10 +225,10 @@ export const mockHomeData = { }, { category: 'PARTNER', - endDate: '2025-05-23', + endDate: 1747958400, key: 'event_2', name: 'Event 2', - startDate: '2025-05-19', + startDate: 1747612800, summary: 'The github 2025 event will take place in Oslo from May 19 to May 23, 2025. Attendees can find discounted tickets by using the provided link. This event will focus on various topics related to technology and development. It promises to be an informative experience for those interested in these fields.', suggestedLocation: 'Oslo, Norway', @@ -236,10 +236,10 @@ export const mockHomeData = { }, { category: 'GLOBAL', - endDate: '2025-05-30', + endDate: 1748563200, key: 'event_3', name: 'Event 3', - startDate: '2025-05-26', + startDate: 1748217600, summary: 'The OWASP Global AppSec EU 2025 event will take place from May 26 to May 30, 2025. The location for this event has not been specified in the information provided. This event focuses on application security and brings together professionals from around the world. Participants will have the opportunity to learn and share knowledge about improving security in software development.', suggestedLocation: 'Barcelona, Spain', diff --git a/frontend/__tests__/mockData/mockHomeData.ts b/frontend/__tests__/mockData/mockHomeData.ts index e0c181325e..e3d76b2905 100644 --- a/frontend/__tests__/mockData/mockHomeData.ts +++ b/frontend/__tests__/mockData/mockHomeData.ts @@ -1,7 +1,7 @@ export const mockGraphQLData = { recentProjects: [ { - createdAt: '2024-12-06T20:46:54+00:00', + createdAt: 1734158694, key: 'gamesec-framework', leaders: ['Project Leader1', 'Project Leader2'], name: 'OWASP GameSec Framework', @@ -9,7 +9,7 @@ export const mockGraphQLData = { url: '/projects/gamesec-framework', }, { - createdAt: '2024-12-06T20:46:54+00:00', + createdAt: 1734158694, key: 'owasp-project-2', leaders: ['Project Leader1', 'Project Leader2'], name: 'OWASP project 2', @@ -17,7 +17,7 @@ export const mockGraphQLData = { url: '/projects/owasp-project-2', }, { - createdAt: '2024-12-06T20:46:54+00:00', + createdAt: 1734158694, key: 'owasp-project-3', leaders: ['Project Leader1', 'Project Leader2'], name: 'OWASP project 3', @@ -25,7 +25,7 @@ export const mockGraphQLData = { url: '/projects/owasp-project-3', }, { - createdAt: '2024-12-06T20:46:54+00:00', + createdAt: 1734158694, key: 'owasp-project-4', leaders: ['Project Leader1', 'Project Leader2'], name: 'OWASP project 4', @@ -33,7 +33,7 @@ export const mockGraphQLData = { url: '/projects/owasp-project-4', }, { - createdAt: '2024-12-06T20:46:54+00:00', + createdAt: 1734158694, key: 'owasp-project-5', leaders: ['Project Leader1', 'Project Leader2'], name: 'OWASP project 5', @@ -45,7 +45,7 @@ export const mockGraphQLData = { { authorName: 'Author 1', authorImageUrl: 'https://owasp.org/assets/images/people/author1.png', - publishedAt: '2024-12-14T06:44:54+00:00', + publishedAt: 1734158694, title: 'Post 1', url: 'https://owasp.org/blog/post-1.html', }, @@ -58,7 +58,7 @@ export const mockGraphQLData = { ], recentChapters: [ { - createdAt: '2024-12-14T06:44:54+00:00', + createdAt: 1734158694, key: 'sivagangai', leaders: ['Chapter Leader1', 'Chapter Leader2'], name: 'OWASP Sivagangai', @@ -75,7 +75,7 @@ export const mockGraphQLData = { ], recentIssues: [ { - createdAt: '2024-12-14T06:44:54+00:00', + createdAt: 1734158694, number: 177, title: 'Documentation : Project Setup Documentation Update', author: { @@ -94,7 +94,7 @@ export const mockGraphQLData = { }, isPreRelease: false, name: 'v0.9.2', - publishedAt: '2024-12-13T14:43:46+00:00', + publishedAt: 1734098626, tagName: 'v0.9.2', url: 'https://github.com/owasp/owasp-nest/releases/tag/v0.9.2', }, @@ -111,7 +111,7 @@ export const mockGraphQLData = { closedIssuesCount: 15, repositoryName: 'Home Repo One', organizationName: 'OWASP', - createdAt: '2025-03-01T10:00:00Z', + createdAt: 1740823200, url: 'https://github.com/OWASP/repo-one/milestone/1', }, ], @@ -125,9 +125,9 @@ export const mockGraphQLData = { upcomingEvents: [ { category: 'Category 1', - endDate: '2025-02-28', + endDate: 1740769800, name: 'Event 1', - startDate: '2025-02-27', + startDate: 1740640200, summary: 'Event Summary', suggestedLocation: 'Location 1', url: 'https://nest.owasp.org/events/event-1', @@ -135,7 +135,7 @@ export const mockGraphQLData = { ], recentPullRequests: [ { - createdAt: '2025-03-25T10:00:00Z', + createdAt: 1742896800, title: 'Fix authentication bug', author: { name: 'John Doe', @@ -145,7 +145,7 @@ export const mockGraphQLData = { url: 'https://github.com/example/repo/pull/1', }, { - createdAt: '2025-03-24T15:30:00Z', + createdAt: 1742830200, title: 'Add new feature', author: { login: 'jane-smith', diff --git a/frontend/__tests__/mockData/mockModuleData.ts b/frontend/__tests__/mockData/mockModuleData.ts index 2115239c58..b9b80b932a 100644 --- a/frontend/__tests__/mockData/mockModuleData.ts +++ b/frontend/__tests__/mockData/mockModuleData.ts @@ -2,8 +2,8 @@ export const mockModuleData = { name: 'Intro to Web', description: 'A beginner friendly module.', experienceLevel: 'beginner', - startedAt: '2025-01-01', - endedAt: '2025-03-01', + startedAt: 1735689600, + endedAt: 1740787200, mentors: [{ login: 'mentor1' }], tags: ['tag1'], domains: ['domain1'], diff --git a/frontend/__tests__/mockData/mockOrganizationData.ts b/frontend/__tests__/mockData/mockOrganizationData.ts index 19c621e429..2e08db0921 100644 --- a/frontend/__tests__/mockData/mockOrganizationData.ts +++ b/frontend/__tests__/mockData/mockOrganizationData.ts @@ -174,7 +174,7 @@ export const mockOrganizationDetailsData = { closedIssuesCount: 15, repositoryName: 'Project Repo 1', organizationName: 'OWASP', - createdAt: '2025-03-01T10:00:00Z', + createdAt: 1740823200, // 2025-03-01T10:00:00Z url: 'https://github.com/OWASP/repo-one/milestone/1', }, ], diff --git a/frontend/__tests__/mockData/mockProgramData.ts b/frontend/__tests__/mockData/mockProgramData.ts index f80965afab..07fa9cabf4 100644 --- a/frontend/__tests__/mockData/mockProgramData.ts +++ b/frontend/__tests__/mockData/mockProgramData.ts @@ -5,8 +5,8 @@ export const mockPrograms = [ key: 'program_1', name: 'Program 1', description: 'This is a summary of Program 1.', - startedAt: '2025-01-01', - endedAt: '2025-12-31', + startedAt: 1735689600, + endedAt: 1767139200, status: ProgramStatusEnum.Published, modules: ['Module A', 'Module B'], }, @@ -18,8 +18,8 @@ export const mockProgramDetailsData = { name: 'Test Program', description: 'Sample summary', status: ProgramStatusEnum.Draft, - startedAt: '2025-01-01', - endedAt: '2025-12-31', + startedAt: 1735689600, + endedAt: 1767139200, menteesLimit: 20, experienceLevels: ['beginner', 'intermediate'], admins: [{ login: 'admin-user', avatarUrl: 'https://example.com/avatar.png' }], diff --git a/frontend/__tests__/mockData/mockProjectDetailsData.ts b/frontend/__tests__/mockData/mockProjectDetailsData.ts index fb21da8f27..895f34fd49 100644 --- a/frontend/__tests__/mockData/mockProjectDetailsData.ts +++ b/frontend/__tests__/mockData/mockProjectDetailsData.ts @@ -34,6 +34,8 @@ export const mockProjectDetailsData = { leaders: ['alice', 'bob'], level: 'Lab', name: 'Test Project', + createdAt: 1738768830, // Feb 5, 2025 + updatedAt: 1738931696, // Feb 7, 2025 recentIssues: [ { author: { @@ -42,7 +44,7 @@ export const mockProjectDetailsData = { name: 'Dave Debugger', url: 'https://github.com/arkid15r', }, - createdAt: '2025-02-05T15:20:30Z', + createdAt: 1738768830, repositoryName: 'test-repo', title: 'Fix authentication bug', }, @@ -56,7 +58,7 @@ export const mockProjectDetailsData = { }, isPreRelease: false, name: 'v1.2.0', - publishedAt: '2025-01-20T10:00:00Z', + publishedAt: 1737367200, tagName: 'v1.2.0', }, ], @@ -93,7 +95,6 @@ export const mockProjectDetailsData = { summary: 'An example project showcasing GraphQL and Django integration.', topics: ['graphql', 'django', 'backend'], type: 'Tool', - updatedAt: '2025-02-07T12:34:56Z', url: 'https://github.com/example-project', recentMilestones: [ { @@ -107,7 +108,7 @@ export const mockProjectDetailsData = { closedIssuesCount: 15, repositoryName: 'Project Repo 1', organizationName: 'OWASP', - createdAt: '2025-03-01T10:00:00Z', + createdAt: 1740823200, url: 'https://github.com/OWASP/repo-one/milestone/1', }, ], diff --git a/frontend/__tests__/mockData/mockRepositoryData.ts b/frontend/__tests__/mockData/mockRepositoryData.ts index 812d6f963c..402311bd76 100644 --- a/frontend/__tests__/mockData/mockRepositoryData.ts +++ b/frontend/__tests__/mockData/mockRepositoryData.ts @@ -1,7 +1,7 @@ export const mockRepositoryData = { repository: { name: 'Test Repo', - updatedAt: '2024-01-01T00:00:00Z', + updatedAt: 1704067200, // 2024-01-01T00:00:00Z license: 'MIT', size: 1200, url: 'https://github.com/test-repo', @@ -13,11 +13,11 @@ export const mockRepositoryData = { languages: ['JavaScript', 'TypeScript'], topics: ['web', 'security'], description: 'A sample test repository', - createdAt: '2023-12-15T00:00:00Z', + createdAt: 1702598400, // 2023-12-15T00:00:00Z issues: [ { title: 'Bug fix required', - createdAt: '2024-01-02T10:00:00Z', + createdAt: 1704189600, // 2024-01-02T10:00:00Z repositoryName: 'test-repo-2', author: { avatarUrl: 'https://avatars.githubusercontent.com/avatar.jpg', @@ -35,7 +35,7 @@ export const mockRepositoryData = { name: 'v1.0.0', tagName: 'v1.0.0', isPreRelease: false, - publishedAt: '2024-01-01T12:00:00Z', + publishedAt: 1704110400, // 2024-01-01T12:00:00Z author: { avatarUrl: 'https://avatars.githubusercontent.com/avatar.jpg', name: 'Test User 2', @@ -55,7 +55,7 @@ export const mockRepositoryData = { closedIssuesCount: 15, repositoryName: 'Repo One', organizationName: 'OWASP', - createdAt: '2025-03-01T10:00:00Z', + createdAt: 1740826800, url: 'https://github.com/OWASP/repo-one/milestone/1', }, ], diff --git a/frontend/__tests__/mockData/mockSnapshotData.ts b/frontend/__tests__/mockData/mockSnapshotData.ts index 39fe8bf1b7..992c77653e 100644 --- a/frontend/__tests__/mockData/mockSnapshotData.ts +++ b/frontend/__tests__/mockData/mockSnapshotData.ts @@ -2,16 +2,16 @@ export const mockSnapshotDetailsData = { snapshot: { title: 'New Snapshot', key: '2024-12', - updatedAt: '2025-03-02T20:33:46.880330+00:00', - createdAt: '2025-03-01T22:00:34.361937+00:00', - startAt: '2024-12-01T00:00:00+00:00', - endAt: '2024-12-31T22:00:30+00:00', + updatedAt: 1740944026, // 2025-03-02T20:33:46Z + createdAt: 1740865234, // 2025-03-01T22:00:34Z + startAt: 1733011200, + endAt: 1735689630, status: 'completed', errorMessage: '', newReleases: [ { name: 'v0.9.2', - publishedAt: '2024-12-13T14:43:46+00:00', + publishedAt: 1734101026, // 2024-12-13T14:43:46Z tagName: 'v0.9.2', projectName: 'test-project-1', organizationName: 'owasp', @@ -25,7 +25,7 @@ export const mockSnapshotDetailsData = { }, { name: 'Latest pre-release', - publishedAt: '2024-12-13T13:17:30+00:00', + publishedAt: 1734095850, // 2024-12-13T13:17:30Z tagName: 'pre-release', projectName: 'test-project-2', organizationName: 'owasp', @@ -68,7 +68,7 @@ export const mockSnapshotDetailsData = { { key: 'sivagangai', name: 'OWASP Sivagangai', - createdAt: '2024-07-30T10:07:33+00:00', + createdAt: 1722334053, // 2024-07-30T10:07:33Z suggestedLocation: 'Sivagangai, Tamil Nadu, India', region: 'Asia', summary: @@ -103,8 +103,8 @@ export const mockSnapshotData = { { title: 'New Snapshot', key: '2024-12', - startAt: '2024-12-01T00:00:00+00:00', - endAt: '2024-12-31T22:00:30+00:00', + startAt: 1733011200, + endAt: 1735689630, }, ], } diff --git a/frontend/__tests__/mockData/mockUserDetails.ts b/frontend/__tests__/mockData/mockUserDetails.ts index 30818e94bf..2eb4899657 100644 --- a/frontend/__tests__/mockData/mockUserDetails.ts +++ b/frontend/__tests__/mockData/mockUserDetails.ts @@ -53,7 +53,7 @@ export const mockUserDetailsData = { closedIssuesCount: 15, repositoryName: 'Project Repo 1', organizationName: 'OWASP', - createdAt: '2025-03-01T10:00:00Z', + createdAt: 1740823200, // 2025-03-01T10:00:00Z url: 'https://github.com/OWASP/repo-one/milestone/1', }, ], diff --git a/frontend/__tests__/unit/components/CalendarButton.test.tsx b/frontend/__tests__/unit/components/CalendarButton.test.tsx index fa8607b9de..78d194140c 100644 --- a/frontend/__tests__/unit/components/CalendarButton.test.tsx +++ b/frontend/__tests__/unit/components/CalendarButton.test.tsx @@ -19,8 +19,8 @@ const mockEvent = { title: 'Test Event', description: 'Test description', location: 'Test Location', - startDate: '2025-12-01', - endDate: '2025-12-02', + startDate: 1764547200, // 2025-12-01 + endDate: 1764633600, // 2025-12-02 } describe('CalendarButton', () => { @@ -302,8 +302,8 @@ describe('CalendarButton', () => { title: 'OWASP BeNeLux 2025', description: 'Annual conference', location: 'Belgium', - startDate: '2025-12-02', - endDate: '2025-12-03', + startDate: 1764633600, // 2025-12-02 + endDate: 1764720000, // 2025-12-03 }} className="text-gray-600 hover:text-gray-800 dark:text-gray-400" iconClassName="h-4 w-4" @@ -319,7 +319,7 @@ describe('CalendarButton', () => { { }) }) - describe('hover state', () => { - it('toggles icon on hover - shows FaCalendarPlus when hovering', async () => { - render() + describe('long title overflow handling', () => { + it('remains accessible with very long event titles', () => { + const longTitle = + 'This Is A Very Long Event Title That Extends Beyond Normal Length With Additional Description' + render( + + ) const button = screen.getByRole('button') // Initially should show FaCalendar (not hovered) @@ -349,8 +360,16 @@ describe('CalendarButton', () => { }) }) - it('reverts to FaCalendar icon when mouse leaves', async () => { - render() + it('maintains visibility with flex-shrink-0 class', () => { + render( + + ) const button = screen.getByRole('button') // Capture initial icon (FaCalendar) @@ -373,21 +392,24 @@ describe('CalendarButton', () => { }) }) - it('maintains button functionality during hover state transitions', async () => { - render() - const button = screen.getByRole('button') - - fireEvent.mouseEnter(button) - fireEvent.mouseLeave(button) - - expect(button).not.toBeDisabled() - - fireEvent.click(button) - expect(button).toBeDisabled() - - await waitFor(() => { - expect(button).not.toBeDisabled() - }) + it('works correctly in flex container with long text sibling', () => { + const { container } = render( +
+ + +
+ ) + const button = container.querySelector('button[aria-label="Add Event to Calendar"]') + expect(button).toBeInTheDocument() + expect(button).toHaveClass('flex-shrink-0') }) }) }) diff --git a/frontend/__tests__/unit/components/CardDetailsPage.test.tsx b/frontend/__tests__/unit/components/CardDetailsPage.test.tsx index 2692f9072a..b7660b4c69 100644 --- a/frontend/__tests__/unit/components/CardDetailsPage.test.tsx +++ b/frontend/__tests__/unit/components/CardDetailsPage.test.tsx @@ -711,7 +711,7 @@ describe('CardDetailsPage', () => { author: mockUser, body: 'Milestone description', closedIssuesCount: 5, - createdAt: new Date(Date.now() - 2592000000).toISOString(), + createdAt: 1672531200, openIssuesCount: 2, repositoryName: 'test-repo', state: 'open', @@ -1909,7 +1909,7 @@ describe('CardDetailsPage', () => { author: mockUser, body: `Milestone description ${i + 1}`, closedIssuesCount: 5, - createdAt: new Date(Date.now() - 10000000).toISOString(), + createdAt: Math.floor((Date.now() - 10000000) / 1000), openIssuesCount: 2, repositoryName: `test-repo-${i}`, organizationName: 'test-org', diff --git a/frontend/__tests__/unit/components/ItemCardList.test.tsx b/frontend/__tests__/unit/components/ItemCardList.test.tsx index 46f60eaf72..6600e2cfd6 100644 --- a/frontend/__tests__/unit/components/ItemCardList.test.tsx +++ b/frontend/__tests__/unit/components/ItemCardList.test.tsx @@ -137,7 +137,7 @@ const mockMilestone: Milestone = { }, body: 'Milestone description', closedIssuesCount: 5, - createdAt: '2022-01-01T00:00:00Z', + createdAt: 1640995200, // 2022-01-01T00:00:00Z openIssuesCount: 3, organizationName: 'test-org', progress: 62.5, @@ -157,7 +157,7 @@ const mockPullRequest: PullRequest = { avatarUrl: 'https://github.com/author3.png', url: 'https://github.com/author3', }, - createdAt: '2022-01-01T00:00:00Z', + createdAt: 1640995200, // 2022-01-01T00:00:00Z organizationName: 'test-org', repositoryName: 'test-repo', title: 'Add new feature', @@ -630,10 +630,10 @@ describe('ItemCardList Component', () => { it('renders custom renderDetails content', () => { const customRenderDetails = (item: { - createdAt: string + createdAt: number commentsCount: number organizationName: string - publishedAt: string + publishedAt: number repositoryName: string tagName: string openIssuesCount: number diff --git a/frontend/__tests__/unit/components/Milestones.test.tsx b/frontend/__tests__/unit/components/Milestones.test.tsx index 3bfc7ccc60..05b1e96eb4 100644 --- a/frontend/__tests__/unit/components/Milestones.test.tsx +++ b/frontend/__tests__/unit/components/Milestones.test.tsx @@ -12,9 +12,15 @@ jest.mock('next/navigation', () => ({ })) jest.mock('utils/dateFormatter', () => { - const mockFormatDate = jest.fn((date) => { - const dateStr = date instanceof Date ? date.toISOString() : String(date) - return `Formatted: ${dateStr}` + const mockFormatDate = jest.fn((timestamp: number) => { + if (!timestamp) return '' + const date = new Date(timestamp * 1000) + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + timeZone: 'UTC', + }) }) return { @@ -125,7 +131,7 @@ describe('Milestones', () => { author: createMockUser(), body: 'Test milestone description', closedIssuesCount: 5, - createdAt: '2023-01-01T00:00:00Z', + createdAt: 1672531200, // 2023-01-01T00:00:00Z openIssuesCount: 3, organizationName: 'test-org', progress: 75, @@ -177,7 +183,7 @@ describe('Milestones', () => { it('renders milestone details correctly', () => { const milestone = createMockMilestone({ - createdAt: '2023-01-01T00:00:00Z', + createdAt: 1672531200, // 2023-01-01T00:00:00Z closedIssuesCount: 10, openIssuesCount: 5, repositoryName: 'awesome-repo', @@ -185,7 +191,7 @@ describe('Milestones', () => { render() - expect(screen.getByText(/Formatted: 2023-01-01T00:00:00Z/)).toBeInTheDocument() + expect(screen.getByText('Jan 1, 2023')).toBeInTheDocument() expect(screen.getByText('10 closed')).toBeInTheDocument() expect(screen.getByText('5 open')).toBeInTheDocument() expect(screen.getByTestId('truncated-text')).toHaveTextContent('awesome-repo') diff --git a/frontend/__tests__/unit/components/MultiSearch.test.tsx b/frontend/__tests__/unit/components/MultiSearch.test.tsx index bd4293269e..8d847998c4 100644 --- a/frontend/__tests__/unit/components/MultiSearch.test.tsx +++ b/frontend/__tests__/unit/components/MultiSearch.test.tsx @@ -332,7 +332,7 @@ describe('Rendering', () => { objectID: 'event-1', key: 'js-conf', category: 'other', - startDate: '2024-01-01', + startDate: 1704067200, // 2024-01-01 }, { id: 'event-2', @@ -341,7 +341,7 @@ describe('Rendering', () => { objectID: 'event-2', key: 'py-workshop', category: 'other', - startDate: '2024-02-01', + startDate: 1706745600, // 2024-02-01 }, { id: 'event-3', @@ -350,7 +350,7 @@ describe('Rendering', () => { objectID: 'event-3', key: 'react-meetup', category: 'other', - startDate: '2024-03-01', + startDate: 1709251200, // 2024-03-01 }, ] diff --git a/frontend/__tests__/unit/components/ProgramCard.test.tsx b/frontend/__tests__/unit/components/ProgramCard.test.tsx index 24b22d51af..70b3f90ad8 100644 --- a/frontend/__tests__/unit/components/ProgramCard.test.tsx +++ b/frontend/__tests__/unit/components/ProgramCard.test.tsx @@ -76,8 +76,8 @@ describe('ProgramCard', () => { name: 'Test Program', description: 'This is a test program description', status: ProgramStatusEnum.Published, - startedAt: '2024-01-01T00:00:00Z', - endedAt: '2024-12-31T12:00:00Z', + startedAt: 1704067200, + endedAt: 1735646400, userRole: 'admin', } @@ -338,7 +338,7 @@ describe('ProgramCard', () => { }) it('verifies formatDate explicitly uses timeZone UTC option (catches missing timeZone bug)', () => { - const testDate = '2024-01-01T00:00:00Z' + const testDate = 1704067200 const spy = jest.spyOn(Date.prototype, 'toLocaleDateString') formatDate(testDate) @@ -361,7 +361,7 @@ describe('ProgramCard', () => { }) it('shows only start date when endedAt is missing', () => { - const startOnlyProgram = { ...baseMockProgram, endedAt: '' } + const startOnlyProgram = { ...baseMockProgram, endedAt: 0 } render( { }) it('shows fallback text when both dates are missing', () => { - const noDatesProgram = { ...baseMockProgram, startedAt: '', endedAt: '' } + const noDatesProgram = { ...baseMockProgram, startedAt: 0, endedAt: 0 } render( { }) it('shows fallback text when startedAt is missing but endedAt exists', () => { - const endOnlyProgram = { ...baseMockProgram, startedAt: '' } + const endOnlyProgram = { ...baseMockProgram, startedAt: 0 } render( { name: 'Minimal Program', description: '', status: ProgramStatusEnum.Draft, - startedAt: '', - endedAt: '', + startedAt: 0, + endedAt: 0, } render( diff --git a/frontend/__tests__/unit/components/SingleModuleCard.test.tsx b/frontend/__tests__/unit/components/SingleModuleCard.test.tsx index a7b8876b40..2ae8ebc180 100644 --- a/frontend/__tests__/unit/components/SingleModuleCard.test.tsx +++ b/frontend/__tests__/unit/components/SingleModuleCard.test.tsx @@ -110,8 +110,8 @@ const mockModule: Module = { avatarUrl: 'https://example.com/avatar2.jpg', }, ], - startedAt: '2024-01-01T00:00:00Z', - endedAt: '2024-12-31T23:59:59Z', + startedAt: 1704067200, + endedAt: 1735689599, domains: ['frontend', 'backend'], tags: ['react', 'nodejs'], labels: ['good first issue', 'bug'], diff --git a/frontend/__tests__/unit/components/SnapshotCard.test.tsx b/frontend/__tests__/unit/components/SnapshotCard.test.tsx index 2d8f1792be..2a41a43fa6 100644 --- a/frontend/__tests__/unit/components/SnapshotCard.test.tsx +++ b/frontend/__tests__/unit/components/SnapshotCard.test.tsx @@ -9,8 +9,8 @@ describe('SnapshotCard', () => { key: 'test-snapshot-1', title: 'Test Snapshot', button: { label: 'Open', onclick: mockOnClick }, - startAt: '2025-01-01T00:00:00Z', - endAt: '2025-01-10T00:00:00Z', + startAt: 1735689600, + endAt: 1736467200, } beforeEach(() => { @@ -50,11 +50,11 @@ describe('SnapshotCard', () => { }) it('handles missing startAt or endAt (conditional rendering) - timezone safe', () => { - const { rerender } = render() + const { rerender } = render() const onlyEnd = formatDate(defaultProps.endAt) expect(screen.getByText(new RegExp(onlyEnd))).toBeInTheDocument() - rerender() + rerender() const onlyStart = formatDate(defaultProps.startAt) expect(screen.getByText(new RegExp(onlyStart))).toBeInTheDocument() }) diff --git a/frontend/__tests__/unit/pages/EditModule.test.tsx b/frontend/__tests__/unit/pages/EditModule.test.tsx index 71e13cd14c..24096c9fc6 100644 --- a/frontend/__tests__/unit/pages/EditModule.test.tsx +++ b/frontend/__tests__/unit/pages/EditModule.test.tsx @@ -70,8 +70,8 @@ describe('EditModulePage', () => { name: 'Existing Module', description: 'Old description', experienceLevel: ExperienceLevelEnum.Intermediate, - startedAt: '2025-07-01', - endedAt: '2025-07-31', + startedAt: 1751328000, + endedAt: 1753920000, domains: ['AI'], tags: ['graphql'], projectName: 'Awesome Project', diff --git a/frontend/__tests__/unit/pages/EditProgram.test.tsx b/frontend/__tests__/unit/pages/EditProgram.test.tsx index b989db22ab..0918e1c94c 100644 --- a/frontend/__tests__/unit/pages/EditProgram.test.tsx +++ b/frontend/__tests__/unit/pages/EditProgram.test.tsx @@ -92,8 +92,8 @@ describe('EditProgramPage', () => { name: 'Test', description: 'Test description', menteesLimit: 10, - startedAt: '2025-01-01', - endedAt: '2025-12-31', + startedAt: 1735689600, + endedAt: 1767225600, tags: ['react', 'js'], domains: ['web'], admins: [{ login: 'admin1' }], diff --git a/frontend/__tests__/unit/pages/MyMentorship.test.tsx b/frontend/__tests__/unit/pages/MyMentorship.test.tsx index baad3f5119..af15a4599b 100644 --- a/frontend/__tests__/unit/pages/MyMentorship.test.tsx +++ b/frontend/__tests__/unit/pages/MyMentorship.test.tsx @@ -85,8 +85,8 @@ const mockProgramData = { name: 'Test Program', description: 'Test Description', status: 'draft', - startedAt: '2025-07-28', - endedAt: '2025-08-10', + startedAt: 1753660800, // 2025-07-28 + endedAt: 1754784000, // 2025-08-10 experienceLevels: ['beginner'], menteesLimit: 10, admins: [], diff --git a/frontend/__tests__/unit/pages/ProjectDetails.test.tsx b/frontend/__tests__/unit/pages/ProjectDetails.test.tsx index 7875b874ed..7d0d18fe07 100644 --- a/frontend/__tests__/unit/pages/ProjectDetails.test.tsx +++ b/frontend/__tests__/unit/pages/ProjectDetails.test.tsx @@ -80,6 +80,7 @@ describe('ProjectDetailsPage', () => { await waitFor(() => { expect(screen.getByText('Test Project')).toBeInTheDocument() expect(screen.getByText('Lab')).toBeInTheDocument() + expect(screen.getByText('Feb 7, 2025')).toBeInTheDocument() }) expect(screen.getByText('2.2K Stars')).toBeInTheDocument() expect(screen.getByText('10 Forks')).toBeInTheDocument() diff --git a/frontend/__tests__/unit/pages/Snapshots.test.tsx b/frontend/__tests__/unit/pages/Snapshots.test.tsx index 8802fda472..df29c7ce78 100644 --- a/frontend/__tests__/unit/pages/Snapshots.test.tsx +++ b/frontend/__tests__/unit/pages/Snapshots.test.tsx @@ -27,14 +27,14 @@ const mockSnapshots = [ { key: '2024-12', title: 'Snapshot 1', - startAt: '2023-01-01T00:00:00Z', - endAt: '2023-01-02T00:00:00Z', + startAt: 1672531200, // 2023-01-01T00:00:00Z + endAt: 1672617600, // 2023-01-02T00:00:00Z }, { key: '2024-11', title: 'Snapshot 2', - startAt: '2022-12-01T00:00:00Z', - endAt: '2022-12-31T23:59:59Z', + startAt: 1669852800, // 2022-12-01T00:00:00Z + endAt: 1672531199, // 2022-12-31T23:59:59Z }, ] diff --git a/frontend/__tests__/unit/utils/dateFormatter.test.ts b/frontend/__tests__/unit/utils/dateFormatter.test.ts index df9470ea74..9ba3bdcc36 100644 --- a/frontend/__tests__/unit/utils/dateFormatter.test.ts +++ b/frontend/__tests__/unit/utils/dateFormatter.test.ts @@ -1,35 +1,25 @@ -import { formatDate, formatDateRange } from 'utils/dateFormatter' +import { formatDate, formatDateForInput, formatDateRange } from 'utils/dateFormatter' describe('formatDate function', () => { beforeEach(() => { jest.clearAllMocks() }) - test('formats ISO date string correctly', () => { - expect(formatDate('2023-09-01')).toBe('Sep 1, 2023') - }) - test('formats Unix timestamp correctly', () => { + // time stamp convert and checked from https://www.epochconverter.com/ // 1630454400 is Sep 1, 2021 in Unix timestamp (seconds) expect(formatDate(1630454400)).toBe('Sep 1, 2021') }) - test('throws error for invalid date', () => { - expect(() => formatDate('invalid-date')).toThrow('Invalid date') - }) - - test('handles different date formats', () => { - expect(formatDate('2023/09/01')).toBe('Sep 1, 2023') - expect(formatDate('09/01/2023')).toBe('Sep 1, 2023') - }) - test('formats dates with leading zeros correctly', () => { - expect(formatDate('2023-01-05')).toBe('Jan 5, 2023') + // 1672876800 is Jan 5, 2023 in Unix timestamp (seconds) + expect(formatDate(1672876800)).toBe('Jan 5, 2023') }) test('handles different months', () => { - expect(formatDate('2023-12-25')).toBe('Dec 25, 2023') - expect(formatDate('2023-07-04')).toBe('Jul 4, 2023') + // 1703462400 is Dec 25, 2023 and 1688428800 is Jul 4, 2023 + expect(formatDate(1703462400)).toBe('Dec 25, 2023') + expect(formatDate(1688428800)).toBe('Jul 4, 2023') }) }) @@ -39,46 +29,52 @@ describe('formatDateRange function', () => { }) test('formats date range in same month correctly', () => { - expect(formatDateRange('2023-09-01', '2023-09-04')).toBe('Sep 1 — 4, 2023') + expect(formatDateRange(1693526400, 1693785600)).toBe('Sep 1 — 4, 2023') }) test('formats date range in different months but same year correctly', () => { - expect(formatDateRange('2023-09-29', '2023-10-02')).toBe('Sep 29 — Oct 2, 2023') + expect(formatDateRange(1695945600, 1696204800)).toBe('Sep 29 — Oct 2, 2023') }) test('formats date range in different years correctly', () => { - expect(formatDateRange('2023-12-30', '2024-01-03')).toBe('Dec 30, 2023 — Jan 3, 2024') + expect(formatDateRange(1703894400, 1704240000)).toBe('Dec 30, 2023 — Jan 3, 2024') }) test('formats Unix timestamp date ranges correctly', () => { // Sept 1-4, 2021 - const startTimestamp = 1630454400 // Sep 1, 2021 - const endTimestamp = 1630713600 // Sep 4, 2021 + const startTimestamp = 1630454400 + const endTimestamp = 1630713600 expect(formatDateRange(startTimestamp, endTimestamp)).toBe('Sep 1 — 4, 2021') }) - test('throws error when start date is invalid', () => { - expect(() => formatDateRange('invalid-date', '2023-09-04')).toThrow('Invalid date') + test('handles month boundaries correctly', () => { + expect(formatDateRange(1696032000, 1696204800)).toBe('Sep 30 — Oct 2, 2023') }) - test('throws error when end date is invalid', () => { - expect(() => formatDateRange('2023-09-01', 'invalid-date')).toThrow('Invalid date') + test('handles year boundaries correctly', () => { + // 1104278400 is Dec 29, 2004 + expect(formatDateRange(1104278400, 1104624000)).toBe('Dec 29, 2004 — Jan 2, 2005') }) - test('handles month boundaries correctly', () => { - expect(formatDateRange('2023-09-30', '2023-10-02')).toBe('Sep 30 — Oct 2, 2023') + test('handles single-day ranges correctly', () => { + expect(formatDateRange(1693526400, 1693526400)).toBe('Sep 1, 2023') }) - test('handles year boundaries correctly', () => { - expect(formatDateRange('2023-12-29', '2024-01-02')).toBe('Dec 29, 2023 — Jan 2, 2024') + test('treats 0 as a valid Unix timestamp for endDate', () => { + expect(formatDateRange(0, 0)).toBe('Jan 1, 1970') }) +}) - test('handles single-day ranges correctly', () => { - expect(formatDateRange('2023-09-01', '2023-09-01')).toBe('Sep 1, 2023') +describe('formatDateForInput function', () => { + test('formats Unix timestamp correctly for input fields', () => { + expect(formatDateForInput(1630454400)).toBe('2021-09-01') + }) + + test('returns empty string for invalid input instead of throwing', () => { + expect(formatDateForInput('invalid-date')).toBe('') }) - test('handles mixed input types correctly', () => { - // Sep 1, 2021 as Unix timestamp and ISO string - expect(formatDateRange(1630454400, '2021-09-04')).toBe('Sep 1 — 4, 2021') + test('treats 0 as a valid Unix timestamp', () => { + expect(formatDateForInput(0)).toBe('1970-01-01') }) }) diff --git a/frontend/__tests__/unit/utils/getGoogleCalendarUrl.test.ts b/frontend/__tests__/unit/utils/getGoogleCalendarUrl.test.ts index 4b44102658..1e58b528f4 100644 --- a/frontend/__tests__/unit/utils/getGoogleCalendarUrl.test.ts +++ b/frontend/__tests__/unit/utils/getGoogleCalendarUrl.test.ts @@ -2,79 +2,44 @@ import getGoogleCalendarUrl from 'utils/getGoogleCalendarUrl' describe('getGoogleCalendarUrl', () => { describe('date format detection', () => { - it('detects all-day event from date-only string (YYYY-MM-DD)', () => { + it('detects all-day event from midnight timestamp', () => { const url = getGoogleCalendarUrl({ title: 'Conference', - startDate: '2025-12-01', - endDate: '2025-12-03', + startDate: 1764547200, // 2025-12-01T00:00:00Z + endDate: 1764720000, // 2025-12-03T00:00:00Z }) expect(url).toContain('dates=20251201/20251204') expect(url).not.toMatch(/dates=\d{8}T/) }) - it('detects timed event from ISO string with T', () => { + it('detects timed event from non-midnight timestamp', () => { const url = getGoogleCalendarUrl({ title: 'Meeting', - startDate: '2025-12-01T10:00:00', - endDate: '2025-12-01T11:00:00', - }) - expect(url).toContain('T') - expect(url).toContain('Z') - }) - - it('detects timed event from string with colon', () => { - const url = getGoogleCalendarUrl({ - title: 'Meeting', - startDate: '2025-12-01 10:00:00', - endDate: '2025-12-01 11:00:00', - }) - expect(url).toContain('T') - expect(url).toContain('Z') - }) - - it('treats Date object as timed event', () => { - const url = getGoogleCalendarUrl({ - title: 'Meeting', - startDate: new Date('2025-12-01T10:00:00'), - endDate: new Date('2025-12-01T11:00:00'), + startDate: 1764583200, // 2025-12-01T10:00:00Z + endDate: 1764586800, // 2025-12-01T11:00:00Z }) expect(url).toContain('T') expect(url).toContain('Z') }) }) - describe('diverse date formats', () => { - it('handles ISO 8601 format', () => { + describe('timestamp handling', () => { + it('handles Unix timestamps correctly', () => { const url = getGoogleCalendarUrl({ title: 'Event', - startDate: '2025-12-01T14:30:00.000Z', + startDate: 1764547200, // 2025-12-01T00:00:00Z }) expect(url).toContain('text=Event') expect(url).toContain('dates=') }) - it('handles date-only format', () => { + it('handles midnight timestamp as all-day', () => { const url = getGoogleCalendarUrl({ title: 'All Day Event', - startDate: '2025-12-25', - }) - expect(url).toContain('dates=20251225/20251226') - }) - - it('handles Date object', () => { - const url = getGoogleCalendarUrl({ - title: 'Event', - startDate: new Date(2025, 11, 1, 10, 0, 0), + startDate: 1766880000, // 2025-12-28T00:00:00Z }) - expect(url).toContain('text=Event') - }) - - it('handles timestamp-like ISO string', () => { - const url = getGoogleCalendarUrl({ - title: 'Event', - startDate: '2025-12-01T00:00:00Z', - }) - expect(url).toContain('dates=') + // All-day events get +1 day for end + expect(url).toContain('dates=20251228/20251229') }) }) @@ -82,7 +47,7 @@ describe('getGoogleCalendarUrl', () => { it('defaults endDate to 1 hour after startDate for timed events', () => { const url = getGoogleCalendarUrl({ title: 'Quick Meeting', - startDate: '2025-12-01T10:00:00Z', + startDate: 1764583200, // 2025-12-01T10:00:00Z }) expect(url).toContain('dates=20251201T100000Z/20251201T110000Z') }) @@ -90,8 +55,8 @@ describe('getGoogleCalendarUrl', () => { it('uses provided endDate when available', () => { const url = getGoogleCalendarUrl({ title: 'Long Meeting', - startDate: '2025-12-01T10:00:00Z', - endDate: '2025-12-01T14:00:00Z', + startDate: 1764583200, // 2025-12-01T10:00:00Z + endDate: 1764597600, // 2025-12-01T14:00:00Z }) expect(url).toContain('dates=20251201T100000Z/20251201T140000Z') }) @@ -101,7 +66,7 @@ describe('getGoogleCalendarUrl', () => { it('encodes title with special characters', () => { const url = getGoogleCalendarUrl({ title: 'Meeting & Discussion: Q4 Review', - startDate: '2025-12-01', + startDate: 1764547200, // 2025-12-01T00:00:00Z }) expect(url).toContain('text=Meeting') expect(url).toContain('%26') @@ -111,7 +76,7 @@ describe('getGoogleCalendarUrl', () => { it('encodes description', () => { const url = getGoogleCalendarUrl({ title: 'Event', - startDate: '2025-12-01', + startDate: 1764547200, // 2025-12-01T00:00:00Z description: 'Join us at https://example.com?id=123', }) expect(url).toContain('details=') @@ -121,7 +86,7 @@ describe('getGoogleCalendarUrl', () => { it('encodes location with special characters', () => { const url = getGoogleCalendarUrl({ title: 'Event', - startDate: '2025-12-01', + startDate: 1764547200, // 2025-12-01T00:00:00Z location: '123 Main St, New York, NY 10001', }) expect(url).toContain('location=') @@ -132,7 +97,7 @@ describe('getGoogleCalendarUrl', () => { it('generates correct base URL', () => { const url = getGoogleCalendarUrl({ title: 'Event', - startDate: '2025-12-01', + startDate: 1764547200, // 2025-12-01T00:00:00Z }) expect(url.startsWith('https://calendar.google.com/calendar/render?action=TEMPLATE')).toBe( true @@ -144,8 +109,8 @@ describe('getGoogleCalendarUrl', () => { title: 'OWASP Conference', description: 'Annual security conference', location: 'Belgium', - startDate: '2025-12-02', - endDate: '2025-12-03', + startDate: 1764633600, // 2025-12-02T00:00:00Z + endDate: 1764720000, // 2025-12-03T00:00:00Z }) expect(url).toContain('text=OWASP') expect(url).toContain('details=') @@ -156,7 +121,7 @@ describe('getGoogleCalendarUrl', () => { it('omits empty optional parameters', () => { const url = getGoogleCalendarUrl({ title: 'Simple Event', - startDate: '2025-12-01', + startDate: 1764547200, // 2025-12-01T00:00:00Z }) expect(url).not.toContain('details=') expect(url).not.toContain('location=') @@ -168,7 +133,7 @@ describe('getGoogleCalendarUrl', () => { expect(() => getGoogleCalendarUrl({ title: '', - startDate: '2025-12-01', + startDate: 1764547200, }) ).toThrow() }) @@ -177,29 +142,10 @@ describe('getGoogleCalendarUrl', () => { expect(() => getGoogleCalendarUrl({ title: 'Event', - startDate: '', + startDate: 0, }) ).toThrow() }) - - it('throws error for invalid startDate', () => { - expect(() => - getGoogleCalendarUrl({ - title: 'Event', - startDate: 'invalid-date', - }) - ).toThrow('Invalid startDate') - }) - - it('throws error for invalid endDate', () => { - expect(() => - getGoogleCalendarUrl({ - title: 'Event', - startDate: '2025-12-01', - endDate: 'invalid-date', - }) - ).toThrow('Invalid endDate') - }) }) describe('real-world scenarios', () => { @@ -208,8 +154,8 @@ describe('getGoogleCalendarUrl', () => { title: 'Security Conference 2025', description: 'Annual security conference', location: 'Belgium', - startDate: '2025-12-02', - endDate: '2025-12-03', + startDate: 1764633600, // 2025-12-02T00:00:00Z + endDate: 1764720000, // 2025-12-03T00:00:00Z }) expect(url).toContain('text=Security') expect(url).toContain('dates=20251202/20251204') @@ -220,8 +166,8 @@ describe('getGoogleCalendarUrl', () => { title: 'Security Workshop', description: 'Hands-on security training', location: 'Virtual - Zoom', - startDate: '2025-12-15T09:00:00', - endDate: '2025-12-15T17:00:00', + startDate: 1765789200, // 2025-12-15T09:00:00Z + endDate: 1765818000, // 2025-12-15T17:00:00Z }) expect(url).toContain('text=Security') expect(url).toContain('T') @@ -231,8 +177,8 @@ describe('getGoogleCalendarUrl', () => { const url = getGoogleCalendarUrl({ title: 'Tech Conference 2026', location: 'Austin, USA', - startDate: '2026-10-27', - endDate: '2026-10-30', + startDate: 1793059200, // 2026-10-27T00:00:00Z + endDate: 1793318400, // 2026-10-30T00:00:00Z }) expect(url).toContain('dates=20261027/20261031') }) diff --git a/frontend/__tests__/unit/utils/getIcsFileUrl.test.ts b/frontend/__tests__/unit/utils/getIcsFileUrl.test.ts index dadd248f2b..0727569042 100644 --- a/frontend/__tests__/unit/utils/getIcsFileUrl.test.ts +++ b/frontend/__tests__/unit/utils/getIcsFileUrl.test.ts @@ -9,8 +9,8 @@ describe('getIcsFileUrl', () => { description: 'Discuss Q4 goals', location: 'Conference Room A', url: 'https://meet.google.com/abc-defg-hij', - startDate: '2025-01-01', - endDate: '2025-01-03', + startDate: 1735689600, // 2025-01-01 00:00:00 UTC + endDate: 1735862400, // 2025-01-03 00:00:00 UTC } beforeAll(() => { @@ -37,16 +37,16 @@ describe('getIcsFileUrl', () => { expect(globalThis.URL.createObjectURL).toHaveBeenCalledWith(expect.any(Blob)) }) - it('should correctly format string dates into DateArray', async () => { + it('should correctly format Unix timestamps into DateArray', async () => { ;(createEvent as jest.Mock).mockImplementation((attr, cb) => cb(null, 'val')) - const eventWithStrings = { + const eventWithTimestamps = { ...mockEvent, - startDate: '2025-01-01', - endDate: '2025-01-03', + startDate: 1735689600, // 2025-01-01 00:00:00 UTC + endDate: 1735862400, // 2025-01-03 00:00:00 UTC } - await getIcsFileUrl(eventWithStrings) + await getIcsFileUrl(eventWithTimestamps) expect(createEvent).toHaveBeenCalledWith( expect.objectContaining({ @@ -57,16 +57,16 @@ describe('getIcsFileUrl', () => { ) }) - it('should correctly format Date objects into DateArray (handling 0-index months)', async () => { + it('should correctly format Unix timestamps into DateArray (UTC conversion)', async () => { ;(createEvent as jest.Mock).mockImplementation((attr, cb) => cb(null, 'val')) - const eventWithDateObjects = { + const eventWithTimestamps = { ...mockEvent, - startDate: new Date(2025, 0, 1), - endDate: new Date(2025, 0, 3), + startDate: 1735689600, // 2025-01-01 00:00:00 UTC + endDate: 1735862400, // 2025-01-03 00:00:00 UTC } - await getIcsFileUrl(eventWithDateObjects) + await getIcsFileUrl(eventWithTimestamps) expect(createEvent).toHaveBeenCalledWith( expect.objectContaining({ @@ -82,8 +82,8 @@ describe('getIcsFileUrl', () => { const singleDayEvent = { ...mockEvent, - startDate: '2025-01-01', - endDate: '2025-01-01', + startDate: 1735689600, // 2025-01-01 00:00:00 UTC + endDate: 1735689600, // 2025-01-01 00:00:00 UTC (same day) } await getIcsFileUrl(singleDayEvent) diff --git a/frontend/src/components/Card.tsx b/frontend/src/components/Card.tsx index 61f2770e85..2e10793d7c 100644 --- a/frontend/src/components/Card.tsx +++ b/frontend/src/components/Card.tsx @@ -83,7 +83,12 @@ const Card = ({ {timeline?.start && timeline?.end && (
- {formatDateRange(timeline.start, timeline.end)} + + {formatDateRange( + new Date(timeline.start).getTime() / 1000, + new Date(timeline.end).getTime() / 1000 + )} +
)} diff --git a/frontend/src/components/ItemCardList.tsx b/frontend/src/components/ItemCardList.tsx index bc852da346..36270c327e 100644 --- a/frontend/src/components/ItemCardList.tsx +++ b/frontend/src/components/ItemCardList.tsx @@ -72,10 +72,10 @@ const ItemCardList = ({ showAvatar?: boolean showSingleColumn?: boolean renderDetails: (item: { - createdAt: string + createdAt: number commentsCount: number organizationName: string - publishedAt: string + publishedAt: number repositoryName: string tagName: string openIssuesCount: number diff --git a/frontend/src/components/Milestones.tsx b/frontend/src/components/Milestones.tsx index 5f68e82c6b..1119138fb0 100644 --- a/frontend/src/components/Milestones.tsx +++ b/frontend/src/components/Milestones.tsx @@ -41,7 +41,7 @@ const Milestones: React.FC = ({
- {formatDate(item.createdAt)} + {item.createdAt ? formatDate(item.createdAt) : 'N/A'}
diff --git a/frontend/src/types/calendar.ts b/frontend/src/types/calendar.ts index 6051808ac8..0308cb15ec 100644 --- a/frontend/src/types/calendar.ts +++ b/frontend/src/types/calendar.ts @@ -4,8 +4,8 @@ export type CalendarEvent = { title: string description?: string location?: string - startDate: string | Date - endDate?: string | Date + startDate: number + endDate?: number url?: string } diff --git a/frontend/src/types/card.ts b/frontend/src/types/card.ts index 2c21e4c877..f181e45697 100644 --- a/frontend/src/types/card.ts +++ b/frontend/src/types/card.ts @@ -106,8 +106,8 @@ export interface UserCardProps { export interface SnapshotCardProps { key: string - startAt: string - endAt: string + startAt: number + endAt: number title: string button: Button } diff --git a/frontend/src/types/event.ts b/frontend/src/types/event.ts index a53874094b..41c15e829d 100644 --- a/frontend/src/types/event.ts +++ b/frontend/src/types/event.ts @@ -1,11 +1,11 @@ export type Event = { category: string - endDate?: string + endDate?: number id: string key: string name: string objectID?: string - startDate: string + startDate: number suggestedLocation?: string summary?: string url: string diff --git a/frontend/src/types/issue.ts b/frontend/src/types/issue.ts index 717f73e1de..e0d352e25f 100644 --- a/frontend/src/types/issue.ts +++ b/frontend/src/types/issue.ts @@ -4,7 +4,7 @@ import type { RepositoryDetails, User } from 'types/user' export type Issue = { author?: User body?: string - createdAt: string | number + createdAt: number hint?: string labels?: string[] number?: string | number @@ -19,6 +19,6 @@ export type Issue = { state?: string summary?: string title: string - updatedAt?: string | number + updatedAt?: number url: string } diff --git a/frontend/src/types/mentorship.ts b/frontend/src/types/mentorship.ts index 899c90bb2b..5a3be0cf6a 100644 --- a/frontend/src/types/mentorship.ts +++ b/frontend/src/types/mentorship.ts @@ -13,8 +13,8 @@ export type Program = { status?: ProgramStatusEnum experienceLevels?: ExperienceLevelEnum[] menteesLimit?: number - startedAt: string - endedAt: string + startedAt: number + endedAt: number domains?: string[] tags?: string[] userRole?: string | null @@ -39,7 +39,7 @@ export type ProgramList = { export type Module = { description: string domains?: string[] | null - endedAt: string | number + endedAt: number experienceLevel: ExperienceLevelEnum id: string key: string @@ -47,7 +47,7 @@ export type Module = { mentees?: Contributor[] mentors: Contributor[] name: string - startedAt: string | number + startedAt: number status?: ProgramStatusEnum tags?: string[] | null } diff --git a/frontend/src/types/milestone.ts b/frontend/src/types/milestone.ts index f2e9128c42..4ae8035f18 100644 --- a/frontend/src/types/milestone.ts +++ b/frontend/src/types/milestone.ts @@ -5,8 +5,7 @@ export type Milestone = { author?: User | null body?: string closedIssuesCount?: number - createdAt?: string - id?: string + createdAt?: number openIssuesCount?: number organizationName?: string | null progress?: number diff --git a/frontend/src/types/snapshot.ts b/frontend/src/types/snapshot.ts index ddace62bb6..8fa52ac4df 100644 --- a/frontend/src/types/snapshot.ts +++ b/frontend/src/types/snapshot.ts @@ -3,9 +3,9 @@ import type { Project } from 'types/project' import type { Release } from 'types/release' export type SnapshotDetails = { - endAt: string + endAt: number key: string - startAt: string + startAt: number title: string newReleases: Release[] newProjects: Project[] @@ -13,8 +13,8 @@ export type SnapshotDetails = { } export type Snapshot = { - endAt: string + endAt: number key: string - startAt: string + startAt: number title: string } diff --git a/frontend/src/utils/dateFormatter.ts b/frontend/src/utils/dateFormatter.ts index d509254138..9ee2a6dc90 100644 --- a/frontend/src/utils/dateFormatter.ts +++ b/frontend/src/utils/dateFormatter.ts @@ -1,16 +1,22 @@ -export const formatDate = (input: number | string) => { - if (!input) { +export const toUnixTimestamp = (input: string | number | Date): number => { + if (typeof input === 'number') return input + const date = typeof input === 'string' ? new Date(input) : input + + if (!date || Number.isNaN(date.getTime())) { + return Number.NaN + } + return Math.floor(date.getTime() / 1000) +} + +export const formatDate = (input: number | string | Date | null) => { + if (input === null) { return '' } - const date = - typeof input === 'number' - ? new Date(input * 1000) // Unix timestamp in seconds - : new Date(input) // ISO date string + const timestamp = toUnixTimestamp(input) + if (!Number.isFinite(timestamp)) return '' - if (Number.isNaN(date.getTime())) { - throw new Error('Invalid date') - } + const date = new Date(timestamp * 1000) return date.toLocaleDateString('en-US', { year: 'numeric', @@ -20,14 +26,20 @@ export const formatDate = (input: number | string) => { }) } -export const formatDateRange = (startDate: number | string, endDate: number | string) => { - const start = typeof startDate === 'number' ? new Date(startDate * 1000) : new Date(startDate) - const end = typeof endDate === 'number' ? new Date(endDate * 1000) : new Date(endDate) +export const formatDateRange = ( + startDate: number | string | Date, + endDate: number | string | Date +) => { + const startTimestamp = toUnixTimestamp(startDate) + const endTimestamp = toUnixTimestamp(endDate) - if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) { - throw new Error('Invalid date') + if (!Number.isFinite(startTimestamp) || !Number.isFinite(endTimestamp)) { + return '' } + const start = new Date(startTimestamp * 1000) + const end = new Date(endTimestamp * 1000) + if ( start.getTime() === end.getTime() || (start.getUTCFullYear() === end.getUTCFullYear() && @@ -62,12 +74,11 @@ export const formatDateRange = (startDate: number | string, endDate: number | st } } -export const formatDateForInput = (dateStr: string | number) => { - if (!dateStr) return '' - const date = typeof dateStr === 'number' ? new Date(dateStr * 1000) : new Date(dateStr) - if (Number.isNaN(date.getTime())) { - throw new Error('Invalid date') - } +export const formatDateForInput = (input: number | string | Date) => { + const timestamp = toUnixTimestamp(input) + if (!Number.isFinite(timestamp)) return '' + + const date = new Date(timestamp * 1000) return date.toISOString().slice(0, 10) } diff --git a/frontend/src/utils/getGoogleCalendarUrl.ts b/frontend/src/utils/getGoogleCalendarUrl.ts index 6b0eee1bc4..ae5c8a9c61 100644 --- a/frontend/src/utils/getGoogleCalendarUrl.ts +++ b/frontend/src/utils/getGoogleCalendarUrl.ts @@ -20,10 +20,8 @@ function formatUTCDateTime(date: Date) { return `${year}${month}${day}T${hours}${minutes}${seconds}Z` } -function detectIsAllDay(dateInput: string | Date): boolean { - if (dateInput instanceof Date) return false - return !dateInput.includes('T') && !dateInput.includes(':') -} +// All-day events have timestamps at midnight UTC (divisible by 86400 seconds) +const isAllDayEvent = (timestamp: number) => timestamp % 86400 === 0 export default function getGoogleCalendarUrl(event: CalendarEvent): string { if (!event?.startDate || !event.title) { @@ -32,13 +30,15 @@ export default function getGoogleCalendarUrl(event: CalendarEvent): string { const base = 'https://calendar.google.com/calendar/render?action=TEMPLATE' - const start = new Date(event.startDate) + const start = new Date(event.startDate * 1000) if (Number.isNaN(start.getTime())) throw new Error('Invalid startDate') - const end = event.endDate ? new Date(event.endDate) : new Date(start.getTime() + 60 * 60 * 1000) + const end = event.endDate + ? new Date(event.endDate * 1000) + : new Date(start.getTime() + 60 * 60 * 1000) if (Number.isNaN(end.getTime())) throw new Error('Invalid endDate') - const isAllDay = detectIsAllDay(event.startDate) + const isAllDay = isAllDayEvent(event.startDate) let datesParam: string if (isAllDay) { diff --git a/frontend/src/utils/getIcsFileUrl.ts b/frontend/src/utils/getIcsFileUrl.ts index 0b63ac420c..607583a51c 100644 --- a/frontend/src/utils/getIcsFileUrl.ts +++ b/frontend/src/utils/getIcsFileUrl.ts @@ -7,13 +7,10 @@ export default function getIcsFileUrl(event: CalendarEvent): Promise { reject(new Error('Window not defined (server-side generation not supported)')) return } - const parseDate = (date: string | Date): DateArray => { - if (typeof date === 'string') { - const [year, month, day] = date.split('-').map(Number) - if (!year || !month || !day) throw new Error(`Invalid date string: ${date}`) - return [year, month, day] - } - return [date.getFullYear(), date.getMonth() + 1, date.getDate()] + const parseDate = (date: number): DateArray => { + // Unix timestamp in seconds + const d = new Date(date * 1000) + return [d.getFullYear(), d.getMonth() + 1, d.getDate()] } const getEndDate = (start: DateArray, end: DateArray): DateArray => {