react-electron ipcRenderer in useEffect does not rerender - javascript

I'm trying to make a global accessible object, so that its values can be changed and can be read from every component. I've create a classs with static fields:
export default class Settings {
static first: string;
static second: string;
}
Lets say I have two components in separate files:
import Settings from './Settings'
// located in firstComponent file
export default function FirstComponent() {
Settings.First = 'test' <---------------- SET VALUE
return (some html)
}
// located in secondComponent file
export default function SecondComponent() {
let qq = Settings.First <----------------------- ASSUME HERE IS VALUE "TEST"
}
But it is not working. How I can create static class/fields that will be accessible within all components like C# static classes. Is it even possible?
Thanks in advance
UPD
Looks like the problem in ipcRenderer:
export default function SettingsEditor() {
const [state, setState] = React.useState({
ipAddress: Settings.ipAddress,
});
useEffect(() => {
electron.ipcRenderer.once('readSettings', (args) => {
console.log('Filling settings');
console.log(args); <------ HERE WE HAVE VALUE like 10.10.10.10
setState({ ...state, ipAddress: args.ipAddress});
console.log(state.ipAddress); <------ UNDEFINED
state.ipAddress = args.ipAddress;
console.log(state.ipAddress); <------ UNDEFINED
Settings.ipAddress= args.ipAddress;
console.log(Settings.gateIpAddress); <------ UNDEFINED
});
electron.settingsApi.read();
}, []);
How I can handle this?

The reason is - I'm stupid =)
When I save settings I did it like this:
const settings = new Settings();
settings.ipAddress= state.ipAddress;
console.log(JSON.stringify(settings));
electron.settingsApi.save(settings); <------ PASS OBJECT
But when I return response it was:
ipcMain.on('readSettings', (event, _) => {
storage.getAll((err: Error, data: object) => {
if (err) {
return;
}
const { appSettings } = data;
const settings = new Settings();
settings.ipAddress= appSettings.ipAddress;
console.log('reading settings');
console.log(JSON.stringify(settings));
event.reply('readSettings', JSON.stringify(settings)); <-------- FACEPALM
});
});
What I can say - genius

Related

Nextjs getStaticProps not fired

My [slug].js file, below, has two nextjs helper functions. The getStaticPaths and getStaticProps are exported. In my case, it creates the path posts/[slug]. There is one post file added called hello.json. Now when I navigate to localhost:3000/posts/hello it errors saying:
TypeError: Cannot read property 'fileRelativePath' of undefined. For line 10.
This makes sense after seeing that jsonFile is undefined. As a matter of fact, the whole getStaticProps is never called, the log in there is never logged. Why is this happening?
Thanks in advance.
import React from 'react';
import glob from 'glob';
import { usePlugin } from 'tinacms';
import { useJsonForm } from 'next-tinacms-json';
const Page = ({ jsonFile }) => {
console.log(121212, jsonFile);
// Create the tina form
const [post, form] = useJsonForm(jsonFile);
// Register it with the CMS
usePlugin(form);
return (
<h1>
{post.title}
</h1>
);
};
export default Page;
/**
* By exporting the async function called getStaticProps from a page, Next.js
* pre-renders this page at build time using the props returned by
* getStaticProps.
* The getStaticPaths function defines a list of paths that have
* to be rendered to HTML at build time.
*/
export async function getStaticProps({ ...ctx }) {
console.log(1212, ctx);
const { slug } = ctx.params;
const dynamicPath = `../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(dynamicPath);
console.log(121212, content);
return {
props: {
jsonFile: {
fileRelativePath: `/posts/${slug}.json`,
data: content.default,
},
},
};
}
export async function getStaticPaths() {
//get all .json files in the posts dir
const posts = glob.sync('posts/**/*.json');
const paths = posts.map(file => ({
params: {
slug: `${file.replace('.json', '')}`,
},
}));
return {
paths,
fallback: true,
};
};
After some more digging around I found the issue, posting here to hopefully help future readers with the same issue.
The culprit was this:
const dynamicPath = `../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(dynamicPath);
Using a variable in a dynamic import does not work, only strings or template literals. I used a variable because of a eslint parsing error that can only be resolved by downgrading to an earlier version of eslint. This causes eslint to not work for me in this file, but okay, at least the function is called.
This combined with the observation that the component code is called before the getStaticProps is called made the jsonFile variable undefined and the whole component erroring before it'll ever reach the getStaticProps. You can see that the log starting with 121212 is coming in earlier than 1212. Terminal logs:
121212 {
fileRelativePath: 'posts/hello.json',
data: { title: 'Not the actual data' }
}
1212 hello
This is counter intuitive to me as I figured it would first get the props and pass them to the component immediately, but sadly, defining default props is needed to work around this.
New code:
import React from 'react';
import glob from 'glob';
import { usePlugin } from 'tinacms';
import { useJsonForm } from 'next-tinacms-json';
const Page = ({ jsonFile }) => {
console.log(121212, jsonFile);
// Get content and form for Tina
const [content, form] = useJsonForm(jsonFile);
// Register it with the CMS
usePlugin(form);
return (
<h1>
{content.title}
</h1>
);
};
Page.defaultProps = {
jsonFile: {
fileRelativePath: 'posts/hello.json',
data: {
title: 'Not the actual data',
},
},
};
export default Page;
/**
* By exporting the async function called getStaticProps from a page, Next.js
* pre-renders this page at build time using the props returned by
* getStaticProps.
*/
export async function getStaticProps({ params: { slug } }) {
console.log(1212, slug);
// This line caused the issue
// const dynamicPath = (`../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(`../../posts/${slug}.json`);
return {
props: {
jsonFile: {
fileRelativePath: `posts/${slug}.json`,
data: content.default,
},
},
};
}
/**
* The getStaticPaths function defines a list of paths that have
* to be rendered to HTML at build time.
*/
export async function getStaticPaths() {
//get all .json files in the posts dir
const posts = glob.sync('posts/**/*.json');
return {
paths: posts.map(file => ({
params: {
slug: `${file.replace('.json', '')}`,
},
})),
fallback: true,
};
}

I can't access modified window.location object jest in another module

I want to mock window.location.search.
config.test.js
import config from './config'
import { MULTIPLE_VIDEOS } from './Constants/flagKeys'
describe('', () => {
const flag = { [MULTIPLE_VIDEOS]: true }
global.window = Object.create(window)
Object.defineProperty(window, 'location', {
value: {}
})
afterAll(() => {
global.window = null
})
it('Mark query string flag as true', () => {
global.window.location.search = `?${MULTIPLE_VIDEOS}=true`
expect(config.flags).toEqual(flag)
})
})
config.js
export default { flags: getFlagsFromQueryString() }
function getFlagsFromQueryString () {
const queryString = qs.parse(window.location.search.slice(1))
const flags = {}
Object.entries(queryString).forEach(([name, value]) => {
flags[name] = value.toLowerCase() === 'true'
})
return flags
}
Though I set search value in location object before calling config.flags, I can't access it inside the function, It always returns empty string.
I want window.location.search.slice(1) to return ?multipleVideos=true inside getFlagsFromQueryString function instead of empty string, since I changed the value in the test file.
One thing I noticed, when I export the function and call in the test file it works.
For this type of complex uses. I would recommend you to create a Window service/util class and expose methods from there. easy to test and mock.
Sample:
// Window.js
class Window {
constructor(configs) {}
navigate(href) {
window.location.href = href;
}
}
export default new Window({});
Now you can easily mock Window.js. It is similar to DI pattern.

Jest testing react component with react-intersection-observer [duplicate]

This question already has answers here:
Testing code that uses an IntersectionObserver
(7 answers)
Closed 9 months ago.
I'm trying to test a component that has a child component that includes react-intersection-observer but I always get an error
I tried to import the library but it still fails.
This the initial test
beforeEach(() => {
testContainer = document.createElement("div");
document.body.appendChild(testContainer);
subject = memoize(() =>
mount(<FilterNav {...props} />, { attachTo: testContainer })
);
});
afterEach(() => {
testContainer.remove();
});
context("the main filter is shown initially", () => {
it("sets focus on the primary filter", () => {
subject();
const input = testContainer.querySelector(".main-input");
expect(document.activeElement).toEqual(input);
});
I'm getting this error -> Uncaught [ReferenceError: IntersectionObserver is not defined]
Is there a way I can mock IntersectionObserver?
Thanks
you can do something like this in mock file(i called it intersectionObserverMock.js for exmple):
const intersectionObserverMock = () => ({
 observe: () => null
})
window.IntersectionObserver = jest.fn().mockImplementation(intersectionObserverMock);
and in the test file import the file directly like this:
import '../__mocks__/intersectionObserverMock';
this will solve youre "IntersectionObserver is not defined" issue
Having same issue with Component that using IntersectionObserver.
Update: we need to import intersection-observer directly on test file.
import 'intersection-observer';
Create a mock in your setupTests file:
// Mock IntersectionObserver
class IntersectionObserver {
observe = jest.fn()
disconnect = jest.fn()
unobserve = jest.fn()
}
Object.defineProperty(window, 'IntersectionObserver', {
writable: true,
configurable: true,
value: IntersectionObserver,
})
Object.defineProperty(global, 'IntersectionObserver', {
writable: true,
configurable: true,
value: IntersectionObserver,
})
This will sort it not being defined.
The only thing that worked for me, create a file src\__mocks__\intersectionObserverMock.js
global.IntersectionObserver = class IntersectionObserver {
constructor() {}
observe() {
return null;
}
disconnect() {
return null;
};
unobserve() {
return null;
}
};
In your test import file:
import './__mocks__/intersectionObserverMock'
To fix this issue I'd recommend using mockAllIsIntersecting from test-utils.js in react-intersection-observer. This function mocks the IntersectionObserver.
e.g.
import { mockAllIsIntersecting } from 'react-intersection-observer/test-utils';
it("sets focus on the primary filter", () => {
subject();
mockAllIsIntersecting(true);
const input = testContainer.querySelector(".main-input");
expect(document.activeElement).toEqual(input);
});
I actually was able to solve this issue with a mock function and include it in the window object
const observe = jest.fn();
window.IntersectionObserver = jest.fn(function() {
this.observe = observe;
});
I extended Teebu's solution to trigger a first intersecting element. My test then act as if the intersection would match.
global.IntersectionObserver = class IntersectionObserver {
constructor(private func, private options) {}
observe(element: HTMLElement) {
this.func([{isIntersecting: true, target: element}]);
}
disconnect() {
return null;
};
unobserve() {
return null;
}
};

Passing a parameter to a function inside a named export object literal

When using a named export to return an object literal composed of functions, is it possible to pass a parameter to one of those functions?
For example, let's say the function below returns conditional results depending on if user's an admin:
// gridConfig.js
function getColumnDefs(isAdmin = false) {
// conditionally return columns
return {
orders: [ ... ],
...
}
}
export const config = {
columnDefs: getColumnDefs(),
rowDefs: getRowDefs(),
...
};
// main.js
import { config } from './gridConfig';
function doStuff() {
const { columnDefs, rowDefs } = config;
grid.columnDefs = columnDefs['orders'];
...
}
If I add the parameter to the function call inside the export, it says the param isn't defined. Adding a parameter to the export alias gives syntax errors. Even if it allowed this, I'm not clear where I'd pass my param inside main.js.
Is there some way of passing a parameter when structuring an export in this manner?
Maybe keeping it simple can be useful :)
export const config = (isAdmin) => ({
columnDefs: getColumnDefs(isAdmin),
rowDefs: getRowDefs(),
...
});
// Import
import { config } from '[...]'; // Placeholder path of import
const myConfigFalse = config(false);
const myConfigTrue = config(true);
export const config = admin => ({
columnDefs: getColumnDefs(admin),
rowDefs: getRowDefs(),
});
// main.js
import { config } from './gridConfig';
function doStuff() {
const { columnDefs, rowDefs } = config(admin);//get the admin variable set before this line
grid.columnDefs = columnDefs['orders'];
...
}

Jest spyOn a function not Class or Object type

I am familiar with setting spies on Class or Object methods, but what about when the function is just an export default - such that the method itself is independent, like a utility?
I have some existing code like so:
const Funct1 = props => {
if(props){
Funct2(args);
}
// or return something
};
const Funct2 = props => {
// do something
return true
};
export default Funct1; //Yes the existing export is named the same as the "entry" method above.
And, for example, I'd like to spy on Funct1 getting called and Funct2 returns true.
import Funct1 from "../../../src/components/Funct1";
describe("Test the Thing", () => {
it("New Test", () => {
let props = {
active: true,
agentStatus: "online"
};
const spy = spyOn(Funct2, "method name"); <-- how doe this work if not an obj or class?
Funct1(props);
//If I try Funct2(props) instead, terminal output is "Funct2 is not defined"
expect(spy).toHaveBeenCalledWith(props);
});
});
I am not expert in jest, but my recommendation to think about:
1) When the function is exported as default I use something like:
import Funct1 from "../../../src/components/Funct1";
...
jest.mock("../../../src/components/Funct1");
...
expect(Funct1).toHaveBeenCalledWith(params);
2) When the module (utils.js) has multiple exports as
export const f1 = () => {};
...
export const f8 = () => {};
You can try
import * as Utils from "../../../src/components/utils"
const f8Spy = jest.spyOn(Utils, 'f8');
...
expect(f8Spy).toHaveBeenCalledWith(params);
Similar discussion here
Wrap your function with jest.fn. Like this:
const simpleFn = (arg) => arg;
const simpleFnSpy = jest.fn(simpleFn);
simpleFnSpy(1);
expect(simpleFnSpy).toBeCalledWith(1); // Passes test
I think Jest requires mocks to be inside an Object or Class, but if they aren't like that in your code, you can still put them there in the test:
jest.mock('../pathToUtils', () => ({
func2: jest.fun().mockImplementation((props) => "ok") //return what you want
otherfuncs: ...etc
}));
const mockUtils = require('../pathToUtils')
const func1 = require('./pathToFunc1')
Then in the test:
func1() // call the main function
expect(mockUtils.func2).toHaveBeenCalled
expect(mockUtils.func2).toHaveBeenCalledTimes(1)
expect(mockUtils.func2).toHaveBeenCalledWith(arg1, arg2, etc)
expect(mockUtils.func2).toHaveBeenLastCalledWith(arg1, arg2, etc)
You'll have already done unit testing of the Util function in another file, so you won't actually be running the function again here, this is just mocking for the purpose of the integration testing.
I believe that is not possible to test Funct1 calling Funct2 without modifying the existing code. If the latter is an option, here are two options to introduce dependency injection:
Create and export a factory function:
const Funct2 = props => {
// do something
return true;
};
const Funct1 = CreateFunct1(Funct2);
export function CreateFunct1(Funct2) {
return props => {
if (props) {
Funct2(props);
}
// or return something
};
}
export default Funct1;
// and here is the test:
describe('Test the Thing', () => {
it('New Test', () => {
// Arrange
const funct2Spy = jasmine.createSpy('Funct2');
const funct1 = CreateFunct1(funct2Spy);
const props = "some data";
// Act
funct1(props);
// Assert
expect(funct2Spy).toHaveBeenCalledWith(props);
});
});
Export Funct2 too. Here is a thread on this topic. Maybe its example would have to be tweaked a little bit for your scenario because of the export syntax.

Categories