Skip to content

Commit 0c07653

Browse files
authored
test: use sandbox to avoid memory leaks in test (#1128)
1 parent 85a972c commit 0c07653

30 files changed

+305
-290
lines changed

Diff for: package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"test:unit:debug": "npm run build:test && node --inspect-brk node_modules/.bin/mocha-webpack --webpack-config test/setup/webpack.test.config.js test/specs --recursive --require test/setup/mocha.setup.js",
2727
"test:unit:karma": "npm run build:test && TARGET=browser karma start test/setup/karma.conf.js --single-run",
2828
"test:unit:node": "npm run build:test && npm run test:unit:node:only",
29-
"test:unit:node:only": "TEST_ENV=node mocha-webpack --webpack-config test/setup/webpack.test.config.js test/specs --recursive --require test/setup/mocha.setup.js",
29+
"test:unit:node:only": "cross-env TEST_ENV=node mocha-webpack --webpack-config test/setup/webpack.test.config.js test/specs --recursive --require test/setup/mocha.setup.js",
3030
"test:types": "tsc -p packages/test-utils/types && tsc -p packages/server-test-utils/types"
3131
},
3232
"devDependencies": {
@@ -54,18 +54,18 @@
5454
"karma": "^1.7.0",
5555
"karma-mocha": "^1.3.0",
5656
"karma-phantomjs-launcher": "^1.0.4",
57-
"karma-sinon-chai": "^1.3.1",
57+
"karma-sinon-chai": "^2.0.2",
5858
"karma-sourcemap-loader": "^0.3.7",
5959
"karma-spec-reporter": "^0.0.31",
6060
"karma-webpack": "^2.0.3",
6161
"lerna": "2",
6262
"markdown-it-include": "^1.0.0",
63-
"mocha": "^3.5.0",
63+
"mocha": "^5.2.0",
6464
"mocha-webpack": "^1.0.1",
6565
"prettier": "^1.16.0",
6666
"rollup": "^0.58.2",
67-
"sinon": "^2.3.2",
68-
"sinon-chai": "^2.10.0",
67+
"sinon": "^7.2.3",
68+
"sinon-chai": "^3.3.0",
6969
"typescript": "^3.0.1",
7070
"vee-validate": "^2.1.3",
7171
"vue": "^2.5.22",

Diff for: packages/create-instance/create-component-stubs.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,13 @@ function resolveOptions(component, _Vue) {
6565
return {}
6666
}
6767

68-
return isConstructor(component)
69-
? component.options
70-
: _Vue.extend(component).options
68+
if (isConstructor(component)) {
69+
return component.options
70+
}
71+
const options = _Vue.extend(component).options
72+
component._Ctor = {}
73+
74+
return options
7175
}
7276

7377
export function createStubFromComponent(
@@ -165,6 +169,7 @@ export function createStubsFromStubsObject(
165169
}
166170

167171
acc[stubName] = stub
172+
stub._Ctor = {}
168173

169174
return acc
170175
}, {})

Diff for: packages/create-instance/create-instance.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default function createInstance(
7474
// make sure all extends are based on this instance
7575

7676
const Constructor = _Vue.extend(componentOptions).extend(instanceOptions)
77-
77+
componentOptions._Ctor = {}
7878
Constructor.options._base = _Vue
7979

8080
const scopedSlots = createScopedSlots(options.scopedSlots, _Vue)

Diff for: packages/create-instance/patch-create-element.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function shouldExtend(component, _Vue) {
2121
function extend(component, _Vue) {
2222
const componentOptions = component.options ? component.options : component
2323
const stub = _Vue.extend(componentOptions)
24+
componentOptions._Ctor = {}
2425
stub.options.$_vueTestUtils_original = component
2526
stub.options._base = _Vue
2627
return stub

Diff for: test/resources/components/component-with-events.vue

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export default {
4242
mousedownHandler(event) {
4343
if (event.button === 0) {
4444
this.clickHandler()
45-
console.info(event.defaultPrevented)
4645
}
4746
},
4847
toggleActive() {

Diff for: test/resources/components/recursive-component.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<template>
22
<div>
33
<recursive-component
4-
v-for="item in items"
5-
:key="item"
4+
v-for="(item, i) in items"
5+
:key="i"
66
:items="items ? items[0] : []"
77
>{{ something }}</recursive-component
88
>

Diff for: test/specs/components/TransitionStub.spec.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import { TransitionStub } from '~vue/test-utils'
44
import { itDoNotRunIf } from 'conditional-specs'
55

66
describeWithShallowAndMount('TransitionStub', mountingMethod => {
7-
let consoleError
7+
const sandbox = sinon.createSandbox()
88

99
beforeEach(() => {
10-
consoleError = sinon.stub(console, 'error')
10+
sandbox.stub(console, 'error').callThrough()
1111
})
1212

1313
afterEach(() => {
14-
consoleError.restore()
14+
sandbox.reset()
15+
sandbox.restore()
1516
})
1617

1718
it('update synchronously when used as stubs for Transition', () => {
@@ -76,7 +77,7 @@ describeWithShallowAndMount('TransitionStub', mountingMethod => {
7677
transition: TransitionStub
7778
}
7879
})
79-
expect(consoleError).calledWith(msg)
80+
expect(console.error).calledWith(msg)
8081
})
8182

8283
it('handles keyed transitions', () => {

Diff for: test/specs/config.spec.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,24 @@ import {
99
} from '~vue/test-utils'
1010

1111
describeWithShallowAndMount('config', mountingMethod => {
12-
let configStubsSave, configLogSave, configSilentSave
12+
const sandbox = sinon.createSandbox()
13+
let configStubsSave
14+
let configLogSave
15+
let configSilentSave
1316

1417
beforeEach(() => {
1518
configStubsSave = config.stubs
1619
configLogSave = config.logModifiedComponents
1720
configSilentSave = config.silent
18-
sinon.stub(console, 'error').callThrough()
21+
sandbox.stub(console, 'error').callThrough()
1922
})
2023

2124
afterEach(() => {
2225
config.stubs = configStubsSave
2326
config.logModifiedComponents = configLogSave
2427
config.silent = configSilentSave
25-
console.error.restore()
28+
sandbox.reset()
29+
sandbox.restore()
2630
})
2731

2832
itDoNotRunIf(
@@ -113,7 +117,7 @@ describeWithShallowAndMount('config', mountingMethod => {
113117
const testComponent = {
114118
template: `
115119
<div>
116-
<transition-group><p /><p /></transition-group>
120+
<transition-group><p key="1"/><p key="2" /></transition-group>
117121
</div>
118122
`
119123
}
@@ -135,7 +139,7 @@ describeWithShallowAndMount('config', mountingMethod => {
135139
wrapper.setProps({
136140
prop1: 'new value'
137141
})
138-
expect(console.error).not.calledWith(sinon.match('[Vue warn]'))
142+
expect(console.error).not.calledWith(sandbox.match('[Vue warn]'))
139143
})
140144

141145
it('does throw Vue warning when silent is set to false', () => {
@@ -151,6 +155,6 @@ describeWithShallowAndMount('config', mountingMethod => {
151155
wrapper.setProps({
152156
prop1: 'new value'
153157
})
154-
expect(console.error).calledWith(sinon.match('[Vue warn]'))
158+
expect(console.error).calledWith(sandbox.match('[Vue warn]'))
155159
})
156160
})

Diff for: test/specs/error-wrapper.spec.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { compileToFunctions } from 'vue-template-compiler'
21
import { describeWithShallowAndMount } from '~resources/utils'
32

43
describeWithShallowAndMount('ErrorWrapper', mountingMethod => {
@@ -36,10 +35,12 @@ describeWithShallowAndMount('ErrorWrapper', mountingMethod => {
3635
]
3736
methods.forEach(method => {
3837
it(`${method} throws error when called`, () => {
39-
const compiled = compileToFunctions('<p />')
38+
const TestComponent = {
39+
template: '<p />'
40+
}
4041
const selector = 'div'
4142
const message = `[vue-test-utils]: find did not return ${selector}, cannot call ${method}() on empty Wrapper`
42-
const wrapper = mountingMethod(compiled)
43+
const wrapper = mountingMethod(TestComponent)
4344
const error = wrapper.find(selector)
4445
expect(error.constructor.name).to.equal('ErrorWrapper')
4546
expect(() => error[method]())

Diff for: test/specs/mount.spec.js

+13-11
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ import { describeRunIf, itDoNotRunIf, itSkipIf } from 'conditional-specs'
1010
import Vuex from 'vuex'
1111

1212
describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
13+
const sandbox = sinon.createSandbox()
1314
const windowSave = window
1415

1516
beforeEach(() => {
16-
sinon.stub(console, 'error').callThrough()
17+
sandbox.stub(console, 'error').callThrough()
1718
})
1819

1920
afterEach(() => {
2021
window = windowSave // eslint-disable-line no-native-reassign
21-
console.error.restore()
22+
sandbox.reset()
23+
sandbox.restore()
2224
})
2325

2426
it('returns new VueWrapper with mounted Vue instance if no options are passed', () => {
@@ -101,7 +103,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
101103
'handles extended components added to Vue constructor',
102104
() => {
103105
const ChildComponent = Vue.extend({
104-
template: '<div />',
106+
render: h => h('div'),
105107
mounted() {
106108
this.$route.params
107109
}
@@ -126,10 +128,11 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
126128
)
127129

128130
it('does not use cached component', () => {
129-
ComponentWithMixin.methods.someMethod = sinon.stub()
131+
sandbox.stub(ComponentWithMixin.methods, 'someMethod')
130132
mount(ComponentWithMixin)
131133
expect(ComponentWithMixin.methods.someMethod.callCount).to.equal(1)
132-
ComponentWithMixin.methods.someMethod = sinon.stub()
134+
ComponentWithMixin.methods.someMethod.restore()
135+
sandbox.stub(ComponentWithMixin.methods, 'someMethod')
133136
mount(ComponentWithMixin)
134137
expect(ComponentWithMixin.methods.someMethod.callCount).to.equal(1)
135138
})
@@ -138,7 +141,6 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
138141
if (
139142
!(navigator.userAgent.includes && navigator.userAgent.includes('node.js'))
140143
) {
141-
console.log('window read only. skipping test ...')
142144
return
143145
}
144146

@@ -160,7 +162,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
160162
})
161163

162164
itDoNotRunIf(vueVersion < 2.3, 'overrides methods', () => {
163-
const stub = sinon.stub()
165+
const stub = sandbox.stub()
164166
const TestComponent = Vue.extend({
165167
template: '<div />',
166168
methods: {
@@ -179,8 +181,8 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
179181
})
180182

181183
it.skip('overrides component prototype', () => {
182-
const mountSpy = sinon.spy()
183-
const destroySpy = sinon.spy()
184+
const mountSpy = sandbox.spy()
185+
const destroySpy = sandbox.spy()
184186
const Component = Vue.extend({})
185187
const {
186188
$mount: originalMount,
@@ -233,7 +235,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
233235
it('deletes mounting options before passing options to component', () => {
234236
const wrapper = mount(
235237
{
236-
render: h => h('div')
238+
template: '<div />'
237239
},
238240
{
239241
provide: {
@@ -248,7 +250,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
248250
},
249251
localVue: createLocalVue(),
250252
stubs: {
251-
prop: 'val'
253+
prop: { template: '<div />' }
252254
},
253255
attrs: {
254256
prop: 'val'

Diff for: test/specs/mounting-options/attrs.spec.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { compileToFunctions } from 'vue-template-compiler'
21
import { attrsSupported } from '~resources/utils'
32
import {
43
describeWithMountingMethods,
@@ -15,7 +14,10 @@ describeWithMountingMethods('options.attrs', mountingMethod => {
1514
'handles inherit attrs',
1615
() => {
1716
if (!attrsSupported) return
18-
const wrapper = mountingMethod(compileToFunctions('<p :id="anAttr" />'), {
17+
const TestComponent = {
18+
template: '<p :id="$attrs.anAttr" />'
19+
}
20+
const wrapper = mountingMethod(TestComponent, {
1921
attrs: {
2022
anAttr: 'an attribute'
2123
}
@@ -29,7 +31,7 @@ describeWithMountingMethods('options.attrs', mountingMethod => {
2931
mountingMethod.name === 'renderToString' || vueVersion < 2.5,
3032
'defines attrs as empty object even when not passed',
3133
() => {
32-
const wrapper = mountingMethod(compileToFunctions('<p />'))
34+
const wrapper = mountingMethod({ template: '<p />' })
3335
expect(wrapper.vm.$attrs).to.deep.equal({})
3436
}
3537
)

Diff for: test/specs/mounting-options/listeners.spec.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { compileToFunctions } from 'vue-template-compiler'
21
import { listenersSupported } from '~resources/utils'
32
import {
43
describeWithShallowAndMount,
@@ -14,7 +13,9 @@ describeWithShallowAndMount('options.listeners', mountingMethod => {
1413
() => {
1514
const aListener = () => {}
1615
const wrapper = mountingMethod(
17-
compileToFunctions('<p :id="aListener" />'),
16+
{
17+
template: '<p :id="$listeners.aListener" />'
18+
},
1819
{
1920
listeners: {
2021
aListener
@@ -56,7 +57,7 @@ describeWithShallowAndMount('options.listeners', mountingMethod => {
5657
vueVersion < 2.5,
5758
'defines listeners as empty object even when not passed',
5859
() => {
59-
const wrapper = mountingMethod(compileToFunctions('<p />'))
60+
const wrapper = mountingMethod({ template: '<p />' })
6061
expect(wrapper.vm.$listeners).to.deep.equal({})
6162
}
6263
)

Diff for: test/specs/mounting-options/localVue.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ describeWithMountingMethods('options.localVue', mountingMethod => {
8989
const Extends = {
9090
template: '<div />',
9191
created() {
92-
console.log(this.$route.params)
92+
this.$route.params
9393
}
9494
}
9595
const TestComponent = {

Diff for: test/specs/mounting-options/mocks.spec.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ import { describeWithMountingMethods, vueVersion } from '~resources/utils'
66
import { itDoNotRunIf, itSkipIf, itRunIf } from 'conditional-specs'
77

88
describeWithMountingMethods('options.mocks', mountingMethod => {
9+
const sandbox = sinon.createSandbox()
910
let configMocksSave
1011

1112
beforeEach(() => {
1213
configMocksSave = config.mocks
1314
config.mocks = {}
14-
sinon.stub(console, 'error')
15+
sandbox.stub(console, 'error').callThrough()
1516
})
1617

1718
afterEach(() => {
1819
config.mocks = configMocksSave
19-
console.error.restore()
20+
sandbox.reset()
21+
sandbox.restore()
2022
})
2123

2224
it('adds variables to vm when passed', () => {
@@ -69,7 +71,7 @@ describeWithMountingMethods('options.mocks', mountingMethod => {
6971
mountingMethod.name === 'renderToString',
7072
'adds variables as reactive properties to vm when passed',
7173
() => {
72-
const stub = sinon.stub()
74+
const stub = sandbox.stub()
7375
const $reactiveMock = { value: 'value' }
7476
const wrapper = mountingMethod(
7577
{

0 commit comments

Comments
 (0)