Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

WIP: Add parent in create-instance #586

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

Merged
merged 18 commits into from
Jun 1, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion 7 flow/options.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ declare type Options = { // eslint-disable-line no-undef
context?: Object,
attrs?: Object,
listeners?: Object,
logModifiedComponents?: Boolean
logModifiedComponents?: boolean,
sync?: boolean
}

declare type SlotValue = Component | string | Array<Component | string>

declare type SlotsObject = {[name: string]: SlotValue}
1 change: 0 additions & 1 deletion 1 flow/vue.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@

declare type Component = Object | Function // eslint-disable-line no-undef
declare type VNode = Object // eslint-disable-line no-undef
declare type SlotValue = Component | string | Array<Component> | Array<string>
2 changes: 1 addition & 1 deletion 2 flow/wrapper.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ declare interface BaseWrapper { // eslint-disable-line no-undef

declare type WrapperOptions = { // eslint-disable-line no-undef
attachedToDocument: boolean,
sync: boolean
sync?: boolean
}
6 changes: 3 additions & 3 deletions 6 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@
"rollup": "^0.58.2",
"sinon": "^2.3.2",
"sinon-chai": "^2.10.0",
"vue": "2.5.13",
"vue": "^2.5.16",
"vue-class-component": "^6.1.2",
"vue-loader": "^13.6.2",
"vue-router": "^3.0.1",
"vue-server-renderer": "2.5.13",
"vue-template-compiler": "2.5.13",
"vue-server-renderer": "^2.5.16",
"vue-template-compiler": "^2.5.16",
"vuepress": "^0.10.0",
"vuepress-theme-vue": "^1.0.3",
"vuetify": "^0.16.9",
Expand Down
12 changes: 0 additions & 12 deletions 12 packages/create-instance/add-attrs.js

This file was deleted.

12 changes: 0 additions & 12 deletions 12 packages/create-instance/add-listeners.js

This file was deleted.

13 changes: 0 additions & 13 deletions 13 packages/create-instance/add-provide.js

This file was deleted.

17 changes: 0 additions & 17 deletions 17 packages/create-instance/add-scoped-slots.js

This file was deleted.

74 changes: 24 additions & 50 deletions 74 packages/create-instance/add-slots.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,34 @@
// @flow

import { compileToFunctions } from 'vue-template-compiler'
import { throwError } from 'shared/util'
import { validateSlots } from './validate-slots'

// see https://github.com/vuejs/vue-test-utils/pull/274
function createVNodes (vm: Component, slotValue: string) {
const compiledResult = compileToFunctions(`<div>${slotValue}{{ }}</div>`)
const _staticRenderFns = vm._renderProxy.$options.staticRenderFns
vm._renderProxy.$options.staticRenderFns = compiledResult.staticRenderFns
const elem = compiledResult.render.call(vm._renderProxy, vm.$createElement).children
vm._renderProxy.$options.staticRenderFns = _staticRenderFns
return elem
}

function validateEnvironment (): void {
if (!compileToFunctions) {
throwError('vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined')
}
if (typeof window === 'undefined') {
throwError('the slots string option does not support strings in server-test-uitls.')
}
}
function createVNodesForSlot (
h: Function,
slotValue: SlotValue,
name: string
): Array<VNode> {
const el = typeof slotValue === 'string'
? compileToFunctions(slotValue)
: slotValue

function addSlotToVm (vm: Component, slotName: string, slotValue: SlotValue): void {
let elem
if (typeof slotValue === 'string') {
validateEnvironment()
elem = createVNodes(vm, slotValue)
} else {
elem = vm.$createElement(slotValue)
}
if (Array.isArray(elem)) {
if (Array.isArray(vm.$slots[slotName])) {
vm.$slots[slotName] = [...vm.$slots[slotName], ...elem]
} else {
vm.$slots[slotName] = [...elem]
}
} else {
if (Array.isArray(vm.$slots[slotName])) {
vm.$slots[slotName].push(elem)
} else {
vm.$slots[slotName] = [elem]
}
}
const vnode = h(el)
vnode.data.slot = name
return vnode
}

export function addSlots (vm: Component, slots: Object): void {
validateSlots(slots)
Object.keys(slots).forEach((key) => {
if (Array.isArray(slots[key])) {
slots[key].forEach((slotValue) => {
addSlotToVm(vm, key, slotValue)
})
export function createSlotVNodes (
h: Function,
slots: SlotsObject
): Array<VNode> {
return Object.keys(slots).reduce((acc, key) => {
const content = slots[key]
if (Array.isArray(content)) {
const nodes = content.reduce((accInner, slotDef) => {
return accInner.concat(createVNodesForSlot(h, slotDef, key))
}, [])
return acc.concat(nodes)
} else {
addSlotToVm(vm, key, slots[key])
return acc.concat(createVNodesForSlot(h, content, key))
}
})
}, [])
}
130 changes: 55 additions & 75 deletions 130 packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,29 @@
// @flow

import Vue from 'vue'
import { addSlots } from './add-slots'
import { addScopedSlots } from './add-scoped-slots'
import { createSlotVNodes } from './add-slots'
import addMocks from './add-mocks'
import addAttrs from './add-attrs'
import addListeners from './add-listeners'
import addProvide from './add-provide'
import { addEventLogger } from './log-events'
import { createComponentStubs } from 'shared/stub-components'
import { throwError, warn } from 'shared/util'
import { throwError, warn, vueVersion } from 'shared/util'
import { compileTemplate } from 'shared/compile-template'
import deleteoptions from './delete-mounting-options'
import deleteMountingOptions from './delete-mounting-options'
import createFunctionalComponent from './create-functional-component'
import { componentNeedsCompiling } from 'shared/validators'

function isDestructuringSlotScope (slotScope: string): boolean {
return slotScope[0] === '{' && slotScope[slotScope.length - 1] === '}'
}

function getVueTemplateCompilerHelpers (proxy: Object): Object {
const helpers = {}
const names = ['_c', '_o', '_n', '_s', '_l', '_t', '_q', '_i', '_m', '_f', '_k', '_b', '_v', '_e', '_u', '_g']
names.forEach((name) => {
helpers[name] = proxy[name]
})
return helpers
}
import { validateSlots } from './validate-slots'

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

if (options.mocks) {
addMocks(options.mocks, vue)
addMocks(options.mocks, _Vue)
}

if ((component.options && component.options.functional) || component.functional) {
component = createFunctionalComponent(component, options)
} else if (options.context) {
Expand All @@ -45,23 +32,23 @@ export default function createInstance (
)
}

if (options.provide) {
addProvide(component, options.provide, options)
}

if (componentNeedsCompiling(component)) {
compileTemplate(component)
}

addEventLogger(vue)
addEventLogger(_Vue)

const instanceOptions = {
...options,
propsData: {
...options.propsData
}
}

const Constructor = (typeof component === 'function' && component.prototype instanceof Vue) ? component : vue.extend(component)
deleteMountingOptions(instanceOptions)

const instanceOptions = { ...options, propsData: { ...options.propsData }}
deleteoptions(instanceOptions)
// $FlowIgnore
const stubComponents = createComponentStubs(component.components, options.stubs)

if (options.stubs) {
instanceOptions.components = {
...instanceOptions.components,
Expand All @@ -76,60 +63,53 @@ export default function createInstance (
if (options.logModifiedComponents) {
warn(`an extended child component ${c} has been modified to ensure it has the correct instance properties. This means it is not possible to find the component with a component selector. To find the component, you must stub it manually using the stubs mounting option.`)
}
instanceOptions.components[c] = vue.extend(component.components[c])
instanceOptions.components[c] = _Vue.extend(component.components[c])
}
})

Object.keys(stubComponents).forEach(c => {
vue.component(c, stubComponents[c])
_Vue.component(c, stubComponents[c])
})

const vm = new Constructor(instanceOptions)

// Workaround for Vue < 2.5
vm._staticTrees = []
const Constructor = (typeof component === 'function' && component.prototype instanceof Vue)
? component.extend(instanceOptions)
: _Vue.extend(component).extend(instanceOptions)

addAttrs(vm, options.attrs)
addListeners(vm, options.listeners)
// const Constructor = _Vue.extend(component).extend(instanceOptions)

if (options.scopedSlots) {
if (window.navigator.userAgent.match(/PhantomJS/i)) {
throwError('the scopedSlots option does not support PhantomJS. Please use Puppeteer, or pass a component.')
}
const vueVersion = Number(`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`)
if (vueVersion >= 2.5) {
vm.$_vueTestUtils_scopedSlots = {}
vm.$_vueTestUtils_slotScopes = {}
const renderSlot = vm._renderProxy._t

vm._renderProxy._t = function (name, feedback, props, bindObject) {
const scopedSlotFn = vm.$_vueTestUtils_scopedSlots[name]
const slotScope = vm.$_vueTestUtils_slotScopes[name]
if (scopedSlotFn) {
props = { ...bindObject, ...props }
const helpers = getVueTemplateCompilerHelpers(vm._renderProxy)
let proxy = { ...helpers }
if (isDestructuringSlotScope(slotScope)) {
proxy = { ...helpers, ...props }
} else {
proxy[slotScope] = props
}
return scopedSlotFn.call(proxy)
} else {
return renderSlot.call(vm._renderProxy, name, feedback, props, bindObject)
}
}
Object.keys(instanceOptions.components || {}).forEach(key => {
Constructor.component(key, instanceOptions.components[key])
_Vue.component(key, instanceOptions.components[key])
})

// $FlowIgnore
addScopedSlots(vm, options.scopedSlots)
} else {
throwError('the scopedSlots option is only supported in vue@2.5+.')
}
if (options.slots) {
validateSlots(options.slots)
}

if (options.slots) {
addSlots(vm, options.slots)
// Objects are not resolved in extended components in Vue < 2.5
// https://github.com/vuejs/vue/issues/6436
if (options.provide &&
typeof options.provide === 'object' &&
vueVersion < 2.5
) {
const obj = { ...options.provide }
options.provide = () => obj
}

return vm
const Parent = _Vue.extend({
provide: options.provide,
render (h) {
const slots = options.slots
? createSlotVNodes(h, options.slots)
: undefined
return h(Constructor, {
ref: 'vm',
props: options.propsData,
on: options.listeners,
attrs: options.attrs
}, slots)
}
})

return new Parent()
}
1 change: 1 addition & 0 deletions 1 packages/create-instance/delete-mounting-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export default function deleteMountingOptions (options) {
delete options.clone
delete options.attrs
delete options.listeners
delete options.propsData
}
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.