elm-pages-v3-beta/tests/FormTests.elm

515 lines
20 KiB
Elm
Raw Normal View History

module FormTests exposing (all)
2022-06-18 20:33:20 +03:00
import Date exposing (Date)
2022-06-30 19:53:07 +03:00
import Dict
import Expect
2022-07-24 10:08:15 +03:00
import Form exposing (Form)
2022-07-09 03:56:52 +03:00
import Form.Field as Field
2022-07-24 22:33:03 +03:00
import Form.Validation as Validation exposing (Combined)
2022-03-11 22:52:33 +03:00
import Test exposing (Test, describe, test)
2022-06-18 20:33:20 +03:00
type Uuid
= Uuid String
type Action
= Signout
| SetQuantity ( Uuid, Int )
all : Test
all =
2022-06-18 20:33:20 +03:00
describe "Form Parser" <|
let
passwordConfirmationParser =
2022-07-24 10:11:12 +03:00
Form.init
2022-06-18 20:33:20 +03:00
(\password passwordConfirmation ->
2022-07-23 09:48:36 +03:00
{ combine =
Validation.succeed
(\passwordValue passwordConfirmationValue ->
Validation.succeed { password = passwordValue }
2022-07-24 10:21:55 +03:00
|> Validation.withErrorIf (passwordValue /= passwordConfirmationValue)
2022-07-23 09:48:36 +03:00
passwordConfirmation
"Must match password"
)
|> Validation.andMap password
|> Validation.andMap passwordConfirmation
|> Validation.andThen identity
2022-07-24 22:33:03 +03:00
, view = \_ -> Div
2022-07-23 09:48:36 +03:00
}
2022-06-18 20:33:20 +03:00
)
2022-07-24 10:11:12 +03:00
|> Form.field "password" (Field.text |> Field.required "Password is required")
|> Form.field "password-confirmation" (Field.text |> Field.required "Password confirmation is required")
2022-06-18 20:33:20 +03:00
in
[ test "matching password" <|
\() ->
Form.runOneOfServerSide
2022-06-18 20:33:20 +03:00
(fields
[ ( "password", "mypassword" )
, ( "password-confirmation", "mypassword" )
]
)
(passwordConfirmationParser |> Form.initCombined identity)
|> Expect.equal
2022-06-18 20:33:20 +03:00
( Just { password = "mypassword" }
, Dict.empty
)
2022-06-18 20:33:20 +03:00
, test "non-matching password" <|
\() ->
Form.runOneOfServerSide
2022-06-18 20:33:20 +03:00
(fields
[ ( "password", "mypassword" )
, ( "password-confirmation", "doesnt-match-password" )
]
)
(passwordConfirmationParser |> Form.initCombined identity)
2022-06-18 20:33:20 +03:00
|> Expect.equal
2022-06-27 20:24:34 +03:00
( Just { password = "mypassword" }
2022-06-18 20:33:20 +03:00
, Dict.fromList [ ( "password-confirmation", [ "Must match password" ] ) ]
)
2022-06-18 20:33:20 +03:00
, describe "oneOf" <|
let
oneOfParsers : Form.ServerForms String Action
2022-06-18 20:33:20 +03:00
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")
2022-07-23 09:51:24 +03:00
)
|> Form.combine (\() -> Signout)
(Form.init
(\_ ->
{ combine = Validation.succeed ()
, view = \_ -> Div
}
)
|> Form.hiddenField "kind" (Field.exactValue "signout" "Expected signout")
)
2022-06-18 20:33:20 +03:00
in
[ test "first branch" <|
\() ->
2022-07-24 10:11:12 +03:00
Form.runOneOfServerSide
2022-06-18 20:33:20 +03:00
(fields
[ ( "kind", "signout" )
]
)
2022-06-18 20:33:20 +03:00
oneOfParsers
|> Expect.equal
( Just Signout
, Dict.empty
)
, test "second branch" <|
\() ->
2022-07-24 10:11:12 +03:00
Form.runOneOfServerSide
2022-06-18 20:33:20 +03:00
(fields
[ ( "kind", "setQuantity" )
, ( "uuid", "123" )
, ( "quantity", "1" )
]
)
2022-06-18 20:33:20 +03:00
oneOfParsers
|> Expect.equal
( Just (SetQuantity ( Uuid "123", 1 ))
2022-06-18 20:33:20 +03:00
, Dict.empty
)
, test "3rd" <|
\() ->
Form.runOneOfServerSide
(fields
[ ( "kind", "toggle-all" )
, ( "toggleTo", "" )
]
)
todoForm
|> Expect.equal
( Just (CheckAll False)
, Dict.empty
)
2022-06-18 20:33:20 +03:00
--, test "no match" <|
-- \() ->
-- Form.runOneOfServerSide
-- (fields [])
-- oneOfParsers
-- |> Expect.equal
-- ( Nothing
-- , Dict.fromList []
-- )
, describe "select" <|
2022-01-17 01:31:08 +03:00
let
2022-06-18 20:33:20 +03:00
selectParser =
Form.init
2022-06-18 20:33:20 +03:00
(\media ->
2022-07-23 12:12:23 +03:00
{ combine = media
, view =
\_ -> Div
}
2022-06-18 20:33:20 +03:00
)
2022-07-24 10:11:12 +03:00
|> Form.field "media"
2022-06-18 20:33:20 +03:00
(Field.select
[ ( "book", Book )
, ( "article", Article )
, ( "video", Video )
]
(\_ -> "Invalid")
2022-01-17 01:31:08 +03:00
)
2022-06-18 20:33:20 +03:00
in
[ test "example" <|
\() ->
2022-07-24 10:11:12 +03:00
Form.runOneOfServerSide
2022-06-18 20:33:20 +03:00
(fields
[ ( "media", "book" )
]
)
(selectParser |> Form.initCombined identity)
2022-06-18 20:33:20 +03:00
|> Expect.equal
( Just (Just Book)
, Dict.empty
)
]
, describe "dependent validations" <|
let
2022-07-23 12:12:23 +03:00
checkinFormParser :
2022-07-24 10:08:15 +03:00
Form
2022-07-23 12:12:23 +03:00
String
2022-07-24 22:00:47 +03:00
{ combine : Combined String ( Date, Date ), view : a -> MyView }
2022-07-23 12:12:23 +03:00
data
2022-06-18 20:33:20 +03:00
checkinFormParser =
2022-07-24 10:11:12 +03:00
Form.init
2022-06-18 20:33:20 +03:00
(\checkin checkout ->
2022-07-23 12:12:23 +03:00
{ combine =
Validation.succeed
(\checkinValue checkoutValue ->
Validation.succeed ( checkinValue, checkoutValue )
|> (if Date.toRataDie checkinValue >= Date.toRataDie checkoutValue then
2022-07-24 10:21:55 +03:00
Validation.withError checkin "Must be before checkout"
2022-06-18 20:33:20 +03:00
2022-07-23 12:12:23 +03:00
else
identity
)
)
|> Validation.andMap checkin
|> Validation.andMap checkout
|> Validation.andThen identity
, view =
\_ -> Div
}
2022-06-18 20:33:20 +03:00
)
2022-07-24 10:11:12 +03:00
|> Form.field "checkin"
2022-06-18 20:33:20 +03:00
(Field.date { invalid = \_ -> "Invalid" } |> Field.required "Required")
2022-07-24 10:11:12 +03:00
|> Form.field "checkout"
2022-06-18 20:33:20 +03:00
(Field.date { invalid = \_ -> "Invalid" } |> Field.required "Required")
in
[ test "checkin must be before checkout" <|
\() ->
2022-07-24 10:11:12 +03:00
Form.runOneOfServerSide
2022-06-18 20:33:20 +03:00
(fields
[ ( "checkin", "2022-01-01" )
, ( "checkout", "2022-01-03" )
]
)
(checkinFormParser |> Form.initCombined identity)
2022-06-18 20:33:20 +03:00
|> Expect.equal
( Just ( Date.fromRataDie 738156, Date.fromRataDie 738158 )
, Dict.empty
)
, test "checkout is invalid because before checkin" <|
\() ->
Form.runOneOfServerSide
2022-06-18 20:33:20 +03:00
(fields
[ ( "checkin", "2022-01-03" )
, ( "checkout", "2022-01-01" )
]
)
(checkinFormParser |> Form.initCombined identity)
2022-06-18 20:33:20 +03:00
|> 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" )
]
)
2022-07-24 10:11:12 +03:00
(Form.init
(\postForm_ ->
2022-07-23 21:16:32 +03:00
{ combine =
postForm_.combine ()
, view =
\_ -> ( [], [ Div ] )
}
)
2022-07-24 10:11:12 +03:00
|> Form.dynamic
(\() ->
2022-07-24 10:11:12 +03:00
Form.init
(\password passwordConfirmation ->
2022-07-23 21:16:32 +03:00
{ combine =
Validation.succeed
(\passwordValue passwordConfirmationValue ->
if passwordValue == passwordConfirmationValue then
Validation.succeed { password = passwordValue }
2022-07-23 21:16:32 +03:00
else
2022-07-25 16:15:45 +03:00
passwordConfirmation
|> Validation.fail "Must match password"
2022-07-23 21:16:32 +03:00
)
|> Validation.andMap password
|> Validation.andMap passwordConfirmation
|> Validation.andThen identity
, view = [ Div ]
}
)
2022-07-24 10:11:12 +03:00
|> 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" ] )
]
)
2022-06-18 20:33:20 +03:00
]
]
, describe "dependent parsing" <|
let
2022-07-24 22:00:47 +03:00
linkForm : Form String { combine : Combined String PostAction, view : Form.Context String data -> MyView } data
linkForm =
2022-07-24 10:11:12 +03:00
Form.init
(\url ->
2022-07-23 21:20:22 +03:00
{ combine =
Validation.succeed ParsedLink
|> Validation.andMap url
, view =
\_ -> Div
}
)
2022-07-24 10:11:12 +03:00
|> Form.field "url"
(Field.text
|> Field.required "Required"
|> Field.url
)
2022-07-24 22:00:47 +03:00
postForm : Form String { combine : Combined String PostAction, view : Form.Context String data -> MyView } data
postForm =
2022-07-24 10:11:12 +03:00
Form.init
(\title body ->
2022-07-23 21:20:22 +03:00
{ combine =
Validation.succeed
(\titleValue bodyValue ->
{ title = titleValue
, body = bodyValue
}
)
|> Validation.andMap title
|> Validation.andMap body
|> Validation.map ParsedPost
, view = \_ -> Div
}
)
2022-07-24 10:11:12 +03:00
|> Form.field "title" (Field.text |> Field.required "Required")
|> Form.field "body" Field.text
2022-07-24 22:00:47 +03:00
dependentParser : Form String { combine : Combined String PostAction, view : Form.Context String data -> MyView } data
dependentParser =
2022-07-24 10:11:12 +03:00
Form.init
(\kind postForm_ ->
2022-07-23 21:20:22 +03:00
{ combine =
kind
|> Validation.andThen postForm_.combine
, view = \_ -> Div
}
)
2022-07-24 10:11:12 +03:00
|> Form.field "kind"
(Field.select
[ ( "link", Link )
, ( "post", Post )
]
(\_ -> "Invalid")
|> Field.required "Required"
)
2022-07-24 10:11:12 +03:00
|> Form.dynamic
(\parsedKind ->
case parsedKind of
Link ->
linkForm
Post ->
postForm
)
in
[ test "parses link" <|
\() ->
2022-07-24 10:11:12 +03:00
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
2022-06-18 20:33:20 +03:00
type Media
= Book
| Article
| Video
2022-06-18 20:33:20 +03:00
type MyView
= Div
2022-06-18 20:33:20 +03:00
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
2022-08-24 19:46:47 +03:00
, 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
2022-08-24 19:46:47 +03:00
, 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
2022-08-24 19:46:47 +03:00
, 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
2022-08-24 19:46:47 +03:00
, 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 ()
2022-08-24 19:46:47 +03:00
, 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
2022-08-24 19:46:47 +03:00
, view = \_ -> []
}
)
|> Form.hiddenField "toggleTo" Field.checkbox
|> Form.hiddenKind ( "kind", "toggle-all" ) "Expected kind"