@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,279 +1,279 @@
|
|
|
1
|
-
// @ts-nocheck - Test file with intentional type relaxations for mocking
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
3
|
-
import { createEXPMARendererPlugin } from '../Indicator/expma'
|
|
4
|
-
import { EXPMA_STATE_KEY, type EXPMARenderState } from '@/core/indicators/expmaState'
|
|
5
|
-
import { EXPMA_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 TestableEXPMARenderer 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
|
-
strokeStyle: '',
|
|
27
|
-
lineWidth: 0,
|
|
28
|
-
lineJoin: '',
|
|
29
|
-
lineCap: '',
|
|
30
|
-
} as unknown as CanvasRenderingContext2D
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function createMockPluginHost(state?: EXPMARenderState): PluginHost {
|
|
34
|
-
return {
|
|
35
|
-
setSharedState: vi.fn(),
|
|
36
|
-
getSharedState: vi.fn(<T>(key: string): T | undefined => {
|
|
37
|
-
if (key === EXPMA_STATE_KEY) {
|
|
38
|
-
return state as T
|
|
39
|
-
}
|
|
40
|
-
return undefined
|
|
41
|
-
}),
|
|
42
|
-
clearByOwner: vi.fn(),
|
|
43
|
-
registerService: vi.fn(),
|
|
44
|
-
getService: vi.fn(<T>(name: string) => {
|
|
45
|
-
if (name === 'indicatorScheduler') {
|
|
46
|
-
return {
|
|
47
|
-
getIndicatorMetadata: (indicatorName: string) => {
|
|
48
|
-
if (indicatorName === 'expma') {
|
|
49
|
-
return { name: 'expma', stateKey: EXPMA_STATE_KEY }
|
|
50
|
-
}
|
|
51
|
-
return undefined
|
|
52
|
-
},
|
|
53
|
-
getAllIndicators: () => [],
|
|
54
|
-
} as T
|
|
55
|
-
}
|
|
56
|
-
return undefined
|
|
57
|
-
}),
|
|
58
|
-
getCanvas: vi.fn(),
|
|
59
|
-
getMainPane: vi.fn(),
|
|
60
|
-
getSubPane: vi.fn(),
|
|
61
|
-
getAllSubPanes: vi.fn(),
|
|
62
|
-
getTheme: vi.fn(),
|
|
63
|
-
getStyles: vi.fn(),
|
|
64
|
-
getBarStyles: vi.fn(),
|
|
65
|
-
getConfig: vi.fn(),
|
|
66
|
-
setConfig: vi.fn(),
|
|
67
|
-
on: vi.fn(),
|
|
68
|
-
off: vi.fn(),
|
|
69
|
-
once: vi.fn(),
|
|
70
|
-
emit: vi.fn(),
|
|
71
|
-
} as unknown as PluginHost
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function createMockRenderContext(
|
|
75
|
-
ctx: CanvasRenderingContext2D,
|
|
76
|
-
overrides: Partial<RenderContext> = {}
|
|
77
|
-
): RenderContext {
|
|
78
|
-
const mockPane = {
|
|
79
|
-
height: 200,
|
|
80
|
-
yAxis: {
|
|
81
|
-
priceToY: (price: number) => price * 10,
|
|
82
|
-
getDisplayRange: () => ({ minPrice: 0, maxPrice: 200 }),
|
|
83
|
-
getPriceOffset: () => 0,
|
|
84
|
-
getScaleType: () => 'linear',
|
|
85
|
-
},
|
|
86
|
-
} as unknown as Pane
|
|
87
|
-
|
|
88
|
-
// Create default test data with sufficient length
|
|
89
|
-
const defaultData: KLineData[] = Array.from({ length: 100 }, (_, i) => ({
|
|
90
|
-
timestamp: 1000000000000 + i * 60000,
|
|
91
|
-
open: 100 + i,
|
|
92
|
-
high: 101 + i,
|
|
93
|
-
low: 99 + i,
|
|
94
|
-
close: 100 + i,
|
|
95
|
-
volume: 1000 + i * 100,
|
|
96
|
-
}))
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
ctx,
|
|
100
|
-
data: defaultData,
|
|
101
|
-
range: { start: 0, end: 10 },
|
|
102
|
-
visibleRange: { start: 0, end: 10 },
|
|
103
|
-
crosshair: null,
|
|
104
|
-
crosshairIndex: null,
|
|
105
|
-
dpr: 1,
|
|
106
|
-
scrollLeft: 0,
|
|
107
|
-
pane: mockPane,
|
|
108
|
-
kLineCenters: Array.from({ length: 100 }, (_, i) => i * 10 + 5),
|
|
109
|
-
...overrides,
|
|
110
|
-
} as RenderContext
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function createTestEXPMARenderState(overrides: Partial<EXPMARenderState> = {}): EXPMARenderState {
|
|
114
|
-
return {
|
|
115
|
-
timestamp: Date.now(),
|
|
116
|
-
series: Array.from({ length: 100 }, (_, i) => ({
|
|
117
|
-
fast: 100 + i * 0.2,
|
|
118
|
-
slow: 100 + i * 0.1,
|
|
119
|
-
})),
|
|
120
|
-
params: {
|
|
121
|
-
fastPeriod: 12,
|
|
122
|
-
slowPeriod: 50,
|
|
123
|
-
},
|
|
124
|
-
visibleMin: 100,
|
|
125
|
-
visibleMax: 120,
|
|
126
|
-
...overrides,
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
describe('createEXPMARendererPlugin', () => {
|
|
131
|
-
it('should create a renderer plugin with correct metadata', () => {
|
|
132
|
-
const plugin = createEXPMARendererPlugin()
|
|
133
|
-
|
|
134
|
-
expect(plugin.name).toBe('expma')
|
|
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 = createEXPMARendererPlugin()
|
|
141
|
-
expect(typeof plugin.onInstall).toBe('function')
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it('should declare EXPMA_STATE_KEY namespace', () => {
|
|
145
|
-
const plugin = createEXPMARendererPlugin()
|
|
146
|
-
plugin.onInstall(createMockPluginHost())
|
|
147
|
-
expect(plugin.getDeclaredNamespaces()).toEqual([EXPMA_STATE_KEY])
|
|
148
|
-
})
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
describe('EXPMA renderer draw', () => {
|
|
152
|
-
let ctx: CanvasRenderingContext2D
|
|
153
|
-
let plugin: TestableEXPMARenderer
|
|
154
|
-
|
|
155
|
-
beforeEach(() => {
|
|
156
|
-
ctx = createMockCanvasContext()
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
it('should not draw when StateStore has no EXPMA state', () => {
|
|
160
|
-
const mockHost = createMockPluginHost(undefined)
|
|
161
|
-
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
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 = createTestEXPMARenderState({
|
|
173
|
-
visibleMin: Infinity,
|
|
174
|
-
visibleMax: -Infinity,
|
|
175
|
-
})
|
|
176
|
-
const mockHost = createMockPluginHost(state)
|
|
177
|
-
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
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 = createTestEXPMARenderState()
|
|
189
|
-
const mockHost = createMockPluginHost(state)
|
|
190
|
-
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
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 both fast and slow lines', () => {
|
|
201
|
-
const state = createTestEXPMARenderState()
|
|
202
|
-
const mockHost = createMockPluginHost(state)
|
|
203
|
-
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
204
|
-
plugin.onInstall(mockHost)
|
|
205
|
-
|
|
206
|
-
const context = createMockRenderContext(ctx)
|
|
207
|
-
plugin.draw(context)
|
|
208
|
-
|
|
209
|
-
// Should have stroke calls for both lines
|
|
210
|
-
expect(ctx.stroke).toHaveBeenCalled()
|
|
211
|
-
expect(ctx.beginPath).toHaveBeenCalled()
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
it('should use correct line styles', () => {
|
|
215
|
-
const state = createTestEXPMARenderState()
|
|
216
|
-
const mockHost = createMockPluginHost(state)
|
|
217
|
-
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
218
|
-
plugin.onInstall(mockHost)
|
|
219
|
-
|
|
220
|
-
const context = createMockRenderContext(ctx)
|
|
221
|
-
plugin.draw(context)
|
|
222
|
-
|
|
223
|
-
expect(ctx.lineWidth).toBe(1)
|
|
224
|
-
expect(ctx.lineJoin).toBe('round')
|
|
225
|
-
expect(ctx.lineCap).toBe('round')
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
it('should draw from index 0 (dense array)', () => {
|
|
229
|
-
const state = createTestEXPMARenderState({
|
|
230
|
-
series: Array.from({ length: 10 }, (_, i) => ({ fast: 100 + i, slow: 100 + i * 0.5 })),
|
|
231
|
-
})
|
|
232
|
-
const mockHost = createMockPluginHost(state)
|
|
233
|
-
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
234
|
-
plugin.onInstall(mockHost)
|
|
235
|
-
|
|
236
|
-
const context = createMockRenderContext(ctx, { range: { start: 0, end: 10 } })
|
|
237
|
-
plugin.draw(context)
|
|
238
|
-
|
|
239
|
-
// EXPMA draws from range.start (0 for dense array)
|
|
240
|
-
expect(ctx.beginPath).toHaveBeenCalled()
|
|
241
|
-
})
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
describe('EXPMA renderer config', () => {
|
|
245
|
-
it('getConfig should return current params from StateStore', () => {
|
|
246
|
-
const state = createTestEXPMARenderState({
|
|
247
|
-
params: { fastPeriod: 20, slowPeriod: 60 },
|
|
248
|
-
})
|
|
249
|
-
const mockHost = createMockPluginHost(state)
|
|
250
|
-
const plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
251
|
-
plugin.onInstall(mockHost)
|
|
252
|
-
|
|
253
|
-
const config = plugin.getConfig()
|
|
254
|
-
|
|
255
|
-
expect(config.fastPeriod).toBe(20)
|
|
256
|
-
expect(config.slowPeriod).toBe(60)
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
it('getConfig should return empty object when no state', () => {
|
|
260
|
-
const mockHost = createMockPluginHost(undefined)
|
|
261
|
-
const plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
262
|
-
plugin.onInstall(mockHost)
|
|
263
|
-
|
|
264
|
-
const config = plugin.getConfig()
|
|
265
|
-
|
|
266
|
-
expect(config).toEqual({})
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
it('setConfig should be a no-op', () => {
|
|
270
|
-
const mockHost = createMockPluginHost(createTestEXPMARenderState())
|
|
271
|
-
const plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
272
|
-
plugin.onInstall(mockHost)
|
|
273
|
-
|
|
274
|
-
expect(() => plugin.setConfig({ fastPeriod: 30 })).not.toThrow()
|
|
275
|
-
|
|
276
|
-
const config = plugin.getConfig()
|
|
277
|
-
expect(config.fastPeriod).toBe(12) // Original value from state
|
|
278
|
-
})
|
|
279
|
-
})
|
|
1
|
+
// @ts-nocheck - Test file with intentional type relaxations for mocking
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
3
|
+
import { createEXPMARendererPlugin } from '../Indicator/expma'
|
|
4
|
+
import { EXPMA_STATE_KEY, type EXPMARenderState } from '@/core/indicators/expmaState'
|
|
5
|
+
import { EXPMA_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 TestableEXPMARenderer 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
|
+
strokeStyle: '',
|
|
27
|
+
lineWidth: 0,
|
|
28
|
+
lineJoin: '',
|
|
29
|
+
lineCap: '',
|
|
30
|
+
} as unknown as CanvasRenderingContext2D
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createMockPluginHost(state?: EXPMARenderState): PluginHost {
|
|
34
|
+
return {
|
|
35
|
+
setSharedState: vi.fn(),
|
|
36
|
+
getSharedState: vi.fn(<T>(key: string): T | undefined => {
|
|
37
|
+
if (key === EXPMA_STATE_KEY) {
|
|
38
|
+
return state as T
|
|
39
|
+
}
|
|
40
|
+
return undefined
|
|
41
|
+
}),
|
|
42
|
+
clearByOwner: vi.fn(),
|
|
43
|
+
registerService: vi.fn(),
|
|
44
|
+
getService: vi.fn(<T>(name: string) => {
|
|
45
|
+
if (name === 'indicatorScheduler') {
|
|
46
|
+
return {
|
|
47
|
+
getIndicatorMetadata: (indicatorName: string) => {
|
|
48
|
+
if (indicatorName === 'expma') {
|
|
49
|
+
return { name: 'expma', stateKey: EXPMA_STATE_KEY }
|
|
50
|
+
}
|
|
51
|
+
return undefined
|
|
52
|
+
},
|
|
53
|
+
getAllIndicators: () => [],
|
|
54
|
+
} as T
|
|
55
|
+
}
|
|
56
|
+
return undefined
|
|
57
|
+
}),
|
|
58
|
+
getCanvas: vi.fn(),
|
|
59
|
+
getMainPane: vi.fn(),
|
|
60
|
+
getSubPane: vi.fn(),
|
|
61
|
+
getAllSubPanes: vi.fn(),
|
|
62
|
+
getTheme: vi.fn(),
|
|
63
|
+
getStyles: vi.fn(),
|
|
64
|
+
getBarStyles: vi.fn(),
|
|
65
|
+
getConfig: vi.fn(),
|
|
66
|
+
setConfig: vi.fn(),
|
|
67
|
+
on: vi.fn(),
|
|
68
|
+
off: vi.fn(),
|
|
69
|
+
once: vi.fn(),
|
|
70
|
+
emit: vi.fn(),
|
|
71
|
+
} as unknown as PluginHost
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createMockRenderContext(
|
|
75
|
+
ctx: CanvasRenderingContext2D,
|
|
76
|
+
overrides: Partial<RenderContext> = {}
|
|
77
|
+
): RenderContext {
|
|
78
|
+
const mockPane = {
|
|
79
|
+
height: 200,
|
|
80
|
+
yAxis: {
|
|
81
|
+
priceToY: (price: number) => price * 10,
|
|
82
|
+
getDisplayRange: () => ({ minPrice: 0, maxPrice: 200 }),
|
|
83
|
+
getPriceOffset: () => 0,
|
|
84
|
+
getScaleType: () => 'linear',
|
|
85
|
+
},
|
|
86
|
+
} as unknown as Pane
|
|
87
|
+
|
|
88
|
+
// Create default test data with sufficient length
|
|
89
|
+
const defaultData: KLineData[] = Array.from({ length: 100 }, (_, i) => ({
|
|
90
|
+
timestamp: 1000000000000 + i * 60000,
|
|
91
|
+
open: 100 + i,
|
|
92
|
+
high: 101 + i,
|
|
93
|
+
low: 99 + i,
|
|
94
|
+
close: 100 + i,
|
|
95
|
+
volume: 1000 + i * 100,
|
|
96
|
+
}))
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
ctx,
|
|
100
|
+
data: defaultData,
|
|
101
|
+
range: { start: 0, end: 10 },
|
|
102
|
+
visibleRange: { start: 0, end: 10 },
|
|
103
|
+
crosshair: null,
|
|
104
|
+
crosshairIndex: null,
|
|
105
|
+
dpr: 1,
|
|
106
|
+
scrollLeft: 0,
|
|
107
|
+
pane: mockPane,
|
|
108
|
+
kLineCenters: Array.from({ length: 100 }, (_, i) => i * 10 + 5),
|
|
109
|
+
...overrides,
|
|
110
|
+
} as RenderContext
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function createTestEXPMARenderState(overrides: Partial<EXPMARenderState> = {}): EXPMARenderState {
|
|
114
|
+
return {
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
series: Array.from({ length: 100 }, (_, i) => ({
|
|
117
|
+
fast: 100 + i * 0.2,
|
|
118
|
+
slow: 100 + i * 0.1,
|
|
119
|
+
})),
|
|
120
|
+
params: {
|
|
121
|
+
fastPeriod: 12,
|
|
122
|
+
slowPeriod: 50,
|
|
123
|
+
},
|
|
124
|
+
visibleMin: 100,
|
|
125
|
+
visibleMax: 120,
|
|
126
|
+
...overrides,
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
describe('createEXPMARendererPlugin', () => {
|
|
131
|
+
it('should create a renderer plugin with correct metadata', () => {
|
|
132
|
+
const plugin = createEXPMARendererPlugin()
|
|
133
|
+
|
|
134
|
+
expect(plugin.name).toBe('expma')
|
|
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 = createEXPMARendererPlugin()
|
|
141
|
+
expect(typeof plugin.onInstall).toBe('function')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should declare EXPMA_STATE_KEY namespace', () => {
|
|
145
|
+
const plugin = createEXPMARendererPlugin()
|
|
146
|
+
plugin.onInstall(createMockPluginHost())
|
|
147
|
+
expect(plugin.getDeclaredNamespaces()).toEqual([EXPMA_STATE_KEY])
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
describe('EXPMA renderer draw', () => {
|
|
152
|
+
let ctx: CanvasRenderingContext2D
|
|
153
|
+
let plugin: TestableEXPMARenderer
|
|
154
|
+
|
|
155
|
+
beforeEach(() => {
|
|
156
|
+
ctx = createMockCanvasContext()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should not draw when StateStore has no EXPMA state', () => {
|
|
160
|
+
const mockHost = createMockPluginHost(undefined)
|
|
161
|
+
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
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 = createTestEXPMARenderState({
|
|
173
|
+
visibleMin: Infinity,
|
|
174
|
+
visibleMax: -Infinity,
|
|
175
|
+
})
|
|
176
|
+
const mockHost = createMockPluginHost(state)
|
|
177
|
+
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
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 = createTestEXPMARenderState()
|
|
189
|
+
const mockHost = createMockPluginHost(state)
|
|
190
|
+
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
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 both fast and slow lines', () => {
|
|
201
|
+
const state = createTestEXPMARenderState()
|
|
202
|
+
const mockHost = createMockPluginHost(state)
|
|
203
|
+
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
204
|
+
plugin.onInstall(mockHost)
|
|
205
|
+
|
|
206
|
+
const context = createMockRenderContext(ctx)
|
|
207
|
+
plugin.draw(context)
|
|
208
|
+
|
|
209
|
+
// Should have stroke calls for both lines
|
|
210
|
+
expect(ctx.stroke).toHaveBeenCalled()
|
|
211
|
+
expect(ctx.beginPath).toHaveBeenCalled()
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('should use correct line styles', () => {
|
|
215
|
+
const state = createTestEXPMARenderState()
|
|
216
|
+
const mockHost = createMockPluginHost(state)
|
|
217
|
+
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
218
|
+
plugin.onInstall(mockHost)
|
|
219
|
+
|
|
220
|
+
const context = createMockRenderContext(ctx)
|
|
221
|
+
plugin.draw(context)
|
|
222
|
+
|
|
223
|
+
expect(ctx.lineWidth).toBe(1)
|
|
224
|
+
expect(ctx.lineJoin).toBe('round')
|
|
225
|
+
expect(ctx.lineCap).toBe('round')
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('should draw from index 0 (dense array)', () => {
|
|
229
|
+
const state = createTestEXPMARenderState({
|
|
230
|
+
series: Array.from({ length: 10 }, (_, i) => ({ fast: 100 + i, slow: 100 + i * 0.5 })),
|
|
231
|
+
})
|
|
232
|
+
const mockHost = createMockPluginHost(state)
|
|
233
|
+
plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
234
|
+
plugin.onInstall(mockHost)
|
|
235
|
+
|
|
236
|
+
const context = createMockRenderContext(ctx, { range: { start: 0, end: 10 } })
|
|
237
|
+
plugin.draw(context)
|
|
238
|
+
|
|
239
|
+
// EXPMA draws from range.start (0 for dense array)
|
|
240
|
+
expect(ctx.beginPath).toHaveBeenCalled()
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
describe('EXPMA renderer config', () => {
|
|
245
|
+
it('getConfig should return current params from StateStore', () => {
|
|
246
|
+
const state = createTestEXPMARenderState({
|
|
247
|
+
params: { fastPeriod: 20, slowPeriod: 60 },
|
|
248
|
+
})
|
|
249
|
+
const mockHost = createMockPluginHost(state)
|
|
250
|
+
const plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
251
|
+
plugin.onInstall(mockHost)
|
|
252
|
+
|
|
253
|
+
const config = plugin.getConfig()
|
|
254
|
+
|
|
255
|
+
expect(config.fastPeriod).toBe(20)
|
|
256
|
+
expect(config.slowPeriod).toBe(60)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('getConfig should return empty object when no state', () => {
|
|
260
|
+
const mockHost = createMockPluginHost(undefined)
|
|
261
|
+
const plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
262
|
+
plugin.onInstall(mockHost)
|
|
263
|
+
|
|
264
|
+
const config = plugin.getConfig()
|
|
265
|
+
|
|
266
|
+
expect(config).toEqual({})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('setConfig should be a no-op', () => {
|
|
270
|
+
const mockHost = createMockPluginHost(createTestEXPMARenderState())
|
|
271
|
+
const plugin = createEXPMARendererPlugin() as TestableEXPMARenderer
|
|
272
|
+
plugin.onInstall(mockHost)
|
|
273
|
+
|
|
274
|
+
expect(() => plugin.setConfig({ fastPeriod: 30 })).not.toThrow()
|
|
275
|
+
|
|
276
|
+
const config = plugin.getConfig()
|
|
277
|
+
expect(config.fastPeriod).toBe(12) // Original value from state
|
|
278
|
+
})
|
|
279
|
+
})
|