Skip to content

Commit 9116dd2

Browse files
DavertMikclaude
andcommitted
feat: add exact and strictMode step options for per-step strict mode
Enable strict mode on individual steps without changing helper config: step.opts({ exact: true }) // Playwright-compatible naming step.opts({ strictMode: true }) // alias Throws MultipleElementsFound when multiple elements match, even with strict: false in helper config. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2b8ea08 commit 9116dd2

4 files changed

Lines changed: 45 additions & 5 deletions

File tree

lib/helper/Playwright.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4281,12 +4281,12 @@ async function proceedClick(locator, context = null, options = {}) {
42814281
assertElementExists(els, locator, 'Clickable element')
42824282
}
42834283

4284-
const elementIndex = store.currentStep?.opts?.elementIndex
4284+
const opts = store.currentStep?.opts
42854285
let element
4286-
if (elementIndex != null) {
4286+
if (opts?.elementIndex != null) {
42874287
element = selectElement(els, locator, this)
42884288
} else {
4289-
if (this.options.strict) assertOnlyOneElement(els, locator, this)
4289+
if (this.options.strict || opts?.exact === true || opts?.strictMode === true) assertOnlyOneElement(els, locator, this)
42904290
element = els.length > 1 ? (await getVisibleElements(els))[0] : els[0]
42914291
}
42924292

lib/helper/extras/elementSelection.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ function resolveElementIndex(value) {
99
return value
1010
}
1111

12+
function isStrictStep(opts, helper) {
13+
if (opts?.exact === true || opts?.strictMode === true) return true
14+
return helper.options.strict
15+
}
16+
1217
function selectElement(els, locator, helper) {
13-
const rawIndex = store.currentStep?.opts?.elementIndex
18+
const opts = store.currentStep?.opts
19+
const rawIndex = opts?.elementIndex
1420
const elementIndex = resolveElementIndex(rawIndex)
1521

1622
if (elementIndex != null) {
@@ -37,7 +43,7 @@ function selectElement(els, locator, helper) {
3743
return els[idx]
3844
}
3945

40-
if (helper.options.strict) {
46+
if (isStrictStep(opts, helper)) {
4147
if (els.length > 1) {
4248
const webElements = els.map(el => new WebElement(el, helper))
4349
throw new MultipleElementsFound(locator, webElements)

lib/step/config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/**
22
* @typedef {Object} StepOptions
33
* @property {number|'first'|'last'} [elementIndex] - Select a specific element when multiple match. 1-based positive index, negative from end, or 'first'/'last'.
4+
* @property {boolean} [exact] - Enable strict mode for this step. Throws if multiple elements match.
5+
* @property {boolean} [strictMode] - Alias for exact.
46
* @property {boolean} [ignoreCase] - Perform case-insensitive text matching.
57
*/
68

test/helper/webapi.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,5 +2388,37 @@ export function tests() {
23882388
expect(err).to.exist
23892389
expect(err.message).to.include('elementIndex')
23902390
})
2391+
2392+
it('should enable strict mode per-step with exact: true', async () => {
2393+
await I.amOnPage('/info')
2394+
store.currentStep = { opts: { exact: true } }
2395+
let err
2396+
try {
2397+
await I.click('#grab-multiple a')
2398+
} catch (e) {
2399+
err = e
2400+
}
2401+
expect(err).to.exist
2402+
expect(err.constructor.name).to.equal('MultipleElementsFound')
2403+
})
2404+
2405+
it('should enable strict mode per-step with strictMode: true', async () => {
2406+
await I.amOnPage('/info')
2407+
store.currentStep = { opts: { strictMode: true } }
2408+
let err
2409+
try {
2410+
await I.click('#grab-multiple a')
2411+
} catch (e) {
2412+
err = e
2413+
}
2414+
expect(err).to.exist
2415+
expect(err.constructor.name).to.equal('MultipleElementsFound')
2416+
})
2417+
2418+
it('should not throw with exact: true when single element found', async () => {
2419+
await I.amOnPage('/info')
2420+
store.currentStep = { opts: { exact: true } }
2421+
await I.click('#first-link')
2422+
})
23912423
})
23922424
}

0 commit comments

Comments
 (0)