diff --git a/src/components/designer/bitcoind/BitcoindDetails.spec.tsx b/src/components/designer/bitcoind/BitcoindDetails.spec.tsx new file mode 100644 index 0000000..2a9b943 --- /dev/null +++ b/src/components/designer/bitcoind/BitcoindDetails.spec.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { Status } from 'types'; +import { getNetwork, injections, renderWithProviders } from 'utils/tests'; +import BitcoindDetails from './BitcoindDetails'; + +describe('BitcoindDetails', () => { + const renderComponent = (status?: Status) => { + const network = getNetwork(1, 'test network', status); + const initialState = { + network: { + networks: [network], + }, + }; + const node = network.nodes.bitcoin[0]; + const cmp = ; + const result = renderWithProviders(cmp, { initialState }); + return { + ...result, + node, + }; + }; + + describe('with node Stopped', () => { + it('should display Node Type', () => { + const { getByText, node } = renderComponent(); + expect(getByText('Node Type')).toBeInTheDocument(); + expect(getByText(node.type)).toBeInTheDocument(); + }); + + it('should display Implementation', () => { + const { getByText, node } = renderComponent(); + expect(getByText('Implementation')).toBeInTheDocument(); + expect(getByText(node.implementation)).toBeInTheDocument(); + }); + + it('should display Version', () => { + const { getByText, node } = renderComponent(); + expect(getByText('Version')).toBeInTheDocument(); + expect(getByText(`v${node.version}`)).toBeInTheDocument(); + }); + + it('should display Status', () => { + const { getByText, node } = renderComponent(); + expect(getByText('Status')).toBeInTheDocument(); + expect(getByText(Status[node.status])).toBeInTheDocument(); + }); + + it('should not display Block Height', () => { + const { queryByText } = renderComponent(); + expect(queryByText('Block Height')).toBeNull(); + }); + }); + + describe('with node Started', () => { + const chainMock = injections.bitcoindService.getBlockchainInfo as jest.Mock; + const walletMock = injections.bitcoindService.getWalletInfo as jest.Mock; + + beforeEach(() => { + chainMock.mockResolvedValue({ blocks: 123, bestblockhash: 'abcdef' }); + walletMock.mockResolvedValue({ balance: 10 }); + }); + + it('should display correct Status', async () => { + const { findByText, node } = renderComponent(Status.Started); + expect(await findByText('Status')).toBeInTheDocument(); + expect(await findByText(Status[node.status])).toBeInTheDocument(); + }); + + it('should display RPC Host', async () => { + const { findByText, node } = renderComponent(Status.Started); + expect(await findByText('RPC Host')).toBeInTheDocument(); + expect(await findByText(`127.0.0.1:${node.ports.rpc}`)).toBeInTheDocument(); + }); + + it('should display Wallet Balance', async () => { + const { findByText } = renderComponent(Status.Started); + expect(await findByText('Wallet Balance')).toBeInTheDocument(); + expect(await findByText('10 BTC')).toBeInTheDocument(); + }); + + it('should display Block Height', async () => { + const { findByText } = renderComponent(Status.Started); + expect(await findByText('Block Height')).toBeInTheDocument(); + expect(await findByText('123')).toBeInTheDocument(); + }); + + it('should display Block Hash', async () => { + const { findByText } = renderComponent(Status.Started); + expect(await findByText('Block Hash')).toBeInTheDocument(); + expect(await findByText('abcdef')).toBeInTheDocument(); + }); + + it('should display an error if data fetching fails', async () => { + walletMock.mockRejectedValue(new Error('connection failed')); + const { findByText } = renderComponent(Status.Started); + expect(await findByText('connection failed')).toBeInTheDocument(); + }); + }); +}); diff --git a/src/components/designer/bitcoind/BitcoindDetails.tsx b/src/components/designer/bitcoind/BitcoindDetails.tsx index 4b93533..4069ef0 100644 --- a/src/components/designer/bitcoind/BitcoindDetails.tsx +++ b/src/components/designer/bitcoind/BitcoindDetails.tsx @@ -24,7 +24,7 @@ const BitcoindDetails: React.FC<{ node: BitcoinNode }> = ({ node }) => { const details: DetailValues = [ { label: 'Node Type', value: node.type }, { label: 'Implementation', value: node.implementation }, - { label: 'Version', value: 'v0.18.1' }, + { label: 'Version', value: `v${node.version}` }, { label: 'Status', value: , diff --git a/src/components/designer/bitcoind/MineBlocksInput.spec.tsx b/src/components/designer/bitcoind/MineBlocksInput.spec.tsx new file mode 100644 index 0000000..505d35e --- /dev/null +++ b/src/components/designer/bitcoind/MineBlocksInput.spec.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { fireEvent } from '@testing-library/dom'; +import { getNetwork, injections, renderWithProviders } from 'utils/tests'; +import MineBlocksInput from './MineBlocksInput'; + +describe('MineBlocksInput', () => { + const renderComponent = () => { + const network = getNetwork(1, 'test network'); + const initialState = { + network: { + networks: [network], + }, + }; + const cmp = ; + const result = renderWithProviders(cmp, { initialState }); + return { + ...result, + input: result.container.querySelector('input') as HTMLInputElement, + btn: result.getByText('Mine').parentElement as HTMLElement, + }; + }; + + it('should render label', () => { + const { getByText } = renderComponent(); + expect(getByText('Manually Mine Blocks')).toBeInTheDocument(); + }); + + it('should render button', () => { + const { btn } = renderComponent(); + expect(btn).toBeInTheDocument(); + expect(btn).toBeInstanceOf(HTMLButtonElement); + }); + + it('should render input field', () => { + const { input } = renderComponent(); + expect(input).toBeInTheDocument(); + expect(input).toBeInstanceOf(HTMLInputElement); + }); + + it('should use a default value of 3 for the input', () => { + const { input } = renderComponent(); + expect(input.value).toEqual('3'); + }); + + it('should mine a block when the button is clicked', () => { + const mineMock = injections.bitcoindService.mine as jest.Mock; + mineMock.mockResolvedValue(true); + const { input, btn } = renderComponent(); + const numBlocks = 5; + fireEvent.change(input, { target: { value: numBlocks } }); + fireEvent.click(btn); + expect(mineMock).toBeCalledWith(numBlocks); + }); + + it('should display an error if mining fails', async () => { + const mineMock = injections.bitcoindService.mine as jest.Mock; + mineMock.mockRejectedValue(new Error('connection failed')); + const { input, btn, findByText } = renderComponent(); + const numBlocks = 5; + fireEvent.change(input, { target: { value: numBlocks } }); + fireEvent.click(btn); + expect(await findByText(/connection failed/)).toBeInTheDocument(); + }); + + it('should display an error if blocks is below 1', async () => { + const { input, btn, findByText } = renderComponent(); + const numBlocks = -5; + fireEvent.change(input, { target: { value: numBlocks } }); + fireEvent.click(btn); + expect(await findByText(/must be a positve number/)).toBeInTheDocument(); + }); +}); diff --git a/src/store/models/bitcoind.ts b/src/store/models/bitcoind.ts index df46702..7c67a77 100644 --- a/src/store/models/bitcoind.ts +++ b/src/store/models/bitcoind.ts @@ -25,6 +25,9 @@ const bitcoindModel: BitcoindModel = { actions.setWalletinfo(await injections.bitcoindService.getWalletInfo()); }), mine: thunk(async (actions, { blocks, node }, { injections }) => { + if (blocks < 0) { + throw new Error('The number of blocks to mine must be a positve number'); + } await injections.bitcoindService.mine(blocks); await actions.getInfo(node); }),