Browse Source

chore(counter): remove sample counter component, state, and tests

feat/auto-update
jamaljsr 5 years ago
committed by jamaljsr
parent
commit
d30abf98af
  1. 64
      e2e/Counter.e2e.ts
  2. 6
      e2e/Home.e2e.ts
  3. 16
      e2e/pages/Counter.ts
  4. 4
      e2e/pages/Home.ts
  5. 1
      e2e/pages/index.ts
  6. 7
      src/components/Routes.spec.tsx
  7. 3
      src/components/Routes.tsx
  8. 12
      src/components/counter/Counter.module.less
  9. 96
      src/components/counter/Counter.spec.tsx
  10. 74
      src/components/counter/Counter.tsx
  11. 1
      src/components/counter/index.ts
  12. 2
      src/components/home/Home.spec.tsx
  13. 6
      src/components/home/Home.tsx
  14. 7
      src/i18n/locales/en-US.json
  15. 7
      src/i18n/locales/es.json
  16. 54
      src/store/models/counter.spec.ts
  17. 48
      src/store/models/counter.ts
  18. 3
      src/store/models/index.ts

64
e2e/Counter.e2e.ts

@ -1,64 +0,0 @@
import { Counter, Home } from './pages';
import { assertNoConsoleErrors, pageUrl, getPageUrl } from './helpers';
fixture`Counter`
.page(pageUrl)
.beforeEach(Home.clickCounterLink)
.afterEach(assertNoConsoleErrors);
test('should be on the route /counter', async t => {
await t.expect(getPageUrl()).match(/.*#\/counter$/);
});
test('should display updated count after increment button click', async t => {
await t
.click(Counter.increment)
.expect(Counter.getCounterText())
.eql('1');
});
test('should display updated count after decrement button click', async t => {
await t
.click(Counter.decrement)
.expect(Counter.getCounterText())
.eql('-1');
});
test('should not change when count is even and odd button clicked', async t => {
await t
.click(Counter.incrementOdd)
.expect(Counter.getCounterText())
.eql('0');
});
test('should change when count is odd and odd button clicked', async t => {
await t
.click(Counter.increment)
.click(Counter.incrementOdd)
.expect(Counter.getCounterText())
.eql('3');
});
test('should change a second later if async button clicked', async t => {
await t
.click(Counter.incrementAsync)
.expect(Counter.loadingIcon)
.ok()
.expect(Counter.getCounterText())
.eql('1');
});
test('should show error if count is 3 and async button clicked', async t => {
await t
.click(Counter.increment) // increment to 1
.click(Counter.incrementOdd) // increment to 3
.click(Counter.incrementAsync)
.expect(Counter.loadingIcon.exists)
.ok()
.expect(Counter.getCounterText())
.eql('3')
.expect(Counter.error.exists)
.ok()
.expect(Counter.getErrorText())
.eql('Increment Async prohibited when count is 3.');
});

6
e2e/Home.e2e.ts

@ -14,9 +14,9 @@ test('should show success alert when "Click Me" button clicked', async t => {
.ok();
});
test('should navgiate to /counter', async t => {
test('should navgiate to /network', async t => {
await t
.click(Home.counterLink)
.click(Home.networkLink)
.expect(getPageUrl())
.contains('/counter');
.contains('/network');
});

16
e2e/pages/Counter.ts

@ -1,16 +0,0 @@
import { Selector } from 'testcafe';
class Counter {
counter: Selector = Selector('[data-tid=counter]');
increment: Selector = Selector('[data-tid=incr-btn]');
decrement: Selector = Selector('[data-tid=decr-btn]');
incrementOdd: Selector = Selector('[data-tid=odd-btn]');
incrementAsync: Selector = Selector('[data-tid=async-btn]');
loadingIcon: Selector = Selector('[data-tid=async-loader]');
error: Selector = Selector('[data-tid=error]');
getCounterText = () => this.counter.innerText;
getErrorText = () => this.error.innerText;
}
export default new Counter();

4
e2e/pages/Home.ts

@ -2,10 +2,10 @@ import { Selector } from 'testcafe';
class Home {
successAlert: Selector = Selector('[data-tid=success]');
counterLink: Selector = Selector('[data-tid=counter-link]');
networkLink: Selector = Selector('[data-tid=network-link]');
clickMeButton: Selector = Selector('[data-tid=me-btn]');
clickCounterLink = async (t: TestController) => t.click(this.counterLink);
clickNetworkLink = async (t: TestController) => t.click(this.networkLink);
}
export default new Home();

1
e2e/pages/index.ts

@ -1,4 +1,3 @@
export { default as App } from './App';
export { default as Home } from './Home';
export { default as Counter } from './Counter';
export { default as NewNetwork } from './NewNetwork';

7
src/components/Routes.spec.tsx

@ -1,6 +1,6 @@
import React from 'react';
import { renderWithProviders } from 'utils/tests';
import Routes, { HOME, COUNTER, NETWORK } from './Routes';
import Routes, { HOME, NETWORK } from './Routes';
describe('App container', () => {
const renderComponent = (route: string) => {
@ -12,11 +12,6 @@ describe('App container', () => {
expect(getByTestId('me-btn')).toHaveTextContent('home.me-btn');
});
it('should render the counter page', () => {
const { getByTestId } = renderComponent(COUNTER);
expect(getByTestId('counter')).toHaveTextContent('0');
});
it('should render the new network page', () => {
const { getByTestId } = renderComponent(NETWORK);
expect(getByTestId('submit')).toHaveTextContent('cmps.new-network.btn-create');

3
src/components/Routes.tsx

@ -2,18 +2,15 @@ import React from 'react';
import { Switch, Route } from 'react-router';
import { AppLayout } from './layouts';
import { Home } from './home';
import { Counter } from './counter';
import { NewNetwork } from './network';
export const HOME = '/';
export const COUNTER = '/counter';
export const NETWORK = '/network';
const Routes = () => (
<AppLayout>
<Switch>
<Route path={HOME} exact component={Home} />
<Route path={COUNTER} component={Counter} />
<Route path={NETWORK} component={NewNetwork} />
</Switch>
</AppLayout>

12
src/components/counter/Counter.module.less

@ -1,12 +0,0 @@
.body {
padding: 20px;
text-align: center;
.counter {
font-size: 5rem;
}
.btnGroup .btn {
margin: 5px;
}
}

96
src/components/counter/Counter.spec.tsx

@ -1,96 +0,0 @@
import React from 'react';
import {
fireEvent,
waitForElement,
waitForElementToBeRemoved,
} from '@testing-library/react';
import { renderWithProviders } from 'utils/tests';
import Counter from './Counter';
describe('Counter component', () => {
const renderComponent = () => {
return renderWithProviders(<Counter />);
};
it('should contain default counter value of zero', () => {
const { getByTestId } = renderComponent();
expect(getByTestId('counter')).toHaveTextContent('0');
});
it('should increment by one when plus btn is clicked', () => {
const { getByTestId } = renderComponent();
const btn = getByTestId('incr-btn');
fireEvent.click(btn);
expect(getByTestId('counter')).toHaveTextContent('1');
});
it('should decrement by one when minus btn is clicked', () => {
const { getByTestId } = renderComponent();
const btn = getByTestId('decr-btn');
fireEvent.click(btn);
expect(getByTestId('counter')).toHaveTextContent('-1');
});
it('should not increment when odd btn is clicked and count is even', () => {
const { getByTestId } = renderComponent();
const btn = getByTestId('odd-btn');
fireEvent.click(btn);
expect(getByTestId('counter')).toHaveTextContent('0');
});
it('should increment by two when odd btn is clicked and count is odd', () => {
const { getByTestId } = renderComponent();
// first increment to 1 so that the current count is odd
const incr = getByTestId('incr-btn');
fireEvent.click(incr);
const btn = getByTestId('odd-btn');
fireEvent.click(btn);
expect(getByTestId('counter')).toHaveTextContent('3');
});
it('should show loader when async btn is clicked', async () => {
const { getByTestId } = renderComponent();
const btn = getByTestId('async-btn');
fireEvent.click(btn);
const loader = await waitForElement(() => getByTestId('async-loader'));
expect(loader).not.toBeNull();
});
it('should increment after some time when async btn is clicked', async () => {
const { getByTestId } = renderComponent();
const btn = getByTestId('async-btn');
fireEvent.click(btn);
// wait for loader to show and then be removed
await waitForElement(() => getByTestId('async-loader'));
await waitForElementToBeRemoved(() => getByTestId('async-loader'));
expect(btn).toHaveTextContent('cmps.counter.increment-async');
expect(getByTestId('counter')).toHaveTextContent('1');
});
it('should raise error when count is 3 and async btn is clicked', async () => {
const { getByTestId } = renderComponent();
// first increment to 3
const incr = getByTestId('incr-btn');
fireEvent.click(incr);
fireEvent.click(incr);
fireEvent.click(incr);
const btn = getByTestId('async-btn');
fireEvent.click(btn);
// wait for loader to show and then be removed
await waitForElement(() => getByTestId('async-loader'));
await waitForElementToBeRemoved(() => getByTestId('async-loader'));
expect(btn).toHaveTextContent('cmps.counter.increment-async');
expect(getByTestId('error')).toHaveTextContent(
'models.counter.increment-async.error',
);
});
});

74
src/components/counter/Counter.tsx

@ -1,74 +0,0 @@
import React, { useEffect } from 'react';
import { Alert, Button, Icon } from 'antd';
import { useStoreState, useStoreActions } from 'store';
import { useAsyncCallback } from 'react-async-hook';
import { useTranslation } from 'react-i18next';
import styles from './Counter.module.less';
import { info } from 'electron-log';
const Counter = () => {
useEffect(() => info('Rendering Counter component'), []);
const { t } = useTranslation();
const { count } = useStoreState(s => s.counter);
const { increment, decrement, incrementIfOdd, incrementAsync } = useStoreActions(
a => a.counter,
);
const { execute: incrementAsyncCb, loading, error } = useAsyncCallback(() =>
incrementAsync(),
);
const [incrementCb, decrementCb, incrementIfOddCb] = [
increment,
decrement,
incrementIfOdd,
].map(x => () => x());
return (
<div className={styles.body}>
{error && <Alert message={error.message} type="error" data-tid="error" />}
<h1 className={styles.counter} data-tid="counter">
{loading ? <Icon type="loading" data-tid="async-loader" /> : count}
</h1>
<div className={styles.btnGroup}>
<Button
type="primary"
icon="plus"
className={styles.btn}
data-tid="incr-btn"
onClick={incrementCb}
>
{t('cmps.counter.increment', 'Increment')}
</Button>
<Button
type="primary"
icon="minus"
className={styles.btn}
data-tid="decr-btn"
onClick={decrementCb}
>
{t('cmps.counter.decrement', 'Decrement')}
</Button>
<Button
type="primary"
icon="question"
className={styles.btn}
data-tid="odd-btn"
onClick={incrementIfOddCb}
>
{t('cmps.counter.increment-odd', 'Increment Odd')}
</Button>
<Button
type="primary"
icon="retweet"
className={styles.btn}
data-tid="async-btn"
onClick={incrementAsyncCb}
loading={loading}
>
{t('cmps.counter.increment-async', 'Increment Async')}
</Button>
</div>
</div>
);
};
export default Counter;

1
src/components/counter/index.ts

@ -1 +0,0 @@
export { default as Counter } from './Counter';

2
src/components/home/Home.spec.tsx

@ -15,7 +15,7 @@ describe('Home component', () => {
it('should contain a link to Counter page', () => {
const { getByTestId } = renderComponent();
expect(getByTestId('counter-link')).toHaveTextContent('Counter');
expect(getByTestId('network-link')).toHaveTextContent('Network');
});
it('should not show alert message', () => {

6
src/components/home/Home.tsx

@ -2,8 +2,8 @@ import React, { useState, useEffect } from 'react';
import { Card, Button, Alert } from 'antd';
import { Link } from 'react-router-dom';
import { useTranslation, Trans } from 'react-i18next';
import { COUNTER } from 'components/Routes';
import { info } from 'electron-log';
import { NETWORK } from 'components/Routes';
const Home = () => {
useEffect(() => info('Rendering Home component'), []);
@ -27,8 +27,8 @@ const Home = () => {
<p>
<Trans i18nKey="cmps.home.play">
Play with the{' '}
<Link to={COUNTER} data-tid="counter-link">
Counter
<Link to={NETWORK} data-tid="network-link">
Network
</Link>{' '}
thing
</Trans>

7
src/i18n/locales/en-US.json

@ -5,17 +5,12 @@
"cmps.app-layout.item1": "Item 1",
"cmps.app-layout.item2": "Item 2",
"cmps.app-layout.item3": "Item 3",
"cmps.app-layout.new-network": "Network",
"cmps.home.card-title": "Welcome to Polar",
"cmps.home.card-description": "Much evil soon high in hope do view. Out may few northward believing attempted. Yet timed being songs marry one defer men our. Although finished blessing do of. Consider speaking me prospect whatever if. Ten nearer rather hunted six parish indeed number. Allowance repulsive sex may contained can set suspected abilities cordially. Do part am he high rest that. So fruit to ready it being views match. Was justice improve age article between. No projection as up preference reasonably delightful celebrated. Preserved and abilities assurance tolerably breakfast use saw. And painted letters forming far village elderly compact. Her rest west each spot his and you knew. Estate gay wooded depart six far her. Of we be have it lose gate bred. Do separate removing or expenses in. Had covered but evident chapter matters anxious.",
"cmps.home.me-btn": "Click Me!",
"cmps.home.play": "Play with the <2>Counter</2> thing",
"cmps.home.success-text": "Success Tips",
"cmps.counter.increment": "Increment",
"cmps.counter.decrement": "Decrement",
"cmps.counter.increment-odd": "Increment Odd",
"cmps.counter.increment-async": "Increment Async",
"models.counter.increment-async.error": "Increment Async prohibited when count is 3.",
"cmps.app-layout.new-network": "Network",
"cmps.new-network.title": "Create a new Lightning Network",
"cmps.new-network.name-label": "Network Name",
"cmps.new-network.name-phldr": "My Lightning Simnet",

7
src/i18n/locales/es.json

@ -5,17 +5,12 @@
"cmps.app-layout.item1": "Objeto 1",
"cmps.app-layout.item2": "Objeto 2",
"cmps.app-layout.item3": "Objeto 3",
"cmps.app-layout.new-network": "Red",
"cmps.home.card-title": "Bienvenido a Polar",
"cmps.home.card-description": "Mucho mal pronto pronto alto en esperanza ver. Fuera se pueden intentar algunos creyentes hacia el norte. Sin embargo, las canciones son cronometradas y se casan con un hombre diferente. Aunque terminó la bendición de hacer. Considere hablarme prospectar lo que sea si. Diez más cercanos, más bien cazados, seis parroquias de hecho número. Permiso que puede contener el sexo repulsivo puede establecer sospechas habilidades cordialmente. Haz parte, en lo alto, descansa eso. Así que las frutas para que estén listas sean vistas coinciden. Se hizo justicia entre la edad del artículo. Ninguna proyección como preferencia celebrada razonablemente encantadora celebrada. Preservado y garantía de habilidades tolerablemente el uso de desayunos. Y pintó letras formando pueblo lejano compacto de ancianos. Ella descansa al oeste de cada punto suyo y tú lo sabías. Finca gay boscosa partió seis lejos de ella. De nosotros seremos perdidos por la puerta criada. Haga retiros separados o gastos en. Había cubierto, pero el capítulo evidente importa ansioso.",
"cmps.home.me-btn": "Haz click en mi",
"cmps.home.play": "Jugar con el <2>contador</2>",
"cmps.home.success-text": "Consejos de éxito",
"cmps.counter.increment": "Incremento",
"cmps.counter.decrement": "Decremento",
"cmps.counter.increment-odd": "Incremento impar",
"cmps.counter.increment-async": "Incremento asincrónico",
"models.counter.increment-async.error": "Incremento de asíncrono prohibido cuando el conteo es 3.",
"cmps.app-layout.new-network": "Red",
"cmps.new-network.title": "Crear una nueva red Lightning",
"cmps.new-network.name-label": "Nombre de red",
"cmps.new-network.name-phldr": "My Lightning Simnet",

54
src/store/models/counter.spec.ts

@ -1,54 +0,0 @@
import counterModel from './counter';
import { createStore } from 'easy-peasy';
describe('counter model', () => {
// initialize store for type inference
let store = createStore(counterModel);
beforeEach(() => {
// reset the store before each test run
store = createStore(counterModel);
});
it('should have a valid initial state', () => {
expect(store.getState().count).toBe(0);
});
it('should increment by one', () => {
store.getActions().increment();
expect(store.getState().count).toBe(1);
});
it('should decrement by one', () => {
store.getActions().decrement();
expect(store.getState().count).toBe(-1);
});
it('should increment by two if count is odd', () => {
// initialize state with an odd number
store = createStore(counterModel, { initialState: { count: 1 } });
store.getActions().incrementIfOdd();
expect(store.getState().count).toBe(3);
});
it('should not increment by two if count is even', () => {
store.getActions().incrementIfOdd();
expect(store.getState().count).toBe(0);
});
it('should increment after a delay', async () => {
await store.getActions().incrementAsync();
expect(store.getState().count).toBe(1);
});
it('should fail to increment after a delay if count is three', async () => {
expect.assertions(1);
try {
// initialize state with count set to three
store = createStore(counterModel, { initialState: { count: 3 } });
await store.getActions().incrementAsync();
} catch (e) {
expect(e.message).toMatch('models.counter.increment-async.error');
}
});
});

48
src/store/models/counter.ts

@ -1,48 +0,0 @@
import { Action, action, Thunk, thunk } from 'easy-peasy';
import i18n from 'i18next';
import { info } from 'electron-log';
export interface CounterModel {
count: number;
increment: Action<CounterModel>;
decrement: Action<CounterModel>;
incrementIfOdd: Action<CounterModel>;
incrementAsync: Thunk<CounterModel, number | void>;
}
const counterModel: CounterModel = {
// state vars
count: 0,
// reducer actions (mutations allowed thx to immer)
increment: action(state => {
state.count++;
info(`Incremented count in redux state to ${state.count}`);
}),
decrement: action(state => {
state.count = state.count - 1;
info(`Decremented count in redux state to ${state.count}`);
}),
incrementIfOdd: action(state => {
info(`Incrementing count in redux state by 2 if "${state.count}" is odd`);
if (state.count % 2 !== 0) {
state.count += 2;
info(`Incremented count in redux state to ${state.count}`);
}
}),
incrementAsync: thunk(async (actions, payload, { getState }) => {
info(`Incremented count in redux state asynchronously`);
return new Promise((resolve, reject) => {
setTimeout(() => {
if (getState().count !== 3) {
actions.increment();
resolve();
} else {
info(`Failed to increment count because it is currently ${getState().count}`);
reject(new Error(i18n.t('models.counter.increment-async.error')));
}
}, payload || 1000);
});
}),
};
export default counterModel;

3
src/store/models/index.ts

@ -2,19 +2,16 @@ import { AnyAction } from 'redux';
import { connectRouter, RouterState } from 'connected-react-router';
import { History } from 'history';
import { reducer, Reducer } from 'easy-peasy';
import counterModel, { CounterModel } from './counter';
import networkModel, { NetworkModel } from './network';
export interface RootModel {
router: Reducer<RouterState, AnyAction>;
counter: CounterModel;
network: NetworkModel;
}
export const createModel = (history: History<any>): RootModel => {
const rootModel: RootModel = {
router: reducer(connectRouter(history) as any),
counter: counterModel,
network: networkModel,
};
return rootModel;

Loading…
Cancel
Save