Elmish.Test
- Package
- purescript-elmish-testing-library
- Repository
- collegevine/purescript-elmish-testing-library
Elmish Testing Library tries to roughly follow the general API shape of libraries like JS Selenium or Ruby/Rails Capybara:
testComponent
ortestElement
to mount the component and start a test.find "button.some-class"
- to find a single element (will crash when not found or found more than one).findAll "div.main-content"
- to find zero or multiple elements.- Query elements via
text
,html
,attr
,tagName
, and so on. - Simulate events via
fireEvent
or convenience facades likeclick
,clickOn
, orchange
- Chain operations via
>>
, likefind "button" >> text
orfind "div" >> find "a" >> attr "href"
within
to zoom into an element and run some code in its context.
An illustrating example:
spec :: Spec Unit
spec =
describe "My component" $
it "illustrates how to write a test" $
testComponent { init, view, update } do
find "h1" >> text >>= shouldEqual "Hello"
find "input[type=text]" >> change "Some text in the textbox"
find "button" >> click
within "div.wrapper" do
text >>= (_ `shouldContain` "Some text inside a wapper")
findAll "a" >>= traverse_ \anchor ->
anchor ## attr "href" >>= shouldEqual "http://foo.bar"
Every DOM-manipulating operation (e.g. find
, text
, fireEvent
, and so
on) doesn't take a DOM node as a parameter, but instead runs in a monad
that always has a "current" (or "focused") node in context. All operations
always apply to that "current" node.
The current node can be changed (or "refocused", or "zoomed") via within
or within'
. These functions find (or take) another node and run a
computation with that node as "current", thus making all operations inside
a within
apply to it.
In operator form >>
, these functions allow "chaining" operations
together, as in:
find "button" >> click
-- equivalent to:
b <- find "button"
within' b click
The difference is admittedly only cosmetic, but it allows for nice looking code, like:
-- click a button within the 6th subnode of the root <div>
find "div" >> childAt 5 >> find "button" >> click
Finally, if you have an Element
value on your hands, you can apply a
DOM-manipulating operation to it via $$
(prefix) or ##
(postfix), for
example:
button <- find "button"
click $$ button -- equivalent to: within' button click
button ## click -- equivalent to: within' button click
These operators are necessary, because operations can't be applied as
functions (e.g. click button
), since they don't take a node as parameter.
Re-exports from Elmish.Test.Bootstrap
#testElement Source
testElement :: forall m a. MonadAff m => ReactElement -> ReaderT TestState m a -> m a
A convenience version of testComponent
for "pure" components - i.e.
components that consist only of view
, no init
or update
.
#testComponent Source
testComponent :: forall m a msg state. MonadAff m => ComponentDef msg state -> ReaderT TestState m a -> m a
Mount the given component to a DOM element, run the given computation in the context of that element, return the computation's result.
Example:
describe "My component" $
it "should work" $
testComponent { init, view, update } do
find "h1" >> text >>= shouldEqual "Hello"
Re-exports from Elmish.Test.Combinators
#within Source
within :: forall m a. Testable m => String -> m a -> m a
Finds an element by the given selector and runs the given computation in the context of that element.
Example:
describe "My component" $
it "should work" $
testComponent { init, view, update } do
within "section:nth-child(1)" do
find "h2" >> text >>= shouldEqual "First Section"
clickOn "button"
within "section:nth-child(2)" do
find "h2" >> text >>= shouldEqual "Second Section"
find "input[type=text]" >> change "Some Text"
#chainM Source
chainM :: forall m a. Testable m => m Element -> m a -> m a
Used in its operator form >>
, this function chains two DOM operations
together, taking the output of the first operation and making it context of
the second one. For example:
buttonInsideDiv <- find "div" >> find "button"
inputValue <- find "input" >> attr "value"
Re-exports from Elmish.Test.Events
#fireEvent Source
fireEvent :: forall m r. Testable m => CanPassToJavaScript (Record r) => String -> Record r -> m Unit
Simulates a React-friendly event with the given name and given args, firing it on the current-context element. The second argument is a record of values that gets merged into the event object.
For example:
find "button" >> fireEvent "click" {}
find "input" >> fireEvent "change" { target: { value: "some text" } }
find "input" >> fireEvent "keyDown" { key: "Enter", keyCode: 13, which: 13 }
This function uses the Simulate
function from react-dom/test-utils
. See
https://reactjs.org/docs/test-utils.html#simulate
If the arguments record contains a field target
, which in turn contains a
field value
, the value of that field is assigned to the value
property
of the current-context element before firing the event. This special case
is intended to support events targeted at input fields, such as change
or
input
, where the event handler usually tries to access the
event.target.value
field.
Re-exports from Elmish.Test.Query
Re-exports from Elmish.Test.SpinWait
#waitWhile' Source
waitWhile' :: forall m. Testable m => Milliseconds -> m Boolean -> m Unit
Performs active wait while the given condition is true. Times out with a crash after given time period.
#waitUntil' Source
waitUntil' :: forall m. Testable m => Milliseconds -> m Boolean -> m Unit
Performs active wait while the given condition is true. Times out with a crash after given time period.