@363045841yyt/klinechart 0.7.4 → 0.7.5

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.
@@ -1,192 +1,192 @@
1
- /**
2
- * Mock ChartController for Vue adapter tests.
3
- *
4
- * Mirrors the framework-agnostic signal-bearing shape from
5
- * @363045841yyt/klinechart-core without spinning up the real Chart engine.
6
- *
7
- * To keep this test file runnable from the repo root vitest (which does not
8
- * alias @363045841yyt/klinechart-core), we inline a tiny `Signal` implementation
9
- * that is shape-compatible with `packages/core/src/reactivity/signal.ts`.
10
- */
11
-
12
- import type { Signal } from '@363045841yyt/klinechart-core/reactivity'
13
- import type {
14
- ActiveIndicator,
15
- ChartController,
16
- ChartMountOptions,
17
- ChartViewport,
18
- DrawingController,
19
- DrawingState,
20
- IndicatorDefinition,
21
- IndicatorSelectorController,
22
- KLineData,
23
- ToolbarController,
24
- ToolDefinition,
25
- ToolId,
26
- } from '@363045841yyt/klinechart-core'
27
-
28
- // ---------------------------------------------------------------------------
29
- // Inline mini-signal �?Object.is-equality, sync notify. Drop-in compatible
30
- // with `@363045841yyt/klinechart-core/reactivity` for shape-only test purposes.
31
- // ---------------------------------------------------------------------------
32
-
33
- function createSignal<T>(initial: T): Signal<T> {
34
- let value = initial
35
- const subs = new Set<() => void>()
36
- const read = (): T => value
37
- const peek = (): T => value
38
- const set = (next: T): void => {
39
- if (Object.is(value, next)) return
40
- value = next
41
- for (const listener of [...subs]) listener()
42
- }
43
- const subscribe = (listener: () => void): (() => void) => {
44
- subs.add(listener)
45
- return () => {
46
- subs.delete(listener)
47
- }
48
- }
49
- return Object.assign(read, { peek, set, subscribe }) as Signal<T>
50
- }
51
-
52
- export interface MockChartController extends ChartController {
53
- /** spy: how many times `dispose` was called */
54
- disposeCalls: () => number
55
- /** test-only signal mutators */
56
- _setViewport: (vp: ChartViewport) => void
57
- _setData: (data: ReadonlyArray<KLineData>) => void
58
- }
59
-
60
- function createMockIndicatorSelector(): IndicatorSelectorController {
61
- const catalog = createSignal<ReadonlyArray<IndicatorDefinition>>([])
62
- const active = createSignal<ReadonlyArray<ActiveIndicator>>([])
63
- const menuOpen = createSignal<boolean>(false)
64
- const searchQuery = createSignal<string>('')
65
- const filteredMain = createSignal<ReadonlyArray<IndicatorDefinition>>([])
66
- const filteredSub = createSignal<ReadonlyArray<IndicatorDefinition>>([])
67
-
68
- return {
69
- catalog,
70
- active,
71
- menuOpen,
72
- searchQuery,
73
- filteredMain,
74
- filteredSub,
75
- add: () => null,
76
- remove: () => false,
77
- updateParams: () => false,
78
- reorder: () => false,
79
- openMenu: () => menuOpen.set(true),
80
- closeMenu: () => menuOpen.set(false),
81
- toggleMenu: () => menuOpen.set(!menuOpen.peek()),
82
- setSearchQuery: (q: string) => searchQuery.set(q),
83
- isActive: () => false,
84
- dispose: () => {},
85
- }
86
- }
87
-
88
- function createMockToolbar(): ToolbarController {
89
- const tools = createSignal<ReadonlyArray<ToolDefinition>>([])
90
- const activeTool = createSignal<ToolId | null>(null)
91
- const disabledTools = createSignal<ReadonlySet<ToolId>>(new Set())
92
- return {
93
- tools,
94
- activeTool,
95
- disabledTools,
96
- selectTool: (id) => activeTool.set(id),
97
- clearSelection: () => activeTool.set(null),
98
- setDisabled: () => {},
99
- dispose: () => {},
100
- }
101
- }
102
-
103
- function createMockDrawing(): DrawingController {
104
- const state = createSignal<DrawingState>({
105
- activeTool: null,
106
- drawingCount: 0,
107
- })
108
- return {
109
- state,
110
- setActiveTool: (tool) =>
111
- state.set({ ...state.peek(), activeTool: tool }),
112
- clearAll: () => state.set({ ...state.peek(), drawingCount: 0 }),
113
- deleteLast: () =>
114
- state.set({
115
- ...state.peek(),
116
- drawingCount: Math.max(0, state.peek().drawingCount - 1),
117
- }),
118
- dispose: () => {},
119
- }
120
- }
121
-
122
- export function createMockChartController(
123
- opts: Partial<ChartMountOptions> = {},
124
- ): MockChartController {
125
- let disposeCalls = 0
126
-
127
- const viewport = createSignal<ChartViewport>({
128
- zoomLevel: opts.initialZoomLevel ?? 3,
129
- kWidth: 6,
130
- visibleFrom: 0,
131
- visibleTo: 0,
132
- })
133
- const data = createSignal<ReadonlyArray<KLineData>>(opts.data ?? [])
134
- const theme = createSignal<'light' | 'dark'>(opts.theme ?? 'light')
135
- const indicatorSelector = createMockIndicatorSelector()
136
- const toolbar = createMockToolbar()
137
- const drawing = createMockDrawing()
138
-
139
- return {
140
- viewport,
141
- data,
142
- theme,
143
- indicatorSelector,
144
- toolbar,
145
- drawing,
146
- setData: (next) => data.set(next),
147
- appendData: (next) => data.set([...data.peek(), ...next]),
148
- updateData: (next) => data.set(next),
149
- setTheme: (next) => theme.set(next),
150
- zoomToLevel: (level) =>
151
- viewport.set({ ...viewport.peek(), zoomLevel: level }),
152
- zoomIn: () =>
153
- viewport.set({
154
- ...viewport.peek(),
155
- zoomLevel: viewport.peek().zoomLevel + 1,
156
- }),
157
- zoomOut: () =>
158
- viewport.set({
159
- ...viewport.peek(),
160
- zoomLevel: viewport.peek().zoomLevel - 1,
161
- }),
162
- handlePointerEvent: () => false,
163
- handleWheelEvent: () => {},
164
- handleScrollEvent: () => {},
165
- handlePinchZoom: () => {},
166
- addIndicator: () => null,
167
- removeIndicator: () => false,
168
- updateIndicatorParams: () => false,
169
- updateRendererConfig: () => {},
170
- setDrawingTool: (tool) => drawing.setActiveTool(tool),
171
- clearDrawings: () => drawing.clearAll(),
172
- removeDrawing: () => {},
173
- resizeSubPane: () => false,
174
- createSubPane: () => false,
175
- clearSubPanes: () => {},
176
- updateCustomMarkers: () => {},
177
- clearCustomMarkers: () => {},
178
- updateSettingsFacade: () => {},
179
- updateOptionsFacade: () => {},
180
- dispose: () => {
181
- disposeCalls += 1
182
- },
183
- disposeCalls: () => disposeCalls,
184
- _setViewport: (vp) => viewport.set(vp),
185
- _setData: (next) => data.set(next),
186
- }
187
- }
188
-
189
- /** Signal helper used by reactivity bridge tests. */
190
- export function createTestSignal<T>(initial: T): Signal<T> {
191
- return createSignal(initial)
192
- }
1
+ /**
2
+ * Mock ChartController for Vue adapter tests.
3
+ *
4
+ * Mirrors the framework-agnostic signal-bearing shape from
5
+ * @363045841yyt/klinechart-core without spinning up the real Chart engine.
6
+ *
7
+ * To keep this test file runnable from the repo root vitest (which does not
8
+ * alias @363045841yyt/klinechart-core), we inline a tiny `Signal` implementation
9
+ * that is shape-compatible with `packages/core/src/reactivity/signal.ts`.
10
+ */
11
+
12
+ import type { Signal } from '@363045841yyt/klinechart-core/reactivity'
13
+ import type {
14
+ ActiveIndicator,
15
+ ChartController,
16
+ ChartMountOptions,
17
+ ChartViewport,
18
+ DrawingController,
19
+ DrawingState,
20
+ IndicatorDefinition,
21
+ IndicatorSelectorController,
22
+ KLineData,
23
+ ToolbarController,
24
+ ToolDefinition,
25
+ ToolId,
26
+ } from '@363045841yyt/klinechart-core'
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Inline mini-signal �?Object.is-equality, sync notify. Drop-in compatible
30
+ // with `@363045841yyt/klinechart-core/reactivity` for shape-only test purposes.
31
+ // ---------------------------------------------------------------------------
32
+
33
+ function createSignal<T>(initial: T): Signal<T> {
34
+ let value = initial
35
+ const subs = new Set<() => void>()
36
+ const read = (): T => value
37
+ const peek = (): T => value
38
+ const set = (next: T): void => {
39
+ if (Object.is(value, next)) return
40
+ value = next
41
+ for (const listener of [...subs]) listener()
42
+ }
43
+ const subscribe = (listener: () => void): (() => void) => {
44
+ subs.add(listener)
45
+ return () => {
46
+ subs.delete(listener)
47
+ }
48
+ }
49
+ return Object.assign(read, { peek, set, subscribe }) as Signal<T>
50
+ }
51
+
52
+ export interface MockChartController extends ChartController {
53
+ /** spy: how many times `dispose` was called */
54
+ disposeCalls: () => number
55
+ /** test-only signal mutators */
56
+ _setViewport: (vp: ChartViewport) => void
57
+ _setData: (data: ReadonlyArray<KLineData>) => void
58
+ }
59
+
60
+ function createMockIndicatorSelector(): IndicatorSelectorController {
61
+ const catalog = createSignal<ReadonlyArray<IndicatorDefinition>>([])
62
+ const active = createSignal<ReadonlyArray<ActiveIndicator>>([])
63
+ const menuOpen = createSignal<boolean>(false)
64
+ const searchQuery = createSignal<string>('')
65
+ const filteredMain = createSignal<ReadonlyArray<IndicatorDefinition>>([])
66
+ const filteredSub = createSignal<ReadonlyArray<IndicatorDefinition>>([])
67
+
68
+ return {
69
+ catalog,
70
+ active,
71
+ menuOpen,
72
+ searchQuery,
73
+ filteredMain,
74
+ filteredSub,
75
+ add: () => null,
76
+ remove: () => false,
77
+ updateParams: () => false,
78
+ reorder: () => false,
79
+ openMenu: () => menuOpen.set(true),
80
+ closeMenu: () => menuOpen.set(false),
81
+ toggleMenu: () => menuOpen.set(!menuOpen.peek()),
82
+ setSearchQuery: (q: string) => searchQuery.set(q),
83
+ isActive: () => false,
84
+ dispose: () => {},
85
+ }
86
+ }
87
+
88
+ function createMockToolbar(): ToolbarController {
89
+ const tools = createSignal<ReadonlyArray<ToolDefinition>>([])
90
+ const activeTool = createSignal<ToolId | null>(null)
91
+ const disabledTools = createSignal<ReadonlySet<ToolId>>(new Set())
92
+ return {
93
+ tools,
94
+ activeTool,
95
+ disabledTools,
96
+ selectTool: (id) => activeTool.set(id),
97
+ clearSelection: () => activeTool.set(null),
98
+ setDisabled: () => {},
99
+ dispose: () => {},
100
+ }
101
+ }
102
+
103
+ function createMockDrawing(): DrawingController {
104
+ const state = createSignal<DrawingState>({
105
+ activeTool: null,
106
+ drawingCount: 0,
107
+ })
108
+ return {
109
+ state,
110
+ setActiveTool: (tool) =>
111
+ state.set({ ...state.peek(), activeTool: tool }),
112
+ clearAll: () => state.set({ ...state.peek(), drawingCount: 0 }),
113
+ deleteLast: () =>
114
+ state.set({
115
+ ...state.peek(),
116
+ drawingCount: Math.max(0, state.peek().drawingCount - 1),
117
+ }),
118
+ dispose: () => {},
119
+ }
120
+ }
121
+
122
+ export function createMockChartController(
123
+ opts: Partial<ChartMountOptions> = {},
124
+ ): MockChartController {
125
+ let disposeCalls = 0
126
+
127
+ const viewport = createSignal<ChartViewport>({
128
+ zoomLevel: opts.initialZoomLevel ?? 3,
129
+ kWidth: 6,
130
+ visibleFrom: 0,
131
+ visibleTo: 0,
132
+ })
133
+ const data = createSignal<ReadonlyArray<KLineData>>(opts.data ?? [])
134
+ const theme = createSignal<'light' | 'dark'>(opts.theme ?? 'light')
135
+ const indicatorSelector = createMockIndicatorSelector()
136
+ const toolbar = createMockToolbar()
137
+ const drawing = createMockDrawing()
138
+
139
+ return {
140
+ viewport,
141
+ data,
142
+ theme,
143
+ indicatorSelector,
144
+ toolbar,
145
+ drawing,
146
+ setData: (next) => data.set(next),
147
+ appendData: (next) => data.set([...data.peek(), ...next]),
148
+ updateData: (next) => data.set(next),
149
+ setTheme: (next) => theme.set(next),
150
+ zoomToLevel: (level) =>
151
+ viewport.set({ ...viewport.peek(), zoomLevel: level }),
152
+ zoomIn: () =>
153
+ viewport.set({
154
+ ...viewport.peek(),
155
+ zoomLevel: viewport.peek().zoomLevel + 1,
156
+ }),
157
+ zoomOut: () =>
158
+ viewport.set({
159
+ ...viewport.peek(),
160
+ zoomLevel: viewport.peek().zoomLevel - 1,
161
+ }),
162
+ handlePointerEvent: () => false,
163
+ handleWheelEvent: () => {},
164
+ handleScrollEvent: () => {},
165
+ handlePinchZoom: () => {},
166
+ addIndicator: () => null,
167
+ removeIndicator: () => false,
168
+ updateIndicatorParams: () => false,
169
+ updateRendererConfig: () => {},
170
+ setDrawingTool: (tool) => drawing.setActiveTool(tool),
171
+ clearDrawings: () => drawing.clearAll(),
172
+ removeDrawing: () => {},
173
+ resizeSubPane: () => false,
174
+ createSubPane: () => false,
175
+ clearSubPanes: () => {},
176
+ updateCustomMarkers: () => {},
177
+ clearCustomMarkers: () => {},
178
+ updateSettingsFacade: () => {},
179
+ updateOptionsFacade: () => {},
180
+ dispose: () => {
181
+ disposeCalls += 1
182
+ },
183
+ disposeCalls: () => disposeCalls,
184
+ _setViewport: (vp) => viewport.set(vp),
185
+ _setData: (next) => data.set(next),
186
+ }
187
+ }
188
+
189
+ /** Signal helper used by reactivity bridge tests. */
190
+ export function createTestSignal<T>(initial: T): Signal<T> {
191
+ return createSignal(initial)
192
+ }
@@ -1,132 +1,132 @@
1
- /**
2
- * Contract test for @363045841yyt/klinechart.
3
- *
4
- * Phase 1D agent's brief: make these pass without weakening assertions,
5
- * preserving the legacy KMapPlugin.install signature.
6
- */
7
-
8
- import { describe, it, expect, vi, afterEach } from 'vitest'
9
- import { defineComponent, h, nextTick, ref, shallowRef } from 'vue'
10
- import { mount } from '@vue/test-utils'
11
- import * as VueAdapter from '../index'
12
- import { coreSignalToVueRef } from '../index'
13
- import { createMockChartController, createTestSignal } from './_mockController'
14
-
15
- describe('@363045841yyt/klinechart �?public API surface', () => {
16
- it('exports createChart, useChart, useIndicatorSelector, KLineChart, KMapPlugin', () => {
17
- expect(typeof VueAdapter.createChart).toBe('function')
18
- expect(typeof VueAdapter.useChart).toBe('function')
19
- expect(typeof VueAdapter.useIndicatorSelector).toBe('function')
20
- expect(VueAdapter.KLineChart).toBeDefined()
21
- expect(typeof VueAdapter.KMapPlugin.install).toBe('function')
22
- })
23
-
24
- it('KMapPlugin.install is callable with a mock app and registers KLineChart', () => {
25
- const registered: Record<string, unknown> = {}
26
- const mockApp = {
27
- component(name: string, comp: unknown) {
28
- registered[name] = comp
29
- },
30
- } as unknown as Parameters<typeof VueAdapter.KMapPlugin.install>[0]
31
- VueAdapter.KMapPlugin.install(mockApp)
32
- expect(registered.KLineChart).toBe(VueAdapter.KLineChart)
33
- })
34
- })
35
-
36
- describe('@363045841yyt/klinechart �?SSR safety', () => {
37
- it('module import does not touch window or document', () => {
38
- // Import above ran in node env without jsdom. If it touched window, this
39
- // file would not have loaded. Test documents the contract.
40
- expect(true).toBe(true)
41
- })
42
- })
43
-
44
- describe('@363045841yyt/klinechart �?useChart lifecycle', () => {
45
- afterEach(() => {
46
- // Reset the injected factory so other tests start clean.
47
- VueAdapter.__setControllerFactory(null)
48
- })
49
-
50
- it('mounts on first render via template ref', async () => {
51
- const mockController = createMockChartController({ data: [] })
52
- const factorySpy = vi.fn(() => mockController)
53
- VueAdapter.__setControllerFactory(factorySpy)
54
-
55
- const HostComponent = defineComponent({
56
- name: 'Host',
57
- setup() {
58
- const containerRef = ref<HTMLElement | null>(null)
59
- const { chart } = VueAdapter.useChart(containerRef, { data: [] })
60
- return { containerRef, chart }
61
- },
62
- render() {
63
- return h('div', { ref: 'containerRef' })
64
- },
65
- })
66
-
67
- const wrapper = mount(HostComponent, { attachTo: document.body })
68
- await nextTick()
69
-
70
- expect(factorySpy).toHaveBeenCalledTimes(1)
71
- const factoryArg = factorySpy.mock.calls[0]?.[0]
72
- expect(factoryArg?.container).toBeInstanceOf(HTMLElement)
73
- expect(wrapper.vm.chart).toBe(mockController)
74
-
75
- wrapper.unmount()
76
- })
77
-
78
- it('disposes on unmount', async () => {
79
- const mockController = createMockChartController({ data: [] })
80
- VueAdapter.__setControllerFactory(() => mockController)
81
-
82
- const HostComponent = defineComponent({
83
- name: 'Host',
84
- setup() {
85
- const containerRef = ref<HTMLElement | null>(null)
86
- const { chart } = VueAdapter.useChart(containerRef, { data: [] })
87
- return { containerRef, chart }
88
- },
89
- render() {
90
- return h('div', { ref: 'containerRef' })
91
- },
92
- })
93
-
94
- const wrapper = mount(HostComponent, { attachTo: document.body })
95
- await nextTick()
96
-
97
- expect(mockController.disposeCalls()).toBe(0)
98
- wrapper.unmount()
99
- // Allow lifecycle hooks to settle.
100
- await nextTick()
101
- expect(mockController.disposeCalls()).toBe(1)
102
- })
103
-
104
- it('reactivity bridge: signal change updates returned ref', async () => {
105
- // Mount a tiny scoped component so coreSignalToVueRef can register
106
- // its onScopeDispose cleanup. Without a setup scope the ref is still
107
- // wired up correctly, but cleanup would not be automatic.
108
- const signal = createTestSignal<number>(1)
109
- const bridgedRef = shallowRef<{ value: number } | null>(null)
110
-
111
- const HostComponent = defineComponent({
112
- name: 'BridgeHost',
113
- setup() {
114
- const r = coreSignalToVueRef(signal)
115
- bridgedRef.value = r as unknown as { value: number }
116
- return () => h('div', String(r.value))
117
- },
118
- })
119
-
120
- const wrapper = mount(HostComponent, { attachTo: document.body })
121
- expect(bridgedRef.value?.value).toBe(1)
122
- expect(wrapper.text()).toBe('1')
123
-
124
- signal.set(42)
125
- await nextTick()
126
-
127
- expect(bridgedRef.value?.value).toBe(42)
128
- expect(wrapper.text()).toBe('42')
129
-
130
- wrapper.unmount()
131
- })
132
- })
1
+ /**
2
+ * Contract test for @363045841yyt/klinechart.
3
+ *
4
+ * Phase 1D agent's brief: make these pass without weakening assertions,
5
+ * preserving the legacy KMapPlugin.install signature.
6
+ */
7
+
8
+ import { describe, it, expect, vi, afterEach } from 'vitest'
9
+ import { defineComponent, h, nextTick, ref, shallowRef } from 'vue'
10
+ import { mount } from '@vue/test-utils'
11
+ import * as VueAdapter from '../index'
12
+ import { coreSignalToVueRef } from '../index'
13
+ import { createMockChartController, createTestSignal } from './_mockController'
14
+
15
+ describe('@363045841yyt/klinechart �?public API surface', () => {
16
+ it('exports createChart, useChart, useIndicatorSelector, KLineChart, KMapPlugin', () => {
17
+ expect(typeof VueAdapter.createChart).toBe('function')
18
+ expect(typeof VueAdapter.useChart).toBe('function')
19
+ expect(typeof VueAdapter.useIndicatorSelector).toBe('function')
20
+ expect(VueAdapter.KLineChart).toBeDefined()
21
+ expect(typeof VueAdapter.KMapPlugin.install).toBe('function')
22
+ })
23
+
24
+ it('KMapPlugin.install is callable with a mock app and registers KLineChart', () => {
25
+ const registered: Record<string, unknown> = {}
26
+ const mockApp = {
27
+ component(name: string, comp: unknown) {
28
+ registered[name] = comp
29
+ },
30
+ } as unknown as Parameters<typeof VueAdapter.KMapPlugin.install>[0]
31
+ VueAdapter.KMapPlugin.install(mockApp)
32
+ expect(registered.KLineChart).toBe(VueAdapter.KLineChart)
33
+ })
34
+ })
35
+
36
+ describe('@363045841yyt/klinechart �?SSR safety', () => {
37
+ it('module import does not touch window or document', () => {
38
+ // Import above ran in node env without jsdom. If it touched window, this
39
+ // file would not have loaded. Test documents the contract.
40
+ expect(true).toBe(true)
41
+ })
42
+ })
43
+
44
+ describe('@363045841yyt/klinechart �?useChart lifecycle', () => {
45
+ afterEach(() => {
46
+ // Reset the injected factory so other tests start clean.
47
+ VueAdapter.__setControllerFactory(null)
48
+ })
49
+
50
+ it('mounts on first render via template ref', async () => {
51
+ const mockController = createMockChartController({ data: [] })
52
+ const factorySpy = vi.fn(() => mockController)
53
+ VueAdapter.__setControllerFactory(factorySpy)
54
+
55
+ const HostComponent = defineComponent({
56
+ name: 'Host',
57
+ setup() {
58
+ const containerRef = ref<HTMLElement | null>(null)
59
+ const { chart } = VueAdapter.useChart(containerRef, { data: [] })
60
+ return { containerRef, chart }
61
+ },
62
+ render() {
63
+ return h('div', { ref: 'containerRef' })
64
+ },
65
+ })
66
+
67
+ const wrapper = mount(HostComponent, { attachTo: document.body })
68
+ await nextTick()
69
+
70
+ expect(factorySpy).toHaveBeenCalledTimes(1)
71
+ const factoryArg = factorySpy.mock.calls[0]?.[0]
72
+ expect(factoryArg?.container).toBeInstanceOf(HTMLElement)
73
+ expect(wrapper.vm.chart).toBe(mockController)
74
+
75
+ wrapper.unmount()
76
+ })
77
+
78
+ it('disposes on unmount', async () => {
79
+ const mockController = createMockChartController({ data: [] })
80
+ VueAdapter.__setControllerFactory(() => mockController)
81
+
82
+ const HostComponent = defineComponent({
83
+ name: 'Host',
84
+ setup() {
85
+ const containerRef = ref<HTMLElement | null>(null)
86
+ const { chart } = VueAdapter.useChart(containerRef, { data: [] })
87
+ return { containerRef, chart }
88
+ },
89
+ render() {
90
+ return h('div', { ref: 'containerRef' })
91
+ },
92
+ })
93
+
94
+ const wrapper = mount(HostComponent, { attachTo: document.body })
95
+ await nextTick()
96
+
97
+ expect(mockController.disposeCalls()).toBe(0)
98
+ wrapper.unmount()
99
+ // Allow lifecycle hooks to settle.
100
+ await nextTick()
101
+ expect(mockController.disposeCalls()).toBe(1)
102
+ })
103
+
104
+ it('reactivity bridge: signal change updates returned ref', async () => {
105
+ // Mount a tiny scoped component so coreSignalToVueRef can register
106
+ // its onScopeDispose cleanup. Without a setup scope the ref is still
107
+ // wired up correctly, but cleanup would not be automatic.
108
+ const signal = createTestSignal<number>(1)
109
+ const bridgedRef = shallowRef<{ value: number } | null>(null)
110
+
111
+ const HostComponent = defineComponent({
112
+ name: 'BridgeHost',
113
+ setup() {
114
+ const r = coreSignalToVueRef(signal)
115
+ bridgedRef.value = r as unknown as { value: number }
116
+ return () => h('div', String(r.value))
117
+ },
118
+ })
119
+
120
+ const wrapper = mount(HostComponent, { attachTo: document.body })
121
+ expect(bridgedRef.value?.value).toBe(1)
122
+ expect(wrapper.text()).toBe('1')
123
+
124
+ signal.set(42)
125
+ await nextTick()
126
+
127
+ expect(bridgedRef.value?.value).toBe(42)
128
+ expect(wrapper.text()).toBe('42')
129
+
130
+ wrapper.unmount()
131
+ })
132
+ })