Skip to content

Commit 47580f1

Browse files
authored
test: add tests for data-binding & lifecycles (#1592)
1 parent ba37154 commit 47580f1

File tree

13 files changed

+1701
-62
lines changed

13 files changed

+1701
-62
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { expect, test, describe } from 'vitest'
2+
import path from 'path'
3+
import fs from 'fs'
4+
import { generateApp } from '@/generator/generateApp'
5+
import { advancedDataBindingSchema } from './mockData'
6+
7+
describe('Advanced Data Binding', () => {
8+
test('should generate v-model for nested object bindings', async () => {
9+
const instance = generateApp()
10+
const res = await instance.generate(advancedDataBindingSchema)
11+
const { genResult, errors } = res
12+
13+
// 检查是否有错误
14+
expect(errors).toHaveLength(0)
15+
16+
// 找到生成的 Vue 页面文件
17+
const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
18+
expect(vueFile).toBeDefined()
19+
20+
const content = vueFile.fileContent
21+
22+
// 验证包含 v-model 绑定
23+
expect(content).toContain('v-model')
24+
25+
// 验证嵌套对象数据绑定
26+
expect(content).toContain('v-model="state.formData.address.detail"')
27+
expect(content).toContain('v-model="state.formData.address.zipCode"')
28+
29+
// 验证基础字段绑定
30+
expect(content).toContain('v-model="state.formData.username"')
31+
expect(content).toContain('v-model="state.formData.enabled"')
32+
expect(content).toContain('v-model="state.formData.gender"')
33+
expect(content).toContain('v-model="state.formData.remarks"')
34+
expect(content).toContain('v-model="state.formData.category"')
35+
36+
// 写入测试结果文件
37+
const outputDir = path.resolve(__dirname, './result/advanced')
38+
fs.mkdirSync(outputDir, { recursive: true })
39+
40+
for (const { fileName, fileContent } of genResult) {
41+
if (fileName.endsWith('.vue')) {
42+
fs.writeFileSync(path.join(outputDir, fileName), fileContent)
43+
}
44+
}
45+
})
46+
47+
test('should handle multiple component types with data binding', async () => {
48+
const instance = generateApp()
49+
const res = await instance.generate(advancedDataBindingSchema)
50+
const { genResult } = res
51+
52+
const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
53+
const content = vueFile.fileContent
54+
55+
// 统计 v-model 绑定数量
56+
const vModelMatches = content.match(/v-model[^=]*="[^"]+"/g) || []
57+
58+
// 应该有8个 v-model 绑定
59+
expect(vModelMatches).toHaveLength(8)
60+
61+
// 验证所有组件类型都正确生成
62+
expect(content).toContain('<tiny-input')
63+
expect(content).toContain('<tiny-switch')
64+
expect(content).toContain('<tiny-radio')
65+
expect(content).toContain('<textarea')
66+
expect(content).toContain('<select')
67+
})
68+
69+
test('should generate reactive state for nested objects', async () => {
70+
const instance = generateApp()
71+
const res = await instance.generate(advancedDataBindingSchema)
72+
const { genResult } = res
73+
74+
const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
75+
const content = vueFile.fileContent
76+
77+
// 验证嵌套状态对象结构
78+
expect(content).toContain('formData: {')
79+
expect(content).toContain('address: {')
80+
expect(content).toContain("detail: ''")
81+
expect(content).toContain("zipCode: ''")
82+
83+
// 验证状态是响应式的
84+
expect(content).toContain('vue.reactive(')
85+
})
86+
87+
test('should handle radio button groups correctly', async () => {
88+
const instance = generateApp()
89+
const res = await instance.generate(advancedDataBindingSchema)
90+
const { genResult } = res
91+
92+
const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
93+
const content = vueFile.fileContent
94+
95+
// 验证单选框组绑定到同一个字段
96+
const genderBindings = content.match(/v-model="state\.formData\.gender"/g) || []
97+
expect(genderBindings).toHaveLength(2) // 两个单选框绑定到同一字段
98+
99+
// 验证单选框的 value 属性
100+
expect(content).toContain('value="male"')
101+
expect(content).toContain('value="female"')
102+
})
103+
})
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { expect, test, describe } from 'vitest'
2+
import path from 'path'
3+
import fs from 'fs'
4+
import { generateApp } from '@/generator/generateApp'
5+
import { basicDataBindingSchema } from './mockData'
6+
7+
describe('Basic Data Binding', () => {
8+
test('should generate v-model for basic form components', async () => {
9+
const instance = generateApp()
10+
const res = await instance.generate(basicDataBindingSchema)
11+
const { genResult, errors } = res
12+
13+
// 检查是否有错误
14+
expect(errors).toHaveLength(0)
15+
16+
// 找到生成的 Vue 页面文件
17+
const vueFile = genResult.find((file) => file.fileName === 'BasicDataBindingPage.vue')
18+
expect(vueFile).toBeDefined()
19+
20+
const content = vueFile.fileContent
21+
22+
// 验证包含 v-model 绑定
23+
expect(content).toContain('v-model')
24+
25+
// 验证具体的数据绑定
26+
expect(content).toContain('v-model="state.username"')
27+
expect(content).toContain('v-model="state.role"')
28+
expect(content).toContain('v-model="state.agreed"')
29+
30+
// 验证组件正确渲染
31+
expect(content).toContain('<tiny-input')
32+
expect(content).toContain('<tiny-select')
33+
expect(content).toContain('<input')
34+
35+
// 验证状态定义
36+
expect(content).toContain("username: ''")
37+
expect(content).toContain("role: ''")
38+
expect(content).toContain('agreed: false')
39+
40+
// 写入测试结果文件(可选,用于调试)
41+
const outputDir = path.resolve(__dirname, './result/basic')
42+
fs.mkdirSync(outputDir, { recursive: true })
43+
44+
for (const { fileName, fileContent } of genResult) {
45+
if (fileName.endsWith('.vue')) {
46+
fs.writeFileSync(path.join(outputDir, fileName), fileContent)
47+
}
48+
}
49+
})
50+
51+
test('should handle different component types correctly', async () => {
52+
const instance = generateApp()
53+
const res = await instance.generate(basicDataBindingSchema)
54+
const { genResult } = res
55+
56+
const vueFile = genResult.find((file) => file.fileName === 'BasicDataBindingPage.vue')
57+
const content = vueFile.fileContent
58+
59+
// 验证不同组件类型的 v-model 生成
60+
const vModelMatches = content.match(/v-model[^=]*="[^"]+"/g) || []
61+
62+
// 应该有3个 v-model 绑定
63+
expect(vModelMatches).toHaveLength(3)
64+
65+
// 验证每个绑定都是正确的
66+
expect(vModelMatches).toContain('v-model="state.username"')
67+
expect(vModelMatches).toContain('v-model="state.role"')
68+
expect(vModelMatches).toContain('v-model="state.agreed"')
69+
})
70+
})
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { expect, test, describe } from 'vitest'
2+
import path from 'path'
3+
import fs from 'fs'
4+
import { generateApp } from '@/generator/generateApp'
5+
import { edgeCaseDataBindingSchema } from './mockData'
6+
7+
describe('Edge Case Data Binding Tests', () => {
8+
test('should handle empty field gracefully', async () => {
9+
const instance = generateApp()
10+
const res = await instance.generate(edgeCaseDataBindingSchema)
11+
const { genResult, errors } = res
12+
13+
// 不应该因为空字段而报错
14+
expect(errors).toHaveLength(0)
15+
16+
const vueFile = genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
17+
expect(vueFile).toBeDefined()
18+
19+
const content = vueFile.fileContent
20+
21+
// 空字段应该不生成v-model或生成空的v-model
22+
// 确保不会导致语法错误
23+
expect(content).not.toContain('v-model=""')
24+
expect(content).not.toContain('v-model="undefined"')
25+
})
26+
27+
test('should remove this. prefix correctly', async () => {
28+
const instance = generateApp()
29+
const res = await instance.generate(edgeCaseDataBindingSchema)
30+
const { genResult } = res
31+
32+
const vueFile = genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
33+
const content = vueFile.fileContent
34+
35+
// 应该移除this.前缀
36+
expect(content).toContain('v-model="state.withThis"')
37+
expect(content).not.toContain('v-model="this.state.withThis"')
38+
})
39+
40+
test('should handle deep nested field paths', async () => {
41+
const instance = generateApp()
42+
const res = await instance.generate(edgeCaseDataBindingSchema)
43+
const { genResult } = res
44+
45+
const vueFile = genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
46+
const content = vueFile.fileContent
47+
48+
// 深层嵌套路径应该正确处理
49+
expect(content).toContain('v-model="state.level1.level2.level3.deepField"')
50+
51+
// 验证状态结构正确生成
52+
expect(content).toContain('level1: {')
53+
expect(content).toContain('level2: {')
54+
expect(content).toContain('level3: {')
55+
expect(content).toContain("deepField: ''")
56+
})
57+
58+
test('should maintain correct state structure for nested objects', async () => {
59+
const instance = generateApp()
60+
const res = await instance.generate(edgeCaseDataBindingSchema)
61+
const { genResult } = res
62+
63+
const vueFile = genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
64+
const content = vueFile.fileContent
65+
66+
// 验证响应式状态结构
67+
expect(content).toContain('vue.reactive(')
68+
expect(content).toContain("noComponentType: ''")
69+
expect(content).toContain("withThis: ''")
70+
71+
// 写入测试结果
72+
const outputDir = path.resolve(__dirname, './result/edge-case')
73+
fs.mkdirSync(outputDir, { recursive: true })
74+
75+
for (const { fileName, fileContent } of genResult) {
76+
if (fileName.endsWith('.vue')) {
77+
fs.writeFileSync(path.join(outputDir, fileName), fileContent)
78+
}
79+
}
80+
})
81+
82+
test('should handle null and undefined values in JSDataBinding', async () => {
83+
// 创建包含null/undefined值的特殊schema
84+
const nullValueSchema = {
85+
...edgeCaseDataBindingSchema,
86+
pageSchema: [
87+
{
88+
...edgeCaseDataBindingSchema.pageSchema[0],
89+
children: [
90+
{
91+
componentName: 'TinyInput',
92+
props: {
93+
modelValue: {
94+
type: 'JSDataBinding',
95+
value: null // null值
96+
}
97+
}
98+
},
99+
{
100+
componentName: 'TinyInput',
101+
props: {
102+
modelValue: {
103+
type: 'JSDataBinding',
104+
value: undefined // undefined值
105+
}
106+
}
107+
}
108+
]
109+
}
110+
]
111+
}
112+
113+
const instance = generateApp()
114+
const res = await instance.generate(nullValueSchema)
115+
116+
// 不应该因为null/undefined而抛出异常
117+
expect(res.errors).toHaveLength(0)
118+
119+
const vueFile = res.genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
120+
expect(vueFile).toBeDefined()
121+
})
122+
})

0 commit comments

Comments
 (0)