Elmish.Enzyme
- Package
- purescript-elmish-enzyme
- Repository
- collegevine/purescript-elmish-enzyme
Bindings for the Enzyme library to use with the Elmish library.
The API here is presented in monadic style, with the idea that there is
always a "current" DOM element (or, more precisely, current
Wrapper
, which may refer to zero or more actual DOM elements),
with respect to which all operations run. A test starts with either
testComponent
or testElement
, and it's that component (or element) that
becomes the "current" element at the start, for example:
-- The main module:
myComponent :: ComponentDef Message State
myComponent = { init, view, update } where ...
-- The test module:
import Elmish.Enzyme (testComponent, text)
someTest = it "contains the right text" do
testComponent myComponent do
content <- text
content `shouldContain` "right"
From there, it's possible to "drill down" into a particular element, making
it "current" temporarily, via withSelector
or withElement
(similar to
Capybara's within
), for example:
import Elmish.Enzyme (testComponent, text, withSelector)
someTest = it "has two buttons with the right text" do
testComponent myComponent do
withSelector "button.first-button" do
content <- text
content `shouldEqual` "I'm the first button!"
withSelector "button.second-button" do
content <- text
content `shouldEqual` "I'm the second button!"
Alternatively, operations that return a Wrapper
may be combined
in a chain using the >>
operator (which is just a convenient facade for
withElement
under the hood):
import Elmish.Enzyme (testComponent, text, find, findAll, at, (>>))
someTest = it "has two buttons with the right text" do
testComponent myComponent do
-- A short chain, just two functions:
b1 <- find "button.first-button" >> text
b1 `shouldEqual` "I'm the first button!"
-- A longer chain: `findAll`, then `at`, then `text`
b2 <- findAll "button" >> at 1 >> text
b2 `shouldEqual` "I'm the second button!"
-- An even longer chain:
b3 <- findAll ".card" >> at 3 >> findAll "button" >> at 1 >> text
b3 `shouldEqual` "I'm second button in 4th card!"
The end result is an API that somewhat resembles how Emzyme would be used from JavaScript, as well as other similar testing libraries, sych as Capybara.
#EnzymeM Source
type EnzymeM :: NodeMultiplicity -> Type -> Type
type EnzymeM (context :: NodeMultiplicity) = ReaderT (Wrapper context) Aff
Monad for running Enzyme tests. Keeps a reference to the "current" DOM
element(s) and tracks the current element's multiplicity (whether it's a
single node or multiple) at type level (the context
parameter), allowing
to check validity of certain functions at compile-time (e.g. text
only
works on a single element).
#testComponent Source
testComponent :: forall m msg state. MonadAff m => ComponentDef msg state -> EnzymeM SingleNode Unit -> m Unit
Mounts the given component and runs the given action with the mounted component as current context.
it "displays content" do
testComponent (MyComponent.def props) do
exists ".t--my-content" >>= shouldEqual true
#testElement Source
testElement :: forall m. MonadAff m => ReactElement -> EnzymeM SingleNode Unit -> m Unit
Mounts the given element and runs the given action with the mounted element as current context.
pending' "displays content"
testElement (MyElement.render props) do
exists ".t--my-content" >>= shouldEqual true
#at Source
at :: Int -> EnzymeM ManyNodes (Wrapper SingleNode)
The current context can contain multiple DOM elements. This function returns the element at the given index (zero-based). See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/at.html for more info.
findAll "button" >> at 3 >> text >>= shouldEqual "Fourth button"
#childAt Source
childAt :: forall n. Int -> EnzymeM n (Wrapper SingleNode)
Returns the child node at index idx
of the current Wrapper
.childAt n
is equivalent to children >> at n
See
https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/childAt.html ffor
more info.
#children Source
children :: forall n. EnzymeM n (Wrapper ManyNodes)
Returns the child nodes of the current Wrapper
. When the current context
contains multiple elements, the result will contain the children of each
element. See
https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/children.html for
more info.
#debug Source
debug :: forall n. DebugWarning => EnzymeM n String
Returns the string representing the DOM tree of the current element(s). See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/debug.html for more info.
#exists Source
exists :: forall n. String -> EnzymeM n Boolean
Returns a Boolean
indicating whether a given selector exists within the
current element.
See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/exists.html
for more info.
#find Source
find :: forall n. String -> EnzymeM n (Wrapper SingleNode)
Finds a single element matching the given selector within the current context. If zero or multiple elements match the selector, crashes with a descriptive error message.
See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/find.html for more info.
#forEach Source
forEach :: EnzymeM SingleNode Unit -> EnzymeM ManyNodes Unit
Basically, a DSL-friendly equivalent of for_
: for each element in the
current Wrapper
performs the given effect.
Example:
findAll "button" >> forEach (simulate "click")
findAll ".t--foo" >> forEach do
find "input[type=text]" >> simulate' "change" { target: { value: "new text" } }
find "input[type=checkbox]" >> simulate "change"
find ".t--bar" >> text >>= shouldEqual "qux"
#is Source
is :: String -> EnzymeM SingleNode Boolean
Returns a Boolean
indicating whether the current element matches
the given selector.
See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/is.html
for more info.
#mapEach Source
mapEach :: forall a. EnzymeM SingleNode a -> EnzymeM ManyNodes (Array a)
Basically, a DSL-friendly equivalent of map
: for each element in the
current Wrapper
performs the given action and returns all results
of that action as an array.
Example:
allNames <- findAll ".t--foo" >> mapEach text
allValues <- findAll ".t--foo" >> mapEach (prop "value")
#name Source
name :: EnzymeM SingleNode String
Returns tag name of the current element. See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/name.html for more info.
#parent Source
parent :: forall n. EnzymeM n (Wrapper n)
Returns parent of the current element. When the current context contains multiple elements, the result will contain exactly as many parents, even if some of the elements have the same parent. See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/parent.html for more info.
#prop Source
prop :: forall a. String -> EnzymeM SingleNode a
Returns the value of the current element’s prop with a certain key. See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/prop.html for more info.
#simulate Source
simulate :: String -> EnzymeM SingleNode Unit
A convenience function for calling simulate'
without an event
arg.
#simulate' Source
simulate' :: forall r. String -> Record r -> EnzymeM SingleNode Unit
Simulates a certain event type on the current element. The event argument is a record that gets merged with a simulated React synthetic event before being passed to the component’s event handler. See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/simulate.html for more info. NOTE: This doesn’t actually emit an event, but just triggers the event handler on the wrapper.
NOTE 2: This function only works for native HTML elements. For emitting
events on custom React components, use simulateCustom
.
#simulateCustom' Source
simulateCustom' :: forall a. String -> a -> EnzymeM SingleNode Unit
Simulates an event on a custom React component (i.e. not an HTML element).
For reasons to complicated to discuss here, the regular simulate
doesn't
work on custom components, so we provide this workaround.
NOTE: the second parameter is passed to the event handler without any checks whatsoever. This is, of course, not type-safe, but it is in line with what the event handler should expect anyway: after all, the underlying JavaScript component may pass anything at all as event argument.
#spy Source
spy :: forall n. DebugWarning => EnzymeM n (Wrapper n)
Logs a string representing the DOM tree of the current element(s) and
returns the original Wrapper
, so that it can be used in a withElementM
chain, like so:
spy >> find "foo" >> spy >> childAt 0 >> spy >> text >>= shouldEqual "Foo"
#state Source
state :: EnzymeM SingleNode Foreign
Returns the state of the current element. See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/state.html for more info.
#text Source
text :: EnzymeM SingleNode String
Returns the text within the current element. See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/text.html for more info.
#trace Source
trace :: forall n. DebugWarning => EnzymeM n Unit
Logs a string representing the DOM tree of the current element(s).
#unsafeSetState Source
unsafeSetState :: forall state. state -> EnzymeM SingleNode Unit
Sets the state of the current element. Note that this is See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/setState.html for more info.
NOTE: this is a type-unsafe operation. There is no check to make sure the state being set here has the type of the actual state the component in question is using.
#update Source
update :: forall n. EnzymeM n Unit
Updates the current element to reflect the latest state. Call this function whenever you think there could be an async change of state that caused a re-render. For some reason, Enzyme won't pick up the changes automatically. See https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/update.html for more info.
NOTE: this only works on the "root" element, which means it cannot be
called inside withSelector
or withElement
.
#waitUntil' Source
waitUntil' :: forall n. Milliseconds -> EnzymeM n Boolean -> EnzymeM n Unit
Performs active wait while the given condition is true. Times out with a crash after given time period.
#waitWhile' Source
waitWhile' :: forall n. Milliseconds -> EnzymeM n Boolean -> EnzymeM n Unit
Performs active wait while the given condition is true. Times out with a crash after given time period.
#withElement Source
withElement :: forall a nOuter nInner. Wrapper nInner -> EnzymeM nInner a -> EnzymeM nOuter a
Takes a Wrapper
and runs an EnzymeM
computation with the given
wrapper as the new implicit wrapper. This can be thought of as analogous to
Capybara’s within
.
button <- find "button.my-button"
withElement button do
simulate "click"
prop "disabled" >>= shouldEqual true
#withElementM Source
withElementM :: forall a nOuter nInner. EnzymeM nOuter (Wrapper nInner) -> EnzymeM nInner a -> EnzymeM nOuter a
A version of withElement
that takes the Wrapper
wrapped in
EnzymeM
rather than "naked". Aliased as the >>
operator, this allows
for handy chaining of operations that return a Wrapper
, for
example:
findAll ".foo" >> at 1 >> find ".bar" >> simulate "click"
#withSelector Source
withSelector :: forall a n. String -> EnzymeM SingleNode a -> EnzymeM n a
A convenience function which finds an element for the given selector and
passes it to withElement
.
withSelector ".t--my-button" do
simulate "click"
prop "disabled" >>= shouldEqual true
Re-exports from Elmish.Enzyme.Foreign
#Wrapper Source
data Wrapper :: NodeMultiplicity -> Type
data Wrapper (w :: NodeMultiplicity)
#SingleNode Source
data SingleNode :: NodeMultiplicity
#ManyNodes Source
data ManyNodes :: NodeMultiplicity