Skip to content

Commit

Permalink
Add unit tests for new ActionMenuItem component
Browse files Browse the repository at this point in the history
  • Loading branch information
kulmann committed Nov 16, 2021
1 parent 1609e52 commit 0846a70
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ We have enabled batch actions in the context menu for when multiple resources ar

https://github.com/owncloud/web/pull/5973
https://github.com/owncloud/web/issues/5968
https://github.com/owncloud/web/issues/5977
16 changes: 13 additions & 3 deletions packages/web-app-files/src/components/ActionMenuItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,23 @@
:is="action.componentType"
v-bind="getComponentProps(action, items)"
:class="['oc-text-bold', action.class]"
data-testid="action-handler"
v-on="getComponentListeners(action, items)"
>
<oc-icon v-if="action.icon" :name="action.icon" size="medium" />
<oc-img v-else-if="action.img" :src="action.img" alt="" class="oc-icon oc-icon-m" />
<span class="oc-files-context-action-label">{{ action.label(filterParams) }}</span>
<oc-icon v-if="action.icon" data-testid="action-icon" :name="action.icon" size="medium" />
<oc-img
v-else-if="action.img"
data-testid="action-img"
:src="action.img"
alt=""
class="oc-icon oc-icon-m"
/>
<span class="oc-files-context-action-label" data-testid="action-label">{{
action.label(filterParams)
}}</span>
<span
v-if="action.opensInNewWindow"
data-testid="action-sr-hint"
class="oc-invisible-sr"
v-text="$gettext('(Opens in new window)')"
/>
Expand Down
3 changes: 2 additions & 1 deletion packages/web-app-files/src/mixins/actions/delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default {
},
{
// this menu item is ONLY for the trashbin (permanently delete a file/folder)
name: 'delete-permanent',
icon: 'delete',
label: () => this.$gettext('Delete'),
handler: this.$_delete_trigger,
Expand All @@ -45,7 +46,7 @@ export default {
return resources.length > 0
},
componentType: 'oc-button',
class: 'oc-files-actions-delete-trigger'
class: 'oc-files-actions-delete-permanent-trigger'
}
]
}
Expand Down
97 changes: 58 additions & 39 deletions packages/web-app-files/tests/__fixtures__/fileActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,79 +86,98 @@ exports.apps = {

const fileActions = {
download: {
label: 'Download',
class: 'oc-files-actions-sidebar-download-trigger',
selector: '.oc-files-actions-sidebar-download-trigger',
handler: jest.fn()
name: 'download-file',
icon: 'file_download',
handler: jest.fn(),
label: () => 'Download',
componentType: 'oc-button',
class: 'oc-files-actions-download-file-trigger',
selector: '.oc-files-actions-download-file-trigger'
},
copy: {
label: 'Copy',
class: 'oc-files-actions-sidebar-copy-trigger',
selector: '.oc-files-actions-sidebar-copy-trigger',
handler: jest.fn()
name: 'copy',
icon: 'file_copy',
handler: jest.fn(),
label: () => 'Copy',
componentType: 'oc-button',
class: 'oc-files-actions-copy-trigger',
selector: '.oc-files-actions-copy-trigger'
},
rename: {
label: 'Rename',
class: 'oc-files-actions-sidebar-rename-trigger',
selector: '.oc-files-actions-sidebar-rename-trigger',
handler: jest.fn()
name: 'rename',
icon: 'edit',
handler: jest.fn(),
label: () => 'Rename',
componentType: 'oc-button',
class: 'oc-files-actions-rename-trigger',
selector: '.oc-files-actions--rename-trigger'
},
move: {
label: 'Move',
class: 'oc-files-actions-sidebar-move-trigger',
selector: '.oc-files-actions-sidebar-move-trigger',
handler: jest.fn()
name: 'move',
icon: 'folder-move',
handler: jest.fn(),
label: () => 'Move',
componentType: 'oc-button',
class: 'oc-files-actions-move-trigger',
selector: '.oc-files-actions-move-trigger'
},
delete: {
label: 'Delete',
class: 'oc-files-actions-sidebar-delete-trigger',
selector: '.oc-files-actions-sidebar-delete-trigger',
handler: jest.fn()
name: 'delete',
icon: 'delete',
handler: jest.fn(),
label: () => 'Delete',
componentType: 'oc-button',
class: 'oc-files-actions-delete-trigger',
selector: '.oc-files-actions-delete-trigger'
},

'markdown-editor': {
label: 'Open in Markdown Editor',
class: 'oc-files-actions-sidebar-markdown-editor-trigger',
selector: '.oc-files-actions-sidebar-markdown-editor-trigger',
handler: jest.fn(),
label: () => 'Open in Markdown Editor',
class: 'oc-files-actions-markdown-editor-trigger',
selector: '.oc-files-actions-markdown-editor-trigger',
opensInNewWindow: true
},
'draw-io': {
label: 'Open in DrawIO',
class: 'oc-files-actions-sidebar-draw-io-trigger',
selector: '.oc-files-actions-sidebar-draw-io-trigger',
handler: jest.fn(),
label: () => 'Open in DrawIO',
class: 'oc-files-actions-draw-io-trigger',
selector: '.oc-files-actions-draw-io-trigger',
opensInNewWindow: true
},
mediaviewer: {
label: 'Open in MediaViewer',
class: 'oc-files-actions-sidebar-mediaviewer-trigger',
selector: '.oc-files-actions-sidebar-mediaviewer-trigger',
handler: jest.fn()
handler: jest.fn(),
label: () => 'Open in MediaViewer',
class: 'oc-files-actions-mediaviewer-trigger',
selector: '.oc-files-actions-mediaviewer-trigger'
},
'open-folder': {
label: 'Open Folder',
class: 'oc-files-actions-sidebar-navigate-trigger',
selector: '.oc-files-actions-sidebar-navigate-trigger',
handler: jest.fn()
navigate: {
name: 'navigate',
icon: 'folder-open',
route: 'files-personal',
label: () => 'Open Folder',
componentType: 'router-link',
class: 'oc-files-actions-navigate-trigger',
selector: '.oc-files-actions-navigate-trigger'
}
}

exports.fileActions = fileActions

exports.getActions = function (actions = []) {
const defaultActions = ['download', 'markdown-editor', 'draw-io', 'mediaviewer', 'open-folder']
const defaultActions = ['download', 'markdown-editor', 'draw-io', 'mediaviewer', 'navigate']

const res = []
for (const key of actions) {
const action = fileActions[key]

const actionObj = {
icon: key,
name: action.name,
icon: action.icon || key,
handler: action.handler,
label: action.label,
isEnabled: () => true,
label: () => action.label,
componentType: 'oc-button',
componentType: action.componentType || 'oc-button',
class: action.class,
canBeDefault: defaultActions.indexOf(key) > -1,
opensInNewWindow: action.opensInNewWindow || false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { shallowMount, createLocalVue, mount } from '@vue/test-utils'
import Vuex from 'vuex'
import DesignSystem from 'owncloud-design-system'
import GetTextPlugin from 'vue-gettext'

import ActionMenuItem from '@files/src/components/ActionMenuItem'
import { fileActions } from '../../__fixtures__/fileActions'

const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(DesignSystem)
localVue.use(GetTextPlugin, {
translations: 'does-not-matter.json',
silent: true
})

const selectors = {
handler: '[data-testid="action-handler"]',
icon: '[data-testid="action-icon"]',
img: '[data-testid="action-img"]',
label: '[data-testid="action-label"]',
srHint: '[data-testid="action-sr-hint"]'
}

describe('ActionMenuItem component', () => {
it('renders an icon if there is one defined in the action', () => {
const action = fileActions.download
const wrapper = getShallowWrapper(action)
expect(wrapper.find(selectors.icon).exists()).toBeTruthy()
expect(wrapper.find(selectors.icon).attributes().name).toBe(action.icon)
})
it('renders an image if there is one defined in the action', () => {
const action = { ...fileActions.download, img: 'https://owncloud.tld/img.png' }
const wrapper1 = getShallowWrapper(action)
expect(wrapper1.find(selectors.icon).exists()).toBeTruthy()
expect(wrapper1.find(selectors.icon).attributes().name).toBe(action.icon)
delete action.icon
const wrapper2 = getShallowWrapper(action)
expect(wrapper2.find(selectors.icon).exists()).toBeFalsy()
expect(wrapper2.find(selectors.img).exists()).toBeTruthy()
expect(wrapper2.find(selectors.img).attributes().src).toBe(action.img)
})
it('renders the action label', () => {
const action = fileActions.download
const wrapper = getShallowWrapper(action)
expect(wrapper.find(selectors.label).exists()).toBeTruthy()
expect(wrapper.find(selectors.label).text()).toBe(action.label())
})
describe('component is of type oc-button', () => {
it('calls the action handler on button click', async () => {
const action = fileActions.download
const spyHandler = jest.spyOn(action, 'handler')
const wrapper = getWrapper(action)
const button = wrapper.find(selectors.handler)
expect(button.exists()).toBeTruthy()
expect(button.element.tagName).toBe('BUTTON')
await button.trigger('click')
expect(spyHandler).toBeCalled()
})
})
describe('component is of type router-link', () => {
it('has a link', () => {
const action = fileActions.navigate
const wrapper = getWrapper(action)
const link = wrapper.find(selectors.handler)
expect(link.exists()).toBeTruthy()
expect(link.element.tagName).toBe('ROUTER-LINK-STUB')
// FIXME: to.name cannot be accessed, because the attributes().to holds a string containing `[object Object]`.
// That doesn't allow checking the name of the router-link.
// expect(link.attributes().href).toBe(action.route)
})
})
})

function getShallowWrapper(action, items = [], appearance = null) {
return shallowMount(ActionMenuItem, {
localVue,
propsData: {
action,
items,
...(appearance && { appearance })
}
})
}

function getWrapper(action, items = [], appearance = null) {
return mount(ActionMenuItem, {
localVue,
stubs: {
'router-link': true
},
propsData: {
action,
items,
...(appearance && { appearance })
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,8 @@ describe('Batch Actions component', () => {
jest.resetModules()
})

describe.each(['files-personal', 'files-favorites', 'files-public-list', 'files-shared-with-me'])(
'%s page',
(page) => {
const $route = {
name: page
}

it('should not display action buttons if no items are selected', () => {
const store = createStore({ selected: [] })
const wrapper = createShallowMountWrapper({
store,
mocks: {
$route: {
...$route,
meta: {
hasBulkActions: false
}
}
}
})
const actionButtons = wrapper.findAll(elSelector.ocButtonStub)

expect(actionButtons.length).toEqual(0)
})
}
)
it.todo('renders an empty list if there are no batch actions available')
it.todo('renders a button for each available batch action')
})

function createShallowMountWrapper(options = {}) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function createWrapper(options = {}) {
return shallowMount(FileDrop, {
localVue,
stubs: { translate: true, 'oc-dropzone': true },
props: {
propsData: {
rootPath: '/',
path: '/'
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('FileActions', () => {
jest.clearAllMocks()
})
it('renders action handlers as clickable elements', async () => {
const actions = ['copy', 'download', 'move', 'open-folder', 'markdown-editor']
const actions = ['copy', 'download', 'move', 'navigate', 'markdown-editor']
const wrapper = getWrapper(filesPersonalRoute, actions)

for (const button of actions) {
Expand Down

0 comments on commit 0846a70

Please sign in to comment.