From 51b0d481f72fb033706b1f27b3a7014804c5428b Mon Sep 17 00:00:00 2001 From: Rachel Fish Date: Wed, 9 Aug 2023 14:45:46 -0600 Subject: [PATCH 1/7] api+lit: Create listSubServerStatus function --- app/src/api/lit.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/api/lit.ts b/app/src/api/lit.ts index aa9ebf03d..174e5a9a8 100644 --- a/app/src/api/lit.ts +++ b/app/src/api/lit.ts @@ -1,7 +1,9 @@ import * as ACCOUNT from 'types/generated/lit-accounts_pb'; import * as SESSION from 'types/generated/lit-sessions_pb'; +import * as STATUS from 'types/generated/lit-status_pb'; import { Accounts } from 'types/generated/lit-accounts_pb_service'; import { Sessions } from 'types/generated/lit-sessions_pb_service'; +import { Status } from 'types/generated/lit-status_pb_service'; import { b64 } from 'util/strings'; import { MAX_DATE } from 'util/constants'; import BaseApi from './base'; @@ -84,6 +86,12 @@ class LitApi extends BaseApi { const res = await this._grpc.request(Sessions.RevokeSession, req, this._meta); return res.toObject(); } + + async listSubServerStatus(): Promise { + const req = new STATUS.SubServerStatusReq(); + const res = await this._grpc.request(Status.SubServerStatus, req, this._meta); + return res.toObject(); + } } export default LitApi; From a54fe4dba2bada1e091547bc18adbc02452417ec Mon Sep 17 00:00:00 2001 From: Rachel Fish Date: Wed, 9 Aug 2023 14:47:16 -0600 Subject: [PATCH 2/7] store: Create subServerStore --- app/src/store/store.ts | 11 ++++- app/src/store/stores/batchStore.ts | 18 +++++--- app/src/store/stores/index.ts | 1 + app/src/store/stores/subServerStore.ts | 58 ++++++++++++++++++++++++++ app/src/types/state.ts | 6 +++ 5 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 app/src/store/stores/subServerStore.ts diff --git a/app/src/store/store.ts b/app/src/store/store.ts index cd4485427..d434320e6 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -16,6 +16,7 @@ import { RouterStore, SessionStore, SettingsStore, + SubServerStore, SwapStore, } from './stores'; import { @@ -49,6 +50,7 @@ export class Store { orderStore = new OrderStore(this); settingsStore = new SettingsStore(this); sessionStore = new SessionStore(this); + subServerStore = new SubServerStore(this); /** the store which synchronizes with the browser history */ router: RouterStore; @@ -157,11 +159,18 @@ export class Store { * makes the initial API calls to fetch the data we need to display in the app */ async fetchAllData() { + await this.subServerStore.fetchStatus(); await this.nodeStore.fetchInfo(); await this.channelStore.fetchChannels(); - await this.swapStore.fetchSwaps(); await this.nodeStore.fetchBalances(); await this.sessionStore.fetchSessions(); + + if ( + this.subServerStore.subServers.loop?.running && + !this.subServerStore.subServers.loop?.error + ) { + await this.swapStore.fetchSwaps(); + } } /** connects to the LND and Loop websocket streams if not already connected */ diff --git a/app/src/store/stores/batchStore.ts b/app/src/store/stores/batchStore.ts index a2a84f4b9..a722385eb 100644 --- a/app/src/store/stores/batchStore.ts +++ b/app/src/store/stores/batchStore.ts @@ -313,11 +313,17 @@ export default class BatchStore { * initialize the batch store */ init() { - // when the pubkey is fetched from the API and set in the nodeStore, fetch - // the node's tier - when( - () => !!this._store.nodeStore.pubkey && !this.nodeTier, - () => this.fetchNodeTier(), - ); + // make sure the pool subserver is running before initializing + if ( + this._store.subServerStore.subServers.pool?.running && + !this._store.subServerStore.subServers.pool?.error + ) { + // when the pubkey is fetched from the API and set in the nodeStore, fetch + // the node's tier + when( + () => !!this._store.nodeStore.pubkey && !this.nodeTier, + () => this.fetchNodeTier(), + ); + } } } diff --git a/app/src/store/stores/index.ts b/app/src/store/stores/index.ts index 7a31ee829..72366b569 100644 --- a/app/src/store/stores/index.ts +++ b/app/src/store/stores/index.ts @@ -8,3 +8,4 @@ export { default as SettingsStore } from './settingsStore'; export { default as SwapStore } from './swapStore'; export { default as RouterStore } from './routerStore'; export { default as SessionStore } from './sessionStore'; +export { default as SubServerStore } from './subServerStore'; diff --git a/app/src/store/stores/subServerStore.ts b/app/src/store/stores/subServerStore.ts new file mode 100644 index 000000000..1d941c1de --- /dev/null +++ b/app/src/store/stores/subServerStore.ts @@ -0,0 +1,58 @@ +import { makeAutoObservable, runInAction } from 'mobx'; +import { Store } from 'store'; +import { SubServerStatus } from 'types/state'; + +/** processed data for specific subservices we need to display in the UI */ +interface SubServers { + loop: SubServerStatus; + pool: SubServerStatus; +} + +export default class SubServerStore { + private _store: Store; + + loading = false; + + subServers: SubServers = { + loop: { + disabled: false, + running: true, + error: '', + }, + pool: { + disabled: false, + running: true, + error: '', + }, + }; + + constructor(store: Store) { + makeAutoObservable(this, {}, { deep: false, autoBind: true }); + + this._store = store; + } + + /** fetch subserver statuses from the lit API */ + async fetchStatus() { + try { + this.loading = true; + const serverStatus = await this._store.api.lit.listSubServerStatus(); + + serverStatus.subServersMap.map(([serverName, serverStatus]) => { + runInAction(() => { + if (serverName === 'pool') { + this.subServers.pool = serverStatus; + } else if (serverName === 'loop') { + this.subServers.loop = serverStatus; + } + }); + }); + } catch (error) { + this._store.appView.handleError(error, 'Unable to fetch SubServer Status'); + } finally { + runInAction(() => { + this.loading = false; + }); + } + } +} diff --git a/app/src/types/state.ts b/app/src/types/state.ts index 060e898c3..65d350952 100644 --- a/app/src/types/state.ts +++ b/app/src/types/state.ts @@ -69,3 +69,9 @@ export enum ChannelStatus { * duration is not just a random number. */ export type LeaseDuration = number; + +export interface SubServerStatus { + disabled: boolean; + running: boolean; + error: string; +} From 1c0117394973a7dbc3f3ed0117b335e83d0f6380 Mon Sep 17 00:00:00 2001 From: Rachel Fish Date: Wed, 9 Aug 2023 14:56:20 -0600 Subject: [PATCH 3/7] ui+common: SubServerRequired component --- app/src/assets/icons/plug.svg | 3 + app/src/components/base/icons.tsx | 13 +++- .../components/common/SubServerRequired.tsx | 77 +++++++++++++++++++ app/src/i18n/locales/en-US.json | 3 + 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 app/src/assets/icons/plug.svg create mode 100644 app/src/components/common/SubServerRequired.tsx diff --git a/app/src/assets/icons/plug.svg b/app/src/assets/icons/plug.svg new file mode 100644 index 000000000..f11635d89 --- /dev/null +++ b/app/src/assets/icons/plug.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/src/components/base/icons.tsx b/app/src/components/base/icons.tsx index f3bd9db74..000e70718 100644 --- a/app/src/components/base/icons.tsx +++ b/app/src/components/base/icons.tsx @@ -32,9 +32,10 @@ import { ReactComponent as CancelIcon } from 'assets/icons/slash.svg'; import { ReactComponent as UserPlusIcon } from 'assets/icons/user-plus.svg'; import { ReactComponent as QRCodeIcon } from 'assets/icons/qr.svg'; import { ReactComponent as BoltOutlinedIcon } from 'assets/icons/bolt-outlined.svg'; +import { ReactComponent as PlugIcon } from 'assets/icons/plug.svg'; interface IconProps { - size?: 'x-small' | 'small' | 'medium' | 'large'; + size?: 'x-small' | 'small' | 'medium' | 'large' | 'x-large'; onClick?: () => void; disabled?: boolean; } @@ -51,7 +52,7 @@ const Icon = styled.span` cursor: pointer; &:hover { color: ${props.theme.colors.blue}; - background-color: ${props.theme.colors.offWhite}; + background-color: ${props.theme.colors.offWhite}; } `} @@ -88,6 +89,13 @@ const Icon = styled.span` width: 36px; height: 36px; `} + + ${props => + props.size === 'x-large' && + ` + width: 48px; + height: 48px; + `} `; export const AlertTriangle = Icon.withComponent(AlertTriangleIcon); @@ -123,3 +131,4 @@ export const BarChart = Icon.withComponent(BarChartIcon); export const List = Icon.withComponent(ListIcon); export const QRCode = Icon.withComponent(QRCodeIcon); export const BoltOutlined = Icon.withComponent(BoltOutlinedIcon); +export const Plug = Icon.withComponent(PlugIcon); diff --git a/app/src/components/common/SubServerRequired.tsx b/app/src/components/common/SubServerRequired.tsx new file mode 100644 index 000000000..e680b69ef --- /dev/null +++ b/app/src/components/common/SubServerRequired.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import styled from '@emotion/styled'; +import { usePrefixedTranslation } from 'hooks'; +import { Plug } from '../base'; +import { SubServerStatus } from 'types/state'; + +const Styled = { + Wrapper: styled.div` + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + `, + StatusMessage: styled.div` + display: inline-block; + border-radius: 24px; + padding: 3px 16px 3px 6px; + font-size: ${props => props.theme.sizes.s}; + color: ${props => props.theme.colors.lightningGray}; + font-weight: 600; + white-space: nowrap; + text-align: center; + + svg { + margin-bottom: 16px; + color: ${props => props.theme.colors.gold}; + } + `, + Error: styled.div` + font-weight: bold; + `, +}; + +interface StatusProps { + isDisabled: boolean; + errorMessage?: string; +} + +export const SubServerStatusMessage: React.FC = ({ + isDisabled, + errorMessage, +}) => { + const { l } = usePrefixedTranslation('cmps.common.SubServerStatus'); + const { Wrapper, StatusMessage, Error } = Styled; + return ( + + + + + {isDisabled ? ( +

{l('isDisabled')}

+ ) : ( + <> +

{l('isError')}

+ {errorMessage} + + )} +
+
+ ); +}; + +interface Props { + status: SubServerStatus; +} + +const SubServerRequired: React.FC = ({ status, children }) => { + if (status.disabled) { + return ; + } else if (status.error) { + return ; + } + + return <>{children}; +}; + +export default SubServerRequired; diff --git a/app/src/i18n/locales/en-US.json b/app/src/i18n/locales/en-US.json index a6bfb67e1..55a8f0092 100644 --- a/app/src/i18n/locales/en-US.json +++ b/app/src/i18n/locales/en-US.json @@ -31,6 +31,9 @@ "cmps.common.PageHeader.exportTip": "Download CSV", "cmps.common.PageHeader.helpTip": "Take a Tour", "cmps.common.Wizard.backTip": "Back to Previous", + "cmps.common.SubServerStatus.isDisabled": "This subserver is turned off so this interface is not accessible.", + "cmps.common.SubServerStatus.isError": "This subserver encountered an error and failed to start up.", + "cmps.common.SubServerStatus.isNotRunning": "The {{name}} subserver is not running so this interface is not accessible.", "cmps.connect.AddSession.create": "Create a new session", "cmps.connect.AddSession.label": "Label", "cmps.connect.AddSession.permissions": "Permissions", From 6f278895972659a8d44c1211215c4da42112d19a Mon Sep 17 00:00:00 2001 From: Rachel Fish Date: Wed, 9 Aug 2023 14:57:32 -0600 Subject: [PATCH 4/7] ui+pages: Integrate SubServerRequired component into loop and pool pages --- app/src/components/history/HistoryPage.tsx | 13 +++-- app/src/components/loop/LoopPage.tsx | 44 +++++++++------- app/src/components/pool/PoolPage.tsx | 61 +++++++++++++--------- 3 files changed, 67 insertions(+), 51 deletions(-) diff --git a/app/src/components/history/HistoryPage.tsx b/app/src/components/history/HistoryPage.tsx index edd7fb67b..8da3b5e59 100644 --- a/app/src/components/history/HistoryPage.tsx +++ b/app/src/components/history/HistoryPage.tsx @@ -4,6 +4,7 @@ import styled from '@emotion/styled'; import { usePrefixedTranslation } from 'hooks'; import { useStore } from 'store'; import PageHeader from 'components/common/PageHeader'; +import SubServerRequired from 'components/common/SubServerRequired'; import HistoryList from './HistoryList'; const Styled = { @@ -14,14 +15,16 @@ const Styled = { const HistoryPage: React.FC = () => { const { l } = usePrefixedTranslation('cmps.history.HistoryPage'); - const { swapStore } = useStore(); + const { swapStore, subServerStore } = useStore(); const { Wrapper } = Styled; return ( - - - - + + + + + + ); }; diff --git a/app/src/components/loop/LoopPage.tsx b/app/src/components/loop/LoopPage.tsx index ae88bba8f..a1f0d94f9 100644 --- a/app/src/components/loop/LoopPage.tsx +++ b/app/src/components/loop/LoopPage.tsx @@ -5,6 +5,7 @@ import { usePrefixedTranslation } from 'hooks'; import { useStore } from 'store'; import { Badge } from 'components/base'; import PageHeader from 'components/common/PageHeader'; +import SubServerRequired from 'components/common/SubServerRequired'; import ChannelList from './ChannelList'; import LoopActions from './LoopActions'; import LoopTiles from './LoopTiles'; @@ -26,6 +27,7 @@ const LoopPage: React.FC = () => { registerSidecarView, channelStore, nodeStore, + subServerStore, } = useStore(); const title = ( @@ -41,26 +43,28 @@ const LoopPage: React.FC = () => { const { PageWrap } = Styled; return ( - - {appView.processingSwapsVisible ? ( - - ) : buildSwapView.showWizard ? ( - - ) : registerSidecarView.showWizard ? ( - - ) : ( - <> - - - - - )} - - + + + {appView.processingSwapsVisible ? ( + + ) : buildSwapView.showWizard ? ( + + ) : registerSidecarView.showWizard ? ( + + ) : ( + <> + + + + + )} + + + ); }; diff --git a/app/src/components/pool/PoolPage.tsx b/app/src/components/pool/PoolPage.tsx index 6a2a4d145..d8aa4caf3 100644 --- a/app/src/components/pool/PoolPage.tsx +++ b/app/src/components/pool/PoolPage.tsx @@ -5,6 +5,7 @@ import { usePrefixedTranslation } from 'hooks'; import { useStore } from 'store'; import { Badge, Column, Row } from 'components/base'; import PageHeader from 'components/common/PageHeader'; +import SubServerRequired from 'components/common/SubServerRequired'; import AccountSection from './AccountSection'; import BatchSection from './BatchSection'; import OrderFormSection from './OrderFormSection'; @@ -34,18 +35,24 @@ const Styled = { const PoolPage: React.FC = () => { const { l } = usePrefixedTranslation('cmps.pool.PoolPage'); - const { accountStore, orderStore, batchStore } = useStore(); + const { accountStore, orderStore, batchStore, subServerStore } = useStore(); useEffect(() => { - accountStore.fetchAccounts(); - orderStore.fetchOrders(); - batchStore.fetchNextBatchInfo(); - if (!batchStore.batches.size) { - // fetch batches if there aren't any in the store - batchStore.fetchBatches(); + if ( + subServerStore.subServers.pool?.running && + !subServerStore.subServers.pool?.error + ) { + accountStore.fetchAccounts(); + orderStore.fetchOrders(); + batchStore.fetchNextBatchInfo(); + if (!batchStore.batches.size) { + // fetch batches if there aren't any in the store + batchStore.fetchBatches(); + } + // start polling when this component is mounted + batchStore.startPolling(); } - // start polling when this component is mounted - batchStore.startPolling(); + // stop polling when this component is unmounted return () => { batchStore.stopPolling(); @@ -63,23 +70,25 @@ const PoolPage: React.FC = () => { const { Wrapper, Row, Col } = Styled; return ( - - - - - - - - - - - - - + + + + + + + + + + + + + + + ); }; From 6fa13bc2c1142c91defe3302e6bb5a3007306c1e Mon Sep 17 00:00:00 2001 From: Rachel Fish Date: Sun, 13 Aug 2023 01:20:57 -0600 Subject: [PATCH 5/7] tests: Add sample data for SubServerStatus endpoint --- app/src/setupProxy.js | 1 + app/src/util/tests/sampleData.ts | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/app/src/setupProxy.js b/app/src/setupProxy.js index 007d5cda8..eb8ed21b7 100644 --- a/app/src/setupProxy.js +++ b/app/src/setupProxy.js @@ -11,6 +11,7 @@ module.exports = function (app) { '/frdrpc.FaradayServer', '/litrpc.Session', '/litrpc.Accounts', + '/litrpc.Status', ], { target: 'https://localhost:8443', diff --git a/app/src/util/tests/sampleData.ts b/app/src/util/tests/sampleData.ts index 74a717592..0a444eb9e 100644 --- a/app/src/util/tests/sampleData.ts +++ b/app/src/util/tests/sampleData.ts @@ -1,5 +1,6 @@ import * as AUCT from 'types/generated/auctioneerrpc/auctioneer_pb'; import * as LIT from 'types/generated/lit-sessions_pb'; +import * as STATUS from 'types/generated/lit-status_pb'; import * as LND from 'types/generated/lnd_pb'; import * as LOOP from 'types/generated/loop_pb'; import * as POOL from 'types/generated/trader_pb'; @@ -1059,6 +1060,59 @@ export const litListSessions: LIT.ListSessionsResponse.AsObject = { ], }; +export const litSubServerStatus: STATUS.SubServerStatusResp.AsObject = { + subServersMap: [ + [ + 'faraday', + { + disabled: false, + running: true, + error: '', + }, + ], + [ + 'lit', + { + disabled: false, + running: true, + error: '', + }, + ], + [ + 'lnd', + { + disabled: false, + running: true, + error: '', + }, + ], + [ + 'loop', + { + disabled: false, + running: true, + error: '', + }, + ], + [ + 'pool', + { + disabled: false, + running: true, + error: '', + }, + ], + [ + 'taproot-assets', + { + disabled: false, + running: true, + error: '', + }, + ], + ], +}; + // collection of sample API responses export const sampleApiResponses: Record = { 'lnrpc.Lightning.GetInfo': lndGetInfo, @@ -1095,4 +1149,5 @@ export const sampleApiResponses: Record = { 'poolrpc.Trader.Leases': poolLeases, 'poolrpc.Trader.RegisterSidecar': poolRegisterSidecar, 'litrpc.Sessions.ListSessions': litListSessions, + 'litrpc.Status.SubServerStatus': litSubServerStatus, }; From 67a048aa2c755a1d4aba4798c05f2451d435dff3 Mon Sep 17 00:00:00 2001 From: Rachel Fish Date: Tue, 22 Aug 2023 12:37:17 -0600 Subject: [PATCH 6/7] store: Create canFetchData getters for loop and pool --- app/src/components/pool/PoolPage.tsx | 5 +---- app/src/store/store.ts | 5 +---- app/src/store/stores/batchStore.ts | 17 +++++++++++++---- app/src/store/stores/swapStore.ts | 12 ++++++++++++ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/app/src/components/pool/PoolPage.tsx b/app/src/components/pool/PoolPage.tsx index d8aa4caf3..c4923dd07 100644 --- a/app/src/components/pool/PoolPage.tsx +++ b/app/src/components/pool/PoolPage.tsx @@ -38,10 +38,7 @@ const PoolPage: React.FC = () => { const { accountStore, orderStore, batchStore, subServerStore } = useStore(); useEffect(() => { - if ( - subServerStore.subServers.pool?.running && - !subServerStore.subServers.pool?.error - ) { + if (batchStore.canFetchData) { accountStore.fetchAccounts(); orderStore.fetchOrders(); batchStore.fetchNextBatchInfo(); diff --git a/app/src/store/store.ts b/app/src/store/store.ts index d434320e6..a68ba8bab 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -165,10 +165,7 @@ export class Store { await this.nodeStore.fetchBalances(); await this.sessionStore.fetchSessions(); - if ( - this.subServerStore.subServers.loop?.running && - !this.subServerStore.subServers.loop?.error - ) { + if (this.swapStore.canFetchData) { await this.swapStore.fetchSwaps(); } } diff --git a/app/src/store/stores/batchStore.ts b/app/src/store/stores/batchStore.ts index a722385eb..1f5d96d74 100644 --- a/app/src/store/stores/batchStore.ts +++ b/app/src/store/stores/batchStore.ts @@ -111,6 +111,18 @@ export default class BatchStore { return false; } + /** checks the subserver status to ensure pool is running */ + get canFetchData() { + if ( + this._store.subServerStore.subServers.pool.running && + !this._store.subServerStore.subServers.pool.error + ) { + return true; + } + + return false; + } + /** * fetches the next set of past batches from the API */ @@ -314,10 +326,7 @@ export default class BatchStore { */ init() { // make sure the pool subserver is running before initializing - if ( - this._store.subServerStore.subServers.pool?.running && - !this._store.subServerStore.subServers.pool?.error - ) { + if (this.canFetchData) { // when the pubkey is fetched from the API and set in the nodeStore, fetch // the node's tier when( diff --git a/app/src/store/stores/swapStore.ts b/app/src/store/stores/swapStore.ts index a67fcc517..165b61cfd 100644 --- a/app/src/store/stores/swapStore.ts +++ b/app/src/store/stores/swapStore.ts @@ -59,6 +59,18 @@ export default class SwapStore { ); } + /** checks the subserver status to ensure loop is running */ + get canFetchData() { + if ( + this._store.subServerStore.subServers.loop.running && + !this._store.subServerStore.subServers.loop.error + ) { + return true; + } + + return false; + } + /** stores the id of a dismissed swap */ dismissSwap(swapId: string) { this.dismissedSwapIds.push(swapId); From 5ff38630280cd2fa7dc0c923eee904b040c9db37 Mon Sep 17 00:00:00 2001 From: Rachel Fish Date: Wed, 23 Aug 2023 11:57:50 -0600 Subject: [PATCH 7/7] tests: Add tests for SubServerStatus component --- .../common/SubServerStatusMessage.spec.tsx | 30 +++++++++++++++++++ .../components/loop/LoopPage.spec.tsx | 11 +++++++ .../components/pool/PoolPage.spec.tsx | 12 ++++++++ 3 files changed, 53 insertions(+) create mode 100644 app/src/__tests__/components/common/SubServerStatusMessage.spec.tsx diff --git a/app/src/__tests__/components/common/SubServerStatusMessage.spec.tsx b/app/src/__tests__/components/common/SubServerStatusMessage.spec.tsx new file mode 100644 index 000000000..a8a7a02e1 --- /dev/null +++ b/app/src/__tests__/components/common/SubServerStatusMessage.spec.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { renderWithProviders } from 'util/tests'; +import { prefixTranslation } from 'util/translate'; +import { SubServerStatusMessage } from 'components/common/SubServerRequired'; + +describe('SubServer Status Message Component', () => { + const render = (isDisabled: boolean, errorMessage?: string) => { + const cmp = ( + + ); + return renderWithProviders(cmp); + }; + + it('should display disabled', () => { + const { getByText } = render(true); + const { l } = prefixTranslation('cmps.common.SubServerStatus'); + expect(getByText(l('isDisabled'))).toBeInTheDocument(); + }); + + it('should display error', () => { + const { getByText } = render(false); + const { l } = prefixTranslation('cmps.common.SubServerStatus'); + expect(getByText(l('isError'))).toBeInTheDocument(); + }); + + it('should match error message', () => { + const { getByText } = render(false, 'Test error message'); + expect(getByText('Test error message')).toBeInTheDocument(); + }); +}); diff --git a/app/src/__tests__/components/loop/LoopPage.spec.tsx b/app/src/__tests__/components/loop/LoopPage.spec.tsx index a4c69c17b..b74c59fe1 100644 --- a/app/src/__tests__/components/loop/LoopPage.spec.tsx +++ b/app/src/__tests__/components/loop/LoopPage.spec.tsx @@ -9,6 +9,7 @@ import { formatSats } from 'util/formatters'; import { renderWithProviders } from 'util/tests'; import { loopListSwaps } from 'util/tests/sampleData'; import { createStore, Store } from 'store'; +import { prefixTranslation } from 'util/translate'; import LoopPage from 'components/loop/LoopPage'; const grpcMock = grpc as jest.Mocked; @@ -216,5 +217,15 @@ describe('LoopPage component', () => { expect(store.settingsStore.channelSort.field).toBeUndefined(); expect(store.settingsStore.channelSort.descending).toBe(true); }); + + it('should display subserver disabled message', () => { + const { getByText, store } = render(); + const { l } = prefixTranslation('cmps.common.SubServerStatus'); + + store.subServerStore.subServers.loop.disabled = true; + render(); + + expect(getByText(l('isDisabled'))).toBeInTheDocument(); + }); }); }); diff --git a/app/src/__tests__/components/pool/PoolPage.spec.tsx b/app/src/__tests__/components/pool/PoolPage.spec.tsx index 345220693..f23d1176b 100644 --- a/app/src/__tests__/components/pool/PoolPage.spec.tsx +++ b/app/src/__tests__/components/pool/PoolPage.spec.tsx @@ -3,6 +3,7 @@ import { fireEvent } from '@testing-library/react'; import { saveAs } from 'file-saver'; import { renderWithProviders } from 'util/tests'; import { createStore, Store } from 'store'; +import { prefixTranslation } from 'util/translate'; import PoolPage from 'components/pool/PoolPage'; describe('PoolPage', () => { @@ -10,6 +11,7 @@ describe('PoolPage', () => { beforeEach(async () => { store = createStore(); + await store.fetchAllData(); }); const render = () => { @@ -27,4 +29,14 @@ describe('PoolPage', () => { fireEvent.click(getByText('download.svg')); expect(saveAs).toBeCalledWith(expect.any(Blob), 'leases.csv'); }); + + it('should display subserver disabled message', () => { + const { getByText, store } = render(); + const { l } = prefixTranslation('cmps.common.SubServerStatus'); + + store.subServerStore.subServers.pool.disabled = true; + render(); + + expect(getByText(l('isDisabled'))).toBeInTheDocument(); + }); });