Wrong Cypress intercept being hit - cypress

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?

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)')
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 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)
}

Related

Use Jmeter function value as variable in request

I'm trying to wrap my head around Jmeter functions and how to actually use them in an HTTP JSON request. All examples I've found on function use them as titles in HTTP requests, which is not helpful.
All I want to do is this, and then use that variable in my POST request. But that doesn't work and I have no clue why not.
...
"dates": {
"invoiceDate": ${testdate},
...
In all examples I've seen, the function is set as the HTTP sampler name, like so:
This is not helpful at all since I can't use that as a variable.
I don't think 20211 is the valid year, do you mean 2021?
If you put the function inside User Defined Variables - it will be evaluated only once and won't be random, you should rather inline it into the request body as User Defined Variables are evaluated only once, when the test starts
If you're trying to post JSON - I think you should surround the function with quotation marks, otherwise the JSON will be invalid. Suggested change:
{
"dates" : {
"invoiceDate": "${__RandomDate(,,2021-06-20,,)}"
}
}

Is there a way to properly drill down into a JSON response with random property names

I'm trying to test an API response where key values are randomized alphanumerics. This is making it difficult for me to drill down into the JSON response to get the data I want to test.
I am using SuperTest/Mocha/Chai. At this point I'm just trying to test to see if the property 'id', 'name', and 'pattern' exist, and to verify the values of those properties.
Unfortunately since the parent of those properties is a randomized value, i've been unable to access it.
I'm new to API testing in general, so I apologize if I'm not including some important information. Normally I would do something like this:
Example of expects I normally write:
end(function(err, res) {
expect(res.body).to.have.property('id');
expect(res.body.id).to.equal(0);
}
So far, the only way I've found to do it is to put response.text into a variable, then use split and splice to separate out the data I want. This is ugly and probably inefficient.
Example JSON I'm working with:
{ idTag1: 'randomValue',
idTag2:
{ 'randomValue':
{ id: 'an integer',
name: 'a basic string',
pattern: 'a basic string'
}
}
}

how to send regex to server side in meteor

In my application, I'm building a query object some thing like below
Object {pointType: /analog/i, _id: Object}
I tried to store it in session variable,
Session.set("currentPointsQueryObject",queryObj);
Then on click event I'm getting this object
var res= Session.get("currentPointsQueryObject");
console.log(res);
but here I'm getting like below
Object {pointType: Object, _id: Object}
Meanwhile, I sent group_id to the server
by geting it from session variable like
var group_id=Session.get("currentGroupId");
which is working fine(it is displaying id in server log)
Then, I've tried storing it in global variable, which returning as expected
like below on click event
Object {pointType: /analog/i, _id: Object}
but when I sent it to server side method (Immediate line after console.log() )
Meteor.call("updateGroupPoints",res,function(err,data){
console.log("updated points");
console.log(data);
});
when I log res in server console, it is showing
{ pointType: {}, _id: { '$nin': [] } }
Althoug I have something in pointType, It is not passed to the server.
Anyone had idea, Is this the thing related storing?
You cannot directly serialize RegExp to EJSON, but you can:
var regexp = /^[0-9]+$/;
var serialized = regexp.source;
Send serialized and then deserialize:
new RegExp(serialized)
Take a look at : Meteor: Save RegExp Object to Session
/analog/i is a regular expression, right? Values stored in Session and values sent to methods must be part of EJSON values. Regular expression aren't.
There's a handy way to teach EJSON how to serialize/parse Regular Expressions (RegExp) as of 2015 documented in this SO question:
How to extend EJSON to serialize RegEx for Meteor Client-Server interactions?
Basically, we can extend the RegExp object class and use EJSON.addType to teach the serialization to both client and server. Hope this helps someone out there in the Universe. :)
Simply stringify your RegExp via .toString(), send it to the server and then parse it back to RegExp.

Backbone navigate triggers twice in Firefox

Trying to use Backbone's navigate property.
this.navigate("week/" + companyName + "/" + employeeNo + "/" + weekEnd, { trigger: true, replace: false });
The code above is executed once.
It hits this:
routes: {
"week/:companyName/:employeeNo/:weekEnd": "getWeek"
},
And then this function gets hit twice:
getWeek: function (companyName, employeeNo, weekEnd) {
console.log('getWeek:', companyName, employeeNo, weekEnd);
}
It is logged twice in Firefox, only once in IE and Chrome.
What's the issue here? I originally didn't even have trigger set to true, and Firefox ignored that and still triggered the URL.
I had a similar issue recently with Firefox doing two server calls after a Backbone.navigate. In my case it was because we had not encoded the string. Does your company name have any characters which should be encoded?
You could try:
this.navigate("week/" + escape(companyName) + "/" + employeeNo + "/" + weekEnd, { trigger: true, replace: false });
Stepping in as I've run into the same issue and got to the underlying problem here.
As everyone mentioned before, the problem comes from URL encoding. Now as to why the issue only appears in Firefox...
Let's start by summarizing quickly how the routes are called when the hash changes. There are 3 key functions here:
loadUrl: this function is the one that will call your route handler.
navigate: this is the function used to change the route manually. If the trigger flag is set to true, the function will call loadUrl.
checkUrl: this function is set as callback for the onhashchange event on the window object (when it's available of course). It also runs loadUrl on certain conditions.
Now, we're getting to the interesting part.
When you run navigate, Backbone will cache the fragment you navigated to. The hash changing, checkUrl will also be called. This function will then check if the cached hash equals the current one, so as not to execute loadUrl if you called navigate before, because it would mean it has already been called. To make that comparison, checkUrl gets the current hash with the function getFragment, which uses getHash. Here is getHash's code:
getHash: function(window) {
var match = (window || this).location.href.match(/#(.*)$/);
return match ? match[1] : '';
},
And you got your problem. location.href is URI-encoded in firefox, but is not in chrome. So if you navigated to another hash (with or without the trigger flag), in firefox, Backbone will cache the unencoded version of your hash, and then compare it with the encoded version. If your hash contained a should-be-encoded character, the result of the comparison will be negative, and Backbone will execute the route handler it should not execute.
As per the solution, well, folks said it before, your URIs should be encoded.
Question may be old, but for me this was still relevant. Encoding the url wasn't enough in my case. I replaced the GetHash() function in Backbone with:
getHash: function (t) {
var e = (t || this).location.href.match(/#(.*)$/);
return match ? this.decodeFragment(match[1]) : '';
}

How do I call an SSJS method with parameters from javascript

I have a url containing a hash e.g http://www.acme.com/home.xsp#key=1234
When the url above loads in the browser I need to call a serverside javascript based on the value in the hash.
I have found a few ways of retriving the hash client side like this
var key = getHashUrlVars()["key"];
so I have the key available in my client side script in the onclientload event.
So in the same onClientLoad event I now need to call my server side javascript method so I have tried the following
'#{javascript:doStuff(key)}'
'#{javascript:doStuff(' + key + ')}'
..and a few other ways. but I can't get it to work.
maybe there is an XSP command I can use instead?
any ideas how to solve this?
You could do a XSP.partialRefreshPost in CSJS and use parameters to send your data to the server:
var p = { "key": getHashUrlVars()["key"] }
XSP.partialRefreshPost( '#{id:_element_to_refresh_}', {params: p} );
To access the parameters in SSJS just try this:
doStuff( param.key )
You could use an empty div-element as a target execute the SSJS code. Or you can use the executeOnServer - method: http://xpages.info/XPagesHome.nsf/Entry.xsp?documentId=88065536729EA065852578CB0066ADEC
Hope this helps
Sven

Resources