Skip to content

Conversation

@rgaignault
Copy link
Contributor

@rgaignault rgaignault commented Jan 16, 2026

Motivation

Second part of shadow DOM support for actions. This PR adds selector generation for elements inside shadow DOM, using a /shadow/ marker to indicate shadow boundaries.
Follows up on the first PR that added action name support.

Changes

  • Generate selectors that cross shadow DOM boundaries with /shadow/ marker
  • Added unit tests for shadow DOM selectors
  • Added e2e tests for selector generation

These two pictures are coming from the same click with and without the parameter enabled. The selector is correct and more precise that's why the width and height are smaller.

Before

image

After

image

Selector query

image

Test instructions

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.

@rgaignault rgaignault requested a review from a team as a code owner January 16, 2026 14:13
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccb1ca4218

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cit-pr-commenter
Copy link

cit-pr-commenter bot commented Jan 16, 2026

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 164.92 KiB 165.16 KiB +250 B +0.15%
Rum Profiler 4.33 KiB 4.33 KiB 0 B 0.00%
Rum Recorder 24.48 KiB 24.48 KiB 0 B 0.00%
Logs 56.14 KiB 56.14 KiB 0 B 0.00%
Flagging 944 B 944 B 0 B 0.00%
Rum Slim 122.15 KiB 122.40 KiB +250 B +0.20%
Worker 23.63 KiB 23.63 KiB 0 B 0.00%
🚀 CPU Performance
Action Name Base CPU Time (ms) Local CPU Time (ms) 𝚫%
RUM - add global context 0.004 0.0046 +15.00%
RUM - add action 0.012 0.0142 +18.33%
RUM - add error 0.0114 0.0151 +32.46%
RUM - add timing 0.0026 0.0037 +42.31%
RUM - start view 0.0032 0.0037 +15.63%
RUM - start/stop session replay recording 0.0007 0.0009 +28.57%
Logs - log message 0.0133 0.0144 +8.27%
🧠 Memory Performance
Action Name Base Memory Consumption Local Memory Consumption 𝚫
RUM - add global context 27.38 KiB 26.37 KiB -1.01 KiB
RUM - add action 52.43 KiB 49.01 KiB -3.42 KiB
RUM - add timing 26.06 KiB 26.14 KiB +79 B
RUM - add error 57.57 KiB 55.15 KiB -2.42 KiB
RUM - start/stop session replay recording 26.09 KiB 24.24 KiB -1.85 KiB
RUM - start view 427.23 KiB 423.07 KiB -4.16 KiB
Logs - log message 99.40 KiB 48.62 KiB -50.79 KiB

🔗 RealWorld

@datadog-datadog-prod-us1
Copy link

datadog-datadog-prod-us1 bot commented Jan 16, 2026

✅ Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

🎯 Code Coverage
Patch Coverage: 84.38%
Overall Coverage: 77.29% (+0.03%)

View detailed report

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 729735f | Docs | Datadog PR Page | Was this helpful? Give us feedback!

Copy link
Contributor

@BeltranBulbarellaDD BeltranBulbarellaDD left a comment

Choose a reason for hiding this comment

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

Nice! Works perfectly with the html test
Looks good in the explorer UI too

import { isNodeShadowRoot } from '../browser/htmlDomUtils'
import { DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE } from './action/actionNameConstants'

export const SHADOW_DOM_MARKER = '::shadow'
Copy link
Collaborator

Choose a reason for hiding this comment

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

💬 suggestion: ‏Could be nice to add a comment explaining the reasoning behind choosing this marker, like this is not a supported CSS selector etc.


while (currentTarget) {
const rootNode = currentTarget.getRootNode() as Document | ShadowRoot
result.unshift({ rootNode, target: currentTarget })
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: there's an opportunity to improve computational complexity here a bit.

Every time you call unshift(), every element that's already in the array needs to be copied to a new position to make space for the element you're unshifting. A single call to unshift() is thus O(n), which makes a loop like this O(n^2). A better approach is to use push(), which is amortized O(1), resulting in an amortized O(n) loop, and then reverse() the array, another O(n) operation. By using push() and reverse(), the overall task is O(n) instead of O(n^2).

Given that result is going to be very short in most cases, I wouldn't expect a substantial performance difference from this change, but it's good to develop the habit of using this approach, since the impact can be surprisingly large in cases where the array is larger!

shadowRoot.appendChild(button)

const selector = getSelectorFromElement(button, undefined)
expect(selector).toBe(`BODY>DIV:nth-of-type(1)${SHADOW_DOM_MARKER}#foo`)
Copy link
Contributor

Choose a reason for hiding this comment

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

Just for clarity, is this the result we intended? The #foo inside and outside the shadow subtree are two different #foos, and that's perfectly fine; I'd actually have expected the result here to be this:

BODY>DIV#foo${SHADOW_DOM_MARKER}#foo

Using the id at both leaves results in a less fragile selector, so it's a better choice, unless there's some issue that's leading us to avoid it.

* Note: This is NOT a valid CSS selector, it's an internal marker that requires custom
* parsing logic.
*/
export const SHADOW_DOM_MARKER = '::shadow'
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for not realizing this earlier, but I think this should actually be ::shadow>; otherwise the selectors you're generating would like like e.g. ::shadowBUTTON, when what we really want is ::shadow>BUTTON.

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.

6 participants