diff options
Diffstat (limited to 'spec/frontend/ide/lib/mirror_spec.js')
-rw-r--r-- | spec/frontend/ide/lib/mirror_spec.js | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/spec/frontend/ide/lib/mirror_spec.js b/spec/frontend/ide/lib/mirror_spec.js new file mode 100644 index 00000000000..21bed5948f3 --- /dev/null +++ b/spec/frontend/ide/lib/mirror_spec.js @@ -0,0 +1,184 @@ +import createDiff from '~/ide/lib/create_diff'; +import { + canConnect, + createMirror, + SERVICE_NAME, + PROTOCOL, + MSG_CONNECTION_ERROR, + SERVICE_DELAY, +} from '~/ide/lib/mirror'; +import { getWebSocketUrl } from '~/lib/utils/url_utility'; + +jest.mock('~/ide/lib/create_diff', () => jest.fn()); + +const TEST_PATH = '/project/ide/proxy/path'; +const TEST_DIFF = { + patch: 'lorem ipsum', + toDelete: ['foo.md'], +}; +const TEST_ERROR = 'Something bad happened...'; +const TEST_SUCCESS_RESPONSE = { + data: JSON.stringify({ error: { code: 0 }, payload: { status_code: 200 } }), +}; +const TEST_ERROR_RESPONSE = { + data: JSON.stringify({ error: { code: 1, Message: TEST_ERROR }, payload: { status_code: 200 } }), +}; +const TEST_ERROR_PAYLOAD_RESPONSE = { + data: JSON.stringify({ + error: { code: 0 }, + payload: { status_code: 500, error_message: TEST_ERROR }, + }), +}; + +const buildUploadMessage = ({ toDelete, patch }) => + JSON.stringify({ + code: 'EVENT', + namespace: '/files', + event: 'PATCH', + payload: { diff: patch, delete_files: toDelete }, + }); + +describe('ide/lib/mirror', () => { + describe('canConnect', () => { + it('can connect if the session has the expected service', () => { + const result = canConnect({ services: ['test1', SERVICE_NAME, 'test2'] }); + + expect(result).toBe(true); + }); + + it('cannot connect if the session does not have the expected service', () => { + const result = canConnect({ services: ['test1', 'test2'] }); + + expect(result).toBe(false); + }); + }); + + describe('createMirror', () => { + const origWebSocket = global.WebSocket; + let mirror; + let mockWebSocket; + + beforeEach(() => { + mockWebSocket = { + close: jest.fn(), + send: jest.fn(), + }; + global.WebSocket = jest.fn().mockImplementation(() => mockWebSocket); + mirror = createMirror(); + }); + + afterEach(() => { + global.WebSocket = origWebSocket; + }); + + const waitForConnection = (delay = SERVICE_DELAY) => { + const wait = new Promise(resolve => { + setTimeout(resolve, 10); + }); + + jest.advanceTimersByTime(delay); + + return wait; + }; + const connectPass = () => waitForConnection().then(() => mockWebSocket.onopen()); + const connectFail = () => waitForConnection().then(() => mockWebSocket.onerror()); + const sendResponse = msg => { + mockWebSocket.onmessage(msg); + }; + + describe('connect', () => { + let connection; + + beforeEach(() => { + connection = mirror.connect(TEST_PATH); + }); + + it('waits before creating web socket', () => { + // ignore error when test suite terminates + connection.catch(() => {}); + + return waitForConnection(SERVICE_DELAY - 10).then(() => { + expect(global.WebSocket).not.toHaveBeenCalled(); + }); + }); + + it('is canceled when disconnected before finished waiting', () => { + mirror.disconnect(); + + return waitForConnection(SERVICE_DELAY).then(() => { + expect(global.WebSocket).not.toHaveBeenCalled(); + }); + }); + + describe('when connection is successful', () => { + beforeEach(connectPass); + + it('connects to service', () => { + const expectedPath = `${getWebSocketUrl(TEST_PATH)}?service=${SERVICE_NAME}`; + + return connection.then(() => { + expect(global.WebSocket).toHaveBeenCalledWith(expectedPath, [PROTOCOL]); + }); + }); + + it('disconnects when connected again', () => { + const result = connection + .then(() => { + // https://gitlab.com/gitlab-org/gitlab/issues/33024 + // eslint-disable-next-line promise/no-nesting + mirror.connect(TEST_PATH).catch(() => {}); + }) + .then(() => { + expect(mockWebSocket.close).toHaveBeenCalled(); + }); + + return result; + }); + }); + + describe('when connection fails', () => { + beforeEach(connectFail); + + it('rejects with error', () => { + return expect(connection).rejects.toEqual(new Error(MSG_CONNECTION_ERROR)); + }); + }); + }); + + describe('upload', () => { + let state; + + beforeEach(() => { + state = { changedFiles: [] }; + createDiff.mockReturnValue(TEST_DIFF); + + const connection = mirror.connect(TEST_PATH); + + return connectPass().then(() => connection); + }); + + it('creates a diff from the given state', () => { + const result = mirror.upload(state); + + sendResponse(TEST_SUCCESS_RESPONSE); + + return result.then(() => { + expect(createDiff).toHaveBeenCalledWith(state); + expect(mockWebSocket.send).toHaveBeenCalledWith(buildUploadMessage(TEST_DIFF)); + }); + }); + + it.each` + response | description + ${TEST_ERROR_RESPONSE} | ${'error in error'} + ${TEST_ERROR_PAYLOAD_RESPONSE} | ${'error in payload'} + `('rejects if response has $description', ({ response }) => { + const result = mirror.upload(state); + + sendResponse(response); + + return expect(result).rejects.toEqual({ message: TEST_ERROR }); + }); + }); + }); +}); |