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 { LabeledFormGroup } from '../utils/forms/LabeledFormGroup';
import { Settings } from './reducers/settings';
import { useDomId } from '../utils/helpers/hooks';
interface RealTimeUpdatesProps {
settings: Settings;
@ -16,39 +17,46 @@ const intervalValue = (interval?: number) => (!interval ? '' : `${interval}`);
export const RealTimeUpdatesSettings = (
{ settings: { realTimeUpdates }, toggleRealTimeUpdates, setRealTimeUpdatesInterval }: RealTimeUpdatesProps,
) => (
<SimpleCard title="Real-time updates" className="h-100">
<FormGroup>
<ToggleSwitch checked={realTimeUpdates.enabled} onChange={toggleRealTimeUpdates}>
Enable or disable real-time updates.
<FormText>
Real-time updates are currently being <b>{realTimeUpdates.enabled ? 'processed' : 'ignored'}</b>.
</FormText>
</ToggleSwitch>
</FormGroup>
<LabeledFormGroup
noMargin
label="Real-time updates frequency (in minutes):"
labelClassName={classNames('form-label', { 'text-muted': !realTimeUpdates.enabled })}
>
<Input
type="number"
min={0}
placeholder="Immediate"
disabled={!realTimeUpdates.enabled}
value={intervalValue(realTimeUpdates.interval)}
onChange={({ target }) => setRealTimeUpdatesInterval(Number(target.value))}
/>
{realTimeUpdates.enabled && (
<FormText>
{realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 && (
<span>
Updates will be reflected in the UI every <b>{realTimeUpdates.interval}</b> minute{realTimeUpdates.interval > 1 && 's'}.
</span>
)}
{!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'}
</FormText>
)}
</LabeledFormGroup>
</SimpleCard>
);
) => {
const inputId = useDomId();
return (
<SimpleCard title="Real-time updates" className="h-100">
<FormGroup>
<ToggleSwitch checked={realTimeUpdates.enabled} onChange={toggleRealTimeUpdates}>
Enable or disable real-time updates.
<FormText>
Real-time updates are currently being <b>{realTimeUpdates.enabled ? 'processed' : 'ignored'}</b>.
</FormText>
</ToggleSwitch>
</FormGroup>
<LabeledFormGroup
noMargin
label="Real-time updates frequency (in minutes):"
labelClassName={classNames('form-label', { 'text-muted': !realTimeUpdates.enabled })}
id={inputId}
>
<Input
type="number"
min={0}
placeholder="Immediate"
disabled={!realTimeUpdates.enabled}
value={intervalValue(realTimeUpdates.interval)}
id={inputId}
onChange={({ target }) => setRealTimeUpdatesInterval(Number(target.value))}
/>
{realTimeUpdates.enabled && (
<FormText>
{realTimeUpdates.interval !== undefined && realTimeUpdates.interval > 0 && (
<span>
Updates will be reflected in the UI
every <b>{realTimeUpdates.interval}</b> minute{realTimeUpdates.interval > 1 && 's'}.
</span>
)}
{!realTimeUpdates.interval && 'Updates will be reflected in the UI as soon as they happen.'}
</FormText>
)}
</LabeledFormGroup>
</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 { Input } from 'reactstrap';
import { FormText } from '../../src/utils/forms/FormText';
import {
RealTimeUpdatesSettings as RealTimeUpdatesSettingsOptions,
Settings,
} from '../../src/settings/reducers/settings';
import { RealTimeUpdatesSettings } from '../../src/settings/RealTimeUpdatesSettings';
import { ToggleSwitch } from '../../src/utils/ToggleSwitch';
import { LabeledFormGroup } from '../../src/utils/forms/LabeledFormGroup';
describe('<RealTimeUpdatesSettings />', () => {
const toggleRealTimeUpdates = jest.fn();
const setRealTimeUpdatesInterval = jest.fn();
let wrapper: ShallowWrapper;
const createWrapper = (realTimeUpdates: Partial<RealTimeUpdatesSettingsOptions> = {}) => {
const settings = Mock.of<Settings>({ realTimeUpdates });
wrapper = shallow(
const setUp = (realTimeUpdates: Partial<RealTimeUpdatesSettingsOptions> = {}) => ({
user: userEvent.setup(),
...render(
<RealTimeUpdatesSettings
settings={settings}
settings={Mock.of<Settings>({ realTimeUpdates })}
toggleRealTimeUpdates={toggleRealTimeUpdates}
setRealTimeUpdatesInterval={setRealTimeUpdatesInterval}
/>,
);
return wrapper;
};
),
});
afterEach(jest.clearAllMocks);
afterEach(() => wrapper?.unmount());
it('renders enabled real time updates as expected', () => {
const wrapper = createWrapper({ enabled: true });
const toggle = wrapper.find(ToggleSwitch);
const label = wrapper.find(LabeledFormGroup);
const input = wrapper.find(Input);
const formText = wrapper.find(FormText);
setUp({ enabled: true });
expect(toggle.prop('checked')).toEqual(true);
expect(toggle.html()).toContain('processed');
expect(toggle.html()).not.toContain('ignored');
expect(label.prop('labelClassName')).not.toContain('text-muted');
expect(input.prop('disabled')).toEqual(false);
expect(formText).toHaveLength(2);
expect(screen.getByLabelText(/^Enable or disable real-time updates./)).toBeChecked();
expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('processed');
expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('ignored');
expect(screen.getByText('Real-time updates frequency (in minutes):')).not.toHaveAttribute(
'class',
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', () => {
const wrapper = createWrapper({ enabled: false });
const toggle = wrapper.find(ToggleSwitch);
const label = wrapper.find(LabeledFormGroup);
const input = wrapper.find(Input);
const formText = wrapper.find(FormText);
setUp({ enabled: false });
expect(toggle.prop('checked')).toEqual(false);
expect(toggle.html()).not.toContain('processed');
expect(toggle.html()).toContain('ignored');
expect(label.prop('labelClassName')).toContain('text-muted');
expect(input.prop('disabled')).toEqual(true);
expect(formText).toHaveLength(1);
expect(screen.getByLabelText(/^Enable or disable real-time updates./)).not.toBeChecked();
expect(screen.getByText(/^Real-time updates are currently being/)).not.toHaveTextContent('processed');
expect(screen.getByText(/^Real-time updates are currently being/)).toHaveTextContent('ignored');
expect(screen.getByText('Real-time updates frequency (in minutes):')).toHaveAttribute(
'class',
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([
@ -67,43 +57,35 @@ describe('<RealTimeUpdatesSettings />', () => {
[10, 'minutes'],
[100, 'minutes'],
])('shows expected children when interval is greater than 0', (interval, minutesWord) => {
const wrapper = createWrapper({ enabled: true, interval });
const span = wrapper.find('span');
const input = wrapper.find(Input);
setUp({ enabled: true, interval });
expect(span).toHaveLength(1);
expect(span.html()).toEqual(
`<span>Updates will be reflected in the UI every <b>${interval}</b> ${minutesWord}.</span>`,
expect(screen.getByText(/^Updates will be reflected in the UI every/)).toHaveTextContent(
`${interval} ${minutesWord}`,
);
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) => {
const wrapper = createWrapper({ enabled: true, interval });
const span = wrapper.find('span');
const formText = wrapper.find(FormText).at(1);
const input = wrapper.find(Input);
setUp({ enabled: true, interval });
expect(span).toHaveLength(0);
expect(formText.html()).toContain('Updates will be reflected in the UI as soon as they happen.');
expect(input.prop('value')).toEqual('');
expect(screen.queryByText(/^Updates will be reflected in the UI every/)).not.toBeInTheDocument();
expect(screen.getByText('Updates will be reflected in the UI as soon as they happen.')).toBeInTheDocument();
});
it('updates real time updates on input change', () => {
const wrapper = createWrapper();
const input = wrapper.find(Input);
it('updates real time updates when typing on input', async () => {
const { user } = setUp({ enabled: true });
expect(setRealTimeUpdatesInterval).not.toHaveBeenCalled();
input.simulate('change', { target: { value: '10' } });
expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(10);
await user.type(screen.getByLabelText('Real-time updates frequency (in minutes):'), '5');
expect(setRealTimeUpdatesInterval).toHaveBeenCalledWith(5);
});
it('toggles real time updates on switch change', () => {
const wrapper = createWrapper();
const toggle = wrapper.find(ToggleSwitch);
it('toggles real time updates on switch change', async () => {
const { user } = setUp({ enabled: true });
expect(toggleRealTimeUpdates).not.toHaveBeenCalled();
toggle.simulate('change');
await user.click(screen.getByText(/^Enable or disable real-time updates./));
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 {
DEFAULT_SHORT_URLS_ORDERING,
Settings,
ShortUrlsListSettings as ShortUrlsSettings,
} from '../../src/settings/reducers/settings';
import { Settings, ShortUrlsListSettings as ShortUrlsSettings } from '../../src/settings/reducers/settings';
import { ShortUrlsListSettings } from '../../src/settings/ShortUrlsListSettings';
import { OrderingDropdown } from '../../src/utils/OrderingDropdown';
import { ShortUrlsOrder } from '../../src/short-urls/data';
describe('<ShortUrlsListSettings />', () => {
let wrapper: ShallowWrapper;
const setSettings = jest.fn();
const createWrapper = (shortUrlsList?: ShortUrlsSettings) => {
wrapper = shallow(
const setUp = (shortUrlsList?: ShortUrlsSettings) => ({
user: userEvent.setup(),
...render(
<ShortUrlsListSettings settings={Mock.of<Settings>({ shortUrlsList })} setShortUrlsListSettings={setSettings} />,
);
),
});
return wrapper;
};
afterEach(() => wrapper?.unmount());
afterEach(jest.clearAllMocks);
it.each([
[undefined, DEFAULT_SHORT_URLS_ORDERING],
[{}, DEFAULT_SHORT_URLS_ORDERING],
[{ defaultOrdering: {} }, {}],
[{ defaultOrdering: { field: 'longUrl', dir: 'DESC' } as ShortUrlsOrder }, { field: 'longUrl', dir: 'DESC' }],
[{ defaultOrdering: { field: 'visits', dir: 'ASC' } as ShortUrlsOrder }, { field: 'visits', dir: 'ASC' }],
[undefined, 'Order by: Created at - DESC'],
[{}, 'Order by: Created at - DESC'],
[{ defaultOrdering: {} }, 'Order by...'],
[{ defaultOrdering: { field: 'longUrl', dir: 'DESC' } as ShortUrlsOrder }, 'Order by: Long URL - DESC'],
[{ defaultOrdering: { field: 'visits', dir: 'ASC' } as ShortUrlsOrder }, 'Order by: Visits - ASC'],
])('shows expected ordering', (shortUrlsList, expectedOrder) => {
const wrapper = createWrapper(shortUrlsList);
const dropdown = wrapper.find(OrderingDropdown);
expect(dropdown.prop('order')).toEqual(expectedOrder);
setUp(shortUrlsList);
expect(screen.getByRole('button')).toHaveTextContent(expectedOrder);
});
it.each([
[undefined, undefined],
['longUrl', 'ASC'],
['visits', undefined],
['title', 'DESC'],
])('invokes setSettings when ordering changes', (field, dir) => {
const wrapper = createWrapper();
const dropdown = wrapper.find(OrderingDropdown);
['Clear selection', undefined, undefined],
['Long URL', 'longUrl', 'ASC'],
['Visits', 'visits', 'ASC'],
['Title', 'title', 'ASC'],
])('invokes setSettings when ordering changes', async (name, field, dir) => {
const { user } = setUp();
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 } });
});
});