Updated component tests to use best practices and improved syntax

This commit is contained in:
sam-keen 2022-03-18 14:41:30 +00:00
parent 504f088f00
commit edbf8244df
7 changed files with 77 additions and 89 deletions

View File

@ -4,7 +4,7 @@ interface SecondsAgoProps {
date: string | undefined;
}
export const SecondsAgo = ({ date }: SecondsAgoProps) => {
export const SecondsAgo = ({ date, ...props }: SecondsAgoProps) => {
const [now, setNow] = useState(Date.now());
useEffect(() => {
@ -18,10 +18,16 @@ export const SecondsAgo = ({ date }: SecondsAgoProps) => {
return <>Date unknown</>;
}
console.log(
`now: ${now}, before: ${new Date(
date
).getTime()}, date getting passed in: ${date}`
);
const timeAgoInSeconds = Math.floor((now - new Date(date).getTime()) / 1000);
return (
<div>
<div {...props}>
{timeAgoInSeconds === 1 ? '1 second' : `${timeAgoInSeconds} seconds`} ago
</div>
);

View File

@ -1,24 +1,37 @@
import { render, screen } from '@testing-library/react';
import { render, screen, act } from '@testing-library/react';
import { SecondsAgo } from './index';
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
describe('Seconds ago', () => {
it('should render successfully', () => {
const dateInString = Date.now().toString();
const { baseElement } = render(<SecondsAgo date={dateInString} />);
expect(baseElement).toBeTruthy();
const dateInString = new Date().toString();
render(<SecondsAgo data-testid="test-seconds-ago" date={dateInString} />);
expect(screen.getByTestId('test-seconds-ago')).toBeInTheDocument();
});
it('should show the correct amount of seconds ago', async () => {
const secondsToWait = 2;
it('should show the correct amount of seconds ago', (done) => {
const secondsToWait = 10;
const dateInString = new Date().toString();
await new Promise((r) => setTimeout(r, secondsToWait * 1000));
act(() => {
jest.advanceTimersByTime(secondsToWait * 1000);
});
render(<SecondsAgo date={dateInString} />);
jest.runOnlyPendingTimers();
done();
expect(
screen.getByText(`${secondsToWait} seconds ago`)
).toBeInTheDocument();
render(<SecondsAgo data-testid="test-seconds-ago" date={dateInString} />);
expect(screen.getByTestId('test-seconds-ago')).toHaveTextContent(
`${secondsToWait} seconds ago`
);
});
});

View File

@ -6,7 +6,15 @@ interface StatusMessageProps {
className?: string;
}
export const StatusMessage = ({ children, className }: StatusMessageProps) => {
export const StatusMessage = ({
children,
className,
...props
}: StatusMessageProps) => {
const classes = classnames('font-alpha text-h4 mb-28', className);
return <h3 className={classes}>{children}</h3>;
return (
<h3 className={classes} {...props}>
{children}
</h3>
);
};

View File

@ -1,10 +1,11 @@
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { StatusMessage } from './index';
describe('Status message', () => {
it('should render successfully', () => {
const { baseElement } = render(<StatusMessage>test</StatusMessage>);
expect(baseElement).toBeTruthy();
render(
<StatusMessage data-testid="status-message-test">test</StatusMessage>
);
expect(screen.getByTestId('status-message-test')).toBeInTheDocument();
});
});

View File

@ -1,9 +1,9 @@
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { Table, TableRow, TableHeader, TableCell } from './index';
describe('Renders all table components', () => {
const { container } = render(
render(
<Table data-testid="test-table">
<TableRow data-testid="test-tr">
<TableHeader data-testid="test-th">Title</TableHeader>
@ -12,110 +12,68 @@ describe('Renders all table components', () => {
</Table>
);
const table = container.querySelector('[data-testid="test-table"]');
const tr = container.querySelector('[data-testid="test-tr"]');
const th = container.querySelector('[data-testid="test-th"]');
const td = container.querySelector('[data-testid="test-td"]');
expect(table).toBeInTheDocument();
expect(th).toBeInTheDocument();
expect(tr).toBeInTheDocument();
expect(td).toBeInTheDocument();
expect(screen.getByTestId('test-table')).toBeInTheDocument();
expect(screen.getByTestId('test-tr')).toBeInTheDocument();
expect(screen.getByTestId('test-th')).toHaveTextContent('Title');
expect(screen.getByTestId('test-td')).toHaveTextContent('Content');
});
describe('Table row', () => {
it('should include classes based on custom "modifier" prop', () => {
const { baseElement: withoutModifier } = render(
render(
<Table>
<TableRow>
<TableCell>Without modifier</TableCell>
</TableRow>
</Table>
);
const { baseElement: withModifier } = render(
<Table>
<TableRow modifier="bordered">
<TableRow data-testid="modifier-test" modifier="bordered">
<TableCell>With modifier</TableCell>
</TableRow>
</Table>
);
const noModifierTr = withoutModifier.querySelector('tr');
const modifierTr = withModifier.querySelector('tr');
const classNameToCheck = 'border-white-40';
expect(noModifierTr && !noModifierTr.classList.contains(classNameToCheck));
expect(modifierTr && modifierTr.classList.contains(classNameToCheck));
expect(screen.getByTestId('modifier-test')).toHaveClass('border-white-40');
});
});
describe('Table header', () => {
it('should accept props i.e. scope="row"', () => {
const { baseElement } = render(
render(
<Table>
<TableRow>
<TableHeader scope="row">Test</TableHeader>
<TableHeader data-testid="props-test" scope="row">
Test
</TableHeader>
</TableRow>
</Table>
);
const th = baseElement.querySelector('th');
expect(th && th.hasAttribute('scope'));
expect(screen.getByTestId('props-test')).toHaveAttribute('scope');
});
it('should include custom class based on scope="row"', () => {
const { baseElement: withoutScope } = render(
render(
<Table>
<TableRow>
<TableHeader>Without scope attribute</TableHeader>
<TableHeader data-testid="scope-class-test" scope="row">
With scope attribute
</TableHeader>
</TableRow>
</Table>
);
const { baseElement: withScope } = render(
<Table>
<TableRow>
<TableHeader scope="row">With scope attribute</TableHeader>
</TableRow>
</Table>
);
const withoutScopeTr = withoutScope.querySelector('tr');
const withScopeTr = withScope.querySelector('tr');
const classNameToCheck = 'text-left';
expect(
withoutScopeTr && !withoutScopeTr.classList.contains(classNameToCheck)
);
expect(withScopeTr && withScopeTr.classList.contains(classNameToCheck));
expect(screen.getByTestId('scope-class-test')).toHaveClass('text-left');
});
});
describe('Table cell', () => {
it('should include class based on custom "modifier" prop', () => {
const { baseElement: withoutModifier } = render(
render(
<Table>
<TableRow>
<TableCell>Without modifier</TableCell>
<TableCell data-testid="modifier-class-test" modifier="bordered">
With modifier
</TableCell>
</TableRow>
</Table>
);
const { baseElement: withModifier } = render(
<Table>
<TableRow>
<TableCell modifier="bordered">With modifier</TableCell>
</TableRow>
</Table>
);
const noModifierTd = withoutModifier.querySelector('td');
const modifierTd = withModifier.querySelector('td');
const classNameToCheck = 'py-4';
expect(noModifierTd && !noModifierTd.classList.contains(classNameToCheck));
expect(modifierTd && modifierTd.classList.contains(classNameToCheck));
expect(screen.getByTestId('modifier-class-test')).toHaveClass('py-4');
});
});

View File

@ -3,11 +3,11 @@ import { TruncateInline } from './truncate';
describe('Truncate', () => {
it('should render successfully', () => {
const { baseElement } = render(
<TruncateInline text={'Texty McTextFace'} />
render(
<TruncateInline data-testid="truncate-test" text={'Texty McTextFace'} />
);
expect(baseElement).toBeTruthy();
expect(screen.getByTestId('truncate-test')).toBeInTheDocument();
});
it('it truncates as expected', () => {

View File

@ -22,6 +22,7 @@ export function TruncateInline({
children,
startChars,
endChars,
...props
}: TruncateInlineProps) {
if (text === null) {
return <span data-testid="empty-truncation" />;
@ -31,6 +32,7 @@ export function TruncateInline({
const wrapperProps = {
title: text,
className,
...props,
};
if (children !== undefined) {