Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Gregg Setzer committed Jul 15, 2024
1 parent ddd72db commit bcaab54
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 20 deletions.
3 changes: 2 additions & 1 deletion src/components/ImageCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,13 @@ const ImageCard = ({
href={linkUrl}
target={newBrowserTab ? '_blank' : '_self'}
rel={newBrowserTab ? 'noopener noreferrer' : undefined}
aria-label={`Link to ${title}`}
>
<img className="w-full max-h-48 object-cover" src={imgSrc} alt={alt} />
</a>
<div className="px-6 py-4">
<p className="font-bold mb-2">
<a href={linkUrl} className="hover:underline">
<a href={linkUrl} className="hover:underline" aria-label={`Link to ${title}`}>

Check failure on line 67 in src/components/ImageCard.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Replace `·href={linkUrl}·className="hover:underline"·aria-label={`Link·to·${title}`}` with `⏎············href={linkUrl}⏎············className="hover:underline"⏎············aria-label={`Link·to·${title}`}⏎··········`
{title}
</a>
</p>
Expand Down
7 changes: 2 additions & 5 deletions src/components/Skeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ interface SkeletonProps {
className?: string;
}

const Skeleton: React.FC<SkeletonProps> = ({
times = 3,
className = 'h-10 w-full',
}) => {
const Skeleton = ({times = 3, className = 'h-10 w-full'}: SkeletonProps) => {

Check failure on line 9 in src/components/Skeleton.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Replace `times·=·3,·className·=·'h-10·w-full'` with `·times·=·3,·className·=·'h-10·w-full'·`
const outerClassNames = classNames(
'relative',
'overflow-hidden',
Expand All @@ -32,7 +29,7 @@ const Skeleton: React.FC<SkeletonProps> = ({
const skeletonItems = Array(times)
.fill(0)
.map((_, i) => (
<div key={i} className={outerClassNames}>
<div key={i} className={outerClassNames} role="presentation">
<div className={innerClassNames} />
</div>
));
Expand Down
32 changes: 30 additions & 2 deletions tests/components/Button.test.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
import { render, screen } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import Button from '../../src/components/Button';

describe('Button', () => {
describe('Button Component', () => {
test('Renders a button', () => {
render(<Button label="Test" onClick={() => {}} />);

const buttonElement = screen.getByRole('button', { name: 'Test' });

expect(buttonElement).toBeInTheDocument();
});
test('Renders a button with primary styling applied', () => {
render(<Button label="Test" primary={true} onClick={() => {}} />);

const buttonElement = screen.getByRole('button', { name: 'Test' });

// Regex allows for the background color to change, and ensuring the test is less brittle.
expect(buttonElement.className).toMatch(/bg-\w+-\d+/);
});

test('Renders a button with non-primary styling applied', () => {
render(<Button label="Test" primary={false} onClick={() => {}} />);

const buttonElement = screen.getByRole('button', { name: 'Test' });

expect(buttonElement.className).toMatch(/bg-gray-300/);
});

test('Calls the onClick handler', () => {
const handleOnClick = jest.fn();

render(<Button label="Test" onClick={handleOnClick} />);

const buttonElement = screen.getByRole('button', { name: 'Test' });

fireEvent.click(buttonElement);

expect(handleOnClick).toHaveBeenCalledTimes(1);
});
});
62 changes: 62 additions & 0 deletions tests/components/ImageCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { render, screen } from '@testing-library/react';
import ImageCard from '../../src/components/ImageCard';
import React from 'react';

const renderElement = (newBrowserTab = false) => {
return (
<ImageCard
alt="Alt Text"
description="Description"
title="Title"
imgSrc="Image Src"
linkUrl="https://www.react.dev"
newBrowserTab={newBrowserTab}
/>
)

Check failure on line 15 in tests/components/ImageCard.test.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Insert `;`
}

Check failure on line 16 in tests/components/ImageCard.test.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Insert `;`

describe('ImageCard Component', () => {
test('Renders an ImageCard', () => {
render(renderElement());

const linkElements = screen.getAllByRole('link');
expect(linkElements.length).toBe(2);

const imageElement = screen.getByAltText('Alt Text');
expect(imageElement).toBeInTheDocument();

const titleElement = screen.getByText('Title');
expect(titleElement).toBeInTheDocument();

const descriptionElement = screen.getByText('Description');
expect(descriptionElement).toBeInTheDocument();
});

test('Renders an ImageCard with links that open in the same browser tab', () => {
render(renderElement());

const [imageLinkElement, titleLinkElement] = screen.getAllByRole('link');

expect(imageLinkElement).toBeInTheDocument();
expect(imageLinkElement).toHaveAttribute('href', 'https://www.react.dev');
expect(imageLinkElement).not.toHaveAttribute('rel');

expect(titleLinkElement).toBeInTheDocument();
expect(titleLinkElement).toHaveAttribute('href', 'https://www.react.dev');
expect(imageLinkElement).not.toHaveAttribute('rel');
});

test('Renders an ImageCard with links that open in a new browser tab', () => {
render(renderElement(true));

const [imageLinkElement, titleLinkElement] = screen.getAllByRole('link');

expect(imageLinkElement).toBeInTheDocument();
expect(imageLinkElement).toHaveAttribute('href', 'https://www.react.dev');
expect(imageLinkElement).toHaveAttribute('rel', 'noopener noreferrer');

expect(titleLinkElement).toBeInTheDocument();
expect(titleLinkElement).toHaveAttribute('href', 'https://www.react.dev');
expect(imageLinkElement).toHaveAttribute('rel', 'noopener noreferrer');
});
});

Check failure on line 62 in tests/components/ImageCard.test.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Insert `⏎`
24 changes: 12 additions & 12 deletions tests/components/Input.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ describe('Input Component', () => {
/>
);

const inputElement = screen.getByLabelText('Label');
const element = screen.getByLabelText('Label');

expect(inputElement).toBeInTheDocument();
expect(inputElement).toHaveAttribute('id', 'testId');
expect(inputElement).toHaveAttribute('name', 'testName');
expect(inputElement).toHaveAttribute('type', 'text');
expect(inputElement).toHaveAttribute('min', '5');
expect(inputElement).toHaveAttribute('max', '10');
expect(element).toBeInTheDocument();
expect(element).toHaveAttribute('id', 'testId');
expect(element).toHaveAttribute('name', 'testName');
expect(element).toHaveAttribute('type', 'text');
expect(element).toHaveAttribute('min', '5');
expect(element).toHaveAttribute('max', '10');
});

test('Calls the onChange handler when the input value changes', () => {
Expand All @@ -43,9 +43,9 @@ describe('Input Component', () => {
/>
);

const inputElement = screen.getByLabelText('Label');
const element = screen.getByLabelText('Label');

fireEvent.change(inputElement, { target: { value: 'new value' } });
fireEvent.change(element, { target: { value: 'new value' } });

expect(handleChange).toHaveBeenCalledTimes(1);
});
Expand All @@ -68,10 +68,10 @@ describe('Input Component', () => {
/>
);

const inputElement = screen.getByLabelText(`Label ${type}`);
const element = screen.getByLabelText(`Label ${type}`);

expect(inputElement).toBeInTheDocument();
expect(inputElement).toHaveAttribute('type', type);
expect(element).toBeInTheDocument();
expect(element).toHaveAttribute('type', type);
cleanup();
});
});
Expand Down
99 changes: 99 additions & 0 deletions tests/components/Select.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import Input from '../../src/components/Input';

Check failure on line 2 in tests/components/Select.test.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

'Input' is defined but never used
import Select, { Option } from '../../src/components/Select';
import React from 'react';

const labelName = 'Select an Option'

Check failure on line 6 in tests/components/Select.test.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Insert `;`
const options: Option[] = [
{ label: 'Option 1', value: '1', inactive: false },
{ label: 'Option 2', value: '2', inactive: false },
{ label: 'Option 3', value: '3', inactive: true },
];

const renderElement = (onChange = () => {}) => {
return (
<Select
id="testId"
name="testName"
label={labelName}
value="1"
onChange={onChange}
options={options}
/>
);
}

Check failure on line 24 in tests/components/Select.test.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Insert `;`

describe('Select Component', () => {
afterEach(cleanup);

test('Renders a select input field with the correct attributes', () => {
render(renderElement());

const element = screen.getByLabelText(labelName);

expect(element).toBeInTheDocument();
expect(element).toHaveAttribute('id', 'testId');
expect(element).toHaveAttribute('name', 'testName');

expect(screen.getAllByRole('option')).toHaveLength(options.length);
});

test('Renders a select input field with the correct options', () => {
render(renderElement());

const optionElements = screen.getAllByRole('option');
options.forEach((option, index) => {
expect(optionElements[index]).toHaveTextContent(option.label);
expect(optionElements[index]).toHaveAttribute('value', option.value);
if (option.inactive) {
expect(optionElements[index]).toBeDisabled();
}
});
});

test('Calls the onChange handler when the value changes', () => {
const handleChange = jest.fn();

render(renderElement(handleChange));

const element = screen.getByLabelText(labelName);

fireEvent.change(element, { target: { value: '2' } });

expect(handleChange).toHaveBeenCalledTimes(1);
});

test('Applies the custom hero css treatment', () => {
render(
<Select
id="labelId"
name="labelName"
label="Label"
value="1"
onChange={() => {}}
options={options}
hero={true}
/>
);

const element = screen.getByRole('combobox');
expect(element).toHaveClass('custom-select');
});

test('Hides label when hideLabel is true', () => {
render(
<Select
id="hiddenLabelId"
name="hiddenLabelName"
label="Hidden label"
value="3"
onChange={() => {}}
options={options}
hideLabel={true}
/>
);

const element = screen.getByText('Hidden label');
expect(element).toHaveClass('sr-only');
});
});

Check failure on line 99 in tests/components/Select.test.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Insert `⏎`
22 changes: 22 additions & 0 deletions tests/components/Skeleton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { render, screen } from '@testing-library/react';
import Skeleton from '../../src/components/Skeleton';
import React from 'react';

describe('Skeleton Component', () => {
test('Renders the correct number of skeleton items', () => {
const times = 5;
render(<Skeleton times={times} />);

const elements = screen.getAllByRole('presentation');
expect(elements).toHaveLength(times);
});

test('Applies custom className', () => {
const customClassName = 'bg-test-500';
render(<Skeleton className={customClassName} />);

const skeletonItems = screen.getAllByRole('presentation');

expect(skeletonItems[0]).toHaveClass(customClassName);
});
})

Check failure on line 22 in tests/components/Skeleton.test.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Insert `;⏎`
27 changes: 27 additions & 0 deletions tests/components/SkeletonGrid.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 SkeletonGrid from '../../src/components/SkeletonGrid';

jest.mock('../../src/components/Skeleton', () => {
return function DummySkeleton(props: { className: string; times: number }) {
return <div data-testid="skeleton" className={props.className}>Skeleton</div>;
};
});

describe('SkeletonGrid', () => {
test('Renders the correct number of Skeleton components', () => {
const count = 5;
render(<SkeletonGrid count={count} />);

// Check if the correct number of Skeleton components are rendered
const skeletonItems = screen.getAllByTestId('skeleton');
expect(skeletonItems).toHaveLength(count);
});

test('Renders default number of Skeleton components when count is not provided', () => {
render(<SkeletonGrid />);

const skeletonItems = screen.getAllByTestId('skeleton');
expect(skeletonItems).toHaveLength(4);
});
});

0 comments on commit bcaab54

Please sign in to comment.