Thomas Perrin
Thomas Perrin

Reputation: 784

With cypress, control key pressed but keyup not trigerred after click event

I have this selection of multiple cells in a table while the control key must be pressed in order to select all the clicked cells:

cy.get("[data-cy=cell-1],[data-cy=cell-5],[data-cy=cell-32]")
  .click({ multiple: true, ctrlKey: true})

It works well, I see the multiple selection since selected cells have a different background color. I coded the page so when the window:keyup event on the control key happens, a dialog opens to edit the cells content. This works very well on dev, but with cypress, the .click({ multiple: true, ctrlKey: true}) doesn't trigger my dialog, and if I had a manual .type({ctrl}) after, it triggers the dialog but the multiple selection is lost

cy.get("[data-cy=cell-1],[data-cy=cell-5],[data-cy=cell-32]")
  .click({ multiple: true, ctrlKey: true})
cy.get("#my-table-container").type("{ctrl}")

It acts like for my first click({multiple: true, ctrlKey: true}) the control key is down but never released. Do you know how could I use the click multiple and control but triggering the keyup at the end?

UPDATE: @HostListener setup I am on Angular 12, here is exactly how I setup the key event with the @HostListener (I also listen the escape key to clear the cell selection):

@HostListener("window:keyup", ["$event"])
handleKeyDown(event: KeyboardEvent) {
    switch(event.key){
      case "Escape": this.clearSelection(); break;
      case "Control":this.openEditDialog(); break;
      }
    }

Upvotes: 1

Views: 2137

Answers (2)

Thomas Perrin
Thomas Perrin

Reputation: 784

It appears Cypress don't react as intended with the HostListener setup with event.key instead of event.keyCode (which is deprecated). I adapted in my component the @HostListener to use event.keyCode === 17 and it worked.

Upvotes: 1

Fody
Fody

Reputation: 31904

Since you're using the window:keyup event, you could try to trigger that event, presume it's firing on the ctrl key (code 17).

cy.get("[data-cy=cell-1],[data-cy=cell-5],[data-cy=cell-32]")
  .click({ multiple: true, ctrlKey: true})
cy.get("#my-table-container").trigger('keyup', { keyCode: 17 })

Also a keydown/keyup combo might work better (try with or without the ctrlKey modifier on .click())

cy.get('#my-table-container').trigger('keydown', { keyCode: 17 })
cy.get('[data-cy=cell-1]').click({ctrlKey: true})
cy.get('[data-cy=cell-5]').click({ctrlKey: true})
cy.get('[data-cy=cell-32]').click({ctrlKey: true})
cy.get('#my-table-container').trigger('keyup', { keyCode: 17 })

Lastly, the target of the .trigger() may need to be something other than #my-table-container, depending on the way addEventListener() is invoked. Would need to see more of the HTML and source to be sure.


Angular @HostListener

If you have an event listener set by the @HostListener decorator as follows

@HostListener("window:keyup", ["$event"])
onKeyup(event: KeyboardEvent): void {
  event.preventDefault();
  console.log('keyup', event)
}

when you look a the console.log, the event has the path property path: (4) [body, html, document, Window] which means you can trigger it on <body>.

To check, the following test passes

it('triggers keyup set by @HostListener on window', () => {
  let ev;
  cy.visit('http://localhost:4200').then(win => {
    cy.stub(win.console, 'log', (eventName, event) => {
      ev = event
    }).as('consoleLog')
  })

  cy.get('body').trigger('keyup', {keyCode:17})

  cy.get('@consoleLog')
    .should(() => {
      expect(ev.type).to.equal('keyup')
      expect(ev.keyCode).to.equal(17)
    })
})

So to trigger the dialog

cy.get("[data-cy=cell-1],[data-cy=cell-5],[data-cy=cell-32]")
  .click({ multiple: true, ctrlKey: true})
cy.get('body')
  .focus()
  .trigger('keyup', { keyCode: 17 })

Upvotes: 1

Related Questions