Skip to content

feat: warn when a text is passed where a selector is expected - #5696

Open
DavertMik wants to merge 1 commit into
4.xfrom
feat/warn-on-text-as-selector
Open

feat: warn when a text is passed where a selector is expected#5696
DavertMik wants to merge 1 commit into
4.xfrom
feat/warn-on-text-as-selector

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

Problem

waitForElement, seeElement, waitForVisible, grabTextFrom and the rest of the locator-only family expect a CSS or XPath selector. Unlike click or fillField, they do not fall back to searching by text — the string goes straight to the engine, is parsed as a CSS descendant chain, matches nothing, and the step fails after the full timeout with a message that says nothing about the real cause:

I.waitForElement('Description Persistence Suite') // waits 10s, then "still not present on page"

dontSeeElement is worse: the text matches nothing, so the assertion silently passes.

Solution

A heuristic in lib/helper/extras/selectorCheck.js recognises a string that cannot be a selector — several words, no CSS or XPath punctuation, not a chain of tag names:

export function looksLikeSelector(value) {
  if (CSS_CHARS.some(char => value.includes(char))) return true
  if (XPATH_CHARS.some(char => value.includes(char))) return true

  const words = value.trim().split(/\s+/)
  if (words.length === 1) return true
  if (words.every(word => TAG_NAME.test(word))) return true

  return false
}

Output in debug mode, with a suggestion picked from the step name:

I wait for element "Description Persistence Suite"
  › [Warning] "Description Persistence Suite" doesn't look like a CSS or XPath selector.
    I.waitForElement() expects an element locator, so this text is matched as CSS
    and finds nothing. Use I.waitForText() to wait for a text on page.

With strict: true the same check throws InvalidSelector instead, so the test fails immediately rather than after the timeout. This follows the existing focusCheck convention exactly — silent by default, visible under --debug, fatal under strict.

Scope

Wired into 32 locator-only methods in Playwright, Puppeteer and WebDriver: the see*/dontSee*Element family, all waitFor* element waits, all grab* element getters, plus scrollTo, scrollIntoView, saveElementScreenshot and moveCursorTo.

Deliberately not wired into methods where a text is legal: click, clickLink, doubleClick, rightClick, forceClick, fillField, checkOption, selectOption, see, dontSee, waitForText, seeTextEquals. Appium overrides several of these methods with its own implementations, so mobile locators are untouched.

False positives

The predicate also gates the strict-mode throw, so a false positive would break a passing test. Locators with spaces that are left alone: div span, my-app my-button, ul > li, text=Save Changes, android=new UiSelector().text("Save now"), ~accessibility id, //*[contains(@class,"x")].

Verified by a static sweep (acorn AST) of every string literal passed as the first argument to a wired method across test/, examples/ and lib/330 literals, 0 flagged.

Testing

  • 14 unit tests in test/unit/selectorCheck_test.js
  • Full unit suite: 783 passed, 0 failed
  • Acceptance (codecept.Playwright.js --debug): 59 passed, 1 pre-existing failure (Dynamic Config › make API call — external API returns HTML), 0 warnings emitted
  • End-to-end positive control confirms the warning fires on a real run

🤖 Generated with Claude Code

https://claude.ai/code/session_01NSR3yk8NgMFkPSspsynKUN

Methods like waitForElement, seeElement and grabTextFrom expect a CSS or
XPath locator and, unlike click or fillField, do not fall back to searching
by text. A sentence passed to them is matched as CSS, finds nothing, and the
step fails on timeout with a message that says nothing about the real cause.

Adds a heuristic that recognises such strings: several words, no CSS or XPath
punctuation, not a chain of tag names. Wired into 32 locator-only methods
across Playwright, Puppeteer and WebDriver.

In debug mode it prints a [Warning] with a suggestion for the method used;
with strict: true it throws InvalidSelector so the test fails immediately
instead of after the full timeout.

Valid locators with spaces are left alone: `div span`, `my-app my-button`,
`text=Save Changes` and `~accessibility id` all pass the check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NSR3yk8NgMFkPSspsynKUN
@DavertMik

Copy link
Copy Markdown
Contributor Author

Should be rewritten as part of Locator class

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.

1 participant