From b1d78e546f36fe20eb6ed4374e2000fbabb06500 Mon Sep 17 00:00:00 2001 From: Alex Regan Date: Sun, 28 May 2017 08:53:21 -0700 Subject: [PATCH] Tests & Breadcrumb improvements (#466) * Add bootstrap -> bootstrap-vue migration notes * add bFormFieldset label text alignment options * fixes per @pi0 * add focus method proxied to element ref * Popover dom node leak fix * badge: add refs and generate variants * improving error messages when matcher fn called improperly * badge test suite * propagate click event and emit click with event obj * fix for dynamically setting the active link Before the link would always set the last item of the array as the active one. However, the active state was read from the user submitted items.active prop, rather than the normalized item.__active. Some more semantic improvements to make the code clearer * formatting * use only one lookup of array length * two components and items lists for tests * breadcrumb testing * stop scroll to top behavior --- __tests__/components/badge.spec.js | 44 ++++++++++++-- __tests__/components/breadcrumb.spec.js | 79 +++++++++++++++++++++++-- __tests__/helpers.js | 8 ++- examples/badge/demo.html | 18 +++++- examples/badge/demo.js | 14 ++++- examples/breadcrumb/demo.html | 3 +- examples/breadcrumb/demo.js | 20 ++++++- lib/components/breadcrumb.vue | 49 ++++++++------- lib/mixins/link.js | 8 ++- 9 files changed, 204 insertions(+), 39 deletions(-) diff --git a/__tests__/components/badge.spec.js b/__tests__/components/badge.spec.js index 3bbd3746f6a..f8703a5ebba 100755 --- a/__tests__/components/badge.spec.js +++ b/__tests__/components/badge.spec.js @@ -1,6 +1,42 @@ -import {loadFixture, testVM} from '../helpers'; +import { loadFixture, testVM } from '../helpers' + +const variantList = [ + 'default', + 'primary', + 'success', + 'info', + 'warning', + 'danger', +].map(variant => { + return { ref: `badge_${variant}`, variant } +}) describe('badge', async() => { - beforeEach(loadFixture('badge')); - testVM(); -}); \ No newline at end of file + beforeEach(loadFixture('badge')) + testVM() + + it('should apply variant classes', async() => { + const { app: { $refs, $el } } = window + + expect($refs.badge_pill).toHaveAllClasses(['badge', 'badge-pill']) + + variantList.forEach(({ ref, variant }) => { + const vm = $refs[ref][0] + expect(vm).toHaveAllClasses(['badge', `badge-${variant}`]) + }) + }) + + it('should apply default pill class when not passed variant', async() => { + const { app: { $refs, $el } } = window + + const vm = $refs.no_props + expect(vm).toHaveClass('badge-default') + }) + + it('should not apply pill class when not passed pill boolean prop', async() => { + const { app: { $refs, $el } } = window + + const vm = $refs.no_props + expect(vm).not.toHaveClass('badge-pill') + }) +}); diff --git a/__tests__/components/breadcrumb.spec.js b/__tests__/components/breadcrumb.spec.js index 71d38a3c8d1..767e7d8488d 100755 --- a/__tests__/components/breadcrumb.spec.js +++ b/__tests__/components/breadcrumb.spec.js @@ -1,6 +1,77 @@ -import {loadFixture, testVM} from '../helpers'; +import { loadFixture, testVM } from '../helpers' describe('breadcrumb', async() => { - beforeEach(loadFixture('breadcrumb')); - testVM(); -}); \ No newline at end of file + beforeEach(loadFixture('breadcrumb')) + testVM() + + it('should apply bootstrap breadcrumb classes', async() => { + const { app: { $refs, $el } } = window + const vm = $refs.breadcrumb1 + const $ol = vm.$el + + expect($ol.classList.contains('breadcrumb')).toBe(true) + + Array.from($ol.children).forEach($li => { + if ($li.tagName === 'LI') { + expect($li.classList.contains('breadcrumb-item')).toBe(true) + } + }) + }) + + it('should apply ARIA roles', async() => { + const { app: { $refs, $el } } = window + const vm = $refs.breadcrumb1 + const $ol = vm.$el + + expect($ol.getAttribute('role')).toBe('navigation') + + Array.from($ol.children).forEach($li => { + if ($li.tagName === 'LI') { + expect($li.getAttribute('role')).toBe('presentation') + } + }) + }) + + it('should apply active class', async() => { + const { app: { $refs, $el } } = window + const vm = $refs.breadcrumb2 + const $listItems = Array.from(vm.$el.children) + + app.items2.forEach((item, i) => { + if (item.active) { + expect($listItems[i].classList.contains('active')).toBe(true) + } + }) + }) + + it('should default active class to last item only when no true active prop provided', async() => { + const { app: { $refs, $el } } = window + const vm = $refs.breadcrumb1 + const $listItems = Array.from(vm.$el.children) + const itemsLength = app.items.length + + app.items.forEach((item, i) => { + const isLast = i === itemsLength - 1 + + if (isLast) { + expect($listItems[i].classList.contains('active')).toBe(true) + } else { + expect($listItems[i].classList.contains('active')).toBe(false) + } + }) + }) + + it('should emit a click event with the item when clicked', async() => { + const { app: { $refs, $el } } = window + const vm = $refs.breadcrumb2 + const spy = jest.fn(); + + vm.$on('click', spy) + const $listItems = Array.from(vm.$el.children) + + app.items2.forEach((item, index) => { + $listItems[index].click() + expect(spy).toHaveBeenCalledWith(item) + }) + }) +}); diff --git a/__tests__/helpers.js b/__tests__/helpers.js index 499e7e24cb0..3f1ae0e8ece 100755 --- a/__tests__/helpers.js +++ b/__tests__/helpers.js @@ -8,7 +8,12 @@ const throwIfNotVueInstance = vm => { if (!vm instanceof Vue) { // debugging breadcrumbs in case a non-Vue instance gets erroneously passed // makes the error easier to fix than example: "Cannot read _prevClass of undefined" - throw new TypeError('The matcher `vmToHaveClasses` expects Vue instance.') + throw new TypeError(`The matcher function expects Vue instance. Given ${typeof vm}`) + } +} +const throwIfNotArray = array => { + if (!Array.isArray(array)) { + throw new TypeError(`The matcher requires an array. Given ${typeof array}`) } } @@ -59,6 +64,7 @@ expect.extend({ }, toHaveAllClasses(vm, classList) { throwIfNotVueInstance(vm) + throwIfNotArray(classList) let pass = true; let missingClassNames = [] diff --git a/examples/badge/demo.html b/examples/badge/demo.html index 967f3fefe06..c1736fd2389 100755 --- a/examples/badge/demo.html +++ b/examples/badge/demo.html @@ -1,5 +1,17 @@
-

Example heading New

-

Example heading New

-
Example heading New
+

Example heading + New +

+

Example heading + New +

+
Example heading + New +
+

+ {{ variant }} +

diff --git a/examples/badge/demo.js b/examples/badge/demo.js index f59c37eb80c..2ca845b262b 100755 --- a/examples/badge/demo.js +++ b/examples/badge/demo.js @@ -1,3 +1,15 @@ window.app = new Vue({ - el: '#app' + el: '#app', + data() { + return { + variants: [ + 'default', + 'primary', + 'success', + 'info', + 'warning', + 'danger', + ] + } + }, }); diff --git a/examples/breadcrumb/demo.html b/examples/breadcrumb/demo.html index 71a727e3b80..c71729e0e19 100755 --- a/examples/breadcrumb/demo.html +++ b/examples/breadcrumb/demo.html @@ -1,3 +1,4 @@
- + +
diff --git a/examples/breadcrumb/demo.js b/examples/breadcrumb/demo.js index 44f4aba815a..467c6d8e81f 100755 --- a/examples/breadcrumb/demo.js +++ b/examples/breadcrumb/demo.js @@ -2,14 +2,30 @@ window.app = new Vue({ el: '#app', data: { items: [{ + text: 'Home', + link: 'https://bootstrap-vue.github.io' + }, { text: 'Admin', - link: '#' + to: '#', + active: false }, { text: 'Manage', link: '#' }, { text: 'Library', + }], + items2: [{ + text: 'Home', + link: 'https://bootstrap-vue.github.io' + }, { + text: 'Admin', + link: '#', active: true + }, { + text: 'Manage', + link: '#' + }, { + text: 'Library', }] - } + }, }); diff --git a/lib/components/breadcrumb.vue b/lib/components/breadcrumb.vue index 3f9abb8f745..ce2dd2b3b4d 100755 --- a/lib/components/breadcrumb.vue +++ b/lib/components/breadcrumb.vue @@ -1,17 +1,16 @@