Skip to content

Conversation

@nizans
Copy link

@nizans nizans commented Dec 15, 2025

When baseElement is not provided and render() falls back to document.body, this PR sets a data-testid on the body to ensure getElementLocatorSelectors(baseElement) generates a stable root selector.

Without a stable attribute on the default baseElement, the generated selector sometimes rely's on the document.body text content.
This results in a flakey selector since sometimes the body includes some content that might change.

Adding the test id enforce the generated selector to be a stable testid-based selector.

Issue described here:
#42

src/pure.tsx Outdated
Comment on lines 74 to 77
// default to document.body instead of documentElement to avoid output of potentially-large
// head elements (such as JSS style blocks) in debug output
baseElement = document.body
document.body.dataset.testid = 'test-body'
Copy link
Author

Choose a reason for hiding this comment

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

Currently attached the testid only in cases where baseElement was not provided.

So if consumers provides a different baseElement it might fail again.

Not sure if the library should always attach it or not.

src/pure.tsx Outdated
// default to document.body instead of documentElement to avoid output of potentially-large
// head elements (such as JSS style blocks) in debug output
baseElement = document.body
document.body.dataset.testid = 'test-body'
Copy link
Member

@sheremet-va sheremet-va Dec 15, 2025

Choose a reason for hiding this comment

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

  1. The id needs to be generated to be unique, see nanoid for example - ported in VItest as https://github.com/vitest-dev/vitest/blob/8353defd6743925f363a7cf4d04f0297eaecc9af/packages/utils/src/nanoid.ts#L5
  2. testid name can be redefined by the user via browser.locators.testIdAttribute, I think just using id should be fine if it's not defined already, since the locator generator will always prefer it instead

Copy link
Author

@nizans nizans Dec 15, 2025

Choose a reason for hiding this comment

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

Weirdly enough the generator prefers the text content in my attempt

I tried to look at https://github.com/sheremet-va/ivya/blob/56a31c992baab353f82b46f3cadde9292eb3076a/src/selectorGenerator.ts#L112C10-L112C29
Looks like it requires a deeper dive.
Maybe its a bug in the Ivya code ?

Screen.Recording.2025-12-15.at.18.51.39.mov

Copy link
Author

@nizans nizans Dec 15, 2025

Choose a reason for hiding this comment

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

Maybe we can take a different approach and grab the testIdAttribute from the config and use it?

@nizans
Copy link
Author

nizans commented Dec 15, 2025

@sheremet-va

Pushed a change where ids are generated using nanoid and the user defined test id attribute is respected.

Let me know what you think about this approach

if (task.file.projectName === 'prod (chromium)') {
skip('Cannot mock nanoid in prod build')
}
vi.mock('@vitest/utils/helpers', { spy: true })
Copy link
Member

Choose a reason for hiding this comment

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

This is not how vi.mock works. It is always hoisted to the top of the file, this is a compiler hint, not a function call.

Copy link
Author

@nizans nizans Dec 17, 2025

Choose a reason for hiding this comment

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

Oh thanks for clarifying

Updated and moved this mock to the top.

const stuff = document.createElement('div')
stuff.textContent = 'DOM content that might change'
document.body.appendChild(stuff)
setTimeout(() => {
Copy link
Member

@sheremet-va sheremet-va Dec 22, 2025

Choose a reason for hiding this comment

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

Removing this code doesn't break the test, that seems weird. With your changes or without

import type { JSX } from 'react/jsx-runtime'

export function ComponentThatChanges({
timeout = 1500,
Copy link
Member

Choose a reason for hiding this comment

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

Let's not artificially make our tests slower. Is there a better way to test this?

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.

2 participants