mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-11-30 23:06:10 +03:00
515 lines
20 KiB
Elm
515 lines
20 KiB
Elm
module FormTests exposing (all)
|
|
|
|
import Date exposing (Date)
|
|
import Dict
|
|
import Expect
|
|
import Form exposing (Form)
|
|
import Form.Field as Field
|
|
import Form.Validation as Validation exposing (Combined)
|
|
import Test exposing (Test, describe, test)
|
|
|
|
|
|
type Uuid
|
|
= Uuid String
|
|
|
|
|
|
type Action
|
|
= Signout
|
|
| SetQuantity ( Uuid, Int )
|
|
|
|
|
|
all : Test
|
|
all =
|
|
describe "Form Parser" <|
|
|
let
|
|
passwordConfirmationParser =
|
|
Form.init
|
|
(\password passwordConfirmation ->
|
|
{ combine =
|
|
Validation.succeed
|
|
(\passwordValue passwordConfirmationValue ->
|
|
Validation.succeed { password = passwordValue }
|
|
|> Validation.withErrorIf (passwordValue /= passwordConfirmationValue)
|
|
passwordConfirmation
|
|
"Must match password"
|
|
)
|
|
|> Validation.andMap password
|
|
|> Validation.andMap passwordConfirmation
|
|
|> Validation.andThen identity
|
|
, view = \_ -> Div
|
|
}
|
|
)
|
|
|> Form.field "password" (Field.text |> Field.required "Password is required")
|
|
|> Form.field "password-confirmation" (Field.text |> Field.required "Password confirmation is required")
|
|
in
|
|
[ test "matching password" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "password", "mypassword" )
|
|
, ( "password-confirmation", "mypassword" )
|
|
]
|
|
)
|
|
(passwordConfirmationParser |> Form.initCombined identity)
|
|
|> Expect.equal
|
|
( Just { password = "mypassword" }
|
|
, Dict.empty
|
|
)
|
|
, test "non-matching password" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "password", "mypassword" )
|
|
, ( "password-confirmation", "doesnt-match-password" )
|
|
]
|
|
)
|
|
(passwordConfirmationParser |> Form.initCombined identity)
|
|
|> Expect.equal
|
|
( Just { password = "mypassword" }
|
|
, Dict.fromList [ ( "password-confirmation", [ "Must match password" ] ) ]
|
|
)
|
|
, describe "oneOf" <|
|
|
let
|
|
oneOfParsers : Form.ServerForms String Action
|
|
oneOfParsers =
|
|
Form.initCombined SetQuantity
|
|
(Form.init
|
|
(\_ uuid quantity ->
|
|
{ combine =
|
|
Validation.succeed Tuple.pair
|
|
|> Validation.andMap (uuid |> Validation.map Uuid)
|
|
|> Validation.andMap quantity
|
|
, view =
|
|
\_ -> Div
|
|
}
|
|
)
|
|
|> Form.hiddenField "kind" (Field.exactValue "setQuantity" "Expected setQuantity")
|
|
|> Form.hiddenField "uuid" (Field.text |> Field.required "Required")
|
|
|> Form.field "quantity" (Field.int { invalid = \_ -> "Expected int" } |> Field.required "Required")
|
|
)
|
|
|> Form.combine (\() -> Signout)
|
|
(Form.init
|
|
(\_ ->
|
|
{ combine = Validation.succeed ()
|
|
, view = \_ -> Div
|
|
}
|
|
)
|
|
|> Form.hiddenField "kind" (Field.exactValue "signout" "Expected signout")
|
|
)
|
|
in
|
|
[ test "first branch" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "kind", "signout" )
|
|
]
|
|
)
|
|
oneOfParsers
|
|
|> Expect.equal
|
|
( Just Signout
|
|
, Dict.empty
|
|
)
|
|
, test "second branch" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "kind", "setQuantity" )
|
|
, ( "uuid", "123" )
|
|
, ( "quantity", "1" )
|
|
]
|
|
)
|
|
oneOfParsers
|
|
|> Expect.equal
|
|
( Just (SetQuantity ( Uuid "123", 1 ))
|
|
, Dict.empty
|
|
)
|
|
, test "3rd" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "kind", "toggle-all" )
|
|
, ( "toggleTo", "" )
|
|
]
|
|
)
|
|
todoForm
|
|
|> Expect.equal
|
|
( Just (CheckAll False)
|
|
, Dict.empty
|
|
)
|
|
|
|
--, test "no match" <|
|
|
-- \() ->
|
|
-- Form.runOneOfServerSide
|
|
-- (fields [])
|
|
-- oneOfParsers
|
|
-- |> Expect.equal
|
|
-- ( Nothing
|
|
-- , Dict.fromList []
|
|
-- )
|
|
, describe "select" <|
|
|
let
|
|
selectParser =
|
|
Form.init
|
|
(\media ->
|
|
{ combine = media
|
|
, view =
|
|
\_ -> Div
|
|
}
|
|
)
|
|
|> Form.field "media"
|
|
(Field.select
|
|
[ ( "book", Book )
|
|
, ( "article", Article )
|
|
, ( "video", Video )
|
|
]
|
|
(\_ -> "Invalid")
|
|
)
|
|
in
|
|
[ test "example" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "media", "book" )
|
|
]
|
|
)
|
|
(selectParser |> Form.initCombined identity)
|
|
|> Expect.equal
|
|
( Just (Just Book)
|
|
, Dict.empty
|
|
)
|
|
]
|
|
, describe "dependent validations" <|
|
|
let
|
|
checkinFormParser :
|
|
Form
|
|
String
|
|
{ combine : Combined String ( Date, Date ), view : a -> MyView }
|
|
data
|
|
checkinFormParser =
|
|
Form.init
|
|
(\checkin checkout ->
|
|
{ combine =
|
|
Validation.succeed
|
|
(\checkinValue checkoutValue ->
|
|
Validation.succeed ( checkinValue, checkoutValue )
|
|
|> (if Date.toRataDie checkinValue >= Date.toRataDie checkoutValue then
|
|
Validation.withError checkin "Must be before checkout"
|
|
|
|
else
|
|
identity
|
|
)
|
|
)
|
|
|> Validation.andMap checkin
|
|
|> Validation.andMap checkout
|
|
|> Validation.andThen identity
|
|
, view =
|
|
\_ -> Div
|
|
}
|
|
)
|
|
|> Form.field "checkin"
|
|
(Field.date { invalid = \_ -> "Invalid" } |> Field.required "Required")
|
|
|> Form.field "checkout"
|
|
(Field.date { invalid = \_ -> "Invalid" } |> Field.required "Required")
|
|
in
|
|
[ test "checkin must be before checkout" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "checkin", "2022-01-01" )
|
|
, ( "checkout", "2022-01-03" )
|
|
]
|
|
)
|
|
(checkinFormParser |> Form.initCombined identity)
|
|
|> Expect.equal
|
|
( Just ( Date.fromRataDie 738156, Date.fromRataDie 738158 )
|
|
, Dict.empty
|
|
)
|
|
, test "checkout is invalid because before checkin" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "checkin", "2022-01-03" )
|
|
, ( "checkout", "2022-01-01" )
|
|
]
|
|
)
|
|
(checkinFormParser |> Form.initCombined identity)
|
|
|> Expect.equal
|
|
( Just ( Date.fromRataDie 738158, Date.fromRataDie 738156 )
|
|
, Dict.fromList
|
|
[ ( "checkin", [ "Must be before checkout" ] )
|
|
]
|
|
)
|
|
, test "sub-form" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "password", "mypassword" )
|
|
, ( "password-confirmation", "doesnt-match" )
|
|
]
|
|
)
|
|
(Form.init
|
|
(\postForm_ ->
|
|
{ combine =
|
|
postForm_.combine ()
|
|
, view =
|
|
\_ -> ( [], [ Div ] )
|
|
}
|
|
)
|
|
|> Form.dynamic
|
|
(\() ->
|
|
Form.init
|
|
(\password passwordConfirmation ->
|
|
{ combine =
|
|
Validation.succeed
|
|
(\passwordValue passwordConfirmationValue ->
|
|
if passwordValue == passwordConfirmationValue then
|
|
Validation.succeed { password = passwordValue }
|
|
|
|
else
|
|
passwordConfirmation
|
|
|> Validation.fail "Must match password"
|
|
)
|
|
|> Validation.andMap password
|
|
|> Validation.andMap passwordConfirmation
|
|
|> Validation.andThen identity
|
|
, view = [ Div ]
|
|
}
|
|
)
|
|
|> Form.field "password" (Field.text |> Field.password |> Field.required "Required")
|
|
|> Form.field "password-confirmation" (Field.text |> Field.password |> Field.required "Required")
|
|
)
|
|
|> Form.initCombined identity
|
|
)
|
|
|> Expect.equal
|
|
( Nothing
|
|
, Dict.fromList
|
|
[ ( "password-confirmation", [ "Must match password" ] )
|
|
]
|
|
)
|
|
]
|
|
]
|
|
, describe "dependent parsing" <|
|
|
let
|
|
linkForm : Form String { combine : Combined String PostAction, view : Form.Context String data -> MyView } data
|
|
linkForm =
|
|
Form.init
|
|
(\url ->
|
|
{ combine =
|
|
Validation.succeed ParsedLink
|
|
|> Validation.andMap url
|
|
, view =
|
|
\_ -> Div
|
|
}
|
|
)
|
|
|> Form.field "url"
|
|
(Field.text
|
|
|> Field.required "Required"
|
|
|> Field.url
|
|
)
|
|
|
|
postForm : Form String { combine : Combined String PostAction, view : Form.Context String data -> MyView } data
|
|
postForm =
|
|
Form.init
|
|
(\title body ->
|
|
{ combine =
|
|
Validation.succeed
|
|
(\titleValue bodyValue ->
|
|
{ title = titleValue
|
|
, body = bodyValue
|
|
}
|
|
)
|
|
|> Validation.andMap title
|
|
|> Validation.andMap body
|
|
|> Validation.map ParsedPost
|
|
, view = \_ -> Div
|
|
}
|
|
)
|
|
|> Form.field "title" (Field.text |> Field.required "Required")
|
|
|> Form.field "body" Field.text
|
|
|
|
dependentParser : Form String { combine : Combined String PostAction, view : Form.Context String data -> MyView } data
|
|
dependentParser =
|
|
Form.init
|
|
(\kind postForm_ ->
|
|
{ combine =
|
|
kind
|
|
|> Validation.andThen postForm_.combine
|
|
, view = \_ -> Div
|
|
}
|
|
)
|
|
|> Form.field "kind"
|
|
(Field.select
|
|
[ ( "link", Link )
|
|
, ( "post", Post )
|
|
]
|
|
(\_ -> "Invalid")
|
|
|> Field.required "Required"
|
|
)
|
|
|> Form.dynamic
|
|
(\parsedKind ->
|
|
case parsedKind of
|
|
Link ->
|
|
linkForm
|
|
|
|
Post ->
|
|
postForm
|
|
)
|
|
in
|
|
[ test "parses link" <|
|
|
\() ->
|
|
Form.runOneOfServerSide
|
|
(fields
|
|
[ ( "kind", "link" )
|
|
, ( "url", "https://elm-radio.com/episode/wrap-early-unwrap-late" )
|
|
]
|
|
)
|
|
(dependentParser |> Form.initCombined identity)
|
|
|> Expect.equal
|
|
( Just (ParsedLink "https://elm-radio.com/episode/wrap-early-unwrap-late")
|
|
, Dict.empty
|
|
)
|
|
]
|
|
]
|
|
|
|
|
|
type PostAction
|
|
= ParsedLink String
|
|
| ParsedPost { title : String, body : Maybe String }
|
|
|
|
|
|
type PostKind
|
|
= Link
|
|
| Post
|
|
|
|
|
|
type Media
|
|
= Book
|
|
| Article
|
|
| Video
|
|
|
|
|
|
type MyView
|
|
= Div
|
|
|
|
|
|
fields : List ( String, String ) -> List ( String, String )
|
|
fields list =
|
|
list
|
|
|
|
|
|
todoForm : Form.ServerForms String TodoAction
|
|
todoForm =
|
|
editItemForm
|
|
|> Form.initCombined UpdateEntry
|
|
|> Form.combine Add newItemForm
|
|
|> Form.combine Check completeItemForm
|
|
|> Form.combine Delete deleteItemForm
|
|
|> Form.combine (\_ -> DeleteComplete) clearCompletedForm
|
|
|> Form.combine CheckAll toggleAllForm
|
|
|
|
|
|
type TodoAction
|
|
= UpdateEntry ( String, String )
|
|
| Add String
|
|
| Delete String
|
|
| DeleteComplete
|
|
| Check ( Bool, String )
|
|
| CheckAll Bool
|
|
|
|
|
|
editItemForm : Form.HtmlForm String ( String, String ) input msg
|
|
editItemForm =
|
|
Form.init
|
|
(\itemId description ->
|
|
{ combine =
|
|
Validation.succeed Tuple.pair
|
|
|> Validation.andMap itemId
|
|
|> Validation.andMap description
|
|
, view = \_ -> []
|
|
}
|
|
)
|
|
|> Form.hiddenField "itemId"
|
|
(Field.text
|
|
|> Field.required "Must be present"
|
|
)
|
|
|> Form.field "description"
|
|
(Field.text
|
|
|> Field.required "Must be present"
|
|
)
|
|
|> Form.hiddenKind ( "kind", "edit-item" ) "Expected kind"
|
|
|
|
|
|
newItemForm : Form.HtmlForm String String input msg
|
|
newItemForm =
|
|
Form.init
|
|
(\description ->
|
|
{ combine =
|
|
Validation.succeed identity
|
|
|> Validation.andMap description
|
|
, view = \_ -> []
|
|
}
|
|
)
|
|
|> Form.field "description" (Field.text |> Field.required "Must be present")
|
|
|> Form.hiddenKind ( "kind", "new-item" ) "Expected kind"
|
|
|
|
|
|
completeItemForm : Form.HtmlForm String ( Bool, String ) input msg
|
|
completeItemForm =
|
|
Form.init
|
|
(\todoId complete ->
|
|
{ combine =
|
|
Validation.succeed Tuple.pair
|
|
|> Validation.andMap complete
|
|
|> Validation.andMap todoId
|
|
, view = \_ -> []
|
|
}
|
|
)
|
|
|> Form.hiddenField "todoId"
|
|
(Field.text
|
|
|> Field.required "Must be present"
|
|
)
|
|
|> Form.hiddenField "complete"
|
|
Field.checkbox
|
|
|> Form.hiddenKind ( "kind", "complete" ) "Expected kind"
|
|
|
|
|
|
deleteItemForm : Form.HtmlForm String String input msg
|
|
deleteItemForm =
|
|
Form.init
|
|
(\todoId ->
|
|
{ combine =
|
|
Validation.succeed identity
|
|
|> Validation.andMap todoId
|
|
, view = \_ -> []
|
|
}
|
|
)
|
|
|> Form.hiddenField "todoId"
|
|
(Field.text
|
|
|> Field.required "Must be present"
|
|
--|> Field.withInitialValue (.id >> Form.Value.string)
|
|
)
|
|
|> Form.hiddenKind ( "kind", "delete" ) "Expected kind"
|
|
|
|
|
|
clearCompletedForm : Form.HtmlForm String () { entriesCompleted : Int } msg
|
|
clearCompletedForm =
|
|
Form.init
|
|
{ combine = Validation.succeed ()
|
|
, view = \_ -> []
|
|
}
|
|
|> Form.hiddenKind ( "kind", "clear-completed" ) "Expected kind"
|
|
|
|
|
|
toggleAllForm : Form.HtmlForm String Bool input msg
|
|
toggleAllForm =
|
|
Form.init
|
|
(\toggleTo ->
|
|
{ combine =
|
|
Validation.succeed identity
|
|
|> Validation.andMap toggleTo
|
|
, view = \_ -> []
|
|
}
|
|
)
|
|
|> Form.hiddenField "toggleTo" Field.checkbox
|
|
|> Form.hiddenKind ( "kind", "toggle-all" ) "Expected kind"
|