Best way to create Observable, using from, in RxJS 5.x? - javascript

Now that we have "lettable" operators, how should we go about creating an Observable from another?
When I try to do:
import {Observable} from 'rxjs/Observable'
const source = Observable.from(someOtherStream)
I get the error Observable.from is not a function, which makes sense, because from is now something else that needs to be imported separately.
I don't want to do
import 'rxjs/add/observable/from' anymore due to the prototypal problems there.
What I ended up doing was:
import { Observable } from 'rxjs/Observable'
import { from } from 'rxjs/observable/from'
const myNewStream = from.call(
Observable,
someOtherStream
)
But this really feels "hacky" for some reason to me. Does anyone have any better ways of going about this?

The lettables are behind rxjs/operators here's the write up on it.
For from you should be able to import it without importing Observable.
import { from } from 'rxjs/observable/from';
const prom = new Promise(res => setTimeout(
() => res('promise'), 3000
));
from(prom).subscribe(x => console.log(x));
webpackbin example
Using from this way allows you to pipe() to the lettable operators.
import { from } from 'rxjs/observable/from';
import { map } from 'rxjs/operators/map';
const prom = new Promise(res => setTimeout(
() => res('promise'), 3000
));
from(prom).pipe(
map(x => x.split('').reverse().join(''))
).subscribe(x => console.log(x));
webpackbin example

I think this is misunderstanding what from does. It's just a function that takes a "stream" (that's Observable, Promise, array, ...) and creates an Observable from it.
This means you use it just like any other function:
import { from } from 'rxjs/observable/from'
from(someOtherStream).pipe(...).subscribe(...)

Related

React Calling function from another file and useEffect

Hello I am trying to render the updated stuff from the database whenever the update is invoked to the database. I found this solution but right now my "add" method is not in the same file as the "fetch" method like in the linked question. I tried the below but it still isn't working:
file 1: (the return method will render the UploadedImages by mapping them)
const [UploadedImages,setUploadedImages] = useState([])
const fetchUserAllPhotos = async () => {
const res = await client.get('/get-photos')
setUploadedImages(res.data.photoList)
}
useEffect(()=>{
fetchUserAllPhotos()
},[])
file 2:
import {fetchUserAllPhotos} from './Gallery/AllPhotos';
const addNewPhoto = async () => {
if (success) {
await fetchUserAllPhotos()
}
}
However inside the file 1's return (render) method it is not giving the updated result whenever a new photos is added (I need to sign out and sign back in in order to see the change). How can I go about solving it?
what if the function is wrapped inside another one?
export default function PhotoGrid(props){
const [UploadedImages,setUploadedImages] = useState([])
const fetchUserAllPhotos = async () => {
const res = await client.get('/get-photos')
setUploadedImages(res.data.photoList)
}
useEffect(()=>{
fetchUserAllPhotos()
},[])
}
you need to add this line at the bottom of file1
export {fetchUserAllPhotos}
In general, every time you want to import function, array, object etc it should look like this:
file1:
const func = () => {
//implementaion of the function
}
export {func}
file2:
import {func} from './file1' //you need to give the path of file1
func() //now you can use the fanction everywhere in file2
note: you can export as many functions as you wants just be careful it important to use the same name in the export and in the import
export {func1, func2, func3, obj1, abj2, arr1, arr2}
if you are exporting function from react component you need to define the function outside the component
const func = () => {
}
export default function MyReactComponent(prop) {
//implementation of your component
}
export {func}
If you need to use prop of your component inside the function you can pass this prop as a function parameter.
I recomend to avoid from exporting function from react component because when your project becomes bigger it will start to be frustrate to find were you impliment each fanction. instead it is a best practice to add a new js file that you implement there all the fetches function, I usually call this file api.js
This file should look something like this (I took this code from my project there I used axios to make the fetches to the server):
import axios from "axios"
const url = 'http://localhost:8080'
const api = {
getQuestions : async () => {
return axios.get(`${url}/question/all`)
},
getQuestionById : async (id) => {
return axios.get(`${url}/question/${id}`)
}
}
export default api;

How to access plugin from nuxt.js store?

I have this gtag (analytics plugin) that I can access on my components but never on my store.
I would appreciate any opinions. Thanks
plugins/vue-gtag.js
import Vue from "vue"
import VueGtag from "vue-gtag"
export default ({ app }, inject) => {
Vue.use(VueGtag, {
config: {
id: process.env.ga_stream_id
}
})
}
store/gaUserProperty.js
import Vue from "vue"
import { User } from "~/models/user/User"
export const states = () => ({})
const getterObjects = {}
const mutationObjects = {}
Object.keys(states).forEach(key => {
getterObjects[key] = state => state[key]
mutationObjects[key] = (state, value) => (state[key] = value)
})
export const state = () => states
export const getters = { ...getterObjects }
export const mutations = { ...mutationObjects }
export const actions = {
async sendUserProperties({ dispatch, commit }) {
let res = await this.$UserApi.getUser()
if (!(res instanceof User)) {
} else {
// I can access this on my components and pages but for some reason not here....
console.log(this.$gtag)
}
}
}
To import this properly, I would export the instance (or any of its internals) from main.(ts|js):
const Instance = new Vue({...whatever});
// only export what you need in other parts of the app
export const { $gtag, $store, $t, $http } = Instance;
// or export the entire Instance
export default Instance;
now you can import it in your store:
import Instance from '#/main';
// or:
import { $gtag } from '#/main';
// use Instance.$gtag or $gtag, depending on what you imported.
As other answers mentioned, in current Vuex version the Vue instance is available under this._vm inside the store. I'd refrain from relying on it, though, as it's not part of the exposed Vuex API and not documented anywhere. In other words, Vuex developers do not guarantee it will still be there in future versions.
To be even more specific, Vue's promise is that all v2 code will work in v3. But only if you use the exposed API's.
And the discussion here is not even on whether it will be removed or not (it most likely won't). To me, it's more a matter of principle: if it starts with a _ and it's not documented, it translates into a message from authors: "We're reserving the right to change this at any time, without warning!"
You can access the Vue instance through this._vm in the Vuex store, so you would just need to do:
console.log(this._vm.$gtag)
That should do the trick.
According to nuxtjs the plugins are available in the store actions.
https://nuxtjs.org/docs/directory-structure/plugins
Sometimes you want to make functions or values available across your
app. You can inject those variables into Vue instances (client side),
the context (server side) and even in the Vuex store. It is a
convention to prefix those functions with a $.

Vuex abstract common methods for inheritance (Large SPA)

Large SPA Vuex question - is there a way to abstract common methods that can be inherited or injected into Vuex namespace modules? What I have below works, but feels kludgy as I have to standardize states, etc. I could pass in assoc. array keys to accommodate different state stores, but looking to streamline. Ive look at Vuex plugins and I think there is way to use that to some degree, but again not ideal if I wanted something as simple as getById()
Another approach is to create a Vue IoC factory container, Vue provider, with a Vuex driver, services for for common components, but that is a lot of overhead and I feel would be an overkill, but maybe that is the best approach for a large SPA. Would appreciate some guidance as to where this is the right approach.
vuex-common.js
Collection of common service methods. I could create a common for getters, mutations, actions, etc.
import isArray from 'lodash/isArray'
export function getById () {
return state => {
const find = val => state.data.find(x => x.id === val)
return val => isArray(val) ? val.map(find) : find(val)
}
}
Namespace store example -- store/modules/Users.js
// removed other imports for brevity sake
import { getById } from 'vuex-common'
import forEach from 'lodash/forEach'
import {fetchUsers} from '~/api/apis/users.api';
const initialState = () => ({
data: []
});
const state = initialState();
const getters = {
getById: getById() // vuex-common
// removed other getters for brevity sake
}
const actions = {
async getUsers({commit, getters}) {
try {
const {data} = await fetchUsers();
forEach(data, (u) => {
/* Import getById vuex-common function */
if (!getters.getById(u.id)) {
commit('ADD_USER', u);
}
});
} catch (e) {
console.error("ERROR", e.message, e.name);
}
},
}
// removed mutations and exports for brevity sake

Jasmine spy on RxJS 5.5 operators

I am trying to spy on RxJS operators with Jasmine. There are different use cases in my tests where I want to be in control on what a Observable returns. To illustrate what I am trying to do I have created the example above even thought it does not make to much sense as this observable always returns the same hard coded string. Anyway it is a good example to show what I am trying to achieve:
Imagine I have the following Class.
import {Observable} from 'rxjs/Observable';
import {of} from 'rxjs/observable/of';
export class AwesomeTest {
constructor() {
}
getHero(): Observable<string> {
return of('Spiderman');
}
}
And the following test:
import {AwesomeTest} from './awesomTest';
import {of} from 'rxjs/observable/of';
import createSpyObj = jasmine.createSpyObj;
import createSpy = jasmine.createSpy;
describe('Awesome Test', () => {
let sut;
beforeEach(() => {
sut = new AwesomeTest()
})
fit('must be true', () => {
// given
const expectedHero = 'Superman'
const asserter = {
next: hero => expect(hero).toBe(expectedHero),
error: () => fail()
}
createSpy(of).and.returnValue(of('Superman'))
// when
const hero$ = sut.getHero()
// then
hero$.subscribe(asserter)
});
});
I try to spy on the Observable of operator and return a Observable with a value that I specified inside my test instead of the actual value it will return. How can I achieve this?
Before the new Rx Import Syntax I was able to do something like this:
spyOn(Observable.prototype,'switchMap').and.returnValue(Observable.of(message))
In your spec file, everything as a wildcard (don't worry about tree shaking, this is just for the tests)
import * as rxjs from 'rxjs';
You can then use rxjs for your spying
spyOn(rxjs, 'switchMap').and.returnValue(rxjs.of(message))

How can I mock an ES6 module import using Jest?

I want to test that one of my ES6 modules calls another ES6 module in a particular way. With Jasmine this is super easy --
The application code:
// myModule.js
import dependency from './dependency';
export default (x) => {
dependency.doSomething(x * 2);
}
And the test code:
//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';
describe('myModule', () => {
it('calls the dependency with double the input', () => {
spyOn(dependency, 'doSomething');
myModule(2);
expect(dependency.doSomething).toHaveBeenCalledWith(4);
});
});
What's the equivalent with Jest? I feel like this is such a simple thing to want to do, but I've been tearing my hair out trying to figure it out.
The closest I've come is by replacing the imports with requires, and moving them inside the tests/functions. Neither of which are things I want to do.
// myModule.js
export default (x) => {
const dependency = require('./dependency'); // Yuck
dependency.doSomething(x * 2);
}
//myModule-test.js
describe('myModule', () => {
it('calls the dependency with double the input', () => {
jest.mock('../dependency');
myModule(2);
const dependency = require('../dependency'); // Also yuck
expect(dependency.doSomething).toBeCalledWith(4);
});
});
For bonus points, I'd love to make the whole thing work when the function inside dependency.js is a default export. However, I know that spying on default exports doesn't work in Jasmine (or at least I could never get it to work), so I'm not holding out hope that it's possible in Jest either.
Edit: Several years have passed and this isn't really the right way to do this any more (and probably never was, my bad).
Mutating an imported module is nasty and can lead to side effects like tests that pass or fail depending on execution order.
I'm leaving this answer in its original form for historical purposes, but you should really use jest.spyOn or jest.mock. Refer to the jest docs or the other answers on this page for details.
Original answer follows:
I've been able to solve this by using a hack involving import *. It even works for both named and default exports!
For a named export:
// dependency.js
export const doSomething = (y) => console.log(y)
// myModule.js
import { doSomething } from './dependency';
export default (x) => {
doSomething(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';
describe('myModule', () => {
it('calls the dependency with double the input', () => {
dependency.doSomething = jest.fn(); // Mutate the named export
myModule(2);
expect(dependency.doSomething).toBeCalledWith(4);
});
});
Or for a default export:
// dependency.js
export default (y) => console.log(y)
// myModule.js
import dependency from './dependency'; // Note lack of curlies
export default (x) => {
dependency(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';
describe('myModule', () => {
it('calls the dependency with double the input', () => {
dependency.default = jest.fn(); // Mutate the default export
myModule(2);
expect(dependency.default).toBeCalledWith(4); // Assert against the default
});
});
You have to mock the module and set the spy by yourself:
import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
doSomething: jest.fn()
}))
describe('myModule', () => {
it('calls the dependency with double the input', () => {
myModule(2);
expect(dependency.doSomething).toBeCalledWith(4);
});
});
Fast forwarding to 2020, I found this blog post to be the solution: Jest mock default and named export
Using only ES6 module syntax:
// esModule.js
export default 'defaultExport';
export const namedExport = () => {};
// esModule.test.js
jest.mock('./esModule', () => ({
__esModule: true, // this property makes it work
default: 'mockedDefaultExport',
namedExport: jest.fn(),
}));
import defaultExport, { namedExport } from './esModule';
defaultExport; // 'mockedDefaultExport'
namedExport; // mock function
Also one thing you need to know (which took me a while to figure out) is that you can't call jest.mock() inside the test; you must call it at the top level of the module. However, you can call mockImplementation() inside individual tests if you want to set up different mocks for different tests.
To mock an ES6 dependency module default export using Jest:
import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');
// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);
describe('myModule', () => {
it('calls the dependency once with double the input', () => {
myModule(2);
expect(dependency).toHaveBeenCalledTimes(1);
expect(dependency).toHaveBeenCalledWith(4);
});
});
The other options didn't work for my case.
Adding more to Andreas' answer. I had the same problem with ES6 code, but I did not want to mutate the imports. That looked hacky. So I did this:
import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');
describe('myModule', () => {
it('calls the dependency with double the input', () => {
myModule(2);
});
});
And added file dependency.js in the " __ mocks __" folder parallel to file dependency.js. This worked for me. Also, this gave me the option to return suitable data from the mock implementation. Make sure you give the correct path to the module you want to mock.
The question is already answered, but you can resolve it like this:
File dependency.js
const doSomething = (x) => x
export default doSomething;
File myModule.js
import doSomething from "./dependency";
export default (x) => doSomething(x * 2);
File myModule.spec.js
jest.mock('../dependency');
import doSomething from "../dependency";
import myModule from "../myModule";
describe('myModule', () => {
it('calls the dependency with double the input', () => {
doSomething.mockImplementation((x) => x * 10)
myModule(2);
expect(doSomething).toHaveBeenCalledWith(4);
console.log(myModule(2)) // 40
});
});
None of the answers here seemed to work for me (the original function was always being imported rather than the mock), and it seems that ESM support in Jest is still work in progress.
After discovering this comment, I found out that jest.mock() does not actually work with regular imports, because the imports are always run before the mock (this is now also officially documented). Because of this, I am importing my dependencies using await import(). This even works with a top-level await, so I just have to adapt my imports:
import { describe, expect, it, jest } from '#jest/globals';
jest.unstable_mockModule('../dependency', () => ({
doSomething: jest.fn()
}));
const myModule = await import('../myModule');
const dependency = await import('../dependency');
describe('myModule', async () => {
it('calls the dependency with double the input', () => {
myModule(2);
expect(dependency.doSomething).toBeCalledWith(4);
});
});
I solved this another way. Let's say you have your dependency.js
export const myFunction = () => { }
I create a depdency.mock.js file besides it with the following content:
export const mockFunction = jest.fn();
jest.mock('dependency.js', () => ({ myFunction: mockFunction }));
And in the test, before I import the file that has the dependency, I use:
import { mockFunction } from 'dependency.mock'
import functionThatCallsDep from './tested-code'
it('my test', () => {
mockFunction.returnValue(false);
functionThatCallsDep();
expect(mockFunction).toHaveBeenCalled();
})
I tried all the solutions and none worked or were showing lots of TS errors.
This is how I solved it:
format.ts file:
import camelcaseKeys from 'camelcase-keys'
import parse from 'xml-parser'
class Format {
parseXml (xml: string) {
return camelcaseKeys(parse(xml), {
deep: true,
})
}
}
const format = new Format()
export { format }
format.test.ts file:
import format from './format'
import camelcaseKeys from 'camelcase-keys'
import parse from 'xml-parser'
jest.mock('xml-parser', () => jest.fn().mockReturnValue('parsed'))
jest.mock('camelcase-keys', () => jest.fn().mockReturnValue('camel cased'))
describe('parseXml', () => {
test('functions called', () => {
const result = format.parseXml('XML')
expect(parse).toHaveBeenCalledWith('XML')
expect(camelcaseKeys).toHaveBeenCalledWith('parsed', { deep: true })
expect(result).toBe('camel cased')
})
})
I made some modifications on #cam-jackson original answer and side effects has gone. I used lodash library to deep clone the object under test and then made any modification I want on that object. But be ware that cloning heavy objects can have negative impact on test performance and test speed.
objectUndertest.js
const objectUnderTest = {};
export default objectUnderTest;
objectUnderTest.myFunctionUnterTest = () => {
return "this is original function";
};
objectUndertest.test.js
import _ from "lodash";
import objectUndertest from "./objectUndertest.js";
describe("objectUndertest", () => {
let mockObject = objectUndertest;
beforeEach(() => {
mockObject = _.cloneDeep(objectUndertest);
});
test("test function", () => {
mockObject.myFunctionUnterTest = () => {
return "this is mocked function.";
};
expect(mockObject.myFunctionUnterTest()).toBe("this is mocked function.");
});
});

Categories