Skip to content

Commit

Permalink
Refactor MessageUtils unit tests to share code
Browse files Browse the repository at this point in the history
- Now it doesn't duplicate the same code
  • Loading branch information
mofojed committed Jun 21, 2023
1 parent 1cb0f3d commit 7d41761
Showing 1 changed file with 94 additions and 132 deletions.
226 changes: 94 additions & 132 deletions packages/jsapi-utils/src/MessageUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,147 +12,109 @@ it('Throws an exception if called on a window without parent', async () => {
);
});

describe('requestParentResponse with opener', () => {
let addListenerSpy: jest.SpyInstance;
let removeListenerSpy: jest.SpyInstance;
let listenerCallback;
let messageId;
const mockPostMessage = jest.fn((data: Message<unknown>) => {
messageId = data.id;
});
/**
* Set up the mock for window.parent or window.opener, and return a cleanup function.
* @param type Whether to mock window.parent or window.opener
* @param mockPostMessage The mock postMessage function to use
* @returns Cleanup function
*/
function setupWindowParentMock(
type: string,
mockPostMessage: jest.Mock
): () => void {
if (type !== 'parent' && type !== 'opener') {
throw new Error(`Invalid type ${type}`);
}
if (type === 'parent') {
const windowParentSpy = jest.spyOn(window, 'parent', 'get').mockReturnValue(
TestUtils.createMockProxy<Window>({
postMessage: mockPostMessage,
})
);
return () => {
windowParentSpy.mockRestore();
};
}

const originalWindowOpener = window.opener;
beforeEach(() => {
addListenerSpy = jest
.spyOn(window, 'addEventListener')
.mockImplementation((event, cb) => {
listenerCallback = cb;
});
removeListenerSpy = jest.spyOn(window, 'removeEventListener');
window.opener = { postMessage: mockPostMessage };
});
afterEach(() => {
addListenerSpy.mockRestore();
removeListenerSpy.mockRestore();
mockPostMessage.mockClear();
window.opener = { postMessage: mockPostMessage };
return () => {
window.opener = originalWindowOpener;
messageId = undefined;
});
};
}

it('Posts message to parent and subscribes to response', async () => {
requestParentResponse('request');
expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining(makeMessage('request', messageId)),
'*'
);
expect(addListenerSpy).toHaveBeenCalledWith(
'message',
expect.any(Function)
);
});

it('Resolves with the payload from the parent window response and unsubscribes', async () => {
const PAYLOAD = 'PAYLOAD';
const promise = requestParentResponse('request');
listenerCallback({
data: makeResponse(messageId, PAYLOAD),
describe.each([['parent'], ['opener']])(
`requestParentResponse with %s`,
type => {
let parentCleanup: () => void;
let addListenerSpy: jest.SpyInstance;
let removeListenerSpy: jest.SpyInstance;
let listenerCallback;
let messageId;
const mockPostMessage = jest.fn((data: Message<unknown>) => {
messageId = data.id;
});
const result = await promise;
expect(result).toBe(PAYLOAD);
expect(removeListenerSpy).toHaveBeenCalledWith('message', listenerCallback);
});

it('Ignores unrelated response, rejects on timeout', async () => {
jest.useFakeTimers();
const promise = requestParentResponse('request');
listenerCallback({
data: makeMessage('wrong-id'),
beforeEach(() => {
addListenerSpy = jest
.spyOn(window, 'addEventListener')
.mockImplementation((event, cb) => {
listenerCallback = cb;
});
removeListenerSpy = jest.spyOn(window, 'removeEventListener');
parentCleanup = setupWindowParentMock(type, mockPostMessage);
});
afterEach(() => {
addListenerSpy.mockRestore();
removeListenerSpy.mockRestore();
mockPostMessage.mockClear();
parentCleanup();
messageId = undefined;
});
jest.runOnlyPendingTimers();
await expect(promise).rejects.toThrow('Request timed out');
jest.useRealTimers();
});

it('Times out if no response', async () => {
jest.useFakeTimers();
const promise = requestParentResponse('request');
jest.runOnlyPendingTimers();
expect(removeListenerSpy).toHaveBeenCalled();
await expect(promise).rejects.toThrow('Request timed out');
jest.useRealTimers();
});
});
it('Posts message to parent and subscribes to response', async () => {
requestParentResponse('request');
expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining(makeMessage('request', messageId)),
'*'
);
expect(addListenerSpy).toHaveBeenCalledWith(
'message',
expect.any(Function)
);
});

describe('requestParentResponse with parent', () => {
let addListenerSpy: jest.SpyInstance;
let removeListenerSpy: jest.SpyInstance;
let windowParentSpy: jest.SpyInstance;
let listenerCallback;
let messageId;
const mockPostMessage = jest.fn((data: Message<unknown>) => {
messageId = data.id;
});
beforeEach(() => {
addListenerSpy = jest
.spyOn(window, 'addEventListener')
.mockImplementation((event, cb) => {
listenerCallback = cb;
it('Resolves with the payload from the parent window response and unsubscribes', async () => {
const PAYLOAD = 'PAYLOAD';
const promise = requestParentResponse('request');
listenerCallback({
data: makeResponse(messageId, PAYLOAD),
});
removeListenerSpy = jest.spyOn(window, 'removeEventListener');
windowParentSpy = jest.spyOn(window, 'parent', 'get').mockReturnValue(
TestUtils.createMockProxy<Window>({
postMessage: mockPostMessage,
})
);
});

afterEach(() => {
addListenerSpy.mockRestore();
removeListenerSpy.mockRestore();
windowParentSpy.mockRestore();
mockPostMessage.mockClear();
messageId = undefined;
});

it('Posts message to parent and subscribes to response', async () => {
requestParentResponse('request');
expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining(makeMessage('request', messageId)),
'*'
);
expect(addListenerSpy).toHaveBeenCalledWith(
'message',
expect.any(Function)
);
});

it('Resolves with the payload from the parent window response and unsubscribes', async () => {
const PAYLOAD = 'PAYLOAD';
const promise = requestParentResponse('request');
listenerCallback({
data: makeResponse(messageId, PAYLOAD),
const result = await promise;
expect(result).toBe(PAYLOAD);
expect(removeListenerSpy).toHaveBeenCalledWith(
'message',
listenerCallback
);
});
const result = await promise;
expect(result).toBe(PAYLOAD);
expect(removeListenerSpy).toHaveBeenCalledWith('message', listenerCallback);
});

it('Ignores unrelated response, rejects on timeout', async () => {
jest.useFakeTimers();
const promise = requestParentResponse('request');
listenerCallback({
data: makeMessage('wrong-id'),
it('Ignores unrelated response, rejects on timeout', async () => {
jest.useFakeTimers();
const promise = requestParentResponse('request');
listenerCallback({
data: makeMessage('wrong-id'),
});
jest.runOnlyPendingTimers();
await expect(promise).rejects.toThrow('Request timed out');
jest.useRealTimers();
});
jest.runOnlyPendingTimers();
await expect(promise).rejects.toThrow('Request timed out');
jest.useRealTimers();
});

it('Times out if no response', async () => {
jest.useFakeTimers();
const promise = requestParentResponse('request');
jest.runOnlyPendingTimers();
expect(removeListenerSpy).toHaveBeenCalled();
await expect(promise).rejects.toThrow('Request timed out');
jest.useRealTimers();
});
});
it('Times out if no response', async () => {
jest.useFakeTimers();
const promise = requestParentResponse('request');
jest.runOnlyPendingTimers();
expect(removeListenerSpy).toHaveBeenCalled();
await expect(promise).rejects.toThrow('Request timed out');
jest.useRealTimers();
});
}
);

0 comments on commit 7d41761

Please sign in to comment.