Skip to content

Commit 238d040

Browse files
committed
Working on tests
1 parent f785a53 commit 238d040

File tree

7 files changed

+138
-32
lines changed

7 files changed

+138
-32
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"main": "index.js",
66
"scripts": {
77
"build": "spago build",
8-
"docs": "rimraf ./output/React.Halo.* && spago docs --format markdown"
8+
"docs": "rimraf ./output/React.Halo.* && spago docs --format markdown",
9+
"test": "spago test --config ./spago.test.dhall"
910
},
1011
"author": "",
1112
"license": "ISC",

spago.dhall

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ You can edit this file as you like.
66
, license = "BSD-3-Clause"
77
, repository = "https://github.com/robertdp/purescript-react-halo.git"
88
, dependencies =
9-
[ "aff"
10-
, "free"
11-
, "freeap"
12-
, "react-basic-hooks"
13-
, "wire"
14-
]
9+
[ "aff", "free", "freeap", "react-basic-hooks", "wire" ]
1510
, packages = ./packages.dhall
1611
, sources = [ "src/**/*.purs" ]
1712
}

spago.test.dhall

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
let conf = ./spago.dhall
2+
3+
let dependencies = [ "console", "spec" ]
4+
5+
let sources = [ "test/**/*.purs" ]
6+
7+
in conf //
8+
{ dependencies = conf.dependencies # dependencies
9+
, sources = conf.sources # sources
10+
}

src/React/Halo/Component.purs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,9 @@ useHalo ::
3333
useHalo { props, initialState, eval } =
3434
React.coerceHook React.do
3535
state /\ setState <- React.useState' initialState
36-
halo <- React.useMemo unit \_ -> unsafePerformEffect (createInitialState initialState eval setState props)
37-
React.useEffectOnce do
38-
runInitialize halo props
39-
pure do
40-
runFinalize halo
41-
React.useEffectAlways do
42-
handleUpdate halo props
43-
mempty
36+
halo <- React.useMemo unit \_ -> unsafePerformEffect (createInitialState { props, initialState, eval, update: setState })
37+
React.useEffectOnce (runInitialize halo *> pure (runFinalize halo))
38+
React.useEffectAlways (handleUpdate halo props *> mempty)
4439
pure (state /\ handleAction halo)
4540

4641
type ComponentSpec props state action m

src/React/Halo/Internal/Eval.purs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ evalHaloF hs@(HaloState s) = case _ of
3939
Tuple a state'
4040
| not unsafeRefEq state state' -> do
4141
Ref.write state' s.state
42-
s.render state'
42+
s.update state'
4343
pure a
4444
| otherwise -> pure a
4545
Subscribe sub k ->
4646
liftEffect do
4747
sid <- State.fresh SubscriptionId hs
48-
unlessM (Ref.read s.unmounted) do
48+
unlessM (Ref.read s.finalized) do
4949
canceller <- Event.subscribe (sub sid) (handleAction hs)
5050
Ref.modify_ (Map.insert sid canceller) s.subscriptions
5151
pure (k sid)
@@ -111,9 +111,9 @@ makeEval f = case _ of
111111
runAff :: Aff Unit -> Effect Unit
112112
runAff = Aff.runAff_ (either throwError pure)
113113

114-
runInitialize :: forall props state action. HaloState props action state -> props -> Effect Unit
115-
runInitialize hs@(HaloState s) props = do
116-
Ref.write props s.props
114+
runInitialize :: forall props state action. HaloState props action state -> Effect Unit
115+
runInitialize hs@(HaloState s) = do
116+
props <- Ref.read s.props
117117
runAff $ evalHaloM hs $ s.eval $ Initialize props
118118

119119
handleUpdate :: forall props state action. HaloState props action state -> props -> Effect Unit
@@ -125,12 +125,12 @@ handleUpdate hs@(HaloState s) props = do
125125

126126
handleAction :: forall props state action. HaloState props state action -> action -> Effect Unit
127127
handleAction hs@(HaloState s) action = do
128-
unlessM (Ref.read s.unmounted) do
128+
unlessM (Ref.read s.finalized) do
129129
runAff $ evalHaloM hs $ s.eval $ Action action
130130

131131
runFinalize :: forall props state action. HaloState props state action -> Effect Unit
132132
runFinalize hs@(HaloState s) = do
133-
Ref.write true s.unmounted
133+
Ref.write true s.finalized
134134
subscriptions <- Ref.modify' (\s' -> { state: Map.empty, value: s' }) s.subscriptions
135135
sequence_ (Map.values subscriptions)
136136
forks <- Ref.modify' (\s' -> { state: Map.empty, value: s' }) s.forks

src/React/Halo/Internal/State.purs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import React.Halo.Internal.Types (ForkId, Lifecycle, SubscriptionId)
1414
newtype HaloState props state action
1515
= HaloState
1616
{ eval :: Lifecycle props action -> HaloM props state action Aff Unit
17-
, render :: state -> Effect Unit
18-
, unmounted :: Ref Boolean
17+
, update :: state -> Effect Unit
18+
, finalized :: Ref Boolean
1919
, props :: Ref props
2020
, state :: Ref state
2121
, fresh :: Ref Int
@@ -26,17 +26,20 @@ newtype HaloState props state action
2626
-- | Creates a starting `HaloState`, ready for initialization.
2727
createInitialState ::
2828
forall props state action.
29-
state ->
30-
(Lifecycle props action -> HaloM props state action Aff Unit) ->
31-
(state -> Effect Unit) -> props -> Effect (HaloState props state action)
32-
createInitialState initialState eval render props' = do
33-
unmounted <- Ref.new false
29+
{ props :: props
30+
, initialState :: state
31+
, eval :: Lifecycle props action -> HaloM props state action Aff Unit
32+
, update :: state -> Effect Unit
33+
} ->
34+
Effect (HaloState props state action)
35+
createInitialState spec@{ eval, update } = do
36+
finalized <- Ref.new false
3437
fresh' <- Ref.new 0
35-
props <- Ref.new props'
36-
state <- Ref.new initialState
38+
props <- Ref.new spec.props
39+
state <- Ref.new spec.initialState
3740
subscriptions <- Ref.new Map.empty
3841
forks <- Ref.new Map.empty
39-
pure $ HaloState { eval, render, unmounted, props, state, fresh: fresh', subscriptions, forks }
42+
pure $ HaloState { eval, update, finalized, props, state, fresh: fresh', subscriptions, forks }
4043

4144
-- | Issue a new identifier, unique to this component.
4245
fresh :: forall props state action a. (Int -> a) -> HaloState props state action -> Effect a

test/Main.purs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
module Test.Main where
2+
3+
import Prelude
4+
import Effect (Effect)
5+
import Effect.Aff (launchAff_)
6+
import Effect.Class (liftEffect)
7+
import Effect.Ref as Ref
8+
import React.Halo (Lifecycle(..), modify_)
9+
import React.Halo.Internal.Eval as Eval
10+
import React.Halo.Internal.State as State
11+
import Test.Spec (Spec, describe, it)
12+
import Test.Spec.Assertions (shouldEqual)
13+
import Test.Spec.Reporter (consoleReporter)
14+
import Test.Spec.Runner (runSpec)
15+
16+
main :: Effect Unit
17+
main =
18+
launchAff_ do
19+
runSpec [ consoleReporter ] do
20+
describe "purescript-react-halo" do
21+
describe "Props" runPropsTests
22+
describe "State" runStateTests
23+
describe "Subscriptions" runSubscriptionTests
24+
describe "Parallelism" runParallelismTests
25+
describe "Forking" runForkingTests
26+
27+
runPropsTests :: Spec Unit
28+
runPropsTests = do
29+
describe "Update" do
30+
it "does not fire in initialization" do
31+
{ expect } <- makeUpdateState
32+
expect 0
33+
it "does not fire when props are referentially equal" do
34+
{ state, initialProps, expect } <- makeUpdateState
35+
liftEffect $ Eval.handleUpdate state initialProps
36+
expect 0
37+
it "does fire when props are not referentially equal" do
38+
{ state, expect } <- makeUpdateState
39+
liftEffect $ Eval.handleUpdate state { value: "new object" }
40+
expect 1
41+
where
42+
makeUpdateState =
43+
liftEffect do
44+
count <- Ref.new 0
45+
let
46+
eval = case _ of
47+
Update _ _ -> liftEffect $ Ref.modify_ (add 1) count
48+
_ -> pure unit
49+
50+
initialProps = { value: "" }
51+
52+
expect x = liftEffect (Ref.read count) >>= shouldEqual x
53+
state <- State.createInitialState { props: initialProps, initialState: unit, eval, update: mempty }
54+
Eval.runInitialize state
55+
pure { state, initialProps, expect }
56+
57+
runStateTests :: Spec Unit
58+
runStateTests = do
59+
it "correctly modifies the state" do
60+
{ modify, expect, read } <- makeState { value: "" }
61+
modify \s -> s { value = "first" }
62+
modify \s -> s { value = s.value <> " test" }
63+
value <- read
64+
value `shouldEqual` { value: "first test" }
65+
expect 2
66+
it "does not modify the state when the reference does not change" do
67+
{ modify, expect, read } <- makeState { value: "" }
68+
modify identity
69+
value <- read
70+
value `shouldEqual` { value: "" }
71+
expect 0
72+
where
73+
makeState initialState =
74+
liftEffect do
75+
count <- Ref.new 0
76+
value <- Ref.new initialState
77+
let
78+
update state = do
79+
Ref.write state value
80+
Ref.modify_ (add 1) count
81+
82+
read = liftEffect $ Ref.read value
83+
84+
expect x = liftEffect (Ref.read count) >>= shouldEqual x
85+
86+
eval = case _ of
87+
Action f -> modify_ f
88+
_ -> pure unit
89+
state <- State.createInitialState { props: unit, initialState, eval, update }
90+
Eval.runInitialize state
91+
let
92+
modify = liftEffect <<< Eval.handleAction state
93+
pure { expect, modify, state, read }
94+
95+
runSubscriptionTests :: Spec Unit
96+
runSubscriptionTests = pure unit
97+
98+
runParallelismTests :: Spec Unit
99+
runParallelismTests = pure unit
100+
101+
runForkingTests :: Spec Unit
102+
runForkingTests = pure unit

0 commit comments

Comments
 (0)