Revise tests

Link to fuzz test docs and Fuzzer docs

Move part9 to be part12

Update part11

Update 12, and some other Mains

Rearrange things, drop 2 modules

Add a new part12

Fix READMEs

Move some things up a directory

Update part11

Use ! []

Update parts7-9

Fix part12g

Swap part11 and part12

Fix readmes for part11 and part12

Add HtmlRunner to part8

Update part8 and part9 READMEs

rm part10/test
This commit is contained in:
Richard Feldman
2016-06-26 01:00:27 -07:00
parent 00e641d186
commit b557ee0842
45 changed files with 481 additions and 883 deletions

View File

@@ -1,14 +1,15 @@
module ElmHub exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.App
import Http
import Auth
import Task exposing (Task)
import Json.Decode exposing (Decoder)
import Dict exposing (Dict)
import SearchResult
import SearchResult exposing (ResultId)
searchFeed : String -> Cmd Msg
@@ -26,13 +27,13 @@ searchFeed query =
responseDecoder : Decoder (List SearchResult.Model)
responseDecoder =
-- TODO make use of SearchResult's decoder
Json.Decode.succeed []
Json.Decode.at [ "items" ] (Json.Decode.list SearchResult.decoder)
type alias Model =
{ query : String
, results : Dict SearchResult.ResultId SearchResult.Model
, results : Dict ResultId SearchResult.Model
, errorMessage : Maybe String
}
@@ -40,6 +41,7 @@ initialModel : Model
initialModel =
{ query = "tutorial"
, results = Dict.empty
, errorMessage = Nothing
}
@@ -52,23 +54,42 @@ view model =
]
, input [ class "search-query", onInput SetQuery, defaultValue model.query ] []
, button [ class "search-button", onClick Search ] [ text "Search" ]
, viewErrorMessage model.errorMessage
, ul [ class "results" ] (viewSearchResults model.results)
]
viewSearchResults : Dict SearchResult.ResultId SearchResult.Model -> List (Html a)
viewErrorMessage : Maybe String -> Html a
viewErrorMessage errorMessage =
case errorMessage of
Just message ->
div [ class "error" ] [ text message ]
Nothing ->
text ""
viewSearchResults : Dict ResultId SearchResult.Model -> List (Html Msg)
viewSearchResults results =
results
|> Dict.values
|> List.sortBy (.stars >> negate)
|> List.map (\_ -> div [] [ text "TODO replace this line with view logic from SearchResult" ])
|> List.map viewSearchResult
viewSearchResult : SearchResult.Model -> Html Msg
viewSearchResult result =
-- TODO call SearchResult.view to render a search result.
--
-- Hint: Use Html.App.map and UpdateSearchResult to translate from
-- SearchResult.Msg into the Msg type we have defined in this module.
div [] []
type Msg
= Search
| SetQuery String
| DeleteById SearchResult.ResultId
| SetResults (List SearchResult.Model)
| UpdateSearchResult ResultId SearchResult.Msg
| HandleSearchResponse (List SearchResult.Model)
| HandleSearchError Http.Error
@@ -77,24 +98,43 @@ update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search ->
( model, searchFeed model.query )
model ! [ searchFeed model.query ]
SetQuery query ->
( { model | query = query }, Cmd.none )
{ model | query = query, errorMessage = Nothing } ! []
SetResults results ->
HandleSearchError error ->
case error of
Http.UnexpectedPayload str ->
{ model | errorMessage = Just str } ! []
_ ->
{ model | errorMessage = Just "Error loading search results" } ! []
HandleSearchResponse results ->
let
resultsById : Dict SearchResult.ResultId SearchResult.Model
resultsById : Dict ResultId SearchResult.Model
resultsById =
results
|> List.map (\result -> ( result.id, result ))
|> Dict.fromList
in
( { model | results = resultsById }, Cmd.none )
{ model | results = resultsById } ! []
DeleteById id ->
let
newModel =
{ model | results = Dict.remove id model.results }
in
( newModel, Cmd.none )
UpdateSearchResult id childMsg ->
case Dict.get id model.results of
Nothing ->
model ! []
Just childModel ->
let
( newChildModel, childCmd ) =
SearchResult.update childMsg childModel
cmd =
Cmd.map (UpdateSearchResult id) childCmd
newResults =
Dict.insert id newChildModel model.results
in
{ model | results = newResults } ! [ cmd ]

View File

@@ -1,6 +1,7 @@
module Main exposing (..)
import ElmHub exposing (..)
import Html.App
main : Program Never
@@ -9,5 +10,5 @@ main =
{ view = view
, update = update
, init = ( initialModel, searchFeed initialModel.query )
, inputs = []
, subscriptions = \_ -> Sub.none
}

View File

@@ -18,3 +18,7 @@ to fail; in that case, just run `elm-package install` again.)
```bash
elm-live Main.elm --open --output=elm.js
```
## References
* [Elm Architecture Tutorial](https://github.com/evancz/elm-architecture-tutorial)

View File

@@ -2,19 +2,26 @@ module SearchResult exposing (..)
import Html exposing (..)
import Html.Attributes exposing (class, target, href, property, defaultValue)
import Html.Events exposing (..)
import Json.Decode exposing (Decoder)
import Json.Decode.Pipeline exposing (..)
type alias Model =
{ id : Int
, name : String
, stars : Int
, expanded : Bool
}
type alias ResultId =
Int
type alias Model =
{ id : ResultId
, name : String
, stars : Int
}
type Msg
= Expand
| Collapse
decoder : Decoder Model
@@ -23,19 +30,29 @@ decoder =
|> required "id" Json.Decode.int
|> required "full_name" Json.Decode.string
|> required "stargazers_count" Json.Decode.int
|> hardcoded True
view : Model -> Html a
view result =
li []
[ span [ class "star-count" ] [ text (toString result.stars) ]
, a
[ href ("https://github.com/" ++ result.name)
, target "_blank"
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
-- TODO implement Expand and Collapse logic
model ! []
view : Model -> Html Msg
view model =
if model.expanded then
li []
[ span [ class "star-count" ] [ text (toString model.stars) ]
, a [ href ("https://github.com/" ++ model.name), target "_blank" ]
[ text model.name ]
-- TODO send a Collapse message on click
, button [ class "hide-result" ]
[ text "X" ]
]
else
li []
-- TODO send an Expand message on click
[ button [ class "expand-result" ]
[ text "Show" ]
]
[ text result.name ]
, button
-- TODO onClick, send a delete action to the address
[ class "hide-result" ]
[ text "X" ]
]

View File

@@ -90,12 +90,3 @@ a:hover {
button:focus, input:focus {
outline: none;
}
.error {
background-color: #FF9632;
padding: 20px;
box-sizing: border-box;
overflow-x: auto;
font-family: monospace;
font-size: 18px;
}

View File

@@ -1,15 +0,0 @@
module Main where
import Signal exposing (Signal)
import ElmTest exposing (consoleRunner)
import Console exposing (IO, run)
import Task
import Tests
console : IO ()
console = consoleRunner Tests.all
port runner : Signal (Task.Task x ())
port runner = run console

View File

@@ -1,35 +0,0 @@
module Tests (..) where
import ElmTest exposing (..)
import ElmHub exposing (responseDecoder)
import Json.Decode exposing (decodeString)
all : Test
all =
suite
"Decoding responses from GitHub"
[ test "they can decode empty responses"
<| let
emptyResponse =
"""{ "items": [] }"""
in
assertEqual
(decodeString responseDecoder emptyResponse)
(Ok [])
, test "they can decode responses with results in them"
<| let
response =
"""{ "items": [
{ "id": 5, "full_name": "foo", "stargazers_count": 42 },
{ "id": 3, "full_name": "bar", "stargazers_count": 77 }
] }"""
in
assertEqual
(decodeString responseDecoder response)
(Ok
[ { id = 5, name = "foo", stars = 42 }
, { id = 3, name = "bar", stars = 77 }
]
)
]

View File

@@ -1,18 +0,0 @@
{
"version": "1.0.0",
"summary": "Like GitHub, but for Elm stuff.",
"repository": "https://github.com/rtfeldman/elm-workshop.git",
"license": "BSD-3-Clause",
"source-directories": [
".",
".."
],
"exposed-modules": [],
"dependencies": {
"NoRedInk/elm-decode-pipeline": "1.1.2 <= v < 2.0.0",
"elm-lang/core": "4.0.1 <= v < 5.0.0",
"elm-lang/html": "1.0.0 <= v < 2.0.0",
"evancz/elm-http": "3.0.1 <= v < 4.0.0"
},
"elm-version": "0.17.0 <= v < 0.18.0"
}