just a general question - fairly new to js. I have a script that converts a csv to json. I'd like to test the json output without writing it to a file using Jest. Is that possible with Jest's expect?
As in jest documentation
Use .toMatchObject to check that a JavaScript object matches a subset
of the properties of an object. It will match received objects with
properties that are not in the expected object.
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
area: 20,
wallColor: 'white',
},
};
const desiredHouse = {
bath: true,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
wallColor: expect.stringMatching(/white|yellow/),
},
};
expect(houseForSale).toMatchObject(desiredHouse);
Related
I would like to test the shape of my json in my mocha expectations. Some things I know like 'name' but others(_id) come from the database. For them, I only care that they are set with the proper type.
Here I have my expectation:
expect(object).to.eql({
recipe:
{
_id: '5fa5503a1fa816347f3c93fe',
name: 'thing',
usedIngredients: []
}
})
I would rather do something like this if possible:
expect(object).to.eql({
recipe:
{
_id: is.a('string'),
name: 'thing',
usedIngredients: []
}
})
Does anyone know of a way to accomplish this? Or is it best to just break this up into multiple tests?
You can do this by using chai-json-pattern plugin.
Chai JSON pattern allows you to create blueprints for JavaScript objects to ensure validation of key information. It enables you to use JSON syntax extends with easy to use validators. It came up mostly for testing API with cucumber-js but can be used in any application. Additionally, you can extend base functionality with custom validators
E.g.
const chai = require('chai');
const chaiJsonPattern = require('chai-json-pattern').default;
chai.use(chaiJsonPattern);
const { expect } = chai;
describe('64715893', () => {
it('should pass', () => {
const object = {
recipe: {
_id: Math.random().toString(),
name: 'thing',
usedIngredients: [Math.random() + 'whatever'],
},
};
expect(object).to.matchPattern(`{
"recipe": {
"_id": String,
"name": "thing",
"usedIngredients": Array,
},
}`);
});
});
test result:
64715893
✓ should pass
1 passing (50ms)
Working on a React, Redux + Typescript project, I am trying to add Immutable JS to the stack.
I started with working on a large nested object that could really use being safer as an immutable data structure.
import { Record, fromJS } from "immutable";
const obj = {
name: "werwr",
overview: {
seasons: {
2017: [{ period: 1, rates: 2 }]
}
}
};
// -- Using fromJS
const objJS = fromJS(obj);
const nObj = objJS.getIn(["overview", "seasons", "2017"]);
console.log(nObj); // I get an immutable list cool!
// -- Using Record, infer the type
const objRecord = Record(obj)();
const nRec = objRecord.getIn(["overview", "seasons", "2017"]);
console.log(nRec); // but I get a JS array
// -- Using both
const makeRec = Record(objJS);
const bothRecord = makeRec({ name: "name" });
console.log(bothRecord); // fails
Runnable code in codesandbox: https://codesandbox.io/s/naughty-panini-9bpgn?file=/src/index.ts
using fromJS. The conversion works well and deep but I lose all
type information.
using a Record. It keeps track of the type but nested arrays are
still mutable.
passing the converted object into a Record and manually add the type but I ran into an error: Cannot read property 'get' of
undefined
Whats the proper way to convert such an object to a fully immutable data structure while not loosing the type? Thanks!
You can use classes to construct deep structures.
interface IRole {
name: string;
related: IRole[];
}
const roleRecord = Record({
name: '',
related: List<Role>(),
});
class Role extends roleRecord {
name: string;
related: List<Role>;
constructor(config: IRole) {
super(Object.assign({}, config, {
related: config.related && List(config.related.map(r => new Role(r))),
}));
}
}
const myRole = new Role({
name: 'President',
related: [
{name: 'VP',
related:[
{name: 'AVP',
related: []}
]}
]});
With this type of structure, myRole will be all nested Role classes.
NOTE: I will add a bit of caution, we have been using this structure in a production application for almost 4 years now (angular, typescript, redux), and I added the immutablejs for safety from mutated actions and stores. If I had to do it over, the strict immutable store and actions that comes with NGRX would be my choice. Immutablejs is great at what it does, but the complexity it adds to the app is a trade off (Especially for onboarding new/greener coders).
Record is a factory for Record-Factories. As such, the argument should be an object template (aka default values), not actual data! (see docs).
const MyRecord = Record({
name: "werwr",
overview: null
});
const instance = MyRecord(somedata);
As you already noticed, the Record factory will not transform data to immutable. If you want to do that, you have to either do it manually with Maps and Lists, fromJS or the constructor of records.
The last approach is a bit weird, because then your record factory suddendly becomes a class:
const SeasonRecord = Record({
period: null, rates: null
})
class MyRecord extends Record({
name: "default_name",
seasons: Map()
}, 'MyRecord') {
constructor(values = {}, name) {
if(values.seasons) {
// straight forward Map of seasons:
// values = fromJS(values);
// Map of sub-records
values.seasons = Object.entries(values.seasons).reduce(
(acc, [year, season]) => {
acc[year] = SeasonRecord(season);
return acc;
}, {});
values.seasons = Map(values.seasons);
}
super(values, name);
}
}
const x = new MyRecord({
seasons: {
2017: { period: 1, rates: 2 }
}
})
console.log('period of 2017', x.seasons.get('2017').period)
I strongly suggest to not use unecessarily nest objects (record -> overview -> season) as it makes everything more complicated (and if you use large amounts of records, it might impact performance).
My general recommendation for Records is to keep them as flat as possible. The shown nesting of records allows to use the property access syntax instead of get, but is too tendious most of the time. Simply doing fromJS() for the values of a record and then use getIn is easier.
How would I go about searching an object for a key containing a given string without using a loop?
Here is an example of my object
let item = {
ItemID: 1,
ItemName: "Box",
ItemMinHeight: 10,
ItemMaxHeight: 30,
ItemMinDepth: 11,
ItemMaxDepth: 18,
ItemMinWidth: 20,
ItemMaxWidth: 50
};
I need to get the key value pairs that would contain "Depth", so it should return
ItemMinDepth and ItemMaxDepth
You can use filter:
const listOfDepthKeys = Object.keys(item).filter(key => key.includes('Depth'));
Note this is still using a loop, as filter is a loop internally; it's no different from a for loop except that you aren't the one writing for :)
If you want the values, too:
const list = Object.entries(item).filter(entry => entry[0].includes('Depth'));
const pairs = Object.fromEntries(list);
By using lodash/fp (a full functional variant of Lodash), you can achieve this by this way:
_.flow([
_.toPairs,
_.filter(([key, value]) => key.includes('Depth')),
_.fromPairs
])(item)
Lodash fp is fully built upon _.curry function which allows to build intermediate functions as long as you didn't provide all required parameters. _.flow allows to chain functions in an elegant and perfectly combines with curried functions.
Here is the doc of this library https://gist.github.com/jfmengels/6b973b69c491375117dc
You can use lodash
let items = {
ItemID: 1,
ItemName: "Box",
ItemMinHeight: 10,
ItemMaxHeight: 30,
ItemMinDepth: 11,
ItemMaxDepth: 18,
ItemMinWidth: 20,
ItemMaxWidth: 50
};
Use lodash keys method
const depthItems = {}
_.keys(items).map(key => {
if(key.indexOf("Depth") !== -1) {
depthItems[key] = items[key]
}
})
console.log(depthItems)
I have parts of a state managed with the help of Immutable collections.
For example,
const FolderModel = Record({
id: null,
name: '',
items: List()
})
const DocsModel = Record({
folder1: new FolderModel({
id: 1,
name: 'Избранное'
}),
folder2: new FolderModel({
id: 2,
name: 'Отложенное'
}),
folder3: new FolderModel({
id: 3,
name: 'Топ тендеры'
})
})
const initialState = new DocsModel()
I also save my state in a localStorage, so the problem is when retrieve the state from localStorage I don't know how to convert JS object back to a nested collection (e.g Record containing a field that is a List).
I already tried using Immutable.fromJS() method but apparently it doesn't work for Records. Did anybody face the same problem? Please help resolving it
I've had success using the transit-immutable-js lib.
From the docs:
"Transit is a serialisation format which builds on top of JSON to provide a richer set of types. It is extensible, which makes it a good choice for easily providing serialisation and deserialisation capabilities for Immutable's types."
Example usage with Records:
var FooRecord = Immutable.Record({ a: 1, b: 2, }, 'Foo'),
foo = new FooRecord(),
serialize = transit.withRecords([FooRecord]),
json = serialize.toJSON(foo);
console.log(json); //=> json string of your data
I believe you need to serialize your collection with JSON.stringify() before setting it to localStorage. After getting it back from localStorage you can deserialize it with JSON.parse()
I am querying my mongodb using mongoose, but i don't understand why the returned sub docs are just of type Object, instead of JSON.
Using
hero.find({} ,{'deck' : {$elemMatch:{name:'Guard Tower'}}}, function(err, tower) {
console.log(tower);
}
returns
[ { _id: 507ac406ba6ecb1316000001,
deck:
[ { name: 'Guard Tower',
description: 'This tower shoots stuff.',
cost: 13,
sellValue: 7,
radius: 180,
speed: 40,
dmg_min: 0,
dmg_max: 0,
actual_height: 40,
sprite: 'guardtower_red.png',
anim: [Object],
size: [Object],
projectile: [Object],
upgrade: [Object] } ] } ]
Subdocument like anim, size, projectile, upgrade, is Object, i need the information nested, how can i get the information? Without making another query?
The all docs and subdocs are objects in JavaScript. It's just that console.log uses the default depth of 2 when calling util.inspect to format your document for output. You can output all levels of the document by calling util.inspect yourself:
var util = require('util');
hero.find({} ,{'deck' : {$elemMatch:{name:'Guard Tower'}}}, function(err, tower) {
console.log(util.inspect(tower, false, null));
});
JohnnyHK is correct however a simpler approach if you just want to log out JSON would be
console.log(tower.toJSON());
You can see my comment to Rodrigo about why this works.
Making the query on Mongoose using find() as you did will return Mongoose Documents (not JSON). You can use the lean() method to return POJOs:
hero
.find({} ,{'deck' : {$elemMatch:{name:'Guard Tower'}}})
.lean()
.exec(function(err, tower) {
//tower is a JSON here
console.log(tower);
});
But what JohnnyHK said it's true about the console log, it will only show nested documents as [Object].
UPDATE: Beware that using .lean() will return objects and any virtual fields or special getters you might have will be ignored.