Files
nebula.js/commands/serve/web/hooks/__tests__/useConnection.test.js
Tobias Åström 3f87dfc2b7 fix: fetch and apply csrf to WS call (#1679)
* fix: fetch and apply csrf to WS call

* fix: update tests

* fix: update more tests

* fix: update more tests
2025-02-24 08:53:34 +01:00

262 lines
9.4 KiB
JavaScript

import { renderHook, act } from '@testing-library/react';
import {
useConnection,
handleConnectionSuccess,
handleConnectionFailure,
handleSessionNotification,
} from '../useConnection';
import * as connectModule from '../../connect';
import { RouterWrapper } from '../../utils';
import getCsrfToken from '../../utils/getCsrfToken';
jest.mock('../../utils/getCsrfToken', () => jest.fn());
describe('useConnection Module', () => {
let connectMock;
let renderResult;
let info;
let glob;
let getDocList;
let getConfiguration;
let cachedConnectionsData;
let addCachedConnections;
beforeEach(() => {
info = {};
addCachedConnections = jest.fn();
cachedConnectionsData = { addCachedConnections };
getDocList = jest.fn();
getConfiguration = jest.fn().mockResolvedValue({ qFeatures: { qIsDesktop: false } });
glob = { getDocList, getConfiguration };
connectMock = jest.fn().mockResolvedValue(glob);
jest.spyOn(connectModule, 'connect').mockImplementation(connectMock);
getCsrfToken.mockResolvedValue('A-CSRF-TOKEN');
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
describe('useConnection()', () => {
test('should return correct expected values from hook', async () => {
await act(async () => {
renderResult = renderHook(() => useConnection({ info, cachedConnectionsData }), { wrapper: RouterWrapper });
});
expect(renderResult.result.current).toMatchObject({
glob: undefined,
treatAsDesktop: false,
error: undefined,
activeStep: 0,
setGlobal: expect.any(Function),
setTreatAsDesktop: expect.any(Function),
setError: expect.any(Function),
setActiveStep: expect.any(Function),
});
});
test('should not proceed in any flow and NOT cache the provided connection if already was in "/" path', async () => {
info = { engineUrl: 'someEngineUrl#01' };
await act(async () => {
renderResult = renderHook(() => useConnection({ info, cachedConnectionsData }), { wrapper: RouterWrapper });
});
expect(addCachedConnections).toHaveBeenCalledTimes(0);
});
test('should not proceed in any flow and NOT cache the provided connection if there was no info or engine url', async () => {
window.location.assign('/some-other-route');
await act(async () => {
renderResult = renderHook(() => useConnection({ info, cachedConnectionsData }), { wrapper: RouterWrapper });
});
expect(addCachedConnections).toHaveBeenCalledTimes(0);
});
test('should not proceed in any flow and NOT cache the provided connection if info was invalid', async () => {
window.location.assign('/some-other-route');
info = { engineUrl: 'someEngineUrl#01', invalid: true };
await act(async () => {
renderResult = renderHook(() => useConnection({ info, cachedConnectionsData }), { wrapper: RouterWrapper });
});
expect(addCachedConnections).toHaveBeenCalledTimes(0);
expect(renderResult.result.current.error).toEqual({
message: 'Connection failed',
hints: ['The WebSocket URL is not valid.'],
});
});
test('should proceed in success flow and cache the provided connection successfully', async () => {
window.location.assign('/some-other-route');
info = { engineUrl: 'someEngineUrl#01' };
await act(async () => {
renderResult = renderHook(() => useConnection({ info, cachedConnectionsData }), { wrapper: RouterWrapper });
});
expect(addCachedConnections).toHaveBeenCalledTimes(1);
expect(addCachedConnections).toHaveBeenCalledWith({ info });
});
test('should proceed in failure flow and dont cache the provided connection', async () => {
connectMock.mockRejectedValue({ isRejected: true });
window.location.assign('/some-other-route');
info = { engineUrl: 'someEngineUrl#01' };
await act(async () => {
renderResult = renderHook(() => useConnection({ info, cachedConnectionsData }), { wrapper: RouterWrapper });
});
expect(addCachedConnections).toHaveBeenCalledTimes(0);
});
});
describe('utils()', () => {
let setGlobal;
let setTreatAsDesktop;
let setError;
beforeEach(() => {
setGlobal = jest.fn();
setTreatAsDesktop = jest.fn();
setError = jest.fn();
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
describe('handleConnectionSuccess()', () => {
test('should setGlobal', async () => {
info = { engineUrl: 'someEngineUrl#01' };
handleConnectionSuccess({ result: info, setGlobal, setError, setTreatAsDesktop });
expect(setGlobal).toHaveBeenCalledTimes(1);
expect(setGlobal).toHaveBeenCalledWith(info);
});
test('should return if there was no `getDocList` in info (result in here) object', async () => {
getConfiguration.mockResolvedValue({ qFeatures: { qIsDesktop: true } });
info = { engineUrl: 'someEngineUrl#01', getConfiguration };
await handleConnectionSuccess({ result: info, setGlobal, setError, setTreatAsDesktop });
expect(setGlobal).toHaveBeenCalledTimes(1);
expect(setGlobal).toHaveBeenCalledWith(info);
expect(setTreatAsDesktop).toHaveBeenCalledTimes(0);
});
test('should setTreatAsDesktop if `config.qFeatures.qIsDesktop` set to true', async () => {
getConfiguration.mockResolvedValue({ qFeatures: { qIsDesktop: true } });
info = {
engineUrl: 'someEngineUrl#01',
getConfiguration,
getDocList: jest.fn(),
};
await handleConnectionSuccess({ result: info, setGlobal, setError, setTreatAsDesktop });
expect(setTreatAsDesktop).toHaveBeenCalledTimes(1);
expect(setTreatAsDesktop).toHaveBeenCalledWith(true);
});
test('should not set setTreatAsDesktop if ther ewas error while tring to get configuration', async () => {
getConfiguration.mockRejectedValue(undefined);
info = {
engineUrl: 'someEngineUrl#01',
getConfiguration,
getDocList: jest.fn(),
};
try {
await handleConnectionSuccess({ result: info, setGlobal, setError, setTreatAsDesktop });
} catch (error) {
expect(error.message).toBe('Failed to get configuration');
}
});
});
describe('handleConnectionFailure()', () => {
test('should setError', async () => {
const error = new Error('someError#01');
handleConnectionFailure({ error, setError });
expect(setError).toHaveBeenCalledTimes(1);
expect(setError).toHaveBeenCalledWith({
hints: [],
message: 'Something went wrong, check the devtools console',
});
});
test('should setError if error was instance of WebSocket', async () => {
const error = new Error('someError#01');
error.target = new WebSocket('ws://localhost:1234');
info = { engineUrl: 'ws://localhost:1234' };
handleConnectionFailure({ error, info, setError });
expect(setError).toHaveBeenCalledTimes(1);
expect(setError).toHaveBeenCalledWith({
hints: [],
message: 'Connection failed to ws://localhost:1234',
});
});
test('should setError and add hint if url was for a qlik cloud SDE and there was no web integration id', async () => {
const error = new Error('someError#01');
error.target = new WebSocket('wss://some.remote.sde.qlikdev.com');
info = { engineUrl: 'wss://some.remote.sde.qlikdev.com' };
handleConnectionFailure({ error, info, setError });
expect(setError).toHaveBeenCalledTimes(1);
expect(setError).toHaveBeenCalledWith({
hints: [
'- If you are connecting to Qlik Cloud, make sure to provide a web integration id or client id.',
'- For Qlik Sense on Windows, make sure the proxy setup is correct and that you are authenticated.',
'- Press the ? in the top right for more information on how to set up the connection correctly.',
],
message: 'Connection failed to wss://some.remote.sde.qlikdev.com',
});
});
});
describe('handleSessionNotification()', () => {
const sessionOnMock = jest.fn();
beforeEach(() => {
info = {
session: {
on: sessionOnMock,
},
};
});
test('should call `session.on` if it was in info object', () => {
sessionOnMock.mockImplementationOnce((evt, callback) => {
if (evt === 'notification:OnAuthenticationInformation') {
callback({ mustAuthenticate: true });
}
});
handleSessionNotification({ result: info, setError, setGlobal });
expect(sessionOnMock).toHaveBeenCalledTimes(1);
expect(sessionOnMock).toHaveBeenCalledWith('notification:OnAuthenticationInformation', expect.any(Function));
expect(setError).toHaveBeenCalledTimes(1);
expect(setError).toHaveBeenCalledWith({
message: 'Could not authenticate.',
hints: [
`In your virtual proxy advanced settings in the QMC, make sure to whitelist ${window.location.host}, ensure "Has secure attribute" is enabled and that "SameSite attribute" is set to "None".`,
],
});
expect(setGlobal).toHaveBeenCalledTimes(1);
expect(setGlobal).toHaveBeenCalledWith(null);
});
});
});
});