-
Notifications
You must be signed in to change notification settings - Fork 668
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(types): add typescript declarations #15
Changes from 2 commits
80c68ff
1ab7d70
a0e9e63
49381d2
8279cf0
1d0a6fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,94 @@ | ||||
import Vue, { VNodeData, Component, ComponentOptions, FunctionalComponentOptions } from 'vue' | ||||
|
||||
/** | ||||
* Utility type to declare an extended Vue constructor | ||||
*/ | ||||
type VueClass<V extends Vue> = (new (...args: any[]) => V) & typeof Vue | ||||
|
||||
/** | ||||
* Utility type for a selector | ||||
*/ | ||||
type Selector = string | Component | ||||
|
||||
/** | ||||
* Utility type for slots | ||||
*/ | ||||
type Slots = { | ||||
[key: string]: (Component | string)[] | Component | string | ||||
} | ||||
|
||||
/** | ||||
* Utility type for stubs which can be a string of template as a shorthand | ||||
* If it is an array of string, the specified children are replaced by blank components | ||||
*/ | ||||
type Stubs = { | ||||
[key: string]: Component | string | boolean | ||||
} | string[] | ||||
|
||||
/** | ||||
* Base class of Wrapper and WrapperArray | ||||
* It has common methods on both Wrapper and WrapperArray | ||||
*/ | ||||
interface BaseWrapper { | ||||
contains (selector: Selector): boolean | ||||
exists (): boolean | ||||
|
||||
hasAttribute (attribute: string, value: string): boolean | ||||
hasClass (className: string): boolean | ||||
hasProp (prop: string, value: any): boolean | ||||
hasStyle (style: string, value: string): boolean | ||||
|
||||
is (selector: Selector): boolean | ||||
isEmpty (): boolean | ||||
isVueInstance (): boolean | ||||
|
||||
update (): void | ||||
setData (data: object): void | ||||
setProps (props: object): void | ||||
trigger (eventName: string, options?: object): void | ||||
} | ||||
|
||||
interface Wrapper<V extends Vue> extends BaseWrapper { | ||||
readonly vm: V | ||||
readonly element: HTMLElement | ||||
readonly options: WrapperOptions | ||||
|
||||
find<R extends Vue> (selector: Selector): Wrapper<R> | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can separate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing this out. // find<R extends Vue, Ctor extends VueClass<R> = VueClass<R>> (selector: Ctor): Wrapper<R>
// `find`'s type parameters will be `<Vue, typeof ClassComponent>`
const found = wrapper.find(ClassComponent) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly. VueJS really needs it. |
||||
findAll<R extends Vue> (selector: Selector): WrapperArray<R> | ||||
|
||||
html (): string | ||||
text (): string | ||||
name (): string | ||||
} | ||||
|
||||
interface WrapperArray<V extends Vue> extends BaseWrapper { | ||||
readonly length: number | ||||
|
||||
at (index: number): Wrapper<V> | ||||
} | ||||
|
||||
interface WrapperOptions { | ||||
attachedToDocument: boolean | ||||
} | ||||
|
||||
interface MountOptions<V extends Vue> extends ComponentOptions<V> { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder the status of vuejs/vue#5887. That PR will break this declaration. But 5887 does not seem to have activities... @ktsn @yyx990803 What's your opinion? I can change 5887 to have a compatible version by changing ComponentOptions, though. @DanielRosenwasser There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think vuejs/vue#5887 will not be merged soon since we need to have some migration time for the new declaration. So we can use the current declaration for now. |
||||
attachToDocument?: boolean | ||||
clone?: boolean | ||||
context?: VNodeData | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is only used by FunctionalComponent, can we make two separate options for different usage? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about that but I ended up to choose type declaration simplicity. Also I have no idea to declare if a constructor of functional component is passed to |
||||
localVue?: typeof Vue | ||||
intercept?: object | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @eddyerburgh I wonder why intercept is by default global. https://github.com/vuejs/vue-test-utils/blob/master/docs/en/api/mount.md vue-test-utils/src/lib/create-instance.js Line 40 in a2137fa
I know this option is used to be If users want to modify Vue's prototype globally, we can, if strongly desired, provide another global API like mount. But fairly users can just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't add them globally, it's just a poorly named function. I'll refactor to make that clear There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've refactored in 086f8ae |
||||
slots?: Slots | ||||
stub?: Stubs | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder why this API is called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you're right, stubs is more appropriate 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated 👍 |
||||
} | ||||
|
||||
type ShallowOptions<V extends Vue> = MountOptions<V> | ||||
|
||||
export declare function createLocalVue (): typeof Vue | ||||
|
||||
export declare function mount<V extends Vue, Ctor extends VueClass<V> = VueClass<V>> (component: Ctor, options?: MountOptions<V>): Wrapper<V> | ||||
export declare function mount<V extends Vue> (component: ComponentOptions<V>, options?: MountOptions<V>): Wrapper<V> | ||||
export declare function mount (component: FunctionalComponentOptions, options?: MountOptions<Vue>): Wrapper<Vue> | ||||
|
||||
export declare function shallow<V extends Vue, Ctor extends VueClass<V> = VueClass<V>> (component: Ctor, options?: ShallowOptions<V>): Wrapper<V> | ||||
export declare function shallow<V extends Vue> (component: ComponentOptions<V>, options?: ShallowOptions<V>): Wrapper<V> | ||||
export declare function shallow (component: FunctionalComponentOptions, options?: ShallowOptions<Vue>): Wrapper<Vue> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import Vuex from 'vuex' | ||
import { mount, createLocalVue } from '../' | ||
import { normalOptions, functionalOptions, Normal, ClassComponent } from './resources' | ||
|
||
/** | ||
* Should create wrapper vm based on (function) component options or constructors | ||
* The users can specify component type via the type parameter | ||
*/ | ||
const normalWrapper = mount<Normal>(normalOptions) | ||
const normalFoo: string = normalWrapper.vm.foo | ||
|
||
const classWrapper = mount<ClassComponent>(ClassComponent) | ||
const classFoo: string = classWrapper.vm.bar | ||
|
||
const functinalWrapper = mount(functionalOptions) | ||
|
||
/** | ||
* Test for mount options | ||
*/ | ||
const localVue = createLocalVue() | ||
localVue.use(Vuex) | ||
|
||
const store = new Vuex.Store({}) | ||
|
||
mount<ClassComponent>(ClassComponent, { | ||
attachToDocument: true, | ||
clone: true, | ||
localVue, | ||
intercept: { | ||
$store: store | ||
}, | ||
slots: { | ||
default: `<div>Foo</div>`, | ||
foo: [normalOptions, functionalOptions], | ||
bar: ClassComponent | ||
}, | ||
stub: { | ||
foo: normalOptions, | ||
bar: functionalOptions, | ||
baz: ClassComponent, | ||
qux: `<div>Test</div>` | ||
} | ||
}) | ||
|
||
mount(functionalOptions, { | ||
context: { | ||
props: { foo: 'test' } | ||
}, | ||
stub: ['child'] | ||
}) | ||
|
||
/** | ||
* MountOptions should receive Vue's component options | ||
*/ | ||
mount<ClassComponent>(ClassComponent, { | ||
propsData: { | ||
test: 'test' | ||
}, | ||
created () { | ||
this.bar | ||
} | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import Vue, { ComponentOptions, FunctionalComponentOptions } from 'vue' | ||
|
||
/** | ||
* Normal component options | ||
*/ | ||
export interface Normal extends Vue { | ||
foo: string | ||
} | ||
export const normalOptions: ComponentOptions<Normal> = { | ||
name: 'normal', | ||
data () { | ||
return { | ||
foo: 'bar' | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Functional component options | ||
*/ | ||
export const functionalOptions: FunctionalComponentOptions = { | ||
functional: true, | ||
render (h) { | ||
return h('div') | ||
} | ||
} | ||
|
||
/** | ||
* Component constructor declared with vue-class-component etc. | ||
*/ | ||
export class ClassComponent extends Vue { | ||
bar = 'bar' | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import Vuex from 'vuex' | ||
import { shallow, createLocalVue } from '../' | ||
import { normalOptions, functionalOptions, Normal, ClassComponent } from './resources' | ||
|
||
/** | ||
* Should create wrapper vm based on (function) component options or constructors | ||
* The users can specify component type via the type parameter | ||
*/ | ||
const normalWrapper = shallow<Normal>(normalOptions) | ||
const normalFoo: string = normalWrapper.vm.foo | ||
|
||
const classWrapper = shallow<ClassComponent>(ClassComponent) | ||
const classFoo: string = classWrapper.vm.bar | ||
|
||
const functinalWrapper = shallow(functionalOptions) | ||
|
||
/** | ||
* Test for shallow options | ||
*/ | ||
const localVue = createLocalVue() | ||
localVue.use(Vuex) | ||
|
||
const store = new Vuex.Store({}) | ||
|
||
shallow<ClassComponent>(ClassComponent, { | ||
attachToDocument: true, | ||
clone: true, | ||
localVue, | ||
intercept: { | ||
$store: store | ||
}, | ||
slots: { | ||
default: `<div>Foo</div>`, | ||
foo: [normalOptions, functionalOptions], | ||
baz: ClassComponent | ||
}, | ||
stub: { | ||
foo: normalOptions, | ||
bar: functionalOptions, | ||
baz: ClassComponent, | ||
qux: `<div>Test</div>` | ||
} | ||
}) | ||
|
||
shallow(functionalOptions, { | ||
context: { | ||
props: { foo: 'test' } | ||
}, | ||
stub: ['child'] | ||
}) | ||
|
||
/** | ||
* ShallowOptions should receive Vue's component options | ||
*/ | ||
shallow<ClassComponent>(ClassComponent, { | ||
propsData: { | ||
test: 'test' | ||
}, | ||
created () { | ||
this.bar | ||
} | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { mount } from '../' | ||
import { normalOptions, Normal, ClassComponent } from './resources' | ||
|
||
/** | ||
* Tests for BaseWrapper API | ||
*/ | ||
let wrapper = mount<Normal>(normalOptions) | ||
|
||
let bool: boolean = wrapper.contains('.foo') | ||
bool = wrapper.contains(normalOptions) | ||
bool = wrapper.contains(ClassComponent) | ||
|
||
bool = wrapper.exists() | ||
|
||
bool = wrapper.hasAttribute('foo', 'bar') | ||
bool = wrapper.hasClass('foo-class') | ||
bool = wrapper.hasProp('checked', true) | ||
bool = wrapper.hasStyle('color', 'red') | ||
|
||
bool = wrapper.is(normalOptions) | ||
bool = wrapper.isEmpty() | ||
bool = wrapper.isVueInstance() | ||
|
||
wrapper.update() | ||
wrapper.setData({ foo: 'bar' }) | ||
wrapper.setProps({ checked: true }) | ||
wrapper.trigger('mousedown.enter', { | ||
preventDefault: true | ||
}) | ||
|
||
/** | ||
* Tests for Wrapper API | ||
*/ | ||
wrapper.vm.foo | ||
wrapper.vm.$emit('event', 'arg') | ||
|
||
let el: HTMLElement = wrapper.element | ||
|
||
bool = wrapper.options.attachedToDocument | ||
|
||
wrapper = wrapper.find('.foo') | ||
let array = wrapper.findAll<Normal>(normalOptions) | ||
|
||
let str: string = wrapper.html() | ||
str = wrapper.text() | ||
str = wrapper.name() | ||
|
||
/** | ||
* Tests for WrapperArray API | ||
*/ | ||
let num: number = array.length | ||
wrapper = array.at(1) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"compilerOptions": { | ||
"module": "es2015", | ||
"moduleResolution": "node", | ||
"allowSyntheticDefaultImports": true, | ||
"strict": true, | ||
"noEmit": true | ||
}, | ||
"include": [ | ||
"**/*.ts" | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[key: string]: Component | string | true
https://github.com/vuejs/vue-test-utils/blob/master/src/lib/stub-components.js#L28-L31