Skip to content

Poc UI rendering validation#877

Draft
JZDesign wants to merge 22 commits into
mainfrom
jzdesign/poc-ui-rendering-validation
Draft

Poc UI rendering validation#877
JZDesign wants to merge 22 commits into
mainfrom
jzdesign/poc-ui-rendering-validation

Conversation

@JZDesign

@JZDesign JZDesign commented May 12, 2026

Copy link
Copy Markdown
Contributor

Motivation / Description

Changes introduced

Linear ticket (if any)

Additional comments

running with a local version of purchases-ui-js (currently in a draft pr 289)

JZDesign and others added 20 commits May 11, 2026 16:01
Documents the design for a testing-only extractor that renders a paywall
at a specified viewport/locale/dark-mode and emits a per-component layout
JSON matching the cross-platform extractor schema. Approach shares the
mount pipeline with presentPaywall so production parity is guaranteed by
construction, while the extractor module is gated to be tree-shaken from
the published bundle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10-phase TDD plan covering: upstream data attrs in purchases-ui-js,
the buildPaywallMountProps refactor, build-time gating, the tree
walker / DOM reader / synthesizer / extractor function, installer,
Node CLI + Playwright orchestrator, CI guard, and the E2E fixture
test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure helper that resolves locale fallback, builds variablesPerPackage,
and parses package info. presentPaywall now calls it; this is the
single mount pipeline that the upcoming extractor will share.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mode-gated Vite define for the upcoming dev-only extractor. Production
builds evaluate it to false (so Rollup will tree-shake gated branches);
dev builds and tests evaluate it to true.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure generator that yields every component entry from PaywallData.
Descends stacks, footers, purchase_button/package wrappers, tabs,
carousels, and timelines. Used by the upcoming extractPaywallLayout.
…ge tests

Earlier walker yielded a tab's id but used walkStackChildren on its
stack, dropping the stack node itself. Switch to walkStack to match
how footer/purchase_button/package handle their inner stacks. Add
tests for tabs/carousel/timeline/badge to lock in coverage.
Looks up rendered nodes via data-rc-component-id; returns ComponentLayout
with frame relative to the container, plus state, nativeType, domTag,
and label (for text components). Frames are rounded to integers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Builds an Offering with one blank package per package_id referenced
by the paywall data. Used by the extractor when the caller does not
supply a real offering; gets rendered through the same paywall mount
pipeline with no purchase functionality required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The synthesizer's local recursion missed packages reachable only via
sticky_footer, tabs/control, carousel/pages, or stack.badge. Reuse the
walker (which already handles every wrapping type) by adding the
original node reference to WalkEntry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mounts <Paywall> via the shared buildPaywallMountProps pipeline into a
sized container, walks the paywall data tree, reads per-component
frames from data-rc-component-id elements, returns ExtractedLayout.

Also stubs window.matchMedia in vitest.setup.js (jsdom omits it) and
fixes fixtureUiConfig/fixturePaywallData to supply the minimal fields
the Paywall component requires (app.fonts, a traversable stack).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dev/test builds attach window.__rcExtractPaywallLayout__ via a gated
dynamic import; production builds strip the entire dev/ subtree from
the bundle via Rollup dead-code elimination. Includes a smoke test
that confirms the global is set when the gate is true.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The playground is the page Playwright loads in Task 7.2's Node
orchestrator: it imports purchases-js's main entry, which (in dev
mode) dynamically imports the extractor installer. Adds three npm
scripts (extract, extract:e2e, check-extractor-stripped) whose
implementations land in subsequent tasks.
Launches a Vite dev server pointing at scripts/extractor-playground,
launches headless Chromium, navigates to the playground page, waits
for window.__rcExtractPaywallLayout__ to be installed, calls it via
page.evaluate, and writes the returned JSON to the --out path.

Also updates extractor-playground/main.ts to import only the extractor
installer rather than all of src/main.ts, avoiding TC39 decorator and
type-only import issues that crash Playwright Chromium. Adds a
devFixupPlugin that replaces __RC_PAYWALL_EXTRACTOR__ at transform time
(Vite define only applies at build time, not dev-server mode) and strips
any residual decorator lines as a defensive measure.

Updates eslint.config.js to allow Node globals and browser globals in
scripts/*.mjs files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move browser launch inside try block so Vite server is closed when
chromium.launch throws. Validate numeric args (width/height/scale) are
positive finite numbers; reject missing flag values upfront. Print a
progress line before browser launch.
Reads dist/Purchases.es.js and dist/Purchases.umd.js and exits 1 if
'extractPaywallLayout', 'installExtractor', or '__rcExtractPaywallLayout__'
appears in either. Wired as 'pnpm run check-extractor-stripped'.
Runs the full Node orchestrator against a fixture paywall and asserts
the output JSON has metadata.platform=web, the required component IDs,
and that the title text renders with a non-zero frame. Catches
regressions across the entire pipeline: Vite dev server + Playwright
+ buildPaywallMountProps + walker + reader + JSON output.

Also excludes scripts/ from the vitest test runner so the E2E harness
is not picked up as a vitest suite (it uses node:assert, not vitest).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 'Verify extractor was stripped from prod bundle' to the test job
(runs check-extractor-stripped after build). Add 'Run paywall layout
extractor E2E' to the test-e2e job (Playwright already installed in
the image). Refresh api-report to confirm no extractor symbols leaked
into the public API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Make WalkEntry.node optional so test fixtures don't have to set it.
- Narrow fixtureUiConfig() return to UIConfig (was Offering['uiConfig']).
- Replace the field-by-field prop list in extractPaywallLayout's mount
  call with ...mountProps so new fields added to PaywallMountProps flow
  automatically; the production path already uses spread.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0534acf6-09e8-4d95-9d0e-cf443a8141bd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jzdesign/poc-ui-rendering-validation

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented May 12, 2026

Copy link
Copy Markdown
1 Error
🚫 Label the PR using one of the change type labels. If you are not sure which label to use, choose pr:other.
1 Warning
⚠️ This PR increases the size of the repo by more than 100.00 KB (increased by 154.13 KB).
Label Description
pr:feat A new feature. Use along with pr:breaking to force a major release.
pr:fix A bug fix. Use along with pr:force_minor to force a minor release.
pr:other Other changes. Catch-all for anything that doesn't fit the above categories. Releases that only contain this label will not be released. Use along with pr:force_patch, or pr:force_minor to force a patch or minor release.
pr:RevenueCatUI Use along any other tag to mark a PR that only contains RevenueCatUI changes
pr:next_release Preparing a new release
pr:dependencies Updating a dependency
pr:phc_dependencies Updating purchases-hybrid-common dependency
pr:changelog_ignore The PR will not be included in the changelog. This label doesn't determine the type of bump of the version and must be combined with pr:feat, pr:fix or pr:other.

Generated by 🚫 Danger

JZDesign and others added 2 commits May 12, 2026 11:48
Adds a --offerings <file> (+ optional --offering-id <id>) path to the
CLI that normalizes an RC /offerings API response into the extractor's
input shape. Synthesizes missing PaywallData.id and default_locale,
falls back to an empty ui_config when the response doesn't carry one.

Adds ExtractInput.offeringId so the metadata's offeringId can be set
without supplying a full Offering object — required because the API
response only contains paywall_components, not package data. When an
identifier is passed without a full offering, the synthesizer still
builds blank packages so buildPaywallMountProps doesn't NPE.

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.

3 participants