diff --git a/elm-package.json b/elm-package.json deleted file mode 100644 index f20a6c1..0000000 --- a/elm-package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": "1.0.7", - "summary": "Run elm-test tests in a browser.", - "repository": "https://github.com/elm-community/html-test-runner.git", - "license": "BSD-3-Clause", - "source-directories": [ - "src" - ], - "exposed-modules": [ - "Test.Runner.Html" - ], - "dependencies": { - "elm-community/elm-test": "4.1.0 <= v < 5.0.0", - "elm-lang/core": "5.0.0 <= v < 6.0.0", - "elm-lang/html": "2.0.0 <= v < 3.0.0", - "mdgriffith/style-elements": "4.2.1 <= v < 5.0.0", - "mgold/elm-random-pcg": "4.0.2 <= v < 6.0.0" - }, - "elm-version": "0.18.0 <= v < 0.19.0" -} diff --git a/elm.json b/elm.json new file mode 100644 index 0000000..aaf1d6d --- /dev/null +++ b/elm.json @@ -0,0 +1,21 @@ +{ + "type": "package", + "name": "elm-community/html-test-runner", + "summary": "Run elm-test tests in a browser.", + "license": "BSD-3-Clause", + "version": "1.0.7", + "exposed-modules": [ + "Test.Runner.Html" + ], + "elm-version": "0.19.0 <= v < 0.20.0", + "dependencies": { + "elm-explorations/test": "1.0.0 <= v < 2.0.0", + "elm-lang/browser": "1.0.0 <= v < 2.0.0", + "elm-lang/core": "6.0.0 <= v < 7.0.0", + "elm-lang/html": "3.0.0 <= v < 4.0.0", + "elm-lang/random": "1.0.0 <= v < 2.0.0", + "elm-lang/time": "1.0.0 <= v < 2.0.0", + "mdgriffith/style-elements": "5.0.1 <= v < 6.0.0" + }, + "test-dependencies": {} +} \ No newline at end of file diff --git a/example/Main.elm b/example/Main.elm index 0856622..db4eda3 100644 --- a/example/Main.elm +++ b/example/Main.elm @@ -2,7 +2,7 @@ module HtmlRunnerExample exposing (..) {-| HOW TO RUN THIS EXAMPLE -1. Run elm-reactor from the same directory as your tests' elm-package.json. (For example, if you have tests/elm-package.json, then cd into tests/ and +1. Run elm-reactor from the same directory as your tests' elm.json. (For example, if you have tests/elm.json, then cd into tests/ and run elm-reactor.) 2. Visit and bring up this file. @@ -39,7 +39,7 @@ testWithoutNums = describe "withoutNums" [ fuzzWith { runs = 100 } (tuple3 ( string, float, string )) "adding numbers to strings has no effect" <| \( prefix, num, suffix ) -> - withoutNums (prefix ++ toString num ++ suffix) + withoutNums (prefix ++ String.fromFloat num ++ suffix) |> Expect.equal (withoutNums (prefix ++ suffix)) ] @@ -50,7 +50,7 @@ testExpectations = [ test "this should succeed" <| \() -> "blah" - |> Expect.equal " blah" + |> Expect.equal "blah" , test "this should fail" <| \() -> "something" diff --git a/src/Test/Runner/Exploration.elm b/src/Test/Runner/Exploration.elm index e7950eb..ca9538a 100644 --- a/src/Test/Runner/Exploration.elm +++ b/src/Test/Runner/Exploration.elm @@ -7,12 +7,58 @@ module Test.Runner.Exploration , formatFailure , fromTest , step + , toString ) import Expect -import Random.Pcg +import Random import Test import Test.Runner +import Test.Runner.Failure + + +toString : Runner -> String +toString (Runner internals) = + let + keyValue key value = + " " ++ key ++ ": " ++ value + in + String.join "\n" + [ keyValue "passed" (String.fromInt internals.passed) + , keyValue "todos" ("\n [ " ++ String.join " \n" (List.map failureToString internals.todos) ++ "]") + , keyValue "failures" ("\n [ " ++ String.join " \n" (List.map failureToString internals.failures) ++ "]") + , keyValue "incomplete" + (case internals.incomplete of + Nothing -> + "Nothing" + + Just reason -> + "Just " + ++ (case reason of + Skip -> + "Skip" + + Only -> + "Only" + + Custom label -> + "Custom " ++ label + ) + ) + , keyValue "queue" + (case internals.queue of + [] -> + "[]" + + q -> + String.join " " (List.map (.labels >> String.join ", ") q) + ) + ] + + +failureToString : Failure -> String +failureToString (Failure labels messages) = + "failure: " ++ String.join ", " labels ++ ", reasons: " ++ String.join ", " (List.map .description messages) type Runner @@ -48,10 +94,17 @@ type Reason type Failure - = Failure (List String) (List { given : Maybe String, message : String }) + = Failure + (List String) + (List + { given : Maybe String + , description : String + , reason : Test.Runner.Failure.Reason + } + ) -fromTest : Int -> Random.Pcg.Seed -> Test.Test -> Runner +fromTest : Int -> Random.Seed -> Test.Test -> Runner fromTest runs seed test = let new queue incomplete = @@ -81,33 +134,38 @@ formatFailure : (String -> a) -> (String -> a) -> Failure - -> ( List a, List { given : Maybe String, message : String } ) + -> + ( List a + , List + { given : Maybe String + , description : String + , reason : Test.Runner.Failure.Reason + } + ) formatFailure formatFirst formatLast (Failure labels errors) = ( Test.Runner.formatLabels formatFirst formatLast labels, errors ) step : Runner -> Status step (Runner internals) = - case - ( internals.incomplete - , internals.todos - , internals.failures - , internals.queue - ) - of - ( Nothing, [], [], [] ) -> - Pass internals.passed + case ( internals.failures, internals.queue ) of + ( [], [] ) -> + case internals.incomplete of + Nothing -> + case internals.todos of + [] -> + Pass internals.passed - ( Nothing, todos, [], [] ) -> - Todo internals.passed todos + todos -> + Todo internals.passed todos - ( Just reason, _, [], [] ) -> - Incomplete internals.passed reason + Just incompleteReason -> + Incomplete internals.passed incompleteReason - ( _, _, failures, [] ) -> + ( failures, [] ) -> Fail internals.passed failures - ( _, _, _, next :: queue ) -> + ( _, next :: queue ) -> next.run () |> fromExpectation { internals | queue = queue } next.labels @@ -119,14 +177,14 @@ fromExpectation internals labels expectations = List.foldr partition ( [], [] ) expectations partition e = - case ( Test.Runner.isTodo e, Test.Runner.getFailure e ) of - ( True, Just result ) -> - Tuple.mapFirst ((::) result) - - ( False, Just result ) -> - Tuple.mapSecond ((::) result) - - ( _, Nothing ) -> + case Test.Runner.getFailureReason e of + Just reason -> + if Test.Runner.isTodo e then + Tuple.mapFirst ((::) reason) + else + Tuple.mapSecond ((::) reason) + + Nothing -> identity in if not <| List.isEmpty failures then diff --git a/src/Test/Runner/Html.elm b/src/Test/Runner/Html.elm index 7c2e980..4d58a34 100644 --- a/src/Test/Runner/Html.elm +++ b/src/Test/Runner/Html.elm @@ -9,8 +9,8 @@ one of these tests in elm-reactor to have it run and show outputs. -} -import Html -import Random.Pcg as Random +import Browser +import Random import Test exposing (Test) import Test.Runner.Html.App as App import Test.Runner.Html.View as View @@ -20,7 +20,7 @@ import View as View {-| A program which will run tests and report their results. -} type alias TestProgram = - Program Never App.Model App.Msg + Program () App.Model App.Msg {-| Run the test and report the results. @@ -39,8 +39,8 @@ run = -} runWithOptions : Maybe Int -> Maybe Random.Seed -> Test -> TestProgram runWithOptions runs seed test = - Html.program - { init = App.init (Maybe.withDefault 100 runs) seed test + Browser.embed + { init = always (App.init (Maybe.withDefault 100 runs) seed test) , update = App.update , view = App.present >> View.view , subscriptions = \_ -> Sub.none diff --git a/src/Test/Runner/Html/App.elm b/src/Test/Runner/Html/App.elm index ff443da..f5618b8 100644 --- a/src/Test/Runner/Html/App.elm +++ b/src/Test/Runner/Html/App.elm @@ -9,21 +9,21 @@ module Test.Runner.Html.App import Expect exposing (Expectation) import Process -import Random.Pcg as Random +import Random import Task import Test exposing (Test) import Test.Runner.Exploration as Runner import Test.Runner.Html.View as View -import Time exposing (Time) +import Time type Model = NotStarted (Maybe Random.Seed) Int Test - | Started Time Time Runner.Status + | Started Time.Posix Time.Posix Runner.Status type Msg - = Dispatch Time + = Dispatch Time.Posix dispatch : Cmd Msg @@ -48,7 +48,7 @@ update : Msg -> Model -> ( Model, Cmd Msg ) update (Dispatch now) model = case model of NotStarted Nothing runs test -> - ( floor now + ( Time.toMillis Time.utc now |> Random.initialSeed |> start runs test |> Started now now @@ -78,4 +78,11 @@ present model = Nothing Started startTime now status -> - Just ( now - startTime, status ) + let + diff = + Time.toMillis Time.utc now - Time.toMillis Time.utc startTime + in + Just + ( Time.millisToPosix diff + , status + ) diff --git a/src/Test/Runner/Html/View.elm b/src/Test/Runner/Html/View.elm index e10a77e..b34a3a1 100644 --- a/src/Test/Runner/Html/View.elm +++ b/src/Test/Runner/Html/View.elm @@ -1,8 +1,8 @@ module Test.Runner.Html.View exposing (..) import Test.Runner.Exploration as Runner -import Time exposing (Time) +import Time type alias Model = - Maybe ( Time, Runner.Status ) + Maybe ( Time.Posix, Runner.Status ) diff --git a/src/View.elm b/src/View.elm index 414c7f4..490c32b 100644 --- a/src/View.elm +++ b/src/View.elm @@ -1,6 +1,5 @@ module View exposing (view) -import Color exposing (Color, rgb) import Element exposing (..) import Element.Attributes exposing (..) import Html exposing (Html) @@ -10,8 +9,9 @@ import Style.Border as Border import Style.Color as Color import Style.Font as Font import Test.Runner.Exploration as Runner +import Test.Runner.Failure as Failure import Test.Runner.Html.View as View -import Time exposing (Time) +import Time view : View.Model -> Html a @@ -36,31 +36,35 @@ type Palette | Warning +rgb255 r g b = + Style.rgb (r / 255) (g / 255) (b / 255) + + color : Palette -> Color color palette = case palette of Primary -> - rgb 41 60 75 + rgb255 41 60 75 Secondary -> -- gray color on elm blog is rgb 221 221 221 but it doesn't meet -- accessibility standards for contrast http://webaim.org/resources/contrastchecker/ - rgb 84 84 84 + rgb255 84 84 84 Accent -> - rgb 96 181 204 + rgb255 96 181 204 Background -> - rgb 255 255 255 + rgb255 255 255 255 Good -> - rgb 0 100 0 + rgb255 0 100 0 Bad -> - rgb 179 0 0 + rgb255 179 0 0 Warning -> - rgb 122 67 0 + rgb255 122 67 0 withColor : @@ -173,12 +177,12 @@ running completed remaining = column None [] [ header (Header Primary) [ paddingBottom 24 ] (text "Running Tests...") - , row None [] [ text (toString completed ++ " completed") ] - , row None [] [ text (toString remaining ++ " remaining") ] + , row None [] [ text (String.fromInt completed ++ " completed") ] + , row None [] [ text (String.fromInt remaining ++ " remaining") ] ] -finished : Time -> Int -> List a -> ( Palette, String ) -> Element Styles variation msg +finished : Time.Posix -> Int -> List a -> ( Palette, String ) -> Element Styles variation msg finished duration passed failures ( headlineColor, headlineText ) = column None [] @@ -189,8 +193,8 @@ finished duration passed failures ( headlineColor, headlineText ) = [ spacing 10 ] [ [ bold "Duration", bold "Passed", bold "Failed" ] , [ text (formattedDuration duration) - , text (toString passed) - , text (toString (List.length failures)) + , text (String.fromInt passed) + , text (String.fromInt (List.length failures)) ] ] ] @@ -225,19 +229,17 @@ oneFailure failure = (coloredLabel '✗' Bad) failure - inContext { given, message } = + inContext ({ given } as reason) = column None [ spacing 10 ] [ wrappedRow None [] [ whenJust given givenCode ] - , wrappedRow None [] [ code None message ] + , paragraph None [ width (px 800) ] [ code None (viewReason reason) ] ] in el None - [ inlineStyle - [ ( "display", "list-item" ) - , ( "margin", "10px" ) - , ( "padding", "10px" ) - ] + [ inlineStyle "display" "list-item" + , inlineStyle "margin" "10px" + , inlineStyle "padding" "10px" ] <| column None @@ -245,6 +247,44 @@ oneFailure failure = (labels ++ [ spacer 3 ] ++ List.map inContext expectations) +viewReason : { a | reason : Failure.Reason, description : String } -> String +viewReason { reason, description } = + case reason of + Failure.Custom -> + description + + Failure.Equality one two -> + description ++ " " ++ one ++ " " ++ two + + Failure.Comparison one two -> + description ++ " " ++ one ++ " " ++ two + + Failure.ListDiff expected actual -> + "expected\n" + ++ String.join " \n" expected + ++ "actual\n" + ++ String.join " \n" actual + + Failure.CollectionDiff { expected, actual, extra, missing } -> + String.join "\n" + [ formatKeyValue "expected" expected + , formatKeyValue "actual" actual + , formatKeyValue "extra" (String.join ", " extra) + , formatKeyValue "missing" (String.join ", " missing) + ] + + Failure.TODO -> + description + + Failure.Invalid _ -> + description + + +formatKeyValue : String -> String -> String +formatKeyValue key val = + key ++ ": " ++ val + + givenCode : String -> Element Styles variations msg givenCode value = code None ("Given " ++ value) @@ -257,18 +297,16 @@ coloredLabel char textColor str = [ text (String.cons char (String.cons ' ' str)) ] -formattedDuration : Time -> String +formattedDuration : Time.Posix -> String formattedDuration time = - toString time ++ " ms" + String.fromInt (Time.toMillis Time.utc time) ++ " ms" code : style -> String -> Element style variations msg code style str = node "pre" <| el style - [ inlineStyle - [ ( "white-space", "pre-wrap" ) - , ( "font-family", "monospace" ) - ] + [ inlineStyle "white-space" "pre-wrap" + , inlineStyle "font-family" "monospace" ] (text str) diff --git a/tests/Fixtures.elm b/tests/Fixtures.elm index 36e4439..ac6ed2f 100644 --- a/tests/Fixtures.elm +++ b/tests/Fixtures.elm @@ -49,4 +49,4 @@ twoTests () = noTestsDescription : String noTestsDescription = - "This `describe \"nothing\"` has no tests in it. Let's give it some!" + "This `describe nothing` has no tests in it. Let's give it some!" diff --git a/tests/TestRunnerExplorationTest.elm b/tests/TestRunnerExplorationTest.elm index 3164117..5e2525c 100644 --- a/tests/TestRunnerExplorationTest.elm +++ b/tests/TestRunnerExplorationTest.elm @@ -2,9 +2,10 @@ module TestRunnerExplorationTest exposing (suite) import Expect import Fixtures -import Random.Pcg as Random +import Random import Test exposing (..) import Test.Runner.Exploration as Runner exposing (Runner) +import Test.Runner.Failure suite : Test @@ -20,7 +21,8 @@ suite = , failures = [ ( [] , [ { given = Nothing - , message = Fixtures.noTestsDescription + , reason = Test.Runner.Failure.Invalid Test.Runner.Failure.EmptyList + , description = Fixtures.noTestsDescription } ] ) @@ -54,7 +56,8 @@ suite = , failures = [ ( [ "both", "two" ] , [ { given = Nothing - , message = "message" + , reason = Test.Runner.Failure.Custom + , description = "message" } ] ) @@ -70,7 +73,8 @@ suite = , failures = [ ( [ "todo then failing", "done" ] , [ { given = Nothing - , message = "just cause" + , reason = Test.Runner.Failure.Custom + , description = "just cause" } ] ) @@ -86,7 +90,8 @@ suite = , failures = [ ( [ "todo then passing" ] , [ { given = Nothing - , message = "haven't done this yet" + , reason = Test.Runner.Failure.TODO + , description = "haven't done this yet" } ] ) @@ -149,8 +154,9 @@ expect : List ( List String , List - { given : Maybe String - , message : String + { description : String + , given : Maybe String + , reason : Test.Runner.Failure.Reason } ) } @@ -163,8 +169,8 @@ expect final runner = expectFinal passed failures = Expect.all - [ .passed >> flip Expect.equal passed - , .failures >> flip Expect.equal (format failures) + [ \subject -> Expect.equal subject.passed passed + , \subject -> Expect.equal subject.failures (format failures) ] final in @@ -195,7 +201,47 @@ expect final runner = _ -> Expect.fail <| - "Given: " - ++ toString runner - ++ "\nExpected: " - ++ toString final + "Given:\n" + ++ Runner.toString runner + ++ "\n\nExpected:\n" + ++ finalToString final + + +finalToString final = + let + keyValue key value = + " " ++ key ++ ": " ++ value + + failureToString ( labels, messages ) = + String.join ", " labels ++ "->" ++ String.join ", " (List.map .description messages) + in + String.join "\n" + [ keyValue "steps" (String.fromInt final.steps) + , keyValue "passed" (String.fromInt final.passed) + , keyValue "status" (statusToString final.status) + , keyValue "failures" ("\n " ++ String.join " \n" (List.map failureToString final.failures)) + ] + + +statusToString status = + case status of + Running -> + "Running" + + Fail -> + "Fail" + + Todo -> + "Todo" + + Pass -> + "Pass" + + IncompleteSkip -> + "Incomplete skip" + + IncompleteOnly -> + "Incomplete only" + + IncompleteCustom -> + "Incomplete custom" diff --git a/tests/elm-package.json b/tests/elm-package.json deleted file mode 100644 index 09daa91..0000000 --- a/tests/elm-package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "1.0.3", - "summary": "Run elm-test tests in a browser.", - "repository": "https://github.com/elm-community/html-test-runner.git", - "license": "BSD-3-Clause", - "source-directories": [ - ".", - "../src" - ], - "exposed-modules": [], - "dependencies": { - "elm-community/elm-test": "4.1.0 <= v < 5.0.0", - "elm-lang/core": "5.0.0 <= v < 6.0.0", - "elm-lang/html": "2.0.0 <= v < 3.0.0", - "mgold/elm-random-pcg": "4.0.2 <= v < 6.0.0", - "mdgriffith/style-elements": "4.2.1 <= v < 5.0.0" - }, - "elm-version": "0.18.0 <= v < 0.19.0" -} diff --git a/tests/elm.json b/tests/elm.json new file mode 100644 index 0000000..d8a5f63 --- /dev/null +++ b/tests/elm.json @@ -0,0 +1,30 @@ +{ + "type": "application", + "source-directories": [ + "../src", + "." + ], + "elm-version": "0.19.0", + "dependencies": { + "elm-explorations/test": "1.0.0", + "elm-lang/browser": "1.0.0", + "elm-lang/core": "6.0.0", + "elm-lang/html": "3.0.0", + "elm-lang/json": "1.0.0", + "elm-lang/random": "1.0.0", + "elm-lang/time": "1.0.0", + "mdgriffith/style-elements": "5.0.1" + }, + "test-dependencies": {}, + "do-not-edit-this-by-hand": { + "transitive-dependencies": { + "Skinney/murmur3": "2.0.7", + "eeue56/elm-lazy": "1.0.1", + "eeue56/elm-lazy-list": "2.0.0", + "eeue56/elm-shrink": "2.0.0", + "elm-lang/regex": "1.0.0", + "elm-lang/url": "1.0.0", + "elm-lang/virtual-dom": "3.0.0" + } + } +} \ No newline at end of file