cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
47.02k stars 3.18k forks source link

cy.visit() Typescript promise chain error - Type 'Chainable<HTMLInputElement>' is missing the following properties #6206

Closed Dan503 closed 4 years ago

Dan503 commented 4 years ago

Current behavior:

The following Typescript code produces an error:

const loadInput = (): Promise<HTMLInputElement> => cy.visit('./core/selectors/selectors-test-file.html')
    .then((contentWindow: Window) => {
        let { document } = contentWindow
        const $input = <HTMLInputElement>document.getElementById('textInputID')
        return $input
    })

it("Shouldn't break", async ()=> {
    const $input = await loadInput()
    expect($input.value).to.equal('')
})

The error message I get:

TS2739: Type 'Chainable<HTMLInputElement>' is missing the following properties from type 'Promise<HTMLInputElement>': catch, [Symbol.toStringTag]

Desired behavior:

The loadInput() function should return a promise that later returns the HTML input DOM element.

Versions

Work around

I worked around the issue by doing this:

const loadInput = (): Promise<HTMLInputElement> => new Promise((resolve) => {
    cy.visit('./core/selectors/selectors-test-file.html')
        .then((contentWindow: Window) => {
            let { document } = contentWindow
            const $input = <HTMLInputElement>document.getElementById('textInputID')
            resolve($input)
        })
})

But now Cypress is complaining about nesting cy.visit() inside a promise. It's just a warning though so I can ignore it.

karol-majewski commented 4 years ago

I'm not aware of any additional constraints you have, but just in case: isn't that what you're looking for?

cy.visit('./core/selectors/selectors-test-file.html')
  .then(window$ => window$.document.getElementById('textInputID') as HTMLInputElement)
  .should('have.property', 'value', '');

Or, since DOM elements with ids are global variables:

cy
  .visit('./core/selectors/selectors-test-file.html')
  .its('document.textInputID')
  .should('have.property', 'value', '');

Alternatively, if you need to access input from another place:

let input: HTMLInputElement;

cy.visit('./core/selectors/selectors-test-file.html').then(window$ => {
  input = window$.document.getElementById('textInputID') as HTMLInputElement;
});

(Cypress thenables are not Promises, so your original code cannot work.)

Dan503 commented 4 years ago

I use the loadInput function a lot and in different scenarios to reduce code repetition.

Based on your comment above though, it sounds like this pattern will work:

const loadInput = (callback) => {
  cy.visit('./core/selectors/selectors-test-file.html').then(window$ => {
    input = window$.document.getElementById('textInputID') as HTMLInputElement;
    callback(input)
  });
}

it("Should work", ()=> {
  loadInput($input => {
    expect($input.value).to.equal('')
  })
})
Dan503 commented 4 years ago

I like the promise async/await syntax a lot more though.

I switched to using cypress-promise to make my code cleaner. Based on the number of downloads, many other developers feel the same way.

It has the annoying issue of triggering the warning though.

https://github.com/NicholasBoll/cypress-promise/issues/6

Is there a way that the warning can be suppressed if we choose that we would prefer to work with promises?

FFdhorkin commented 4 years ago

I like the promise async/await syntax a lot more though.

I switched to using cypress-promise to make my code cleaner. Based on the number of downloads, many other developers feel the same way.

It has the annoying issue of triggering the warning though.

NicholasBoll/cypress-promise#6

Is there a way that the warning can be suppressed if we choose that we would prefer to work with promises?

Just to chime in here... We're using Cypress to validate our API - no UI involved, except for Cypress itself (to inspect requests). Our code often times has >= 5 levels of indentation because we have to keep doing nested then blocks to pull out variables we want to use. And a lot of our helper methods are really messy - I'd like to have a getCount() method that just returns an integer rather than Chainable.

cypress-promise would enable that, and make our code MUCH more readable. But it would also create dozens of warnings in our console, and that's a non-starter. It would be nice to be have some way to suppress that warning. I'm tempted to make a fork of Cypress just to remove that warning... but that seems like a pretty extreme measure.

sainthkh commented 4 years ago

It's a duplicate of #1417.