Electron fetch is not defined when calling function from global shortcuts - node.js

I'm getting an error that fetch is not defined when calling my electron code from a global shortcut handler but not when calling it via IPC from the renderer.
Here's my code:
[utils/api.ts]
export const callAPI = async (inputText: string) => {
const data = {
input_text: inputText,
};
const res = await fetch(`${backendUrl()}/api/x`, {
method: 'post',
headers: jsonHeaders,
body: JSON.stringify(data),
});
[main.ts]
app
.whenReady()
.then(() => {
createWindow();
registerGlobalShortcuts(); // registering here
app.on('activate', () => {
if (mainWindow === null) createWindow();
});
})
.catch(console.log);
[globalShortcuts.ts]
import { callAPI } from './utils/api.ts';
const { globalShortcut } = require('electron');
const { clipboard } = require('electron');
const generateOutput = () => {
const text = clipboard.readText();
console.log('text', text);
callAPI(text);
};
export const registerGlobalShortcuts = () => {
globalShortcut.register('Control+CommandOrControl+C', () => {
console.log('shortcut hit');
generateOutput();
});
};
When I run this, and hit the keyboard shortcut, I get this error in the terminal:
(node:23565) UnhandledPromiseRejectionWarning: ReferenceError: fetch is not defined
at callOpenAI (/Users/hima/Workspace/general-desktop-agent/desktop-electron/src/main/lib
/textManipulation.ts:38:15)
at generateTextContinuationPrediction (/Users/hima/Workspace/general-desktop-agent/deskt
op-electron/src/main/globalShortcuts.ts:11:13)
at Function.<anonymous> (/Users/hima/Workspace/general-desktop-agent/desktop-electron/sr
c/main/globalShortcuts.ts:17:5)
But also in my code, in my renderer, I also call window.electron.callAPI(text) there as well, and fetch is found when doing that and the call works perfectly.
So I don't understand why when making the API call from the global shortcut that it says fetch is not defined?

Related

Can't fetch my data from my backend(NodeJS) to my react native app

I have tried to get my json data but it's not working. It works to get the data on postman and on chrome but when I try to fetch the data it comes back as null. I'm using apisauce.
any ideas? I'm new to react native :)
const [listings, setListings] = useState([])
useEffect(() => {
loadListings()
}, []);
const loadListings = async () => {
const response = await listingsApi.getListings()
console.log(response.data, "response.data")
setListings(response.data)
}
import { create } from "apisauce";
const apiClient = create({
baseURL: "http://127.0.0.1:9000/api",
});
export default apiClient;
import client from "./client";
const endpoint = "/listings";
const getListings = () => client.get(endpoint);
export default {
getListings,
};
You can call loadListing like that (because loadListing is asynchronous function)
useEffect(() => {
loadListings().then(() => {}).catch(() => {})
},[])
or can be called using IIFE
useEffect(() => {
(async() => {
await loadListings();
})()
},[])

Error: React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render

import { setCookies, removeCookies } from "cookies-next";
import { useRouter } from "next/router";
import { useEffect } from "react";
const { URL } = process.env;
export const getServerSideProps = async (context) => {
const userAuthToken = context.req.cookies["authToken"];
const data = {
authToken: userAuthToken,
};
const requestJSON = JSON.stringify(data);
const response = await fetch(URL + "api/userFetch", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: requestJSON,
});
const responseData = await response.json();
return {
props: { datas: responseData },
};
};
const Home = ({ datas }) => {
const router = useRouter();
if (datas[0].error == true) {
useEffect(() => {
setTimeout(() => {
router.push("/");
}, 3000);
}, []);
removeCookies("authToken");
return <h1>Something Went Wrong</h1>;
} else {
return <h1>Welcome To Home{datas[0].error}</h1>;
}
};
export default Home;
This code is running fine on development server but when I try to build this code in production I get this error **
./pages/Home.js
28:5 Error: React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?**
I tried everything I could but can't fix the error
Just do exactly what the error is telling you. Move the useEffect call out of the conditional block. You can still conditionally perform the operation within the hook. For example:
const Home = ({ datas }) => {
const router = useRouter();
useEffect(() => {
if (datas[0].error == true) {
setTimeout(() => {
router.push("/");
}, 3000);
}
}, []);
if (datas[0].error == true) {
removeCookies("authToken");
return <h1>Something Went Wrong</h1>;
} else {
return <h1>Welcome To Home{datas[0].error}</h1>;
}
};
Specifically, as the error states, the same hooks must always be called on every render. (I don't know enough under the hood of React to describe why that's the case, it just seems necessary for stability/consistency/etc.) But the operation being performed by the hook in this case can still be effectively a no-op if the intended condition is not met.

How to reset global variable on custom hook

I need to reset global variable on custom hook when unit testing React component. I have read few tutorials and StackOverflow answers to this simple task, but without luck to implement it correctly.
The problem
userInfo is undefined in the first and second test but when runs the third test userInfo is defined then on useEffect doesn't change the value... So my question is how to reset userInfo for each test.
jest.resetModules // doesn't work
jest.isolateModules // doesn't work
My simplest possible setup for single test is as following:
My Environment
"jest": "^24.9.0",
My Hook
import {useState, useEffect} from "react";
// This variable is an object save user info
let userInfo = null;
export default (authService) => {
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (userInfo !== null || authService === null) {
return;
}
setLoading(true);
authService
?.getUser()
.then((response) => {
userInfo = {owners: [{...response, cost_center: response.costCenter || "N/A"}]};
})
.catch(() => {
setError({
title: "authService Error",
message: "Error getting user",
status: 500
});
})
.finally(() => setLoading(false));
}, [authService]);
return [userInfo, error, loading];
};
My Test
import {renderHook} from "#testing-library/react-hooks";
import * as sinon from "sinon";
import {getSpyOfUseOktaAuth} from "../../../__tests__/";
import {Info, InfoFromRequest, InfoWithNoCostCenter} from "../../../__tests__/";
describe("useGetUserInfo", () => {
let clock;
beforeEach(() => {
clock = sinon.useFakeTimers();
jest.useFakeTimers();
});
afterAll(() => {
clock.restore();
});
it("should set the error value after the getUserInfo function throws an error", async () => {
const useGetUserInfo = require("../index").default;
const errorMessage = {
title: "authService Error",
message: "Error getting user from",
status: 500
};
const getAuthMock = getSpyOfUseAuth({
Auth: {
signOut: jest.fn(),
getUser: jest.fn(async () => {
throw new Error("Auth Error");
})
},
authState: {}
});
const {result, rerender, waitForNextUpdate} = renderHook(() =>
useGetUserInfo(getAuthMock.results.Auth)
);
rerender();
await waitForNextUpdate();
expect(result.current[1]).toEqual(errorMessage);
getAuthMock.instance.mockRestore();
});
it("should return the user info from after run the getUserInfo function", async () => {
const useGetUserInfo = require("../index").default;
let authService = null;
const {result, rerender, waitForNextUpdate} = renderHook(() => useGetOktaUserInfo(authService));
const getAuthMock = getSpyOfUseAuth({
Auth: {
signOut: jest.fn(),
getUser: jest.fn(async () => Info)
},
authState: {}
});
authService = getAuthMock.results.Auth;
rerender();
await waitForNextUpdate();
expect(result.current[0]).toEqual(InfoFromRequest);
getAuthMock.instance.mockRestore();
});
it("should set cost_center as in data as N/A if costCenter is not defined in user info ", async () => {
const useGetUserInfo = require("../index").default;
const getAuthMock = getSpyOfUseAuth({
Auth: {
signOut: jest.fn(),
getUser: jest.fn(async () => InfoWithNoCostCenter)
},
authState: {}
});
const {result, rerender} = renderHook(() => useGetUserInfo(getAuthMock.results.Auth));
rerender();
expect(result.current[0].owners[0].cost_center).toEqual("N/A");
getAuthMock.instance.mockRestore();
});
});
I would say that either you export the 'userInfo' variable from the hook and you set it to null manually before each test, or you treat 'userInfo' as a state variable just like 'error' and 'loading'
If you go for the first option, you will need to export by reference Node Modules - exporting a variable versus exporting functions that reference it?
For the second option, it would be something like this
import {useState, useEffect} from "react";
export default (authService) => {
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const [userInfo, setUserInfo] = useState(null);
useEffect(() => {
if (userInfo !== null || authService === null) {
return;
}
setLoading(true);
authService
?.getUser()
.then((response) => {
setUserInfo({owners: [{...response, cost_center: response.costCenter || "N/A"}]});
})
.catch(() => {
setError({
title: "authService Error",
message: "Error getting user",
status: 500
});
})
.finally(() => setLoading(false));
}, [authService]);
return [userInfo, error, loading];
};
The solution I got is to separate each case into different files. As jest load each file in a different process, this will be enough

Jest mocking a function within another function

I have searched quite a bit, and whilst there are many questions asked about mocking functions, none of the solutions I've found work for my setup, and I'm not quite sure why. I have the following:
questionnaire-service.js
const service = require('./request-service')();
function questionnaireService() {
function createQuestionnaire() {
const opts = {
url: `http://some-url.com`,
body: {
data: "some-data"
}
};
return service.post(opts);
}
return Object.freeze({
createQuestionnaire
});
}
module.exports = questionnaireService;
request-service.js
const got = require('got');
const merge = require('lodash.merge');
function requestService() {
function post(options) {
let opts = {
method: 'POST',
headers: {
accept: 'application/json',
'Content-Type': 'application/json'
},
json: true,
body: options.body
};
opts = merge(opts, options);
return got(opts.url, opts);
}
return Object.freeze({
post
});
}
module.exports = requestService;
I am trying to write tests for the questionnaire service, and want to mock the 'post' function. I have tried the following
questionnaire-service.test.js
const requestService = require('./request-service')();
const questionnaireService = require('./questionnaire-service')();
const createdQuestionnaire = require('./test-fixtures/res/get_questionnaire.json');
describe('questionnaire service routes', () => {
it('Should create a new questionnaire', async () => {
const spy = jest.spyOn(requestService.post);
spy.mockReturnValue(createdQuestionnaire);
const response = await questionnaireService.createQuestionnaire();
expect(requestService.post).toBeCalled();
expect(response).toMatch(createdQuestionnaire);
}
it('Should create a new questionnaire', async () => {
jest.doMock('./questionnaire-service', () =>
jest.fn(() => ({
createQuestionnaire: () => createdQuestionnaire
}))
);
const response = await questionnaireService.createQuestionnaire();
expect(questionnaireService.createQuestionnaire).toBeCalled();
expect(response).toMatch(createdQuestionnaire);
}
it('Should create a new questionnaire', async () => {
jest.doMock('./request-service', () =>
jest.fn(() => ({
post: () => createdQuestionnaire
}))
);
const response = await questionnaireService.createQuestionnaire();
expect(requestService.post).toBeCalled();
expect(response).toMatch(createdQuestionnaire);
}
});
All of the above yield the same error: RequestError: getaddrinfo ENOTFOUND some_token some_token:443 which sounds like it's being thrown by the 'GOT' module not finding a url to hit. Can someone shed some light on how to get this to work correctly?
When you make a require("somemodule") it loads this module and executes it, so it executes require calls inside this module. Then all the modules are cached, so whenever you make a require again, it doesn't execute again, and returns already cached dependencies.
When you do doMock, you need to reset this cache, and make new require in your test for everything, that depends on mocked module.
jest.doMock('./questionnaire-service', () =>
jest.fn(() => ({
createQuestionnaire: () => createdQuestionnaire
}))
);
jest.resetModules();
const questionnaireService = require(`questionnaire-service`)();
const response = await questionnaireService.createQuestionnaire();

Thunk + Redux (in React Native) : Can't get action to work

I'm brand new to this stack. I've seen quite a few other questions about this and have read the Thunk documentation but I can't stitch this together.
When I run the code below I get the error "Actions must be plain objects, use custom middleware for async actions" which is exactly the problem I'm trying to solve with Thunk.
My action looks like this:
src/actions/recipes.js
// this calls the API
function fetchApiGetRecipes() {
return fetch('https://mywebsite.com/endpoint/', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + idToken
}
}).then((json) => {
dispatch({
type: 'RECIPES_REPLACE',
data: json
})
});
}
// this is passed into my container to use to refresh the recipe list
export function getRecipes() {
if (Firebase === null) return () => new Promise(resolve => resolve());
if (Firebase.auth().currentUser !== null) {
Firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// console.log(idToken);
return dispatch => new Promise(resolve => fetchApiGetRecipes(idToken) )
}).catch(function(error) {
// Handle error
});
} else {
console.log("Null user");
}
}
What is the correct syntax to use Thunk here and fix the error I'm getting when the app starts up?
EDIT: I create the store like this:
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/es/storage'; // default: localStorage if web, AsyncStorage if react-native
import thunk from 'redux-thunk';
import reducers from '../reducers';
// Redux Persist config
const config = {
key: 'root',
storage,
blacklist: ['status'],
};
const reducer = persistCombineReducers(config, reducers);
const middleware = [thunk];
const configureStore = () => {
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
compose(applyMiddleware(...middleware)),
);
const persistor = persistStore(
store,
null,
() => { store.getState(); },
);
return { persistor, store };
};
export default configureStore;
Your getRecipes function doesn't return a function in the if (Firebase.auth().currentUser !== null) clause.
You need to return a function where you are just doing
Firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// console.log(idToken);
return dispatch => new Promise(resolve => fetchApiGetRecipes(idToken) )
}).catch(function(error) {
// Handle error
});
The dispatch function (I assume is the intended one to return) is being returned in the then clause of the promise. That doesn't return the dispatch function to the outer method getRecipies. Hence the error
probably you forgot pass dispatch to func args?
// you use dispatch in this func
function fetchApiGetRecipes() {...}
// you forget pass
return dispatch => new Promise(resolve => fetchApiGetRecipes(idToken) )

Resources