Skip to content

Commit

Permalink
test: 添加单元测试 (#13)
Browse files Browse the repository at this point in the history
* test: 添加单元测试
  • Loading branch information
LittleSound authored May 26, 2022
1 parent bfdca54 commit 447e4e8
Show file tree
Hide file tree
Showing 4 changed files with 341 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ node_modules/
.vscode
.github
package.json
tsconfig.json
1 change: 1 addition & 0 deletions packages/.test/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './mount'
49 changes: 49 additions & 0 deletions packages/.test/mount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { InjectionKey, Ref } from 'vue'
import { createApp, defineComponent, h, provide, ref } from 'vue'

type InstanceType<V> = V extends { new (...arg: any[]): infer X } ? X : never
type VM<V> = InstanceType<V> & { unmount(): void }

export function mount<V>(Comp: V) {
const el = document.createElement('div')
const app = createApp(Comp)

const unmount = () => app.unmount()
const comp = app.mount(el) as any as VM<V>
comp.unmount = unmount
return comp
}

export function useSetup<V>(setup: () => V) {
const Comp = defineComponent({
setup,
render() {
return h('div', [])
},
})

return mount(Comp)
}

export const Key: InjectionKey<Ref<number>> = Symbol('num')

export function useInjectedSetup<V>(setup: () => V) {
const Comp = defineComponent({
setup,
render() {
return h('div', [])
},
})

const Provider = defineComponent({
components: Comp,
setup() {
provide(Key, ref(1))
},
render() {
return h('div', [])
},
})

return mount(Provider)
}
290 changes: 290 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import { nextTick, ref } from 'vue'
import { describe, expect, it } from 'vitest'
import { useSetup } from '../packages/.test'
import { useForm } from '.'

describe('useForm', () => {
it('should be defined', () => {
expect(useForm).toBeDefined()
})

it('should work', async () => {
const wrapper = useSetup(() => {
const { form, status } = useForm({
form: () => ({
name: '',
age: '',
}),
rule: {
age: val => !isNaN(+val) || 'expect numbers',
},
})
return { form, status }
})

// Is it modified
expect(wrapper.status.name.isDirty).false
expect(wrapper.status.age.isDirty).false

wrapper.form.age = 'abc'

// Is it modified
expect(wrapper.status.name.isDirty).false
expect(wrapper.status.age.isDirty).true

await nextTick()
// age
expect(wrapper.form.age).toBe('abc')
expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('expect numbers')
// name
expect(wrapper.form.name).toBe('')
expect(wrapper.status.name.isError).false
expect(wrapper.status.name.message).toBe('')

wrapper.form.age = '18'

// Is it modified
expect(wrapper.status.name.isDirty).false
expect(wrapper.status.age.isDirty).true

await nextTick()
// age
expect(wrapper.form.age).toBe('18')
expect(wrapper.status.age.isError).false
expect(wrapper.status.age.message).toBe('')
// name
expect(wrapper.form.name).toBe('')
expect(wrapper.status.name.isError).false
expect(wrapper.status.name.message).toBe('')

wrapper.unmount()
})

// 规则验证在第一次值更改后开始工作
it('Rule validation starts working after the first value change', async () => {
const wrapper = useSetup(() => {
const { form, status } = useForm({
form: () => ({
name: '',
age: '',
}),
rule: {
age: val => !!val || 'required',
name: val => !!val || 'required',
},
})
return { form, status }
})

await nextTick()
// age
expect(wrapper.status.age.isError).false
expect(wrapper.status.age.message).toBe('')
// name
expect(wrapper.status.name.isError).false
expect(wrapper.status.name.message).toBe('')

wrapper.form.age = '18'

// Is it modified
expect(wrapper.status.name.isDirty).false
expect(wrapper.status.age.isDirty).true

await nextTick()
// age
expect(wrapper.status.age.isError).false
expect(wrapper.status.age.message).toBe('')
// name
expect(wrapper.status.name.isError).false
expect(wrapper.status.name.message).toBe('')

wrapper.form.age = ''

// Is it modified
expect(wrapper.status.name.isDirty).false
expect(wrapper.status.age.isDirty).false

await nextTick()
// age
expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('required')
// name
expect(wrapper.status.name.isError).false
expect(wrapper.status.name.message).toBe('')

wrapper.unmount()
})

// 响应规则更改
it('Responding to rule changes', async () => {
const useI18n = () => {
const local = ref('en')
return {
local,
t: (key: string) => local.value === 'en' ? key : '必填',
}
}

const wrapper = useSetup(() => {
const { t, local } = useI18n()
const { form, status } = useForm({
form: () => ({
age: '',
}),
rule: {
age: val => !!val || t('required'),
},
})
return { form, status, local }
})

await nextTick()
expect(wrapper.status.age.isError).false
expect(wrapper.status.age.message).toBe('')

// Rule validation starts working after the first value change
wrapper.local = 'zh-CN'

await nextTick()
expect(wrapper.status.age.isError).false
expect(wrapper.status.age.message).toBe('')

wrapper.form.age = '18'
await nextTick()
wrapper.form.age = ''

await nextTick()
expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('必填')

wrapper.local = 'en'

await nextTick()
expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('required')

wrapper.unmount()
})

// 使用数组定义多个规则
it('Define multiple rules using an array', async () => {
const wrapper = useSetup(() => {
const { form, status } = useForm({
form: () => ({
age: '',
}),
rule: {
age: [
val => !!val || 'required',
val => !isNaN(+val) || 'expect numbers',
],
},
})
return { form, status }
})

wrapper.form.age = 'abc'

await nextTick()
expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('expect numbers')

wrapper.form.age = ''

await nextTick()
expect(wrapper.status.age.message).toBe('required')
expect(wrapper.status.age.isError).true

wrapper.unmount()
})

// 手动触发验证和清除错误
it('Manually trigger validation and clearErrors', async () => {
const wrapper = useSetup(() => {
const { form, status, clearErrors, verify } = useForm({
form: () => ({
age: '',
}),
rule: {
age: val => !!val || 'required',
},
})
return { form, status, clearErrors, verify }
})

wrapper.verify()

expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('required')

wrapper.status.age.setError('a error')

expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('a error')

// 等待后再次检查,确保不会被副作用覆盖
// Check again after waiting to make sure it is not overwritten by side effects
await nextTick()
expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('a error')

wrapper.clearErrors()

expect(wrapper.status.age.isError).false
expect(wrapper.status.age.message).toBe('')

wrapper.unmount()
})

// 表单重置功能
it('reset the form', async () => {
// Increments by one for each call
const counter = ((i = 0) => () => i++)()

const wrapper = useSetup(() => {
const { form, status, reset } = useForm({
form: () => ({
// The initial value is mutable
age: `${counter()}`,
isAgree: true,
}),
rule: {
isAgree: val => !!val || 'required',
age: [
val => !!val || 'required',
val => !isNaN(+val) || 'expect numbers',
],
},
})
return { form, status, reset }
})

wrapper.form.isAgree = false
wrapper.form.age = 'abc'

await nextTick()
// isAgree
expect(wrapper.status.isAgree.isError).true
expect(wrapper.status.isAgree.message).toBe('required')
// age
expect(wrapper.status.age.isError).true
expect(wrapper.status.age.message).toBe('expect numbers')

wrapper.reset()

await nextTick()
// isAgree
expect(wrapper.form.isAgree).toBe(true)
expect(wrapper.status.isAgree.isDirty).false
expect(wrapper.status.isAgree.isError).false
expect(wrapper.status.isAgree.message).toBe('')
// age
expect(wrapper.form.age).toBe('1')
expect(wrapper.status.age.isDirty).false
expect(wrapper.status.age.isError).false
expect(wrapper.status.age.message).toBe('')

wrapper.unmount()
})
})

0 comments on commit 447e4e8

Please sign in to comment.