Skip to content

Commit 66d4d44

Browse files
feat: Add global configuration settings (vuejs#53)
* feat: add globalProperties to mountingOptions * refactor: move globalProperties to global property * feat: add global config * refactor: update types and fix broken tests * refactor: update rollup build * tests: add mocks tests * fix: fix missing type * chore: remove globalProperties * chore: remove left over stubs * chore: merge conflicts Co-authored-by: Lachlan Miller <[email protected]>
1 parent 140b72b commit 66d4d44

File tree

7 files changed

+159
-33
lines changed

7 files changed

+159
-33
lines changed

rollup.config.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function createEntry(options) {
2121
const config = {
2222
input,
2323
external: [
24-
'vue',
24+
'vue',
2525
'lodash/mergeWith',
2626
'lodash/camelCase',
2727
'lodash/upperFirst',
@@ -37,7 +37,7 @@ function createEntry(options) {
3737
}
3838

3939
if (format === 'es') {
40-
config.output.file = isBrowser ? pkg.browser : pkg.module
40+
config.output.file = isBrowser ? pkg.browser : pkg.module
4141
}
4242
if (format === 'cjs') {
4343
config.output.file = pkg.main

src/config.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { GlobalMountOptions } from './types'
2+
3+
export const config: { global: GlobalMountOptions } = {
4+
global: {}
5+
}

src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { mount } from './mount'
22
import { RouterLinkStub } from './components/RouterLinkStub'
3+
import { config } from './config'
34

4-
export { mount, RouterLinkStub }
5+
export { mount, RouterLinkStub, config }

src/mount.ts

+21-30
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import {
44
VNode,
55
defineComponent,
66
VNodeNormalizedChildren,
7-
ComponentOptions,
87
transformVNodeArgs,
9-
Plugin,
10-
Directive,
11-
Component,
128
reactive,
139
ComponentPublicInstance,
1410
ComponentOptionsWithObjectProps,
@@ -17,6 +13,9 @@ import {
1713
ExtractPropTypes
1814
} from 'vue'
1915

16+
import { config } from './config'
17+
import { GlobalMountOptions } from './types'
18+
import { mergeGlobalProperties } from './utils'
2019
import { createWrapper, VueWrapper } from './vue-wrapper'
2120
import { attachEmitListener } from './emitMixin'
2221
import { createDataMixin } from './dataMixin'
@@ -36,17 +35,7 @@ interface MountingOptions<Props> {
3635
default?: Slot
3736
[key: string]: Slot
3837
}
39-
global?: {
40-
plugins?: Plugin[]
41-
mixins?: ComponentOptions[]
42-
mocks?: Record<string, any>
43-
stubs?: Record<any, any>
44-
provide?: Record<any, any>
45-
// TODO how to type `defineComponent`? Using `any` for now.
46-
components?: Record<string, Component | object>
47-
directives?: Record<string, Directive>
48-
}
49-
stubs?: Record<string, any>
38+
global?: GlobalMountOptions
5039
}
5140

5241
// Component declared with defineComponent
@@ -139,11 +128,13 @@ export function mount(
139128
// create the app
140129
const app = createApp(Parent)
141130

131+
const global = mergeGlobalProperties(config.global, options?.global)
132+
142133
// global mocks mixin
143-
if (options?.global?.mocks) {
134+
if (global?.mocks) {
144135
const mixin = {
145136
beforeCreate() {
146-
for (const [k, v] of Object.entries(options.global?.mocks)) {
137+
for (const [k, v] of Object.entries(global.mocks)) {
147138
this[k] = v
148139
}
149140
}
@@ -153,30 +144,30 @@ export function mount(
153144
}
154145

155146
// use and plugins from mounting options
156-
if (options?.global?.plugins) {
157-
for (const use of options?.global?.plugins) app.use(use)
147+
if (global?.plugins) {
148+
for (const use of global.plugins) app.use(use)
158149
}
159150

160151
// use any mixins from mounting options
161-
if (options?.global?.mixins) {
162-
for (const mixin of options?.global?.mixins) app.mixin(mixin)
152+
if (global?.mixins) {
153+
for (const mixin of global.mixins) app.mixin(mixin)
163154
}
164155

165-
if (options?.global?.components) {
166-
for (const key of Object.keys(options?.global?.components))
167-
app.component(key, options.global.components[key])
156+
if (global?.components) {
157+
for (const key of Object.keys(global.components))
158+
app.component(key, global.components[key])
168159
}
169160

170-
if (options?.global?.directives) {
171-
for (const key of Object.keys(options?.global?.directives))
172-
app.directive(key, options.global.directives[key])
161+
if (global?.directives) {
162+
for (const key of Object.keys(global.directives))
163+
app.directive(key, global.directives[key])
173164
}
174165

175166
// provide any values passed via provides mounting option
176-
if (options?.global?.provide) {
177-
for (const key of Reflect.ownKeys(options.global.provide)) {
167+
if (global?.provide) {
168+
for (const key of Reflect.ownKeys(global.provide)) {
178169
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/1863
179-
app.provide(key, options.global.provide[key])
170+
app.provide(key, global.provide[key])
180171
}
181172
}
182173

src/types.ts

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Component, ComponentOptions, Directive, Plugin } from 'vue'
2+
13
import { DOMWrapper } from './dom-wrapper'
24
import { ErrorWrapper } from './error-wrapper'
35

@@ -23,3 +25,13 @@ interface NameSelector {
2325

2426
export type FindComponentSelector = RefSelector | NameSelector | string
2527
export type FindAllComponentsSelector = NameSelector | string
28+
29+
export type GlobalMountOptions = {
30+
plugins?: Plugin[]
31+
mixins?: ComponentOptions[]
32+
mocks?: Record<string, any>
33+
provide?: Record<any, any>
34+
components?: Record<string, Component | object>
35+
directives?: Record<string, Directive>
36+
stubs?: Record<any, any>
37+
}

src/utils.ts

+21
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,28 @@ import camelCase from 'lodash/camelCase'
22
import upperFirst from 'lodash/upperFirst'
33
import kebabCase from 'lodash/kebabCase'
44
import flow from 'lodash/flow'
5+
import mergeWith from 'lodash/mergeWith'
6+
import { GlobalMountOptions } from './types'
57

68
const pascalCase = flow(camelCase, upperFirst)
79

810
export { kebabCase, pascalCase }
11+
12+
export function mergeGlobalProperties(
13+
configGlobal = {},
14+
mountGlobal = {}
15+
): GlobalMountOptions {
16+
return mergeWith({}, configGlobal, mountGlobal, (objValue, srcValue, key) => {
17+
switch (key) {
18+
case 'mocks':
19+
case 'provide':
20+
case 'components':
21+
case 'directives':
22+
case 'globalProperties':
23+
return { ...objValue, ...srcValue }
24+
case 'plugins':
25+
case 'mixins':
26+
return [...(objValue || []), ...(srcValue || [])].filter(Boolean)
27+
}
28+
})
29+
}

tests/config.spec.ts

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { config, mount } from '../src'
2+
import Hello from './components/Hello.vue'
3+
4+
describe('config', () => {
5+
beforeEach(() => {
6+
config.global = {
7+
components: undefined,
8+
directives: undefined,
9+
mixins: undefined,
10+
plugins: undefined,
11+
mocks: undefined,
12+
provide: undefined
13+
}
14+
})
15+
16+
describe('components', () => {
17+
const Component = {
18+
template: '<div>{{ msg }} <hello/></div>',
19+
props: ['msg']
20+
}
21+
22+
it('allows setting components globally', () => {
23+
config.global.components = { Hello }
24+
const wrapper1 = mount(Component, { props: { msg: 'Wrapper1' } })
25+
const wrapper2 = mount(Component, { props: { msg: 'Wrapper2' } })
26+
expect(wrapper1.text()).toEqual('Wrapper1 Hello world')
27+
expect(wrapper2.text()).toEqual('Wrapper2 Hello world')
28+
})
29+
30+
it('allows overwriting globally set component config on a per mount instance', () => {
31+
config.global.components = { Hello }
32+
const HelloLocal = { template: '<div>Hello Overwritten</div>' }
33+
const wrapper1 = mount(Component, { props: { msg: 'Wrapper1' } })
34+
const wrapper2 = mount(Component, {
35+
props: { msg: 'Wrapper2' },
36+
global: { components: { Hello: HelloLocal } }
37+
})
38+
expect(wrapper1.text()).toEqual('Wrapper1 Hello world')
39+
expect(wrapper2.text()).toEqual('Wrapper2 Hello Overwritten')
40+
})
41+
})
42+
43+
describe('directives', () => {
44+
const Directive = {
45+
beforeMount(el: Element) {
46+
el.classList.add('DirectiveAdded')
47+
}
48+
}
49+
const Component = { template: '<div v-directive>msg</div>' }
50+
51+
it('allows setting directives globally', () => {
52+
config.global.directives = { Directive }
53+
expect(mount(Component).classes()).toContain('DirectiveAdded')
54+
expect(mount(Component).classes()).toContain('DirectiveAdded')
55+
})
56+
57+
it('allows overwriting globally set directives', () => {
58+
config.global.directives = { Directive }
59+
const LocalDirective = {
60+
beforeMount(el: Element) {
61+
el.classList.add('LocallyDirectiveAdded')
62+
}
63+
}
64+
65+
expect(mount(Component).classes()).toContain('DirectiveAdded')
66+
expect(
67+
mount(Component, {
68+
global: { directives: { Directive: LocalDirective } }
69+
}).classes()
70+
).toContain('LocallyDirectiveAdded')
71+
})
72+
})
73+
74+
describe('mocks', () => {
75+
it('sets mock everywhere', () => {
76+
config.global.mocks = {
77+
foo: 'bar'
78+
}
79+
const Component = { template: '<div>{{ foo }}</div>' }
80+
expect(mount(Component).text()).toEqual('bar')
81+
expect(mount(Component).text()).toEqual('bar')
82+
})
83+
84+
it('allows overwriting a global mock', () => {
85+
config.global.mocks = {
86+
foo: 'bar'
87+
}
88+
const Component = { template: '<div>{{ foo }}</div>' }
89+
90+
expect(mount(Component).text()).toEqual('bar')
91+
expect(
92+
mount(Component, { global: { mocks: { foo: 'baz' } } }).text()
93+
).toEqual('baz')
94+
})
95+
})
96+
})

0 commit comments

Comments
 (0)