diff --git a/app/soapbox/components/__tests__/autosuggest_emoji-test.js b/app/soapbox/components/__tests__/autosuggest_emoji.test.js similarity index 100% rename from app/soapbox/components/__tests__/autosuggest_emoji-test.js rename to app/soapbox/components/__tests__/autosuggest_emoji.test.js diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index 21178d5e4..3288127a1 100644 --- a/app/soapbox/components/account.tsx +++ b/app/soapbox/components/account.tsx @@ -125,7 +125,7 @@ const Account = ({ const LinkEl: any = showProfileHoverCard ? Link : 'div'; return ( -
+
{ const brandColor = useSelector((state) => getSoapboxConfig(state).get('brandColor')); return ( - + #{hashtag.get('name')} diff --git a/app/soapbox/components/ui/emoji/__tests__/emoji.test.tsx b/app/soapbox/components/ui/emoji/__tests__/emoji.test.tsx index 0f65103d8..85126806f 100644 --- a/app/soapbox/components/ui/emoji/__tests__/emoji.test.tsx +++ b/app/soapbox/components/ui/emoji/__tests__/emoji.test.tsx @@ -1,12 +1,23 @@ -import React from 'react'; +import * as React from 'react'; import { render, screen } from '../../../../jest/test-helpers'; import Emoji from '../emoji'; describe('', () => { - it('renders the given text', () => { - render(); + it('renders a simple emoji', () => { + render(); - expect(screen.getByRole('img').getAttribute('alt')).toBe('smile'); + const img = screen.getByRole('img'); + expect(img.getAttribute('src')).toBe('/packs/emoji/1f600.svg'); + expect(img.getAttribute('alt')).toBe('πŸ˜€'); + }); + + // https://emojipedia.org/emoji-flag-sequence/ + it('renders a sequence emoji', () => { + render(); + + const img = screen.getByRole('img'); + expect(img.getAttribute('src')).toBe('/packs/emoji/1f1fa-1f1f8.svg'); + expect(img.getAttribute('alt')).toBe('πŸ‡ΊπŸ‡Έ'); }); }); diff --git a/app/soapbox/components/ui/emoji/emoji.tsx b/app/soapbox/components/ui/emoji/emoji.tsx index 9bbaa0dec..e9bcda2ed 100644 --- a/app/soapbox/components/ui/emoji/emoji.tsx +++ b/app/soapbox/components/ui/emoji/emoji.tsx @@ -29,12 +29,12 @@ const toCodePoints = (unicodeSurrogates: string): string[] => { return points; }; -interface IEmoji { - className?: string, +interface IEmoji extends React.ImgHTMLAttributes { emoji: string, } -const Emoji: React.FC = ({ className, emoji }): JSX.Element | null => { +const Emoji: React.FC = (props): JSX.Element | null => { + const { emoji, alt, ...rest } = props; const codepoints = toCodePoints(removeVS16s(emoji)); const filename = codepoints.join('-'); @@ -43,9 +43,9 @@ const Emoji: React.FC = ({ className, emoji }): JSX.Element | null => { return ( {emoji} ); }; diff --git a/app/soapbox/components/ui/form-actions/__tests__/form-actions.test.tsx b/app/soapbox/components/ui/form-actions/__tests__/form-actions.test.tsx new file mode 100644 index 000000000..3c954ca69 --- /dev/null +++ b/app/soapbox/components/ui/form-actions/__tests__/form-actions.test.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { render, screen } from '../../../../jest/test-helpers'; +import FormActions from '../form-actions'; + +describe('', () => { + it('renders successfully', () => { + render(
child
); + + expect(screen.getByTestId('child')).toBeInTheDocument(); + }); +}); diff --git a/app/soapbox/components/ui/icon-button/icon-button.tsx b/app/soapbox/components/ui/icon-button/icon-button.tsx index 291b0b4f8..10d08fc80 100644 --- a/app/soapbox/components/ui/icon-button/icon-button.tsx +++ b/app/soapbox/components/ui/icon-button/icon-button.tsx @@ -22,6 +22,7 @@ const IconButton = React.forwardRef((props: IIconButton, ref: React.ForwardedRef 'bg-white dark:bg-transparent': !transparent, }, className)} {...filteredProps} + data-testid='icon-button' > diff --git a/app/soapbox/components/ui/modal/__tests__/modal.test.tsx b/app/soapbox/components/ui/modal/__tests__/modal.test.tsx new file mode 100644 index 000000000..3894a4604 --- /dev/null +++ b/app/soapbox/components/ui/modal/__tests__/modal.test.tsx @@ -0,0 +1,138 @@ +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { render, screen } from '../../../../jest/test-helpers'; +import Modal from '../modal'; + +describe('', () => { + it('renders', () => { + render(); + expect(screen.getByTestId('modal')).toBeInTheDocument(); + }); + + it('renders children', () => { + render(
); + expect(screen.getByTestId('child')).toBeInTheDocument(); + }); + + it('focuses the primary action', () => { + render( + null} + confirmationText='Click me' + />, + ); + + expect(screen.getByRole('button')).toHaveFocus(); + }); + + describe('onClose prop', () => { + it('renders the Icon to close the modal', async() => { + const mockFn = jest.fn(); + const user = userEvent.setup(); + + render(); + expect(screen.getByTestId('icon-button')).toBeInTheDocument(); + + expect(mockFn).not.toBeCalled(); + await user.click(screen.getByTestId('icon-button')); + expect(mockFn).toBeCalled(); + }); + + it('does not render the Icon to close the modal', () => { + render(); + expect(screen.queryAllByTestId('icon-button')).toHaveLength(0); + }); + }); + + describe('confirmationAction prop', () => { + it('renders the confirmation button', async() => { + const mockFn = jest.fn(); + const user = userEvent.setup(); + + render( + , + ); + + expect(mockFn).not.toBeCalled(); + await user.click(screen.getByRole('button')); + expect(mockFn).toBeCalled(); + }); + + it('does not render the actions to', () => { + render(); + expect(screen.queryAllByTestId('modal-actions')).toHaveLength(0); + }); + + describe('with secondaryAction', () => { + it('renders the secondary button', async() => { + const confirmationAction = jest.fn(); + const secondaryAction = jest.fn(); + const user = userEvent.setup(); + + render( + , + ); + + await user.click(screen.getByText(/secondary/i)); + expect(secondaryAction).toBeCalled(); + expect(confirmationAction).not.toBeCalled(); + }); + + it('does not render the secondary action', () => { + render( + null} + confirmationText='Click me' + />, + ); + expect(screen.queryAllByRole('button')).toHaveLength(1); + }); + }); + + describe('with cancelAction', () => { + it('renders the cancel button', async() => { + const confirmationAction = jest.fn(); + const cancelAction = jest.fn(); + const user = userEvent.setup(); + + render( + , + ); + + await user.click(screen.getByText(/cancel/i)); + expect(cancelAction).toBeCalled(); + expect(confirmationAction).not.toBeCalled(); + }); + + it('does not render the cancel action', () => { + render( + null} + confirmationText='Click me' + />, + ); + expect(screen.queryAllByRole('button')).toHaveLength(1); + }); + }); + }); +}); diff --git a/app/soapbox/components/ui/modal/modal.tsx b/app/soapbox/components/ui/modal/modal.tsx index d9a7e96ab..4270465fc 100644 --- a/app/soapbox/components/ui/modal/modal.tsx +++ b/app/soapbox/components/ui/modal/modal.tsx @@ -17,7 +17,7 @@ interface IModal { confirmationDisabled?: boolean, confirmationText?: string, confirmationTheme?: 'danger', - onClose: () => void, + onClose?: () => void, secondaryAction?: () => void, secondaryText?: string, title: string | React.ReactNode, @@ -46,7 +46,7 @@ const Modal: React.FC = ({ }, [buttonRef]); return ( -
+
@@ -71,7 +71,7 @@ const Modal: React.FC = ({
{confirmationAction && ( -
+
{cancelAction && (