Skip to content

Cypress with react and google API services – how to stub autocomplete

I am trying to test a react webapp (created in a separate project), that contains a popup where there’s an input containing a google auto-complete for cities:

(I changed text because of language)

popup

I have in “search city” a text input where if data is inserted, google searches for cities and returns results (eg I search Rome, Italy):

google search cities

When I press “save data” there’s a function that checks google results, then closes the popup:

in a file:

export const useGoogleApiDesktop = () => {
    
    let autocompleteService
    
    if (window.google && window.google.maps) {
        autocompleteService = new window.google.maps.places.AutocompleteService()
    }
}

in another file (the one called):

const googleApi = useGoogleApiDesktop()

const onSubmitClick = useCallback(async () => {
        [...]
        const res: GoogleApiPlacesResponse = await googleApi.autocompleteService.getPlacePredictions({
            input: addressComputed,
            types: ['(cities)'],
            componentRestrictions: { country: 'it' }
        })
    }, [])

When I use it in plain browser, everything works fine; but if I try to launch it with cypress to test it, it returns me this error:

Cypress error

I am trying to avoid this error and simply go on and close the popup, since during my tests I do not need to write anything on that line; I only need to write something on the other textareas and close the popup.

Since I couldn’t do it, I’ve tried to stub that call, but I am totally new in using cy.stub() and does not work:

function selectAddress(bookingConfig) {
  // opens the popup  
  cy.get('.reservationsWhereAdd').click()

 
  // trying to add the google library
  const win = cy.state('window')
  const document = win.document
  const script = document.createElement('script')
  script.src = `https://maps.googleapis.com/maps/api/js?key=[myApiKey]&libraries=places&language=it`
  script.async = true

  // this is commented since I don't think I need it
  // window.initMap = function () {
  //   // JS API is loaded and available
  //   console.log('lanciato')
  // }

  // Append the ‘script’ element to ‘head’
  document.head.appendChild(script)

  // type something in some fields
  cy.get('#street').type(bookingConfig.street)
  cy.get('#streetNumber').type(bookingConfig.streetNum)
  cy.get('#nameOnTheDoorbell').type(bookingConfig.nameOnTheDoorbell)
  cy.get('#addressAlias').type(bookingConfig.addressAlias)

  // this correctly finds and prints the object
  console.log('--->', win.google.maps.places)


  cy.stub(googleApi.autocompleteService, 'getPlacePredictions')

  // this closes the popup
  cy.get('.flex-1 > .btn').click()

}

this cy.stub however does not works, and I don’t get why: it says googleApi is not defined

googleApi not defined

Any idea on how to solve this? Thanks!

UPDATE:

After the error, working with the cypress window, I manually closed the popup, reopened it, filled the fields, and clicked on save data. It worked, so I added a cy.wait(1000) just after opening the popup and it works for 95% of the times (9 times on 10). Any Idea on how to “wait for loading the google api, then fill the fields”?

Answer

As the update block said, I discovered that the problem was that it kept really long time to load the google API, because it’s not local and needs time to be retrieved.

So at first I just put a cy.wait(2000) before executing my code; but this couldn’t be the answer: what happens if I run the code on a slow network? Or if it takes more time for my application to load?

So, i created a command, that first waits for the google API to load; if it fails to load after 5 attempts, the test fails. Then, after that, my code is being executed. This way my test won’t fail really easily.

Here’s the code:

in cypress/support/command.js

Cypress.Commands.add('waitForGoogleApi', () => {
  let mapWaitCount = 0
  const mapWaitMax = 5

  cyMapLoad()

  function cyMapLoad() {
    mapWaitCount++

    cy.window().then(win => {
      if (typeof win.google != 'undefined') {
        console.log(`Done at attempt #${mapWaitCount}:`, win)
        return true
      } else if (mapWaitCount <= mapWaitMax) {
        console.log('Waiting attempt #' + mapWaitCount) // just log
        cy.wait(2000)
        cyMapLoad()
      } else if (mapWaitCount > mapWaitMax) {
        console.log('Failed to load google api')
        return false
      }
    })
  }
})

in file you want to use it:

cy.waitForGoogleApi().then(() => {
    // here comes the code to execute after loading the google Apis
})