import React from 'react';
import { render, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import {
AlgoliaSearchProvider,
useAlgoliaSearch
} from '../AlgoliaSearchProvider';
jest.mock('algoliasearch', () => {
return jest.fn(() => ({
initIndex: jest.fn()
}));
});
jest.mock('../algoliaRelatedArticles', () => ({
searchRelatedArticles: jest.fn()
}));
import algoliasearch from 'algoliasearch';
import { searchRelatedArticles } from '../algoliaRelatedArticles';
const mockAlgoliasearch = algoliasearch as jest.MockedFunction<
typeof algoliasearch
>;
const mockSearchRelatedArticles = searchRelatedArticles as jest.MockedFunction<
typeof searchRelatedArticles
>;
const TestComponent: React.FC = () => {
const { getRelatedArticles } = useAlgoliaSearch();
React.useEffect(
() => {
if (getRelatedArticles) {
getRelatedArticles();
}
},
[getRelatedArticles]
);
return
Test Component
;
};
const mockAnalyticsStream = jest.fn();
const mockAlgoliaSearchKeys = {
applicationId: 'test-app-id',
apiKey: 'test-api-key',
indexName: 'test-index'
};
const mockArticle = {
id: 'test-article-123',
label: 'Test Article Label',
headline: 'Test Article Headline',
section: 'news',
topics: [{ name: 'Technology' }],
bylines: []
};
describe('AlgoliaSearchProvider', () => {
let mockIndex: any;
beforeEach(() => {
jest.clearAllMocks();
mockIndex = {
search: jest.fn()
};
mockAlgoliasearch.mockReturnValue({
initIndex: jest.fn().mockReturnValue(mockIndex)
} as any);
});
describe('Provider functionality', () => {
it('should render children correctly', () => {
const { getByTestId } = render(
Child Content
);
expect(getByTestId('child-component')).toBeInTheDocument();
});
it('should create algolia index with correct parameters', () => {
render(
Content
);
expect(mockAlgoliasearch).toHaveBeenCalledWith(
'test-app-id',
'test-api-key'
);
expect(
mockAlgoliasearch('test-app-id', 'test-api-key').initIndex
).toHaveBeenCalledWith('test-index');
});
it('should handle null algoliaSearchKeys', () => {
render(
Content
);
expect(mockAlgoliasearch).not.toHaveBeenCalled();
});
it('should recreate index when algoliaSearchKeys change', () => {
const { rerender } = render(
Content
);
expect(mockAlgoliasearch).toHaveBeenCalledTimes(1);
const newKeys = {
...mockAlgoliaSearchKeys,
applicationId: 'new-app-id'
};
rerender(
Content
);
expect(mockAlgoliasearch).toHaveBeenCalledTimes(2);
expect(mockAlgoliasearch).toHaveBeenLastCalledWith(
'new-app-id',
'test-api-key'
);
});
});
describe('getRelatedArticles functionality', () => {
it('should call searchRelatedArticles with correct parameters when index and article exist', async () => {
const mockResults = {
query: 'test query',
sliceName: 'test slice',
items: [],
count: 5
};
mockSearchRelatedArticles.mockResolvedValue(mockResults);
render(
);
await waitFor(() => {
expect(mockSearchRelatedArticles).toHaveBeenCalledWith(
mockIndex,
mockArticle,
mockAnalyticsStream
);
});
});
it('should return null when no index is available', async () => {
let resultFromHook: any;
const TestComponentWithResult: React.FC = () => {
const { getRelatedArticles } = useAlgoliaSearch();
React.useEffect(
() => {
const fetchResults = async () => {
if (getRelatedArticles) {
resultFromHook = await getRelatedArticles();
}
};
fetchResults();
},
[getRelatedArticles]
);
return Test
;
};
render(
);
await waitFor(() => {
expect(resultFromHook).toBeNull();
});
expect(mockSearchRelatedArticles).not.toHaveBeenCalled();
});
it('should return null when no article is provided', async () => {
let resultFromHook: any;
const TestComponentWithResult: React.FC = () => {
const { getRelatedArticles } = useAlgoliaSearch();
React.useEffect(
() => {
const fetchResults = async () => {
if (getRelatedArticles) {
resultFromHook = await getRelatedArticles();
}
};
fetchResults();
},
[getRelatedArticles]
);
return Test
;
};
render(
);
await waitFor(() => {
expect(resultFromHook).toBeNull();
});
expect(mockSearchRelatedArticles).not.toHaveBeenCalled();
});
it('should return results from searchRelatedArticles', async () => {
const mockResults = {
query: 'test query',
sliceName: 'test slice',
items: [{ id: 1 }, { id: 2 }, { id: 3 }],
count: 3
};
mockSearchRelatedArticles.mockResolvedValue(mockResults);
let resultFromHook: any;
const TestComponentWithResult: React.FC = () => {
const { getRelatedArticles } = useAlgoliaSearch();
React.useEffect(
() => {
const fetchResults = async () => {
if (getRelatedArticles) {
resultFromHook = await getRelatedArticles();
}
};
fetchResults();
},
[getRelatedArticles]
);
return Test
;
};
render(
);
await waitFor(() => {
expect(resultFromHook).toEqual(mockResults);
});
});
it('should memoize getRelatedArticles callback based on dependencies', () => {
let firstCallback: any;
let secondCallback: any;
const CallbackCapture: React.FC = () => {
const { getRelatedArticles } = useAlgoliaSearch();
if (!firstCallback) {
firstCallback = getRelatedArticles;
} else if (!secondCallback) {
secondCallback = getRelatedArticles;
}
return Test
;
};
const { rerender } = render(
);
rerender(
);
expect(firstCallback).toBe(secondCallback);
});
});
describe('useAlgoliaSearch hook', () => {
it('should provide context value when used within provider', () => {
let contextValue: any;
const TestComponentInsideProvider: React.FC = () => {
contextValue = useAlgoliaSearch();
return Test
;
};
render(
);
expect(contextValue).toHaveProperty('getRelatedArticles');
expect(typeof contextValue.getRelatedArticles).toBe('function');
});
});
});