Skip to content

TE-15304: ship updated dom-serializer bundle with scroll capture#522

Merged
sushobhit-lt merged 5 commits into
LambdaTest:stagefrom
shrinishLT:TE-15304
May 29, 2026
Merged

TE-15304: ship updated dom-serializer bundle with scroll capture#522
sushobhit-lt merged 5 commits into
LambdaTest:stagefrom
shrinishLT:TE-15304

Conversation

@shrinishLT
Copy link
Copy Markdown
Contributor

@shrinishLT shrinishLT commented May 26, 2026

Summary

Re-embeds the prod (obfuscated) DOM-serializer bundle. The new bundle stamps:

  • data-smartui-page-scroll-x/y on <html> (window-level scroll)
  • data-smartui-scroll-top/left on every scrollable element (inner-container scroll)

…so the rendering service can reproduce the user's actual scroll position before taking the screenshot. Fixes blank/wrong-page screenshots on UIs that use virtualized scrolling (Vault PDF viewer, react-window, react-virtualized, etc.).

What changed in this PR

One file only: src/dom/dom-serializer.js — replaced with the rebuilt obfuscated bundle from the serializer repo. No TypeScript source code changes. src/lib/server.ts:63 already reads this file and ships its bytes to SDKs over the existing /domserializer route.

Bundle size: 62 266 → 63 700 bytes (the new serialize-scroll module).

Backward compatibility

  • Older SDKs continue to work — they just receive a slightly different bundle and call SmartUIDOM.serialize(...) the same way.
  • Older dotui ignores unknown data-smartui-* attributes on the HTML — silent no-op until dotui is also updated.

Dependencies

Test plan

  • Bundle built locally from the serializer repo via npm run build:prod.
  • Re-validated end-to-end with the Playwright POC harness against the obfuscated bundle — captures and replays scroll state byte-identically.
  • Reviewer runs the ~/Documents/scripts/sdk-playwright/sdk/Makefile upd target locally to repack with this bundle and run against the test URL list.

🤖 Generated with Claude Code

Shrinish Vhanbatte and others added 5 commits May 26, 2026 15:45
Re-embeds the prod (obfuscated) bundle from smartui-cli-dom-serializer
PR LambdaTest#11 into src/dom/dom-serializer.js. The new bundle stamps page-
and per-element scroll-state data attributes during DOM serialization
so the rendering service can reproduce the user's scroll position
before screenshotting (fixes blank screenshots on virtualized
scrollers like the Vault PDF viewer).

No TypeScript source code changes — src/lib/server.ts already reads
this file and ships its contents to SDKs over HTTP at the existing
/domserializer route. Older SDKs and older dotui ignore the new
attributes (forward/backward compatible).

Bundle size: 62 266 -> 63 700 bytes (the new serialize-scroll module).

Depends on: https://github.com/LambdatestIncPrivate/smartui-cli-dom-serializer/pull/11
RFC: https://github.com/LambdatestIncPrivate/internal-docs/pull/1991
JIRA: https://lambdatest.atlassian.net/browse/TE-15304

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the two new opt-in flags through the CLI so they reach both the
serializer (running in the SDK's browser process) and dotui (in the
snapshot payload on Kafka).

- src/types.ts: declare both flags on the Snapshot.options interface.
- src/lib/schemaValidation.ts: extend snapshot.options schema so the
  flags pass validation. Without this, the existing
  additionalProperties:false would reject snapshots that set them.
- src/lib/processSnapshot.ts: forward both flags into processedOptions
  so they are carried through to the Kafka payload that dotui
  consumes. Mirrors the existing useExtendedViewport plumbing.
- src/dom/dom-serializer.js: refresh the obfuscated bundle from the
  updated serializer (now gates capture by these same flags).
  62 266 -> 64 765 bytes (the new gating + early-exit logic).

Both flags default false: when a snapshot doesn't set them, the new
code paths are no-ops at every layer.

Depends on: https://github.com/LambdatestIncPrivate/smartui-cli-dom-serializer/pull/11
RFC: https://github.com/LambdatestIncPrivate/internal-docs/pull/1991

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pixel scroll values are captured from the test's browser/viewport.
If the snapshot fans out to other browsers/viewports/devices via web
or mobile config, only the matching combo reproduces correctly; others
drift. Surface this as a one-line warn so users see it during the run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
processSnapshot.ts has two parallel functions that each process options
into processedOptions: prepareSnapshot (line 44, used for the LSRS-cap
path) and processSnapshot (line 290, the default export used for the
local-discovery path). The earlier commit only added forwarding to the
first one — local-discovery flows (the common case) dropped the new
flags silently. Add forwarding + warning to processSnapshot too.

Also refreshes the obfuscated dom-serializer bundle to match the latest
serializer build (capture is now unconditional at the serializer layer
— see smartui-cli-dom-serializer commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sushobhit-lt sushobhit-lt merged commit 6c41cb1 into LambdaTest:stage May 29, 2026
shrinishLT pushed a commit to shrinishLT/smartui-cli that referenced this pull request May 30, 2026
## Cause

The dom-serializer bundle is generated by webpack + webpack-obfuscator
and committed to this repo as a pre-built artifact (src/dom/dom-
serializer.js). webpack-obfuscator's output is **non-deterministic** —
for the same source, builds randomly produce either:

  (A) an IIFE-wrapped bundle: `(function(...){...})()`            ~20%
  (B) a bundle starting with a hoisted helper function declaration:
      `function a0_0xNNNN(...){...}`                             ~80%

The bundle that landed on this branch (and was merged via PR LambdaTest#522 to
stage) happened to be form (B). Any SDK consumer that injects the
bundle via `page.evaluate(str)` then breaks with:

    SyntaxError: Unexpected token 'function'

…because page.evaluate parses its input as an expression, and a leading
function declaration is a statement, not an expression. eval() and
script-tag injection are unaffected, so:

  ✓ @lambdatest/cypress-driver    (uses eval())
  ✓ @lambdatest/selenium-driver   (executeScript as script tag)
  ✗ @lambdatest/playwright-driver (page.evaluate)
  ✗ @lambdatest/puppeteer-driver  (page.evaluate)
  ✗ @lambdatest/webdriverio-driver (browser.execute)

This masking is why the regression wasn't caught by CI — most flows
use selenium/cypress.

## Fix in this commit

Re-runs `npm run build:prod` until the obfuscator lands an IIFE-wrapped
roll, then ships that bundle. No source/code change — only the pre-
built artifact at src/dom/dom-serializer.js is replaced.

Verified empirically: 10-run experiment (5 baseline + 5 TE-15304),
both setups produced ~80% bad rolls. After this commit, the bundle
starts with `(function(_0x40c4a2,_0x2284c6){...` — page.evaluate
consumers work, snapshot is captured end-to-end.

## Follow-up (not in this PR)

webpack-obfuscator's `stringArray: true` + `selfDefending: true`
features are what cause the random helper hoisting. A future PR
against smartui-cli-dom-serializer should disable those (and
`splitStrings`) so future bundles are deterministic and never
require a manual re-roll. That change is non-trivial — it reduces
obfuscation strength somewhat — and is left for a separate PR/
discussion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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