diff --git a/Readme.md b/Readme.md index 09c4cb3..45dcc56 100644 --- a/Readme.md +++ b/Readme.md @@ -71,7 +71,7 @@ view : Model -> Html.Html Msg view model = div [ onClick Set ] [ text model ] -specs : Node +specs : Node msg specs = describe "Example" [ it "clicking on the div should change the text" @@ -87,6 +87,7 @@ main = , update = update , view = view , init = init + , initCmd = Cmd.none } specs ``` diff --git a/source/Spec.elm b/source/Spec.elm index f7d2d0b..e33a013 100644 --- a/source/Spec.elm +++ b/source/Spec.elm @@ -52,8 +52,10 @@ module Spec exposing @docs run, runWithProgram -} import Spec.Assertions exposing (pass, fail, error) -import Spec.Runner exposing (Prog, State, Msg) +import Spec.Runner exposing (Prog, State) +import Spec.Messages exposing (Msg) import Spec.Types exposing (..) +import Spec.CoreTypes exposing (..) import Spec.Native import Task exposing (Task) @@ -66,20 +68,20 @@ type alias Step = {-| Representation of a test. -} -type alias Test = - Spec.Types.Test +type alias Test msg = + Spec.Types.Test msg {-| The outcome of an assertion or step. -} type alias Outcome - = Spec.Types.Outcome + = Spec.CoreTypes.Outcome {-| Representation of a test tree (Node). -} -type alias Node = - Spec.Types.Node +type alias Node msg = + Spec.Types.Node msg flip = @@ -94,21 +96,21 @@ flip = ] ] -} -group : String -> List Node -> Node +group : String -> List (Node msg) -> Node msg group name nodes = GroupNode { name = name, nodes = nodes } {-| Alias for `group`. -} -context : String -> List Node -> Node +context : String -> List (Node msg) -> Node msg context = group {-| Alias for `group`. -} -describe : String -> List Node -> Node +describe : String -> List (Node msg) -> Node msg describe = group @@ -117,7 +119,7 @@ describe = test "description" -} -test : String -> List Assertion -> Node +test : String -> List Assertion -> Node msg test name steps = TestNode { steps = steps @@ -127,34 +129,35 @@ test name steps = , name = name , path = [] , id = -1 + , initCmd = Nothing } {-| Alias for `it`. -} -it : String -> List Assertion -> Node +it : String -> List Assertion -> Node msg it = test {-|-} -before : List Assertion -> Node +before : List Assertion -> Node msg before = Before {-|-} -layout : List (String, Rect) -> Node +layout : List (String, Rect) -> Node msg layout = Layout {-|-} -after : List Assertion -> Node +after : List Assertion -> Node msg after = After {-|-} -http : List Request -> Node +http : List Request -> Node msg http = Http @@ -303,13 +306,13 @@ steps = {-| Runs the given tests without an app / component. -} -run : Node -> Program Never (State String msg) (Msg msg) +run : Node msg -> Program Never (State String msg) (Msg msg) run = Spec.Runner.run {-| Runs the given tests with the given app / component. -} -runWithProgram : Prog model msg -> Node -> Program Never (State model msg) (Msg msg) +runWithProgram : Prog model msg -> Node msg -> Program Never (State model msg) (Msg msg) runWithProgram = Spec.Runner.runWithProgram diff --git a/source/Spec/Assertions.elm b/source/Spec/Assertions.elm index ca050cd..db8b122 100644 --- a/source/Spec/Assertions.elm +++ b/source/Spec/Assertions.elm @@ -8,11 +8,12 @@ module Spec.Assertions exposing (..) import Spec.Types exposing (..) import Task exposing (Task) +import Spec.CoreTypes exposing (Outcome(..)) {-| The outcome of an assertion or step. -} type alias Outcome - = Spec.Types.Outcome + = Spec.CoreTypes.Outcome {-| Creates a failed outcome with the given message. -} diff --git a/source/Spec/CoreTypes.elm b/source/Spec/CoreTypes.elm new file mode 100644 index 0000000..e7fffed --- /dev/null +++ b/source/Spec/CoreTypes.elm @@ -0,0 +1,12 @@ +module Spec.CoreTypes exposing (..) + + +{-| Represents an outcome for a step: + * Error - if there was an error during the step (element not found for example) + * Fail - represents failure + * Pass - represents success +-} +type Outcome + = Error String + | Fail String + | Pass String diff --git a/source/Spec/Messages.elm b/source/Spec/Messages.elm new file mode 100644 index 0000000..08e8c7c --- /dev/null +++ b/source/Spec/Messages.elm @@ -0,0 +1,10 @@ +module Spec.Messages exposing (..) + +import Spec.CoreTypes exposing (..) + +{-| Messages for a test program. +-} +type Msg msg + = Next (Maybe Outcome) + | NoOp () + | App msg diff --git a/source/Spec/Reporter.elm b/source/Spec/Reporter.elm index 51485e8..9445c02 100644 --- a/source/Spec/Reporter.elm +++ b/source/Spec/Reporter.elm @@ -6,6 +6,8 @@ module Spec.Reporter exposing (render) -} import Spec.Styles as Styles exposing (stylesheet) import Spec.Types exposing (..) +import Spec.CoreTypes exposing (Outcome) +import Spec.CoreTypes exposing (Outcome(..)) import Json.Encode @@ -48,7 +50,7 @@ renderOutcome outcome = {-| Renders a test. -} -renderTest : Test -> Html.Html msg +renderTest : Test msg -> Html.Html msg renderTest model = let requests = @@ -89,7 +91,7 @@ renderTest model = {-| Renders the test results. -} -render : List Test -> Html.Html msg +render : List (Test msg) -> Html.Html msg render tests = let styles = diff --git a/source/Spec/Runner.elm b/source/Spec/Runner.elm index c4b3d3d..9468578 100644 --- a/source/Spec/Runner.elm +++ b/source/Spec/Runner.elm @@ -4,7 +4,9 @@ module Spec.Runner exposing (..) @docs run, runWithProgram -} -import Spec.Types exposing (Outcome(..), Assertion, Test, Node) +import Spec.Types exposing (Assertion, Test, Node) +import Spec.CoreTypes exposing (Outcome(..)) +import Spec.Messages exposing (Msg(..)) import Spec.Reporter import Json.Encode as Json @@ -19,22 +21,13 @@ import Html type alias State model msg = { update : msg -> model -> ( model, Cmd msg ) , view : model -> Html.Html msg - , finishedTests : List Test + , finishedTests : List (Test msg) , appInit : () -> model - , tests : List Test + , tests : List (Test msg) , counter : Int , app : model } - -{-| Messages for a test program. --} -type Msg msg - = Next (Maybe Outcome) - | NoOp () - | App msg - - {-| Representation of an app. -} type alias Prog model msg = @@ -42,6 +35,7 @@ type alias Prog model msg = , subscriptions : model -> Sub msg , view : model -> Html.Html msg , init : () -> model + , initCmd : Cmd msg } @@ -79,41 +73,55 @@ update msg model = Nothing -> test in - case test.steps of - -- Take the nex step - step :: remainingSteps -> - let - _ = - Native.Spec.mockHttpRequests test - - _ = - Native.Spec.setLayout test.layout - - -- Remove that step from the test - testWithoutStep = - { updatedTest | steps = remainingSteps } - - -- Create a task from that step - stepTask = - Native.Spec.raf - |> Task.andThen (\_ -> step) - |> Task.perform (Next << Just) - in - -- Execute - ( { model | tests = testWithoutStep :: remainingTests } - , stepTask - ) - - -- If there is no other steps go for the next test - [] -> - ( { model - | finishedTests = model.finishedTests ++ [updatedTest] - , counter = model.counter + 1 - , app = model.appInit () - , tests = remainingTests - } - , perform (Next Nothing) - ) + case test.initCmd of + Just cmd -> + -- if there is app init Cmd to run, run it + let + _ = + Native.Spec.mockHttpRequests test + + _ = + Native.Spec.setLayout test.layout + + testWithoutCmd = { test | initCmd = Nothing } + in + ({ model | tests = testWithoutCmd :: remainingTests }, Cmd.batch [ cmd, perform (Next Nothing) ] ) + Nothing -> + case test.steps of + -- Take the next step + step :: remainingSteps -> + let + _ = + Native.Spec.mockHttpRequests test + + _ = + Native.Spec.setLayout test.layout + + -- Remove that step from the test + testWithoutStep = + { updatedTest | steps = remainingSteps } + + -- Create a task from that step + stepTask = + Native.Spec.raf + |> Task.andThen (\_ -> step) + |> Task.perform (Next << Just) + in + -- Execute + ( { model | tests = testWithoutStep :: remainingTests } + , stepTask + ) + + -- If there is no other steps go for the next test + [] -> + ( { model + | finishedTests = model.finishedTests ++ [updatedTest] + , counter = model.counter + 1 + , app = model.appInit () + , tests = remainingTests + } + , perform (Next Nothing) + ) -- When everything is finished report [] -> @@ -125,13 +133,14 @@ update msg model = view : State model msg -> Html.Html (Msg msg) view model = let + app : (String, Html.Html (Msg msg)) app = ( toString model.counter, Html.map App (model.view model.app) ) nodes = if List.isEmpty model.tests then [ app - , ( "report", Spec.Reporter.render model.finishedTests ) + , ( "report", Html.map App (Spec.Reporter.render model.finishedTests) ) ] else [ app ] @@ -142,38 +151,41 @@ view model = {-| Runs the given tests without an app / component. -} -run : Node -> Program Never (State String msg) (Msg msg) +run : Node msg -> Program Never (State String msg) (Msg msg) run tests = runWithProgram { update = (\_ _ -> ( "", Cmd.none )) , subscriptions = (\_ -> Sub.none) , view = (\_ -> Html.text "") , init = (\_ -> "") + , initCmd = Cmd.none } tests {-| Runs the given tests with the given app / component. -} -runWithProgram : Prog model msg -> Node -> Program Never (State model msg) (Msg msg) +runWithProgram : Prog model msg -> Node msg -> Program Never (State model msg) (Msg msg) runWithProgram data tests = let - processedTests = + processedTests : Cmd (Msg msg) -> List (Test msg) + processedTests initCmd = tests |> Spec.Types.flatten [] - |> List.indexedMap (\index item -> { item | id = index }) + |> List.indexedMap (\index item -> { item | id = index, initCmd = Just initCmd }) - testToRun = + testToRun : Cmd (Msg msg) -> List (Test msg) + testToRun initCmd = case Native.Spec.getTestId () of - Just id -> List.filter (.id >> ((==) id)) processedTests - Nothing -> processedTests + Just id -> List.filter (.id >> ((==) id)) (processedTests initCmd) + Nothing -> processedTests initCmd in Html.program { subscriptions = (\model -> Sub.map App (data.subscriptions model.app)) , update = update , view = view , init = - ( { tests = testToRun + ( { tests = testToRun (Cmd.map App data.initCmd) , update = data.update , appInit = data.init , finishedTests = [] @@ -188,7 +200,7 @@ runWithProgram data tests = {-| Sends the report to the CLI when running tests in a terminal. -} -report : List Test -> Cmd (Msg msg) +report : List (Test msg) -> Cmd (Msg msg) report tests = let mockedRequests test = diff --git a/source/Spec/Types.elm b/source/Spec/Types.elm index 867ae25..a0cda10 100644 --- a/source/Spec/Types.elm +++ b/source/Spec/Types.elm @@ -3,13 +3,16 @@ module Spec.Types exposing (..) {-| This module contains the types for specs. -} import Task exposing (Task) +import Spec.CoreTypes exposing (..) +import Spec.Messages exposing (Msg) {-| Representation of a test. -} -type alias Test = +type alias Test msg = { layout : List (String, Rect) , requests : List Request , results : List Outcome + , initCmd : Maybe (Cmd (Msg msg)) , steps : List Assertion , path : List String , name : String @@ -40,19 +43,19 @@ type alias Rect = {-| Representation of a test tree (Node). -} -type Node +type Node msg = Layout (List (String, Rect)) | Before (List Assertion) | After (List Assertion) | Http (List Request) - | GroupNode Group - | TestNode Test + | GroupNode (Group msg) + | TestNode (Test msg) {-| Representation of a test group. -} -type alias Group = - { nodes : List Node +type alias Group msg = + { nodes : List (Node msg) , name : String } @@ -68,17 +71,6 @@ type alias Assertion type alias Step = Assertion -{-| Represents an outcome for a step: - * Error - if there was an error during the step (element not found for example) - * Fail - represents failure - * Pass - represents success --} -type Outcome - = Error String - | Fail String - | Pass String - - {-| Text data for assertions. -} type alias TextData = @@ -121,7 +113,7 @@ outcomeToString outcome = {-| Turns a tree into a flat list of tests. -} -flatten : List Test -> Node -> List Test +flatten : List (Test msg) -> Node msg -> List (Test msg) flatten tests node = case node of -- There branches are processed in the group below diff --git a/spec/AssertionSpec.elm b/spec/AssertionSpec.elm index 89b1da1..aa9b966 100644 --- a/spec/AssertionSpec.elm +++ b/spec/AssertionSpec.elm @@ -43,7 +43,7 @@ view model = ] -specs : Node +specs : Node msg specs = describe "Spec.Assertions" [ describe ".containsText" @@ -166,6 +166,7 @@ specs = main = runWithProgram { init = init + , initCmd = Cmd.none , update = update , view = view , subscriptions = \_ -> Sub.none diff --git a/spec/CssPropertiesSpec.elm b/spec/CssPropertiesSpec.elm index 642cc59..9741077 100644 --- a/spec/CssPropertiesSpec.elm +++ b/spec/CssPropertiesSpec.elm @@ -83,7 +83,7 @@ view : Model -> Html.Html Msg view model = div [] [ node "style" [] [ text style ] ] -specs : Node +specs : Node msg specs = let map key = @@ -103,4 +103,5 @@ main = , update = update , view = view , init = init + , initCmd = Cmd.none } specs diff --git a/spec/ExampleSpec.elm b/spec/ExampleSpec.elm index 4031ec5..92355ac 100644 --- a/spec/ExampleSpec.elm +++ b/spec/ExampleSpec.elm @@ -23,7 +23,7 @@ view : Model -> Html.Html Msg view model = div [ onClick Set ] [ text model ] -specs : Node +specs : Node msg specs = describe "Example" [ it "clicking on the div should change the text" @@ -39,4 +39,5 @@ main = , update = update , view = view , init = init + , initCmd = Cmd.none } specs diff --git a/spec/HttpSpec.elm b/spec/HttpSpec.elm index 2d1e2b1..0c7a6c1 100644 --- a/spec/HttpSpec.elm +++ b/spec/HttpSpec.elm @@ -77,4 +77,5 @@ main = , update = update , view = view , init = init + , initCmd = Cmd.none } tests diff --git a/spec/InitHttpSpec.elm b/spec/InitHttpSpec.elm new file mode 100644 index 0000000..7664284 --- /dev/null +++ b/spec/InitHttpSpec.elm @@ -0,0 +1,74 @@ +import Spec exposing (..) +import Spec.Expect as Expect + +import Html.Events exposing (onClick, on, keyCode) +import Html.Attributes exposing (class, attribute) +import Html exposing (..) + +import Http + +import Json.Encode as JE +import Json.Decode as JD + +import Task exposing (Task) + +type alias Model = String + +type Msg + = Request + | Loaded (Result Http.Error String) + + +init : () -> Model +init _ = + "" + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Request -> + ( model, load ) + + Loaded (Err e) -> + ( model, Cmd.none ) + + Loaded (Ok result) -> + ( result, Cmd.none ) + + +load : Cmd Msg +load = + Http.get "/test" JD.string |> Http.send Loaded + + +view : Model -> Html.Html Msg +view model = + node "test" [] + [ div [ class "value"] [ text model ] + ] + + +specs : Node msg +specs = + describe "Spec.Steps" + [ http + [ { method = "GET" + , url = "/test" + , response = { status = 200, body = "\"new-value\"" } + } + ] + , describe ".setValue" + [ it "should set value of element on init" + [ assert.containsText { text = "new-value", selector = ".value" } + ] + ] + ] + +main = + runWithProgram + { init = init + , initCmd = load + , update = update + , view = view + , subscriptions = \_ -> Sub.none + } specs diff --git a/spec/InitSpec.elm b/spec/InitSpec.elm new file mode 100644 index 0000000..9e56572 --- /dev/null +++ b/spec/InitSpec.elm @@ -0,0 +1,61 @@ +import Spec exposing (..) +import Spec.Expect as Expect + +import Html.Events exposing (onClick, on, keyCode) +import Html.Attributes exposing (class, attribute) +import Html exposing (..) + +import Json.Encode as JE +import Json.Decode as JD + +import Task exposing (Task) + +type alias Model = String + +type Msg + = SetValue String + + +init : () -> Model +init _ = + "" + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + SetValue value -> + ( value, Cmd.none ) + + +view : Model -> Html.Html Msg +view model = + node "test" [] + [ div [ class "value"] [ text model ] + ] + + +specs : Node msg +specs = + describe "Spec.Steps" + [ describe ".setValue" + [ it "should set value of element on init" + [ assert.containsText { text = "new-value", selector = ".value" } + ] + ] + ] + +main = + runWithProgram + { init = init + , update = update + , view = view + , subscriptions = \_ -> Sub.none + , initCmd = fire (SetValue "new-value") + } specs + + +fire : msg -> Cmd msg +fire msg = + Task.perform identity (Task.succeed msg) + diff --git a/spec/LayoutSpec.elm b/spec/LayoutSpec.elm index c5eb032..da5022c 100644 --- a/spec/LayoutSpec.elm +++ b/spec/LayoutSpec.elm @@ -50,7 +50,7 @@ view model = , node "test2" [] [] ] -specs : Node +specs : Node msg specs = describe "Layout mocking" [ context "getBoundingClientRect" @@ -103,6 +103,7 @@ specs = main = runWithProgram { init = init + , initCmd = Cmd.none , update = update , view = view , subscriptions = \_ -> Sub.none diff --git a/spec/StepsSpec.elm b/spec/StepsSpec.elm index 43ac983..b9ff762 100644 --- a/spec/StepsSpec.elm +++ b/spec/StepsSpec.elm @@ -40,7 +40,7 @@ view model = ] -specs : Node +specs : Node msg specs = describe "Spec.Steps" [ before [ assert.elementPresent "body" ] @@ -91,6 +91,7 @@ specs = main = runWithProgram { init = init + , initCmd = Cmd.none , update = update , view = view , subscriptions = \_ -> Sub.none