fix(accessibility): patch prototype command for class-based Nightwatch commands#57
Closed
kamal-kaur04 wants to merge 3 commits into
Closed
Conversation
…h commands
commandWrapper() patched `originalCommand.command` directly, which only works
for web-element commands that export a plain object (`module.exports.command = fn`).
executeScript/executeAsyncScript (and the protocol/client/appium commands) export
a class with `command` on the prototype, so the patch landed on a non-existent
static method and the real prototype command Nightwatch invokes stayed unpatched.
As a result `performScan` never ran for `browser.execute('mobile:scroll', ...)`
(and all other class-based commands listed in commands.json) on App Accessibility
sessions — no accessibility scan was captured for those interactions.
Detect whether `command` lives as an own property (object export) or on the
prototype (class export) and patch the correct target, mirroring how the
`protocolAction` branch already handles class-based commands. The existing
`shouldPatchExecuteScript` recursion guard now becomes effective and prevents the
plugin's own `browserstack_executor` scan scripts from re-triggering a scan.
Verified on a real-device Android App Accessibility session: `mobile:scroll` now
triggers exactly one performScan, with no recursion, and the test passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r scripts
shouldPatchExecuteScript() treated any non-string script as a script to skip
(`!script || typeof script !== 'string'` -> return true), so user scripts passed
as functions — `browser.execute(function(){...})` / `executeAsyncScript(fn)` — never
triggered performScan, even though they can mutate page/screen state just like a
string script.
The plugin's own scan scripts are always strings carrying the `browserstack_executor`
token (verified: every internal execute* call in the SDK passes a string), so a
non-string script is always a user script and is safe to scan — the string-based
recursion guard is unaffected.
Treat an empty/undefined script as skip (unchanged) and a function script as a user
script to scan.
Verified on a real-device Android App Accessibility session: executeAsyncScript(fn)
now triggers exactly one performScan; the 18 internal browserstack_executor scan
scripts are still skipped (no recursion); test passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eScript guard
Adds unit tests for the two accessibility command-wrapping fixes:
- shouldPatchExecuteScript: empty -> skip, internal browserstack_executor /
accessibility scripts -> skip, user string scripts -> scan, and (regression)
function-form user scripts -> scan.
- commandWrapper: wraps the own `command` of object-export (web-element) commands
and the prototype `command` of class-export commands (executeScript) without
creating a phantom static; verifies the wrapped command triggers performScan,
delegates to the original, honours the recursion guard, and scans function-form
scripts. Drives commandWrapper deterministically by stubbing
require.resolve('nightwatch') and injecting fake command modules via require.cache.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
07souravkunda
approved these changes
Jun 11, 2026
rounak610
approved these changes
Jun 11, 2026
This was referenced Jun 12, 2026
Merged
Contributor
Author
|
Merged in #58 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
performScannever fired onbrowser.execute('mobile:scroll', ...)— and on every other class-based command listed incommands.json— during App Accessibility sessions. No accessibility scan was captured for those interactions.Root cause
commandWrapper()patchedoriginalCommand.commanddirectly. That only works for web-element commands, which export a plain object (module.exports.command = fn). ButexecuteScript/executeAsyncScript(and theapi/protocol,api/client-commands, and appium commands) export a class withcommandon the prototype:So
originalCommand.commandread a non-existent static method (originalCommandFnwasundefined) and the wrapper assigned a new static method that Nightwatch never calls — it invokes the prototypecommand. The patch silently no-op'd, no error thrown.Fix
Detect whether
commandlives as an own property (object export) or on the prototype (class export) and patch the correct target — mirroring how theprotocolActionbranch already handles class-based commands.The pre-existing
shouldPatchExecuteScriptrecursion guard (dead code until now, since the patch never attached) becomes effective and prevents the plugin's ownbrowserstack_executorscan scripts from re-triggering a scan.How tested
@nightwatch/browserstack+ appium).app.execute('mobile:scroll', ...)now triggers exactly oneperformScan; instrumented trace confirmsPATCH cmd=executeScript target=prototypeandFIRE wrapped cmd=executeScript arg0=mobile:scroll.browserstack_executor: {...appAllyScan...}re-entries all hit the guard and skipped re-scanning.eslintclean.Type of change