bootstrap-vue
Version:
With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extens
530 lines (473 loc) • 18.3 kB
JavaScript
import { mount } from '@vue/test-utils'
import { isVue3 } from '../../vue'
import { waitNT } from '../../../tests/utils'
import { BTable } from './table'
const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }]
const testFields = ['a', 'b', 'c']
describe('table > tbody row events', () => {
it('should emit row-clicked event when a row is clicked', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Row-clicked will only occur if there is a registered listener
'row-clicked': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-clicked')).toBeUndefined()
await $rows.at(1).trigger('click')
expect(wrapper.emitted('row-clicked')).toBeDefined()
expect(wrapper.emitted('row-clicked').length).toBe(1)
expect(wrapper.emitted('row-clicked')[0][0]).toEqual(testItems[1]) // Row item
expect(wrapper.emitted('row-clicked')[0][1]).toEqual(1) // Row index
expect(wrapper.emitted('row-clicked')[0][2]).toBeInstanceOf(MouseEvent) // Event
wrapper.destroy()
})
it('should not emit row-clicked event when prop busy is set', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
busy: true
},
listeners: {
// Row-clicked will only occur if there is a registered listener
'row-clicked': () => {}
}
})
expect(wrapper).toBeDefined()
expect(wrapper.element.tagName).toBe('TABLE')
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-clicked')).toBeUndefined()
await $rows.at(1).trigger('click')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
wrapper.destroy()
})
it('should not emit row-clicked event when vm.localBusy is true', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Row-clicked will only occur if there is a registered listener
'row-clicked': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-clicked')).toBeUndefined()
await wrapper.setData({
localBusy: true
})
await $rows.at(1).trigger('click')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
wrapper.destroy()
})
it('should emit row-dblclicked event when a row is dblclicked', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Row-dblclicked will only occur if there is a registered listener
'row-dblclicked': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-dblclicked')).toBeUndefined()
await $rows.at(1).trigger('dblclick')
expect(wrapper.emitted('row-dblclicked')).toBeDefined()
expect(wrapper.emitted('row-dblclicked').length).toBe(1)
expect(wrapper.emitted('row-dblclicked')[0][0]).toEqual(testItems[1]) // Row item
expect(wrapper.emitted('row-dblclicked')[0][1]).toEqual(1) // Row index
expect(wrapper.emitted('row-dblclicked')[0][2]).toBeInstanceOf(MouseEvent) // Event
wrapper.destroy()
})
it('should not emit row-dblclicked event when a row is dblclicked and table busy', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
busy: true
},
listeners: {
// Row-dblclicked will only occur if there is a registered listener
'row-dblclicked': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-dblclicked')).toBeUndefined()
await $rows.at(1).trigger('dblclick')
expect(wrapper.emitted('row-dblclicked')).toBeUndefined()
wrapper.destroy()
})
it('should emit row-middle-clicked event when a row is middle clicked', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Row-middle-clicked will only occur if there is a registered listener
'row-middle-clicked': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-middle-clicked')).toBeUndefined()
await $rows.at(1).trigger('auxclick', { which: 2 })
expect(wrapper.emitted('row-middle-clicked')).toBeDefined()
expect(wrapper.emitted('row-middle-clicked').length).toBe(1)
expect(wrapper.emitted('row-middle-clicked')[0][0]).toEqual(testItems[1]) // Row item
expect(wrapper.emitted('row-middle-clicked')[0][1]).toEqual(1) // Row index
expect(wrapper.emitted('row-middle-clicked')[0][2]).toBeInstanceOf(Event) // Event
wrapper.destroy()
})
it('should not emit row-middle-clicked event when a row is middle clicked and table busy', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
busy: true
},
listeners: {
// Row-middle-clicked will only occur if there is a registered listener
'row-middle-clicked': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-middle-clicked')).toBeUndefined()
await $rows.at(1).trigger('auxclick', { which: 2 })
expect(wrapper.emitted('row-middle-clicked')).toBeUndefined()
wrapper.destroy()
})
it('should emit row-contextmenu event when a row is right clicked', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Row-contextmenu will only occur if there is a registered listener
'row-contextmenu': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-contextmenu')).toBeUndefined()
await $rows.at(1).trigger('contextmenu')
expect(wrapper.emitted('row-contextmenu')).toBeDefined()
expect(wrapper.emitted('row-contextmenu').length).toBe(1)
expect(wrapper.emitted('row-contextmenu')[0][0]).toEqual(testItems[1]) // Row item
expect(wrapper.emitted('row-contextmenu')[0][1]).toEqual(1) // Row index
expect(wrapper.emitted('row-contextmenu')[0][2]).toBeInstanceOf(Event) // Event
wrapper.destroy()
})
it('should not emit row-contextmenu event when a row is right clicked and table busy', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
busy: true
},
listeners: {
// Row-contextmenu will only occur if there is a registered listener
'row-contextmenu': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-contextmenu')).toBeUndefined()
await $rows.at(1).trigger('contextmenu')
expect(wrapper.emitted('row-contextmenu')).toBeUndefined()
wrapper.destroy()
})
it('should emit row-hovered event when a row is hovered', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Row-hovered will only occur if there is a registered listener
'row-hovered': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-hovered')).toBeUndefined()
await $rows.at(1).trigger('mouseenter')
expect(wrapper.emitted('row-hovered')).toBeDefined()
expect(wrapper.emitted('row-hovered').length).toBe(1)
expect(wrapper.emitted('row-hovered')[0][0]).toEqual(testItems[1]) // Row item
expect(wrapper.emitted('row-hovered')[0][1]).toEqual(1) // Row index
expect(wrapper.emitted('row-hovered')[0][2]).toBeInstanceOf(MouseEvent) // Event
wrapper.destroy()
})
it('should not emit row-hovered event when a row is hovered and no listener', async () => {
if (isVue3) {
// We can't track if we have an event listener in vue3 so we skip this test for vue 3
return
}
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-hovered')).toBeUndefined()
await $rows.at(1).trigger('mouseenter')
expect(wrapper.emitted('row-hovered')).toBeUndefined()
wrapper.destroy()
})
it('should not emit row-hovered event when a row is hovered and table busy', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
busy: true
},
listeners: {
// Row-hovered will only occur if there is a registered listener
'row-hovered': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-hovered')).toBeUndefined()
await $rows.at(1).trigger('mouseenter')
expect(wrapper.emitted('row-hovered')).toBeUndefined()
wrapper.destroy()
})
it('should emit row-unhovered event when a row is unhovered', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Row-unhovered will only occur if there is a registered listener
'row-unhovered': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-unhovered')).toBeUndefined()
await $rows.at(1).trigger('mouseleave')
expect(wrapper.emitted('row-unhovered')).toBeDefined()
expect(wrapper.emitted('row-unhovered').length).toBe(1)
expect(wrapper.emitted('row-unhovered')[0][0]).toEqual(testItems[1]) // Row item
expect(wrapper.emitted('row-unhovered')[0][1]).toEqual(1) // Row index
expect(wrapper.emitted('row-unhovered')[0][2]).toBeInstanceOf(MouseEvent) // Event
wrapper.destroy()
})
it('should not emit row-unhovered event when a row is hovered and no listener', async () => {
if (isVue3) {
// We can't track if we have an event listener in vue3 so we skip this test for vue 3
return
}
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-unhovered')).toBeUndefined()
await $rows.at(1).trigger('mouseleave')
expect(wrapper.emitted('row-unhovered')).toBeUndefined()
wrapper.destroy()
})
it('should not emit row-unhovered event when a row is unhovered and table busy', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
busy: true
},
listeners: {
// Row-unhovered will only occur if there is a registered listener
'row-unhovered': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-unhovered')).toBeUndefined()
await $rows.at(1).trigger('mouseleave')
expect(wrapper.emitted('row-unhovered')).toBeUndefined()
wrapper.destroy()
})
it('should emit row-clicked event when a row is focusable and enter pressed', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Rows will only have tabindex=0 when a row-clicked listener present
'row-clicked': () => {}
},
attachTo: document.body
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-clicked')).toBeUndefined()
$rows.at(1).element.focus() // Event only works when the TR is focused
await waitNT(wrapper.vm)
await $rows.at(1).trigger('keydown.enter')
await waitNT(wrapper.vm)
expect(wrapper.emitted('row-clicked')).toBeDefined()
expect(wrapper.emitted('row-clicked').length).toBe(1)
expect(wrapper.emitted('row-clicked')[0][0]).toEqual(testItems[1]) // Row item
expect(wrapper.emitted('row-clicked')[0][1]).toEqual(1) // Row index
// Note: the KeyboardEvent is passed to the row-clicked handler
expect(wrapper.emitted('row-clicked')[0][2]).toBeInstanceOf(KeyboardEvent) // Event
wrapper.destroy()
})
it('should not emit row-clicked event when a row is focusable, enter pressed, and table busy', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
busy: true
},
listeners: {
// Row-clicked will only occur if there is a registered listener
'row-clicked': () => {}
}
})
expect(wrapper).toBeDefined()
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect(wrapper.emitted('row-clicked')).toBeUndefined()
$rows.at(1).element.focus() // Event only works when the TR is focused
await $rows.at(1).trigger('keydown.enter')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
wrapper.destroy()
})
it('should not emit row-clicked event when clicking on a button or other interactive element', async () => {
const wrapper = mount(BTable, {
attachTo: document.body,
propsData: {
// Add extra virtual columns
fields: [].concat(testFields, ['d', 'e', 'f']),
// We just use a single row for testing
items: [testItems[0]]
},
slots: {
// In Vue 2.6x, slots get translated into scopedSlots
'cell(a)': '<button id="a">button</button>',
'cell(b)': '<input id="b">',
'cell(c)': '<a href="#" id="c">link</a>',
'cell(d)':
'<div class="dropdown-menu"><div id="d" class="dropdown-item">dropdown</div></div>',
'cell(e)': '<label for="e">label</label><input id="e">',
'cell(f)': '<label class="f-label"><input id="e"></label>'
},
listeners: {
// Row-clicked will only occur if there is a registered listener
'row-clicked': () => {}
}
})
expect(wrapper).toBeDefined()
expect(wrapper.element.tagName).toBe('TABLE')
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(1)
expect(wrapper.emitted('row-clicked')).toBeUndefined()
const $btn = wrapper.find('button[id="a"]')
expect($btn.exists()).toBe(true)
await $btn.trigger('click')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
const $input = wrapper.find('input[id="b"]')
expect($input.exists()).toBe(true)
await $input.trigger('click')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
const $link = wrapper.find('a[id="c"]')
expect($link.exists()).toBe(true)
await $link.trigger('click')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
const $dd = wrapper.find('div[id="d"]')
expect($dd.exists()).toBe(true)
await $dd.trigger('click')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
const $label = wrapper.find('label[for="e"]')
expect($label.exists()).toBe(true)
await $label.trigger('click')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
const $labelf = wrapper.find('label.f-label')
expect($labelf.exists()).toBe(true)
await $labelf.trigger('click')
expect(wrapper.emitted('row-clicked')).toBeUndefined()
wrapper.destroy()
})
it('keyboard events moves focus to appropriate rows', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems
},
listeners: {
// Tabindex will only be set if there is a row-clicked listener
'row-clicked': () => {}
},
attachTo: document.body
})
expect(wrapper).toBeDefined()
await waitNT(wrapper.vm)
const $rows = wrapper.findAll('tbody > tr')
expect($rows.length).toBe(3)
expect($rows.wrappers.every(w => w.attributes('tabindex') === '0')).toBe(true)
expect(document.activeElement).not.toBe($rows.at(0).element)
expect(document.activeElement).not.toBe($rows.at(1).element)
expect(document.activeElement).not.toBe($rows.at(2).element)
$rows.at(0).element.focus()
expect(document.activeElement).toBe($rows.at(0).element)
await $rows.at(0).trigger('keydown.end')
expect(document.activeElement).toBe($rows.at(2).element)
await $rows.at(2).trigger('keydown.home')
expect(document.activeElement).toBe($rows.at(0).element)
await $rows.at(0).trigger('keydown.down')
expect(document.activeElement).toBe($rows.at(1).element)
await $rows.at(1).trigger('keydown.up')
expect(document.activeElement).toBe($rows.at(0).element)
await $rows.at(0).trigger('keydown.down', { shiftKey: true })
expect(document.activeElement).toBe($rows.at(2).element)
await $rows.at(2).trigger('keydown.up', { shiftKey: true })
expect(document.activeElement).toBe($rows.at(0).element)
// Should only move focus if TR was target
await $rows
.at(0)
.find('td')
.trigger('keydown.down')
expect(document.activeElement).toBe($rows.at(0).element)
wrapper.destroy()
})
})