Skip to content

Commit

Permalink
adding some UTs
Browse files Browse the repository at this point in the history
  • Loading branch information
ganesshkumar committed Dec 30, 2024
1 parent a586ef9 commit d1b23de
Show file tree
Hide file tree
Showing 52 changed files with 3,470 additions and 236 deletions.
36 changes: 36 additions & 0 deletions components/AuthorAndDescription.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import AuthorAndDescription from './AuthorAndDescription';

describe('AuthorAndDescription', () => {
test('renders author and description', () => {
const author = 'John Doe';
const description = 'This is a test description.';
render(<AuthorAndDescription author={author} description={description} />);

const authorElement = screen.getByText(`${author}`);
const descriptionElement = screen.getByText(description);

expect(authorElement).toBeInTheDocument();
expect(descriptionElement).toBeInTheDocument();
});

test('renders author with correct class', () => {
const author = 'Jane Doe';
render(<AuthorAndDescription author={author} description="" />);

const authorElement = screen.getByText(`${author}`);
expect(authorElement).toBeInTheDocument();
expect(authorElement).toHaveClass('group-hover:text-violet-500');
});

test('renders description with correct class', () => {
const description = 'Another test description.';
render(<AuthorAndDescription author="" description={description} />);

const descriptionElement = screen.getByText(description);
expect(descriptionElement).toBeInTheDocument();
expect(descriptionElement).toHaveClass('mr-5');
});
});
45 changes: 45 additions & 0 deletions components/CardAnnotations.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import CardAnnotations from './CardAnnotations';

describe('CardAnnotations', () => {
test('renders favorite annotation', () => {
render(<CardAnnotations isFavorite={true} isNotADayOld={false} isTrending={false} />);
const favoriteAnnotation = screen.getByText('Favorite');
expect(favoriteAnnotation).toBeInTheDocument();
expect(favoriteAnnotation).toHaveAttribute('title', 'Favorite plugin');
});

test('renders new annotation', () => {
render(<CardAnnotations isFavorite={false} isNotADayOld={true} isTrending={false} category="Plugin" />);
const newAnnotation = screen.getByText('New Plugin');
expect(newAnnotation).toBeInTheDocument();
expect(newAnnotation).toHaveAttribute('title', 'Less than a day old');
});

test('renders trending annotation', () => {
render(<CardAnnotations isFavorite={false} isNotADayOld={false} isTrending={true} />);
const trendingAnnotation = screen.getByText('Trending');
expect(trendingAnnotation).toBeInTheDocument();
expect(trendingAnnotation).toHaveAttribute('title', 'Trending plugin');
});

test('renders multiple annotations', () => {
render(<CardAnnotations isFavorite={true} isNotADayOld={true} isTrending={true} category="Plugin" />);
const favoriteAnnotation = screen.getByText('Favorite');
const newAnnotation = screen.getByText('New Plugin');
const trendingAnnotation = screen.getByText('Trending');

expect(favoriteAnnotation).toBeInTheDocument();
expect(newAnnotation).toBeInTheDocument();
expect(trendingAnnotation).toBeInTheDocument();
});

test('renders no annotations when all props are false', () => {
render(<CardAnnotations isFavorite={false} isNotADayOld={false} isTrending={false} />);
expect(screen.queryByText('Favorite')).not.toBeInTheDocument();
expect(screen.queryByText('New')).not.toBeInTheDocument();
expect(screen.queryByText('Trending')).not.toBeInTheDocument();
});
});
5 changes: 0 additions & 5 deletions components/CardAnnotations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ const CardAnnotations = ({
category = '',
}) => {
return (
// <div className='absolute -top-5 -left-5 text-3xl'>
// {isFavorite && <div title='Favorite plugin'>🤩</div>}
// {isNotADayOld && <div title='Less than a day old'>🥳</div>}
// {isTrending && <div title='Trending plugin'>🔥</div>}
// </div>
<div className="flex text-xs gap-x-2 mt-4">
{isFavorite && (
<div
Expand Down
34 changes: 34 additions & 0 deletions components/Category.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { CategoryIcon } from './Category';

describe('CategoryIcon', () => {
const categories = [
{ name: 'Task Management', testId: 'task-management-icon' },
{ name: 'File Management', testId: 'file-management-icon' },
{ name: 'Note Enhancements', testId: 'note-enhancements-icon' },
{ name: 'Data Visualization', testId: 'data-visualization-icon' },
{ name: '3rd Party Integrations', testId: 'third-party-integrations-icon' },
{ name: 'Productivity Tools', testId: 'productivity-tools-icon' },
{ name: 'Coding & Technical Tools', testId: 'coding-technical-tools-icon' },
{ name: 'Creative & Writing Tools', testId: 'creative-writing-tools-icon' },
{ name: 'Privacy & Security', testId: 'privacy-security-icon' },
{ name: 'Customization & UI', testId: 'customization-ui-icon' },
{ name: 'Collaboration & Sharing', testId: 'collaboration-sharing-icon' },
{ name: 'Learning & Knowledge Management', testId: 'learning-knowledge-management-icon' },
{ name: 'Miscellaneous', testId: 'miscellaneous-icon' },
{ name: 'Uncategorized', testId: 'uncategorized-icon' },
{ name: 'Unknown', testId: 'default-icon' },
];

categories.forEach(({ name, testId }) => {
test(`renders correct icon for ${name}`, () => {
render(<CategoryIcon category={name} size={48} />);
const icon = screen.queryByTestId(testId)
expect(icon).toBeInTheDocument();
expect(icon).toHaveAttribute('width', '48');
expect(icon).toHaveAttribute('height', '48');
});
});
});
47 changes: 15 additions & 32 deletions components/Category.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,6 @@ import {
Box,
} from 'react-feather';

const Categories = [
'Task Management',
'File Management',
'Note Enhancements',
'Data Visualization',
'3rd Party Integrations',
'Productivity Tools',
'Coding & Technical Tools',
'Creative & Writing Tools',
'Privacy & Security',
'Customization & UI',
'Collaboration & Sharing',
'Learning & Knowledge Management',
'Miscellaneous',
'Uncategorized',
];

export const CategoryIcon = ({
category,
size = 48,
Expand All @@ -57,34 +40,34 @@ const CategoryIconInternal = ({
}) => {
switch (category) {
case 'Task Management':
return <CheckCircle className="text-green-400" size={size} />;
return <CheckCircle className="text-green-400" size={size} data-testid="task-management-icon" />;
case 'File Management':
return <Folder className="text-blue-500" size={size} />;
return <Folder className="text-blue-500" size={size} data-testid="file-management-icon" />;
case 'Note Enhancements':
return <Edit className="text-yellow-400" size={size} />;
return <Edit className="text-yellow-400" size={size} data-testid="note-enhancements-icon" />;
case 'Data Visualization':
return <PieChart className="text-orange-500" size={size} />;
return <PieChart className="text-orange-500" size={size} data-testid="data-visualization-icon" />;
case '3rd Party Integrations':
return <RefreshCw className="text-cyan-400" size={size} />;
return <RefreshCw className="text-cyan-400" size={size} data-testid="third-party-integrations-icon" />;
case 'Productivity Tools':
return <Clock className="text-lime-400" size={size} />;
return <Clock className="text-lime-400" size={size} data-testid="productivity-tools-icon" />;
case 'Coding & Technical Tools':
return <Code className="text-teal-500" size={size} />;
return <Code className="text-teal-500" size={size} data-testid="coding-technical-tools-icon" />;
case 'Creative & Writing Tools':
return <PenTool className="text-pink-400" size={size} />;
return <PenTool className="text-pink-400" size={size} data-testid="creative-writing-tools-icon" />;
case 'Privacy & Security':
return <Shield className="text-gray-500" size={size} />;
return <Shield className="text-gray-500" size={size} data-testid="privacy-security-icon" />;
case 'Customization & UI':
return <Sliders className="text-indigo-400" size={size} />;
return <Sliders className="text-indigo-400" size={size} data-testid="customization-ui-icon" />;
case 'Collaboration & Sharing':
return <Users className="text-amber-400" size={size} />;
return <Users className="text-amber-400" size={size} data-testid="collaboration-sharing-icon" />;
case 'Learning & Knowledge Management':
return <Book className="text-emerald-400" size={size} />;
return <Book className="text-emerald-400" size={size} data-testid="learning-knowledge-management-icon" />;
case 'Miscellaneous':
return <Box className="text-rose-400" size={size} />;
return <Box className="text-rose-400" size={size} data-testid="miscellaneous-icon" />;
case 'Uncategorized':
return <HelpCircle className="text-neutral-400" size={size} />;
return <HelpCircle className="text-neutral-400" size={size} data-testid="uncategorized-icon" />;
default:
return <Frown className="text-red-400" size={size} />;
return <Frown className="text-red-400" size={size} data-testid="default-icon" />;
}
};
13 changes: 13 additions & 0 deletions components/Divider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import Divider from './Divider';

describe('Divider Component', () => {
it('renders the Divider component', () => {
render(<Divider />);

// Check if the HR.Trimmed component is rendered
expect(screen.getByRole('separator')).toBeInTheDocument();
});
});
27 changes: 27 additions & 0 deletions components/Faq.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import Faqs from './Faq';

const mockFaqs = [
{
question: 'What is your return policy?',
answer: 'You can return any item within 30 days of purchase.',
},
{
question: 'How do I track my order?',
answer: 'You can track your order using the tracking number provided in your order confirmation email.',
},
];

describe('Faqs Component', () => {
it('renders the Faqs component', () => {
render(<Faqs faqs={mockFaqs} />);

// Check if the FAQ questions and answers are rendered
mockFaqs.forEach((faq) => {
expect(screen.getByText(faq.question)).toBeInTheDocument();
expect(screen.getByText(faq.answer)).toBeInTheDocument();
});
});
});
40 changes: 6 additions & 34 deletions components/Faq.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,12 @@
import { Accordion } from 'flowbite-react';
import React from 'react';
import { Faq } from '../lib/abstractions';

const faqs = [
{
question: '1. What is the source of plugin list?',
answer:
'The list of plugins are obtained by parsing the `community-plugins.json` file from `obsidianmd/obsidian-releases` GitHub repository.',
},
{
question: '2. What is the source of plugin description?',
answer:
'The description of the plugin is populated from the content corresponding to the plugin as found in `community-plugins.json` file.',
},
{
question: '3. What is the source of release changelog?',
answer:
'The changelog is obtained from the body of release as found in `GET /repos/{repo}/releases` API.',
},
{
question: '4. How are tags populated for a plugin?',
answer:
'The tags are the topics of the GitHub repo, as found in `GET /repos/{repo}` API. To populate this, you can go to the GitHub repo, click open the gear icon in the About section and update the topics.',
},
{
question: '5. How are the trending plugins calculated?',
answer:
'The trending plugins are calculated using the z-score obtained over the daily number of downloads - with a small delay added exponentially for the older records.',
},
{
question: '6. Other question?',
answer:
'If you question is not answered, please open an issue in the GitHub repository (https://github.com/ganesshkumar/obsidian-plugins-stats-ui). We would be happy to answer it for you!',
},
];
interface IFaqProps {
faqs: Faq[];
}

const Faq = () => {
const Faqs = ({faqs}: IFaqProps) => {
return (
<div className="w-auto">
<Accordion>
Expand All @@ -58,4 +30,4 @@ const Faq = () => {
);
};

export default Faq;
export default Faqs;
64 changes: 64 additions & 0 deletions components/Favorites.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Favorites from './Favorites';
import { setFavorite, unsetFavorite } from '../utils/favorites';

jest.mock('../utils/favorites', () => ({
setFavorite: jest.fn(),
unsetFavorite: jest.fn(),
}));

Object.assign(navigator, {
clipboard: {
writeText: jest.fn(),
},
});

describe('Favorites', () => {
const plugin = { pluginId: 'test-plugin' };
const setFavorites = jest.fn();

beforeEach(() => {
jest.clearAllMocks();
});

test('renders favorite button when not favorite', () => {
render(<Favorites isFavorite={false} plugin={plugin} setFavorites={setFavorites} />);
expect(screen.getByText('favorite')).toBeInTheDocument();
});

test('renders unfavorite button when favorite', () => {
render(<Favorites isFavorite={true} plugin={plugin} setFavorites={setFavorites} />);
expect(screen.getByText('unfavorite')).toBeInTheDocument();
});

test('calls setFavorite when favorite button is clicked', () => {
render(<Favorites isFavorite={false} plugin={plugin} setFavorites={setFavorites} />);
fireEvent.click(screen.getByText('favorite'));
expect(setFavorite).toHaveBeenCalledWith(plugin.pluginId, setFavorites);
});

test('calls unsetFavorite when unfavorite button is clicked', () => {
render(<Favorites isFavorite={true} plugin={plugin} setFavorites={setFavorites} />);
fireEvent.click(screen.getByText('unfavorite'));
expect(unsetFavorite).toHaveBeenCalledWith(plugin.pluginId, setFavorites);
});

test('copies link to clipboard when share button is clicked', () => {
render(<Favorites isFavorite={false} plugin={plugin} setFavorites={setFavorites} />);
fireEvent.click(screen.getByText('share'));
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
'http://localhost:4000/plugins/test-plugin'
);
});

test('displays "copied link to clipboard" after share button is clicked', () => {
render(<Favorites isFavorite={false} plugin={plugin} setFavorites={setFavorites} />);
fireEvent.click(screen.getByText('share'));
expect(screen.getByText('copied link to clipboard')).toBeInTheDocument();
// expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
// 'http://localhost:4000/plugins/test-plugin'
// );
});
});
2 changes: 1 addition & 1 deletion components/Favorites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const Favorites = ({ isFavorite, plugin, setFavorites }) => {
process.env.hostname ||
process.env.VERCEL_URL ||
process.env.NODE_ENV === 'production'
? 'https://obsidian-plugin-stats.ganesshkumar.app'
? 'https://obsidian-plugin-stats.ganesshkumar.com'
: 'http://localhost:4000';

const shareClicked = () => {
Expand Down
Loading

0 comments on commit d1b23de

Please sign in to comment.