0

I have set up a number of intercepts in the body of my tests. Here they are, pasted from the Cypress log, with the alias added

cy:intercept ➟  // alias: getRecipesSku(971520)
Method: GET
Matcher: "https://wpsite.com/wp-json/wp/v2/posts?tags[]=6287&_fields**"
Mocked Response: [{ ... }]

cy:intercept ➟  // alias: getRecipesSku(971520,971310)
Method: GET
Matcher: "https://wpsite.com/wp-json/wp/v2/posts?tags[]=6287&tags[]=6289&_fields**"
Mocked Response: [{ ... }]

Our application's tests also mocks a number of routes by default, (coming from an apiClient.initialize) including this one below. FWIW, this is defined earlier than those above:

cy:intercept ➟  // alias: getJustcookRecipes
Method: GET
Matcher: "https://wpsite.com/wp-json/**"
Mocked Response: [{ ... }]

My test code sets up those first two intercepts, and ultimately calls the routes. Here is the code, heavily abridged:

it('refreshes the recipes when switching protein tabs', () => {
  apiClient.initialize()

  /* do lots of other stuff; load up the page, perform other tests, etc */
  
  // call a function that sets up the intercepts. you can see from the cypress output
  // that the intercepts are created correctly, so I don't feel I need to include the code here.
  interceptJustCook({ skus: [beefCuts[0].id] }, [beefCut1Recipe])
  interceptJustCook({ skus: [beefCuts[0].id, beefCuts[1].id] }, twoBeefRecipes)
  
  // [#1] select 1 item; 
  // calls route associated with intercept "getRecipesSku(971520)"  
  page.click.checkboxWithSku(beefCuts[0].id)
  
  /* assert against that call */
  
  // [#2] select 2nd item (now 2 items are selected); 
  // calls route associated with intercept "getRecipesSku(971520, 971310)"
  page.click.checkboxWithSku(beefCuts[1].id)

In the Cypress logs, the first call (marked by comment #1) is intercepted correctly:

cy:fetch ➟  (getRecipesSku(971520)) STUBBED 
GET https://wpsite.com/wp-json/wp/v2/posts?tags[]=6287&_fields=jetpack_featured_media_url,title.rendered,excerpt.rendered,link,id&per_page=100&page=1&orderby=date

However, the second call (marked by comment #2) is intercepted by the wrong route mocker:

cy:fetch ➟  (getJustCookRecipes) STUBBED 
GET https://wpsite.com/wp-json/wp/v2/posts?tags[]=6287&tags[]=6289&_fields=jetpack_featured_media_url,title.rendered,excerpt.rendered,link,id&per_page=100&page=1&orderby=date

You can see for yourself that the URL called at #2 does indeed match the getRecipesSku(971520, 971310) intercept, but it is caught by the getJustcookRecipes intercept. Now, I suppose the URL for that latter intercept would catch my second custom intercept. But it would also, in the same way, catch my first custom intercept, but that first one works.

(update:) I tried commenting out the place in the code where the getJustcookRecipes intercept is created so that it doesn't exist. Now, the call that should hit getRecipesSku(971520,971310) isn't being mocked at all! I checked and the mocked and called urls are a match.

Why is this going wrong and how do I fix it?

Jonathan Tuzman
  • 11,568
  • 18
  • 69
  • 129

2 Answers2

4

Something in the glob pattern for the 2nd intercept @getRecipesSku(971520,971310) is refusing to match.

It's probably not worth while analyzing what exactly (you may not be able to fix it in glob), but switching to a regex will match.

See regex101.com online test

cy.intercept(/wpsite\.com\/wp-json\/wp\/v2\/posts\?tags\[]=6287&tags\[]=6289&_fields/, {})
  .as('getRecipesSku(971520,971310)')

enter image description here


The request query string may be badly formed

Looking at the docs for URLSearchParams, the implication is that the query string should be key/value pairs.

But the 2nd request has two identical keys using tags[] as the key.

It looks as if the correct format would be /wp/v2/posts?tags=[6287,6289] since the square brackets don't have a lot of meaning otherwise.

It may be that the server is handling the format tags[]=6287&tags[]=6289, but Cypress is probably not. If you run the following intercept you see the query object has only one tags[] key and it's the last one in the URL.

cy.intercept({
    method: 'GET',
    pathname: '/wp-json/wp/v2/posts',
  },
  (req) => {
    console.log('url', req.url)
    console.log('query', req.query)  // Proxy {tags[]: '6289', ...
  }
)
Paolo
  • 3,530
  • 7
  • 21
-1

@Paolo was definitely on the right track. The WP API we're consuming uses tags[]=1,tags=[2] instead of tags[1,2] so that's correct, but some research into globs showed me that brackets are special. Why the first bracket isn't messing it up but the second one is, I'm not sure and I kind of don't care.

I thought about switching to regex, but instead I was able to escape the offender in the glob without switching to regex and needing to escape aaaallll the slashes in the url:

const interceptRecipes = (
  { category, skus }: RecipeFilterArgs,
  recipes: Recipe[]
) => {
  let queryString = ''
  if (category) queryString = `categories[]=${CATEGORY_MAP[category]}`
  if (skus) {
    const tagIdMap = SkuToTagIdMap as Record<number, { tagId: number }>
    // brackets break the glob pattern, so we need to escape them
    queryString = skus.map((sku) => `tags\\[]=${tagIdMap[sku].tagId}`).join('&')
  }
  const url = `${baseUrl}?${queryString}${otherParams}`
  const alias = category ? `get${category}Recipes` : `getRecipesSku(${skus})`
  cy.intercept('GET', url, recipes).as(alias)
}
Jonathan Tuzman
  • 11,568
  • 18
  • 69
  • 129
  • `tags\\[]=123&tags\\[]=456` does not work. Do you have a reproducible example? – A.A.Qadosh Jan 28 '23 at 22:51
  • @A.A.Qadosh I updated my answer to make clear that the double-slash is part of the string literal. The glob pattern itself would I guess only have a single backslash. – Jonathan Tuzman Jan 30 '23 at 16:03
  • Yeah, I don't think it works either. And your clarification did the opposite. I think @A.A.Qadosh is asking you for something reproducable, so it would be much more helpful if you added the code that uses `queryString`. – Paolo Jan 30 '23 at 20:41