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 -> Typetype 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 UnitMounts 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 UnitMounts 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 StringReturns 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 BooleanReturns 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 UnitBasically, 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 BooleanReturns 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 StringReturns 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 aReturns 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 UnitA convenience function for calling simulate' without an event arg.
#simulate' Source
simulate' :: forall r. String -> Record r -> EnzymeM SingleNode UnitSimulates 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 UnitSimulates 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 ForeignReturns 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 StringReturns 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 UnitLogs a string representing the DOM tree of the current element(s).
#unsafeSetState Source
unsafeSetState :: forall state. state -> EnzymeM SingleNode UnitSets 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 UnitUpdates 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 UnitPerforms 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 UnitPerforms 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 aTakes 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 aA 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 aA 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 -> Typedata Wrapper (w :: NodeMultiplicity)
#SingleNode Source
data SingleNode :: NodeMultiplicity#ManyNodes Source
data ManyNodes :: NodeMultiplicity