From d4f116279076a16514a80d4a5dff63d3f5948782 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Jun 2026 23:39:01 -0400 Subject: [PATCH] =?UTF-8?q?test(ui):=20React=20parity=20=E2=80=94=20StartT?= =?UTF-8?q?ransition=20applies=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports React's ReactStartTransition: a state update wrapped in rt.StartTransition commits to the host DOM; multiple updates inside one transition all take effect; a same-value transition update is a no-op. Verified via the native reconciler harness. All pass; no GWC bug. No version bump. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/REACT_TEST_PARITY.md | 2 +- ui/transition_native_test.go | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 ui/transition_native_test.go diff --git a/docs/REACT_TEST_PARITY.md b/docs/REACT_TEST_PARITY.md index ac2d671c..e716f548 100644 --- a/docs/REACT_TEST_PARITY.md +++ b/docs/REACT_TEST_PARITY.md @@ -50,7 +50,7 @@ re-renders/effects are observable inline. SSR behavior is tested via | SSR primitive children (number/bool/null) | render primitives as children | 🚫 | Go static typing: html.* children are ui.Node; primitives go via ui.Text | | onlyChild-test | Children.only | ⬜ | | | ReactContextValidator / NewContext | context | ✅ | ui/context_native_test.go | -| ReactStartTransition | transitions | ⬜ | (GWC has StartTransition) | +| ReactStartTransition | transitions | ✅ | ui/transition_native_test.go (transition-wrapped updates commit; multi-update; same-value dedup) | | ReactJSX* / ES6Class / PureComponent / Version / Profiler-devtools | — | 🚫 | no Go analog | ## react-reconciler (spirit-applicable) diff --git a/ui/transition_native_test.go b/ui/transition_native_test.go new file mode 100644 index 00000000..dd13b6e2 --- /dev/null +++ b/ui/transition_native_test.go @@ -0,0 +1,60 @@ +//go:build !js || !wasm + +package ui_test + +import ( + "fmt" + "testing" + + "github.com/monstercameron/GoWebComponents/internal/platform/mockdom" + "github.com/monstercameron/GoWebComponents/internal/runtime" +) + +// Ported from React's ReactStartTransition: a state update wrapped in +// StartTransition is applied (it commits to the host DOM); a same-value +// transition update does not re-render; multiple updates inside one transition +// all take effect. +func TestStartTransitionAppliesUpdates(t *testing.T) { + adapter := mockdom.NewMockDOMAdapter() + rt := runtime.NewRuntime(runtime.Config{DOMAdapter: adapter, Reset: true}) + root := adapter.CreateElement("div") + + var parseSetA, parseSetB func(any) + rt.RenderInto(root, runtime.CreateElement(func() *runtime.Element { + parseGetA, parseSA := runtime.GoUseState(rt, 0) + parseGetB, parseSB := runtime.GoUseState(rt, 0) + parseSetA = parseSA + parseSetB = parseSB + return runtime.CreateElement("span", map[string]any{}, fmt.Sprintf("%v-%v", parseGetA(), parseGetB())) + }, map[string]any{})) + + read := func() string { + parseNode, _ := adapter.GetChildren(root)[0].(*mockdom.MockDOMNode) + return parseNode.TextContent + } + + if read() != "0-0" { + t.Fatalf("mount = %q, want 0-0", read()) + } + + // A single transition update commits. + rt.StartTransition(func() { parseSetA(7) }) + if read() != "7-0" { + t.Errorf("after StartTransition setA(7) = %q, want 7-0", read()) + } + + // Multiple updates inside one transition all take effect. + rt.StartTransition(func() { + parseSetA(1) + parseSetB(2) + }) + if read() != "1-2" { + t.Errorf("after multi-update transition = %q, want 1-2", read()) + } + + // A same-value transition update is a no-op (final value unchanged). + rt.StartTransition(func() { parseSetA(1) }) + if read() != "1-2" { + t.Errorf("after same-value transition = %q, want 1-2", read()) + } +}