module Fantomas.Core.Tests.Stroustrup.ExperimentalElmishTests

open NUnit.Framework
open FsUnit
open Fantomas.Core.Tests.TestHelpers
open Fantomas.Core

let config =
    { config with
        ExperimentalElmish = true }

[<Test>]
let ``input without attributes`` () =
    formatSourceString
        """let i = input []
"""
        config
    |> prepend newline
    |> should
        equal
        """
let i = input []
"""

[<Test>]
let ``short input with single attribute`` () =
    formatSourceString
        """let i = input [ Type "text" ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let i = input [ Type "text" ]
"""

[<Test>]
let ``multiline input with multiple attributes`` () =
    formatSourceString
        """let i = input [ Type "text"; Required "required" ]
"""
        { config with MaxArrayOrListWidth = 20 }
    |> prepend newline
    |> should
        equal
        """
let i =
    input [
        Type "text"
        Required "required"
    ]
"""

[<Test>]
let ``div without children or attributes`` () =
    formatSourceString
        """let d = div [] []
"""
        config
    |> prepend newline
    |> should
        equal
        """
let d = div [] []
"""

[<Test>]
let ``div with short attributes`` () =
    formatSourceString
        """let d = div [ ClassName "mt-4" ] []
"""
        config
    |> prepend newline
    |> should
        equal
        """
let d = div [ ClassName "mt-4" ] []
"""

[<Test>]
let ``div with no attributes and short children`` () =
    formatSourceString
        """let d = div [] [ str "meh" ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let d = div [] [ str "meh" ]
"""

[<Test>]
let ``div with multiline attributes`` () =
    formatSourceString
        """let d = div [ ClassName "container"; OnClick (fun _ -> printfn "meh")  ] []
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let d =
    div [
        ClassName "container"
        OnClick(fun _ -> printfn "meh")
    ] []
"""

[<Test>]
let ``div with not attributes and multiple elmish children`` () =
    formatSourceString
        """let d =
    div [] [
      span [] [ str "a" ]
      span [] [ str "b" ]
    ]
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let d =
    div [] [
        span [] [ str "a" ]
        span [] [ str "b" ]
    ]
"""

[<Test>]
let ``div with single attribute and children`` () =
    formatSourceString
        """let view =
    div [ ClassName "container" ] [
        h1 [] [ str "A heading 1" ]
        p [] [ str "A paragraph" ]
    ]
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let view =
    div [ ClassName "container" ] [
        h1 [] [ str "A heading 1" ]
        p [] [ str "A paragraph" ]
    ]
"""

[<Test>]
let ``div with multiple attributes and children`` () =
    formatSourceString
        """
let d =
    div [ ClassName "container"; OnClick (fun _ -> printfn "meh") ] [
        span [] [str "foo"]
        code [] [str "bar"]
    ]
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let d =
    div [
        ClassName "container"
        OnClick(fun _ -> printfn "meh")
    ] [
        span [] [ str "foo" ]
        code [] [ str "bar" ]
    ]
"""

[<Test>]
let ``short div with short p`` () =
    formatSourceString
        """let d =
    div [] [ p [] [ str "meh" ] ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let d = div [] [ p [] [ str "meh" ] ]
"""

[<Test>]
let ``short div with multiple short children`` () =
    formatSourceString
        """let d =
    div [] [
      br [] ; br []
    ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let d = div [] [ br []; br [] ]
"""

[<Test>]
let ``div with long children but a long setting`` () =
    formatSourceString
        """let d =
    div [] [
        p [] [ str "fooooooooo" ]
        p [] [ str "baaaaaaaar" ]
    ]
"""
        { config with
            MaxArrayOrListWidth = 150 }
    |> prepend newline
    |> should
        equal
        """
let d = div [] [ p [] [ str "fooooooooo" ]; p [] [ str "baaaaaaaar" ] ]
"""

// here the p is 38 characters
// this makes the div multiline but the p not.

[<Test>]
let ``short div with slightly longer p`` () =
    formatSourceString
        """let d =
    div [] [ p [] [ str "meeeeeeeeeeeeeeeeeeeeeh" ] ]
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let d =
    div [] [
        p [] [ str "meeeeeeeeeeeeeeeeeeeeeh" ]
    ]
"""

[<Test>]
let ``div with longer p`` () =
    formatSourceString
        """let d =
    div [] [ p [] [ str "meeeeeeeeeeeeeeeeeeeeehhhh" ] ]
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let d =
    div [] [
        p [] [ str "meeeeeeeeeeeeeeeeeeeeehhhh" ]
    ]
"""

[<Test>]
let counter () =
    formatSourceString
        """
let view model dispatch =
  div [] [
    button [ OnClick (fun _ -> dispatch Decrement) ] [
        str "-"
    ]
    div [] [
        str (sprintf "%A" model)
    ]
    button [ OnClick (fun _ -> dispatch Increment) ] [
        str "+"
    ]
  ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let view model dispatch =
    div [] [
        button [ OnClick(fun _ -> dispatch Decrement) ] [ str "-" ]
        div [] [ str (sprintf "%A" model) ]
        button [ OnClick(fun _ -> dispatch Increment) ] [ str "+" ]
    ]
"""

[<Test>]
let ``view entry`` () =
    formatSourceString
        """
let viewEntry todo dispatch =
  li [ classList [ ("completed", todo.completed); ("editing", todo.editing) ] ]
     [ div [ ClassName "view" ]
           [ input [ ClassName "toggle"
                     Type "checkbox"
                     Checked todo.completed
                     OnChange (fun _ -> Check (todo.id,(not todo.completed)) |> dispatch) ]
             label [ OnDoubleClick (fun _ -> EditingEntry (todo.id,true) |> dispatch) ]
                   [ str todo.description ]
             button [ ClassName "destroy"
                      OnClick (fun _-> Delete todo.id |> dispatch) ]
                    []
           ]
       input [ ClassName "edit"
               valueOrDefault todo.description
               Name "title"
               Id ("todo-" + (string todo.id))
               OnInput (fun ev -> UpdateEntry (todo.id, !!ev.target?value) |> dispatch)
               OnBlur (fun _ -> EditingEntry (todo.id,false) |> dispatch)
               onEnter (EditingEntry (todo.id,false)) dispatch ]
    ]
"""
        { config with
            MaxArrayOrListWidth = 40
            MaxInfixOperatorExpression = 50 }
    |> prepend newline
    |> should
        equal
        """
let viewEntry todo dispatch =
    li [
        classList [
            ("completed", todo.completed)
            ("editing", todo.editing)
        ]
    ] [
        div [ ClassName "view" ] [
            input [
                ClassName "toggle"
                Type "checkbox"
                Checked todo.completed
                OnChange(fun _ -> Check(todo.id, (not todo.completed)) |> dispatch)
            ]
            label [
                OnDoubleClick(fun _ -> EditingEntry(todo.id, true) |> dispatch)
            ] [ str todo.description ]
            button [
                ClassName "destroy"
                OnClick(fun _ -> Delete todo.id |> dispatch)
            ] []
        ]
        input [
            ClassName "edit"
            valueOrDefault todo.description
            Name "title"
            Id("todo-" + (string todo.id))
            OnInput(fun ev ->
                UpdateEntry(todo.id, !!ev.target?value)
                |> dispatch)
            OnBlur(fun _ -> EditingEntry(todo.id, false) |> dispatch)
            onEnter (EditingEntry(todo.id, false)) dispatch
        ]
    ]
"""

[<Test>]
let ``multiline attributes, no children`` () =
    formatSourceString
        """let a =
               button [ ClassName "destroy"
                        OnClick(fun _-> Delete todo.id |> dispatch) ]
                      []
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let a =
    button [
        ClassName "destroy"
        OnClick(fun _ -> Delete todo.id |> dispatch)
    ] []
"""

[<Test>]
let ``table and tbody`` () =
    formatSourceString
        """
table [ ClassName "table table-striped table-hover mb-0" ]
              [ tbody []
                    [ tokenDetailRow "TokenName" (str tokenName)
                      tokenDetailRow "LeftColumn" (ofInt leftColumn)
                      tokenDetailRow "RightColumn" (ofInt rightColumn)
                      tokenDetailRow "Content" (pre [] [ code [] [ str token.Content ] ])
                      tokenDetailRow "ColorClass" (str colorClass)
                      tokenDetailRow "CharClass" (str charClass)
                      tokenDetailRow "Tag" (ofInt tag)
                      tokenDetailRow "FullMatchedLength"
                          (span [ ClassName "has-text-weight-semibold" ] [ ofInt fullMatchedLength ]) ] ]
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
table [
    ClassName "table table-striped table-hover mb-0"
] [
    tbody [] [
        tokenDetailRow "TokenName" (str tokenName)
        tokenDetailRow "LeftColumn" (ofInt leftColumn)
        tokenDetailRow "RightColumn" (ofInt rightColumn)
        tokenDetailRow "Content" (pre [] [ code [] [ str token.Content ] ])
        tokenDetailRow "ColorClass" (str colorClass)
        tokenDetailRow "CharClass" (str charClass)
        tokenDetailRow "Tag" (ofInt tag)
        tokenDetailRow "FullMatchedLength" (span [ ClassName "has-text-weight-semibold" ] [ ofInt fullMatchedLength ])
    ]
]
"""

[<Test>]
let ``child with empty children`` () =
    formatSourceString
        """
let commands dispatch =
    Button.button
        [ Button.Color Primary
          Button.Custom
              [ ClassName "rounded-0"
                OnClick(fun _ -> dispatch GetTrivia) ] ]
        [ i [ ClassName "fas fa-code mr-1" ] []
          str "Get trivia" ]
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let commands dispatch =
    Button.button [
        Button.Color Primary
        Button.Custom [
            ClassName "rounded-0"
            OnClick(fun _ -> dispatch GetTrivia)
        ]
    ] [
        i [ ClassName "fas fa-code mr-1" ] []
        str "Get trivia"
    ]
"""

[<Test>]
let ``clock with two spaces`` () =
    formatSourceString
        """
let view (CurrentTime time) dispatch =
    svg
      [ ViewBox "0 0 100 100"
        SVG.Width "350px" ]
      [ circle
          [ Cx "50"
            Cy "50"
            R "45"
            SVG.Fill "#0B79CE" ] []
        // Hours
        clockHand (Hour time.Hour) "lightgreen" "2" 25.0
        handTop time.Hour "lightgreen" 25.0 12.0
        // Minutes
        clockHand (Minute time.Minute) "white" "2" 35.0
        handTop time.Minute "white" 35.0 60.0
        // Seconds
        clockHand (Second time.Second) "#023963" "1" 40.0
        handTop time.Second "#023963" 40.0 60.0
        // circle in the center
        circle
          [ Cx "50"
            Cy "50"
            R "3"
            SVG.Fill "#0B79CE"
            SVG.Stroke "#023963"
            SVG.StrokeWidth 1.0 ] []
      ]
"""
        { config with
            IndentSize = 2
            MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
let view (CurrentTime time) dispatch =
  svg [
    ViewBox "0 0 100 100"
    SVG.Width "350px"
  ] [
    circle [
      Cx "50"
      Cy "50"
      R "45"
      SVG.Fill "#0B79CE"
    ] []
    // Hours
    clockHand (Hour time.Hour) "lightgreen" "2" 25.0
    handTop time.Hour "lightgreen" 25.0 12.0
    // Minutes
    clockHand (Minute time.Minute) "white" "2" 35.0
    handTop time.Minute "white" 35.0 60.0
    // Seconds
    clockHand (Second time.Second) "#023963" "1" 40.0
    handTop time.Second "#023963" 40.0 60.0
    // circle in the center
    circle [
      Cx "50"
      Cy "50"
      R "3"
      SVG.Fill "#0B79CE"
      SVG.Stroke "#023963"
      SVG.StrokeWidth 1.0
    ] []
  ]
"""

[<Test>]
let ``input with attribute array`` () =
    formatSourceString
        """let ia = input [| Type "hidden"; Name "code"; Required "required" |]
"""
        { config with MaxArrayOrListWidth = 50 }
    |> prepend newline
    |> should
        equal
        """
let ia =
    input [|
        Type "hidden"
        Name "code"
        Required "required"
    |]
"""

[<Test>]
let ``div with children array`` () =
    formatSourceString
        """let d =
    div [||] [| p [||] [| str "oh my foobar" |] |]
"""
        { config with MaxArrayOrListWidth = 35 }
    |> prepend newline
    |> should
        equal
        """
let d =
    div [||] [|
        p [||] [| str "oh my foobar" |]
    |]
"""

[<Test>]
let ``mix lists and array`` () =
    formatSourceString
        """let view dispatch model =
    div [| Class "container" |]
        [
          h1 [] [| str "my title" |]
          button [| OnClick (fun _ -> dispatch Msg.Foo) |] [
                str "click me"
          ]
        ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let view dispatch model =
    div [| Class "container" |] [
        h1 [] [| str "my title" |]
        button [| OnClick(fun _ -> dispatch Msg.Foo) |] [ str "click me" ]
    ]
"""

[<Test>]
let ``short feliz element`` () =
    formatSourceString
        """let a =
    Html.h1 [ prop.text "some title" ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let a = Html.h1 [ prop.text "some title" ]
"""

[<Test>]
let ``multiline feliz element`` () =
    formatSourceString
        """let a =
        Html.button [
            prop.style [ style.marginLeft 5 ]
            prop.onClick (fun _ -> setCount(count - 1))
            prop.text "Decrement"
        ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let a =
    Html.button [
        prop.style [ style.marginLeft 5 ]
        prop.onClick (fun _ -> setCount (count - 1))
        prop.text "Decrement"
    ]
"""

[<Test>]
let ``nested feliz elements`` () =
    formatSourceString
        """let a =
    Html.div [
        Html.h1 [ prop.text "short" ]
        Html.button [
            prop.style [ style.marginRight 5 ]
            prop.onClick (fun _ -> setCount(count + 1))
            prop.text "Increment"
        ]
    ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let a =
    Html.div [
        Html.h1 [ prop.text "short" ]
        Html.button [
            prop.style [ style.marginRight 5 ]
            prop.onClick (fun _ -> setCount (count + 1))
            prop.text "Increment"
        ]
    ]
"""

[<Test>]
let ``feliz counter sample`` () =
    formatSourceString
        """module App

open Feliz

let counter = React.functionComponent(fun () ->
    let (count, setCount) = React.useState(0)
    Html.div [
        Html.button [
            prop.style [ style.marginRight 5 ]
            prop.onClick (fun _ -> setCount(count + 1))
            prop.text "Increment"
        ]

        Html.button [
            prop.style [ style.marginLeft 5 ]
            prop.onClick (fun _ -> setCount(count - 1))
            prop.text "Decrement"
        ]

        Html.h1 count
    ])

open Browser.Dom

ReactDOM.render(counter, document.getElementById "root")
"""
        config
    |> prepend newline
    |> should
        equal
        """
module App

open Feliz

let counter =
    React.functionComponent (fun () ->
        let (count, setCount) = React.useState (0)

        Html.div [
            Html.button [
                prop.style [ style.marginRight 5 ]
                prop.onClick (fun _ -> setCount (count + 1))
                prop.text "Increment"
            ]

            Html.button [
                prop.style [ style.marginLeft 5 ]
                prop.onClick (fun _ -> setCount (count - 1))
                prop.text "Decrement"
            ]

            Html.h1 count
        ])

open Browser.Dom

ReactDOM.render (counter, document.getElementById "root")
"""

[<Test>]
let ``feliz syntax`` () =
    formatSourceString
        """
Html.h1 42

Html.div "Hello there!"

Html.div [ Html.h1 "So lightweight" ]

Html.ul [
  Html.li "One"
  Html.li [ Html.strong "Two" ]
  Html.li [ Html.em "Three" ]
]
"""
        { config with MaxArrayOrListWidth = 40 }
    |> prepend newline
    |> should
        equal
        """
Html.h1 42

Html.div "Hello there!"

Html.div [ Html.h1 "So lightweight" ]

Html.ul [
    Html.li "One"
    Html.li [ Html.strong "Two" ]
    Html.li [ Html.em "Three" ]
]
"""

[<Test>]
let ``feliz construct with a single element as child, 999`` () =
    formatSourceString
        """
let drawer =
    Mui.drawer [
        // drawer.open' props.IsOpen

        drawer.children
            [
                Html.div [ prop.className classes.toolbar ]
                props.Items
                |> List.map (fun s ->
                    Mui.listItem
                        [
                        listItem.button true
                        match state with
                        | Some t when t = s -> listItem.selected true
                        | _ -> listItem.selected false
                        prop.text s
                        prop.onClick (fun _ ->
                        s
                        |> MenuItemClick
                        |> dispatch)
                        ])
                |> Mui.list
            ]
        ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let drawer =
    Mui.drawer [
        // drawer.open' props.IsOpen

        drawer.children [
            Html.div [ prop.className classes.toolbar ]
            props.Items
            |> List.map (fun s ->
                Mui.listItem [
                    listItem.button true
                    match state with
                    | Some t when t = s -> listItem.selected true
                    | _ -> listItem.selected false
                    prop.text s
                    prop.onClick (fun _ -> s |> MenuItemClick |> dispatch)
                ])
            |> Mui.list
        ]
    ]
"""

[<Test>]
let ``react hook`` () =
    formatSourceString
        """
let private useLocationDetail (auth0 : Auth0Hook) (roles : RolesHook) id =
    let id = Guid.Parse(id)
    let eventCtx = React.useContext (eventContext)
    let (creatorName, setCreatorName) = React.useState<string option> (None)

    let location =
        React.useMemo ((fun () -> getLocation eventCtx.Events id), [| eventCtx.Events; id |])

    React.useEffect
        ((fun () ->
            if roles.IsEditorOrAdmin
               && not (String.IsNullOrWhiteSpace(location.Creator)) then
                auth0.getAccessTokenSilently ()
                |> Promise.bind (fun authToken ->
                    let url =
                        sprintf "%s/users/%s" Common.backendUrl (location.Creator)

                    fetch
                        url
                        [ requestHeaders [ HttpRequestHeaders.ContentType "application/json"
                                           Common.authHeader authToken
                                           Common.subscriptionHeader ] ])
                |> Promise.bind (fun res -> res.text ())
                |> Promise.iter (fun json ->
                    let usersResult = Decode.fromString nameDecoder json

                    match usersResult with
                    | Ok name -> setCreatorName (Some name)
                    | Error err -> JS.console.log err)),
         [| box roles.Roles
            box location.Creator |])

    location, creatorName
"""
        { config with
            SpaceBeforeColon = true
            MaxArrayOrListWidth = 40
            MaxInfixOperatorExpression = 50 }
    |> prepend newline
    |> should
        equal
        """
let private useLocationDetail (auth0 : Auth0Hook) (roles : RolesHook) id =
    let id = Guid.Parse(id)
    let eventCtx = React.useContext (eventContext)
    let (creatorName, setCreatorName) = React.useState<string option> (None)

    let location =
        React.useMemo ((fun () -> getLocation eventCtx.Events id), [| eventCtx.Events; id |])

    React.useEffect (
        (fun () ->
            if
                roles.IsEditorOrAdmin
                && not (String.IsNullOrWhiteSpace(location.Creator))
            then
                auth0.getAccessTokenSilently ()
                |> Promise.bind (fun authToken ->
                    let url = sprintf "%s/users/%s" Common.backendUrl (location.Creator)

                    fetch url [
                        requestHeaders [
                            HttpRequestHeaders.ContentType "application/json"
                            Common.authHeader authToken
                            Common.subscriptionHeader
                        ]
                    ])
                |> Promise.bind (fun res -> res.text ())
                |> Promise.iter (fun json ->
                    let usersResult = Decode.fromString nameDecoder json

                    match usersResult with
                    | Ok name -> setCreatorName (Some name)
                    | Error err -> JS.console.log err)),
        [| box roles.Roles
           box location.Creator |]
    )

    location, creatorName
"""

[<Test>]
let ``keep comment after closing bracket, 1089`` () =
    formatSourceString
        """
        Gen.frequency [ 8,
                        2,
                        Gen.map5 (fun b1 b2 expr1 expr2 pat ->
                            SynExpr.ForEach(DebugPointAtFor.No, SeqExprOnly b1, b2, pat, expr1, expr2, zero))
                            Arb.generate<_> Arb.generate<_> genSubDeclExpr genSubDeclExpr genSubSynPat ] //
"""
        config
    |> prepend newline
    |> should
        equal
        """
Gen.frequency [
    8,
    2,
    Gen.map5
        (fun b1 b2 expr1 expr2 pat -> SynExpr.ForEach(DebugPointAtFor.No, SeqExprOnly b1, b2, pat, expr1, expr2, zero))
        Arb.generate<_>
        Arb.generate<_>
        genSubDeclExpr
        genSubDeclExpr
        genSubSynPat
] //
"""

[<Test>]
let ``keep comment after closing bracket, single web mode`` () =
    formatSourceString
        """
        Gen.frequency [ 8,
                        2,
                        Gen.map5 (fun b1 b2 expr1 expr2 pat ->
                            SynExpr.ForEach(DebugPointAtFor.No, SeqExprOnly b1, b2, pat, expr1, expr2, zero))
                            Arb.generate<_> Arb.generate<_> genSubDeclExpr genSubDeclExpr genSubSynPat ] //
"""
        config
    |> prepend newline
    |> should
        equal
        """
Gen.frequency [
    8,
    2,
    Gen.map5
        (fun b1 b2 expr1 expr2 pat -> SynExpr.ForEach(DebugPointAtFor.No, SeqExprOnly b1, b2, pat, expr1, expr2, zero))
        Arb.generate<_>
        Arb.generate<_>
        genSubDeclExpr
        genSubDeclExpr
        genSubSynPat
] //
"""

[<Test>]
let ``don't repeat comment in nested Elmish element, 1347`` () =
    formatSourceString
        """
let html =
    Html.div [
        prop.className "navbar-menu"
        prop.children [
            Html.div [
                prop.className "navbar-start"
                prop.children [
                    Html.a [
                        prop.className "navbar-item"
                    ]
                    (*
                    Html.a [ prop.className "navbar-item"; prop.href (baseUrl +/ "Files") ] [
                        prop.text "Files"
                    ]*)
                ]
            ]
        ]
    ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let html =
    Html.div [
        prop.className "navbar-menu"
        prop.children [
            Html.div [
                prop.className "navbar-start"
                prop.children [
                    Html.a [ prop.className "navbar-item" ]
                (*
                    Html.a [ prop.className "navbar-item"; prop.href (baseUrl +/ "Files") ] [
                        prop.text "Files"
                    ]*)
                ]
            ]
        ]
    ]
"""

[<Test>]
let ``don't repeat comment in nested Elmish element, single element mode`` () =
    formatSourceString
        """
let html =
    Html.div [
        prop.className "navbar-menu"
        prop.children [
            Html.div [
                prop.className "navbar-start"
                prop.children [
                    Html.a [
                        prop.className "navbar-item"
                    ]
                    (*
                    Html.a [ prop.className "navbar-item"; prop.href (baseUrl +/ "Files") ] [
                        prop.text "Files"
                    ]*)
                ]
            ]
        ]
    ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let html =
    Html.div [
        prop.className "navbar-menu"
        prop.children [
            Html.div [
                prop.className "navbar-start"
                prop.children [
                    Html.a [ prop.className "navbar-item" ]
                (*
                    Html.a [ prop.className "navbar-item"; prop.href (baseUrl +/ "Files") ] [
                        prop.text "Files"
                    ]*)
                ]
            ]
        ]
    ]
"""

[<Test>]
let ``don't repeat comment in nested Elmish element, short block comment`` () =
    formatSourceString
        """
let html =
    Html.div [
        prop.className "navbar-menu"
        prop.children [
            Html.div [
                prop.className "navbar-start"
                prop.children [
                    Html.a [
                        prop.className "navbar-item"
                    ]
                    (* meh *)
                ]
            ]
        ]
    ]
"""
        config
    |> prepend newline
    |> should
        equal
        """
let html =
    Html.div [
        prop.className "navbar-menu"
        prop.children [
            Html.div [
                prop.className "navbar-start"
                prop.children [
                    Html.a [ prop.className "navbar-item" ]
                (* meh *)
                ]
            ]
        ]
    ]
"""

[<Test>]
let ``empty single list long expression, 1510`` () =
    formatSourceString
        """
[<ReactComponent>]
let Dashboard () =
    Html.div [
        Html.div []
        Html.div [
            Html.text "hola muy buenas"
        ]
    ]
"""
        { config with
            RecordMultilineFormatter = MultilineFormatterType.NumberOfItems
            MaxArrayOrListWidth = 20
            // MaxElmishWidth = 10
            MultiLineLambdaClosingNewline = true }
    |> prepend newline
    |> should
        equal
        """
[<ReactComponent>]
let Dashboard () =
    Html.div [
        Html.div []
        Html.div [
            Html.text "hola muy buenas"
        ]
    ]
"""

[<Test>]
let ``block comment in elmish expression with two lists, 1601`` () =
    formatSourceString
        """
module CapitalGuardian.App

open Fable.Core.JsInterop
open Fable.React
open Feliz

[<ReactComponent()>]
let private App () =
    div [] [
        str "meh 2000k"
        (*
                          {small && <Navigation />}
              <Container>
                {!small && <Header />}
                {!small && <Navigation />}
                {routeResult || <NotFoundPage />}
              </Container>
              <ToastContainer />
        *)
    ]

exportDefault App
"""
        config
    |> prepend newline
    |> should
        equal
        """
module CapitalGuardian.App

open Fable.Core.JsInterop
open Fable.React
open Feliz

[<ReactComponent>]
let private App () =
    div [] [
        str "meh 2000k"
    (*
                          {small && <Navigation />}
              <Container>
                {!small && <Header />}
                {!small && <Navigation />}
                {routeResult || <NotFoundPage />}
              </Container>
              <ToastContainer />
        *)
    ]

exportDefault App
"""

[<Test>]
let ``block comment in elmish expression with two lists, two children`` () =
    formatSourceString
        """
module CapitalGuardian.App

open Fable.Core.JsInterop
open Fable.React
open Feliz

[<ReactComponent()>]
let private App () =
    div [] [
        str "meh 2000k"
        str "other meh"
        (*
                          {small && <Navigation />}
              <Container>
                {!small && <Header />}
                {!small && <Navigation />}
                {routeResult || <NotFoundPage />}
              </Container>
              <ToastContainer />
        *)
    ]

exportDefault App
"""
        config
    |> prepend newline
    |> should
        equal
        """
module CapitalGuardian.App

open Fable.Core.JsInterop
open Fable.React
open Feliz

[<ReactComponent>]
let private App () =
    div [] [
        str "meh 2000k"
        str "other meh"
    (*
                          {small && <Navigation />}
              <Container>
                {!small && <Header />}
                {!small && <Navigation />}
                {routeResult || <NotFoundPage />}
              </Container>
              <ToastContainer />
        *)
    ]

exportDefault App
"""

[<Test>]
let ``comment inside empty elmish children, 1179`` () =
    formatSourceString
        """
a [] [
    // def
]
"""
        config
    |> prepend newline
    |> should
        equal
        """
a [] [
// def
]
"""

[<Test>]
let ``comment after opening bracket in Elmish expression without children, 2037`` () =
    formatSourceString
        """
ReactDom.render (React.strictMode [ // comment 
                                    App() ], root)
"""
        config
    |> prepend newline
    |> should
        equal
        """
ReactDom.render (
    React.strictMode [ // comment
        App()
    ],
    root
)
"""

[<Test>]
let ``record type definition and elmish dsl are controlled separately`` () =
    formatSourceString
        """
type Point =
    {
        /// Great comment
        X: int
        Y: int
    }

type Model = {
    Points: Point list
}
    
let view dispatch model =
    div
        []
        [
            h1 [] [ str "Some title" ]
            ul
                []
                [
                    for p in model.Points do
                        li [] [ str $"%i{p.X}, %i{p.Y}" ]
                ]
            hr []
        ]
        
let stillCramped = [
    // yow
    x ; y ; z
]
"""
        config
    |> prepend newline
    |> should
        equal
        """
type Point =
    {
        /// Great comment
        X: int
        Y: int
    }

type Model = { Points: Point list }

let view dispatch model =
    div [] [
        h1 [] [ str "Some title" ]
        ul [] [
            for p in model.Points do
                li [] [ str $"%i{p.X}, %i{p.Y}" ]
        ]
        hr []
    ]

let stillCramped =
    [
      // yow
      x
      y
      z ]
"""

[<Test>]
let ``fsharp_multiline_bracket_style = stroustrup also applies for applications that ends with list arguments`` () =
    formatSourceString
        """
type Point =
    {
        /// Great comment
        X: int
        Y: int
    }

type Model = {
    Points: Point list
}
    
let view dispatch model =
    div
        []
        [
            h1 [] [ str "Some title" ]
            ul
                []
                [
                    for p in model.Points do
                        li [] [ str $"%i{p.X}, %i{p.Y}" ]
                ]
            hr []
        ]
        
let alsoStroup = [
    // yow
    x ; y ; z
]
"""
        { FormatConfig.Default with
            MultilineBracketStyle = Stroustrup }
    |> prepend newline
    |> should
        equal
        """
type Point = {
    /// Great comment
    X: int
    Y: int
}

type Model = { Points: Point list }

let view dispatch model =
    div [] [
        h1 [] [ str "Some title" ]
        ul [] [
            for p in model.Points do
                li [] [ str $"%i{p.X}, %i{p.Y}" ]
        ]
        hr []
    ]

let alsoStroup = [
    // yow
    x
    y
    z
]
"""
