Skip to content

Commit 42a153a

Browse files
committed
feat(visible): Add visible() method on Wrapper (vuejs#327)
1 parent dd270b6 commit 42a153a

File tree

13 files changed

+268
-1
lines changed

13 files changed

+268
-1
lines changed

Diff for: dist/vue-test-utils.js

+30
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,12 @@ WrapperArray.prototype.exists = function exists () {
419419
return this.length > 0 && this.wrappers.every(function (wrapper) { return wrapper.exists(); })
420420
};
421421

422+
WrapperArray.prototype.visible = function visible () {
423+
this.throwErrorIfWrappersIsEmpty('visible');
424+
425+
return this.length > 0 && this.wrappers.every(function (wrapper) { return wrapper.visible(); })
426+
};
427+
422428
WrapperArray.prototype.emitted = function emitted () {
423429
this.throwErrorIfWrappersIsEmpty('emitted');
424430

@@ -590,6 +596,10 @@ ErrorWrapper.prototype.exists = function exists () {
590596
return false
591597
};
592598

599+
ErrorWrapper.prototype.visible = function visible () {
600+
throwError(("find did not return " + (this.selector) + ", cannot call visible() on empty Wrapper"));
601+
};
602+
593603
ErrorWrapper.prototype.hasAttribute = function hasAttribute () {
594604
throwError(("find did not return " + (this.selector) + ", cannot call hasAttribute() on empty Wrapper"));
595605
};
@@ -874,6 +884,26 @@ Wrapper.prototype.exists = function exists () {
874884
return true
875885
};
876886

887+
/**
888+
* Utility to check wrapper is visible. Returns false if a parent element has display: none or visibility: hidden style.
889+
*/
890+
Wrapper.prototype.visible = function visible () {
891+
var element = this.element;
892+
893+
if (!element) {
894+
return false
895+
}
896+
897+
while (element) {
898+
if (element.style && (element.style.visibility === 'hidden' || element.style.display === 'none')) {
899+
return false
900+
}
901+
element = element.parentElement;
902+
}
903+
904+
return true
905+
};
906+
877907
/**
878908
* Checks if wrapper has an attribute with matching value
879909
*/

Diff for: docs/en/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
* [text](api/wrapper/text.md)
5353
* [trigger](api/wrapper/trigger.md)
5454
* [update](api/wrapper/update.md)
55+
* [visible](api/wrapper/visible.md)
5556
* [WrapperArray](api/wrapper-array/README.md)
5657
* [at](api/wrapper-array/at.md)
5758
* [contains](api/wrapper-array/contains.md)
@@ -67,6 +68,7 @@
6768
* [setProps](api/wrapper-array/setProps.md)
6869
* [trigger](api/wrapper-array/trigger.md)
6970
* [update](api/wrapper-array/update.md)
71+
* [visible](api/wrapper-array/visible.md)
7072
* [components](api/components/README.md)
7173
* [TransitionStub](api/components/TransitionStub.md)
7274
* [TransitionGroupStub](api/components/TransitionGroupStub.md)

Diff for: docs/en/SUMMARY.md

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* [text](api/wrapper/text.md)
4949
* [trigger](api/wrapper/trigger.md)
5050
* [update](api/wrapper/update.md)
51+
* [visible](api/wrapper/visible.md)
5152
* [WrapperArray](api/wrapper-array/README.md)
5253
* [at](api/wrapper-array/at.md)
5354
* [contains](api/wrapper-array/contains.md)
@@ -63,6 +64,7 @@
6364
* [setProps](api/wrapper-array/setProps.md)
6465
* [trigger](api/wrapper-array/trigger.md)
6566
* [update](api/wrapper-array/update.md)
67+
* [visible](api/wrapper-array/visible.md)
6668
* [components](api/components/README.md)
6769
* [TransitionStub](api/components/TransitionStub.md)
6870
* [TransitionGroupStub](api/components/TransitionGroupStub.md)

Diff for: docs/en/api/wrapper-array/visible.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# visible()
2+
3+
Assert every `Wrapper` in `WrapperArray` is visible.
4+
5+
Returns false if at least one ancestor element has `display: none` or `visibility: hidden` style.
6+
7+
This can be used to assert that a component is hidden by `v-show`.
8+
9+
- **Returns:** `{boolean}`
10+
11+
- **Example:**
12+
13+
```js
14+
import { mount } from 'vue-test-utils'
15+
import { expect } from 'chai'
16+
import Foo from './Foo.vue'
17+
18+
const wrapper = mount(Foo)
19+
expect(wrapper.visible()).toBe(true)
20+
expect(wrapper.findAll('.is-not-visible').visible()).toBe(false)
21+
expect(wrapper.findAll('.is-visible').visible()).toBe(true)
22+
```

Diff for: docs/en/api/wrapper/visible.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# visible()
2+
3+
Assert `Wrapper` or `WrapperArray` is visible.
4+
5+
Returns false if an ancestor element has `display: none` or `visibility: hidden` style.
6+
7+
This can be used to assert that a component is hidden by `v-show`.
8+
9+
- **Returns:** `{boolean}`
10+
11+
- **Example:**
12+
13+
```js
14+
import { mount } from 'vue-test-utils'
15+
import { expect } from 'chai'
16+
import Foo from './Foo.vue'
17+
18+
const wrapper = mount(Foo)
19+
expect(wrapper.visible()).toBe(true)
20+
expect(wrapper.find('.is-not-visible').visible()).toBe(false)
21+
expect(wrapper.findAll('div').visible()).toBe(true)
22+
expect(wrapper.findAll('.is-not-visible').visible()).toBe(false)
23+
```

Diff for: flow/wrapper.flow.js

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ declare interface BaseWrapper { // eslint-disable-line no-undef
1313
emitted(event?: string): { [name: string]: Array<Array<any>> } | Array<Array<any>> | void,
1414
emittedByOrder(): Array<{ name: string; args: Array<any> }> | void,
1515
exists(): boolean,
16+
visible(): boolean | void,
1617
hasAttribute(attribute: string, value: string): boolean | void,
1718
hasClass(className: string): boolean | void,
1819
hasProp(prop: string, value: string): boolean | void,

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
],
1111
"scripts": {
1212
"build": "node build/build.js",
13-
"build:test": "NODE_ENV=test node build/build.js",
13+
"build:test": "cross-env NODE_ENV=test node build/build.js",
1414
"coverage": "cross-env NODE_ENV=coverage nyc --reporter=lcov --reporter=text npm run test:unit",
1515
"docs": "cd docs && gitbook install && gitbook serve",
1616
"docs:deploy": "build/update-docs.sh",

Diff for: src/wrappers/error-wrapper.js

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export default class ErrorWrapper implements BaseWrapper {
3636
return false
3737
}
3838

39+
visible (): void {
40+
throwError(`find did not return ${this.selector}, cannot call visible() on empty Wrapper`)
41+
}
42+
3943
hasAttribute (): void {
4044
throwError(`find did not return ${this.selector}, cannot call hasAttribute() on empty Wrapper`)
4145
}

Diff for: src/wrappers/wrapper-array.js

+6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ export default class WrapperArray implements BaseWrapper {
4242
return this.length > 0 && this.wrappers.every(wrapper => wrapper.exists())
4343
}
4444

45+
visible (): boolean {
46+
this.throwErrorIfWrappersIsEmpty('visible')
47+
48+
return this.length > 0 && this.wrappers.every(wrapper => wrapper.visible())
49+
}
50+
4551
emitted (): void {
4652
this.throwErrorIfWrappersIsEmpty('emitted')
4753

Diff for: src/wrappers/wrapper.js

+20
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,26 @@ export default class Wrapper implements BaseWrapper {
120120
return true
121121
}
122122

123+
/**
124+
* Utility to check wrapper is visible. Returns false if a parent element has display: none or visibility: hidden style.
125+
*/
126+
visible (): boolean {
127+
let element = this.element
128+
129+
if (!element) {
130+
return false
131+
}
132+
133+
while (element) {
134+
if (element.style && (element.style.visibility === 'hidden' || element.style.display === 'none')) {
135+
return false
136+
}
137+
element = element.parentElement
138+
}
139+
140+
return true
141+
}
142+
123143
/**
124144
* Checks if wrapper has an attribute with matching value
125145
*/

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

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<template>
2+
<div v-show="rootReady">
3+
<div v-show="!ready" class="not-ready">not-ready</div>
4+
5+
<div v-show="ready" class="parent ready">
6+
<div class="child ready">ready</div>
7+
</div>
8+
</div>
9+
</template>
10+
11+
<script>
12+
export default {
13+
name: 'component-with-show',
14+
data: () => ({
15+
ready: false,
16+
rootReady: true
17+
})
18+
}
19+
</script>

Diff for: test/unit/specs/mount/Wrapper/visible.spec.js

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { compileToFunctions } from 'vue-template-compiler'
2+
import { mount } from '~vue-test-utils'
3+
import ComponentWithVShow from '~resources/components/component-with-v-show.vue'
4+
import ComponentWithVIf from '~resources/components/component-with-v-if.vue'
5+
6+
describe('visible', () => {
7+
it('returns true if element has no inline style', () => {
8+
const compiled = compileToFunctions('<div><div><span class="visible"></span></div></div>')
9+
const wrapper = mount(compiled)
10+
const element = wrapper.find('.visible')
11+
expect(element.visible()).to.equal(true)
12+
})
13+
14+
it('returns false if element has inline style display: none', () => {
15+
const compiled = compileToFunctions('<div><div><span style="display: none;" class="visible"></span></div></div>')
16+
const wrapper = mount(compiled)
17+
const element = wrapper.find('.visible')
18+
expect(element.visible()).to.equal(false)
19+
})
20+
21+
it('returns false if element has inline style visibility: hidden', () => {
22+
const compiled = compileToFunctions('<div><div><span style="visibility: hidden;" class="visible"></span></div></div>')
23+
const wrapper = mount(compiled)
24+
const element = wrapper.find('.visible')
25+
expect(element.visible()).to.equal(false)
26+
})
27+
28+
it('returns true if element has v-show true', () => {
29+
const wrapper = mount(ComponentWithVShow)
30+
wrapper.vm.$set(wrapper.vm, 'ready', true)
31+
wrapper.update()
32+
33+
const notReadyElement = wrapper.find('.not-ready')
34+
expect(notReadyElement.visible()).to.equal(false)
35+
36+
const readyElement = wrapper.find('.parent.ready')
37+
expect(readyElement.visible()).to.equal(true)
38+
})
39+
40+
it('returns false if element has v-show true', () => {
41+
const wrapper = mount(ComponentWithVShow)
42+
wrapper.vm.$set(wrapper.vm, 'ready', true)
43+
wrapper.update()
44+
45+
const notReadyElement = wrapper.find('.not-ready')
46+
expect(notReadyElement.visible()).to.equal(false)
47+
48+
const readyElement = wrapper.find('.parent.ready')
49+
expect(readyElement.visible()).to.equal(true)
50+
})
51+
52+
it('returns true if parent element has v-show true', () => {
53+
const wrapper = mount(ComponentWithVShow)
54+
wrapper.vm.$set(wrapper.vm, 'ready', true)
55+
wrapper.update()
56+
57+
const notReadyElement = wrapper.find('.not-ready')
58+
expect(notReadyElement.visible()).to.equal(false)
59+
60+
const readyChildElement = wrapper.find('.child.ready')
61+
expect(readyChildElement.visible()).to.equal(true)
62+
})
63+
64+
it('returns false if parent element has v-show false', () => {
65+
const wrapper = mount(ComponentWithVShow)
66+
wrapper.vm.$set(wrapper.vm, 'ready', true)
67+
wrapper.update()
68+
69+
const notReadyElement = wrapper.find('.not-ready')
70+
expect(notReadyElement.visible()).to.equal(false)
71+
72+
const readyChildElement = wrapper.find('.child.ready')
73+
expect(readyChildElement.visible()).to.equal(true)
74+
})
75+
76+
it('returns false if root element has v-show false and parent has v-show true', () => {
77+
const wrapper = mount(ComponentWithVShow)
78+
wrapper.vm.$set(wrapper.vm, 'ready', true)
79+
wrapper.vm.$set(wrapper.vm, 'rootReady', false)
80+
wrapper.update()
81+
82+
const notReadyElement = wrapper.find('.not-ready')
83+
expect(notReadyElement.visible()).to.equal(false)
84+
85+
const readyChildElement = wrapper.find('.child.ready')
86+
expect(readyChildElement.visible()).to.equal(false)
87+
})
88+
89+
it('returns false if root element has v-show true and parent has v-show false', () => {
90+
const wrapper = mount(ComponentWithVShow)
91+
wrapper.vm.$set(wrapper.vm, 'ready', false)
92+
wrapper.vm.$set(wrapper.vm, 'rootReady', true)
93+
wrapper.update()
94+
95+
const notReadyElement = wrapper.find('.not-ready')
96+
expect(notReadyElement.visible()).to.equal(true)
97+
98+
const readyChildElement = wrapper.find('.child.ready')
99+
expect(readyChildElement.visible()).to.equal(false)
100+
})
101+
102+
it('returns true if all elements are visible', () => {
103+
const wrapper = mount(ComponentWithVShow)
104+
wrapper.vm.$set(wrapper.vm, 'ready', true)
105+
wrapper.vm.$set(wrapper.vm, 'rootReady', true)
106+
wrapper.update()
107+
108+
const readyChildElement = wrapper.find('.ready')
109+
expect(readyChildElement.visible()).to.equal(true)
110+
})
111+
112+
it('returns false if one element is not visible', () => {
113+
const wrapper = mount(ComponentWithVShow)
114+
wrapper.vm.$set(wrapper.vm, 'ready', true)
115+
wrapper.vm.$set(wrapper.vm, 'rootReady', true)
116+
wrapper.update()
117+
118+
const readyChildElement = wrapper.find('.ready, .not-ready')
119+
expect(readyChildElement.visible()).to.equal(false)
120+
})
121+
122+
it('fails if one element is absent', () => {
123+
const wrapper = mount(ComponentWithVIf)
124+
wrapper.vm.$set(wrapper.vm, 'ready', false)
125+
wrapper.update()
126+
127+
const fn = () => wrapper.find('.child.ready').visible()
128+
expect(fn).to.throw()
129+
})
130+
131+
it('returns true if one element is present', () => {
132+
const wrapper = mount(ComponentWithVIf)
133+
wrapper.vm.$set(wrapper.vm, 'ready', true)
134+
wrapper.update()
135+
expect(wrapper.find('.child.ready').visible()).to.equal(true)
136+
})
137+
})

Diff for: types/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type RefSelector = {
4545
interface BaseWrapper {
4646
contains (selector: Selector): boolean
4747
exists (): boolean
48+
visible (): boolean
4849

4950
attributes(): { [name: string]: string } | void
5051
classes(): Array<string> | void

0 commit comments

Comments
 (0)