Merge pull request #663 from acelaya-forks/feature/rtl-again

Feature/rtl again
This commit is contained in:
Alejandro Celaya 2022-06-06 22:42:28 +02:00 committed by GitHub
commit 2451167296
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 110 additions and 129 deletions

View file

@ -5,6 +5,7 @@ import { SimpleCard } from '../utils/SimpleCard';
import { FormText } from '../utils/forms/FormText'; import { FormText } from '../utils/forms/FormText';
import { LabeledFormGroup } from '../utils/forms/LabeledFormGroup'; import { LabeledFormGroup } from '../utils/forms/LabeledFormGroup';
import { Settings } from './reducers/settings'; import { Settings } from './reducers/settings';
import { useDomId } from '../utils/helpers/hooks';
interface RealTimeUpdatesProps { interface RealTimeUpdatesProps {
settings: Settings; settings: Settings;
@ -16,7 +17,10 @@ const intervalValue = (interval?: number) => (!interval ? '' : `${interval}`);
export const RealTimeUpdatesSettings = ( export const RealTimeUpdatesSettings = (
{ settings: { realTimeUpdates }, toggleRealTimeUpdates, setRealTimeUpdatesInterval }: RealTimeUpdatesProps, { settings: { realTimeUpdates }, toggleRealTimeUpdates, setRealTimeUpdatesInterval }: RealTimeUpdatesProps,
) => ( ) => {
const inputId = useDomId();
return (
<SimpleCard title="Real-time updates" className="h-100"> <SimpleCard title="Real-time updates" className="h-100">
<FormGroup> <FormGroup>
<ToggleSwitch checked={realTimeUpdates.enabled} onChange={toggleRealTimeUpdates}> <ToggleSwitch checked={realTimeUpdates.enabled} onChange={toggleRealTimeUpdates}>
@ -30,6 +34,7 @@ export const RealTimeUpdatesSettings = (
noMargin noMargin
label="Real-time updates frequency (in minutes):" label="Real-time updates frequency (in minutes):"
labelClassName={classNames('form-label', { 'text-muted': !realTimeUpdates.enabled })} labelClassName={classNames('form-label', { 'text-muted': !realTimeUpdates.enabled })}
id={inputId}
> >
<Input <Input
type="number" type="number"
@ -37,13 +42,15 @@ export const RealTimeUpdatesSettings = (
placeholder="Immediate" placeholder="Immediate"
disabled={!realTimeUpdates.enabled} disabled={!realTimeUpdates.enabled}
value={intervalValue(realTimeUpdates.interval)} value={intervalValue(realTimeUpdates.interval)}
id={inputId}
onChange={({ target }) => setRealTimeUpdatesInterval(Number(target.value))} onChange={({ target }) => setRealTimeUpdatesInterval(Number(target.value))}
/> />
{realTimeUpdates.enabled && ( {realTimeUpdates.enabled && (
<FormText> <FormText>
{realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 && ( {realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 && (
<span> <span>
Updates will be reflected in the UI every <b>{realTimeUpdates.interval}</b> minute{realTimeUpdates.interval > 1 && 's'}. Updates will be reflected in the UI
every <b>{realTimeUpdates.interval}</b> minute{realTimeUpdates.interval > 1 && 's'}.
</span> </span>
)} )}
{!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'} {!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'}
@ -51,4 +58,5 @@ export const RealTimeUpdatesSettings = (
)} )}
</LabeledFormGroup> </LabeledFormGroup>
</SimpleCard> </SimpleCard>
); );
};

View file

@ -1,64 +1,54 @@
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 { Input } from 'reactstrap';
import { FormText } from '../../src/utils/forms/FormText';
import { import {
RealTimeUpdatesSettings as RealTimeUpdatesSettingsOptions, RealTimeUpdatesSettings as RealTimeUpdatesSettingsOptions,
Settings, Settings,
} from '../../src/settings/reducers/settings'; } from '../../src/settings/reducers/settings';
import { RealTimeUpdatesSettings } from '../../src/settings/RealTimeUpdatesSettings'; import { RealTimeUpdatesSettings } from '../../src/settings/RealTimeUpdatesSettings';
import { ToggleSwitch } from '../../src/utils/ToggleSwitch';
import { LabeledFormGroup } from '../../src/utils/forms/LabeledFormGroup';
describe('<RealTimeUpdatesSettings />', () => { describe('<RealTimeUpdatesSettings />', () => {
const toggleRealTimeUpdates = jest.fn(); const toggleRealTimeUpdates = jest.fn();
const setRealTimeUpdatesInterval = jest.fn(); const setRealTimeUpdatesInterval = jest.fn();
let wrapper: ShallowWrapper; const setUp = (realTimeUpdates: Partial<RealTimeUpdatesSettingsOptions> = {}) => ({
const createWrapper = (realTimeUpdates: Partial<RealTimeUpdatesSettingsOptions> = {}) => { user: userEvent.setup(),
const settings = Mock.of<Settings>({ realTimeUpdates }); ...render(
wrapper = shallow(
<RealTimeUpdatesSettings <RealTimeUpdatesSettings
settings={settings} settings={Mock.of<Settings>({ realTimeUpdates })}
toggleRealTimeUpdates={toggleRealTimeUpdates} toggleRealTimeUpdates={toggleRealTimeUpdates}
setRealTimeUpdatesInterval={setRealTimeUpdatesInterval} setRealTimeUpdatesInterval={setRealTimeUpdatesInterval}
/>, />,
); ),
});
return wrapper;
};
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
afterEach(() => wrapper?.unmount());
it('renders enabled real time updates as expected', () => { it('renders enabled real time updates as expected', () => {
const wrapper = createWrapper({ enabled: true }); setUp({ enabled: true });
const toggle = wrapper.find(ToggleSwitch);
const label = wrapper.find(LabeledFormGroup);
const input = wrapper.find(Input);
const formText = wrapper.find(FormText);
expect(toggle.prop('checked')).toEqual(true); expect(screen.getByLabelText(/^Enable or disable real-time updates./)).toBeChecked();
expect(toggle.html()).toContain('processed'); expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('processed');
expect(toggle.html()).not.toContain('ignored'); expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('ignored');
expect(label.prop('labelClassName')).not.toContain('text-muted'); expect(screen.getByText('Real-time updates frequency (in minutes):')).not.toHaveAttribute(
expect(input.prop('disabled')).toEqual(false); 'class',
expect(formText).toHaveLength(2); expect.stringContaining('text-muted'),
);
expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).not.toHaveAttribute('disabled');
expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument();
}); });
it('renders disabled real time updates as expected', () => { it('renders disabled real time updates as expected', () => {
const wrapper = createWrapper({ enabled: false }); setUp({ enabled: false });
const toggle = wrapper.find(ToggleSwitch);
const label = wrapper.find(LabeledFormGroup);
const input = wrapper.find(Input);
const formText = wrapper.find(FormText);
expect(toggle.prop('checked')).toEqual(false); expect(screen.getByLabelText(/^Enable or disable real-time updates./)).not.toBeChecked();
expect(toggle.html()).not.toContain('processed'); expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('processed');
expect(toggle.html()).toContain('ignored'); expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('ignored');
expect(label.prop('labelClassName')).toContain('text-muted'); expect(screen.getByText('Real-time updates frequency (in minutes):')).toHaveAttribute(
expect(input.prop('disabled')).toEqual(true); 'class',
expect(formText).toHaveLength(1); expect.stringContaining('text-muted'),
);
expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).toHaveAttribute('disabled');
expect(screen.queryByText('Updates will be reflected in the UI as soon as they happen.')).not.toBeInTheDocument();
}); });
it.each([ it.each([
@ -67,43 +57,35 @@ describe('<RealTimeUpdatesSettings />', () => {
[10, 'minutes'], [10, 'minutes'],
[100, 'minutes'], [100, 'minutes'],
])('shows expected children when interval is greater than 0', (interval, minutesWord) => { ])('shows expected children when interval is greater than 0', (interval, minutesWord) => {
const wrapper = createWrapper({ enabled: true, interval }); setUp({ enabled: true, interval });
const span = wrapper.find('span');
const input = wrapper.find(Input);
expect(span).toHaveLength(1); expect(screen.getByText(/^Updates will be reflected in the UI every/)).toHaveTextContent(
expect(span.html()).toEqual( `${interval} ${minutesWord}`,
`<span>Updates will be reflected in the UI every <b>${interval}</b> ${minutesWord}.</span>`,
); );
expect(input.prop('value')).toEqual(`${interval}`); expect(screen.getByLabelText('Real-time updates frequency (in minutes):')).toHaveValue(interval);
expect(screen.queryByText('Updates will be reflected in the UI as soon as they happen.')).not.toBeInTheDocument();
}); });
it.each([[undefined], [0]])('shows expected children when interval is 0 or undefined', (interval) => { it.each([[undefined], [0]])('shows expected children when interval is 0 or undefined', (interval) => {
const wrapper = createWrapper({ enabled: true, interval }); setUp({ enabled: true, interval });
const span = wrapper.find('span');
const formText = wrapper.find(FormText).at(1);
const input = wrapper.find(Input);
expect(span).toHaveLength(0); expect(screen.queryByText(/^Updates will be reflected in the UI every/)).not.toBeInTheDocument();
expect(formText.html()).toContain('Updates will be reflected in the UI as soon as they happen.'); expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument();
expect(input.prop('value')).toEqual('');
}); });
it('updates real time updates on input change', () => { it('updates real time updates when typing on input', async () => {
const wrapper = createWrapper(); const { user } = setUp({ enabled: true });
const input = wrapper.find(Input);
expect(setRealTimeUpdatesInterval).not.toHaveBeenCalled(); expect(setRealTimeUpdatesInterval).not.toHaveBeenCalled();
input.simulate('change', { target: { value: '10' } }); await user.type(screen.getByLabelText('Real-time updates frequency (in minutes):'), '5');
expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(10); expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(5);
}); });
it('toggles real time updates on switch change', () => { it('toggles real time updates on switch change', async () => {
const wrapper = createWrapper(); const { user } = setUp({ enabled: true });
const toggle = wrapper.find(ToggleSwitch);
expect(toggleRealTimeUpdates).not.toHaveBeenCalled(); expect(toggleRealTimeUpdates).not.toHaveBeenCalled();
toggle.simulate('change'); await user.click(screen.getByText(/^Enable or disable real-time updates./));
expect(toggleRealTimeUpdates).toHaveBeenCalled(); expect(toggleRealTimeUpdates).toHaveBeenCalled();
}); });
}); });

View file

@ -1,52 +1,43 @@
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 { import { Settings, ShortUrlsListSettings as ShortUrlsSettings } from '../../src/settings/reducers/settings';
DEFAULT_SHORT_URLS_ORDERING,
Settings,
ShortUrlsListSettings as ShortUrlsSettings,
} from '../../src/settings/reducers/settings';
import { ShortUrlsListSettings } from '../../src/settings/ShortUrlsListSettings'; import { ShortUrlsListSettings } from '../../src/settings/ShortUrlsListSettings';
import { OrderingDropdown } from '../../src/utils/OrderingDropdown';
import { ShortUrlsOrder } from '../../src/short-urls/data'; import { ShortUrlsOrder } from '../../src/short-urls/data';
describe('<ShortUrlsListSettings />', () => { describe('<ShortUrlsListSettings />', () => {
let wrapper: ShallowWrapper;
const setSettings = jest.fn(); const setSettings = jest.fn();
const createWrapper = (shortUrlsList?: ShortUrlsSettings) => { const setUp = (shortUrlsList?: ShortUrlsSettings) => ({
wrapper = shallow( user: userEvent.setup(),
...render(
<ShortUrlsListSettings settings={Mock.of<Settings>({ shortUrlsList })} setShortUrlsListSettings={setSettings} />, <ShortUrlsListSettings settings={Mock.of<Settings>({ shortUrlsList })} setShortUrlsListSettings={setSettings} />,
); ),
});
return wrapper;
};
afterEach(() => wrapper?.unmount());
afterEach(jest.clearAllMocks); afterEach(jest.clearAllMocks);
it.each([ it.each([
[undefined, DEFAULT_SHORT_URLS_ORDERING], [undefined, 'Order by: Created at - DESC'],
[{}, DEFAULT_SHORT_URLS_ORDERING], [{}, 'Order by: Created at - DESC'],
[{ defaultOrdering: {} }, {}], [{ defaultOrdering: {} }, 'Order by...'],
[{ defaultOrdering: { field: 'longUrl', dir: 'DESC' } as ShortUrlsOrder }, { field: 'longUrl', dir: 'DESC' }], [{ defaultOrdering: { field: 'longUrl', dir: 'DESC' } as ShortUrlsOrder }, 'Order by: Long URL - DESC'],
[{ defaultOrdering: { field: 'visits', dir: 'ASC' } as ShortUrlsOrder }, { field: 'visits', dir: 'ASC' }], [{ defaultOrdering: { field: 'visits', dir: 'ASC' } as ShortUrlsOrder }, 'Order by: Visits - ASC'],
])('shows expected ordering', (shortUrlsList, expectedOrder) => { ])('shows expected ordering', (shortUrlsList, expectedOrder) => {
const wrapper = createWrapper(shortUrlsList); setUp(shortUrlsList);
const dropdown = wrapper.find(OrderingDropdown); expect(screen.getByRole('button')).toHaveTextContent(expectedOrder);
expect(dropdown.prop('order')).toEqual(expectedOrder);
}); });
it.each([ it.each([
[undefined, undefined], ['Clear selection', undefined, undefined],
['longUrl', 'ASC'], ['Long URL', 'longUrl', 'ASC'],
['visits', undefined], ['Visits', 'visits', 'ASC'],
['title', 'DESC'], ['Title', 'title', 'ASC'],
])('invokes setSettings when ordering changes', (field, dir) => { ])('invokes setSettings when ordering changes', async (name, field, dir) => {
const wrapper = createWrapper(); const { user } = setUp();
const dropdown = wrapper.find(OrderingDropdown);
expect(setSettings).not.toHaveBeenCalled(); expect(setSettings).not.toHaveBeenCalled();
dropdown.simulate('change', field, dir); await user.click(screen.getByRole('button'));
await user.click(screen.getByRole('menuitem', { name }));
expect(setSettings).toHaveBeenCalledWith({ defaultOrdering: { field, dir } }); expect(setSettings).toHaveBeenCalledWith({ defaultOrdering: { field, dir } });
}); });
}); });