Frontend: full lightweight-charts coverage in Vitest (beyond jsdom stub) #211

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

Summary

The price chart on /trade and /charts uses TradingView lightweight-charts v5 (open-source canvas library — not the hosted TradingView widget). Node-based Vitest runs under jsdom with a global module stub (lightweightChartsJsdomMock.ts) because jsdom lacks a real Canvas/layout stack. Component tests assert integration wiring (createChart call count, setData payloads, indicator addSeries/removeSeries) but do not exercise the real library. Chart rendering, autoscale behavior, pane layout, resize, and crosshair interactions are deferred to manual QA and sparse Playwright coverage (layout/outage only). This issue tracks closing that gap with full chart testing in Vitest while keeping CI fast and deterministic.


Current codebase

Chart implementation

Area Files
Shell / data fetching frontend-dapp/src/components/charts/PriceChart.tsx
Canvas + library wiring frontend-dapp/src/components/charts/PriceChartLightweightCanvas.tsx
Indicator sync (no full recreate) frontend-dapp/src/components/charts/priceChartLightweightIndicatorSync.ts
OHLC / volume mapping frontend-dapp/src/components/charts/priceChartCandles.ts, priceChartCandlesPlaceholder.ts
Pure math (SMA / RSI) frontend-dapp/src/components/charts/priceChartIndicators.ts
USD Y-axis clamp frontend-dapp/src/components/charts/priceChartPriceScale.ts, priceChartPaneHeights.ts
Headline “Last” price frontend-dapp/src/components/charts/chartHeadlinePrice.ts
UI chrome PriceChartOverlayMenu.tsx, PriceChartEmptyState.tsx

Vitest stub (global)

  • frontend-dapp/src/test/lightweightChartsJsdomMock.ts — vi.mock('lightweight-charts') registered in both:
    • frontend-dapp/vitest.config.ts (unit tests)
    • frontend-dapp/vitest.config.integration.ts (indexer HTTP integration tests)
  • Exports lwChartTestDouble with seriesSpies[] and reset() for assertions on setData.
  • Stub implements: createChart, addSeries, removeSeries, addPane, removePane, panes, timeScale().fitContent, minimal priceScale() / createPriceLine.

Existing tests (stub-backed or pure)

Test file What it covers
__tests__/PriceChart.test.tsx Loading/empty/outage, interval/pair refetch, headline, MA toggle via spy count, createChart once per mount (#148)
__tests__/priceChartLightweightIndicatorSync.test.ts syncPriceChartIndicatorOverlays with hand-rolled chart mocks
__tests__/priceChartCandles.test.ts, priceChartIndicators.test.ts, priceChartPriceScale.test.ts, chartHeadlinePrice.test.ts, priceChartCandlesPlaceholder.test.ts Pure helpers only
src/pages/ChartsPage.integration.test.tsx Indexer HTTP for /charts (still uses stub)
src/pages/TradePage.test.tsx Imports mock explicitly

Documented policy

Playwright today

  • Layout only: e2e/trade-page-responsive.spec.ts (trade-sub-lg-chart-col bounding boxes).
  • Outage copy: e2e/trade-indexer-outage.spec.ts (trade-chart-unavailable).
  • No dedicated spec for candle render, zoom/pan, indicator visuals, or autoscale (QA template 5.1.12 is manual).

Product invariants (must remain true)

See docs/frontend.md § Trade page — price chart invariants (#113, #148, #149, #150, #151).


Why this is needed

  1. Regression blind spot: Bugs in autoscaleInfoProvider, multi-pane heights, applyOptions after resize, or async createChart races may pass stub tests because the mock never invokes real scale logic or canvas lifecycle.
  2. Stub drift: New lightweight-charts APIs (subscribeVisibleLogicalRangeChange, custom formatters, etc.) can ship in production while the mock stays incomplete — tests green, browser broken.
  3. CI confidence: Gap analysis and QA templates still treat chart behavior as manual / E2E-adjacent; we want repeatable Vitest coverage for chart paths without requiring full LocalTerra + Playwright for every PR.
  4. Epic alignment: Testing P2 epic (#105, #199) — reduce permanent stand-ins where a bounded real-environment test is feasible.

Constraints and guardrails

  1. Naming: Issues/PRs must say lightweight-charts or TradingView lightweight-charts, not “TradingView widget.”
  2. Do not break default unit CI: npm run test in frontend-dapp must stay fast; any real-library suite should be opt-in (separate Vitest project/config or describe.runIf) unless proven stable in jsdom/happy-dom + canvas shim.
  3. Keep pure tests pure: Continue testing priceChartPriceScale, indicators, candle mapping without importing the library.
  4. Indexer integration tests: ChartsPage.integration.test.tsx / make test-charts-integration validate HTTP + React data flow; chart pixel/render tests are a separate concern — do not require Postgres for canvas tests.
  5. No silent skips: Per #105, do not add permanent test.skip without a linked follow-up.
  6. Prefer test IDs for DOM; canvas pixel assertions only where they add signal (library version pin sensitivity).
  7. Security / abuse: Chart code must not dangerouslySetInnerHTML indexer fields; malformed OHLC should not throw uncaught and take down the trade workspace (see attack vectors below).

Evaluate and implement one primary strategy (document choice in PR + docs/testing.md):

Option A — Real lightweight-charts in Vitest with Canvas shim (preferred if stable)

  • Add dev dependency such as @vitest/browser + vitest-canvas-mock / canvas (or run chart specs in happy-dom with canvas polyfill) in a dedicated project, e.g. vitest.config.charts.ts.
  • Do not load lightweightChartsJsdomMock.ts for that project.
  • Mount PriceChartLightweightCanvas (or thin wrapper) with fixture candle arrays; assert:
    • chart container has non-zero dimensions after double rAF sizing (#151)
    • series count / pane count after indicator toggles
    • no throw on single-candle and 500+ candle datasets
  • Keep existing stubbed PriceChart.test.tsx for fast React/indexer behavior OR migrate assertions that require real library to the new project.

Option B — Enriched contract test double (incremental, lower risk)

  • Expand lightweightChartsJsdomMock.ts to record applyOptions, autoscaleInfoProvider invocations, pane indices, and removePane(2) for RSI.
  • Add PriceChartLightweightCanvas.test.tsx that drives effects without real canvas but verifies exact option objects passed to createChart / addSeries.
  • Does not fully satisfy “real chart” but reduces stub drift; pair with minimal Playwright smoke if Option A is deferred.

Option C — Vitest browser mode / Playwright component tests

  • Reuse Playwright’s browser for a *.chart.spec.tsx slice; align with existing playwright.config.ts workers (5 workers per repo rule).
  • Higher maintenance; use only if Option A fails in CI.

Deliverable: At minimum, chart behavior covered by tests that import the real lightweight-charts module for: init, setData on interval change, indicator pane add/remove, and autoscale clamp path. Update stub policy docs when done.


Relevant files (implementation touch list)

  • frontend-dapp/src/test/lightweightChartsJsdomMock.ts
  • frontend-dapp/vitest.config.ts, vitest.config.integration.ts, new chart-specific config if needed
  • frontend-dapp/package.json (scripts: e.g. test:charts)
  • frontend-dapp/src/components/charts/PriceChartLightweightCanvas.tsx
  • frontend-dapp/src/components/charts/priceChartLightweightIndicatorSync.ts
  • frontend-dapp/src/components/charts/__tests__/*
  • docs/testing.md, skills/AGENTS_FRONTEND_PRICE_CHART.md, skills/AGENTS_TESTING_P2_EPIC.md
  • gaps/GAP_1780023683.md (close chart Vitest gap row when done)
  • Optional: Makefile target mirroring test-charts-integration for local ergonomics

Acceptance criteria

  • A documented Vitest entry point runs real lightweight-charts tests (not only the jsdom mock).
  • Init path: createChart produces a mounted chart for valid OHLC fixtures (≥1 candle and multi-candle).
  • Interval setData: Changing candle props updates series without second createChart (regression for #148).
  • Indicators: Toggling MA7 / MA25 / RSI changes series/pane topology as per priceChartLightweightIndicatorSync (add/remove, removePane(2) on RSI off).
  • USD autoscale: Visible-range clamp never returns priceRange.minValue < 0 and respects lowest visible low (#151) — via real provider or recorded provider output.
  • Volume pane: Histogram series receives quote-first, base-fallback points (#150).
  • Empty / invalid data: Malformed rows do not crash the test harness; empty successful response does not mount canvas (existing PriceChart behavior preserved).
  • Default npm run test remains green; chart-real suite wired in CI (or documented required job) without test.skip for chart coverage.
  • docs/testing.md and price-chart agent playbook updated to describe the split (stub vs real-library Vitest vs Playwright).

Test plan — functional paths

# Path Setup Expected
1 First load with candles Mock indexer or direct props Chart container visible; loading text clears
2 Single candle 1 valid OHLC row Chart renders (no empty state)
3 Many candles 200+ rows Init < timeout; no OOM in CI
4 Empty [] success getCandles → [] Empty state; no price-chart-lightweight-canvas
5 All-invalid OHLC empty strings Empty state
6 Interval switch Same pair, change 1h→1d One createChart per mount; setData updates
7 Rapid interval hammer 10+ switches (#148) No freeze; single chart instance
8 Pair switch pairA → pairB createChart count increments (remount)
9 Stale async init Unmount before import() resolves No throw; no orphan chart
10 MA7 on/off 20+ candles Line series added then removed
11 RSI on/off 20+ candles Pane added; removePane(2) on off
12 Volume quote=0 volume_quote: 0, volume_base > 0 Histogram uses base
13 Headline tapeLastPriceUsd vs candle close (#149) Correct trade-chart-headline-price (stub suite OK)
14 Indexer outage 502 on candles trade-chart-unavailable
15 Resize / flex parent Narrow container width applyOptions width/height > 0 after layout (#151)
16 Fullscreen toggle User click requestFullscreen path (mock API in jsdom)

Test plan — attack vectors / failure modes

Vector Test approach Expected guard
Prototype pollution / weird candle JSON open/close as non-numeric strings, Object payloads Filtered out; empty state or partial chart; no uncaught exception
Extreme numeric strings 1e309, -Infinity, NaN No throw; invalid points dropped in priceChartCandles
Huge candle arrays 50k points (perf) CI timeout or documented cap; no main-thread hang in test job
XSS via indexer Candles with <script> in open_time Rendered as text only; no script execution in chart labels
Race: interval then pair Fast clicks chartInitIdRef drops stale init; no double-remove crash
Race: indicator toggle during load Toggle MA before chartModelReady No throw when sync runs with null refs
Memory leak Mount/unmount 20× chart.remove() called (spy or real API)
Out-of-order getCandles Slow pair B, fast pair A Latest pair wins; no mixed setData
CORS / auth N/A Chart is read-only indexer data No secrets in chart options

Verification criteria (definition of done)

  1. cd frontend-dapp && npm run test — all existing unit tests pass.
  2. New command (e.g. npm run test:charts or documented vitest --config vitest.config.charts.ts) passes locally and in CI.
  3. At least one test imports lightweight-charts without vi.mock and asserts library-backed behavior (not only lwChartTestDouble).
  4. docs/testing.md no longer states that all Vitest chart testing is stub-only without pointing to the real-library suite.
  5. Manual smoke (optional but recommended): /trade chart visible on localnet + indexer; zoom/pan once — recorded in PR test plan.
  6. Gap doc row for /charts Vitest stub updated or closed.

  • #113 — chart invariants
  • #105 — stub policy
  • #148 — timeframe selector / single chart instance
  • #150 — indicators
  • #151 — USD scale & viewport
  • #165 — indexer outage copy
  • #205 — charts integration fixtures
## Summary The price chart on `/trade` and `/charts` uses **TradingView [lightweight-charts](https://github.com/tradingview/lightweight-charts)** v5 (open-source canvas library — **not** the hosted TradingView widget). Node-based Vitest runs under **jsdom** with a global module stub (`lightweightChartsJsdomMock.ts`) because jsdom lacks a real Canvas/layout stack. Component tests assert **integration wiring** (`createChart` call count, `setData` payloads, indicator `addSeries`/`removeSeries`) but **do not exercise the real library**. Chart rendering, autoscale behavior, pane layout, resize, and crosshair interactions are deferred to manual QA and sparse Playwright coverage (layout/outage only). This issue tracks closing that gap with **full chart testing in Vitest** while keeping CI fast and deterministic. --- ## Current codebase ### Chart implementation | Area | Files | |------|--------| | Shell / data fetching | `frontend-dapp/src/components/charts/PriceChart.tsx` | | Canvas + library wiring | `frontend-dapp/src/components/charts/PriceChartLightweightCanvas.tsx` | | Indicator sync (no full recreate) | `frontend-dapp/src/components/charts/priceChartLightweightIndicatorSync.ts` | | OHLC / volume mapping | `frontend-dapp/src/components/charts/priceChartCandles.ts`, `priceChartCandlesPlaceholder.ts` | | Pure math (SMA / RSI) | `frontend-dapp/src/components/charts/priceChartIndicators.ts` | | USD Y-axis clamp | `frontend-dapp/src/components/charts/priceChartPriceScale.ts`, `priceChartPaneHeights.ts` | | Headline “Last” price | `frontend-dapp/src/components/charts/chartHeadlinePrice.ts` | | UI chrome | `PriceChartOverlayMenu.tsx`, `PriceChartEmptyState.tsx` | ### Vitest stub (global) - **`frontend-dapp/src/test/lightweightChartsJsdomMock.ts`** — `vi.mock('lightweight-charts')` registered in both: - `frontend-dapp/vitest.config.ts` (unit tests) - `frontend-dapp/vitest.config.integration.ts` (indexer HTTP integration tests) - Exports **`lwChartTestDouble`** with `seriesSpies[]` and `reset()` for assertions on `setData`. - Stub implements: `createChart`, `addSeries`, `removeSeries`, `addPane`, `removePane`, `panes`, `timeScale().fitContent`, minimal `priceScale()` / `createPriceLine`. ### Existing tests (stub-backed or pure) | Test file | What it covers | |-----------|----------------| | `__tests__/PriceChart.test.tsx` | Loading/empty/outage, interval/pair refetch, headline, MA toggle via spy count, `createChart` once per mount ([#148](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/148)) | | `__tests__/priceChartLightweightIndicatorSync.test.ts` | `syncPriceChartIndicatorOverlays` with hand-rolled chart mocks | | `__tests__/priceChartCandles.test.ts`, `priceChartIndicators.test.ts`, `priceChartPriceScale.test.ts`, `chartHeadlinePrice.test.ts`, `priceChartCandlesPlaceholder.test.ts` | Pure helpers only | | `src/pages/ChartsPage.integration.test.tsx` | Indexer HTTP for `/charts` (still uses stub) | | `src/pages/TradePage.test.tsx` | Imports mock explicitly | ### Documented policy - [`docs/testing.md`](docs/testing.md): *“lightweight-charts is stubbed under jsdom … real library runs in the browser (manual QA / Playwright).”* - [`skills/AGENTS_FRONTEND_PRICE_CHART.md`](skills/AGENTS_FRONTEND_PRICE_CHART.md): extend stub when adding APIs; prefer `data-testid` over canvas assertions in Vitest. - [`gaps/GAP_1780023683.md`](gaps/GAP_1780023683.md): `/charts` route gap — *“stubbed in Vitest; real behavior = browser/E2E”*. - [`skills/AGENTS_TESTING_P2_EPIC.md`](skills/AGENTS_TESTING_P2_EPIC.md): stub catalog ([#105](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105)). ### Playwright today - **Layout only:** `e2e/trade-page-responsive.spec.ts` (`trade-sub-lg-chart-col` bounding boxes). - **Outage copy:** `e2e/trade-indexer-outage.spec.ts` (`trade-chart-unavailable`). - **No** dedicated spec for candle render, zoom/pan, indicator visuals, or autoscale (QA template 5.1.12 is manual). ### Product invariants (must remain true) See [docs/frontend.md § Trade page — price chart invariants](docs/frontend.md#trade-page-price-chart-invariants) ([#113](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/113), [#148](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/148), [#149](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/149), [#150](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/150), [#151](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/151)). --- ## Why this is needed 1. **Regression blind spot:** Bugs in `autoscaleInfoProvider`, multi-pane heights, `applyOptions` after resize, or async `createChart` races may pass stub tests because the mock never invokes real scale logic or canvas lifecycle. 2. **Stub drift:** New lightweight-charts APIs (`subscribeVisibleLogicalRangeChange`, custom formatters, etc.) can ship in production while the mock stays incomplete — tests green, browser broken. 3. **CI confidence:** Gap analysis and QA templates still treat chart behavior as **manual / E2E-adjacent**; we want **repeatable Vitest** coverage for chart paths without requiring full LocalTerra + Playwright for every PR. 4. **Epic alignment:** Testing P2 epic ([#105](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105), [#199](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/199)) — reduce permanent stand-ins where a bounded real-environment test is feasible. --- ## Constraints and guardrails 1. **Naming:** Issues/PRs must say **lightweight-charts** or **TradingView lightweight-charts**, not “TradingView widget.” 2. **Do not break default unit CI:** `npm run test` in `frontend-dapp` must stay fast; any real-library suite should be **opt-in** (separate Vitest project/config or `describe.runIf`) unless proven stable in jsdom/happy-dom + canvas shim. 3. **Keep pure tests pure:** Continue testing `priceChartPriceScale`, indicators, candle mapping without importing the library. 4. **Indexer integration tests:** `ChartsPage.integration.test.tsx` / `make test-charts-integration` validate HTTP + React data flow; chart pixel/render tests are a separate concern — do not require Postgres for canvas tests. 5. **No silent skips:** Per [#105](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105), do not add permanent `test.skip` without a linked follow-up. 6. **Prefer test IDs for DOM;** canvas pixel assertions only where they add signal (library version pin sensitivity). 7. **Security / abuse:** Chart code must not `dangerouslySetInnerHTML` indexer fields; malformed OHLC should not throw uncaught and take down the trade workspace (see attack vectors below). --- ## Recommended direction Evaluate and implement **one primary strategy** (document choice in PR + `docs/testing.md`): ### Option A — Real `lightweight-charts` in Vitest with Canvas shim (preferred if stable) - Add dev dependency such as **`@vitest/browser`** + **`vitest-canvas-mock`** / **`canvas`** (or run chart specs in **`happy-dom`** with canvas polyfill) in a dedicated project, e.g. `vitest.config.charts.ts`. - **Do not** load `lightweightChartsJsdomMock.ts` for that project. - Mount `PriceChartLightweightCanvas` (or thin wrapper) with fixture candle arrays; assert: - chart container has non-zero dimensions after double `rAF` sizing ([#151](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/151)) - series count / pane count after indicator toggles - no throw on single-candle and 500+ candle datasets - Keep existing stubbed `PriceChart.test.tsx` for fast React/indexer behavior OR migrate assertions that require real library to the new project. ### Option B — Enriched contract test double (incremental, lower risk) - Expand `lightweightChartsJsdomMock.ts` to record **`applyOptions`**, **`autoscaleInfoProvider` invocations**, pane indices, and **`removePane(2)`** for RSI. - Add **`PriceChartLightweightCanvas.test.tsx`** that drives effects without real canvas but verifies **exact option objects** passed to `createChart` / `addSeries`. - Does **not** fully satisfy “real chart” but reduces stub drift; pair with minimal Playwright smoke if Option A is deferred. ### Option C — Vitest browser mode / Playwright component tests - Reuse Playwright’s browser for a **`*.chart.spec.tsx`** slice; align with existing `playwright.config.ts` workers (5 workers per repo rule). - Higher maintenance; use only if Option A fails in CI. **Deliverable:** At minimum, chart behavior covered by tests that **import the real `lightweight-charts` module** for: init, `setData` on interval change, indicator pane add/remove, and autoscale clamp path. Update stub policy docs when done. --- ## Relevant files (implementation touch list) - `frontend-dapp/src/test/lightweightChartsJsdomMock.ts` - `frontend-dapp/vitest.config.ts`, `vitest.config.integration.ts`, **new** chart-specific config if needed - `frontend-dapp/package.json` (scripts: e.g. `test:charts`) - `frontend-dapp/src/components/charts/PriceChartLightweightCanvas.tsx` - `frontend-dapp/src/components/charts/priceChartLightweightIndicatorSync.ts` - `frontend-dapp/src/components/charts/__tests__/*` - `docs/testing.md`, `skills/AGENTS_FRONTEND_PRICE_CHART.md`, `skills/AGENTS_TESTING_P2_EPIC.md` - `gaps/GAP_1780023683.md` (close chart Vitest gap row when done) - Optional: `Makefile` target mirroring `test-charts-integration` for local ergonomics --- ## Acceptance criteria - [ ] A documented Vitest entry point runs **real** `lightweight-charts` tests (not only the jsdom mock). - [ ] **Init path:** `createChart` produces a mounted chart for valid OHLC fixtures (≥1 candle and multi-candle). - [ ] **Interval `setData`:** Changing candle props updates series without second `createChart` (regression for [#148](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/148)). - [ ] **Indicators:** Toggling MA7 / MA25 / RSI changes series/pane topology as per `priceChartLightweightIndicatorSync` (add/remove, `removePane(2)` on RSI off). - [ ] **USD autoscale:** Visible-range clamp never returns `priceRange.minValue < 0` and respects lowest visible `low` ([#151](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/151)) — via real provider or recorded provider output. - [ ] **Volume pane:** Histogram series receives quote-first, base-fallback points ([#150](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/150)). - [ ] **Empty / invalid data:** Malformed rows do not crash the test harness; empty successful response does not mount canvas (existing `PriceChart` behavior preserved). - [ ] Default `npm run test` remains green; chart-real suite wired in CI (or documented required job) without `test.skip` for chart coverage. - [ ] `docs/testing.md` and price-chart agent playbook updated to describe the split (stub vs real-library Vitest vs Playwright). --- ## Test plan — functional paths | # | Path | Setup | Expected | |---|------|--------|----------| | 1 | First load with candles | Mock indexer or direct props | Chart container visible; loading text clears | | 2 | Single candle | 1 valid OHLC row | Chart renders (no empty state) | | 3 | Many candles | 200+ rows | Init < timeout; no OOM in CI | | 4 | Empty `[]` success | `getCandles` → `[]` | Empty state; no `price-chart-lightweight-canvas` | | 5 | All-invalid OHLC | empty strings | Empty state | | 6 | Interval switch | Same pair, change 1h→1d | One `createChart` per mount; `setData` updates | | 7 | Rapid interval hammer | 10+ switches ([#148](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/148)) | No freeze; single chart instance | | 8 | Pair switch | `pairA` → `pairB` | `createChart` count increments (remount) | | 9 | Stale async init | Unmount before `import()` resolves | No throw; no orphan chart | | 10 | MA7 on/off | 20+ candles | Line series added then removed | | 11 | RSI on/off | 20+ candles | Pane added; `removePane(2)` on off | | 12 | Volume quote=0 | `volume_quote: 0`, `volume_base > 0` | Histogram uses base | | 13 | Headline | `tapeLastPriceUsd` vs candle close ([#149](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/149)) | Correct `trade-chart-headline-price` (stub suite OK) | | 14 | Indexer outage | 502 on candles | `trade-chart-unavailable` | | 15 | Resize / flex parent | Narrow container width | `applyOptions` width/height > 0 after layout ([#151](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/151)) | | 16 | Fullscreen toggle | User click | `requestFullscreen` path (mock API in jsdom) | --- ## Test plan — attack vectors / failure modes | Vector | Test approach | Expected guard | |--------|---------------|----------------| | **Prototype pollution / weird candle JSON** | `open`/`close` as non-numeric strings, `Object` payloads | Filtered out; empty state or partial chart; no uncaught exception | | **Extreme numeric strings** | `1e309`, `-Infinity`, `NaN` | No throw; invalid points dropped in `priceChartCandles` | | **Huge candle arrays** | 50k points (perf) | CI timeout or documented cap; no main-thread hang in test job | | **XSS via indexer** | Candles with `<script>` in `open_time` | Rendered as text only; no script execution in chart labels | | **Race: interval then pair** | Fast clicks | `chartInitIdRef` drops stale init; no double-remove crash | | **Race: indicator toggle during load** | Toggle MA before `chartModelReady` | No throw when sync runs with null refs | | **Memory leak** | Mount/unmount 20× | `chart.remove()` called (spy or real API) | | **Out-of-order `getCandles`** | Slow pair B, fast pair A | Latest pair wins; no mixed `setData` | | **CORS / auth N/A** | Chart is read-only indexer data | No secrets in chart options | --- ## Verification criteria (definition of done) 1. `cd frontend-dapp && npm run test` — all existing unit tests pass. 2. New command (e.g. `npm run test:charts` or documented `vitest --config vitest.config.charts.ts`) passes locally and in CI. 3. At least one test imports **`lightweight-charts`** without `vi.mock` and asserts library-backed behavior (not only `lwChartTestDouble`). 4. `docs/testing.md` no longer states that *all* Vitest chart testing is stub-only without pointing to the real-library suite. 5. Manual smoke (optional but recommended): `/trade` chart visible on localnet + indexer; zoom/pan once — recorded in PR test plan. 6. Gap doc row for `/charts` Vitest stub updated or closed. --- ## Related issues - [#113](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/113) — chart invariants - [#105](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105) — stub policy - [#148](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/148) — timeframe selector / single chart instance - [#150](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/150) — indicators - [#151](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/151) — USD scale & viewport - [#165](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/165) — indexer outage copy - [#205](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/205) — charts integration fixtures
PlasticDigits commented 2026-05-29 03:37:27 +00:00 (Migrated from gitlab.com)

mentioned in commit fc9434c0b1

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

mentioned in commit 7c19e567d6

mentioned in commit 7c19e567d62f05795264c6b919b3d0771553398f
PlasticDigits commented 2026-05-29 03:37:47 +00:00 (Migrated from gitlab.com)

Implementation summary (GitLab #211)

Closed the Vitest gap for TradingView lightweight-charts (open-source canvas library — not the hosted TradingView widget) using Option A: real library in a dedicated Vitest project with a Node canvas shim.

What landed on main (7c19e56)

  • vitest.config.charts.ts + npm run test:charts / make test-frontend-charts — does not load lightweightChartsJsdomMock.ts
  • src/test/chartsSetup.ts — patches Canvas 2D, jsdom layout sizes, and getComputedStyle for hex colors
  • *.charts.test.{ts,tsx} (14 tests): real createChart, setData, MA/RSI pane topology, quote→base volume, USD autoscale clamp, PriceChartLightweightCanvas mount/update
  • Default npm run test:run unchanged (stub-backed PriceChart.test.tsx etc.)
  • CI: frontend job runs npm run test:charts after unit tests
  • Docs/skills: docs/testing.md, docs/frontend.md (price chart invariants), skills/AGENTS_FRONTEND_PRICE_CHART.md, skills/AGENTS_TESTING_P2_EPIC.md, gaps/GAP_1780023683.md

Verification checklist

  • cd frontend-dapp && npm run test:run — all unit tests green
  • cd frontend-dapp && npm run test:charts — 14 real-library tests green (needs canvas from npm ci)
  • make test-frontend-charts from repo root
  • CI frontend job passes (unit + test:charts)
  • Manual smoke: /trade and /charts — chart renders, interval switch, MA7/RSI toggles
  • Stub suite still covers outage / empty candles (PriceChart.test.tsx)

@brouie — please run through the checklist and confirm this meets #211 acceptance criteria. Leaving the issue open until verified.

## Implementation summary (GitLab #211) Closed the Vitest gap for **TradingView lightweight-charts** (open-source canvas library — not the hosted TradingView widget) using **Option A**: real library in a dedicated Vitest project with a Node `canvas` shim. ### What landed on `main` (7c19e56) - **`vitest.config.charts.ts`** + **`npm run test:charts`** / **`make test-frontend-charts`** — does **not** load `lightweightChartsJsdomMock.ts` - **`src/test/chartsSetup.ts`** — patches Canvas 2D, jsdom layout sizes, and `getComputedStyle` for hex colors - **`*.charts.test.{ts,tsx}`** (14 tests): real `createChart`, `setData`, MA/RSI pane topology, quote→base volume, USD autoscale clamp, `PriceChartLightweightCanvas` mount/update - Default **`npm run test:run`** unchanged (stub-backed `PriceChart.test.tsx` etc.) - **CI**: `frontend` job runs `npm run test:charts` after unit tests - **Docs/skills**: docs/testing.md, docs/frontend.md (price chart invariants), skills/AGENTS_FRONTEND_PRICE_CHART.md, skills/AGENTS_TESTING_P2_EPIC.md, gaps/GAP_1780023683.md ### Verification checklist - [ ] `cd frontend-dapp && npm run test:run` — all unit tests green - [ ] `cd frontend-dapp && npm run test:charts` — 14 real-library tests green (needs `canvas` from `npm ci`) - [ ] `make test-frontend-charts` from repo root - [ ] CI `frontend` job passes (unit + `test:charts`) - [ ] Manual smoke: `/trade` and `/charts` — chart renders, interval switch, MA7/RSI toggles - [ ] Stub suite still covers outage / empty candles (`PriceChart.test.tsx`) @brouie — please run through the checklist and confirm this meets #211 acceptance criteria. Leaving the issue **open** until verified.
PlasticDigits commented 2026-05-29 05:18:11 +00:00 (Migrated from gitlab.com)

Verification pass (agent, 2026-05-29)

Re-ran the #211 deliverable on current main (07ff805). Implementation uses Option A: real TradingView lightweight-charts (open-source canvas library — not the hosted TradingView widget) in a dedicated Vitest project with Node canvas shim.

What is on main

Area Detail
Entry point vitest.config.charts.ts → npm run test:charts, make test-frontend-charts
Setup src/test/chartsSetup.ts (Canvas 2D + layout/CSS vars)
Real-library tests *.charts.test.{ts,tsx} — 14 tests in 2 files
Fast stub suite Default npm run test:run still uses lightweightChartsJsdomMock.ts
CI frontend job runs test:charts after unit tests
Docs / skills docs/testing.md, docs/frontend.md (invariants), skills/AGENTS_FRONTEND_PRICE_CHART.md, skills/AGENTS_TESTING_P2_EPIC.md, gaps/GAP_1780023683.md

Local verification (this session)

  • cd frontend-dapp && npm run test:run — 662 tests passed
  • cd frontend-dapp && npm run test:charts — 14 real-library tests passed
  • make test-frontend-charts from repo root — green
  • main synced with origin/main (clean worktree)

Checklist for human sign-off (@brouie)

Please confirm #211 acceptance criteria:

  • npm run test:run — stub-backed PriceChart.test.tsx (outage, empty candles, interval/pair #148) still green
  • npm run test:charts — real lightweight-charts import (not vi.mock); init (1 + 220 candles), setData without second createChart, MA7 add/remove, RSI removePane(2), quote→base volume, USD autoscale minValue >= 0, PriceChartLightweightCanvas mount/update/unmount
  • CI frontend job includes test:charts
  • Manual smoke: /trade and /charts — chart renders; interval switch; MA7 / RSI toggles; zoom/pan once
  • Docs/skills cross-links match the split (stub vs real-library vs Playwright)

Leaving the issue open until verified. @brouie — please run the checklist and close or comment with gaps.

## Verification pass (agent, 2026-05-29) Re-ran the **#211** deliverable on current `main` (`07ff805`). Implementation uses **Option A**: real **TradingView lightweight-charts** (open-source canvas library — not the hosted TradingView widget) in a dedicated Vitest project with Node `canvas` shim. ### What is on `main` | Area | Detail | |------|--------| | Entry point | `vitest.config.charts.ts` → `npm run test:charts`, `make test-frontend-charts` | | Setup | `src/test/chartsSetup.ts` (Canvas 2D + layout/CSS vars) | | Real-library tests | `*.charts.test.{ts,tsx}` — **14 tests** in 2 files | | Fast stub suite | Default `npm run test:run` still uses `lightweightChartsJsdomMock.ts` | | CI | `frontend` job runs `test:charts` after unit tests | | Docs / skills | `docs/testing.md`, `docs/frontend.md` (invariants), `skills/AGENTS_FRONTEND_PRICE_CHART.md`, `skills/AGENTS_TESTING_P2_EPIC.md`, `gaps/GAP_1780023683.md` | ### Local verification (this session) - [x] `cd frontend-dapp && npm run test:run` — **662** tests passed - [x] `cd frontend-dapp && npm run test:charts` — **14** real-library tests passed - [x] `make test-frontend-charts` from repo root — green - [x] `main` synced with `origin/main` (clean worktree) ### Checklist for human sign-off (@brouie) Please confirm **#211** acceptance criteria: - [ ] `npm run test:run` — stub-backed `PriceChart.test.tsx` (outage, empty candles, interval/pair #148) still green - [ ] `npm run test:charts` — real `lightweight-charts` import (not `vi.mock`); init (1 + 220 candles), `setData` without second `createChart`, MA7 add/remove, RSI `removePane(2)`, quote→base volume, USD autoscale `minValue >= 0`, `PriceChartLightweightCanvas` mount/update/unmount - [ ] CI `frontend` job includes `test:charts` - [ ] Manual smoke: `/trade` and `/charts` — chart renders; interval switch; MA7 / RSI toggles; zoom/pan once - [ ] Docs/skills cross-links match the split (stub vs real-library vs Playwright) Leaving the issue **open** until verified. @brouie — please run the checklist and close or comment with gaps.
PlasticDigits commented 2026-05-29 05:40:51 +00:00 (Migrated from gitlab.com)

marked as related to #225

marked as related to #225
PlasticDigits commented 2026-05-29 05:40:52 +00:00 (Migrated from gitlab.com)

mentioned in issue #225

mentioned in issue #225
PlasticDigits commented 2026-05-29 05:40:57 +00:00 (Migrated from gitlab.com)

mentioned in issue #226

mentioned in issue #226
PlasticDigits commented 2026-05-29 05:40:58 +00:00 (Migrated from gitlab.com)

marked as related to #226

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

mentioned in issue #227

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

marked as related to #227

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

mentioned in issue #228

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

marked as related to #228

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

mentioned in issue #229

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

marked as related to #229

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

mentioned in issue #230

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

marked as related to #230

marked as related to #230
PlasticDigits commented 2026-05-29 12:15:55 +00:00 (Migrated from gitlab.com)

Verification complete (agent, 2026-05-29)

Verified GitLab #211 on main @ 308a04a using worktree verify/issue-211. Option A is implemented: real TradingView lightweight-charts (open-source canvas library — not the hosted TradingView widget) in dedicated Vitest project vitest.config.charts.ts with Node canvas shim (src/test/chartsSetup.ts).

What was verified

Criterion Result
npm run test:run (default Vitest, stub-backed) 706 tests passed
npm run test:charts / make test-frontend-charts 21 passed, 1 it.runIf(CI) soak (2000 candles) — not a silent skip
Real lightweight-charts import (no vi.mock) priceChartLightweightRealLibrary.charts.test.ts, PriceChartLightweightCanvas.charts.test.tsx
CI job frontend-charts-vitest Present in .github/workflows/test.yml (isolated from unit job)
Docs / skills / gap docs/testing.md, docs/frontend.md (invariants), skills/AGENTS_FRONTEND_PRICE_CHART.md, skills/AGENTS_TESTING_P2_EPIC.md, gaps/GAP_1780023683.md

Acceptance criteria mapping

  • Documented entry point: npm run test:charts, make test-frontend-charts
  • Init: single + multi-candle (220, 500, 1500; CI 2000)
  • Interval setData without second createChart (real + stub #148)
  • Indicators: MA7 add/remove, RSI pane + removePane(2) on disable
  • USD autoscale: minValue >= 0, visible-range clamp (#151, #229 harness)
  • Volume quote→base fallback (#150)
  • Empty/invalid: stub PriceChart.test.tsx + pure priceChartCandles.test.ts (#226 vectors)
  • Default unit CI fast; chart suite separate required job
  • Docs describe stub vs real-library vs Playwright split

Manual smoke (localnet + indexer)

  • /charts @ http://127.0.0.1:5173 — canvas mounted, interval 1h→1d, MA7 toggle, chart live region updates
  • /trade — workspace blocked by LCD JSON-RPC {"code":-32701,"message":"not implemented"} on this LocalTerra node (unrelated to #211 Vitest deliverable); candles API on indexer OK

Indexer: http://127.0.0.1:3001 healthy; LCD http://127.0.0.1:1317 up.

Reproduce

make test-frontend-charts
cd frontend-dapp && npm run test:run && npm run test:charts

No code changes in this verification pass; worktree matched origin/main.

Closing #211 — all issue verification criteria and acceptance criteria satisfied.

## Verification complete (agent, 2026-05-29) Verified GitLab **#211** on `main` @ `308a04a` using worktree `verify/issue-211`. **Option A** is implemented: real **TradingView lightweight-charts** (open-source canvas library — not the hosted TradingView widget) in dedicated Vitest project `vitest.config.charts.ts` with Node `canvas` shim (`src/test/chartsSetup.ts`). ### What was verified | Criterion | Result | |-----------|--------| | `npm run test:run` (default Vitest, stub-backed) | **706** tests passed | | `npm run test:charts` / `make test-frontend-charts` | **21** passed, **1** `it.runIf(CI)` soak (2000 candles) — not a silent skip | | Real `lightweight-charts` import (no `vi.mock`) | `priceChartLightweightRealLibrary.charts.test.ts`, `PriceChartLightweightCanvas.charts.test.tsx` | | CI job `frontend-charts-vitest` | Present in `.github/workflows/test.yml` (isolated from unit job) | | Docs / skills / gap | `docs/testing.md`, `docs/frontend.md` (invariants), `skills/AGENTS_FRONTEND_PRICE_CHART.md`, `skills/AGENTS_TESTING_P2_EPIC.md`, `gaps/GAP_1780023683.md` | ### Acceptance criteria mapping - [x] Documented entry point: `npm run test:charts`, `make test-frontend-charts` - [x] Init: single + multi-candle (220, 500, 1500; CI 2000) - [x] Interval `setData` without second `createChart` (real + stub #148) - [x] Indicators: MA7 add/remove, RSI pane + `removePane(2)` on disable - [x] USD autoscale: `minValue >= 0`, visible-range clamp (#151, #229 harness) - [x] Volume quote→base fallback (#150) - [x] Empty/invalid: stub `PriceChart.test.tsx` + pure `priceChartCandles.test.ts` (#226 vectors) - [x] Default unit CI fast; chart suite separate required job - [x] Docs describe stub vs real-library vs Playwright split ### Manual smoke (localnet + indexer) - [x] **`/charts`** @ `http://127.0.0.1:5173` — canvas mounted, interval 1h→1d, MA7 toggle, chart live region updates - [ ] **`/trade`** — workspace blocked by LCD JSON-RPC `{"code":-32701,"message":"not implemented"}` on this LocalTerra node (unrelated to #211 Vitest deliverable); candles API on indexer OK Indexer: `http://127.0.0.1:3001` healthy; LCD `http://127.0.0.1:1317` up. ### Reproduce ```bash make test-frontend-charts cd frontend-dapp && npm run test:run && npm run test:charts ``` No code changes in this verification pass; worktree matched `origin/main`. Closing **#211** — all issue verification criteria and acceptance criteria satisfied.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-29 12:16:01 +00:00
PlasticDigits commented 2026-09-01 08:14:37 +00:00 (Migrated from gitlab.com)

mentioned in issue #717

mentioned in issue #717
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#211
No description provided.