diff --git a/README.md b/README.md index 79d5a2a..dd47791 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,11 @@ Then, we can add our assertion to the chain. - `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 -- `await expect(selector).to.have.count(number)` - Test how many elements exist in the DOM with the supplied selector +- `await expect(selector).to.have.count(n)` - Test that exactly `n` elements exist in the DOM with the supplied selector +- `await expect(selector).to.have.count.above(n)` - Test that more than `n` elements exist in the DOM with the supplied selector +- `await expect(selector).to.have.count.below(n)` - Test that less than `n` elements exist in the DOM with the supplied selector +- `await expect(selector).to.have.count.at.least(n)` - Test that at least `n` elements exist in the DOM with the supplied selector +- `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).to.have.focus()` - (alias for `to.be.focused()`) diff --git a/src/assertions/count.js b/src/assertions/count.js index 0b59c7b..4865cd1 100644 --- a/src/assertions/count.js +++ b/src/assertions/count.js @@ -6,20 +6,28 @@ import getElements from '../util/getElements' -const count = (client, chai, utils, options) => - async function(expected) { - const [elements, selector] = await getElements( - utils.flag(this, 'object'), - client - ) +const count = (client, chai, utils, options) => { + async function assertCount(expected) { + const { getValueAndSelector } = utils.flag(this, 'chai-webdriverio-async') + const [count, selector] = await getValueAndSelector() this.assert( - elements.length === expected, - `Expected <${selector}> to appear in the DOM ${expected} times, but it shows up ${ - elements.length - } times instead.`, + count === expected, + `Expected <${selector}> to appear in the DOM ${expected} times, but it shows up ${count} times instead.`, `Expected <${selector}> not to appear in the DOM ${expected} times, but it does.` ) } + assertCount.chain = function chainCount() { + const obj = utils.flag(this, 'object') + utils.flag(this, 'chai-webdriverio-async', { + type: 'count', + getValueAndSelector: async () => { + const [elements, selector] = await getElements(obj, client) + return [elements.length, selector] + }, + }) + } + return assertCount +} export default count diff --git a/src/index.js b/src/index.js index 1056a41..b7b0e6f 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,8 @@ import value from './assertions/value' export default function(client, options = {}) { return function chaiWebdriverIO(chai, utils) { + const { Assertion } = chai + const methodsToAdd = { attribute, clickable, @@ -29,12 +31,117 @@ export default function(client, options = {}) { for (const name in methodsToAdd) { const method = methodsToAdd[name](client, chai, utils, options) - utils.addMethod(chai.Assertion.prototype, name, function() { - const promise = method.apply(this, arguments) - this._obj = promise - this.then = promise.then.bind(promise) - return this - }) + if (typeof method.chain === 'function') { + Assertion.addChainableMethod( + name, + function() { + const promise = method.apply(this, arguments) + this._obj = promise + this.then = promise.then.bind(promise) + return this + }, + method.chain + ) + } else { + Assertion.addMethod(name, function() { + const promise = method.apply(this, arguments) + this._obj = promise + this.then = promise.then.bind(promise) + return this + }) + } } + + Assertion.overwriteMethod('above', function(_super) { + return function assertAbove(n) { + const ourFlag = utils.flag(this, 'chai-webdriverio-async') + if (ourFlag) { + return (async () => { + const { getValueAndSelector } = ourFlag + + const [value, selector] = await getValueAndSelector() + + this.assert( + value > n, + `Expected <${selector}> to appear in the DOM more than #{exp} times, but it shows up #{act} times instead.`, + `Expected <${selector}> not to appear in the DOM more than #{exp} times, but it shows up #{act} times instead.`, + n, + value + ) + })() + } else { + _super.apply(this, arguments) + } + } + }) + + Assertion.overwriteMethod('least', function(_super) { + return function assertAtLeast(n) { + const ourFlag = utils.flag(this, 'chai-webdriverio-async') + if (ourFlag) { + return (async () => { + const { getValueAndSelector } = ourFlag + + const [value, selector] = await getValueAndSelector() + + this.assert( + value >= n, + `Expected <${selector}> to appear in the DOM at least #{exp} times, but it shows up #{act} times instead.`, + `Expected <${selector}> not to appear in the DOM at least #{exp} times, but it shows up #{act} times instead.`, + n, + value + ) + })() + } else { + _super.apply(this, arguments) + } + } + }) + + Assertion.overwriteMethod('below', function(_super) { + return function assertBelow(n) { + const ourFlag = utils.flag(this, 'chai-webdriverio-async') + if (ourFlag) { + return (async () => { + const { getValueAndSelector } = ourFlag + + const [value, selector] = await getValueAndSelector() + + this.assert( + value < n, + `Expected <${selector}> to appear in the DOM less than #{exp} times, but it shows up #{act} times instead.`, + `Expected <${selector}> not to appear in the DOM less than #{exp} times, but it shows up #{act} times instead.`, + n, + value + ) + })() + } else { + _super.apply(this, arguments) + } + } + }) + + Assertion.overwriteMethod('most', function(_super) { + return function assertAtMost(n) { + const ourFlag = utils.flag(this, 'chai-webdriverio-async') + if (ourFlag) { + return (async () => { + const { getValueAndSelector } = ourFlag + + const [value, selector] = await getValueAndSelector() + + this.assert( + value <= n, + `Expected <${selector}> to appear in the DOM at most #{exp} times, but it shows up #{act} times instead.`, + `Expected <${selector}> not to appear in the DOM at most #{exp} times, but it shows up #{act} times instead.`, + n, + value + ) + })() + } else { + _super.apply(this, arguments) + } + } + }) } } diff --git a/test/assertions/count-test.js b/test/assertions/count-test.js index 4fb8454..82c8fe4 100644 --- a/test/assertions/count-test.js +++ b/test/assertions/count-test.js @@ -54,4 +54,124 @@ describe('count', () => { ) }) }) + describe(`when not negated with above`, function() { + it(`resolves when element count is above expectation`, async function() { + await expect('.some-selector').to.have.count.above(1) + await expect('.some-selector').to.have.count.above(0) + }) + it(`rejects when element count is not above expectation`, async function() { + await expect( + expect('.some-selector') + .to.have.count.above(2) + .then(null) + ).to.be.rejectedWith( + 'Expected <.some-selector> to appear in the DOM more than 2 times, but it shows up 2 times instead.' + ) + }) + }) + describe(`when negated with above`, function() { + it(`resolves when element count is not above expectation`, async function() { + await expect('.some-selector').to.not.have.count.above(2) + await expect('.some-selector').to.not.have.count.above(3) + }) + it(`rejects when element count is above expectation`, async function() { + await expect( + expect('.some-selector') + .to.not.have.count.above(1) + .then(null) + ).to.be.rejectedWith( + 'Expected <.some-selector> not to appear in the DOM more than 1 times, but it shows up 2 times instead.' + ) + }) + }) + describe(`when not negated with at.least`, function() { + it(`resolves when element count is at least expectation`, async function() { + await expect('.some-selector').to.have.count.at.least(2) + await expect('.some-selector').to.have.count.at.least(1) + }) + it(`rejects when element count is not at least expectation`, async function() { + await expect( + expect('.some-selector') + .to.have.count.at.least(3) + .then(null) + ).to.be.rejectedWith( + 'Expected <.some-selector> to appear in the DOM at least 3 times, but it shows up 2 times instead.' + ) + }) + }) + describe(`when negated with at.least`, function() { + it(`resolves when element count is not at least expectation`, async function() { + await expect('.some-selector').to.not.have.count.at.least(3) + await expect('.some-selector').to.not.have.count.at.least(4) + }) + it(`rejects when element count is at least expectation`, async function() { + await expect( + expect('.some-selector') + .to.not.have.count.at.least(2) + .then(null) + ).to.be.rejectedWith( + 'Expected <.some-selector> not to appear in the DOM at least 2 times, but it shows up 2 times instead.' + ) + }) + }) + describe(`when not negated with below`, function() { + it(`resolves when element count is below expectation`, async function() { + await expect('.some-selector').to.have.count.below(3) + await expect('.some-selector').to.have.count.below(4) + }) + it(`rejects when element count is not below expectation`, async function() { + await expect( + expect('.some-selector') + .to.have.count.below(2) + .then(null) + ).to.be.rejectedWith( + 'Expected <.some-selector> to appear in the DOM less than 2 times, but it shows up 2 times instead.' + ) + }) + }) + describe(`when negated with below`, function() { + it(`resolves when element count is not below expectation`, async function() { + await expect('.some-selector').to.not.have.count.below(2) + await expect('.some-selector').to.not.have.count.below(1) + }) + it(`rejects when element count is not below expectation`, async function() { + await expect( + expect('.some-selector') + .to.not.have.count.below(3) + .then(null) + ).to.be.rejectedWith( + 'Expected <.some-selector> not to appear in the DOM less than 3 times, but it shows up 2 times instead.' + ) + }) + }) + describe(`when not negated with at.most`, function() { + it(`resolves when element count is at most expectation`, async function() { + await expect('.some-selector').to.have.count.at.most(2) + await expect('.some-selector').to.have.count.at.most(3) + }) + it(`rejects when element count is not at most expectation`, async function() { + await expect( + expect('.some-selector') + .to.have.count.at.most(1) + .then(null) + ).to.be.rejectedWith( + 'Expected <.some-selector> to appear in the DOM at most 1 times, but it shows up 2 times instead.' + ) + }) + }) + describe(`when negated with at.most`, function() { + it(`resolves when element count is not at most expectation`, async function() { + await expect('.some-selector').to.not.have.count.at.most(1) + await expect('.some-selector').to.not.have.count.at.most(0) + }) + it(`rejects when element count is at most expectation`, async function() { + await expect( + expect('.some-selector') + .to.not.have.count.at.most(2) + .then(null) + ).to.be.rejectedWith( + 'Expected <.some-selector> not to appear in the DOM at most 2 times, but it shows up 2 times instead.' + ) + }) + }) })