Cypress: Switching from cy.route() to cy.intercept() - cypress

It seems that most people I read about experence zero trouble with this. I, on the other hand, have a test suite which someone else wrote, in which I'm trying to replace route() with intercept(). The API intercepts are done to handle button clicks etc., and about 99.9% percent of them fails if I just replace it. So, there's obviously some syntax in/use of intercept() I've not found a description for.
Example:
This works:
cy.route('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', result);
This does not work. The button click is not executed:
cy.intercept('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', result);
I've tried adding '**' in front of "/prosjekt...", and I've tried removing 'POST', with no luck.
Any ideas? I'll gladly post more info if necessary.
UPDATE:
Futher attempts:
Getting some hints here and there, it seems that this is a more correct way of using intercept():
return cy.intercept('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', {
body: result
});
This doesn't work, either.
The variables result in these examples is an object describing what is sent back to the frontend of the POST-request in the route matches the api path.
For troubleshooting, I can see that when using intercept(), there is ONE route that is not working when using intercept (the bottom one in the picture). However, I cannot for the life of me see why, and how the route match can be written differently?

Most likely, you're mixing the old use of cy.route() and cy.server(). In my experience, those two won't work well together. It's easier when you're starting fresh with just cy.intercept().
Your update is correct too; You have to encapsulate the return value you want mocked in {body: value}.

from what I am seeing in your circled screenshot, the API is not called after you try to intercept it. (the count under # column is -)
You need to track when the API is to be called and ensure you intercept before the call is made. Cypres can help you with this. You can go through the run steps in the cypress window.
You could also share this if you don't mind.
If you are 100% certain the button makes the call. Steps should be:
cy.intercept()
cy.get('button').click()
In the cypress window, right after the click, you should see the API being called.

Related

Cypress. Why is my route alias not matching?

My POST request to ocr/receipt is never matched. I've...
created a route, matching **/ocr/**, specifying POST, and given it an alias.
called wait() with a long timeout.
I can watch the request complete in the network pane while the wait spinner turns happily in the test pane. Why is Cypress not matching this route?
beforeEach(function () {
cy.route('POST','**/ocr/**').as('ocr');
});
it('Création frais depuis le bouton « appareil photo »', function () {
cy.get('.in-progress').first().click()
cy.wait('#ocr', {'timeout':15000});
cy.get('#grpChoices > :nth-child(1)').click();
});
Well who would have guessed. Method is case sensitive, and it only works in lower case. So...
route('post','**/ocr/**').as('ocr')
fixed it.
The doc won't help you.
The other recurring reason for routes not triggering is if your app uses the fetch api. Fetch is not compatible with cypress.
In my case I defined the same alias in cy.route().as('acme') and cy.request().as('acme'). Even though the Cypress docs points out that cy.wait does not support requests it does not point out that aliases should be unique. Rename or removing cy.request.as('foo') solves this problem.
Make sure for all requests, you have a response.

url::to(xxx/yyy) returns different results depending on context

I'm using the URL::to call to embed a link in an outgoing mail message. What I get when I do this is something like: "baseroot/public/index.php/xxx/yyy".
And yet when I do the same call, for example, within a route call, I get "baseroute/xxx/yyy".
Any idea?
The source of URL::to resides at
http://laravel.com/api/source-class-Illuminate.Routing.UrlGenerator.html#76-98
(linked to from http://laravel.com/api/class-Illuminate.Routing.UrlGenerator.html).
I suggest you add debug printing to your copy and see what values $this->getScheme() and $this->getRootPath() yield. These must be the source of the discrepancy, apparently caused by different this objects.
I had a very similar problem with URL::to('user/123') returning an incorrect value when visiting the homepage vs. another page. After some investigation, in my case it was a matter of case-sensitivity (!) in the request's url. I hope it's somehow related to your mysterious case.
More about my case: URL:to('user/123') gave me different results whether I visited http://localhost/MyApp/public/someurl or http://localhost/Myapp/public/someurl. In the former it gave the correct result http://localhost/MyApp/public/user/123, while the latter gave the wrong result of http://localhost/user/123.
.
From here, less important notes from my investigation, for future Laravel archaeologists. I hope I'm not talking all nonsense. I am new to Laravel, using a local Laravel 4 installation + WAMP on a Windows machine.
UrlGenerator's to() method uses $root = $this->getRootUrl($scheme);. The latter uses $this->request->root();, where request is \Symfony\Component\HttpFoundation\Request.
Request::root() indeed defaults to a wrong value e.g. http://localhost when visiting someurl with the incorrect case.
The culprit is Symfony\Component\HttpFoundation\Request (in vendor\symfony\http-foundation\Symfony\Component\HttpFoundation\Request.php). Its getBaseUrl() calls prepareBaseUrl(), and there the actual logic of comparing the requestUri with the baseUrl is finally performed.
For the few archaeologists still following, in my case the $baseUrl was /MyApp/public/index.php while the $requestUri was /Myapp/public/someurl, which sadly led the code to not satisfy this conditional:
if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, dirname($baseUrl))) {
return rtrim($prefix, '/');
}

Modernizr.load() confusion with Yepnope

I'm more than a little confused about Modernizr and it's relation to Yepnope.js. As I understand it, Modernizr comes with Yepnope.js (assuming you select the Modernizr.load() option). According to the Yepnope documentation, there are optional prefix plugins which can used. For example, you can test for versions of IE (assuming you also load the yepnope.ie-prefix.js script). However, when I attempt to run the following, I get 'undefined' alert:
Modernizr.load({
load: 'ie!my-ie-specific.js',
complete : function (url, result, key){
alert(url, result, key);
}
});
What am I doing wrong? Does Modernizr include Yepnope completely or only bits and pieces?
I got stuck on this too. Struggled for an hour.
The complete callback doesn't support tests. Change it to callback which fires when the external script is loaded.
complete runs when all scripts are loaded, OR if nothing is loaded.
I wish complete took the result variable though. I could use that.

Meteor 0.5.9: replacement for using Session in a server method?

So, I was attempting to do something like the following:
if(Meteor.isServer){
Meteor.methods({connect_to_api: function(vars){
// get data from remote API
return data;
}});
}
if(Meteor.isClient){
Template.myTpl.content = function(){
Meteor.call('connect_to_api', vars, function(err,data){
Session.set('placeholder', data);
});
return Session.get('placeholder');
};
}
This seemed to be working fine, but, of course, now breaks in 0.5.9 as the Session object has been removed from the server. How in the world do you now create a reactive Template that uses a server-only (stuff we don't want loading on the client) method call and get data back from that Method call. You can't put any Session references in the callback function because it doesn't exist on the server, and I don't know of any other reactive data sources available for this scenario.
I'm pretty new to Meteor, so I'm really trying to pin down best-practices stuff that has the best chance of being future-proof. Apparently the above implementation was not it.
EDIT: To clarify, this is not a problem of when I'm returning from the Template function. This is a problem of Session existing on the server. The above code will generate the following error message on the server:
Exception while invoking method 'connect_to_api' ReferenceError: Session is not defined
at Meteor.methods.connect_to_api (path/to/file.js:#:#)
at _.extend.protocol_handlers.method.exception ... etc etc
Setting the session in the callback seems to work fine, see this project I created on github: https://github.com/jtblin/meteor_session_test. In this example, I return data in a server method, and set it in the session in the callback.
There are 2 issues with your code:
1) Missing closing brace placement in Meteor.methods. The code should be:
Meteor.methods({
connect_to_api: function(vars) {
// get data from remote API
return data;
}
});
2) As explained above, you return the value in the session, before the callback is completed, i.e. before the callback method had the time to set the session variable. I guess this is why you don't see any data in the session variable yet.
I feel like an idiot (not the first time, not the last). Thanks to jtblin for showing me that Session.set does indeed work in the callback, I went back and scoured my Meteor.method function. Turns out there was one spot buried in the code where I was using Session.get which was what was throwing the error. Once I passed that value in from the client rather than trying to get it in the method itself, all was right with the world.
Oh, and you can indeed order things as above without issue.

Adding custom code to mootools addEvent

Even though I've been using mootools for a while now, I haven't really gotten into playing with the natives yet. Currently I'm trying to extend events by adding a custom addEvent method beside the original. I did that using the following code(copied from mootools core)
Native.implement([Element, Window, Document], {
addMyEvent:function(){/* code here */}
}
Now the problem is that I can't seem to figure out, how to properly overwrite the existing fireEvent method in a way that I can still call the orignal method after executing my own logic.
I could probably get the desired results with some ugly hacks but I'd prefer learning the elegant way :)
Update: Tried a couple of ugly hacks. None of them worked. Either I don't understand closures or I'm tweaking the wrong place. I tried saving Element.fireEvent to a temporary variable(with and without using closures), which I would then call from the overwritten fireEvent function(overwritten using Native.implement - the same as above). The result is an endless loop with fireEvent calling itself over and over again.
Update 2:
I followed the execution using firebug and it lead me to Native.genericize, which seems to act as a kind of proxy for the methods of native classes. So instead of referencing the actual fireEvent method, I referenced the proxy and that caused the infinite loop. Google didn't find any useful documentation about this and I'm a little wary about poking around under the hood when I don't completely understand how it works, so any help is much appreciated.
Update 3 - Original problem solved:
As I replied to Dimitar's comment below, I managed to solve the original problem by myself. I was trying to make a method for adding events that destroy themselves after a certain amount of executions. Although the original problem is solved, my question about extending natives remain.
Here's the finished code:
Native.implement([Element, Window, Document], {
addVolatileEvent:function(type,fn,counter,internal){
if(!counter)
counter=1;
var volatileFn=function(){
fn.run(arguments);
counter-=1;
if(counter<1)
{
this.removeEvent(type,volatileFn);
}
}
this.addEvent(type,volatileFn,internal);
}
});
is the name right? That's the best I could come up with my limited vocabulary.
document.id("clicker").addEvents({
"boobies": function() {
console.info("nipple police");
this.store("boobies", (this.retrieve("boobies")) ? this.retrieve("boobies") + 1 : 1);
if (this.retrieve("boobies") == 5)
this.removeEvents("boobies");
},
"click": function() {
// original function can callback boobies "even"
this.fireEvent("boobies");
// do usual stuff.
}
});
adding a simple event handler that counts the number of iterations it has gone through and then self-destroys.
think of events as simple callbacks under a particular key, some of which are bound to particular events that get fired up.
using element storage is always advisable if possible - it allows you to share data on the same element between different scopes w/o complex punctures or global variables.
Natives should not be modded like so, just do:
Element.implement({
newMethod: function() {
// this being the element
return this;
}
});
document.id("clicker").newMethod();
unless, of course, you need to define something that applies to window or document as well.

Resources