lefthandedgoat / canopy

f# web automation and testing library, built on top of Selenium (friendly to c# also)
http://lefthandedgoat.github.io/canopy/
MIT License
506 stars 115 forks source link

<< enters text too fast #465

Closed amieres closed 5 years ago

amieres commented 5 years ago

I have a simple web application written in WebSharper that accepts a Regex, an input and shows the resulting matches:

module REGEX =
    open WebSharper.UI
    open WebSharper.UI.Html

    [< SPAEntryPoint >]
    let main() =
        let rexV   = Var.Create """(Err|Warn|Info) \((\d+)\,\s*(\d+)\) - \((\d+)\,\s*(\d+)\)\: "([^"]+?)"\.""" //"
        let parmsV = Var.Create "g"
        let inputV = Var.Create "Err (1, 7) - (1, 12): \"This shows over there as an error\".\nWarn (2, 7) - (2, 12): \"This shows over there as a warning\".\nInfo (3, 7) - (3, 12): \"This shows over there as information\"."
        div [] [
            div [] [ div [] [ text "RegEx:"    ] ; Doc.InputAreaV [ attr.style "width: 1000px; height:80px " ; attr.id "regex" ] rexV.V   ]
            div [] [          text "Options: "   ; Doc.InputV     [ attr.style "width: 700px"                ; attr.id "parms" ] parmsV.V ]
            div [] [ div [] [ text "Input:"    ] ; Doc.InputAreaV [ attr.style "width: 1000px; height:80px " ; attr.id "input" ] inputV.V ]
            div [] [
                View.Map3 (fun inp rx opt ->
                    ul [] 
                       [ match inp with
                         | REGEX rx opt m -> yield! m |> Array.map (text >> List.singleton >> (li [])) //|> Seq.map (fun x -> x :> Doc)
                         | _              -> yield  text "<no match>" 
                       ]
                    ) inputV.View rexV.View parmsV.View
                |> Doc.BindView id
            ]
        ]
        |> Doc.Run JS.Document.Body

I am trying to use canopy to test it. With this code:

module TestCanopy =
    open canopy
    open canopy.runner.classic
    open canopy.configuration
    open canopy.classic
    open OpenQA.Selenium

    start chrome

    url "http://localhost:9005/testing/testing.html"

    "regex1" &&& fun _->
        click "#regex"
        "#regex" << """(Err|Warn|Info) \((\d+)\,\s*(\d+)\) - \((\d+)\,\s*(\d+)\)\: "([^"]+?)"\."""
        "#parms" << "g"
        "#input" << """Err (1, 7) - (1, 12): "This shows over there as an error".
                       Warn (2, 7) - (2, 12): "This shows over there as a warning".
                       Info (3, 7) - (3, 12): "This shows over there as information"."""

    run()

The issue is the operator << seems to type too fast for the webpage to catch up and as a result the values entered are missing some letters.

Is there a way to slow it down?

or have it copy/paste the values instead?

lefthandedgoat commented 5 years ago

Doc.Run JS.Document.Body That makes me think that the html is actually injected via javascript, which means that the page may be 'loaded' but scripts are still executing which may be why its acting strangely in terms of keystrokes.

Before entering a value into a selector, you can do a displayed "#regex" which will probably take 1-3ms to execute and may slow things down enough to help.

You can also try using sleep 0.01 to put a 10ms delay in there.

For either solution you probably only need to do it before the first line that types into a box.

Hope this helps!

amieres commented 5 years ago

Thanks for the quick reply

I added a sleep 1.0 after url ... same result.

The input field shows:

Er (1,7  (1, 12): "This shwsovr ther sanerro"
                        an (2 7  2, 1): "hs shows ove tere as  arnng"
                     Ifo (3, 7 - (3, 2: Thi hws ovr there as informaton".

Instead of:

Err (1, 7) - (1, 12): "This shows over there as an error".
                   Warn (2, 7) - (2, 12): "This shows over there as a warning".
                   Info (3, 7) - (3, 12): "This shows over there as information".

I should point out that the code actually executes on every stroke so it maybe slow. But it is fast enough for human input,

lefthandedgoat commented 5 years ago

You can make your own version of << that sleeps between each character stroke

let ( <<<< ) selector (value:string) =
    value.ToCharArray() 
    |> Array.map string
    |> Array.iter (fun character -> 
        sleep 0.01
        selector << character)

use: "#selector" <<<< "text to type slowly"

And you will need to disable this configuration so it wont clear the text boxes between each character


canopy.configuration.optimizeByDisablingClearBeforeWrite <- true
amieres commented 5 years ago

Yes! that worked Thank you for the tip.

I disabled/enabled in the operator:

let ( <<<< ) selector (value:string) =
    canopy.configuration.optimizeByDisablingClearBeforeWrite <- false
    selector << ""
    canopy.configuration.optimizeByDisablingClearBeforeWrite <- true
    value.ToCharArray() 
    |> Array.map string
    |> Array.iter (fun character -> 
        //sleep 0.01
        selector << character)
    canopy.configuration.optimizeByDisablingClearBeforeWrite <- false

It is a bit slow though, it took 25 seconds even though sleep is commented out.

I am going to try with copy paste. Is there a way to send ctrl-c & ctrl-v?

lefthandedgoat commented 5 years ago

You would have to drop down to selenium for that. (never tried copy paste before)

You can use the element function to get an element, then call methods on it like in this SO post:

https://stackoverflow.com/questions/11750447/performing-a-copy-and-paste-with-selenium-2

amieres commented 5 years ago

Thank you!

amieres commented 5 years ago

OK this is my version using Copy/Paste on Windows. Its really fast!

let ( <<< ) selector (value:string) =
    someElement selector
    |> Option.iter (fun element ->
        System.Windows.Forms.Clipboard.SetText value
        element.Clear()
        element.Click()
        element.SendKeys(Keys.Control + "v")
    )
amirrajan commented 5 years ago

@amieres I'd strongly recommend not to use sleep if at all possible (haven't read the entire thread yet, just figured I'd throw my two cents in there).