Typescript syntax - How to spy on an Ajax request? - ajax

I am trying to write a unit test where I want to verify that a ajax call has been made.
The code is simple :
it('test spycall',()=>{
spyOn($,"ajax");
//my method call which in turns use ajax
MyFunc();
expect($.ajax.calls.mostRecent().args[0]["url"].toEqual("myurl");
});
The error that I get :
Property 'calls' doesn't exist on type '{settings:jqueryAjaxSettings):jQueryXHR;(url:string, settings?:JQueryAjaxSettings}

$.ajax.calls, among others, is part of the Jasmine testing framework, not JQuery itself (As you know, Jasmine (or rather, Jasmine-Jquery, the plugin you're using) is adding certain debugging functions to JQuery's prototype in order to, well, be able to test ajax calls).
The bad part is that your .d.ts typescript definition file, the file that acts as an interface between typescript and pure JS libraries isn't aware of Jasmine's functions.
There are several ways you could approach fixing this, like
looking if someone has adjusted the JQuery .d.ts file for Jasmine's functions or
creating the new .d.ts file yourself by modifying the original one or, (what I would be doing)
overwriting the typescript definition by declaring $.ajax as any, or not including the typescript definition at all in your testing codebase and declaring $ as any.

There are 2 ways to get rid of the error:
// 1
expect(($.ajax as any).calls.mostRecent().args[0].url).toEqual("myurl");
// 2
let ajaxSpy = spyOn($,"ajax");
expect(ajaxSpy.calls.mostRecent().args[0].url).toEqual("myurl");
You can also use partial matching:
expect(($.ajax as any).calls.mostRecent().args).toEqual([
jasmine.objectContaining({url: "myurl"})
]);

Related

Cypress command vs JS function

The Cypress documentation suggests that commands are the right way to reuse fragments of code, e.g.
Cypress.Commands.add("logout", () => {
cy.get("[data-cy=profile-picture]").click();
cy.contains("Logout").click();
});
cy.logout();
For simple cases like this, why would I use a command over a plain JS function (and all the nice IDE assistance that comes with it). What are the drawbacks of rewriting the above snippet as
export function logout(){
cy.get("[data-cy=profile-picture]").click();
cy.contains("Logout").click();
}
// and now somewhere in a test
logout();
Based on my experience with Cypress (one year project and several hundred test cases), I can say that a plan JS function is great for grouping cy commands.
From my point of view, a custom cy command may be really useful only if it is incorporated into the chain processing (utilizes the subject parameter or returns a Chainable to be used further in the chain). Otherwise, a plain JS function is preferable due to it simplicity and full IDE support (unless you're using an additional plugin).
If you for any reason need to do something inside the cypress loop, you can always wrap you code by cy.then() in a plain JS function:
function myFunction() {
cy.then(() => {
console.log(("I'm inside the Cypress event loop"))
})
}
Commands are for behavior that is needed across all tests. For example, cy.setup or cy.login. Otherwise, use functions.
See official docs: https://docs.cypress.io/api/cypress-api/custom-commands#1-Don-t-make-everything-a-custom-command

QUnit exports global functions

I'm trying to get QUnit to work with my production environment, and noticed that QUnit exports a log function with this definition:
function ( callback ) {
config[key].push( callback );
}
Why does it do this? There seem to be a hole bunch of functions that are exported globally, like begin, deepEqual, done, etc etc. Isn't it better if all these functions are within a namespace instead?
Shouldn't this behaviour be noted in the documentation? I had another log function defined in my codebase (shorthand for console.log), and this rendered strange bugs from QUnits side because log("a log message") is not correct usage of QUnit's log function.
Is there a way to contain QUnit within its namespace? Giving the code an object instead of window does not work.

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.

Microsoft AJAX: Unable to get property 'x' of undefined or null reference

How do I troubleshoot the following error being thrown by a Microsoft AJAX JavaScript framework method? It is an automatically generated line of JavaScript from a custom User Control in a Web Forms App (Sitefinity 5 CMS)
Error Message:
Unable to get property 'FancyBlockDesigner' of undefined or null reference
Here is the JavaScript that is throwing the error:
Sys.Application.add_init(function() {
$create(SitefinityWebApp.Esd.TheLab.SampleHtmlEditor.FancyBlockDesigner, null, null, {"Editor":"propertyEditor_ctl00_ctl00_ctl00_ctl00_ctl00_Editor","propertyEditor":"propertyEditor"}, $get("propertyEditor_ctl00_ctl00_ctl00"));
});
Rather than discuss the ascx and cs files that try to abstract this detail away from me, I want to know what this error means. If I understand the detail, the abstraction might make more sense.
"$create" function in ASP.NET Ajax creates an instance of JavaScript class. Microsoft had their own opinion on how to make JavaScript object orientated and as time had shown, their approach wasn't exactly perfect.
Anyhow, to try to explain what is happening, let me give a bit of an overview oh how it works. We start by a server side control which implements IScriptControl interface which mandates two members: GetScriptDescriptors and GetScriptReferences. The second one is pretty straightforward - it lets you register references to all JavaScript files that you control will require. The GetScriptDescriptors, on the other hand, let's you define all the instances of JavaScript classes you want to use as well as it lets you set their properties - initialize them, if you will.
What the autogenerated JavaScript code you've pasted says is basically that you have defined in GetScriptDescriptors that you will need an instance of type "SitefinityWebApp.Esd.TheLab.SampleHtmlEditor.FancyBlockDesigner" where you want Editor property to be initialized. This code will go and look for a JavaScript constructor that looks like this:
function SitefinityWebApp.Esd.TheLab.SampleHtmlEditor.FancyBlockDesigner(element) {
}
that most probably also has a prototype defined, something like:
SitefinityWebApp.Esd.TheLab.SampleHtmlEditor.FancyBlockDesigner.prototype = {
}
Now, since the error you have posted states: "Unable to get property 'FancyBlockDesigner' of undefined or null reference", most probably one of the following is the problem:
You have not included the JavaScript file which contains the class (constructor + prototype) that I've talked about above
You have forgot to add the "FancyBlockDesigner" to the constructor (it seems that you do have other object, perhaps through MS Ajax namespaces - "SitefinityWebApp.Esd.TheLab"
You have not registerd the "SampleHtmlEditor" namespace. Make sure at the top of your JS file you have this: Type.registerNamespace("SitefinityWebApp.Esd.TheLab.SampleHtmlEditor");
So, short story long, the function with name "SitefinityWebApp.Esd.TheLab.SampleHtmlEditor.FancyBlockDesigner" cannot be found.
Hope this helps,
Ivan

ChemDoodle Ajax Incompatibility with Pollen.js

I'm trying to use iChemLabs cloud services from a html5 web worker. Normally the cloudservices requires jQuery but I can't import that into a web worker so I'm using Pollen instead with a ChemDoodle Web Components library with which I have stripped out the document-related things.
jQuery.Hive.Pollen provides a nice ajax function very similar to jQuery, but I can't seem to get it to work at all. I know this problem will be tricky to solve considering that Access-control-headers need to be set to allow any of you to actually find the solution. However, I'm a beginning javascript programmer and I was wondering if my two weeks of frustration is actually a small difference. I am trying to invoke the following function:
var cloudmolecule;
ChemDoodle.iChemLabs.readSMILES('N1(C)C(=O)N(C)C(C(=C1N1)N(C=1)C)=O', function(mol){
cloudmolecule = mol;
});
Here is a link to the library code I am using, see the 'q.ajax' call and substitute jQuery = q for p = q (p is for pollen) in that block of code.
Right now I'm just trying to get the ajax call to work in an ordinary block of javascript with the plan to migrate to a web worker later.
If anybody could point out the problem to me I would be extremely grateful.
solved! turns out iChemLabs rejects these two extra headers that pollen creates:
_xhr.setRequestHeader("X-Requested-With", "Worker-XMLHttpRequest");
_xhr.setRequestHeader("X-Worker-Hive", "Pollen-JS" );
Simply comment them out
Also, Pollen ajax seems to return a JSON object containing the data in JSON format AND as a string, so do
o = JSON.parse(data.string)//data is the parameter to the callback function
The reduced ChemDoodle library (without document-related methods) will work like a charm with pollen ajax.

Resources