Skip to content

Commit 9bb9a87

Browse files
willsotoeddyerburgh
authored andcommitted
feat(wrapper): add support for getting prop, attribute and classes by key (#941)
1 parent 8b566a6 commit 9bb9a87

File tree

9 files changed

+103
-18
lines changed

9 files changed

+103
-18
lines changed

Diff for: docs/api/wrapper/attributes.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
## attributes()
1+
## attributes([key])
22

3-
Returns `Wrapper` DOM node attribute object.
3+
Returns `Wrapper` DOM node attribute object. If `key` is provided, the value for the `key` will be returned.
44

5-
- **Returns:** `{[attribute: string]: any}`
5+
- **Arguments:**
6+
- `{string} key` **optional**
7+
8+
- **Returns:** `{[attribute: string]: any} | string`
69

710
- **Example:**
811

@@ -12,4 +15,5 @@ import Foo from './Foo.vue'
1215

1316
const wrapper = mount(Foo)
1417
expect(wrapper.attributes().id).toBe('foo')
18+
expect(wrapper.attributes('id')).toBe('foo')
1519
```

Diff for: docs/api/wrapper/classes.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
## classes()
1+
## classes([className])
22

33
Return `Wrapper` DOM node classes.
44

5-
Returns Array of class names.
5+
Returns Array of class names. Or a boolean if a class name is provided.
66

7-
- **Returns:** `Array<{string}>`
7+
- **Arguments:**
8+
- `{string} className` **optional**
9+
10+
- **Returns:** `Array<{string}> | boolean`
811

912
- **Example:**
1013

@@ -14,4 +17,5 @@ import Foo from './Foo.vue'
1417

1518
const wrapper = mount(Foo)
1619
expect(wrapper.classes()).toContain('bar')
20+
expect(wrapper.classes('bar')).toBe(true)
1721
```

Diff for: docs/api/wrapper/props.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
## props()
1+
## props([key])
22

3-
Return `Wrapper` `vm` props object.
3+
Return `Wrapper` `vm` props object. If `key` is provided, the value for the `key` will be returned.
44

55
**Note the Wrapper must contain a Vue instance.**
66

7-
- **Returns:** `{[prop: string]: any}`
7+
- **Arguments:**
8+
- `{string} key` **optional**
9+
10+
- **Returns:** `{[prop: string]: any} | any`
811

912
- **Example:**
1013

@@ -18,4 +21,5 @@ const wrapper = mount(Foo, {
1821
}
1922
})
2023
expect(wrapper.props().bar).toBe('baz')
24+
expect(wrapper.props('bar')).toBe('baz')
2125
```

Diff for: flow/wrapper.flow.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ declare type Components = { [name: string]: Component };
99
declare interface BaseWrapper {
1010
// eslint-disable-line no-undef
1111
at(index: number): Wrapper | void;
12-
attributes(): { [name: string]: string } | void;
13-
classes(): Array<string> | void;
12+
attributes(key?: string): { [name: string]: string } | string | void;
13+
classes(className?: string): Array<string> | boolean | void;
1414
contains(selector: Selector): boolean | void;
1515
emitted(
1616
event?: string
@@ -31,7 +31,7 @@ declare interface BaseWrapper {
3131
isVisible(): boolean | void;
3232
isVueInstance(): boolean | void;
3333
name(): string | void;
34-
props(): { [name: string]: any } | void;
34+
props(key?: string): { [name: string]: any } | any | void;
3535
text(): string | void;
3636
setData(data: Object): void;
3737
setComputed(computed: Object): void;

Diff for: packages/test-utils/src/wrapper.js

+25-6
Original file line numberDiff line numberDiff line change
@@ -78,22 +78,25 @@ export default class Wrapper implements BaseWrapper {
7878
/**
7979
* Returns an Object containing all the attribute/value pairs on the element.
8080
*/
81-
attributes (): { [name: string]: string } {
81+
attributes (key?: string): { [name: string]: string } | string {
8282
const attributes = this.element.attributes
8383
const attributeMap = {}
8484
for (let i = 0; i < attributes.length; i++) {
8585
const att = attributes.item(i)
8686
attributeMap[att.localName] = att.value
8787
}
88+
if (key) {
89+
return attributeMap[key]
90+
}
8891
return attributeMap
8992
}
9093

9194
/**
9295
* Returns an Array containing all the classes on the element
9396
*/
94-
classes (): Array<string> {
95-
const className = this.element.getAttribute('class')
96-
let classes = className ? className.split(' ') : []
97+
classes (className?: string): Array<string> | boolean {
98+
const classAttribute = this.element.getAttribute('class')
99+
let classes = classAttribute ? classAttribute.split(' ') : []
97100
// Handle converting cssmodules identifiers back to the original class name
98101
if (this.vm && this.vm.$style) {
99102
const cssModuleIdentifiers = Object.keys(this.vm.$style)
@@ -106,9 +109,17 @@ export default class Wrapper implements BaseWrapper {
106109
return acc
107110
}, {})
108111
classes = classes.map(
109-
className => cssModuleIdentifiers[className] || className
112+
name => cssModuleIdentifiers[name] || name
110113
)
111114
}
115+
116+
if (className) {
117+
if (classes.indexOf(className) > -1) {
118+
return true
119+
} else {
120+
return false
121+
}
122+
}
112123
return classes
113124
}
114125

@@ -436,7 +447,7 @@ export default class Wrapper implements BaseWrapper {
436447
/**
437448
* Returns an Object containing the prop name/value pairs on the element
438449
*/
439-
props (): { [name: string]: any } {
450+
props (key?: string): { [name: string]: any } | any {
440451
if (this.isFunctionalComponent) {
441452
throwError(
442453
`wrapper.props() cannot be called on a mounted ` +
@@ -457,6 +468,11 @@ export default class Wrapper implements BaseWrapper {
457468
}
458469
})
459470
}
471+
472+
if (key) {
473+
return props[key]
474+
}
475+
460476
return props
461477
}
462478

@@ -468,6 +484,7 @@ export default class Wrapper implements BaseWrapper {
468484
throwError('wrapper.setChecked() must be passed a boolean')
469485
}
470486
const tagName = this.element.tagName
487+
// $FlowIgnore
471488
const type = this.attributes().type
472489

473490
if (tagName === 'SELECT') {
@@ -515,6 +532,7 @@ export default class Wrapper implements BaseWrapper {
515532
*/
516533
setSelected (): void {
517534
const tagName = this.element.tagName
535+
// $FlowIgnore
518536
const type = this.attributes().type
519537

520538
if (tagName === 'OPTION') {
@@ -744,6 +762,7 @@ export default class Wrapper implements BaseWrapper {
744762
*/
745763
setValue (value: any): void {
746764
const tagName = this.element.tagName
765+
// $FlowIgnore
747766
const type = this.attributes().type
748767

749768
if (tagName === 'SELECT') {

Diff for: packages/test-utils/types/index.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ interface BaseWrapper {
5656
visible (): boolean
5757

5858
attributes(): { [name: string]: string }
59+
attributes(key: string): string | void
5960
classes(): Array<string>
61+
classes(className: string): boolean
6062
props(): { [name: string]: any }
63+
props(key: string): any | void
6164

6265
hasAttribute (attribute: string, value: string): boolean
6366
hasClass (className: string): boolean

Diff for: test/specs/wrapper/attributes.spec.js

+16
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,20 @@ describeWithShallowAndMount('attributes', mountingMethod => {
1515
const wrapper = mountingMethod(compiled)
1616
expect(wrapper.attributes()).to.eql({})
1717
})
18+
19+
it('returns the given attribute if wrapper contains attribute matching value', () => {
20+
const attribute = 'attribute'
21+
const value = 'value'
22+
const compiled = compileToFunctions(`<div ${attribute}=${value}></div>`)
23+
const wrapper = mountingMethod(compiled)
24+
expect(wrapper.attributes('attribute')).to.eql(value)
25+
})
26+
27+
it('returns undefined if the wrapper does not contain the matching value', () => {
28+
const attribute = 'attribute'
29+
const value = 'value'
30+
const compiled = compileToFunctions(`<div ${attribute}=${value}></div>`)
31+
const wrapper = mountingMethod(compiled)
32+
expect(wrapper.attributes('fake')).to.eql(undefined)
33+
})
1834
})

Diff for: test/specs/wrapper/classes.spec.js

+16
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,20 @@ describeWithShallowAndMount('classes', mountingMethod => {
3030
expect(wrapper.classes()).to.contain('b-class')
3131
expect(wrapper.find('text').classes()).to.contain('c-class')
3232
})
33+
34+
it('returns true if the element has the class', () => {
35+
const compiled = compileToFunctions(
36+
'<svg class="a-class b-class"><text class="c-class"/></svg>'
37+
)
38+
const wrapper = mountingMethod(compiled)
39+
expect(wrapper.classes('a-class')).to.eql(true)
40+
expect(wrapper.classes('b-class')).to.eql(true)
41+
expect(wrapper.find('text').classes('c-class')).to.eql(true)
42+
expect(wrapper.classes('x-class')).to.eql(false)
43+
})
44+
45+
it('returns false if the element does not have the class', () => {
46+
const wrapper = mountingMethod(ComponentWithCssModules)
47+
expect(wrapper.classes('x-class')).to.eql(false)
48+
})
3349
})

Diff for: test/specs/wrapper/props.spec.js

+19
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,23 @@ describeWithShallowAndMount('props', mountingMethod => {
9191
.to.throw()
9292
.with.property('message', message)
9393
})
94+
95+
it('returns the given prop if a key is provided', () => {
96+
const prop1 = {}
97+
const prop2 = 'string val'
98+
const wrapper = mountingMethod(ComponentWithProps, {
99+
propsData: { prop1, prop2 }
100+
})
101+
expect(wrapper.props('prop1')).to.eql({})
102+
expect(wrapper.props('prop2')).to.eql('string val')
103+
})
104+
105+
it('returns undefined if the given key is not found', () => {
106+
const prop1 = {}
107+
const prop2 = 'string val'
108+
const wrapper = mountingMethod(ComponentWithProps, {
109+
propsData: { prop1, prop2 }
110+
})
111+
expect(wrapper.props('propNotHere')).to.eql(undefined)
112+
})
94113
})

0 commit comments

Comments
 (0)