This is kind of a newb question as there are still some small things about javascript that baffle me.
I have the following code:
db.units.find({}, { _id: 1, type: 1, name: 1 }).sort({ type: 1, name: 1}).exec(function(err, units) {
if(err) return next(err);
_.each(units, function(u, i) {
this[i]["available"] = !_.contains(_.pluck(_.pluck(req.workorder.units, 'unit'), 'name'), u.name);
console.log(this[i].available);
console.log(this[i]);
console.log(_.extend(this[i],{available:this[i].available}));
}, units);
return res.render('workorder/show', {
workorder: req.workorder,
invoices: db.invoices.find({ workorders: req.params.id }),
units: units
});
});
and here is the result:
true
{ _id: 513bd489b0e7e2634b60dc47,
name: 'Visual Sump Inspection UST-22C NC',
type: 'Service' }
its is logging the available property but not in the object alone, why is this?
The problem is related to how mongoose treats objects. the object can only be modified with the usage of document.toObject()
http://mongoosejs.com/docs/api.html#document_Document-toObject
Likeso:
_.each(units, function(u, i) {
u = _.extend(u.toObject(), {'available': !_.contains(_.pluck(_.pluck(req.workorder.units, 'unit'), 'name'), u.name)});
});
I suspect that Mongoose is defining the available property in a way that makes it readable but non-enumerable. It could be using a getter, or it could be setting enumerable: false with an Object.defineProperty() or Object.defineProperties call.
For a quick test, try pasting this code into the Chrome console or in your node.js app:
var obj = { a: 1 };
Object.defineProperties( obj, {
b: {
get: function() {
return 2;
}
},
c: {
value: 3,
enumerable: false
}
});
console.log( obj );
console.log( obj.a );
console.log( obj.b );
console.log( obj.c );
In Chrome, the code logs:
Object {a: 1}
1
2
3
So all three properties are showing up when read explicitly, but b and c are not enumerated when the object as a whole is logged.
Related
I'm new to jest unit test case scenario, I have a scenario where in the response from the service that I called is of the below format
Artifact {
name: 'detection-v1.zip',
file_path: 'artifact\\bn-ds-anomalydetection-v1.zip',
is_tenant: false,
metadata: [
Registerfact {
name: 'ad',
_meta: [Object],
line_meta: [Object]
},
Registerfact {
name: 'ad-generic',
_meta: [Object],
line_meta: [Object]
}
]
}
how can i compare the above response in the jest service , I was trying to create a object but the Artifact name before the object is confusing how should i proceed
The test case is
test('test processArtifact method', async()=>{
const mockGetRestClient = jest.fn();
try{
const response = await factService.processfact(artifact)
console.log("response---",response)
// expect(response).toEqual()
}
catch(e){ }
})
I know its silly question ,but i'm confused hence posted it.
How should i create the static object to be put in .toEqual() ?
You can declare a global/static var with your response object on top of file. Or better declare it in some constants file and import here.
For Comparison:
Usually, if you have a simple object, you can use JSON.stringify. However, it may give error due to different order of object keys.
You should use assert for the deep comparison. There is method assert.deepEqual() which does deep comparison of objects.
an example for using assert from official docs
import assert from 'node:assert';
const obj1 = {
a: {
b: 1
}
};
const obj2 = {
a: {
b: 2
}
};
const obj3 = {
a: {
b: 1
}
};
const obj4 = Object.create(obj1);
assert.deepEqual(obj1, obj1);
// OK
// Values of b are different:
assert.deepEqual(obj1, obj2);
// AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } }
assert.deepEqual(obj1, obj3);
// OK
// Prototypes are ignored:
assert.deepEqual(obj1, obj4);
// AssertionError: { a: { b: 1 } } deepEqual {}
Hope this helps, let me know if you have any questions.
You can use JSON.stringify in order to convert your object into a string and then compare this result to the one you expect.
console.log(JSON.stringify({
name: 'detection-v1.zip',
file_path: 'artifact\\bn-ds-anomalydetection-v1.zip',
is_tenant: false,
metadata: [
{Registerfact: {
name: 'ad',
_meta: {},
line_meta: {}
}},
{Registerfact: {
name: 'ad-generic',
_meta: {},
line_meta: {}
}}
]
}));
Given the following snippet:
let PARENT = {
CHILD_1: {
no: '1',
page: 3,
toString: function(){ return this.no; } },
CHILD_2: {
no: '2',
page: 4,
toString: function(){ return this.no; } },
}
console.log( JSON.stringify(PARENT, null, 1) )
for ( let c in PARENT ) {
// c is a CHILD_n object here, right?
console.log(c) // why 'CHILD_n' instead of 'no' from toString()?
console.log(c.page) // why undefined instead of 'page'?
}
I don't get the expected results:
Why is c resolved to the CHILD_n property's name rather than the value returned by toString()?
Why is c.page undefined? 1. Isn't c a CHILD_n object with its properties available?
How to get the expected values?
As mentioned in the comments, for... in loops though the keys; i.e. the strings 'CHILD_1' and 'CHILD_2' as opposed to the objects.
for... of iterates through the values, however, you can't directly iterate an object, as objects are not iterable. for (value of {}) {} will throw an error.
Instead, you'll have to obtain the iterable list of values via e.g. Object.values prior to iterating using for... of.
Lastly, toString is not automatically invoked by console.log, so you'll have to call it explicitly.
let PARENT = {
CHILD_1: {
no: '1',
page: 3,
toString: function(){ return this.no; } },
CHILD_2: {
no: '2',
page: 4,
toString: function(){ return this.no; } },
};
for ( let c of Object.values(PARENT) ) {
console.log(c);
console.log(c.toString());
console.log(c.page);
}
Let's say something returns a complex object:
const value = {
...,
client: { ... },
...
};
I want to test a bunch of properties, and for client I want to make sure that it has a certain name, but does not have id at all.
How do I implement it?
expect(value).toMatchObject({
...,
client: ???,
...
});
If it was just name I would have done client: { name: 'expected' }.
I tried client: { id: undefined, name: "expected" }, but undefined is not the same as "does not have a property".
I know I can do a separate case of expect(value).not.toHaveProperty('client.name'), but it would be great to just have one toMatchObject (I need this in multiple tests).
Sorted with this, but still keen to know if there is a better way:
expect.extend({
matchingAll(actual, expected) {
return { pass: expected.every(e => e.asymmetricMatch(actual)) };
}
});
expect(value).toMatchObject({
...,
client: expect.matchingAll([
expect.objectContaining({ name: 'expected' }),
expect.not.objectContaining({ id: expect.anything() }),
]),
...
});
clear the property undefined and after to compare
function clear(a){
Object.keys(a).forEach(key => {
if (a[key] === undefined) {
delete a[key];
}
});
}
let client = { id: undefined, name: "expected" }
clear(client )
console.log(client)
I'm trying to reduce the amount of duplicate code i'm writing in JS objects. I have a methods that I want to use where almost nothing is changing except the target and I'd like to extract that out and somehow get the target through the objects property name. Hopefully the example I put together makes sense.
myObject = {
d: {
get: function(list, id) {
// do stuff
},
prop1: {
data: [],
list: myObject.config.lists.prop1.guid,
get: function(a,b) {
myObject.d.get(a,b)
}
},
// I want to write this once and use the object key ("prop2") as an argument
prop2: {
data: [],
list: myObject.config.lists.prop2.guid,
get: function(a,b) {
myObject.d.get(a,b)
}
}
}
};
Tried something like this but getting error "Cannot read prop 'spec' of undefined:"
myObject = {
d: {
get: function(list, id) {
// do stuff
}
},
// Use this to duplicate shared funtions for similar
spec: function(target) {
return {
data: [],
list: myObject.config.lists[target].guid,
get: function() {
myObject.d.get(a, b);
},
update: "",
delete: ""
};
},
// some how return `myObject.spec.get()`, allowing me to use myObject.d.prop1.get()
prop1: myObject.spec.apply(this, "prop1"),
prop2: myObject.spec.apply(this, "prop2")
};
So far the only way I was able to get it working was by setting prop1 and prop2 outside of the initial deceleration like this and explicitly declaring the target like #Bergi suggested:
var myObject = myObject || {};
myObject = {
d: {
get: function(list, id) {
// do stuff
}
},
// Use this to duplicate shared funtions for similar
spec: function(target) {
return {
data: [],
list: target,
get: function() {
myObject.d.get(a, b);
},
update: "",
delete: ""
};
}
};
// some how return `myObject.spec.get()`, allowing me to use myObject.d.prop1.get()
myObject.prop1 = myObject.spec("prop1");
myObject.prop2 = myObject.spec("prop2");
I want to have a function with default parameters inside nested objects, and I want to be able to call it either f() or specifying only individual parameters.
// A function with nested objects with default parameters:
function f({ a = 1, callback = ({ name, param } = { name: "qwe", param: 123 }) } = {}) {
console.log("a:", a);
console.log("callback:", callback);
}
// And I want to run it like this:
f();
f({ callback: { params: "456" } });
// But 'callback.name' becomes undefined.
When destructuring is mixed with default parameters, I admit the code is hard to read and write (especially when there are nested objects...).
But I think you are trying to do that:
function f({callback: {name = "cbFunction", params = "123"} = {}} = {}) {
console.log(name);
console.log(params);
}
f();
f({callback: {params: '789'}});
I found none of the answers here to be what he wanted. But it IS actually possible in a somewhat sexy way by doing this:
(EDIT: Simplified syntax and also show how to add default values for subobjects)
function f({
a = 1,
callback = {}
} = {}) {
callback = { // default values
name: "cbFunction",
params: "123",
...callback // overwrites it with given values
}
// do the same for any subobjects
callback.subObject = {
arg1: 'hi',
arg2: 'hello',
...callback.subObject
}
console.log("a:", a)
console.log("callback:", callback)
}
f()
f({a: 2, callback: {params: '789', subObject: {arg2: 'goodbye'}}})
Turned out to call it like this solves the problem, but is it the best way?
function f({
a = 1,
callback = ({
name,
param
} = {
name: "qwe",
param: 123
})
} = {}) {
console.log("a:", a);
console.log("callback:", callback);
}
f();
f({ callback: { name, params: "456" } });
Answer by #Badacadabra is nearly correct but missing the other top level parameter specified in the question.
function f({a = 1, callback: {name = "qwe", params = "123"} = {}} = {}) {
console.log(a);
console.log(name);
console.log(params);
}
However note that within the function body the properties of callback are addressed without the containing object. You could reconstitute them into such an object if you wanted with the line:
const callback = { name, params }
Either way, from the invocation point this works to fill in all missing values from all levels such as:
f({a: 2})
f({a: 2, callback: { name: "abc"}})
f({a: 2, callback: { params: "456" }})
etc.
EDIT
In response to Joakim's comment:
TotalAMD also said in a comment that "I want to use several nested objects with same fields name". So if he tries that approach with callback1 and callback2 as arguments then he would have to use different field names in them.
I missed that original requirement. One way to maintain the desired, duplicated nested names within the function interface would be to alias them within the scope of the function, as follows:
function f({
a = 1,
callback1: {name: name1 = "abc", params: params1 = "123"} = {},
callback2: {name: name2 = "def", params: params2 = "456"} = {},
} = {}) {
console.log(a);
console.log(name1);
console.log(params1);
console.log(name2);
console.log(params2);
}
You can then call the function with the designed interface and expected results:
f ({ callback1: { name: "One" }, callback2: { name: "Two" } })
Caveat: Whilst technically possible and potentially useful, this could get messy at deeper nesting levels. It might then be worth looking for an alternative interface design with less indirection.