Skip to content

Commit

Permalink
feat: make .text and .value support .include, .members, and .satisfy
Browse files Browse the repository at this point in the history
  • Loading branch information
jedwards1211 committed Feb 22, 2021
1 parent 32b12fb commit d964ebd
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 201 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Then, we can add our assertion to the chain.
- `await expect(selector).to.have.text('string')` - Test the text value of the selected element(s) against supplied string. Succeeds if at least one element matches exactly
- `await expect(selector).to.have.text(/regex/)` - Test the text value of the selected element(s) against the supplied regular expression. Succeeds if at least one element matches
- `await expect(selector).to.have.text(/regex/)` - Test the text value of the selected element(s) against the supplied regular expression. Succeeds if at least one element matches
- `await expect(selector).text.to.ordered.include.members(['foo', 'bar'])` - Test that the text of the selected elements includes `'foo'` followed by `'bar'`
- `await expect(selector).text.to.have.members(['foo', 'bar'])` - Test that the text of the selected elements is exactly the set `['foo', 'bar']`, in any order
- `await expect(selector).text.to.satisfy(fn)` - Test that `fn` returns `true` when called with the array of texts of the selected elements
- `await expect(selector).to.have.attribute('attributeName')` - Test whether [at least one] matching element has the given attribute
- `await expect(selector).to.have.attribute('attributeName', 'string')` - Test the attribute value of the selected element(s) against supplied string. Succeeds if at least one element matches exactly
- `await expect(selector).to.have.attribute('attributeName', /regex/)` - Test the attribute value of the selected element(s) against supplied regular expression. Succeeds if at least one element matches exactly
Expand All @@ -45,6 +48,9 @@ Then, we can add our assertion to the chain.
- `await expect(selector).to.have.count.at.most(n)` - Test that at most `n` elements exist in the DOM with the supplied selector
- `await expect(selector).to.have.value('string')` - Test that [at least one] selected element has a value matching the given string
- `await expect(selector).to.have.value(/regex/)` - Test that [at least one] selected element has a value matching the given regular expression
- `await expect(selector).value.to.ordered.include.members(['foo', 'bar'])` - Test that the value of the selected elements includes `'foo'` followed by `'bar'`
- `await expect(selector).value.to.have.members(['foo', 'bar'])` - Test that the value of the selected elements is exactly the set `['foo', 'bar']`, in any order
- `await expect(selector).value.to.satisfy(fn)` - Test that `fn` returns `true` when called with the array of values of the selected elements
- `await expect(selector).to.have.focus()` - (alias for `to.be.focused()`)

You can also always add a `not` in there to negate the assertion:
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"trailingComma": "es5"
},
"config": {
"mocha": "-r @babel/register test/configure.js 'test/**/*.js'",
"mocha": "-r @babel/register test/configure.js test/index.js 'test/**/*.js'",
"commitizen": {
"path": "cz-conventional-changelog"
}
Expand Down Expand Up @@ -96,7 +96,7 @@
"@jedwards1211/eslint-config-flow": "^2.0.0",
"babel-eslint": "^10.0.1",
"babel-plugin-istanbul": "^5.1.0",
"chai": "^4.2.0",
"chai": "^4.3.0",
"chai-as-promised": "^7.1.1",
"codecov": "^3.1.0",
"copy": "^0.3.2",
Expand All @@ -111,7 +111,7 @@
"husky": "^1.1.4",
"istanbul": "^0.4.5",
"lint-staged": "^8.0.4",
"mocha": "^8.2.1",
"mocha": "^8.3.0",
"nyc": "^13.1.0",
"prettier": "^1.15.2",
"prettier-eslint": "^8.8.2",
Expand Down
39 changes: 22 additions & 17 deletions src/assertions/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,27 @@

import getElements from '../util/getElements'

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

const { getValueAndSelector } = utils.flag(this, 'chai-webdriverio-async')
const [texts, selector] = await getValueAndSelector()

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

const [elements, selector] = await getElements(
utils.flag(this, 'object'),
client
)
if (!elements.length) {
if (!texts.length) {
throw new chai.AssertionError(
negate
? `Expected element <${selector}> to not have text ${expectedStr}, but no matching elements were found`
: `Expected element <${selector}> to have text ${expectedStr}, but no matching elements were found`
)
}

const texts = []
const filteredList = (await Promise.all(
elements.map(async element => {
const text = await element.getText()
texts.push(text)
return expected instanceof RegExp
? text.match(expected)
: text === expected
})
)).filter(Boolean)
const filteredList = texts.filter(text =>
expected instanceof RegExp ? text.match(expected) : text === expected
)

this.assert(
filteredList.length > 0,
Expand All @@ -46,5 +38,18 @@ const text = (client, chai, utils, options) =>
.join(', ')}`
)
}
assertText.chain = function chainText() {
const obj = utils.flag(this, 'object')
utils.flag(this, 'chai-webdriverio-async', {
type: 'text',
message: `elements' text for #{selector}`,
getValueAndSelector: async () => {
const [elements, selector] = await getElements(obj, client)
return [await Promise.all(elements.map(e => e.getText())), selector]
},
})
}
return assertText
}

export default text
39 changes: 22 additions & 17 deletions src/assertions/value.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,27 @@

import getElements from '../util/getElements'

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

const { getValueAndSelector } = utils.flag(this, 'chai-webdriverio-async')
const [values, selector] = await getValueAndSelector()

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

const [elements, selector] = await getElements(
utils.flag(this, 'object'),
client
)
if (!elements.length) {
if (!values.length) {
throw new chai.AssertionError(
negate
? `Expected element <${selector}> to not have value ${expectedStr}, but no matching elements were found`
: `Expected element <${selector}> to have value ${expectedStr}, but no matching elements were found`
)
}

const values = []
const filteredList = (await Promise.all(
elements.map(async element => {
const value = await element.getValue()
values.push(value)
return expected instanceof RegExp
? value.match(expected)
: value === expected
})
)).filter(Boolean)
const filteredList = values.filter(value =>
expected instanceof RegExp ? value.match(expected) : value === expected
)

this.assert(
filteredList.length > 0,
Expand All @@ -46,5 +38,18 @@ const value = (client, chai, utils, options) =>
.join(', ')}`
)
}
assertValue.chain = function chainValue() {
const obj = utils.flag(this, 'object')
utils.flag(this, 'chai-webdriverio-async', {
type: 'value',
message: `elements' values for #{selector}`,
getValueAndSelector: async () => {
const [elements, selector] = await getElements(obj, client)
return [await Promise.all(elements.map(e => e.getValue())), selector]
},
})
}
return assertValue
}

export default value
73 changes: 71 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,64 @@ export default function(client, options = {}) {
return (async () => {
const { getValueAndSelector, ...rest } = ourFlag
const [value, selector] = await getValueAndSelector()
return handler.call(this, { ...rest, value, selector, args })
if (!handler) return _super.apply(this, args)
return handler.call(this, {
...rest,
_super,
value,
selector,
args,
})
})()
} else {
_super.apply(this, arguments)
_super.apply(this, args)
}
}
})
}

function defaultOverwriteAssert(_super) {
return function chaiWebdriverIOAssertion(...args) {
const ourFlag = utils.flag(this, 'chai-webdriverio-async')
if (ourFlag) {
return (async () => {
const { getValueAndSelector, message } = ourFlag
const [value, selector] = await getValueAndSelector()
const assertion = new Assertion(value)
utils.transferFlags(this, assertion)
utils.flag(assertion, 'object', value)
if (message) {
utils.flag(
assertion,
'message',
[
utils.flag(this, 'message'),
message.replace(/#\{selector\}/g, `<${selector}>`),
]
.filter(Boolean)
.join(': ')
)
}
return _super.apply(assertion, args)
})()
} else {
_super.apply(this, args)
}
}
}

function defaultOverwriteMethod(name) {
Assertion.overwriteMethod(name, defaultOverwriteAssert)
}

function defaultOverwriteChainableMethod(name) {
Assertion.overwriteChainableMethod(
name,
defaultOverwriteAssert,
chain => chain
)
}

overwriteMethod('above', function assertAbove({
value,
selector,
Expand Down Expand Up @@ -124,5 +173,25 @@ export default function(client, options = {}) {
value
)
})

overwriteMethod('within', function assertAtMost({
value,
selector,
args: [lower, upper],
}) {
this.assert(
value >= lower && value <= upper,
`Expected <${selector}> to appear in the DOM between ${lower} and ${upper} times, but it shows up ${value} times instead.`,
`Expected <${selector}> not to appear in the DOM between ${lower} and ${upper} times, but it shows up ${value} times instead.`
)
})

defaultOverwriteMethod('members')
defaultOverwriteMethod('oneOf')
defaultOverwriteMethod('satisfy')
defaultOverwriteChainableMethod('include')
defaultOverwriteChainableMethod('includes')
defaultOverwriteChainableMethod('contain')
defaultOverwriteChainableMethod('contains')
}
}
9 changes: 2 additions & 7 deletions test/assertions/attribute-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,18 @@
* https://github.com/marcodejongh/chai-webdriverio
*/

import chai, { expect } from 'chai'
import FakeClient from '../stubs/fake-client'
import { expect } from 'chai'
import FakeElement from '../stubs/fake-element'
import { describe, beforeEach, it } from 'mocha'
import chaiWebdriverio from '../../src'
import fakeClient from '../stubs/fakeClient'

const fakeClient = new FakeClient()
const fakeElement1 = new FakeElement()
const fakeElement2 = new FakeElement()

describe('attribute', () => {
beforeEach(() => {
fakeClient.__resetStubs__()
fakeElement1.__resetStubs__()
fakeElement2.__resetStubs__()

chai.use(chaiWebdriverio(fakeClient))
})

describe(`When element doesn't exist`, function() {
Expand Down
11 changes: 2 additions & 9 deletions test/assertions/booleanAssertionTest.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
import chai, { expect } from 'chai'

import { expect } from 'chai'
import { describe, beforeEach, afterEach, it } from 'mocha'
import FakeClient from '../stubs/fake-client'
import FakeElement from '../stubs/fake-element'
import chaiWebdriverio from '../../src'
import { upperFirst, lowerCase } from 'lodash'
import fakeClient from '../stubs/fakeClient'

export const booleanAssertionTest = ({
method,
expectation = lowerCase(method),
allowNone,
}) =>
describe(method, () => {
let fakeClient
let fakeElement1

beforeEach(() => {
fakeClient = new FakeClient()
fakeElement1 = new FakeElement()

fakeElement1[`is${upperFirst(method)}`].resolves(false)
fakeClient.$$.withArgs('.some-selector').resolves([fakeElement1])
fakeClient.$$.withArgs('.other-selector').resolves([])

chai.use(chaiWebdriverio(fakeClient))
})

afterEach(() => {
fakeClient.__resetStubs__()
fakeElement1.__resetStubs__()
})

Expand Down
44 changes: 35 additions & 9 deletions test/assertions/count-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,21 @@
* https://github.com/marcodejongh/chai-webdriverio
*/

import chai, { expect } from 'chai'
import FakeClient from '../stubs/fake-client'
import { expect } from 'chai'
import FakeElement from '../stubs/fake-element'
import chaiWebdriverio from '../../src/index'
import { describe, beforeEach, it } from 'mocha'
import fakeClient from '../stubs/fakeClient'

describe('count', () => {
let elements
let fakeClient

beforeEach(() => {
elements = [new FakeElement(), new FakeElement()]
fakeClient = new FakeClient()

fakeClient.$$.rejects('ArgumentError')
fakeClient.$$.withArgs('.some-selector').resolves(elements)

chai.use(chaiWebdriverio(fakeClient))
})

afterEach(() => fakeClient.__resetStubs__())

describe('When not negated', () => {
it(`resolves when element count matches expectation`, async function() {
await expect('.some-selector').to.have.count(2)
Expand Down Expand Up @@ -174,4 +168,36 @@ describe('count', () => {
)
})
})

describe(`when not negated with within`, function() {
it(`resolves when element count is within expectation`, async function() {
await expect('.some-selector').to.have.count.within(2, 4)
await expect('.some-selector').to.have.count.within(1, 2)
await expect('.some-selector').to.have.count.within(1, 4)
})
it(`rejects when element count is not within expectation`, async function() {
await expect(
expect('.some-selector')
.to.have.count.within(0, 1)
.then(null)
).to.be.rejectedWith(
'Expected <.some-selector> to appear in the DOM between 0 and 1 times, but it shows up 2 times instead.'
)
})
})
describe(`when negated with within`, function() {
it(`resolves when element count is not within expectation`, async function() {
await expect('.some-selector').to.not.have.count.within(0, 1)
await expect('.some-selector').to.not.have.count.within(3, 4)
})
it(`rejects when element count is within expectation`, async function() {
await expect(
expect('.some-selector')
.to.not.have.count.within(1, 2)
.then(null)
).to.be.rejectedWith(
'Expected <.some-selector> not to appear in the DOM between 1 and 2 times, but it shows up 2 times instead.'
)
})
})
})
Loading

0 comments on commit d964ebd

Please sign in to comment.