Skip to content
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

feat(test-utils): add 'overview' function #1491

Merged
merged 6 commits into from Apr 8, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/api/wrapper/overview.md
@@ -0,0 +1,43 @@
## overview

Prints a simple overview of the `Wrapper`.

- **Example:**

```js
import { mount } from '@vue/test-utils'
import Component from './Component.vue'

const wrapper = mount(Component)
wrapper.overview()

// Console output
/*
Wrapper (Visible):

Html:
<div class="test">
<p>My name is Tess Ting</p>
</div>

Data: {
firstName: Tess,
lastName: Ting
}

Computed: {
fullName: Tess Ting'
}

Emitted: {',
foo: [',
0: [ hello, world ],
1: [ bye, world ]'
],
bar: [
0: [ hey ]'
]
}

*/
```
1 change: 1 addition & 0 deletions flow/wrapper.flow.js
Expand Up @@ -26,6 +26,7 @@ declare interface BaseWrapper {
isVisible(): boolean | void;
isVueInstance(): boolean | void;
name(): string | void;
overview(): void;
props(key?: string): { [name: string]: any } | any | void;
text(): string | void;
selector: Selector | void;
Expand Down
8 changes: 8 additions & 0 deletions packages/test-utils/src/error-wrapper.js
Expand Up @@ -187,6 +187,14 @@ export default class ErrorWrapper implements BaseWrapper {
)
}

overview(): void {
throwError(
`find did not return ${buildSelectorString(
this.selector
)}, cannot call overview() on empty Wrapper`
)
}

props(): void {
throwError(
`find did not return ${buildSelectorString(
Expand Down
9 changes: 9 additions & 0 deletions packages/test-utils/src/wrapper-array.js
Expand Up @@ -143,6 +143,15 @@ export default class WrapperArray implements BaseWrapper {
)
}

overview(): void {
this.throwErrorIfWrappersIsEmpty('overview()')

throwError(
`overview() must be called on a single wrapper, use at(i) ` +
`to access a wrapper`
)
}

props(): void {
this.throwErrorIfWrappersIsEmpty('props')

Expand Down
71 changes: 71 additions & 0 deletions packages/test-utils/src/wrapper.js
Expand Up @@ -324,6 +324,77 @@ export default class Wrapper implements BaseWrapper {
return this.vnode.tag
}

/**
* Prints a simple overview of the wrapper current state
* with useful information for debugging
*/
overview(): void {
if (!this.isVueInstance()) {
throwError(`wrapper.overview() can only be called on a Vue instance`)
}

const identation = 4
const formatJSON = (json: any, replacer: Function | null = null) =>
JSON.stringify(json, replacer, identation).replace(/"/g, '')

const visibility = this.isVisible() ? 'Visible' : 'Not visible'

const html = this.html()
? this.html().replace(/^(?!\s*$)/gm, ' '.repeat(identation)) + '\n'
: ''

// $FlowIgnore
const data = formatJSON(this.vm.$data)

/* eslint-disable operator-linebreak */
// $FlowIgnore
const computed = this.vm._computedWatchers
? formatJSON(
// $FlowIgnore
...Object.keys(this.vm._computedWatchers).map(computedKey => ({
// $FlowIgnore
[computedKey]: this.vm[computedKey]
}))
)
: // $FlowIgnore
this.vm.$options.computed
? formatJSON(
// $FlowIgnore
...Object.entries(this.vm.$options.computed).map(([key, value]) => ({
// $FlowIgnore
[key]: value()
}))
)
: '{}'
/* eslint-enable operator-linebreak */

const emittedJSONReplacer = (key, value) =>
value instanceof Array
? value.map((calledWith, index) => {
const callParams = calledWith.map(param =>
typeof param === 'object'
? JSON.stringify(param)
.replace(/"/g, '')
.replace(/,/g, ', ')
: param
)

return `${index}: [ ${callParams.join(', ')} ]`
})
: value

const emitted = formatJSON(this.emitted(), emittedJSONReplacer)

console.log(
'\n' +
`Wrapper (${visibility}):\n\n` +
`Html:\n${html}\n` +
`Data: ${data}\n\n` +
`Computed: ${computed}\n\n` +
`Emitted: ${emitted}\n`
)
}

/**
* Returns an Object containing the prop name/value pairs on the element
*/
Expand Down
1 change: 1 addition & 0 deletions test/specs/error-wrapper.spec.js
Expand Up @@ -22,6 +22,7 @@ describeWithShallowAndMount('ErrorWrapper', mountingMethod => {
'isVisible',
'isVueInstance',
'name',
'overview',
'props',
'setComputed',
'setMethods',
Expand Down
24 changes: 24 additions & 0 deletions test/specs/wrapper-array/overview.spec.js
@@ -0,0 +1,24 @@
import { describeWithShallowAndMount } from '~resources/utils'
import { compileToFunctions } from 'vue-template-compiler'
import '@vue/test-utils'

describeWithShallowAndMount('overview', mountingMethod => {
it('throws error if wrapper array contains no items', () => {
const wrapper = mountingMethod(compileToFunctions('<div />'))
const message = '[vue-test-utils]: overview() cannot be called on 0 items'

expect(() => wrapper.findAll('p').overview())
.to.throw()
.with.property('message', message)
})

it('throws error when called on a WrapperArray', () => {
const wrapper = mountingMethod(compileToFunctions('<div><div /></div>'))
const message =
'[vue-test-utils]: overview() must be called on a single wrapper, use at(i) to access a wrapper'

expect(() => wrapper.findAll('div').overview())
.to.throw()
.with.property('message', message)
})
})