Skip to content

Commit f5bd5f9

Browse files
committed
feat: allow assertion object to be a promise resolving to element(s)
1 parent 7465a62 commit f5bd5f9

14 files changed

+79
-216
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,26 @@ You can also always add a `not` in there to negate the assertion:
4747

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

50+
## Assertions on promises resolving to elements
51+
52+
You can also use the same assertions above on promises that resolve to an element or array of elements:
53+
54+
```js
55+
await expect(browser.$('.foo')).to.be.clickable()
56+
```
57+
58+
This allows you to use the [Page Object Pattern](https://webdriver.io/docs/pageobjects/).
59+
60+
However, if an assertion fails, the error message won't contain the selector _unless_ you attach it to `promise.selector`, e.g.:
61+
62+
```js
63+
await expect(
64+
Object.assign(browser.$('.foo'), { selector: '.foo' })
65+
).to.be.clickable()
66+
```
67+
68+
Obviously you wouldn't want to do this manually, but rather wrap the `browser` methods to attach the `selector` property.
69+
5070
## How to wait for one of these conditions to be true?
5171

5272
Just use [`chai-wait-for`](https://github.com/jcoreio/chai-wait-for) and replace `expect` on any assertion with `waitFor`,

src/assertions/attribute.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
44
* https://github.com/marcodejongh/chai-webdriverio
55
*/
66

7+
import getElements from '../util/getElements'
8+
79
const attribute = (client, chai, utils, options) =>
810
async function(attribute, expected) {
9-
const selector = utils.flag(this, 'object')
1011
const negate = utils.flag(this, 'negate')
1112

13+
const [elements, selector] = await getElements(
14+
utils.flag(this, 'object'),
15+
client
16+
)
1217
if (arguments.length === 1) {
13-
const elements = await client.$$(selector)
1418
if (!elements.length) {
1519
throw new chai.AssertionError(
1620
negate
@@ -43,7 +47,6 @@ const attribute = (client, chai, utils, options) =>
4347
const expectedStr =
4448
typeof expected === 'string' ? JSON.stringify(expected) : expected
4549

46-
const elements = await client.$$(selector)
4750
if (!elements.length) {
4851
throw new chai.AssertionError(
4952
negate

src/assertions/booleanAssertion.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@
44
* https://github.com/marcodejongh/chai-webdriverio
55
*/
66

7+
import getElements from '../util/getElements'
8+
79
const booleanAssertion = ({ predicate, expectation, allowNone }) => (
810
client,
911
chai,
1012
utils,
1113
options
1214
) =>
1315
async function(expected) {
14-
const selector = utils.flag(this, 'object')
1516
const negate = utils.flag(this, 'negate')
1617

17-
const elements = await client.$$(selector)
18+
const [elements, selector] = await getElements(
19+
utils.flag(this, 'object'),
20+
client
21+
)
1822
if (!allowNone && !elements.length) {
1923
throw new chai.AssertionError(
2024
negate

src/assertions/count.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
* https://github.com/marcodejongh/chai-webdriverio
55
*/
66

7+
import getElements from '../util/getElements'
8+
79
const count = (client, chai, utils, options) =>
810
async function(expected) {
9-
const selector = utils.flag(this, 'object')
10-
11-
const elements = await client.$$(selector)
11+
const [elements, selector] = await getElements(
12+
utils.flag(this, 'object'),
13+
client
14+
)
1215

1316
this.assert(
1417
elements.length === expected,

src/assertions/text.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
* https://github.com/marcodejongh/chai-webdriverio
55
*/
66

7+
import getElements from '../util/getElements'
8+
79
const text = (client, chai, utils, options) =>
810
async function(expected) {
9-
const selector = utils.flag(this, 'object')
1011
const negate = utils.flag(this, 'negate')
1112

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

15-
const elements = await client.$$(selector)
16+
const [elements, selector] = await getElements(
17+
utils.flag(this, 'object'),
18+
client
19+
)
1620
if (!elements.length) {
1721
throw new chai.AssertionError(
1822
negate

src/assertions/value.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
* https://github.com/marcodejongh/chai-webdriverio
55
*/
66

7+
import getElements from '../util/getElements'
8+
79
const value = (client, chai, utils, options) =>
810
async function(expected) {
9-
const selector = utils.flag(this, 'object')
1011
const negate = utils.flag(this, 'negate')
1112

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

15-
const elements = await client.$$(selector)
16+
const [elements, selector] = await getElements(
17+
utils.flag(this, 'object'),
18+
client
19+
)
1620
if (!elements.length) {
1721
throw new chai.AssertionError(
1822
negate

src/util/getElements.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default async function getElements(selectorOrPromise, client) {
2+
if (selectorOrPromise instanceof Promise) {
3+
const resolved = await selectorOrPromise
4+
const selector = selectorOrPromise.selector || String(resolved)
5+
if (typeof resolved === 'string') selectorOrPromise = resolved
6+
else if (Array.isArray(resolved)) return [resolved, selector]
7+
else if (resolved) return [[resolved], selector]
8+
}
9+
10+
return typeof selectorOrPromise === 'string'
11+
? [await client.$$(selectorOrPromise), selectorOrPromise]
12+
: [[], selectorOrPromise]
13+
}

test/assertions/booleanAssertionTest.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,29 @@ export const booleanAssertionTest = ({
3636
fakeElement1[`is${upperFirst(method)}`].resolves(true)
3737
await expect('.some-selector').to.be[method]()
3838
})
39+
it(`resolves when element is ${expectation} -- via promise`, async function() {
40+
fakeElement1[`is${upperFirst(method)}`].resolves(true)
41+
await expect(Promise.resolve(fakeElement1)).to.be[method]()
42+
})
3943
it(`rejects when element is not ${expectation}`, async function() {
4044
await expect('.some-selector')
4145
.to.be[method]()
4246
.to.be.rejectedWith(
4347
`Expected element <.some-selector> to be ${expectation} but it is not`
4448
)
4549
})
50+
it(`rejects when element is not ${expectation} -- via promise`, async function() {
51+
await expect(
52+
Object.assign(Promise.resolve(fakeElement1), {
53+
selector: '.some-selector',
54+
})
55+
)
56+
.to.be[method]()
57+
.to.be.rejectedWith(
58+
`Expected element <.some-selector> to be ${expectation} but it is not`
59+
)
60+
})
61+
4662
it(`rejects when element does not exist`, async function() {
4763
await expect('.other-selector')
4864
.to.be[method]()

util/default-config.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

util/default-config.js.flow

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)