Загрузка данных
import { IChartApi } from 'lightweight-charts';
import { of } from 'rxjs';
import { DataSource } from '@core/DataSource';
import { DOMModel } from '@core/DOMModel';
import { EventManager } from '@core/EventManager';
import { Indicator } from '@core/Indicator';
import { IndicatorManager } from '@core/IndicatorManager';
import { Pane } from '@core/Pane';
import { PaneManager } from '@core/PaneManager';
import { IndicatorsIds } from '@src/constants';
jest.mock('@core/Indicator');
jest.mock('@src/core/Indicators', () => ({
indicatorsMap: () => ({
ema: {
newPane: false,
label: 'EMA',
series: [],
},
}),
}));
jest.mock('@src/utils', () => {
const actual = jest.requireActual<typeof import('@src/utils')>('@src/utils');
return {
...actual,
applyNextIndicatorColors: (config: unknown) => config,
getIndicatorColors: () => [],
};
});
type EntityFactory = (
zIndex: number,
moveUp: (id: string) => void,
moveDown: (id: string) => void,
) => Indicator;
describe('IndicatorManager', () => {
const IndicatorMock = Indicator as jest.MockedClass<typeof Indicator>;
let manager: IndicatorManager;
let mainPane: Pane;
let setEntity: jest.Mock;
let removeEntity: jest.Mock;
let getMainPane: jest.Mock;
let getPaneById: jest.Mock;
let addPane: jest.Mock;
beforeEach(() => {
IndicatorMock.mockImplementation(
(params) =>
({
id: params.id,
destroy: jest.fn(),
getConfig: jest.fn(() => params.config),
getIndicatorType: jest.fn(() => params.type),
}) as unknown as Indicator,
);
mainPane = {
getId: jest.fn(() => 0),
} as unknown as Pane;
getMainPane = jest.fn(() => mainPane);
getPaneById = jest.fn();
addPane = jest.fn(() => mainPane);
setEntity = jest.fn((factory: EntityFactory) => factory(1, jest.fn(), jest.fn()));
removeEntity = jest.fn();
const paneManager = {
getMainPane,
getPaneById,
addPane,
} as unknown as PaneManager;
const eventManager = {
symbolId: jest.fn(() => of('SBER')),
symbol: jest.fn(() => of('SBER')),
} as unknown as EventManager;
const DOM = {
setEntity,
removeEntity,
} as unknown as DOMModel;
manager = new IndicatorManager({
eventManager,
dataSource: {} as DataSource,
lwcChart: {} as IChartApi,
paneManager,
DOM,
});
});
it('не должен добавлять индикатор без типа', () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
manager.addIndicator({});
expect(setEntity).not.toHaveBeenCalled();
expect(manager.getIndicators().value.size).toBe(0);
consoleSpy.mockRestore();
});
it('должен добавлять индикатор', () => {
manager.addIndicator({
id: 'ema-1',
indicatorType: IndicatorsIds.EMA,
});
expect(manager.getIndicators().value.has('ema-1')).toBe(true);
expect(getMainPane).toHaveBeenCalledTimes(1);
expect(IndicatorMock).toHaveBeenCalledTimes(1);
});
it('должен публиковать добавленные индикаторы', () => {
let entities: Indicator[] = [];
const subscription = manager.entities().subscribe((value) => {
entities = value;
});
manager.addIndicator({
id: 'ema-1',
indicatorType: IndicatorsIds.EMA,
});
expect(entities).toHaveLength(1);
expect(entities[0]).toBe(manager.getIndicators().value.get('ema-1'));
subscription.unsubscribe();
});
it('должен использовать pane из snapshot', () => {
const pane = {
getId: jest.fn(() => 3),
} as unknown as Pane;
getPaneById.mockReturnValue(pane);
manager.addIndicator({
id: 'ema-1',
indicatorType: IndicatorsIds.EMA,
paneId: 3,
});
expect(getPaneById).toHaveBeenCalledWith(3);
expect(addPane).not.toHaveBeenCalled();
});
it('должен создать pane если pane из snapshot отсутствует', () => {
getPaneById.mockReturnValue(undefined);
manager.addIndicator({
id: 'ema-1',
indicatorType: IndicatorsIds.EMA,
paneId: 3,
});
expect(getPaneById).toHaveBeenCalledWith(3);
expect(addPane).toHaveBeenCalledTimes(1);
});
it('должен удалять индикатор из состояния и DOM', () => {
manager.addIndicator({
id: 'ema-1',
indicatorType: IndicatorsIds.EMA,
});
const indicator = manager.getIndicators().value.get('ema-1');
if (!indicator) {
throw new Error('Индикатор не был добавлен');
}
manager.deleteIndicator('ema-1');
expect(manager.getIndicators().value.has('ema-1')).toBe(false);
expect(removeEntity).toHaveBeenCalledWith(indicator);
expect(indicator.destroy).toHaveBeenCalledTimes(1);
});
it('не должен удалять несуществующий индикатор', () => {
manager.deleteIndicator('unknown');
expect(removeEntity).not.toHaveBeenCalled();
});
});
import { BehaviorSubject, Subscription } from 'rxjs';
import { Chart } from '@core/Chart';
interface ChartInternals {
eventManager: {
symbolId: () => BehaviorSubject<string>;
getInterval: () => BehaviorSubject<null>;
};
compareManager: {
itemsObs: () => BehaviorSubject<{ symbolId: string }[]>;
};
dataSource: {
setSymbols: jest.Mock;
loadTill: jest.Mock;
};
lwcChart: {
timeScale: () => {
getVisibleRange: () => null;
};
};
subscriptions: Subscription;
activeSymbolIds: string[];
currentInterval: null;
setupDataSourceSubs: () => void;
}
describe('Chart', () => {
function createChartInternals(): ChartInternals {
return Object.assign(Object.create(Chart.prototype), {
eventManager: {
symbolId: () => new BehaviorSubject(''),
getInterval: () => new BehaviorSubject(null),
},
compareManager: {
itemsObs: () =>
new BehaviorSubject([
{
symbolId: 'SBER',
},
{
symbolId: 'SBER',
},
]),
},
dataSource: {
setSymbols: jest.fn(),
loadTill: jest.fn(() => Promise.resolve()),
},
lwcChart: {
timeScale: () => ({
getVisibleRange: () => null,
}),
},
subscriptions: new Subscription(),
activeSymbolIds: [],
currentInterval: null,
}) as ChartInternals;
}
it('должен исключать пустой основной symbolId и удалять дубликаты', () => {
const chart = createChartInternals();
chart.setupDataSourceSubs();
expect(chart.dataSource.setSymbols).toHaveBeenLastCalledWith(['SBER']);
chart.subscriptions.unsubscribe();
});
});