Skip to content

Commit

Permalink
feat: allow assertion object to be a promise resolving to element(s)
Browse files Browse the repository at this point in the history
  • Loading branch information
jedwards1211 committed Feb 18, 2021
1 parent 7465a62 commit f5bd5f9
Show file tree
Hide file tree
Showing 14 changed files with 79 additions and 216 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ You can also always add a `not` in there to negate the assertion:

- `await expect(selector).not.to.have.text('property')`

## Assertions on promises resolving to elements

You can also use the same assertions above on promises that resolve to an element or array of elements:

```js
await expect(browser.$('.foo')).to.be.clickable()
```

This allows you to use the [Page Object Pattern](https://webdriver.io/docs/pageobjects/).

However, if an assertion fails, the error message won't contain the selector _unless_ you attach it to `promise.selector`, e.g.:

```js
await expect(
Object.assign(browser.$('.foo'), { selector: '.foo' })
).to.be.clickable()
```

Obviously you wouldn't want to do this manually, but rather wrap the `browser` methods to attach the `selector` property.

## How to wait for one of these conditions to be true?

Just use [`chai-wait-for`](https://github.com/jcoreio/chai-wait-for) and replace `expect` on any assertion with `waitFor`,
Expand Down
9 changes: 6 additions & 3 deletions src/assertions/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
* https://github.com/marcodejongh/chai-webdriverio
*/

import getElements from '../util/getElements'

const attribute = (client, chai, utils, options) =>
async function(attribute, expected) {
const selector = utils.flag(this, 'object')
const negate = utils.flag(this, 'negate')

const [elements, selector] = await getElements(
utils.flag(this, 'object'),
client
)
if (arguments.length === 1) {
const elements = await client.$$(selector)
if (!elements.length) {
throw new chai.AssertionError(
negate
Expand Down Expand Up @@ -43,7 +47,6 @@ const attribute = (client, chai, utils, options) =>
const expectedStr =
typeof expected === 'string' ? JSON.stringify(expected) : expected

const elements = await client.$$(selector)
if (!elements.length) {
throw new chai.AssertionError(
negate
Expand Down
8 changes: 6 additions & 2 deletions src/assertions/booleanAssertion.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@
* https://github.com/marcodejongh/chai-webdriverio
*/

import getElements from '../util/getElements'

const booleanAssertion = ({ predicate, expectation, allowNone }) => (
client,
chai,
utils,
options
) =>
async function(expected) {
const selector = utils.flag(this, 'object')
const negate = utils.flag(this, 'negate')

const elements = await client.$$(selector)
const [elements, selector] = await getElements(
utils.flag(this, 'object'),
client
)
if (!allowNone && !elements.length) {
throw new chai.AssertionError(
negate
Expand Down
9 changes: 6 additions & 3 deletions src/assertions/count.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
* https://github.com/marcodejongh/chai-webdriverio
*/

import getElements from '../util/getElements'

const count = (client, chai, utils, options) =>
async function(expected) {
const selector = utils.flag(this, 'object')

const elements = await client.$$(selector)
const [elements, selector] = await getElements(
utils.flag(this, 'object'),
client
)

this.assert(
elements.length === expected,
Expand Down
8 changes: 6 additions & 2 deletions src/assertions/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
* https://github.com/marcodejongh/chai-webdriverio
*/

import getElements from '../util/getElements'

const text = (client, chai, utils, options) =>
async function(expected) {
const selector = utils.flag(this, 'object')
const negate = utils.flag(this, 'negate')

const expectedStr =
typeof expected === 'string' ? JSON.stringify(expected) : expected

const elements = await client.$$(selector)
const [elements, selector] = await getElements(
utils.flag(this, 'object'),
client
)
if (!elements.length) {
throw new chai.AssertionError(
negate
Expand Down
8 changes: 6 additions & 2 deletions src/assertions/value.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
* https://github.com/marcodejongh/chai-webdriverio
*/

import getElements from '../util/getElements'

const value = (client, chai, utils, options) =>
async function(expected) {
const selector = utils.flag(this, 'object')
const negate = utils.flag(this, 'negate')

const expectedStr =
typeof expected === 'string' ? JSON.stringify(expected) : expected

const elements = await client.$$(selector)
const [elements, selector] = await getElements(
utils.flag(this, 'object'),
client
)
if (!elements.length) {
throw new chai.AssertionError(
negate
Expand Down
13 changes: 13 additions & 0 deletions src/util/getElements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default async function getElements(selectorOrPromise, client) {
if (selectorOrPromise instanceof Promise) {
const resolved = await selectorOrPromise
const selector = selectorOrPromise.selector || String(resolved)
if (typeof resolved === 'string') selectorOrPromise = resolved
else if (Array.isArray(resolved)) return [resolved, selector]
else if (resolved) return [[resolved], selector]
}

return typeof selectorOrPromise === 'string'
? [await client.$$(selectorOrPromise), selectorOrPromise]
: [[], selectorOrPromise]
}
16 changes: 16 additions & 0 deletions test/assertions/booleanAssertionTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,29 @@ export const booleanAssertionTest = ({
fakeElement1[`is${upperFirst(method)}`].resolves(true)
await expect('.some-selector').to.be[method]()
})
it(`resolves when element is ${expectation} -- via promise`, async function() {
fakeElement1[`is${upperFirst(method)}`].resolves(true)
await expect(Promise.resolve(fakeElement1)).to.be[method]()
})
it(`rejects when element is not ${expectation}`, async function() {
await expect('.some-selector')
.to.be[method]()
.to.be.rejectedWith(
`Expected element <.some-selector> to be ${expectation} but it is not`
)
})
it(`rejects when element is not ${expectation} -- via promise`, async function() {
await expect(
Object.assign(Promise.resolve(fakeElement1), {
selector: '.some-selector',
})
)
.to.be[method]()
.to.be.rejectedWith(
`Expected element <.some-selector> to be ${expectation} but it is not`
)
})

it(`rejects when element does not exist`, async function() {
await expect('.other-selector')
.to.be[method]()
Expand Down
13 changes: 0 additions & 13 deletions util/default-config.js

This file was deleted.

4 changes: 0 additions & 4 deletions util/default-config.js.flow

This file was deleted.

62 changes: 0 additions & 62 deletions util/doesOneElementSatisfy.js

This file was deleted.

9 changes: 0 additions & 9 deletions util/doesOneElementSatisfy.js.flow

This file was deleted.

89 changes: 0 additions & 89 deletions util/element-exists.js

This file was deleted.

27 changes: 0 additions & 27 deletions util/element-exists.js.flow

This file was deleted.

0 comments on commit f5bd5f9

Please sign in to comment.