Jest Making Test Await Promise Resolve - javascript

I have the following code I am working with this API https://developer.mozilla.org/en-US/docs/Web/API/Performance/measureUserAgentSpecificMemory. The class is stripped right back
export class Memory {
private stopped = false
private isUserAgentSpecificMemorySupported = true
public memoryData: any = []
constructor() {}
public startMonitoring(): () => void {
if (this.isUserAgentSpecificMemorySupported) {
this.scheduleMeasurement()
}
return () => {
this.stopped = true
}
}
private async performMeasurement(): Promise<void> {
const memory = await (window.performance as any).measureUserAgentSpecificMemory()
const type = memory.breakdown.filter((e: any) => e.types.includes('JavaScript'))
this.memoryData.push(type[0].bytes)
}
}
Jest file.
import {Memory} from './memory'
type UserAgentSpecificMemoryBreakdown = {
bytes: number
types: Array<string>
}
type UserAgentSpecificMemory = {
bytes: number
breakdown: Array<UserAgentSpecificMemoryBreakdown>
}
type MockWindow = {
crossOriginIsolated?: boolean
performance: {
measureUserAgentSpecificMemory?: () => Promise<UserAgentSpecificMemory>
}
}
const data = {
bytes: 1500,
breakdown: [
{
bytes: 1000000,
types: ['JavaScript'],
},
{
bytes: 0,
types: ['DOM'],
},
],
}
describe('Test Memory Class', () => {
let mockWindow: MockWindow
let windowSpy: jest.SpyInstance
beforeEach(() => {
windowSpy = jest.spyOn(window, 'window', 'get')
mockWindow = {
...window,
performance: {
measureUserAgentSpecificMemory: jest.fn(() => Promise.resolve(data)),
},
}
windowSpy.mockImplementation(() => mockWindow)
})
afterEach(() => {
windowSpy.mockRestore()
})
it('should measure User Agent Specific Memory', async () => {
let memory = new Memory()
memory.startMonitoring()
expect(memory.memoryData).toEqual([1000000])
})
})
I am not sure how to make the test file await for the value in the test?
Any help would be great.

window is an object and if it doesn’t contain window function, you can not spy on it.
For your production code, just mock measureUserAgentSpecificMemory function are enough:
import { Memory } from './memory'
describe('Memory', () => {
const data = {
bytes: 1500,
breakdown: [
{
bytes: 1000000,
types: ['JavaScript'],
},
{
bytes: 0,
types: ['DOM'],
},
],
};
let memory: Memory;
let measureUserAgentSpecificMemory: jest.Mock;
beforeEach(() => {
measureUserAgentSpecificMemory = jest.fn().mockResolvedValue(data);
(window as any).performance = {
measureUserAgentSpecificMemory,
};
memory = new Memory();
});
it('should measure User Agent Specific Memory', async () => {
memory.startMonitoring();
expect(memory.memoryData).toEqual([1000000]);
expect(measureUserAgentSpecificMemory).toHaveBeenCalled();
});
});

Related

Mock Class Constructor being extended in Jest

I am receiving An argument for 'options' was not provided. when I try to mock a class with a constructor.
Color.test.ts
...
describe('TEST getColorById', () => {
describe('GIVEN color id', () => {
// mockColor will return orange as color
const mockRoot = mockColor({
id: 1,
});
const mockReturn = { id: 1, color: 'orange' };
class MockColor extends Color {
// overrides the response of post for await colorClass.getColorById with the expected mockReturn
public async post(): Promise<any> {
return { response: [mockReturn] };
}
}
// ? how can I simulate instantiating class with constructor
const colorClass = new MockColor();
describe('WHEN calling getColorById', () => {
test('THEN it loads color info', async () => {
const received = await colorClass.getColorById({ id: 1 });
expect(received).toEqual([mockRoot]);
});
});
});
});
Color.ts
...
export class Color {
private token: string;
private baseUrl: string;
constructor(options: { token; baseUrl? }) {
super(options);
this.token = options.token;
this.baseUrl = options.baseUrl;
}
async getColorById(object: { id: number }): Promise<ColorRoot> {
...
}
}

How can we make jest wait for an event before making an assert?

I'm creating a façade for the nats streaming lib as follows:
import nats, { Message, Stan, Subscription, SubscriptionOptions } from 'node-nats-streaming'
class NatsHelper {
private client: Stan | null = null
public connect(url: string, clusterID: string, clientID: string, listener: (...args: any[]) => void, verboseConnection: boolean = true): void {
const clientIDString = `${clientID}-${randomBytes(4).toString('hex')}`
if (verboseConnection) {
console.log(`Connecting to NATS cluster '${clusterID}' with clientID '${clientIDString}' on url '${url}'`)
}
const connectionAttempt = nats.connect(
clusterID,
clientIDString,
{
url
}
)
const setupConnection = (...args: any[]): void => {
this.client = connectionAttempt
this.client.on('close', (): void => {
if (verboseConnection) {
console.log(`Connection with NATS cluster '${clusterID}' with clientID '${clientIDString}' on url '${url}' was closed`)
}
this.client = null
process.exit()
})
process.on('SIGINT', () => this.client?.close())
process.on('SIGTERM', () => this.client?.close())
if (verboseConnection) {
console.log(`Connected to NATS cluster '${clusterID}' with clientID '${clientIDString}' on url '${url}' successfuly`)
}
listener(...args)
}
connectionAttempt.on('connect', setupConnection)
}
}
It happens though that I'm not able to test if the provided listener function is called, because it relies on the Stan 'connect' event to happen and jest finishes the test before it happens.
How can I make jest wait for this event to happen, and then executes the expect function?
You have overcomplicated this. It's perfectly possible to write the test for the original code without modifying it by mocking out the library using jest.mock(), and injecting mock implementations for your on method. Like this:
import nats from "node-nats-streaming";
import { mock } from "jest-mock-extended";
import { NatsHelper } from "./nats";
jest.mock("node-nats-streaming");
describe("NatsHelper", () => {
it("calls listener on connectEvent", () => {
const client = mock<nats.Stan>();
client.on.mockImplementation((name, callback) => {
if (name !== "close") {
callback();
}
return client;
});
jest.mocked(nats).connect.mockReturnValue(client);
const connector = new NatsHelper();
const listener = jest.fn();
connector.connect("foo", "foo", "foo", listener);
expect(listener).toHaveBeenCalled();
});
});
[EDIT] Found the solution I was looking
It happens that we can "convert" an event into a Promise, as follows:
import { randomBytes } from 'crypto'
import nats from 'node-nats-streaming'
export class NullClientError extends Error {
constructor() {
super('Nats client is not connected')
this.name = 'NullClientError'
}
}
export class NatsHelper {
private verboseConnectionString: string
private client: nats.Stan
private connector: nats.Stan
constructor(
private readonly verboseConnection: boolean = true
) { }
public async connect(url: string, clusterID: string, clientID: string, callback: (...args: any[]) => void): Promise<void> {
const clientIDString = `${clientID}-${randomBytes(4).toString('hex')}`
this.verboseConnectionString = `NATS cluster '${clusterID}' with clientID '${clientIDString}' on url '${url}'`
if (this.verboseConnection) {
console.log(`Connecting to ${this.verboseConnectionString}`)
}
this.connector = nats.connect(
clusterID,
clientIDString,
{
url
}
)
this.connector.on('connect', (...args: any[]) => {
const realCallback = this.setupListener(callback)
realCallback(...args)
})
return await new Promise(
resolve => {
if (this.connector) {
this.connector.on('connect', () => {
resolve()
})
}
}
)
}
private setupListener(listener: (...args: any[]) => void): (...args: any[]) => void {
const setupConnection = (...args: any[]): void => {
if (this.connector === undefined) {
throw new NullClientError()
}
this.client = this.connector
if (this.client === undefined) {
throw new NullClientError()
}
this.client.on('close', (): void => {
if (this.verboseConnection) {
console.log(`Connection with ${this.verboseConnectionString} was closed`)
}
process.exit()
})
process.on('SIGINT', () => this.client?.close())
process.on('SIGTERM', () => this.client?.close())
if (this.verboseConnection) {
console.log(`Connected to ${this.verboseConnectionString} successfuly`)
}
listener(...args)
}
return setupConnection
}
}
And then test it with asynchronous tests:
describe('NatsHelper', () => {
test('ensure NatsHelper calls connect with correct values', async () => {
const connectSpy = jest.spyOn(nats, 'connect')
const sut = new NatsHelper(false)
const { url, clusterID, clientID, listener } = makeConnectionParams()
await sut.connect(url, clusterID, clientID, listener)
const clientIDString = connectSpy.mock.calls[0][1]
expect(clientIDString).toContain(clientID)
expect(connectSpy).toHaveBeenCalledWith(clusterID, clientIDString, { url })
})
test('ensure NatsHelper forwards the callback when connected', async () => {
const connectionParms = makeConnectionParams()
const { url, clusterID, clientID } = connectionParms
const listenerSpy = jest.spyOn(connectionParms, 'listener')
const sut = new NatsHelper(false)
await sut.connect(url, clusterID, clientID, connectionParms.listener)
expect(listenerSpy).toHaveBeenCalledTimes(1)
})
}

How to unit test(jest) the content of on ready event for an electron app?

I have this piece of code to be tested:
electronApp.on('ready', async () => {
const filter = {
urls: ['*://*.company.com/*'],
};
try {
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders['Origin'] = '*';
callback({ requestHeaders: details.requestHeaders });
});
} catch (error) {
log.error(error.message);
}
start(electronApp);
});
I have created unit test for the 'start main process' like this:
import { App as MockApp } from '../../__mocks__/electron'; // This mocks partially electron module
...
it('should start the main process', () => {
const mockApp = app as unknown as jest.Mocked<App>;
mockApp.emit('ready');
expect(mockApp.requestSingleInstanceLock).toHaveBeenCalledTimes(1);
});
It works. but I don't find a way to test that try/catch and the session -> onBeforeSendHeaders.
Any suggestion? Thanks a lot.
Finally it worked:
import { App as MockApp } from '../../__mocks__/electron';
...
it('should start the main process', (done) => {
const mockApp = app as unknown as jest.Mocked<App>;
const mockCb = jest.fn();
session.defaultSession.webRequest.onBeforeSendHeaders = (filter: string[], cb: (details, reqCb) => void): void => {
cb({ url: '*://*.company.com/*', requestHeaders: {} }, mockCb);
expect(mockCb).toHaveBeenCalledTimes(1);
expect(mockCb).toHaveBeenCalledWith({ requestHeaders: { Origin: '*' } });
done();
};
mockApp.emit('ready');
});
I also mocked some functions of electron library mocks__/electron.ts,
export const session = {
defaultSession: {
webRequest: {
onBeforeSendHeaders: (filter: string[], cb: (details, reqCb) => void): void => {
cb({ url: 'http"//some/site.com' }, jest.fn());
},
},
},
};
Just in case someone has similar issue.

jest testing of method using native module

I am trying to create an unit test for my function.
Below the method "requestAuthorization" is a method in swift and i am invoking it through RN's Native Modules.
I get this error "Cannot read property 'requestAuthorization' of undefined"
If the method runs successfully it resolves back a string "Authorized Successfully!"
How can i test this????
index.ts (my method "requestAuthorization" is defined here)
const requestAuthorization = (
read: [],
write: [] = []
): Promise<boolean> => {
const readAuth = read.reduce((obj, cur) => {
return { ...obj, [cur]: true };
}, {});
const writeAuth = write.reduce((obj, cur) => {
return { ...obj, [cur]: true };
}, {});
return NativeModules.MyHealthLibrary.requestAuthorization(writeAuth, readAuth);
};
const HealthKit = {
requestAuthorization
}
export default HealthKit;
Now in my test.js
import { type } from '../authTypes';
import { NativeModules } from 'react-native';
jest.mock('react-native', () => {
return {
NativeModules: {
MyHealthLibrary: {
requestAuthorization: jest.fn(() => Promise.resolve())
}
}
}
})
const utils = jest.createMockFromModule('../index.ts').default;
utils.requestAuthorization = jest.fn((read, write) => {
return NativeModules.MyHealthLibrary.requestAuthorization(read, write)
});
test('healthkit permissions', () => {
expect(utils.requestAuthorization.mock).toBeTruthy();
let read = [type.heartRate]
let write = [type.heartRate]
expect(read).toEqual(["HKQuantityTypeIdentifierHeartRate"])
expect(write).toEqual(["HKQuantityTypeIdentifierHeartRate"])
await utils.requestAuthorization(read, write).then(result => {
console.log("result:",result) //ALWAYS UNDEFINED
})
});
UPDATE:
In my swift method "requestAuthorization", it resolves a string "Authorized Successfully!".
jest.mock('../node_modules/react-native/Libraries/BatchedBridge/NativeModules', () => ({
MyHealthLibrary: {
requestAuthorization: jest.fn(() => Promise.resolve())
},
}));
test('healthkit permissions', async() => {
let read = [type.heartRate]
let write = [type.heartRate]
expect(read).toEqual(["HKQuantityTypeIdentifierHeartRate"])
expect(write).toEqual(["HKQuantityTypeIdentifierHeartRate"])
let result = await NativeModules.MyHealthLibrary.requestAuthorization(read, write)
console.log("result:",result)
//NOT PRINTING "Authorized Successfully!" INSTEAD PRINTING "undefined"
});

Testing a ResizeObserver inside an Effect Fn

I'm struggling a bit with unit testing the following:
const { element, state } = props;
const { theme } = state;
const { computedHeight, computedWidth } = theme;
const elementToSize = document.querySelector(element);
const observer = new ResizeObserver(element => {
const { contentRect } = element[0];
// set the viewport size in the state
dispatch(setViewportSize(state, contentRect.width, contentRect.height));
// perform the task function
wrapper(600, computedWidth, computedHeight, contentRect.width, contentRect.height).
then(() => {
elementToSize.style.height = `${contentRect.height <= computedHeight ? contentRect.height : computedHeight}px`;
elementToSize.style.width = `${contentRect.width <= computedWidth ? contentRect.width : computedWidth}px`;
}).catch( e => new Error(e));
});
observer.observe(document.documentElement);
const wrapper = async (ms, ...callbackArgs) => {
try {
await asyncInterval(checkSize, ms, ...callbackArgs);
} catch {
new Error('async Interval has failed...');
}
return await asyncInterval(checkSize, ms, ...callbackArgs);
};
};
export const SizeableEffect = (element, state) => [effectFn, { element, state } ];
In the code above, I have difficulties with unit testing the code inside the ResizeObserver.
I have the following in my test.
import ResizeObserver from 'resize-observer-polyfill';
import { SizeableEffect } from 'effects';
import { domMock } from '../mocks/dom';
jest.mock('resize-observer-polyfill');
describe('SizeableEffect', () => {
let stateMock, dispatch, effect, effectFn;
beforeEach(() => {
stateMock = {
viewportWidth: null,
viewportHeight: null,
theme: {
chatFrameSize: {
width: 300,
height: 500
}
}
};
dispatch = jest.fn();
effect = SizeableEffect('elementMock', stateMock);
effectFn = effect[0];
Object.defineProperties(window, {
document: {
value: domMock()
}
});
});
it('the effect should be called with the element', () => {
expect(effect[1]).toEqual(
expect.objectContaining({
element: 'elementMock',
state: stateMock
})
);
});
it('the effect function should perform the operations', () => {
effectFn(dispatch, { element: 'some', state: stateMock });
expect(document.querySelector).toHaveBeenCalledWith('some');
expect(dispatch).not.toHaveBeenCalled();
expect(ResizeObserver).toHaveBeenCalled();
});
});
And as you can see from the screenshot, I have uncovered lines where the ResizeObserver is doing the login by measuring the width' and height's against the viewport.
How can I cover those lines in the best way?

Categories

Resources