Conversation
…-a-user-is-activated-into-an-optimizely-experiment
HarveyPeachey
left a comment
There was a problem hiding this comment.
Wouldn't it be easier/better to do this in the decision event listener, which already tells us when an activation/impression occurs?
…-a-user-is-activated-into-an-optimizely-experiment
There was a problem hiding this comment.
Pull request overview
Adds standalone Piano/Reverb “activation” analytics to capture when a user is activated into an Optimizely experiment (server-side and client-side), decoupled from view/click events, with dedupe to prevent duplicate firing.
Changes:
- Introduces an activation event model (
buildActivationEventModel) and a newsendOptimizelyActivationEventutility to send the beacon. - Adds
useOptimizelyActivationEventhook to collect ATI context + expose a stable activation-sender callback. - Adds once-per-experiment activation dedupe and activation-event triggering for both
useServerSideanduseClientSideOptimizely flows, plus expanded test coverage.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/app/lib/analyticsUtils/sendOptimizelyActivationEvent/index.ts | Adds guarded sender for Optimizely activation beacons. |
| src/app/lib/analyticsUtils/sendOptimizelyActivationEvent/index.test.ts | Unit tests for activation-beacon guard/send behavior. |
| src/app/lib/analyticsUtils/sendBeacon/index.ts | Extends Reverb sending to support activation and additional labels. |
| src/app/lib/analyticsUtils/sendBeacon/index.test.ts | Adds coverage for activation event wiring to userActionEvent. |
| src/app/lib/analyticsUtils/analytics.const.ts | Adds ACTIVATION_EVENT constant. |
| src/app/hooks/usePWAInstallTracker/index.test.tsx | Updates hook tests to include ToggleContext provider wrapper. |
| src/app/hooks/useOptimizelyVariation/useServerSide/index.ts | Adds activation-event firing + ref-based dedupe to server-side hook. |
| src/app/hooks/useOptimizelyVariation/useServerSide/index.test.tsx | Updates tests for new provider requirements + activation side-effects. |
| src/app/hooks/useOptimizelyVariation/useClientSide/index.ts | Adds activation-event firing + ref-based dedupe to client-side hook. |
| src/app/hooks/useOptimizelyVariation/useClientSide/index.test.tsx | New tests covering activation event sending + dedupe. |
| src/app/hooks/useOptimizelyVariation/useClientSide/index.test.ts | Removes old client-side hook tests (replaced by .test.tsx). |
| src/app/hooks/useOptimizelyVariation/activateExperiment/index.ts | Adds dedupe and activation callback hook point. |
| src/app/hooks/useOptimizelyVariation/activateExperiment/index.test.ts | Adds tests for dedupe and activation callback invocation. |
| src/app/hooks/useOptimizelyActivationEvent/index.ts | New hook to gather ATI context and send activation beacons. |
| src/app/hooks/useOptimizelyActivationEvent/index.test.tsx | Tests for activation hook behavior and tracking-toggle handling. |
| src/app/components/ATIAnalytics/types.ts | Extends Reverb event typing to support activation + extra labels. |
| src/app/components/ATIAnalytics/atiUrl/index.ts | Adds buildActivationEventModel for Reverb activation events. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ove redundant activation properties
…tivated-into-an-optimizely-experiment' of https://github.com/bbc/simorgh into WS-3079-send-a-piano-activation-event-when-a-user-is-activated-into-an-optimizely-experiment
elvinasv
left a comment
There was a problem hiding this comment.
Looks like we're really close! Great flow diagram 👍
…ct; related tests
|
Hello. |
Hello, |
…yActivationEvent --> sendExperimentActivationEvent
… DECISION listener [copilot]
|
I've cross-referenced the schema in buildReverbEventModel, the function we use via the The main differences are the missing attributes are Comparing the spec v2.0.0 to v1.0.1 , |
…-a-user-is-activated-into-an-optimizely-experiment
…tivated-into-an-optimizely-experiment' of github.com:bbc/simorgh into WS-3079-send-a-piano-activation-event-when-a-user-is-activated-into-an-optimizely-experiment
…-a-user-is-activated-into-an-optimizely-experiment
…-a-user-is-activated-into-an-optimizely-experiment
…-a-user-is-activated-into-an-optimizely-experiment
Resolves JIRA: https://bbc.atlassian.net/browse/WS-3079
Summary
Adds a standalone Piano/Reverb "activation" beacon that fires the moment a user is bucketed into an Optimizely experiment (client- or server-side), decoupled from any page-view/click event, following the official "Activation (v1.0.1) on Web" viewability-model event spec.
Code changes
buildActivationEventModelinatiUrlto build a spec-compliant viewability beacon (event.action: 'serve',interaction_type: 'optimizely_activation',spec_id/spec_version,group/experiencefields)sendOptimizelyActivationEventand a module-levelactivationContextbridge that carries the current page's ATI tracking context into the non-React OptimizelyDECISIONnotification listenerwithOptimizelyProvider'sDECISIONlistener now dedupes per experiment vianotifyDecision(now returns whether the decision was newly recorded) and fires the activation beacon once per unique decisionactivateExperimentnow guards against re-activating or concurrently double-activating the same experiment via a module-levelSetuseServerSidenow derives a singleactiveVariationvalue and always activates viauseEffect(previously activated on some paths outside the effect)appName/platformthroughEventTrackingContext→activationContext→ the beacon builder soapp_name/app_typereflect each service's real ATI config instead of being hardcodedTesting
yarn devand open a page with an active Optimizely experimenteventPublisher: 'viewability'andevent.interaction_type: 'optimizely_activation'yarn jest src/app/hooks/useOptimizelyVariation src/app/contexts/EventTrackingContext src/app/legacy/containers/PageHandlers/withOptimizelyProvider src/app/lib/analyticsUtils src/app/lib/optimizelyDecisionStore.test.tsFlow Diagram
sequenceDiagram participant ETC as EventTrackingContextProvider participant AC as activationContext (module) participant Hook as useServerSide / useClientSide participant SDK as Optimizely SDK participant Listener as DECISION listener (withOptimizelyProvider) participant Store as optimizelyDecisionStore participant Send as sendOptimizelyActivationEvent participant Beacon as sendBeacon (Piano/Reverb) Note over ETC: 1. Render time (synchronous, not an effect) ETC->>AC: setActivationContext({ trackingIsEnabled, pageIdentifier, platform, appName, producerName, statsDestination, isSignedIn, hashedId }) Note over Hook: 2. A descendant resolves an experiment variation Hook->>SDK: activateExperiment() → optimizely.activate() (server-side)<br/>or useDecision() (client-side) SDK-->>Listener: 3. fires DECISION notification (decisionInfo) Listener->>Listener: resolveDecision() → { decisionKey, impressionDispatched } alt decisionKey && variationKey && variationKey !== 'off' Listener->>Store: 4. notifyDecision(decisionKey) Store-->>Listener: isNewDecision (true = first time this session) alt impressionDispatched alt isNewDecision Listener->>AC: 5. getActivationContext() AC-->>Listener: bridged ATI context Listener->>Send: sendOptimizelyActivationEvent({ experimentName, experimentVariant, ...context }) Send->>Send: 6. guard: trackingIsEnabled, variant valid, required ATI fields present Send->>Beacon: 7. sendBeacon(reverbParams) end Listener->>SDK: optimizely.track('visit' / 'page-views' / 'signed-in-page-views') end endUseful Links