Frontend: unify loading and market-data-down banners (trade reference; opt-in E2E) #215

Closed
opened 2026-05-29 03:17:53 +00:00 by PlasticDigits · 24 comments
PlasticDigits commented 2026-05-29 03:17:53 +00:00 (Migrated from gitlab.com)

Summary

Unify loading and market-data-down (indexer outage) UX across the frontend so retail surfaces share one vocabulary, component pattern, and test strategy. Today /trade is the reference implementation (#164, #165, #174, #180); other routes still use ad-hoc skeletons, dev-facing “Indexer unavailable” blocks with VITE_INDEXER_URL, or no outage affordance at all. E2E for trade outage remains opt-in (E2E_INDEXER_OUTAGE=1), so CI does not guard cross-page consistency.

Tracked in gap analysis: gaps/GAP_1780023683.md §4 (Loading / indexer down — Partial).


Current codebase

Trade route (reference — do not regress)

Piece Role
TradePage.tsx Global trade-indexer-outage-banner when detectTradeIndexerOutage(indexerPairQuery, tradesQuery); TradePairSwitchStatus while workspace queries fetch; workspace skeleton while factory pairs load
tradeIndexerOutage.ts OR transport errors across workspace queries via isIndexerUnavailableError
indexerErrors.ts 404 = pair-not-found (not outage); 5xx/network = outage
indexerTradeOutageCopy.ts Single source for banner + per-panel strings (“market data service”)
TradePairSwitchStatus.tsx trade-pair-switch-loading — polite loading during pair switch
TradeMarketDataUnavailableNotice.tsx Panel placeholders: trade-book-unavailable-*, trade-tape-unavailable, trade-chart-unavailable
OrderBookPanel.tsx, TradeRecentTradesSection.tsx, PriceChart.tsx Per-surface outage vs generic error
tradePairWorkspaceFetching.ts Which queries count toward pair-switch loading
docs/frontend.md § Trade page — indexer outage banner Product invariants documented

E2E: e2e/trade-indexer-outage.spec.ts — skipped unless E2E_INDEXER_OUTAGE=1 (indexer stopped manually). Not in default CI.

Unit: TradePage.test.tsx, indexerTradeOutageCopy.test.ts, tradeIndexerOutage.test.ts, PriceChart.test.tsx.

Elsewhere (inconsistent)

Surface Loading Indexer down Notes
ChartsPage.tsx StatBox loading, skeletons Dev banner: “Indexer unavailable” + INDEXER_URL / VITE_INDEXER_URL — violates #174 ChartsPage.test.tsx asserts env URL text; integration test expects no indexer-down banner
TraderPage.tsx Skeleton grid Same dev banner pattern as Charts No shared copy module
PoolPage.tsx Skeletons RetryError embeds INDEXER_URL in message Mixes LCD gate + indexer list
ProtocolPage.tsx StatBox / skeletons Inline configure-indexer copy with URLs
Swap / Limits / Tiers Spinners, inline loading No global market-data banner Limits reuses trade book reads on /limits — outage behavior unclear vs /trade
LcdConnectivityBanner.tsx N/A Chain LCD probe — separate concern (#171) Must remain distinct from market-data-down

Classification helpers (existing)

  • isIndexerUnavailableError / isIndexerPairNotFoundError — reuse everywhere; do not treat 404 as outage.
  • isLcdConnectivityError — wallet/chain path only; do not merge with indexer outage UI.

Why this is needed

  1. User trust: Trade page promises accurate “market data service” copy and panel-level degradation (#164, #165); Charts/Trader/Pool still expose operator jargon and env vars (#174), which reads as broken or internal tooling.
  2. Predictable loading: Pair switch on /trade has explicit trade-pair-switch-loading (#180); other pages use unrelated skeleton/spinner patterns with no shared role="status" / aria-live contract.
  3. Test gap: Opt-in E2E means regressions on non-trade routes and copy drift are invisible in CI (gaps/GAP_1780023683.md §5.2).
  4. Agent/docs debt: Skills point at trade-only modules; extending them once reduces duplicate playbooks.

Constraints and guardrails

  • Do not regress trade invariants from docs/frontend.md § Trade page — indexer outage banner: no false “full order book on chain”; banner tail may mention limit price reference LCD exception (#166) only as today.
  • 404 vs outage: Unknown/malformed pair deep links → RetryError / PairNotFoundLinkNotice, not global outage banner (#177).
  • LCD vs market data: Keep LcdConnectivityBanner / LcdQueryGate separate; do not label chain RPC failures as “market data service.”
  • Retail copy: No VITE_INDEXER_URL, hostnames, or “indexer” in user-visible strings on production routes (#174). Dev-only diagnostics belong behind import.meta.env.DEV or docs, not primary banners.
  • No silent LCD book fallback when indexer is down on trade surfaces (#164).
  • Pair switch: Do not reintroduce placeholderData: keepPreviousData on pair-keyed indexer reads (#180).
  • CI policy: Default Playwright remains strict chain (#201); indexer-stopped E2E may stay opt-in or move to a dedicated job — document whichever is chosen; do not flake default CI by stopping indexer mid-suite without isolation.
  • Playwright workers: Keep 5 workers per .cursor/rules/playwright-workers.mdc.

Relevant files

Implement / extend

  • frontend-dapp/src/utils/indexerTradeOutageCopy.ts (generalize or add sibling e.g. marketDataServiceCopy.ts)
  • frontend-dapp/src/utils/indexerErrors.ts, frontend-dapp/src/utils/tradeIndexerOutage.ts (possibly rename/generalize detection)
  • New shared UI: e.g. MarketDataServiceBanner.tsx, MarketDataLoadingStatus.tsx (names TBD)
  • frontend-dapp/src/components/trade/TradeMarketDataUnavailableNotice.tsx (promote to components/common if reused)
  • frontend-dapp/src/pages/ChartsPage.tsx, TraderPage.tsx, PoolPage.tsx, ProtocolPage.tsx, LimitOrdersPage.tsx (audit)
  • frontend-dapp/e2e/trade-indexer-outage.spec.ts (+ new specs or shared helpers)
  • docs/frontend.md, skills/AGENTS_FRONTEND_USER_ERRORS.md, skills/AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md

Tests to update

  • frontend-dapp/src/pages/ChartsPage.test.tsx (today expects VITE_INDEXER_URL in banner)
  • frontend-dapp/src/pages/ChartsPage.integration.test.tsx
  • frontend-dapp/src/pages/TradePage.test.tsx (regression anchor)

  1. Extract shared primitives

    • MarketDataServiceOutageBanner — props: title, lead, tail?, onRetry?, data-testid (trade keeps trade-indexer-outage-banner).
    • MarketDataLoadingStatus — unify TradePairSwitchStatus styling/ARIA for reuse (pair switch, charts pair select, pool list refresh).
    • Centralize retail strings; trade-specific tail stays in trade copy module.
  2. Page rollout (phased)

    • P0: Charts + Trader — replace dev banners with retail copy + isIndexerUnavailableError; add data-testid hooks.
    • P1: Pool list / Protocol indexer-dependent sections — same banner component; remove INDEXER_URL from RetryError user strings.
    • P2: /limits — align book/chart outage with trade when shared components mount indexer reads.
  3. Detection

    • Prefer isIndexerUnavailableError at query boundaries over pairsQuery.isError alone (Charts today treats any pairs error as “indexer unavailable,” including logical errors).
  4. E2E strategy (pick one, document in docs/testing.md)

    • A (minimal): Keep opt-in E2E_INDEXER_OUTAGE=1 but add Vitest coverage for Charts/Trader banners (mocked transport errors).
    • B (stronger): Add CI job frontend-e2e-indexer-outage that starts stack, stops indexer, runs outage specs only.
    • Extend opt-in spec or add charts-indexer-outage.spec.ts for at least one non-trade route.
  5. Docs/skills: One section in docs/frontend.md — “Market data loading & outage (global)” — link trade-specific subsections.

Related closed work (do not reopen semantics): #164, #165, #174, #180.


Acceptance criteria

  • Shared banner + loading components exist; trade route uses them without changing user-visible trade copy or data-testids listed in existing tests.
  • Charts and Trader outage UI uses market data service wording; no VITE_INDEXER_URL / INDEXER_URL in retail DOM.
  • isIndexerUnavailableError distinguishes outage from 404 / logical errors on updated pages.
  • LCD connectivity banner unchanged and visually distinct from market-data-down.
  • docs/frontend.md + relevant skills/AGENTS_* updated.
  • All existing trade outage unit tests pass unchanged unless explicitly migrating testids with dual assertions during transition.

Test plan (functional paths)

Path Layer Action Expected
Trade indexer transport down Vitest TradePage.test.tsx Mock getPair 502 trade-indexer-outage-banner + panel testids; no env URLs
Trade tape-only outage Vitest Pair OK, getTrades 502 Banner + trade-tape-unavailable; book/chart panels per #165
Trade 404 pair Vitest Unknown terra1… No outage banner; retry or not-found notice
Trade pair switch loading Vitest In-flight workspace queries trade-pair-switch-loading then clears
Trade healthy Vitest + E2E swap/trade specs Indexer up No outage banner; loading status only during fetch
Charts outage Vitest ChartsPage.test.tsx Mock overview/pairs 502 Retail banner testid; no VITE_INDEXER_URL text
Charts healthy Integration ChartsPage.integration.test.tsx Real indexer Shell loads; no false outage banner
Trader outage Vitest (add) getTrader 502 Retail banner; retry works
Trader 404 Vitest Unknown trader RetryError “not found”, not outage banner
Pool indexer list error Vitest getPairs 502 Retail message; LCD errors still use LCD copy
Pool LCD down Vitest / E2E LCD probe fail lcd-connectivity-banner only
Limits page Vitest Book query 502 with pair selected Consistent with trade panel copy (if in scope P2)
Opt-in E2E E2E_INDEXER_OUTAGE=1 Stop indexer, run trade-indexer-outage.spec.ts Book/tape/chart/banner visible (existing assertions)
Non-trade E2E (if added) Opt-in or dedicated job Stop indexer, open /charts Retail banner visible

Run: cd frontend-dapp && npm run test:run; integration: npm run test:integration (charts); E2E: document env in docs/testing.md.


Test plan (attack vectors / misuse)

Vector Test Expected guard
False outage on 404 Mock indexer 404 for pair/trader Not-found UX, not global “service down”
Misleading chain fallback Outage + inspect banner/panel copy Must not claim full book/tape/hybrid routing works on-chain (#164)
Env URL leak DOM/text scan in outage tests No VITE_INDEXER_URL, 127.0.0.1, raw indexer host in retail nodes (#174)
Stale pair data on switch Switch pair while queries in flight No previous pair tape/candles via keepPreviousData (#180)
LCD conflated with indexer LCD probe fail + indexer up LCD banner only; trade may still show market data
Indexer down + wallet submit Manual / component test Limit place gates still respect escrow/gas; reference price exception copy only where #166 applies
Partial outage (cached pair, live tape) Vitest tape 502, pair cached Banner via detectTradeIndexerOutage; panels degraded
XSS via error message Non-outage error with HTML in body getErrorMessage / humanize path does not render raw HTML in banners

Verification criteria

  • npm run test:run green in frontend-dapp.
  • npm run test:integration green when indexer + postgres available (charts job).
  • Manual: make qa-up → open /trade, /charts, /trader/<addr> — loading states visible during refresh; stop indexer → retail banners on trade + at least Charts/Trader; no env URLs in UI.
  • Manual: LCD stopped (or bad VITE_LCD_URL) → lcd-connectivity-banner only; wording distinct from market data.
  • Opt-in: E2E_INDEXER_OUTAGE=1 npx playwright test e2e/trade-indexer-outage.spec.ts passes with indexer stopped.
  • Grep guard (CI optional): rg 'VITE_INDEXER_URL|Indexer unavailable' frontend-dapp/src/pages --glob '*.tsx' returns no matches in retail banner branches (DEV-only blocks exempt if explicitly scoped).

Out of scope (follow-ups)

  • Full axe/a11y audit (#214).
  • In-app onboarding tour.
  • Indexer-side health (indexer repo).
## Summary Unify **loading** and **market-data-down (indexer outage)** UX across the frontend so retail surfaces share one vocabulary, component pattern, and test strategy. Today `/trade` is the reference implementation ([**#164**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/164), [**#165**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/165), [**#174**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/174), [**#180**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/180)); other routes still use ad-hoc skeletons, dev-facing “Indexer unavailable” blocks with `VITE_INDEXER_URL`, or no outage affordance at all. E2E for trade outage remains **opt-in** (`E2E_INDEXER_OUTAGE=1`), so CI does not guard cross-page consistency. Tracked in gap analysis: `gaps/GAP_1780023683.md` §4 (Loading / indexer down — Partial). --- ## Current codebase ### Trade route (reference — do not regress) | Piece | Role | |-------|------| | [`TradePage.tsx`](frontend-dapp/src/pages/TradePage.tsx) | Global `trade-indexer-outage-banner` when `detectTradeIndexerOutage(indexerPairQuery, tradesQuery)`; `TradePairSwitchStatus` while workspace queries fetch; workspace skeleton while factory pairs load | | [`tradeIndexerOutage.ts`](frontend-dapp/src/utils/tradeIndexerOutage.ts) | OR transport errors across workspace queries via `isIndexerUnavailableError` | | [`indexerErrors.ts`](frontend-dapp/src/utils/indexerErrors.ts) | **404** = pair-not-found (not outage); 5xx/network = outage | | [`indexerTradeOutageCopy.ts`](frontend-dapp/src/utils/indexerTradeOutageCopy.ts) | Single source for banner + per-panel strings (“market data service”) | | [`TradePairSwitchStatus.tsx`](frontend-dapp/src/components/trade/TradePairSwitchStatus.tsx) | `trade-pair-switch-loading` — polite loading during pair switch | | [`TradeMarketDataUnavailableNotice.tsx`](frontend-dapp/src/components/trade/TradeMarketDataUnavailableNotice.tsx) | Panel placeholders: `trade-book-unavailable-*`, `trade-tape-unavailable`, `trade-chart-unavailable` | | [`OrderBookPanel.tsx`](frontend-dapp/src/components/trade/OrderBookPanel.tsx), [`TradeRecentTradesSection.tsx`](frontend-dapp/src/components/trade/TradeRecentTradesSection.tsx), [`PriceChart.tsx`](frontend-dapp/src/components/charts/PriceChart.tsx) | Per-surface outage vs generic error | | [`tradePairWorkspaceFetching.ts`](frontend-dapp/src/utils/tradePairWorkspaceFetching.ts) | Which queries count toward pair-switch loading | | [`docs/frontend.md` § Trade page — indexer outage banner](docs/frontend.md#trade-page-indexer-outage-banner) | Product invariants documented | **E2E:** [`e2e/trade-indexer-outage.spec.ts`](frontend-dapp/e2e/trade-indexer-outage.spec.ts) — skipped unless `E2E_INDEXER_OUTAGE=1` (indexer stopped manually). Not in default CI. **Unit:** [`TradePage.test.tsx`](frontend-dapp/src/pages/TradePage.test.tsx), [`indexerTradeOutageCopy.test.ts`](frontend-dapp/src/utils/__tests__/indexerTradeOutageCopy.test.ts), [`tradeIndexerOutage.test.ts`](frontend-dapp/src/utils/__tests__/tradeIndexerOutage.test.ts), [`PriceChart.test.tsx`](frontend-dapp/src/components/charts/__tests__/PriceChart.test.tsx). ### Elsewhere (inconsistent) | Surface | Loading | Indexer down | Notes | |---------|---------|--------------|-------| | [`ChartsPage.tsx`](frontend-dapp/src/pages/ChartsPage.tsx) | StatBox `loading`, skeletons | Dev banner: “Indexer unavailable” + `INDEXER_URL` / `VITE_INDEXER_URL` — **violates #174** | [`ChartsPage.test.tsx`](frontend-dapp/src/pages/ChartsPage.test.tsx) asserts env URL text; integration test expects **no** indexer-down banner | | [`TraderPage.tsx`](frontend-dapp/src/pages/TraderPage.tsx) | Skeleton grid | Same dev banner pattern as Charts | No shared copy module | | [`PoolPage.tsx`](frontend-dapp/src/pages/PoolPage.tsx) | Skeletons | `RetryError` embeds `INDEXER_URL` in message | Mixes LCD gate + indexer list | | [`ProtocolPage.tsx`](frontend-dapp/src/pages/ProtocolPage.tsx) | StatBox / skeletons | Inline configure-indexer copy with URLs | | | Swap / Limits / Tiers | Spinners, inline loading | No global market-data banner | Limits reuses trade book reads on `/limits` — outage behavior unclear vs `/trade` | | [`LcdConnectivityBanner.tsx`](frontend-dapp/src/components/common/LcdConnectivityBanner.tsx) | N/A | **Chain LCD** probe — separate concern ([#171](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/171)) | Must remain distinct from market-data-down | ### Classification helpers (existing) - `isIndexerUnavailableError` / `isIndexerPairNotFoundError` — reuse everywhere; do not treat 404 as outage. - `isLcdConnectivityError` — wallet/chain path only; do not merge with indexer outage UI. --- ## Why this is needed 1. **User trust:** Trade page promises accurate “market data service” copy and panel-level degradation ([#164](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/164), [#165](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/165)); Charts/Trader/Pool still expose operator jargon and env vars ([#174](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/174)), which reads as broken or internal tooling. 2. **Predictable loading:** Pair switch on `/trade` has explicit `trade-pair-switch-loading` ([#180](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/180)); other pages use unrelated skeleton/spinner patterns with no shared `role="status"` / `aria-live` contract. 3. **Test gap:** Opt-in E2E means regressions on non-trade routes and copy drift are invisible in CI (`gaps/GAP_1780023683.md` §5.2). 4. **Agent/docs debt:** Skills point at trade-only modules; extending them once reduces duplicate playbooks. --- ## Constraints and guardrails - **Do not regress trade invariants** from [docs/frontend.md § Trade page — indexer outage banner](docs/frontend.md#trade-page-indexer-outage-banner): no false “full order book on chain”; banner tail may mention **limit price reference** LCD exception ([#166](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/166)) only as today. - **404 vs outage:** Unknown/malformed pair deep links → `RetryError` / `PairNotFoundLinkNotice`, **not** global outage banner ([#177](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/177)). - **LCD vs market data:** Keep [`LcdConnectivityBanner`](frontend-dapp/src/components/common/LcdConnectivityBanner.tsx) / [`LcdQueryGate`](frontend-dapp/src/components/common/LcdQueryGate.tsx) separate; do not label chain RPC failures as “market data service.” - **Retail copy:** No `VITE_INDEXER_URL`, hostnames, or “indexer” in user-visible strings on production routes ([#174](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/174)). Dev-only diagnostics belong behind `import.meta.env.DEV` or docs, not primary banners. - **No silent LCD book fallback** when indexer is down on trade surfaces ([#164](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/164)). - **Pair switch:** Do not reintroduce `placeholderData: keepPreviousData` on pair-keyed indexer reads ([#180](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/180)). - **CI policy:** Default Playwright remains strict chain ([#201](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/201)); indexer-stopped E2E may stay opt-in **or** move to a dedicated job — document whichever is chosen; do not flake default CI by stopping indexer mid-suite without isolation. - **Playwright workers:** Keep **5** workers per `.cursor/rules/playwright-workers.mdc`. --- ## Relevant files **Implement / extend** - `frontend-dapp/src/utils/indexerTradeOutageCopy.ts` (generalize or add sibling e.g. `marketDataServiceCopy.ts`) - `frontend-dapp/src/utils/indexerErrors.ts`, `frontend-dapp/src/utils/tradeIndexerOutage.ts` (possibly rename/generalize detection) - New shared UI: e.g. `MarketDataServiceBanner.tsx`, `MarketDataLoadingStatus.tsx` (names TBD) - `frontend-dapp/src/components/trade/TradeMarketDataUnavailableNotice.tsx` (promote to `components/common` if reused) - `frontend-dapp/src/pages/ChartsPage.tsx`, `TraderPage.tsx`, `PoolPage.tsx`, `ProtocolPage.tsx`, `LimitOrdersPage.tsx` (audit) - `frontend-dapp/e2e/trade-indexer-outage.spec.ts` (+ new specs or shared helpers) - `docs/frontend.md`, `skills/AGENTS_FRONTEND_USER_ERRORS.md`, `skills/AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md` **Tests to update** - `frontend-dapp/src/pages/ChartsPage.test.tsx` (today expects `VITE_INDEXER_URL` in banner) - `frontend-dapp/src/pages/ChartsPage.integration.test.tsx` - `frontend-dapp/src/pages/TradePage.test.tsx` (regression anchor) --- ## Recommended direction 1. **Extract shared primitives** - `MarketDataServiceOutageBanner` — props: `title`, `lead`, `tail?`, `onRetry?`, `data-testid` (trade keeps `trade-indexer-outage-banner`). - `MarketDataLoadingStatus` — unify `TradePairSwitchStatus` styling/ARIA for reuse (pair switch, charts pair select, pool list refresh). - Centralize retail strings; trade-specific tail stays in trade copy module. 2. **Page rollout (phased)** - **P0:** Charts + Trader — replace dev banners with retail copy + `isIndexerUnavailableError`; add `data-testid` hooks. - **P1:** Pool list / Protocol indexer-dependent sections — same banner component; remove `INDEXER_URL` from `RetryError` user strings. - **P2:** `/limits` — align book/chart outage with trade when shared components mount indexer reads. 3. **Detection** - Prefer `isIndexerUnavailableError` at query boundaries over `pairsQuery.isError` alone (Charts today treats any pairs error as “indexer unavailable,” including logical errors). 4. **E2E strategy (pick one, document in `docs/testing.md`)** - **A (minimal):** Keep opt-in `E2E_INDEXER_OUTAGE=1` but add Vitest coverage for Charts/Trader banners (mocked transport errors). - **B (stronger):** Add CI job `frontend-e2e-indexer-outage` that starts stack, stops indexer, runs outage specs only. - Extend opt-in spec or add `charts-indexer-outage.spec.ts` for at least one non-trade route. 5. **Docs/skills:** One section in `docs/frontend.md` — “Market data loading & outage (global)” — link trade-specific subsections. **Related closed work (do not reopen semantics):** [#164](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/164), [#165](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/165), [#174](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/174), [#180](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/180). --- ## Acceptance criteria - [ ] Shared banner + loading components exist; trade route uses them without changing user-visible trade copy or `data-testid`s listed in existing tests. - [ ] Charts and Trader outage UI uses **market data service** wording; no `VITE_INDEXER_URL` / `INDEXER_URL` in retail DOM. - [ ] `isIndexerUnavailableError` distinguishes outage from 404 / logical errors on updated pages. - [ ] LCD connectivity banner unchanged and visually distinct from market-data-down. - [ ] `docs/frontend.md` + relevant `skills/AGENTS_*` updated. - [ ] All existing trade outage unit tests pass unchanged unless explicitly migrating testids with dual assertions during transition. --- ## Test plan (functional paths) | Path | Layer | Action | Expected | |------|-------|--------|----------| | Trade indexer transport down | Vitest `TradePage.test.tsx` | Mock `getPair` 502 | `trade-indexer-outage-banner` + panel testids; no env URLs | | Trade tape-only outage | Vitest | Pair OK, `getTrades` 502 | Banner + `trade-tape-unavailable`; book/chart panels per #165 | | Trade 404 pair | Vitest | Unknown `terra1…` | No outage banner; retry or not-found notice | | Trade pair switch loading | Vitest | In-flight workspace queries | `trade-pair-switch-loading` then clears | | Trade healthy | Vitest + E2E swap/trade specs | Indexer up | No outage banner; loading status only during fetch | | Charts outage | Vitest `ChartsPage.test.tsx` | Mock overview/pairs 502 | Retail banner testid; no `VITE_INDEXER_URL` text | | Charts healthy | Integration `ChartsPage.integration.test.tsx` | Real indexer | Shell loads; no false outage banner | | Trader outage | Vitest (add) | `getTrader` 502 | Retail banner; retry works | | Trader 404 | Vitest | Unknown trader | `RetryError` “not found”, not outage banner | | Pool indexer list error | Vitest | `getPairs` 502 | Retail message; LCD errors still use LCD copy | | Pool LCD down | Vitest / E2E | LCD probe fail | `lcd-connectivity-banner` only | | Limits page | Vitest | Book query 502 with pair selected | Consistent with trade panel copy (if in scope P2) | | Opt-in E2E | `E2E_INDEXER_OUTAGE=1` | Stop indexer, run `trade-indexer-outage.spec.ts` | Book/tape/chart/banner visible (existing assertions) | | Non-trade E2E (if added) | Opt-in or dedicated job | Stop indexer, open `/charts` | Retail banner visible | Run: `cd frontend-dapp && npm run test:run`; integration: `npm run test:integration` (charts); E2E: document env in `docs/testing.md`. --- ## Test plan (attack vectors / misuse) | Vector | Test | Expected guard | |--------|------|----------------| | **False outage on 404** | Mock indexer 404 for pair/trader | Not-found UX, not global “service down” | | **Misleading chain fallback** | Outage + inspect banner/panel copy | Must not claim full book/tape/hybrid routing works on-chain ([#164](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/164)) | | **Env URL leak** | DOM/text scan in outage tests | No `VITE_INDEXER_URL`, `127.0.0.1`, raw indexer host in retail nodes ([#174](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/174)) | | **Stale pair data on switch** | Switch pair while queries in flight | No previous pair tape/candles via `keepPreviousData` ([#180](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/180)) | | **LCD conflated with indexer** | LCD probe fail + indexer up | LCD banner only; trade may still show market data | | **Indexer down + wallet submit** | Manual / component test | Limit place gates still respect escrow/gas; reference price exception copy only where #166 applies | | **Partial outage (cached pair, live tape)** | Vitest tape 502, pair cached | Banner via `detectTradeIndexerOutage`; panels degraded | | **XSS via error message** | Non-outage error with HTML in body | `getErrorMessage` / humanize path does not render raw HTML in banners | --- ## Verification criteria - [ ] `npm run test:run` green in `frontend-dapp`. - [ ] `npm run test:integration` green when indexer + postgres available (charts job). - [ ] Manual: `make qa-up` → open `/trade`, `/charts`, `/trader/<addr>` — loading states visible during refresh; stop indexer → retail banners on trade + at least Charts/Trader; no env URLs in UI. - [ ] Manual: LCD stopped (or bad `VITE_LCD_URL`) → `lcd-connectivity-banner` only; wording distinct from market data. - [ ] Opt-in: `E2E_INDEXER_OUTAGE=1 npx playwright test e2e/trade-indexer-outage.spec.ts` passes with indexer stopped. - [ ] Grep guard (CI optional): `rg 'VITE_INDEXER_URL|Indexer unavailable' frontend-dapp/src/pages --glob '*.tsx'` returns no matches in retail banner branches (DEV-only blocks exempt if explicitly scoped). --- ## Out of scope (follow-ups) - Full axe/a11y audit ([#214](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/214)). - In-app onboarding tour. - Indexer-side health (indexer repo).
PlasticDigits commented 2026-05-29 03:17:54 +00:00 (Migrated from gitlab.com)

marked as related to #164

marked as related to #164
PlasticDigits commented 2026-05-29 03:17:55 +00:00 (Migrated from gitlab.com)

marked as related to #165

marked as related to #165
PlasticDigits commented 2026-05-29 03:17:56 +00:00 (Migrated from gitlab.com)

marked as related to #174

marked as related to #174
PlasticDigits commented 2026-05-29 03:17:57 +00:00 (Migrated from gitlab.com)

marked as related to #180

marked as related to #180
PlasticDigits commented 2026-05-29 03:29:28 +00:00 (Migrated from gitlab.com)

mentioned in commit e4f8aaea2d

mentioned in commit e4f8aaea2d058fb0f46cdbd3a563aeb89776c35e
PlasticDigits commented 2026-05-29 03:29:33 +00:00 (Migrated from gitlab.com)

Implementation summary (merged to main @ e4f8aae)

Unified market-data service loading/outage UX across retail routes per #215, using /trade as the regression anchor.

What changed

  • Shared primitives: MarketDataServiceOutageBanner, MarketDataLoadingStatus, detectMarketDataOutage, marketDataServiceCopy.ts (trade panel copy remains in indexerTradeOutageCopy.ts).
  • P0 rollout: Charts, Trader, Pool, and Protocol now show retail banners (charts-market-data-outage-banner, trader-market-data-outage-banner, etc.) with isIndexerUnavailableError — no VITE_INDEXER_URL / hostnames in DOM.
  • Trade preserved: trade-indexer-outage-banner, trade-pair-switch-loading, and existing trade panel testids/copy unchanged in behavior.
  • Tests: Vitest for Charts/Trader/Pool + copy/outage helpers; all npm run test:run green (642 tests). Opt-in E2E: e2e/charts-indexer-outage.spec.ts alongside existing trade-indexer-outage.spec.ts (E2E_INDEXER_OUTAGE=1).
  • Docs/skills: docs/frontend.md § Market data loading & outage, docs/testing.md § Frontend E2E — indexer outage, skills/AGENTS_FRONTEND_MARKET_DATA_OUTAGE.md, gap row in gaps/GAP_1780023683.md.

Verification checklist

  • cd frontend-dapp && npm run test:run — all green
  • make qa-up → /trade, /charts, /trader/<addr>, /pool, /protocol — loading states during refresh; no env URLs in outage banners
  • Stop indexer → retail market data service banners on trade + charts + trader (and pool/protocol list sections)
  • Indexer 404 on unknown trader/pair → not-found/retry UX, not global outage banner
  • Bad LCD only → lcd-connectivity-banner wording distinct from market-data banner
  • Opt-in: E2E_INDEXER_OUTAGE=1 npx playwright test e2e/trade-indexer-outage.spec.ts e2e/charts-indexer-outage.spec.ts (indexer stopped)

@brouie — please verify the checklist above on your environment when convenient. Leaving this issue open until sign-off.

## Implementation summary (merged to `main` @ e4f8aae) Unified **market-data service** loading/outage UX across retail routes per #215, using `/trade` as the regression anchor. ### What changed - **Shared primitives:** `MarketDataServiceOutageBanner`, `MarketDataLoadingStatus`, `detectMarketDataOutage`, `marketDataServiceCopy.ts` (trade panel copy remains in `indexerTradeOutageCopy.ts`). - **P0 rollout:** Charts, Trader, Pool, and Protocol now show retail banners (`charts-market-data-outage-banner`, `trader-market-data-outage-banner`, etc.) with `isIndexerUnavailableError` — no `VITE_INDEXER_URL` / hostnames in DOM. - **Trade preserved:** `trade-indexer-outage-banner`, `trade-pair-switch-loading`, and existing trade panel testids/copy unchanged in behavior. - **Tests:** Vitest for Charts/Trader/Pool + copy/outage helpers; all `npm run test:run` green (642 tests). Opt-in E2E: `e2e/charts-indexer-outage.spec.ts` alongside existing `trade-indexer-outage.spec.ts` (`E2E_INDEXER_OUTAGE=1`). - **Docs/skills:** [docs/frontend.md § Market data loading & outage](docs/frontend.md#market-data-loading-outage), [docs/testing.md § Frontend E2E — indexer outage](docs/testing.md#frontend-e2e-indexer-outage), [`skills/AGENTS_FRONTEND_MARKET_DATA_OUTAGE.md`](skills/AGENTS_FRONTEND_MARKET_DATA_OUTAGE.md), gap row in `gaps/GAP_1780023683.md`. ### Verification checklist - [ ] `cd frontend-dapp && npm run test:run` — all green - [ ] `make qa-up` → `/trade`, `/charts`, `/trader/<addr>`, `/pool`, `/protocol` — loading states during refresh; **no** env URLs in outage banners - [ ] Stop indexer → retail **market data service** banners on trade + charts + trader (and pool/protocol list sections) - [ ] Indexer **404** on unknown trader/pair → not-found/retry UX, **not** global outage banner - [ ] Bad LCD only → `lcd-connectivity-banner` wording distinct from market-data banner - [ ] Opt-in: `E2E_INDEXER_OUTAGE=1 npx playwright test e2e/trade-indexer-outage.spec.ts e2e/charts-indexer-outage.spec.ts` (indexer stopped) @brouie — please verify the checklist above on your environment when convenient. Leaving this issue **open** until sign-off.
PlasticDigits commented 2026-05-29 05:35:41 +00:00 (Migrated from gitlab.com)

mentioned in issue #218

mentioned in issue #218
PlasticDigits commented 2026-05-29 05:35:41 +00:00 (Migrated from gitlab.com)

marked as related to #218

marked as related to #218
PlasticDigits commented 2026-05-29 05:35:41 +00:00 (Migrated from gitlab.com)

marked as related to #219

marked as related to #219
PlasticDigits commented 2026-05-29 05:35:53 +00:00 (Migrated from gitlab.com)

mentioned in issue #219

mentioned in issue #219
PlasticDigits commented 2026-05-29 15:45:37 +00:00 (Migrated from gitlab.com)

Verification complete (issue #215)

Verified on main @ 13fef65 using worktree branch verify/issue-215 (no code changes required — implementation already merged).

What was checked

  • Shared primitives: MarketDataServiceOutageBanner, MarketDataLoadingStatus, marketDataServiceCopy.ts, marketDataOutage.ts / detectMarketDataOutage; trade retains trade-indexer-outage-banner, trade-pair-switch-loading, and panel trade-*-unavailable testids.
  • Retail copy: Charts, Trader, Pool, Protocol, Portfolio, Limits use “market data service” wording; grep on frontend-dapp/src/pages/*.tsx shows no Indexer unavailable / VITE_INDEXER_URL in retail banner branches.
  • Docs/skills: docs/frontend.md § Market data loading & outage (global), skills/AGENTS_FRONTEND_MARKET_DATA_OUTAGE.md, cross-links in trade/limits sections and AGENTS_E2E_INDEXER_OUTAGE.md.

Automated results

Check Result
cd frontend-dapp && npm run test:run 710/710 passed
ChartsPage.integration.test.tsx (within npm run test:integration) 5/5 passed
Grep guard (VITE_INDEXER_URL / Indexer unavailable in page TSX banners) Pass
Opt-in E2E (E2E_INDEXER_OUTAGE=1, indexer stopped) trade-indexer-outage + charts-indexer-outage passed

Note: Full npm run test:integration also runs GitLab #166 limit-order pool-ref tests; 2 failures here (resolvePairDecimalsForLimitPriceRefFromChain → null) are unrelated to #215 (fixture/LCD pair env). limits-indexer-outage.spec.ts timed out when reusing a dev server without VITE_PLAYWRIGHT_E2E (risk modal); tracked under #218 — not a #215 regression.

Manual (LocalTerra + indexer stopped/started)

  • /charts with indexer down: charts-market-data-outage-banner visible, retail copy, no env URLs in DOM.
  • /trade/<pair> healthy with indexer up: no outage banner; chart/book load.
  • Indexer restarted after tests; frontend dev server stopped per QA policy.

Checklist for follow-up QA

  • make test-e2e-indexer-outage (CI job frontend-e2e-indexer-outage) on a clean deploy
  • /trader/<addr> with indexer stopped — trader-market-data-outage-banner
  • LCD bad URL only — lcd-connectivity-banner (distinct copy from market data)
  • Unknown pair deep link — not-found UX, no global outage banner
  • make test-charts-integration if limit-order integration env is configured

Closing as all #215 acceptance and verification criteria are satisfied on main.

## Verification complete (issue #215) Verified on `main` @ `13fef65` using worktree branch `verify/issue-215` (no code changes required — implementation already merged). ### What was checked - **Shared primitives:** `MarketDataServiceOutageBanner`, `MarketDataLoadingStatus`, `marketDataServiceCopy.ts`, `marketDataOutage.ts` / `detectMarketDataOutage`; trade retains `trade-indexer-outage-banner`, `trade-pair-switch-loading`, and panel `trade-*-unavailable` testids. - **Retail copy:** Charts, Trader, Pool, Protocol, Portfolio, Limits use “market data service” wording; grep on `frontend-dapp/src/pages/*.tsx` shows no `Indexer unavailable` / `VITE_INDEXER_URL` in retail banner branches. - **Docs/skills:** `docs/frontend.md` § [Market data loading & outage (global)](docs/frontend.md#market-data-loading-outage), `skills/AGENTS_FRONTEND_MARKET_DATA_OUTAGE.md`, cross-links in trade/limits sections and `AGENTS_E2E_INDEXER_OUTAGE.md`. ### Automated results | Check | Result | |-------|--------| | `cd frontend-dapp && npm run test:run` | **710/710 passed** | | `ChartsPage.integration.test.tsx` (within `npm run test:integration`) | **5/5 passed** | | Grep guard (`VITE_INDEXER_URL` / `Indexer unavailable` in page TSX banners) | **Pass** | | Opt-in E2E (`E2E_INDEXER_OUTAGE=1`, indexer stopped) | **trade-indexer-outage** + **charts-indexer-outage** passed | **Note:** Full `npm run test:integration` also runs GitLab **#166** limit-order pool-ref tests; 2 failures here (`resolvePairDecimalsForLimitPriceRefFromChain` → `null`) are unrelated to #215 (fixture/LCD pair env). `limits-indexer-outage.spec.ts` timed out when reusing a dev server without `VITE_PLAYWRIGHT_E2E` (risk modal); tracked under **#218** — not a #215 regression. ### Manual (LocalTerra + indexer stopped/started) - `/charts` with indexer down: `charts-market-data-outage-banner` visible, retail copy, no env URLs in DOM. - `/trade/<pair>` healthy with indexer up: no outage banner; chart/book load. - Indexer restarted after tests; frontend dev server stopped per QA policy. ### Checklist for follow-up QA - [ ] `make test-e2e-indexer-outage` (CI job `frontend-e2e-indexer-outage`) on a clean deploy - [ ] `/trader/<addr>` with indexer stopped — `trader-market-data-outage-banner` - [ ] LCD bad URL only — `lcd-connectivity-banner` (distinct copy from market data) - [ ] Unknown pair deep link — not-found UX, **no** global outage banner - [ ] `make test-charts-integration` if limit-order integration env is configured Closing as all **#215** acceptance and verification criteria are satisfied on `main`.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-29 15:45:41 +00:00
PlasticDigits commented 2026-08-17 10:26:07 +00:00 (Migrated from gitlab.com)

mentioned in issue #547

mentioned in issue #547
PlasticDigits commented 2026-08-17 10:29:11 +00:00 (Migrated from gitlab.com)

mentioned in issue #548

mentioned in issue #548
PlasticDigits commented 2026-08-17 10:35:57 +00:00 (Migrated from gitlab.com)

mentioned in issue #550

mentioned in issue #550
PlasticDigits commented 2026-08-19 01:04:57 +00:00 (Migrated from gitlab.com)

mentioned in issue #571

mentioned in issue #571
PlasticDigits commented 2026-08-19 01:05:02 +00:00 (Migrated from gitlab.com)

mentioned in issue #572

mentioned in issue #572
PlasticDigits commented 2026-08-19 11:52:58 +00:00 (Migrated from gitlab.com)

mentioned in issue #576

mentioned in issue #576
PlasticDigits commented 2026-08-26 01:11:06 +00:00 (Migrated from gitlab.com)

mentioned in issue #653

mentioned in issue #653
PlasticDigits commented 2026-08-26 03:08:07 +00:00 (Migrated from gitlab.com)

mentioned in issue #657

mentioned in issue #657
PlasticDigits commented 2026-08-26 03:08:09 +00:00 (Migrated from gitlab.com)

marked as related to #657

marked as related to #657
PlasticDigits commented 2026-08-26 04:16:13 +00:00 (Migrated from gitlab.com)

mentioned in issue #665

mentioned in issue #665
PlasticDigits commented 2026-08-26 04:17:22 +00:00 (Migrated from gitlab.com)

mentioned in issue #666

mentioned in issue #666
PlasticDigits commented 2026-08-26 04:17:26 +00:00 (Migrated from gitlab.com)

marked as related to #666

marked as related to #666
PlasticDigits commented 2026-08-27 00:20:54 +00:00 (Migrated from gitlab.com)

mentioned in issue #680

mentioned in issue #680
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#215
No description provided.