Skip to content

Commit f03392f

Browse files
committed
Merge branch 'master' into lachlan/types
2 parents 9a5f84e + e6586be commit f03392f

18 files changed

+498
-114
lines changed

package.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@vue/test-utils",
3-
"version": "2.0.0-alpha.4",
3+
"version": "2.0.0-alpha.6",
44
"license": "MIT",
55
"main": "dist/vue-test-utils.cjs.js",
66
"browser": "dist/vue-test-utils.browser.js",
@@ -12,10 +12,6 @@
1212
"README.md",
1313
"dist/index.d.ts"
1414
],
15-
"dependencies": {
16-
"dom-event-types": "^1.0.0",
17-
"lodash": "^4.17.15"
18-
},
1915
"devDependencies": {
2016
"@babel/core": "^7.9.0",
2117
"@babel/preset-env": "^7.8.4",
@@ -26,11 +22,12 @@
2622
"@rollup/plugin-replace": "^2.3.2",
2723
"@types/estree": "^0.0.42",
2824
"@types/jest": "^24.9.1",
29-
"@types/lodash": "^4.14.149",
3025
"@types/node": "12.12.35",
26+
"@vue/compiler-dom": "^3.0.0-beta.12",
3127
"@vue/compiler-sfc": "^3.0.0-beta.12",
3228
"babel-jest": "^25.2.3",
3329
"babel-preset-jest": "^25.2.1",
30+
"dom-event-types": "^1.0.0",
3431
"flush-promises": "^1.0.2",
3532
"husky": "^4.2.3",
3633
"jest": "^25.1.0",
@@ -48,6 +45,7 @@
4845
"vuex": "^4.0.0-beta.1"
4946
},
5047
"peerDependencies": {
48+
"@vue/compiler-dom": "^3.0.0-beta.12",
5149
"@vue/compiler-sfc": "^3.0.0-beta.12",
5250
"vue": "^3.0.0-beta.12"
5351
},

rollup.config.js

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ function createEntry(options) {
2626
external: [
2727
'vue',
2828
'@vue/compiler-dom',
29-
'lodash/mergeWith',
30-
'lodash/isString'
3129
],
3230
plugins: [
3331
replace({
@@ -43,30 +41,24 @@ function createEntry(options) {
4341
globals: {
4442
vue: 'Vue',
4543
'@vue/compiler-dom': 'VueCompilerDOM',
46-
'lodash/mergeWith': '_.mergeWith',
47-
'lodash/isString': '_.isString',
4844
}
4945
}
5046
}
5147

52-
if (['es', 'cjs'].includes(format)) {
53-
config.external.push('dom-event-types')
54-
}
55-
5648
if (format === 'es') {
57-
config.output.file = isBrowser ? pkg.browser : pkg.module
49+
config.output.file = pkg.module
5850
}
5951
if (format === 'cjs') {
6052
config.output.file = pkg.main
6153
}
62-
console.log('file is', config.output.file)
54+
console.log(`Building ${format}: ${config.output.file}`)
6355

6456
config.plugins.push(
6557
ts({
6658
check: format === 'es' && isBrowser,
6759
tsconfigOverride: {
6860
compilerOptions: {
69-
declaration: format === 'es' && isBrowser,
61+
declaration: format === 'es',
7062
target: 'es5', // not sure what this should be?
7163
module: format === 'cjs' ? 'es2015' : 'esnext'
7264
},
@@ -80,7 +72,6 @@ function createEntry(options) {
8072

8173
export default [
8274
createEntry({ format: 'es', input: 'src/index.ts', isBrowser: false }),
83-
createEntry({ format: 'es', input: 'src/index.ts', isBrowser: true }),
8475
createEntry({ format: 'iife', input: 'src/index.ts', isBrowser: true }),
8576
createEntry({ format: 'cjs', input: 'src/index.ts', isBrowser: false }),
8677
]

src/create-dom-event.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as eventTypes from 'dom-event-types'
1+
import eventTypes from 'dom-event-types'
22

33
interface TriggerOptions {
44
code?: String

src/dom-wrapper.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,23 @@ export class DOMWrapper<ElementType extends Element> {
1010
this.element = element
1111
}
1212

13-
classes(className?: string) {
13+
classes(): string[]
14+
classes(className: string): boolean
15+
classes(className?: string): string[] | boolean {
1416
const classes = this.element.classList
1517

1618
if (className) return classes.contains(className)
1719

1820
return Array.from(classes)
1921
}
2022

21-
attributes(key?: string) {
22-
const attributes = this.element.attributes
23-
const attributeMap = {}
24-
for (let i = 0; i < attributes.length; i++) {
25-
const att = attributes.item(i)
26-
attributeMap[att.localName] = att.value
23+
attributes(): { [key: string]: string }
24+
attributes(key: string): string
25+
attributes(key?: string): { [key: string]: string } | string {
26+
const attributes = Array.from(this.element.attributes)
27+
const attributeMap: Record<string, string> = {}
28+
for (const attribute of attributes) {
29+
attributeMap[attribute.localName] = attribute.value
2730
}
2831

2932
return key ? attributeMap[key] : attributeMap

src/mount.ts

Lines changed: 77 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@ import {
66
VNodeNormalizedChildren,
77
transformVNodeArgs,
88
reactive,
9+
FunctionalComponent,
910
ComponentPublicInstance,
1011
ComponentOptionsWithObjectProps,
1112
ComponentOptionsWithArrayProps,
1213
ComponentOptionsWithoutProps,
1314
ExtractPropTypes,
14-
Component
15+
Component,
16+
AppConfig,
17+
VNodeProps
1518
} from 'vue'
1619

1720
import { config } from './config'
1821
import { GlobalMountOptions } from './types'
19-
import { mergeGlobalProperties, isString } from './utils'
22+
import { mergeGlobalProperties } from './utils'
2023
import { processSlot } from './utils/compileSlots'
2124
import { createWrapper, VueWrapper } from './vue-wrapper'
2225
import { attachEmitListener } from './emitMixin'
@@ -27,7 +30,6 @@ import {
2730
MOUNT_PARENT_NAME
2831
} from './constants'
2932
import { stubComponents } from './stubs'
30-
import { parse } from '@vue/compiler-dom'
3133

3234
type Slot = VNode | string | { render: Function } | Function
3335

@@ -53,6 +55,11 @@ type ExtractComponent<T> = T extends { new (): infer PublicInstance }
5355
? PublicInstance
5456
: any
5557

58+
// Functional component
59+
export function mount<TestedComponent extends FunctionalComponent>(
60+
originalComponent: TestedComponent,
61+
options?: MountingOptions<any>
62+
): VueWrapper<ComponentPublicInstance>
5663
// Component declared with defineComponent
5764
export function mount<TestedComponent extends ComponentPublicInstance>(
5865
originalComponent: { new (): TestedComponent } & Component,
@@ -80,50 +87,68 @@ export function mount(
8087
originalComponent: any,
8188
options?: MountingOptions<any>
8289
): VueWrapper<any> {
83-
const component = { ...originalComponent }
90+
// normalise the incoming component
91+
const component =
92+
typeof originalComponent === 'function'
93+
? defineComponent({
94+
setup: (_, { attrs, slots }) => () =>
95+
h(originalComponent, attrs, slots)
96+
})
97+
: { ...originalComponent }
8498

8599
const el = document.createElement('div')
86100
el.id = MOUNT_ELEMENT_ID
87101

88102
if (options?.attachTo) {
89-
const to = isString(options.attachTo)
90-
? document.querySelector(options.attachTo)
91-
: options.attachTo
92-
93-
if (!to) {
94-
throw new Error(
95-
`Unable to find the element matching the selector ${options.attachTo} given as the \`attachTo\` option`
96-
)
103+
let to: Element | null
104+
if (typeof options.attachTo === 'string') {
105+
to = document.querySelector(options.attachTo)
106+
if (!to) {
107+
throw new Error(
108+
`Unable to find the element matching the selector ${options.attachTo} given as the \`attachTo\` option`
109+
)
110+
}
111+
} else {
112+
to = options.attachTo
97113
}
114+
98115
to.appendChild(el)
99116
}
100117

101118
// handle any slots passed via mounting options
102119
const slots: VNodeNormalizedChildren =
103120
options?.slots &&
104-
Object.entries(options.slots).reduce((acc, [name, slot]) => {
105-
// case of an SFC getting passed
106-
if (typeof slot === 'object' && 'render' in slot) {
107-
acc[name] = slot.render
108-
return acc
109-
}
121+
Object.entries(options.slots).reduce(
122+
(
123+
acc: { [key: string]: Function },
124+
[name, slot]: [string, Slot]
125+
): { [key: string]: Function } => {
126+
// case of an SFC getting passed
127+
if (typeof slot === 'object' && 'render' in slot) {
128+
acc[name] = slot.render
129+
return acc
130+
}
110131

111-
if (typeof slot === 'function') {
112-
acc[name] = slot
113-
return acc
114-
}
132+
if (typeof slot === 'function') {
133+
acc[name] = slot
134+
return acc
135+
}
115136

116-
if (typeof slot === 'object') {
117-
acc[name] = () => slot
118-
return acc
119-
}
137+
if (typeof slot === 'object') {
138+
acc[name] = () => slot
139+
return acc
140+
}
141+
142+
if (typeof slot === 'string') {
143+
// slot is most probably a scoped slot string or a plain string
144+
acc[name] = (props: VNodeProps) => h(processSlot(slot), props)
145+
return acc
146+
}
120147

121-
if (typeof slot === 'string') {
122-
// slot is most probably a scoped slot string or a plain string
123-
acc[name] = (props) => h(processSlot(slot), props)
124148
return acc
125-
}
126-
}, {})
149+
},
150+
{}
151+
)
127152

128153
// override component data with mounting options data
129154
if (options?.data) {
@@ -142,6 +167,9 @@ export function mount(
142167
ref: MOUNT_COMPONENT_REF
143168
})
144169

170+
const global = mergeGlobalProperties(config.global, options?.global)
171+
component.components = { ...component.components, ...global.components }
172+
145173
// create the wrapper component
146174
const Parent = defineComponent({
147175
name: MOUNT_PARENT_NAME,
@@ -160,14 +188,15 @@ export function mount(
160188

161189
// create the app
162190
const app = createApp(Parent)
163-
const global = mergeGlobalProperties(config.global, options?.global)
164191

165192
// global mocks mixin
166193
if (global?.mocks) {
167194
const mixin = {
168195
beforeCreate() {
169-
for (const [k, v] of Object.entries(global.mocks)) {
170-
this[k] = v
196+
for (const [k, v] of Object.entries(
197+
global.mocks as { [key: string]: any }
198+
)) {
199+
;(this as any)[k] = v
171200
}
172201
}
173202
}
@@ -176,34 +205,37 @@ export function mount(
176205
}
177206

178207
// AppConfig
179-
if (global?.config) {
180-
for (const [k, v] of Object.entries(global.config)) {
208+
if (global.config) {
209+
for (const [k, v] of Object.entries(global.config) as [
210+
keyof Omit<AppConfig, 'isNativeTag'>,
211+
any
212+
][]) {
181213
app.config[k] = v
182214
}
183215
}
184216

185217
// use and plugins from mounting options
186-
if (global?.plugins) {
218+
if (global.plugins) {
187219
for (const use of global.plugins) app.use(use)
188220
}
189221

190222
// use any mixins from mounting options
191-
if (global?.mixins) {
223+
if (global.mixins) {
192224
for (const mixin of global.mixins) app.mixin(mixin)
193225
}
194226

195-
if (global?.components) {
227+
if (global.components) {
196228
for (const key of Object.keys(global.components))
197229
app.component(key, global.components[key])
198230
}
199231

200-
if (global?.directives) {
232+
if (global.directives) {
201233
for (const key of Object.keys(global.directives))
202234
app.directive(key, global.directives[key])
203235
}
204236

205237
// provide any values passed via provides mounting option
206-
if (global?.provide) {
238+
if (global.provide) {
207239
for (const key of Reflect.ownKeys(global.provide)) {
208240
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/1863
209241
app.provide(key, global.provide[key])
@@ -214,8 +246,8 @@ export function mount(
214246
app.mixin(attachEmitListener())
215247

216248
// stubs
217-
if (options?.global?.stubs || options?.shallow) {
218-
stubComponents(options?.global?.stubs, options?.shallow)
249+
if (global.stubs || options?.shallow) {
250+
stubComponents(global.stubs, options?.shallow)
219251
} else {
220252
transformVNodeArgs()
221253
}
@@ -227,9 +259,6 @@ export function mount(
227259
return createWrapper(app, App, setProps)
228260
}
229261

230-
export function shallowMount(
231-
originalComponent,
232-
options?: MountingOptions<any>
233-
) {
234-
return mount(originalComponent, { ...options, shallow: true })
262+
export const shallowMount: typeof mount = (component: any, options?: any) => {
263+
return mount(component, { ...options, shallow: true })
235264
}

src/stubs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { transformVNodeArgs, h, createVNode } from 'vue'
2-
import { hyphenate } from '@vue/shared'
2+
import { hyphenate } from './utils/vueShared'
33
import { MOUNT_COMPONENT_REF, MOUNT_PARENT_NAME } from './constants'
44
import { config } from './config'
55
import { matchName } from './utils/matchName'

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export type FindAllComponentsSelector = NameSelector | string
2121

2222
export type GlobalMountOptions = {
2323
plugins?: Plugin[]
24-
config?: AppConfig
24+
config?: Omit<AppConfig, 'isNativeTag'> // isNativeTag is readonly, so we omit it
2525
mixins?: ComponentOptions[]
2626
mocks?: Record<string, any>
2727
provide?: Record<any, any>

0 commit comments

Comments
 (0)