Real quick about jasmine.addMatchers. With the latest Jasmine build from git, it appears as though the format for doing custom matchers is vastly different than code I'm seeing in the 'Jasmine JavaScript Testing' book. In the book it has code such as:
this.actual or maybe even this.isNot
The new format is something like:
compare: function (actual, expected) {
return {
pass: some true or false statement...
}
}
So, in this case, the 'this.actual' is actually the passed in argument 'actual', which is cool. How about accessing the isNot property if we're calling a new matcher such as:
expect(investment).not.toBeAGoodInvestment();
So, inside the body of 'toBeAGoodInvestment', we should be able to access the 'isNot' property. Not sure how to do that with the new format. I figured out how to set the this.message from the old way to the new way as in:
return {
pass: some statement...,
message: 'some message'
}
The message we would want to have show up in the jasmine reporter would be dynamic based on whatever the 'isNot' is set to.
After digging around in the actual Jasmine.js source, I found out where the arguments were getting passed into custom matcher's compare function, and indeed, the 'isNot' was not making it's way in at all. The 'this.isNot' was available in the context of the 'Expectation.prototype.wrapCompare' function within the Jasmine source itself but where it was really needed was the custom matcher I created.
So now in this wrapCompare function, I simply added the args.push statement within the 'if' statement as in:
if (this.isNot) {
//-- Added this line
args.push(this.isNot);
matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
}
Now, calling the matcher, I can do this:
expect(investment).not.toBeAGoodInvestment();
And then the actual matcher it will look something like this:
toBeAGoodInvestment: function () {
return {
compare: function (actual, isNot) {
return {
pass: actual.isGood(),
message: 'Expected investment to be a ' +
((isNot) ? 'bad' : 'good') + ' investment'
}
}
};
}
Nice little research task here to figure out what Jasmine was doing behind the scenes.
Any other way to get the 'isNot' injected into the compare function, let me know.
Related
Anyone have a good solution to extending console.log so that it auto prints class name and method as a prefix? I'm using web components and use strict is turned on.
someFunction() {
let varA = "hello"
console.log(this.constructor.name, "someFunction", {varA})
}
Would like to automate this part: this.constructor.name, "someFunction", ...
arguments.callee.name will print the function name, but no longer works with strict mode turned on.
Extending console.log in a centralized location via:
export var log = function(){
var args = Array.prototype.slice.call(arguments);
args.unshift(this.constructor.name + ": ");
console.log.apply(console, args);
}
does not work as this.constructor.name does not print the correct context and if it's not in a web component, it doesn't work at all.
Extending console.log in each web component defeats the purpose (or half of it).
Could fold a function that extends console.log in the build for each web component but would still have the problem of not being able to call arguments.calleee.
Using lit-element, but this is a native javascript issue.
console.trace() may be of use here, instead of a custom console method.
Or, use the new Error()'s .stack property.
class A {
b() {
return function c() {
return function d() {
return (function e() {
return new Error().stack;
})();
}
}
}
}
console.log("here's the stack:\n", new A().b()()());
so based on sean-7777's answer, I suppose we could slice the stack output like this:
let funcname = new Error().stack.split('\n')[1];
funcname=funcname.slice(funcname.indexOf("at ")+3, funcname.indexOf(" ("));
console.log(debugline, 'debug text');
If you put it into a helper function, you could just grab line index 2 (since index 1 would give you the name of the helper function).
I was hoping for something a little more straightforward but hacking the output does work.
UPDATE:
export function prtfun() {
let debugline = new Error().stack.split('\n')[2];
return debugline.slice(debugline.indexOf("at ")+3, debugline.indexOf(" ("));
}
call it from anywhere like this:
console.log(prtfun(), 'log text...');
and it will print ClassName.FunctionName log text...
Works great. I'd disable this in production though.
I have one very interesting question.
We know that we should check every parameter in a function.
So we want to check : if param exists, if type is correct, if it is contain some property in case of JSON type etc. and it should be done in every function.
actually how we can optimize and and avoid a tons of conditional if-else?
I tried to do something like that, but I don't know if it's good. Because it just throws an error and stops a program. But I don't know how it can be done better. I thought that es7 decorators will help me but it isn't because I can't use it for the properties checking because it works only with classes and methods.
export function isExists(param) {
if (param) {
return true;
}
throw new TypeError("Param should exists. Please check it.");
}
export function isJsonObject(itemToTest) {
if (itemToTest) {
return ~Object.prototype.toString.call(itemToTest).search(/Object/);
}
throw new TypeError("itemToTest should exists if you want to test it. Please check it.");
}
export function isOwnPropertyExist(objToTest, propName) {
isExists(objToTest); // Here i want to continue a program workflow or it will be broken if my checking won't have success
isExists(prop);
isJsonObject(objToTest);
if (objToTest.hasOwnProperty(propName)) {
return true;
}
throw new TypeError("objToTest should exists and contains a property with name ");
}
Thanks for any help! This question is tormenting me a more than 2 years.
P.S. I don't want to use Typescript (I just want to know, how do you resolve this moment)
I was trying to add an additional url attribute as a function to my page-object while using nightwatchjs.
Like:
module.exports = {
url: function() {
return this.api.launchUrl + '/content/site1.xhtml';
},
cancelUrl: function() {
return this.api.launchUrl + '/content/cancel_site1.xhtml';
}
}
Anyhow nightwatch is not able to get that 2nd attribute cancelUrl, ie undefined.
Why is that so? Shouldn't nightwatch be able to access that attribute as it is nothing more than a function call returning a string or am I misunderstanding a javascript or special page-object concept?
--
I am aware that there should be a page-object for each site so there should not be a 2nd site. Anyhow I would like to understand why this is not working technically.
Not sure I can answer the "why" (other than to say that when nightwatch loads up your page objects as globally available it must be wrapping your js file and filtering on 'known' functions) but I can offer a solution: add a command to your page object with the desired function. For example:
let pageCommands = {
cancelUrl: function() {
return this.api.launchUrl + '/content/cancel_site1.xhtml';
}
};
module.exports = {
commands: [pageCommands],
...
}
It's not the typical use of page commands, but your test would then be able to access the cancelUrl function on the page object instance.
More on page commands here
I've been rewriting various bits of code I've 'inherited' and come across something I don't understand. Both jslint and jshint think the below function is a constructor, and I have no idea why.
function GEReqsDone(failed) {
if (!failed) {
alert('Thank you for your submission! The page will now be reloaded.');
document.location.replace(mwConfig.wgScript + '?title=' + encodeURIComponent(mwConfig.wgPageName) + '&action=purge');
} else {
alert('An error occurred while submitting the edit.');
button.disabled = false;
button.innerHTML = 'Update price';
}
}
This is a callback from query using $.ajax() that queries the mediawiki API to automatically edit to update a price on a page. If the edit succeeds failed is not defined and the page reloads. If it fails, failed is set to true and it resets the button used to trigger the update.
button is simply a button element, the wg* variables are part of the mediaWiki object here used to access the pagename and url prefix (usually /index.php).
Does anyone know why jshint and jslint seem to think this function should be new GEReqsDone() rather than GEReqsDone()?
Constructors are the only functions in JavaScript that should start with a capital letter. JSLint/JSHint will see that it starts with an uppercase G and assume it is a constructor.
This is the only convention we have to make sure people know that the function should be used as a constructor. Some people write defensively to avoid people missing the new keyword:
var SomeConstructor = function () {
if (!(this instanceof SomeConstructor))
return new SomeConstructor();
}
if (!window['console']) {
window.console = {
log: function(msg) {}
}
}
$(window).ready(function() {
Site.onReady();
});
var Site = {
host: null,
path: null,
etc..
And there have var Helpers, var Site, looks pretty good, but can't understand the purpose? Anyone who knows that?
if (!window['console']) {
window.console = {
log: function(msg) {}
}
}
This checks to see if there's anything currently assigned to window.console already and if there's not, it assigns a custom object that has a 'log' function. This makes window.console.log usable no matter what, and if there's already a native (or earlier defined) version of the function, it will be used.
$(window).ready(function() {
Site.onReady();
});
var Site = {
host: null,
path: null,
etc..
I have no idea what this is for, but Site is undefined at the time it is placed into the anonymous callback for $(window).ready(), which is something that should be avoided (just place the $(window).ready() below where site is defined)
As for this specific snippet:
$(window).ready(function() {
Site.onReady();
});
this passes an anonymous function to the $(window).ready() function, which will call it when the DOM is ready. Using an anonymous function directly avoids the need to name a function and then pass it in later.
function myFunc() { //we can use myFunc anywhere now, which may be unwanted
Site.onReady();
}
$(window).ready(myFunc);
Lastly:
var Site = {
host: null,
path: null,
etc..
The var myVar = {key1:"value", key2:"other_value"}; syntax creates a new object with keys and values that can be used like this: myVar.key1 = "newValue!"
Looks like it initializes several global objects that are expected on the page. For example console, which is available in Firefox/Firebug for logging, but not other browsers. So by checking for existence of window['console'] and adding it when necessary, you can trust in the JavaScript code you can call console.log() without causing an error.
I assume Site, Helpers, etc all do something similar.
its defining a 'console' object literal on the window object, if it is not already there, which has a function log. This means in your code you can write
console.log('something')
even if the browser doesn't support it.