Merge pull request #659 from acelaya-forks/feature/moar-rtl

Feature/moar rtl
This commit is contained in:
Alejandro Celaya
2022-06-04 10:25:17 +02:00
committed by GitHub
10 changed files with 287 additions and 222 deletions

View File

@@ -26,7 +26,7 @@ export const DeleteServerModal: FC<DeleteServerModalConnectProps> = (
return ( return (
<Modal isOpen={isOpen} toggle={toggle} centered> <Modal isOpen={isOpen} toggle={toggle} centered>
<ModalHeader toggle={toggle}><span className="text-danger">Remove server</span></ModalHeader> <ModalHeader toggle={toggle} className="text-danger">Remove server</ModalHeader>
<ModalBody> <ModalBody>
<p>Are you sure you want to remove <b>{server ? server.name : ''}</b>?</p> <p>Are you sure you want to remove <b>{server ? server.name : ''}</b>?</p>
<p> <p>

View File

@@ -39,7 +39,7 @@ export const ManageServersRowDropdown = (
<DropdownItem tag={Link} to={`${serverUrl}/edit`}> <DropdownItem tag={Link} to={`${serverUrl}/edit`}>
<FontAwesomeIcon icon={editIcon} fixedWidth /> Edit server <FontAwesomeIcon icon={editIcon} fixedWidth /> Edit server
</DropdownItem> </DropdownItem>
<DropdownItem onClick={() => setAutoConnect(server, !server.autoConnect)}> <DropdownItem onClick={() => setAutoConnect(server, !isAutoConnect)}>
<FontAwesomeIcon icon={autoConnectIcon} fixedWidth /> {isAutoConnect ? 'Do not a' : 'A'}uto-connect <FontAwesomeIcon icon={autoConnectIcon} fixedWidth /> {isAutoConnect ? 'Do not a' : 'A'}uto-connect
</DropdownItem> </DropdownItem>
<DropdownItem divider /> <DropdownItem divider />

View File

@@ -1,13 +1,13 @@
import { parseISO, format as formatDate, getUnixTime, formatDistance } from 'date-fns'; import { parseISO, format as formatDate, getUnixTime, formatDistance } from 'date-fns';
import { isDateObject } from './helpers/date'; import { isDateObject } from './helpers/date';
export interface DateProps { export interface TimeProps {
date: Date | string; date: Date | string;
format?: string; format?: string;
relative?: boolean; relative?: boolean;
} }
export const Time = ({ date, format = 'yyyy-MM-dd HH:mm', relative = false }: DateProps) => { export const Time = ({ date, format = 'yyyy-MM-dd HH:mm', relative = false }: TimeProps) => {
const dateObject = isDateObject(date) ? date : parseISO(date); const dateObject = isDateObject(date) ? date : parseISO(date);
return ( return (

View File

@@ -1,5 +1,5 @@
import { shallow, ShallowWrapper } from 'enzyme'; import { render, screen } from '@testing-library/react';
import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import userEvent from '@testing-library/user-event';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { DeleteServerModal } from '../../src/servers/DeleteServerModal'; import { DeleteServerModal } from '../../src/servers/DeleteServerModal';
@@ -8,56 +8,63 @@ import { ServerWithId } from '../../src/servers/data';
jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: jest.fn() })); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: jest.fn() }));
describe('<DeleteServerModal />', () => { describe('<DeleteServerModal />', () => {
let wrapper: ShallowWrapper;
const deleteServerMock = jest.fn(); const deleteServerMock = jest.fn();
const navigate = jest.fn(); const navigate = jest.fn();
const toggleMock = jest.fn(); const toggleMock = jest.fn();
const serverName = 'the_server_name'; const serverName = 'the_server_name';
const setUp = () => {
beforeEach(() => {
(useNavigate as any).mockReturnValue(navigate); (useNavigate as any).mockReturnValue(navigate);
wrapper = shallow( return {
<DeleteServerModal user: userEvent.setup(),
server={Mock.of<ServerWithId>({ name: serverName })} ...render(
toggle={toggleMock} <DeleteServerModal
isOpen server={Mock.of<ServerWithId>({ name: serverName })}
deleteServer={deleteServerMock} toggle={toggleMock}
/>, isOpen
); deleteServer={deleteServerMock}
}); />,
afterEach(() => wrapper.unmount()); ),
};
};
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
it('renders a modal window', () => { it('renders a modal window', () => {
expect(wrapper.find(Modal)).toHaveLength(1); setUp();
expect(wrapper.find(ModalHeader)).toHaveLength(1);
expect(wrapper.find(ModalBody)).toHaveLength(1); expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(wrapper.find(ModalFooter)).toHaveLength(1); expect(screen.getByRole('heading')).toHaveTextContent('Remove server');
}); });
it('displays the name of the server as part of the content', () => { it('displays the name of the server as part of the content', () => {
const modalBody = wrapper.find(ModalBody); setUp();
expect(modalBody.find('p').first().text()).toEqual( expect(screen.getByText(/^Are you sure you want to remove/)).toBeInTheDocument();
`Are you sure you want to remove ${serverName}?`, expect(screen.getByText(serverName)).toBeInTheDocument();
);
}); });
it('toggles when clicking cancel button', () => { it.each([
const cancelBtn = wrapper.find(Button).first(); [() => screen.getByRole('button', { name: 'Cancel' })],
[() => screen.getByLabelText('Close')],
])('toggles when clicking cancel button', async (getButton) => {
const { user } = setUp();
cancelBtn.simulate('click'); expect(toggleMock).not.toHaveBeenCalled();
await user.click(getButton());
expect(toggleMock).toHaveBeenCalledTimes(1); expect(toggleMock).toHaveBeenCalledTimes(1);
expect(deleteServerMock).not.toHaveBeenCalled(); expect(deleteServerMock).not.toHaveBeenCalled();
expect(navigate).not.toHaveBeenCalled(); expect(navigate).not.toHaveBeenCalled();
}); });
it('deletes server when clicking accept button', () => { it('deletes server when clicking accept button', async () => {
const acceptBtn = wrapper.find(Button).last(); const { user } = setUp();
acceptBtn.simulate('click'); expect(toggleMock).not.toHaveBeenCalled();
expect(deleteServerMock).not.toHaveBeenCalled();
expect(navigate).not.toHaveBeenCalled();
await user.click(screen.getByRole('button', { name: 'Delete' }));
expect(toggleMock).toHaveBeenCalledTimes(1); expect(toggleMock).toHaveBeenCalledTimes(1);
expect(deleteServerMock).toHaveBeenCalledTimes(1); expect(deleteServerMock).toHaveBeenCalledTimes(1);

View File

@@ -1,93 +1,103 @@
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { shallow, ShallowWrapper } from 'enzyme'; import userEvent from '@testing-library/user-event';
import { Button } from 'reactstrap'; import { render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import ServersExporter from '../../src/servers/services/ServersExporter'; import ServersExporter from '../../src/servers/services/ServersExporter';
import { ManageServers as createManageServers } from '../../src/servers/ManageServers'; import { ManageServers as createManageServers } from '../../src/servers/ManageServers';
import { ServersMap, ServerWithId } from '../../src/servers/data'; import { ServersMap, ServerWithId } from '../../src/servers/data';
import { SearchField } from '../../src/utils/SearchField';
import { Result } from '../../src/utils/Result';
describe('<ManageServers />', () => { describe('<ManageServers />', () => {
const exportServers = jest.fn(); const exportServers = jest.fn();
const serversExporter = Mock.of<ServersExporter>({ exportServers }); const serversExporter = Mock.of<ServersExporter>({ exportServers });
const ImportServersBtn = () => null;
const ManageServersRow = () => null;
const useTimeoutToggle = jest.fn().mockReturnValue([false, jest.fn()]); const useTimeoutToggle = jest.fn().mockReturnValue([false, jest.fn()]);
const ManageServers = createManageServers(serversExporter, ImportServersBtn, useTimeoutToggle, ManageServersRow); const ManageServers = createManageServers(
let wrapper: ShallowWrapper; serversExporter,
() => <span>ImportServersBtn</span>,
useTimeoutToggle,
({ hasAutoConnect }) => <span>ManageServersRow {hasAutoConnect ? '[YES]' : '[NO]'}</span>,
);
const createServerMock = (value: string, autoConnect = false) => Mock.of<ServerWithId>( const createServerMock = (value: string, autoConnect = false) => Mock.of<ServerWithId>(
{ id: value, name: value, url: value, autoConnect }, { id: value, name: value, url: value, autoConnect },
); );
const createWrapper = (servers: ServersMap = {}) => { const setUp = (servers: ServersMap = {}) => ({
wrapper = shallow(<ManageServers servers={servers} />); user: userEvent.setup(),
...render(<MemoryRouter><ManageServers servers={servers} /></MemoryRouter>),
return wrapper; });
};
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
afterEach(() => wrapper?.unmount());
it('shows search field which allows searching servers, affecting te amount of rendered rows', () => { it('shows search field which allows searching servers, affecting te amount of rendered rows', async () => {
const wrapper = createWrapper({ const { user } = setUp({
foo: createServerMock('foo'), foo: createServerMock('foo'),
bar: createServerMock('bar'), bar: createServerMock('bar'),
baz: createServerMock('baz'), baz: createServerMock('baz'),
}); });
const searchField = wrapper.find(SearchField); const search = async (searchTerm: string) => {
await user.clear(screen.getByPlaceholderText('Search...'));
await user.type(screen.getByPlaceholderText('Search...'), searchTerm);
};
expect(wrapper.find(ManageServersRow)).toHaveLength(3); expect(screen.getAllByText(/^ManageServersRow/)).toHaveLength(3);
expect(wrapper.find('tbody').find('tr')).toHaveLength(0); expect(screen.queryByText('No servers found.')).not.toBeInTheDocument();
searchField.simulate('change', 'foo'); await search('foo');
expect(wrapper.find(ManageServersRow)).toHaveLength(1); await waitFor(() => expect(screen.getAllByText(/^ManageServersRow/)).toHaveLength(1));
expect(wrapper.find('tbody').find('tr')).toHaveLength(0); expect(screen.queryByText('No servers found.')).not.toBeInTheDocument();
searchField.simulate('change', 'ba'); await search('ba');
expect(wrapper.find(ManageServersRow)).toHaveLength(2); await waitFor(() => expect(screen.getAllByText(/^ManageServersRow/)).toHaveLength(2));
expect(wrapper.find('tbody').find('tr')).toHaveLength(0); expect(screen.queryByText('No servers found.')).not.toBeInTheDocument();
searchField.simulate('change', 'invalid'); await search('invalid');
expect(wrapper.find(ManageServersRow)).toHaveLength(0); await waitFor(() => expect(screen.queryByText(/^ManageServersRow/)).not.toBeInTheDocument());
expect(wrapper.find('tbody').find('tr')).toHaveLength(1); expect(screen.getByText('No servers found.')).toBeInTheDocument();
}); });
it.each([ it.each([
[createServerMock('foo'), 3], [createServerMock('foo'), 3],
[createServerMock('foo', true), 4], [createServerMock('foo', true), 4],
])('shows different amount of columns if there are at least one auto-connect server', (server, expectedCols) => { ])('shows different amount of columns if there are at least one auto-connect server', (server, expectedCols) => {
const wrapper = createWrapper({ server }); setUp({ server });
const row = wrapper.find(ManageServersRow);
expect(wrapper.find('th')).toHaveLength(expectedCols); expect(screen.getAllByRole('columnheader')).toHaveLength(expectedCols);
expect(row.prop('hasAutoConnect')).toEqual(server.autoConnect); if (server.autoConnect) {
expect(screen.getByText(/\[YES\]/)).toBeInTheDocument();
expect(screen.queryByText(/\[NO\]/)).not.toBeInTheDocument();
} else {
expect(screen.queryByText(/\[YES\]/)).not.toBeInTheDocument();
expect(screen.getByText(/\[NO\]/)).toBeInTheDocument();
}
}); });
it.each([ it.each([
[{}, 1], [{}, 0],
[{ foo: createServerMock('foo') }, 2], [{ foo: createServerMock('foo') }, 1],
])('shows export button if the list of servers is not empty', (servers, expectedButtons) => { ])('shows export button if the list of servers is not empty', (servers, expectedButtons) => {
const wrapper = createWrapper(servers); setUp(servers);
const exportBtn = wrapper.find(Button); expect(screen.queryAllByRole('button', { name: 'Export servers' })).toHaveLength(expectedButtons);
expect(exportBtn).toHaveLength(expectedButtons);
}); });
it('allows exporting servers when clicking on button', () => { it('allows exporting servers when clicking on button', async () => {
const wrapper = createWrapper({ foo: createServerMock('foo') }); const { user } = setUp({ foo: createServerMock('foo') });
const exportBtn = wrapper.find(Button).first();
expect(exportServers).not.toHaveBeenCalled(); expect(exportServers).not.toHaveBeenCalled();
exportBtn.simulate('click'); await user.click(screen.getByRole('button', { name: 'Export servers' }));
expect(exportServers).toHaveBeenCalled(); expect(exportServers).toHaveBeenCalled();
}); });
it('shows an error message if an error occurs while importing servers', () => { it.each([[true], [false]])('shows an error message if an error occurs while importing servers', (hasError) => {
useTimeoutToggle.mockReturnValue([true, jest.fn()]); useTimeoutToggle.mockReturnValue([hasError, jest.fn()]);
const wrapper = createWrapper({ foo: createServerMock('foo') }); setUp({ foo: createServerMock('foo') });
const result = wrapper.find(Result);
expect(result).toHaveLength(1); if (hasError) {
expect(result.prop('type')).toEqual('error'); expect(
screen.getByText('The servers could not be imported. Make sure the format is correct.'),
).toBeInTheDocument();
} else {
expect(
screen.queryByText('The servers could not be imported. Make sure the format is correct.'),
).not.toBeInTheDocument();
}
}); });
}); });

View File

@@ -1,66 +1,58 @@
import { shallow, ShallowWrapper } from 'enzyme'; import { render, screen } from '@testing-library/react';
import { UncontrolledTooltip } from 'reactstrap'; import { MemoryRouter } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Link } from 'react-router-dom';
import { ManageServersRow as createManageServersRow } from '../../src/servers/ManageServersRow'; import { ManageServersRow as createManageServersRow } from '../../src/servers/ManageServersRow';
import { ServerWithId } from '../../src/servers/data'; import { ServerWithId } from '../../src/servers/data';
describe('<ManageServersRow />', () => { describe('<ManageServersRow />', () => {
const ManageServersRowDropdown = () => null; const ManageServersRow = createManageServersRow(() => <span>ManageServersRowDropdown</span>);
const ManageServersRow = createManageServersRow(ManageServersRowDropdown);
const server: ServerWithId = { const server: ServerWithId = {
name: 'My server', name: 'My server',
url: 'https://example.com', url: 'https://example.com',
apiKey: '123', apiKey: '123',
id: 'abc', id: 'abc',
}; };
let wrapper: ShallowWrapper; const setUp = (hasAutoConnect = false, autoConnect = false) => render(
const createWrapper = (hasAutoConnect = false, autoConnect = false) => { <MemoryRouter>
wrapper = shallow(<ManageServersRow server={{ ...server, autoConnect }} hasAutoConnect={hasAutoConnect} />); <table>
<tbody>
return wrapper; <ManageServersRow server={{ ...server, autoConnect }} hasAutoConnect={hasAutoConnect} />
}; </tbody>
</table>
afterEach(() => wrapper?.unmount()); </MemoryRouter>,
);
it.each([ it.each([
[true, 4], [true, 4],
[false, 3], [false, 3],
])('renders expected amount of columns', (hasAutoConnect, expectedCols) => { ])('renders expected amount of columns', (hasAutoConnect, expectedCols) => {
const wrapper = createWrapper(hasAutoConnect); setUp(hasAutoConnect);
const td = wrapper.find('td');
const th = wrapper.find('th'); const td = screen.getAllByRole('cell');
const th = screen.getAllByRole('columnheader');
expect(td.length + th.length).toEqual(expectedCols); expect(td.length + th.length).toEqual(expectedCols);
}); });
it('renders a dropdown', () => { it('renders a dropdown', () => {
const wrapper = createWrapper(); setUp();
const dropdown = wrapper.find(ManageServersRowDropdown); expect(screen.getByText('ManageServersRowDropdown')).toBeInTheDocument();
expect(dropdown).toHaveLength(1);
expect(dropdown.prop('server')).toEqual(expect.objectContaining(server));
}); });
it.each([ it.each([
[true, 1], [true],
[false, 0], [false],
])('renders auto-connect icon only if server is autoConnect', (autoConnect, expectedIcons) => { ])('renders auto-connect icon only if server is autoConnect', (autoConnect) => {
const wrapper = createWrapper(true, autoConnect); const { container } = setUp(true, autoConnect);
const icon = wrapper.find(FontAwesomeIcon); expect(container).toMatchSnapshot();
const iconTooltip = wrapper.find(UncontrolledTooltip);
expect(icon).toHaveLength(expectedIcons);
expect(iconTooltip).toHaveLength(expectedIcons);
}); });
it('renders server props where appropriate', () => { it('renders server props where appropriate', () => {
const wrapper = createWrapper(); setUp();
const link = wrapper.find(Link);
const td = wrapper.find('td').first();
expect(link.prop('to')).toEqual(`/server/${server.id}`); const link = screen.getByRole('link');
expect(link.prop('children')).toEqual(server.name);
expect(td.prop('children')).toEqual(server.url); expect(link).toHaveAttribute('href', `/server/${server.id}`);
expect(link).toHaveTextContent(server.name);
expect(screen.getByText(server.url)).toBeInTheDocument();
}); });
}); });

View File

@@ -1,84 +1,60 @@
import { shallow, ShallowWrapper } from 'enzyme'; import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { DropdownItem } from 'reactstrap'; import { MemoryRouter } from 'react-router-dom';
import { ServerWithId } from '../../src/servers/data'; import { ServerWithId } from '../../src/servers/data';
import { ManageServersRowDropdown as createManageServersRowDropdown } from '../../src/servers/ManageServersRowDropdown'; import { ManageServersRowDropdown as createManageServersRowDropdown } from '../../src/servers/ManageServersRowDropdown';
describe('<ManageServersRowDropdown />', () => { describe('<ManageServersRowDropdown />', () => {
const DeleteServerModal = () => null; const ManageServersRowDropdown = createManageServersRowDropdown(
const ManageServersRowDropdown = createManageServersRowDropdown(DeleteServerModal); ({ isOpen }) => <span>DeleteServerModal {isOpen ? '[OPEN]' : '[CLOSED]'}</span>,
);
const setAutoConnect = jest.fn(); const setAutoConnect = jest.fn();
let wrapper: ShallowWrapper; const setUp = (autoConnect = false) => {
const createWrapper = (autoConnect = false) => {
const server = Mock.of<ServerWithId>({ id: 'abc123', autoConnect }); const server = Mock.of<ServerWithId>({ id: 'abc123', autoConnect });
return {
wrapper = shallow(<ManageServersRowDropdown setAutoConnect={setAutoConnect} server={server} />); user: userEvent.setup(),
...render(
return wrapper; <MemoryRouter>
<ManageServersRowDropdown setAutoConnect={setAutoConnect} server={server} />
</MemoryRouter>,
),
};
}; };
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
it('renders expected amount of dropdown items', () => { it('renders expected amount of dropdown items', async () => {
const wrapper = createWrapper(); const { user } = setUp();
const items = wrapper.find(DropdownItem);
expect(items).toHaveLength(5); expect(screen.queryByRole('menu')).not.toBeInTheDocument();
expect(items.find('[divider]')).toHaveLength(1); await user.click(screen.getByRole('button'));
expect(items.at(0).prop('to')).toEqual('/server/abc123'); expect(screen.getByRole('menu')).toBeInTheDocument();
expect(items.at(1).prop('to')).toEqual('/server/abc123/edit');
expect(screen.getAllByRole('menuitem')).toHaveLength(4);
expect(screen.getByRole('menuitem', { name: 'Connect' })).toHaveAttribute('href', '/server/abc123');
expect(screen.getByRole('menuitem', { name: 'Edit server' })).toHaveAttribute('href', '/server/abc123/edit');
}); });
it('allows toggling auto-connect', () => { it('allows toggling auto-connect', async () => {
const wrapper = createWrapper(); const { user } = setUp();
expect(setAutoConnect).not.toHaveBeenCalled(); expect(setAutoConnect).not.toHaveBeenCalled();
wrapper.find(DropdownItem).at(2).simulate('click'); await user.click(screen.getByRole('button'));
await user.click(screen.getByRole('menuitem', { name: 'Auto-connect' }));
expect(setAutoConnect).toHaveBeenCalledWith(expect.objectContaining({ id: 'abc123' }), true); expect(setAutoConnect).toHaveBeenCalledWith(expect.objectContaining({ id: 'abc123' }), true);
}); });
it('renders a modal', () => { it('renders deletion modal', async () => {
const wrapper = createWrapper(); const { user } = setUp();
const modal = wrapper.find(DeleteServerModal);
expect(modal).toHaveLength(1); expect(screen.queryByText('DeleteServerModal [OPEN]')).not.toBeInTheDocument();
expect(modal.prop('redirectHome')).toEqual(false); expect(screen.getByText('DeleteServerModal [CLOSED]')).toBeInTheDocument();
expect(modal.prop('server')).toEqual(expect.objectContaining({ id: 'abc123' }));
expect(modal.prop('isOpen')).toEqual(false);
});
it('allows toggling the modal', () => { await user.click(screen.getByRole('button'));
const wrapper = createWrapper(); await user.click(screen.getByRole('menuitem', { name: 'Remove server' }));
const modalToggle = wrapper.find(DropdownItem).last();
expect(wrapper.find(DeleteServerModal).prop('isOpen')).toEqual(false); expect(screen.getByText('DeleteServerModal [OPEN]')).toBeInTheDocument();
expect(screen.queryByText('DeleteServerModal [CLOSED]')).not.toBeInTheDocument();
modalToggle.simulate('click');
expect(wrapper.find(DeleteServerModal).prop('isOpen')).toEqual(true);
(wrapper.find(DeleteServerModal).prop('toggle') as Function)();
expect(wrapper.find(DeleteServerModal).prop('isOpen')).toEqual(false);
});
it('can be toggled', () => {
const wrapper = createWrapper();
expect(wrapper.prop('isOpen')).toEqual(false);
(wrapper.prop('toggle') as Function)();
expect(wrapper.prop('isOpen')).toEqual(true);
(wrapper.prop('toggle') as Function)();
expect(wrapper.prop('isOpen')).toEqual(false);
});
it.each([
[true, 'Do not auto-connect'],
[false, 'Auto-connect'],
])('shows different auto-connect toggle text depending on current server status', (autoConnect, expectedText) => {
const wrapper = createWrapper(autoConnect);
const item = wrapper.find(DropdownItem).at(2);
expect(item.html()).toContain(expectedText);
}); });
}); });

View File

@@ -1,6 +1,6 @@
import { shallow, ShallowWrapper } from 'enzyme'; import { render, screen } from '@testing-library/react';
import { ListGroup } from 'reactstrap';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { MemoryRouter } from 'react-router-dom';
import { ServersListGroup } from '../../src/servers/ServersListGroup'; import { ServersListGroup } from '../../src/servers/ServersListGroup';
import { ServerWithId } from '../../src/servers/data'; import { ServerWithId } from '../../src/servers/data';
@@ -9,44 +9,36 @@ describe('<ServersListGroup />', () => {
Mock.of<ServerWithId>({ name: 'foo', id: '123' }), Mock.of<ServerWithId>({ name: 'foo', id: '123' }),
Mock.of<ServerWithId>({ name: 'bar', id: '456' }), Mock.of<ServerWithId>({ name: 'bar', id: '456' }),
]; ];
let wrapped: ShallowWrapper; const setUp = (params: { servers?: ServerWithId[]; withChildren?: boolean; embedded?: boolean }) => {
const createComponent = (params: { servers?: ServerWithId[]; withChildren?: boolean; embedded?: boolean }) => {
const { servers = [], withChildren = true, embedded } = params; const { servers = [], withChildren = true, embedded } = params;
wrapped = shallow( return render(
<ServersListGroup servers={servers} embedded={embedded}> <MemoryRouter>
{withChildren ? 'The list of servers' : undefined} <ServersListGroup servers={servers} embedded={embedded}>
</ServersListGroup>, {withChildren ? 'The list of servers' : undefined}
</ServersListGroup>
</MemoryRouter>,
); );
return wrapped;
}; };
afterEach(() => wrapped?.unmount());
it('renders title', () => { it('renders title', () => {
const wrapped = createComponent({}); setUp({});
const title = wrapped.find('h5'); expect(screen.getByRole('heading')).toHaveTextContent('The list of servers');
expect(title).toHaveLength(1);
expect(title.text()).toEqual('The list of servers');
}); });
it('does not render title when children is not provided', () => { it('does not render title when children is not provided', () => {
const wrapped = createComponent({ withChildren: false }); setUp({ withChildren: false });
const title = wrapped.find('h5'); expect(screen.queryByRole('heading')).not.toBeInTheDocument();
expect(title).toHaveLength(0);
}); });
it.each([ it.each([
[servers], [servers],
[[]], [[]],
])('shows servers list', (servers) => { ])('shows servers list', (servers) => {
const wrapped = createComponent({ servers }); setUp({ servers });
expect(wrapped.find(ListGroup)).toHaveLength(servers.length ? 1 : 0); expect(screen.queryAllByRole('list')).toHaveLength(servers.length ? 1 : 0);
expect(wrapped.find('ServerListItem')).toHaveLength(servers.length); expect(screen.queryAllByRole('link')).toHaveLength(servers.length);
}); });
it.each([ it.each([
@@ -54,9 +46,7 @@ describe('<ServersListGroup />', () => {
[false, 'servers-list__list-group'], [false, 'servers-list__list-group'],
[undefined, 'servers-list__list-group'], [undefined, 'servers-list__list-group'],
])('renders proper classes for embedded', (embedded, expectedClasses) => { ])('renders proper classes for embedded', (embedded, expectedClasses) => {
const wrapped = createComponent({ servers, embedded }); setUp({ servers, embedded });
const listGroup = wrapped.find(ListGroup); expect(screen.getByRole('list')).toHaveAttribute('class', `${expectedClasses} list-group`);
expect(listGroup.prop('className')).toEqual(expectedClasses);
}); });
}); });

View File

@@ -0,0 +1,98 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<ManageServersRow /> renders auto-connect icon only if server is autoConnect 1`] = `
<div>
<table>
<tbody>
<tr
class="responsive-table__row"
>
<td
class="responsive-table__cell"
data-th="Auto-connect"
>
<svg
aria-hidden="true"
class="svg-inline--fa fa-check text-primary"
data-icon="check"
data-prefix="fas"
focusable="false"
id="autoConnectIcon"
role="img"
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M438.6 105.4C451.1 117.9 451.1 138.1 438.6 150.6L182.6 406.6C170.1 419.1 149.9 419.1 137.4 406.6L9.372 278.6C-3.124 266.1-3.124 245.9 9.372 233.4C21.87 220.9 42.13 220.9 54.63 233.4L159.1 338.7L393.4 105.4C405.9 92.88 426.1 92.88 438.6 105.4H438.6z"
fill="currentColor"
/>
</svg>
</td>
<th
class="responsive-table__cell"
data-th="Name"
>
<a
href="/server/abc"
>
My server
</a>
</th>
<td
class="responsive-table__cell"
data-th="Base URL"
>
https://example.com
</td>
<td
class="responsive-table__cell text-end"
>
<span>
ManageServersRowDropdown
</span>
</td>
</tr>
</tbody>
</table>
</div>
`;
exports[`<ManageServersRow /> renders auto-connect icon only if server is autoConnect 2`] = `
<div>
<table>
<tbody>
<tr
class="responsive-table__row"
>
<td
class="responsive-table__cell"
data-th="Auto-connect"
/>
<th
class="responsive-table__cell"
data-th="Name"
>
<a
href="/server/abc"
>
My server
</a>
</th>
<td
class="responsive-table__cell"
data-th="Base URL"
>
https://example.com
</td>
<td
class="responsive-table__cell text-end"
>
<span>
ManageServersRowDropdown
</span>
</td>
</tr>
</tbody>
</table>
</div>
`;

View File

@@ -1,30 +1,22 @@
import { shallow, ShallowWrapper } from 'enzyme'; import { render } from '@testing-library/react';
import { DateProps, Time } from '../../src/utils/Time'; import { TimeProps, Time } from '../../src/utils/Time';
import { parseDate } from '../../src/utils/helpers/date'; import { parseDate } from '../../src/utils/helpers/date';
describe('<Time />', () => { describe('<Time />', () => {
let wrapper: ShallowWrapper; const setUp = (props: TimeProps) => render(<Time {...props} />);
const createWrapper = (props: DateProps) => {
wrapper = shallow(<Time {...props} />);
return wrapper;
};
afterEach(() => wrapper?.unmount());
it.each([ it.each([
[{ date: parseDate('2020-05-05', 'yyyy-MM-dd') }, '1588636800000', '2020-05-05 00:00'], [{ date: parseDate('2020-05-05', 'yyyy-MM-dd') }, '1588636800000', '2020-05-05 00:00'],
[{ date: parseDate('2021-03-20', 'yyyy-MM-dd'), format: 'dd/MM/yyyy' }, '1616198400000', '20/03/2021'], [{ date: parseDate('2021-03-20', 'yyyy-MM-dd'), format: 'dd/MM/yyyy' }, '1616198400000', '20/03/2021'],
])('includes expected dateTime and format', (props, expectedDateTime, expectedFormatted) => { ])('includes expected dateTime and format', (props, expectedDateTime, expectedFormatted) => {
const wrapper = createWrapper(props); const { container } = setUp(props);
expect(wrapper.prop('dateTime')).toEqual(expectedDateTime); expect(container.firstChild).toHaveAttribute('datetime', expectedDateTime);
expect(wrapper.prop('children')).toEqual(expectedFormatted); expect(container.firstChild).toHaveTextContent(expectedFormatted);
}); });
it('renders relative times when requested', () => { it('renders relative times when requested', () => {
const wrapper = createWrapper({ date: new Date(), relative: true }); const { container } = setUp({ date: new Date(), relative: true });
expect(container.firstChild).toHaveTextContent(' ago');
expect(wrapper.prop('children')).toContain(' ago');
}); });
}); });