This repository has been archived by the owner on Jul 12, 2019. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(icons-vue): update render and add tests (#383)
* fix: icons-vue class and style attributes * fix: icons-vue class and style attributes part 2 * fix: icons-vue class and style attrutes - remove console.log * fix: icons-vue class and style attrutes - minimise diff * chore(icons-vue): add initial test setup for createFromInfo * chore: update pr tests and fix aria-label * fix(icons-vue): add title support and update tests * chore(icons-vue): remove unused variable * chore: fix class test and remove style story
- Loading branch information
Showing
7 changed files
with
250 additions
and
46 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
210 changes: 210 additions & 0 deletions
210
packages/icons-vue/src/__tests__/createFromInfo-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
/** | ||
* Copyright IBM Corp. 2018, 2018 | ||
* | ||
* This source code is licensed under the Apache-2.0 license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @jest-environment jsdom | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const Module = require('module'); | ||
const rollup = require('rollup').rollup; | ||
const virtual = require('rollup-plugin-virtual'); | ||
const vm = require('vm'); | ||
// Include version of Vue that has built-in template support | ||
const Vue = require('vue/dist/vue'); | ||
|
||
async function getModuleFromString( | ||
string, | ||
{ external = ['@carbon/icon-helpers'], name = '<MockIconModule>' } = {} | ||
) { | ||
const bundle = await rollup({ | ||
input: '__entrypoint__', | ||
external, | ||
plugins: [ | ||
virtual({ | ||
__entrypoint__: string, | ||
}), | ||
], | ||
}); | ||
const { code } = await bundle.generate({ | ||
format: 'cjs', | ||
}); | ||
const sandbox = { | ||
console, | ||
module: new Module(name), | ||
require, | ||
}; | ||
vm.createContext(sandbox); | ||
vm.runInContext(code, sandbox); | ||
|
||
return sandbox.module.exports; | ||
} | ||
|
||
describe('createFromInfo', () => { | ||
describe('createModuleFromInfo', () => { | ||
let createModuleFromInfo; | ||
let descriptor; | ||
let info; | ||
let mountNode; | ||
let MockIconComponent; | ||
let render; | ||
|
||
beforeEach(async () => { | ||
createModuleFromInfo = require('../createFromInfo').createModuleFromInfo; | ||
|
||
descriptor = { | ||
attrs: { | ||
width: 16, | ||
height: 16, | ||
viewBox: '0 0 16 16', | ||
}, | ||
content: [ | ||
{ | ||
elem: 'circle', | ||
attrs: { | ||
cx: 8, | ||
cy: 8, | ||
r: 8, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
info = { | ||
descriptor, | ||
moduleName: 'MockIcon', | ||
}; | ||
|
||
mountNode = document.createElement('div'); | ||
document.body.appendChild(mountNode); | ||
|
||
MockIconComponent = await getModuleFromString(createModuleFromInfo(info)); | ||
|
||
render = ({ components, template, ...rest }) => { | ||
const rootNode = mountNode.appendChild(document.createElement('div')); | ||
|
||
new Vue({ | ||
el: rootNode, | ||
components, | ||
template, | ||
...rest, | ||
}); | ||
|
||
// Vue ends up replacing `rootNode` so we need to use the `mountNode` to | ||
// look for the last `<MockIcon />` added to the DOM, most likely this | ||
// is the last <svg> node that has been inserted | ||
return mountNode.querySelector('svg:last-of-type'); | ||
}; | ||
}); | ||
|
||
afterEach(() => { | ||
mountNode.parentNode.removeChild(mountNode); | ||
}); | ||
|
||
it('should create a renderable component', async () => { | ||
const originalConsoleError = console.error; | ||
console.error = jest.fn(); | ||
|
||
render({ | ||
components: { | ||
[MockIconComponent.name]: MockIconComponent, | ||
}, | ||
template: `<MockIcon />`, | ||
}); | ||
|
||
// Verify that we can render without errors | ||
expect(console.error).not.toHaveBeenCalled(); | ||
|
||
console.error = originalConsoleError; | ||
}); | ||
|
||
it('should treat focusable as a string', async () => { | ||
const defaultNode = render({ | ||
components: { | ||
[MockIconComponent.name]: MockIconComponent, | ||
}, | ||
template: `<MockIcon />`, | ||
}); | ||
const focusableNode = render({ | ||
components: { | ||
[MockIconComponent.name]: MockIconComponent, | ||
}, | ||
template: `<MockIcon focusable="true" />`, | ||
}); | ||
|
||
expect(defaultNode.getAttribute('focusable')).toBe('false'); | ||
expect(focusableNode.getAttribute('focusable')).toBe('true'); | ||
}); | ||
|
||
it('should support rendering a title in the SVG markup', async () => { | ||
const node = render({ | ||
components: { | ||
[MockIconComponent.name]: MockIconComponent, | ||
}, | ||
template: `<MockIcon tabindex="0" title="Custom title" />`, | ||
}); | ||
|
||
const children = Array.from(node.children); | ||
expect(children[0].tagName).toBe('title'); | ||
|
||
for (let i = 1; i < descriptor.content.length; i++) { | ||
// We do i + 1 here since 0 is used for title above | ||
expect(children[i].tagName).toBe(descriptor.content[i].elem); | ||
} | ||
}); | ||
|
||
it('should support custom class names', async () => { | ||
const customClass = 'foo'; | ||
const dynamicClass = 'bar'; | ||
const node = render({ | ||
components: { | ||
[MockIconComponent.name]: MockIconComponent, | ||
}, | ||
data() { | ||
return { | ||
myDynamicClass: dynamicClass, | ||
}; | ||
}, | ||
template: `<MockIcon class="${customClass}" v-bind:class="myDynamicClass" />`, | ||
}); | ||
|
||
expect(node.classList.contains(customClass)).toBe(true); | ||
expect(node.classList.contains(dynamicClass)).toBe(true); | ||
}); | ||
|
||
it('should be focusable if aria-label and tabindex is used', async () => { | ||
const label = 'custom-label'; | ||
const node = render({ | ||
components: { | ||
[MockIconComponent.name]: MockIconComponent, | ||
}, | ||
template: `<MockIcon aria-label="${label}" tabindex="0" />`, | ||
}); | ||
|
||
expect(node.getAttribute('aria-label')).toBe(label); | ||
expect(node.getAttribute('role')).toBe('img'); | ||
expect(node.getAttribute('tabindex')).toBe('0'); | ||
expect(node.getAttribute('focusable')).toBe('true'); | ||
}); | ||
|
||
it('should create a clickable component', async () => { | ||
const onClick = jest.fn(); | ||
const node = render({ | ||
components: { | ||
[MockIconComponent.name]: MockIconComponent, | ||
}, | ||
data: { | ||
onClick, | ||
}, | ||
template: `<MockIcon aria-label="custom-label" tabindex="0" v-on:click="onClick" />`, | ||
}); | ||
|
||
node.dispatchEvent(new MouseEvent('click')); | ||
|
||
expect(onClick).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters