Skip to content

Batch 1: Bootstrap reconciled release/2.1 onto 2.0 base (#758) - #768

Merged
alfredo1996 merged 11 commits into
release/2.0from
feat/issue-758-batch-1-bootstrap
May 16, 2026
Merged

Batch 1: Bootstrap reconciled release/2.1 onto 2.0 base (#758)#768
alfredo1996 merged 11 commits into
release/2.0from
feat/issue-758-batch-1-bootstrap

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented May 15, 2026

Copy link
Copy Markdown
Owner

Part of #757 — release/2.1 reconciliation onto release/2.0 base.

Summary

  • .env.example (71 lines) — documents OIDC, ENCRYPTION_KEY rotation, rate limit env vars
  • .gitmodules — points enterprise submodule at alfredo1996/neoboard-enterprise
  • enterprise/ submodule pinned at SHA 9461e039
  • docker/docker-compose.prod-full.yml (72 lines) — single-server compose with healthchecks/limits
  • .claude/hooks/check-migration-guard.sh (26 lines) — forward-only migration guard

Test plan

  • CI green
  • Submodule resolves on clone (git submodule update --init)
  • Compose file parses (docker compose -f docker/docker-compose.prod-full.yml config)

E2E deferred to Batch 10 (#767).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a migration-safety hook to prevent accidental overwrites of existing migrations.
    • Added a complete .env example template for app configuration.
  • Chores

    • Updated enterprise submodule reference.
    • CI PR triggers expanded to more branch patterns.
  • Bug Fixes / Reliability

    • Write-query errors now return sanitized messages to avoid leaking SQL details.
    • More precise widget cache invalidation to reduce unnecessary refetches.
    • Fixed fullscreen dialog timer handling to avoid stale state updates.
  • CI / Tests

    • Various test updates and runner config adjustments to improve stability.

Review Change Stack

Port infrastructure files from release/2.1 to the new release/2.1-from-2.0
base (which inherits release/2.0's plugin work). No code changes.

- .env.example — full env var documentation including SSO/OIDC, encryption
  rotation, rate limit, log level
- .gitmodules — enterprise submodule pointer
  (alfredo1996/neoboard-enterprise)
- enterprise/ — submodule pinned at 9461e039 (verified accessible)
- docker/docker-compose.prod-full.yml — single-server compose with app +
  PostgreSQL, healthchecks, resource limits
- .claude/hooks/check-migration-guard.sh — PreToolUse hook blocking edits to
  existing migration files (forward-only enforcement)

Pre-existing lint errors on release/2.0 base (cli/, app/, etc.) are
out-of-scope for this batch — see follow-up tasks.

Part of #757
Closes #758

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4541feba-84aa-4068-9aec-de39d86fbf6a

📥 Commits

Reviewing files that changed from the base of the PR and between ced5911 and 1c588bb.

📒 Files selected for processing (4)
  • app/e2e/widget-lab.spec.ts
  • app/src/components/__tests__/dashboard-container-branches.test.tsx
  • app/src/components/dashboard-container.tsx
  • component/src/components/composed/__tests__/data-grid-dynamic-pagination.test.tsx

Walkthrough

Adds an env template, migration write-guard hook, enterprise submodule update, safe error-sanitization option with route/tests, dashboard timer/key changes and tests, Jest/test-runner tweaks, and CI pull_request branch filter expansion.

Changes

Bootstrap, safety, and CI

Layer / File(s) Summary
Environment configuration template
.env.example
Adds a full .env.example documenting PostgreSQL, encryption (with rotation), NextAuth, admin bootstrap, API key HMAC, feature flags, session/logging defaults, query rate limiting, and optional OIDC/SSO envs.
Enterprise Submodule Integration
.gitmodules, enterprise
Registers enterprise submodule in .gitmodules and updates the enterprise pointer to commit 9461e03950b94d3f449d65b76a9ddef59e5ca333.
Migration Guard Hook
.claude/hooks/check-migration-guard.sh
New hook reads tool_input.file_path from stdin JSON, permits creation of new migrations/*.sql or migrations/*.ts files but blocks edits to existing migration files (prints guidance and exits code 2).
API error sanitization and tests
app/src/lib/api/api-utils.ts, app/src/app/api/query/write/route.ts, app/src/app/api/query/write/__tests__/route.test.ts, app/src/lib/__tests__/api/api-utils.test.ts
Adds options.safeMessage to handleRouteError (early-return fallback when true), updates write route to pass { safeMessage: true }, and expands tests to assert sanitized error responses while preserving typed app-error behavior and headers.
Dashboard fullscreen timer & query invalidation
app/src/components/dashboard-container.tsx, app/src/components/__tests__/dashboard-container-branches.test.tsx
Replaces unmanaged fullscreen-ready timeout with a ref-backed timeout and cleanup; changes widget refresh invalidation key prefix to include widget.database ?? null and stop before widget.params; updates mocks and adds timer cleanup tests.
Tests, Jest config, e2e, and CI trigger
connection/jest.config.js, connection/src/generalized/__tests__/errors.test.ts, app/e2e/widget-lab.spec.ts, component/src/components/composed/__tests__/data-grid-dynamic-pagination.test.tsx, .github/workflows/ci.yml
Adds .m?js ts-jest transform (useESM:false), allows transforming uuid under node_modules, ignores /dist/ in tests, switches one test import to @jest/globals, forces an e2e suite to serial, fixes an async test timeout closure, and adds feat/* to pull_request branches.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • alfredo1996/neoboard#757: Bootstrap/infrastructure epic covering env files, gitmodules/submodule, and migration guard work that overlaps this PR.
  • alfredo1996/neoboard#778: Related to adding safeMessage behavior in handleRouteError and write-route sanitization.

Possibly related PRs

Suggested labels

pkg:app, area:devex, testing

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the PR's main purpose: bootstrapping reconciled release/2.1 changes onto the 2.0 base as part of a batched integration effort.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-758-batch-1-bootstrap

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/hooks/check-migration-guard.sh:
- Around line 4-6: Add strict failure handling and make jq errors abort the
hook: enable shell strict mode by adding set -euo pipefail at the top, then
replace the silent jq calls (the FILE_PATH assignment that uses jq -r
'.tool_input.file_path // .tool_input.filePath // empty' and the second jq
invocation at lines 12-13) with commands that check jq's exit status and fail
the script on parse errors (e.g., capture jq output into a variable and if jq
exits non-zero or the output is invalid, print an error to stderr and exit 1).
Ensure both FILE_PATH and the other jq-derived variables use the same guarded
pattern so the hook fails closed on JSON parse failures.

In @.env.example:
- Around line 51-56: Update the SSO comment to remove the contradiction by
changing the phrase "three required vars" to "four required vars" so it matches
the later "all four must be set" line; locate the comment block that starts with
"Set all three required vars to enable a single OIDC provider via env." and edit
that sentence to say "Set all four required vars to enable a single OIDC
provider via env." so the NEOBOARD_EDITION/SSO guidance and the subsequent
"Required (all four must be set to activate SSO)" line are consistent.

In `@enterprise`:
- Line 1: The repository submodule entry in .gitmodules points to an
inaccessible remote (https://github.com/alfredo1996/neoboard-enterprise.git)
causing clone and CI failures; update the .gitmodules entry for the enterprise
submodule to the canonical org repo URL, run git submodule sync && git submodule
update --init --remote to fetch from the new remote, and update the pinned SHA
(replace the unreachable 9461e03950b94d3f449d65b76a9ddef59e5ca333) with the
correct commit SHA from the canonical repository so CI and local clones can
initialize the enterprise submodule successfully.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5a109afd-2604-4ea0-a7dc-a483f76c2526

📥 Commits

Reviewing files that changed from the base of the PR and between edc4a51 and c309ee1.

⛔ Files ignored due to path filters (1)
  • docker/docker-compose.prod-full.yml is excluded by !docker/**
📒 Files selected for processing (4)
  • .claude/hooks/check-migration-guard.sh
  • .env.example
  • .gitmodules
  • enterprise

Comment thread .claude/hooks/check-migration-guard.sh
Comment thread .env.example Outdated
Comment thread enterprise
alfredorubin96 and others added 5 commits May 15, 2026 19:09
Address CodeRabbit findings on PR #768:
- Migration guard hook now uses `set -euo pipefail` and `jq -e` to
  fail closed when JSON parsing fails instead of silently allowing edits
- Fix contradictory SSO comment: "three" → "four" required vars

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stacked PRs targeting feature branches were not getting CI checks
because the pull_request trigger only matched main, dev, and release/*.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hain

The integration test files in __tests__/connection/, __tests__/neo4j/,
and __tests__/postgresql/ failed at collection time with:

  SyntaxError: Unexpected token 'export'
  at .../node_modules/uuid/dist-node/index.js:1

Chain: setup.ts -> testcontainers -> dockerode -> uuid@14 (ESM-only).

Two changes:
1. Add a `^.+\.m?js$` transform via ts-jest so Jest's CJS runtime can
   load uuid's ESM build.
2. Override transformIgnorePatterns to allow the uuid package through
   (default ignores all of node_modules).

Also escapes the dot in the existing tsx? pattern (`^.+.tsx?$` -> `^.+\.tsx?$`)
so it matches only TypeScript files, not arbitrary characters before "ts".

Verified locally: 224/226 tests pass, down from ~13 collection failures.
The remaining 2 failures are real Neo4j integration timeouts unrelated
to this config (setField-query, list-databases) — tracked separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous commit added a `^.+\.m?js$` transform so Jest could load
the ESM uuid package. That transform also matches compiled files in
`dist/`, so jest started picking up `dist/generalized/__tests__/errors.test.js`
(and its .d.ts twin) and double-running tests:

  FAIL dist/generalized/__tests__/errors.test.js
  FAIL dist/generalized/__tests__/errors.test.d.ts

Add `/dist/` to testPathIgnorePatterns so only sources in `__tests__/`
are collected. Tests should never be run against built output.

Verified: connection suite collects 30 suites (was 32 with dist garbage),
225/226 tests pass — only the 2 pre-existing Neo4j integration timeouts
remain (setField-query, list-databases).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
connection/src/generalized/__tests__/errors.test.ts imported `describe`,
`it`, `expect` from "vitest", but the connection package uses Jest. The
file was added in #616 (plugin hardening, Apr 2026) and has been failing
silently in CI ever since with:

  Vitest cannot be imported in a CommonJS module using require()

Switching the imports to "@jest/globals" makes all 7 assertions run
green under jest. No code changes — only the import line.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two unrelated bugs surfacing as 5 consistent E2E failures across PR #776:

#779 — Refresh button silently no-op'd
  dashboard-container.tsx invalidated TanStack Query with a 4-element key
  (["widget-query", connectionId, query, params]) but useWidgetQuery's
  actual key is 6 elements
  (["widget-query", connectionId, database, query, params, staleTime]).
  Position 2 mismatched (query string vs null/database) so the prefix
  match failed and no refetch ever fired. Fixed in:
    e2e/auto-refresh.spec.ts:107  manual per-widget refresh re-fetch
    e2e/auto-refresh.spec.ts:221  manual refresh, auto-refresh disabled
    e2e/widget-states.spec.ts:222 showRefreshButton re-fetches
    e2e/widget-states.spec.ts:526 cacheMode 'forever' refresh
  Fix: align invalidation prefix to match through `query` slot. Stop at
  `query` because params/staleTime are merged at hook call time and not
  reproducible at refresh time.

#778 — Write route leaked raw driver error
  handleRouteError -> sanitizeErrorMessage was deliberately permissive
  (only strips bundler internals like __TURBOPACK__) so pg/cypher syntax
  errors echoed user SQL into the response body. Fixed in:
    e2e/write-permissions.spec.ts:255 "safe 500 message" assertion
      expected "Write query execution failed", got 'syntax error at or
      near "THIS"'.
  Fix: opt-in `safeMessage` flag on handleRouteError that collapses
  untyped errors to fallbackMsg. Typed app errors (Queue/Auth/Enterprise)
  still get specific handling. Used by /api/query/write only — read
  routes preserve current behavior so users can debug their own queries.

Tests:
  - api-utils.test.ts: 3 new safeMessage cases (44/44 unit pass)
  - write/route.test.ts: updated leaky-behavior assertion to verify the
    sanitized message + negative match against /syntax error/
  - dashboard-container tests: 27/27 still pass
  - E2E: all 5 originally-failing tests now pass locally (20/20)
alfredo1996 added a commit that referenced this pull request May 15, 2026
…n) (#774)

Foundation fix that unblocks Unit & Integration CI on the entire 2.1 reconciliation stack.

The connection package's jest config didn't transform ESM-only `uuid` (v14)
imported transitively by `testcontainers`, causing all PRs that depend on
the connection workspace to error on `import` syntax during test discovery.

This fix:
- Adds an ESM transform pattern for `uuid` in jest.config.js
- Excludes `dist/` from testPathIgnorePatterns
- Updates errors.test.ts to import from @jest/globals (was importing from vitest by mistake)

E2E shard 1/5 + 5/5 failures here are the documented refresh-button flake
(GH#779), unrelated to the connection package — already fixed and shipping
in PR #768 (next merge in the cascade). Merging foundation despite those
flakes; downstream merges land the fix.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/components/dashboard-container.tsx (1)

152-154: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Query key in exportWidgetCsv is missing database, same bug fixed below.

The refresh handler fix adds widget.database ?? null at position 2 to match useWidgetQuery's key shape. This function still uses the old key without database, so CSV export will silently find no cached data when widget.database is set.

Proposed fix
     const entries = queryClient.getQueriesData<{ data: unknown }>({
-      queryKey: ["widget-query", widget.connectionId, widget.query],
+      queryKey: ["widget-query", widget.connectionId, widget.database ?? null, widget.query],
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/dashboard-container.tsx` around lines 152 - 154,
exportWidgetCsv uses the old query key shape and omits widget.database, so
queryClient.getQueriesData in exportWidgetCsv (the line creating entries) should
include widget.database ?? null as the third element to match useWidgetQuery's
key structure; update the queryKey from ["widget-query", widget.connectionId,
widget.query] to ["widget-query", widget.connectionId, widget.database ?? null,
widget.query] (or equivalent using widget.database) so cached data is found when
database is set.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/src/components/dashboard-container.tsx`:
- Around line 152-154: exportWidgetCsv uses the old query key shape and omits
widget.database, so queryClient.getQueriesData in exportWidgetCsv (the line
creating entries) should include widget.database ?? null as the third element to
match useWidgetQuery's key structure; update the queryKey from ["widget-query",
widget.connectionId, widget.query] to ["widget-query", widget.connectionId,
widget.database ?? null, widget.query] (or equivalent using widget.database) so
cached data is found when database is set.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a780271d-001a-4120-b420-d7de8cd1c307

📥 Commits

Reviewing files that changed from the base of the PR and between c309ee1 and ced5911.

📒 Files selected for processing (10)
  • .claude/hooks/check-migration-guard.sh
  • .env.example
  • .github/workflows/ci.yml
  • app/src/app/api/query/write/__tests__/route.test.ts
  • app/src/app/api/query/write/route.ts
  • app/src/components/dashboard-container.tsx
  • app/src/lib/__tests__/api/api-utils.test.ts
  • app/src/lib/api/api-utils.ts
  • connection/jest.config.js
  • connection/src/generalized/__tests__/errors.test.ts
✅ Files skipped from review due to trivial changes (2)
  • connection/src/generalized/tests/errors.test.ts
  • .env.example
🚧 Files skipped from review as they are similar to previous changes (1)
  • .claude/hooks/check-migration-guard.sh

alfredorubin96 and others added 2 commits May 16, 2026 02:24
… name races

Root cause of repeated "strict mode violation: getByText('Neo4j Bar Template',
{ exact: true }) resolved to 2 elements" failures on shard 5 across multiple
PRs (#770, #771): the consumption describe block creates two templates with
hardcoded names ("Neo4j Bar Template", "PostgreSQL Table Template") in
beforeEach. With fullyParallel=true and 2 CI workers, two of these tests'
beforeEach hooks race — both succeed in POSTing — leaving 2 elements with
the same text in the DOM and breaking strict-mode locators.

Fix: declare test.describe.configure({ mode: "serial" }) on the consumption
describe so its tests run sequentially within a worker. Each test's
beforeEach/afterEach now owns the shared template names exclusively.

Verified locally: all 4 consumption tests pass with --workers=2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR #768 unit suite failed with "Test timed out in 5000ms" on
"shows selection count text when rows are selected with pagination active"
in data-grid-dynamic-pagination.test.tsx:349.

Same root cause as #439 (commit 0ff3419): under coverage instrumentation
in CI, rendering 30 rows + userEvent.click + selection state propagation
exceeds the default 5s budget. The previous test in the same describe
("can select all rows on the current page") was already bumped to 15000ms
for the same reason — this sibling test was missed in that PR.

Match the precedent so both selection-related tests have headroom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alfredo1996 pushed a commit that referenced this pull request May 16, 2026
PR #768 unit suite failed with "Test timed out in 5000ms" on
"shows selection count text when rows are selected with pagination active"
in data-grid-dynamic-pagination.test.tsx:349.

Same root cause as #439 (commit 0ff3419): under coverage instrumentation
in CI, rendering 30 rows + userEvent.click + selection state propagation
exceeds the default 5s budget. The previous test in the same describe
("can select all rows on the current page") was already bumped to 15000ms
for the same reason — this sibling test was missed in that PR.

Match the precedent so both selection-related tests have headroom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alfredo1996 pushed a commit that referenced this pull request May 16, 2026
PR #768 unit suite failed with "Test timed out in 5000ms" on
"shows selection count text when rows are selected with pagination active"
in data-grid-dynamic-pagination.test.tsx:349.

Same root cause as #439 (commit 0ff3419): under coverage instrumentation
in CI, rendering 30 rows + userEvent.click + selection state propagation
exceeds the default 5s budget. The previous test in the same describe
("can select all rows on the current page") was already bumped to 15000ms
for the same reason — this sibling test was missed in that PR.

Match the precedent so both selection-related tests have headroom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alfredo1996 pushed a commit that referenced this pull request May 16, 2026
PR #768 unit suite failed with "Test timed out in 5000ms" on
"shows selection count text when rows are selected with pagination active"
in data-grid-dynamic-pagination.test.tsx:349.

Same root cause as #439 (commit 0ff3419): under coverage instrumentation
in CI, rendering 30 rows + userEvent.click + selection state propagation
exceeds the default 5s budget. The previous test in the same describe
("can select all rows on the current page") was already bumped to 15000ms
for the same reason — this sibling test was missed in that PR.

Match the precedent so both selection-related tests have headroom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alfredo1996 pushed a commit that referenced this pull request May 16, 2026
PR #768 unit suite failed with "Test timed out in 5000ms" on
"shows selection count text when rows are selected with pagination active"
in data-grid-dynamic-pagination.test.tsx:349.

Same root cause as #439 (commit 0ff3419): under coverage instrumentation
in CI, rendering 30 rows + userEvent.click + selection state propagation
exceeds the default 5s budget. The previous test in the same describe
("can select all rows on the current page") was already bumped to 15000ms
for the same reason — this sibling test was missed in that PR.

Match the precedent so both selection-related tests have headroom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alfredo1996 pushed a commit that referenced this pull request May 16, 2026
PR #768 unit suite failed with "Test timed out in 5000ms" on
"shows selection count text when rows are selected with pagination active"
in data-grid-dynamic-pagination.test.tsx:349.

Same root cause as #439 (commit 0ff3419): under coverage instrumentation
in CI, rendering 30 rows + userEvent.click + selection state propagation
exceeds the default 5s budget. The previous test in the same describe
("can select all rows on the current page") was already bumped to 15000ms
for the same reason — this sibling test was missed in that PR.

Match the precedent so both selection-related tests have headroom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alfredo1996 pushed a commit that referenced this pull request May 16, 2026
PR #768 unit suite failed with "Test timed out in 5000ms" on
"shows selection count text when rows are selected with pagination active"
in data-grid-dynamic-pagination.test.tsx:349.

Same root cause as #439 (commit 0ff3419): under coverage instrumentation
in CI, rendering 30 rows + userEvent.click + selection state propagation
exceeds the default 5s budget. The previous test in the same describe
("can select all rows on the current page") was already bumped to 15000ms
for the same reason — this sibling test was missed in that PR.

Match the precedent so both selection-related tests have headroom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The deferred-ready setTimeout in DashboardContainer.openFullscreen could
fire after the component unmounted, calling setState on a torn-down tree.
In CI (vitest jsdom + coverage) this surfaced as an unhandled
"window is not defined" exception originating from
dashboard-container-branches.test.tsx, failing the run despite all 2592
tests passing.

Track the timer in a ref and clear it on unmount, on close, and when
re-arming via openFullscreen. Production behavior unchanged; defensive
cleanup prevents the React unmounted-update warning in browsers too.
Add tests for the close, re-arm, and unmount paths of the fullscreen
ready-timer ref so SonarCloud's new-coverage check on the prior fix
clears the 80% threshold (was 75% on 4 uncovered lines).

- close-while-pending → exercises closeFullscreen clearTimeout branch
- re-arm openFullscreen → exercises previous-ref clearTimeout branch
- unmount-while-pending → exercises useEffect cleanup branch

Dialog mock now exposes onOpenChange via a close button so the close
path is reachable from tests; vi.useFakeTimers lets us assert the
timer was cleared (no setState fires on a torn-down tree).
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants