Skip to content

Support for classes inspection #141

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

Merged
merged 5 commits into from
Nov 20, 2017
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/wrappers/error-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ export default class ErrorWrapper implements BaseWrapper {
throwError(`find did not return ${this.selector}, cannot call at() on empty Wrapper`)
}

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

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

contains (): void {
throwError(`find did not return ${this.selector}, cannot call contains() on empty Wrapper`)
}
Expand Down Expand Up @@ -72,6 +80,10 @@ export default class ErrorWrapper implements BaseWrapper {
throwError(`find did not return ${this.selector}, cannot call name() on empty Wrapper`)
}

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

text (): void {
throwError(`find did not return ${this.selector}, cannot call text() on empty Wrapper`)
}
Expand Down
18 changes: 18 additions & 0 deletions src/wrappers/wrapper-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ export default class WrapperArray implements BaseWrapper {
return this.wrappers[index]
}

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

throwError('attributes must be called on a single wrapper, use at(i) to access a wrapper')
}

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

throwError('classes must be called on a single wrapper, use at(i) to access a wrapper')
}

contains (selector: Selector): boolean {
this.throwErrorIfWrappersIsEmpty('contains')

Expand Down Expand Up @@ -107,6 +119,12 @@ export default class WrapperArray implements BaseWrapper {
throwError('name must be called on a single wrapper, use at(i) to access a wrapper')
}

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

throwError('props must be called on a single wrapper, use at(i) to access a wrapper')
}

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

Expand Down
49 changes: 49 additions & 0 deletions src/wrappers/wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,38 @@ export default class Wrapper implements BaseWrapper {
throwError('at() must be called on a WrapperArray')
}

/**
* Returns an Object containing all the attribute/value pairs on the element.
*/
attributes (): { [name: string]: string } {
const attributes = [...this.element.attributes] // NameNodeMap is not iterable
const attributeMap = {}
attributes.forEach((att) => {
attributeMap[att.localName] = att.value
})
return attributeMap
}

/**
* Returns an Array containing all the classes on the element
*/
classes (): Array<string> {
let classes = [...this.element.classList]
// Handle converting cssmodules identifiers back to the original class name
if (this.vm && this.vm.$style) {
const cssModuleIdentifiers = {}
let moduleIdent
Object.keys(this.vm.$style).forEach((key) => {
moduleIdent = this.vm.$style[key]
// CSS Modules may be multi-class if they extend others. Extended classes should be already present in $style.
moduleIdent = moduleIdent.split(' ')[0]
cssModuleIdentifiers[moduleIdent] = key
})
classes = classes.map(className => cssModuleIdentifiers[className] || className)
}
return classes
}

/**
* Checks if wrapper contains provided selector.
*/
Expand Down Expand Up @@ -309,6 +341,23 @@ export default class Wrapper implements BaseWrapper {
return this.vnode.tag
}

/**
* Returns an Object containing the prop name/value pairs on the element
*/
props (): { [name: string]: any } {
if (!this.isVueComponent) {
throwError('wrapper.props() must be called on a Vue instance')
}
// $props object does not exist in Vue 2.1.x, so use $options.propsData instead
let _props
if (this.vm && this.vm.$options && this.vm.$options.propsData) {
_props = this.vm.$options.propsData
} else {
_props = this.vm.$props
}
return _props || {} // Return an empty object if no props exist
}

/**
* Sets vm data
*/
Expand Down
7 changes: 6 additions & 1 deletion test/resources/components/component-with-css-modules.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
<template>
<div :class="$style['color-red']"></div>
<div :class="$style['extension']"></div>
</template>

<style module>
.color-red {
color: red;
}

.extension {
composes: color-red;
background: blue;
}
</style>

<script>
Expand Down
25 changes: 25 additions & 0 deletions test/unit/specs/mount/Wrapper/attributes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { compileToFunctions } from 'vue-template-compiler'
import mount from '~src/mount'

describe('attributes', () => {
it('returns true if wrapper contains attribute matching value', () => {
const attribute = 'attribute'
const value = 'value'
const compiled = compileToFunctions(`<div ${attribute}=${value}></div>`)
const wrapper = mount(compiled)
expect(wrapper.attributes()).to.eql({ attribute: value })
})

it('returns empty object if wrapper does not contain any attributes', () => {
const compiled = compileToFunctions('<div />')
const wrapper = mount(compiled)
expect(wrapper.attributes()).to.eql({})
})

it('returns empoty object if wrapper element is null', () => {
const compiled = compileToFunctions('<div />')
const wrapper = mount(compiled)
wrapper.element = null
expect(wrapper.attributes()).to.eql({})
})
})
24 changes: 24 additions & 0 deletions test/unit/specs/mount/Wrapper/classes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

import { compileToFunctions } from 'vue-template-compiler'
import mount from '~src/mount'
import ComponentWithCssModules from '~resources/components/component-with-css-modules.vue'

describe('classes', () => {
it('returns array of class names if wrapper has class names', () => {
const compiled = compileToFunctions('<div class="a-class b-class" />')
const wrapper = mount(compiled)
expect(wrapper.classes()).to.eql(['a-class', 'b-class'])
})

it('returns empty array if wrapper has no classes', () => {
const compiled = compileToFunctions('<div />')
const wrapper = mount(compiled)
expect(wrapper.classes()).to.eql([])
})

it('returns original class names when element mapped in css modules', () => {
const wrapper = mount(ComponentWithCssModules)

expect(wrapper.classes()).to.eql(['extension', 'color-red'])
})
})
28 changes: 28 additions & 0 deletions test/unit/specs/mount/Wrapper/props.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { compileToFunctions } from 'vue-template-compiler'
import mount from '~src/mount'
import ComponentWithProps from '~resources/components/component-with-props.vue'

describe('props', () => {
it('returns true if wrapper has prop', () => {
const prop1 = {}
const prop2 = 'string val'
const wrapper = mount(ComponentWithProps, {
propsData: { prop1, prop2 }
})
expect(wrapper.props()).to.eql({ prop1: {}, prop2: 'string val' })
})

it('returns an empty object if wrapper does not have props', () => {
const compiled = compileToFunctions('<div />')
const wrapper = mount(compiled)
expect(wrapper.props()).to.eql({})
})

it('throws an error if called on a non vm wrapper', () => {
const compiled = compileToFunctions('<div><p /></div>')
const p = mount(compiled).findAll('p').at(0)
const message = '[vue-test-utils]: wrapper.props() must be called on a Vue instance'
const fn = () => p.props()
expect(fn).to.throw().with.property('message', message)
})
})
18 changes: 18 additions & 0 deletions test/unit/specs/mount/WrapperArray/attributes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { compileToFunctions } from 'vue-template-compiler'
import mount from '~src/mount'

describe('attributes', () => {
it('throws error if wrapper array contains no items', () => {
const compiled = compileToFunctions('<div />')
const message = '[vue-test-utils]: attributes cannot be called on 0 items'
expect(() => mount(compiled).findAll('p').attributes('p')).to.throw().with.property('message', message)
})

it('throws error when called on a WrapperArray', () => {
const compiled = compileToFunctions('<div><div /></div>')
const wrapper = mount(compiled)
const message = '[vue-test-utils]: attributes must be called on a single wrapper, use at(i) to access a wrapper'
const fn = () => wrapper.findAll('div').attributes()
expect(fn).to.throw().with.property('message', message)
})
})
18 changes: 18 additions & 0 deletions test/unit/specs/mount/WrapperArray/classes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { compileToFunctions } from 'vue-template-compiler'
import mount from '~src/mount'

describe('classes', () => {
it('throws error if wrapper array contains no items', () => {
const compiled = compileToFunctions('<div />')
const message = '[vue-test-utils]: classes cannot be called on 0 items'
expect(() => mount(compiled).findAll('p').classes('p')).to.throw().with.property('message', message)
})

it('throws error when called on a WrapperArray', () => {
const compiled = compileToFunctions('<div><div /></div>')
const wrapper = mount(compiled)
const message = '[vue-test-utils]: classes must be called on a single wrapper, use at(i) to access a wrapper'
const fn = () => wrapper.findAll('div').classes()
expect(fn).to.throw().with.property('message', message)
})
})
18 changes: 18 additions & 0 deletions test/unit/specs/mount/WrapperArray/props.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { compileToFunctions } from 'vue-template-compiler'
import mount from '~src/mount'

describe('props', () => {
it('throws error if wrapper array contains no items', () => {
const compiled = compileToFunctions('<div />')
const message = '[vue-test-utils]: props cannot be called on 0 items'
expect(() => mount(compiled).findAll('p').props('p')).to.throw().with.property('message', message)
})

it('throws error when called on a WrapperArray', () => {
const compiled = compileToFunctions('<div><div /></div>')
const wrapper = mount(compiled)
const message = '[vue-test-utils]: props must be called on a single wrapper, use at(i) to access a wrapper'
const fn = () => wrapper.findAll('div').props()
expect(fn).to.throw().with.property('message', message)
})
})
21 changes: 21 additions & 0 deletions test/unit/specs/wrappers/error-wrapper.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ describe('ErrorWrapper', () => {
expect(() => error.at()).to.throw().with.property('message', message)
})

it('attributes throws error when called', () => {
const selector = 'div'
const message = `[vue-test-utils]: find did not return ${selector}, cannot call attributes() on empty Wrapper`
const error = new ErrorWrapper(selector)
expect(() => error.attributes()).to.throw().with.property('message', message)
})

it('classes throws error when called', () => {
const selector = 'div'
const message = `[vue-test-utils]: find did not return ${selector}, cannot call classes() on empty Wrapper`
const error = new ErrorWrapper(selector)
expect(() => error.classes()).to.throw().with.property('message', message)
})

it('contains throws error when called', () => {
const selector = 'div'
const message = `[vue-test-utils]: find did not return ${selector}, cannot call contains() on empty Wrapper`
Expand Down Expand Up @@ -106,6 +120,13 @@ describe('ErrorWrapper', () => {
expect(() => error.name()).to.throw().with.property('message', message)
})

it('props throws error when called', () => {
const selector = 'div'
const message = `[vue-test-utils]: find did not return ${selector}, cannot call props() on empty Wrapper`
const error = new ErrorWrapper(selector)
expect(() => error.props()).to.throw().with.property('message', message)
})

it('text throws error when called', () => {
const selector = 'div'
const message = `[vue-test-utils]: find did not return ${selector}, cannot call text() on empty Wrapper`
Expand Down
36 changes: 36 additions & 0 deletions test/unit/specs/wrappers/wrapper-array.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,30 @@ describe('WrapperArray', () => {
expect(() => wrapperArray.findAll()).to.throw().with.property('message', message)
})

it('attributes throws error if called when there are 0 items in wrapper array', () => {
const wrapperArray = new WrapperArray()
const message = '[vue-test-utils]: attributes cannot be called on 0 items'
expect(() => wrapperArray.attributes()).to.throw().with.property('message', message)
})

it('attributes throws error if called when there are items in wrapper array', () => {
const wrapperArray = new WrapperArray([1])
const message = '[vue-test-utils]: attributes must be called on a single wrapper, use at(i) to access a wrapper'
expect(() => wrapperArray.attributes()).to.throw().with.property('message', message)
})

it('classes throws error if called when there are 0 items in wrapper array', () => {
const wrapperArray = new WrapperArray()
const message = '[vue-test-utils]: classes cannot be called on 0 items'
expect(() => wrapperArray.classes()).to.throw().with.property('message', message)
})

it('classes throws error if called when there are items in wrapper array', () => {
const wrapperArray = new WrapperArray([1])
const message = '[vue-test-utils]: classes must be called on a single wrapper, use at(i) to access a wrapper'
expect(() => wrapperArray.classes()).to.throw().with.property('message', message)
})

it('contains returns true if every wrapper.contains() returns true', () => {
const selector = 'selector'
const contains = sinon.stub()
Expand Down Expand Up @@ -194,6 +218,18 @@ describe('WrapperArray', () => {
expect(() => wrapperArray.name()).to.throw().with.property('message', message)
})

it('props throws error if called when there are 0 items in wrapper array', () => {
const wrapperArray = new WrapperArray()
const message = '[vue-test-utils]: props cannot be called on 0 items'
expect(() => wrapperArray.props()).to.throw().with.property('message', message)
})

it('props throws error if called when there are items in wrapper array', () => {
const wrapperArray = new WrapperArray([1])
const message = '[vue-test-utils]: props must be called on a single wrapper, use at(i) to access a wrapper'
expect(() => wrapperArray.props()).to.throw().with.property('message', message)
})

it('text throws error if called when there are 0 items in wrapper array', () => {
const wrapperArray = new WrapperArray()
const message = '[vue-test-utils]: text cannot be called on 0 items'
Expand Down