Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jedwards1211 committed Dec 17, 2020
1 parent 0c1167c commit 66b7427
Show file tree
Hide file tree
Showing 7 changed files with 442 additions and 74 deletions.
4 changes: 3 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"sourceType": "module"
},
"env": {
"es6": true
"es6": true,
"commonjs": true,
"shared-node-browser": true
}
}
65 changes: 43 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,52 @@
# es2015-library-skeleton
# chai-wait-for

[![CircleCI](https://circleci.com/gh/jedwards1211/es2015-library-skeleton.svg?style=svg)](https://circleci.com/gh/jedwards1211/es2015-library-skeleton)
[![Coverage Status](https://codecov.io/gh/jedwards1211/es2015-library-skeleton/branch/master/graph/badge.svg)](https://codecov.io/gh/jedwards1211/es2015-library-skeleton)
[![CircleCI](https://circleci.com/gh/jcoreio/chai-wait-for.svg?style=svg)](https://circleci.com/gh/jcoreio/chai-wait-for)
[![Coverage Status](https://codecov.io/gh/jcoreio/chai-wait-for/branch/master/graph/badge.svg)](https://codecov.io/gh/jcoreio/chai-wait-for)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![npm version](https://badge.fury.io/js/es2015-library-skeleton.svg)](https://badge.fury.io/js/es2015-library-skeleton)
[![npm version](https://badge.fury.io/js/chai-wait-for.svg)](https://badge.fury.io/js/chai-wait-for)

This is my personal skeleton for creating an ES2015 library npm package. You are welcome to use it.
poll an assertion until it succeeds. Provides an especially clean syntax for working with some chai plugins like `chai-fs`, `chai-webdriverio-async` etc:

## Quick start
```js
await waitFor('#submittedMessage').to.have.text('Your changes have been saved!')
```

# Usage

```sh
npx 0-60 clone https://github.com/jedwards1211/es2015-library-skeleton.git
npm install --save-dev chai-wait-for
```

## Tools used

- babel 7
- mocha
- chai
- istanbul
- nyc
- eslint
- flow
- prettier
- husky
- semantic-release
- renovate
- Circle CI
- Codecov.io
```js
// First, use the plugin
const chai = require('chai')
const chaiWaitFor = require('chai-wait-for')
chai.use(chaiWaitFor)

// Then create your `waitFor` with default options:
const waitFor = chaiWaitFor.bindWaitFor({
// If no assertion attempt succeeds before this time elapses (in milliseconds), the waitFor will fail.
timeout: 5000,
// If an assertion attempt fails, it will retry after this amount of time (in milliseconds)
retryInterval: 100,
})

it('wait for something', async function () {
this.timeout(10000)

const myObj = { foo: 0 }

setInterval(() => myObj.foo++, 1000)

// Then use it just like you would expect():
await waitFor(myObj).to.have.property('foo').that.equals(3)

// You can also use a getter function:
await waitFor(() => myObj.foo).to.equal(4)

// If you need to override the defaults:
waitFor.timeout(1000)(myObj).to.have.property('foo').that.equals(3)
waitFor.retryInterval(500)(myObj).to.have.property('foo').that.equals(3)
})
```
25 changes: 18 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "es2015-library-skeleton",
"name": "chai-wait-for",
"version": "0.0.0-development",
"description": "my personal ES2015 library project skeleton",
"description": "poll until an assertion succeeds",
"main": "index.js",
"sideEffects": false,
"scripts": {
Expand Down Expand Up @@ -61,17 +61,24 @@
},
"repository": {
"type": "git",
"url": "https://github.com/jedwards1211/es2015-library-skeleton.git"
"url": "https://github.com/jcoreio/chai-wait-for.git"
},
"keywords": [
"es2015"
"chai",
"wait",
"await",
"async",
"async-await",
"poll",
"assertion",
"retry"
],
"author": "Andy Edwards",
"license": "MIT",
"bugs": {
"url": "https://github.com/jedwards1211/es2015-library-skeleton/issues"
"url": "https://github.com/jcoreio/chai-wait-for/issues"
},
"homepage": "https://github.com/jedwards1211/es2015-library-skeleton#readme",
"homepage": "https://github.com/jcoreio/chai-wait-for#readme",
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
Expand All @@ -92,6 +99,9 @@
"babel-eslint": "^10.1.0",
"babel-plugin-istanbul": "^6.0.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"chai-webdriverio-async": "^2.0.0",
"codecov": "^3.8.1",
"copy": "^0.3.2",
"cross-env": "^7.0.3",
Expand All @@ -108,7 +118,8 @@
"prettier": "^2.2.1",
"prettier-eslint": "^12.0.0",
"rimraf": "^3.0.2",
"semantic-release": "^17.3.0"
"semantic-release": "^17.3.0",
"sinon": "^9.2.2"
},
"dependencies": {
"@babel/runtime": "^7.12.5"
Expand Down
95 changes: 92 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,93 @@
/* @flow */
const chai = require('chai')

/* eslint-disable no-console, no-undef */
console.log('Hello world!')
class WaitFor {
constructor(options, buildAssertion) {
this.options = options
this.buildAssertion = buildAssertion
}

then(onResolve, onReject) {
const { timeout, retryInterval } = this.options
let numAttempts = 0
let startTime = new Date().getTime()
let timeoutTime = startTime + timeout

const poll = async () => {
numAttempts++
const thisAttemptStartTime = new Date().getTime()
try {
await this.buildAssertion()
} catch (error) {
const delay =
Math.min(timeoutTime, thisAttemptStartTime + retryInterval) -
new Date().getTime()

if (delay <= 0) {
error.message += ` (timed out after ${timeout}ms, ${numAttempts} attempts)`
throw error
}

await new Promise((resolve) => setTimeout(resolve, delay))
await poll()
}
}

return poll().then(onResolve, onReject)
}
}

function bindWaitFor(options) {
const bound = (value, ...args) =>
new WaitFor(options, () =>
chai.expect(typeof value === 'function' ? value() : value, ...args)
)
bound.timeout = (timeout) => bindWaitFor({ ...options, timeout })
bound.retryInterval = (retryInterval) =>
bindWaitFor({ ...options, retryInterval })
return bound
}

module.exports = (chai, utils) => {
const Assertion = chai.Assertion

const propertyNames = Object.getOwnPropertyNames(Assertion.prototype)

const propertyDescs = {}
for (const name of propertyNames) {
propertyDescs[name] = Object.getOwnPropertyDescriptor(
Assertion.prototype,
name
)
}

// We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage.
const methodNames = propertyNames.filter((name) => {
return name !== 'assert' && typeof propertyDescs[name].value === 'function'
})

methodNames.forEach((methodName) => {
WaitFor.prototype[methodName] = function () {
return new WaitFor(this.options, () => {
const assertion = this.buildAssertion()
return assertion[methodName].apply(assertion, arguments)
})
}
})

const getterNames = propertyNames.filter((name) => {
return name !== '_obj' && typeof propertyDescs[name].get === 'function'
})

getterNames.forEach((getterName) => {
Object.defineProperty(WaitFor.prototype, getterName, {
get() {
return new WaitFor(this.options, () => {
const assertion = this.buildAssertion()
return assertion[getterName]
})
},
})
})
}

module.exports.bindWaitFor = bindWaitFor
2 changes: 2 additions & 0 deletions test/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"env": {
"node": true,
"es6": true,
"mocha": true
}
}
Loading

0 comments on commit 66b7427

Please sign in to comment.