Skip to content

UVE: Experiments doesn't work #36484

Description

@KevinDavilaDotCMS

Problem Statement

withExperiments (the @dotcms/experiments HOC used to wrap DotCMSLayoutBody for A/B testing, see core-web/libs/sdk/experiments) can leave the wrapped page permanently hidden (visibility: hidden, never flips to visible) instead of rendering. This affects both real site visitors going through an experiment redirect and content editors previewing an experiment variant inside the Universal Visual Editor (UVE).

The component's DotExperimentHandlingComponent hides content behind a <div style={{ visibility: 'hidden' }}> while useExperimentVariant()'s shouldWaitForVariant is true, expecting it to flip to false shortly after. Three separate, independent bugs could each leave it stuck at true forever:

  1. Unhandled promise rejection in DotExperimentsProviderDotExperiments.getInstance(config).ready().then(() => setInstance(...)) had no .catch. If initialization ever rejected (bad apiKey/server, network failure, indexedDB error), setInstance never ran, the DotExperimentsContext stayed null forever, and useExperimentVariant could never resolve a matching variant.

  2. Inconsistent "inside the editor" detectionDotExperimentsProvider.tsx treated any truthy getUVEState()?.mode (EDIT_MODE, PREVIEW_MODE, LIVE) as "inside the editor" and skipped creating a DotExperiments instance entirely. But useExperimentVariant.ts only treated UVE_MODE.EDIT specifically as "inside the editor" — so when UVE was in PREVIEW_MODE (e.g. content editors using dotCMS's "preview this experiment variant" feature), the hook fell through expecting an instance from context that the provider had deliberately never created, waiting on it forever.

  3. Example app (examples/nextjs-experiments) not forwarding the variantName query paramsrc/app/[[...slug]]/page.tsx built the dotCMS page fetch path from route params only and never read searchParams, so the ?variantName=... query param appended by the SDK's client-side redirect (useExperiments' locationChanged) never reached dotCMSClient.page.get(). dotCMS always returned the DEFAULT variant's viewAs.variantId, which never matched the client-assigned variant name, so the match check in useExperimentVariant never succeeded — same permanent-hidden symptom, this time for real (non-UVE) traffic, plus the wrong variant's content/containers being served regardless.

Steps to Reproduce

Scenario A — real visitor redirect flow (bug #1 and #3):

  1. Set up the nextjs-experiments example app against a dotCMS instance with a running experiment on the home page.
  2. Load the site as a normal visitor (no UVE). The client-side SDK detects the assigned variant and redirects to /?variantName=dotexperiment-<expId>-<variantName>.
  3. Observe: the redirect succeeds (200 response), but the page body never appears — pageAsset.viewAs.variantId logs as 'DEFAULT' regardless of the URL's variantName param.

Scenario B — UVE preview mode (bug #2):

  1. Open the same page inside the dotCMS Universal Visual Editor.
  2. Use the "preview this experiment variant" feature, which loads the iframe with ?mode=PREVIEW_MODE&variantName=...&experimentId=....
  3. Observe console log: DotExperimentsProvider: DotExperiments instance not initialized because it is inside the editor. followed by a blank page — the wrapped component never renders.

Scenario C — init failure (bug #1, harder to reproduce on demand):

  1. Configure @dotcms/experiments with an invalid apiKey/server so the experiments-fetch call fails.
  2. Observe the page stays blank indefinitely with no console error, since the failed ready() promise is silently swallowed.

Acceptance Criteria

  • A visitor redirected to an experiment variant URL (outside UVE) sees the assigned variant's content render (not a blank/hidden page).
  • Previewing an experiment variant inside UVE (mode=PREVIEW_MODE) renders the page instead of staying hidden.
  • A failed DotExperiments initialization (bad config, network error) no longer leaves the page permanently hidden.
  • Regression tests cover all three scenarios where feasible (PREVIEW-mode case covered in useExperimentVariant.spec.tsx; the example-app searchParams forwarding and init-failure fallback are not covered by automated tests yet).

dotCMS Version

latest

Severity

High - Major functionality broken

Links

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions