Skip to content

Commit 0902c08

Browse files
committed
tests: add unit tests for tooltips, toasts, and API error handling
1 parent 97e80d8 commit 0902c08

File tree

9 files changed

+202
-27
lines changed

9 files changed

+202
-27
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import { renderWithProviders } from 'util/tests';
3+
import { createStore, Store } from 'store';
4+
import AlertContainer from 'components/common/AlertContainer';
5+
6+
describe('AlertContainer component', () => {
7+
let store: Store;
8+
9+
const render = () => {
10+
store = createStore();
11+
return renderWithProviders(<AlertContainer />, store);
12+
};
13+
14+
it('should display an alert when added to the store', async () => {
15+
const { findByText } = render();
16+
store.uiStore.notify('test error', 'test title');
17+
expect(await findByText('test error')).toBeInTheDocument();
18+
expect(await findByText('test title')).toBeInTheDocument();
19+
});
20+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import { fireEvent, waitFor } from '@testing-library/react';
3+
import { renderWithProviders } from 'util/tests';
4+
import { createStore, Store } from 'store';
5+
import Tip from 'components/common/Tip';
6+
7+
describe('Tip component', () => {
8+
let store: Store;
9+
10+
const render = (placement?: string) => {
11+
store = createStore();
12+
const cmp = (
13+
<Tip placement={placement} overlay="test tip">
14+
<span>test content</span>
15+
</Tip>
16+
);
17+
return renderWithProviders(cmp, store);
18+
};
19+
20+
it('should display a tooltip on hover', async () => {
21+
const { getByText } = render();
22+
fireEvent.mouseEnter(getByText('test content'));
23+
expect(getByText('test tip')).toBeInTheDocument();
24+
});
25+
26+
it('should display a tooltip on bottom', async () => {
27+
const { getByText, container } = render('bottom');
28+
fireEvent.mouseEnter(getByText('test content'));
29+
waitFor(() => {
30+
expect(container.querySelector('.rc-tooltip-placement-bottom')).toBeInTheDocument();
31+
});
32+
});
33+
34+
it('should display a tooltip on left', async () => {
35+
const { getByText, container } = render('left');
36+
fireEvent.mouseEnter(getByText('test content'));
37+
waitFor(() => {
38+
expect(container.querySelector('.rc-tooltip-placement-left')).toBeInTheDocument();
39+
});
40+
});
41+
42+
it('should display a tooltip on right', async () => {
43+
const { getByText, container } = render('right');
44+
fireEvent.mouseEnter(getByText('test content'));
45+
waitFor(() => {
46+
expect(container.querySelector('.rc-tooltip-placement-right')).toBeInTheDocument();
47+
});
48+
});
49+
});

app/src/__tests__/components/layout/Layout.spec.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ describe('Layout component', () => {
99
};
1010

1111
it('should display the hamburger menu', () => {
12-
const { getByTitle } = render();
13-
expect(getByTitle('menu')).toBeInTheDocument();
12+
const { getByText } = render();
13+
expect(getByText('menu.svg')).toBeInTheDocument();
1414
});
1515

1616
it('should toggle collapsed state', () => {
17-
const { getByTitle, store } = render();
17+
const { getByText, store } = render();
1818
expect(store.settingsStore.sidebarVisible).toBe(true);
19-
fireEvent.click(getByTitle('menu'));
19+
fireEvent.click(getByText('menu.svg'));
2020
expect(store.settingsStore.sidebarVisible).toBe(false);
21-
fireEvent.click(getByTitle('menu'));
21+
fireEvent.click(getByText('menu.svg'));
2222
expect(store.settingsStore.sidebarVisible).toBe(true);
2323
});
2424

app/src/__tests__/components/loop/SwapWizard.spec.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,5 @@ describe('SwapWizard component', () => {
107107
const { getByText } = render();
108108
expect(getByText('Configuring Loops')).toBeInTheDocument();
109109
});
110-
111-
it('should display an error message', () => {
112-
store.buildSwapStore.swapError = new Error('error-test');
113-
const { getByText } = render();
114-
expect(getByText('error-test')).toBeInTheDocument();
115-
});
116110
});
117111
});

app/src/__tests__/store/buildSwapStore.spec.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { values } from 'mobx';
12
import { SwapDirection } from 'types/state';
23
import { grpc } from '@improbable-eng/grpc-web';
34
import { waitFor } from '@testing-library/react';
@@ -42,6 +43,19 @@ describe('BuildSwapStore', () => {
4243
expect(store.terms.out).toEqual({ min: 250000, max: 1000000 });
4344
});
4445

46+
it('should handle errors fetching loop terms', async () => {
47+
grpcMock.unary.mockImplementationOnce(desc => {
48+
if (desc.methodName === 'GetLoopInTerms') throw new Error('test-err');
49+
return undefined as any;
50+
});
51+
expect(rootStore.uiStore.alerts.size).toBe(0);
52+
await store.getTerms();
53+
await waitFor(() => {
54+
expect(rootStore.uiStore.alerts.size).toBe(1);
55+
expect(values(rootStore.uiStore.alerts)[0].message).toBe('test-err');
56+
});
57+
});
58+
4559
it('should adjust the amount after fetching the loop terms', async () => {
4660
store.setAmount(100);
4761
await store.getTerms();
@@ -78,6 +92,21 @@ describe('BuildSwapStore', () => {
7892
expect(store.quote.prepayAmount).toEqual(1337);
7993
});
8094

95+
it('should handle errors fetching loop quote', async () => {
96+
grpcMock.unary.mockImplementationOnce(desc => {
97+
if (desc.methodName === 'LoopOutQuote') throw new Error('test-err');
98+
return undefined as any;
99+
});
100+
store.setDirection(SwapDirection.OUT);
101+
store.setAmount(600);
102+
expect(rootStore.uiStore.alerts.size).toBe(0);
103+
await store.getQuote();
104+
await waitFor(() => {
105+
expect(rootStore.uiStore.alerts.size).toBe(1);
106+
expect(values(rootStore.uiStore.alerts)[0].message).toBe('test-err');
107+
});
108+
});
109+
81110
it('should perform a loop in', async () => {
82111
store.setDirection(SwapDirection.IN);
83112
store.setAmount(600);
@@ -129,18 +158,18 @@ describe('BuildSwapStore', () => {
129158
await waitFor(() => expect(deadline).toEqual(0));
130159
});
131160

132-
it('should handle loop errors', async () => {
161+
it('should handle errors when performing a loop', async () => {
133162
grpcMock.unary.mockImplementationOnce(desc => {
134-
if (desc.methodName === 'LoopIn') throw new Error('asdf');
163+
if (desc.methodName === 'LoopIn') throw new Error('test-err');
135164
return undefined as any;
136165
});
137166
store.setDirection(SwapDirection.IN);
138167
store.setAmount(600);
139-
140-
expect(store.swapError).toBeUndefined();
168+
expect(rootStore.uiStore.alerts.size).toBe(0);
141169
store.requestSwap();
142170
await waitFor(() => {
143-
expect(store.swapError).toBeDefined();
171+
expect(rootStore.uiStore.alerts.size).toBe(1);
172+
expect(values(rootStore.uiStore.alerts)[0].message).toBe('test-err');
144173
});
145174
});
146175

app/src/__tests__/store/channelStore.spec.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { observable, ObservableMap, values } from 'mobx';
2+
import { grpc } from '@improbable-eng/grpc-web';
3+
import { waitFor } from '@testing-library/react';
24
import { BalanceMode } from 'util/constants';
35
import { lndListChannels } from 'util/tests/sampleData';
4-
import { createStore, SettingsStore } from 'store';
6+
import { createStore, Store } from 'store';
57
import Channel from 'store/models/channel';
68
import ChannelStore from 'store/stores/channelStore';
79

10+
const grpcMock = grpc as jest.Mocked<typeof grpc>;
11+
812
describe('ChannelStore', () => {
9-
let settingsStore: SettingsStore;
13+
let rootStore: Store;
1014
let store: ChannelStore;
1115

1216
const channelSubset = (channels: ObservableMap<string, Channel>) => {
@@ -20,9 +24,8 @@ describe('ChannelStore', () => {
2024
};
2125

2226
beforeEach(() => {
23-
const rootStore = createStore();
27+
rootStore = createStore();
2428
store = rootStore.channelStore;
25-
settingsStore = rootStore.settingsStore;
2629
});
2730

2831
it('should fetch list of channels', async () => {
@@ -31,6 +34,19 @@ describe('ChannelStore', () => {
3134
expect(store.channels.size).toEqual(lndListChannels.channelsList.length);
3235
});
3336

37+
it('should handle errors fetching channels', async () => {
38+
grpcMock.unary.mockImplementationOnce(desc => {
39+
if (desc.methodName === 'ListChannels') throw new Error('test-err');
40+
return undefined as any;
41+
});
42+
expect(rootStore.uiStore.alerts.size).toBe(0);
43+
await store.fetchChannels();
44+
await waitFor(() => {
45+
expect(rootStore.uiStore.alerts.size).toBe(1);
46+
expect(values(rootStore.uiStore.alerts)[0].message).toBe('test-err');
47+
});
48+
});
49+
3450
it('should update existing channels with the same id', async () => {
3551
expect(store.channels.size).toEqual(0);
3652
await store.fetchChannels();
@@ -47,7 +63,7 @@ describe('ChannelStore', () => {
4763

4864
it('should sort channels correctly when using receive mode', async () => {
4965
await store.fetchChannels();
50-
settingsStore.setBalanceMode(BalanceMode.receive);
66+
rootStore.settingsStore.setBalanceMode(BalanceMode.receive);
5167
store.channels = channelSubset(store.channels);
5268
store.sortedChannels.forEach((c, i) => {
5369
if (i === 0) return;
@@ -59,7 +75,7 @@ describe('ChannelStore', () => {
5975

6076
it('should sort channels correctly when using send mode', async () => {
6177
await store.fetchChannels();
62-
settingsStore.setBalanceMode(BalanceMode.send);
78+
rootStore.settingsStore.setBalanceMode(BalanceMode.send);
6379
store.channels = channelSubset(store.channels);
6480
store.sortedChannels.forEach((c, i) => {
6581
if (i === 0) return;
@@ -71,7 +87,7 @@ describe('ChannelStore', () => {
7187

7288
it('should sort channels correctly when using routing mode', async () => {
7389
await store.fetchChannels();
74-
settingsStore.setBalanceMode(BalanceMode.routing);
90+
rootStore.settingsStore.setBalanceMode(BalanceMode.routing);
7591
store.channels = channelSubset(store.channels);
7692
store.sortedChannels.forEach((c, i) => {
7793
if (i === 0) return;
Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
import { values } from 'mobx';
2+
import { grpc } from '@improbable-eng/grpc-web';
3+
import { waitFor } from '@testing-library/react';
14
import { lndChannelBalance, lndWalletBalance } from 'util/tests/sampleData';
2-
import { createStore, NodeStore } from 'store';
5+
import { createStore, NodeStore, Store } from 'store';
6+
7+
const grpcMock = grpc as jest.Mocked<typeof grpc>;
38

49
describe('NodeStore', () => {
10+
let rootStore: Store;
511
let store: NodeStore;
612

713
beforeEach(() => {
8-
store = createStore().nodeStore;
14+
rootStore = createStore();
15+
store = rootStore.nodeStore;
916
});
1017

1118
it('should fetch node balances', async () => {
@@ -15,4 +22,17 @@ describe('NodeStore', () => {
1522
expect(store.wallet.channelBalance).toEqual(lndChannelBalance.balance);
1623
expect(store.wallet.walletBalance).toEqual(lndWalletBalance.totalBalance);
1724
});
25+
26+
it('should handle errors fetching channels', async () => {
27+
grpcMock.unary.mockImplementationOnce(desc => {
28+
if (desc.methodName === 'ChannelBalance') throw new Error('test-err');
29+
return undefined as any;
30+
});
31+
expect(rootStore.uiStore.alerts.size).toBe(0);
32+
await store.fetchBalances();
33+
await waitFor(() => {
34+
expect(rootStore.uiStore.alerts.size).toBe(1);
35+
expect(values(rootStore.uiStore.alerts)[0].message).toBe('test-err');
36+
});
37+
});
1838
});

app/src/__tests__/store/swapStore.spec.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
import { values } from 'mobx';
12
import * as LOOP from 'types/generated/loop_pb';
3+
import { grpc } from '@improbable-eng/grpc-web';
24
import { waitFor } from '@testing-library/react';
35
import { loopListSwaps } from 'util/tests/sampleData';
4-
import { createStore, SwapStore } from 'store';
6+
import { createStore, Store, SwapStore } from 'store';
7+
8+
const grpcMock = grpc as jest.Mocked<typeof grpc>;
59

610
describe('SwapStore', () => {
11+
let rootStore: Store;
712
let store: SwapStore;
813

914
beforeEach(async () => {
10-
const rootStore = createStore();
15+
rootStore = createStore();
1116
store = rootStore.swapStore;
1217
});
1318

@@ -17,6 +22,19 @@ describe('SwapStore', () => {
1722
expect(store.sortedSwaps).toHaveLength(7);
1823
});
1924

25+
it('should handle errors fetching channels', async () => {
26+
grpcMock.unary.mockImplementationOnce(desc => {
27+
if (desc.methodName === 'ListSwaps') throw new Error('test-err');
28+
return undefined as any;
29+
});
30+
expect(rootStore.uiStore.alerts.size).toBe(0);
31+
await store.fetchSwaps();
32+
await waitFor(() => {
33+
expect(rootStore.uiStore.alerts.size).toBe(1);
34+
expect(values(rootStore.uiStore.alerts)[0].message).toBe('test-err');
35+
});
36+
});
37+
2038
it('should update existing swaps with the same id', async () => {
2139
expect(store.swaps.size).toEqual(0);
2240
await store.fetchSwaps();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { values } from 'mobx';
2+
import { createStore, UiStore } from 'store';
3+
4+
describe('UiStore', () => {
5+
let store: UiStore;
6+
7+
beforeEach(() => {
8+
store = createStore().uiStore;
9+
});
10+
11+
it('should add an alert', async () => {
12+
expect(store.alerts.size).toBe(0);
13+
store.notify('test message', 'test title');
14+
expect(store.alerts.size).toBe(1);
15+
const alert = values(store.alerts)[0];
16+
expect(alert.message).toBe('test message');
17+
expect(alert.title).toBe('test title');
18+
expect(alert.type).toBe('error');
19+
});
20+
21+
it('should clear an alert', () => {
22+
expect(store.alerts.size).toBe(0);
23+
store.notify('test message', 'test title');
24+
expect(store.alerts.size).toBe(1);
25+
const alert = values(store.alerts)[0];
26+
store.clearAlert(alert.id);
27+
expect(store.alerts.size).toBe(0);
28+
});
29+
});

0 commit comments

Comments
 (0)