What is the difference between ".toBeCalled" and ".toHaveBeenCalled"? - jestjs

When should each of these methods be called, and why?
const spyStart = spyOn(el, 'func1');
expect(spyStart).toHaveBeenCalled();
const spyStart = spyOn(el, 'func1');
expect(spyStart).toBeCalled();

Actually .toBeCalled is an alias to .toHaveBeenCalled, so they do the same thing.
From the docs:
.toHaveBeenCalled() Also under the alias: .toBeCalled()

Related

Is it absolutely the case that arrays are used by reference in other modules in Node.js?

I have
// file cars.js
var bodyshop = require('./bodyshop')
var connections = [];
many functions which operate on connections. adding them, changing them etc.
code in this file includes things like
bodyshop.meld(blah)
bodyshop.mix(blah)
exports.connections = connections
and then
// file bodyshop.js
let cars = require('./cars');
even more functions which operate on connections. adding them, changing them etc.
code in this file includes things like
cars.connections[3].color = pink
cars.connections.splice(deleteMe, 1)
module.exports = { meld, mix, flatten }
Is it absolutely honestly the case that code in bodyshop such as cars.connections.splice(deleteMe, 1) will indeed delete an item from "the" connections (ie, the one and only connections, declared in cars.js) and code in bodyshop such as cars.connections[3].color = pink will indeed change the color of index 3 of "the" self-same one and only connections?
Is it quite OK / safe / acceptable that I used the syntax "module.exports = { }" at the end of bodyshop, rather than three lines like "exports.meld = meld" ?
Is this sentence indeed to totally correct?? "In Node.js if you export from M an array, when using the array in another module X which requires M, the array will be by reference in X, i.e. not by copy" ... ?
I created two files with the following methods and the array as you mentioned.
First File: test1.js
const testArray = [];
const getArray = () => {
return testArray;
};
module.exports = {
testArray,
getArray
}
Second File: test2.js
const { testArray, getArray } = require('./test1');
console.log('testing the required array before modifying it');
console.log(getArray());
testArray.push('test');
console.log('testing the method result after modifying the required array content');
console.log(getArray());
If you can create the mentioned files and run them locally, you will see the following result.
>node test2.js
testing the required array before modifying it
[]
testing the method result after modifying the required array content
[ 'test' ]
The points observed is,
yes, it's okay if you want to export it with the syntax module.exports = { }, It not much an issue.
If any of the methods modify this array outside of the required file, it will affect here as well, This because require will be a reference, not a copy.
The one possible solution will be creating a JSON copy of it while requiring as below:
const { testArray, getArray } = require('./test1');
const testArrayCopy = JSON.parse(JSON.stringify(testArray));
console.log('testing the required array before modifying it');
console.log(getArray());
testArrayCopy.push('test');
console.log('testing the method result after modifying the required array content');
console.log(getArray());
This is the result:
>node test2.js
testing the required array before modifying it
[]
testing the method result after modifying the required array content
[]
Note: JSON copy will not help you in parsing DateTime properly.

Compact way to assign nested property to root of a new object

I'm wondering if there's a compact way to pull a property from an object, and then assign said property to the root of a new object, using the same property name.
Basically, I'd like to do the following without needing the first line:
const targetProp = someObj.data.targetProp;
const newObj = {
targetProp
}
What I'd imagine it might look like:
const newObj = {
[someObj.data.targetProp]
}
Where newObj would then have a property named 'targetProp', with the value of someObj.data.targetProp
There's no need for an extra variable:
const newObj = {
targetProp: someObj.data.targetProp
}
Destructuring is an option, which will reduce the size of the original code, but that requires keeping a first line:
const { targetProp } = someObj.data;
const newObj = { targetProp };
I don't think there's anything better than these two options.

Questions regarding Node.js syntax

everyone!
I am reading some code in an attempt to learn node.js, it's available here.
Anyways, I have a few questions regarding some of the syntax of JS. The first bits are in index.html
1.
var argv = require("minimist")(process.argv.slice(2), {
default: { albums: true }
});
What is going on after the comma? Are we setting default values? We never declared albums, so then how are we setting a default value?
2.
What do we call it when we have a module, then a statement in parenthesis? Is this part of overriding the constructor?
var sinceDate = require("moment")(argv.sinceDate, "YYYY/MM/DD");
var sinceDate = require("moment")(argv.sinceDate, "YYYY/MM/DD");
if (!sinceDate.isValid()) {
require("debug")("download")(
"invalid sinceDate '" +
argv.sinceDate +
"', date filter disabled (get all)."
);
sinceDate = 0;
}
In get_all.js , it is used in the third line.
var debug = require("debug")("json");
Thanks a bunch!
To understand this you need to first understand in JavaScript function are First-Class Functions, mean functions can be treated as regular variables. Thus you can pass those as arguments to other functions aka callbacks, or you can return a function from a function aka Closure. Also you can store functions into another variables.
For more: https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function
Answers to your questions:
1.
var argv = require("minimist")(process.argv.slice(2), {
default: { albums: true }
});
What is going on after the comma? Are we setting default values? We never declared albums, so then how are we setting a default value?
Answer:
Yes we are setting default values, but not for the variables you declared rather you are passing these default values to "minimist" module. This module is probably using albums and what you asked it that the default value for albums is true.
2.
What do we call it when we have a module, then a statement in parenthesis? Is this part of overriding the constructor?
var sinceDate = require("moment")(argv.sinceDate, "YYYY/MM/DD");
var sinceDate = require("moment")(argv.sinceDate, "YYYY/MM/DD");
if (!sinceDate.isValid()) {
require("debug")("download")(
"invalid sinceDate '" +
argv.sinceDate +
"', date filter disabled (get all)."
);
sinceDate = 0;
}
In get_all.js , it is used in the third line.
var debug = require("debug")("json");
Answer:
As we discussed above functions being First-Class Functions. Here the "moment" module is returning a constructor function and you are calling that constructor function just after requiring it. Though it can be done as follow:
var moment = require("moment");
var sinceDate = moment(argv.sinceDate, "YYYY/MM/DD");
In above code, I required moment library once and used it as constructor function for sinceDate.
The same concept is for module debug, its returning a function and you are calling that function just after require with argument json.

chai-smoothie: to.eventually.be.displayed on a function doesn't work?

First-off, thanks for the chai-smoothie!
I've been trying to polish some of the test code and use chai-smoothie more, but I have ran into some trouble:
This is what it looked like before:
return expect(Promise.all([
userMenuPage.userMenuItemIcon.isDisplayed(),
userMenuPage.userMenuItemText().isDisplayed()
])).to.eventually.be.eql([true, true])
and this is how I expected it to work in chai smoothie
expect(userMenuPage.userMenuItemIcon).to.eventually.be.displayed
expect(userMenuPage.userMenuItemText()).to.eventually.be.displayed
when running the new code, I receive the following error:
Step Definition: steps/userMenuSteps.js:23
Message:
TypeError: assertion._obj.locator is not a function
at <repo>/tests/node_modules/chai-smoothie/lib/src/chai-smoothie.ts:42:65
at <repo>/tests/node_modules/protractor/built/element.js:798:32
at ManagedPromise.invokeCallback_ (<repo>/tests/node_modules/selenium-webdriver/lib/promise.js:1379:14)
at TaskQueue.execute_ (<repo>/tests/node_modules/selenium-webdriver/lib/promise.js:2913:14)
at TaskQueue.executeNext_ (<repo>/tests/node_modules/selenium-webdriver/lib/promise.js:2896:21)
at asyncRun (<repo>/tests/node_modules/selenium-webdriver/lib/promise.js:2775:27)
at <repo>/tests/node_modules/selenium-webdriver/lib/promise.js:639:7
at process._tickCallback (internal/process/next_tick.js:103:7)
Page model definition looks like this:
userMenuItemText: () => $('#desktop-menu #desktop-menu-classic span').getText(),
userMenuItemIcon: $('#desktop-menu #desktop-menu-classic .fa-fjunk'),
The issue is with the second row "userMenuPage.userMenuItemText().isDisplayed()". If I just use "userMenuPage.userMenuItemIcon.isDisplayed()", then I get no problem, and if I just use "userMenuPage.userMenuItemText().isDisplayed()", then I get the failure.
Do you have any recommendationts on how to overcome this and still use chai smoothie?
The problem is caused by the assertion to.eventually.be.displayed applied to the text of the element, rather than the element itself.
Update: as of version 0.2.1, chai-smoothie can work with either.
Hope this helps!
Jan
Nevertheless, it might be cleaner to assert on the element to verify its visibility, and on its text to verify the contents:
So instead of:
const userMenuPage = {
userMenuItemText: () => $('#desktop-menu #desktop-menu-classic span').getText(),
userMenuItemIcon: $('#desktop-menu #desktop-menu-classic .fa-fjunk')
}
maybe try:
const userMenuPage = {
userMenuItem: $('#desktop-menu #desktop-menu-classic span'),
userMenuItemIcon: $('#desktop-menu #desktop-menu-classic .fa-fjunk')
}
and then:
expect(userMenuPage.userMenuItem).to.eventually.be.displayed
expect(userMenuPage.userMenuItem.getText()).to.eventually.equal('expected text')
With Serenity/JS you could also define the page object as follows:
class UserMenuPage {
static Item = Target.the('Menu item').located(by.css('#desktop-menu #desktop-menu-classic span');
static Item_Text = Text.of(UserMenuPage.Item);
static Item_Icon = Target.the('Menu icon').located(by.css('#desktop-menu #desktop-menu-classic .fa-fjunk');
}
which allows you to define "questions" such as UserMenuPage.Item_Text in the same place as targets and avoid having to call .getText() in the scenario.

How to use dot(.) to MongoDB(Mongoose) schema as a parameter [duplicate]

It's difficult to explain the case by words, let me give an example:
var myObj = {
'name': 'Umut',
'age' : 34
};
var prop = 'name';
var value = 'Onur';
myObj[name] = value; // This does not work
eval('myObj.' + name) = value; //Bad coding ;)
How can I set a variable property with variable value in a JavaScript object?
myObj[prop] = value;
That should work. You mixed up the name of the variable and its value. But indexing an object with strings to get at its properties works fine in JavaScript.
myObj.name=value
or
myObj['name']=value (Quotes are required)
Both of these are interchangeable.
Edit: I'm guessing you meant myObj[prop] = value, instead of myObj[name] = value. Second syntax works fine: http://jsfiddle.net/waitinforatrain/dNjvb/1/
You can get the property the same way as you set it.
foo = {
bar: "value"
}
You set the value
foo["bar"] = "baz";
To get the value
foo["bar"]
will return "baz".
You could also create something that would be similar to a value object (vo);
SomeModelClassNameVO.js;
function SomeModelClassNameVO(name,id) {
this.name = name;
this.id = id;
}
Than you can just do;
var someModelClassNameVO = new someModelClassNameVO('name',1);
console.log(someModelClassNameVO.name);
simple as this
myObj.name = value;
When you create an object myObj as you have, think of it more like a dictionary. In this case, it has two keys, name, and age.
You can access these dictionaries in two ways:
Like an array (e.g. myObj[name]); or
Like a property (e.g. myObj.name); do note that some properties are reserved, so the first method is preferred.
You should be able to access it as a property without any problems. However, to access it as an array, you'll need to treat the key like a string.
myObj["name"]
Otherwise, javascript will assume that name is a variable, and since you haven't created a variable called name, it won't be able to access the key you're expecting.
You could do the following:
var currentObj = {
name: 'Umut',
age : 34
};
var newValues = {
name: 'Onur',
}
Option 1:
currentObj = Object.assign(currentObj, newValues);
Option 2:
currentObj = {...currentObj, ...newValues};
Option 3:
Object.keys(newValues).forEach(key => {
currentObj[key] = newValues[key];
});

Resources