Skip to content

Commit bff37a1

Browse files
authored
Merge pull request #21 from hyperdivision/lots-of-things
Add a bunch of things:
2 parents b017488 + 47295a4 commit bff37a1

File tree

3 files changed

+85
-42
lines changed

3 files changed

+85
-42
lines changed

README.md

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -116,34 +116,42 @@ Import the vhs test function. Works almost identically to `tape`, except your t
116116

117117
### `vhs(description, async testFn)`
118118

119-
Describe your test with a `description` string, and pass an async `testFn` which receives the `t` assertion variable. This assertion variable includes all of the `tape` helpers, with a few extras that are helpful for testig dom elements and components.
119+
Describe your test with a `description` string, and pass an async `testFn` which receives the `t` assertion variable. This assertion variable includes all of the `tape` helpers, with a few extras that are helpful for testing dom elements and components.
120120

121121
### `vhs.delay(ms)(description, async testFn)`
122122

123+
Delay all vhs-test helpers by `ms`, unless otherwise noted in the test helper description.
124+
123125
### `vhs.slow(description, async testFn)`
124126

127+
Shorthand for `vhs.delay(500)`.
128+
125129
### `vhs.skip(description, async testFn)`
126130

131+
Same as `tape` `t.skip`.
132+
127133
### `vhs.only(description, async testFn)`
128134

135+
Same as `tape` `t.only`.
136+
129137
### `t.element`
130138

131139
The HTMLElement element where your test should work inside.
132140

133-
### `await t.appendChild(el)`
141+
### `await t.appendChild(el, [msg])`
134142

135-
Takes an element, append it and then waits for onload.
143+
Takes an element, append it and then waits for onload. Asserts when complete with a `msg`.
136144
```js
137145
const newDiv = document.createElement('div')
138146
newDiv.innerText = 'New div to append'
139-
await t.appendChild(newDiv)
147+
await t.appendChild(newDiv, 'Appended newDiv')
140148
```
141149

142-
### `await t.sleep(ms)`
150+
### `await t.sleep(ms, [msg])`
143151

144-
Async sleepf for `ms`.
152+
Async sleep for `ms` and asserts when complete with `msg`.
145153

146-
### `await t.onload(element)`
154+
### `await t.onload(element, [msg])`
147155

148156
Wait for the element to be fully mounted and rendered into the page.
149157

@@ -153,37 +161,45 @@ t.element.appendChild(myElement)
153161
await t.onload(myElement)
154162
```
155163

156-
### `await t.unload(element)`
164+
### `await t.unload(element, [msg])`
157165

158166
Same as `t.onload` except it lets you wait for an element to be fully unloaded from the document.
159167

160-
### `await t.raf()`
168+
### `await t.raf([msg])`
161169

162-
Lets you wait for an animation frame to fire. This gives an opportunity for the page to repaint and reflow after making modifications to the DOM. Always waits for a RequestAnimationFrame and ignores any delay paramters.
170+
Lets you wait for an animation frame to fire. This gives an opportunity for the page to repaint and reflow after making modifications to the DOM. Always waits for a RequestAnimationFrame and ignores any delay parameters. Only asserts when passed a `msg`. Does not insert additional delays.
163171

164-
### `await t.delay()`
172+
### `await t.delay([msg])`
165173

166-
Similar to `await t.raf()`, except this will sleep when a test delay is set, so you can watch your test in slow motion. When no delay is set, these will revert to just a `t.raf()`.
174+
Similar to `await t.raf()`, except this will sleep when a test delay is set, so you can watch your test in slow motion. When no delay is set, these will revert to just a `t.raf()`. Only asserts when passed a `msg`.
167175

168-
### `await t.click(elementOrQuerySelector)`
176+
### `await t.click(elementOrQuerySelector, [msg])`
169177

170178
Accepts a query selector string that resolves to an element or an element. Calls `element.click()` followed by a `t.delay()`.
171179

172-
### `await t.focus(elementOrQuerySelector)`
180+
### `await t.focus(elementOrQuerySelector, [msg])`
173181

174182
Accepts a query selector string that resolves to an element or an element. Calls `element.focus()` followed by a `t.delay()`.
175183

176-
### `await t.type(string, [event])`
184+
### `await t.type(string, [event], [msg])`
177185

178186
Dispatches `new window.KeyboardEvent` defaulting to the `keydown` event, for each character in `string`. Helpful for typing into the currently focused element on screen. This helper is a WIP, and doesn't work everywhere. Includes a `t.delay()` call so updates are rendered every keypress.
179187

188+
### `await once(emitter, name, [msg])`
189+
190+
Shortcut to use [`'events.once'`](https://github.com/davidmarkclements/events.once#readme), which is useful for catching events as promises.
191+
180192
## FAQ
181193

194+
### How do I run vhs-tests?
195+
196+
`vhs-tests` are geared towards a Node.js style common.js environment, so you will need a bundler like browserify or webpack to bundle them into the browser or an electron app.
197+
182198
### How do I load global styles or assumed side effects?
183199

184-
If your components or tests require global styles or sprite sheets to work, write a module that mounts these assets into the page as a side effect of `require`ing or `import`ing that file.
200+
If your components or tests require global styles or sprite sheets to work, write a module that mounts these assets into the page as a side effect of `require`ing or `import`ing that file.
185201

186-
In each test, require the global style module, and your module loadig system will de-duplicate the calls to the global side-effects, and each of your tests will still work.
202+
In each test, require the global style module, and your module loading system will de-duplicate the calls to the global side-effects, and each of your tests will still work.
187203

188204
```js
189205
// global-styles.css
@@ -202,15 +218,8 @@ require('../../global-styles')
202218
// vhs('The rest of your tests...
203219
```
204220

205-
Additionally, you can always load a test bundle into a page with styles and spritesheets already mounted, or utilize features in your bundler to hande that insertion for you.
206-
207-
## See also
221+
Additionally, you can always load a test bundle into a page with styles and spritesheets already mounted, or utilize features in your bundler to hande that insertion for you.
208222

209-
https://github.com/choojs/choo/blob/master/index.js
210-
https://github.com/choojs/choo
211-
https://github.com/choojs/nanorouter/blob/master/index.js
212-
https://github.com/choojs/nanohref
213-
https://github.com/rtsao/csjs
214223

215224
## Contributors
216225

index.js

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const tape = require('tape')
22
const onload = require('fast-on-load')
3+
const once = require('events.once')
34

45
const createElement = document.createElement.bind(document)
56

@@ -50,48 +51,80 @@ function create (delay, fn) {
5051
return queueTest
5152

5253
function createTestHarness (t, element) {
54+
const tOnce = t.once
5355
return Object.assign(t, {
5456
element,
55-
sleep: ms => new Promise((resolve) => setTimeout(resolve, ms)),
56-
onload: node => new Promise(resolve => {
57+
sleep: (ms, msg) => {
58+
msg = msg || `Sleep for ${ms}ms`
59+
const sleepPromise = new Promise((resolve) => setTimeout(resolve, ms))
60+
return sleepPromise.then(() => t.pass(msg))
61+
},
62+
onload: (node, msg = 'Element onload') => new Promise(resolve => {
5763
const resolveFn = () => {
5864
onload.delete(node, resolveFn)
59-
t.delay().then(resolve)
65+
t.delay().then(() => {
66+
t.pass(msg)
67+
resolve()
68+
})
6069
}
6170
node.isConnected ? resolveFn() : onload(node, resolveFn)
6271
}),
63-
unload: node => new Promise(resolve => {
72+
unload: (node, msg = 'Element unload') => new Promise(resolve => {
6473
const resolveFn = () => {
6574
onload.delete(node, undefined, resolveFn)
66-
t.delay().then(resolve)
75+
t.delay().then(() => {
76+
t.pass(msg)
77+
resolve()
78+
})
6779
}
6880
!node.isConnected ? resolveFn() : onload(node, undefined, resolveFn)
6981
}),
70-
raf: () => new Promise(resolve => window.requestAnimationFrame(() => resolve())),
71-
delay () {
72-
return delay ? t.sleep(delay) : t.raf()
82+
raf: (msg) => {
83+
const rafPromise = new Promise(resolve => window.requestAnimationFrame(() => resolve()))
84+
return msg ? rafPromise.then(() => t.pass(msg)) : rafPromise
85+
},
86+
delay (msg) {
87+
const delayPromise = delay ? t.sleep(delay) : t.raf()
88+
return msg ? delayPromise.then(() => t.pass(msg)) : delayPromise
7389
},
74-
click (stringOrElement) {
90+
click (stringOrElement, msg) {
91+
msg = msg || `Clicked on ${typeof stringOrElement === 'string' ? stringOrElement : 'element'}`
7592
toElement(stringOrElement).click()
76-
return t.delay()
93+
return t.delay().then(() => t.pass(msg))
7794
},
78-
focus (stringOrElement) {
95+
focus (stringOrElement, msg) {
96+
msg = msg || `Focused on ${typeof stringOrElement === 'string' ? stringOrElement : 'element'}`
7997
toElement(stringOrElement).focus()
80-
return t.delay()
98+
return t.delay().then(() => t.pass(msg))
8199
},
82-
async type (str, event) {
100+
async type (str, event, msg) {
101+
if (typeof event === 'string' && !msg) {
102+
msg = event
103+
event = null
104+
}
105+
msg = msg || `Typed ${str}`
83106
for (const c of str.split('')) {
84107
const el = t.element.querySelector(':focus')
85108
if (!el) return
86109
await t.delay()
87110
el.dispatchEvent(new window.KeyboardEvent(event || 'keydown', { key: c }))
88111
}
89-
return t.delay()
112+
return t.delay().then(() => t.pass(msg))
90113
},
91-
async appendChild (el) {
92-
if (!el) return
114+
appendChild (el, msg = 'Appended child to test element') {
93115
t.element.appendChild(el)
94-
return t.onload(el)
116+
return t.onload(el, msg).then(t.delay)
117+
},
118+
once (emitter, name, msg) {
119+
// t is expected to be an event emitter
120+
if (typeof emitter === 'string') return tOnce.call(t, emitter, name)
121+
msg = msg || `${name} event emitted`
122+
return once(emitter, name).then(results => {
123+
return t.delay().then(() => {
124+
t.pass(msg)
125+
return results
126+
})
127+
})
95128
}
96129
})
97130

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"url": "https://github.com/hyperdivision/vhs-tape/issues"
88
},
99
"dependencies": {
10+
"events.once": "^2.0.2",
1011
"fast-on-load": "^1.1.0",
1112
"tape": "^4.6.2"
1213
},

0 commit comments

Comments
 (0)