Alexander Zeitler
Alexander Zeitler

Reputation: 13109

Add custom Cypress command when using TypeScript

I'm trying to create a new cypress command which allows me to post a file using formData as cy.request doesn't support formData yet.

I'm using request-promise-native for the POST itself.

First, in my commands.ts I'm extending the Cypress.Chainable interface like this:

declare global {
  namespace Cypress {
    interface Chainable<Subject = any> {
      postFormData(
        url: string, 
        formData: FormData, 
        token: string): Chainable<FullResponse>
    }
  }
}

FullResponse is the response type definition for request-promise-native.

My postFormData function looks like this:

function postFormData(
  url,
  formData,
  token
): Cypress.Chainable<FullResponse> {  // this is line 52
  const response: FullResponse = await post(url, {
    auth: { bearer: token },
    formData
  })
  return cy.wrap(response) // this is line 58
}

Finally, I'm registering the new command:

Cypress.Commands.add('postFormData', postFormData)

In my test.ts, I'm calling the command like this:

const response = cy.postFormData(
  url,
  formData,
  accessToken)
expect(response.statusCode).to.equal(202)

However, tsc gives me these errors:

commands.ts:52:4 -  error TS1064: The return type of an async function or method must be the global Promise<T> type.

commands.ts:58:3 - error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.

cy.wrap returns a Chainable but not a Promise, so how can I solve this?

Upvotes: 2

Views: 2440

Answers (1)

Alexander Zeitler
Alexander Zeitler

Reputation: 13109

This is a possible solution

import { post, FullResponse } from 'request-promise-native'

declare global {
  namespace Cypress {
    interface Chainable<Subject = any> {
      postFormData(
        url: string, 
        formData: FormData, 
        token: string): Chainable<FullResponse>
    }
  }
}

function postFormData(url, formData, token): Cypress.Chainable<any> {
  return cy.wrap(
    post(url, {
      auth: { bearer: token },
      formData
    })
  )

Cypress.Commands.add('postFormData', postFormData)

The correct usage is:

cy.postFormData(
  url,
  formData,
  accessToken)
  .then((response) => {
    expect(response.statusCode).to.equal(202)
  })

Update

As request is in maintainance mode and there's an issue with browsers and formData, here's a solution using axios:

import axios, { AxiosResponse } from 'axios'

declare global {
  namespace Cypress {
    interface Chainable<Subject = any> {
      postFormData(
        url: string,
        formData: FormData,
        token: string
      ): Chainable<AxiosResponse>
    }
  }
}

function postFormData(url, formData, token): Cypress.Chainable<any> {
  return cy.wrap(
    axios(url, {
      method: 'post',
      url,
      data: formData,
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'multipart/form-data'
      }
    })
  )
}

Cypress.Commands.add('postFormData', postFormData)

Usage:

cy.postFormData(
  url,
  formData,
  accessToken)
  .then((response) => {
    expect(response.status).to.equal(202)
  })

Upvotes: 1

Related Questions