5

sendKeys() method would send all the keys at once (actually, one at a time but very quickly):

var elm = element(by.id("myinput"));
elm.sendKeys("test");

Is there a way to slow the typing down so that Protractor would send one character at a time with a small delay between each of the characters?

We can slow down Protractor entirely, but that does not change the way sendKeys() works and it would also slow everything down while we just need the "send keys" part and only in specific cases.

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195

4 Answers4

6

The idea is to use browser.actions() and construct a series of "send keys" command - one for every character in a string. After every "send keys" command we are adding a delay by introducing a custom sleep action. At the end, here is a reusable function we've come up with:

function slowType(elm, keys, delay) {
    var action = browser.actions().mouseMove(elm).click();

    for (var i = 0; i < keys.length; i++) {
        action = action.sendKeys(keys[i]).sleep(delay);
    }

    return action.perform();
}

Usage:

slowType(elm, "some text", 100);
Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • Basically, it's the same approach we're using. However, there is a risk when you click on element. Sometimes the element is programmed to highlight all existed value. Trying to send END key before sending any values. And we may need other approach for the case of clear all existed value. – Nguyen Vu Hoang Jul 04 '16 at 14:57
  • @NguyenVuHoang ah, good point. I remember once we had that and sent ctrl/command + A to select the existing text and then type. Please see if it makes sense for you to provide your implementation - it could be better than ours. Thanks! – alecxe Jul 04 '16 at 14:58
  • But why would you want to simulate slow typing? wouldn't it slow down your tests? just trying to understand! – Ram Pasala Jul 04 '16 at 15:00
  • @igniteram1 of course. We had to use it couple times for the dynamically formatted and validated inputs like phone numbers and dates. This proved to work across the major browsers while the regular "send keys" had issues in IE and randomly in other browsers because of the action happening "too quick". May be it's our application problem at the end, but I hope that'll help someone in similar cases. Good question, thanks! – alecxe Jul 04 '16 at 15:05
  • 1
    yup that would help actually for people having project specific requirements, great work man! – Ram Pasala Jul 04 '16 at 15:44
  • Seem like this is common way to simulate the typing, only excepts using `browser.action()`, but `$(el).sendKeys()`, I found it work just fine for me. And for sleep, I use `browser.driver.sleep()`. These are having clearer syntax I think. – Linh Pham Jul 06 '16 at 04:31
  • @alecxe I faced similar kind of issue, in our application the `sendKeys()` are sending text too fast and since few of the fields have dependency on `buttons` I need to actually slow down and see what actually is happening but we have forms/tables of multiple text fields and if I apply your solution I would end up using 2 for loops which I want to avoid(Time Complexity) how can we achieve this more efficiently? Thanks! – Ram Pasala Jul 26 '16 at 12:53
  • and more over why are we using `browser.actions` to click the element? is there a specific reason? it could be done directly as well right? – Ram Pasala Jul 26 '16 at 12:55
  • @igniteram1 thanks for trying it out. Are you sure you are not trying to optimize pre-maturely? Actions help to conveniently group and chain the commands and, it might be quicker than trying to do the same with regular sendKeys. – alecxe Jul 26 '16 at 14:32
  • [ts] `Property 'sleep' does not exist on type 'ActionSequence'`. – Zainu Mar 28 '18 at 06:14
  • I tried this but instead of "browser.actions" i used "sendkeys" directly and result was that the string got typed in a jumbled manner. e.g. "web" got typed as "ewb" did any one else face this problem. I think we have to take into account and execute for loop in async manner. not sure how though – Monnie_tester Apr 07 '19 at 16:58
2

If you don't want to create a custom sleep() action, this approach works:

slowType: function(elm, keys, delay) {
    elm.click();

    for (var i =0; i < keys.length;i++) {
        browser.actions().sendKeys(keys[i]).perform();
        browser.sleep(delay);
    }

}
krewmarco
  • 157
  • 1
  • 4
1

The described previously approaches do not work with async/await functions. Here is the method that can be used instead, since Protractor deprecates control flow

/**
* @param {ElementFinder} $element
* @param {string} keys string to type
* @param {number} [delay=200] delay between characters
* @param {number} [timeout=timeouts.ms1000] timeout for waiting for an element to be interactable
*/
slowType: ($element, keys, delay = 200, timeout = 1000) => browser
    .wait(
        // waits for element to be interactable for 'timeout' ms,
        // otherwise throws an error with passed element locator
        ExpectedConditions.elementToBeClickable($element),
        timeout,
        "waitThenSendKeys to " + $element.locator()
    ).then(() => $element.click())
    .then(() => $element.clear())
    .then( async () => {
        for (let i = 0; i < keys.length; i++) {
            await $element.sendKeys(keys[i]);
            await browser.sleep(delay);
        }
    })

Then in your test just import this method (i.e. const {slowType} = require("actions/element-actions");)

And use the method as follows

await slowType($searchInput, "my search string", 500);

Sergey Pleshakov
  • 7,964
  • 2
  • 17
  • 40
1

I had my auto-completer producing inconsistent results if whole string is typed in input box at once (With protractor's speed), fixed it with the following solution.

var elm = element(by.id("myinput"));
elm.sendKeys("tes");  // input all except last character
browser.sleep(1000);  // add delay of 1 second
elm.sendKeys("t");    // input last character
NK_Mimrot
  • 332
  • 2
  • 12