@363045841yyt/klinechart-core 0.7.3 → 0.7.5-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -201
- package/README.zh-CN.md +201 -201
- package/dist/engine/renderers/webgl/candleSurface.js +47 -47
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -2
- package/dist/version.js.map +1 -1
- package/package.json +129 -122
- package/src/__tests__/signal.test.ts +124 -124
- package/src/config/chartSettings.ts +66 -66
- package/src/controllers/__tests__/drawing.test.ts +214 -214
- package/src/controllers/__tests__/indicatorSelector.test.ts +481 -481
- package/src/controllers/__tests__/toolbar.test.ts +225 -225
- package/src/controllers/createChartController.ts +665 -665
- package/src/controllers/createDrawingController.ts +96 -96
- package/src/controllers/createIndicatorSelectorController.ts +307 -307
- package/src/controllers/createToolbarController.ts +146 -146
- package/src/controllers/index.ts +19 -19
- package/src/controllers/types.ts +284 -284
- package/src/engine/__tests__/chart.dpr.test.ts +401 -401
- package/src/engine/__tests__/paneRenderer.resize.test.ts +92 -92
- package/src/engine/chart-store.ts +121 -121
- package/src/engine/chart.d.ts +617 -617
- package/src/engine/chart.ts +2815 -2815
- package/src/engine/controller/__tests__/interaction.dpr.test.ts +259 -259
- package/src/engine/controller/interaction.ts +722 -722
- package/src/engine/controller/markerInteraction.ts +130 -130
- package/src/engine/controller/pinchTracker.ts +82 -82
- package/src/engine/controller/tooltipPosition.ts +48 -48
- package/src/engine/draw/__tests__/pixelAlign.spec.ts +176 -176
- package/src/engine/draw/pixelAlign.ts +259 -259
- package/src/engine/drawing/index.ts +655 -655
- package/src/engine/drawing/interaction.ts +842 -842
- package/src/engine/drawing/plugin.ts +343 -343
- package/src/engine/indicators/__tests__/__fixtures__/golden/atr.json +38 -38
- package/src/engine/indicators/__tests__/__fixtures__/golden/dema.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/hma.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/index.ts +55 -55
- package/src/engine/indicators/__tests__/__fixtures__/golden/kama.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/tema.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/wma.json +40 -40
- package/src/engine/indicators/__tests__/__fixtures__/synthetic.ts +65 -65
- package/src/engine/indicators/__tests__/_propertyAssertions.ts +76 -76
- package/src/engine/indicators/__tests__/atr.test.ts +153 -153
- package/src/engine/indicators/__tests__/calculators.test.ts +614 -614
- package/src/engine/indicators/__tests__/cmf-mfi.test.ts +100 -100
- package/src/engine/indicators/__tests__/dema.test.ts +73 -73
- package/src/engine/indicators/__tests__/donchian.test.ts +70 -70
- package/src/engine/indicators/__tests__/hma.test.ts +73 -73
- package/src/engine/indicators/__tests__/ichimoku.test.ts +105 -105
- package/src/engine/indicators/__tests__/kama.test.ts +80 -80
- package/src/engine/indicators/__tests__/keltner.test.ts +65 -65
- package/src/engine/indicators/__tests__/pivot-fib.test.ts +110 -110
- package/src/engine/indicators/__tests__/roc.test.ts +68 -68
- package/src/engine/indicators/__tests__/sar.test.ts +86 -86
- package/src/engine/indicators/__tests__/scheduler.test.ts +831 -831
- package/src/engine/indicators/__tests__/soa.test.ts +533 -533
- package/src/engine/indicators/__tests__/structure.test.ts +110 -110
- package/src/engine/indicators/__tests__/supertrend.test.ts +65 -65
- package/src/engine/indicators/__tests__/tema.test.ts +68 -68
- package/src/engine/indicators/__tests__/trix.test.ts +70 -70
- package/src/engine/indicators/__tests__/volatility.test.ts +117 -117
- package/src/engine/indicators/__tests__/volume.test.ts +115 -115
- package/src/engine/indicators/__tests__/volumeProfile.test.ts +74 -74
- package/src/engine/indicators/__tests__/vwap.test.ts +69 -69
- package/src/engine/indicators/__tests__/wma.test.ts +112 -112
- package/src/engine/indicators/__tests__/zones.test.ts +95 -95
- package/src/engine/indicators/atrState.ts +27 -27
- package/src/engine/indicators/bollState.ts +51 -51
- package/src/engine/indicators/calculators.ts +2593 -2593
- package/src/engine/indicators/cciState.ts +25 -25
- package/src/engine/indicators/chaikinVolState.ts +32 -32
- package/src/engine/indicators/cmfState.ts +27 -27
- package/src/engine/indicators/demaState.ts +27 -27
- package/src/engine/indicators/donchianState.ts +43 -43
- package/src/engine/indicators/eneState.ts +43 -43
- package/src/engine/indicators/expmaState.ts +43 -43
- package/src/engine/indicators/fastkState.ts +25 -25
- package/src/engine/indicators/fibState.ts +41 -41
- package/src/engine/indicators/hmaState.ts +27 -27
- package/src/engine/indicators/hvState.ts +28 -28
- package/src/engine/indicators/ichimokuState.ts +70 -70
- package/src/engine/indicators/indicator.worker.ts +169 -169
- package/src/engine/indicators/indicatorDefinitionRegistry.ts +62 -62
- package/src/engine/indicators/indicatorMetadata.ts +110 -110
- package/src/engine/indicators/indicatorRegistry.ts +106 -106
- package/src/engine/indicators/indicatorRuntime.ts +1548 -1548
- package/src/engine/indicators/kamaState.ts +34 -34
- package/src/engine/indicators/keltnerState.ts +49 -49
- package/src/engine/indicators/kstState.ts +42 -42
- package/src/engine/indicators/maState.ts +36 -36
- package/src/engine/indicators/macdState.ts +76 -76
- package/src/engine/indicators/mfiState.ts +27 -27
- package/src/engine/indicators/momState.ts +25 -25
- package/src/engine/indicators/obvState.ts +25 -25
- package/src/engine/indicators/parkinsonState.ts +28 -28
- package/src/engine/indicators/pivotState.ts +51 -51
- package/src/engine/indicators/pvtState.ts +25 -25
- package/src/engine/indicators/rocState.ts +27 -27
- package/src/engine/indicators/rsiState.ts +65 -65
- package/src/engine/indicators/sarState.ts +41 -41
- package/src/engine/indicators/scheduler.ts +1205 -1205
- package/src/engine/indicators/soa.ts +352 -352
- package/src/engine/indicators/stateComposer.ts +1262 -1262
- package/src/engine/indicators/stochState.ts +26 -26
- package/src/engine/indicators/structureState.ts +69 -69
- package/src/engine/indicators/supertrendState.ts +37 -37
- package/src/engine/indicators/temaState.ts +27 -27
- package/src/engine/indicators/trixState.ts +35 -35
- package/src/engine/indicators/vmaState.ts +27 -27
- package/src/engine/indicators/volumeProfileState.ts +63 -63
- package/src/engine/indicators/vwapState.ts +29 -29
- package/src/engine/indicators/wmaState.ts +27 -27
- package/src/engine/indicators/wmsrState.ts +25 -25
- package/src/engine/indicators/workerProtocol.ts +613 -613
- package/src/engine/indicators/zonesState.ts +47 -47
- package/src/engine/layout/pane.ts +161 -161
- package/src/engine/marker/registry.ts +265 -265
- package/src/engine/paneRenderer.ts +169 -169
- package/src/engine/renderers/Indicator/atr.ts +237 -237
- package/src/engine/renderers/Indicator/boll.ts +317 -317
- package/src/engine/renderers/Indicator/cci.ts +275 -275
- package/src/engine/renderers/Indicator/chaikinVol.ts +138 -138
- package/src/engine/renderers/Indicator/cmf.ts +137 -137
- package/src/engine/renderers/Indicator/dema.ts +136 -136
- package/src/engine/renderers/Indicator/donchian.ts +137 -137
- package/src/engine/renderers/Indicator/ene.ts +271 -271
- package/src/engine/renderers/Indicator/expma.ts +197 -197
- package/src/engine/renderers/Indicator/fastk.ts +316 -316
- package/src/engine/renderers/Indicator/fib.ts +141 -141
- package/src/engine/renderers/Indicator/hma.ts +136 -136
- package/src/engine/renderers/Indicator/hv.ts +124 -124
- package/src/engine/renderers/Indicator/ichimoku.ts +181 -181
- package/src/engine/renderers/Indicator/index.ts +241 -241
- package/src/engine/renderers/Indicator/indicatorData.ts +650 -650
- package/src/engine/renderers/Indicator/kama.ts +136 -136
- package/src/engine/renderers/Indicator/keltner.ts +137 -137
- package/src/engine/renderers/Indicator/kst.ts +302 -302
- package/src/engine/renderers/Indicator/ma.ts +200 -200
- package/src/engine/renderers/Indicator/macd.ts +477 -477
- package/src/engine/renderers/Indicator/macdLegend.ts +141 -141
- package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +272 -272
- package/src/engine/renderers/Indicator/mfi.ts +142 -142
- package/src/engine/renderers/Indicator/mom.ts +311 -311
- package/src/engine/renderers/Indicator/obv.ts +123 -123
- package/src/engine/renderers/Indicator/parkinson.ts +124 -124
- package/src/engine/renderers/Indicator/pivot.ts +131 -131
- package/src/engine/renderers/Indicator/pvt.ts +123 -123
- package/src/engine/renderers/Indicator/roc.ts +143 -143
- package/src/engine/renderers/Indicator/rsi.ts +390 -390
- package/src/engine/renderers/Indicator/sar.ts +113 -113
- package/src/engine/renderers/Indicator/scale/atr_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/cci_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/fastk_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/indicator_scale.ts +204 -204
- package/src/engine/renderers/Indicator/scale/kst_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/macd_scale.ts +22 -22
- package/src/engine/renderers/Indicator/scale/mom_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/rsi_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/stoch_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/volume_scale.ts +26 -26
- package/src/engine/renderers/Indicator/scale/wmsr_scale.ts +19 -19
- package/src/engine/renderers/Indicator/stoch.ts +359 -359
- package/src/engine/renderers/Indicator/structure.ts +126 -126
- package/src/engine/renderers/Indicator/subPaneConfig.ts +265 -265
- package/src/engine/renderers/Indicator/supertrend.ts +115 -115
- package/src/engine/renderers/Indicator/tema.ts +136 -136
- package/src/engine/renderers/Indicator/trix.ts +158 -158
- package/src/engine/renderers/Indicator/vma.ts +124 -124
- package/src/engine/renderers/Indicator/volumeProfile.ts +125 -125
- package/src/engine/renderers/Indicator/vwap.ts +123 -123
- package/src/engine/renderers/Indicator/wma.ts +136 -136
- package/src/engine/renderers/Indicator/wmsr.ts +328 -328
- package/src/engine/renderers/Indicator/zones.ts +104 -104
- package/src/engine/renderers/__tests__/boll.renderer.test.ts +314 -314
- package/src/engine/renderers/__tests__/ene.renderer.test.ts +305 -305
- package/src/engine/renderers/__tests__/expma.renderer.test.ts +279 -279
- package/src/engine/renderers/__tests__/ma.renderer.test.ts +426 -426
- package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +502 -502
- package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +173 -173
- package/src/engine/renderers/candle.ts +459 -459
- package/src/engine/renderers/crosshair.ts +69 -69
- package/src/engine/renderers/customMarkers.ts +162 -162
- package/src/engine/renderers/extremaMarkers.ts +246 -246
- package/src/engine/renderers/gridLines.ts +90 -90
- package/src/engine/renderers/lastPrice.ts +97 -97
- package/src/engine/renderers/paneTitle.ts +136 -136
- package/src/engine/renderers/subVolume.ts +236 -236
- package/src/engine/renderers/timeAxis.ts +121 -121
- package/src/engine/renderers/webgl/candleSurface.ts +955 -955
- package/src/engine/renderers/webgl/sharedWebGLSurface.ts +146 -146
- package/src/engine/renderers/yAxis.ts +105 -105
- package/src/engine/scale/__tests__/logFormula.spec.ts +148 -148
- package/src/engine/scale/logFormula.ts +130 -130
- package/src/engine/scale/price.ts +39 -39
- package/src/engine/scale/priceScale.ts +264 -264
- package/src/engine/subPaneManager.ts +427 -427
- package/src/engine/theme/colors.ts +642 -642
- package/src/engine/theme/fonts.ts +20 -20
- package/src/engine/utils/klineConfig.ts +49 -49
- package/src/engine/utils/tickCount.ts +11 -11
- package/src/engine/utils/tickPosition.ts +214 -214
- package/src/engine/utils/zoom.ts +83 -83
- package/src/engine/viewport/viewport.ts +67 -67
- package/src/index.ts +3 -3
- package/src/plugin/ConfigManager.ts +93 -93
- package/src/plugin/EventBus.ts +77 -77
- package/src/plugin/HookSystem.ts +106 -106
- package/src/plugin/PluginHost.ts +243 -243
- package/src/plugin/PluginRegistry.ts +92 -92
- package/src/plugin/StateStore.ts +73 -73
- package/src/plugin/index.ts +19 -19
- package/src/plugin/rendererPluginManager.ts +368 -368
- package/src/plugin/stateKeys.ts +8 -8
- package/src/plugin/types.ts +526 -526
- package/src/reactivity/index.ts +2 -2
- package/src/reactivity/signal.ts +119 -119
- package/src/semantic/controller.ts +251 -251
- package/src/semantic/drawShape.ts +260 -260
- package/src/semantic/index.ts +28 -28
- package/src/semantic/schema.json +256 -256
- package/src/semantic/types.ts +251 -251
- package/src/semantic/validator.ts +349 -349
- package/src/types/kLine.ts +13 -13
- package/src/types/price.ts +56 -56
- package/src/types/volumePrice.ts +33 -33
- package/src/utils/dateFormat.ts +208 -208
- package/src/utils/kLineDraw/axis.ts +562 -562
- package/src/utils/priceToY.ts +34 -34
- package/src/utils/volumePrice.ts +202 -202
- package/src/version.ts +1 -1
|
@@ -1,305 +1,305 @@
|
|
|
1
|
-
// @ts-nocheck - Test file with intentional type relaxations for mocking
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
3
|
-
import { createENERendererPlugin } from '../Indicator/ene'
|
|
4
|
-
import { ENE_STATE_KEY, type ENERenderState } from '@/core/indicators/eneState'
|
|
5
|
-
import { ENE_COLORS } from '@/core/theme/colors'
|
|
6
|
-
import type { PluginHost, RenderContext, RendererPluginWithHost } from '@/plugin'
|
|
7
|
-
import type { KLineData } from '@/types/price'
|
|
8
|
-
import type { Pane } from '@/core/layout/pane'
|
|
9
|
-
|
|
10
|
-
// Type helper for tests
|
|
11
|
-
interface TestableENERenderer extends RendererPluginWithHost {
|
|
12
|
-
draw: (context: RenderContext) => void
|
|
13
|
-
getConfig: () => Record<string, unknown>
|
|
14
|
-
setConfig: (config: Record<string, unknown>) => void
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function createMockCanvasContext(): CanvasRenderingContext2D {
|
|
18
|
-
return {
|
|
19
|
-
save: vi.fn(),
|
|
20
|
-
restore: vi.fn(),
|
|
21
|
-
translate: vi.fn(),
|
|
22
|
-
beginPath: vi.fn(),
|
|
23
|
-
moveTo: vi.fn(),
|
|
24
|
-
lineTo: vi.fn(),
|
|
25
|
-
stroke: vi.fn(),
|
|
26
|
-
fill: vi.fn(),
|
|
27
|
-
closePath: vi.fn(),
|
|
28
|
-
strokeStyle: '',
|
|
29
|
-
fillStyle: '',
|
|
30
|
-
lineWidth: 0,
|
|
31
|
-
lineJoin: '',
|
|
32
|
-
lineCap: '',
|
|
33
|
-
} as unknown as CanvasRenderingContext2D
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function createMockPluginHost(state?: ENERenderState): PluginHost {
|
|
37
|
-
return {
|
|
38
|
-
setSharedState: vi.fn(),
|
|
39
|
-
getSharedState: vi.fn(<T>(key: string): T | undefined => {
|
|
40
|
-
if (key === ENE_STATE_KEY) {
|
|
41
|
-
return state as T
|
|
42
|
-
}
|
|
43
|
-
return undefined
|
|
44
|
-
}),
|
|
45
|
-
clearByOwner: vi.fn(),
|
|
46
|
-
registerService: vi.fn(),
|
|
47
|
-
getService: vi.fn(<T>(name: string) => {
|
|
48
|
-
if (name === 'indicatorScheduler') {
|
|
49
|
-
return {
|
|
50
|
-
getIndicatorMetadata: (indicatorName: string) => {
|
|
51
|
-
if (indicatorName === 'ene') {
|
|
52
|
-
return { name: 'ene', stateKey: ENE_STATE_KEY }
|
|
53
|
-
}
|
|
54
|
-
return undefined
|
|
55
|
-
},
|
|
56
|
-
getAllIndicators: () => [],
|
|
57
|
-
} as T
|
|
58
|
-
}
|
|
59
|
-
return undefined
|
|
60
|
-
}),
|
|
61
|
-
getCanvas: vi.fn(),
|
|
62
|
-
getMainPane: vi.fn(),
|
|
63
|
-
getSubPane: vi.fn(),
|
|
64
|
-
getAllSubPanes: vi.fn(),
|
|
65
|
-
getTheme: vi.fn(),
|
|
66
|
-
getStyles: vi.fn(),
|
|
67
|
-
getBarStyles: vi.fn(),
|
|
68
|
-
getConfig: vi.fn(),
|
|
69
|
-
setConfig: vi.fn(),
|
|
70
|
-
on: vi.fn(),
|
|
71
|
-
off: vi.fn(),
|
|
72
|
-
once: vi.fn(),
|
|
73
|
-
emit: vi.fn(),
|
|
74
|
-
} as unknown as PluginHost
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function createMockRenderContext(
|
|
78
|
-
ctx: CanvasRenderingContext2D,
|
|
79
|
-
overrides: Partial<RenderContext> = {}
|
|
80
|
-
): RenderContext {
|
|
81
|
-
const mockPane = {
|
|
82
|
-
yAxis: {
|
|
83
|
-
priceToY: (price: number) => price * 10,
|
|
84
|
-
},
|
|
85
|
-
} as unknown as Pane
|
|
86
|
-
|
|
87
|
-
// Create default test data with sufficient length
|
|
88
|
-
const defaultData: KLineData[] = Array.from({ length: 100 }, (_, i) => ({
|
|
89
|
-
timestamp: 1000000000000 + i * 60000,
|
|
90
|
-
open: 100 + i,
|
|
91
|
-
high: 101 + i,
|
|
92
|
-
low: 99 + i,
|
|
93
|
-
close: 100 + i,
|
|
94
|
-
volume: 1000 + i * 100,
|
|
95
|
-
}))
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
ctx,
|
|
99
|
-
data: defaultData,
|
|
100
|
-
range: { start: 0, end: 10 },
|
|
101
|
-
visibleRange: { start: 0, end: 10 },
|
|
102
|
-
crosshair: null,
|
|
103
|
-
crosshairIndex: null,
|
|
104
|
-
dpr: 1,
|
|
105
|
-
scrollLeft: 0,
|
|
106
|
-
pane: mockPane,
|
|
107
|
-
kLineCenters: Array.from({ length: 100 }, (_, i) => i * 10 + 5),
|
|
108
|
-
...overrides,
|
|
109
|
-
} as RenderContext
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function createTestENERenderState(overrides: Partial<ENERenderState> = {}): ENERenderState {
|
|
113
|
-
return {
|
|
114
|
-
timestamp: Date.now(),
|
|
115
|
-
series: Array.from({ length: 100 }, (_, i) =>
|
|
116
|
-
i < 9
|
|
117
|
-
? undefined
|
|
118
|
-
: { upper: 111 + i * 0.1, middle: 100 + i * 0.1, lower: 89 + i * 0.1 }
|
|
119
|
-
),
|
|
120
|
-
params: {
|
|
121
|
-
period: 10,
|
|
122
|
-
deviation: 11,
|
|
123
|
-
},
|
|
124
|
-
visibleMin: 89,
|
|
125
|
-
visibleMax: 122,
|
|
126
|
-
...overrides,
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
describe('createENERendererPlugin', () => {
|
|
131
|
-
it('should create a renderer plugin with correct metadata', () => {
|
|
132
|
-
const plugin = createENERendererPlugin()
|
|
133
|
-
|
|
134
|
-
expect(plugin.name).toBe('ene')
|
|
135
|
-
expect(plugin.version).toBe('2.1.0')
|
|
136
|
-
expect(plugin.paneId).toBe('main')
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('should have onInstall method', () => {
|
|
140
|
-
const plugin = createENERendererPlugin()
|
|
141
|
-
expect(typeof plugin.onInstall).toBe('function')
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it('should declare ENE_STATE_KEY namespace', () => {
|
|
145
|
-
const plugin = createENERendererPlugin()
|
|
146
|
-
plugin.onInstall(createMockPluginHost())
|
|
147
|
-
expect(plugin.getDeclaredNamespaces()).toEqual([ENE_STATE_KEY])
|
|
148
|
-
})
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
describe('ENE renderer draw', () => {
|
|
152
|
-
let ctx: CanvasRenderingContext2D
|
|
153
|
-
let plugin: TestableENERenderer
|
|
154
|
-
|
|
155
|
-
beforeEach(() => {
|
|
156
|
-
ctx = createMockCanvasContext()
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
it('should not draw when StateStore has no ENE state', () => {
|
|
160
|
-
const mockHost = createMockPluginHost(undefined)
|
|
161
|
-
plugin = createENERendererPlugin() as TestableENERenderer
|
|
162
|
-
plugin.onInstall(mockHost)
|
|
163
|
-
|
|
164
|
-
const context = createMockRenderContext(ctx)
|
|
165
|
-
plugin.draw(context)
|
|
166
|
-
|
|
167
|
-
expect(ctx.beginPath).not.toHaveBeenCalled()
|
|
168
|
-
expect(ctx.stroke).not.toHaveBeenCalled()
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('should not draw when state has no valid data', () => {
|
|
172
|
-
const state = createTestENERenderState({
|
|
173
|
-
visibleMin: Infinity,
|
|
174
|
-
visibleMax: -Infinity,
|
|
175
|
-
})
|
|
176
|
-
const mockHost = createMockPluginHost(state)
|
|
177
|
-
plugin = createENERendererPlugin() as TestableENERenderer
|
|
178
|
-
plugin.onInstall(mockHost)
|
|
179
|
-
|
|
180
|
-
const context = createMockRenderContext(ctx)
|
|
181
|
-
plugin.draw(context)
|
|
182
|
-
|
|
183
|
-
expect(ctx.beginPath).not.toHaveBeenCalled()
|
|
184
|
-
expect(ctx.stroke).not.toHaveBeenCalled()
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('should save and restore context', () => {
|
|
188
|
-
const state = createTestENERenderState()
|
|
189
|
-
const mockHost = createMockPluginHost(state)
|
|
190
|
-
plugin = createENERendererPlugin() as TestableENERenderer
|
|
191
|
-
plugin.onInstall(mockHost)
|
|
192
|
-
|
|
193
|
-
const context = createMockRenderContext(ctx)
|
|
194
|
-
plugin.draw(context)
|
|
195
|
-
|
|
196
|
-
expect(ctx.save).toHaveBeenCalledTimes(1)
|
|
197
|
-
expect(ctx.restore).toHaveBeenCalledTimes(1)
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it('should draw band fill', () => {
|
|
201
|
-
const state = createTestENERenderState()
|
|
202
|
-
const mockHost = createMockPluginHost(state)
|
|
203
|
-
plugin = createENERendererPlugin() as TestableENERenderer
|
|
204
|
-
plugin.onInstall(mockHost)
|
|
205
|
-
|
|
206
|
-
const context = createMockRenderContext(ctx)
|
|
207
|
-
plugin.draw(context)
|
|
208
|
-
|
|
209
|
-
expect(ctx.fill).toHaveBeenCalled()
|
|
210
|
-
expect(ctx.closePath).toHaveBeenCalled()
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
it('should draw all three lines (upper, middle, lower)', () => {
|
|
214
|
-
const state = createTestENERenderState()
|
|
215
|
-
const mockHost = createMockPluginHost(state)
|
|
216
|
-
plugin = createENERendererPlugin() as TestableENERenderer
|
|
217
|
-
plugin.onInstall(mockHost)
|
|
218
|
-
|
|
219
|
-
const context = createMockRenderContext(ctx)
|
|
220
|
-
plugin.draw(context)
|
|
221
|
-
|
|
222
|
-
// Should have stroke calls for the three lines
|
|
223
|
-
expect(ctx.stroke).toHaveBeenCalled()
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
it('should use correct line styles', () => {
|
|
227
|
-
const state = createTestENERenderState()
|
|
228
|
-
const mockHost = createMockPluginHost(state)
|
|
229
|
-
plugin = createENERendererPlugin() as TestableENERenderer
|
|
230
|
-
plugin.onInstall(mockHost)
|
|
231
|
-
|
|
232
|
-
const context = createMockRenderContext(ctx)
|
|
233
|
-
plugin.draw(context)
|
|
234
|
-
|
|
235
|
-
expect(ctx.lineWidth).toBe(1)
|
|
236
|
-
expect(ctx.lineJoin).toBe('round')
|
|
237
|
-
expect(ctx.lineCap).toBe('round')
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('should use theme colors', () => {
|
|
241
|
-
const state = createTestENERenderState()
|
|
242
|
-
const mockHost = createMockPluginHost(state)
|
|
243
|
-
plugin = createENERendererPlugin() as TestableENERenderer
|
|
244
|
-
plugin.onInstall(mockHost)
|
|
245
|
-
|
|
246
|
-
const context = createMockRenderContext(ctx)
|
|
247
|
-
plugin.draw(context)
|
|
248
|
-
|
|
249
|
-
// Verify fillStyle was set to band fill color
|
|
250
|
-
expect(ctx.fillStyle).toBe(ENE_COLORS.BAND_FILL)
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
it('should skip undefined values at start of series', () => {
|
|
254
|
-
const state = createTestENERenderState({
|
|
255
|
-
series: Array.from({ length: 15 }, (_, i) =>
|
|
256
|
-
i < 9 ? undefined : { upper: 111, middle: 100, lower: 89 }
|
|
257
|
-
),
|
|
258
|
-
})
|
|
259
|
-
const mockHost = createMockPluginHost(state)
|
|
260
|
-
plugin = createENERendererPlugin() as TestableENERenderer
|
|
261
|
-
plugin.onInstall(mockHost)
|
|
262
|
-
|
|
263
|
-
const context = createMockRenderContext(ctx, { range: { start: 0, end: 15 } })
|
|
264
|
-
|
|
265
|
-
expect(() => plugin.draw(context)).not.toThrow()
|
|
266
|
-
expect(ctx.stroke).toHaveBeenCalled()
|
|
267
|
-
})
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
describe('ENE renderer config', () => {
|
|
271
|
-
it('getConfig should return current params from StateStore', () => {
|
|
272
|
-
const state = createTestENERenderState({
|
|
273
|
-
params: { period: 15, deviation: 15 },
|
|
274
|
-
})
|
|
275
|
-
const mockHost = createMockPluginHost(state)
|
|
276
|
-
const plugin = createENERendererPlugin() as TestableENERenderer
|
|
277
|
-
plugin.onInstall(mockHost)
|
|
278
|
-
|
|
279
|
-
const config = plugin.getConfig()
|
|
280
|
-
|
|
281
|
-
expect(config.period).toBe(15)
|
|
282
|
-
expect(config.deviation).toBe(15)
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
it('getConfig should return empty object when no state', () => {
|
|
286
|
-
const mockHost = createMockPluginHost(undefined)
|
|
287
|
-
const plugin = createENERendererPlugin() as TestableENERenderer
|
|
288
|
-
plugin.onInstall(mockHost)
|
|
289
|
-
|
|
290
|
-
const config = plugin.getConfig()
|
|
291
|
-
|
|
292
|
-
expect(config).toEqual({})
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
it('setConfig should be a no-op', () => {
|
|
296
|
-
const mockHost = createMockPluginHost(createTestENERenderState())
|
|
297
|
-
const plugin = createENERendererPlugin() as TestableENERenderer
|
|
298
|
-
plugin.onInstall(mockHost)
|
|
299
|
-
|
|
300
|
-
expect(() => plugin.setConfig({ period: 25 })).not.toThrow()
|
|
301
|
-
|
|
302
|
-
const config = plugin.getConfig()
|
|
303
|
-
expect(config.period).toBe(10) // Original value from state
|
|
304
|
-
})
|
|
305
|
-
})
|
|
1
|
+
// @ts-nocheck - Test file with intentional type relaxations for mocking
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
3
|
+
import { createENERendererPlugin } from '../Indicator/ene'
|
|
4
|
+
import { ENE_STATE_KEY, type ENERenderState } from '@/core/indicators/eneState'
|
|
5
|
+
import { ENE_COLORS } from '@/core/theme/colors'
|
|
6
|
+
import type { PluginHost, RenderContext, RendererPluginWithHost } from '@/plugin'
|
|
7
|
+
import type { KLineData } from '@/types/price'
|
|
8
|
+
import type { Pane } from '@/core/layout/pane'
|
|
9
|
+
|
|
10
|
+
// Type helper for tests
|
|
11
|
+
interface TestableENERenderer extends RendererPluginWithHost {
|
|
12
|
+
draw: (context: RenderContext) => void
|
|
13
|
+
getConfig: () => Record<string, unknown>
|
|
14
|
+
setConfig: (config: Record<string, unknown>) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createMockCanvasContext(): CanvasRenderingContext2D {
|
|
18
|
+
return {
|
|
19
|
+
save: vi.fn(),
|
|
20
|
+
restore: vi.fn(),
|
|
21
|
+
translate: vi.fn(),
|
|
22
|
+
beginPath: vi.fn(),
|
|
23
|
+
moveTo: vi.fn(),
|
|
24
|
+
lineTo: vi.fn(),
|
|
25
|
+
stroke: vi.fn(),
|
|
26
|
+
fill: vi.fn(),
|
|
27
|
+
closePath: vi.fn(),
|
|
28
|
+
strokeStyle: '',
|
|
29
|
+
fillStyle: '',
|
|
30
|
+
lineWidth: 0,
|
|
31
|
+
lineJoin: '',
|
|
32
|
+
lineCap: '',
|
|
33
|
+
} as unknown as CanvasRenderingContext2D
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function createMockPluginHost(state?: ENERenderState): PluginHost {
|
|
37
|
+
return {
|
|
38
|
+
setSharedState: vi.fn(),
|
|
39
|
+
getSharedState: vi.fn(<T>(key: string): T | undefined => {
|
|
40
|
+
if (key === ENE_STATE_KEY) {
|
|
41
|
+
return state as T
|
|
42
|
+
}
|
|
43
|
+
return undefined
|
|
44
|
+
}),
|
|
45
|
+
clearByOwner: vi.fn(),
|
|
46
|
+
registerService: vi.fn(),
|
|
47
|
+
getService: vi.fn(<T>(name: string) => {
|
|
48
|
+
if (name === 'indicatorScheduler') {
|
|
49
|
+
return {
|
|
50
|
+
getIndicatorMetadata: (indicatorName: string) => {
|
|
51
|
+
if (indicatorName === 'ene') {
|
|
52
|
+
return { name: 'ene', stateKey: ENE_STATE_KEY }
|
|
53
|
+
}
|
|
54
|
+
return undefined
|
|
55
|
+
},
|
|
56
|
+
getAllIndicators: () => [],
|
|
57
|
+
} as T
|
|
58
|
+
}
|
|
59
|
+
return undefined
|
|
60
|
+
}),
|
|
61
|
+
getCanvas: vi.fn(),
|
|
62
|
+
getMainPane: vi.fn(),
|
|
63
|
+
getSubPane: vi.fn(),
|
|
64
|
+
getAllSubPanes: vi.fn(),
|
|
65
|
+
getTheme: vi.fn(),
|
|
66
|
+
getStyles: vi.fn(),
|
|
67
|
+
getBarStyles: vi.fn(),
|
|
68
|
+
getConfig: vi.fn(),
|
|
69
|
+
setConfig: vi.fn(),
|
|
70
|
+
on: vi.fn(),
|
|
71
|
+
off: vi.fn(),
|
|
72
|
+
once: vi.fn(),
|
|
73
|
+
emit: vi.fn(),
|
|
74
|
+
} as unknown as PluginHost
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function createMockRenderContext(
|
|
78
|
+
ctx: CanvasRenderingContext2D,
|
|
79
|
+
overrides: Partial<RenderContext> = {}
|
|
80
|
+
): RenderContext {
|
|
81
|
+
const mockPane = {
|
|
82
|
+
yAxis: {
|
|
83
|
+
priceToY: (price: number) => price * 10,
|
|
84
|
+
},
|
|
85
|
+
} as unknown as Pane
|
|
86
|
+
|
|
87
|
+
// Create default test data with sufficient length
|
|
88
|
+
const defaultData: KLineData[] = Array.from({ length: 100 }, (_, i) => ({
|
|
89
|
+
timestamp: 1000000000000 + i * 60000,
|
|
90
|
+
open: 100 + i,
|
|
91
|
+
high: 101 + i,
|
|
92
|
+
low: 99 + i,
|
|
93
|
+
close: 100 + i,
|
|
94
|
+
volume: 1000 + i * 100,
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
ctx,
|
|
99
|
+
data: defaultData,
|
|
100
|
+
range: { start: 0, end: 10 },
|
|
101
|
+
visibleRange: { start: 0, end: 10 },
|
|
102
|
+
crosshair: null,
|
|
103
|
+
crosshairIndex: null,
|
|
104
|
+
dpr: 1,
|
|
105
|
+
scrollLeft: 0,
|
|
106
|
+
pane: mockPane,
|
|
107
|
+
kLineCenters: Array.from({ length: 100 }, (_, i) => i * 10 + 5),
|
|
108
|
+
...overrides,
|
|
109
|
+
} as RenderContext
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function createTestENERenderState(overrides: Partial<ENERenderState> = {}): ENERenderState {
|
|
113
|
+
return {
|
|
114
|
+
timestamp: Date.now(),
|
|
115
|
+
series: Array.from({ length: 100 }, (_, i) =>
|
|
116
|
+
i < 9
|
|
117
|
+
? undefined
|
|
118
|
+
: { upper: 111 + i * 0.1, middle: 100 + i * 0.1, lower: 89 + i * 0.1 }
|
|
119
|
+
),
|
|
120
|
+
params: {
|
|
121
|
+
period: 10,
|
|
122
|
+
deviation: 11,
|
|
123
|
+
},
|
|
124
|
+
visibleMin: 89,
|
|
125
|
+
visibleMax: 122,
|
|
126
|
+
...overrides,
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
describe('createENERendererPlugin', () => {
|
|
131
|
+
it('should create a renderer plugin with correct metadata', () => {
|
|
132
|
+
const plugin = createENERendererPlugin()
|
|
133
|
+
|
|
134
|
+
expect(plugin.name).toBe('ene')
|
|
135
|
+
expect(plugin.version).toBe('2.1.0')
|
|
136
|
+
expect(plugin.paneId).toBe('main')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should have onInstall method', () => {
|
|
140
|
+
const plugin = createENERendererPlugin()
|
|
141
|
+
expect(typeof plugin.onInstall).toBe('function')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should declare ENE_STATE_KEY namespace', () => {
|
|
145
|
+
const plugin = createENERendererPlugin()
|
|
146
|
+
plugin.onInstall(createMockPluginHost())
|
|
147
|
+
expect(plugin.getDeclaredNamespaces()).toEqual([ENE_STATE_KEY])
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
describe('ENE renderer draw', () => {
|
|
152
|
+
let ctx: CanvasRenderingContext2D
|
|
153
|
+
let plugin: TestableENERenderer
|
|
154
|
+
|
|
155
|
+
beforeEach(() => {
|
|
156
|
+
ctx = createMockCanvasContext()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should not draw when StateStore has no ENE state', () => {
|
|
160
|
+
const mockHost = createMockPluginHost(undefined)
|
|
161
|
+
plugin = createENERendererPlugin() as TestableENERenderer
|
|
162
|
+
plugin.onInstall(mockHost)
|
|
163
|
+
|
|
164
|
+
const context = createMockRenderContext(ctx)
|
|
165
|
+
plugin.draw(context)
|
|
166
|
+
|
|
167
|
+
expect(ctx.beginPath).not.toHaveBeenCalled()
|
|
168
|
+
expect(ctx.stroke).not.toHaveBeenCalled()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('should not draw when state has no valid data', () => {
|
|
172
|
+
const state = createTestENERenderState({
|
|
173
|
+
visibleMin: Infinity,
|
|
174
|
+
visibleMax: -Infinity,
|
|
175
|
+
})
|
|
176
|
+
const mockHost = createMockPluginHost(state)
|
|
177
|
+
plugin = createENERendererPlugin() as TestableENERenderer
|
|
178
|
+
plugin.onInstall(mockHost)
|
|
179
|
+
|
|
180
|
+
const context = createMockRenderContext(ctx)
|
|
181
|
+
plugin.draw(context)
|
|
182
|
+
|
|
183
|
+
expect(ctx.beginPath).not.toHaveBeenCalled()
|
|
184
|
+
expect(ctx.stroke).not.toHaveBeenCalled()
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should save and restore context', () => {
|
|
188
|
+
const state = createTestENERenderState()
|
|
189
|
+
const mockHost = createMockPluginHost(state)
|
|
190
|
+
plugin = createENERendererPlugin() as TestableENERenderer
|
|
191
|
+
plugin.onInstall(mockHost)
|
|
192
|
+
|
|
193
|
+
const context = createMockRenderContext(ctx)
|
|
194
|
+
plugin.draw(context)
|
|
195
|
+
|
|
196
|
+
expect(ctx.save).toHaveBeenCalledTimes(1)
|
|
197
|
+
expect(ctx.restore).toHaveBeenCalledTimes(1)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('should draw band fill', () => {
|
|
201
|
+
const state = createTestENERenderState()
|
|
202
|
+
const mockHost = createMockPluginHost(state)
|
|
203
|
+
plugin = createENERendererPlugin() as TestableENERenderer
|
|
204
|
+
plugin.onInstall(mockHost)
|
|
205
|
+
|
|
206
|
+
const context = createMockRenderContext(ctx)
|
|
207
|
+
plugin.draw(context)
|
|
208
|
+
|
|
209
|
+
expect(ctx.fill).toHaveBeenCalled()
|
|
210
|
+
expect(ctx.closePath).toHaveBeenCalled()
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('should draw all three lines (upper, middle, lower)', () => {
|
|
214
|
+
const state = createTestENERenderState()
|
|
215
|
+
const mockHost = createMockPluginHost(state)
|
|
216
|
+
plugin = createENERendererPlugin() as TestableENERenderer
|
|
217
|
+
plugin.onInstall(mockHost)
|
|
218
|
+
|
|
219
|
+
const context = createMockRenderContext(ctx)
|
|
220
|
+
plugin.draw(context)
|
|
221
|
+
|
|
222
|
+
// Should have stroke calls for the three lines
|
|
223
|
+
expect(ctx.stroke).toHaveBeenCalled()
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('should use correct line styles', () => {
|
|
227
|
+
const state = createTestENERenderState()
|
|
228
|
+
const mockHost = createMockPluginHost(state)
|
|
229
|
+
plugin = createENERendererPlugin() as TestableENERenderer
|
|
230
|
+
plugin.onInstall(mockHost)
|
|
231
|
+
|
|
232
|
+
const context = createMockRenderContext(ctx)
|
|
233
|
+
plugin.draw(context)
|
|
234
|
+
|
|
235
|
+
expect(ctx.lineWidth).toBe(1)
|
|
236
|
+
expect(ctx.lineJoin).toBe('round')
|
|
237
|
+
expect(ctx.lineCap).toBe('round')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('should use theme colors', () => {
|
|
241
|
+
const state = createTestENERenderState()
|
|
242
|
+
const mockHost = createMockPluginHost(state)
|
|
243
|
+
plugin = createENERendererPlugin() as TestableENERenderer
|
|
244
|
+
plugin.onInstall(mockHost)
|
|
245
|
+
|
|
246
|
+
const context = createMockRenderContext(ctx)
|
|
247
|
+
plugin.draw(context)
|
|
248
|
+
|
|
249
|
+
// Verify fillStyle was set to band fill color
|
|
250
|
+
expect(ctx.fillStyle).toBe(ENE_COLORS.BAND_FILL)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('should skip undefined values at start of series', () => {
|
|
254
|
+
const state = createTestENERenderState({
|
|
255
|
+
series: Array.from({ length: 15 }, (_, i) =>
|
|
256
|
+
i < 9 ? undefined : { upper: 111, middle: 100, lower: 89 }
|
|
257
|
+
),
|
|
258
|
+
})
|
|
259
|
+
const mockHost = createMockPluginHost(state)
|
|
260
|
+
plugin = createENERendererPlugin() as TestableENERenderer
|
|
261
|
+
plugin.onInstall(mockHost)
|
|
262
|
+
|
|
263
|
+
const context = createMockRenderContext(ctx, { range: { start: 0, end: 15 } })
|
|
264
|
+
|
|
265
|
+
expect(() => plugin.draw(context)).not.toThrow()
|
|
266
|
+
expect(ctx.stroke).toHaveBeenCalled()
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
describe('ENE renderer config', () => {
|
|
271
|
+
it('getConfig should return current params from StateStore', () => {
|
|
272
|
+
const state = createTestENERenderState({
|
|
273
|
+
params: { period: 15, deviation: 15 },
|
|
274
|
+
})
|
|
275
|
+
const mockHost = createMockPluginHost(state)
|
|
276
|
+
const plugin = createENERendererPlugin() as TestableENERenderer
|
|
277
|
+
plugin.onInstall(mockHost)
|
|
278
|
+
|
|
279
|
+
const config = plugin.getConfig()
|
|
280
|
+
|
|
281
|
+
expect(config.period).toBe(15)
|
|
282
|
+
expect(config.deviation).toBe(15)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('getConfig should return empty object when no state', () => {
|
|
286
|
+
const mockHost = createMockPluginHost(undefined)
|
|
287
|
+
const plugin = createENERendererPlugin() as TestableENERenderer
|
|
288
|
+
plugin.onInstall(mockHost)
|
|
289
|
+
|
|
290
|
+
const config = plugin.getConfig()
|
|
291
|
+
|
|
292
|
+
expect(config).toEqual({})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('setConfig should be a no-op', () => {
|
|
296
|
+
const mockHost = createMockPluginHost(createTestENERenderState())
|
|
297
|
+
const plugin = createENERendererPlugin() as TestableENERenderer
|
|
298
|
+
plugin.onInstall(mockHost)
|
|
299
|
+
|
|
300
|
+
expect(() => plugin.setConfig({ period: 25 })).not.toThrow()
|
|
301
|
+
|
|
302
|
+
const config = plugin.getConfig()
|
|
303
|
+
expect(config.period).toBe(10) // Original value from state
|
|
304
|
+
})
|
|
305
|
+
})
|