-
Notifications
You must be signed in to change notification settings - Fork 232
feat: add polling to BackendCapabilitiesBanner, persist OnboardingChe… #1610
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,83 +1,133 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { describe, it, expect, afterEach } from 'vitest' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { describe, it, expect, afterEach, vi } from 'vitest' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { render, screen, cleanup, within } from '@testing-library/react' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import BackendCapabilitiesBanner from './BackendCapabilitiesBanner' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { CapabilityNotice } from '../hooks/useReadinessReport' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vi.mock('../hooks/queries/useReadinessQuery', () => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useReadinessQuery: vi.fn(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useReadinessQuery } from '../hooks/queries/useReadinessQuery' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterEach(cleanup) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const defaultProps = { loadError: false, loading: false, belowRealtimeBar: false } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mockQuery = useReadinessQuery as unknown as ReturnType<typeof vi.fn> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function mockReturn(overrides: Partial<ReturnType<typeof useReadinessQuery>> = {}) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockQuery.mockReturnValue({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notices: [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loadError: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loading: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| report: null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refresh: vi.fn(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...overrides, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function notice(id: string, kind: CapabilityNotice['kind'] = 'disabled', text = 'Some issue.'): CapabilityNotice { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { id, kind, text } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe('BackendCapabilitiesBanner', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| afterEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockQuery.mockReset() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('renders nothing when no notices and no error', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { container } = render(<BackendCapabilitiesBanner {...defaultProps} notices={[]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { container } = render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(container.firstChild).toBeNull() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('renders load-error message when loadError is true and no notices', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} loadError notices={[]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ loadError: true, notices: [] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByRole('status')).toHaveTextContent(/could not load backend service status/i) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('renders notice text', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('database', 'limited', 'DB is down.')]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('database', 'limited', 'DB is down.')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByText(/DB is down\./)).toBeInTheDocument() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('attaches a doc link for database notice', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('database', 'limited')]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('database', 'limited')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const link = screen.getByRole('link', { name: /database setup/i }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(link).toHaveAttribute('href', expect.stringContaining('#database-setup')) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(link).toHaveAttribute('target', '_blank') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(link).toHaveAttribute('rel', 'noopener noreferrer') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('attaches a doc link for queue-workers notice', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('queue-workers')]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('queue-workers')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const link = screen.getByRole('link', { name: /redis \/ worker setup/i }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(link).toHaveAttribute('href', expect.stringContaining('CONTRIBUTING')) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('attaches a doc link for indexer notice', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('indexer')]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('indexer')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const link = screen.getByRole('link', { name: /environment setup/i }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(link).toHaveAttribute('href', expect.stringContaining('ENVIRONMENT')) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('attaches a doc link for auto-rebalancer notice', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('auto-rebalancer')]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('auto-rebalancer')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const link = screen.getByRole('link', { name: /environment setup/i }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(link).toBeInTheDocument() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('renders multiple notices each with their own link', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const notices = [notice('database', 'limited'), notice('queue-workers')] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { container } = render(<BackendCapabilitiesBanner {...defaultProps} notices={notices} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { container } = render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const banner = container.querySelector('[role="status"]')! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(within(banner as HTMLElement).getByRole('link', { name: /database setup/i })).toBeInTheDocument() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(within(banner as HTMLElement).getByRole('link', { name: /redis \/ worker setup/i })).toBeInTheDocument() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('applies top-14 class when belowRealtimeBar is true', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('database', 'limited')]} belowRealtimeBar />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('database', 'limited')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner belowRealtimeBar />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByRole('status').className).toContain('top-14') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('applies top-0 class when belowRealtimeBar is false', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('database', 'limited')]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('database', 'limited')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByRole('status').className).toContain('top-0') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('uses amber styling when any notice is limited', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('database', 'limited')]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('database', 'limited')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByRole('status').className).toContain('amber') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('uses slate styling when all notices are disabled', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner {...defaultProps} notices={[notice('queue-workers', 'disabled')]} />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('queue-workers', 'disabled')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByRole('status').className).not.toContain('amber') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('updates banner after a simulated capability change on subsequent poll', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockReturn({ notices: [notice('database', 'limited', 'Initial issue.')] }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { unmount } = render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByText(/Initial issue\./)).toBeInTheDocument() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unmount() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mockQuery.mockReturnValue({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notices: [notice('database', 'limited', 'Updated issue.')], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loadError: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loading: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| report: null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refresh: vi.fn(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BackendCapabilitiesBanner />) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(screen.getByText(/Updated issue\./)).toBeInTheDocument() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+117
to
+131
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Keep the component mounted when simulating a poll update. Unmounting before changing the mock resets Update the mock return value and call Proposed test adjustment- const { unmount } = render(<BackendCapabilitiesBanner />)
+ const view = render(<BackendCapabilitiesBanner />)
expect(screen.getByText(/Initial issue\./)).toBeInTheDocument()
- unmount()
mockQuery.mockReturnValue({
notices: [notice('database', 'limited', 'Updated issue.')],
loadError: false,
loading: false,
report: null,
refresh: vi.fn(),
})
- render(<BackendCapabilitiesBanner />)
+ view.rerender(<BackendCapabilitiesBanner />)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import React from 'react' | ||
| import { describe, expect, it } from 'vitest' | ||
| import { fireEvent, render, screen } from '@testing-library/react' | ||
| import { describe, expect, it, afterEach } from 'vitest' | ||
| import { cleanup, fireEvent, render, screen } from '@testing-library/react' | ||
| import { QueryClient, QueryClientProvider } from '@tanstack/react-query' | ||
| import CorrelationHeatmap, { correlationColor } from './CorrelationHeatmap' | ||
|
|
||
| const assets = ['XLM', 'BTC', 'ETH'] | ||
|
|
@@ -23,9 +24,19 @@ const matrices = { | |
| ], | ||
| } | ||
|
|
||
| const queryClient = new QueryClient({ | ||
| defaultOptions: { queries: { retry: false }, mutations: { retry: false } }, | ||
| }) | ||
|
|
||
| function Wrapper({ children }: { children: React.ReactNode }) { | ||
| return React.createElement(QueryClientProvider, { client: queryClient }, children) | ||
| } | ||
|
|
||
| afterEach(cleanup) | ||
|
|
||
| describe('CorrelationHeatmap', () => { | ||
| it('renders diagonal cells as 1.0', () => { | ||
| render(<CorrelationHeatmap assets={assets} correlations={matrices} />) | ||
| render(<CorrelationHeatmap assets={assets} correlations={matrices} />, { wrapper: Wrapper }) | ||
|
|
||
| expect(screen.getByTestId('correlation-cell-0-0')).toHaveTextContent('1.0') | ||
| expect(screen.getByTestId('correlation-cell-1-1')).toHaveTextContent('1.0') | ||
|
|
@@ -39,15 +50,15 @@ describe('CorrelationHeatmap', () => { | |
| }) | ||
|
|
||
| it('shows exact coefficient and pair names in the tooltip', async () => { | ||
| render(<CorrelationHeatmap assets={assets} correlations={matrices} />) | ||
| render(<CorrelationHeatmap assets={assets} correlations={matrices} />, { wrapper: Wrapper }) | ||
|
|
||
| fireEvent.mouseEnter(screen.getByTestId('correlation-cell-0-1')) | ||
|
|
||
| expect(await screen.findByRole('tooltip')).toHaveTextContent('XLM / BTC: -0.50') | ||
| }) | ||
|
|
||
| it('switches matrices when the time range changes', () => { | ||
| render(<CorrelationHeatmap assets={assets} correlations={matrices} />) | ||
| render(<CorrelationHeatmap assets={assets} correlations={matrices} />, { wrapper: Wrapper }) | ||
|
|
||
| expect(screen.getByTestId('correlation-cell-0-1')).toHaveTextContent('-0.50') | ||
|
|
||
|
|
@@ -64,9 +75,18 @@ describe('CorrelationHeatmap', () => { | |
| manyAssets.map((__, columnIndex) => (rowIndex === columnIndex ? 1 : 0.1)), | ||
| ) | ||
|
|
||
| render(<CorrelationHeatmap assets={manyAssets} correlations={{ '30D': matrix }} />) | ||
| render(<CorrelationHeatmap assets={manyAssets} correlations={{ '30D': matrix }} />, { wrapper: Wrapper }) | ||
|
|
||
| expect(screen.getAllByRole('gridcell')).toHaveLength(100) | ||
| expect(screen.queryByText('A11')).not.toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('opens a drill-down modal when a cell is clicked with correct asset pair context', async () => { | ||
| render(<CorrelationHeatmap assets={assets} correlations={matrices} />, { wrapper: Wrapper }) | ||
|
|
||
| fireEvent.click(screen.getByTestId('correlation-cell-0-1')) | ||
|
|
||
| expect(screen.getByText(/correlation detail/i)).toBeInTheDocument() | ||
| expect(screen.getByRole('heading', { name: /correlation detail/i })).toBeInTheDocument() | ||
| }) | ||
|
Comment on lines
+84
to
+91
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
fd -H -t f 'vitest.config.*|vite.config.*' frontend --exec cat -n
fd -H -t f 'setup*.ts*' frontend/src --exec cat -n
rg -n --type=ts --type=tsx 'usePriceCandlestickQuery|PRICE_CHART' frontend/src -g '!**/*.test.*'Repository: ritik4ever/stellar-portfolio-rebalancer Length of output: 50396 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== locate heatmap tests and component =="
fd -H -t f 'CorrelationHeatmap' frontend
echo
echo "== test excerpt =="
file="$(fd -H -t f 'CorrelationHeatmap.test.tsx' frontend | head -n1 || true)"
if [ -n "$file" ]; then
wc -l "$file"
sed -n '1,140p' "$file" | cat -n
fi
echo
echo "== component outline relevant file =="
if [ -n "$file" ]; then
comp="$(git rev-parse --show-toplevel)/frontend/src/components/CorrelationHeatmap.tsx"
if [ -f "$comp" ]; then
wc -l "$comp"
ast-grep outline "$comp" --view expanded || true
sed -n '1,260p' "$comp" | cat -n
fi
fi
echo
echo "== hook usages =="
rg -n --type=ts --type=tsx 'usePriceCandlestick|QueryClient|refetchInterval|DataChart|data:' frontend/src -g '!**/*.test.*' --glob '!**/node_modules/**' --max-count 200Repository: ritik4ever/stellar-portfolio-rebalancer Length of output: 17764 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== remaining component render/modal section =="
file="$(git rev-parse --show-toplevel)/frontend/src/components/CorrelationHeatmap.tsx"
sed -n '260,330p' "$file" | cat -n
echo
echo "== hook file =="
hook="$(git rev-parse --show-toplevel)/frontend/src/hooks/queries/usePriceCandlestickQuery.ts" || true
if [ -f "$hook" ]; then
wc -l "$hook"
sed -n '1,220p' "$hook" | cat -n
else
fd -H -t f 'usePriceCandlestickQuery' frontend
fi
echo
echo "== global fetch/api mocks in Vitest setup files =="
sed -n '1,180p' frontend/src/test/setup.ts | cat -n
rg -n --glob '!**/node_modules/**' 'vi\.stubGlobal\(|fetch|global.fetch|axios|XMLHttpRequest|msw|setupServer|http|api|usePriceCandlestickQuery|queryClient' frontend/src frontend/vitest.config.ts frontend/src/test/setup.tsRepository: ritik4ever/stellar-portfolio-rebalancer Length of output: 50395 Assert the clicked pair and mock the candlestick query. The assertions only check the static modal title, so this would pass for the wrong XLM/BTC modal; assert that 🤖 Prompt for AI Agents |
||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: ritik4ever/stellar-portfolio-rebalancer
Length of output: 32422
🏁 Script executed:
Repository: ritik4ever/stellar-portfolio-rebalancer
Length of output: 8725
🏁 Script executed:
Repository: ritik4ever/stellar-portfolio-rebalancer
Length of output: 39398
Use one readiness source for App layout and the banner.
Appstill derivesshowBackendBanner,contentTopPad, anderrorTopfromuseReadinessReport, whileBackendCapabilitiesBannerreadsuseReadinessQuery. Sincerefresh()is not passed intoApp, readiness can update in the banner before the page offset/error positioning catch up, and the two hooks also poll separately. Derive all readiness decisions fromuseReadinessQueryor pass the same hook result into the banner.🤖 Prompt for AI Agents