riot-test-utils
Version:
lightweight testing utility of Riot
420 lines (269 loc) • 9.03 kB
Markdown
Riot-test-utils
===============
This provides lightweight testing of `Riot` tags.
It is highly inspired by [`React-test-utils`](https://reactjs.org/docs/test-utils.html) [`Enzyme`](https://github.com/airbnb/enzyme), great testing utility for [`React`](https://reactjs.org/).
Shallow-rendering is provided by [`Riot-shallowize`](https://www.npmjs.com/package/riot-shallowize) including the limitation about transclusions.
Features:
- Shorter way to setup unit-testing of tags.
- Wrapper API of an instance of tags to inspect easily
- DOM Traversing API similar to jQuery
- jQuery integration
- *Shallow-rendering*.
- Snapshot testing
This library is being developing and have **breaking changes** even if minor update. Any contributions are welcome!
Installation
------------
Install via `npm`:
```bash
npm install -D riot-test-utils
```
UMD module is also available.
For example loading from CDN like jsdelivr is like:
```html
<script src="https://cdn.jsdelivr.net/npm/riot@3.9/riot.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/riot-test-utils@1.1.0/dist/index.umd.min.js"></script>
```
Dependent packages `riot-shallowize`, `simulate-event` and other utilities are bundled in UMD.
Usage
-----
Import as:
```js
// es5/commonjs
var mount = require('riot-test-utils').mount;
// es6
import { mount } from 'riot-test-utils';
```
Create wrapper for the tag to test as:
```js
var wrapper = mount('tag');
var wrapperWithOpts = mount('tag-with-opts', { title: 'RiotJS' });
```
and look outerHTML:
```js
assert(wrapper.html() === '<tag data-is="tag"><p>Hello, world!</p></tag>');
```
Templating also works.
```js
var wrapper = mount('<tag><p>{opts.greeting}</p></tag>', { greeting: 'Hello, world!' });
assert(wrapper.html() === '<tag data-is="tag"><p>Hello, world!</p></tag>');
```
### Embedding style
We can write single tag source in the following style:
```js
var wrapper = mount('<tag><p>Hello, world!</p></tag>');
```
Specify the name to test when you have multiple tags:
```js
var wrapper = mount(
[
'<tag1><tag2 /></tag1>',
'<tag2><p>Hello, world!</p></tag2>',
].join('\n'),
'tag1'
);
```
### Shallow rendering
This library provides great **shallow-rendering** feature as `React-test-utils`.
It truly separates your tests of one tag from the others.
For example:
```js
var shallow = require('react-test-utils').shallow;
var wrapper = shallow(
[
'<inner><p>{opts.data}</p></inner>',
'<outer><inner data={ opts.innerData }/></outer>'
].join('\n'),
'outer',
{ innerData: 'Hello!' }
);
```
This is *shallow* rendered as:
```js
assert(wrapper.html() === '<outer data-is="outer"><inner data="Hello!"></inner></outer>');
```
Of course, you can mount by name being registered.
```js
var wrapper = shallow('outer', { innerData: 'Hello!' });
```
DOM testing
-----------
You can get DOM Element by `root`.
Then, you can inspect by DOM API or some utility like jQuery.
```js
var wrapper = mount('tag', { greeting: 'Hellow, world' });
// find by DOM API
assert(wrapper.root.querySelector('p').textContent === 'Hellow, world' );
// find by jQuery
assert($(wrapper.root).find('p').text() === 'Hello, world');
```
And gather internal elements by `find()` method and inspect them.
```js
const itemsWrapper = wrapper.find('ul.todo-list > li');
assert(itemsWrapper.length === 5);
assert(itemsWrapper.get(0).text() === 'Buy a car');
```
Snapshot testing
----------------
You can use `toJSON()` to use **[snapshot-testing](https://facebook.github.io/jest/docs/en/snapshot-testing.html)**
to keep your tags.
With `jest` for example:
```js
it('should match snapshot', function () {
var wrapper = shallow('<tag><h1>Example:</h1><p>Hello, world!</p></tag>');
expect(wrapper.toJSON()).toMatchSnapshot();
});
```
Then, you will see the following result if different:
```diff
<tag
data-is={
Array [
"tag"
]
}
>
- Hello, world!
+ <h1>
+ Example:
+ </h1>
+ <p>
+ Hello, world!
+ </p>
</tag>
```
Public API
----------
### Module
#### mount(tagName, [opts], [mountOptions])
#### mount(singleTagSource, [opts], [mountOptions])
#### mount(multipleTagSource, tagName, [opts], [mountOptions])
Mount a tag with full-rendering.
#### shallow(tagName, [opts], [mountOptions])
#### shallow(singleTagSource, [opts], [mountOptions])
#### shallow(multipleTagSource, tagName, [opts], [mountOptions])
Mount a tag with shallow-rendering.
They return `RiotWrapper`.
#### Parameters of mount/shallow
| Parameter | Type | Description |
| ----------------- | ------ | ------------------------------------------------------------ |
| tagName | String | name of tag to test. You need to have loaded the tag somewhere without `singleTagSource` or `multipleTagSource`. |
| singleTagSource | String | content of one tag to test. |
| multipleTagSource | String | content of tags including one to test. It requires `tagName` too. |
| opts | Object | tag interface |
| mountOptions | Object | Options to mount. It has one optional property `attachTo`. See below. |
##### mountOptions.attachedTo
An option to specify the element to mount on.
###### Example
```html
<div>
<span id="app"></span>
</div>
<script>
var span = document.getElementById('id');
var wrapper = mount('my-tag', { attachTo: span });
</script>
```
### RiotWrapper
Wrapper of tag instance to be mounted or shallow-mounted.
#### instance
Get `TagInstance` of root.
#### root
Get root DOM Element.
It is equivalent to `instance.root`.
#### opts
Get opts, including "data-is" attribute added during rendering.
It is equivalent to `instance.opts`.
#### parent
Get the parent tag instance.
It is equivalent to `instance.parent` and always `null`.
#### tags
Get nested tags.
It is equivalent to `instance.tags`.
#### refs
Get refs.
It is equivalent to `instance.refs`.
#### on(event, callback)
#### one(event, callback)
#### trigger(event, ...args)
#### off(event, [callback])
All the observable methods.
It is equivalent to `instance,on()` and the others but returns the wrapper itself.
Note: `this` is always the unwrapped instance in callbacks.
#### isMounted
Get the flag if the tag is mounted or not.
It is equivalent to `instance.isMounted`.
#### mount()
Mount the tag.
Note it is already mounted initially.
It is equivalent to `instance.mount()`.
#### unmount([keepTheParent])
Unmount the tag.
It is equivalent to `instance.unmount()`.
#### update([data])
Update the tag and its children.
It is equivalent to `instance.update()`.
#### mixin(mixin)
Apply mixin to the tag.
It is equivalent to `instance.mixin()`.
#### find(selector)
Find internal elements by CSS selector.
This returns `WeakWrapper`
#### html()
Get outer HTML by string.
#### text()
Get internal concatenated text.
#### toJSON()
Get json form to create snapshot
#### simulate(eventType, [options])
Fire event.
For example:
```js
wrapper.simulate('click');
```
```js
wrapper.simulate('keyup', { key: 'a', keyCode: 97, metaKey: true });
```
All the events are listed in [source](./src/lib/Simulate/eventTypes.ts).
### WeakWrapper
Wrapper of DOM elements to be found.
#### instance
Tag instance. This always returns `null`.
#### root
**Throws** unless wrapped elements are single.
#### length
Get number of wrapped elements.
#### get([index])
Get wrapped element(s) as `Element` or array of `Element`.
#### find(selector)
Find more under the wrapped element(s) by CSS selector.
#### text()
#### html()
#### toJSON()
Contents APIs above are supported as well as `RiotWrapper`, but first two methods **throws** unless single.
#### simulate(eventType, [options])
Fire event.
If wrapper has multiple elements, it fires each element in the order of appearance.
## jQuery integration
You can use some methods to inspect DOM of [jQuery](https://jquery.com/) integrated with `RiotWrapper` and `ShallowWrapper`.
Check [Riot-test-utils-jquery](https://www.npmjs.com/package/riot-test-utils-jquery) out.
Enzyme integration
------------------
Check [Riot-enzyme](https://www.npmjs.com/package/riot-enzyme) out, but nearly archived.
Requirement
-----------
- JavaScript runtime, ES5 compatible at least and supports:
- `Symbol` for snapshot testing.
- DOM Environment like real browsers or `jsdom` and supports:
- `querySelectorAll` and `compareDocumentPosition` for finding(IE9 is dropped!).
TODO
----
- [x] Event simulated
- [x] Deep rendering
- [ ] Compiler options to set parsers
- [x] Implement update.
- [ ] To test the attributes of root opts
- [ ] Full-featured finding API.
- [x] More efficient API for multiple tags to compile once shared and use anywhere.
- [ ] Other testing utility.
- [x] Finding API on `querySelctorAll` similar to jQuery or Cheerio like Enzyme
- [ ] *To promote* results of find() if they are actually tag instances.