datajunction-ui
Version:
DataJunction UI
301 lines (249 loc) • 7.84 kB
JSX
import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { CollectionsSection } from '../CollectionsSection';
import DJClientContext from '../../../providers/djclient';
vi.mock('../MyWorkspacePage.css', () => ({}));
describe('<CollectionsSection />', () => {
const mockDjClient = {
listAllCollections: vi.fn(),
};
const mockCollections = [
{
name: 'my_collection',
description: 'My test collection',
nodeCount: 5,
createdBy: { username: 'test.user@example.com' },
},
{
name: 'other_collection',
description: 'Another collection',
nodeCount: 10,
createdBy: { username: 'other.user@example.com' },
},
];
const mockCurrentUser = {
username: 'test.user@example.com',
};
beforeEach(() => {
vi.clearAllMocks();
});
const renderWithContext = props => {
return render(
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
<MemoryRouter>
<CollectionsSection {...props} />
</MemoryRouter>
</DJClientContext.Provider>,
);
};
it('should render loading state', () => {
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: [] },
});
renderWithContext({
collections: [],
loading: true,
currentUser: mockCurrentUser,
});
expect(screen.getByText('Collections')).toBeInTheDocument();
});
it('should fetch and display all collections', async () => {
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: mockCollections },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
expect(mockDjClient.listAllCollections).toHaveBeenCalled();
});
await waitFor(() => {
expect(screen.getByText('my_collection')).toBeInTheDocument();
expect(screen.getByText('other_collection')).toBeInTheDocument();
});
});
it('should show owner as "you" for current user collections', async () => {
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: mockCollections },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
expect(screen.getByText('by you')).toBeInTheDocument();
});
});
it('should show username for other user collections', async () => {
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: mockCollections },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
expect(screen.getByText('by other.user')).toBeInTheDocument();
});
});
it('should display node counts', async () => {
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: mockCollections },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
expect(screen.getByText('5 nodes')).toBeInTheDocument();
expect(screen.getByText('10 nodes')).toBeInTheDocument();
});
});
it('should render empty state when no collections', async () => {
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: [] },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
expect(screen.getByText('No collections yet')).toBeInTheDocument();
});
});
it('should display user and other collections', async () => {
const collections = [
{
name: 'other_first',
description: 'Other collection',
createdBy: { username: 'other@example.com' },
nodeCount: 1,
},
{
name: 'my_first',
description: 'My collection',
createdBy: { username: 'test.user@example.com' },
nodeCount: 2,
},
];
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: collections },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
// Both collections should be displayed
expect(screen.getByText('my_first')).toBeInTheDocument();
expect(screen.getByText('other_first')).toBeInTheDocument();
});
});
it('should limit display to 8 collections', async () => {
const manyCollections = Array.from({ length: 12 }, (_, i) => ({
name: `collection_${i}`,
nodeCount: i,
createdBy: { username: 'test@example.com' },
}));
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: manyCollections },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
const links = screen.getAllByRole('link');
// Should have at most 9 links (8 collections + 1 "Create Collection")
expect(links.length).toBeLessThanOrEqual(9);
});
});
it('should log response when fetching all collections succeeds', async () => {
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: mockCollections },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
expect(consoleSpy).toHaveBeenCalledWith(
'All collections response:',
expect.anything(),
);
});
consoleSpy.mockRestore();
});
it('should handle API errors gracefully', async () => {
mockDjClient.listAllCollections.mockRejectedValue(new Error('API error'));
const fallbackCollections = [
{
name: 'fallback_collection',
nodeCount: 1,
createdBy: { username: 'test@example.com' },
},
];
renderWithContext({
collections: fallbackCollections,
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
// Should fall back to props collections
expect(screen.getByText('fallback_collection')).toBeInTheDocument();
});
});
it('should handle collections without createdBy', async () => {
const collectionsWithoutOwner = [
{
name: 'no_owner',
nodeCount: 1,
createdBy: null,
},
];
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: collectionsWithoutOwner },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
expect(screen.getByText('by unknown')).toBeInTheDocument();
});
});
it('should handle hover interactions on collection cards', async () => {
mockDjClient.listAllCollections.mockResolvedValue({
data: { listCollections: mockCollections },
});
renderWithContext({
collections: [],
loading: false,
currentUser: mockCurrentUser,
});
await waitFor(() => {
expect(screen.getByText('my_collection')).toBeInTheDocument();
});
const collectionLink = screen.getByText('my_collection').closest('a');
// Test mouse enter - should change styles
if (collectionLink) {
fireEvent.mouseEnter(collectionLink);
expect(collectionLink).toBeInTheDocument();
// Test mouse leave - should reset styles
fireEvent.mouseLeave(collectionLink);
expect(collectionLink).toBeInTheDocument();
}
});
});