From 0651008f3ebd4b59dd0c43365363236c95639b42 Mon Sep 17 00:00:00 2001 From: "fengzhihao.infeng" Date: Mon, 31 Aug 2026 15:27:35 +0800 Subject: [PATCH] test: verify SSR fixture hydration --- package.json | 3 ++- scripts/check-ssr-hydration.js | 47 ++++++++++++++++++++++++++++++++++ ssr-test/app.js | 2 +- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 scripts/check-ssr-hydration.js diff --git a/package.json b/package.json index 84319855..1255822b 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,11 @@ "build": "webpack --config webpack.config.prop.js && npm run build:types", "build:types": "node -e \"require('fs-extra').removeSync('lib')\" && tsc -p tsconfig.build.json", "build:ssr": "npm --prefix ssr-test ci --ignore-scripts && npm --prefix ssr-test run build", + "check:ssr-hydration": "node scripts/check-ssr-hydration.js", "check:package-export": "node scripts/check-package-export.js", "build:analyze": "ANALYZE=true webpack --config webpack.config.prop.js", "doc": "webpack --config webpack.config.doc.js", - "verify": "npm test -- --runInBand && npm run lint && npm run build && npm run check:package-export && npm run doc && npm run build:ssr && npm pack --dry-run", + "verify": "npm test -- --runInBand && npm run lint && npm run build && npm run check:package-export && npm run doc && npm run build:ssr && npm run check:ssr-hydration && npm pack --dry-run", "pub": "npm publish", "prepublishOnly": "npm run build", "predeploy": "npm run doc", diff --git a/scripts/check-ssr-hydration.js b/scripts/check-ssr-hydration.js new file mode 100644 index 00000000..0959020b --- /dev/null +++ b/scripts/check-ssr-hydration.js @@ -0,0 +1,47 @@ +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const { JSDOM } = require('jsdom'); + +const serverMarkup = [ + '', + '
', + '', +].join(''); +const dom = new JSDOM(serverMarkup, { + runScripts: 'outside-only', + url: 'http://127.0.0.1:8005/', +}); + +async function verifyHydration() { + dom.window.requestAnimationFrame = callback => dom.window.setTimeout(callback, 0); + dom.window.cancelAnimationFrame = id => dom.window.clearTimeout(id); + + const bundlePath = path.join(__dirname, '..', 'ssr-test', 'client-dist', 'main.js'); + dom.window.eval(fs.readFileSync(bundlePath, 'utf8')); + + const openButton = dom.window.document.querySelector('#root button'); + assert(openButton, 'The SSR fixture must preserve its server-rendered button during hydration'); + assert.strictEqual(openButton.textContent, 'open'); + + openButton.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true })); + await new Promise(resolve => dom.window.setTimeout(resolve, 50)); + + assert.strictEqual(openButton.textContent, 'close', 'The hydrated fixture must respond to user input'); + assert( + dom.window.document.querySelector('[aria-label="Close viewer"]'), + 'The hydrated fixture must render the Viewer component' + ); + + console.log('SSR fixture hydrated and opened the Viewer component.'); +} + +verifyHydration() + .then(() => { + dom.window.close(); + }) + .catch(error => { + dom.window.close(); + console.error(error); + process.exit(1); + }); diff --git a/ssr-test/app.js b/ssr-test/app.js index 37b8a5db..93d0d5e8 100644 --- a/ssr-test/app.js +++ b/ssr-test/app.js @@ -1,7 +1,7 @@ const React = require('react'); const Viewer = typeof window === 'undefined' ? () => null - : require('../dist/index').default; + : require('../dist/index'); function App () { const [ visible, setVisible ] = React.useState(false);