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

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.

Related

react-electron ipcRenderer in useEffect does not rerender

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

mock a library and run unit test

I am trying to mock a library using jest.
Function "requestAuthorization" is just a swift function. The library 'my-health-library' uses Native Modules to access the "requestAuthorization" method.
Test file:
import MyHealthLibrary, { type } from 'my-health-library';
jest.mock('my-health-library', () => {
return {
MyHealthLibrary: {
__esModule: true,
default: {
requestAuthorization: jest.fn(() => Promise.resolve('the response')),
type: type
}
};
}
});
it('Testing to see if Library works', () => {
let read = [type.heartRate]
let write = [type.heartRate]
expect(MyHealthLibrary.requestAuthorization(read, write)).toBe(true)
})
This test keeps failing with "The module factory of jest.mock() is not allowed to reference any out-of-scope variables.".
I could mock my function "requestAuthorization" but how do i mock the type???
The "type" is just an enum
export enum type {
bodyMassIndex = 'HKQuantityTypeIdentifierBodyMassIndex',
heartRate = 'HKQuantityTypeIdentifierHeartRate',
bodyTemperature = 'HKQuantityTypeIdentifierBodyTemperature',
bloodPressureSystolic = 'HKQuantityTypeIdentifierBloodPressureSystolic',
bloodPressureDiastolic = 'HKQuantityTypeIdentifierBloodPressureDiastolic',
bloodGlucose = 'HKQuantityTypeIdentifierBloodGlucose'
}
According to the doc you can jest.requireActual('my-health-library'):
jest.mock('my-health-library', () => {
const { type } = jest.requireActual('my-health-library');
return {
MyHealthLibrary: {
__esModule: true,
default: {
requestAuthorization: jest.fn(() => Promise.resolve('the response')),
type
}
};
}
});
EDIT: however, given the way you import the module (import MHL, { type } from 'm-h-l') I think the export should be:
return {
__esModule: true,
default: {
// the default export, i.e. the MyHealthLibrary object of your import statement
requestAuthorization: jest.fn(() => Promise.resolve('the response'))
},
type // out of the default export, to import it as desconstructed { type }
};

Convincing TS that an object coming through a proxy is never null

I have the following fn that creates a proxy:
const getStorageProxy = (store?: Storage) => {
const proxyObj: { store?: DataStorage } = {};
return new Proxy(proxyObj, {
get(obj, prop) {
if (!obj.store) {
obj.store = new DataStorage(store);
}
return obj.store[prop];
},
});
};
And then I export several instances of this proxy:
const storageA = getStorageProxy(storeA);
const storageB = getStorageProxy(storeB);
export { storageA, storageB };
The reason for this is that I don't want to actually instantiate the DataStorage class unless it's used, and for convenience I want to be able to type:
import { storageA } from 'storage';
This all works. The problem is that according to TS both of my exported storage instances are of type DataStorage | void because of:
const proxyObj: { store?: DataStorage } = {};
They're actually not because of the getter in the proxy, but how do I tell this to TS without actually assigning something to proxyObj.store on instantiation?

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