Skip to content

Commit 6e1d328

Browse files
authored
Merge pull request #81 from pikax/feat/options.attachto
feat: add options.attachTo
2 parents 5b9cbd0 + d7cae03 commit 6e1d328

File tree

5 files changed

+88
-4
lines changed

5 files changed

+88
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ component | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-u
6161
directives | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global)
6262
stubs | ✅
6363
attachToDocument | ❌| will rename to `attachTo`. See [here](https://github.com/vuejs/vue-test-utils/pull/1492)
64+
attachTo | ✅
6465
attrs | ❌ |
6566
scopedSlots | ⚰️ | scopedSlots are merged with slots in Vue 3
6667
context | ⚰️ | different from Vue 2, may not make sense anymore.

rollup.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function createEntry(options) {
2727
'lodash/upperFirst',
2828
'lodash/kebabCase',
2929
'lodash/flow',
30+
'lodash/isString',
3031
'dom-event-types'
3132
],
3233
plugins: [resolve()],

src/mount.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515

1616
import { config } from './config'
1717
import { GlobalMountOptions } from './types'
18-
import { mergeGlobalProperties } from './utils'
18+
import { mergeGlobalProperties, isString } from './utils'
1919
import { createWrapper, VueWrapper } from './vue-wrapper'
2020
import { attachEmitListener } from './emitMixin'
2121
import { createDataMixin } from './dataMixin'
@@ -36,6 +36,7 @@ interface MountingOptions<Props> {
3636
[key: string]: Slot
3737
}
3838
global?: GlobalMountOptions
39+
attachTo?: HTMLElement | string
3940
}
4041

4142
// Component declared with defineComponent
@@ -76,10 +77,21 @@ export function mount(
7677
): VueWrapper<any> {
7778
const component = { ...originalComponent }
7879

79-
// Reset the document.body
80-
document.getElementsByTagName('html')[0].innerHTML = ''
8180
const el = document.createElement('div')
8281
el.id = MOUNT_ELEMENT_ID
82+
83+
if (options?.attachTo) {
84+
const to = isString(options.attachTo)
85+
? document.querySelector(options.attachTo)
86+
: options.attachTo
87+
88+
el.appendChild(to)
89+
}
90+
if (el.children.length === 0) {
91+
// Reset the document.body
92+
document.getElementsByTagName('html')[0].innerHTML = ''
93+
}
94+
8395
document.body.appendChild(el)
8496

8597
// handle any slots passed via mounting options

src/utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ 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 isString from 'lodash/isString'
56
import mergeWith from 'lodash/mergeWith'
67
import { GlobalMountOptions } from './types'
78

89
const pascalCase = flow(camelCase, upperFirst)
910

10-
export { kebabCase, pascalCase }
11+
export { kebabCase, pascalCase, isString }
1112

1213
export function mergeGlobalProperties(
1314
configGlobal: GlobalMountOptions = {},
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { mount } from '../../src'
2+
3+
const innerHTML = '<input><span>Hello world</span>'
4+
const outerHTML = `<div id="attach-to">${innerHTML}</div>`
5+
const ssrHTML = `<div id="attach-to" data-server-rendered="true">${innerHTML}</div>`
6+
const template = '<div id="attach-to"><input /><span>Hello world</span></div>'
7+
const TestComponent = { template }
8+
9+
describe('options.attachTo', () => {
10+
it('attaches to a provided HTMLElement', () => {
11+
const div = document.createElement('div')
12+
div.id = 'root'
13+
document.body.appendChild(div)
14+
expect(document.getElementById('root')).not.toBeNull()
15+
expect(document.getElementById('attach-to')).toBeNull()
16+
const wrapper = mount(TestComponent, {
17+
attachTo: div
18+
})
19+
20+
const root = document.getElementById('root')
21+
const rendered = document.getElementById('attach-to')
22+
expect(wrapper.vm.$el.parentNode).not.toBeNull()
23+
expect(root).toBeNull()
24+
expect(rendered).not.toBeNull()
25+
expect(rendered.outerHTML).toBe(outerHTML)
26+
wrapper.unmount()
27+
expect(document.getElementById('attach-to')).toBeNull()
28+
})
29+
it('attaches to a provided CSS selector string', () => {
30+
const div = document.createElement('div')
31+
div.id = 'root'
32+
document.body.appendChild(div)
33+
expect(document.getElementById('root')).not.toBeNull()
34+
expect(document.getElementById('attach-to')).toBeNull()
35+
const wrapper = mount(TestComponent, {
36+
attachTo: '#root'
37+
})
38+
39+
const root = document.getElementById('root')
40+
const rendered = document.getElementById('attach-to')
41+
expect(wrapper.vm.$el.parentNode).not.toBeNull()
42+
expect(root).toBeNull()
43+
expect(rendered).not.toBeNull()
44+
expect(rendered.outerHTML).toBe(outerHTML)
45+
wrapper.unmount()
46+
expect(document.getElementById('attach-to')).toBeNull()
47+
})
48+
49+
it('correctly hydrates markup', () => {
50+
expect(document.getElementById('attach-to')).toBeNull()
51+
52+
const div = document.createElement('div')
53+
div.id = 'attach-to'
54+
div.setAttribute('data-server-rendered', 'true')
55+
div.innerHTML = innerHTML
56+
document.body.appendChild(div)
57+
expect(div.outerHTML).toBe(ssrHTML)
58+
const wrapper = mount(TestComponent, {
59+
attachTo: '#attach-to'
60+
})
61+
62+
const rendered = document.getElementById('attach-to')
63+
expect(wrapper.vm.$el.parentNode).not.toBeNull()
64+
expect(rendered).not.toBeNull()
65+
expect(rendered.outerHTML).toBe(outerHTML)
66+
wrapper.unmount()
67+
expect(document.getElementById('attach-to')).toBeNull()
68+
})
69+
})

0 commit comments

Comments
 (0)