Skip to content

WS-2284 - Fix lost view events in Optimizely#13858

Open
louisearchibald wants to merge 19 commits intolatestfrom
WS-2284-fix-lost-view-events-in-optimizely
Open

WS-2284 - Fix lost view events in Optimizely#13858
louisearchibald wants to merge 19 commits intolatestfrom
WS-2284-fix-lost-view-events-in-optimizely

Conversation

@louisearchibald
Copy link
Copy Markdown
Contributor

@louisearchibald louisearchibald commented Mar 31, 2026

Resolves JIRA: WS-2284

Summary

Adds a notification listener to our OptimizelyPageMetrics code to prevent page-view events firing before experiments activate.

Code changes

  • Adds a DecisionListener type.
  • Adds a decideAll check before rendering tracking.
  • Adds a listener to handle experiment assignment after page load.
  • Ensures tracking only runs when the user is in a relevant experiment.
  • Cleans up notification listener on unmount.
  • Adds @optimizely/optimizely-sdk as a dependency.
  • Adds unit tests.

Testing

  1. List the steps required to test this PR.

@louisearchibald louisearchibald self-assigned this Mar 31, 2026

let mounted = true;

optimizely.onReady().then(() => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this still send a page view too early on first load? We’re marking the user as “in the experiment” from decideAll here, so tracking can start before the new Optimizely listener has actually fired.

If the listener is meant to be the safeguard, should that callback be the thing that unlocks tracking rather than decideAll?

Maybe worth doing some manual testing if poss to verify

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd agree with this point, we should be waiting until the event fires to set isInExperiment I think

@louisearchibald louisearchibald marked this pull request as ready for review April 8, 2026 08:02
@louisearchibald louisearchibald requested a review from a team as a code owner April 8, 2026 08:02
Copilot AI review requested due to automatic review settings April 8, 2026 08:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses lost Optimizely page-view events by ensuring tracking in OptimizelyPageMetrics only renders after the user is confirmed to be bucketed into a relevant experiment, including handling late experiment assignment via an Optimizely decision notification listener.

Changes:

  • Adds @optimizely/optimizely-sdk to access Optimizely enums for notification types.
  • Adds an initial decideAll-based membership check before rendering tracking components.
  • Attaches and cleans up a decision notification listener to detect post-load bucketing into relevant experiments.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/app/components/OptimizelyPageMetrics/index.tsx Adds initial bucketing check and a decision notification listener to gate tracking rendering until experiment membership is confirmed.
package.json Adds a direct dependency on @optimizely/optimizely-sdk for notification enums.
yarn.lock Updates lockfile to reflect the added Optimizely SDK dependency/version resolution.

Comment on lines +48 to +57
// on initial load, check if the user is in any relevant experiment and set state accordingly
useEffect(() => {
if (
!optimizelyExperimentsEnabled ||
!optimizely ||
!experimentsForPageType
) {
setIsInExperiment(false);
return undefined;
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The early-exit guard (optimizelyExperimentsEnabled/optimizely/experimentsForPageType checks) and mounted-flag pattern is duplicated across two effects, which makes future changes easy to apply inconsistently. Consider consolidating into a single effect that does the initial decideAll check and attaches the notification listener, returning one shared cleanup.

Copilot uses AI. Check for mistakes.
expect(
screen.queryByTestId('page-view-tracking'),
).not.toBeInTheDocument();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this assertion will be of any value as it will instantly pass even if this element was later added. May be best to remove it 🤔

],
);

let decisionListener: NotificationListener<any> | null = null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this type be addressed or at least explicitly ignored as it's in a test

!experimentsForPageType
) {
setIsInExperiment(false);
return undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally find this control flow a little confusing, I'd prefer the use of else if rather than the early return in this case.

Comment on lines +104 to +105
typeof optimizely.notificationCenter.addNotificationListener ===
'function'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would this ever not be true?

Comment on lines +117 to +119
const isRelevantExperiment =
typeof flagKey === 'string' &&
experimentsForPageType.includes(flagKey);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can the flagKey ever not be a string in practice?

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.

5 participants