Skip to content

Commit

Permalink
fix: prevent infinite loop with setProps with immediate watchers
Browse files Browse the repository at this point in the history
* prevent `setProps` from being called on non-top level wrappers
* remove useless `silent` option from config
  • Loading branch information
xanf committed Dec 14, 2020
1 parent d2add54 commit 98b8e68
Show file tree
Hide file tree
Showing 19 changed files with 143 additions and 202 deletions.
17 changes: 1 addition & 16 deletions docs/api/config.md
Expand Up @@ -47,7 +47,7 @@ config.deprecationWarningHandler = (method, message) => {
- type: `{ [name: string]: Component | boolean | string }`
- default: `{}`

The stub stored in `config.stubs` is used by default.
The stub stored in `config.stubs` is used by default.
Stubs to use in components. These are overwritten by `stubs` passed in the mounting options.

When passing `stubs` as an array in the mounting options, `config.stubs` are converted to an array, and will stub components with a basic component that returns `<${component name}-stub>`.
Expand Down Expand Up @@ -112,18 +112,3 @@ config.provide['$logger'] = {
}
}
```

### `silent`

- type: `Boolean`
- default: `true`

It suppresses warnings triggered by Vue while mutating component's observables (e.g. props). When set to `false`, all warnings are visible in the console. This is a configurable way which relies on `Vue.config.silent`.

Example:

```js
import { config } from '@vue/test-utils'

config.silent = false
```
4 changes: 3 additions & 1 deletion docs/api/wrapper/setProps.md
Expand Up @@ -8,7 +8,9 @@

Sets `Wrapper` `vm` props and forces update.

**Note the Wrapper must contain a Vue instance.**
::: warning
`setProps` could be called only for top-level component, mounted by `mount` or `shallowMount`
:::

```js
import { mount } from '@vue/test-utils'
Expand Down
15 changes: 0 additions & 15 deletions docs/ja/api/config.md
Expand Up @@ -73,18 +73,3 @@ config.provide['$logger'] = {
}
}
```

### `silent`

- 型: `Boolean`
- デフォルト: `true`

Vue がコンポーネントの変更を感知するプロパティ(例えば props )が変更される時に出す警告を出力しません。`false` をセットするとすべての警告はコンソールに表示されません。この機能は `Vue.config.silent` を使って実現しています。

例:

```js
import { config } from '@vue/test-utils'

config.silent = false
```
15 changes: 0 additions & 15 deletions docs/ru/api/config.md
Expand Up @@ -74,18 +74,3 @@ config.provide['$logger'] = {
}
}
```

### `silent`

- Тип: `Boolean`
- По умолчанию: `true`

Подавляет предупреждения, вызванные Vue во время изменения наблюдаемых компонентов (например, входных параметров). Если установлено значение `false`, все предупреждения показываются в консоли. Это настраиваемый способ, который основывается на `Vue.config.silent`.

Пример:

```js
import { config } from '@vue/test-utils'

config.silent = false
```
4 changes: 3 additions & 1 deletion docs/ru/api/wrapper/setProps.md
Expand Up @@ -8,7 +8,9 @@

Устанавливает входные параметры `Wrapper` `vm` и выполняет принудительное обновление.

**Обратите внимание, что `Wrapper` должен содержать экземпляр Vue.**
::: warning Обратите внимание!
`setProps` может быть вызван только на `wrapper` верхнего уровня, который был создан с помощью `mount` или `shallowMount`
:::

```js
import { mount } from '@vue/test-utils'
Expand Down
17 changes: 1 addition & 16 deletions docs/zh/api/config.md
Expand Up @@ -24,7 +24,7 @@ config.showDeprecationWarnings = false
- 类型:`{ [name: string]: Component | boolean | string }`
- 默认值:`{}`

存储在 `config.stubs` 中的存根会被默认使用。
存储在 `config.stubs` 中的存根会被默认使用。
用到的组件存根。它们会被传入挂载选项的 `stubs` 覆写。

当把 `stubs` 作为一个数组传入挂载选项时,`config.stubs` 会被转换为一个数组,然后用只返回一个 `<${component name}-stub>` 的基础组件进行存根。
Expand Down Expand Up @@ -89,18 +89,3 @@ config.provide['$logger'] = {
}
}
```

### `silent`

- 类型:`Boolean`
- 默认值:`true`

在组件的可观察内容 (如 props) 发生突变时,警告会被 Vue 阻止。当设置为 `false` 时,所有的警告都会出现在控制台中。这是一个 `Vue.config.silent` 的配置方式。

示例;

```js
import { config } from '@vue/test-utils'

config.silent = false
```
1 change: 0 additions & 1 deletion flow/config.flow.js
Expand Up @@ -3,6 +3,5 @@ declare type Config = {
mocks?: Object,
methods?: { [name: string]: Function },
provide?: Object,
silent?: boolean,
showDeprecationWarnings?: boolean
}
18 changes: 14 additions & 4 deletions packages/create-instance/create-instance.js
Expand Up @@ -11,7 +11,7 @@ import createScopedSlots from './create-scoped-slots'
import { createStubsFromStubsObject } from './create-component-stubs'
import { patchCreateElement } from './patch-create-element'

function createContext(options, scopedSlots) {
function createContext(options, scopedSlots, currentProps) {
const on = {
...(options.context && options.context.on),
...options.listeners
Expand All @@ -20,8 +20,8 @@ function createContext(options, scopedSlots) {
attrs: {
...options.attrs,
// pass as attrs so that inheritAttrs works correctly
// propsData should take precedence over attrs
...options.propsData
// props should take precedence over attrs
...currentProps
},
...(options.context || {}),
on,
Expand Down Expand Up @@ -110,16 +110,26 @@ export default function createInstance(
parentComponentOptions.provide = function() {
return {
...getValuesFromCallableOption.call(this, originalParentComponentProvide),
// $FlowIgnore
...getValuesFromCallableOption.call(this, options.provide)
}
}

const originalParentComponentData = parentComponentOptions.data
parentComponentOptions.data = function() {
return {
...getValuesFromCallableOption.call(this, originalParentComponentData),
vueTestUtils_childProps: { ...options.propsData }
}
}

parentComponentOptions.$_doNotStubChildren = true
parentComponentOptions.$_isWrapperParent = true
parentComponentOptions._isFunctionalContainer = componentOptions.functional
parentComponentOptions.render = function(h) {
return h(
Constructor,
createContext(options, scopedSlots),
createContext(options, scopedSlots, this.vueTestUtils_childProps),
createChildren(this, h, options)
)
}
Expand Down
47 changes: 23 additions & 24 deletions packages/server-test-utils/dist/vue-server-test-utils.js
Expand Up @@ -13,7 +13,7 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
var Vue__default = /*#__PURE__*/_interopDefaultLegacy(Vue);
var cheerio__default = /*#__PURE__*/_interopDefaultLegacy(cheerio);

//
//

function createVNodes(vm, slotValue, name) {
var el = vueTemplateCompiler.compileToFunctions(
Expand Down Expand Up @@ -1698,7 +1698,7 @@ var CREATE_ELEMENT_ALIAS = semver.gt(Vue__default['default'].version, '2.1.5')
? '_c'
: '_h';

//
//

function findDOMNodes(
element,
Expand All @@ -1716,7 +1716,7 @@ function findDOMNodes(
return nodes.concat([].slice.call(element.querySelectorAll(selector)))
}

//
//

function isDomSelector(selector) {
if (typeof selector !== 'string') {
Expand Down Expand Up @@ -1956,7 +1956,7 @@ function matches(node, selector) {
return vmMatchesName(componentInstance, nameSelector)
}

//
//

function findAllInstances(rootVm) {
var instances = [rootVm];
Expand Down Expand Up @@ -2094,7 +2094,7 @@ function normalizeProvide(provide) {
return provide
}

//
//

function getOption(option, config) {
if (option === false) {
Expand Down Expand Up @@ -2149,7 +2149,6 @@ var config = {
mocks: {},
methods: {},
provide: {},
silent: true,
showDeprecationWarnings:
typeof process.env.SHOW_DEPRECATIONS !== 'undefined'
? process.env.SHOW_DEPRECATIONS
Expand Down Expand Up @@ -7541,7 +7540,7 @@ function ocd(str, options) {
.replace(/>(\s*)(?=<!--\s*\/)/g, '> ');
}

//
//

function getSelectorType(selector) {
if (isDomSelector(selector)) { return DOM_SELECTOR }
Expand Down Expand Up @@ -7569,7 +7568,7 @@ function getSelector(
}
}

//
//

var WrapperArray = function WrapperArray(wrappers) {
var length = wrappers.length;
Expand Down Expand Up @@ -7795,7 +7794,7 @@ WrapperArray.prototype.destroy = function destroy () {
this.wrappers.forEach(function (wrapper) { return wrapper.destroy(); });
};

//
//

var buildSelectorString = function (selector) {
if (getSelectorType(selector) === REF_SELECTOR) {
Expand Down Expand Up @@ -9439,7 +9438,7 @@ function createDOMEvent(type, options) {
return event
}

//
//

var Wrapper = function Wrapper(
node,
Expand Down Expand Up @@ -10321,7 +10320,7 @@ Wrapper.prototype.trigger = function trigger (type, options) {
return nextTick()
};

//
//

var VueWrapper = /*@__PURE__*/(function (Wrapper) {
function VueWrapper(vm, options) {
Expand Down Expand Up @@ -10360,7 +10359,7 @@ var VueWrapper = /*@__PURE__*/(function (Wrapper) {
return VueWrapper;
}(Wrapper));

//
//

function createWrapper(
node,
Expand Down Expand Up @@ -12862,7 +12861,7 @@ function cloneDeep(value) {

var cloneDeep_1 = cloneDeep;

//
//

/**
* Used internally by vue-server-test-utils and test-utils to propagate/create vue instances.
Expand Down Expand Up @@ -12931,7 +12930,7 @@ function _createLocalVue(
return instance
}

//
//

function compileTemplate(component) {
if (component.template) {
Expand Down Expand Up @@ -12986,7 +12985,7 @@ function compileTemplateForSlots(slots) {
});
}

//
//

function isValidSlot(slot) {
return isVueComponent(slot) || typeof slot === 'string'
Expand Down Expand Up @@ -13083,7 +13082,7 @@ function validateOptions(options, component) {
Vue__default['default'].config.productionTip = false;
Vue__default['default'].config.devtools = false;

//
//

function throwError(msg) {
throw new Error(("[vue-test-utils]: " + msg))
Expand Down Expand Up @@ -13192,7 +13191,7 @@ function warnDeprecated(method, fallback) {
}
}

//
//

function addMocks(
_Vue,
Expand All @@ -13219,7 +13218,7 @@ function addMocks(
});
}

//
//

function logEvents(
vm,
Expand Down Expand Up @@ -13256,7 +13255,7 @@ function addStubs(_Vue, stubComponents) {
_Vue.mixin(( obj = {}, obj[BEFORE_RENDER_LIFECYCLE_HOOK] = addStubComponentsMixin, obj ));
}

//
//

var MOUNTING_OPTIONS = [
'attachToDocument',
Expand All @@ -13280,7 +13279,7 @@ function extractInstanceOptions(options) {
return instanceOptions
}

//
//

function isDestructuringSlotScope(slotScope) {
return /^{.*}$/.test(slotScope)
Expand Down Expand Up @@ -13409,7 +13408,7 @@ function createScopedSlots(
return scopedSlots
}

//
//

function isVueComponentStub(comp) {
return (comp && comp.template) || isVueComponent(comp)
Expand Down Expand Up @@ -13725,7 +13724,7 @@ function patchCreateElement(_Vue, stubs, stubAllComponents) {
_Vue.mixin(( obj = {}, obj[BEFORE_RENDER_LIFECYCLE_HOOK] = patchCreateElementMixin, obj ));
}

//
//

function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; }

Expand Down Expand Up @@ -13849,7 +13848,7 @@ function createInstance(

var config$1 = testUtils.config;

//
//

Vue__default['default'].config.productionTip = false;
Vue__default['default'].config.devtools = false;
Expand Down Expand Up @@ -13884,7 +13883,7 @@ function renderToString(
return renderer.renderToString(vm)
}

//
//

function render(
component,
Expand Down
1 change: 0 additions & 1 deletion packages/server-test-utils/types/index.d.ts
Expand Up @@ -45,7 +45,6 @@ interface VueTestUtilsConfigOptions {
mocks?: object
methods?: Record<string, Function>
provide?: object,
silent?: Boolean
}

export declare let config: VueTestUtilsConfigOptions
Expand Down
1 change: 0 additions & 1 deletion packages/server-test-utils/types/test/renderToString.ts
Expand Up @@ -66,4 +66,3 @@ config.methods = {
config.provide = {
foo: {}
}
config.silent = true

0 comments on commit 98b8e68

Please sign in to comment.