Skip to content

Commit e8dabd3

Browse files
committed
feat: create initial block-chart
1 parent 1b6fcd9 commit e8dabd3

File tree

6 files changed

+639
-3
lines changed

6 files changed

+639
-3
lines changed

packages/app/__tests__/setup.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ config.global.stubs['router-link'] = true
55

66
config.global.config.warnHandler = () => true
77

8+
config.global.renderStubDefaultSlot = true
9+
config.global.mocks.$t = (key: string) => key
10+
811
window.clientConfig = {
912
useCase: () => Promise.reject('Not implemented'),
1013
open: {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { vi } from 'vitest'
2+
import * as Evaluation from '@modules/evaluation/composables/use-evaluation'
3+
4+
function createRuntimeMock() {
5+
const callbacks = {
6+
stdout: [] as Function[],
7+
stderr: [] as Function[],
8+
}
9+
10+
return {
11+
run: vi.fn(),
12+
onDone: vi.fn().mockResolvedValue(undefined),
13+
on: (event: keyof typeof callbacks, callback: any) => callbacks[event]?.push(callback),
14+
emit: (event: keyof typeof callbacks, ...args: any[]) => {
15+
callbacks[event].forEach((cb) => cb(...args))
16+
},
17+
}
18+
}
19+
20+
export function useEvaluationMock() {
21+
const useEvaluation = vi.fn()
22+
const useEvaluationRun = vi.fn()
23+
24+
const runtime = createRuntimeMock()
25+
26+
useEvaluation.mockReturnValue({
27+
run: useEvaluationRun,
28+
})
29+
30+
useEvaluationRun.mockReturnValue(runtime as any)
31+
32+
vi.spyOn(Evaluation, 'useEvaluation').mockImplementation(() => useEvaluation())
33+
34+
return { useEvaluation, useEvaluationRun, runtime }
35+
}
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
import { useMountWrapper } from '__tests__/fixtures/component'
2+
import { describe, expect, it } from 'vitest'
3+
import { MarkdownNodeComponent } from '@language-kit/markdown'
4+
import { useBlockStub } from '../__tests__/stubs'
5+
6+
import BlockChart from './BlockChart.vue'
7+
import VBtn from '@components/VBtn.vue'
8+
import ANSICard from '@modules/evaluation/components/ANSICard.vue'
9+
import { inspect, waitFor } from '@composables/utils'
10+
import MonacoEditor from '@components/MonacoEditor.vue'
11+
import VChart from '@components/VChart.vue'
12+
import VTooltipVue from '@components/VTooltip.vue'
13+
import VInputVue from '@components/VInput.vue'
14+
import ToolbarAlignment from './ToolbarAlignment.vue'
15+
16+
describe('BlockChart (unit)', () => {
17+
const component = useMountWrapper(BlockChart, {
18+
shallow: true,
19+
global: {
20+
stubs: {
21+
Block: useBlockStub(),
22+
VTooltip: VTooltipVue,
23+
},
24+
},
25+
})
26+
27+
function createNode(data?: Partial<MarkdownNodeComponent>) {
28+
const node = new MarkdownNodeComponent()
29+
30+
node.name = 'chart'
31+
node.attrs = data?.attrs ?? {}
32+
node.body = data?.body ?? ''
33+
34+
return node
35+
}
36+
37+
function findChart() {
38+
return component.wrapper!.findComponent(VChart)
39+
}
40+
41+
function findEditor() {
42+
return component.wrapper!.findComponent<typeof MonacoEditor>('[data-test-id="editor"]')
43+
}
44+
45+
function findToolbarBtn(name: 'edit' | 'debug' | 'dataset') {
46+
return component.wrapper!.findComponent<typeof VBtn>(`[data-test-id="toolbar-${name}-btn"]`)
47+
}
48+
49+
function findDatasetView() {
50+
return component.wrapper!.findComponent<typeof MonacoEditor>(
51+
'[data-test-id="dataset-view"]'
52+
)
53+
}
54+
55+
function findDebugView() {
56+
return component.wrapper!.findComponent<typeof ANSICard>('[data-test-id="debug-view"]')
57+
}
58+
59+
function findInput(id: 'height' | 'width') {
60+
return component.wrapper!.findComponent<typeof VInputVue>(
61+
`[data-test-id="toolbar-input-${id}"]`
62+
)
63+
}
64+
65+
function findToolbarAlignment() {
66+
return component.wrapper!.findComponent(ToolbarAlignment)
67+
}
68+
69+
it('should set chart options', async () => {
70+
const node = createNode({
71+
body: `
72+
import { useChart } from 'app:chart'
73+
74+
const chart = useChart()
75+
76+
chart.type = 'bar'
77+
78+
`,
79+
})
80+
81+
component.mount({
82+
props: {
83+
modelValue: node,
84+
},
85+
})
86+
87+
await waitFor(() => findChart().exists())
88+
89+
expect(findChart().props('options')).toEqual({ type: 'bar' })
90+
})
91+
92+
it('should show editor on edit-btn click', async () => {
93+
const node = createNode({
94+
body: 'console.log("Hello world!")',
95+
})
96+
97+
component.mount({
98+
props: {
99+
'modelValue': node,
100+
'onUpdate:modelValue': (n: MarkdownNodeComponent) => {
101+
node.body = n.body
102+
},
103+
},
104+
})
105+
106+
const btn = findToolbarBtn('edit')
107+
108+
expect(btn.exists()).toBe(true)
109+
110+
expect(findEditor().exists()).toBe(false)
111+
112+
await btn.trigger('click')
113+
114+
expect(findEditor().exists()).toBe(true)
115+
})
116+
117+
it('should update node when editor is saved', async () => {
118+
const node = createNode({
119+
body: 'console.log("Hello world!")',
120+
})
121+
122+
component.mount({
123+
props: {
124+
'modelValue': node,
125+
'onUpdate:modelValue': (n: MarkdownNodeComponent) => {
126+
node.body = n.body
127+
},
128+
},
129+
})
130+
131+
await findToolbarBtn('edit').trigger('click')
132+
133+
const editor = findEditor()
134+
135+
await editor.setValue('console.log("Update!")')
136+
137+
await editor.trigger('keydown.ctrl.s')
138+
139+
expect(node.body).toBe('console.log("Update!")')
140+
})
141+
142+
it('should debug view show output logs', async () => {
143+
const node = createNode({
144+
body: 'console.log("foo");',
145+
})
146+
147+
component.mount({
148+
props: {
149+
modelValue: node,
150+
},
151+
})
152+
153+
await waitFor(() => findChart().exists())
154+
155+
await findToolbarBtn('debug').trigger('click')
156+
157+
const debugView = findDebugView()
158+
159+
expect(debugView.exists()).toBe(true)
160+
161+
expect(debugView.props('modelValue')).toEqual(['foo\n'])
162+
})
163+
164+
it('should dataset view show chat options', async () => {
165+
const node = createNode({
166+
body: `
167+
import { useChart } from 'app:chart'
168+
169+
const chart = useChart()
170+
171+
chart.type = 'bar'
172+
`,
173+
})
174+
175+
component.mount({
176+
props: {
177+
modelValue: node,
178+
},
179+
})
180+
181+
await waitFor(() => findChart().exists())
182+
183+
await findToolbarBtn('dataset').trigger('click')
184+
185+
const datasetView = findDatasetView()
186+
187+
expect(datasetView.exists()).toBe(true)
188+
189+
expect(datasetView.attributes('model-value')).toEqual(
190+
inspect({
191+
type: 'bar',
192+
})
193+
)
194+
})
195+
196+
it('should show error in debug view when evaluation fails', async () => {
197+
const node = createNode({
198+
body: 'throw new Error("Internal error")',
199+
})
200+
201+
component.mount({
202+
props: {
203+
modelValue: node,
204+
},
205+
})
206+
207+
await waitFor(() => findDebugView().exists())
208+
209+
const debugView = findDebugView()
210+
211+
expect(debugView.props('modelValue')).toEqual(['Internal error\n'])
212+
})
213+
214+
it.each(['height', 'width'] as const)(
215+
'should when change chart %s update node',
216+
async (name) => {
217+
const node = createNode({
218+
body: 'throw new Error("Internal error")',
219+
})
220+
221+
component.mount({
222+
props: {
223+
'modelValue': node,
224+
'onUpdate:modelValue': (n: MarkdownNodeComponent) => {
225+
node.attrs = n.attrs
226+
},
227+
},
228+
})
229+
230+
const input = findInput(name)
231+
232+
expect(input.exists()).toBe(true)
233+
234+
await input.setValue('100')
235+
236+
expect(node.attrs[name]).toBe(100)
237+
}
238+
)
239+
240+
it('should when change alignment update node', async () => {
241+
const node = createNode({
242+
body: 'throw new Error("Internal error")',
243+
})
244+
245+
component.mount({
246+
props: {
247+
'modelValue': node,
248+
'onUpdate:modelValue': (n: MarkdownNodeComponent) => {
249+
node.attrs = n.attrs
250+
},
251+
},
252+
})
253+
254+
const alignment = findToolbarAlignment()
255+
256+
expect(alignment.exists()).toBe(true)
257+
258+
await alignment.setValue('center')
259+
260+
expect(node.attrs.align).toBe('center')
261+
})
262+
})

0 commit comments

Comments
 (0)