From 80c68ff8a4983826a1b1c42f25ee9205973bd0a5 Mon Sep 17 00:00:00 2001 From: ktsn Date: Mon, 17 Jul 2017 11:03:36 +0900 Subject: [PATCH 1/6] feat(types): add typescript declarations --- package.json | 8 +++- types/index.d.ts | 94 +++++++++++++++++++++++++++++++++++++++++ types/test/mount.ts | 62 +++++++++++++++++++++++++++ types/test/resources.ts | 33 +++++++++++++++ types/test/shallow.ts | 62 +++++++++++++++++++++++++++ types/test/wrapper.ts | 52 +++++++++++++++++++++++ types/tsconfig.json | 12 ++++++ yarn.lock | 4 ++ 8 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 types/index.d.ts create mode 100644 types/test/mount.ts create mode 100644 types/test/resources.ts create mode 100644 types/test/shallow.ts create mode 100644 types/test/wrapper.ts create mode 100644 types/tsconfig.json diff --git a/package.json b/package.json index 6882af111..fb6aff99f 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,11 @@ "version": "0.1.0", "description": "Utilities for testing Vue components.", "main": "dist/vue-test-utils.js", + "types": "types/index.d.ts", "files": [ "src", - "dist/*.js" + "dist/*.js", + "types/index.d.ts" ], "scripts": { "build": "node build/build.js", @@ -16,10 +18,11 @@ "lint:docs": "eslint --ext js,vue,md docs --ignore-path .gitignore", "lint:fix": "npm run lint -- --fix", "postinstall": "node build/install-hooks.js", - "test": "npm run lint && npm run lint:docs && npm run flow && npm run test:unit && npm run test:integration && npm run test:integration:karma", + "test": "npm run lint && npm run lint:docs && npm run flow && npm run test:types && npm run test:unit && npm run test:integration && npm run test:integration:karma", "test:integration": "cross-env BABEL_ENV=test mocha-webpack --webpack-config build/webpack.test.config.js test/integration/specs --recursive --require test/integration/setup/mocha.setup.js", "test:integration:karma": "cross-env BABEL_ENV=test TARGET=browser karma start test/integration/setup/karma.conf.js --single-run", "test:unit": "cross-env BABEL_ENV=test mocha-webpack --webpack-config build/webpack.test.config.js test/unit/specs --recursive --require test/unit/setup/mocha.setup.js", + "test:types": "tsc -p types", "release": "bash build/release.sh", "release:note": "node build/gen-release-note.js" }, @@ -72,6 +75,7 @@ "shelljs": "^0.7.8", "sinon": "^2.3.2", "sinon-chai": "^2.10.0", + "typescript": "^2.4.1", "vue": "^2.3.3", "vue-loader": "^12.2.1", "vue-router": "^2.6.0", diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 000000000..57a7a376d --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,94 @@ +import Vue, { VNodeData, Component, ComponentOptions, FunctionalComponentOptions } from 'vue' + +/** + * Utility type to declare an extended Vue constructor + */ +type VueClass = (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 extends BaseWrapper { + readonly vm: V + readonly element: HTMLElement + readonly options: WrapperOptions + + find (selector: Selector): Wrapper + findAll (selector: Selector): WrapperArray + + html (): string + text (): string + name (): string +} + +interface WrapperArray extends BaseWrapper { + readonly length: number + + at (index: number): Wrapper +} + +interface WrapperOptions { + attachedToDocument: boolean +} + +interface MountOptions extends ComponentOptions { + attachToDocument?: boolean + clone?: boolean + context?: VNodeData + localVue?: typeof Vue + intercept?: object + slots?: Slots + stub?: Stubs +} + +type ShallowOptions = MountOptions + +export declare function createLocalVue (): typeof Vue + +export declare function mount = VueClass> (component: Ctor, options?: MountOptions): Wrapper +export declare function mount (component: ComponentOptions, options?: MountOptions): Wrapper +export declare function mount (component: FunctionalComponentOptions, options?: MountOptions): Wrapper + +export declare function shallow = VueClass> (component: Ctor, options?: ShallowOptions): Wrapper +export declare function shallow (component: ComponentOptions, options?: ShallowOptions): Wrapper +export declare function shallow (component: FunctionalComponentOptions, options?: ShallowOptions): Wrapper diff --git a/types/test/mount.ts b/types/test/mount.ts new file mode 100644 index 000000000..dc17a5145 --- /dev/null +++ b/types/test/mount.ts @@ -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(normalOptions) +const normalFoo: string = normalWrapper.vm.foo + +const classWrapper = mount(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, { + attachToDocument: true, + clone: true, + localVue, + intercept: { + $store: store + }, + slots: { + default: `
Foo
`, + foo: [normalOptions, functionalOptions], + bar: ClassComponent + }, + stub: { + foo: normalOptions, + bar: functionalOptions, + baz: ClassComponent, + qux: `
Test
` + } +}) + +mount(functionalOptions, { + context: { + props: { foo: 'test' } + }, + stub: ['child'] +}) + +/** + * MountOptions should receive Vue's component options + */ +mount(ClassComponent, { + propsData: { + test: 'test' + }, + created () { + this.bar + } +}) diff --git a/types/test/resources.ts b/types/test/resources.ts new file mode 100644 index 000000000..4195c5c8d --- /dev/null +++ b/types/test/resources.ts @@ -0,0 +1,33 @@ +import Vue, { ComponentOptions, FunctionalComponentOptions } from 'vue' + +/** + * Normal component options + */ +export interface Normal extends Vue { + foo: string +} +export const normalOptions: ComponentOptions = { + 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' +} diff --git a/types/test/shallow.ts b/types/test/shallow.ts new file mode 100644 index 000000000..3da15817e --- /dev/null +++ b/types/test/shallow.ts @@ -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(normalOptions) +const normalFoo: string = normalWrapper.vm.foo + +const classWrapper = shallow(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, { + attachToDocument: true, + clone: true, + localVue, + intercept: { + $store: store + }, + slots: { + default: `
Foo
`, + foo: [normalOptions, functionalOptions], + baz: ClassComponent + }, + stub: { + foo: normalOptions, + bar: functionalOptions, + baz: ClassComponent, + qux: `
Test
` + } +}) + +shallow(functionalOptions, { + context: { + props: { foo: 'test' } + }, + stub: ['child'] +}) + +/** + * ShallowOptions should receive Vue's component options + */ +shallow(ClassComponent, { + propsData: { + test: 'test' + }, + created () { + this.bar + } +}) diff --git a/types/test/wrapper.ts b/types/test/wrapper.ts new file mode 100644 index 000000000..666664e8d --- /dev/null +++ b/types/test/wrapper.ts @@ -0,0 +1,52 @@ +import { mount } from '../' +import { normalOptions, Normal, ClassComponent } from './resources' + +/** + * Tests for BaseWrapper API + */ +let wrapper = mount(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('.bar') + +let str: string = wrapper.html() +str = wrapper.text() +str = wrapper.name() + +/** + * Tests for WrapperArray API + */ +let num: number = array.length +wrapper = array.at(1) diff --git a/types/tsconfig.json b/types/tsconfig.json new file mode 100644 index 000000000..c0e76be17 --- /dev/null +++ b/types/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "es2015", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "strict": true, + "noEmit": true + }, + "include": [ + "**/*.ts" + ] +} diff --git a/yarn.lock b/yarn.lock index 049856692..636ffdd5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6342,6 +6342,10 @@ typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +typescript@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc" + uglify-js@^2.6, uglify-js@^2.8.27: version "2.8.27" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.27.tgz#47787f912b0f242e5b984343be8e35e95f694c9c" From 1ab7d7023477f2aaacc34dcae81505a3d7348957 Mon Sep 17 00:00:00 2001 From: ktsn Date: Mon, 17 Jul 2017 12:24:20 +0900 Subject: [PATCH 2/6] fix(types): move type parameter of WrapperArray#at to WrapperArray itself --- types/index.d.ts | 6 +++--- types/test/wrapper.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index 57a7a376d..dc0d5430b 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -54,17 +54,17 @@ interface Wrapper extends BaseWrapper { readonly options: WrapperOptions find (selector: Selector): Wrapper - findAll (selector: Selector): WrapperArray + findAll (selector: Selector): WrapperArray html (): string text (): string name (): string } -interface WrapperArray extends BaseWrapper { +interface WrapperArray extends BaseWrapper { readonly length: number - at (index: number): Wrapper + at (index: number): Wrapper } interface WrapperOptions { diff --git a/types/test/wrapper.ts b/types/test/wrapper.ts index 666664e8d..48af8a176 100644 --- a/types/test/wrapper.ts +++ b/types/test/wrapper.ts @@ -39,7 +39,7 @@ let el: HTMLElement = wrapper.element bool = wrapper.options.attachedToDocument wrapper = wrapper.find('.foo') -let array = wrapper.findAll('.bar') +let array = wrapper.findAll(normalOptions) let str: string = wrapper.html() str = wrapper.text() From a0e9e636f64f0739f22ae45681e7ef9f187dbde7 Mon Sep 17 00:00:00 2001 From: ktsn Date: Mon, 17 Jul 2017 14:34:30 +0900 Subject: [PATCH 3/6] fix(types): stub should not accept false value --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index dc0d5430b..a196ebb7c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -22,7 +22,7 @@ type Slots = { * If it is an array of string, the specified children are replaced by blank components */ type Stubs = { - [key: string]: Component | string | boolean + [key: string]: Component | string | true } | string[] /** From 49381d282705eec926654f54ed316136c23e71b0 Mon Sep 17 00:00:00 2001 From: ktsn Date: Mon, 17 Jul 2017 15:22:36 +0900 Subject: [PATCH 4/6] fix(types): change find/findAll type to infer vm type from an argument --- types/index.d.ts | 11 +++++++++-- types/test/wrapper.ts | 15 +++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index a196ebb7c..b5a74eb84 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -53,8 +53,15 @@ interface Wrapper extends BaseWrapper { readonly element: HTMLElement readonly options: WrapperOptions - find (selector: Selector): Wrapper - findAll (selector: Selector): WrapperArray + find = VueClass> (selector: Ctor): Wrapper + find (selector: ComponentOptions): Wrapper + find (selector: FunctionalComponentOptions): Wrapper + find (selector: string): Wrapper + + findAll = VueClass> (selector: Ctor): WrapperArray + findAll (selector: ComponentOptions): WrapperArray + findAll (selector: FunctionalComponentOptions): WrapperArray + findAll (selector: string): WrapperArray html (): string text (): string diff --git a/types/test/wrapper.ts b/types/test/wrapper.ts index 48af8a176..412423dff 100644 --- a/types/test/wrapper.ts +++ b/types/test/wrapper.ts @@ -1,5 +1,5 @@ import { mount } from '../' -import { normalOptions, Normal, ClassComponent } from './resources' +import { normalOptions, functionalOptions, Normal, ClassComponent } from './resources' /** * Tests for BaseWrapper API @@ -38,8 +38,15 @@ let el: HTMLElement = wrapper.element bool = wrapper.options.attachedToDocument -wrapper = wrapper.find('.foo') -let array = wrapper.findAll(normalOptions) +let found = wrapper.find('.foo') +found = wrapper.find(normalOptions) +found = wrapper.find(functionalOptions) +found = wrapper.find(ClassComponent) + +let array = wrapper.findAll('.bar') +array = wrapper.findAll(normalOptions) +array = wrapper.findAll(functionalOptions) +array = wrapper.findAll(ClassComponent) let str: string = wrapper.html() str = wrapper.text() @@ -49,4 +56,4 @@ str = wrapper.name() * Tests for WrapperArray API */ let num: number = array.length -wrapper = array.at(1) +found = array.at(1) From 8279cf06465b2b254cb085f399ccbe6f45d5b91a Mon Sep 17 00:00:00 2001 From: ktsn Date: Mon, 17 Jul 2017 15:23:44 +0900 Subject: [PATCH 5/6] fix(types): remove type parameters of mount/shallow with options object --- types/test/mount.ts | 2 +- types/test/shallow.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/types/test/mount.ts b/types/test/mount.ts index dc17a5145..8ec16ed5b 100644 --- a/types/test/mount.ts +++ b/types/test/mount.ts @@ -6,7 +6,7 @@ import { normalOptions, functionalOptions, Normal, ClassComponent } from './reso * Should create wrapper vm based on (function) component options or constructors * The users can specify component type via the type parameter */ -const normalWrapper = mount(normalOptions) +const normalWrapper = mount(normalOptions) const normalFoo: string = normalWrapper.vm.foo const classWrapper = mount(ClassComponent) diff --git a/types/test/shallow.ts b/types/test/shallow.ts index 3da15817e..908ff678b 100644 --- a/types/test/shallow.ts +++ b/types/test/shallow.ts @@ -6,7 +6,7 @@ import { normalOptions, functionalOptions, Normal, ClassComponent } from './reso * Should create wrapper vm based on (function) component options or constructors * The users can specify component type via the type parameter */ -const normalWrapper = shallow(normalOptions) +const normalWrapper = shallow(normalOptions) const normalFoo: string = normalWrapper.vm.foo const classWrapper = shallow(ClassComponent) From 1d0a6fdc1ea43e60f8e3d444ceb438cb21427ba7 Mon Sep 17 00:00:00 2001 From: ktsn Date: Mon, 17 Jul 2017 17:20:40 +0900 Subject: [PATCH 6/6] fix(types): rename stub to stubs in mount/shallow options --- types/index.d.ts | 2 +- types/test/mount.ts | 4 ++-- types/test/shallow.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index b5a74eb84..7a9959fff 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -85,7 +85,7 @@ interface MountOptions extends ComponentOptions { localVue?: typeof Vue intercept?: object slots?: Slots - stub?: Stubs + stubs?: Stubs } type ShallowOptions = MountOptions diff --git a/types/test/mount.ts b/types/test/mount.ts index 8ec16ed5b..f57da5948 100644 --- a/types/test/mount.ts +++ b/types/test/mount.ts @@ -34,7 +34,7 @@ mount(ClassComponent, { foo: [normalOptions, functionalOptions], bar: ClassComponent }, - stub: { + stubs: { foo: normalOptions, bar: functionalOptions, baz: ClassComponent, @@ -46,7 +46,7 @@ mount(functionalOptions, { context: { props: { foo: 'test' } }, - stub: ['child'] + stubs: ['child'] }) /** diff --git a/types/test/shallow.ts b/types/test/shallow.ts index 908ff678b..a1537d366 100644 --- a/types/test/shallow.ts +++ b/types/test/shallow.ts @@ -34,7 +34,7 @@ shallow(ClassComponent, { foo: [normalOptions, functionalOptions], baz: ClassComponent }, - stub: { + stubs: { foo: normalOptions, bar: functionalOptions, baz: ClassComponent, @@ -46,7 +46,7 @@ shallow(functionalOptions, { context: { props: { foo: 'test' } }, - stub: ['child'] + stubs: ['child'] }) /**