Skip to content

feat(actor): chain first(), last() and at() on helper actions - #5788

Open
DavertMik wants to merge 2 commits into
4.xfrom
feat/step-element-selection-chain
Open

DavertMik wants to merge 2 commits into
4.xfrom
feat/step-element-selection-chain

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

Summary

Selects one match from a multi-element locator without step.opts():

I.click('.btn').first()
I.fillField('input', 'x').last()
await I.checkOption('.opt').at(2)   // at(-2) counts from the end
I.click('a', step.opts({ exact: true })).last()   // other options are kept

This works because recordStep only queues the step, so a chained call runs before the step does and can set step.opts.elementIndex. The chain overrides strict mode the same way elementIndex does. step.opts() keeps working, so nothing breaks at runtime.

Changes

  • lib/actor.js: adds the chain to the promise of every helper action. Calling it after the step has run throws an error.
  • lib/helper/Puppeteer.js: forceClick hardcoded els[0], ignoring strict mode and the index. It now uses selectElement.
  • Typings: adds CodeceptJS.StepPromise, returned by the actions that enforce strict mode.
  • Docs: element-selection, locators and migration-4 now lead with the chain.

Behaviour to review

  • Default typings: these actions now return StepPromise instead of void, so projects using no-floating-promises get warnings on unawaited calls.

  • Actions that ignore the chain: it exists on every helper action but has no effect where selection isn't used:

    • non-element actions such as amOnPage
    • switchTo
    • WebDriver moveCursorTo, focus, blur, scrollTo, forceRightClick
    • Puppeteer saveElementScreenshot, clickXY

    None of these throw in strict mode on those helpers, so they are out of scope here.

  • Not available yet: custom steps and page object methods don't get the chain. .timeout() and .retry() aren't included, because chaining them needs recorder changes.

Testing

  • New test/acceptance/element_selection_test.js. For each action it checks that the step throws MultipleElementsFound without the chain, passes with .first(), and rejects .at(100).
  • Local runs: Playwright 56 passed, Puppeteer 53 passed, unit tests 908 passed with 11 pending. within and session passed on Playwright.
  • tsd locally reports only the cross-file Config conflict errors, which don't appear on a fresh install.
  • Not run locally: WebDriver (no Selenium here), so CI is its first run. The new scenarios aren't tagged for the CDPBrowser family, and Appium runtime behaviour wasn't checked.

🤖 Generated with Claude Code

DavertMik and others added 2 commits November 6, 2026 15:42
I.click('a').first() selects a match without step.opts(), and overrides
strict mode like elementIndex does. Puppeteer forceClick now honours strict
mode and the selected index, matching the other helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WebDriver grabValueFromAll drops empty values, so the array comparison
could not tell which field was filled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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