import { start } from '~/code_review/signals'; import diffsEventHub from '~/diffs/event_hub'; import { EVT_MR_PREPARED } from '~/diffs/constants'; import { getDerivedMergeRequestInformation } from '~/diffs/utils/merge_request'; jest.mock('~/diffs/utils/merge_request'); describe('~/code_review', () => { const io = diffsEventHub; beforeAll(() => { getDerivedMergeRequestInformation.mockImplementation(() => ({ namespace: 'x', project: 'y', id: '1', })); }); describe('start', () => { it.each` description | argument ${'no event hub is provided'} | ${{}} ${'no parameters are provided'} | ${undefined} `('throws an error if $description', async ({ argument }) => { await expect(() => start(argument)).rejects.toThrow('signalBus is a required argument'); }); describe('observeMergeRequestFinishingPreparation', () => { const callArgs = {}; const apollo = {}; let querySpy; let apolloSubscribeSpy; let subscribeSpy; let nextSpy; let unsubscribeSpy; let observable; beforeEach(() => { querySpy = jest.fn(); apolloSubscribeSpy = jest.fn(); subscribeSpy = jest.fn(); unsubscribeSpy = jest.fn(); nextSpy = jest.fn(); observable = { next: nextSpy, subscribe: subscribeSpy.mockReturnValue({ unsubscribe: unsubscribeSpy, }), }; querySpy.mockResolvedValue({ data: { project: { mergeRequest: { id: 'gql:id:1', preparedAt: 'x' } } }, }); apolloSubscribeSpy.mockReturnValue(observable); apollo.query = querySpy; apollo.subscribe = apolloSubscribeSpy; callArgs.signalBus = io; callArgs.apolloClient = apollo; }); it('does not query at all if the page does not seem like a merge request', async () => { getDerivedMergeRequestInformation.mockImplementationOnce(() => ({})); await start(callArgs); expect(querySpy).not.toHaveBeenCalled(); expect(apolloSubscribeSpy).not.toHaveBeenCalled(); }); describe('on a merge request page', () => { it('requests the preparedAt (and id) for the current merge request', async () => { await start(callArgs); expect(querySpy).toHaveBeenCalledWith( expect.objectContaining({ variables: { projectPath: 'x/y', iid: '1', }, }), ); }); it('does not subscribe to any updates if the preparedAt value is already populated', async () => { await start(callArgs); expect(apolloSubscribeSpy).not.toHaveBeenCalled(); }); describe('when the project does not exist', () => { beforeEach(() => { querySpy.mockResolvedValue({ data: { project: null }, }); }); it('does not fail and quits silently', () => { expect(async () => { await start(callArgs); }).not.toThrow(); }); }); describe('if the merge request is still asynchronously preparing', () => { beforeEach(() => { querySpy.mockResolvedValue({ data: { project: { mergeRequest: { id: 'gql:id:1', preparedAt: null } } }, }); }); it('subscribes to updates', async () => { await start(callArgs); expect(apolloSubscribeSpy).toHaveBeenCalledWith( expect.objectContaining({ variables: { issuableId: 'gql:id:1' } }), ); expect(observable.subscribe).toHaveBeenCalled(); }); describe('when the MR has been updated', () => { let emitSpy; let behavior; beforeEach(() => { emitSpy = jest.spyOn(diffsEventHub, '$emit'); nextSpy.mockImplementation((data) => behavior?.(data)); subscribeSpy.mockImplementation((handler) => { behavior = handler; return { unsubscribe: unsubscribeSpy }; }); }); it('does nothing if the MR has not yet finished preparing', async () => { await start(callArgs); observable.next({ data: { mergeRequestMergeStatusUpdated: { preparedAt: null } } }); expect(unsubscribeSpy).not.toHaveBeenCalled(); expect(emitSpy).not.toHaveBeenCalled(); }); it('emits an event and unsubscribes when the MR is prepared', async () => { await start(callArgs); observable.next({ data: { mergeRequestMergeStatusUpdated: { preparedAt: 'x' } } }); expect(unsubscribeSpy).toHaveBeenCalled(); expect(emitSpy).toHaveBeenCalledWith(EVT_MR_PREPARED); }); }); }); }); }); }); });