Skip to content

Commit

Permalink
[BOT] Farabi/bot-1034/test coverage chart store and toolbar store (de…
Browse files Browse the repository at this point in the history
…riv-com#14109)

* chore: added test case for chart store and toolbar store

* chore: 100% test coverage for chart store

* chore: increased coverage for test cases

* chore: full coverage of both stores

* chore: use setIsRunning
  • Loading branch information
farabi-deriv authored Mar 13, 2024
1 parent ff15d9a commit c6bf825
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 17 deletions.
158 changes: 158 additions & 0 deletions packages/bot-web-ui/src/stores/__tests__/chart-store.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { ServerTime } from '@deriv/bot-skeleton';
import { LocalStore } from '@deriv/shared';
import { mockStore } from '@deriv/stores';
import { TStores } from '@deriv/stores/types';
import { mock_ws } from 'Utils/mock';
import { mockDBotStore } from 'Stores/useDBotStore';
import ChartStore, { g_subscribers_map } from '../chart-store';
import 'Utils/mock/mock-local-storage';

window.Blockly = {
derivWorkspace: {
getAllBlocks: jest.fn(() => ({
find: jest.fn(),
})),
},
};

jest.mock('@deriv/bot-skeleton/src/scratch/dbot', () => ({}));

describe('ChartStore', () => {
const mock_store: TStores = mockStore({
common: {
server_time: {
clone: jest.fn(() => ({
unix: jest.fn(() => '2024-03-11T10:56:02.239Z'),
})),
},
},
});

let chartStore: ChartStore;
const mock_DBot_store = mockDBotStore(mock_store, mock_ws);

beforeEach(() => {
ServerTime.init(mock_store.common);
chartStore = new ChartStore(mock_DBot_store);
});

it('should initialize ChartStore with default values', () => {
expect(chartStore.symbol).toBeUndefined();
expect(chartStore.is_chart_loading).toBeUndefined();
expect(chartStore.chart_type).toBe('line');
});

it('should return true when contracts exist and the first contract is ended', () => {
const mockContractStore = {
contracts: [{ is_ended: true }, { is_ended: false }],
};
mock_DBot_store.transactions = mockContractStore;
const result = chartStore.is_contract_ended;

expect(result).toBe(true);
});

it('should update symbol correctly', () => {
const mockWorkspace = {
getAllBlocks: jest.fn(() => [{ type: 'trade_definition_market', getFieldValue: jest.fn(() => 'EURUSD') }]),
};

window.Blockly.derivWorkspace = mockWorkspace;
chartStore.updateSymbol();

expect(chartStore.symbol).toEqual('EURUSD');
});

it('should update granularity correctly', () => {
chartStore.updateGranularity(60);
expect(chartStore.granularity).toEqual(60);
});

it('should update chart type correctly', () => {
chartStore.updateChartType('candle');
expect(chartStore.chart_type).toEqual('candle');
});

it('should set chart status correctly', () => {
chartStore.setChartStatus(true);
expect(chartStore.is_chart_loading).toEqual(true);
});

it('should subscribe to ticks history', () => {
const req = { subscribe: 1 };
const callback = jest.fn();
chartStore.wsSubscribe(req, callback);

expect(mock_DBot_store.ws.subscribeTicksHistory).toHaveBeenCalledWith(req, callback);
});

it('should forget ticks history', () => {
const subscribe_req = { subscribe: 1 };
const callback = jest.fn();
chartStore.wsSubscribe(subscribe_req, callback);
const key = JSON.stringify(subscribe_req);

expect(g_subscribers_map).toHaveProperty(key);
chartStore.wsForget(subscribe_req);
expect(g_subscribers_map).not.toHaveProperty(key);
});

it('should forget stream', () => {
const stream_id = 'test_stream_id';
chartStore.wsForgetStream(stream_id);

expect(mock_DBot_store.ws.forgetStream).toHaveBeenCalledWith(stream_id);
});

it('should send request and return server time', async () => {
const req = { time: 1 };
const result = await chartStore.wsSendRequest(req);

expect(result.msg_type).toEqual('time');
expect(result.time).toEqual('2024-03-11T10:56:02.239Z');
});

it('should send request and return active symbols', async () => {
const req = { active_symbols: 1 };
await chartStore.wsSendRequest(req);

expect(mock_DBot_store.ws.activeSymbols).toHaveBeenCalledWith();
});

it('should send request using storage.send', async () => {
const req = { storage: 'storage' };
await chartStore.wsSendRequest(req);

expect(mock_DBot_store.ws.storage.send).toHaveBeenCalledWith(req);
});

it('should get markets order', () => {
const active_symbols = [
{ market: 'EURUSD', display_name: 'EUR/USD' },
{ market: 'AUDUSD', display_name: 'AUD/USD' },
{ market: 'CADUSD', display_name: 'CAD/USD' },
];
const marketsOrder = chartStore.getMarketsOrder(active_symbols);

expect(marketsOrder).toEqual(['AUDUSD', 'CADUSD', 'EURUSD']);
});

it('should get markets order with synthetic index', () => {
const active_symbols = [{ market: 'synthetic_index', display_name: 'Synthetic Index' }];
const marketsOrder = chartStore.getMarketsOrder(active_symbols);

expect(marketsOrder).toEqual(['synthetic_index']);
});

it('should update symbol and save to local storage', () => {
const mockSymbol = 'USDJPY';
chartStore.onSymbolChange(mockSymbol);

expect(chartStore.symbol).toEqual(mockSymbol);
});

it('should call restoreFromStorage ', () => {
LocalStore.set('bot.chart_props', '{none}');
chartStore.restoreFromStorage();
});
});
101 changes: 101 additions & 0 deletions packages/bot-web-ui/src/stores/__tests__/toolbar-store.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { mockStore } from '@deriv/stores';
import { TStores } from '@deriv/stores/types';
import { mock_ws } from 'Utils/mock';
import { mockDBotStore } from 'Stores/useDBotStore';
import ToolbarStore from '../toolbar-store';

jest.mock('@deriv/bot-skeleton/src/scratch/dbot', () => ({}));

const mock_undo = jest.fn();
const mock_cleanup = jest.fn();
const mock_zoom = jest.fn();

window.Blockly = {
derivWorkspace: {
undo: mock_undo,
hasRedoStack: jest.fn(),
hasUndoStack: jest.fn(),
cached_xml: {
main: 'main',
},
getMetrics: jest.fn(() => ({
viewWidth: 100,
viewHeight: 100,
})),
zoom: mock_zoom,
cleanUp: mock_cleanup,
},
utils: {
genUid: jest.fn(),
},
Events: {
setGroup: jest.fn(),
},
svgResize: jest.fn(),
};

describe('ToolbarStore', () => {
const mock_store: TStores = mockStore({});
const mock_DBot_store = mockDBotStore(mock_store, mock_ws);
let toolbarStore: ToolbarStore;

beforeEach(() => {
toolbarStore = new ToolbarStore(mock_DBot_store);
});

it('should initialize ToolbarStore with default values', () => {
expect(toolbarStore.is_animation_info_modal_open).toBe(false);
expect(toolbarStore.is_dialog_open).toBe(false);
expect(toolbarStore.file_name).toBe('Untitled Bot');
expect(toolbarStore.has_undo_stack).toBe(false);
expect(toolbarStore.has_redo_stack).toBe(false);
expect(toolbarStore.is_reset_button_clicked).toBe(false);
});

it('should show dialog on reset button click', () => {
toolbarStore.onResetClick();
expect(toolbarStore.is_dialog_open).toBe(true);
});

it('should hide dialog on close reset dialog click', () => {
toolbarStore.closeResetDialog();
expect(toolbarStore.is_dialog_open).toBe(false);
});

it('should not show dialog onResetOkButtonClick', () => {
toolbarStore.onResetOkButtonClick();
expect(toolbarStore.is_dialog_open).toBe(false);
expect(toolbarStore.is_reset_button_clicked).toBe(false);
});

it('should show dialog onResetOkButtonClick while bot is running', () => {
mock_DBot_store.run_panel.setIsRunning(true);
toolbarStore.onResetOkButtonClick();
expect(toolbarStore.is_reset_button_clicked).toBe(true);
});

it('should call reset default strategy', async () => {
await toolbarStore.resetDefaultStrategy();
expect(window.Blockly.derivWorkspace.strategy_to_load).toBe('main');
});

it('should call onSortClick', () => {
toolbarStore.onSortClick();
expect(mock_cleanup).toHaveBeenCalled();
});

it('should handle undo and redo click correctly', () => {
toolbarStore.onUndoClick(true);
expect(mock_undo).toHaveBeenCalledWith(true);
});

it('should call onZoomInOutClick ', () => {
toolbarStore.onZoomInOutClick(true);
expect(mock_zoom).toHaveBeenCalledWith(50, 50, 1);
});

it('should call onZoomInOutClick ', () => {
toolbarStore.onZoomInOutClick(false);
expect(mock_zoom).toHaveBeenCalledWith(50, 50, -1);
});
});
10 changes: 5 additions & 5 deletions packages/bot-web-ui/src/stores/chart-store.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { action, computed, makeObservable, observable, reaction } from 'mobx';
// import { tabs_title } from '../constants/bot-contents';
import { ServerTime } from '@deriv/bot-skeleton';
import { LocalStore } from '@deriv/shared';
import RootStore from './root-store';
// eslint-disable-next-line import/no-extraneous-dependencies
import {
ActiveSymbolsRequest,
ServerTimeRequest,
TicksHistoryRequest,
TicksStreamRequest,
TradingTimesRequest,
} from '@deriv/api-types';
import { ServerTime } from '@deriv/bot-skeleton';
import { LocalStore } from '@deriv/shared';
import RootStore from './root-store';

const g_subscribers_map: Partial<Record<string, ReturnType<typeof WS.subscribeTicksHistory>>> = {};
export const g_subscribers_map: Partial<Record<string, ReturnType<typeof WS.subscribeTicksHistory>>> = {};
let WS: RootStore['ws'];

export default class ChartStore {
Expand Down
21 changes: 10 additions & 11 deletions packages/bot-web-ui/src/stores/toolbar-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ interface IToolbarStore {
setHasRedoStack: () => void;
}

const Blockly = window.Blockly;
export default class ToolbarStore implements IToolbarStore {
root_store: any;

Expand Down Expand Up @@ -79,8 +78,8 @@ export default class ToolbarStore implements IToolbarStore {
};

resetDefaultStrategy = async () => {
const workspace = Blockly.derivWorkspace;
workspace.current_strategy_id = Blockly.utils.genUid();
const workspace = window.Blockly.derivWorkspace;
workspace.current_strategy_id = window.Blockly.utils.genUid();
await load({
block_string: workspace.cached_xml.main,
file_name: config.default_file_name,
Expand All @@ -99,31 +98,31 @@ export default class ToolbarStore implements IToolbarStore {
indentWorkspace: { x, y },
},
} = config;
Blockly.derivWorkspace.cleanUp(x, y);
window.Blockly.derivWorkspace.cleanUp(x, y);
};

onUndoClick = (is_redo: boolean): void => {
Blockly.Events.setGroup('undo_clicked');
Blockly.derivWorkspace.undo(is_redo);
Blockly.svgResize(Blockly.derivWorkspace); // Called for CommentDelete event.
window.Blockly.Events.setGroup('undo_clicked');
window.Blockly.derivWorkspace.undo(is_redo);
window.Blockly.svgResize(window.Blockly.derivWorkspace); // Called for CommentDelete event.
this.setHasRedoStack();
this.setHasUndoStack();
Blockly.Events.setGroup(false);
window.Blockly.Events.setGroup(false);
};

onZoomInOutClick = (is_zoom_in: boolean): void => {
const workspace = Blockly.derivWorkspace;
const workspace = window.Blockly.derivWorkspace;
const metrics = workspace.getMetrics();
const addition = is_zoom_in ? 1 : -1;

workspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, addition);
};

setHasUndoStack = (): void => {
this.has_undo_stack = Blockly.derivWorkspace?.hasUndoStack();
this.has_undo_stack = window.Blockly.derivWorkspace?.hasUndoStack();
};

setHasRedoStack = (): void => {
this.has_redo_stack = Blockly.derivWorkspace?.hasRedoStack();
this.has_redo_stack = window.Blockly.derivWorkspace?.hasRedoStack();
};
}
21 changes: 21 additions & 0 deletions packages/bot-web-ui/src/utils/mock/mock-local-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// to use this localStorage mock, add the following import statement to the test file:
// import 'Utils/mock/mock-local-storage';

let store: Record<string, string> = {};

export const mockLocalStorage = (() => {
return {
getItem(key: string) {
return store[key] || null;
},
setItem(key: string, value: string) {
store[key] = value.toString();
},
clear() {
store = {};
},
removeItem(key: string) {
delete store[key];
},
};
})();
15 changes: 14 additions & 1 deletion packages/bot-web-ui/src/utils/mock/ws-mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,20 @@ export const mock_ws = {
send: jest.fn(),
},
contractUpdate: jest.fn(),
subscribeTicksHistory: jest.fn(),
subscribeTicksHistory: jest.fn((req, callback) => {
const subscriber = {
history: {
times: [],
prices: [],
},
msg_type: 'history',
pip_size: 3,
req_id: 31,
unsubscribe: jest.fn(),
};
callback();
return subscriber;
}),
forgetStream: jest.fn(),
activeSymbols: jest.fn(),
send: jest.fn(),
Expand Down

0 comments on commit c6bf825

Please sign in to comment.