Skip to content
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

fix: handle global stubs #943

Merged
merged 7 commits into from
Aug 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
"rollup": "^0.58.2",
"sinon": "^2.3.2",
"sinon-chai": "^2.10.0",
"typescript": "^3.0.1",
"vee-validate": "2.1.0-beta.5",
"vue": "2.5.16",
"vue-class-component": "^6.1.2",
"vue-loader": "^13.6.2",
Expand All @@ -71,7 +73,6 @@
"vue-template-compiler": "2.5.16",
"vuepress": "^0.14.2",
"vuepress-theme-vue": "^1.0.3",
"vuetify": "^0.16.9",
"vuex": "^3.0.1",
"webpack": "^3.0.1",
"webpack-node-externals": "^1.6.0"
Expand Down
10 changes: 10 additions & 0 deletions packages/create-instance/add-hook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This is used instead of Vue.mixin. The reason is that
// Vue.mixin is slower, and remove modfied options
// https://github.com/vuejs/vue/issues/8710

export function addHook (options, hook, fn) {
if (options[hook] && !Array.isArray(options[hook])) {
options[hook] = [options[hook]]
}
(options[hook] || (options[hook] = [])).push(fn)
}
11 changes: 5 additions & 6 deletions packages/create-instance/add-stubs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createStubsFromStubsObject } from 'shared/create-component-stubs'
import { addHook } from './add-hook'

export function addStubs (component, stubs, _Vue) {
const stubComponents = createStubsFromStubsObject(
Expand All @@ -13,10 +14,8 @@ export function addStubs (component, stubs, _Vue) {
)
}

_Vue.mixin({
beforeMount: addStubComponentsMixin,
// beforeCreate is for components created in node, which
// never mount
beforeCreate: addStubComponentsMixin
})
addHook(_Vue.options, 'beforeMount', addStubComponentsMixin)
// beforeCreate is for components created in node, which
// never mount
addHook(_Vue.options, 'beforeCreate', addStubComponentsMixin)
}
17 changes: 8 additions & 9 deletions packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { componentNeedsCompiling, isPlainObject } from 'shared/validators'
import { validateSlots } from './validate-slots'
import createScopedSlots from './create-scoped-slots'
import { extendExtendedComponents } from './extend-extended-components'
import Vue from 'vue'

function vueExtendUnsupportedOption (option: string) {
return `options.${option} is not supported for ` +
Expand All @@ -36,15 +35,12 @@ const UNSUPPORTED_VERSION_OPTIONS = [

export default function createInstance (
component: Component,
options: Options
options: Options,
_Vue: Component
): Component {
// Remove cached constructor
delete component._Ctor

const _Vue = options.localVue
? options.localVue.extend()
: Vue.extend()

// make sure all extends are based on this instance
_Vue.options._base = _Vue

Expand Down Expand Up @@ -75,7 +71,8 @@ export default function createInstance (
component = createFunctionalComponent(component, options)
} else if (options.context) {
throwError(
`mount.context can only be used when mounting a ` + `functional component`
`mount.context can only be used when mounting a ` +
`functional component`
)
}

Expand All @@ -84,13 +81,15 @@ export default function createInstance (
}

// Replace globally registered components with components extended
// from localVue. This makes sure the beforeMount mixins to add stubs
// is applied to globally registered components.
// from localVue.
// Vue version must be 2.3 or greater, because of a bug resolving
// extended constructor options (https://github.com/vuejs/vue/issues/4976)
if (vueVersion > 2.2) {
for (const c in _Vue.options.components) {
if (!isRequiredComponent(c)) {
const extendedComponent = _Vue.extend(_Vue.options.components[c])
extendedComponent.options.$_vueTestUtils_original =
_Vue.options.components[c]
_Vue.component(c, _Vue.extend(_Vue.options.components[c]))
}
}
Expand Down
25 changes: 11 additions & 14 deletions packages/create-instance/extend-extended-components.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { warn } from 'shared/util'
import { addHook } from './add-hook'

function createdFrom (extendOptions, componentOptions) {
while (extendOptions) {
Expand Down Expand Up @@ -75,12 +76,10 @@ export function extendExtendedComponents (
`config.logModifiedComponents option to false.`
)
}

const extendedComp = _Vue.extend(comp)
// used to identify instance when calling find with component selector
if (extendedComp.extendOptions.options) {
extendedComp.extendOptions.options.$_vueTestUtils_original = comp
}
extendedComp.extendOptions.$_vueTestUtils_original = comp
// Used to identify component in a render tree
extendedComp.options.$_vueTestUtils_original = comp
extendedComponents[c] = extendedComp
}
// If a component has been replaced with an extended component
Expand All @@ -93,15 +92,13 @@ export function extendExtendedComponents (
shouldExtendComponent
)
})
if (extendedComponents) {
_Vue.mixin({
created () {
if (createdFrom(this.constructor, component)) {
Object.assign(
this.$options.components,
extendedComponents
)
}
if (Object.keys(extendedComponents).length > 0) {
addHook(_Vue.options, 'beforeCreate', function addExtendedOverwrites () {
if (createdFrom(this.constructor, component)) {
Object.assign(
this.$options.components,
extendedComponents
)
}
})
}
Expand Down
16 changes: 8 additions & 8 deletions packages/create-instance/log-events.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @flow
import { addHook } from './add-hook'

export function logEvents (
vm: Component,
Expand All @@ -13,12 +14,11 @@ export function logEvents (
}
}

export function addEventLogger (vue: Component): void {
vue.mixin({
beforeCreate: function () {
this.__emitted = Object.create(null)
this.__emittedByOrder = []
logEvents(this, this.__emitted, this.__emittedByOrder)
}
})
export function addEventLogger (_Vue: Component): void {
addHook(_Vue.options, 'beforeCreate', function () {
this.__emitted = Object.create(null)
this.__emittedByOrder = []
logEvents(this, this.__emitted, this.__emittedByOrder)
}
)
}
2 changes: 1 addition & 1 deletion packages/server-test-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"typescript": "^2.6.2"
},
"peerDependencies": {
"@vue/test-utils": "1.x",
"@vue/test-utils": "*",
"vue": "2.x",
"vue-server-renderer": "2.x",
"vue-template-compiler": "^2.x"
Expand Down
4 changes: 3 additions & 1 deletion packages/server-test-utils/src/renderToString.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { throwError } from 'shared/util'
import { createRenderer } from 'vue-server-renderer'
import { mergeOptions } from 'shared/merge-options'
import config from './config'
import testUtils from '@vue/test-utils'

Vue.config.productionTip = false
Vue.config.devtools = false
Expand All @@ -30,7 +31,8 @@ export default function renderToString (

const vm = createInstance(
component,
mergeOptions(options, config)
mergeOptions(options, config),
testUtils.createLocalVue(options.localVue)
)
let renderedString = ''

Expand Down
8 changes: 6 additions & 2 deletions packages/test-utils/src/matches.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ export function matches (node, selector) {
return element && element.matches && element.matches(selector.value)
}

const componentInstance = selector.value.functional
? node && node[FUNCTIONAL_OPTIONS]
const isFunctionalSelector = typeof selector.value === 'function'
? selector.value.options.functional
: selector.value.functional

const componentInstance = isFunctionalSelector
? node[FUNCTIONAL_OPTIONS]
: node.child

if (!componentInstance) {
Expand Down
5 changes: 3 additions & 2 deletions packages/test-utils/src/mount.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { mergeOptions } from 'shared/merge-options'
import config from './config'
import warnIfNoWindow from './warn-if-no-window'
import createWrapper from './create-wrapper'

import createLocalVue from './create-local-vue'
Vue.config.productionTip = false
Vue.config.devtools = false

Expand All @@ -34,7 +34,8 @@ export default function mount (

const parentVm = createInstance(
component,
mergedOptions
mergedOptions,
createLocalVue(options.localVue)
)

const vm = parentVm.$mount(elm).$refs.vm
Expand Down
6 changes: 0 additions & 6 deletions test/specs/create-local-vue.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Vue from 'vue'
import Vuex from 'vuex'
import Vuetify from 'vuetify'
import VueRouter from 'vue-router'
import { createLocalVue } from '~vue/test-utils'
import Component from '~resources/components/component.vue'
Expand Down Expand Up @@ -112,11 +111,6 @@ describeWithShallowAndMount('createLocalVue', mountingMethod => {
localVue.use(plugin, pluginOptions)
})

it('installs Vutify successfuly', () => {
const localVue = createLocalVue()
localVue.use(Vuetify)
})

it('installs plugin into local Vue regardless of previous install in Vue', () => {
let installCount = 0

Expand Down
17 changes: 17 additions & 0 deletions test/specs/external-libraries.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createLocalVue, mount } from '~vue/test-utils'
import VeeValidate from 'vee-validate'
import { describeWithShallowAndMount } from '~resources/utils'

describeWithShallowAndMount('external libraries', () => {
it.skip('works with vee validate', () => {
const TestComponent = {
template: '<div />'
}
const localVue = createLocalVue()
localVue.use(VeeValidate)
const wrapper = mount(TestComponent, {
localVue
})
wrapper.vm.errors.collect('text')
})
})
28 changes: 27 additions & 1 deletion test/specs/mount.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ComponentWithProps from '~resources/components/component-with-props.vue'
import ComponentWithMixin from '~resources/components/component-with-mixin.vue'
import ComponentAsAClass from '~resources/components/component-as-a-class.vue'
import { injectSupported, vueVersion } from '~resources/utils'
import { describeRunIf, itDoNotRunIf } from 'conditional-specs'
import { describeRunIf, itDoNotRunIf, itSkipIf } from 'conditional-specs'
import Vuex from 'vuex'

describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
Expand Down Expand Up @@ -101,6 +101,32 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
expect(wrapper.findAll('div').length).to.equal(1)
})

itSkipIf(
vueVersion < 2.3,
'handles extended components added to Vue constructor', () => {
const ChildComponent = Vue.extend({
template: '<div />',
mounted () {
this.$route.params
}
})
Vue.component('child-component', ChildComponent)
const TestComponent = {
template: '<child-component />'
}
let wrapper
try {
wrapper = mount(TestComponent, {
mocks: {
$route: {}
}
})
} catch (err) {} finally {
delete Vue.options.components['child-component']
expect(wrapper.find(ChildComponent).exists()).to.equal(true)
}
})

it('does not use cached component', () => {
ComponentWithMixin.methods.someMethod = sinon.stub()
mount(ComponentWithMixin)
Expand Down
16 changes: 16 additions & 0 deletions test/specs/wrapper/find.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,22 @@ describeWithShallowAndMount('find', mountingMethod => {
expect(wrapper.find({ ref: 'foo' })).to.be.an('object')
})

itSkipIf(vueVersion < 2.3,
'returns functional extended component', () => {
const FunctionalExtendedComponent = Vue.extend({
functional: true,
render: h => h('div')
})
const TestComponent = {
template: '<div><functional-extended-component /></div>',
components: {
FunctionalExtendedComponent
}
}
const wrapper = mountingMethod(TestComponent)
expect(wrapper.find(FunctionalExtendedComponent).exists()).to.equal(true)
})

it('returns Wrapper of Vue Component matching the extended component', () => {
const BaseComponent = {
template: '<div><a-component /></div>',
Expand Down
15 changes: 10 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10788,6 +10788,11 @@ typescript@^2.6.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836"
integrity sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==

typescript@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1.tgz#43738f29585d3a87575520a4b93ab6026ef11fdb"
integrity sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==

uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376"
Expand Down Expand Up @@ -11139,6 +11144,11 @@ vary@^1.0.0:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=

vee-validate@2.1.0-beta.5:
version "2.1.0-beta.5"
resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.1.0-beta.5.tgz#311c4629face2383964beb06768b379500f25a69"
integrity sha512-XN0FCHBEWKbxKprjzIwIr7ff5U+aFaJZlkoKr1ReuncMwHgG/6iW0hEI4bDuNXx1YKPmm4SyhoXkAOBHIUIwTA==

vendors@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
Expand Down Expand Up @@ -11370,11 +11380,6 @@ vuepress@^0.14.2:
webpackbar "^2.6.1"
workbox-build "^3.1.0"

vuetify@^0.16.9:
version "0.16.9"
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-0.16.9.tgz#fd61f219e4a40d7afe5e24a803df5658a40b38e4"
integrity sha512-HVzdKnMETHINURJhGCMGT3wFw6R8eEHCVcqdC6o/z+eAWNQH7xg1k2hCsKo680lkjiL7tp3vRd9lF+3DpLm/+g==

vuex@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"
Expand Down