Skip to content

Commit

Permalink
Chain custom commands end with $ (#8134)
Browse files Browse the repository at this point in the history
* allow chain custom commands

* Update CustomCommands.md
  • Loading branch information
jayrepo committed Mar 21, 2022
1 parent 5e76205 commit d9ef72f
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 2 deletions.
4 changes: 2 additions & 2 deletions packages/wdio-utils/src/shim.ts
Expand Up @@ -251,7 +251,7 @@ let wrapCommand = function wrapCommand<T>(commandName: string, fn: Function): (.
* await $('foo').$('bar')
* ```
*/
if (ELEMENT_QUERY_COMMANDS.includes(prop)) {
if (ELEMENT_QUERY_COMMANDS.includes(prop) || prop.endsWith('$')) {
// this: WebdriverIO.Element
return wrapCommand(prop, function (this: any, ...args: any) {
return this[prop].apply(this, args)
Expand Down Expand Up @@ -336,7 +336,7 @@ let wrapCommand = function wrapCommand<T>(commandName: string, fn: Function): (.
*/
const command = hasWdioSyncSupport && wdioSync && Boolean(global.browser) && !runAsync && !asyncSpec
? wdioSync!.wrapCommand(commandName, fn)
: ELEMENT_QUERY_COMMANDS.includes(commandName)
: ELEMENT_QUERY_COMMANDS.includes(commandName) || commandName.endsWith('$')
? chainElementQuery
: wrapCommandFn

Expand Down
19 changes: 19 additions & 0 deletions packages/wdio-utils/tests/shim-async.test.ts
Expand Up @@ -246,6 +246,25 @@ describe('wrapCommand', () => {
expect(scope.getTagName).toBeCalledTimes(1)
})

it('allows to chain element promises for custom command', async () => {
const rawCommand = jest.fn()
const scope: Partial<BrowserObject> = {
options: {
beforeCommand: jest.fn(),
afterCommand: jest.fn()
},
getTagName: jest.fn().mockResolvedValue('Yayy'),
user$: rawCommand
}
rawCommand.mockReturnValue(Promise.resolve(scope))
const commandB = wrapCommand('user$', rawCommand)
expect(await commandB.call(scope, 'bar').user$('foo').getTagName()).toBe('Yayy')
expect(scope.user$).toBeCalledTimes(2)
expect(scope.user$).toBeCalledWith('bar')
expect(scope.user$).toBeCalledWith('foo')
expect(scope.getTagName).toBeCalledTimes(1)
})

it('allows to access indexed element', async () => {
const rawCommand$ = jest.fn()
const rawCommand$$ = jest.fn()
Expand Down
7 changes: 7 additions & 0 deletions website/docs/CustomCommands.md
Expand Up @@ -84,6 +84,13 @@ console.log(typeof browser.myCustomElementCommand2) // outputs "undefined"
console.log(elem3.myCustomElementCommand2('foobar')) // outputs "function"
```

__Note:__ If you need to chain a custom command, the command should end with `$`,
```js
browser.addCommand("user$", (locator) => { return ele })
browser.addCommand("user$", (locator) => { return ele }, true)
await browser.user$('foo').user$('bar').click()
```

Be careful to not overload the `browser` scope with too many custom commands.

We recommend defining custom logic in [page objects](PageObjects.md), so they are bound to a specific page.
Expand Down

0 comments on commit d9ef72f

Please sign in to comment.