chore: add mock API route handlers and dev env config

This commit is contained in:
Ilia Mashkov
2026-05-05 09:41:39 +03:00
parent 41edc7edf7
commit 4b18fc454e
10 changed files with 659 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
export { default as ExperienceSection } from './ui/ExperienceSection/ExperienceSection';
@@ -0,0 +1,87 @@
vi.mock('$shared/api', () => ({
getCollection: vi.fn(),
}));
import { render, screen } from '@testing-library/react';
import { getCollection } from '$shared/api';
import ExperienceSection from './ExperienceSection';
const mockItems = [
{
id: '1',
collectionId: 'c1',
collectionName: 'experience',
created: '',
updated: '',
company: 'Acme Corp',
role: 'Senior Developer',
start_date: '2022-01-01T00:00:00Z',
end_date: null,
description: 'Built critical systems.',
order: 1,
},
{
id: '2',
collectionId: 'c1',
collectionName: 'experience',
created: '',
updated: '',
company: 'Beta Ltd',
role: 'Junior Developer',
start_date: '2020-01-01T00:00:00Z',
end_date: '2021-12-31T00:00:00Z',
description: 'Learned the ropes.',
order: 2,
},
];
const listResponse = (items: typeof mockItems) => ({
items,
page: 1,
perPage: 50,
totalItems: items.length,
totalPages: 1,
});
describe('ExperienceSection', () => {
beforeEach(() => {
vi.mocked(getCollection).mockResolvedValue(listResponse(mockItems) as never);
});
describe('rendering', () => {
it('renders a card for each experience record', async () => {
render(await ExperienceSection());
expect(screen.getByText('Senior Developer')).toBeInTheDocument();
expect(screen.getByText('Junior Developer')).toBeInTheDocument();
});
it('renders company names', async () => {
render(await ExperienceSection());
expect(screen.getByText('Acme Corp')).toBeInTheDocument();
expect(screen.getByText('Beta Ltd')).toBeInTheDocument();
});
it('formats open-ended period as "Present"', async () => {
render(await ExperienceSection());
expect(screen.getByText('2022 — Present')).toBeInTheDocument();
});
it('formats closed period with year range', async () => {
render(await ExperienceSection());
expect(screen.getByText('2020 — 2021')).toBeInTheDocument();
});
it('renders description text', async () => {
render(await ExperienceSection());
expect(screen.getByText('Built critical systems.')).toBeInTheDocument();
});
});
describe('empty state', () => {
it('renders empty container when no items', async () => {
vi.mocked(getCollection).mockResolvedValue(listResponse([]) as never);
const { container } = render(await ExperienceSection());
expect(container.firstChild).toBeEmptyDOMElement();
});
});
});
@@ -0,0 +1,28 @@
import { ExperienceCard } from '$entities/experience';
import type { ExperienceRecord } from '$shared/api';
import { getCollection } from '$shared/api';
import { formatYearRange } from '$shared/lib';
/**
* Experience section component.
* Lists work history entries sorted by order field.
*/
export default async function ExperienceSection() {
const { items } = await getCollection<ExperienceRecord>('experience', {
sort: 'order',
});
return (
<div className="space-y-6">
{items.map((exp) => (
<ExperienceCard
key={exp.id}
title={exp.role}
company={exp.company}
period={formatYearRange(exp.start_date, exp.end_date)}
description={exp.description}
/>
))}
</div>
);
}