Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps-portfolio/src/app/articles/[slug]/page.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// src/app/articles/[slug]/page.test.tsx
import { getArticle } from '../../lib/data';

describe('ArticlePage', () => {
it('should return an article for a valid slug', async () => {
const article = await getArticle('2020-12-31-the-art-of-slos');
expect(article).toBeDefined();
expect(article?.title).toBe('The Art of SLOs');
});

it('should return undefined for an invalid slug', async () => {
const article = await getArticle('invalid-slug');
expect(article).toBeUndefined();
});
});
21 changes: 10 additions & 11 deletions apps-portfolio/src/app/lib/data.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// src/app/lib/data.ts
import { db } from '../../lib/db';

import { slugify } from '../../lib/utils';
import { slugify, formatDate } from '../../lib/utils';

// Server-side function
export async function getTalks() {
Expand All @@ -13,7 +13,7 @@ export async function getTalks() {
});
return talks.map((talk) => ({
...talk,
slug: `${talk.date}-${slugify(talk.title)}`
slug: `${formatDate(talk.date)}-${slugify(talk.title)}`
}));
} catch (error) {
console.error('Error fetching talks:', error);
Expand All @@ -31,7 +31,7 @@ export async function getArticles() {
});
return articles.map((article) => ({
...article,
slug: `${article.publish_date}-${slugify(article.title)}`,
slug: `${formatDate(new Date(article.publish_date))}-${slugify(article.title)}`,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 For consistency and robustness, it's better to use the existing parseDateString utility to parse the publish_date string. The new Date(dateString) constructor can have inconsistent behavior across different JavaScript environments, whereas parseDateString is guaranteed to handle the YYYY-MM-DD format correctly. You'll also need to import parseDateString from ../../lib/utils.

Suggested change
slug: `${formatDate(new Date(article.publish_date))}-${slugify(article.title)}`,
slug: `${formatDate(parseDateString(article.publish_date))}-${slugify(article.title)}`,

type: 'article'
}));
} catch (error) {
Expand Down Expand Up @@ -62,13 +62,13 @@ export async function getFutureTalks() {
},
where: {
date: {
gte: new Date().toISOString().split('T')[0],
gte: new Date()
}
}
});
return talks.map((talk) => ({
...talk,
slug: `${talk.date}-${slugify(talk.title)}`
slug: `${formatDate(talk.date)}-${slugify(talk.title)}`
}));
}

Expand All @@ -79,13 +79,13 @@ export async function getPastTalks() {
},
where: {
date: {
lt: new Date().toISOString().split('T')[0],
lt: new Date()
}
}
});
return talks.map((talk) => ({
...talk,
slug: `${talk.date}-${slugify(talk.title)}`
slug: `${formatDate(talk.date)}-${slugify(talk.title)}`
}));
}

Expand All @@ -104,7 +104,7 @@ export async function getHighlightedTalks() {
});
return talks.map((talk) => ({
...talk,
slug: `${talk.date}-${slugify(talk.title)}`
slug: `${formatDate(talk.date)}-${slugify(talk.title)}`
}));
}

Expand All @@ -122,7 +122,6 @@ export async function getHighlightedArticles() {
});
return articles.map((article) => ({
...article,
slug: `${article.publish_date}-${slugify(article.title)}`
slug: `${formatDate(new Date(article.publish_date))}-${slugify(article.title)}`
}));
}

}
79 changes: 12 additions & 67 deletions apps-portfolio/src/app/talks/[slug]/page.test.tsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,15 @@
import { render, screen } from '@testing-library/react';
import TalkPage from './page';
import { getTalk } from '../../lib/data'; // Import getTalk directly
import { notFound } from 'next/navigation';
import { vi } from 'vitest';

// Mock the next/navigation notFound function
vi.mock('next/navigation', () => ({
notFound: vi.fn(() => { throw new Error('notFound called'); }),
}));

// Mock the getTalk function from src/app/lib/data.ts
vi.mock('../../lib/data', () => ({
getTalk: vi.fn(),
// Ensure other exports from data.ts are also mocked if used by getTalk
getTalks: vi.fn(),
getArticles: vi.fn(),
getFutureTalks: vi.fn(),
getHighlightedTalks: vi.fn(),
getHighlightedArticles: vi.fn(),
}));

describe.skip('TalkPage', () => {
const mockTalk = {
id: 1,
title: 'Test Talk Title',
event: 'Test Event',
date: '2025-01-01',
location: 'Test Location',
country_code: 'us',
session_url: 'http://example.com/session',
video_url: 'http://example.com/video',
slides_url: 'http://example.com/slides',
status: 'delivered',
tags: 'tag1,tag2',
image: '/images/placeholder-image.png',
event_description: 'Event description.',
talk_description: 'Talk description.',
slug: '2025-01-01-test-talk-title',
};

beforeEach(() => {
// Reset mocks before each test
(getTalk as vi.Mock).mockReset(); // Use getTalk directly
(notFound as vi.Mock).mockReset();
});

it('renders talk details when talk is found', async () => {
// Mock getTalk to return the mockTalk
(getTalk as vi.Mock).mockResolvedValue(mockTalk);

await render(<TalkPage params={{ slug: mockTalk.slug }} />);

// Assert that the talk title is displayed
expect(screen.getByText(mockTalk.title)).toBeInTheDocument();
expect(screen.getByText(`${mockTalk.event} - ${mockTalk.date}`)).toBeInTheDocument();
expect(screen.queryByText('404: This page could not be found.')).not.toBeInTheDocument();
expect(notFound).not.toHaveBeenCalled();
// src/app/talks/[slug]/page.test.tsx
import { getTalk } from '../../lib/data';

describe('TalkPage', () => {
it('should return a talk for a valid slug', async () => {
const talk = await getTalk('2025-11-08-agenti-in-volo-unanalisi-approfondita-sulla-creazione-di-un-agente-di-viaggio-in-tempo-reale');
expect(talk).toBeDefined();
expect(talk?.title).toBe("Agenti in Volo: Un'Analisi Approfondita sulla Creazione di un Agente di Viaggio in Tempo Reale ✈️");
});

it('calls notFound when talk is not found', async () => {
// Mock getTalk to return undefined (talk not found)
(getTalk as vi.Mock).mockResolvedValue(undefined); // Simulate talk not found

await render(<TalkPage params={{ slug: 'non-existent-slug' }} />);

// Assert that notFound was called
expect(notFound).toHaveBeenCalled();
it('should return undefined for an invalid slug', async () => {
const talk = await getTalk('invalid-slug');
expect(talk).toBeUndefined();
});
});
});
10 changes: 7 additions & 3 deletions apps-portfolio/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
// src/lib/utils.ts

export const formatDate = (date: Date): string => {
if (!date) return '';
return new Date(date).toISOString().split('T')[0];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Using toISOString can lead to off-by-one day errors due to timezone conversions, as it always returns the date in UTC. A safer approach is to construct the date string from the local date components (getFullYear, getMonth, getDate). This ensures that the formatted date matches the local date, regardless of the timezone.

Suggested change
return new Date(date).toISOString().split('T')[0];
const d = new Date(date);
const year = d.getFullYear();
const month = `${d.getMonth() + 1}`.padStart(2, '0');
const day = `${d.getDate()}`.padStart(2, '0');
return `${year}-${month}-${day}`;

};

export const slugify = (str: string) => {
if (!str) return '';
const a = 'àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúúūǘůűųẃẍÿýžźż·/_,:;'
Expand Down Expand Up @@ -30,6 +36,4 @@ export function extractYouTubeVideoId(url: string) {
export function parseDateString(dateString: string): Date {
const [year, month, day] = dateString.split('-').map(Number);
return new Date(year, month - 1, day);
}


}