Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions packages/vue-generator/test/testcases/data-binding/advanced.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { expect, test, describe } from 'vitest'
import path from 'path'
import fs from 'fs'
import { generateApp } from '@/generator/generateApp'
import { advancedDataBindingSchema } from './mockData'

describe('Advanced Data Binding', () => {
test('should generate v-model for nested object bindings', async () => {
const instance = generateApp()
const res = await instance.generate(advancedDataBindingSchema)
const { genResult, errors } = res

// 检查是否有错误
expect(errors).toHaveLength(0)

// 找到生成的 Vue 页面文件
const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
expect(vueFile).toBeDefined()

const content = vueFile.fileContent

// 验证包含 v-model 绑定
expect(content).toContain('v-model')

// 验证嵌套对象数据绑定
expect(content).toContain('v-model="state.formData.address.detail"')
expect(content).toContain('v-model="state.formData.address.zipCode"')

// 验证基础字段绑定
expect(content).toContain('v-model="state.formData.username"')
expect(content).toContain('v-model="state.formData.enabled"')
expect(content).toContain('v-model="state.formData.gender"')
expect(content).toContain('v-model="state.formData.remarks"')
expect(content).toContain('v-model="state.formData.category"')

// 写入测试结果文件
const outputDir = path.resolve(__dirname, './result/advanced')
fs.mkdirSync(outputDir, { recursive: true })

for (const { fileName, fileContent } of genResult) {
if (fileName.endsWith('.vue')) {
fs.writeFileSync(path.join(outputDir, fileName), fileContent)
}
}
})

test('should handle multiple component types with data binding', async () => {
const instance = generateApp()
const res = await instance.generate(advancedDataBindingSchema)
const { genResult } = res

const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
const content = vueFile.fileContent

// 统计 v-model 绑定数量
const vModelMatches = content.match(/v-model[^=]*="[^"]+"/g) || []

// 应该有8个 v-model 绑定
expect(vModelMatches).toHaveLength(8)

// 验证所有组件类型都正确生成
expect(content).toContain('<tiny-input')
expect(content).toContain('<tiny-switch')
expect(content).toContain('<tiny-radio')
expect(content).toContain('<textarea')
expect(content).toContain('<select')
})

test('should generate reactive state for nested objects', async () => {
const instance = generateApp()
const res = await instance.generate(advancedDataBindingSchema)
const { genResult } = res

const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
const content = vueFile.fileContent

// 验证嵌套状态对象结构
expect(content).toContain('formData: {')
expect(content).toContain('address: {')
expect(content).toContain("detail: ''")
expect(content).toContain("zipCode: ''")

// 验证状态是响应式的
expect(content).toContain('vue.reactive(')
})

test('should handle radio button groups correctly', async () => {
const instance = generateApp()
const res = await instance.generate(advancedDataBindingSchema)
const { genResult } = res

const vueFile = genResult.find((file) => file.fileName === 'AdvancedFormPage.vue')
const content = vueFile.fileContent

// 验证单选框组绑定到同一个字段
const genderBindings = content.match(/v-model="state\.formData\.gender"/g) || []
expect(genderBindings).toHaveLength(2) // 两个单选框绑定到同一字段

// 验证单选框的 value 属性
expect(content).toContain('value="male"')
expect(content).toContain('value="female"')
})
})
70 changes: 70 additions & 0 deletions packages/vue-generator/test/testcases/data-binding/basic.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { expect, test, describe } from 'vitest'
import path from 'path'
import fs from 'fs'
import { generateApp } from '@/generator/generateApp'
import { basicDataBindingSchema } from './mockData'

describe('Basic Data Binding', () => {
test('should generate v-model for basic form components', async () => {
const instance = generateApp()
const res = await instance.generate(basicDataBindingSchema)
const { genResult, errors } = res

// 检查是否有错误
expect(errors).toHaveLength(0)

// 找到生成的 Vue 页面文件
const vueFile = genResult.find((file) => file.fileName === 'BasicDataBindingPage.vue')
expect(vueFile).toBeDefined()

const content = vueFile.fileContent

// 验证包含 v-model 绑定
expect(content).toContain('v-model')

// 验证具体的数据绑定
expect(content).toContain('v-model="state.username"')
expect(content).toContain('v-model="state.role"')
expect(content).toContain('v-model="state.agreed"')

// 验证组件正确渲染
expect(content).toContain('<tiny-input')
expect(content).toContain('<tiny-select')
expect(content).toContain('<input')

// 验证状态定义
expect(content).toContain("username: ''")
expect(content).toContain("role: ''")
expect(content).toContain('agreed: false')

// 写入测试结果文件(可选,用于调试)
const outputDir = path.resolve(__dirname, './result/basic')
fs.mkdirSync(outputDir, { recursive: true })

for (const { fileName, fileContent } of genResult) {
if (fileName.endsWith('.vue')) {
fs.writeFileSync(path.join(outputDir, fileName), fileContent)
}
}
})

test('should handle different component types correctly', async () => {
const instance = generateApp()
const res = await instance.generate(basicDataBindingSchema)
const { genResult } = res

const vueFile = genResult.find((file) => file.fileName === 'BasicDataBindingPage.vue')
const content = vueFile.fileContent

// 验证不同组件类型的 v-model 生成
const vModelMatches = content.match(/v-model[^=]*="[^"]+"/g) || []

// 应该有3个 v-model 绑定
expect(vModelMatches).toHaveLength(3)

// 验证每个绑定都是正确的
expect(vModelMatches).toContain('v-model="state.username"')
expect(vModelMatches).toContain('v-model="state.role"')
expect(vModelMatches).toContain('v-model="state.agreed"')
})
})
122 changes: 122 additions & 0 deletions packages/vue-generator/test/testcases/data-binding/edge-case.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { expect, test, describe } from 'vitest'
import path from 'path'
import fs from 'fs'
import { generateApp } from '@/generator/generateApp'
import { edgeCaseDataBindingSchema } from './mockData'

describe('Edge Case Data Binding Tests', () => {
test('should handle empty field gracefully', async () => {
const instance = generateApp()
const res = await instance.generate(edgeCaseDataBindingSchema)
const { genResult, errors } = res

// 不应该因为空字段而报错
expect(errors).toHaveLength(0)

const vueFile = genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
expect(vueFile).toBeDefined()

const content = vueFile.fileContent

// 空字段应该不生成v-model或生成空的v-model
// 确保不会导致语法错误
expect(content).not.toContain('v-model=""')
expect(content).not.toContain('v-model="undefined"')
})

test('should remove this. prefix correctly', async () => {
const instance = generateApp()
const res = await instance.generate(edgeCaseDataBindingSchema)
const { genResult } = res

const vueFile = genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
const content = vueFile.fileContent

// 应该移除this.前缀
expect(content).toContain('v-model="state.withThis"')
expect(content).not.toContain('v-model="this.state.withThis"')
})

test('should handle deep nested field paths', async () => {
const instance = generateApp()
const res = await instance.generate(edgeCaseDataBindingSchema)
const { genResult } = res

const vueFile = genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
const content = vueFile.fileContent

// 深层嵌套路径应该正确处理
expect(content).toContain('v-model="state.level1.level2.level3.deepField"')

// 验证状态结构正确生成
expect(content).toContain('level1: {')
expect(content).toContain('level2: {')
expect(content).toContain('level3: {')
expect(content).toContain("deepField: ''")
})

test('should maintain correct state structure for nested objects', async () => {
const instance = generateApp()
const res = await instance.generate(edgeCaseDataBindingSchema)
const { genResult } = res

const vueFile = genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
const content = vueFile.fileContent

// 验证响应式状态结构
expect(content).toContain('vue.reactive(')
expect(content).toContain("noComponentType: ''")
expect(content).toContain("withThis: ''")

// 写入测试结果
const outputDir = path.resolve(__dirname, './result/edge-case')
fs.mkdirSync(outputDir, { recursive: true })

for (const { fileName, fileContent } of genResult) {
if (fileName.endsWith('.vue')) {
fs.writeFileSync(path.join(outputDir, fileName), fileContent)
}
}
})

test('should handle null and undefined values in JSDataBinding', async () => {
// 创建包含null/undefined值的特殊schema
const nullValueSchema = {
...edgeCaseDataBindingSchema,
pageSchema: [
{
...edgeCaseDataBindingSchema.pageSchema[0],
children: [
{
componentName: 'TinyInput',
props: {
modelValue: {
type: 'JSDataBinding',
value: null // null值
}
}
},
{
componentName: 'TinyInput',
props: {
modelValue: {
type: 'JSDataBinding',
value: undefined // undefined值
}
}
}
]
}
]
}

const instance = generateApp()
const res = await instance.generate(nullValueSchema)

// 不应该因为null/undefined而抛出异常
expect(res.errors).toHaveLength(0)

const vueFile = res.genResult.find((file) => file.fileName === 'EdgeCaseDataBindingPage.vue')
expect(vueFile).toBeDefined()
})
})
Loading