Sinon - ensure object does not have property - sinon

Is there a way with Sinon to have a negative match? Specifically that an object does not have a given property?
Thanks!

There isn't currently a built-in matcher for that.
Sinon allows you to create custom matchers so you could create your own, here is one for doesNotHave based on the built-in has matcher:
import * as sinon from 'sinon';
const doesNotHave = (prop) => sinon.match(function (actual) {
if(typeof value === "object") {
return !(prop in actual);
}
return actual[prop] === undefined;
}, "doesNotHave");
test('properties', () => {
const obj = { foo: 'bar' };
sinon.assert.match(obj, sinon.match.has('foo')); // SUCCESS
sinon.assert.match(obj, doesNotHave('baz')); // SUCCESS
})

I've just realized that it's possible to specify undefined in the object's shape to make the check:
sinon.assert.match(actual, {
shouldNotExists: undefined
});
Not completely sure if it's 100% valid, but seems to do the job.

You cannot use sinon for this, you have to use something like chai.
You would do:
cont { expect } = require("chai");
expect({ foo: true }).to.not.have.keys(['bar']);
https://runkit.com/embed/w9qwrw2ltmpz

Related

Vue Pinia Store - how to get the initial state?

I have an object in my pinia store like
import { defineStore } from "pinia";
export const useSearchStore = defineStore("store", {
state: () => {
return {
myobj: {
foo: 0,
bar: 2000,
too: 1000,
},
};
},
getters: {
changed() {
// doesn't work
return Object.entries(this.myobj).filter(([key, value]) => value != initialvalue
);
},
},
});
How do I get the initial value to test if the object changed. Or how can I return a filtered object with only those entries different from initial state?
My current workaround:
in a created hook I make a hard copy of the store object I then can compare to. I guess there is a more elegant way...
I had done this (although I do not know if there a better way to avoid cloning without duplicating your initial state).
Define your initial state outside and assign it to a variable as follows;
const initialState = {
foo: 0,
bar: 2000,
too: 1000
}
Then you can use cloning to retain the original state;
export const useSearchStore = defineStore("store", {
state: () => {
return {
myobj: structuredClone(initialState),
};
},
getters: {
changed: (state) => deepEquals(initialState, state.myobj);
},
});
where deepEquals is a method which deep compares the two objects (which you would have to implement). I would use lodash (npm i lodash and npm i #types/lodash --save-dev if you're using TypeScript) for this.
Full code (with lodash);
import { defineStore } from "pinia";
import { cloneDeep, isEqual } from "lodash";
const initialState = {
foo: 0,
bar: 2000,
too: 1000
}
export const useSearchStore = defineStore("store", {
state: () => ({
myobj: cloneDeep(initialState)
}),
getters: {
changed(state) {
return isEqual(initialState, state.myobj);
},
},
});
If you also want the differences between the two you can use the following function (the _ is lodash - import _ from "lodash");
function difference(object, base) {
function changes(object, base) {
return _.transform(object, function (result: object, value, key) {
if (!_.isEqual(value, base[key])) {
result[key] =
_.isObject(value) && _.isObject(base[key])
? changes(value, base[key])
: value;
}
});
}
return changes(object, base);
}
courtesy of https://gist.github.com/Yimiprod/7ee176597fef230d1451
EDIT:
The other way you would do this is to use a watcher to subscribe to changes. The disadvantage to this is that you either have to be OK with your state marked as "changed" if you change back the data to the initial state. Otherwise, you would have to implement a system (perhaps using a stack data structure) to maintain a list of changes so that if two changes which cancel each other out occur then you would remark the state as "unchanged". You would have to keep another variable (boolean) in the state which holds whether the state has been changed/unchanged - but this would be more complicated to implement and (depending on your use case) not worth it.

Using composables with OptionsAPI

Our team is converting an app from Vue 2 to 3 and I am working on the final steps.
I am investigating whether we can convert our mixin files to composables. I have yet to find any documentation to support whether you can use composables with optionsAPI.
I have tried a little sample but I am seeing the limitations:
COMPOSABLE file useComposables:
import { ref, computed } from 'vue'
export default () => {
let first = ref('First')
let last = ref('Last')
let mycomputed = computed(() => {
return `${first.value} *** ${last.value}`
})
return {
first, mycomputed
}
}
COMPONENT:
import useComposables from '#/utils/useComposable'
created () {
let { first, mycomputed } = useComposables()
console.log('first', first.value)
console.log('mycomputed', mycomputed.value)
},
<template>
mycomputed {{ mycomputed }}
</template>
So, I see when I try to do interpolation on mycomputed computed variable in the template, the component doesn't have access to the computed variable because it is not in the computed option and doesn't belong to 'this'.
I can't seem to find any documentation to support using composables with options API.
Am I missing something or is this a no-go?
Thanks
OP achieved to solve that by using the following:
setup() {
let { first, mycomputed } = useComposables()
return {
first, mycomputed
}
},

Lazy loading references from normalized Redux store

Yo! I'm using Redux and Normalizr. The API I'm working with sends down objects that look like this:
{
name: 'Foo',
type: 'ABCD-EFGH-IJKL-MNOP'
}
or like this
{
name: 'Foo2',
children: [
'ABCD-EFGH-IJKL-MNOP',
'QRST-UVWX-YZAB-CDEF'
]
}
I want to be able to asynchronously fetch those related entities (type and children) when the above objects are accessed from the state (in mapStateToProps). Unfortunately, this does not seem to mesh with the Redux way as mapStateToProps is not the right place to call actions. Is there an obvious solution to this case that I'm overlooking (other than pre-fetching all of my data)?
Not sure that I have correctly understood your use-case, but if you want to fetch data, one simple common way is to trigger it from a React component:
var Component = React.createClass({
componentDidMount: function() {
if (!this.props.myObject) {
dispatch(actions.loadObject(this.props.myObjectId));
}
},
render: function() {
const heading = this.props.myObject ?
'My object name is ' + this.props.myObject.name
: 'No object loaded';
return (
<div>
{heading}
</div>
);
},
});
Given the "myObjectId" prop, the component triggers the "myObject" fetching after mounting.
Another common way would be to fetch the data, if it's not already here, from a Redux async action creator (see Redux's doc for more details about this pattern):
// sync action creator:
const FETCH_OBJECT_SUCCESS = 'FETCH_OBJECT_SUCCESS';
function fetchObjectSuccess(objectId, myObject) {
return {
type: FETCH_OBJECT_SUCCESS,
objectId,
myObject,
};
}
// async action creator:
function fetchObject(objectId) {
return (dispatch, getState) => {
const currentAppState = getState();
if (!currentAppState.allObjects[objectId]) {
// fetches the object if not already present in app state:
return fetch('some_url_.../' + objectId)
.then(myObject => (
dispatch(fetchObjectSuccess(objectId, myObject))
));
} else {
return Promise.resolve(); // nothing to wait for
}
};
}

React rendering recursion stops without error

I've encountered a problem with rendering some elements in React.
(I use ImmutableJS)
renderComponents: function(components) {
if(components.isEmpty()) return [];
var table = [];
components.map(function(component) {
table.push(<ComponentTableElement key={ component.get('id') } data={ component } />);
if(component.has('children')) {
var children = component.get('children');
table.concat(this.renderComponents(children));
}
});
return table;
},
As I looked for error, I found that this.renderComponents(children) doesn't return anything at all and the code somehow stops.
I mean before that line everything works ok, but then after this line, when i try to console.log something, it doesn't show up. And it doesn't even reach return table.
So what is wrong with that code?
In the context of the function you pass to map, this refers to the window object, not to the current component instance, so this.renderComponents is undefined when you try to call it.
components.map(function(component) {
this === window;
});
You can pass a value to use as this in the body of your function as the second parameter of Array::map.
components.map(function(component) {
table.push(<ComponentTableElement key={ component.get('id') } data={ component } />);
if(component.has('children')) {
var children = component.get('children');
// here, `this` refers to the component instance
table.concat(this.renderComponents(children));
}
}, this);
If you're using ES6, you can also use fat-arrow functions, which are automatically bound to this.
components.map((component) => {
table.push(<ComponentTableElement key={ component.get('id') } data={ component } />);
if(component.has('children')) {
var children = component.get('children');
// here, `this` refers to the component instance
table.concat(this.renderComponents(children));
}
});

How to get prototype of a registered webcomponent without instantiating it

Consider the following code in a javascript library;
document.registerElement('my-component', { prototype: { foo: true }});
It seems registerElement returns a function which can be used as a constructor.
How can I get a reference to this function later ?
var tempDom = document.createElement('my-component')
console.log(tempDom.__proto__)
Seems working but it requires creating an instance first.
I think you just need to save the return from registerElement() in a variable, and then use that variable later. If you do not save the return then I believe it is lost.
// Save the return in a variable
var mycomp = document.registerElement('my-component');
// Use the var to create the element
document.body.appendChild(new mycomp());
// Then you can do things with the new tag
var mytag = document.getElementsByTagName("my-component")[0];
mytag.textContent = "I am a my-component element.";
prototype method will give you an expected result.
var mc = document.registerElement(
'my-component', { prototype: { foo: true }}
);
console.log(mc.prototype);
//⇒ my-component {foo: true}
Hope it helps.

Resources