I am fairly new to AngularJS (v 1.6) so this may be a dumb question.
I am trying to create a component that will be used on the home page of my application. No data will be passed to it, and instead I will make an API call.
Here is what it looks like thus far:
class MyInboxController {
constructor(api) {
this.api = api;
this.$onInit = () => {
this.count = this.api.getAllMessages().then(function(data) { data.length });
}
}
}
MyInboxController.$inject = ['api'];
export const myInboxComponent = {
template: require('./my-inbox.html'),
controller: MyInboxController
};
Essentially, this component will live on the home page of my application in the navigation. My problem is that when I call an api service I have in oninit, it never seems to get data back in time for the page to load. Any advice would be fantastic.
The code likely should be:
class MyInboxController {
constructor(api) {
this.api = api;
this.$onInit = () => {
api.getAllMessages().then( (data) => {
this.count = data.length
});
};
}
}
The .then method of returns a promise. The value from a promise needs to be extracted with a function provided to the .then method.
Related
I'm writing unit test for once of my .ts file. Where I'm facing a problem and unable to find the solution. Hopefully someone can help me to resolve it.
Problem
While writing unit test. I'm unable to test the value for profile. After calling a method called getProfile().
File setup
Profile.ts
import { getProfileAPI} from "./api";
class ProfileDetails implements IProfileDetails {
public profile: string = ''
constructor() {}
getProfile = async () => {
const { data } = await getProfileAPI();
if (data) {
this.profile = data
}
};
}
const profileDetail = new ProfileDetails();
export default profileDetail;
Profile.spec.ts
import Profile from './Profile';
describe('Profile', () => {
it('getProfile', async () => {
Profile.getProfile = jest.fn();
await Profile.getProfile();
expect(Profile.getProfile).toHaveBeenCalled();
});
});
So the challenge I'm facing here is, I can able to mock the getProfile method. But I'm not able to mock the getProfileAPI function which is called inside the getProfile method.
How can I mock a function which is called inside a mocked method (or) is there any other way to resolve this. Kindly help.
Thanks in advance.
Before answering your questions, I may have some comments :
your test is wrong, all it does is calling the method then checking if it is called, of course it will always pass !
you are not really mocking, in fact you're erasing the old method and it may have some impacts on other tests.
your method "getProfile" should be called "getAndSetProfile", or "syncProfile", or something like that, getProfile is confusing for a developer, he will think it only get the profile and returns it.
I don't recommend creating & exporting an instance of ProfileDetails like this, you should take a look on DI (Dependency Injection) with typedi for example.
Do not forget :
A unit test means that any dependency inside your "unit" should be mock, you must only test the logic inside your "unit" (in your case, the getProfile function, or the class itself).
Here, you are invoking a method called "getProfileAPI" from another service that is not mocked, so you are currently testing its logic too.
This test should work :
Profile.spec.ts
jest.mock('./api', () => ({
getProfileAPI: jest.fn(),
}));
import { getProfileAPI } from "./api";
import Profile from './Profile';
describe('Profile', () => {
it('getProfile', async () => {
await Profile.getProfile();
expect(getProfileAPI).toHaveBeenCalled();
});
});
In our example, Profile.profile will be empty, because even if we mocked to getProfileAPI method, we didn't make it return something. You could test both cases :
jest.mock('./api', () => ({
getProfileAPI: jest.fn(),
}));
import { getProfileAPI } from "./api";
import Profile from './Profile';
const mockGetProfileAPI = getProfileAPI as jest.Mock; // Typescript fix for mocks, else mockResolvedValue method will show an error
describe('Profile', () => {
describe('getProfile', () => {
describe('with data', () => {
const profile = 'TEST_PROFILE';
beforeEach(() => {
mockGetProfileAPI.mockResolvedValue({
data: profile,
});
});
it('should call getProfileAPI method', async () => {
await Profile.getProfile();
expect(mockGetProfileAPI).toHaveBeenCalled(); // Please note that "expect(getProfileAPI).toHaveBeenCalled();" would work
});
it('should set profile', async () => {
await Profile.getProfile();
expect(Profile.profile).toBe(profile);
});
});
describe.skip('with no data', () => {
it('should not set profile', async () => {
await Profile.getProfile();
expect(Profile.profile).toStrictEqual(''); // the default value
});
});
});
});
NB : I skipped the last test because it won't work in your case. Profile isn't recreated between tests, and as it is an object, it keeps the value of Profile.profile (btw, this is a bit weird) between each tests. This is one of the reasons why you should not export a new instance of the class.
I am writing a react component which will load a URL within an iframe, then when the iframe's onLoad event fires it will call contentWindow.postMessage(). I'd like to prove this functionality using Jest, Enzyme, and JSDOM.
My component wraps react-iframe and looks pretty simple:
export class FilteredIframe extends React.PureComponent<FilteredIframeProps> {
onload = (e:Window) => {
console.log("ONLOAD CALLED");
if (this.props.filters) {
e.postMessage(this.props.filters, this.props.url);
}
}
render() {
return (<Iframe url={this.props.url}
display="initial"
position="static"
onLoad={this.onload}
/>);
}
}
I'm trying to figure out how to get enzyme/jsdom to test this, but I'm failing:
test("Posts message once the frame has loaded", async () => {
const payLoad = { data: "data" };
const result = mount(<FilteredIframe url="https:///www.bing.com" filters={payLoad}/>);
})
When running this in jest, I never see the "ONLOAD CALLED" message in the console. Is there some sort of special thing I need to do for jsdom or enzyme to make it actually call onLoad?
I revisited this and figured out I can call onLoad() of the iframe inside my component directly. I now have something like this:
test("Posts message once the frame has loaded", async () => {
const payLoad = { data: "data" };
const result = mount(<FilteredIframe url="https:///www.bing.com" filters={payLoad} />);
const iframe = result.find("iframe");
//mock contentWindow so we can examine messages
let receivedFilters = {};
const mockIFrameContents = {
contentWindow : {
postMessage: function (filters, url) {
receivedFilters = filters;
}
}
}
result.instance().setIframeRef(mockIFrameContents);
//Signal the contents have loaded
iframe.props().onLoad();
expect(receivedFilters === payLoad).toBeTruthy();
});
I also modified the component a little to use a ref for the iframe itself, and use the ref's contentWindow rather than the event target. But the real answer here was just to mock up the iframe contentWindow and call it's onLoad() directly, rather than try to get it to actually load something.
Forcing an update on a mounted wrapper worked for me.
<iframe onLoad={this.iframeLoaded}></iframe>
and test like so...
const mountWrapper = mount(<App />);
let container;
describe('iframe', () => {
beforeEach(() => {
container = mountWrapper.find('iframe');
});
it('calls iframeLoaded() when loaded', () => {
const spy = jest.spyOn(mountWrapper.instance(), 'iframeLoaded');
mountWrapper.instance().forceUpdate();
container.simulate('load');
expect(spy).toHaveBeenCalledTimes(1);
});
});
You need to attach mounted iframe to document, there is attachTo option for mount to do this.
OPs answer had the pieces I needed. If you don't need the iframe loaded, but just the trigger (like if the iframe src is a pdf), trigger onLoad and update.
act(() => {
result.find('iframe').props().onLoad();
});
result.update();
We are using the sinon to test our api call in reactjs application Like this:-
import * as Actions from 'routes/actions/Actions';
const requestAction = {
RequestShell() { Actions.request(); },
};
describe('testing for Actions', () => {
it('check whether request() method call is happening properly or not', () => {
const requestData = sinon.spy(requestAction, 'RequestShell');
requestAction.RequestShell();
sinon.assert.calledOnce(requestData);
requestData.restore();
});
Now I need to compare if Actions.request() return type is Json object or not. How can I test the return type of the action by using sinon? Please assist me.
Try with this
JS
it('check whether request() method call is happening properly or not', () => {
const requestData = sinon.spy(requestAction, 'RequestShell');
requestAction.RequestShell();
assert(requestData.calledOnce);
requestAction.RequestShell.restore();
});
refer this linksinon spies
I have been trying to learn about being a full-stack web developer. I have decided to use the MERN stack. I am in the midst of trying to write my first "full-stack" application. However I can't seem to figure out how to store the data from my get call and submit it to the class as a property. The get call will reach an end point I have set up in nodejs which will make a call to Mongo and return an array of numbers. The get call below works as I can console.log the number of elements in that array. I have tried a number of different ways but I can't seem to figure out how to get the number out of the THEN promise and into my class to display on the screen. Any help would be greatly appreciated!
const React = require('react');
const ReactDOM = require('react-dom');
const axios = require('axios');
//call with npm build
var num = axios.get('/api').then(result => {
console.log(result.data.length)
return result.data.length;
})
//Only show how many unused coupons are left.
var Message = React.createClass({
render: function () {
return <h1>There are {this.props.number} coupons left!</h1>
}//end of of render outer function
})
ReactDOM.render(<Message number={num} />,document.getElementById('content'))
First, api call is an asynchronous operation, you can't call in a sync way.
And, instead of returning derived from api value, axios.get function returns Promise, which resolving this value.
Correct solution would be create <App /> component and call to api in componentDidMount lifecycle callback, set corresponding state variable on success response and render <Message /> component providing this variable to it.
Look at example:
var App = React.createClass({
getInitialState() {
return {
num: null
};
}
componentDidMount() {
var num = axios.get('/api').then(result => {
this.setState({ num: result.data.length });
});
},
render() {
if (this.state.num) {
return <Message number={this.state.num} />
}
return <div />
}
});
ReactDOM.render(<App />,document.getElementById('content'))
In react-router, is there any way to pass a property from the Route-definition that can be picked up within the Router.run function? I want specific actions to fire for specific routes. Something like this perhaps:
<Route handler={someComponent} resolve={someAction} />
In Router.Run i want to execute that given action defined in resolve. Is there any way of doing that?
The reason for doing this is to make sure that each route can instantiate service-calls (from a defined action) to make sure that the stores have the data needed for that route. Doing it in the component is also a possibility, the problem we have now is that several components within the same route needs the same data, making it requesting data from the API several times, and also triggering rerendering for each result comming in from those calls.
You can do something like this in React Router by adding a static (using createClass) or a property on the component class (when using ES6 classes) and then executing them with React Router.
var Component1 = React.createClass({
statics: fetchData: function(params) {
return API.getData(params);
},
// ...
});
class Component2 extends React.Component {
// ...
}
Component2.fetchData = (params) => API.getData(params);
Then, when you run your router, look for all matched routes that have a fetchData static method and call it. We'll assume here that fetchData (and thus API.getData) returns a Promise:
Router.run(routes, function(Root, state) {
var data = {};
var routesWithFetchData = state.routes.filter(function (route) {
return route.handler.fetchData
});
var allFetchDataPromises = routesWithFetchData.map(function (route) {
return route.handler.fetchData(state.params).then(function (routeData) {
data[route.name] = routeData;
});
});
Promise.all(allFetchDataPromises).then(function() {
React.render(<Root data={data} />, container);
});
});
(See this React Router example for more details.)
You can solve the "multiple components fetch the same data" problem by ensuring that the API module will catch requests for the same data and assign them the same promise. Pseudocode:
API = {
getData: function(params) {
if (requests[params]) {
return requests[params];
} else {
request[params] = new Promise(function(resolve, reject) {
// fetch data here
});
}
}
};