Skip to content

Commit

Permalink
fix: stop globalProperties config leak (#1187) (#1202)
Browse files Browse the repository at this point in the history
  • Loading branch information
purepear committed Jan 6, 2022
1 parent 1b3870d commit e5a331c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 1 deletion.
22 changes: 21 additions & 1 deletion src/utils.ts
Expand Up @@ -14,6 +14,26 @@ function mergeStubs(target: Record<string, any>, source: GlobalMountOptions) {
}
}

// perform 1-level-deep-pseudo-clone merge in order to prevent config leaks
// example: vue-router overwrites globalProperties.$router
function mergeAppConfig(
configGlobalConfig: GlobalMountOptions['config'],
mountGlobalConfig: GlobalMountOptions['config']
): Required<GlobalMountOptions>['config'] {
return {
...configGlobalConfig,
...mountGlobalConfig,
globalProperties: {
...configGlobalConfig?.globalProperties,
...mountGlobalConfig?.globalProperties
},
compilerOptions: {
...configGlobalConfig?.compilerOptions,
...mountGlobalConfig?.compilerOptions
}
}
}

export function mergeGlobalProperties(
mountGlobal: GlobalMountOptions = {}
): Required<GlobalMountOptions> {
Expand All @@ -34,7 +54,7 @@ export function mergeGlobalProperties(
components: { ...configGlobal.components, ...mountGlobal.components },
provide: { ...configGlobal.provide, ...mountGlobal.provide },
mocks: { ...configGlobal.mocks, ...mountGlobal.mocks },
config: { ...configGlobal.config, ...mountGlobal.config },
config: mergeAppConfig(configGlobal.config, mountGlobal.config),
directives: { ...configGlobal.directives, ...mountGlobal.directives },
renderStubDefaultSlot
}
Expand Down
38 changes: 38 additions & 0 deletions tests/config.spec.ts
@@ -1,4 +1,5 @@
import { defineComponent, ComponentPublicInstance, h, inject } from 'vue'
import type { App } from 'vue'
import { config, mount } from '../src'
import Hello from './components/Hello.vue'
import ComponentWithSlots from './components/ComponentWithSlots.vue'
Expand Down Expand Up @@ -51,6 +52,43 @@ describe('config', () => {
})
})

describe('config integrity', () => {
it('should not leak config when plugins overwrite globalProperties', async () => {
// test with a function because it's not an "easy to clone" primitive type
const globalRouterMock = { push: jest.fn() }
const pluginRouterMock = { push: jest.fn() }
const Component = defineComponent({ template: '<div />' })

class Plugin {
static install(_app: App) {
_app.config.globalProperties.$router = pluginRouterMock
}
}

config.global.config.globalProperties = {
$router: globalRouterMock
}

// first with plugin to overwrite globalRouterMock with pluginRouterMock
const wrapper1 = mount(Component, {
global: {
plugins: [Plugin]
}
})

// then without plugin to check if the plugin overwrite is gone
const wrapper2 = mount(Component)

wrapper1.vm.$router.push('/route-1')
wrapper2.vm.$router.push('/route-2')

expect(pluginRouterMock.push).toHaveBeenCalledTimes(1)
expect(pluginRouterMock.push).toHaveBeenCalledWith('/route-1')
expect(globalRouterMock.push).toHaveBeenCalledTimes(1)
expect(globalRouterMock.push).toHaveBeenCalledWith('/route-2')
})
})

describe('renderStubDefaultSlot', () => {
it('should override shallow option when set to true', () => {
const comp = mount(ComponentWithSlots, {
Expand Down

0 comments on commit e5a331c

Please sign in to comment.