diff --git a/packages/ui-components/src/__tests__/EditableSpan.test.ts b/packages/ui-components/src/__tests__/EditableSpan.test.ts new file mode 100644 index 000000000..5c218aa5e --- /dev/null +++ b/packages/ui-components/src/__tests__/EditableSpan.test.ts @@ -0,0 +1,26 @@ +import { describe, test, expect } from 'vitest'; +import { render } from '@testing-library/svelte'; +import EditableSpan from '../lib/components/EditableSpan.svelte'; + +describe('Editable Span', () => { + test('should show the correct value', async () => { + const screen = render(EditableSpan, { + displayValue: '123' + }); + + expect(screen.getByText('Block:')).toBeInTheDocument(); + + // test that the input is not visible + expect(screen.getByTestId('editableSpan')).toHaveClass('opacity-0'); + + // test that the input is visible when clicked + await screen.getByTestId('editableSpanWrapper').click(); + expect(screen.getByTestId('editableSpan')).not.toHaveClass('opacity-0'); + + // test that the input is hidden when the enter key is pressed + screen.getByTestId('editableSpan').click(); + screen + .getByTestId('editableSpan') + .dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); + }); +}); diff --git a/packages/ui-components/src/__tests__/OrderTradesListTable.test.ts b/packages/ui-components/src/__tests__/OrderTradesListTable.test.ts new file mode 100644 index 000000000..d7d74f33c --- /dev/null +++ b/packages/ui-components/src/__tests__/OrderTradesListTable.test.ts @@ -0,0 +1,227 @@ +import { render, screen, waitFor } from '@testing-library/svelte'; +import { test, vi, type Mock } from 'vitest'; +import { expect } from '../lib/test/matchers'; +import { mockIPC } from '@tauri-apps/api/mocks'; +import type { Trade } from '../lib/typeshare/subgraphTypes'; +import { formatUnits } from 'viem'; +import OrderTradesListTable from '../lib/components/tables/OrderTradesListTable.svelte'; +import { QueryClient } from '@tanstack/svelte-query'; + +const mockTradeOrdersList: Trade[] = [ + { + id: '1', + timestamp: '1632000000', + tradeEvent: { + sender: 'sender_address', + transaction: { + id: 'transaction_id', + from: 'sender_address', + timestamp: '1632000000', + blockNumber: '0' + } + }, + outputVaultBalanceChange: { + amount: '-100', + vault: { + id: 'id', + vault_id: 'vault-id', + token: { + id: 'output_token', + address: 'output_token', + name: 'output_token', + symbol: 'output_token', + decimals: '1' + } + }, + id: '1', + typename: 'Withdraw', + newVaultBalance: '0', + oldVaultBalance: '0', + timestamp: '0', + transaction: { + id: 'transaction_id', + from: 'sender_address', + timestamp: '1632000000', + blockNumber: '0' + }, + orderbook: { id: '1' } + }, + order: { + id: 'order_id', + orderHash: 'orderHash' + }, + inputVaultBalanceChange: { + vault: { + id: 'id', + vault_id: 'vault-id', + token: { + id: 'output_token', + address: 'output_token', + name: 'output_token', + symbol: 'output_token', + decimals: '1' + } + }, + amount: '50', + id: '1', + typename: 'Withdraw', + newVaultBalance: '0', + oldVaultBalance: '0', + timestamp: '0', + transaction: { + id: 'transaction_id', + from: 'sender_address', + timestamp: '1632000000', + blockNumber: '0' + }, + orderbook: { id: '1' } + }, + orderbook: { + id: '0x00' + } + }, + { + id: '2', + timestamp: '1632000000', + tradeEvent: { + sender: 'sender_address', + transaction: { + id: 'transaction_id', + from: 'sender_address', + timestamp: '1632000000', + blockNumber: '0' + } + }, + outputVaultBalanceChange: { + amount: '-100', + vault: { + id: 'id', + vault_id: 'vault-id', + token: { + id: 'output_token', + address: 'output_token', + name: 'output_token', + symbol: 'output_token', + decimals: '1' + } + }, + id: '1', + typename: 'Withdraw', + newVaultBalance: '0', + oldVaultBalance: '0', + timestamp: '0', + transaction: { + id: 'transaction_id', + from: 'sender_address', + timestamp: '1632000000', + blockNumber: '0' + }, + orderbook: { id: '1' } + }, + order: { + id: 'order_id', + orderHash: 'orderHash' + }, + inputVaultBalanceChange: { + vault: { + id: 'id', + vault_id: 'vault-id', + token: { + id: 'output_token', + address: 'output_token', + name: 'output_token', + symbol: 'output_token', + decimals: '1' + } + }, + amount: '50', + id: '1', + typename: 'Withdraw', + newVaultBalance: '0', + oldVaultBalance: '0', + timestamp: '0', + transaction: { + id: 'transaction_id', + from: 'sender_address', + timestamp: '1632000000', + blockNumber: '0' + }, + orderbook: { id: '1' } + }, + orderbook: { + id: '0x00' + } + } +]; + +vi.mock('@tanstack/svelte-query'); + +test('renders table with correct data', async () => { + const queryClient = new QueryClient(); + + const mockQuery = vi.mocked(await import('@tanstack/svelte-query')); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + mockQuery.createInfiniteQuery = vi.fn((__options, _queryClient) => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + subscribe: (fn: (value: any) => void) => { + fn({ + data: { pages: [mockTradeOrdersList] }, + status: 'success', + isFetching: false, + isFetched: true + }); + return { unsubscribe: () => {} }; + } + })) as Mock; + + render(OrderTradesListTable, { + context: new Map([['$$_queryClient', queryClient]]), + props: { id: '1', subgraphUrl: 'https://example.com' } + }); + + await waitFor(async () => { + // get all the io ratios + const rows = screen.getAllByTestId('io-ratio'); + + // checking the io ratios + for (let i = 0; i < mockTradeOrdersList.length; i++) { + const inputDisplay = formatUnits( + BigInt(mockTradeOrdersList[i].inputVaultBalanceChange.amount), + Number(mockTradeOrdersList[i].inputVaultBalanceChange.vault.token.decimals) + ); + const outputDisplay = formatUnits( + BigInt(mockTradeOrdersList[i].outputVaultBalanceChange.amount), + Number(mockTradeOrdersList[i].outputVaultBalanceChange.vault.token.decimals) + ); + const ioRatio = Number(inputDisplay) / (Number(outputDisplay) * -1); + const oiRatio = (Number(outputDisplay) * -1) / Number(inputDisplay); + expect(rows[i]).toHaveTextContent(ioRatio.toString()); + expect(rows[i]).toHaveTextContent(oiRatio.toString()); + } + }); +}); + +test('renders a debug button for each trade', async () => { + const queryClient = new QueryClient(); + + mockIPC((cmd) => { + if (cmd === 'order_trades_list') { + return mockTradeOrdersList; + } + }); + + render(OrderTradesListTable, { + context: new Map([['$$_queryClient', queryClient]]), + props: { + id: '1', + subgraphUrl: 'https://example.com', + rpcUrl: 'example.com', + handleDebugTradeModal: () => {} + } + }); + + await waitFor(async () => { + const buttons = screen.getAllByTestId('debug-trade-button'); + expect(buttons).toHaveLength(mockTradeOrdersList.length); + }); +}); diff --git a/packages/ui-components/src/__tests__/TableTimeFilters.test.svelte b/packages/ui-components/src/__tests__/TableTimeFilters.test.svelte new file mode 100644 index 000000000..340d07393 --- /dev/null +++ b/packages/ui-components/src/__tests__/TableTimeFilters.test.svelte @@ -0,0 +1,9 @@ + + + diff --git a/packages/ui-components/src/__tests__/TableTimeFilters.test.ts b/packages/ui-components/src/__tests__/TableTimeFilters.test.ts new file mode 100644 index 000000000..09b1a9d10 --- /dev/null +++ b/packages/ui-components/src/__tests__/TableTimeFilters.test.ts @@ -0,0 +1,59 @@ +import { render, fireEvent, screen } from '@testing-library/svelte'; +import { get, writable } from 'svelte/store'; +import { test, expect } from 'vitest'; +import TableTimeFiltersTest from './TableTimeFilters.test.svelte'; + +const TIME_DELTA_24_HOURS = 60 * 60 * 24; +const TIME_DELTA_48_HOURS = TIME_DELTA_24_HOURS * 2; + +test('initial start/end time difference is set to all time', async () => { + const startTimeStore = writable(); + const endTimeStore = writable(); + + render(TableTimeFiltersTest, { startTimeStore, endTimeStore }); + + const twentyFourHoursButton = screen.getByText('24 Hours'); + expect(twentyFourHoursButton).toBeEnabled(); + expect(get(endTimeStore)).toBe(undefined); + expect(get(startTimeStore)).toBe(undefined); +}); + +test('clicking All Time button updates timeDelta', async () => { + const startTimeStore = writable(0); + const endTimeStore = writable(0); + + render(TableTimeFiltersTest, { startTimeStore, endTimeStore }); + + const allTimeButton = screen.getByText('All Time'); + await fireEvent.click(allTimeButton); + + expect(allTimeButton).toBeDisabled(); + expect(get(startTimeStore)).toBe(undefined); + expect(get(endTimeStore)).toBe(undefined); +}); + +test('clicking 48 Hours button updates start/end timestamp', async () => { + const startTimeStore = writable(0); + const endTimeStore = writable(0); + + render(TableTimeFiltersTest, { startTimeStore, endTimeStore }); + + const fortyEightHoursButton = screen.getByText('48 Hours'); + await fireEvent.click(fortyEightHoursButton); + + expect(fortyEightHoursButton).toBeDisabled(); + expect(get(endTimeStore) - get(startTimeStore)).toBe(TIME_DELTA_48_HOURS); +}); + +test('clicking 24 Hours button updates start/end timestamp', async () => { + const startTimeStore = writable(0); + const endTimeStore = writable(0); + + render(TableTimeFiltersTest, { startTimeStore, endTimeStore }); + + const twentyFourHoursButton = screen.getByText('24 Hours'); + await fireEvent.click(twentyFourHoursButton); + + expect(twentyFourHoursButton).toBeDisabled(); + expect(get(endTimeStore) - get(startTimeStore)).toBe(TIME_DELTA_24_HOURS); +}); diff --git a/packages/ui-components/src/__tests__/TanstackPageContentDetail.test.svelte b/packages/ui-components/src/__tests__/TanstackPageContentDetail.test.svelte new file mode 100644 index 000000000..50b19de04 --- /dev/null +++ b/packages/ui-components/src/__tests__/TanstackPageContentDetail.test.svelte @@ -0,0 +1,22 @@ + + + + + {data} + + + {data} + + + {data} + + + {below} + + diff --git a/packages/ui-components/src/__tests__/TanstackPageContentDetail.test.ts b/packages/ui-components/src/__tests__/TanstackPageContentDetail.test.ts new file mode 100644 index 000000000..699774ed6 --- /dev/null +++ b/packages/ui-components/src/__tests__/TanstackPageContentDetail.test.ts @@ -0,0 +1,71 @@ +import { render, screen, waitFor } from '@testing-library/svelte'; +import { test } from 'vitest'; +import { expect } from '../lib/test/matchers'; +import TanstackPageContentDetailTest from './TanstackPageContentDetail.test.svelte'; +import { createResolvableQuery } from '../lib/__mocks__/queries'; + +test('shows query data in correct places', async () => { + const { query, resolve } = createResolvableQuery(() => { + return 'test data'; + }); + + render(TanstackPageContentDetailTest, { + query, + emptyMessage: 'No data', + below: 'Below' + }); + + resolve(); + + await waitFor(() => { + expect(screen.getByTestId('top')).toHaveTextContent('test data'); + expect(screen.getByTestId('card')).toHaveTextContent('test data'); + expect(screen.getByTestId('chart')).toHaveTextContent('test data'); + expect(screen.getByTestId('below')).toHaveTextContent('Below'); + }); +}); + +test('shows empty message', async () => { + const { query, resolve } = createResolvableQuery(() => { + return undefined; + }); + + render(TanstackPageContentDetailTest, { + query, + emptyMessage: 'No data', + below: 'Below' + }); + + resolve(); + + await waitFor(() => { + expect(screen.getByTestId('emptyMessage')).toHaveTextContent('No data'); + }); +}); + +test('shows the loading spinner when query is still loading/fetching and hides it when data is fetched', async () => { + const { query, resolve } = createResolvableQuery(() => { + return 'test data'; + }); + + render(TanstackPageContentDetailTest, { + query, + emptyMessage: 'No data', + below: 'Below' + }); + + await waitFor(() => { + expect(screen.getByTestId('loadingSpinner')).toBeInTheDocument(); + }); + + resolve(); + + await waitFor(() => { + expect(screen.queryByTestId('loadingSpinner')).not.toBeInTheDocument(); + + expect(screen.getByTestId('top')).toHaveTextContent('test data'); + expect(screen.getByTestId('card')).toHaveTextContent('test data'); + expect(screen.getByTestId('chart')).toHaveTextContent('test data'); + expect(screen.getByTestId('below')).toHaveTextContent('Below'); + }); +}); diff --git a/packages/ui-components/src/lib/components/EditableSpan.svelte b/packages/ui-components/src/lib/components/EditableSpan.svelte new file mode 100644 index 000000000..7028927f9 --- /dev/null +++ b/packages/ui-components/src/lib/components/EditableSpan.svelte @@ -0,0 +1,53 @@ + + + + +
+ Block: +
+ {displayValue} + {displayValue} +
+
diff --git a/packages/ui-components/src/lib/components/charts/OrderTradesChart.svelte b/packages/ui-components/src/lib/components/charts/OrderTradesChart.svelte new file mode 100644 index 000000000..4afda3e0c --- /dev/null +++ b/packages/ui-components/src/lib/components/charts/OrderTradesChart.svelte @@ -0,0 +1,39 @@ + + + d.time} + valueTransform={(d) => d.value} + emptyMessage="No trades found" + {lightweightChartsTheme} +/> diff --git a/packages/ui-components/src/lib/components/charts/TableTimeFilters.svelte b/packages/ui-components/src/lib/components/charts/TableTimeFilters.svelte new file mode 100644 index 000000000..9bf2d33ac --- /dev/null +++ b/packages/ui-components/src/lib/components/charts/TableTimeFilters.svelte @@ -0,0 +1,53 @@ + + + + { + setNow(); + timeDelta = undefined; + startTimestamp = undefined; + endTimestamp = undefined; + }} + active={timeDelta === undefined} + size="xs" + class="px-2 py-1">All Time + { + setNow(); + timeDelta = TIME_DELTA_48_HOURS; + startTimestamp = now - TIME_DELTA_48_HOURS; + endTimestamp = now; + }} + active={timeDelta === TIME_DELTA_48_HOURS} + size="xs" + class="px-2 py-1">48 Hours + { + setNow(); + timeDelta = TIME_DELTA_24_HOURS; + startTimestamp = now - TIME_DELTA_24_HOURS; + endTimestamp = now; + }} + active={timeDelta === TIME_DELTA_24_HOURS} + size="xs" + class="px-2 py-1">24 Hours + diff --git a/packages/ui-components/src/lib/components/detail/TanstackPageContentDetail.svelte b/packages/ui-components/src/lib/components/detail/TanstackPageContentDetail.svelte new file mode 100644 index 000000000..d45ec7a72 --- /dev/null +++ b/packages/ui-components/src/lib/components/detail/TanstackPageContentDetail.svelte @@ -0,0 +1,43 @@ + + +{#if data} +
+ +
+
+
+ +
+
+ +
+
+
+ +
+{:else if $query.isFetching || $query.isLoading} +
+ +
+{:else} +
+ {emptyMessage} +
+{/if} diff --git a/packages/ui-components/src/lib/components/tables/OrderTradesListTable.svelte b/packages/ui-components/src/lib/components/tables/OrderTradesListTable.svelte new file mode 100644 index 000000000..88050b2cd --- /dev/null +++ b/packages/ui-components/src/lib/components/tables/OrderTradesListTable.svelte @@ -0,0 +1,149 @@ + + + + + {#if tradesCount !== undefined} +
{tradesCount} Trades
+ {/if} +
+ + + + + Date + Sender + Transaction Hash + Input + Output + IO Ratio + + + + + + {formatTimestampSecondsAsLocal(BigInt(item.timestamp))} + + + + + + + + + {formatUnits( + BigInt(item.inputVaultBalanceChange.amount), + Number(item.inputVaultBalanceChange.vault.token.decimals ?? 0) + )} + {item.inputVaultBalanceChange.vault.token.symbol} + + + {formatUnits( + BigInt(item.outputVaultBalanceChange.amount) * BigInt(-1), + Number(item.outputVaultBalanceChange.vault.token.decimals ?? 0) + )} + {item.outputVaultBalanceChange.vault.token.symbol} + + + {Math.abs( + Number( + formatUnits( + BigInt(item.inputVaultBalanceChange.amount), + Number(item.inputVaultBalanceChange.vault.token.decimals ?? 0) + ) + ) / + Number( + formatUnits( + BigInt(item.outputVaultBalanceChange.amount), + Number(item.outputVaultBalanceChange.vault.token.decimals ?? 0) + ) + ) + )} + + ({Math.abs( + Number( + formatUnits( + BigInt(item.outputVaultBalanceChange.amount), + Number(item.outputVaultBalanceChange.vault.token.decimals ?? 0) + ) + ) / + Number( + formatUnits( + BigInt(item.inputVaultBalanceChange.amount), + Number(item.inputVaultBalanceChange.vault.token.decimals ?? 0) + ) + ) + )}) + + + {#if rpcUrl && handleDebugTradeModal} + + + + {/if} + +
diff --git a/packages/ui-components/src/lib/index.ts b/packages/ui-components/src/lib/index.ts index 98e7c6191..76b1eb20c 100644 --- a/packages/ui-components/src/lib/index.ts +++ b/packages/ui-components/src/lib/index.ts @@ -23,7 +23,12 @@ export { default as ChartTimeFilters } from './components/charts/ChartTimeFilter export { default as LightweightChart } from './components/charts/LightweightChart.svelte'; export { default as TanstackLightweightChartLine } from './components/charts/TanstackLightweightChartLine.svelte'; export { default as MockComponent } from './__mocks__/MockComponent.svelte'; +export { default as OrderTradesChart } from './components/charts/OrderTradesChart.svelte'; +export { default as TableTimeFilters } from './components/charts/TableTimeFilters.svelte'; +export { default as OrderTradesListTable } from './components/tables/OrderTradesListTable.svelte'; export { default as Checkbox } from './components/checkbox/Checkbox.svelte'; +export { default as TanstackPageContentDetail } from './components/detail/TanstackPageContentDetail.svelte'; +export { default as EditableSpan } from './components/EditableSpan.svelte'; export { default as BlockQuote } from './components/BlockQuote.svelte'; export { default as Heading } from './components/Heading.svelte'; export { default as Text } from './components/Text.svelte'; @@ -63,3 +68,4 @@ export { lightCodeMirrorTheme, darkCodeMirrorTheme } from './utils/codeMirrorThe // Stores export { mockConfigSource } from './__mocks__/settings'; export { mockSettingsStore } from './__mocks__/settings'; +export { colorTheme, lightweightChartsTheme } from './stores/darkMode'; diff --git a/packages/webapp/src/routes/orders/[id]/+page.svelte b/packages/webapp/src/routes/orders/[id]/+page.svelte deleted file mode 100644 index 0e7cba3e1..000000000 --- a/packages/webapp/src/routes/orders/[id]/+page.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - - - -{#if $query.data} - {#each $query.data as trade} - {trade.id} - - {/each} -{/if} diff --git a/packages/webapp/src/routes/orders/[network]-[id]/+page.svelte b/packages/webapp/src/routes/orders/[network]-[id]/+page.svelte new file mode 100644 index 000000000..c2f45ac62 --- /dev/null +++ b/packages/webapp/src/routes/orders/[network]-[id]/+page.svelte @@ -0,0 +1,17 @@ + + +

Order Trades

+ + diff --git a/packages/webapp/src/routes/orders/[id]/TradeCount.svelte b/packages/webapp/src/routes/orders/[network]-[id]/TradeCount.svelte similarity index 100% rename from packages/webapp/src/routes/orders/[id]/TradeCount.svelte rename to packages/webapp/src/routes/orders/[network]-[id]/TradeCount.svelte diff --git a/packages/webapp/src/routes/orders/[id]/TradeDetail.svelte b/packages/webapp/src/routes/orders/[network]-[id]/TradeDetail.svelte similarity index 100% rename from packages/webapp/src/routes/orders/[id]/TradeDetail.svelte rename to packages/webapp/src/routes/orders/[network]-[id]/TradeDetail.svelte diff --git a/tauri-app/src/lib/components/EditableSpan.svelte b/tauri-app/src/lib/components/EditableSpan.svelte deleted file mode 100644 index 1d9ac1ce4..000000000 --- a/tauri-app/src/lib/components/EditableSpan.svelte +++ /dev/null @@ -1,53 +0,0 @@ - - - - -
- Block: -
- {displayValue} - {displayValue} -
-
diff --git a/tauri-app/src/lib/components/EditableSpan.test.ts b/tauri-app/src/lib/components/EditableSpan.test.ts deleted file mode 100644 index 7760c6f2e..000000000 --- a/tauri-app/src/lib/components/EditableSpan.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, test, expect } from 'vitest'; -import { render } from '@testing-library/svelte'; -import EditableSpan from './EditableSpan.svelte'; - -describe('Editable Span', () => { - test('should show the correct value', async () => { - const screen = render(EditableSpan, { - displayValue: '123', - }); - - expect(screen.getByText('Block:')).toBeInTheDocument(); - - // test that the input is not visible - expect(screen.getByTestId('editableSpan')).toHaveClass('opacity-0'); - - // test that the input is visible when clicked - await screen.getByTestId('editableSpanWrapper').click(); - expect(screen.getByTestId('editableSpan')).not.toHaveClass('opacity-0'); - - // test that the input is hidden when the enter key is pressed - screen.getByTestId('editableSpan').click(); - screen - .getByTestId('editableSpan') - .dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); - }); -}); diff --git a/tauri-app/src/lib/components/charts/OrderTradesChart.svelte b/tauri-app/src/lib/components/charts/OrderTradesChart.svelte deleted file mode 100644 index 9d94573c8..000000000 --- a/tauri-app/src/lib/components/charts/OrderTradesChart.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - - d.time} - valueTransform={(d) => d.value} - emptyMessage="No trades found" - {lightweightChartsTheme} -/> diff --git a/tauri-app/src/lib/components/charts/TableTimeFilters.svelte b/tauri-app/src/lib/components/charts/TableTimeFilters.svelte deleted file mode 100644 index 3b8ff5fcb..000000000 --- a/tauri-app/src/lib/components/charts/TableTimeFilters.svelte +++ /dev/null @@ -1,53 +0,0 @@ - - - - { - setNow(); - timeDelta = undefined; - startTimestamp = undefined; - endTimestamp = undefined; - }} - active={timeDelta === undefined} - size="xs" - class="px-2 py-1">All Time - { - setNow(); - timeDelta = TIME_DELTA_48_HOURS; - startTimestamp = now - TIME_DELTA_48_HOURS; - endTimestamp = now; - }} - active={timeDelta === TIME_DELTA_48_HOURS} - size="xs" - class="px-2 py-1">48 Hours - { - setNow(); - timeDelta = TIME_DELTA_24_HOURS; - startTimestamp = now - TIME_DELTA_24_HOURS; - endTimestamp = now; - }} - active={timeDelta === TIME_DELTA_24_HOURS} - size="xs" - class="px-2 py-1">24 Hours - diff --git a/tauri-app/src/lib/components/charts/TableTimeFilters.test.svelte b/tauri-app/src/lib/components/charts/TableTimeFilters.test.svelte deleted file mode 100644 index 5f7f5de98..000000000 --- a/tauri-app/src/lib/components/charts/TableTimeFilters.test.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/tauri-app/src/lib/components/charts/TableTimeFilters.test.ts b/tauri-app/src/lib/components/charts/TableTimeFilters.test.ts deleted file mode 100644 index eb4913020..000000000 --- a/tauri-app/src/lib/components/charts/TableTimeFilters.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { render, fireEvent, screen } from '@testing-library/svelte'; -import { get, writable } from 'svelte/store'; -import { test, expect } from 'vitest'; -import TableTimeFiltersTest from './TableTimeFilters.test.svelte'; - -const TIME_DELTA_24_HOURS = 60 * 60 * 24; -const TIME_DELTA_48_HOURS = TIME_DELTA_24_HOURS * 2; - -test('initial start/end time difference is set to all time', async () => { - const startTimeStore = writable(); - const endTimeStore = writable(); - - render(TableTimeFiltersTest, { startTimeStore, endTimeStore }); - - const twentyFourHoursButton = screen.getByText('24 Hours'); - expect(twentyFourHoursButton).toBeEnabled(); - expect(get(endTimeStore)).toBe(undefined); - expect(get(startTimeStore)).toBe(undefined); -}); - -test('clicking All Time button updates timeDelta', async () => { - const startTimeStore = writable(0); - const endTimeStore = writable(0); - - render(TableTimeFiltersTest, { startTimeStore, endTimeStore }); - - const allTimeButton = screen.getByText('All Time'); - await fireEvent.click(allTimeButton); - - expect(allTimeButton).toBeDisabled(); - expect(get(startTimeStore)).toBe(undefined); - expect(get(endTimeStore)).toBe(undefined); -}); - -test('clicking 48 Hours button updates start/end timestamp', async () => { - const startTimeStore = writable(0); - const endTimeStore = writable(0); - - render(TableTimeFiltersTest, { startTimeStore, endTimeStore }); - - const fortyEightHoursButton = screen.getByText('48 Hours'); - await fireEvent.click(fortyEightHoursButton); - - expect(fortyEightHoursButton).toBeDisabled(); - expect(get(endTimeStore) - get(startTimeStore)).toBe(TIME_DELTA_48_HOURS); -}); - -test('clicking 24 Hours button updates start/end timestamp', async () => { - const startTimeStore = writable(0); - const endTimeStore = writable(0); - - render(TableTimeFiltersTest, { startTimeStore, endTimeStore }); - - const twentyFourHoursButton = screen.getByText('24 Hours'); - await fireEvent.click(twentyFourHoursButton); - - expect(twentyFourHoursButton).toBeDisabled(); - expect(get(endTimeStore) - get(startTimeStore)).toBe(TIME_DELTA_24_HOURS); -}); diff --git a/tauri-app/src/lib/components/detail/OrderDetail.svelte b/tauri-app/src/lib/components/detail/OrderDetail.svelte index fb832ba15..3a839bd4e 100644 --- a/tauri-app/src/lib/components/detail/OrderDetail.svelte +++ b/tauri-app/src/lib/components/detail/OrderDetail.svelte @@ -6,23 +6,24 @@ import { formatTimestampSecondsAsLocal } from '@rainlanguage/ui-components'; import { ButtonVaultLink } from '@rainlanguage/ui-components'; import { Hash, HashType } from '@rainlanguage/ui-components'; + import CodeMirrorRainlang from '$lib/components/CodeMirrorRainlang.svelte'; - import TanstackPageContentDetail from './TanstackPageContentDetail.svelte'; - import { handleOrderRemoveModal } from '$lib/services/modal'; + import { settings } from '$lib/stores/settings'; + import { TanstackPageContentDetail } from '@rainlanguage/ui-components'; + import { handleOrderRemoveModal, handleDebugTradeModal } from '$lib/services/modal'; import { createQuery } from '@tanstack/svelte-query'; import { QKEY_ORDER } from '@rainlanguage/ui-components'; import { orderDetail } from '$lib/queries/orderDetail'; - import OrderTradesListTable from '../tables/OrderTradesListTable.svelte'; - import OrderTradesChart from '../charts/OrderTradesChart.svelte'; + import { OrderTradesListTable } from '@rainlanguage/ui-components'; + import { OrderTradesChart } from '@rainlanguage/ui-components'; import OrderQuote from '../detail/TanstackOrderQuote.svelte'; import { onDestroy } from 'svelte'; import { queryClient } from '$lib/queries/queryClient'; import OrderVaultsVolTable from '../tables/OrderVaultsVolTable.svelte'; - import { settings } from '$lib/stores/settings'; - - export let id: string; - export let network: string; + import { colorTheme, lightweightChartsTheme } from '$lib/stores/darkMode'; + export let id, network; const subgraphUrl = $settings?.subgraphs?.[network] || ''; + const rpcUrl = $settings?.networks?.[network]?.rpc || ''; $: orderDetailQuery = createQuery({ queryKey: [id, QKEY_ORDER + id], @@ -118,7 +119,7 @@ - + @@ -139,7 +140,7 @@ {/if} - + diff --git a/tauri-app/src/lib/components/detail/TanstackOrderQuote.svelte b/tauri-app/src/lib/components/detail/TanstackOrderQuote.svelte index 19efdfcce..c675a7faf 100644 --- a/tauri-app/src/lib/components/detail/TanstackOrderQuote.svelte +++ b/tauri-app/src/lib/components/detail/TanstackOrderQuote.svelte @@ -17,7 +17,7 @@ Tooltip, } from 'flowbite-svelte'; import { BugOutline, PauseSolid, PlaySolid } from 'flowbite-svelte-icons'; - import EditableSpan from '../EditableSpan.svelte'; + import { EditableSpan } from '@rainlanguage/ui-components'; export let id: string; export let order: Order; diff --git a/tauri-app/src/lib/components/detail/TanstackPageContentDetail.svelte b/tauri-app/src/lib/components/detail/TanstackPageContentDetail.svelte deleted file mode 100644 index 836a2b2a9..000000000 --- a/tauri-app/src/lib/components/detail/TanstackPageContentDetail.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - -{#if data} -
- -
-
-
- -
-
- -
-
-
- -
-{:else if $query.isFetching || $query.isLoading} -
- -
-{:else} -
- {emptyMessage} -
-{/if} diff --git a/tauri-app/src/lib/components/detail/TanstackPageContentDetail.test.svelte b/tauri-app/src/lib/components/detail/TanstackPageContentDetail.test.svelte deleted file mode 100644 index f60460a95..000000000 --- a/tauri-app/src/lib/components/detail/TanstackPageContentDetail.test.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {data} - - - {data} - - - {data} - - - {below} - - diff --git a/tauri-app/src/lib/components/detail/TanstackPageContentDetail.test.ts b/tauri-app/src/lib/components/detail/TanstackPageContentDetail.test.ts deleted file mode 100644 index 4d13b49fc..000000000 --- a/tauri-app/src/lib/components/detail/TanstackPageContentDetail.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/svelte'; -import { test } from 'vitest'; -import { expect } from '$lib/test/matchers'; -import TanstackPageContentDetailTest from './TanstackPageContentDetail.test.svelte'; -import { createResolvableQuery } from '@rainlanguage/ui-components'; - -test('shows query data in correct places', async () => { - const { query, resolve } = createResolvableQuery(() => { - return 'test data'; - }); - - render(TanstackPageContentDetailTest, { - query, - emptyMessage: 'No data', - below: 'Below', - }); - - resolve(); - - await waitFor(() => { - expect(screen.getByTestId('top')).toHaveTextContent('test data'); - expect(screen.getByTestId('card')).toHaveTextContent('test data'); - expect(screen.getByTestId('chart')).toHaveTextContent('test data'); - expect(screen.getByTestId('below')).toHaveTextContent('Below'); - }); -}); - -test('shows empty message', async () => { - const { query, resolve } = createResolvableQuery(() => { - return undefined; - }); - - render(TanstackPageContentDetailTest, { - query, - emptyMessage: 'No data', - below: 'Below', - }); - - resolve(); - - await waitFor(() => { - expect(screen.getByTestId('emptyMessage')).toHaveTextContent('No data'); - }); -}); - -test('shows the loading spinner when query is still loading/fetching and hides it when data is fetched', async () => { - const { query, resolve } = createResolvableQuery(() => { - return 'test data'; - }); - - render(TanstackPageContentDetailTest, { - query, - emptyMessage: 'No data', - below: 'Below', - }); - - await waitFor(() => { - expect(screen.getByTestId('loadingSpinner')).toBeInTheDocument(); - }); - - resolve(); - - await waitFor(() => { - expect(screen.queryByTestId('loadingSpinner')).not.toBeInTheDocument(); - - expect(screen.getByTestId('top')).toHaveTextContent('test data'); - expect(screen.getByTestId('card')).toHaveTextContent('test data'); - expect(screen.getByTestId('chart')).toHaveTextContent('test data'); - expect(screen.getByTestId('below')).toHaveTextContent('Below'); - }); -}); diff --git a/tauri-app/src/lib/components/detail/VaultDetail.svelte b/tauri-app/src/lib/components/detail/VaultDetail.svelte index e5d917125..fc4ed5392 100644 --- a/tauri-app/src/lib/components/detail/VaultDetail.svelte +++ b/tauri-app/src/lib/components/detail/VaultDetail.svelte @@ -13,7 +13,7 @@ import { vaultDetail } from '$lib/queries/vaultDetail'; import { QKEY_VAULT } from '@rainlanguage/ui-components'; import { handleDepositModal, handleWithdrawModal } from '$lib/services/modal'; - import TanstackContentDetail from '$lib/components/detail/TanstackPageContentDetail.svelte'; + import { TanstackPageContentDetail } from '@rainlanguage/ui-components'; import VaultBalanceChart from '$lib/components/charts/VaultBalanceChart.svelte'; import { onDestroy } from 'svelte'; import { queryClient } from '$lib/queries/queryClient'; @@ -46,7 +46,7 @@ }); - +
- + diff --git a/tauri-app/src/lib/components/tables/OrderTradesListTable.svelte b/tauri-app/src/lib/components/tables/OrderTradesListTable.svelte deleted file mode 100644 index 08b431034..000000000 --- a/tauri-app/src/lib/components/tables/OrderTradesListTable.svelte +++ /dev/null @@ -1,146 +0,0 @@ - - - - - {#if tradesCount !== undefined} -
{tradesCount} Trades
- {/if} -
- - - - - Date - Sender - Transaction Hash - Input - Output - IO Ratio - - - - - - {formatTimestampSecondsAsLocal(BigInt(item.timestamp))} - - - - - - - - - {formatUnits( - BigInt(item.inputVaultBalanceChange.amount), - Number(item.inputVaultBalanceChange.vault.token.decimals ?? 0), - )} - {item.inputVaultBalanceChange.vault.token.symbol} - - - {formatUnits( - BigInt(item.outputVaultBalanceChange.amount) * BigInt(-1), - Number(item.outputVaultBalanceChange.vault.token.decimals ?? 0), - )} - {item.outputVaultBalanceChange.vault.token.symbol} - - - {Math.abs( - Number( - formatUnits( - BigInt(item.inputVaultBalanceChange.amount), - Number(item.inputVaultBalanceChange.vault.token.decimals ?? 0), - ), - ) / - Number( - formatUnits( - BigInt(item.outputVaultBalanceChange.amount), - Number(item.outputVaultBalanceChange.vault.token.decimals ?? 0), - ), - ), - )} - - ({Math.abs( - Number( - formatUnits( - BigInt(item.outputVaultBalanceChange.amount), - Number(item.outputVaultBalanceChange.vault.token.decimals ?? 0), - ), - ) / - Number( - formatUnits( - BigInt(item.inputVaultBalanceChange.amount), - Number(item.inputVaultBalanceChange.vault.token.decimals ?? 0), - ), - ), - )}) - - - - - - -
diff --git a/tauri-app/src/lib/components/tables/OrderTradesListTable.test.ts b/tauri-app/src/lib/components/tables/OrderTradesListTable.test.ts deleted file mode 100644 index 95bf9fd40..000000000 --- a/tauri-app/src/lib/components/tables/OrderTradesListTable.test.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/svelte'; -import { test, vi, type Mock } from 'vitest'; -import { expect } from '$lib/test/matchers'; -import { mockIPC } from '@tauri-apps/api/mocks'; -import type { Trade } from '$lib/typeshare/subgraphTypes'; -import { formatUnits } from 'viem'; -import OrderTradesListTable from './OrderTradesListTable.svelte'; -import { QueryClient } from '@tanstack/svelte-query'; - -vi.mock('$lib/stores/settings', async (importOriginal) => { - const { writable } = await import('svelte/store'); - const { mockSettingsStore } = await import('@rainlanguage/ui-components'); - - const _activeOrderbook = writable(); - - return { - ...((await importOriginal()) as object), - settings: mockSettingsStore, - subgraphUrl: writable('https://example.com'), - activeOrderbook: { - ..._activeOrderbook, - load: vi.fn(() => _activeOrderbook.set(true)), - }, - }; -}); - -vi.mock('$lib/services/modal', async () => { - return { - handleDepositGenericModal: vi.fn(), - handleDepositModal: vi.fn(), - handleWithdrawModal: vi.fn(), - }; -}); - -const mockTradeOrdersList: Trade[] = [ - { - id: '1', - timestamp: '1632000000', - tradeEvent: { - sender: 'sender_address', - transaction: { - id: 'transaction_id', - from: 'sender_address', - timestamp: '1632000000', - blockNumber: '0', - }, - }, - outputVaultBalanceChange: { - amount: '-100', - vault: { - id: 'id', - vault_id: 'vault-id', - token: { - id: 'output_token', - address: 'output_token', - name: 'output_token', - symbol: 'output_token', - decimals: '1', - }, - }, - id: '1', - typename: 'Withdraw', - newVaultBalance: '0', - oldVaultBalance: '0', - timestamp: '0', - transaction: { - id: 'transaction_id', - from: 'sender_address', - timestamp: '1632000000', - blockNumber: '0', - }, - orderbook: { id: '1' }, - }, - order: { - id: 'order_id', - orderHash: 'orderHash', - }, - inputVaultBalanceChange: { - vault: { - id: 'id', - vault_id: 'vault-id', - token: { - id: 'output_token', - address: 'output_token', - name: 'output_token', - symbol: 'output_token', - decimals: '1', - }, - }, - amount: '50', - id: '1', - typename: 'Withdraw', - newVaultBalance: '0', - oldVaultBalance: '0', - timestamp: '0', - transaction: { - id: 'transaction_id', - from: 'sender_address', - timestamp: '1632000000', - blockNumber: '0', - }, - orderbook: { id: '1' }, - }, - orderbook: { - id: '0x00', - }, - }, - { - id: '2', - timestamp: '1632000000', - tradeEvent: { - sender: 'sender_address', - transaction: { - id: 'transaction_id', - from: 'sender_address', - timestamp: '1632000000', - blockNumber: '0', - }, - }, - outputVaultBalanceChange: { - amount: '-100', - vault: { - id: 'id', - vault_id: 'vault-id', - token: { - id: 'output_token', - address: 'output_token', - name: 'output_token', - symbol: 'output_token', - decimals: '1', - }, - }, - id: '1', - typename: 'Withdraw', - newVaultBalance: '0', - oldVaultBalance: '0', - timestamp: '0', - transaction: { - id: 'transaction_id', - from: 'sender_address', - timestamp: '1632000000', - blockNumber: '0', - }, - orderbook: { id: '1' }, - }, - order: { - id: 'order_id', - orderHash: 'orderHash', - }, - inputVaultBalanceChange: { - vault: { - id: 'id', - vault_id: 'vault-id', - token: { - id: 'output_token', - address: 'output_token', - name: 'output_token', - symbol: 'output_token', - decimals: '1', - }, - }, - amount: '50', - id: '1', - typename: 'Withdraw', - newVaultBalance: '0', - oldVaultBalance: '0', - timestamp: '0', - transaction: { - id: 'transaction_id', - from: 'sender_address', - timestamp: '1632000000', - blockNumber: '0', - }, - orderbook: { id: '1' }, - }, - orderbook: { - id: '0x00', - }, - }, -]; - -vi.mock('@tanstack/svelte-query'); - -test('renders table with correct data', async () => { - const queryClient = new QueryClient(); - - const mockQuery = vi.mocked(await import('@tanstack/svelte-query')); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - mockQuery.createInfiniteQuery = vi.fn((__options, _queryClient) => ({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - subscribe: (fn: (value: any) => void) => { - fn({ - data: { pages: [mockTradeOrdersList] }, - status: 'success', - isFetching: false, - isFetched: true, - }); - return { unsubscribe: () => {} }; - }, - })) as Mock; - - render(OrderTradesListTable, { - context: new Map([['$$_queryClient', queryClient]]), - props: { id: '1' }, - }); - - await waitFor(async () => { - // get all the io ratios - const rows = screen.getAllByTestId('io-ratio'); - - // checking the io ratios - for (let i = 0; i < mockTradeOrdersList.length; i++) { - const inputDisplay = formatUnits( - BigInt(mockTradeOrdersList[i].inputVaultBalanceChange.amount), - Number(mockTradeOrdersList[i].inputVaultBalanceChange.vault.token.decimals), - ); - const outputDisplay = formatUnits( - BigInt(mockTradeOrdersList[i].outputVaultBalanceChange.amount), - Number(mockTradeOrdersList[i].outputVaultBalanceChange.vault.token.decimals), - ); - const ioRatio = Number(inputDisplay) / (Number(outputDisplay) * -1); - const oiRatio = (Number(outputDisplay) * -1) / Number(inputDisplay); - expect(rows[i]).toHaveTextContent(ioRatio.toString()); - expect(rows[i]).toHaveTextContent(oiRatio.toString()); - } - }); -}); - -test('renders a debug button for each trade', async () => { - const queryClient = new QueryClient(); - - mockIPC((cmd) => { - if (cmd === 'order_trades_list') { - return mockTradeOrdersList; - } - }); - - render(OrderTradesListTable, { - context: new Map([['$$_queryClient', queryClient]]), - props: { id: '1' }, - }); - - await waitFor(async () => { - const buttons = screen.getAllByTestId('debug-trade-button'); - expect(buttons).toHaveLength(mockTradeOrdersList.length); - }); -}); diff --git a/tauri-app/src/lib/components/tables/OrderVaultsVolTable.svelte b/tauri-app/src/lib/components/tables/OrderVaultsVolTable.svelte index cfe091ded..7167946a8 100644 --- a/tauri-app/src/lib/components/tables/OrderVaultsVolTable.svelte +++ b/tauri-app/src/lib/components/tables/OrderVaultsVolTable.svelte @@ -8,7 +8,7 @@ import { Hash, HashType } from '@rainlanguage/ui-components'; import { formatUnits } from 'viem'; - import TableTimeFilters from '../charts/TableTimeFilters.svelte'; + import { TableTimeFilters } from '@rainlanguage/ui-components'; import { bigintStringToHex } from '@rainlanguage/ui-components'; export let id: string; diff --git a/tauri-app/src/routes/license/License.svelte b/tauri-app/src/routes/license/License.svelte index 7ccbf1749..ec2abc45f 100644 --- a/tauri-app/src/routes/license/License.svelte +++ b/tauri-app/src/routes/license/License.svelte @@ -1,10 +1,7 @@