Sinon restore cannot read property 'restore' of undefined - sinon

I have the following code which I am using Sinon for
import React from 'react';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { IncrementalSearch } from './IncrementalSearch';
describe('<IncrementalSearch />', () => {
afterEach(() => {
this.constructorSpy.restore();
});
it('calls constructor', () => {
this.constructorSpy = sinon.spy(IncrementalSearch, 'constructor');
const wrapper = mount(<IncrementalSearch />);
expect(IncrementalSearch.prototype.constructor.calledOnce).toEqual(true);
// expect(1).toEqual(1);
});
});
Now when I run this I get the following error
TypeError: Cannot read property 'restore' of undefined
What am I doing wrong here?

You have two problems here.
The immediate problem of this referring to something else than what you believe it to.
You misunderstand what the constructor property refers to.
The this pointer in afterEach refers to another context (the outer describe context) than the this in the test. Generally, to make life simpler, avoid using this when not necessary and simply use normal variables.
To fix the first problem, let's just rework your example a bit:
import React from 'react';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { IncrementalSearch } from './IncrementalSearch';
describe('<IncrementalSearch />', () => {
let constructorSpy;
afterEach(() => {
constructorSpy.restore();
});
it('calls constructor', () => {
constructorSpy = sinon.spy(IncrementalSearch, 'constructor');
const wrapper = mount(<IncrementalSearch />);
expect(IncrementalSearch.prototype.constructor.calledOnce).toEqual(true);
});
});
Your test will still fail, but for another reason
You should read this thread on the Sinon issue tracker, especially the discussion between me and machineghost. The quick summary in your regard is that you need to read up on how Javascript protypes actually work, as the constructor property does not have anything to do with the constructor keyword used in ES6 "classes." It refers to the function that created the object. Changing it after the fact, will not have any effect.
The actual constructor is the function ("class") you export from ./IncrementalSearch. Just see how babel transpiles it. So, in essence, if you really want this test to pass, which has nothing to do with your code, but is just a check if React is actually calling your code (which you should really just trust it does...), it should suffice to wrap the function. I haven't tested this though, as this might fail due to the spy not calling the wrapped function with new.

Related

Next.js dynamic import with custom loading component: "Warning: Expected server HTML to contain a matching <em> in <div>."

I have a next.js page with dynamic imports, which worked well until now,
but since I started using the custom loading components I get the warning
Warning: Expected server HTML to contain a matching <em> in <div>.
What can I do to avoid this warning ?
Here a reduced example:
// pages/index.tsx:
import React from 'react';
import dynamic from 'next/dynamic';
import { LoadingComponent } from '../src/LoadingComponent';
// -- The custom loading fallback component
const loadingFallback = { loading: LoadingComponent };
// -- The dynamically loaded component
const DynamicallyLoadedComponent = dynamic(
() => import('../src/DynamicallyLoadedComponent'),
loadingFallback
);
// -- The main component
function Main(){
return <DynamicallyLoadedComponent />;
}
export default Main;
// src/DynamicallyLoadedComponent.tsx:
export default function DynamicallyLoadedComponent(){
return <div>ready loaded, final page content.</div>;
};
// src/LoadingComponent.tsx:
export const LoadingComponent = () => {
return <em>... loading ...</em>;
}
Apparently you can not extract the part { loading: LoadingComponent } into a variable, so it has to be written
inline, e.g.:
const DynamicallyLoadedComponent = dynamic(
() => import('../src/DynamicallyLoadedComponent'),
{ loading: LoadingComponent } // <-- needs to be written like this, a variable can't be used
);
Maybe this is because Next.js analyses the code in a kind of "pre-compiling" step and expects to find exactly this pattern (?).
Probably this is the same requirement as mentioned under Basic usage regarding the dynamic import itself:
Note: In import('path/to/component'), the path must be explicitly written.
It can't be a template string nor a variable.
Furthermore the import() has to be inside the dynamic() call for Next.js to be able to match
webpack bundles / module ids to the specific dynamic() call and preload them before rendering.
dynamic() can't be used inside of React rendering as it needs to be marked in the top level
of the module for preloading to work, similar to React.lazy.

Next.js getInitialProps not rendering on the index.js page

I really can't figure out what is wrong with this code on Next.js.
index.js :
import { getUsers } from "../utils/users";
import React from "react";
Home.getInitialProps = async (ctx) => {
let elements = [];
getUsers().then((res) => {
res.map((el) => {
elements.push(el.name);
});
console.log(elements);
});
return { elements: elements };
};
function Home({ elements }) {
return (
<div>
{elements.map((el, i) => {
<p key={i}>{el}</p>;
})}
</div>
);
}
export default Home;
This doesn't render anything on my main page but still console logs the right data on server side (inside the vscode console). I really can't figure out what's going on, I followed precisely the article on the next.js site.
The getUsers function is an async function that returns an array of objects (with name,surname props), in this case in the .then I'm grabbing the names and pushing them into an array that correctly logs out to the console.
How can I make this data that I get render on the page?? Surely something to do with SSR.
The problem is using async function. Try as following.
...
elements = await getUsers();
...
In your code, component is rendered before response is finished. So the data is not rendered. Suggest using "async...await...". Infact "async" and "await" are like a couple of one.

window not defined while import AgoraRTC from 'agora-rtc-sdk-ng' in nextjs

I get an error window is undefined while importing agora.io.
Kindly import it like shown below
const AgoraRTC = (await import('agora-rtc-sdk-ng')).default
}
AGORA uses "window" object which is a client side object and Next JS renders on server side. So You need to use "dynamic" for client side rendering. Via Dynamic you can import the file on client side so it will work.
You should copy your whole Agora code and put it in a file.
For example in my case I put it in "../components/agoraclientside.js"
So in my index.js file I wrote :
import dynamic from "next/dynamic";
const ClientSideControls = dynamic(
() => {
return import("../components/agoraclientside");
},
{ ssr: false }
);
Then in your return function use <ClientSideControls />

Using a Map inside of Vuex state

I'm building a prototype module to learn about Vuex and running into what seems like a simple issue -- I'm having trouble using a Map as one of my state variables.
I want to use Vuex to store some basic user preferences across multiple modules and figured a Map would be a simple way to do it since the prefs could be handled as simple key/value pairs. But I'm either not defining the Map correctly, or I'm not using it correctly within the mutations.
<script lang="ts">
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
userSettings: Map,
},
mutations: {
addUserSetting(state, payload) {
if (state.userSettings == null) {
state.userSettings = new Map();
}
state.userSettings.set(payload.key, payload.value);
}
},
})
</script>
I'd be willing to use another object, and I started with an array of UserPref objects but that didn't go well, either.
Vue 2 doesn't have proper support for Map.
Assuming your keys are strings I suggest using an Object instead. To get a totally empty object (with no inherited properties, not even toString) you can use Object.create(null), or just use a normal object using {} if that seems unnecessary.
e.g.
import Vue from 'vue'
export default new Vuex.Store({
state: {
userSettings: Object.create(null),
},
mutations: {
addUserSetting(state, payload) {
Vue.set(state.userSettings, payload.key, payload.value);
}
}
})
Vue.set must be used as the properties are being added.

Why can I not call toArray() on FirebaseListObservable?

In an Ionic Project, I have:
import { AngularFireDatabase, FirebaseListObservable} from 'angularfire2/database';
and a class with the field:
songs: FirebaseListObservable<any>;
therefore, the line of code:
this.songs = db.list('/songs');
works and allows me to put the line:
<button ion-button ouline *ngFor="let song of songs | async">
in my html without problem.
Now, FirebaseListObservable extends the RxJS Observable (source).
Furthermore, Observable has a method toArray(). But, when I run my project, I see:
Typescript Error
Property 'toArray' does not exist on type 'FirebaseListObservable<any>'.
Why is this? Is there another way I can get an array from what songs is observing?
I don't really sure why the toArray() not working , but i can suggest you a way to get the array you want from the DB.( I usually do that when i want just the array without the ability to listen to any changes of the DB - like Observable does) :
this.db.list('/songs')
.first().toPromise()
.then(response => {
//code to handle the response - in this case its a list
This.items = response;
})
.catch(error => { //error code here });
dont forget to import the rxjs first and toPromise
I really hope it fits your wish and helps you :)

Resources