-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate to JSDOM #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import { GlobalRegistrator } from '@happy-dom/global-registrator'; | ||
import registerDom from 'global-jsdom' | ||
|
||
export const ensureDom_ = () => { | ||
if (typeof window === "undefined") { | ||
GlobalRegistrator.register() | ||
registerDom() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
module Elmish.Test.Discover | ||
( childAt | ||
, children | ||
, find | ||
, findAll | ||
, findFirst | ||
, findNth | ||
) | ||
where | ||
|
||
import Prelude | ||
|
||
import Data.Array (fold, length, mapMaybe, (!!)) | ||
import Data.Maybe (Maybe(..)) | ||
import Effect.Class (liftEffect) | ||
import Elmish.Test.State (class Testable, crash, currentNode) | ||
import Web.DOM (Element) | ||
import Web.DOM.Element as DOM | ||
import Web.DOM.Node (childNodes) | ||
import Web.DOM.NodeList as NodeList | ||
import Web.DOM.ParentNode (QuerySelector(..), querySelectorAll) | ||
|
||
-- | Finds exactly one element by CSS selector. If the selector matches zero | ||
-- | elements or more than one, this function will throw an exception. | ||
-- | | ||
-- | find "button" >> click | ||
-- | | ||
find :: ∀ m. Testable m => String -> m Element | ||
find selector = | ||
findAll selector >>= case _ of | ||
[el] -> pure el | ||
els -> crash $ "Expected to find one element matching '" <> selector <> "', but found " <> show (length els) | ||
|
||
-- | Finds the first element out of possibly many matching the given selector. | ||
-- | If there are no elements matching the selector, throws an exception. | ||
findFirst :: ∀ m. Testable m => String -> m Element | ||
findFirst = findNth 0 | ||
|
||
|
||
-- | Finds the n-th (zero-based) element out of possibly many matching the given | ||
-- | selector. If there are no elements matching the selector, throws an | ||
-- | exception. | ||
findNth :: ∀ m. Testable m => Int -> String -> m Element | ||
findNth idx selector = | ||
findAll selector >>= \all -> case all !! idx of | ||
Just el -> pure el | ||
Nothing -> crash $ fold | ||
[ "Expected to find " | ||
, show idx | ||
, "th element matching '" | ||
, selector | ||
, "', but there are only " | ||
, show (length all) | ||
, " elements" | ||
] | ||
|
||
-- | Finds zero or more elements by CSS selector. | ||
-- | | ||
-- | findAll "button" >>= traverse_ \b -> click $$ b | ||
-- | | ||
-- | divs <- find "div" | ||
-- | length divs `shouldEqual` 10 | ||
-- | | ||
findAll :: ∀ m. Testable m => String -> m (Array Element) | ||
findAll selector = do | ||
current <- currentNode | ||
liftEffect $ | ||
querySelectorAll (QuerySelector selector) (DOM.toParentNode current) | ||
>>= NodeList.toArray | ||
<#> mapMaybe DOM.fromNode | ||
|
||
-- | Returns all immediate child elements of the current-context element. | ||
-- | | ||
-- | find "div" >> children >>= traverse_ \child -> | ||
-- | tag <- tagName | ||
-- | when (tag == "BUTTON") | ||
-- | click | ||
-- | | ||
children :: ∀ m. Testable m => m (Array Element) | ||
children = do | ||
current <- currentNode | ||
liftEffect $ | ||
childNodes (DOM.toNode current) | ||
>>= NodeList.toArray | ||
<#> mapMaybe DOM.fromNode | ||
|
||
-- | Within the current-context element, finds a child element at the given | ||
-- | index. Crashes if the is no child with the given index. | ||
childAt :: ∀ m. Testable m => Int -> m Element | ||
childAt idx = do | ||
cs <- children | ||
case cs !! idx of | ||
Just e -> | ||
pure e | ||
Nothing -> | ||
crash $ fold | ||
[ "Expected to find a child element at index " | ||
, show idx | ||
, ", but there are only " | ||
, show (length cs) | ||
, " children" | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,6 @@ newtype DomProp (a :: Type) = DomProp String | |
|
||
value = DomProp "value" :: DomProp String | ||
|
||
href = DomProp "href" :: DomProp String | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Turns out this is a not a property. Only accessible via |
||
|
||
disabled = DomProp "disabled" :: DomProp Boolean | ||
|
||
checked = DomProp "checked" :: DomProp Boolean |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,2 @@ | ||
export const innerText_ = e => e.innerText || "" | ||
export const outerHTML_ = e => e.outerHTML || "" | ||
export const prop_ = (name, e) => e[name] || null // converting `undefined` to `null` so it can be handled via `Nullable` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,7 @@ | ||
module Elmish.Test.Query | ||
( attr | ||
, childAt | ||
, children | ||
, count | ||
, exists | ||
, find | ||
, findAll | ||
, findFirst | ||
, findNth | ||
, html | ||
, prop | ||
, tagName | ||
|
@@ -16,108 +11,31 @@ module Elmish.Test.Query | |
|
||
import Prelude | ||
|
||
import Data.Array (fold, length, mapMaybe, null, (!!)) | ||
import Data.Maybe (Maybe(..), fromMaybe) | ||
import Data.Array (length, null) | ||
import Data.Maybe (fromMaybe) | ||
import Data.Nullable as N | ||
import Effect.Class (liftEffect) | ||
import Effect.Uncurried (EffectFn1, EffectFn2, runEffectFn1, runEffectFn2) | ||
import Elmish.Test.Discover (findAll) | ||
import Elmish.Test.DomProps (class DomPropType, DomProp, defaultValue) | ||
import Elmish.Test.State (class Testable, crash, currentNode) | ||
import Elmish.Test.State (class Testable, currentNode) | ||
import Web.DOM (Element) | ||
import Web.DOM.Element as DOM | ||
import Web.DOM.Node (childNodes) | ||
import Web.DOM.NodeList as NodeList | ||
import Web.DOM.ParentNode (QuerySelector(..), querySelectorAll) | ||
|
||
-- | Finds exactly one element by CSS selector. If the selector matches zero | ||
-- | elements or more than one, this function will throw an exception. | ||
-- | | ||
-- | find "button" >> click | ||
-- | | ||
find :: ∀ m. Testable m => String -> m Element | ||
find selector = | ||
findAll selector >>= case _ of | ||
[el] -> pure el | ||
els -> crash $ "Expected to find one element matching '" <> selector <> "', but found " <> show (length els) | ||
|
||
-- | Finds the first element out of possibly many matching the given selector. | ||
-- | If there are no elements matching the selector, throws an exception. | ||
findFirst :: ∀ m. Testable m => String -> m Element | ||
findFirst = findNth 0 | ||
|
||
|
||
-- | Finds the n-th (zero-based) element out of possibly many matching the given | ||
-- | selector. If there are no elements matching the selector, throws an | ||
-- | exception. | ||
findNth :: ∀ m. Testable m => Int -> String -> m Element | ||
findNth idx selector = | ||
findAll selector >>= \all -> case all !! idx of | ||
Just el -> pure el | ||
Nothing -> crash $ fold | ||
[ "Expected to find " | ||
, show idx | ||
, "th element matching '" | ||
, selector | ||
, "', but there are only " | ||
, show (length all) | ||
, " elements" | ||
] | ||
|
||
-- | Finds zero or more elements by CSS selector. | ||
-- | | ||
-- | findAll "button" >>= traverse_ \b -> click $$ b | ||
-- | | ||
-- | divs <- find "div" | ||
-- | length divs `shouldEqual` 10 | ||
-- | | ||
findAll :: ∀ m. Testable m => String -> m (Array Element) | ||
findAll selector = do | ||
current <- currentNode | ||
liftEffect $ | ||
querySelectorAll (QuerySelector selector) (DOM.toParentNode current) | ||
>>= NodeList.toArray | ||
<#> mapMaybe DOM.fromNode | ||
|
||
-- | Returns all immediate child elements of the current-context element. | ||
-- | | ||
-- | find "div" >> children >>= traverse_ \child -> | ||
-- | tag <- tagName | ||
-- | when (tag == "BUTTON") | ||
-- | click | ||
-- | | ||
children :: ∀ m. Testable m => m (Array Element) | ||
children = do | ||
current <- currentNode | ||
liftEffect $ | ||
childNodes (DOM.toNode current) | ||
>>= NodeList.toArray | ||
<#> mapMaybe DOM.fromNode | ||
|
||
-- | Within the current-context element, finds a child element at the given | ||
-- | index. Crashes if the is no child with the given index. | ||
childAt :: ∀ m. Testable m => Int -> m Element | ||
childAt idx = do | ||
cs <- children | ||
case cs !! idx of | ||
Just e -> | ||
pure e | ||
Nothing -> | ||
crash $ fold | ||
[ "Expected to find a child element at index " | ||
, show idx | ||
, ", but there are only " | ||
, show (length cs) | ||
, " children" | ||
] | ||
import Web.DOM.Node (textContent) | ||
|
||
-- | Returns `true` if at least one element exists matching the given CSS | ||
-- | selector. | ||
exists :: ∀ m. Testable m => String -> m Boolean | ||
exists selector = not null <$> findAll selector | ||
|
||
-- | Returns the number of elements within the current context that match the | ||
-- | given selector. | ||
count :: ∀ m. Testable m => String -> m Int | ||
count selector = length <$> findAll selector | ||
Comment on lines
+33
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Turns out we're utilizing this in our codebase a lot, I had to convert all those places to |
||
|
||
-- | Returns full inner text of the current-context element. | ||
text :: ∀ m. Testable m => m String | ||
text = currentNode >>= (liftEffect <<< runEffectFn1 innerText_) | ||
text = currentNode >>= \el -> liftEffect $ textContent (DOM.toNode el) | ||
|
||
-- | Returns HTML representation of the current-context element. | ||
html :: ∀ m. Testable m => m String | ||
|
@@ -141,8 +59,6 @@ prop :: ∀ m a. Testable m => DomPropType a => DomProp a -> m a | |
prop name = currentNode >>= \e -> liftEffect $ | ||
runEffectFn2 prop_ name e <#> N.toMaybe <#> fromMaybe defaultValue | ||
|
||
foreign import innerText_ :: EffectFn1 Element String | ||
|
||
foreign import outerHTML_ :: EffectFn1 Element String | ||
|
||
foreign import prop_ :: ∀ a. EffectFn2 (DomProp a) Element (N.Nullable a) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I broke up the
Query
module intoQuery
(reading properties of elements) andDiscover
(manipulating/traversing the DOM tree). Initially it was necessary for my implementation ofinnerText
to avoid cyclic imports. I changed the implementation since then, but decided to leave the separation.