Skip to content

Commit 6b60124

Browse files
yoiteyouCYan1203
andauthored
feat: enhance resource selection (#1410)
Co-authored-by: niuran <[email protected]>
1 parent 1a0edca commit 6b60124

File tree

11 files changed

+1011
-107
lines changed

11 files changed

+1011
-107
lines changed

frontend/src/components/__tests__/endpoints/NewEndpoint.spec.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,16 +133,17 @@ vi.mock('@/packs/useFetchApi', () => ({
133133
}))
134134

135135
vi.mock('@/components/shared/deploy_instance/fetchResourceInCategory', () => ({
136-
fetchResourcesInCategory: vi.fn(() => {
136+
fetchResourcesInType: vi.fn(() => {
137137
return Promise.resolve([
138138
{
139-
label: 'Others',
139+
label: 'CPU',
140140
options: [
141141
{
142142
name: 'Resource 1',
143143
label: "Resource 1",
144144
is_available: true,
145-
resources:'res1',
145+
resources: {"cpu":{"type":"Intel","num":"1"},"memory":"2Gi"},
146+
priceValue:'120',
146147
order_detail_id: 1,
147148
type: 'cpu',
148149
id: 1
@@ -184,13 +185,14 @@ describe('NewEndpoint', () => {
184185
expect(wrapper.vm.dataForm.endpoint_cluster).toBe('cluster-1')
185186
expect(wrapper.vm.endpointResources).toEqual([
186187
{
187-
"label": "Others",
188-
"options": [
188+
label: 'CPU',
189+
options: [
189190
{
190191
name: 'Resource 1',
191192
label: "Resource 1",
192193
is_available: true,
193-
resources: 'res1',
194+
resources: {"cpu":{"type":"Intel","num":"1"},"memory":"2Gi"},
195+
priceValue:'120',
194196
order_detail_id: 1,
195197
type: 'cpu',
196198
id: 1

frontend/src/components/__tests__/finetune/NewFinetune.spec.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,17 @@ vi.mock('@/packs/useFetchApi', () => ({
9696
}))
9797

9898
vi.mock('@/components/shared/deploy_instance/fetchResourceInCategory', () => ({
99-
fetchResourcesInCategory: vi.fn(() => {
99+
fetchResourcesInType: vi.fn(() => {
100100
return Promise.resolve([
101101
{
102-
label: 'Others',
102+
label: 'CPU',
103103
options: [
104104
{
105105
name: 'Resource 1',
106106
label: 'Resource 1',
107107
is_available: true,
108-
resources: 'res1',
108+
resources: {"cpu":{"type":"Intel","num":"1"},"memory":"2Gi"},
109+
priceValue:'120',
109110
order_detail_id: 1,
110111
type: 'cpu',
111112
id: 1
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest'
2+
import { mount } from '@vue/test-utils'
3+
import NewNotebook from '@/components/notebooks/NewNotebook.vue'
4+
5+
// Mock i18n
6+
vi.mock('vue-i18n', () => ({
7+
useI18n: () => ({ t: (k, p) => (p ? `${k}` : `${k}`) })
8+
}))
9+
10+
// Mock element-plus
11+
vi.mock('element-plus', () => ({
12+
ElMessage: { warning: vi.fn(), success: vi.fn(), error: vi.fn() }
13+
}))
14+
15+
// Mock SvgIcon and CsgButton
16+
vi.mock('@/components/shared/CsgButton.vue', () => ({
17+
default: { name: 'CsgButton', template: '<button><slot></slot></button>' }
18+
}))
19+
20+
// Mock Vue3Lottie component
21+
vi.mock('vue3-lottie', () => ({
22+
Vue3Lottie: {
23+
name: 'Vue3Lottie',
24+
template: '<div data-testid="lottie-animation"><slot></slot></div>',
25+
props: ['animationData', 'width', 'height', 'loop', 'autoPlay', 'speed'],
26+
setup(props, { expose }) {
27+
const lottieRef = {
28+
goToAndStop: vi.fn(),
29+
play: vi.fn(),
30+
pause: vi.fn()
31+
}
32+
expose({ lottieRef })
33+
return { lottieRef }
34+
}
35+
}
36+
}))
37+
38+
// Mock user store
39+
vi.mock('@/stores/UserStore.js', () => ({
40+
default: () => ({ username: 'tester' })
41+
}))
42+
43+
// Mock nameRule injection via provide when mounting
44+
45+
// Mock fetchResource helper
46+
vi.mock('@/components/shared/deploy_instance/fetchResourceInCategory.js', () => ({
47+
fetchResourcesInType: vi.fn(async () => ([
48+
{
49+
label: 'GPU',
50+
options: [
51+
{ id: 11,
52+
order_detail_id: 101,
53+
is_available: true,
54+
label: 'A100',
55+
type: 'gpu',
56+
currentUser: 'tester',
57+
deploy_name: 'nb-1',
58+
resources: {"cpu":{"type":"Intel","num":"1"},"memory":"2Gi"},
59+
priceValue:'120',
60+
resource_id: 11 }
61+
]
62+
}
63+
]))
64+
}))
65+
66+
// Mock API
67+
const apiMock = vi.fn((url, options) => ({
68+
json: () => {
69+
if (url === '/cluster') {
70+
return Promise.resolve({
71+
data: { value: { data: [{ cluster_id: 'c1', region: 'region-1' }] } },
72+
error: { value: null },
73+
response: { value: { status: 200 } }
74+
})
75+
}
76+
if (url.startsWith('/runtime_framework')) {
77+
return Promise.resolve({
78+
data: { value: { data: [
79+
{ id: 1, frame_version: 'PyTorch 2.2', frame_name: 'pytorch', compute_type: 'gpu' },
80+
{ id: 2, frame_version: 'TensorFlow 2.15', frame_name: 'tensorflow', compute_type: 'cpu' }
81+
] } },
82+
error: { value: null },
83+
response: { value: { status: 200 } }
84+
})
85+
}
86+
return Promise.resolve({ data: { value: { data: [] } }, error: { value: null }, response: { value: { status: 200 } } })
87+
},
88+
post: () => ({
89+
json: () => Promise.resolve({
90+
data: { value: { data: { id: 999 } }, msg: 'ok' },
91+
error: { value: null },
92+
response: { value: { status: 200 } }
93+
})
94+
})
95+
}))
96+
vi.mock('@/packs/useFetchApi', () => ({ default: (url, options) => apiMock(url, options) }))
97+
98+
const mountNew = async (props = {}) => {
99+
const wrapper = mount(NewNotebook, {
100+
global: {
101+
provide: { nameRule: /.*/ },
102+
stubs: {
103+
SvgIcon: { template: '<div />' }
104+
}
105+
},
106+
props
107+
})
108+
await new Promise(r => setTimeout(r, 50))
109+
await wrapper.vm.$nextTick()
110+
return wrapper
111+
}
112+
113+
describe('NewNotebook', () => {
114+
beforeEach(() => {
115+
apiMock.mockClear()
116+
})
117+
118+
it('mounts and loads clusters, resources and frameworks', async () => {
119+
console.log('Starting test...')
120+
const wrapper = await mountNew()
121+
122+
// 增加等待时间并添加多个 nextTick 确保所有异步操作完成
123+
await new Promise(resolve => setTimeout(resolve, 100))
124+
await wrapper.vm.$nextTick()
125+
await wrapper.vm.$nextTick()
126+
127+
// 打印调试信息
128+
console.log('API calls made:', apiMock.mock.calls)
129+
130+
// 验证 API 调用
131+
expect(apiMock).toHaveBeenCalled()
132+
expect(apiMock.mock.calls.some(call => call[0] === '/cluster')).toBe(true)
133+
expect(apiMock.mock.calls.some(call => call[0] === '/runtime_framework?deploy_type=5')).toBe(true)
134+
135+
// 验证默认选中项
136+
expect(wrapper.vm.dataForm.notebook_cluster).toBe('c1')
137+
expect(wrapper.vm.dataForm.resource).toBe('11/101')
138+
expect([1, '']).toContain(wrapper.vm.dataForm.runtime_framework_id)
139+
})
140+
141+
it('submit form triggers creation and redirects', async () => {
142+
const originalHref = window.location.href
143+
Object.defineProperty(window, 'location', { value: { href: '', pathname: '' }, writable: true })
144+
const wrapper = await mountNew()
145+
146+
// 填充必要字段
147+
wrapper.vm.dataForm.title = 'nb-1'
148+
await wrapper.vm.createNotebook()
149+
await new Promise(r => setTimeout(r, 20))
150+
151+
const notebooksCall = apiMock.mock.calls.find(([url]) => url === '/notebooks')
152+
expect(notebooksCall).toBeTruthy()
153+
154+
const [, requestOptions] = notebooksCall
155+
expect(requestOptions?.headers?.['Content-Type']).toBe('application/json')
156+
157+
const payload = JSON.parse(requestOptions.body)
158+
expect(payload).toMatchObject({
159+
order_detail_id: 101,
160+
resource_id: 11,
161+
deploy_name: 'nb-1',
162+
currentUser: 'tester'
163+
})
164+
165+
expect(window.location.href).toBe('/notebooks/999')
166+
167+
// restore
168+
window.location.href = originalHref
169+
})
170+
171+
it('cancel navigates back', async () => {
172+
const backSpy = vi.spyOn(window.history, 'back').mockImplementation(() => {})
173+
const wrapper = await mountNew()
174+
await wrapper.vm.handleCancel()
175+
expect(backSpy).toHaveBeenCalled()
176+
backSpy.mockRestore()
177+
})
178+
})

0 commit comments

Comments
 (0)