I'm trying to create a singleton service class in which I instanciate a connection object, which connect to the backend, in order to reuse the connection object in every component, so I've done that:
const {
Kuzzle,
WebSocket
} = require('kuzzle-sdk');
class KuzzleService {
static instance = null;
static async createInstance() {
var object = new KuzzleService();
object.kuzzle = new Kuzzle(
new WebSocket('localhost'),{defaultIndex: 'index'}
);
await object.kuzzle.connect();
const credentials = { username: 'user', password: 'pass' };
const jwt = await object.kuzzle.auth.login('local', credentials);
return object;
}
static async getInstance () {
if (!KuzzleService.instance) {
KuzzleService.instance = await KuzzleService.createInstance();
}
return KuzzleService.instance;
}
}
const kuzzleService = KuzzleService.getInstance();
export default kuzzleService;
But when I import the service in a component as follow:
import kuzzleService from "../services/kuzzle-service.js";
And I print that:
async componentDidMount(){
console.log(JSON.stringify(kuzzleService.kuzzle));
}
It gives me "undefined". Should I import the service another way ?
This is probably because when you export kuzzleService, the promise given by .getInstance() isn't resolved yet.
You should export the .getInstance function and await it in componentDidMount, like that:
export default KuzzleService; // export the singleton directly
async componentDidMount(){
const kuzzle = await KuzzleService.getInstance();
console.log(kuzzle);
}
Related
I'm following this guide to create an auth composition library: https://mattlaw.dev/blog/vuejs-the-composition-api-and-firebase-authentication/
At a high level, you define a file /src/components/auth/index.ts and that file has refs and "use" functions defined and directly exported, like:
export const user = ref(null);
export function useSignup() {
const email = ref("");
const password = ref("");
async function signup() {
if (email.value == "" || password.value == "") return;
const creds = await auth.createUserWithEmailAndPassword(
email.value,
password.value
);
if (!creds.user) throw Error("Signup failed");
user.value = creds.user;
}
return {
email,
password,
signup,
};
}
I want to replace the "auth.createUserWithEmailAndPassword" with a fake injected auth service so I can purely test the useSignup function, but I don't want to have to initiate a full component to wrap this as part of my test. Is there a way to get a vue app context, "provide" a DI property, and then test this from jest so I'm purely testing the useSignup code?
One way to test useSignup() without any component context is to import the auth module in useSignup():
import { ref } from "vue";
import auth from "#/auth"; // 👈
export const user = ref(null);
export function useSignup() {
const email = ref("");
const password = ref("");
async function signup() {
if (email.value == "" || password.value == "") return;
const creds = await auth.createUserWithEmailAndPassword(
email.value,
password.value
);
if (!creds.user) throw Error("Signup failed");
user.value = creds.user;
return creds.user
}
return {
email,
password,
signup,
};
}
Then your test could mock that module:
Use jest.mock('#/auth') at the top of the test file.
In your test, require the module (which has been mocked above), and use mockReturnValue() to set the mock return value.
Setup the credentials ref values, and call signup().
Verify the mock createUserWithEmailAndPassword() was called.
import { useSignup } from "#/useSignup";
jest.mock("#/auth"); 1️⃣
describe("useSignup", () => {
it("signs in", async () => {
2️⃣
const { createUserWithEmailAndPassword } = require("#/auth").default;
const myEmail = "john#gmail.com";
createUserWithEmailAndPassword.mockReturnValue({
user: myEmail
});
3️⃣
const { email, password, signup } = useSignup();
email.value = myEmail;
password.value = "myPassword";
await signup();
4️⃣
expect(createUserWithEmailAndPassword).toHaveBeenCalledWith(email.value, password.value);
})
})
I am using Nuxt and Rollbar. I have a user id state in store.
My question is, how can I set this user id as a custom payload in the transformer function in rollbar.js WITHOUT using localStorage?
Here is my code:
// plugins/rollbar.js
const transformer = function(payload) {
payload.user_id = user_id_from_store // how to get this from store?
}
// store/index.js
export const state = () => ({
userId: ''
})
export const mutations = {
setUserId(state, userId) {
state.userId = userId
}
}
//components/MyComponent.vue
methods: {
fetch() {
const userId = fetchUserId()
this.$store.commit('setUserId', userId)
}
}
Things I have tried:
In rollbar.js, create and export a function which takes a context object as argument. Then call this function in transformer function to get user_id:
// plugins/rollbar.js
const getUserId = context => {
const user_id = context.store.state.userId
return user_id
}
const transformer = function(payload) {
payload.user_id = getUserId()
}
export default getUserId
When I console.log(context.store)in getUserId function, I got a Store object, but calling the function in transformer function threw Rollbar: Error while calling custom transform() function. Removing custom transform(). TypeError: Cannot read property 'store' of undefined.
At the end, OP succeeded thanks to inject, more info available here: https://nuxtjs.org/docs/2.x/directory-structure/plugins#inject-in-root--context
This one is indeed needed for libraries that are not directly into the Vue ecosystem but that we wish to have working in our Nuxt app.
I have an AuthService that I use in a namespaced store in my Nuxt app. I need to commit mutations from AuthService to the namespaced store but I can't figure out how to import the store into my AuthService.
I've seen examples where the store is imported into the JS file, but the store is explicitly defined in the Vue app. Because I'm using Nuxt with the Module mode for my store, I'm not sure of the root path where I can import my store into the AuthService file. As I understand it, Nuxt handles creating the root store and all the namespaced store behind the scenes when use "Module mode"
My Nuxt store directory includes index.js (which is empty) and auth.js which has the mutations I want to call from AuthService.
auth.js
import AuthService from '../firebase/authService'
const authService = new AuthService()
export const state = () => ({
user: null
})
export const mutations = {
setUser (state, user) {
state.user = user
}
}
export const actions = {
async signUp ({ commit }, payload) {
try {
await authServices.createUser(payload)
return Promise.resolve()
} catch (err) {
const notification = {
duration: 5000,
message: err.message,
type: 'error'
}
commit('ui/activateNotification', notification, { root: true })
return Promise.reject()
}
}
}
authService.js
import { fAuth, fDb } from './config'
// I think I need to import auth store here but I'm not sure how
export default class AuthClient {
async createUser (payload) {
try {
const res = await fAuth.createUserWithEmailAndPassword(payload.email, payload.password)
const { uid } = res.user
const user = {
...payload,
uid
}
await this._createUserDoc(user)
this._initAuthListener()
return Promise.resolve()
} catch (err) {
return Promise.reject(err)
}
}
async _createUserDoc (user) {
await fDb.collection('users').doc(user.uid).set(user)
}
_initAuthListener () {
fAuth.onAuthStateChanged(async (user) => {
try {
if (user) {
const userProfileRef = fDb.collection('users').doc(user.uid)
const userProfileDoc = await userProfileRef.get()
const { uid, userName } = userProfileDoc.data()
// Here is where I want to call a mutation from the auth store
this.store.commit('setUser', {
uid,
userName
})
} else {
this.store.commit('setUser', null)
}
} catch (err) {
console.log(err)
}
})
}
}
I'd like to propose a solution using a plugin.
In the external module (externalModule.js) we define store variable and export an init function that receives Nuxt context as argument. The function assignes the store from context to the variable which can be now used in the module:
let store;
export function init (context) {
store = context.store;
};
(...further business logic using store)
Then in the plugins folder we create a plugin file (let's call it storeInit.js). The file imports the init function from the external module and exports default plugin function required by Nuxt. The function receives context from Nuxt and we call the init function passing the context further:
import { init } from '[pathTo]/externalModule.js';
export default (context, inject) => {
init(context);
};
Then we register the plugin in the nuxt.config.js file:
module.exports = {
...
plugins: [
{ src: '~/plugins/storeInit' }
],
...
}
This way when the app is built by Nuxt and plugins are registered, the context object is passed to the external module and we can use anything from it, among others the store.
In index.js file which is in store folder you need to return store like this
import Vuex from 'vuex'
const createStore = () => {
return new Vuex.Store({
state: {
counter: 0
},
mutations: {
increment (state) {
state.counter++
}
}
})
}
export default createStore
and in your authService.js file you need to import store like this
import $store from '~/store'
by this you will be able to access your store
$store.commit('setUser', null)
I hope this works for you
Important Note: you don't need to install vuex because it is already shipped with nuxtjs
You can access as window.$nuxt.$store
Note: My nuxt version is 2.14.11
I have a repository (in a MenuRepository.js file) that has an index() method, when I try to call that method from my mounted() function in my Vue instance, I get the following error
This has been working before, So I can't imagine what happened..
This is the code of my Vue instance.
class MenuRepository {
async index () {
const result = await Nova.request().get('/')
return result.data
}
}
export default MenuRepository
And this is the Vue file
import MenuRepository from '../repositories/MenuRepository'
export default {
async mounted () {
try {
const menus = await MenuRepository.index()
} catch (err) {
console.error(err)
}
}
}
Solution
The issue was that it wasn't being instantiated.
Use
export default new MenuRepository()
Instead of
export default MenuRepository
Good day. I have the following problem:
I have an item editor.
How it works: I push 'Add' button, fill some information, click 'Save' button.
_onSaveClicked function in my react component handles click event and call function from service, which sends params from edit form to server and return promise.
_onSaveClicked implements
.then(response => {
console.log('I\'m in then() block.');
console.log('response', response.data);
})
function and waits for promise result. It works in real situation.
I created fake service and placed it instead of real service.
Service's function contains:
return Promise.resolve({data: 'test response'});
As you can see fake service return resolved promise and .then() block should work immediatly. But .then() block never works.
Jest test:
jest.autoMockOff();
const React = require('react');
const ReactDOM = require('react-dom');
const TestUtils = require('react-addons-test-utils');
const expect = require('expect');
const TestService = require('./service/TestService ').default;
let testService = new TestService ();
describe('TestComponent', () => {
it('correct test component', () => {
//... some initial code here
let saveButton = TestUtils.findRenderedDOMComponentWithClass(editForm, 'btn-primary');
TestUtils.Simulate.click(saveButton);
// here I should see response in my console, but I don't
});
});
React component save function:
_onSaveClicked = (data) => {
this.context.testService.saveData(data)
.then(response => {
console.log('I\'m in then() block.');
console.log('response', response.data);
});
};
Service:
export default class TestService {
saveData = (data) => {
console.log('I\'m in services saveData function');
return Promise.resolve({data: data});
};
}
I see only "I'm in services saveData function" in my console.
How to make it works? I need to immitate server response.
Thank you for your time.
You can wrap your testing component in another one like:
class ContextInitContainer extends React.Component {
static childContextTypes = {
testService: React.PropTypes.object
};
getChildContext = () => {
return {
testService: {
saveData: (data) => {
return {
then: function(callback) {
return callback({
// here should be your response body object
})
}
}
}
}
};
};
render() {
return this.props.children;
}
}
then:
<ContextInitContainer>
<YourTestingComponent />
</ContextInitContainer>
So your promise will be executed immediately.