-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 5b87e87
Showing
10 changed files
with
3,794 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
name: Verify | ||
|
||
on: [push] | ||
|
||
jobs: | ||
formatting: | ||
name: Check Formatting | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v1 | ||
- uses: volta-cli/action@v1 | ||
- name: Get yarn cache directory path | ||
id: yarn-cache-dir-path | ||
run: echo "::set-output name=dir::$(yarn cache dir)" | ||
- uses: actions/cache@v1 | ||
id: yarn-cache | ||
with: | ||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} | ||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} | ||
restore-keys: | | ||
${{ runner.os }}-yarn- | ||
- run: yarn install | ||
- run: yarn prettier --check . | ||
|
||
test: | ||
name: Test | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v1 | ||
- uses: volta-cli/action@v1 | ||
- name: Get yarn cache directory path | ||
id: yarn-cache-dir-path | ||
run: echo "::set-output name=dir::$(yarn cache dir)" | ||
- uses: actions/cache@v1 | ||
id: yarn-cache | ||
with: | ||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} | ||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} | ||
restore-keys: | | ||
${{ runner.os }}-yarn- | ||
- run: yarn install | ||
- run: yarn build | ||
- run: yarn test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Pika Output | ||
pkg/ | ||
|
||
# Dependencies | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Pika Output | ||
pkg/ | ||
|
||
# Dependencies | ||
node_modules/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# `qunit-wait-for` | ||
|
||
> Wait for a QUnit Assertion | ||
## Installation | ||
|
||
Install the dependency | ||
|
||
``` | ||
yarn add -D qunit-wait-for | ||
``` | ||
|
||
and install the helper in your JavaScript code. | ||
|
||
```javascript | ||
import QUnit from 'qunit'; | ||
import { installWaitFor } from 'qunit-wait-for'; | ||
|
||
installWaitFor(QUnit) | ||
``` | ||
|
||
If you're using Ember, the right place for that snippet is your `tests/test-helper.js`. | ||
|
||
## Usage | ||
|
||
`qunit-wait-for` allows you to wait for an assertion that might not pass _right now_ but will pass _soon_. The idea is that you write a test where your assertions converge on the desired state of your application, checking over and over again until the criteria are either met or a timeout is reached. This allows you to write tests that can be resilient to slight timing issues (which is common in UI testing) without needing to add explicit timeouts to your tests. | ||
|
||
To use it, pass a callback to `assert.waitFor` and within in place your normal assertion: | ||
|
||
```javascript | ||
await assert.waitFor(() => { | ||
assert.dom('[data-test-my-element]').exists(); | ||
}); | ||
``` | ||
|
||
The resulting promise resolve when either the condition is met or the timeout is reached; this promise should be `await`-ed so ensure one of those two things has happened before moving on. | ||
|
||
## Prior Art | ||
|
||
* [Converging on a Condition in QUnit](https://alexlafroscia.com/qunit-assert-converge-on/) | ||
A blog post I wrote a while ago, describing a similar API that was not based on using a normal assertion to test that the condition has been met | ||
* [`waitFor` from `@testing-library/dom`](https://testing-library.com/docs/dom-testing-library/api-async#waitfor) | ||
Inspired the API of this library, where you also pass a callback that performs an otherwise normal test assertion |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"name": "qunit-wait-for", | ||
"version": "1.0.0", | ||
"description": "Wait for a QUnit assertion", | ||
"author": "Alex LaFroscia <alex@lafroscia.com>", | ||
"license": "MIT", | ||
"scripts": { | ||
"build": "pika build", | ||
"fmt": "prettier --write .", | ||
"test": "qunit tests" | ||
}, | ||
"devDependencies": { | ||
"@pika/pack": "^0.5.0", | ||
"@pika/plugin-build-node": "^0.9.2", | ||
"@pika/plugin-build-web": "^0.9.2", | ||
"@pika/plugin-ts-standard-pkg": "^0.9.2", | ||
"@types/qunit": "^2.9.0", | ||
"prettier": "^2.0.2", | ||
"qunit": "^2.9.3", | ||
"testdouble": "^3.13.1", | ||
"testdouble-qunit": "^2.1.1", | ||
"typescript": "^3.8.3" | ||
}, | ||
"@pika/pack": { | ||
"pipeline": [ | ||
[ | ||
"@pika/plugin-ts-standard-pkg" | ||
], | ||
[ | ||
"@pika/plugin-build-node" | ||
], | ||
[ | ||
"@pika/plugin-build-web" | ||
] | ||
] | ||
}, | ||
"volta": { | ||
"node": "12.16.1", | ||
"yarn": "1.22.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { TimeoutError, waitUntil } from "./wait-until"; | ||
|
||
type AssertionCallback = () => void; | ||
type Options = { | ||
timeout: number; | ||
}; | ||
|
||
interface Result { | ||
result: boolean; | ||
} | ||
|
||
export function installWaitFor(QUnit: QUnit) { | ||
QUnit.extend(QUnit.assert, { | ||
async waitFor( | ||
assertionCallback: AssertionCallback, | ||
{ timeout = 1000 }: Options = { timeout: undefined } | ||
) { | ||
const originalPushResult = this.pushResult; | ||
let lastResult: Result; | ||
|
||
this.pushResult = (result: Result) => { | ||
lastResult = result; | ||
}; | ||
|
||
try { | ||
await waitUntil(() => { | ||
assertionCallback(); | ||
|
||
return lastResult.result; | ||
}, timeout); | ||
} catch (e) { | ||
if (!(e instanceof TimeoutError)) { | ||
throw e; | ||
} | ||
} finally { | ||
this.pushResult = originalPushResult; | ||
} | ||
|
||
this.pushResult(lastResult); | ||
}, | ||
}); | ||
} | ||
|
||
declare global { | ||
interface Assert { | ||
waitFor( | ||
callback: AssertionCallback, | ||
options?: Options | ||
): () => Promise<void>; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
const futureTick = setTimeout; | ||
|
||
const TIMEOUTS = [0, 1, 2, 5, 7]; | ||
const MAX_TIMEOUT = 10; | ||
|
||
export function waitUntil(callback: () => unknown, timeout: number) { | ||
const waitUntilTimeoutError = new TimeoutError( | ||
"Condition not met within timeout" | ||
); | ||
|
||
return new Promise((resolve, reject) => { | ||
let time = 0; | ||
|
||
// eslint-disable-next-line require-jsdoc | ||
function scheduleCheck(timeoutsIndex: number) { | ||
let interval = TIMEOUTS[timeoutsIndex]; | ||
if (interval === undefined) { | ||
interval = MAX_TIMEOUT; | ||
} | ||
|
||
futureTick(function () { | ||
time += interval; | ||
|
||
let value: unknown; | ||
|
||
try { | ||
value = callback(); | ||
} catch (error) { | ||
reject(error); | ||
} | ||
|
||
if (value) { | ||
resolve(); | ||
} else if (time < timeout) { | ||
scheduleCheck(timeoutsIndex + 1); | ||
} else { | ||
reject(waitUntilTimeoutError); | ||
} | ||
}, interval); | ||
} | ||
|
||
scheduleCheck(0); | ||
}); | ||
} | ||
|
||
export class TimeoutError extends Error {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
const QUnit = require("qunit"); | ||
const td = require("testdouble"); | ||
const installVerify = require("testdouble-qunit"); | ||
const { installWaitFor } = require("../pkg"); | ||
const { test } = QUnit; | ||
|
||
installVerify(QUnit); | ||
installWaitFor(QUnit); | ||
|
||
test("it can wait for a condition to be successful", async function (assert) { | ||
const stub = td.when(td.function()()).thenReturn(1, 2); | ||
|
||
await assert.waitFor(() => { | ||
assert.equal(stub(), 2); | ||
}); | ||
}); | ||
|
||
test("it throws a non-timeout error", async function (assert) { | ||
assert.expect(1); | ||
|
||
const e = new Error("Some Error"); | ||
const stub = td.when(td.function()()).thenThrow(e); | ||
|
||
try { | ||
await assert.waitFor(() => { | ||
assert.ok(stub()); | ||
}); | ||
} catch (error) { | ||
assert.equal(error, e); | ||
} | ||
}); | ||
|
||
test("it can time out while waiting", async function (assert) { | ||
const stub = td.when(td.function()()).thenReturn(1); | ||
|
||
td.replace(assert, "pushResult"); | ||
|
||
await assert.waitFor(() => { | ||
assert.equal(stub(), 2); | ||
}); | ||
|
||
const { | ||
calls: [{ args }], | ||
} = td.explain(assert.pushResult); | ||
|
||
td.reset(); // Let `assert` work again | ||
|
||
assert.deepEqual(args, [ | ||
{ result: false, actual: 1, expected: 2, message: undefined }, | ||
]); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "ES2020", | ||
"module": "ESNext" | ||
}, | ||
"files": ["src/index.ts"] | ||
} |
Oops, something went wrong.