Abstract Wiring Node
For the vast majority of use cases, standard wiring nodes work great. For
certain situations however, a new kind of node is required. Abstract wiring
nodes become useful when you need to include a single abstract node in your
wiring tree that is able to behave differently depending in different
situations. To create an abstract wiring node, you need to add both types and
getCurrentType to a standard wiring node.
types (Object)
An object where they key is the type name that will be returned from
getCurrentType and the object is a type node.
types: {
buttonItem: {
extend: (val, {findButton}) => ({
clickButton: async () => {
const {click} = await findButton()
click()
},
}),
serialize: (val, {buttonString}, baseString) => {
return `${baseString} ${buttonString}`
},
children: {
button: {
serialize: val => `[${val.textContent}]`,
},
},
}
getCurrentType (Function)
(element, { ...elementHelpers }) => currentTypeString
A function that takes an element,and the helpers for that element, and returns the string that points to the type that should currently be used for this node.
getCurrentType: (val, {queryByTestId}) => {
if (queryByTestId('button')) {
return 'buttonItem'
}
return 'checkboxItem'
}
Type Options
The general idea of options declared in a type is that they are supposed to augment the functions declared in the base type.
extend (Function)
(element, { ...elementHelpers }) => ({ ...newElementHelpers })
In a type, extend behaves the same way as a standard
extend, except that instead of just
returning new functions, it returns functions that will be added to any
functions returned from the base extend. That means if you return funcA from
the base node, and return funcB from the type, both funcA and funcB will
be available in the test. Additionally, funcA will be available in the type
extend.
serialize (Function)
(element, { ...elementHelpers }, baseString) => serializedString
In a type, the only change to serialize is that it gets passed a new third
baseString argument. baseString is the result of running the serialize
function in the base node. This allows you to render some shared string in the
base node and then extend it in your types. It should be noted that the base
node serialize doesn't actually have to return a string. A common use case is
to return a function from the base node and let the type serializers pass
argument into that function.
children (Object)
Similar to extend and serialize the children object in a type adds on to
the children object of the base node. This allows you add new children that
only exist for that type, while being able to declare children in the base node
that you know will exist for all types.
Example
import React from 'react'
import {getRender} from 'react-wiring-library'
const List = () => {
return (
<ul data-testid="list">
<li data-testid="item">
<span data-testid="name">Row One</span>
<button data-testid="button">Button Name</button>
</li>
<li data-testid="item">
<span data-testid="name">Row One</span>
<input data-testid="checkbox" type="checkbox" />
</li>
</ul>
)
}
const wiring = {
children: {
list: {
findValue: 'list',
serialize: (val, {itemStrings, combine}) => combine(...itemStrings),
children: {
item: {
findValue: 'item',
serialize: (val, {nameString}) => `- ${nameString}`,
isMultiple: true,
children: {
name: {
findValue: 'name',
serialize: val => val.textContent,
},
},
getCurrentType: (val, {queryByTestId}) => {
if (queryByTestId('button')) {
return 'buttonItem'
}
return 'checkboxItem'
},
types: {
buttonItem: {
extend: (val, {findButton}) => ({
clickButton: async () => {
const {click} = await findButton()
click()
},
}),
serialize: (val, {buttonString}, baseString) => {
return `${baseString} ${buttonString}`
},
children: {
button: {
findValue: 'button',
serialize: val => `[${val.textContent}]`,
},
},
},
checkboxItem: {
extend: (val, {findCheckbox}) => ({
toggleCheckbox: async () => {
const {click} = await findCheckbox()
click()
},
}),
serialize: (val, {checkboxString}, baseString) => {
return `${baseString} ${checkboxString}`
},
children: {
checkbox: {
findValue: 'checkbox',
serialize: val => (val.checked ? '✅' : '⬜️'),
},
},
},
},
},
},
},
},
}
test('should correctly handle abstract components', async () => {
const render = getRender(wiring)
const {findList} = render(<List />)
const {findItem, list} = await findList()
// - Row One [Button Name]
// - Row Two ⬜️
expect(list).toMatchSnapshot('on initial render')
const {clickButton} = await findItem({index: 0})
await clickButton()
const {toggleCheckbox} = await findItem({index: 1})
await toggleCheckbox()
// - Row One [Button Name]
// - Row Two ✅
expect(list).toMatchSnapshot('after toggling checkbox')
})
