Cypress: Add `keystrokeDelay` configuration option

Created on 19 Jul 2017  路  18Comments  路  Source: cypress-io/cypress

Currently Cypress uses a setTimeout between keystrokes to mimic how a user would press each key.

Concern 1:

We should enable this property to be globally configurable such as keystrokeDelay.

Concern 2:

When set to false or 0, instead of making each key press be async, we should make it synchronous. This would be helpful to not only type faster but could make things easier for 3rd party JS frameworks such as react.

Currently as it stands since keypresses as async - its indeterminate whether or not your JS framework has finished 'processing' all of the keystrokes. For instance it may fire its change events while typing vs after all characters have been typed. See issue #534 for more detail.

Concern 3:

When typing very long sequences its often useful just to have it all "type at once" and not have to wait. Give developers the choice to decide.

Recommendation

  • set keystrokeDelay to true by default (which is the minimum setImmediate async between key presses)
  • if keystrokeDelay is set to false or 0 typing all characters synchronously
  • if keystrokeDelay is set to a number then use this as the setTimeout value

Passing the delay option to cy.type() will override anything globally set in config.

2锔忊儯 first-timers-only pkdriver ready for work enhancement feature

Most helpful comment

Here's one custom command I've been using in the interim. If you're app sets up form input event listener's on either input and change events, this should cover most cases:

// in your commands file:
Cypress.Commands.add('fill', {
  prevSubject: 'element'
}, (subject, value) => {
  cy.wrap(subject).invoke('val', value).trigger('input').trigger('change')
});

// Usage in spec:
cy.get('[data-my-input-field]').fill('Hello');

All 18 comments

We should enable this property to be globally configurable such as keystrokeDelay.

This would be convenient to configure globally.

For instance it may fire its change events while typing vs after all characters have been typed.

But in a real world with real users, that is how the app will actually behave, right? So why should it behave differently under the test?

@sompylasar The way I'd answer that is that we sometimes take the trade-off of not testing a 100% accurate user experience if the benefit is faster test execution. We're already making some of these trade-offs already. For example, a user doesn't click a button immediately after a page loads; they would likely take some time reading text or scanning the UI and deciding their next course of action. But with end-to-end tests, we're doing all kinds of things super fast because mimicking the time it takes to read text and scan the UI is not worth the cost. Of course, simulating a regular typing speed is super important in some cases, so it's nice to have the flexibility.

@Aaronius

For example, a user doesn't click a button immediately after a page loads

At least there's a way to simulate that with a wait if the UI under test is time-sensitive.

But with end-to-end tests, we're doing all kinds of things super fast because mimicking the time it takes to read text and scan the UI is not worth the cost.

It's too bold of a decision for the authors of a test framework (Cypress) to estimate the cost for each use case that they might not know about. Provide a good API and good defaults, okay, but let the users make decisions.

This would be helpful but maybe another way to accomplish this is by adding a secondary method that will fill out a field without delay and leave the "type" method intact as is.

This way, if you want to "type" into an input it works as expected. If you just want to fill the input you do something like

cy.get('#myInput').fill(value)

or something?

The suggestion for fill() from @Foovanadil seems reasonable especially as it's possible real humans may sometimes copy/paste input and presumably fill() would mimic the same effect.

Seriously need this feature. Those ci build minutes are expensive.

Here's one custom command I've been using in the interim. If you're app sets up form input event listener's on either input and change events, this should cover most cases:

// in your commands file:
Cypress.Commands.add('fill', {
  prevSubject: 'element'
}, (subject, value) => {
  cy.wrap(subject).invoke('val', value).trigger('input').trigger('change')
});

// Usage in spec:
cy.get('[data-my-input-field]').fill('Hello');

Having issues with this when dealing with Formik. I tried adding the events focus and blur but it doesn't seem to work, as soon as I go to the next field, the input is cleared. Ideas ?

Here's one custom command I've been using in the interim. If you're app sets up form input event listener's on either input and change events, this should cover most cases:

// in your commands file:
Cypress.Commands.add('fill', {
  prevSubject: 'element'
}, (subject, value) => {
  cy.wrap(subject).invoke('val', value).trigger('input').trigger('change')
});

// Usage in spec:
cy.get('[data-my-input-field]').fill('Hello');

The problem with this solution is _not_ having all necessary events triggered. I'm using react-final-form and because of the missing events, the field value is never changed if I use this solution. Does anyone have a solution to this? @crcatala maybe? @ls-chloe-schoreisz have you found a way to make it work in your case?

This feature is really important when you have to add a long text into a textarea... @brian-mann any plans on working on this feature any time soon?

@scaryguy haven't found a workaround. I reduced the typing delay to its minimum, but my tests involve breaking string max boundaries (sometimes 255 char...) so an actual implementation of this feature would be much appreciated, I'd probably reduce the time of runs by 60%

It seems that reducing the delay with cy.type("hello", {delay: 1}) doesn't do anything if you try to go below 10ms.

I'm not saying this is _ideal_, however I've managed to finally get this working with React 16.8 with a textarea.

Cypress.Commands.add('fill', {
  prevSubject: 'element',
}, ($subject, value) => {
  const el = $subject[0];
  el.value = value;
  return cy.wrap($subject).type('t{backspace}');
});

Since cy.type actually manages to trigger React's events properly, writing on extra character then deleting it winds up triggered the change and the application responds to it. I'd prefer to write an empty string (Cypress blocks this) or at least spaces (doesn't appear to work), but it is what it is. I'm hoping for an official fill/paste command in the future.

This might be not much of a difference, but still I like it more:

Cypress.Commands.add('fill', { prevSubject: 'element' }, ($subj, text) => {
    $subj.val(text);
    return cy.wrap($subj).type('{end}');
});

I'm not saying this is _ideal_, however I've managed to finally get this working with React 16.8 with a textarea.

Cypress.Commands.add('fill', {
  prevSubject: 'element',
}, ($subject, value) => {
  const el = $subject[0];
  el.value = value;
  return cy.wrap($subject).type('t{backspace}');
});

Since cy.type actually manages to trigger React's events properly, writing on extra character then deleting it winds up triggered the change and the application responds to it. I'd prefer to write an empty string (Cypress blocks this) or at least spaces (doesn't appear to work), but it is what it is. I'm hoping for an official fill/paste command in the future.

Thanks. Works perfectly. We need an official solution though...

Any updates on this?
Not sure why but I keep loosing the value when running the filling in a loop

None of these solutions worked for me, so I created a npm package that is working on React with Material-UI in the lastest version:

cypress-fill-command

I used some info collected from all related posts, so, if you feel that you need to be tagged on it to give properly credit, contact me.

Takes a regular expression?.. :)

Ops, my mistake in the docs, it should only receive a simple string as a parameter. Already fix it.

Was this page helpful?
0 / 5 - 0 ratings