Skip to content

Commit

Permalink
fix(html): ensure wrapper.html() works correctly for multi-root (#1120)
Browse files Browse the repository at this point in the history
  • Loading branch information
xanf committed Dec 1, 2021
1 parent 3ddc17e commit 3b0f7f5
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 32 deletions.
8 changes: 7 additions & 1 deletion src/baseWrapper.ts
Expand Up @@ -24,6 +24,8 @@ import { isElementVisible } from './utils/isElementVisible'
import { isElement } from './utils/isElement'
import type { DOMWrapper } from './domWrapper'
import { createDOMWrapper, createVueWrapper } from './wrapperFactory'
import { stringifyNode } from './utils/stringifyNode'
import pretty from 'pretty'

export default abstract class BaseWrapper<ElementType extends Node>
implements WrapperLike
Expand Down Expand Up @@ -191,7 +193,11 @@ export default abstract class BaseWrapper<ElementType extends Node>
)
}
abstract setValue(value?: any): Promise<void>
abstract html(): string
html(): string {
return this.getRootNodes()
.map((node) => pretty(stringifyNode(node)))
.join('\n')
}

classes(): string[]
classes(className: string): boolean
Expand Down
7 changes: 0 additions & 7 deletions src/domWrapper.ts
@@ -1,7 +1,6 @@
import { config } from './config'
import BaseWrapper from './baseWrapper'
import WrapperLike from './interfaces/wrapperLike'
import { isElement } from './utils/isElement'
import { registerFactory, WrapperType } from './wrapperFactory'
import { RefSelector } from './types'
import { isRefSelector } from './utils'
Expand All @@ -22,12 +21,6 @@ export class DOMWrapper<NodeType extends Node> extends BaseWrapper<NodeType> {
return this.element.__vueParentComponent
}

html() {
return isElement(this.element)
? this.element.outerHTML
: this.element.toString()
}

find(selector: string | RefSelector): DOMWrapper<any> {
const result = super.find(selector)
if (result.exists() && isRefSelector(selector)) {
Expand Down
9 changes: 7 additions & 2 deletions src/utils/getRootNodes.ts
Expand Up @@ -8,7 +8,10 @@ export function getRootNodes(vnode: VNode): Node[] {
} else if (vnode.shapeFlag & ShapeFlags.COMPONENT) {
const { subTree } = vnode.component!
return getRootNodes(subTree)
} else if (vnode.shapeFlag & ShapeFlags.TEXT_CHILDREN) {
} else if (
vnode.shapeFlag &
(ShapeFlags.TEXT_CHILDREN | ShapeFlags.TELEPORT)
) {
// static node optimization, subTree.children will be static string and will not help us
const result = [vnode.el as Node]
if (vnode.anchor) {
Expand All @@ -30,6 +33,8 @@ export function getRootNodes(vnode: VNode): Node[] {
}
// Missing cases which do not need special handling:
// ShapeFlags.SLOTS_CHILDREN comes with ShapeFlags.ELEMENT
// ShapeFlags.TELEPORT comes with ShapeFlags.TEXT_CHILDREN

// Will hit this default when ShapeFlags is 0
// This is the case for example for unresolved async component without loader
return []
}
5 changes: 5 additions & 0 deletions src/utils/stringifyNode.ts
@@ -0,0 +1,5 @@
export function stringifyNode(node: Node): string {
return node instanceof Element
? node.outerHTML
: new XMLSerializer().serializeToString(node)
}
9 changes: 0 additions & 9 deletions src/vueWrapper.ts
Expand Up @@ -131,15 +131,6 @@ export class VueWrapper<
return emitted(this.vm, eventName)
}

html() {
// cover cases like <Suspense>, multiple root nodes.
if (this.parentElement['__vue_app__']) {
return pretty(this.parentElement.innerHTML)
}

return pretty(this.element.outerHTML)
}

isVisible(): boolean {
const domWrapper = createDOMWrapper(this.element)
return domWrapper.isVisible()
Expand Down
33 changes: 25 additions & 8 deletions tests/html.spec.ts
Expand Up @@ -15,18 +15,35 @@ describe('html', () => {
expect(wrapper.html()).toBe('<div>Text content</div>')
})

it('returns the html when mounting multiple root nodes', () => {
describe('multiple root components', () => {
const originalTemplate = [
'<div>foo</div>',
'<!-- comment node -->',
'some text',
'<div>bar</div>',
'<div>baz</div>'
]

const Component = defineComponent({
render() {
return [h('div', {}, 'foo'), h('div', {}, 'bar'), h('div', {}, 'baz')]
}
name: 'TemplateComponent',
template: originalTemplate.join('')
})

const wrapper = mount(Component)
it('returns the html when mounting multiple root nodes', () => {
const wrapper = mount(Component)
expect(wrapper.html()).toBe(originalTemplate.join('\n'))
})

expect(wrapper.html()).toBe(
'<div>foo</div>\n' + '<div>bar</div>\n' + '<div>baz</div>'
)
it('returns the html when multiple root component is located inside other component', () => {
const ParentComponent = defineComponent({
components: { MultipleRoots: Component },
template: '<div>parent <multiple-roots /></div>'
})
const wrapper = mount(ParentComponent)
expect(wrapper.findComponent(Component).html()).toBe(
originalTemplate.join('\n')
)
})
})

it('returns the html when mounting a Suspense component', () => {
Expand Down
11 changes: 6 additions & 5 deletions tests/mountingOptions/slots.spec.ts
Expand Up @@ -95,12 +95,13 @@ describe('slots', () => {
})

expect(wrapper.find('.named').html()).toBe(
'' +
'<div class="named">' +
'<div id="root">' +
'<div id="msg">Hello world</div>' +
'</div>' +
[
'<div class="named">',
' <div id="root">',
' <div id="msg">Hello world</div>',
' </div>',
'</div>'
].join('\n')
)
})
})
Expand Down

0 comments on commit 3b0f7f5

Please sign in to comment.