Skip to content

Commit 395132a

Browse files
DavertMikclaude
andcommitted
refactor(plugins): centralize on= validation in resolveTrigger
resolveTrigger now takes { name, validModes }, performs the unknown-mode and missing-path/pattern checks itself, prints the error via output.error, and returns null. Each plugin shrinks to one call + early-return; heal passes its narrower mode list as { validModes: ['fail', 'file', 'url'] }. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d6a1dfb commit 395132a

5 files changed

Lines changed: 41 additions & 67 deletions

File tree

lib/plugin/aiTrace.js

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import {
2020

2121
const supportedHelpers = Container.STANDARD_ACTING_HELPERS
2222

23-
const VALID_MODES = new Set(['fail', 'test', 'step', 'file', 'url'])
24-
2523
const defaultConfig = {
2624
on: 'step',
2725
deleteSuccessful: false,
@@ -79,20 +77,8 @@ export default function (config = {}) {
7977
let helper
8078

8179
const cliArgs = parsePluginArgs(config._args)
82-
const trigger = resolveTrigger(cliArgs, config, { on: defaultConfig.on })
83-
84-
if (!VALID_MODES.has(trigger.on)) {
85-
output.error(`aiTrace: unknown on="${trigger.on}". Valid: fail, test, step, file, url`)
86-
return
87-
}
88-
if (trigger.on === 'file' && !trigger.path) {
89-
output.error('aiTrace:on=file requires path=. Example: -p aiTrace:on=file:path=tests/foo.js')
90-
return
91-
}
92-
if (trigger.on === 'url' && !trigger.pattern) {
93-
output.error('aiTrace:on=url requires pattern=. Example: -p aiTrace:on=url:pattern=/users/*')
94-
return
95-
}
80+
const trigger = resolveTrigger(cliArgs, config, { on: defaultConfig.on }, { name: 'aiTrace' })
81+
if (!trigger) return
9682

9783
config = Object.assign(defaultConfig, config)
9884

lib/plugin/heal.js

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ import {
1919
} from '../utils/pluginParser.js'
2020

2121

22-
const VALID_MODES = new Set(['fail', 'file', 'url'])
23-
2422
const defaultConfig = {
2523
on: 'fail',
2624
healLimit: 2,
@@ -64,20 +62,11 @@ export default function (config = {}) {
6462
}
6563

6664
const cliArgs = parsePluginArgs(config._args)
67-
const trigger = resolveTrigger(cliArgs, config, { on: defaultConfig.on })
68-
69-
if (!VALID_MODES.has(trigger.on)) {
70-
output.error(`heal: unsupported on="${trigger.on}". Valid: fail, file, url`)
71-
return
72-
}
73-
if (trigger.on === 'file' && !trigger.path) {
74-
output.error('heal:on=file requires path=. Example: -p heal:on=file:path=tests/foo.js')
75-
return
76-
}
77-
if (trigger.on === 'url' && !trigger.pattern) {
78-
output.error('heal:on=url requires pattern=. Example: -p heal:on=url:pattern=/users/*')
79-
return
80-
}
65+
const trigger = resolveTrigger(cliArgs, config, { on: defaultConfig.on }, {
66+
name: 'heal',
67+
validModes: ['fail', 'file', 'url'],
68+
})
69+
if (!trigger) return
8170

8271
let currentTest = null
8372
let currentStep = null

lib/plugin/pause.js

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import {
1010
getBrowserHelper,
1111
} from '../utils/pluginParser.js'
1212

13-
const VALID_MODES = new Set(['fail', 'test', 'step', 'file', 'url'])
14-
1513
/**
1614
* Pauses test execution interactively. Replaces the legacy `pauseOnFail`
1715
* plugin. The default `on=fail` matches the old `pauseOnFail` behavior.
@@ -46,12 +44,8 @@ const VALID_MODES = new Set(['fail', 'test', 'step', 'file', 'url'])
4644
*/
4745
export default function (config = {}) {
4846
const cliArgs = parsePluginArgs(config._args)
49-
const trigger = resolveTrigger(cliArgs, config, { on: 'fail' })
50-
51-
if (!VALID_MODES.has(trigger.on)) {
52-
output.error(`pause: unknown on="${trigger.on}". Valid: fail, test, step, file, url`)
53-
return
54-
}
47+
const trigger = resolveTrigger(cliArgs, config, { on: 'fail' }, { name: 'pause' })
48+
if (!trigger) return
5549

5650
switch (trigger.on) {
5751
case 'fail':
@@ -61,16 +55,8 @@ export default function (config = {}) {
6155
case 'step':
6256
return initStepMode()
6357
case 'file':
64-
if (!trigger.path) {
65-
output.error('pause:on=file requires path=. Example: -p pause:on=file:path=tests/foo.js')
66-
return
67-
}
6858
return initFileMode(trigger.path, trigger.line)
6959
case 'url':
70-
if (!trigger.pattern) {
71-
output.error('pause:on=url requires pattern=. Example: -p pause:on=url:pattern=/users/*')
72-
return
73-
}
7460
return initUrlMode(trigger.pattern)
7561
}
7662
}

lib/plugin/screenshot.js

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ const defaultConfig = {
3131
ignoreSteps: [],
3232
}
3333

34-
const VALID_MODES = new Set(['fail', 'test', 'step', 'file', 'url'])
35-
3634
/**
3735
* Saves screenshots from the browser at points triggered by `on=`.
3836
*
@@ -85,12 +83,8 @@ export default function (config = {}) {
8583
if (!helper) return
8684

8785
const cliArgs = parsePluginArgs(config._args)
88-
const trigger = resolveTrigger(cliArgs, config, { on: defaultConfig.on })
89-
90-
if (!VALID_MODES.has(trigger.on)) {
91-
output.error(`screenshot: unknown on="${trigger.on}". Valid: fail, test, step, file, url`)
92-
return
93-
}
86+
const trigger = resolveTrigger(cliArgs, config, { on: defaultConfig.on }, { name: 'screenshot' })
87+
if (!trigger) return
9488

9589
const helpers = Container.helpers()
9690
const options = Object.assign({}, defaultConfig, helper.options, config)
@@ -119,16 +113,8 @@ export default function (config = {}) {
119113
case 'step':
120114
return wireOnStep(options, () => true)
121115
case 'file':
122-
if (!trigger.path) {
123-
output.error('screenshot:on=file requires path=. Example: -p screenshot:on=file:path=tests/foo.js')
124-
return
125-
}
126116
return wireOnStep(options, step => matchStepFile(step, trigger.path, trigger.line))
127117
case 'url':
128-
if (!trigger.pattern) {
129-
output.error('screenshot:on=url requires pattern=. Example: -p screenshot:on=url:pattern=/users/*')
130-
return
131-
}
132118
return wireOnUrl(options, trigger.pattern)
133119
}
134120
}

lib/utils/pluginParser.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import Container from '../container.js'
2+
import output from '../output.js'
23

34
const supportedHelpers = Container.STANDARD_ACTING_HELPERS
45

56
const RESERVED_KEYS = new Set(['on', 'path', 'line', 'pattern'])
7+
const ALL_MODES = ['fail', 'test', 'step', 'file', 'url']
68

79
/**
810
* Parse a plugin's _args (from CLI `-p plugin:key=value:key=value`) into a flat dict.
@@ -44,12 +46,37 @@ function coerce(v) {
4446
}
4547

4648
/**
47-
* Compose CLI args > config > defaults into a normalized trigger spec.
48-
* Returns `{ on, path, line, pattern, ...rest }`. `line` is coerced to a number.
49+
* Compose CLI args > config > defaults into a normalized trigger spec, then
50+
* validate it. Returns `{ on, path, line, pattern, ...rest }` with `line`
51+
* coerced to a number, or `null` if validation failed (an error is printed).
52+
*
53+
* @param {object} cliArgs — output of parsePluginArgs(config._args)
54+
* @param {object} config — full plugin config object
55+
* @param {object} defaults — fallback values, e.g. `{ on: 'fail' }`
56+
* @param {object} options
57+
* @param {string} options.name — plugin name, used in error messages
58+
* @param {string[]} [options.validModes] — accepted values for `on`
59+
* (default: fail, test, step, file, url)
4960
*/
50-
export function resolveTrigger(cliArgs = {}, config = {}, defaults = {}) {
61+
export function resolveTrigger(cliArgs = {}, config = {}, defaults = {}, options = {}) {
62+
const { name = 'plugin', validModes = ALL_MODES } = options
5163
const merged = { ...defaults, ...pickKnown(config), ...cliArgs }
5264
if (merged.line != null) merged.line = parseInt(merged.line, 10)
65+
66+
const valid = new Set(validModes)
67+
if (!valid.has(merged.on)) {
68+
output.error(`${name}: unknown on="${merged.on}". Valid: ${validModes.join(', ')}`)
69+
return null
70+
}
71+
if (merged.on === 'file' && !merged.path) {
72+
output.error(`${name}:on=file requires path=. Example: -p ${name}:on=file:path=tests/foo.js`)
73+
return null
74+
}
75+
if (merged.on === 'url' && !merged.pattern) {
76+
output.error(`${name}:on=url requires pattern=. Example: -p ${name}:on=url:pattern=/users/*`)
77+
return null
78+
}
79+
5380
return merged
5481
}
5582

0 commit comments

Comments
 (0)