Загрузка данных
import { CustomPriceAxisPaneView, CustomPriceAxisView } from '@core/Drawings/axis';
import type { CanvasRenderingTarget2D } from 'fancy-canvas';
function createTarget(verticalPixelRatio = 2) {
const context = {
save: jest.fn(),
restore: jest.fn(),
fillRect: jest.fn(),
fillStyle: '',
} as unknown as CanvasRenderingContext2D;
const useBitmapCoordinateSpace = jest.fn(
(
callback: (scope: {
context: CanvasRenderingContext2D;
horizontalPixelRatio: number;
verticalPixelRatio: number;
bitmapSize: { width: number; height: number };
}) => void,
) => {
callback({
context,
horizontalPixelRatio: 2,
verticalPixelRatio,
bitmapSize: { width: 300, height: 200 },
});
},
);
return {
context,
target: { useBitmapCoordinateSpace } as unknown as CanvasRenderingTarget2D,
useBitmapCoordinateSpace,
};
}
function createViewport(width = 400, height = 300): HTMLElement {
const viewport = document.createElement('div');
jest.spyOn(viewport, 'getBoundingClientRect').mockReturnValue({
width,
height,
x: 0,
y: 0,
top: 0,
left: 0,
right: width,
bottom: height,
toJSON: () => ({}),
});
return viewport;
}
describe('CustomPriceAxisPaneView', () => {
it('должен использовать bottom zOrder по умолчанию', () => {
const view = new CustomPriceAxisPaneView({
getAxisSegments: () => [],
});
expect(view.zOrder()).toBe('bottom');
});
it('должен использовать переданный zOrder', () => {
const view = new CustomPriceAxisPaneView({
getAxisSegments: () => [],
zOrder: 'top',
});
expect(view.zOrder()).toBe('top');
});
it('должен возвращать один экземпляр renderer', () => {
const view = new CustomPriceAxisPaneView({
getAxisSegments: () => [],
});
expect(view.renderer()).toBe(view.renderer());
});
it('не должен рисовать при отсутствии сегментов', () => {
const { target, useBitmapCoordinateSpace } = createTarget();
const view = new CustomPriceAxisPaneView({
getAxisSegments: () => [],
});
view.renderer().draw(target);
expect(useBitmapCoordinateSpace).not.toHaveBeenCalled();
});
it('должен рисовать сегмент по вертикали', () => {
const { context, target } = createTarget();
const view = new CustomPriceAxisPaneView({
getAxisSegments: () => [
{
from: 10,
to: 30,
color: '#ff0000',
},
],
});
view.renderer().draw(target);
expect(context.fillStyle).toBe('#ff0000');
expect(context.fillRect).toHaveBeenCalledWith(0, 20, 300, 40);
expect(context.save).toHaveBeenCalledTimes(1);
expect(context.restore).toHaveBeenCalledTimes(1);
});
it('должен корректно рисовать сегмент при обратном порядке координат', () => {
const { context, target } = createTarget();
const view = new CustomPriceAxisPaneView({
getAxisSegments: () => [
{
from: 30,
to: 10,
color: '#00ff00',
},
],
});
view.renderer().draw(target);
expect(context.fillRect).toHaveBeenCalledWith(0, 20, 300, 40);
});
it('должен рисовать несколько сегментов', () => {
const { context, target } = createTarget();
const view = new CustomPriceAxisPaneView({
getAxisSegments: () => [
{
from: 10,
to: 20,
color: '#ff0000',
},
{
from: 30,
to: 50,
color: '#00ff00',
},
],
});
view.renderer().draw(target);
expect(context.fillRect).toHaveBeenNthCalledWith(1, 0, 20, 300, 20);
expect(context.fillRect).toHaveBeenNthCalledWith(2, 0, 60, 300, 40);
});
});
describe('CustomPriceAxisView', () => {
it('должен возвращать данные label внутри viewport', () => {
const viewport = createViewport(50, 300);
const getAxisLabel = jest.fn(() => ({
coordinate: 125,
text: '100.50',
textColor: '#ffffff',
backgroundColor: '#000000',
}));
const view = new CustomPriceAxisView({
getAxisLabel,
labelKind: 'entry',
viewport,
});
expect(view.visible()).toBe(true);
expect(view.tickVisible()).toBe(false);
expect(view.coordinate()).toBe(125);
expect(view.text()).toBe('100.50');
expect(view.textColor()).toBe('#ffffff');
expect(view.backColor()).toBe('#000000');
expect(getAxisLabel).toHaveBeenCalledWith('entry');
});
it('должен возвращать значения по умолчанию при отсутствии label', () => {
const view = new CustomPriceAxisView({
getAxisLabel: () => null,
labelKind: 'entry',
viewport: createViewport(),
});
expect(view.visible()).toBe(false);
expect(view.tickVisible()).toBe(false);
expect(view.coordinate()).toBe(0);
expect(view.text()).toBe('');
expect(view.textColor()).toBe('');
expect(view.backColor()).toBe('');
});
it('не должен быть видимым при пустом тексте', () => {
const view = new CustomPriceAxisView({
getAxisLabel: () => ({
coordinate: 100,
text: '',
textColor: '#ffffff',
backgroundColor: '#000000',
}),
labelKind: 'entry',
viewport: createViewport(),
});
expect(view.visible()).toBe(false);
});
it.each([-1, 301])('не должен быть видимым вне viewport по вертикали: %s', (coordinate) => {
const view = new CustomPriceAxisView({
getAxisLabel: () => ({
coordinate,
text: '100.50',
textColor: '#ffffff',
backgroundColor: '#000000',
}),
labelKind: 'entry',
viewport: createViewport(400, 300),
});
expect(view.visible()).toBe(false);
});
it.each([0, 300])('должен быть видимым на границе viewport по вертикали: %s', (coordinate) => {
const view = new CustomPriceAxisView({
getAxisLabel: () => ({
coordinate,
text: '100.50',
textColor: '#ffffff',
backgroundColor: '#000000',
}),
labelKind: 'entry',
viewport: createViewport(400, 300),
});
expect(view.visible()).toBe(true);
});
it('не должен быть видимым при некорректной координате', () => {
const view = new CustomPriceAxisView({
getAxisLabel: () => ({
coordinate: Number.NaN,
text: '100.50',
textColor: '#ffffff',
backgroundColor: '#000000',
}),
labelKind: 'entry',
viewport: createViewport(),
});
expect(view.visible()).toBe(false);
});
});
import { CustomTimeAxisPaneView, CustomTimeAxisView } from '@core/Drawings/axis';
import type { CanvasRenderingTarget2D } from 'fancy-canvas';
function createTarget(horizontalPixelRatio = 2) {
const context = {
save: jest.fn(),
restore: jest.fn(),
fillRect: jest.fn(),
fillStyle: '',
} as unknown as CanvasRenderingContext2D;
const useBitmapCoordinateSpace = jest.fn(
(
callback: (scope: {
context: CanvasRenderingContext2D;
horizontalPixelRatio: number;
verticalPixelRatio: number;
bitmapSize: { width: number; height: number };
}) => void,
) => {
callback({
context,
horizontalPixelRatio,
verticalPixelRatio: 2,
bitmapSize: { width: 300, height: 200 },
});
},
);
return {
context,
target: { useBitmapCoordinateSpace } as unknown as CanvasRenderingTarget2D,
useBitmapCoordinateSpace,
};
}
function createViewport(width = 400, height = 300): HTMLElement {
const viewport = document.createElement('div');
jest.spyOn(viewport, 'getBoundingClientRect').mockReturnValue({
width,
height,
x: 0,
y: 0,
top: 0,
left: 0,
right: width,
bottom: height,
toJSON: () => ({}),
});
return viewport;
}
describe('CustomTimeAxisPaneView', () => {
it('должен использовать bottom zOrder по умолчанию', () => {
const view = new CustomTimeAxisPaneView({
getAxisSegments: () => [],
});
expect(view.zOrder()).toBe('bottom');
});
it('должен использовать переданный zOrder', () => {
const view = new CustomTimeAxisPaneView({
getAxisSegments: () => [],
zOrder: 'normal',
});
expect(view.zOrder()).toBe('normal');
});
it('должен возвращать один экземпляр renderer', () => {
const view = new CustomTimeAxisPaneView({
getAxisSegments: () => [],
});
expect(view.renderer()).toBe(view.renderer());
});
it('не должен рисовать при отсутствии сегментов', () => {
const { target, useBitmapCoordinateSpace } = createTarget();
const view = new CustomTimeAxisPaneView({
getAxisSegments: () => [],
});
view.renderer().draw(target);
expect(useBitmapCoordinateSpace).not.toHaveBeenCalled();
});
it('должен рисовать сегмент по горизонтали', () => {
const { context, target } = createTarget();
const view = new CustomTimeAxisPaneView({
getAxisSegments: () => [
{
from: 10,
to: 30,
color: '#ff0000',
},
],
});
view.renderer().draw(target);
expect(context.fillStyle).toBe('#ff0000');
expect(context.fillRect).toHaveBeenCalledWith(20, 0, 40, 200);
expect(context.save).toHaveBeenCalledTimes(1);
expect(context.restore).toHaveBeenCalledTimes(1);
});
it('должен корректно рисовать сегмент при обратном порядке координат', () => {
const { context, target } = createTarget();
const view = new CustomTimeAxisPaneView({
getAxisSegments: () => [
{
from: 30,
to: 10,
color: '#00ff00',
},
],
});
view.renderer().draw(target);
expect(context.fillRect).toHaveBeenCalledWith(20, 0, 40, 200);
});
it('должен рисовать несколько сегментов', () => {
const { context, target } = createTarget();
const view = new CustomTimeAxisPaneView({
getAxisSegments: () => [
{
from: 10,
to: 20,
color: '#ff0000',
},
{
from: 30,
to: 50,
color: '#00ff00',
},
],
});
view.renderer().draw(target);
expect(context.fillRect).toHaveBeenNthCalledWith(1, 20, 0, 20, 200);
expect(context.fillRect).toHaveBeenNthCalledWith(2, 60, 0, 40, 200);
});
});
describe('CustomTimeAxisView', () => {
it('должен возвращать данные label внутри viewport', () => {
const viewport = createViewport(300, 50);
const getAxisLabel = jest.fn(() => ({
coordinate: 150,
text: '09:30',
textColor: '#ffffff',
backgroundColor: '#000000',
}));
const view = new CustomTimeAxisView({
getAxisLabel,
labelKind: 'start',
viewport,
});
expect(view.visible()).toBe(true);
expect(view.tickVisible()).toBe(false);
expect(view.coordinate()).toBe(150);
expect(view.text()).toBe('09:30');
expect(view.textColor()).toBe('#ffffff');
expect(view.backColor()).toBe('#000000');
expect(getAxisLabel).toHaveBeenCalledWith('start');
});
it('должен возвращать значения по умолчанию при отсутствии label', () => {
const view = new CustomTimeAxisView({
getAxisLabel: () => null,
labelKind: 'start',
viewport: createViewport(),
});
expect(view.visible()).toBe(false);
expect(view.tickVisible()).toBe(false);
expect(view.coordinate()).toBe(0);
expect(view.text()).toBe('');
expect(view.textColor()).toBe('');
expect(view.backColor()).toBe('');
});
it('не должен быть видимым при пустом тексте', () => {
const view = new CustomTimeAxisView({
getAxisLabel: () => ({
coordinate: 150,
text: '',
textColor: '#ffffff',
backgroundColor: '#000000',
}),
labelKind: 'start',
viewport: createViewport(),
});
expect(view.visible()).toBe(false);
});
it.each([-1, 401])('не должен быть видимым вне viewport по горизонтали: %s', (coordinate) => {
const view = new CustomTimeAxisView({
getAxisLabel: () => ({
coordinate,
text: '09:30',
textColor: '#ffffff',
backgroundColor: '#000000',
}),
labelKind: 'start',
viewport: createViewport(400, 300),
});
expect(view.visible()).toBe(false);
});
it.each([0, 400])('должен быть видимым на границе viewport по горизонтали: %s', (coordinate) => {
const view = new CustomTimeAxisView({
getAxisLabel: () => ({
coordinate,
text: '09:30',
textColor: '#ffffff',
backgroundColor: '#000000',
}),
labelKind: 'start',
viewport: createViewport(400, 300),
});
expect(view.visible()).toBe(true);
});
it('не должен быть видимым при некорректной координате', () => {
const view = new CustomTimeAxisView({
getAxisLabel: () => ({
coordinate: Number.NaN,
text: '09:30',
textColor: '#ffffff',
backgroundColor: '#000000',
}),
labelKind: 'start',
viewport: createViewport(),
});
expect(view.visible()).toBe(false);
});
});
import { AxisLine } from '@core/Drawings/axisLine/axisLine';
import { AxisLinePaneRenderer } from '@core/Drawings/axisLine/paneRenderer';
import { AxisLinePaneView } from '@core/Drawings/axisLine/paneView';
import { createDefaultSettings, getAxisLineSettingsTabs } from '@core/Drawings/axisLine/settings';
import {
createCanvasTarget,
createDrawingTestEnvironment,
createMouseEvent,
createPointerEvent,
} from '../drawingTestUtils';
import type { DrawingHandle } from '@core/Drawings/handles';
import type { AxisLabel } from '@core/Drawings/types';
import type { PrimitiveHoveredItem, Time } from 'lightweight-charts';
jest.mock('@src/theme', () => ({
getThemeStore: () => ({
colors: {
chartBackground: '#ffffff',
chartLineColor: '#3366ff',
chartPriceLineText: '#ffffff',
axisMarkerLabelFill: '#112233',
},
}),
}));
jest.mock('@src/types/defaults', () => ({
Defaults: {
dateFormat: 'date-format',
timeFormat: '24h',
showTime: true,
},
}));
jest.mock('@src/utils', () => ({
formatPrice: (value: number) => String(value),
}));
jest.mock('@src/utils/formatter', () => ({
formatDate: () => 'formatted-date',
}));
jest.mock('@src/translations', () => ({
t: (value: string) => value,
}));
class TestAxisLine extends AxisLine {
public handles(): readonly DrawingHandle<'main'>[] {
return this.getDrawingHandles();
}
public hovered(x: number, y: number): PrimitiveHoveredItem | null {
return this.getHoveredItem(x, y);
}
public timeLabel(kind: string): AxisLabel | null {
return this.getTimeAxisLabel(kind);
}
public priceLabel(kind: string): AxisLabel | null {
return this.getPriceAxisLabel(kind);
}
public pointerDown(event: PointerEvent): void {
this.handlePointerDown(event);
}
public pointerMove(event: PointerEvent): void {
this.handlePointerMove(event);
}
public pointerUp(event: PointerEvent): void {
this.handlePointerUp(event);
}
public doubleClick(event: MouseEvent): void {
this.handleDoubleClick(event);
}
}
function createAxisLine(direction: 'vertical' | 'horizontal', selected = false) {
const env = createDrawingTestEnvironment({ selected });
const openSettings = jest.fn();
const drawing = new TestAxisLine({
...env,
direction,
openSettings,
});
return {
...env,
drawing,
openSettings,
};
}
describe('AxisLine', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('должен начинаться в idle', () => {
const { drawing } = createAxisLine('vertical');
expect(drawing.isCreationPending()).toBe(true);
expect(drawing.getState()).toMatchObject({
mode: 'idle',
time: null,
price: null,
});
});
it('должен создавать vertical line', () => {
const { drawing, container, timeScale } = createAxisLine('vertical');
drawing.pointerDown(createPointerEvent(container, 80, 120));
expect(timeScale.coordinateToTime).toHaveBeenCalledWith(80);
expect(drawing.getState()).toMatchObject({
mode: 'ready',
time: 80,
price: null,
});
});
it('должен создавать horizontal line', () => {
const { drawing, container, coordinateToPrice } = createAxisLine('horizontal');
drawing.pointerDown(createPointerEvent(container, 80, 120));
expect(coordinateToPrice).toHaveBeenCalledWith(120);
expect(drawing.getState()).toMatchObject({
mode: 'ready',
time: null,
price: 120,
});
});
it('не должен начинать drawing правой кнопкой', () => {
const { drawing, container } = createAxisLine('vertical');
drawing.pointerDown(createPointerEvent(container, 80, 120, 1, 2));
expect(drawing.getState()).toMatchObject({
mode: 'idle',
time: null,
});
});
it('должен восстанавливать state', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
hidden: true,
mode: 'ready',
price: 150,
settings: {
lineColor: '#ff0000',
text: 'Test',
},
});
expect(drawing.getState()).toMatchObject({
hidden: true,
mode: 'ready',
price: 150,
settings: {
lineColor: '#ff0000',
text: 'Test',
},
});
});
it('не должен возвращать render data для hidden line', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
hidden: true,
mode: 'ready',
price: 100,
});
expect(drawing.getRenderData()).toBeNull();
});
it('должен возвращать render data vertical line', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 100 as Time,
});
expect(drawing.getRenderData()).toEqual({
direction: 'vertical',
coordinate: 100,
lineColor: '#3366ff',
text: '',
fontSize: 14,
isBold: false,
isItalic: false,
textColor: '#3366ff',
});
});
it('должен возвращать render data horizontal line', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
mode: 'ready',
price: 120,
});
expect(drawing.getRenderData()).toEqual({
direction: 'horizontal',
coordinate: 120,
lineColor: '#3366ff',
text: '',
fontSize: 14,
isBold: false,
isItalic: false,
textColor: '#3366ff',
});
});
it('должен размещать vertical handle по центру высоты', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 80 as Time,
});
expect(drawing.handles()).toEqual([
{
id: 'main',
x: 80,
y: 150,
shape: 'rounded',
},
]);
});
it('должен размещать horizontal handle по центру ширины', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
mode: 'ready',
price: 120,
});
expect(drawing.handles()).toEqual([
{
id: 'main',
x: 200,
y: 120,
shape: 'rounded',
},
]);
});
it('должен возвращать time axis view только для vertical line', () => {
const { drawing } = createAxisLine('vertical');
expect(drawing.timeAxisViews()).toHaveLength(1);
expect(drawing.priceAxisViews()).toHaveLength(0);
});
it('должен возвращать price axis view только для horizontal line', () => {
const { drawing } = createAxisLine('horizontal');
expect(drawing.timeAxisViews()).toHaveLength(0);
expect(drawing.priceAxisViews()).toHaveLength(1);
});
it('должен возвращать time label для selected vertical line', () => {
const { drawing } = createAxisLine('vertical', true);
drawing.setState({
mode: 'ready',
time: 80 as Time,
});
expect(drawing.timeLabel('main')).toEqual({
coordinate: 80,
text: 'formatted-date',
textColor: '#ffffff',
backgroundColor: '#112233',
});
});
it('должен возвращать price label для selected horizontal line', () => {
const { drawing } = createAxisLine('horizontal', true);
drawing.setState({
mode: 'ready',
price: 120,
});
expect(drawing.priceLabel('main')).toEqual({
coordinate: 120,
text: '120',
textColor: '#ffffff',
backgroundColor: '#112233',
});
});
it('должен сохранять time label для unselected vertical line', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 80 as Time,
});
expect(drawing.timeLabel('main')).toEqual({
coordinate: 80,
text: 'formatted-date',
textColor: '#ffffff',
backgroundColor: '#112233',
});
});
it('должен сохранять price label для unselected horizontal line', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
mode: 'ready',
price: 120,
});
expect(drawing.priceLabel('main')).toEqual({
coordinate: 120,
text: '120',
textColor: '#ffffff',
backgroundColor: '#112233',
});
});
it('не должен возвращать time label для hidden vertical line', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
hidden: true,
mode: 'ready',
time: 80 as Time,
});
expect(drawing.timeLabel('main')).toBeNull();
});
it('не должен возвращать price label для hidden horizontal line', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
hidden: true,
mode: 'ready',
price: 120,
});
expect(drawing.priceLabel('main')).toBeNull();
});
it('не должен возвращать time label до завершения создания vertical line', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
mode: 'idle',
time: 80 as Time,
});
expect(drawing.timeLabel('main')).toBeNull();
});
it('не должен возвращать price label до завершения создания horizontal line', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
mode: 'idle',
price: 120,
});
expect(drawing.priceLabel('main')).toBeNull();
});
it('не должен возвращать time label для horizontal line', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
mode: 'ready',
price: 120,
});
expect(drawing.timeLabel('main')).toBeNull();
});
it('не должен возвращать price label для vertical line', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 80 as Time,
});
expect(drawing.priceLabel('main')).toBeNull();
});
it('не должен возвращать label для неизвестного kind', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 80 as Time,
});
expect(drawing.timeLabel('unknown')).toBeNull();
expect(drawing.priceLabel('unknown')).toBeNull();
});
it('должен определять hover vertical line', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 50 as Time,
});
expect(drawing.hovered(54, 20)).toEqual({
cursorStyle: 'ew-resize',
externalId: 'axis-line',
zOrder: 'top',
});
});
it('должен определять hover horizontal line', () => {
const { drawing } = createAxisLine('horizontal');
drawing.setState({
mode: 'ready',
price: 50,
});
expect(drawing.hovered(20, 54)).toEqual({
cursorStyle: 'ns-resize',
externalId: 'axis-line',
zOrder: 'top',
});
});
it('не должен определять hover вдали от line', () => {
const { drawing } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 50 as Time,
});
expect(drawing.hovered(70, 20)).toBeNull();
});
it('должен выбирать unselected line', () => {
const { drawing, interaction, container } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 50 as Time,
});
drawing.pointerDown(createPointerEvent(container, 50, 20));
expect(interaction.select).toHaveBeenCalledTimes(1);
});
it('должен перемещать vertical line', () => {
const { drawing, container } = createAxisLine('vertical', true);
drawing.setState({
mode: 'ready',
time: 50 as Time,
});
drawing.pointerDown(createPointerEvent(container, 50, 20, 1));
drawing.pointerMove(createPointerEvent(container, 90, 20, 1));
expect(drawing.getState()).toMatchObject({
mode: 'dragging',
time: 90,
});
drawing.pointerUp(createPointerEvent(container, 90, 20, 1));
expect(drawing.getState()).toMatchObject({
mode: 'ready',
time: 90,
});
});
it('должен перемещать horizontal line', () => {
const { drawing, container } = createAxisLine('horizontal', true);
drawing.setState({
mode: 'ready',
price: 50,
});
drawing.pointerDown(createPointerEvent(container, 20, 50, 1));
drawing.pointerMove(createPointerEvent(container, 20, 90, 1));
expect(drawing.getState()).toMatchObject({
mode: 'dragging',
price: 90,
});
});
it('не должен реагировать на другой pointerId', () => {
const { drawing, container } = createAxisLine('vertical', true);
drawing.setState({
mode: 'ready',
time: 50 as Time,
});
drawing.pointerDown(createPointerEvent(container, 50, 20, 1));
drawing.pointerMove(createPointerEvent(container, 90, 20, 2));
expect(drawing.getState()).toMatchObject({
time: 50,
});
});
it('должен открывать settings по double click', () => {
const { drawing, container, openSettings } = createAxisLine('vertical');
drawing.setState({
mode: 'ready',
time: 50 as Time,
});
drawing.doubleClick(createMouseEvent(container, 50, 20));
expect(openSettings).toHaveBeenCalledTimes(1);
});
it('должен обновлять settings через Base', () => {
const { drawing } = createAxisLine('vertical');
drawing.updateSettings({
lineColor: '#ff0000',
text: 'Updated',
});
expect(drawing.getSettings()).toMatchObject({
lineColor: '#ff0000',
text: 'Updated',
fontSize: 14,
});
});
});
describe('AxisLinePaneRenderer', () => {
it('не должен рисовать без render data', () => {
const { target, useBitmapCoordinateSpace } = createCanvasTarget();
const renderer = new AxisLinePaneRenderer({
getRenderData: () => null,
} as unknown as AxisLine);
renderer.draw(target);
expect(useBitmapCoordinateSpace).not.toHaveBeenCalled();
});
it('должен рисовать vertical line', () => {
const { context, target } = createCanvasTarget({
bitmapWidth: 300,
bitmapHeight: 200,
});
const renderer = new AxisLinePaneRenderer({
getRenderData: () => ({
direction: 'vertical',
coordinate: 20,
lineColor: '#ff0000',
text: '',
fontSize: 14,
isBold: false,
isItalic: false,
textColor: '#ffffff',
}),
} as unknown as AxisLine);
renderer.draw(target);
expect(context.fillRect).toHaveBeenCalledWith(38, 0, 4, 200);
});
it('должен рисовать horizontal line', () => {
const { context, target } = createCanvasTarget({
bitmapWidth: 300,
bitmapHeight: 200,
});
const renderer = new AxisLinePaneRenderer({
getRenderData: () => ({
direction: 'horizontal',
coordinate: 30,
lineColor: '#ff0000',
text: '',
fontSize: 14,
isBold: false,
isItalic: false,
textColor: '#ffffff',
}),
} as unknown as AxisLine);
renderer.draw(target);
expect(context.fillRect).toHaveBeenCalledWith(0, 58, 300, 4);
});
it('должен рисовать vertical text с поворотом', () => {
const { context, target } = createCanvasTarget({
bitmapWidth: 300,
bitmapHeight: 200,
});
const renderer = new AxisLinePaneRenderer({
getRenderData: () => ({
direction: 'vertical',
coordinate: 20,
lineColor: '#ff0000',
text: 'Test',
fontSize: 14,
isBold: true,
isItalic: true,
textColor: '#ffffff',
}),
} as unknown as AxisLine);
renderer.draw(target);
expect(context.translate).toHaveBeenCalledWith(40, 100);
expect(context.rotate).toHaveBeenCalledWith(-Math.PI / 2);
expect(context.font).toContain('italic');
expect(context.font).toContain('700');
expect(context.fillText).toHaveBeenCalledWith('Test', 0, expect.any(Number));
});
});
describe('AxisLinePaneView', () => {
it('должен возвращать renderer и top zOrder', () => {
const view = new AxisLinePaneView({
getRenderData: jest.fn(),
} as unknown as AxisLine);
expect(view.renderer()).toBeInstanceOf(AxisLinePaneRenderer);
expect(view.zOrder()).toBe('top');
});
});
describe('AxisLine settings', () => {
it('должен создавать defaults', () => {
expect(createDefaultSettings()).toEqual({
lineColor: '#3366ff',
text: '',
fontSize: 14,
isBold: false,
isItalic: false,
textColor: '#3366ff',
});
});
it('должен создавать style и text tabs', () => {
expect(getAxisLineSettingsTabs(createDefaultSettings()).map(({ key }) => key)).toEqual(['style', 'text']);
});
});