aboutsummaryrefslogtreecommitdiff
path: root/specs/Specs/AppStateSpec.hs
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2024-12-25 22:49:41 +0530
committerAkshay Nair <phenax5@gmail.com>2024-12-25 23:48:32 +0530
commitd9b2256047669b5a5dbac4baec7140f18a5b6eff (patch)
treeb904df2f8b7c41481a2e8f30659474c97e998444 /specs/Specs/AppStateSpec.hs
parent83e2570d3c8da9920d66a00c4bdf5650fe1b3336 (diff)
downloadchelleport-d9b2256047669b5a5dbac4baec7140f18a5b6eff.tar.gz
chelleport-d9b2256047669b5a5dbac4baec7140f18a5b6eff.zip
Refactor state update + test fixes
Diffstat (limited to 'specs/Specs/AppStateSpec.hs')
-rw-r--r--specs/Specs/AppStateSpec.hs276
1 files changed, 276 insertions, 0 deletions
diff --git a/specs/Specs/AppStateSpec.hs b/specs/Specs/AppStateSpec.hs
new file mode 100644
index 0000000..eb5ab1b
--- /dev/null
+++ b/specs/Specs/AppStateSpec.hs
@@ -0,0 +1,276 @@
+module Specs.AppStateSpec where
+
+import Chelleport.AppState (initialState, update)
+import Chelleport.Types
+import Chelleport.Utils (uniq)
+import Control.Monad (join)
+import qualified SDL
+import Test.Hspec
+import TestUtils
+
+test :: SpecWith ()
+test = do
+ describe "#initialState" $ do
+ it "returns the initial state of the app" $ do
+ ((initState, _), _) <- runWithMocks initialState
+ stateKeySequence initState `shouldBe` []
+ stateIsMatched initState `shouldBe` False
+ stateIsShiftPressed initState `shouldBe` False
+
+ it "returns grid with 16x9 key sequences" $ do
+ ((initState, _), _) <- runWithMocks initialState
+ length (stateGrid initState) `shouldBe` 9
+ stateGrid initState `shouldSatisfy` all ((== 16) . length)
+ stateGrid initState `shouldSatisfy` all (all ((== 2) . length))
+
+ it "returns grid with all unique key sequences" $ do
+ ((initState, _), _) <- runWithMocks initialState
+ join (stateGrid initState) `shouldBe` uniq (join $ stateGrid initState)
+
+ describe "#update" $ do
+ let defaultState = defaultAppState {stateGrid = [["ABC", "DEF"], ["DJK", "JKL"]]}
+
+ context "with action ChainMouseClick" $ do
+ let currentState = defaultState
+
+ it "hides window, triggers mouse click and shows the window again" $ do
+ (_, mock) <- runWithMocks $ update currentState $ ChainMouseClick LeftClick
+ calls mock `shouldBe` [Mock_hideWindow, Mock_clickMouseButton LeftClick, Mock_showWindow]
+
+ it "continues with action ResetKeys without updating state" $ do
+ ((nextState, action), _) <- runWithMocks $ update currentState $ ChainMouseClick LeftClick
+ action `shouldBe` Just ResetKeys
+ nextState `shouldBe` currentState
+
+ context "when repetition is more than 1" $ do
+ let currentState = defaultState {stateRepetition = 3}
+
+ it "resets repetition back to 1" $ do
+ ((nextState, _), _) <- runWithMocks $ update currentState $ ChainMouseClick LeftClick
+ nextState `shouldBe` currentState {stateRepetition = 1}
+
+ it "clicks multiple times" $ do
+ (_, mock) <- runWithMocks $ update currentState $ ChainMouseClick LeftClick
+ calls mock
+ `shouldBe` [ Mock_hideWindow,
+ Mock_clickMouseButton LeftClick,
+ Mock_clickMouseButton LeftClick,
+ Mock_clickMouseButton LeftClick,
+ Mock_showWindow
+ ]
+
+ context "when repetition is 0" $ do
+ let currentState = defaultState {stateRepetition = 0}
+
+ it "clicks just once" $ do
+ (_, mock) <- runWithMocks $ update currentState $ ChainMouseClick LeftClick
+ calls mock `shouldBe` [Mock_hideWindow, Mock_clickMouseButton LeftClick, Mock_showWindow]
+
+ context "with action IncrementHighlightIndex" $ do
+ let currentState = defaultState
+
+ it "todo: implement" $ do
+ 1 `shouldBe` 1
+
+ context "with action IncrementMouseCursor" $ do
+ let currentState = defaultState
+
+ it "continues with MoveMousePosition" $ do
+ ((_, action), _) <- runWithMocks $ update currentState $ IncrementMouseCursor (10, -5)
+ action `shouldBe` Just (MoveMousePosition (52, 37))
+
+ it "does update state" $ do
+ ((state, _), _) <- runWithMocks $ update currentState $ IncrementMouseCursor (10, -5)
+ state `shouldBe` currentState
+
+ context "when repetition is more than 1" $ do
+ let currentState = defaultState {stateRepetition = 5}
+
+ it "multiplies increment" $ do
+ ((_, action), _) <- runWithMocks $ update currentState $ IncrementMouseCursor (10, -5)
+ action `shouldBe` Just (MoveMousePosition (92, 17))
+
+ context "when repetition is 0" $ do
+ let currentState = defaultState {stateRepetition = 0}
+
+ it "increments just once" $ do
+ ((_, action), _) <- runWithMocks $ update currentState $ IncrementMouseCursor (10, -5)
+ action `shouldBe` Just (MoveMousePosition (52, 37))
+
+ context "with action MouseDragEnd" $ do
+ let currentState = defaultState
+
+ it "hides window, stops dragging and shows the window again" $ do
+ (_, mock) <- runWithMocks $ update currentState MouseDragEnd
+ calls mock `shouldContain` [Mock_hideWindow, Mock_releaseMouseButton, Mock_showWindow]
+
+ it "does not continue or update state" $ do
+ (result, _) <- runWithMocks $ update currentState MouseDragStart
+ result `shouldBe` (currentState, Nothing)
+
+ context "with action MouseDragStart" $ do
+ let currentState = defaultState
+
+ it "hides window, starts dragging and shows the window again" $ do
+ (_, mock) <- runWithMocks $ update currentState MouseDragStart
+ calls mock `shouldContain` [Mock_hideWindow, Mock_pressMouseButton, Mock_showWindow]
+
+ it "does not continue or update state" $ do
+ (result, _) <- runWithMocks $ update currentState MouseDragStart
+ result `shouldBe` (currentState, Nothing)
+
+ context "with action MouseDragToggle" $ do
+ context "when is dragging is true" $ do
+ let currentState = defaultState {stateIsDragging = True}
+
+ it "toggles dragging state" $ do
+ ((state, _), _) <- runWithMocks $ update currentState MouseDragToggle
+ state `shouldBe` state {stateIsDragging = False}
+
+ it "continues with action MouseDragEnd" $ do
+ ((_, action), _) <- runWithMocks $ update currentState MouseDragToggle
+ action `shouldBe` Just MouseDragEnd
+
+ context "when is dragging is false" $ do
+ let currentState = defaultState {stateIsDragging = False}
+
+ it "toggles dragging state" $ do
+ ((state, _), _) <- runWithMocks $ update currentState MouseDragToggle
+ state `shouldBe` state {stateIsDragging = True}
+
+ it "continues with action MouseDragStart" $ do
+ ((_, action), _) <- runWithMocks $ update currentState MouseDragToggle
+ action `shouldBe` Just MouseDragStart
+
+ context "with action HandleKeyInput" $ do
+ context "when mode is ModeSearch" $ do
+ it "todo: implement" $ do
+ 1 `shouldBe` 1
+
+ context "when mode is ModeHints" $ do
+ context "when there are no matches" $ do
+ let currentState = defaultState {stateKeySequence = "D", stateMode = defaultHintsMode}
+
+ context "when input key sequence has matching values in grid" $ do
+ it "does not update" $ do
+ ((nextState, action), _) <- runWithMocks $ update currentState $ HandleKeyInput SDL.KeycodeZ
+ action `shouldBe` Nothing
+ nextState `shouldBe` currentState
+
+ context "when input key sequence does not have matching values in grid" $ do
+ it "adds key to key sequence" $ do
+ ((nextState, action), _) <- runWithMocks $ update currentState $ HandleKeyInput SDL.KeycodeE
+ action `shouldBe` Nothing
+ nextState `shouldBe` currentState {stateKeySequence = "DE"}
+
+ context "when there is a matches" $ do
+ let currentState = defaultState {stateKeySequence = "DE", stateMode = defaultHintsMode}
+
+ context "when input key sequence does not have matching values in grid" $ do
+ it "adds key to key sequence and enables isMatched" $ do
+ ((nextState, _), _) <- runWithMocks $ update currentState $ HandleKeyInput SDL.KeycodeF
+ nextState `shouldBe` currentState {stateKeySequence = "DEF", stateIsMatched = True}
+
+ it "continues with MoveMousePosition action at center of matched cell" $ do
+ ((_, action), _) <- runWithMocks $ update currentState $ HandleKeyInput SDL.KeycodeF
+ action `shouldBe` Just (MoveMousePosition (1640, 370))
+
+ context "with action MoveMousePosition" $ do
+ let currentState = defaultState
+
+ it "moves mouse pointer to the given coordinates" $ do
+ (_, mock) <- runWithMocks $ update currentState $ MoveMousePosition (23, 320)
+ mock `shouldHaveCalled` Mock_moveMousePointer 23 320
+
+ it "does not continue or update state" $ do
+ (result, _) <- runWithMocks $ update currentState $ MoveMousePosition (0, 0)
+ result `shouldBe` (currentState, Nothing)
+
+ context "with action ResetKeys" $ do
+ let currentState = defaultState {stateRepetition = 5}
+
+ it "resets state without any action" $ do
+ ((nextState, action), _) <- runWithMocks $ update currentState ResetKeys
+ action `shouldBe` Nothing
+ nextState `shouldBe` currentState {stateKeySequence = [], stateIsMatched = False, stateRepetition = 1}
+
+ context "with action SetMode" $ do
+ let currentState = defaultState
+
+ context "when mode is ModeHints" $ do
+ it "updates mode in state" $ do
+ ((nextState, action), _) <- runWithMocks $ update currentState $ SetMode defaultHintsMode
+ nextState `shouldBe` currentState {stateMode = defaultHintsMode}
+ action `shouldBe` Nothing
+
+ context "when mode is ModeSearch" $ do
+ it "captures screenshot for word search" $ do
+ ((_, _), mock) <- runWithMocks $ update currentState $ SetMode defaultSearchMode
+ mock `shouldHaveCalled` Mock_captureScreenshot (mockWindowOffsetX, mockWindowOffsetY) (mockWindowWidth, mockWindowHeight)
+
+ it "updates mode in state with ocr words" $ do
+ ((nextState, _), _) <- runWithMocks $ update currentState $ SetMode defaultSearchMode
+ let matchWord = OCRMatch {matchStartX = 40, matchStartY = 5, matchEndX = 100, matchEndY = 20, matchText = "Wow"}
+ nextState `shouldBe` currentState {stateMode = defaultSearchMode {searchWords = [matchWord], searchFilteredWords = [matchWord]}}
+
+ context "with action ShutdownApp" $ do
+ let currentState = defaultState
+
+ it "shuts down app" $ do
+ (_, mock) <- runWithMocks $ update currentState ShutdownApp
+ mock `shouldHaveCalled` Mock_shutdownApp
+
+ it "does not continue or update state" $ do
+ (result, _) <- runWithMocks $ update currentState ShutdownApp
+ result `shouldBe` (currentState, Nothing)
+
+ context "with action TriggerMouseClick" $ do
+ let currentState = defaultState
+
+ it "hides window and triggers mouse click" $ do
+ (_, mock) <- runWithMocks $ update currentState $ TriggerMouseClick LeftClick
+ calls mock `shouldContain` [Mock_hideWindow, Mock_clickMouseButton LeftClick]
+
+ it "continues with action ShutdownApp without updating state" $ do
+ ((nextState, action), _) <- runWithMocks $ update currentState $ TriggerMouseClick LeftClick
+ action `shouldBe` Just ShutdownApp
+ nextState `shouldBe` currentState
+
+ context "when repetition is more than 1" $ do
+ let currentState = defaultState {stateRepetition = 3}
+
+ it "resets repetition back to 1" $ do
+ ((nextState, _), _) <- runWithMocks $ update currentState $ TriggerMouseClick LeftClick
+ nextState `shouldBe` currentState {stateRepetition = 1}
+
+ it "clicks multiple times" $ do
+ (_, mock) <- runWithMocks $ update currentState $ TriggerMouseClick LeftClick
+ calls mock
+ `shouldBe` [ Mock_hideWindow,
+ Mock_clickMouseButton LeftClick,
+ Mock_clickMouseButton LeftClick,
+ Mock_clickMouseButton LeftClick
+ ]
+
+ context "when repetition is 0" $ do
+ let currentState = defaultState {stateRepetition = 0}
+
+ it "clicks just once" $ do
+ (_, mock) <- runWithMocks $ update currentState $ TriggerMouseClick LeftClick
+ calls mock `shouldBe` [Mock_hideWindow, Mock_clickMouseButton LeftClick]
+
+ context "with action UpdateRepetition" $ do
+ let currentState = defaultState
+
+ it "updates shift state without any action" $ do
+ ((nextState, action), _) <- runWithMocks $ update currentState $ UpdateRepetition 7
+ action `shouldBe` Nothing
+ nextState `shouldBe` currentState {stateRepetition = 7}
+
+ context "with action UpdateShiftState" $ do
+ let currentState = defaultState
+
+ it "updates shift state without any action" $ do
+ ((nextState, action), _) <- runWithMocks $ update currentState $ UpdateShiftState True
+ action `shouldBe` Nothing
+ nextState `shouldBe` currentState {stateIsShiftPressed = True}