Created
March 30, 2021 14:20
-
-
Save merlosy/2e4a42555c7c99f27aac13b239cc4fe4 to your computer and use it in GitHub Desktop.
Jest testing utils
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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 | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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