/* eslint-disable @typescript-eslint/ban-types */ import { defineComponent } from 'vue' import { mount } from '@vue/test-utils' import { describe, expect, it, vi } from 'vitest' import { expectTypeOf } from 'expect-type' import { buildProp, buildProps, definePropType, keysOf, mutable } from '../..' import type { propKey } from '../..' import type { ExtractPropTypes, PropType } from 'vue' describe('buildProp', () => { it('Only type', () => { expectTypeOf( buildProp({ type: definePropType<'a' | 'b'>(String), }) ).toEqualTypeOf<{ readonly type: PropType<'a' | 'b'> readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Only values', () => { expectTypeOf( buildProp({ values: [1, 2, 3, 4], } as const) ).toEqualTypeOf<{ readonly type: PropType<1 | 2 | 3 | 4> readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Type and values', () => { expectTypeOf( buildProp({ type: definePropType(Array), values: [1, 2, 3, 4], } as const) ).toEqualTypeOf<{ readonly type: PropType<1 | 2 | 3 | 4 | number[]> readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Values and validator', () => { expectTypeOf( buildProp({ values: ['a', 'b', 'c'], validator: (val: unknown): val is number => typeof val === 'number', } as const) ).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Values and required', () => { expectTypeOf( buildProp({ values: ['a', 'b', 'c'], required: true, } as const) ).toEqualTypeOf<{ readonly type: PropType<'a' | 'b' | 'c'> readonly required: true readonly default?: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Value and default', () => { expectTypeOf( buildProp({ values: ['a', 'b', 'c'], required: false, default: 'b', } as const) ).toEqualTypeOf<{ readonly type: PropType<'a' | 'b' | 'c'> readonly required: false readonly default: 'b' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Type and Array default value', () => { expectTypeOf( buildProp({ type: definePropType(Array), default: () => mutable(['a', 'b'] as const), } as const) ).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: ['a', 'b'] readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Type and Object default value', () => { interface Options { key: string } expectTypeOf( buildProp({ type: definePropType(Object), default: () => mutable({ key: 'value' } as const), } as const) ).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: { key: 'value' } readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Type, validator and Object default value', () => { interface Options { key: string } expectTypeOf( buildProp({ type: definePropType(Object), default: () => ({ key: 'value' }), validator: (val: unknown): val is string => true, } as const) ).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: { key: string } readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Type, validator, required', () => { expectTypeOf( buildProp({ type: definePropType<'a' | 'b' | 'c'>(String), required: true, validator: (val: unknown): val is number => true, } as const) ).toEqualTypeOf<{ readonly type: PropType readonly required: true readonly default?: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Normal type', () => { expectTypeOf( buildProp({ type: String, }) ).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Normal types', () => { expectTypeOf(buildProp({ type: [String, Number, Boolean] })).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Normal type and values', () => { expectTypeOf( buildProp({ type: String, values: ['1', '2', '3'], } as const) ).toEqualTypeOf<{ readonly type: PropType<'1' | '2' | '3'> readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Required and validator', () => { expectTypeOf( buildProp({ required: true, validator: (val: unknown): val is string => true, } as const) ).toEqualTypeOf<{ readonly type: PropType readonly required: true readonly default?: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Required and validator', () => { expectTypeOf( buildProp({ values: keysOf({ a: 'a', b: 'b' }), default: 'a', } as const) ).toEqualTypeOf<{ readonly type: PropType<'a' | 'b'> readonly required: false readonly default: 'a' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Type and default value', () => { expectTypeOf( buildProp({ type: definePropType<{ key: 'a' | 'b' | 'c' } | undefined>(Object), default: () => mutable({ key: 'a' } as const), } as const) ).toEqualTypeOf<{ readonly type: PropType<{ key: 'a' | 'b' | 'c' } | undefined> readonly required: false readonly default: { key: 'a' } readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('Type and default value', () => { expectTypeOf( buildProp({ type: [String, Number], default: '', } as const) ).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: '' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('default value is empty object', () => { expectTypeOf( buildProp({ type: Object, default: () => mutable({} as const), } as const) ).toEqualTypeOf<{ readonly type: PropType> readonly required: false readonly default: {} readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) it('extract', () => { const props = { key1: buildProp({ type: String, required: true, }), key2: buildProp({ type: [String, Number], required: true, }), } as const type Extracted = ExtractPropTypes expectTypeOf().toEqualTypeOf<{ readonly key1: string readonly key2: string | number }>() }) }) describe('buildProps', () => { it('test buildProps', () => { const propsCommon = buildProps({ type: { type: String, default: 'hello', }, } as const) const props = buildProps({ ...propsCommon, key1: { type: definePropType<'a' | 'b'>(String), }, key2: { values: [1, 2, 3, 4], }, key3: { values: [1, 2, 3, 4], default: 2, }, key4: { values: keysOf({ a: 'a', b: 'b' }), default: 'a', }, key5: Boolean, key6: String, key7: null, key8: Object, key9: Date, key10: Set, key11: undefined, // nested key12: buildProp({ type: String, } as const), // default generator key13: { type: [String, Number, Function], default: () => '123' as const, } as const, key14: { type: Function, default: () => '123' as const, } as const, key15: { type: Function, default: () => () => '123' as const, } as const, key16: { type: String, default: () => '123' as const, } as const, } as const) expectTypeOf(props.type).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: 'hello' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() expectTypeOf(props.key1).toEqualTypeOf<{ readonly type: PropType<'a' | 'b'> readonly required: false readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true readonly default: undefined }>() expectTypeOf(props.key2).toEqualTypeOf<{ readonly type: PropType<1 | 2 | 3 | 4> readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() expectTypeOf(props.key3).toEqualTypeOf<{ readonly type: PropType<1 | 2 | 3 | 4> readonly required: false readonly default: 2 readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() expectTypeOf(props.key4).toEqualTypeOf<{ readonly type: PropType<'a' | 'b'> readonly required: false readonly default: 'a' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() expectTypeOf(props.key5).toEqualTypeOf() expectTypeOf(props.key6).toEqualTypeOf() expectTypeOf(props.key7).toEqualTypeOf() expectTypeOf(props.key8).toEqualTypeOf() expectTypeOf(props.key9).toEqualTypeOf() expectTypeOf(props.key10).toEqualTypeOf() expectTypeOf(props.key11).toEqualTypeOf() expectTypeOf(props.key12).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: undefined readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() expectTypeOf(props.key13).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: '123' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() expectTypeOf(props.key14).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: () => '123' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() expectTypeOf(props.key15).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: () => () => '123' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() expectTypeOf(props.key16).toEqualTypeOf<{ readonly type: PropType readonly required: false readonly default: '123' readonly validator: ((val: unknown) => boolean) | undefined [propKey]: true }>() }) }) describe('runtime', () => { it('default value', () => { const warnHandler = vi.fn() const Foo = defineComponent({ props: buildProps({ bar: { type: Boolean }, baz: { values: ['a', 'b', 'c'] }, qux: { values: ['a', 'b', 'c'], required: true }, qux2: { values: ['a', 'b', 'c'], required: true }, } as const), template: `{{ $props }}`, }) const props = mount(Foo as any, { props: { baz: undefined, qux2: undefined, }, global: { config: { warnHandler, }, }, }).props() expect(props.bar).toBe(false) expect(props.baz).toBe(undefined) expect(warnHandler.mock.calls[0][0]).toBe('Missing required prop: "qux"') expect(warnHandler.mock.calls[1][0]).toBe( 'Invalid prop: validation failed for prop "qux2". Expected one of ["a", "b", "c"], got value undefined.' ) }) })