Merge pull request #640 from acelaya-forks/feature/more-rtl-tests

Feature/more rtl tests
This commit is contained in:
Alejandro Celaya 2022-05-09 19:53:46 +02:00 committed by GitHub
commit 686fe5abbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 870 additions and 383 deletions

View file

@ -3,3 +3,5 @@ import 'jest-canvas-mock';
import ResizeObserver from 'resize-observer-polyfill';
(global as any).ResizeObserver = ResizeObserver;
(global as any).scrollTo = () => {};
(global as any).matchMedia = (media: string) => ({ matches: false, media });

View file

@ -9,10 +9,10 @@ module.exports = {
],
coverageThreshold: {
global: {
statements: 85,
statements: 90,
branches: 80,
functions: 80,
lines: 85,
functions: 85,
lines: 90,
},
},
setupFiles: ['<rootDir>/config/jest/setupBeforeEnzyme.js', '<rootDir>/config/jest/setupEnzyme.js'],

View file

@ -18,7 +18,7 @@
"build:serve": "serve -p 5000 ./build",
"test": "jest --env=jsdom --colors --verbose",
"test:coverage": "npm run test -- --coverage --coverageReporters=text --coverageReporters=text-summary",
"test:ci": "npm run test:coverage -- --coverageReporters=clover",
"test:ci": "npm run test:coverage -- --coverageReporters=clover --ci",
"test:pretty": "npm run test:coverage -- --coverageReporters=html",
"mutate": "./node_modules/.bin/stryker run --concurrency 4 --ignoreStatic"
},

View file

@ -1,7 +1,7 @@
import { FC, PropsWithChildren, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
const ScrollToTop = (): FC<PropsWithChildren<unknown>> => ({ children }) => {
export const ScrollToTop: FC<PropsWithChildren<unknown>> = ({ children }) => {
const location = useLocation();
useEffect(() => {
@ -10,5 +10,3 @@ const ScrollToTop = (): FC<PropsWithChildren<unknown>> => ({ children }) => {
return <>{children}</>;
};
export default ScrollToTop;

View file

@ -17,7 +17,9 @@ interface SimplePaginatorProps {
centered?: boolean;
}
const SimplePaginator: FC<SimplePaginatorProps> = ({ pagesCount, currentPage, setCurrentPage, centered = true }) => {
export const SimplePaginator: FC<SimplePaginatorProps> = (
{ pagesCount, currentPage, setCurrentPage, centered = true },
) => {
if (pagesCount < 2) {
return null;
}
@ -35,7 +37,9 @@ const SimplePaginator: FC<SimplePaginatorProps> = ({ pagesCount, currentPage, se
disabled={pageIsEllipsis(pageNumber)}
active={currentPage === pageNumber}
>
<PaginationLink tag="span" onClick={onClick(pageNumber)}>{prettifyPageNumber(pageNumber)}</PaginationLink>
<PaginationLink role="link" tag="span" onClick={onClick(pageNumber)}>
{prettifyPageNumber(pageNumber)}
</PaginationLink>
</PaginationItem>
))}
<PaginationItem disabled={currentPage >= pagesCount}>
@ -44,5 +48,3 @@ const SimplePaginator: FC<SimplePaginatorProps> = ({ pagesCount, currentPage, se
</Pagination>
);
};
export default SimplePaginator;

View file

@ -1,6 +1,6 @@
import axios from 'axios';
import Bottle from 'bottlejs';
import ScrollToTop from '../ScrollToTop';
import { ScrollToTop } from '../ScrollToTop';
import { MainHeader } from '../MainHeader';
import { Home } from '../Home';
import { MenuLayout } from '../MenuLayout';
@ -23,7 +23,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.service('ReportExporter', ReportExporter, 'window', 'jsonToCsv');
// Components
bottle.serviceFactory('ScrollToTop', ScrollToTop);
bottle.serviceFactory('ScrollToTop', () => ScrollToTop);
bottle.serviceFactory('MainHeader', MainHeader, 'ServersDropdown');

View file

@ -40,6 +40,7 @@ export const DomainSelector = ({ listDomains, value, domainsList, onChange }: Do
outline
type="button"
className="domains-dropdown__back-btn"
aria-label="Back to domains list"
onClick={pipe(unselectDomain, hideInput)}
>
<FontAwesomeIcon icon={faUndo} />

View file

@ -2,7 +2,7 @@ import { FC, useEffect, useRef } from 'react';
import { splitEvery } from 'ramda';
import { useLocation } from 'react-router-dom';
import { SimpleCard } from '../utils/SimpleCard';
import SimplePaginator from '../common/SimplePaginator';
import { SimplePaginator } from '../common/SimplePaginator';
import { useQueryState } from '../utils/helpers/hooks';
import { parseQuery } from '../utils/helpers/query';
import { TableOrderIcon } from '../utils/table/TableOrderIcon';

View file

@ -4,7 +4,7 @@ import { min, splitEvery } from 'ramda';
import { faCheck as checkIcon, faRobot as botIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { UncontrolledTooltip } from 'reactstrap';
import SimplePaginator from '../common/SimplePaginator';
import { SimplePaginator } from '../common/SimplePaginator';
import SearchField from '../utils/SearchField';
import { determineOrderDir, Order, sortList } from '../utils/helpers/ordering';
import { prettify } from '../utils/helpers/numbers';

View file

@ -2,7 +2,7 @@ import { FC, useState } from 'react';
import { fromPairs, pipe, reverse, sortBy, splitEvery, toLower, toPairs, type, zipObj } from 'ramda';
import { rangeOf } from '../../utils/utils';
import { Order } from '../../utils/helpers/ordering';
import SimplePaginator from '../../common/SimplePaginator';
import { SimplePaginator } from '../../common/SimplePaginator';
import { roundTen } from '../../utils/helpers/numbers';
import { OrderingDropdown } from '../../utils/OrderingDropdown';
import PaginationDropdown from '../../utils/PaginationDropdown';

View file

@ -1,47 +1,32 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { Link } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { NotFound } from '../../src/common/NotFound';
import { SimpleCard } from '../../src/utils/SimpleCard';
describe('<NotFound />', () => {
let wrapper: ShallowWrapper;
const createWrapper = (props = {}) => {
wrapper = shallow(<NotFound {...props} />).find(SimpleCard);
return wrapper;
};
afterEach(() => wrapper?.unmount());
const setUp = (props = {}) => render(<MemoryRouter><NotFound {...props} /></MemoryRouter>);
it('shows expected error title', () => {
const wrapper = createWrapper();
expect(wrapper.contains('Oops! We could not find requested route.')).toEqual(true);
setUp();
expect(screen.getByText('Oops! We could not find requested route.')).toBeInTheDocument();
});
it('shows expected error message', () => {
const wrapper = createWrapper();
expect(wrapper.contains(
setUp();
expect(screen.getByText(
'Use your browser\'s back button to navigate to the page you have previously come from, or just press this button.',
)).toEqual(true);
)).toBeInTheDocument();
});
it('shows a link to the home', () => {
const wrapper = createWrapper();
const link = wrapper.find(Link);
it.each([
[{}, '/', 'Home'],
[{ to: '/foo/bar', children: 'Hello' }, '/foo/bar', 'Hello'],
[{ to: '/baz-bar', children: <>Foo</> }, '/baz-bar', 'Foo'],
])('shows expected link and text', (props, expectedLink, expectedText) => {
setUp(props);
const link = screen.getByRole('link');
expect(link.prop('to')).toEqual('/');
expect(link.prop('className')).toEqual('btn btn-outline-primary btn-lg');
expect(link.prop('children')).toEqual('Home');
});
it('shows a link with provided props', () => {
const wrapper = createWrapper({ to: '/foo/bar', children: 'Hello' });
const link = wrapper.find(Link);
expect(link.prop('to')).toEqual('/foo/bar');
expect(link.prop('className')).toEqual('btn btn-outline-primary btn-lg');
expect(link.prop('children')).toEqual('Hello');
expect(link).toHaveAttribute('href', expectedLink);
expect(link).toHaveTextContent(expectedText);
expect(link).toHaveAttribute('class', 'btn btn-outline-primary btn-lg');
});
});

View file

@ -1,21 +1,14 @@
import { shallow, ShallowWrapper } from 'enzyme';
import createScrollToTop from '../../src/common/ScrollToTop';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: jest.fn().mockReturnValue({}),
}));
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { ScrollToTop } from '../../src/common/ScrollToTop';
describe('<ScrollToTop />', () => {
let wrapper: ShallowWrapper;
beforeEach(() => {
const ScrollToTop = createScrollToTop();
wrapper = shallow(<ScrollToTop>Foobar</ScrollToTop>);
it.each([
['Foobar'],
['Barfoo'],
['Something'],
])('just renders children', (children) => {
render(<MemoryRouter><ScrollToTop>{children}</ScrollToTop></MemoryRouter>);
expect(screen.getByText(children)).toBeInTheDocument();
});
afterEach(() => wrapper.unmount());
it('just renders children', () => expect(wrapper.text()).toEqual('Foobar'));
});

View file

@ -1,17 +1,10 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import ShlinkVersions, { ShlinkVersionsProps } from '../../src/common/ShlinkVersions';
import { NonReachableServer, NotFoundServer, ReachableServer } from '../../src/servers/data';
describe('<ShlinkVersions />', () => {
let wrapper: ShallowWrapper;
const createWrapper = (props: ShlinkVersionsProps) => {
wrapper = shallow(<ShlinkVersions {...props} />);
return wrapper;
};
afterEach(() => wrapper?.unmount());
const setUp = (props: ShlinkVersionsProps) => render(<ShlinkVersions {...props} />);
it.each([
['1.2.3', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: 'foo' }), 'v1.2.3', 'foo'],
@ -22,15 +15,19 @@ describe('<ShlinkVersions />', () => {
])(
'displays expected versions when selected server is reachable',
(clientVersion, selectedServer, expectedClientVersion, expectedServerVersion) => {
const wrapper = createWrapper({ clientVersion, selectedServer });
const links = wrapper.find('VersionLink');
const serverLink = links.at(0);
const clientLink = links.at(1);
setUp({ clientVersion, selectedServer });
const [serverLink, clientLink] = screen.getAllByRole('link');
expect(serverLink.prop('project')).toEqual('shlink');
expect(serverLink.prop('version')).toEqual(expectedServerVersion);
expect(clientLink.prop('project')).toEqual('shlink-web-client');
expect(clientLink.prop('version')).toEqual(expectedClientVersion);
expect(serverLink).toHaveAttribute(
'href',
`https://github.com/shlinkio/shlink/releases/${expectedServerVersion}`,
);
expect(serverLink).toHaveTextContent(expectedServerVersion);
expect(clientLink).toHaveAttribute(
'href',
`https://github.com/shlinkio/shlink-web-client/releases/${expectedClientVersion}`,
);
expect(clientLink).toHaveTextContent(expectedClientVersion);
},
);
@ -39,10 +36,10 @@ describe('<ShlinkVersions />', () => {
['1.2.3', Mock.of<NotFoundServer>({ serverNotFound: true })],
['1.2.3', Mock.of<NonReachableServer>({ serverNotReachable: true })],
])('displays only client version when selected server is not reachable', (clientVersion, selectedServer) => {
const wrapper = createWrapper({ clientVersion, selectedServer });
const links = wrapper.find('VersionLink');
setUp({ clientVersion, selectedServer });
const links = screen.getAllByRole('link');
expect(links).toHaveLength(1);
expect(links.at(0).prop('project')).toEqual('shlink-web-client');
expect(links[0]).toHaveAttribute('href', 'https://github.com/shlinkio/shlink-web-client/releases/v1.2.3');
});
});

View file

@ -1,26 +1,19 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { render } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import ShlinkVersionsContainer from '../../src/common/ShlinkVersionsContainer';
import { SelectedServer } from '../../src/servers/data';
import { Sidebar } from '../../src/common/reducers/sidebar';
describe('<ShlinkVersionsContainer />', () => {
let wrapper: ShallowWrapper;
const createWrapper = (sidebar: Sidebar) => {
wrapper = shallow(<ShlinkVersionsContainer selectedServer={Mock.all<SelectedServer>()} sidebar={sidebar} />);
return wrapper;
};
afterEach(() => wrapper?.unmount());
const setUp = (sidebar: Sidebar) => render(
<ShlinkVersionsContainer selectedServer={Mock.all<SelectedServer>()} sidebar={sidebar} />,
);
it.each([
[{ sidebarPresent: false }, 'text-center'],
[{ sidebarPresent: true }, 'text-center shlink-versions-container--with-sidebar'],
])('renders proper col classes based on sidebar status', (sidebar, expectedClasses) => {
const wrapper = createWrapper(sidebar);
expect(wrapper.find('div').prop('className')).toEqual(`${expectedClasses}`);
const { container } = setUp(sidebar);
expect(container.firstChild).toHaveAttribute('class', `${expectedClasses}`);
});
});

View file

@ -1,28 +1,23 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { identity } from 'ramda';
import { PaginationItem } from 'reactstrap';
import SimplePaginator from '../../src/common/SimplePaginator';
import { render, screen } from '@testing-library/react';
import { SimplePaginator } from '../../src/common/SimplePaginator';
import { ELLIPSIS } from '../../src/utils/helpers/pagination';
describe('<SimplePaginator />', () => {
let wrapper: ShallowWrapper;
const createWrapper = (pagesCount: number, currentPage = 1) => {
wrapper = shallow(<SimplePaginator pagesCount={pagesCount} currentPage={currentPage} setCurrentPage={identity} />);
return wrapper;
};
afterEach(() => wrapper?.unmount());
const setUp = (pagesCount: number, currentPage = 1) => render(
<SimplePaginator pagesCount={pagesCount} currentPage={currentPage} setCurrentPage={jest.fn()} />,
);
it.each([-3, -2, 0, 1])('renders empty when the amount of pages is smaller than 2', (pagesCount) => {
expect(createWrapper(pagesCount).text()).toEqual('');
const { container } = setUp(pagesCount);
expect(container.firstChild).toEqual(null);
});
describe('ELLIPSIS are rendered where expected', () => {
const getItemsForPages = (pagesCount: number, currentPage: number) => {
const paginator = createWrapper(pagesCount, currentPage);
const items = paginator.find(PaginationItem);
const itemsWithEllipsis = items.filterWhere((item) => item?.key()?.includes(ELLIPSIS));
setUp(pagesCount, currentPage);
const items = screen.getAllByRole('link');
const itemsWithEllipsis = items.filter((item) => item.innerHTML.includes(ELLIPSIS));
return { items, itemsWithEllipsis };
};
@ -30,22 +25,22 @@ describe('<SimplePaginator />', () => {
it('renders first ELLIPSIS', () => {
const { items, itemsWithEllipsis } = getItemsForPages(9, 7);
expect(items.at(2).html()).toContain(ELLIPSIS);
expect(items[1]).toHaveTextContent(ELLIPSIS);
expect(itemsWithEllipsis).toHaveLength(1);
});
it('renders last ELLIPSIS', () => {
const { items, itemsWithEllipsis } = getItemsForPages(9, 2);
expect(items.at(items.length - 3).html()).toContain(ELLIPSIS);
expect(items[items.length - 2]).toHaveTextContent(ELLIPSIS);
expect(itemsWithEllipsis).toHaveLength(1);
});
it('renders both ELLIPSIS', () => {
const { items, itemsWithEllipsis } = getItemsForPages(20, 9);
expect(items.at(2).html()).toContain(ELLIPSIS);
expect(items.at(items.length - 3).html()).toContain(ELLIPSIS);
expect(items[1]).toHaveTextContent(ELLIPSIS);
expect(items[items.length - 2]).toHaveTextContent(ELLIPSIS);
expect(itemsWithEllipsis).toHaveLength(2);
});
});

View file

@ -1,25 +1,17 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { render } from '@testing-library/react';
import { ShlinkLogo, ShlinkLogoProps } from '../../../src/common/img/ShlinkLogo';
import { MAIN_COLOR } from '../../../src/utils/theme';
describe('<ShlinkLogo />', () => {
let wrapper: ShallowWrapper;
const createWrapper = (props: ShlinkLogoProps) => {
wrapper = shallow(<ShlinkLogo {...props} />);
return wrapper;
};
afterEach(() => wrapper?.unmount());
const setUp = (props: ShlinkLogoProps) => render(<ShlinkLogo {...props} />);
it.each([
[undefined, MAIN_COLOR],
['red', 'red'],
['white', 'white'],
])('renders expected color', (color, expectedColor) => {
const wrapper = createWrapper({ color });
expect(wrapper.find('g').prop('fill')).toEqual(expectedColor);
const { container } = setUp({ color });
expect(container.querySelector('g')).toHaveAttribute('fill', expectedColor);
});
it.each([
@ -27,8 +19,12 @@ describe('<ShlinkLogo />', () => {
['foo', 'foo'],
['bar', 'bar'],
])('renders expected class', (className, expectedClassName) => {
const wrapper = createWrapper({ className });
const { container } = setUp({ className });
expect(wrapper.prop('className')).toEqual(expectedClassName);
if (expectedClassName) {
expect(container.firstChild).toHaveAttribute('class', expectedClassName);
} else {
expect(container.firstChild).not.toHaveAttribute('class');
}
});
});

View file

@ -1,4 +1,4 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { ShlinkDomainRedirects } from '../../src/api/types';
import { DomainRow } from '../../src/domains/DomainRow';
@ -6,42 +6,71 @@ import { SelectedServer } from '../../src/servers/data';
import { Domain } from '../../src/domains/data';
describe('<DomainRow />', () => {
let wrapper: ShallowWrapper;
const createWrapper = (domain: Domain, selectedServer = Mock.all<SelectedServer>()) => {
wrapper = shallow(
<DomainRow
domain={domain}
selectedServer={selectedServer}
editDomainRedirects={jest.fn()}
checkDomainHealth={jest.fn()}
/>,
);
return wrapper;
};
afterEach(() => wrapper?.unmount());
it.each([
[undefined, 3],
[Mock.of<ShlinkDomainRedirects>(), 3],
[Mock.of<ShlinkDomainRedirects>({ baseUrlRedirect: 'foo' }), 2],
[Mock.of<ShlinkDomainRedirects>({ invalidShortUrlRedirect: 'foo' }), 2],
[Mock.of<ShlinkDomainRedirects>({ baseUrlRedirect: 'foo', regular404Redirect: 'foo' }), 1],
const redirectsCombinations = [
[Mock.of<ShlinkDomainRedirects>({ baseUrlRedirect: 'foo' })],
[Mock.of<ShlinkDomainRedirects>({ invalidShortUrlRedirect: 'bar' })],
[Mock.of<ShlinkDomainRedirects>({ baseUrlRedirect: 'baz', regular404Redirect: 'foo' })],
[
Mock.of<ShlinkDomainRedirects>(
{ baseUrlRedirect: 'foo', regular404Redirect: 'foo', invalidShortUrlRedirect: 'foo' },
{ baseUrlRedirect: 'baz', regular404Redirect: 'bar', invalidShortUrlRedirect: 'foo' },
),
0,
],
])('shows expected redirects', (redirects, expectedNoRedirects) => {
const wrapper = createWrapper(Mock.of<Domain>({ domain: '', isDefault: true, redirects }));
const noRedirects = wrapper.find('Nr');
const cells = wrapper.find('td');
];
const setUp = (domain: Domain, defaultRedirects?: ShlinkDomainRedirects) => render(
<DomainRow
domain={domain}
defaultRedirects={defaultRedirects}
selectedServer={Mock.all<SelectedServer>()}
editDomainRedirects={jest.fn()}
checkDomainHealth={jest.fn()}
/>,
);
expect(noRedirects).toHaveLength(expectedNoRedirects);
redirects?.baseUrlRedirect && expect(cells.at(1).html()).toContain(redirects.baseUrlRedirect);
redirects?.regular404Redirect && expect(cells.at(2).html()).toContain(redirects.regular404Redirect);
redirects?.invalidShortUrlRedirect && expect(cells.at(3).html()).toContain(redirects.invalidShortUrlRedirect);
it.each(redirectsCombinations)('shows expected redirects', (redirects) => {
setUp(Mock.of<Domain>({ domain: '', isDefault: true, redirects }));
const cells = screen.getAllByRole('cell');
redirects?.baseUrlRedirect && expect(cells[1]).toHaveTextContent(redirects.baseUrlRedirect);
redirects?.regular404Redirect && expect(cells[2]).toHaveTextContent(redirects.regular404Redirect);
redirects?.invalidShortUrlRedirect && expect(cells[3]).toHaveTextContent(redirects.invalidShortUrlRedirect);
expect(screen.queryByText('(as fallback)')).not.toBeInTheDocument();
});
it.each([
[undefined],
[Mock.of<ShlinkDomainRedirects>()],
])('shows expected "no redirects"', (redirects) => {
setUp(Mock.of<Domain>({ domain: '', isDefault: true, redirects }));
const cells = screen.getAllByRole('cell');
expect(cells[1]).toHaveTextContent('No redirect');
expect(cells[2]).toHaveTextContent('No redirect');
expect(cells[3]).toHaveTextContent('No redirect');
expect(screen.queryByText('(as fallback)')).not.toBeInTheDocument();
});
it.each(redirectsCombinations)('shows expected fallback redirects', (fallbackRedirects) => {
setUp(Mock.of<Domain>({ domain: '', isDefault: true }), fallbackRedirects);
const cells = screen.getAllByRole('cell');
fallbackRedirects?.baseUrlRedirect && expect(cells[1]).toHaveTextContent(
`${fallbackRedirects.baseUrlRedirect} (as fallback)`,
);
fallbackRedirects?.regular404Redirect && expect(cells[2]).toHaveTextContent(
`${fallbackRedirects.regular404Redirect} (as fallback)`,
);
fallbackRedirects?.invalidShortUrlRedirect && expect(cells[3]).toHaveTextContent(
`${fallbackRedirects.invalidShortUrlRedirect} (as fallback)`,
);
});
it.each([[true], [false]])('shows icon on default domain only', (isDefault) => {
const { container } = setUp(Mock.of<Domain>({ domain: '', isDefault }));
if (isDefault) {
expect(container.querySelector('#defaultDomainIcon')).toBeInTheDocument();
} else {
expect(container.querySelector('#defaultDomainIcon')).not.toBeInTheDocument();
}
});
});

View file

@ -1,13 +1,10 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { DropdownItem, InputGroup } from 'reactstrap';
import { DomainSelector } from '../../src/domains/DomainSelector';
import { DomainsList } from '../../src/domains/reducers/domainsList';
import { ShlinkDomain } from '../../src/api/types';
import { DropdownBtn } from '../../src/utils/DropdownBtn';
describe('<DomainSelector />', () => {
let wrapper: ShallowWrapper;
const domainsList = Mock.of<DomainsList>({
domains: [
Mock.of<ShlinkDomain>({ domain: 'default.com', isDefault: true }),
@ -15,51 +12,59 @@ describe('<DomainSelector />', () => {
Mock.of<ShlinkDomain>({ domain: 'bar.com' }),
],
});
const createWrapper = (value = '') => {
wrapper = shallow(
<DomainSelector value={value} domainsList={domainsList} listDomains={jest.fn()} onChange={jest.fn()} />,
);
return wrapper;
};
const setUp = (value = '') => render(
<DomainSelector value={value} domainsList={domainsList} listDomains={jest.fn()} onChange={jest.fn()} />,
);
afterEach(jest.clearAllMocks);
afterEach(() => wrapper.unmount());
it.each([
['', 'Domain', 'domains-dropdown__toggle-btn'],
['my-domain.com', 'Domain: my-domain.com', 'domains-dropdown__toggle-btn--active'],
])('shows dropdown by default', (value, expectedText, expectedClassName) => {
const wrapper = createWrapper(value);
const input = wrapper.find(InputGroup);
const dropdown = wrapper.find(DropdownBtn);
])('shows dropdown by default', async (value, expectedText, expectedClassName) => {
setUp(value);
expect(input).toHaveLength(0);
expect(dropdown).toHaveLength(1);
expect(dropdown.find(DropdownItem)).toHaveLength(5);
expect(dropdown.prop('text')).toEqual(expectedText);
expect(dropdown.prop('className')).toEqual(expectedClassName);
const btn = screen.getByRole('button', { name: expectedText });
expect(screen.queryByPlaceholderText('Domain')).not.toBeInTheDocument();
expect(btn).toHaveAttribute(
'class',
`dropdown-btn__toggle btn-block ${expectedClassName} dropdown-toggle btn btn-primary`,
);
fireEvent.click(btn);
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument());
expect(screen.getAllByRole('menuitem')).toHaveLength(4);
});
it('allows toggling between dropdown and input', () => {
const wrapper = createWrapper();
it('allows toggling between dropdown and input', async () => {
setUp();
wrapper.find(DropdownItem).last().simulate('click');
expect(wrapper.find(InputGroup)).toHaveLength(1);
expect(wrapper.find(DropdownBtn)).toHaveLength(0);
expect(screen.queryByPlaceholderText('Domain')).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Domain' })).toBeInTheDocument();
wrapper.find('.domains-dropdown__back-btn').simulate('click');
expect(wrapper.find(InputGroup)).toHaveLength(0);
expect(wrapper.find(DropdownBtn)).toHaveLength(1);
fireEvent.click(screen.getByRole('button', { name: 'Domain' }));
fireEvent.click(await screen.findByText('New domain'));
expect(screen.getByPlaceholderText('Domain')).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Domain' })).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Back to domains list' }));
expect(screen.queryByPlaceholderText('Domain')).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Domain' })).toBeInTheDocument();
});
it.each([
[0, 'default.com<span class="float-end text-muted">default</span>'],
[0, 'default.comdefault'],
[1, 'foo.com'],
[2, 'bar.com'],
])('shows expected content on every item', (index, expectedContent) => {
const item = createWrapper().find(DropdownItem).at(index);
])('shows expected content on every item', async (index, expectedContent) => {
setUp();
expect(item.html()).toContain(expectedContent);
fireEvent.click(screen.getByRole('button', { name: 'Domain' }));
const items = await screen.findAllByRole('menuitem');
expect(items[index]).toHaveTextContent(expectedContent);
});
});

View file

@ -1,108 +1,70 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { DomainsList } from '../../src/domains/reducers/domainsList';
import { ManageDomains } from '../../src/domains/ManageDomains';
import Message from '../../src/utils/Message';
import { Result } from '../../src/utils/Result';
import SearchField from '../../src/utils/SearchField';
import { ProblemDetailsError, ShlinkDomain } from '../../src/api/types';
import { ShlinkApiError } from '../../src/api/ShlinkApiError';
import { DomainRow } from '../../src/domains/DomainRow';
import { SelectedServer } from '../../src/servers/data';
describe('<ManageDomains />', () => {
const listDomains = jest.fn();
const filterDomains = jest.fn();
let wrapper: ShallowWrapper;
const createWrapper = (domainsList: DomainsList) => {
wrapper = shallow(
<ManageDomains
listDomains={listDomains}
filterDomains={filterDomains}
editDomainRedirects={jest.fn()}
checkDomainHealth={jest.fn()}
domainsList={domainsList}
selectedServer={Mock.all<SelectedServer>()}
/>,
);
return wrapper;
};
const setUp = (domainsList: DomainsList) => render(
<ManageDomains
listDomains={listDomains}
filterDomains={filterDomains}
editDomainRedirects={jest.fn()}
checkDomainHealth={jest.fn()}
domainsList={domainsList}
selectedServer={Mock.all<SelectedServer>()}
/>,
);
afterEach(jest.clearAllMocks);
afterEach(() => wrapper?.unmount());
it('shows loading message while domains are loading', () => {
const wrapper = createWrapper(Mock.of<DomainsList>({ loading: true, filteredDomains: [] }));
const message = wrapper.find(Message);
const searchField = wrapper.find(SearchField);
const result = wrapper.find(Result);
const apiError = wrapper.find(ShlinkApiError);
setUp(Mock.of<DomainsList>({ loading: true, filteredDomains: [] }));
expect(message).toHaveLength(1);
expect(message.prop('loading')).toEqual(true);
expect(searchField).toHaveLength(0);
expect(result).toHaveLength(0);
expect(apiError).toHaveLength(0);
expect(screen.getByText('Loading...')).toBeInTheDocument();
expect(screen.queryByText('Error loading domains :(')).not.toBeInTheDocument();
});
it('shows error result when domains loading fails', () => {
const errorData = Mock.of<ProblemDetailsError>();
const wrapper = createWrapper(Mock.of<DomainsList>(
{ loading: false, error: true, errorData, filteredDomains: [] },
));
const message = wrapper.find(Message);
const searchField = wrapper.find(SearchField);
const result = wrapper.find(Result);
const apiError = wrapper.find(ShlinkApiError);
it.each([
[undefined, 'Error loading domains :('],
[Mock.of<ProblemDetailsError>(), 'Error loading domains :('],
[Mock.of<ProblemDetailsError>({ detail: 'Foo error!!' }), 'Foo error!!'],
])('shows error result when domains loading fails', (errorData, expectedErrorMessage) => {
setUp(Mock.of<DomainsList>({ loading: false, error: true, errorData, filteredDomains: [] }));
expect(result).toHaveLength(1);
expect(result.prop('type')).toEqual('error');
expect(apiError).toHaveLength(1);
expect(apiError.prop('errorData')).toEqual(errorData);
expect(searchField).toHaveLength(1);
expect(message).toHaveLength(0);
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
expect(screen.getByText(expectedErrorMessage)).toBeInTheDocument();
});
it('filters domains when SearchField changes', () => {
const wrapper = createWrapper(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
const searchField = wrapper.find(SearchField);
it('filters domains when SearchField changes', async () => {
setUp(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
expect(filterDomains).not.toHaveBeenCalled();
searchField.simulate('change');
expect(filterDomains).toHaveBeenCalledTimes(1);
fireEvent.change(screen.getByPlaceholderText('Search...'), { target: { value: 'Foo' } });
await waitFor(() => expect(filterDomains).toHaveBeenCalledTimes(1));
});
it('shows expected headers', () => {
const wrapper = createWrapper(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
const headerCells = wrapper.find('th');
it('shows expected headers and one row when list of domains is empty', () => {
setUp(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
expect(headerCells).toHaveLength(7);
expect(screen.getAllByRole('columnheader')).toHaveLength(7);
expect(screen.getByText('No results found')).toBeInTheDocument();
});
it('one row when list of domains is empty', () => {
const wrapper = createWrapper(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
const tableBody = wrapper.find('tbody');
const regularRows = tableBody.find('tr');
const domainRows = tableBody.find(DomainRow);
expect(regularRows).toHaveLength(1);
expect(regularRows.html()).toContain('No results found');
expect(domainRows).toHaveLength(0);
});
it('as many DomainRows as domains are provided', () => {
it('has many rows if multiple domains are provided', () => {
const filteredDomains = [
Mock.of<ShlinkDomain>({ domain: 'foo' }),
Mock.of<ShlinkDomain>({ domain: 'bar' }),
Mock.of<ShlinkDomain>({ domain: 'baz' }),
];
const wrapper = createWrapper(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains }));
const tableBody = wrapper.find('tbody');
const regularRows = tableBody.find('tr');
const domainRows = tableBody.find(DomainRow);
setUp(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains }));
expect(regularRows).toHaveLength(0);
expect(domainRows).toHaveLength(filteredDomains.length);
expect(screen.getAllByRole('row')).toHaveLength(filteredDomains.length + 1);
expect(screen.getByText('foo')).toBeInTheDocument();
expect(screen.getByText('bar')).toBeInTheDocument();
expect(screen.getByText('baz')).toBeInTheDocument();
});
});

View file

@ -1,73 +1,30 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { UncontrolledTooltip } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { fireEvent, render, screen } from '@testing-library/react';
import { Mock } from 'ts-mockery';
import { faTimes, faCheck, faCircleNotch } from '@fortawesome/free-solid-svg-icons';
import { DomainStatus } from '../../../src/domains/data';
import { DomainStatusIcon } from '../../../src/domains/helpers/DomainStatusIcon';
describe('<DomainStatusIcon />', () => {
const matchMedia = jest.fn().mockReturnValue(Mock.of<MediaQueryList>({ matches: false }));
let wrapper: ShallowWrapper;
const createWrapper = (status: DomainStatus) => {
wrapper = shallow(<DomainStatusIcon status={status} matchMedia={matchMedia} />);
return wrapper;
};
const setUp = (status: DomainStatus) => render(<DomainStatusIcon status={status} matchMedia={matchMedia} />);
beforeEach(jest.clearAllMocks);
afterEach(() => wrapper?.unmount());
it('renders loading icon when status is "validating"', () => {
const wrapper = createWrapper('validating');
const tooltip = wrapper.find(UncontrolledTooltip);
const faIcon = wrapper.find(FontAwesomeIcon);
expect(tooltip).toHaveLength(0);
expect(faIcon).toHaveLength(1);
expect(faIcon.prop('icon')).toEqual(faCircleNotch);
expect(faIcon.prop('spin')).toEqual(true);
it.each([
['validating' as DomainStatus],
['invalid' as DomainStatus],
['valid' as DomainStatus],
])('renders expected icon and tooltip when status is not validating', (status) => {
const { container } = setUp(status);
expect(container.firstChild).toMatchSnapshot();
});
it.each([
[
'invalid' as DomainStatus,
faTimes,
'Oops! There is some missing configuration, and short URLs shared with this domain will not work.',
],
['valid' as DomainStatus, faCheck, 'Congratulations! This domain is properly configured.'],
])('renders expected icon and tooltip when status is not validating', (status, expectedIcon, expectedText) => {
const wrapper = createWrapper(status);
const tooltip = wrapper.find(UncontrolledTooltip);
const faIcon = wrapper.find(FontAwesomeIcon);
const getTooltipText = (): string => {
const children = tooltip.prop('children');
['invalid' as DomainStatus],
['valid' as DomainStatus],
])('renders proper tooltip based on state', async (status) => {
const { container } = setUp(status);
if (typeof children === 'string') {
return children;
}
return tooltip.find('span').html();
};
expect(tooltip).toHaveLength(1);
expect(tooltip.prop('autohide')).toEqual(status === 'valid');
expect(getTooltipText()).toContain(expectedText);
expect(faIcon).toHaveLength(1);
expect(faIcon.prop('icon')).toEqual(expectedIcon);
expect(faIcon.prop('spin')).toEqual(false);
});
it.each([
[true, 'top-start'],
[false, 'left'],
])('places the tooltip properly based on query match', (isMobile, expectedPlacement) => {
matchMedia.mockReturnValue(Mock.of<MediaQueryList>({ matches: isMobile }));
const wrapper = createWrapper('valid');
const tooltip = wrapper.find(UncontrolledTooltip);
expect(tooltip).toHaveLength(1);
expect(tooltip.prop('placement')).toEqual(expectedPlacement);
container.firstChild && fireEvent.mouseOver(container.firstChild);
expect(await screen.findByRole('tooltip')).toMatchSnapshot();
});
});

View file

@ -0,0 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<DomainStatusIcon /> renders expected icon and tooltip when status is not validating 1`] = `
<svg
aria-hidden="true"
class="svg-inline--fa fa-circle-notch fa-spin fa-fw "
data-icon="circle-notch"
data-prefix="fas"
focusable="false"
role="img"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M222.7 32.15C227.7 49.08 218.1 66.9 201.1 71.94C121.8 95.55 64 169.1 64 255.1C64 362 149.1 447.1 256 447.1C362 447.1 448 362 448 255.1C448 169.1 390.2 95.55 310.9 71.94C293.9 66.9 284.3 49.08 289.3 32.15C294.4 15.21 312.2 5.562 329.1 10.6C434.9 42.07 512 139.1 512 255.1C512 397.4 397.4 511.1 256 511.1C114.6 511.1 0 397.4 0 255.1C0 139.1 77.15 42.07 182.9 10.6C199.8 5.562 217.6 15.21 222.7 32.15V32.15z"
fill="currentColor"
/>
</svg>
`;
exports[`<DomainStatusIcon /> renders expected icon and tooltip when status is not validating 2`] = `
<span>
<svg
aria-hidden="true"
class="svg-inline--fa fa-xmark fa-fw text-danger"
data-icon="xmark"
data-prefix="fas"
focusable="false"
role="img"
viewBox="0 0 320 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"
fill="currentColor"
/>
</svg>
</span>
`;
exports[`<DomainStatusIcon /> renders expected icon and tooltip when status is not validating 3`] = `
<span>
<svg
aria-hidden="true"
class="svg-inline--fa fa-check fa-fw text-muted"
data-icon="check"
data-prefix="fas"
focusable="false"
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>
</span>
`;
exports[`<DomainStatusIcon /> renders proper tooltip based on state 1`] = `
<div
class="tooltip-inner"
role="tooltip"
>
<span>
Oops! There is some missing configuration, and short URLs shared with this domain will not work.
<br />
Check the
<a
href="https://slnk.to/multi-domain-docs"
rel="noopener noreferrer"
target="_blank"
>
documentation
</a>
in order to find out what is missing.
</span>
</div>
`;
exports[`<DomainStatusIcon /> renders proper tooltip based on state 2`] = `
<div
class="tooltip-inner"
role="tooltip"
>
Congratulations! This domain is properly configured.
</div>
`;

View file

@ -4,7 +4,7 @@ import { useLocation } from 'react-router-dom';
import { TagsTable as createTagsTable } from '../../src/tags/TagsTable';
import { SelectedServer } from '../../src/servers/data';
import { rangeOf } from '../../src/utils/utils';
import SimplePaginator from '../../src/common/SimplePaginator';
import { SimplePaginator } from '../../src/common/SimplePaginator';
import { NormalizedTag } from '../../src/tags/data';
jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useLocation: jest.fn() }));

View file

@ -2,7 +2,7 @@ import { shallow, ShallowWrapper } from 'enzyme';
import { Mock } from 'ts-mockery';
import VisitsTable, { VisitsTableProps } from '../../src/visits/VisitsTable';
import { rangeOf } from '../../src/utils/utils';
import SimplePaginator from '../../src/common/SimplePaginator';
import { SimplePaginator } from '../../src/common/SimplePaginator';
import SearchField from '../../src/utils/SearchField';
import { NormalizedVisit } from '../../src/visits/types';
import { ReachableServer, SelectedServer } from '../../src/servers/data';

View file

@ -1,58 +1,20 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { Bar } from 'react-chartjs-2';
import { prettify } from '../../../src/utils/helpers/numbers';
import { MAIN_COLOR, MAIN_COLOR_ALPHA } from '../../../src/utils/theme';
import { HorizontalBarChart } from '../../../src/visits/charts/HorizontalBarChart';
import { render } from '@testing-library/react';
import { HorizontalBarChart, HorizontalBarChartProps } from '../../../src/visits/charts/HorizontalBarChart';
describe('<HorizontalBarChart />', () => {
let wrapper: ShallowWrapper;
const stats = {
foo: 123,
bar: 456,
const setUp = (props: HorizontalBarChartProps) => {
const { container } = render(<HorizontalBarChart {...props} />);
return container.querySelector('canvas')?.getContext('2d')?.__getEvents(); // eslint-disable-line no-underscore-dangle
};
afterEach(() => wrapper?.unmount());
it('renders Bar with expected properties', () => {
wrapper = shallow(<HorizontalBarChart stats={stats} />);
const horizontal = wrapper.find(Bar);
expect(horizontal).toHaveLength(1);
const { datasets: [{ backgroundColor, borderColor }] } = horizontal.prop('data') as any;
const { plugins, scales } = (horizontal.prop('options') ?? {}) as any;
expect(backgroundColor).toEqual(MAIN_COLOR_ALPHA);
expect(borderColor).toEqual(MAIN_COLOR);
expect(plugins.legend).toEqual({ display: false });
expect(scales).toEqual({
x: {
beginAtZero: true,
stacked: true,
ticks: {
precision: 0,
callback: prettify,
},
},
y: { stacked: true },
});
});
it.each([
[{ foo: 23 }, [100, 456], [23, 0]],
[{ foo: 50 }, [73, 456], [50, 0]],
[{ bar: 45 }, [123, 411], [0, 45]],
[{ bar: 20, foo: 13 }, [110, 436], [13, 20]],
[undefined, [123, 456], undefined],
])('splits highlighted data from regular data', (highlightedStats, expectedData, expectedHighlightedData) => {
wrapper = shallow(<HorizontalBarChart stats={stats} highlightedStats={highlightedStats} />);
const horizontal = wrapper.find(Bar);
[{ foo: 123, bar: 456 }, undefined],
[{ one: 999, two: 131313 }, { one: 30, two: 100 }],
[{ one: 999, two: 131313, max: 3 }, { one: 30, two: 100 }],
])('renders chart with expected canvas', (stats, highlightedStats) => {
const events = setUp({ stats, highlightedStats });
const { datasets: [{ data, label }, highlightedData] } = horizontal.prop('data') as any;
expect(label).toEqual('Visits');
expect(data).toEqual(expectedData);
expectedHighlightedData && expect(highlightedData.data).toEqual(expectedHighlightedData);
!expectedHighlightedData && expect(highlightedData).toBeUndefined();
expect(events).toBeTruthy();
expect(events).toMatchSnapshot();
});
});

View file

@ -0,0 +1,521 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<HorizontalBarChart /> renders chart with expected canvas 1`] = `
Array [
Object {
"props": Object {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"e": 0,
"f": 0,
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "setTransform",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "foo",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "bar",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "0",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "500",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
]
`;
exports[`<HorizontalBarChart /> renders chart with expected canvas 2`] = `
Array [
Object {
"props": Object {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"e": 0,
"f": 0,
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "setTransform",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "one",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "two",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "0",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "200,000",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
]
`;
exports[`<HorizontalBarChart /> renders chart with expected canvas 3`] = `
Array [
Object {
"props": Object {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"e": 0,
"f": 0,
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "setTransform",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "one",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "two",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "max",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "0",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
Object {
"props": Object {
"text": "200,000",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "measureText",
},
Object {
"props": Object {
"value": "12px \\"Helvetica Neue\\", 'Helvetica', 'Arial', sans-serif",
},
"transform": Array [
1,
0,
0,
1,
0,
0,
],
"type": "font",
},
]
`;