generated from sachinraja/ts-lib-starter
-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
4 changed files
with
341 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,4 @@ node_modules/ | |
.vscode | ||
.github | ||
package.json | ||
tsconfig.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './mount' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
}) | ||
}) |