Skip to content

Instantly share code, notes, and snippets.

@Yengas
Created April 19, 2020 10:44
Show Gist options
  • Save Yengas/2219e9682d30b0becb7eb64338ca76cf to your computer and use it in GitHub Desktop.
Save Yengas/2219e9682d30b0becb7eb64338ca76cf to your computer and use it in GitHub Desktop.
import React from 'react'
import TitleInput from './TitleInput';
import OptionInput from './OptionInput';
import {withLogic} from "../../utilities/with-logic";
import DataLogic from "./data-logic";
type Option = {
id: string;
text: string;
};
type PollData = {
title: string;
options: Option[];
}
type Props = {
title: string;
options: Option[];
loading: boolean;
changeTitle(title: string): void;
changeOption(id: string, text: string): void;
onSave(data: PollData): void;
reset(): void;
};
const CreatePoll = ({
title,
options,
loading,
changeTitle,
changeOption,
onSave,
reset
}: Props) => (
<div>
<TitleInput
value={title}
onChange={(text) => changeTitle(text)}
/>
<div className="create-poll__options">
{options.map(({id, text}) => (
<OptionInput
key={id}
value={text}
onChange={(text) => changeOption(id, text)}/>
))}
</div>
<div>
<button disabled={loading} onClick={() => onSave({title, options})}>Save</button>
<button disabled={loading} onClick={() => reset()}>Reset</button>
</div>
</div>
);
// bunu burada yapmak zorunda değiliz, bağlama işlemini başka bir yerde de yapabiliriz.
// örnek için DataLogic burada bağlanırken, SaveLogic CreatePoll component'ının kullanıldığı yerde bağlanılıyor
export default withLogic(DataLogic, CreatePoll);
import {useState} from "react";
type Option = {
id: string;
text: string;
}
const DEFAULT_OPTIONS = [
{id: '1', text: ''},
];
function updateOptionText(options: Option[], id: string, text: string) {
return options.reduce<Option[]>((opts, cur) => {
if (cur.id === id) {
return [...opts, {...cur, text}]
}
return [...opts, cur];
}, []);
}
const updateOption = (options: Option[], id: string, text: string) => {
const newOptions = updateOptionText(options, id, text);
// add new option if the last option is edited
if (newOptions[newOptions.length - 1].id === id) {
return [...newOptions, {id: (+id + 1).toString(), text: ''}];
} else {
return newOptions;
}
};
const DataLogic = () => {
const [title, setTitle] = useState('');
const [options, setOptions] = useState<Option[]>(DEFAULT_OPTIONS);
const reset = () => {
setTitle('');
setOptions(DEFAULT_OPTIONS);
};
const changeTitle = (text: string) => {
return setTitle(text);
};
const changeOption = (id: string, text: string) => {
const newOptions = updateOption(options, id, text);
setOptions(newOptions);
};
return {
title,
options,
changeTitle,
changeOption,
reset,
};
};
export default DataLogic;
import {ComponentProps, useState} from 'react';
import CreatePoll from "./index";
import {useServices} from "../../services/context";
const SaveLogic = () => {
const { poll, notification } = useServices();
const [loading, setLoading] = useState(false);
const onSave: ComponentProps<typeof CreatePoll>['onSave'] = async (data) => {
setLoading(true);
try {
await poll.create(data);
notification.success('created poll!');
} catch(err) {
notification.error(err.message);
}
setLoading(false);
};
return {
onSave,
loading
};
};
export default SaveLogic;
import React from 'react';
import MainLayout from '../MainLayout';
import CreatePoll from "../../pages/CreatePoll";
import {withLogic} from "../../utilities/with-logic";
import SaveLogic from "../../pages/CreatePoll/save-logic";
// SaveLogic burada bağlandı CreatePoll'a.
const CP = withLogic(SaveLogic, CreatePoll);
function App() {
return (
<MainLayout>
<CP />
</MainLayout>
);
}
export default App;
import React, { FC } from 'react';
import {renderHook, act} from '@testing-library/react-hooks';
import {anything, instance, mock, reset, verify, when} from 'ts-mockito';
import {NotificationService} from "../../services/notification";
import {PollService} from "../../services/poll";
import SaveLogic from './save-logic';
import {ServiceProvider} from "../../services/context";
import {mockPromise} from "../../utilities/mock-promise";
const mockNotification = mock(NotificationService);
const mockPoll = mock(PollService);
const mockServices = {
notification: instance(mockNotification),
poll: instance(mockPoll),
};
const wrapper: FC = ({ children }) => (<ServiceProvider services={mockServices}>{children}</ServiceProvider>);
describe('<SaveLogic />', () => {
afterEach(() => {
reset(mockNotification);
reset(mockPoll);
});
it('should create successfully', async () => {
const pollData = {} as any;
const { promise, resolve } = mockPromise<void>();
when(mockPoll.create(pollData)).thenReturn(promise);
const { result, waitForNextUpdate } = renderHook(() => SaveLogic(), { wrapper });
act(() => {
result.current.onSave(pollData);
});
verify(mockPoll.create(pollData)).once();
expect(result.current.loading).toBe(true);
act(() => resolve());
await waitForNextUpdate();
expect(result.current.loading).toBe(false);
verify(mockNotification.success(anything())).once();
verify(mockNotification.error(anything())).never();
});
it('should handle create error', async () => {
const pollData = {} as any;
const { promise, reject } = mockPromise<void>();
when(mockPoll.create(pollData)).thenReturn(promise);
const { result, waitForNextUpdate } = renderHook(() => SaveLogic(), { wrapper });
act(() => {
result.current.onSave(pollData);
});
verify(mockPoll.create(pollData)).once();
expect(result.current.loading).toBe(true);
act(() => reject(new Error('some error')));
await waitForNextUpdate();
expect(result.current.loading).toBe(false);
verify(mockNotification.error(anything())).once();
verify(mockNotification.success(anything())).never();
});
});
import React, {ComponentProps} from 'react';
import {fireEvent, render} from '@testing-library/react';
import {wait} from '@testing-library/dom';
import CreatePoll from './index';
const renderComponent = (props: Partial<ComponentProps<typeof CreatePoll>>) => render(
<CreatePoll {...(props as any)} />
);
describe('<CreatePoll />', () => {
it('should have one option at initial state', () => {
const {container} = renderComponent({});
const options = container.querySelector('.create-poll__options');
expect(options).not.toBe(null);
expect(options!.querySelectorAll('input').length).toBe(1);
});
it('should change title', async () => {
const {getByTestId} = renderComponent({});
const title = getByTestId('title') as any;
fireEvent.change(title!, {target: {value: 'some title'}});
await wait(() => expect(title!.value).toBe('some title'));
});
it('should change option', async () => {
const {container} = renderComponent({});
const options = container.querySelector('.create-poll__options');
const lastOption: HTMLInputElement | null = options!.querySelector('input:last-child');
fireEvent.change(lastOption!, {target: {value: 'some option'}});
await wait(() => expect(lastOption!.value).toBe('some option'));
});
it('should create new option', () => {
const {container} = renderComponent({});
const options = container.querySelector('.create-poll__options');
const lastOption = options!.querySelector('input:last-child');
fireEvent.change(lastOption!, {target: {value: 'some option'}});
expect(options!.querySelectorAll('input').length).toBe(2);
});
it('should reset', () => {
const {container, getByTestId, getByText} = renderComponent({});
const title = getByTestId('title') as any;
const option = container.querySelector('.create-poll__options input') as any;
const resetButton = getByText(/reset/i);
fireEvent.change(title!, {target: {value: 'some title'}});
fireEvent.change(option!, {target: {value: 'some option'}});
fireEvent.click(resetButton!);
expect(title!.value).toEqual('');
expect(option!.value).toEqual('');
});
it('should save', () => {
const onSaveSpy = jest.fn();
const {container, getByTestId, getByText} = renderComponent({onSave: onSaveSpy});
const title = getByTestId('title') as any;
const option = container.querySelector('.create-poll__options input') as any;
const saveButton = getByText(/save/i);
fireEvent.change(title!, {target: {value: 'some title'}});
fireEvent.change(option!, {target: {value: 'some option'}});
fireEvent.click(saveButton!);
expect(onSaveSpy).toHaveBeenCalledWith(expect.objectContaining({
title: 'some title',
options: [
{id: '1', text: 'some option'},
{id: '2', text: ''}
]
}));
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment