Skip to content

Instantly share code, notes, and snippets.

@merlosy
Created March 30, 2021 14:20
Show Gist options
  • Save merlosy/2e4a42555c7c99f27aac13b239cc4fe4 to your computer and use it in GitHub Desktop.
Save merlosy/2e4a42555c7c99f27aac13b239cc4fe4 to your computer and use it in GitHub Desktop.
Jest testing utils
/** Mocked location object. Includes the original to rollback to the native one */
export interface MockedLocation {
/** The original location definition */
original: Location;
/** Restore the original location object */
clear(): void;
}
/**
* Wrapper to easily mock `window.location`
* @param location The partial location object
* @see https://www.benmvp.com/blog/mocking-window-location-methods-jest-jsdom/
* @example
* const locationMock = mockLocation({
* href: 'http://dummy.com/my-url/',
* assign: jest.fn()
* });
* // Assert
* expect(window.location.href).toBe('http://dummy.com/my-url/');
* // clean up
* locationMock.clear();
*/
export function mockLocation(location: unknown): MockedLocation {
// keep a copy of the window object to restore
// it at the end of the tests
const original = window.location;
// delete the existing `Location` object from `jsdom`
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete window.location;
// create a new `window.location` object that's *almost* like the real one
Object.defineProperty(window, 'location', {
writable: true,
value: location
});
return {
original,
clear: () => window.location = original
};
}
/**
* Spy on the given method for localStorage object.
* @param method the method name to spy on
* @returns The spy object
*/
export function spyOnLocalStorage(method: jest.FunctionPropertyNames<Required<Storage>>): jest.SpyInstance {
return jest.spyOn(Object.getPrototypeOf(localStorage) as Storage, method);
}
/**
* Spy on the given method for sessionStorage object.
* @param method the method name to spy on
* @returns The spy object
*/
export function spyOnSessionStorage(method: jest.FunctionPropertyNames<Required<Storage>>): jest.SpyInstance {
return jest.spyOn(Object.getPrototypeOf(sessionStorage) as Storage, method);
}
/* eslint-disable @typescript-eslint/ban-types */
interface Mutation {
type: string;
payload?: any;
}
interface Dispatch {
type: string;
payload?: any;
}
interface Store {
state?: {};
getter?: {};
rootState?: {};
rootGetters?: {};
}
/**
* A utility function to unit test Vuex actions, retrieved (and patched) from:
* @see https://github.com/onedaycat/vue-test-actions
* @param action
* @param expectedMutations
* @param expectedDispatchs
* @param actionPayload
* @param store
* @param expectedFailure if true, the action call is expected to raise an error
*/
export async function testAction(
action: any,
expectedMutations: Mutation[] = [],
expectedDispatchs: Dispatch[] = [],
actionPayload?: any,
store?: Store,
expectedFailure?: boolean
): Promise<any> {
let countMutation = 0;
let countDispatch = 0;
// mock commit
const commit = async (type: string, payload?: any): Promise<void> => {
try {
countMutation += 1;
if (expectedMutations.length > 0) {
const mutation = expectedMutations[countMutation - 1];
if (!mutation) {
fail(new Error('Received more mutations than expected'));
}
await expect(type).toBe(mutation.type);
if (payload) {
await expect(payload).toEqual(mutation.payload);
}
}
} catch (e) {
fail(new Error(`[COMMIT ${type} FAIL] \n${e}`));
}
};
// mock dispatch
const dispatch = (type: string, payload?: any): void => {
try {
countDispatch += 1;
if (expectedDispatchs.length > 0) {
const dispatcher = expectedDispatchs[countDispatch - 1];
if (!dispatcher) {
fail(new Error('Received more dispatches than expected'));
}
expect(dispatcher.type).toEqual(type);
if (payload) {
expect(payload).toEqual(dispatcher.payload);
}
}
} catch (e) {
fail(new Error(`[DISPATCH ${type} FAIL] \n${e}`));
}
};
// call the action with mocked store
let result: any = Promise.resolve();
try {
result = await action({ commit, dispatch, ...store }, actionPayload);
if (expectedFailure) {
fail(new Error(`[ACTION ${action.name} was expected to fail]`));
}
} catch (e) {
if (!expectedFailure) {
fail(new Error(`[ACTION ${action.name} FAIL] \n${e}`));
}
}
if (expectedDispatchs.length !== countDispatch) {
fail(new Error(`[DISPATCH COUNT] Expected ${expectedDispatchs.length}, received ${countDispatch}`));
}
if (expectedMutations.length !== countMutation) {
fail(new Error(`[COMMIT COUNT] Expected ${expectedMutations.length}, received ${countMutation}`));
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment