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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"start:bfb:dynamic": "nx run @v6y/bfb-url-dynamic-auditor:start",
"start:bfb:devops": "nx run @v6y/bfb-devops-auditor:start",
"start:all": "nx run-many --target=start --all --parallel --maxParallel=7",
"start:dev:all": "nx run-many --target=start:dev --all --parallel --maxParallel=7",
"stop:all": "nx run-many --target=stop --all",
"build:tsc": "nx run-many --target=build:tsc --all",
"build": "nx run-many --target=build --all",
Expand Down
21 changes: 21 additions & 0 deletions v6y-apps/front/setupTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -538,3 +538,24 @@ vi.mock('./src/commons/components/VitalitySearchBar', () => ({
</div>
),
}));

vi.mock('./src/infrastructure/adapters/api/useQueryAdapter', () => {
return {
useClientQuery: vi.fn(() => ({
isLoading: false,
data: { getApplicationTotalByParams: 0 },
refetch: vi.fn(),
})),
useInfiniteClientQuery: vi.fn(() => ({
status: 'success',
data: { pages: [] }, // Always return a valid object
fetchNextPage: vi.fn(),
isFetching: false,
isFetchingNextPage: false,
})),
};
});

vi.mock('./src/commons/utils/VitalityDataExportUtils', () => ({
exportAppListDataToCSV: vi.fn(),
}));
98 changes: 98 additions & 0 deletions v6y-apps/front/src/__tests__/app-list/VitalityAppList-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import '@testing-library/jest-dom/vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { Mock } from 'vitest';

import VitalityAppList from '../../features/app-list/components/VitalityAppList';
import VitalityAppListHeader from '../../features/app-list/components/VitalityAppListHeader';
import {
useClientQuery,
useInfiniteClientQuery,
} from '../../infrastructure/adapters/api/useQueryAdapter';

describe('VitalityAppList', () => {
it('renders applications when data is available', async () => {
(useInfiniteClientQuery as Mock).mockReturnValue({
status: 'success',
data: {
pages: [
{
totalCount: 1,
getApplicationListByPageAndParams: [{ _id: 1, name: 'Vitality App' }],
},
],
},
fetchNextPage: vi.fn(),
isFetching: false,
isFetchingNextPage: false,
});

render(<VitalityAppList />);

await waitFor(() => {
const appNames = screen.getAllByTestId('app-name');
expect(appNames.length).toBeGreaterThan(0);
expect(appNames[0]).toHaveTextContent('Vitality App');
});
});

it('handles empty application list gracefully', async () => {
(useInfiniteClientQuery as Mock).mockReturnValue({
status: 'success',
data: {
pages: [
{
totalCount: 0,
getApplicationListByPageAndParams: [],
},
],
},
fetchNextPage: vi.fn(),
isFetching: false,
isFetchingNextPage: false,
});

render(<VitalityAppList />);

await waitFor(() => {
expect(screen.getByText('vitality.appListPage.empty')).toBeInTheDocument();
});
});

it('filters applications based on search and keywords', async () => {
(useInfiniteClientQuery as Mock).mockReturnValue({
status: 'success',
data: {
totalCount: 1,
pages: [
{
totalCount: 1,
getApplicationListByPageAndParams: [{ _id: 3, name: 'Filtered App' }],
},
],
},
fetchNextPage: vi.fn(),
isFetching: false,
isFetchingNextPage: false,
});

render(<VitalityAppList />);

await waitFor(() => {
const appNames = screen.getAllByTestId('app-name');
expect(appNames.length).toBeGreaterThan(0);
expect(appNames[0]).toHaveTextContent('Filtered App');
});
});

it('shows total applications count when data is available', async () => {
(useClientQuery as Mock).mockReturnValue({
isLoading: false,
data: { getApplicationTotalByParams: 25 },
});
render(<VitalityAppListHeader onExportApplicationsClicked={vi.fn()} />);

await waitFor(() => {
expect(screen.getByText('25 results')).toBeInTheDocument();
});
});
});
94 changes: 23 additions & 71 deletions v6y-apps/front/src/__tests__/app-list/VitalityAppListView-test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '@testing-library/jest-dom/vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import VitalityAppList from '../../features/app-list/components/VitalityAppList';
Expand All @@ -10,7 +10,6 @@ import {
useInfiniteClientQuery,
} from '../../infrastructure/adapters/api/useQueryAdapter';

// Mock the navigation adapter
const mockRouter = {
replace: vi.fn(),
};
Expand Down Expand Up @@ -57,6 +56,10 @@ vi.mock('../../commons/utils/VitalityDataExportUtils', () => ({
exportAppListDataToCSV: vi.fn(),
}));

vi.mock('../../features/app-list/components/VitalityAppList', () => ({
default: () => <div data-testid="mocked-app-list">Mocked App List</div>,
}));

describe('VitalityAppListView', () => {
beforeEach(() => {
mockGetUrlParams.mockReturnValue([undefined]);
Expand All @@ -71,11 +74,15 @@ describe('VitalityAppListView', () => {

it('renders technology filters and app list', async () => {
render(<VitalityAppListView />);

// Check for technology filter section
expect(screen.getByText('vitality.dashboardPage.filters.technologies')).toBeInTheDocument();
expect(screen.getByText('Select technologies to filter applications and discover matching projects')).toBeInTheDocument();

expect(
screen.getByText(
'Select technologies to filter applications and discover matching projects',
),
).toBeInTheDocument();

// Check for technology checkboxes
expect(screen.getByLabelText('React')).toBeInTheDocument();
expect(screen.getByLabelText('TypeScript')).toBeInTheDocument();
Expand All @@ -86,9 +93,9 @@ describe('VitalityAppListView', () => {
it('shows active filters when keywords are selected', async () => {
// Mock URL params with selected keywords
mockGetUrlParams.mockReturnValue(['react,typescript']);

render(<VitalityAppListView />);

await waitFor(() => {
expect(screen.getByText('vitality.appListPage.activeFiltersLabel')).toBeInTheDocument();
expect(screen.getByText('2 selected')).toBeInTheDocument();
Expand All @@ -99,81 +106,26 @@ describe('VitalityAppListView', () => {

it('handles keyword toggle correctly', async () => {
render(<VitalityAppListView />);

const reactCheckbox = screen.getByLabelText('React');
fireEvent.click(reactCheckbox);

expect(mockCreateUrlQueryParam).toHaveBeenCalledWith('keywords', 'react');
expect(mockRouter.replace).toHaveBeenCalledWith('/apps?keywords=react', { scroll: false });
});

it('removes keywords from URL when unchecked', async () => {
// Start with selected keywords
mockGetUrlParams.mockReturnValue(['react,typescript']);

render(<VitalityAppListView />);

await waitFor(() => {
const reactBadge = screen.getByText('react ×');
fireEvent.click(reactBadge);

expect(mockCreateUrlQueryParam).toHaveBeenCalledWith('keywords', 'typescript');
expect(mockRouter.replace).toHaveBeenCalled();
});
});

it('renders applications when data is available', async () => {
(useInfiniteClientQuery as Mock).mockReturnValue({
status: 'success',
data: {
pages: [{ getApplicationListByPageAndParams: [{ _id: 1, name: 'Vitality App' }] }],
},
});

render(<VitalityAppList />);

await waitFor(() => {
expect(screen.getByText('Vitality App')).toBeInTheDocument();
});
});

it('handles empty application list gracefully', async () => {
(useInfiniteClientQuery as Mock).mockReturnValue({
status: 'success',
data: { pages: [] },
});

render(<VitalityAppList />);

await waitFor(() => {
expect(screen.getByText('vitality.appListPage.empty')).toBeInTheDocument();
});
});

it('filters applications based on search and keywords', async () => {
(useInfiniteClientQuery as Mock).mockReturnValue({
status: 'success',
data: {
pages: [{ getApplicationListByPageAndParams: [{ _id: 3, name: 'Filtered App' }] }],
},
});

render(<VitalityAppList />);

await waitFor(() => {
expect(screen.getByText('Filtered App')).toBeInTheDocument();
});
});
render(<VitalityAppListView />);

it('shows total applications count when data is available', async () => {
(useClientQuery as Mock).mockReturnValue({
isLoading: false,
data: { getApplicationTotalByParams: 25 },
});
render(<VitalityAppListHeader onExportApplicationsClicked={vi.fn()} />);
// Wait for the badge to appear
const reactBadge = await screen.findByText('react ×');
fireEvent.click(reactBadge);

await waitFor(() => {
expect(screen.getByText('25 results')).toBeInTheDocument();
});
// Then assert the calls were made
expect(mockCreateUrlQueryParam).toHaveBeenCalledWith('keywords', 'typescript');
expect(mockRouter.replace).toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions v6y-libs/ui-kit-front/src/components/atoms/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export {
PlayIcon,
EnvelopeClosedIcon,
ChatBubbleIcon,
MixerHorizontalIcon
} from '@radix-ui/react-icons';