Skip to content

test(studio): lock the split view's server render and hydration - #413

Open
Deodat-Lawson wants to merge 2 commits into
mainfrom
claude/clever-yalow-a2dc5d
Open

Deodat-Lawson wants to merge 2 commits into
mainfrom
claude/clever-yalow-a2dc5d

Conversation

@Deodat-Lawson

@Deodat-Lawson Deodat-Lawson commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Adds regression tests for a server-render crash in StudioSplitView. Since feat(studio): split the workspace centre into columns of tabs #409 it built its per-tab portal hosts with document.createElement during render, so any server render threw ReferenceError: document is not defined. GET /dev/studio-tabs returned 500 on next dev.
  • The fix itself is already on main. This PR started as that fix. In the meantime main fixed the same crash independently in 76ed6c3 (split panes): a mounted flag set in useEffect now gates the memoised PanePortals. When resolving conflicts I took main's component as-is, so there is one fix, not two. This PR now changes no source code.
  • Main had no server-render coverage, so the tests stay:
    • __tests__/StudioSplitView.ssr.test.tsx (new, @jest-environment node): renderToString succeeds with no document, draws both panes' strips and leaves pane content out of the markup.
    • __tests__/StudioSplitView.test.tsx (one new case): server-renders, hydrateRoots the markup with nothing logged, then moves Chat to the other pane and checks it's the same element with its draft and a single mount.

Related

Follow-up to #409. Covers the server-render path of the fix in 76ed6c3.

Checklist

  • pnpm check passes (lint + typecheck): not run repo-wide. tsc --noEmit for apps/web and eslint on the touched files both exit 0.
  • pnpm --filter @launchstack/web test passes: ran the documents/_workspace suite only (34 suites, 326 tests, all pass).
  • Changeset added (if packages/core/ changed): N/A, tests only
  • New env vars documented: N/A, none
  • UI changes exercised in a browser: N/A, no UI change. /dev/studio-tabs returns 200 on the merged tree.
  • Docs updated if behavior changed: N/A

Testing

  • The tests guard main's fix: with main's {mounted && <PanePortals … />} gate temporarily removed, all three new tests fail; with it in place, all 20 StudioSplitView tests pass.
  • Against the pre-feat(studio): split the workspace centre into columns of tabs #409-follow-up code (the original bug), the same three tests also failed. That was verified before the merge.
  • Dev route: curl -s -o /dev/null -w "%{http_code}" http://localhost:3011/dev/studio-tabs returns 200 on the merged branch.

Notes for reviewers

  • The product route was never exposed. WorkspaceShell renders <LoadingPage /> until the Better Auth session resolves, and that never happens on the server. Only the auth-free dev harness crashed. These tests make the component's own SSR safety explicit instead of relying on that check.
  • Test quirks:
    • The SSR test is its own file because jsdom supplies a document and would hide this class of bug.
    • In the jsdom file, react-dom/server.node is imported because the browser build needs a TextEncoder that jsdom lacks.
    • Jest runs react@18.3.1, whose server renderer warns "useLayoutEffect does nothing on the server"; the app runs Next's bundled React 19.2 canary, which doesn't. Both tests filter exactly that message and fail on anything else logged.
  • A possible refinement to main's fix, not done here. Main sets mounted in a useEffect, so on a plain client-side mount (the product path) the first commit has no pane content. Panes appear a frame later. This branch originally used useSyncExternalStore(noop, () => true, () => false), which is false on the server and during hydration but true on a client-side mount, so panes render in the first commit. The difference is one frame on first mount; happy to follow up if wanted.

🤖 Generated with Claude Code


Note

Low Risk
Test-only changes that lock in SSR/hydration behavior for the documents workspace split view, with no production logic in this diff.

Overview
Adds regression tests so StudioSplitView can be server-rendered without touching document, and so client hydration still preserves pane DOM/state when tabs move between columns.

A new Node/Jest SSR suite (StudioSplitView.ssr.test.tsx) runs renderToString with no document, asserts render completes without console errors (aside from filtered React 18 useLayoutEffect warnings), and checks the HTML includes the tab strips but not pane body content.

The existing jsdom suite is refactored with a shared viewOf helper, and gains a hydrate-then-move case: server markup is injected, hydrateRoot runs cleanly, pane content appears after hydration, and moving Chat to another column keeps the same element, draft value, and mount count.

Reviewed by Cursor Bugbot for commit 29c8a94. Bugbot is set up for automated code reviews on this repo. Configure here.

StudioSplitView made its per-tab portal hosts with document.createElement
during render, so any server render of it threw "document is not defined".
/dev/studio-tabs returned 500 in `next dev` since #409. The product route
is not affected today only because WorkspaceShell renders a loading page
until the Better Auth session resolves, and that never happens on the
server.

The panes now portal in only when there is a document, read with
useSyncExternalStore: false on the server and during hydration, true in
the browser. A client-side mount (the product path) still gets its panes
on the first render. A hydrated one gets them straight after, which cannot
mismatch because they were never in the server's HTML. Tabs still move
between columns as the same node, with their state intact.

Tests: a node-environment renderToString test (jsdom supplies `document`
and would hide this bug), plus a jsdom test that hydrates server markup
and then moves a pane between columns. All three fail without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
launch-stack Error Error Oct 3, 2026 3:36am UTC
1 Skipped Deployment
Project Deployment Actions Updated
pdr-ai-v2 Ignored Ignored Oct 3, 2026 3:36am UTC

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-23T04:01:06.057261Z addb2ca PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor

cursor Bot commented Sep 23, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_2d2e3577-ac02-4434-ad45-0242e569ec88)

…2dc5d

main fixed the same server-render crash independently in 76ed6c3 (split
panes): a `mounted` flag, set in an effect, now gates the memoised
PanePortals. Resolved StudioSplitView.tsx to main's version and dropped
this branch's useSyncExternalStore flag, so there is one fix, not two.

The tests stay, ported to main's API (rowLayout, renderEmpty, the split,
zoom and resize callbacks, "pane N of M" strip names). main had no
server-render coverage, and removing its `mounted &&` gate fails all three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Deodat-Lawson Deodat-Lawson changed the title fix(studio): render the split view on the server without a document test(studio): lock the split view's server render and hydration Oct 3, 2026

This branch had an error being deployed

1 failed deployment
Preview – launch-stack — 29c8a94b Deployed Oct 3, 2026 by vercel[bot]
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.

1 participant