docs/solutions/best-practices/2026-05-09-lexical-serialization-harvest-rows-need-public-value-roundtrip-tests.md
Lexical serialization unit tests can look portable because they use JSON round-tripping, but the fixture mostly asserts Lexical's editor-state schema. For Slate v2, the useful invariant is narrower: raw document values must be JSON-portable through the public state API.
Add a compact public API test that serializes only Slate document values:
const serialized = JSON.stringify(value);
const parsed = JSON.parse(serialized) as Descendant[];
const editor = createEditor({ initialValue: parsed });
const exported = editor.read((state) => state.value.get());
const rehydrated = createEditor({
initialValue: JSON.parse(JSON.stringify(exported)) as Descendant[],
});
assert.deepEqual(exported, value);
assert.deepEqual(
rehydrated.read((state) => state.value.get()),
value,
);
Keep runtime indexes and framework metadata out of the raw value assertion:
assert.equal(JSON.stringify(exported).includes("pathToId"), false);
assert.equal(JSON.stringify(exported).includes("idToPath"), false);
Slate's public document value is the portable contract. Runtime IDs, indexes, commit metadata, parser APIs, and node class schemas belong to other owners or stay framework-specific. The test proves the behavior applications rely on without pretending Slate should serialize like Lexical.
../lexical/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts.tmp/slate-v2/packages/slate/test/state-tx-public-api-contract.ts